Merge pull request #6341 from guggero/remote-signer-signoutputraw
remote signer: fix SignOutputRaw RPC for incomplete key info, fix healthcheck connection leak
This commit is contained in:
12 files changed
+363
-6
No files matched your search
@@ -543,6 +543,16 @@ func (b *BtcWallet) IsOurAddress(a btcutil.Address) bool {
|
||||
return result && (err == nil)
|
||||
}
|
||||
|
||||
// AddressInfo returns the information about an address, if it's known to this
|
||||
// wallet.
|
||||
//
|
||||
// NOTE: This is a part of the WalletController interface.
|
||||
func (b *BtcWallet) AddressInfo(a btcutil.Address) (waddrmgr.ManagedAddress,
|
||||
error) {
|
||||
|
||||
return b.wallet.AddressInfo(a)
|
||||
}
|
||||
|
||||
// ListAccounts retrieves all accounts belonging to the wallet by default. A
|
||||
// name and key scope filter can be provided to filter through all of the wallet
|
||||
// accounts and return only those matching.
|
||||
|
||||
@@ -216,6 +216,10 @@ type WalletController interface {
|
||||
// IsOurAddress checks if the passed address belongs to this wallet
|
||||
IsOurAddress(a btcutil.Address) bool
|
||||
|
||||
// AddressInfo returns the information about an address, if it's known
|
||||
// to this wallet.
|
||||
AddressInfo(a btcutil.Address) (waddrmgr.ManagedAddress, error)
|
||||
|
||||
// ListAccounts retrieves all accounts belonging to the wallet by
|
||||
// default. A name and key scope filter can be provided to filter
|
||||
// through all of the wallet accounts and return only those matching.
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
// configuration.
|
||||
func HealthCheck(cfg *lncfg.RemoteSigner, timeout time.Duration) func() error {
|
||||
return func() error {
|
||||
_, err := connectRPC(
|
||||
conn, err := connectRPC(
|
||||
cfg.RPCHost, cfg.TLSCertPath, cfg.MacaroonPath, timeout,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -19,6 +19,15 @@ func HealthCheck(cfg *lncfg.RemoteSigner, timeout time.Duration) func() error {
|
||||
"signing node through RPC: %v", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err = conn.Close()
|
||||
if err != nil {
|
||||
log.Warnf("Failed to close health check "+
|
||||
"connection to remote signing node: %v",
|
||||
err)
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,10 @@ import (
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/btcutil/hdkeychain"
|
||||
"github.com/btcsuite/btcd/btcutil/psbt"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||
basewallet "github.com/btcsuite/btcwallet/wallet"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
@@ -51,7 +53,7 @@ type RPCKeyRing struct {
|
||||
|
||||
watchOnlyKeyRing keychain.SecretKeyRing
|
||||
|
||||
coinType uint32
|
||||
netParams *chaincfg.Params
|
||||
|
||||
rpcTimeout time.Duration
|
||||
|
||||
@@ -69,7 +71,8 @@ var _ lnwallet.WalletController = (*RPCKeyRing)(nil)
|
||||
// delegates any signing or ECDH operations to the remove signer through RPC.
|
||||
func NewRPCKeyRing(watchOnlyKeyRing keychain.SecretKeyRing,
|
||||
watchOnlyWalletController lnwallet.WalletController,
|
||||
remoteSigner *lncfg.RemoteSigner, coinType uint32) (*RPCKeyRing, error) {
|
||||
remoteSigner *lncfg.RemoteSigner,
|
||||
netParams *chaincfg.Params) (*RPCKeyRing, error) {
|
||||
|
||||
rpcConn, err := connectRPC(
|
||||
remoteSigner.RPCHost, remoteSigner.TLSCertPath,
|
||||
@@ -83,7 +86,7 @@ func NewRPCKeyRing(watchOnlyKeyRing keychain.SecretKeyRing,
|
||||
return &RPCKeyRing{
|
||||
WalletController: watchOnlyWalletController,
|
||||
watchOnlyKeyRing: watchOnlyKeyRing,
|
||||
coinType: coinType,
|
||||
netParams: netParams,
|
||||
rpcTimeout: remoteSigner.Timeout,
|
||||
signerClient: signrpc.NewSignerClient(rpcConn),
|
||||
walletClient: walletrpc.NewWalletKitClient(rpcConn),
|
||||
@@ -583,6 +586,79 @@ func (r *RPCKeyRing) remoteSign(tx *wire.MsgTx, signDesc *input.SignDescriptor,
|
||||
in := &packet.Inputs[signDesc.InputIndex]
|
||||
txIn := tx.TxIn[signDesc.InputIndex]
|
||||
|
||||
// Things are a bit tricky with the sign descriptor. There basically are
|
||||
// four ways to describe a key:
|
||||
// 1. By public key only. To match this case both family and index
|
||||
// must be set to 0.
|
||||
// 2. By family and index only. To match this case the public key
|
||||
// must be nil and either the family or index must be non-zero.
|
||||
// 3. All values are set and locator is non-empty. To match this case
|
||||
// the public key must be set and either the family or index must
|
||||
// be non-zero.
|
||||
// 4. All values are set and locator is empty. This is a special case
|
||||
// for the very first channel ever created (with the multi-sig key
|
||||
// family which is 0 and the index which is 0 as well). This looks
|
||||
// identical to case 1 and will also be handled like that case.
|
||||
// We only really handle case 1 and 2 here, since 3 is no problem and 4
|
||||
// is identical to 1.
|
||||
switch {
|
||||
// Case 1: Public key only. We need to find out the derivation path for
|
||||
// this public key by asking the wallet. This is only possible for our
|
||||
// internal, custom 1017 scope since we know all keys derived there are
|
||||
// internally stored as p2wkh addresses.
|
||||
case signDesc.KeyDesc.PubKey != nil && signDesc.KeyDesc.IsEmpty():
|
||||
pubKeyBytes := signDesc.KeyDesc.PubKey.SerializeCompressed()
|
||||
addr, err := btcutil.NewAddressWitnessPubKeyHash(
|
||||
btcutil.Hash160(pubKeyBytes), r.netParams,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error deriving address from "+
|
||||
"public key %x: %v", pubKeyBytes, err)
|
||||
}
|
||||
|
||||
managedAddr, err := r.AddressInfo(addr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching address info "+
|
||||
"for public key %x: %v", pubKeyBytes, err)
|
||||
}
|
||||
|
||||
pubKeyAddr, ok := managedAddr.(waddrmgr.ManagedPubKeyAddress)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("address derived for public "+
|
||||
"key %x is not a p2wkh address", pubKeyBytes)
|
||||
}
|
||||
|
||||
scope, path, _ := pubKeyAddr.DerivationInfo()
|
||||
if scope.Purpose != keychain.BIP0043Purpose {
|
||||
return nil, fmt.Errorf("address derived for public "+
|
||||
"key %x is not in custom key scope %d'",
|
||||
pubKeyBytes, keychain.BIP0043Purpose)
|
||||
}
|
||||
|
||||
// We now have all the information we need to complete our key
|
||||
// locator information.
|
||||
signDesc.KeyDesc.KeyLocator = keychain.KeyLocator{
|
||||
Family: keychain.KeyFamily(path.InternalAccount),
|
||||
Index: path.Index,
|
||||
}
|
||||
|
||||
// Case 2: Family and index only. This case is easy, we can just go
|
||||
// ahead and derive the public key from the family and index and then
|
||||
// supply that information in the BIP32 derivation field.
|
||||
case signDesc.KeyDesc.PubKey == nil && !signDesc.KeyDesc.IsEmpty():
|
||||
fullDesc, err := r.watchOnlyKeyRing.DeriveKey(
|
||||
signDesc.KeyDesc.KeyLocator,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error deriving key with "+
|
||||
"family %d and index %d from the watch-only "+
|
||||
"wallet: %v",
|
||||
signDesc.KeyDesc.KeyLocator.Family,
|
||||
signDesc.KeyDesc.KeyLocator.Index, err)
|
||||
}
|
||||
signDesc.KeyDesc.PubKey = fullDesc.PubKey
|
||||
}
|
||||
|
||||
// Make sure we actually know about the input. We either have been
|
||||
// watching the UTXO on-chain or we have been given all the required
|
||||
// info in the sign descriptor.
|
||||
@@ -610,7 +686,8 @@ func (r *RPCKeyRing) remoteSign(tx *wire.MsgTx, signDesc *input.SignDescriptor,
|
||||
Bip32Path: []uint32{
|
||||
keychain.BIP0043Purpose +
|
||||
hdkeychain.HardenedKeyStart,
|
||||
r.coinType + hdkeychain.HardenedKeyStart,
|
||||
r.netParams.HDCoinType +
|
||||
hdkeychain.HardenedKeyStart,
|
||||
uint32(signDesc.KeyDesc.Family) +
|
||||
hdkeychain.HardenedKeyStart,
|
||||
0,
|
||||
|
||||
Reference in new issue
Block a user