github.com/decred/dcrlnd@v0.7.6/lnwallet/dcrwallet/wallet.go (about) 1 package dcrwallet 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/hex" 7 "fmt" 8 "strings" 9 "sync" 10 "sync/atomic" 11 "time" 12 13 "github.com/decred/dcrd/chaincfg/chainhash" 14 "github.com/decred/dcrd/chaincfg/v3" 15 "github.com/decred/dcrd/dcrec/secp256k1/v4" 16 "github.com/decred/dcrd/dcrutil/v4" 17 "github.com/decred/dcrd/hdkeychain/v3" 18 "github.com/decred/dcrd/txscript/v4/stdaddr" 19 "github.com/decred/dcrd/txscript/v4/stdscript" 20 "github.com/decred/dcrd/wire" 21 22 "github.com/decred/dcrlnd/btcwalletcompat" 23 "github.com/decred/dcrlnd/lnwallet" 24 "github.com/decred/dcrlnd/lnwallet/chainfee" 25 26 "decred.org/dcrwallet/v4/wallet" 27 base "decred.org/dcrwallet/v4/wallet" 28 "decred.org/dcrwallet/v4/wallet/txauthor" 29 "decred.org/dcrwallet/v4/wallet/udb" 30 walletloader "github.com/decred/dcrlnd/lnwallet/dcrwallet/loader" 31 ) 32 33 const ( 34 defaultAccount = uint32(udb.DefaultAccountNum) 35 scriptVersion = uint16(0) 36 37 // UnconfirmedHeight is the special case end height that is used to 38 // obtain unconfirmed transactions from ListTransactionDetails. 39 UnconfirmedHeight int32 = -1 40 41 // dryRunImportAccountNumAddrs represents the number of addresses we'll 42 // derive for an imported account's external and internal branch when a 43 // dry run is attempted. 44 dryRunImportAccountNumAddrs = 5 45 ) 46 47 const ( 48 // The following values are used by atomicWalletSync. Their 49 // interpretation is the following: 50 // 51 // - Unsynced: wallet just started and hasn't performed the first sync 52 // yet. 53 // - Synced: wallet is currently synced. 54 // - LostSync: wallet was synced in the past but lost the connection to 55 // the network and it's unknown whether it's synced or not. 56 syncStatusUnsynced uint32 = 0 57 syncStatusSynced = 1 58 syncStatusLostSync = 2 59 ) 60 61 // DcrWallet is an implementation of the lnwallet.WalletController interface 62 // backed by an active instance of dcrwallet. At the time of the writing of 63 // this documentation, this implementation requires a full dcrd node to 64 // operate. 65 // 66 // This struct implements the input.input.Signer, lnWallet.Messageinput.Signer, 67 // keychain.SecretKeyRing and keychain.KeyRing interfaces. 68 // 69 // Note that most of its functions might produce errors or panics until the 70 // wallet has been fully synced. 71 type DcrWallet struct { 72 // wallet is an active instance of dcrwallet. 73 wallet *base.Wallet 74 loader *walletloader.Loader 75 76 // atomicWalletSync controls the current sync status of the wallet. It 77 // MUST be used atomically. 78 atomicWalletSynced uint32 79 80 // syncedChan is a channel that is closed once the wallet has initially 81 // synced to the network. It is protected by atomicWalletSynced. 82 syncedChan chan struct{} 83 84 cfg *Config 85 86 netParams *chaincfg.Params 87 88 syncer WalletSyncer 89 90 ctx context.Context 91 cancelCtx func() 92 93 *walletKeyRing 94 } 95 96 // A compile time check to ensure that DcrWallet implements the 97 // WalletController interface. 98 var _ lnwallet.WalletController = (*DcrWallet)(nil) 99 100 // New returns a new fully initialized instance of DcrWallet given a valid 101 // configuration struct. 102 func New(cfg Config) (*DcrWallet, error) { 103 104 wallet := cfg.Wallet 105 loader := cfg.Loader 106 syncer := cfg.Syncer 107 108 if cfg.Syncer == nil { 109 return nil, fmt.Errorf("cfg.Syncer needs to be specified") 110 } 111 112 if cfg.Wallet == nil { 113 // Ensure the wallet exists or create it when the create flag 114 // is specified 115 netDir := NetworkDir(cfg.DataDir, cfg.NetParams) 116 loader = walletloader.NewLoader(cfg.NetParams, netDir, base.DefaultGapLimit) 117 walletExists, err := loader.WalletExists() 118 if err != nil { 119 return nil, err 120 } 121 122 if !walletExists { 123 // Wallet has never been created, perform initial set up. 124 wallet, err = loader.CreateNewWallet(context.TODO(), cfg.PublicPass, cfg.PrivatePass, 125 cfg.HdSeed, time.Now().Add(-time.Hour*24)) 126 if err != nil { 127 return nil, err 128 } 129 } else { 130 // Wallet has been created and been initialized at this point, 131 // open it along with all the required DB namepsaces, and the 132 // DB itself. 133 wallet, err = loader.OpenExistingWallet(context.TODO(), cfg.PublicPass) 134 if err != nil { 135 return nil, err 136 } 137 } 138 } 139 140 ctx, cancelCtx := context.WithCancel(context.Background()) 141 142 return &DcrWallet{ 143 cfg: &cfg, 144 wallet: wallet, 145 loader: loader, 146 syncer: syncer, 147 syncedChan: make(chan struct{}), 148 atomicWalletSynced: syncStatusUnsynced, 149 netParams: cfg.NetParams, 150 ctx: ctx, 151 cancelCtx: cancelCtx, 152 }, nil 153 } 154 155 // BackEnd returns the underlying ChainService's name as a string. 156 // 157 // This is a part of the WalletController interface. 158 func (b *DcrWallet) BackEnd() string { 159 if _, is := b.syncer.(*RPCSyncer); is { 160 // This package only supports full node backends for the moment 161 return "dcrd" 162 } 163 164 if _, is := b.syncer.(*SPVSyncer); is { 165 // This package only supports full node backends for the moment 166 return "dcrw-spv" 167 } 168 169 return "" 170 } 171 172 // InternalWallet returns a pointer to the internal base wallet which is the 173 // core of dcrwallet. 174 func (b *DcrWallet) InternalWallet() *base.Wallet { 175 return b.wallet 176 } 177 178 // Start initializes the underlying rpc connection, the wallet itself, and 179 // begins syncing to the current available blockchain state. 180 // 181 // This is a part of the WalletController interface. 182 func (b *DcrWallet) Start() error { 183 // We'll start by unlocking the wallet and ensuring that the KeyScope: 184 // (1017, 1) exists within the internal waddrmgr. We'll need this in 185 // order to properly generate the keys required for signing various 186 // contracts. 187 if err := b.wallet.Unlock(context.TODO(), b.cfg.PrivatePass, nil); err != nil { 188 return err 189 } 190 191 // And then start the syncer backend for this wallet. 192 if err := b.syncer.start(b); err != nil { 193 return err 194 } 195 196 return nil 197 } 198 199 // Stop signals the wallet for shutdown. Shutdown may entail closing 200 // any active sockets, database handles, stopping goroutines, etc. 201 // 202 // This is a part of the WalletController interface. 203 func (b *DcrWallet) Stop() error { 204 dcrwLog.Debug("Requesting wallet shutdown") 205 b.cancelCtx() 206 b.syncer.stop() 207 b.syncer.waitForShutdown() 208 209 dcrwLog.Debugf("Wallet has shut down") 210 211 return nil 212 } 213 214 // ConfirmedBalance returns the sum of all the wallet's unspent outputs that 215 // have at least confs confirmations. If confs is set to zero, then all unspent 216 // outputs, including those currently in the mempool will be included in the 217 // final sum. 218 // 219 // This is a part of the WalletController interface. 220 // 221 // TODO(matheusd) Remove witness argument, given that's not applicable to decred 222 func (b *DcrWallet) ConfirmedBalance(confs int32, accountName string) (dcrutil.Amount, error) { 223 acctNb := defaultAccount 224 if accountName != "" && accountName != lnwallet.DefaultAccountName { 225 var err error 226 acctNb, err = b.wallet.AccountNumber(context.TODO(), accountName) 227 if err != nil { 228 return 0, fmt.Errorf("unknown account named %s: %v", accountName, err) 229 } 230 } 231 232 balances, err := b.wallet.AccountBalance(context.TODO(), acctNb, confs) 233 if err != nil { 234 return 0, err 235 } 236 237 return balances.Spendable, nil 238 } 239 240 // NewAddress returns the next external or internal address for the wallet 241 // dictated by the value of the `change` parameter. If change is true, then an 242 // internal address will be returned, otherwise an external address should be 243 // returned. 244 // 245 // This is a part of the WalletController interface. 246 func (b *DcrWallet) NewAddress(t lnwallet.AddressType, change bool, accountName string) (stdaddr.Address, error) { 247 248 switch t { 249 case lnwallet.PubKeyHash: 250 // nop 251 default: 252 return nil, fmt.Errorf("unknown address type") 253 } 254 255 acctNb, err := b.wallet.AccountNumber(context.TODO(), accountName) 256 if err != nil { 257 return nil, fmt.Errorf("unknown account named %s: %v", accountName, err) 258 } 259 260 var addr stdaddr.Address 261 if change { 262 addr, err = b.wallet.NewInternalAddress(context.TODO(), 263 acctNb, base.WithGapPolicyWrap()) 264 } else { 265 addr, err = b.wallet.NewExternalAddress(context.TODO(), 266 acctNb, base.WithGapPolicyWrap()) 267 } 268 269 if err != nil { 270 return nil, err 271 } 272 273 // Convert to a regular p2pkh address, since the addresses returned are 274 // used as paramaters to PayT.(stdaddr.Hash160er).Hash160()[:] which doesn't understand 275 // the native wallet types. 276 return stdaddr.DecodeAddress(addr.String(), b.netParams) 277 } 278 279 // LastUnusedAddress returns the last *unused* address known by the wallet. An 280 // address is unused if it hasn't received any payments. This can be useful in 281 // UIs in order to continually show the "freshest" address without having to 282 // worry about "address inflation" caused by continual refreshing. Similar to 283 // NewAddress it can derive a specified address type, and also optionally a 284 // change address. 285 func (b *DcrWallet) LastUnusedAddress(addrType lnwallet.AddressType, accountName string) ( 286 stdaddr.Address, error) { 287 288 acctNb, err := b.wallet.AccountNumber(context.TODO(), accountName) 289 if err != nil { 290 return nil, fmt.Errorf("unknown account named %s: %v", accountName, err) 291 } 292 293 switch addrType { 294 case lnwallet.PubKeyHash: 295 // nop 296 default: 297 return nil, fmt.Errorf("unknown address type") 298 } 299 a, err := b.wallet.CurrentAddress(acctNb) 300 if err != nil { 301 return nil, err 302 } 303 304 return a, nil 305 } 306 307 // IsOurAddress checks if the passed address belongs to this wallet 308 // 309 // This is a part of the WalletController interface. 310 func (b *DcrWallet) IsOurAddress(a stdaddr.Address) bool { 311 result, err := b.wallet.HaveAddress(context.TODO(), a) 312 return result && (err == nil) 313 } 314 315 // SendOutputs funds, signs, and broadcasts a Decred transaction paying out to 316 // the specified outputs. In the case the wallet has insufficient funds, or the 317 // outputs are non-standard, a non-nil error will be returned. 318 // 319 // This is a part of the WalletController interface. 320 func (b *DcrWallet) SendOutputs(outputs []*wire.TxOut, 321 feeRate chainfee.AtomPerKByte, minConfs int32, label, fromAccount string) (*wire.MsgTx, error) { 322 323 // Sanity check outputs. 324 if len(outputs) < 1 { 325 return nil, lnwallet.ErrNoOutputs 326 } 327 328 acctNb := defaultAccount 329 if fromAccount != "" && fromAccount != lnwallet.DefaultAccountName { 330 var err error 331 acctNb, err = b.wallet.AccountNumber(context.TODO(), fromAccount) 332 if err != nil { 333 return nil, fmt.Errorf("unknown account named %s: %v", fromAccount, err) 334 } 335 } 336 337 // Ensure we haven't changed the default relay fee. 338 // TODO(decred) Potentially change to a construct/sign/publish cycle or 339 // add the fee as a parameter so that we don't risk changing the default 340 // fee rate. 341 oldRelayFee := b.wallet.RelayFee() 342 b.wallet.SetRelayFee(dcrutil.Amount(feeRate)) 343 defer b.wallet.SetRelayFee(oldRelayFee) 344 345 txHash, err := b.wallet.SendOutputs(context.TODO(), outputs, 346 acctNb, acctNb, minConfs) 347 if err != nil { 348 return nil, err 349 } 350 351 txs, _, err := b.wallet.GetTransactionsByHashes(context.TODO(), []*chainhash.Hash{txHash}) 352 if err != nil { 353 return nil, err 354 } 355 356 return txs[0], nil 357 } 358 359 // CreateSimpleTx creates a Bitcoin transaction paying to the specified 360 // outputs. The transaction is not broadcasted to the network, but a new change 361 // address might be created in the wallet database. In the case the wallet has 362 // insufficient funds, or the outputs are non-standard, an error should be 363 // returned. This method also takes the target fee expressed in sat/kw that 364 // should be used when crafting the transaction. 365 // 366 // NOTE: The dryRun argument can be set true to create a tx that doesn't alter 367 // the database. A tx created with this set to true SHOULD NOT be broadcasted. 368 // 369 // This is a part of the WalletController interface. 370 func (b *DcrWallet) CreateSimpleTx(outputs []*wire.TxOut, 371 feeRate chainfee.AtomPerKByte, minConfs int32, dryRun bool) (*txauthor.AuthoredTx, error) { 372 373 // Sanity check outputs. 374 if len(outputs) < 1 { 375 return nil, lnwallet.ErrNoOutputs 376 } 377 378 // TODO(decred) Review semantics for btcwallet's CreateSimpleTx. 379 return nil, fmt.Errorf("CreateSimpleTx unimplemented for dcrwallet") 380 } 381 382 // LockOutpoint marks an outpoint as locked meaning it will no longer be deemed 383 // as eligible for coin selection. Locking outputs are utilized in order to 384 // avoid race conditions when selecting inputs for usage when funding a 385 // channel. 386 // 387 // This is a part of the WalletController interface. 388 func (b *DcrWallet) LockOutpoint(o wire.OutPoint) { 389 b.wallet.LockOutpoint(&o.Hash, o.Index) 390 } 391 392 // UnlockOutpoint unlocks a previously locked output, marking it eligible for 393 // coin selection. 394 // 395 // This is a part of the WalletController interface. 396 func (b *DcrWallet) UnlockOutpoint(o wire.OutPoint) { 397 b.wallet.UnlockOutpoint(&o.Hash, o.Index) 398 } 399 400 // ListUnspentWitness returns a slice of all the unspent outputs the wallet 401 // controls which pay to witness programs either directly or indirectly. 402 // 403 // This is a part of the WalletController interface. 404 func (b *DcrWallet) ListUnspentWitness(minConfs, maxConfs int32, accountName string) ( 405 []*lnwallet.Utxo, error) { 406 407 // First, grab all the unfiltered currently unspent outputs. 408 unspentOutputs, err := b.wallet.ListUnspent(context.TODO(), minConfs, 409 maxConfs, nil, "") 410 if err != nil { 411 return nil, err 412 } 413 414 // Convert the dcrjson formatted unspents into lnwallet.Utxo's 415 witnessOutputs := make([]*lnwallet.Utxo, 0, len(unspentOutputs)) 416 for _, output := range unspentOutputs { 417 if accountName != "" && accountName != output.Account { 418 continue 419 } 420 421 pkScript, err := hex.DecodeString(output.ScriptPubKey) 422 if err != nil { 423 return nil, err 424 } 425 426 scriptClass := stdscript.DetermineScriptType(scriptVersion, pkScript) 427 if scriptClass != stdscript.STPubKeyHashEcdsaSecp256k1 { 428 continue 429 } 430 431 addressType := lnwallet.PubKeyHash 432 txid, err := chainhash.NewHashFromStr(output.TxID) 433 if err != nil { 434 return nil, err 435 } 436 437 // We'll ensure we properly convert the amount given in 438 // DCR to atoms. 439 amt, err := dcrutil.NewAmount(output.Amount) 440 if err != nil { 441 return nil, err 442 } 443 444 utxo := &lnwallet.Utxo{ 445 AddressType: addressType, 446 Value: amt, 447 PkScript: pkScript, 448 OutPoint: wire.OutPoint{ 449 Hash: *txid, 450 Index: output.Vout, 451 Tree: output.Tree, 452 }, 453 Confirmations: output.Confirmations, 454 } 455 witnessOutputs = append(witnessOutputs, utxo) 456 } 457 458 return witnessOutputs, nil 459 } 460 461 // PublishTransaction performs cursory validation (dust checks, etc), then 462 // finally broadcasts the passed transaction to the Decred network. If 463 // publishing the transaction fails, an error describing the reason is 464 // returned (currently ErrDoubleSpend). If the transaction is already 465 // published to the network (either in the mempool or chain) no error 466 // will be returned. 467 func (b *DcrWallet) PublishTransaction(tx *wire.MsgTx, label string) error { 468 n, err := b.wallet.NetworkBackend() 469 if err != nil { 470 return err 471 } 472 if n == nil { 473 return fmt.Errorf("wallet does not have an active backend") 474 } 475 _, err = b.wallet.PublishTransaction(context.TODO(), tx, n) 476 if err != nil { 477 // TODO(decred): review if the string messages are correct. 478 // Possible convert from checking the message to checking the 479 // op. 480 // 481 // NOTE(decred): These checks were removed upstream due to 482 // changing the underlying btcwallet semantics on 483 // PublishTransaction(). 484 if strings.Contains(err.Error(), "already have") { 485 // Transaction was already in the mempool, do 486 // not treat as an error. We do this to mimic 487 // the behaviour of bitcoind, which will not 488 // return an error if a transaction in the 489 // mempool is sent again using the 490 // sendrawtransaction RPC call. 491 return nil 492 } 493 if strings.Contains(err.Error(), "already exists") { 494 // Transaction was already mined, we don't 495 // consider this an error. 496 return nil 497 } 498 if strings.Contains(err.Error(), "by double spending") { 499 // Output was already spent. 500 return lnwallet.ErrDoubleSpend 501 } 502 if strings.Contains(err.Error(), "already spent") { 503 // Output was already spent. 504 return lnwallet.ErrDoubleSpend 505 } 506 if strings.Contains(err.Error(), "already been spent") { 507 // Output was already spent. 508 return lnwallet.ErrDoubleSpend 509 } 510 if strings.Contains(err.Error(), "orphan transaction") { 511 // Transaction is spending either output that 512 // is missing or already spent. 513 return lnwallet.ErrDoubleSpend 514 } 515 if strings.Contains(err.Error(), "by double spending") { 516 // Wallet has a conflicting unmined transaction. 517 return lnwallet.ErrDoubleSpend 518 } 519 return err 520 } 521 return nil 522 } 523 524 // AbandonDoubleSpends abandons any unconfirmed transaction that also spends 525 // any of the specified outpoints. 526 // 527 // This is part of the WalletController interface. 528 func (b *DcrWallet) AbandonDoubleSpends(spentOutpoints ...*wire.OutPoint) error { 529 // Make a map of inputs that were spent to speed up lookup. 530 spent := make(map[wire.OutPoint]struct{}, len(spentOutpoints)) 531 for _, outp := range spentOutpoints { 532 spent[*outp] = struct{}{} 533 } 534 535 // Fetch only unmined txs. 536 start := base.NewBlockIdentifierFromHeight(-1) 537 stop := base.NewBlockIdentifierFromHeight(-1) 538 539 // Collect txs that need to be abandoned. 540 abandon := make(map[chainhash.Hash]struct{}, len(spentOutpoints)) 541 rangeFn := func(block *base.Block) (bool, error) { 542 if block.Header != nil { 543 // Shouldn't happen, but play it safe. 544 return false, nil 545 } 546 547 for _, tx := range block.Transactions { 548 wireTx := new(wire.MsgTx) 549 err := wireTx.FromBytes(tx.Transaction) 550 if err != nil { 551 dcrwLog.Warnf("Error decoding wallet-provided "+ 552 "tx: %v", err) 553 continue 554 } 555 txh := wireTx.TxHash() 556 557 for _, in := range wireTx.TxIn { 558 if _, isSpent := spent[in.PreviousOutPoint]; !isSpent { 559 continue 560 } 561 562 // This input was spent. Register this as a tx 563 // that needs abandoning. 564 abandon[txh] = struct{}{} 565 break 566 } 567 568 } 569 570 return false, nil 571 } 572 573 err := b.wallet.GetTransactions(context.TODO(), rangeFn, start, stop) 574 if err != nil { 575 return err 576 } 577 578 // Finally, abandon all transactions. 579 for txh := range abandon { 580 dcrwLog.Infof("Abandoning double spent tx %s", txh) 581 err := b.wallet.AbandonTransaction(context.Background(), &txh) 582 if err != nil { 583 dcrwLog.Warnf("Error abandoning tx %s: %v", txh, err) 584 } 585 } 586 587 return nil 588 } 589 590 // extractBalanceDelta extracts the net balance delta from the PoV of the 591 // wallet given a TransactionSummary. 592 func extractBalanceDelta( 593 txSummary base.TransactionSummary, 594 tx *wire.MsgTx, 595 ) (dcrutil.Amount, error) { 596 // For each input we debit the wallet's outflow for this transaction, 597 // and for each output we credit the wallet's inflow for this 598 // transaction. 599 var balanceDelta dcrutil.Amount 600 for _, input := range txSummary.MyInputs { 601 balanceDelta -= input.PreviousAmount 602 } 603 for _, output := range txSummary.MyOutputs { 604 balanceDelta += dcrutil.Amount(tx.TxOut[output.Index].Value) 605 } 606 607 return balanceDelta, nil 608 } 609 610 // minedTransactionsToDetails is a helper function which converts a summary 611 // information about mined transactions to a TransactionDetail. 612 func minedTransactionsToDetails( 613 currentHeight int32, 614 block *base.Block, 615 chainParams *chaincfg.Params, 616 ) ([]*lnwallet.TransactionDetail, error) { 617 618 if block.Header == nil { 619 return nil, fmt.Errorf("cannot use minedTransactionsToDetails with mempoool") 620 } 621 622 details := make([]*lnwallet.TransactionDetail, 0, len(block.Transactions)) 623 for _, tx := range block.Transactions { 624 wireTx := &wire.MsgTx{} 625 txReader := bytes.NewReader(tx.Transaction) 626 627 if err := wireTx.Deserialize(txReader); err != nil { 628 return nil, err 629 } 630 631 var destAddresses []stdaddr.Address 632 for _, txOut := range wireTx.TxOut { 633 _, outAddresses := stdscript.ExtractAddrs( 634 txOut.Version, txOut.PkScript, chainParams) 635 destAddresses = append(destAddresses, outAddresses...) 636 } 637 638 blockHash := block.Header.BlockHash() 639 txDetail := &lnwallet.TransactionDetail{ 640 Hash: *tx.Hash, 641 NumConfirmations: currentHeight - int32(block.Header.Height) + 1, 642 BlockHash: &blockHash, 643 BlockHeight: int32(block.Header.Height), 644 Timestamp: block.Header.Timestamp.Unix(), 645 TotalFees: int64(tx.Fee), 646 DestAddresses: destAddresses, 647 RawTx: tx.Transaction, 648 Label: "", 649 } 650 651 balanceDelta, err := extractBalanceDelta(tx, wireTx) 652 if err != nil { 653 return nil, err 654 } 655 txDetail.Value = balanceDelta 656 657 details = append(details, txDetail) 658 } 659 660 return details, nil 661 } 662 663 // unminedTransactionsToDetail is a helper function which converts a summary 664 // for an unconfirmed transaction to a transaction detail. 665 func unminedTransactionsToDetail( 666 summary base.TransactionSummary, 667 chainParams *chaincfg.Params, 668 ) (*lnwallet.TransactionDetail, error) { 669 670 wireTx := &wire.MsgTx{} 671 txReader := bytes.NewReader(summary.Transaction) 672 673 if err := wireTx.Deserialize(txReader); err != nil { 674 return nil, err 675 } 676 677 var destAddresses []stdaddr.Address 678 for _, txOut := range wireTx.TxOut { 679 _, outAddresses := 680 stdscript.ExtractAddrs(txOut.Version, 681 txOut.PkScript, chainParams) 682 destAddresses = append(destAddresses, outAddresses...) 683 } 684 685 txDetail := &lnwallet.TransactionDetail{ 686 Hash: *summary.Hash, 687 TotalFees: int64(summary.Fee), 688 Timestamp: summary.Timestamp, 689 DestAddresses: destAddresses, 690 RawTx: summary.Transaction, 691 Label: "", 692 } 693 694 balanceDelta, err := extractBalanceDelta(summary, wireTx) 695 if err != nil { 696 return nil, err 697 } 698 txDetail.Value = balanceDelta 699 700 return txDetail, nil 701 } 702 703 // ListTransactionDetails returns a list of all transactions which are 704 // relevant to the wallet. 705 // 706 // This is a part of the WalletController interface. 707 func (b *DcrWallet) ListTransactionDetails(startHeight, 708 endHeight int32, accountName string) ([]*lnwallet.TransactionDetail, error) { 709 710 var acctNb uint32 711 if accountName != "" { 712 var err error 713 acctNb, err = b.wallet.AccountNumber(context.TODO(), accountName) 714 if err != nil { 715 return nil, fmt.Errorf("unknown account named %s: %v", accountName, err) 716 } 717 } 718 719 // Grab the best block the wallet knows of, we'll use this to calculate 720 // # of confirmations shortly below. 721 _, currentHeight := b.wallet.MainChainTip(context.TODO()) 722 723 // We'll attempt to find all transactions from start to end. 724 start := base.NewBlockIdentifierFromHeight(startHeight) 725 stop := base.NewBlockIdentifierFromHeight(endHeight) 726 727 txDetails := make([]*lnwallet.TransactionDetail, 0) 728 729 rangeFn := func(block *base.Block) (bool, error) { 730 isMined := block.Header != nil 731 for _, tx := range block.Transactions { 732 if accountName != "" { 733 fromTargetAcct := false 734 for _, in := range tx.MyInputs { 735 if in.PreviousAccount == acctNb { 736 fromTargetAcct = true 737 break 738 } 739 } 740 for _, out := range tx.MyOutputs { 741 if out.Account == acctNb { 742 fromTargetAcct = true 743 break 744 } 745 } 746 if !fromTargetAcct { 747 continue 748 } 749 } 750 751 if isMined { 752 details, err := minedTransactionsToDetails(currentHeight, block, 753 b.netParams) 754 if err != nil { 755 return true, err 756 } 757 758 txDetails = append(txDetails, details...) 759 } else { 760 detail, err := unminedTransactionsToDetail(tx, 761 b.netParams) 762 if err != nil { 763 return true, err 764 } 765 766 txDetails = append(txDetails, detail) 767 } 768 } 769 770 return false, nil 771 } 772 773 err := b.wallet.GetTransactions(context.TODO(), rangeFn, start, stop) 774 if err != nil { 775 return nil, err 776 } 777 778 return txDetails, nil 779 } 780 781 // txSubscriptionClient encapsulates the transaction notification client from 782 // the base wallet. Notifications received from the client will be proxied over 783 // two distinct channels. 784 type txSubscriptionClient struct { 785 txClient base.TransactionNotificationsClient 786 787 confirmed chan *lnwallet.TransactionDetail 788 unconfirmed chan *lnwallet.TransactionDetail 789 790 w *base.Wallet 791 792 wg sync.WaitGroup 793 quit chan struct{} 794 } 795 796 // ConfirmedTransactions returns a channel which will be sent on as new 797 // relevant transactions are confirmed. 798 // 799 // This is part of the TransactionSubscription interface. 800 func (t *txSubscriptionClient) ConfirmedTransactions() chan *lnwallet.TransactionDetail { 801 return t.confirmed 802 } 803 804 // UnconfirmedTransactions returns a channel which will be sent on as 805 // new relevant transactions are seen within the network. 806 // 807 // This is part of the TransactionSubscription interface. 808 func (t *txSubscriptionClient) UnconfirmedTransactions() chan *lnwallet.TransactionDetail { 809 return t.unconfirmed 810 } 811 812 // Cancel finalizes the subscription, cleaning up any resources allocated. 813 // 814 // This is part of the TransactionSubscription interface. 815 func (t *txSubscriptionClient) Cancel() { 816 close(t.quit) 817 t.wg.Wait() 818 819 t.txClient.Done() 820 } 821 822 // notificationProxier proxies the notifications received by the underlying 823 // wallet's notification client to a higher-level TransactionSubscription 824 // client. 825 func (t *txSubscriptionClient) notificationProxier() { 826 defer t.wg.Done() 827 828 out: 829 for { 830 select { 831 case txNtfn := <-t.txClient.C: 832 // TODO(roasbeef): handle detached blocks 833 _, currentHeight := t.w.MainChainTip(context.TODO()) 834 835 // Launch a goroutine to re-package and send 836 // notifications for any newly confirmed transactions. 837 go func() { 838 chainParams := t.w.ChainParams() 839 for _, block := range txNtfn.AttachedBlocks { 840 details, err := minedTransactionsToDetails(currentHeight, &block, chainParams) 841 if err != nil { 842 continue 843 } 844 845 for _, d := range details { 846 select { 847 case t.confirmed <- d: 848 case <-t.quit: 849 return 850 } 851 } 852 } 853 854 }() 855 856 // Launch a goroutine to re-package and send 857 // notifications for any newly unconfirmed transactions. 858 go func() { 859 chainParams := t.w.ChainParams() 860 for _, tx := range txNtfn.UnminedTransactions { 861 detail, err := unminedTransactionsToDetail( 862 tx, chainParams, 863 ) 864 if err != nil { 865 continue 866 } 867 868 select { 869 case t.unconfirmed <- detail: 870 case <-t.quit: 871 return 872 } 873 } 874 }() 875 case <-t.quit: 876 break out 877 } 878 } 879 } 880 881 // SubscribeTransactions returns a TransactionSubscription client which 882 // is capable of receiving async notifications as new transactions 883 // related to the wallet are seen within the network, or found in 884 // blocks. 885 // 886 // This is a part of the WalletController interface. 887 func (b *DcrWallet) SubscribeTransactions() (lnwallet.TransactionSubscription, error) { 888 walletClient := b.wallet.NtfnServer.TransactionNotifications() 889 890 txClient := &txSubscriptionClient{ 891 txClient: walletClient, 892 confirmed: make(chan *lnwallet.TransactionDetail), 893 unconfirmed: make(chan *lnwallet.TransactionDetail), 894 w: b.wallet, 895 quit: make(chan struct{}), 896 } 897 txClient.wg.Add(1) 898 go txClient.notificationProxier() 899 900 return txClient, nil 901 } 902 903 // IsSynced returns a boolean indicating if from the PoV of the wallet, it has 904 // fully synced to the current best block in the main chain. 905 // 906 // This is a part of the WalletController interface. 907 func (b *DcrWallet) IsSynced() (bool, int64, error) { 908 // Grab the best chain state the wallet is currently aware of. 909 walletBestHash, _ := b.wallet.MainChainTip(context.TODO()) 910 walletBestHeader, err := b.wallet.BlockHeader(context.TODO(), &walletBestHash) 911 if err != nil { 912 return false, 0, err 913 } 914 915 // TODO(decred) Check if the wallet is still syncing. This is 916 // currently done by checking the associated chainIO but ideally the 917 // wallet should return the height it's attempting to sync to. 918 if b.cfg.ChainIO != nil { 919 ioHash, _, err := b.cfg.ChainIO.GetBestBlock() 920 if err != nil { 921 return false, 0, err 922 } 923 if !bytes.Equal(walletBestHash[:], ioHash[:]) { 924 return false, walletBestHeader.Timestamp.Unix(), nil 925 } 926 } 927 928 // If the timestamp on the best header is more than 2 hours in the 929 // past, then we're not yet synced. 930 minus2Hours := time.Now().Add(-2 * time.Hour) 931 if walletBestHeader.Timestamp.Before(minus2Hours) { 932 return false, walletBestHeader.Timestamp.Unix(), nil 933 } 934 935 // Check if the wallet has completed the initial sync procedure (discover 936 // addresses, load tx filter, etc). 937 walletSynced := atomic.LoadUint32(&b.atomicWalletSynced) == 1 938 939 return walletSynced, walletBestHeader.Timestamp.Unix(), nil 940 } 941 942 func (b *DcrWallet) BestBlock() (int64, chainhash.Hash, int64, error) { 943 // Grab the best chain state the wallet is currently aware of. 944 walletBestHash, walletBestHeight := b.wallet.MainChainTip(context.TODO()) 945 walletBestHeader, err := b.wallet.BlockHeader(context.TODO(), &walletBestHash) 946 if err != nil { 947 return 0, chainhash.Hash{}, 0, err 948 } 949 950 timestamp := walletBestHeader.Timestamp.Unix() 951 return int64(walletBestHeight), walletBestHash, timestamp, nil 952 } 953 954 // InitialSyncChannel returns the channel used to signal that wallet init has 955 // finished. 956 // 957 // This is a part of the WalletController interface. 958 func (b *DcrWallet) InitialSyncChannel() <-chan struct{} { 959 return b.syncedChan 960 } 961 962 func (b *DcrWallet) onSyncerSynced(synced bool) { 963 dcrwLog.Debug("RPC syncer notified wallet is synced") 964 965 if atomic.CompareAndSwapUint32(&b.atomicWalletSynced, syncStatusLostSync, syncStatusSynced) { 966 // No need to recreate the keyring or close the initial sync 967 // channel, so just return. 968 return 969 } 970 971 // Record in the DB that the wallet doesn't need account discovery 972 // anymore. 973 err := b.cfg.DB.DisableAccountDiscovery() 974 if err != nil { 975 dcrwLog.Errorf("Unable to disable future account discoveries: %v", err) 976 } 977 978 // Now that the wallet is synced and address discovery has ended, we 979 // can create the keyring. We can only do this here (after sync) 980 // because address discovery might upgrade the underlying dcrwallet 981 // coin type. 982 b.walletKeyRing, err = newWalletKeyRing(b.wallet, b.cfg.DB) 983 if err != nil { 984 // Sign operations will fail, so signal the error and prevent 985 // the wallet from considering itself synced (to prevent usage) 986 dcrwLog.Errorf("Unable to create wallet key ring: %v", err) 987 return 988 } 989 990 // Signal that the wallet is synced by closing the channel. 991 if atomic.CompareAndSwapUint32(&b.atomicWalletSynced, syncStatusUnsynced, syncStatusSynced) { 992 close(b.syncedChan) 993 } 994 } 995 996 func (b *DcrWallet) rpcSyncerFinished() { 997 // The RPC syncer stopped, so if we were previously synced we need to 998 // signal that we aren't anymore. 999 atomic.CompareAndSwapUint32(&b.atomicWalletSynced, syncStatusSynced, syncStatusLostSync) 1000 } 1001 1002 // LabelTransaction adds the given external label to the specified transaction. 1003 // 1004 // This is a part of the WalletController interface. 1005 func (b *DcrWallet) LabelTransaction(hash chainhash.Hash, label string, overwrite bool) error { 1006 return fmt.Errorf("unimplemented") 1007 } 1008 1009 // LeaseOutput markes the output as used for some time. 1010 // 1011 // This is a part of the WalletController interface. 1012 func (b *DcrWallet) LeaseOutput(lnwallet.LockID, wire.OutPoint, time.Duration) (time.Time, error) { 1013 return time.Time{}, fmt.Errorf("unimplemented") 1014 } 1015 1016 // ReleaseOutput marks the output as unused. 1017 // 1018 // This is a part of the WalletController interface. 1019 func (b *DcrWallet) ReleaseOutput(lnwallet.LockID, wire.OutPoint) error { 1020 return fmt.Errorf("unimplemented") 1021 } 1022 1023 // ListLeasedOutputs lists leased wallet outputs. 1024 // 1025 // This is a part of the WalletController interface. 1026 func (b *DcrWallet) ListLeasedOutputs() ([]*lnwallet.LockedOutput, error) { 1027 return nil, fmt.Errorf("unimplemented") 1028 } 1029 1030 // GetRecoveryInfo returns the current status of the recovery of the wallet. 1031 // 1032 // This is a part of the WalletController interface. 1033 func (b *DcrWallet) GetRecoveryInfo() (bool, float64, error) { 1034 return false, 0, fmt.Errorf("unimplemented") 1035 } 1036 1037 // FetchTx attempts to fetch a transaction in the wallet's database 1038 // identified by the passed transaction hash. If the transaction can't 1039 // be found, then a nil pointer is returned. 1040 // 1041 // This is a part of the WalletController interface. 1042 func (b *DcrWallet) FetchTx(txid chainhash.Hash) (*wire.MsgTx, error) { 1043 txs, _, err := b.wallet.GetTransactionsByHashes(b.ctx, []*chainhash.Hash{&txid}) 1044 if err != nil { 1045 return nil, err 1046 } 1047 1048 if len(txs) < 1 { 1049 return nil, fmt.Errorf("tx %s not found", txid) 1050 } 1051 1052 return txs[0], nil 1053 } 1054 1055 // RemoveDescendants attempts to remove any transaction from the 1056 // wallet's tx store (that may be unconfirmed) that spends outputs 1057 // created by the passed transaction. This remove propagates 1058 // recursively down the chain of descendent transactions. 1059 // 1060 // This is a part of the WalletController interface. 1061 func (b *DcrWallet) RemoveDescendants(*wire.MsgTx) error { 1062 return fmt.Errorf("RemoveDescendants is unimplemented") 1063 } 1064 1065 // ListAccount lists existing wallet accounts. 1066 // 1067 // This is a part of the WalletController interface. 1068 func (b *DcrWallet) ListAccounts(accountName string) ([]base.AccountProperties, error) { 1069 accounts, err := b.wallet.Accounts(context.Background()) 1070 if err != nil { 1071 return nil, err 1072 } 1073 1074 szHint := len(accounts.Accounts) 1075 if accountName != "" { 1076 szHint = 1 1077 } 1078 res := make([]base.AccountProperties, 0, szHint) 1079 for _, acct := range accounts.Accounts { 1080 if accountName != "" && acct.AccountName != accountName { 1081 continue 1082 } 1083 res = append(res, acct.AccountProperties) 1084 } 1085 1086 return res, nil 1087 } 1088 1089 // ImportAccount imports the specified xpub into the wallet. 1090 // 1091 // This is a part of the WalletController interface. 1092 func (b *DcrWallet) ImportAccount(name string, accountPubKey *hdkeychain.ExtendedKey, dryRun bool) ( 1093 *wallet.AccountProperties, []stdaddr.Address, []stdaddr.Address, error) { 1094 1095 fail := func(err error) (*wallet.AccountProperties, []stdaddr.Address, []stdaddr.Address, error) { 1096 return nil, nil, nil, err 1097 } 1098 1099 if dryRun { 1100 chainParams := b.wallet.ChainParams() 1101 intAddrs, extAddrs, err := lnwallet.DeriveAddrsFromExtPub(accountPubKey, 1102 chainParams, dryRunImportAccountNumAddrs) 1103 if err != nil { 1104 return fail(err) 1105 } 1106 1107 acctProps := &wallet.AccountProperties{ 1108 AccountName: name, 1109 } 1110 return acctProps, intAddrs, extAddrs, nil 1111 } 1112 1113 err := b.wallet.ImportXpubAccount(context.Background(), name, accountPubKey) 1114 if err != nil { 1115 return fail(err) 1116 } 1117 1118 accounts, err := b.ListAccounts(name) 1119 if err != nil { 1120 return fail(err) 1121 } 1122 if len(accounts) == 0 { 1123 return fail(fmt.Errorf("account named %q does not exist", name)) 1124 } 1125 1126 return &accounts[0], nil, nil, nil 1127 } 1128 1129 // ImportPublicKey imports the specified public key into the wallet. 1130 // 1131 // This is a part of the WalletController interface. 1132 func (b *DcrWallet) ImportPublicKey(pubKey *secp256k1.PublicKey) error { 1133 _, err := b.wallet.ImportPublicKey(context.Background(), pubKey.SerializeCompressed()) 1134 return err 1135 } 1136 1137 func (b *DcrWallet) ScriptForOutput(*wire.TxOut) ( 1138 btcwalletcompat.ManagedPubKeyAddress, []byte, []byte, error) { 1139 return nil, nil, nil, fmt.Errorf("dcrwallet.ScriptForOutput is not implemented") 1140 }