// spendNestedWitnessPubKey generates both a sigScript, and valid witness for // spending the passed pkScript with the specified input amount. The generated // sigScript is the version 0 p2wkh witness program corresponding to the queried // key. The witness stack is identical to that of one which spends a regular // p2wkh output. The input amount *must* correspond to the output value of the // previous pkScript, or else verification will fail since the new sighash // digest algorithm defined in BIP0143 includes the input value in the sighash. func spendNestedWitnessPubKeyHash(txIn *wire.TxIn, pkScript []byte, inputValue int64, chainParams *chaincfg.Params, secrets SecretsSource, tx *wire.MsgTx, hashCache *txscript.TxSigHashes, idx int) error { // First we need to obtain the key pair related to this p2sh output. _, addrs, _, err := txscript.ExtractPkScriptAddrs(pkScript, chainParams) if err != nil { return err } privKey, compressed, err := secrets.GetKey(addrs[0]) if err != nil { return err } pubKey := privKey.PubKey() var pubKeyHash []byte if compressed { pubKeyHash = btcutil.Hash160(pubKey.SerializeCompressed()) } else { pubKeyHash = btcutil.Hash160(pubKey.SerializeUncompressed()) } // Next, we'll generate a valid sigScript that'll allow us to spend // the p2sh output. The sigScript will contain only a single push of // the p2wkh witness program corresponding to the matching public key // of this address. p2wkhAddr, err := btcutil.NewAddressWitnessPubKeyHash(pubKeyHash, chainParams) if err != nil { return err } witnessProgram, err := txscript.PayToAddrScript(p2wkhAddr) if err != nil { return err } bldr := txscript.NewScriptBuilder() bldr.AddData(witnessProgram) sigScript, err := bldr.Script() if err != nil { return err } txIn.SignatureScript = sigScript // With the sigScript in place, we'll next generate the proper witness // that'll allow us to spend the p2wkh output. witnessScript, err := txscript.WitnessScript(tx, hashCache, idx, inputValue, witnessProgram, txscript.SigHashAll, privKey, compressed) if err != nil { return err } txIn.Witness = witnessScript return nil }
func lookupInputAccount(w *Wallet, details *wtxmgr.TxDetails, deb wtxmgr.DebitRecord) uint32 { // TODO: Debits should record which account(s?) they // debit from so this doesn't need to be looked up. prevOP := &details.MsgTx.TxIn[deb.Index].PreviousOutPoint prev, err := w.TxStore.TxDetails(&prevOP.Hash) if err != nil { log.Errorf("Cannot query previous transaction details for %v: %v", prevOP.Hash, err) return 0 } if prev == nil { log.Errorf("Missing previous transaction %v", prevOP.Hash) return 0 } prevOut := prev.MsgTx.TxOut[prevOP.Index] _, addrs, _, err := txscript.ExtractPkScriptAddrs(prevOut.PkScript, w.chainParams) var inputAcct uint32 if err == nil && len(addrs) > 0 { inputAcct, err = w.Manager.AddrAccount(addrs[0]) } if err != nil { log.Errorf("Cannot fetch account for previous output %v: %v", prevOP, err) inputAcct = 0 } return inputAcct }
// indexUnconfirmedAddresses modifies the unconfirmed (memory-only) address // index to include mappings for the addresses encoded by the passed public key // script to the transaction. // // This function is safe for concurrent access. func (idx *AddrIndex) indexUnconfirmedAddresses(pkScript []byte, tx *btcutil.Tx) { // The error is ignored here since the only reason it can fail is if the // script fails to parse and it was already validated before being // admitted to the mempool. _, addresses, _, _ := txscript.ExtractPkScriptAddrs(pkScript, idx.chainParams) for _, addr := range addresses { // Ignore unsupported address types. addrKey, err := addrToKey(addr) if err != nil { continue } // Add a mapping from the address to the transaction. idx.unconfirmedLock.Lock() addrIndexEntry := idx.txnsByAddr[addrKey] if addrIndexEntry == nil { addrIndexEntry = make(map[chainhash.Hash]*btcutil.Tx) idx.txnsByAddr[addrKey] = addrIndexEntry } addrIndexEntry[*tx.Hash()] = tx // Add a mapping from the transaction to the address. addrsByTxEntry := idx.addrsByTx[*tx.Hash()] if addrsByTxEntry == nil { addrsByTxEntry = make(map[[addrKeySize]byte]struct{}) idx.addrsByTx[*tx.Hash()] = addrsByTxEntry } addrsByTxEntry[addrKey] = struct{}{} idx.unconfirmedLock.Unlock() } }
// handleFundingResponse processes a response to the workflow initiation sent // by the remote peer. This message then queues a message with the funding // outpoint, and a commitment signature to the remote peer. func (f *fundingManager) handleFundingResponse(fmsg *fundingResponseMsg) { msg := fmsg.msg sourcePeer := fmsg.peer f.resMtx.RLock() resCtx := f.activeReservations[fmsg.peer.id][msg.ChannelID] f.resMtx.RUnlock() fndgLog.Infof("Recv'd fundingResponse for pendingID(%v)", msg.ChannelID) // The remote node has responded with their portion of the channel // contribution. At this point, we can process their contribution which // allows us to construct and sign both the commitment transaction, and // the funding transaction. _, addrs, _, err := txscript.ExtractPkScriptAddrs(msg.DeliveryPkScript, activeNetParams.Params) if err != nil { fndgLog.Errorf("Unable to extract addresses from script: %v", err) resCtx.err <- err return } contribution := &lnwallet.ChannelContribution{ FundingAmount: 0, MultiSigKey: msg.ChannelDerivationPoint, CommitKey: msg.CommitmentKey, DeliveryAddress: addrs[0], RevocationKey: msg.RevocationKey, CsvDelay: msg.CsvDelay, } if err := resCtx.reservation.ProcessContribution(contribution); err != nil { fndgLog.Errorf("Unable to process contribution from %v: %v", sourcePeer, err) fmsg.peer.Disconnect() resCtx.err <- err return } // Now that we have their contribution, we can extract, then send over // both the funding out point and our signature for their version of // the commitment transaction to the remote peer. outPoint := resCtx.reservation.FundingOutpoint() _, sig := resCtx.reservation.OurSignatures() commitSig, err := btcec.ParseSignature(sig, btcec.S256()) if err != nil { fndgLog.Errorf("Unable to parse signature: %v", err) resCtx.err <- err return } // Register a new barrier for this channel to properly synchronize with // the peer's readHandler once the channel is open. fmsg.peer.barrierInits <- *outPoint fndgLog.Infof("Generated ChannelPoint(%v) for pendingID(%v)", outPoint, msg.ChannelID) revocationKey := resCtx.reservation.OurContribution().RevocationKey fundingComplete := lnwire.NewSingleFundingComplete(msg.ChannelID, outPoint, commitSig, revocationKey) sourcePeer.queueMsg(fundingComplete, nil) }
// indexPkScript extracts all standard addresses from the passed public key // script and maps each of them to the associated transaction using the passed // map. func (idx *AddrIndex) indexPkScript(data writeIndexData, pkScript []byte, txIdx int) { // Nothing to index if the script is non-standard or otherwise doesn't // contain any addresses. _, addrs, _, err := txscript.ExtractPkScriptAddrs(pkScript, idx.chainParams) if err != nil || len(addrs) == 0 { return } for _, addr := range addrs { addrKey, err := addrToKey(addr) if err != nil { // Ignore unsupported address types. continue } // Avoid inserting the transaction more than once. Since the // transactions are indexed serially any duplicates will be // indexed in a row, so checking the most recent entry for the // address is enough to detect duplicates. indexedTxns := data[addrKey] numTxns := len(indexedTxns) if numTxns > 0 && indexedTxns[numTxns-1] == txIdx { continue } indexedTxns = append(indexedTxns, txIdx) data[addrKey] = indexedTxns } }
// This example demonstrates extracting information from a standard public key // script. func ExampleExtractPkScriptAddrs() { // Start with a standard pay-to-pubkey-hash script. scriptHex := "76a914128004ff2fcaf13b2b91eb654b1dc2b674f7ec6188ac" script, err := hex.DecodeString(scriptHex) if err != nil { fmt.Println(err) return } // Extract and print details from the script. scriptClass, addresses, reqSigs, err := txscript.ExtractPkScriptAddrs( script, &chaincfg.MainNetParams) if err != nil { fmt.Println(err) return } fmt.Println("Script Class:", scriptClass) fmt.Println("Addresses:", addresses) fmt.Println("Required Signatures:", reqSigs) // Output: // Script Class: pubkeyhash // Addresses: [12gpXQVcCL2qhTNQgyLVdCFG2Qs2px98nV] // Required Signatures: 1 }
// signMultiSigUTXO signs the P2SH UTXO with the given index by constructing a // script containing all given signatures plus the redeem (multi-sig) script. The // redeem script is obtained by looking up the address of the given P2SH pkScript // on the address manager. // The order of the signatures must match that of the public keys in the multi-sig // script as OP_CHECKMULTISIG expects that. // This function must be called with the manager unlocked. func signMultiSigUTXO(mgr *waddrmgr.Manager, tx *wire.MsgTx, idx int, pkScript []byte, sigs []RawSig) error { class, addresses, _, err := txscript.ExtractPkScriptAddrs(pkScript, mgr.ChainParams()) if err != nil { return newError(ErrTxSigning, "unparseable pkScript", err) } if class != txscript.ScriptHashTy { return newError(ErrTxSigning, fmt.Sprintf("pkScript is not P2SH: %s", class), nil) } redeemScript, err := getRedeemScript(mgr, addresses[0].(*btcutil.AddressScriptHash)) if err != nil { return newError(ErrTxSigning, "unable to retrieve redeem script", err) } class, _, nRequired, err := txscript.ExtractPkScriptAddrs(redeemScript, mgr.ChainParams()) if err != nil { return newError(ErrTxSigning, "unparseable redeem script", err) } if class != txscript.MultiSigTy { return newError(ErrTxSigning, fmt.Sprintf("redeem script is not multi-sig: %v", class), nil) } if len(sigs) < nRequired { errStr := fmt.Sprintf("not enough signatures; need %d but got only %d", nRequired, len(sigs)) return newError(ErrTxSigning, errStr, nil) } // Construct the unlocking script. // Start with an OP_0 because of the bug in bitcoind, then add nRequired signatures. unlockingScript := txscript.NewScriptBuilder().AddOp(txscript.OP_FALSE) for _, sig := range sigs[:nRequired] { unlockingScript.AddData(sig) } // Combine the redeem script and the unlocking script to get the actual signature script. sigScript := unlockingScript.AddData(redeemScript) script, err := sigScript.Script() if err != nil { return newError(ErrTxSigning, "error building sigscript", err) } tx.TxIn[idx].SignatureScript = script if err := validateSigScript(tx, idx, pkScript); err != nil { return err } return nil }
func (w *Wallet) findEligibleOutputs(account uint32, minconf int32, bs *waddrmgr.BlockStamp) ([]wtxmgr.Credit, error) { unspent, err := w.TxStore.UnspentOutputs() if err != nil { return nil, err } // TODO: Eventually all of these filters (except perhaps output locking) // should be handled by the call to UnspentOutputs (or similar). // Because one of these filters requires matching the output script to // the desired account, this change depends on making wtxmgr a waddrmgr // dependancy and requesting unspent outputs for a single account. eligible := make([]wtxmgr.Credit, 0, len(unspent)) for i := range unspent { output := &unspent[i] // Only include this output if it meets the required number of // confirmations. Coinbase transactions must have have reached // maturity before their outputs may be spent. if !confirmed(minconf, output.Height, bs.Height) { continue } if output.FromCoinBase { const target = blockchain.CoinbaseMaturity if !confirmed(target, output.Height, bs.Height) { continue } } // Locked unspent outputs are skipped. if w.LockedOutpoint(output.OutPoint) { continue } // Only include the output if it is associated with the passed // account. // // TODO: Handle multisig outputs by determining if enough of the // addresses are controlled. _, addrs, _, err := txscript.ExtractPkScriptAddrs( output.PkScript, w.chainParams) if err != nil || len(addrs) != 1 { continue } addrAcct, err := w.Manager.AddrAccount(addrs[0]) if err != nil || addrAcct != account { continue } eligible = append(eligible, *output) } return eligible, nil }
func lookupOutputChain(w *Wallet, details *wtxmgr.TxDetails, cred wtxmgr.CreditRecord) (account uint32, internal bool) { output := details.MsgTx.TxOut[cred.Index] _, addrs, _, err := txscript.ExtractPkScriptAddrs(output.PkScript, w.chainParams) var ma waddrmgr.ManagedAddress if err == nil && len(addrs) > 0 { ma, err = w.Manager.Address(addrs[0]) } if err != nil { log.Errorf("Cannot fetch account for wallet output: %v", err) } else { account = ma.Account() internal = ma.Internal() } return }
// spendWitnessKeyHash generates, and sets a valid witness for spending the // passed pkScript with the specified input amount. The input amount *must* // correspond to the output value of the previous pkScript, or else verification // will fail since the new sighash digest algorithm defined in BIP0143 includes // the input value in the sighash. func spendWitnessKeyHash(txIn *wire.TxIn, pkScript []byte, inputValue int64, chainParams *chaincfg.Params, secrets SecretsSource, tx *wire.MsgTx, hashCache *txscript.TxSigHashes, idx int) error { // First obtain the key pair associated with this p2wkh address. _, addrs, _, err := txscript.ExtractPkScriptAddrs(pkScript, chainParams) if err != nil { return err } privKey, compressed, err := secrets.GetKey(addrs[0]) if err != nil { return err } pubKey := privKey.PubKey() // Once we have the key pair, generate a p2wkh address type, respecting // the compression type of the generated key. var pubKeyHash []byte if compressed { pubKeyHash = btcutil.Hash160(pubKey.SerializeCompressed()) } else { pubKeyHash = btcutil.Hash160(pubKey.SerializeUncompressed()) } p2wkhAddr, err := btcutil.NewAddressWitnessPubKeyHash(pubKeyHash, chainParams) if err != nil { return err } // With the concrete address type, we can now generate the // corresponding witness program to be used to generate a valid witness // which will allow us to spend this output. witnessProgram, err := txscript.PayToAddrScript(p2wkhAddr) if err != nil { return err } witnessScript, err := txscript.WitnessScript(tx, hashCache, idx, inputValue, witnessProgram, txscript.SigHashAll, privKey, true) if err != nil { return err } txIn.Witness = witnessScript return nil }
// fetchOutputKey attempts to fetch the managed address corresponding to the // passed output script. This function is used to look up the proper key which // should be used to sign a specified input. func (b *BtcWallet) fetchOutputAddr(script []byte) (waddrmgr.ManagedAddress, error) { _, addrs, _, err := txscript.ExtractPkScriptAddrs(script, b.netParams) if err != nil { return nil, err } // If the case of a multi-sig output, several address may be extracted. // Therefore, we simply select the key for the first address we know // of. for _, addr := range addrs { wAddr, err := b.wallet.Manager.Address(addr) if err == nil { return wAddr, nil } } // TODO(roasbeef): use the errors.wrap package return nil, fmt.Errorf("address not found") }
func totalBalances(w *Wallet, m map[uint32]btcutil.Amount) error { unspent, err := w.TxStore.UnspentOutputs() if err != nil { return err } for i := range unspent { output := &unspent[i] var outputAcct uint32 _, addrs, _, err := txscript.ExtractPkScriptAddrs( output.PkScript, w.chainParams) if err == nil && len(addrs) > 0 { outputAcct, err = w.Manager.AddrAccount(addrs[0]) } if err == nil { _, ok := m[outputAcct] if ok { m[outputAcct] += output.Amount } } } return nil }
// groupCreditsByAddr converts a slice of credits to a map from the string // representation of an encoded address to the unspent outputs associated with // that address. func groupCreditsByAddr(credits []wtxmgr.Credit, chainParams *chaincfg.Params) ( map[string][]wtxmgr.Credit, error) { addrMap := make(map[string][]wtxmgr.Credit) for _, c := range credits { _, addrs, _, err := txscript.ExtractPkScriptAddrs(c.PkScript, chainParams) if err != nil { return nil, newError(ErrInputSelection, "failed to obtain input address", err) } // As our credits are all P2SH we should never have more than one // address per credit, so let's error out if that assumption is // violated. if len(addrs) != 1 { return nil, newError(ErrInputSelection, "input doesn't have exactly one address", nil) } encAddr := addrs[0].EncodeAddress() if v, ok := addrMap[encAddr]; ok { addrMap[encAddr] = append(v, c) } else { addrMap[encAddr] = []wtxmgr.Credit{c} } } return addrMap, nil }
// handleSingleFundingRequest creates an initial 'ChannelReservation' within // the wallet, then responds to the source peer with a single funder response // message progressing the funding workflow. // TODO(roasbeef): add error chan to all, let channelManager handle // error+propagate func (f *fundingManager) handleFundingRequest(fmsg *fundingRequestMsg) { // Check number of pending channels to be smaller than maximum allowed // number and send ErrorGeneric to remote peer if condition is violated. if len(f.activeReservations[fmsg.peer.id]) >= cfg.MaxPendingChannels { errMsg := &lnwire.ErrorGeneric{ ChannelPoint: &wire.OutPoint{ Hash: wire.ShaHash{}, Index: 0, }, Problem: "Number of pending channels exceed maximum", Code: lnwire.ErrorMaxPendingChannels, PendingChannelID: fmsg.msg.ChannelID, } fmsg.peer.queueMsg(errMsg, nil) return } msg := fmsg.msg amt := msg.FundingAmount delay := msg.CsvDelay // TODO(roasbeef): error if funding flow already ongoing fndgLog.Infof("Recv'd fundingRequest(amt=%v, delay=%v, pendingId=%v) "+ "from peerID(%v)", amt, delay, msg.ChannelID, fmsg.peer.id) // Attempt to initialize a reservation within the wallet. If the wallet // has insufficient resources to create the channel, then the reservation // attempt may be rejected. Note that since we're on the responding // side of a single funder workflow, we don't commit any funds to the // channel ourselves. // TODO(roasbeef): passing num confs 1 is irrelevant here, make signed? reservation, err := f.wallet.InitChannelReservation(amt, 0, fmsg.peer.addr.IdentityKey, fmsg.peer.addr.Address, 1, delay) if err != nil { // TODO(roasbeef): push ErrorGeneric message fndgLog.Errorf("Unable to initialize reservation: %v", err) fmsg.peer.Disconnect() return } // Once the reservation has been created successfully, we add it to this // peers map of pending reservations to track this particular reservation // until either abort or completion. f.resMtx.Lock() if _, ok := f.activeReservations[fmsg.peer.id]; !ok { f.activeReservations[fmsg.peer.id] = make(pendingChannels) } f.activeReservations[fmsg.peer.id][msg.ChannelID] = &reservationWithCtx{ reservation: reservation, peer: fmsg.peer, } f.resMtx.Unlock() // With our portion of the reservation initialied, process the // initiators contribution to the channel. _, addrs, _, err := txscript.ExtractPkScriptAddrs(msg.DeliveryPkScript, activeNetParams.Params) if err != nil { fndgLog.Errorf("Unable to extract addresses from script: %v", err) return } contribution := &lnwallet.ChannelContribution{ FundingAmount: amt, MultiSigKey: msg.ChannelDerivationPoint, CommitKey: msg.CommitmentKey, DeliveryAddress: addrs[0], CsvDelay: delay, } if err := reservation.ProcessSingleContribution(contribution); err != nil { fndgLog.Errorf("unable to add contribution reservation: %v", err) fmsg.peer.Disconnect() return } fndgLog.Infof("Sending fundingResp for pendingID(%v)", msg.ChannelID) // With the initiator's contribution recorded, response with our // contribution in the next message of the workflow. ourContribution := reservation.OurContribution() deliveryScript, err := txscript.PayToAddrScript(ourContribution.DeliveryAddress) if err != nil { fndgLog.Errorf("unable to convert address to pkscript: %v", err) return } fundingResp := lnwire.NewSingleFundingResponse(msg.ChannelID, ourContribution.RevocationKey, ourContribution.CommitKey, ourContribution.MultiSigKey, ourContribution.CsvDelay, deliveryScript) fmsg.peer.queueMsg(fundingResp, nil) }
func (w *Wallet) addRelevantTx(rec *wtxmgr.TxRecord, block *wtxmgr.BlockMeta) error { // TODO: The transaction store and address manager need to be updated // together, but each operate under different namespaces and are changed // under new transactions. This is not error safe as we lose // transaction semantics. // // I'm unsure of the best way to solve this. Some possible solutions // and drawbacks: // // 1. Open write transactions here and pass the handle to every // waddrmr and wtxmgr method. This complicates the caller code // everywhere, however. // // 2. Move the wtxmgr namespace into the waddrmgr namespace, likely // under its own bucket. This entire function can then be moved // into the waddrmgr package, which updates the nested wtxmgr. // This removes some of separation between the components. // // 3. Use multiple wtxmgrs, one for each account, nested in the // waddrmgr namespace. This still provides some sort of logical // separation (transaction handling remains in another package, and // is simply used by waddrmgr), but may result in duplicate // transactions being saved if they are relevant to multiple // accounts. // // 4. Store wtxmgr-related details under the waddrmgr namespace, but // solve the drawback of #3 by splitting wtxmgr to save entire // transaction records globally for all accounts, with // credit/debit/balance tracking per account. Each account would // also save the relevant transaction hashes and block incidence so // the full transaction can be loaded from the waddrmgr // transactions bucket. This currently seems like the best // solution. // At the moment all notified transactions are assumed to actually be // relevant. This assumption will not hold true when SPV support is // added, but until then, simply insert the transaction because there // should either be one or more relevant inputs or outputs. err := w.TxStore.InsertTx(rec, block) if err != nil { return err } // Check every output to determine whether it is controlled by a wallet // key. If so, mark the output as a credit. for i, output := range rec.MsgTx.TxOut { _, addrs, _, err := txscript.ExtractPkScriptAddrs(output.PkScript, w.chainParams) if err != nil { // Non-standard outputs are skipped. continue } for _, addr := range addrs { ma, err := w.Manager.Address(addr) if err == nil { // TODO: Credits should be added with the // account they belong to, so wtxmgr is able to // track per-account balances. err = w.TxStore.AddCredit(rec, block, uint32(i), ma.Internal()) if err != nil { return err } err = w.Manager.MarkUsed(addr) if err != nil { return err } log.Debugf("Marked address %v used", addr) continue } // Missing addresses are skipped. Other errors should // be propagated. if !waddrmgr.IsError(err, waddrmgr.ErrAddressNotFound) { return err } } } // Send notification of mined or unmined transaction to any interested // clients. // // TODO: Avoid the extra db hits. if block == nil { details, err := w.TxStore.UniqueTxDetails(&rec.Hash, nil) if err != nil { log.Errorf("Cannot query transaction details for notifiation: %v", err) } else { w.NtfnServer.notifyUnminedTransaction(details) } } else { details, err := w.TxStore.UniqueTxDetails(&rec.Hash, &block.Block) if err != nil { log.Errorf("Cannot query transaction details for notifiation: %v", err) } else { w.NtfnServer.notifyMinedTransaction(details, block) } } return nil }
func (s *walletServer) FundTransaction(ctx context.Context, req *pb.FundTransactionRequest) ( *pb.FundTransactionResponse, error) { // TODO: A predicate function for selecting outputs should be created // and passed to a database view of just a particular account's utxos to // prevent reading every unspent transaction output from every account // into memory at once. syncBlock := s.wallet.Manager.SyncedTo() outputs, err := s.wallet.TxStore.UnspentOutputs() if err != nil { return nil, translateError(err) } selectedOutputs := make([]*pb.FundTransactionResponse_PreviousOutput, 0, len(outputs)) var totalAmount btcutil.Amount for i := range outputs { output := &outputs[i] if !confirmed(req.RequiredConfirmations, output.Height, syncBlock.Height) { continue } if !req.IncludeImmatureCoinbases && output.FromCoinBase && !confirmed(blockchain.CoinbaseMaturity, output.Height, syncBlock.Height) { continue } _, addrs, _, err := txscript.ExtractPkScriptAddrs( output.PkScript, s.wallet.ChainParams()) if err != nil || len(addrs) == 0 { // Cannot determine which account this belongs to // without a valid address. Fix this by saving // outputs per account (per-account wtxmgr). continue } outputAcct, err := s.wallet.Manager.AddrAccount(addrs[0]) if err != nil { return nil, translateError(err) } if outputAcct != req.Account { continue } selectedOutputs = append(selectedOutputs, &pb.FundTransactionResponse_PreviousOutput{ TransactionHash: output.OutPoint.Hash[:], OutputIndex: output.Index, Amount: int64(output.Amount), PkScript: output.PkScript, ReceiveTime: output.Received.Unix(), FromCoinbase: output.FromCoinBase, }) totalAmount += output.Amount if req.TargetAmount != 0 && totalAmount > btcutil.Amount(req.TargetAmount) { break } } var changeScript []byte if req.IncludeChangeScript && totalAmount > btcutil.Amount(req.TargetAmount) { changeAddr, err := s.wallet.NewChangeAddress(req.Account, waddrmgr.PubKeyHash) if err != nil { return nil, translateError(err) } changeScript, err = txscript.PayToAddrScript(changeAddr) if err != nil { return nil, translateError(err) } } return &pb.FundTransactionResponse{ SelectedOutputs: selectedOutputs, TotalAmount: int64(totalAmount), ChangePkScript: changeScript, }, nil }