github.com/decred/dcrlnd@v0.7.6/lnwallet/remotedcrwallet/wallet.go (about) 1 package remotedcrwallet 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "io" 8 "math" 9 "strings" 10 "sync" 11 "sync/atomic" 12 "time" 13 14 pb "decred.org/dcrwallet/v4/rpc/walletrpc" 15 "decred.org/dcrwallet/v4/wallet" 16 base "decred.org/dcrwallet/v4/wallet" 17 "google.golang.org/grpc" 18 19 "decred.org/dcrwallet/v4/wallet/txauthor" 20 "github.com/decred/dcrd/chaincfg/chainhash" 21 "github.com/decred/dcrd/chaincfg/v3" 22 "github.com/decred/dcrd/dcrec" 23 "github.com/decred/dcrd/dcrec/secp256k1/v4" 24 "github.com/decred/dcrd/dcrutil/v4" 25 "github.com/decred/dcrd/hdkeychain/v3" 26 "github.com/decred/dcrd/txscript/v4" 27 "github.com/decred/dcrd/txscript/v4/sign" 28 "github.com/decred/dcrd/txscript/v4/stdaddr" 29 "github.com/decred/dcrd/txscript/v4/stdscript" 30 "github.com/decred/dcrd/wire" 31 "github.com/decred/dcrlnd/btcwalletcompat" 32 "github.com/decred/dcrlnd/channeldb" 33 "github.com/decred/dcrlnd/lnwallet" 34 "github.com/decred/dcrlnd/lnwallet/chainfee" 35 ) 36 37 const ( 38 // dryRunImportAccountNumAddrs represents the number of addresses we'll 39 // derive for an imported account's external and internal branch when a 40 // dry run is attempted. 41 dryRunImportAccountNumAddrs = 5 42 ) 43 44 type DcrWallet struct { 45 // syncedChan is a channel that is closed once the wallet has initially synced 46 // to the network. It is protected by atomicWalletSynced. 47 syncedChan chan struct{} 48 49 // atomicWalletSynced is an atomic CAS flag (synced = 1) that indicates 50 // when syncing has completed. 51 atomicWalletSynced uint32 52 53 // conn is the underlying grpc socket connection. 54 conn *grpc.ClientConn 55 56 cfg Config 57 58 chainParams *chaincfg.Params 59 db *channeldb.DB 60 61 *remoteWalletKeyRing 62 63 branchExtXPriv *hdkeychain.ExtendedKey 64 branchIntXPriv *hdkeychain.ExtendedKey 65 66 // account is the account number which controls onchain funds available 67 // for use from dcrlnd. 68 account uint32 69 70 // lockedOutpointsMu controls access to lockedOutpoints. 71 lockedOutpointsMu sync.Mutex 72 73 // lockedOutpoints is a list of outputs that the wallet won't use for 74 // other operations. This is necessary in the driver because grpc 75 // doesn't provide an entpoint to control this directly in the wallet. 76 lockedOutpoints map[wire.OutPoint]struct{} 77 78 wallet pb.WalletServiceClient 79 network pb.NetworkServiceClient 80 ctx context.Context 81 cancelCtx func() 82 } 83 84 // A compile time check to ensure that DcrWallet implements the 85 // WalletController interface. 86 var _ lnwallet.WalletController = (*DcrWallet)(nil) 87 88 // Compile time check to ensure that Dcrwallet implements the 89 // onchainAddrSourcer interface. 90 var _ (onchainAddrSourcer) = (*DcrWallet)(nil) 91 92 func New(cfg Config) (*DcrWallet, error) { 93 94 if cfg.Conn == nil { 95 return nil, fmt.Errorf("conn cannot be empty") 96 } 97 98 // TODO(decred): Check if the node is synced before allowing this to 99 // proceed. 100 ctxb := context.Background() 101 wallet := pb.NewWalletServiceClient(cfg.Conn) 102 103 // Unlock the account. 104 unlockAcctReq := &pb.UnlockAccountRequest{ 105 AccountNumber: uint32(cfg.AccountNumber), 106 Passphrase: cfg.PrivatePass, 107 } 108 _, err := wallet.UnlockAccount(ctxb, unlockAcctReq) 109 if err != nil { 110 return nil, fmt.Errorf("unable to unlock account: %v", err) 111 } 112 113 // Obtain the root master priv key from which all LN-related 114 // keys are derived. By convention, this is a special branch in the 115 // passed account. 116 network := pb.NewNetworkServiceClient(cfg.Conn) 117 req := &pb.GetAccountExtendedPrivKeyRequest{ 118 AccountNumber: uint32(cfg.AccountNumber), 119 } 120 resp, err := wallet.GetAccountExtendedPrivKey(ctxb, req) 121 122 // Irrespective of the return of GetAccountExtendedPrivKey, re-lock the 123 // account. 124 lockAcctReq := &pb.LockAccountRequest{ 125 AccountNumber: uint32(cfg.AccountNumber), 126 } 127 _, lockErr := wallet.LockAccount(ctxb, lockAcctReq) 128 if lockErr != nil { 129 dcrwLog.Errorf("Error while locking account number %d: %v", 130 cfg.AccountNumber, lockErr) 131 } 132 133 // And now check if GetAccountExtendedPrivKey returned an error. 134 if err != nil { 135 return nil, fmt.Errorf("unable to get master LN account "+ 136 "extended priv key: %v", err) 137 } 138 139 acctXPriv, err := hdkeychain.NewKeyFromString( 140 resp.AccExtendedPrivKey, cfg.NetParams, 141 ) 142 if err != nil { 143 return nil, fmt.Errorf("unable to create account xpriv: %v", err) 144 } 145 146 // Derive and store the account's external and internal extended priv 147 // keys so that we can redeem funds stored in this account's utxos and 148 // use them to fund channels, send coins to other nodes, etc. 149 branchExtXPriv, err := acctXPriv.Child(0) 150 if err != nil { 151 return nil, fmt.Errorf("unable to derive the external branch xpriv: %v", err) 152 } 153 branchIntXPriv, err := acctXPriv.Child(1) 154 if err != nil { 155 return nil, fmt.Errorf("unable to derive the internal branch xpriv: %v", err) 156 } 157 158 // Ensure we don't attempt to use a keyring derived from a different 159 // account than previously used by comparing the first external public 160 // key with the one stored in the database. 161 firstKey, err := branchExtXPriv.Child(0) 162 if err != nil { 163 return nil, fmt.Errorf("unable to derive first external key: %v", err) 164 } 165 firstPubKeyBytes := firstKey.SerializedPubKey() 166 if err = cfg.DB.CompareAndStoreAccountID(firstPubKeyBytes); err != nil { 167 return nil, fmt.Errorf("account number %d failed to generate "+ 168 "previously stored account ID: %v", cfg.AccountNumber, err) 169 } 170 171 ctx, cancelCtx := context.WithCancel(ctxb) 172 dcrw := &DcrWallet{ 173 account: uint32(cfg.AccountNumber), 174 syncedChan: make(chan struct{}), 175 chainParams: cfg.NetParams, 176 db: cfg.DB, 177 cfg: cfg, 178 conn: cfg.Conn, 179 wallet: wallet, 180 network: network, 181 branchExtXPriv: branchExtXPriv, 182 branchIntXPriv: branchIntXPriv, 183 lockedOutpoints: make(map[wire.OutPoint]struct{}), 184 ctx: ctx, 185 cancelCtx: cancelCtx, 186 } 187 188 // Finally, create the keyring using the conventions for remote 189 // wallets. 190 dcrw.remoteWalletKeyRing, err = newRemoteWalletKeyRing(acctXPriv, cfg.DB, dcrw) 191 if err != nil { 192 // Sign operations will fail, so signal the error and prevent 193 // the wallet from considering itself synced (to prevent usage) 194 return nil, fmt.Errorf("unable to create wallet key ring: %v", err) 195 } 196 197 return dcrw, nil 198 } 199 200 // BackEnd returns the underlying ChainService's name as a string. 201 // 202 // This is a part of the WalletController interface. 203 func (b *DcrWallet) BackEnd() string { 204 return "remotedcrwallet" 205 } 206 207 // Start initializes the underlying rpc connection, the wallet itself, and 208 // begins syncing to the current available blockchain state. 209 // 210 // This is a part of the WalletController interface. 211 func (b *DcrWallet) Start() error { 212 b.synced() 213 return nil 214 } 215 216 // Stop signals the wallet for shutdown. Shutdown may entail closing 217 // any active sockets, database handles, stopping goroutines, etc. 218 // 219 // This is a part of the WalletController interface. 220 func (b *DcrWallet) Stop() error { 221 b.cancelCtx() 222 return b.conn.Close() 223 } 224 225 // ConfirmedBalance returns the sum of all the wallet's unspent outputs that 226 // have at least confs confirmations. If confs is set to zero, then all unspent 227 // outputs, including those currently in the mempool will be included in the 228 // final sum. 229 // 230 // This is a part of the WalletController interface. 231 func (b *DcrWallet) ConfirmedBalance(confs int32, accountName string) (dcrutil.Amount, error) { 232 var acctNb = b.account 233 if accountName != "" && accountName != lnwallet.DefaultAccountName { 234 res, err := b.wallet.AccountNumber(context.Background(), &pb.AccountNumberRequest{AccountName: accountName}) 235 if err != nil { 236 return 0, fmt.Errorf("unknown account named %s: %v", accountName, err) 237 } 238 acctNb = res.AccountNumber 239 } 240 241 req := &pb.BalanceRequest{ 242 AccountNumber: acctNb, 243 RequiredConfirmations: confs, 244 } 245 resp, err := b.wallet.Balance(context.Background(), req) 246 if err != nil { 247 return 0, err 248 } 249 return dcrutil.Amount(resp.Spendable), nil 250 } 251 252 // NewAddress returns the next external or internal address for the wallet 253 // dictated by the value of the `change` parameter. If change is true, then an 254 // internal address will be returned, otherwise an external address should be 255 // returned. 256 // 257 // This is a part of the WalletController interface. 258 func (b *DcrWallet) NewAddress(t lnwallet.AddressType, change bool, accountName string) (stdaddr.Address, error) { 259 260 switch t { 261 case lnwallet.PubKeyHash: 262 // nop 263 default: 264 return nil, fmt.Errorf("unknown address type") 265 } 266 267 var acctNb = b.account 268 if accountName != lnwallet.DefaultAccountName { 269 res, err := b.wallet.AccountNumber(context.Background(), &pb.AccountNumberRequest{AccountName: accountName}) 270 if err != nil { 271 return nil, fmt.Errorf("unknown account named %s: %v", accountName, err) 272 } 273 acctNb = res.AccountNumber 274 } 275 276 kind := pb.NextAddressRequest_BIP0044_EXTERNAL 277 if change { 278 kind = pb.NextAddressRequest_BIP0044_INTERNAL 279 } 280 req := &pb.NextAddressRequest{ 281 Kind: kind, 282 Account: acctNb, 283 GapPolicy: pb.NextAddressRequest_GAP_POLICY_WRAP, 284 } 285 resp, err := b.wallet.NextAddress(context.Background(), req) 286 if err != nil { 287 return nil, err 288 } 289 290 addr, err := stdaddr.DecodeAddress(resp.Address, b.chainParams) 291 if err != nil { 292 return nil, err 293 } 294 return addr, nil 295 } 296 297 // LastUnusedAddress returns the last *unused* address known by the wallet. An 298 // address is unused if it hasn't received any payments. This can be useful in 299 // UIs in order to continually show the "freshest" address without having to 300 // worry about "address inflation" caused by continual refreshing. Similar to 301 // NewAddress it can derive a specified address type, and also optionally a 302 // change address. 303 func (b *DcrWallet) LastUnusedAddress(addrType lnwallet.AddressType, accountName string) ( 304 stdaddr.Address, error) { 305 306 switch addrType { 307 case lnwallet.PubKeyHash: 308 // nop 309 default: 310 return nil, fmt.Errorf("unknown address type") 311 } 312 313 return nil, fmt.Errorf("LastUnusedAddress unimplemented") 314 } 315 316 // IsOurAddress checks if the passed address belongs to this wallet 317 // 318 // This is a part of the WalletController interface. 319 func (b *DcrWallet) IsOurAddress(a stdaddr.Address) bool { 320 validReq := &pb.ValidateAddressRequest{ 321 Address: a.String(), 322 } 323 validResp, err := b.wallet.ValidateAddress(context.Background(), validReq) 324 if err != nil { 325 dcrwLog.Errorf("Error validating address to determine "+ 326 "ownership: %v", err) 327 return false 328 } 329 return validResp.IsMine 330 } 331 332 // SendOutputs funds, signs, and broadcasts a Decred transaction paying out to 333 // the specified outputs. In the case the wallet has insufficient funds, or the 334 // outputs are non-standard, a non-nil error will be returned. 335 // 336 // This is a part of the WalletController interface. 337 func (b *DcrWallet) SendOutputs(outputs []*wire.TxOut, 338 feeRate chainfee.AtomPerKByte, minConfs int32, label, fromAccount string) (*wire.MsgTx, error) { 339 340 ctxb := context.Background() 341 342 var acctNb = b.account 343 if fromAccount != "" && fromAccount != lnwallet.DefaultAccountName { 344 res, err := b.wallet.AccountNumber(context.Background(), &pb.AccountNumberRequest{AccountName: fromAccount}) 345 if err != nil { 346 return nil, fmt.Errorf("unknown account named %s: %v", fromAccount, err) 347 } 348 acctNb = res.AccountNumber 349 } 350 351 reqOutputs := make([]*pb.ConstructTransactionRequest_Output, len(outputs)) 352 for i, out := range outputs { 353 dest := &pb.ConstructTransactionRequest_OutputDestination{ 354 Script: out.PkScript, 355 ScriptVersion: uint32(out.Version), 356 } 357 reqOutputs[i] = &pb.ConstructTransactionRequest_Output{ 358 Amount: out.Value, 359 Destination: dest, 360 } 361 } 362 req := &pb.ConstructTransactionRequest{ 363 SourceAccount: acctNb, 364 FeePerKb: int32(feeRate), 365 NonChangeOutputs: reqOutputs, 366 RequiredConfirmations: minConfs, 367 } 368 369 resp, err := b.wallet.ConstructTransaction(ctxb, req) 370 if err != nil { 371 return nil, err 372 } 373 374 tx := new(wire.MsgTx) 375 err = tx.FromBytes(resp.UnsignedTransaction) 376 if err != nil { 377 return nil, err 378 } 379 380 // We need to manually sign the transaction here (instead of passing it 381 // to SignTransaction) because we don't hang onto the wallet password, 382 // but we do know the master priv key to the source account and can 383 // therefore derive the private keys for the individual addresses of 384 // the selected utxos. 385 // 386 // Additionally, we need to retrieve the index of each utxo address to 387 // know how to derive the private key. 388 // 389 // This ends up being harder (and slower) than this needs to be but 390 // allows us to not have to keep a completely unlocked remote wallet. 391 392 // Loop over the inputs which we need to sign. 393 for i, in := range tx.TxIn { 394 // Find out the PKScript of the input. 395 txReq := &pb.GetTransactionRequest{ 396 TransactionHash: in.PreviousOutPoint.Hash[:], 397 } 398 txResp, err := b.wallet.GetTransaction(ctxb, txReq) 399 if err != nil { 400 return nil, fmt.Errorf("unable to fetch tx: %v", err) 401 } 402 403 var credit *pb.TransactionDetails_Output 404 for _, c := range txResp.Transaction.Credits { 405 if c.Index == in.PreviousOutPoint.Index { 406 credit = c 407 break 408 } 409 } 410 if credit == nil { 411 return nil, fmt.Errorf("unable to find pkscript of "+ 412 "prev outpoint %v", in.PreviousOutPoint) 413 } 414 pkScript := credit.OutputScript 415 416 // Find out the HD index of the address. 417 validReq := &pb.ValidateAddressRequest{ 418 Address: credit.Address, 419 } 420 validResp, err := b.wallet.ValidateAddress(ctxb, validReq) 421 if err != nil { 422 return nil, err 423 } 424 425 // Derive the private key that signs this utxo. 426 branchXPriv := b.branchExtXPriv 427 if validResp.IsInternal { 428 branchXPriv = b.branchIntXPriv 429 } 430 extPrivKey, err := branchXPriv.Child(validResp.Index) 431 if err != nil { 432 return nil, err 433 } 434 privKey, err := extPrivKey.SerializedPrivKey() 435 if err != nil { 436 return nil, err 437 } 438 439 // Actually sign the input. 440 sigScript, err := sign.SignatureScript( 441 tx, i, pkScript, txscript.SigHashAll, privKey, 442 dcrec.STEcdsaSecp256k1, true, 443 ) 444 if err != nil { 445 return nil, err 446 } 447 in.SignatureScript = sigScript 448 } 449 450 // Now publish the transaction to the network. 451 signedTx, err := tx.Bytes() 452 if err != nil { 453 return nil, err 454 } 455 publishReq := &pb.PublishTransactionRequest{ 456 SignedTransaction: signedTx, 457 } 458 _, err = b.wallet.PublishTransaction(ctxb, publishReq) 459 if err != nil { 460 return nil, err 461 } 462 463 return tx, nil 464 } 465 466 // CreateSimpleTx creates a Bitcoin transaction paying to the specified 467 // outputs. The transaction is not broadcasted to the network, but a new change 468 // address might be created in the wallet database. In the case the wallet has 469 // insufficient funds, or the outputs are non-standard, an error should be 470 // returned. This method also takes the target fee expressed in sat/kw that 471 // should be used when crafting the transaction. 472 // 473 // NOTE: The dryRun argument can be set true to create a tx that doesn't alter 474 // the database. A tx created with this set to true SHOULD NOT be broadcasted. 475 // 476 // This is a part of the WalletController interface. 477 func (b *DcrWallet) CreateSimpleTx(outputs []*wire.TxOut, 478 feeRate chainfee.AtomPerKByte, minConfs int32, dryRun bool) (*txauthor.AuthoredTx, error) { 479 480 // TODO(decred) Review semantics for btcwallet's CreateSimpleTx. 481 return nil, fmt.Errorf("CreateSimpleTx unimplemented for dcrwallet") 482 } 483 484 // LockOutpoint marks an outpoint as locked meaning it will no longer be deemed 485 // as eligible for coin selection. Locking outputs are utilized in order to 486 // avoid race conditions when selecting inputs for usage when funding a 487 // channel. 488 // 489 // This is a part of the WalletController interface. 490 func (b *DcrWallet) LockOutpoint(o wire.OutPoint) { 491 b.lockedOutpointsMu.Lock() 492 b.lockedOutpoints[o] = struct{}{} 493 b.lockedOutpointsMu.Unlock() 494 } 495 496 // UnlockOutpoint unlocks a previously locked output, marking it eligible for 497 // coin selection. 498 // 499 // This is a part of the WalletController interface. 500 func (b *DcrWallet) UnlockOutpoint(o wire.OutPoint) { 501 b.lockedOutpointsMu.Lock() 502 delete(b.lockedOutpoints, o) 503 b.lockedOutpointsMu.Unlock() 504 } 505 506 // ListUnspentWitness returns a slice of all the unspent outputs the wallet 507 // controls which pay to witness programs either directly or indirectly. 508 // 509 // This is a part of the WalletController interface. 510 func (b *DcrWallet) ListUnspentWitness(minConfs, maxConfs int32, accountName string) ( 511 []*lnwallet.Utxo, error) { 512 513 var acctNb = b.account 514 if accountName != "" && accountName != lnwallet.DefaultAccountName { 515 res, err := b.wallet.AccountNumber(context.Background(), &pb.AccountNumberRequest{AccountName: accountName}) 516 if err != nil { 517 return nil, fmt.Errorf("unknown account named %s: %v", accountName, err) 518 } 519 acctNb = res.AccountNumber 520 } 521 522 if maxConfs != 0 && maxConfs != math.MaxInt32 { 523 return nil, fmt.Errorf("maxconfs is not supported") 524 } 525 526 req := &pb.UnspentOutputsRequest{ 527 Account: acctNb, 528 RequiredConfirmations: minConfs, 529 } 530 stream, err := b.wallet.UnspentOutputs(context.Background(), req) 531 if err != nil { 532 return nil, err 533 } 534 535 // Decred only supports p2pkh in its wallets. 536 addressType := lnwallet.PubKeyHash 537 538 // Convert to the appropriate format. 539 utxos := make([]*lnwallet.Utxo, 0) 540 for { 541 msg, err := stream.Recv() 542 if err == io.EOF { 543 break 544 } 545 if err != nil { 546 return nil, err 547 } 548 549 txid, err := chainhash.NewHash(msg.TransactionHash) 550 if err != nil { 551 return nil, err 552 } 553 554 // Ensure this utxo hasn't been locked. 555 outp := wire.OutPoint{ 556 Hash: *txid, 557 Index: msg.OutputIndex, 558 Tree: int8(msg.Tree), 559 } 560 b.lockedOutpointsMu.Lock() 561 _, lockedUtxo := b.lockedOutpoints[outp] 562 b.lockedOutpointsMu.Unlock() 563 if lockedUtxo { 564 continue 565 } 566 567 // TODO(decred): Modify UnspentOutputs() grpc call to return 568 // the confirmation height so that the confirmation count can 569 // be deduced without having to perform a second rpc call. 570 txReq := &pb.GetTransactionRequest{ 571 TransactionHash: txid[:], 572 } 573 txResp, err := b.wallet.GetTransaction(context.Background(), txReq) 574 if err != nil { 575 return nil, err 576 } 577 confs := txResp.Confirmations 578 if confs > maxConfs { 579 continue 580 } 581 582 utxo := &lnwallet.Utxo{ 583 AddressType: addressType, 584 Value: dcrutil.Amount(msg.Amount), 585 PkScript: msg.PkScript, 586 OutPoint: outp, 587 Confirmations: int64(confs), 588 } 589 utxos = append(utxos, utxo) 590 } 591 592 return utxos, nil 593 } 594 595 // PublishTransaction performs cursory validation (dust checks, etc), then 596 // finally broadcasts the passed transaction to the Decred network. If 597 // publishing the transaction fails, an error describing the reason is 598 // returned (currently ErrDoubleSpend). If the transaction is already 599 // published to the network (either in the mempool or chain) no error 600 // will be returned. 601 // 602 // This is a part of the WalletController interface. 603 func (b *DcrWallet) PublishTransaction(tx *wire.MsgTx, label string) error { 604 rawTx, err := tx.Bytes() 605 if err != nil { 606 return err 607 } 608 publishReq := &pb.PublishTransactionRequest{ 609 SignedTransaction: rawTx, 610 } 611 _, err = b.wallet.PublishTransaction(context.Background(), publishReq) 612 dcrwLog.Debugf("PublishTransaction(%s): error %v", newLogClosure( 613 func() string { return tx.TxHash().String() }), err) 614 if err != nil { 615 // TODO(decred): review if the string messages are correct. 616 // Possible convert from checking the message to checking the 617 // op. 618 // 619 // NOTE(decred): These checks were removed upstream due to 620 // changing the underlying btcwallet semantics on 621 // PublishTransaction(). 622 if strings.Contains(err.Error(), "already have") { 623 // Transaction was already in the mempool, do 624 // not treat as an error. We do this to mimic 625 // the behaviour of bitcoind, which will not 626 // return an error if a transaction in the 627 // mempool is sent again using the 628 // sendrawtransaction RPC call. 629 return nil 630 } 631 if strings.Contains(err.Error(), "already exists") { 632 // Transaction was already mined, we don't 633 // consider this an error. 634 return nil 635 } 636 if strings.Contains(err.Error(), "by double spending") { 637 // Output was already spent. 638 return lnwallet.ErrDoubleSpend 639 } 640 if strings.Contains(err.Error(), "already spends the same coins") { 641 // Output was already spent. 642 return lnwallet.ErrDoubleSpend 643 } 644 if strings.Contains(err.Error(), "already spent") { 645 // Output was already spent. 646 return lnwallet.ErrDoubleSpend 647 } 648 if strings.Contains(err.Error(), "already been spent") { 649 // Output was already spent. 650 return lnwallet.ErrDoubleSpend 651 } 652 if strings.Contains(err.Error(), "orphan transaction") { 653 // Transaction is spending either output that 654 // is missing or already spent. 655 return lnwallet.ErrDoubleSpend 656 } 657 if strings.Contains(err.Error(), "by double spending") { 658 // Wallet has a conflicting unmined transaction. 659 return lnwallet.ErrDoubleSpend 660 } 661 return err 662 } 663 664 return nil 665 } 666 667 // AbandonDoubleSpends abandons any unconfirmed transaction that also spends 668 // any of the specified outpoints. 669 // 670 // This is part of the WalletController interface. 671 func (b *DcrWallet) AbandonDoubleSpends(spentOutpoints ...*wire.OutPoint) error { 672 673 // Fetch all unconfirmed transactions. 674 req := &pb.GetTransactionsRequest{ 675 StartingBlockHeight: -1, 676 EndingBlockHeight: 0, 677 } 678 679 stream, err := b.wallet.GetTransactions(context.Background(), req) 680 if err != nil { 681 return err 682 } 683 684 // Make a map of inputs that were spent to speed up lookup. 685 spent := make(map[wire.OutPoint]struct{}, len(spentOutpoints)) 686 for _, outp := range spentOutpoints { 687 spent[*outp] = struct{}{} 688 } 689 690 // Now collect all txs that need to be abandoned. 691 abandon := make(map[chainhash.Hash]struct{}, len(spentOutpoints)) 692 for { 693 msg, err := stream.Recv() 694 if err == io.EOF { 695 break 696 } 697 if err != nil { 698 return err 699 } 700 701 for _, tx := range msg.UnminedTransactions { 702 wireTx := new(wire.MsgTx) 703 err := wireTx.FromBytes(tx.Transaction) 704 if err != nil { 705 dcrwLog.Warnf("Error decoding wallet-provided "+ 706 "tx: %v", err) 707 continue 708 } 709 txh := wireTx.TxHash() 710 711 for _, in := range wireTx.TxIn { 712 if _, isSpent := spent[in.PreviousOutPoint]; !isSpent { 713 continue 714 } 715 716 // This input was spent. Register this as a tx 717 // that needs abandoning. 718 abandon[txh] = struct{}{} 719 break 720 } 721 } 722 } 723 724 // Finally, abandon all transactions. 725 for txh := range abandon { 726 dcrwLog.Infof("Abandoning double spent tx %s", txh) 727 req := &pb.AbandonTransactionRequest{ 728 TransactionHash: txh[:], 729 } 730 _, err := b.wallet.AbandonTransaction(context.Background(), req) 731 if err != nil { 732 dcrwLog.Warnf("Error abandoning tx %s: %v", txh, err) 733 } 734 } 735 736 return nil 737 } 738 739 // extractBalanceDelta extracts the net balance delta from the PoV of the 740 // wallet given a TransactionSummary. 741 func extractBalanceDelta( 742 txSummary *pb.TransactionDetails, 743 tx *wire.MsgTx, 744 ) (dcrutil.Amount, error) { 745 // For each input we debit the wallet's outflow for this transaction, 746 // and for each output we credit the wallet's inflow for this 747 // transaction. 748 var balanceDelta dcrutil.Amount 749 for _, input := range txSummary.Debits { 750 balanceDelta -= dcrutil.Amount(input.PreviousAmount) 751 } 752 for _, output := range txSummary.Credits { 753 balanceDelta += dcrutil.Amount(tx.TxOut[output.Index].Value) 754 } 755 756 return balanceDelta, nil 757 } 758 759 // minedTransactionsToDetails is a helper function which converts a summary 760 // information about mined transactions to a TransactionDetail. 761 func minedTransactionsToDetails( 762 currentHeight int32, 763 block *pb.BlockDetails, 764 chainParams *chaincfg.Params, 765 acctNb uint32, 766 ) ([]*lnwallet.TransactionDetail, error) { 767 768 headerHeight := block.Height 769 770 blockHash, err := chainhash.NewHash(block.Hash) 771 if err != nil { 772 return nil, err 773 } 774 775 details := make([]*lnwallet.TransactionDetail, 0, len(block.Transactions)) 776 for _, tx := range block.Transactions { 777 if acctNb < math.MaxUint32 { 778 fromTargetAcct := false 779 for _, in := range tx.Debits { 780 fromTargetAcct = fromTargetAcct || in.PreviousAccount == acctNb 781 } 782 for _, out := range tx.Credits { 783 fromTargetAcct = fromTargetAcct || out.Account == acctNb 784 } 785 if !fromTargetAcct { 786 continue 787 } 788 } 789 wireTx := &wire.MsgTx{} 790 txReader := bytes.NewReader(tx.Transaction) 791 792 if err := wireTx.Deserialize(txReader); err != nil { 793 return nil, err 794 } 795 796 var destAddresses []stdaddr.Address 797 for _, txOut := range wireTx.TxOut { 798 _, outAddresses := stdscript.ExtractAddrs( 799 txOut.Version, txOut.PkScript, chainParams) 800 destAddresses = append(destAddresses, outAddresses...) 801 } 802 803 txDetail := &lnwallet.TransactionDetail{ 804 Hash: wireTx.TxHash(), 805 NumConfirmations: currentHeight - headerHeight + 1, 806 BlockHash: blockHash, 807 BlockHeight: headerHeight, 808 Timestamp: block.Timestamp, 809 TotalFees: tx.Fee, 810 DestAddresses: destAddresses, 811 RawTx: tx.Transaction, 812 } 813 814 balanceDelta, err := extractBalanceDelta(tx, wireTx) 815 if err != nil { 816 return nil, err 817 } 818 txDetail.Value = balanceDelta 819 820 details = append(details, txDetail) 821 } 822 823 return details, nil 824 } 825 826 // unminedTransactionsToDetail is a helper function which converts a summary 827 // for an unconfirmed transaction to a transaction detail. 828 func unminedTransactionsToDetail( 829 summary *pb.TransactionDetails, 830 chainParams *chaincfg.Params, 831 ) (*lnwallet.TransactionDetail, error) { 832 833 wireTx := &wire.MsgTx{} 834 txReader := bytes.NewReader(summary.Transaction) 835 836 if err := wireTx.Deserialize(txReader); err != nil { 837 return nil, err 838 } 839 840 var destAddresses []stdaddr.Address 841 for _, txOut := range wireTx.TxOut { 842 _, outAddresses := 843 stdscript.ExtractAddrs(txOut.Version, 844 txOut.PkScript, chainParams) 845 destAddresses = append(destAddresses, outAddresses...) 846 } 847 848 txDetail := &lnwallet.TransactionDetail{ 849 Hash: wireTx.TxHash(), 850 TotalFees: summary.Fee, 851 Timestamp: summary.Timestamp, 852 DestAddresses: destAddresses, 853 RawTx: summary.Transaction, 854 } 855 856 balanceDelta, err := extractBalanceDelta(summary, wireTx) 857 if err != nil { 858 return nil, err 859 } 860 txDetail.Value = balanceDelta 861 862 return txDetail, nil 863 } 864 865 // ListTransactionDetails returns a list of all transactions which are 866 // relevant to the wallet. 867 // 868 // This is a part of the WalletController interface. 869 func (b *DcrWallet) ListTransactionDetails(startHeight, 870 endHeight int32, accountName string) ([]*lnwallet.TransactionDetail, error) { 871 872 // Grab the best block the wallet knows of, we'll use this to calculate 873 // # of confirmations shortly below. 874 bestBlockRes, err := b.wallet.BestBlock(context.Background(), &pb.BestBlockRequest{}) 875 if err != nil { 876 return nil, err 877 } 878 currentHeight := int32(bestBlockRes.Height) 879 880 var acctNb = b.account 881 if accountName == "" { 882 acctNb = math.MaxUint32 883 } else if accountName != lnwallet.DefaultAccountName { 884 res, err := b.wallet.AccountNumber(context.Background(), &pb.AccountNumberRequest{AccountName: accountName}) 885 if err != nil { 886 return nil, fmt.Errorf("unknown account named %s: %v", accountName, err) 887 } 888 acctNb = res.AccountNumber 889 } 890 891 req := &pb.GetTransactionsRequest{ 892 StartingBlockHeight: startHeight, 893 EndingBlockHeight: endHeight, 894 } 895 896 stream, err := b.wallet.GetTransactions(context.Background(), req) 897 if err != nil { 898 return nil, err 899 } 900 txs := make([]*lnwallet.TransactionDetail, 0) 901 for { 902 msg, err := stream.Recv() 903 if err == io.EOF { 904 break 905 } 906 if err != nil { 907 return nil, err 908 } 909 910 if msg.MinedTransactions != nil { 911 minedTxs, err := minedTransactionsToDetails(currentHeight, 912 msg.MinedTransactions, b.chainParams, acctNb) 913 if err != nil { 914 return nil, err 915 } 916 txs = append(txs, minedTxs...) 917 } 918 919 for _, tx := range msg.UnminedTransactions { 920 if acctNb < math.MaxUint32 { 921 fromTargetAcct := false 922 for _, in := range tx.Debits { 923 fromTargetAcct = fromTargetAcct || in.PreviousAccount == acctNb 924 } 925 for _, out := range tx.Credits { 926 fromTargetAcct = fromTargetAcct || out.Account == acctNb 927 } 928 if !fromTargetAcct { 929 continue 930 } 931 } 932 933 unminedTx, err := unminedTransactionsToDetail(tx, b.chainParams) 934 if err != nil { 935 return nil, err 936 } 937 txs = append(txs, unminedTx) 938 } 939 } 940 941 return txs, nil 942 } 943 944 // txSubscriptionClient encapsulates the transaction notification client from 945 // the base wallet. Notifications received from the client will be proxied over 946 // two distinct channels. 947 type txSubscriptionClient struct { 948 txClient pb.WalletService_TransactionNotificationsClient 949 950 confirmed chan *lnwallet.TransactionDetail 951 unconfirmed chan *lnwallet.TransactionDetail 952 953 wallet pb.WalletServiceClient 954 chainParams *chaincfg.Params 955 956 wg sync.WaitGroup 957 ctx context.Context 958 cancel func() 959 } 960 961 // ConfirmedTransactions returns a channel which will be sent on as new 962 // relevant transactions are confirmed. 963 // 964 // This is part of the TransactionSubscription interface. 965 func (t *txSubscriptionClient) ConfirmedTransactions() chan *lnwallet.TransactionDetail { 966 return t.confirmed 967 } 968 969 // UnconfirmedTransactions returns a channel which will be sent on as 970 // new relevant transactions are seen within the network. 971 // 972 // This is part of the TransactionSubscription interface. 973 func (t *txSubscriptionClient) UnconfirmedTransactions() chan *lnwallet.TransactionDetail { 974 return t.unconfirmed 975 } 976 977 // Cancel finalizes the subscription, cleaning up any resources allocated. 978 // 979 // This is part of the TransactionSubscription interface. 980 func (t *txSubscriptionClient) Cancel() { 981 t.cancel() 982 t.wg.Wait() 983 } 984 985 // notificationProxier proxies the notifications received by the underlying 986 // wallet's notification client to a higher-level TransactionSubscription 987 // client. 988 func (t *txSubscriptionClient) notificationProxier() { 989 defer t.wg.Done() 990 991 for { 992 msg, err := t.txClient.Recv() 993 if err == io.EOF { 994 // Cancel() was called. 995 break 996 } 997 if err != nil { 998 dcrwLog.Errorf("Error during tx subscription: %v", err) 999 break 1000 } 1001 1002 // TODO(roasbeef): handle detached blocks 1003 ctxb := context.Background() 1004 bestBlockResp, err := t.wallet.BestBlock(ctxb, &pb.BestBlockRequest{}) 1005 if err != nil { 1006 dcrwLog.Errorf("Unable to query best block in tx subscription") 1007 } 1008 currentHeight := int32(bestBlockResp.Height) 1009 1010 // Launch a goroutine to re-package and send notifications for 1011 // any newly confirmed transactions. 1012 go func() { 1013 for _, block := range msg.AttachedBlocks { 1014 details, err := minedTransactionsToDetails( 1015 currentHeight, block, t.chainParams, 1016 math.MaxUint32, 1017 ) 1018 if err != nil { 1019 continue 1020 } 1021 1022 for _, d := range details { 1023 select { 1024 case t.confirmed <- d: 1025 case <-t.ctx.Done(): 1026 return 1027 } 1028 } 1029 } 1030 1031 }() 1032 1033 // Launch a goroutine to re-package and send 1034 // notifications for any newly unconfirmed transactions. 1035 go func() { 1036 for _, tx := range msg.UnminedTransactions { 1037 detail, err := unminedTransactionsToDetail( 1038 tx, t.chainParams, 1039 ) 1040 if err != nil { 1041 continue 1042 } 1043 1044 select { 1045 case t.unconfirmed <- detail: 1046 case <-t.ctx.Done(): 1047 return 1048 } 1049 } 1050 }() 1051 } 1052 } 1053 1054 // SubscribeTransactions returns a TransactionSubscription client which 1055 // is capable of receiving async notifications as new transactions 1056 // related to the wallet are seen within the network, or found in 1057 // blocks. 1058 // 1059 // This is a part of the WalletController interface. 1060 func (b *DcrWallet) SubscribeTransactions() (lnwallet.TransactionSubscription, error) { 1061 req := &pb.TransactionNotificationsRequest{} 1062 ctx, cancel := context.WithCancel(context.Background()) 1063 stream, err := b.wallet.TransactionNotifications(ctx, req) 1064 if err != nil { 1065 cancel() 1066 return nil, err 1067 } 1068 1069 txClient := &txSubscriptionClient{ 1070 txClient: stream, 1071 confirmed: make(chan *lnwallet.TransactionDetail), 1072 unconfirmed: make(chan *lnwallet.TransactionDetail), 1073 wallet: b.wallet, 1074 chainParams: b.chainParams, 1075 ctx: ctx, 1076 cancel: cancel, 1077 } 1078 txClient.wg.Add(1) 1079 go txClient.notificationProxier() 1080 1081 return txClient, nil 1082 } 1083 1084 // IsSynced returns a boolean indicating if from the PoV of the wallet, it has 1085 // fully synced to the current best block in the main chain. 1086 // 1087 // This is a part of the WalletController interface. 1088 func (b *DcrWallet) IsSynced() (bool, int64, error) { 1089 // Grab the best chain state the wallet is currently aware of. 1090 ctxb := context.Background() 1091 bestBlockResp, err := b.wallet.BestBlock(ctxb, &pb.BestBlockRequest{}) 1092 if err != nil { 1093 return false, 0, err 1094 } 1095 walletBestHash := bestBlockResp.Hash 1096 blockInfoResp, err := b.wallet.BlockInfo(ctxb, &pb.BlockInfoRequest{BlockHash: walletBestHash}) 1097 if err != nil { 1098 return false, 0, err 1099 } 1100 headerTS := time.Unix(blockInfoResp.Timestamp, 0) 1101 1102 // TODO(decred) Check if the wallet is still syncing. This is 1103 // currently done by checking the associated chainIO but ideally the 1104 // wallet should return the height it's attempting to sync to. 1105 if b.cfg.ChainIO != nil { 1106 ioHash, _, err := b.cfg.ChainIO.GetBestBlock() 1107 if err != nil { 1108 return false, 0, err 1109 } 1110 if !bytes.Equal(walletBestHash, ioHash[:]) { 1111 return false, headerTS.Unix(), nil 1112 } 1113 } 1114 1115 // If the timestamp on the best header is more than 2 hours in the 1116 // past, then we're not yet synced. 1117 minus2Hours := time.Now().Add(-2 * time.Hour) 1118 if headerTS.Before(minus2Hours) { 1119 return false, headerTS.Unix(), nil 1120 } 1121 1122 // Check if the wallet has completed the initial sync procedure (discover 1123 // addresses, load tx filter, etc). 1124 walletSynced := atomic.LoadUint32(&b.atomicWalletSynced) == 1 1125 1126 return walletSynced, headerTS.Unix(), nil 1127 } 1128 1129 func (b *DcrWallet) BestBlock() (int64, chainhash.Hash, int64, error) { 1130 ctxb := context.Background() 1131 bestBlockResp, err := b.wallet.BestBlock(ctxb, &pb.BestBlockRequest{}) 1132 if err != nil { 1133 return 0, chainhash.Hash{}, 0, err 1134 } 1135 walletBestHash, err := chainhash.NewHash(bestBlockResp.Hash) 1136 if err != nil { 1137 return 0, chainhash.Hash{}, 0, err 1138 } 1139 blockInfoResp, err := b.wallet.BlockInfo(ctxb, &pb.BlockInfoRequest{BlockHash: walletBestHash[:]}) 1140 if err != nil { 1141 return 0, chainhash.Hash{}, 0, err 1142 } 1143 headerTS := time.Unix(blockInfoResp.Timestamp, 0) 1144 1145 return int64(bestBlockResp.Height), *walletBestHash, headerTS.Unix(), nil 1146 } 1147 1148 // InitialSyncChannel returns the channel used to signal that wallet init has 1149 // finished. 1150 // 1151 // This is a part of the WalletController interface. 1152 func (b *DcrWallet) InitialSyncChannel() <-chan struct{} { 1153 return b.syncedChan 1154 } 1155 1156 // synced is called by the chosen chain syncer (rpc/spv) after the wallet has 1157 // successfully synced its state to the chain. 1158 func (b *DcrWallet) synced() { 1159 dcrwLog.Debug("Syncer notified wallet is synced") 1160 1161 // Signal that the wallet is synced by closing the channel. 1162 if atomic.CompareAndSwapUint32(&b.atomicWalletSynced, 0, 1) { 1163 close(b.syncedChan) 1164 } 1165 } 1166 1167 // Bip44AddressInfo returns the BIP44 relevant (account, branch and index) of 1168 // the given wallet address. 1169 func (b *DcrWallet) Bip44AddressInfo(addr stdaddr.Address) (uint32, uint32, uint32, error) { 1170 req := &pb.ValidateAddressRequest{ 1171 Address: addr.String(), 1172 } 1173 resp, err := b.wallet.ValidateAddress(context.Background(), req) 1174 if err != nil { 1175 return 0, 0, 0, err 1176 } 1177 1178 if !resp.IsValid { 1179 return 0, 0, 0, fmt.Errorf("invalid address") 1180 } 1181 if !resp.IsMine { 1182 return 0, 0, 0, fmt.Errorf("not an owned address") 1183 } 1184 if resp.IsScript { 1185 return 0, 0, 0, fmt.Errorf("not a p2pkh address") 1186 } 1187 1188 branch := uint32(0) 1189 if resp.IsInternal { 1190 branch = 1 1191 } 1192 return resp.AccountNumber, branch, resp.Index, nil 1193 } 1194 1195 // LabelTransaction adds the given external label to the specified transaction. 1196 // 1197 // This is a part of the WalletController interface. 1198 func (b *DcrWallet) LabelTransaction(hash chainhash.Hash, label string, overwrite bool) error { 1199 return fmt.Errorf("unimplemented") 1200 } 1201 1202 // LeaseOutput markes the output as used for some time. 1203 // 1204 // This is a part of the WalletController interface. 1205 func (b *DcrWallet) LeaseOutput(lnwallet.LockID, wire.OutPoint, time.Duration) (time.Time, error) { 1206 return time.Time{}, fmt.Errorf("unimplemented") 1207 } 1208 1209 // ReleaseOutput marks the output as unused. 1210 // 1211 // This is a part of the WalletController interface. 1212 func (b *DcrWallet) ReleaseOutput(lnwallet.LockID, wire.OutPoint) error { 1213 return fmt.Errorf("unimplemented") 1214 } 1215 1216 // ListLeasedOutputs lists leased wallet outputs. 1217 // 1218 // This is a part of the WalletController interface. 1219 func (b *DcrWallet) ListLeasedOutputs() ([]*lnwallet.LockedOutput, error) { 1220 return nil, fmt.Errorf("unimplemented") 1221 } 1222 1223 // GetRecoveryInfo returns the current status of the recovery of the wallet. 1224 // 1225 // This is a part of the WalletController interface. 1226 func (b *DcrWallet) GetRecoveryInfo() (bool, float64, error) { 1227 return false, 0, fmt.Errorf("unimplemented") 1228 } 1229 1230 // FetchTx attempts to fetch a transaction in the wallet's database 1231 // identified by the passed transaction hash. If the transaction can't 1232 // be found, then a nil pointer is returned. 1233 // 1234 // This is a part of the WalletController interface. 1235 func (b *DcrWallet) FetchTx(txid chainhash.Hash) (*wire.MsgTx, error) { 1236 req := &pb.GetTransactionRequest{TransactionHash: txid[:]} 1237 res, err := b.wallet.GetTransaction(b.ctx, req) 1238 if err != nil { 1239 return nil, err 1240 } 1241 tx := wire.NewMsgTx() 1242 err = tx.Deserialize(bytes.NewBuffer(res.Transaction.Transaction)) 1243 if err != nil { 1244 return nil, err 1245 } 1246 return tx, err 1247 } 1248 1249 // RemoveDescendants attempts to remove any transaction from the 1250 // wallet's tx store (that may be unconfirmed) that spends outputs 1251 // created by the passed transaction. This remove propagates 1252 // recursively down the chain of descendent transactions. 1253 // 1254 // This is a part of the WalletController interface. 1255 func (b *DcrWallet) RemoveDescendants(*wire.MsgTx) error { 1256 return fmt.Errorf("RemoveDescendants is unimplemented") 1257 } 1258 1259 // ListAccount lists existing wallet accounts. 1260 // 1261 // This is a part of the WalletController interface. 1262 func (b *DcrWallet) ListAccounts(accountName string) ([]base.AccountProperties, error) { 1263 accounts, err := b.wallet.Accounts(context.Background(), &pb.AccountsRequest{}) 1264 if err != nil { 1265 return nil, err 1266 } 1267 1268 szHint := len(accounts.Accounts) 1269 if accountName != "" { 1270 szHint = 1 1271 } 1272 res := make([]base.AccountProperties, 0, szHint) 1273 for _, acct := range accounts.Accounts { 1274 if accountName != "" && acct.AccountName != accountName { 1275 continue 1276 } 1277 res = append(res, base.AccountProperties{ 1278 AccountNumber: acct.AccountNumber, 1279 AccountName: acct.AccountName, 1280 LastReturnedExternalIndex: acct.ExternalKeyCount - 1, 1281 LastReturnedInternalIndex: acct.InternalKeyCount - 1, 1282 ImportedKeyCount: acct.ImportedKeyCount, 1283 AccountEncrypted: acct.AccountEncrypted, 1284 AccountUnlocked: acct.AccountUnlocked, 1285 1286 // The following are not exposed via gRPC currently. 1287 //AccountType: acct.AccountType, 1288 //LastUsedExternalIndex: acct.ExternalKeyCount, 1289 //LastUsedInternalIndex: acct.InternalKeyCount, 1290 }) 1291 } 1292 1293 return res, nil 1294 } 1295 1296 // ImportAccount imports the specified xpub into the wallet. 1297 // 1298 // This is a part of the WalletController interface. 1299 func (b *DcrWallet) ImportAccount(name string, accountPubKey *hdkeychain.ExtendedKey, dryRun bool) ( 1300 *wallet.AccountProperties, []stdaddr.Address, []stdaddr.Address, error) { 1301 1302 fail := func(err error) (*wallet.AccountProperties, []stdaddr.Address, []stdaddr.Address, error) { 1303 return nil, nil, nil, err 1304 } 1305 1306 if dryRun { 1307 intAddrs, extAddrs, err := lnwallet.DeriveAddrsFromExtPub(accountPubKey, 1308 b.chainParams, dryRunImportAccountNumAddrs) 1309 if err != nil { 1310 return fail(err) 1311 } 1312 1313 acctProps := &wallet.AccountProperties{ 1314 AccountName: name, 1315 } 1316 return acctProps, intAddrs, extAddrs, nil 1317 } 1318 1319 req := &pb.ImportExtendedPublicKeyRequest{ 1320 AccountName: name, 1321 Xpub: accountPubKey.String(), 1322 } 1323 _, err := b.wallet.ImportExtendedPublicKey(context.Background(), req) 1324 if err != nil { 1325 return fail(err) 1326 } 1327 1328 accounts, err := b.ListAccounts(name) 1329 if err != nil { 1330 return fail(err) 1331 } 1332 if len(accounts) == 0 { 1333 return fail(fmt.Errorf("account named %q does not exist", name)) 1334 } 1335 1336 return &accounts[0], nil, nil, nil 1337 } 1338 1339 // ImportPublicKey imports the specified public key into the wallet. 1340 // 1341 // This is a part of the WalletController interface. 1342 func (b *DcrWallet) ImportPublicKey(pubKey *secp256k1.PublicKey) error { 1343 return fmt.Errorf("unimplemented in gRPC") 1344 } 1345 1346 func (b *DcrWallet) ScriptForOutput(*wire.TxOut) ( 1347 btcwalletcompat.ManagedPubKeyAddress, []byte, []byte, error) { 1348 return nil, nil, nil, fmt.Errorf("remotedcrwallet.ScriptForOutput is not implemented") 1349 }