multi: forward SendCoins call over RPC

This commit is contained in:
Oliver Gugger 2021-10-14 15:42:52 +02:00
parent 19db382e24
commit a3addcc927
No known key found for this signature in database
GPG key ID: 8E4256593F177720
8 changed files with 117 additions and 14 deletions

3
go.mod
View file

@ -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
View file

@ -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=

View file

@ -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)

View file

@ -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

View file

@ -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,
)
}

View file

@ -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

View file

@ -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,

View file

@ -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,