mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-03 17:26:57 +01:00
multi: forward SendCoins call over RPC
This commit is contained in:
parent
19db382e24
commit
a3addcc927
8 changed files with 117 additions and 14 deletions
3
go.mod
3
go.mod
|
@ -9,10 +9,9 @@ require (
|
|||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f
|
||||
github.com/btcsuite/btcutil v1.0.3-0.20210527170813-e2ba6805a890
|
||||
github.com/btcsuite/btcutil/psbt v1.0.3-0.20210527170813-e2ba6805a890
|
||||
github.com/btcsuite/btcwallet v0.12.1-0.20210916213031-d0868cb9dd94
|
||||
github.com/btcsuite/btcwallet v0.12.1-0.20211008000044-541a8512ccfe
|
||||
github.com/btcsuite/btcwallet/wallet/txauthor v1.1.0
|
||||
github.com/btcsuite/btcwallet/wallet/txrules v1.1.0
|
||||
github.com/btcsuite/btcwallet/wallet/txsizes v1.1.0 // indirect
|
||||
github.com/btcsuite/btcwallet/walletdb v1.3.6-0.20210803004036-eebed51155ec
|
||||
github.com/btcsuite/btcwallet/wtxmgr v1.3.1-0.20210822222949-9b5a201c344c
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f
|
||||
|
|
6
go.sum
6
go.sum
|
@ -91,17 +91,15 @@ github.com/btcsuite/btcutil v1.0.3-0.20210527170813-e2ba6805a890/go.mod h1:0DVlH
|
|||
github.com/btcsuite/btcutil/psbt v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:LVveMu4VaNSkIRTZu2+ut0HDBRuYjqGocxDMNS1KuGQ=
|
||||
github.com/btcsuite/btcutil/psbt v1.0.3-0.20210527170813-e2ba6805a890 h1:0xUNvvwJ7RjzBs4nCF+YrK28S5P/b4uHkpPxY1ovGY4=
|
||||
github.com/btcsuite/btcutil/psbt v1.0.3-0.20210527170813-e2ba6805a890/go.mod h1:LVveMu4VaNSkIRTZu2+ut0HDBRuYjqGocxDMNS1KuGQ=
|
||||
github.com/btcsuite/btcwallet v0.12.1-0.20210916213031-d0868cb9dd94 h1:Bx+xu606h2sZNn5VaZMWvI0GtlGE+y+dHI4hbL5Ld6k=
|
||||
github.com/btcsuite/btcwallet v0.12.1-0.20210916213031-d0868cb9dd94/go.mod h1:gHFk6GQ4IP/a8z6mfwK85GagUPxvAxCmgFy/whrBXhI=
|
||||
github.com/btcsuite/btcwallet v0.12.1-0.20211008000044-541a8512ccfe h1:G7l/t3Y+B7jhRzcKs9z00KdocqdEqaXstwa0KSxH1bQ=
|
||||
github.com/btcsuite/btcwallet v0.12.1-0.20211008000044-541a8512ccfe/go.mod h1:iLN1lG1MW0eREm+SikmPO8AZPz5NglBTEK/ErqkjGpo=
|
||||
github.com/btcsuite/btcwallet/wallet/txauthor v1.0.0/go.mod h1:VufDts7bd/zs3GV13f/lXc/0lXrPnvxD/NvmpG/FEKU=
|
||||
github.com/btcsuite/btcwallet/wallet/txauthor v1.0.1-0.20210329233242-e0607006dce6/go.mod h1:VufDts7bd/zs3GV13f/lXc/0lXrPnvxD/NvmpG/FEKU=
|
||||
github.com/btcsuite/btcwallet/wallet/txauthor v1.1.0 h1:8pO0pvPX1rFRfRiol4oV6kX7dY5y4chPwhfVwUfvwtk=
|
||||
github.com/btcsuite/btcwallet/wallet/txauthor v1.1.0/go.mod h1:ktYuJyumYtwG+QQ832Q+kqvxWJRAei3Nqs5qhSn4nww=
|
||||
github.com/btcsuite/btcwallet/wallet/txrules v1.0.0/go.mod h1:UwQE78yCerZ313EXZwEiu3jNAtfXj2n2+c8RWiE/WNA=
|
||||
github.com/btcsuite/btcwallet/wallet/txrules v1.1.0 h1:Vg8G8zhNVjaCdwJg2QOmLoWn4RTP7K0J9xlwY8CJnLY=
|
||||
github.com/btcsuite/btcwallet/wallet/txrules v1.1.0/go.mod h1:Zn9UTqpiTH+HOd5BLzSBzULzlOPmcoeyQIA0cp0WbQQ=
|
||||
github.com/btcsuite/btcwallet/wallet/txsizes v1.0.0/go.mod h1:pauEU8UuMFiThe5PB3EO+gO5kx87Me5NvdQDsTuq6cs=
|
||||
github.com/btcsuite/btcwallet/wallet/txsizes v1.0.1-0.20210519225359-6ab9b615576f/go.mod h1:pauEU8UuMFiThe5PB3EO+gO5kx87Me5NvdQDsTuq6cs=
|
||||
github.com/btcsuite/btcwallet/wallet/txsizes v1.1.0 h1:wZnOolEAeNOHzHTnznw/wQv+j35ftCIokNrnOTOU5o8=
|
||||
github.com/btcsuite/btcwallet/wallet/txsizes v1.1.0/go.mod h1:pauEU8UuMFiThe5PB3EO+gO5kx87Me5NvdQDsTuq6cs=
|
||||
github.com/btcsuite/btcwallet/walletdb v1.3.4/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU=
|
||||
|
|
|
@ -1118,7 +1118,7 @@ func (w *WalletKit) FundPsbt(_ context.Context,
|
|||
// lock any coins but might still change the wallet DB by
|
||||
// generating a new change address.
|
||||
changeIndex, err = w.cfg.Wallet.FundPsbt(
|
||||
packet, feeSatPerKW, account,
|
||||
packet, minConfs, feeSatPerKW, account,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("wallet couldn't fund PSBT: %v", err)
|
||||
|
|
|
@ -169,7 +169,7 @@ func (w *WalletController) ListLeasedOutputs() ([]*wtxmgr.LockedOutput, error) {
|
|||
}
|
||||
|
||||
// FundPsbt currently does nothing.
|
||||
func (w *WalletController) FundPsbt(_ *psbt.Packet,
|
||||
func (w *WalletController) FundPsbt(_ *psbt.Packet, _ int32,
|
||||
_ chainfee.SatPerKWeight, _ string) (int32, error) {
|
||||
|
||||
return 0, nil
|
||||
|
|
|
@ -642,7 +642,8 @@ func (b *BtcWallet) ImportPublicKey(pubKey *btcec.PublicKey,
|
|||
//
|
||||
// This is a part of the WalletController interface.
|
||||
func (b *BtcWallet) SendOutputs(outputs []*wire.TxOut,
|
||||
feeRate chainfee.SatPerKWeight, minConfs int32, label string) (*wire.MsgTx, error) {
|
||||
feeRate chainfee.SatPerKWeight, minConfs int32,
|
||||
label string) (*wire.MsgTx, error) {
|
||||
|
||||
// Convert our fee rate from sat/kw to sat/kb since it's required by
|
||||
// SendOutputs.
|
||||
|
@ -1080,7 +1081,7 @@ func (b *BtcWallet) ListTransactionDetails(startHeight, endHeight int32,
|
|||
// responsibility to lock the inputs before handing them out.
|
||||
//
|
||||
// This is a part of the WalletController interface.
|
||||
func (b *BtcWallet) FundPsbt(packet *psbt.Packet,
|
||||
func (b *BtcWallet) FundPsbt(packet *psbt.Packet, minConfs int32,
|
||||
feeRate chainfee.SatPerKWeight, accountName string) (int32, error) {
|
||||
|
||||
// The fee rate is passed in using units of sat/kw, so we'll convert
|
||||
|
@ -1115,7 +1116,7 @@ func (b *BtcWallet) FundPsbt(packet *psbt.Packet,
|
|||
// Let the wallet handle coin selection and/or fee estimation based on
|
||||
// the partial TX information in the packet.
|
||||
return b.wallet.FundPsbt(
|
||||
packet, keyScope, accountNum, feeSatPerKB,
|
||||
packet, keyScope, minConfs, accountNum, feeSatPerKB,
|
||||
b.cfg.CoinSelectionStrategy,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -350,8 +350,8 @@ type WalletController interface {
|
|||
// fee rate, an error is returned. No lock lease is acquired for any of
|
||||
// the selected/validated inputs. It is in the caller's responsibility
|
||||
// to lock the inputs before handing them out.
|
||||
FundPsbt(packet *psbt.Packet, feeRate chainfee.SatPerKWeight,
|
||||
account string) (int32, error)
|
||||
FundPsbt(packet *psbt.Packet, minConfs int32,
|
||||
feeRate chainfee.SatPerKWeight, account string) (int32, error)
|
||||
|
||||
// FinalizePsbt expects a partial transaction with all inputs and
|
||||
// outputs fully declared and tries to sign all inputs that belong to
|
||||
|
|
|
@ -11,16 +11,20 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/btcsuite/btcutil/hdkeychain"
|
||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||
btcwallet "github.com/btcsuite/btcwallet/wallet"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
"github.com/lightningnetwork/lnd/lncfg"
|
||||
"github.com/lightningnetwork/lnd/lnrpc/signrpc"
|
||||
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
|
||||
"github.com/lightningnetwork/lnd/lnwallet"
|
||||
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||
"github.com/lightningnetwork/lnd/lnwallet/chanfunding"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/macaroons"
|
||||
"google.golang.org/grpc"
|
||||
|
@ -139,6 +143,26 @@ func (r *RPCKeyRing) NewAddress(addrType lnwallet.AddressType, change bool,
|
|||
return localAddr, nil
|
||||
}
|
||||
|
||||
// LastUnusedAddress returns the last *unused* address known by the wallet. An
|
||||
// address is unused if it hasn't received any payments. This can be useful in
|
||||
// UIs in order to continually show the "freshest" address without having to
|
||||
// worry about "address inflation" caused by continual refreshing. Similar to
|
||||
// NewAddress it can derive a specified address type, and also optionally a
|
||||
// change address. The account parameter must be non-empty as it determines
|
||||
// which account the address should be generated from.
|
||||
func (r *RPCKeyRing) LastUnusedAddress(lnwallet.AddressType,
|
||||
string) (btcutil.Address, error) {
|
||||
|
||||
// Because the underlying wallet will create a new address if the last
|
||||
// derived address has been used in the meantime, we would need to proxy
|
||||
// that call as well. But since that's deep within the btcwallet code,
|
||||
// we cannot easily proxy it without more refactoring. Since this is an
|
||||
// address type that is probably not widely used we can probably get
|
||||
// away with not supporting it.
|
||||
return nil, fmt.Errorf("unused address types are not supported when " +
|
||||
"remote signing is enabled")
|
||||
}
|
||||
|
||||
// ImportAccount imports an account backed by an account extended public key.
|
||||
// The master key fingerprint denotes the fingerprint of the root key
|
||||
// corresponding to the account public key (also known as the key with
|
||||
|
@ -265,6 +289,67 @@ func (r *RPCKeyRing) ImportPublicKey(pubKey *btcec.PublicKey,
|
|||
return nil
|
||||
}
|
||||
|
||||
// SendOutputs funds, signs, and broadcasts a Bitcoin transaction paying out to
|
||||
// the specified outputs. In the case the wallet has insufficient funds, or the
|
||||
// outputs are non-standard, a non-nil error will be returned.
|
||||
//
|
||||
// NOTE: This method requires the global coin selection lock to be held.
|
||||
//
|
||||
// This is a part of the WalletController interface.
|
||||
func (r *RPCKeyRing) SendOutputs(outputs []*wire.TxOut,
|
||||
feeRate chainfee.SatPerKWeight, minConfs int32,
|
||||
label string) (*wire.MsgTx, error) {
|
||||
|
||||
tx, err := r.WalletController.SendOutputs(
|
||||
outputs, feeRate, minConfs, label,
|
||||
)
|
||||
if err != nil && err != btcwallet.ErrTxUnsigned {
|
||||
return nil, err
|
||||
}
|
||||
if err == nil {
|
||||
// This shouldn't happen since our wallet controller is watch-
|
||||
// only and can't sign the TX.
|
||||
return tx, nil
|
||||
}
|
||||
|
||||
// We know at this point that we only have inputs from our own wallet.
|
||||
// So we can just compute the input script using the remote signer.
|
||||
signDesc := input.SignDescriptor{
|
||||
HashType: txscript.SigHashAll,
|
||||
SigHashes: txscript.NewTxSigHashes(tx),
|
||||
}
|
||||
for i, txIn := range tx.TxIn {
|
||||
// We can only sign this input if it's ours, so we'll ask the
|
||||
// watch-only wallet if it can map this outpoint into a coin we
|
||||
// own. If not, then we can't continue because our wallet state
|
||||
// is out of sync.
|
||||
info, err := r.coinFromOutPoint(txIn.PreviousOutPoint)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error looking up utxo: %v", err)
|
||||
}
|
||||
|
||||
// Now that we know the input is ours, we'll populate the
|
||||
// signDesc with the per input unique information.
|
||||
signDesc.Output = &wire.TxOut{
|
||||
Value: info.Value,
|
||||
PkScript: info.PkScript,
|
||||
}
|
||||
signDesc.InputIndex = i
|
||||
|
||||
// Finally, we'll sign the input as is, and populate the input
|
||||
// with the witness and sigScript (if needed).
|
||||
inputScript, err := r.ComputeInputScript(tx, &signDesc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
txIn.SignatureScript = inputScript.SigScript
|
||||
txIn.Witness = inputScript.Witness
|
||||
}
|
||||
|
||||
return tx, r.WalletController.PublishTransaction(tx, label)
|
||||
}
|
||||
|
||||
// DeriveNextKey attempts to derive the *next* key within the key family
|
||||
// (account in BIP43) specified. This method should return the next external
|
||||
// child within this branch.
|
||||
|
@ -534,6 +619,26 @@ func (r *RPCKeyRing) ComputeInputScript(tx *wire.MsgTx,
|
|||
}, nil
|
||||
}
|
||||
|
||||
// coinFromOutPoint attempts to locate details pertaining to a coin based on
|
||||
// its outpoint. If the coin isn't under the control of the backing watch-only
|
||||
// wallet, then an error is returned.
|
||||
func (r *RPCKeyRing) coinFromOutPoint(op wire.OutPoint) (*chanfunding.Coin,
|
||||
error) {
|
||||
|
||||
inputInfo, err := r.WalletController.FetchInputInfo(&op)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &chanfunding.Coin{
|
||||
TxOut: wire.TxOut{
|
||||
Value: int64(inputInfo.Value),
|
||||
PkScript: inputInfo.PkScript,
|
||||
},
|
||||
OutPoint: inputInfo.OutPoint,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// toRPCSignReq converts the given raw transaction and sign descriptors into
|
||||
// their corresponding RPC counterparts.
|
||||
func toRPCSignReq(tx *wire.MsgTx,
|
||||
|
|
|
@ -1037,7 +1037,7 @@ func (r *rpcServer) sendCoinsOnChain(paymentMap map[string]int64,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// If that checks out, we're failry confident that creating sending to
|
||||
// If that checks out, we're fairly confident that creating sending to
|
||||
// these outputs will keep the wallet balance above the reserve.
|
||||
tx, err := r.server.cc.Wallet.SendOutputs(
|
||||
outputs, feeRate, minConfs, label,
|
||||
|
|
Loading…
Add table
Reference in a new issue