2016-08-13 00:29:38 +02:00
|
|
|
package btcwallet
|
|
|
|
|
|
|
|
import (
|
2022-01-05 11:04:19 +01:00
|
|
|
"fmt"
|
|
|
|
|
2022-02-23 14:48:00 +01:00
|
|
|
"github.com/btcsuite/btcd/btcec/v2"
|
|
|
|
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
|
|
|
|
"github.com/btcsuite/btcd/btcutil"
|
|
|
|
"github.com/btcsuite/btcd/btcutil/hdkeychain"
|
2018-06-05 03:34:16 +02:00
|
|
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
|
|
"github.com/btcsuite/btcd/txscript"
|
|
|
|
"github.com/btcsuite/btcd/wire"
|
|
|
|
"github.com/btcsuite/btcwallet/waddrmgr"
|
|
|
|
"github.com/btcsuite/btcwallet/walletdb"
|
2019-01-16 15:47:43 +01:00
|
|
|
"github.com/lightningnetwork/lnd/input"
|
2018-07-31 09:17:17 +02:00
|
|
|
"github.com/lightningnetwork/lnd/keychain"
|
|
|
|
"github.com/lightningnetwork/lnd/lnwallet"
|
2016-08-13 00:29:38 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// FetchInputInfo queries for the WalletController's knowledge of the passed
|
|
|
|
// outpoint. If the base wallet determines this output is under its control,
|
|
|
|
// then the original txout should be returned. Otherwise, a non-nil error value
|
|
|
|
// of ErrNotMine should be returned instead.
|
|
|
|
//
|
|
|
|
// This is a part of the WalletController interface.
|
2019-08-19 23:26:36 +02:00
|
|
|
func (b *BtcWallet) FetchInputInfo(prevOut *wire.OutPoint) (*lnwallet.Utxo, error) {
|
2022-01-05 11:04:20 +01:00
|
|
|
prevTx, txOut, bip32, confirmations, err := b.wallet.FetchInputInfo(
|
|
|
|
prevOut,
|
|
|
|
)
|
2016-08-13 00:29:38 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2018-11-10 02:38:01 +01:00
|
|
|
}
|
2016-08-13 00:29:38 +02:00
|
|
|
|
2019-08-19 23:26:36 +02:00
|
|
|
// Then, we'll populate all of the information required by the struct.
|
|
|
|
addressType := lnwallet.UnknownAddressType
|
|
|
|
switch {
|
2020-10-01 16:21:42 +02:00
|
|
|
case txscript.IsPayToWitnessPubKeyHash(txOut.PkScript):
|
2019-08-19 23:26:36 +02:00
|
|
|
addressType = lnwallet.WitnessPubKey
|
2020-10-01 16:21:42 +02:00
|
|
|
case txscript.IsPayToScriptHash(txOut.PkScript):
|
2019-08-19 23:26:36 +02:00
|
|
|
addressType = lnwallet.NestedWitnessPubKey
|
|
|
|
}
|
|
|
|
|
|
|
|
return &lnwallet.Utxo{
|
2020-10-01 16:21:42 +02:00
|
|
|
AddressType: addressType,
|
|
|
|
Value: btcutil.Amount(txOut.Value),
|
|
|
|
PkScript: txOut.PkScript,
|
|
|
|
Confirmations: confirmations,
|
2019-08-19 23:26:36 +02:00
|
|
|
OutPoint: *prevOut,
|
2022-01-05 11:04:20 +01:00
|
|
|
Derivation: bip32,
|
|
|
|
PrevTx: prevTx,
|
2019-08-19 23:26:36 +02:00
|
|
|
}, nil
|
2016-08-13 00:29:38 +02:00
|
|
|
}
|
|
|
|
|
2022-01-05 11:04:16 +01:00
|
|
|
// ScriptForOutput returns the address, witness program and redeem script for a
|
|
|
|
// given UTXO. An error is returned if the UTXO does not belong to our wallet or
|
|
|
|
// it is not a managed pubKey address.
|
|
|
|
func (b *BtcWallet) ScriptForOutput(output *wire.TxOut) (
|
|
|
|
waddrmgr.ManagedPubKeyAddress, []byte, []byte, error) {
|
|
|
|
|
|
|
|
return b.wallet.ScriptForOutput(output)
|
|
|
|
}
|
|
|
|
|
lnwallet: when signing create account if not found
In this commit, we address an edge case that can happen a user rescans
w/ their seed, while retaining their existing `channel.db`. Once they
rescan, if they go to sign for a channel sweep for example, the
commitment key family (actually an account) may not yet have been
created, causing the signing attempt to fail.
We remedy this always creating the account if we go to sign, and the
account isn't found. The change has been structured to make this the
exception, so we'll avoid always needing to do 2 DB hits (check if
account exists, sign), each time we sign.
A new test has been added to exercise this behavior. If the diff from
the `signer.go` file is removed, then the test will fail.
2019-08-21 04:08:03 +02:00
|
|
|
// deriveFromKeyLoc attempts to derive a private key using a fully specified
|
|
|
|
// KeyLocator.
|
|
|
|
func deriveFromKeyLoc(scopedMgr *waddrmgr.ScopedKeyManager,
|
|
|
|
addrmgrNs walletdb.ReadWriteBucket,
|
|
|
|
keyLoc keychain.KeyLocator) (*btcec.PrivateKey, error) {
|
|
|
|
|
|
|
|
path := waddrmgr.DerivationPath{
|
2021-03-09 23:12:26 +01:00
|
|
|
InternalAccount: uint32(keyLoc.Family),
|
|
|
|
Account: uint32(keyLoc.Family),
|
|
|
|
Branch: 0,
|
|
|
|
Index: keyLoc.Index,
|
lnwallet: when signing create account if not found
In this commit, we address an edge case that can happen a user rescans
w/ their seed, while retaining their existing `channel.db`. Once they
rescan, if they go to sign for a channel sweep for example, the
commitment key family (actually an account) may not yet have been
created, causing the signing attempt to fail.
We remedy this always creating the account if we go to sign, and the
account isn't found. The change has been structured to make this the
exception, so we'll avoid always needing to do 2 DB hits (check if
account exists, sign), each time we sign.
A new test has been added to exercise this behavior. If the diff from
the `signer.go` file is removed, then the test will fail.
2019-08-21 04:08:03 +02:00
|
|
|
}
|
|
|
|
addr, err := scopedMgr.DeriveFromKeyPath(addrmgrNs, path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return addr.(waddrmgr.ManagedPubKeyAddress).PrivKey()
|
|
|
|
}
|
|
|
|
|
2022-01-05 11:04:19 +01:00
|
|
|
// deriveKeyByBIP32Path derives a key described by a BIP32 path. We expect the
|
|
|
|
// first three elements of the path to be hardened according to BIP44, so they
|
|
|
|
// must be a number >= 2^31.
|
|
|
|
func (b *BtcWallet) deriveKeyByBIP32Path(path []uint32) (*btcec.PrivateKey,
|
|
|
|
error) {
|
|
|
|
|
|
|
|
// Make sure we get a full path with exactly 5 elements. A path is
|
|
|
|
// either custom purpose one with 4 dynamic and one static elements:
|
|
|
|
// m/1017'/coinType'/keyFamily'/0/index
|
|
|
|
// Or a default BIP49/89 one with 5 elements:
|
|
|
|
// m/purpose'/coinType'/account'/change/index
|
|
|
|
const expectedDerivationPathDepth = 5
|
|
|
|
if len(path) != expectedDerivationPathDepth {
|
|
|
|
return nil, fmt.Errorf("invalid BIP32 derivation path, "+
|
|
|
|
"expected path length %d, instead was %d",
|
|
|
|
expectedDerivationPathDepth, len(path))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Assert that the first three parts of the path are actually hardened
|
|
|
|
// to avoid under-flowing the uint32 type.
|
|
|
|
if err := assertHardened(path[0], path[1], path[2]); err != nil {
|
|
|
|
return nil, fmt.Errorf("invalid BIP32 derivation path, "+
|
|
|
|
"expected first three elements to be hardened: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
purpose := path[0] - hdkeychain.HardenedKeyStart
|
|
|
|
coinType := path[1] - hdkeychain.HardenedKeyStart
|
|
|
|
account := path[2] - hdkeychain.HardenedKeyStart
|
|
|
|
change, index := path[3], path[4]
|
|
|
|
|
|
|
|
// Is this a custom lnd internal purpose key?
|
|
|
|
switch purpose {
|
|
|
|
case keychain.BIP0043Purpose:
|
|
|
|
// Make sure it's for the same coin type as our wallet's
|
|
|
|
// keychain scope.
|
|
|
|
if coinType != b.chainKeyScope.Coin {
|
|
|
|
return nil, fmt.Errorf("invalid BIP32 derivation "+
|
|
|
|
"path, expected coin type %d, instead was %d",
|
|
|
|
b.chainKeyScope.Coin, coinType)
|
|
|
|
}
|
|
|
|
|
|
|
|
return b.deriveKeyByLocator(keychain.KeyLocator{
|
|
|
|
Family: keychain.KeyFamily(account),
|
|
|
|
Index: index,
|
|
|
|
})
|
|
|
|
|
|
|
|
// Is it a standard, BIP defined purpose that the wallet understands?
|
|
|
|
case waddrmgr.KeyScopeBIP0044.Purpose,
|
|
|
|
waddrmgr.KeyScopeBIP0049Plus.Purpose,
|
|
|
|
waddrmgr.KeyScopeBIP0084.Purpose:
|
|
|
|
|
|
|
|
// We're going to continue below the switch statement to avoid
|
|
|
|
// unnecessary indentation for this default case.
|
|
|
|
|
|
|
|
// Currently, there is no way to import any other key scopes than the
|
|
|
|
// one custom purpose or three standard ones into lnd's wallet. So we
|
|
|
|
// shouldn't accept any other scopes to sign for.
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("invalid BIP32 derivation path, "+
|
|
|
|
"unknown purpose %d", purpose)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Okay, we made sure it's a BIP49/84 key, so we need to derive it now.
|
|
|
|
// Interestingly, the btcwallet never actually uses a coin type other
|
|
|
|
// than 0 for those keys, so we need to make sure this behavior is
|
|
|
|
// replicated here.
|
|
|
|
if coinType != 0 {
|
|
|
|
return nil, fmt.Errorf("invalid BIP32 derivation path, coin " +
|
|
|
|
"type must be 0 for BIP49/84 btcwallet keys")
|
|
|
|
}
|
|
|
|
|
|
|
|
// We only expect to be asked to sign with key scopes that we know
|
|
|
|
// about. So if the scope doesn't exist, we don't create it.
|
|
|
|
scope := waddrmgr.KeyScope{
|
|
|
|
Purpose: purpose,
|
|
|
|
Coin: coinType,
|
|
|
|
}
|
|
|
|
scopedMgr, err := b.wallet.Manager.FetchScopedKeyManager(scope)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("error fetching manager for scope %v: "+
|
|
|
|
"%w", scope, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Let's see if we can hit the private key cache.
|
|
|
|
keyPath := waddrmgr.DerivationPath{
|
|
|
|
InternalAccount: account,
|
|
|
|
Account: account,
|
|
|
|
Branch: change,
|
|
|
|
Index: index,
|
|
|
|
}
|
|
|
|
privKey, err := scopedMgr.DeriveFromKeyPathCache(keyPath)
|
|
|
|
if err == nil {
|
|
|
|
return privKey, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// The key wasn't in the cache, let's fully derive it now.
|
|
|
|
err = walletdb.View(b.db, func(tx walletdb.ReadTx) error {
|
|
|
|
addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
|
|
|
|
|
|
|
|
addr, err := scopedMgr.DeriveFromKeyPath(addrmgrNs, keyPath)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error deriving private key: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
privKey, err = addr.(waddrmgr.ManagedPubKeyAddress).PrivKey()
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("error deriving key from path %#v: %w",
|
|
|
|
keyPath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return privKey, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// assertHardened makes sure each given element is >= 2^31.
|
|
|
|
func assertHardened(elements ...uint32) error {
|
|
|
|
for idx, element := range elements {
|
|
|
|
if element < hdkeychain.HardenedKeyStart {
|
|
|
|
return fmt.Errorf("element at index %d is not hardened",
|
|
|
|
idx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
lnwallet: fix key derivation for very first key in family
In this commit, we fix an existing bug that would cause us to be unable
to derive the very first key in a key family if the wallet hadn't
already derived it in the past. This can happen if a user keeps their
same `channel.db`, but restores their wallet resulting in fresh
`wallet.db` state.
This is an existing issue due to the fact that we don't properly
distinguish between an empty key locator, and the very first key in a
`KeyFamily`: `(0, 0)`. Atm, `KeyLoactor{0, 0}.IsEmpty() == True`,
causing us to be unable to retrieve this key in certain cases since we
fall through and attempt address based derivation.
In order to remedy this, we add a new special case (until we upgrade
`KeyLoactor` formats, but needed for legacy reasons) to _try_ a regular
`KeyLoactor` based derivation if we fail to derive via address, and this
is an "empty" key loc. This has been tested in the field and shown to
work, with the one downside that in this "hot swap restoration" case,
we'll hit the database twice to derive the key.
2019-07-09 01:58:54 +02:00
|
|
|
// deriveKeyByLocator attempts to derive a key stored in the wallet given a
|
|
|
|
// valid key locator.
|
2022-01-05 11:04:14 +01:00
|
|
|
func (b *BtcWallet) deriveKeyByLocator(
|
|
|
|
keyLoc keychain.KeyLocator) (*btcec.PrivateKey, error) {
|
|
|
|
|
lnwallet: fix key derivation for very first key in family
In this commit, we fix an existing bug that would cause us to be unable
to derive the very first key in a key family if the wallet hadn't
already derived it in the past. This can happen if a user keeps their
same `channel.db`, but restores their wallet resulting in fresh
`wallet.db` state.
This is an existing issue due to the fact that we don't properly
distinguish between an empty key locator, and the very first key in a
`KeyFamily`: `(0, 0)`. Atm, `KeyLoactor{0, 0}.IsEmpty() == True`,
causing us to be unable to retrieve this key in certain cases since we
fall through and attempt address based derivation.
In order to remedy this, we add a new special case (until we upgrade
`KeyLoactor` formats, but needed for legacy reasons) to _try_ a regular
`KeyLoactor` based derivation if we fail to derive via address, and this
is an "empty" key loc. This has been tested in the field and shown to
work, with the one downside that in this "hot swap restoration" case,
we'll hit the database twice to derive the key.
2019-07-09 01:58:54 +02:00
|
|
|
// We'll assume the special lightning key scope in this case.
|
|
|
|
scopedMgr, err := b.wallet.Manager.FetchScopedKeyManager(
|
|
|
|
b.chainKeyScope,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-08-14 01:30:13 +02:00
|
|
|
// First try to read the key from the cached store, if this fails, then
|
|
|
|
// we'll fall through to the method below that requires a database
|
|
|
|
// transaction.
|
|
|
|
path := waddrmgr.DerivationPath{
|
|
|
|
InternalAccount: uint32(keyLoc.Family),
|
|
|
|
Account: uint32(keyLoc.Family),
|
|
|
|
Branch: 0,
|
|
|
|
Index: keyLoc.Index,
|
|
|
|
}
|
|
|
|
privKey, err := scopedMgr.DeriveFromKeyPathCache(path)
|
|
|
|
if err == nil {
|
|
|
|
return privKey, nil
|
|
|
|
}
|
|
|
|
|
lnwallet: fix key derivation for very first key in family
In this commit, we fix an existing bug that would cause us to be unable
to derive the very first key in a key family if the wallet hadn't
already derived it in the past. This can happen if a user keeps their
same `channel.db`, but restores their wallet resulting in fresh
`wallet.db` state.
This is an existing issue due to the fact that we don't properly
distinguish between an empty key locator, and the very first key in a
`KeyFamily`: `(0, 0)`. Atm, `KeyLoactor{0, 0}.IsEmpty() == True`,
causing us to be unable to retrieve this key in certain cases since we
fall through and attempt address based derivation.
In order to remedy this, we add a new special case (until we upgrade
`KeyLoactor` formats, but needed for legacy reasons) to _try_ a regular
`KeyLoactor` based derivation if we fail to derive via address, and this
is an "empty" key loc. This has been tested in the field and shown to
work, with the one downside that in this "hot swap restoration" case,
we'll hit the database twice to derive the key.
2019-07-09 01:58:54 +02:00
|
|
|
var key *btcec.PrivateKey
|
lnwallet: when signing create account if not found
In this commit, we address an edge case that can happen a user rescans
w/ their seed, while retaining their existing `channel.db`. Once they
rescan, if they go to sign for a channel sweep for example, the
commitment key family (actually an account) may not yet have been
created, causing the signing attempt to fail.
We remedy this always creating the account if we go to sign, and the
account isn't found. The change has been structured to make this the
exception, so we'll avoid always needing to do 2 DB hits (check if
account exists, sign), each time we sign.
A new test has been added to exercise this behavior. If the diff from
the `signer.go` file is removed, then the test will fail.
2019-08-21 04:08:03 +02:00
|
|
|
err = walletdb.Update(b.db, func(tx walletdb.ReadWriteTx) error {
|
|
|
|
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
|
|
|
|
|
|
|
key, err = deriveFromKeyLoc(scopedMgr, addrmgrNs, keyLoc)
|
|
|
|
if waddrmgr.IsError(err, waddrmgr.ErrAccountNotFound) {
|
|
|
|
// If we've reached this point, then the account
|
|
|
|
// doesn't yet exist, so we'll create it now to ensure
|
|
|
|
// we can sign.
|
|
|
|
acctErr := scopedMgr.NewRawAccount(
|
|
|
|
addrmgrNs, uint32(keyLoc.Family),
|
|
|
|
)
|
|
|
|
if acctErr != nil {
|
|
|
|
return acctErr
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now that we know the account exists, we'll attempt
|
|
|
|
// to re-derive the private key.
|
|
|
|
key, err = deriveFromKeyLoc(
|
|
|
|
scopedMgr, addrmgrNs, keyLoc,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
lnwallet: fix key derivation for very first key in family
In this commit, we fix an existing bug that would cause us to be unable
to derive the very first key in a key family if the wallet hadn't
already derived it in the past. This can happen if a user keeps their
same `channel.db`, but restores their wallet resulting in fresh
`wallet.db` state.
This is an existing issue due to the fact that we don't properly
distinguish between an empty key locator, and the very first key in a
`KeyFamily`: `(0, 0)`. Atm, `KeyLoactor{0, 0}.IsEmpty() == True`,
causing us to be unable to retrieve this key in certain cases since we
fall through and attempt address based derivation.
In order to remedy this, we add a new special case (until we upgrade
`KeyLoactor` formats, but needed for legacy reasons) to _try_ a regular
`KeyLoactor` based derivation if we fail to derive via address, and this
is an "empty" key loc. This has been tested in the field and shown to
work, with the one downside that in this "hot swap restoration" case,
we'll hit the database twice to derive the key.
2019-07-09 01:58:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return key, nil
|
|
|
|
}
|
|
|
|
|
2016-11-19 02:11:59 +01:00
|
|
|
// fetchPrivKey attempts to retrieve the raw private key corresponding to the
|
2018-02-18 00:10:51 +01:00
|
|
|
// passed public key if populated, or the key descriptor path (if non-empty).
|
2021-09-23 16:54:30 +02:00
|
|
|
func (b *BtcWallet) fetchPrivKey(
|
|
|
|
keyDesc *keychain.KeyDescriptor) (*btcec.PrivateKey, error) {
|
|
|
|
|
2018-02-18 00:10:51 +01:00
|
|
|
// If the key locator within the descriptor *isn't* empty, then we can
|
|
|
|
// directly derive the keys raw.
|
lnwallet: fix key derivation for very first key in family
In this commit, we fix an existing bug that would cause us to be unable
to derive the very first key in a key family if the wallet hadn't
already derived it in the past. This can happen if a user keeps their
same `channel.db`, but restores their wallet resulting in fresh
`wallet.db` state.
This is an existing issue due to the fact that we don't properly
distinguish between an empty key locator, and the very first key in a
`KeyFamily`: `(0, 0)`. Atm, `KeyLoactor{0, 0}.IsEmpty() == True`,
causing us to be unable to retrieve this key in certain cases since we
fall through and attempt address based derivation.
In order to remedy this, we add a new special case (until we upgrade
`KeyLoactor` formats, but needed for legacy reasons) to _try_ a regular
`KeyLoactor` based derivation if we fail to derive via address, and this
is an "empty" key loc. This has been tested in the field and shown to
work, with the one downside that in this "hot swap restoration" case,
we'll hit the database twice to derive the key.
2019-07-09 01:58:54 +02:00
|
|
|
emptyLocator := keyDesc.KeyLocator.IsEmpty()
|
2021-09-23 16:54:30 +02:00
|
|
|
if !emptyLocator || keyDesc.PubKey == nil {
|
lnwallet: fix key derivation for very first key in family
In this commit, we fix an existing bug that would cause us to be unable
to derive the very first key in a key family if the wallet hadn't
already derived it in the past. This can happen if a user keeps their
same `channel.db`, but restores their wallet resulting in fresh
`wallet.db` state.
This is an existing issue due to the fact that we don't properly
distinguish between an empty key locator, and the very first key in a
`KeyFamily`: `(0, 0)`. Atm, `KeyLoactor{0, 0}.IsEmpty() == True`,
causing us to be unable to retrieve this key in certain cases since we
fall through and attempt address based derivation.
In order to remedy this, we add a new special case (until we upgrade
`KeyLoactor` formats, but needed for legacy reasons) to _try_ a regular
`KeyLoactor` based derivation if we fail to derive via address, and this
is an "empty" key loc. This has been tested in the field and shown to
work, with the one downside that in this "hot swap restoration" case,
we'll hit the database twice to derive the key.
2019-07-09 01:58:54 +02:00
|
|
|
return b.deriveKeyByLocator(keyDesc.KeyLocator)
|
2018-02-18 00:10:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
hash160 := btcutil.Hash160(keyDesc.PubKey.SerializeCompressed())
|
2016-09-10 22:55:05 +02:00
|
|
|
addr, err := btcutil.NewAddressWitnessPubKeyHash(hash160, b.netParams)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
lnwallet: fix key derivation for very first key in family
In this commit, we fix an existing bug that would cause us to be unable
to derive the very first key in a key family if the wallet hadn't
already derived it in the past. This can happen if a user keeps their
same `channel.db`, but restores their wallet resulting in fresh
`wallet.db` state.
This is an existing issue due to the fact that we don't properly
distinguish between an empty key locator, and the very first key in a
`KeyFamily`: `(0, 0)`. Atm, `KeyLoactor{0, 0}.IsEmpty() == True`,
causing us to be unable to retrieve this key in certain cases since we
fall through and attempt address based derivation.
In order to remedy this, we add a new special case (until we upgrade
`KeyLoactor` formats, but needed for legacy reasons) to _try_ a regular
`KeyLoactor` based derivation if we fail to derive via address, and this
is an "empty" key loc. This has been tested in the field and shown to
work, with the one downside that in this "hot swap restoration" case,
we'll hit the database twice to derive the key.
2019-07-09 01:58:54 +02:00
|
|
|
// Otherwise, we'll attempt to derive the key based on the address.
|
|
|
|
// This will only work if we've already derived this address in the
|
|
|
|
// past, since the wallet relies on a mapping of addr -> key.
|
|
|
|
key, err := b.wallet.PrivKeyForAddress(addr)
|
|
|
|
switch {
|
|
|
|
// If we didn't find this key in the wallet, then there's a chance that
|
|
|
|
// this is actually an "empty" key locator. The legacy KeyLocator
|
|
|
|
// format failed to properly distinguish an empty key locator from the
|
|
|
|
// very first in the index (0, 0).IsEmpty() == true.
|
|
|
|
case waddrmgr.IsError(err, waddrmgr.ErrAddressNotFound) && emptyLocator:
|
|
|
|
return b.deriveKeyByLocator(keyDesc.KeyLocator)
|
|
|
|
|
|
|
|
case err != nil:
|
|
|
|
return nil, err
|
|
|
|
|
|
|
|
default:
|
|
|
|
return key, nil
|
|
|
|
}
|
2016-09-10 22:55:05 +02:00
|
|
|
}
|
|
|
|
|
2017-07-30 20:44:48 +02:00
|
|
|
// maybeTweakPrivKey examines the single and double tweak parameters on the
|
|
|
|
// passed sign descriptor and may perform a mapping on the passed private key
|
|
|
|
// in order to utilize the tweaks, if populated.
|
2019-01-16 15:47:43 +01:00
|
|
|
func maybeTweakPrivKey(signDesc *input.SignDescriptor,
|
2017-07-30 20:44:48 +02:00
|
|
|
privKey *btcec.PrivateKey) (*btcec.PrivateKey, error) {
|
|
|
|
|
|
|
|
var retPriv *btcec.PrivateKey
|
|
|
|
switch {
|
|
|
|
|
|
|
|
case signDesc.SingleTweak != nil:
|
2019-01-16 15:47:43 +01:00
|
|
|
retPriv = input.TweakPrivKey(privKey,
|
2017-07-30 20:44:48 +02:00
|
|
|
signDesc.SingleTweak)
|
|
|
|
|
|
|
|
case signDesc.DoubleTweak != nil:
|
2019-01-16 15:47:43 +01:00
|
|
|
retPriv = input.DeriveRevocationPrivKey(privKey,
|
2017-07-30 20:44:48 +02:00
|
|
|
signDesc.DoubleTweak)
|
|
|
|
|
|
|
|
default:
|
|
|
|
retPriv = privKey
|
|
|
|
}
|
|
|
|
|
|
|
|
return retPriv, nil
|
|
|
|
}
|
|
|
|
|
2016-08-13 00:29:38 +02:00
|
|
|
// SignOutputRaw generates a signature for the passed transaction according to
|
|
|
|
// the data within the passed SignDescriptor.
|
|
|
|
//
|
|
|
|
// This is a part of the WalletController interface.
|
2017-07-30 20:44:48 +02:00
|
|
|
func (b *BtcWallet) SignOutputRaw(tx *wire.MsgTx,
|
2020-04-06 02:06:38 +02:00
|
|
|
signDesc *input.SignDescriptor) (input.Signature, error) {
|
2018-02-18 00:10:51 +01:00
|
|
|
|
2016-10-16 01:02:09 +02:00
|
|
|
witnessScript := signDesc.WitnessScript
|
2016-08-13 00:29:38 +02:00
|
|
|
|
2016-11-19 02:11:59 +01:00
|
|
|
// First attempt to fetch the private key which corresponds to the
|
|
|
|
// specified public key.
|
2018-02-18 00:10:51 +01:00
|
|
|
privKey, err := b.fetchPrivKey(&signDesc.KeyDesc)
|
2016-08-13 00:29:38 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-07-30 20:44:48 +02:00
|
|
|
// If a tweak (single or double) is specified, then we'll need to use
|
|
|
|
// this tweak to derive the final private key to be used for signing
|
|
|
|
// this output.
|
|
|
|
privKey, err = maybeTweakPrivKey(signDesc, privKey)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2016-11-19 02:11:59 +01:00
|
|
|
}
|
|
|
|
|
2017-07-30 20:44:48 +02:00
|
|
|
// TODO(roasbeef): generate sighash midstate if not present?
|
|
|
|
|
2016-08-13 00:29:38 +02:00
|
|
|
amt := signDesc.Output.Value
|
2018-11-18 05:46:51 +01:00
|
|
|
sig, err := txscript.RawTxInWitnessSignature(
|
|
|
|
tx, signDesc.SigHashes, signDesc.InputIndex, amt,
|
|
|
|
witnessScript, signDesc.HashType, privKey,
|
|
|
|
)
|
2016-08-13 00:29:38 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Chop off the sighash flag at the end of the signature.
|
2022-02-23 14:48:00 +01:00
|
|
|
return ecdsa.ParseDERSignature(sig[:len(sig)-1])
|
2016-08-13 00:29:38 +02:00
|
|
|
}
|
|
|
|
|
2018-11-08 20:54:02 +01:00
|
|
|
// ComputeInputScript generates a complete InputScript for the passed
|
2016-08-13 00:29:38 +02:00
|
|
|
// transaction with the signature as defined within the passed SignDescriptor.
|
|
|
|
// This method is capable of generating the proper input script for both
|
2016-09-10 22:55:05 +02:00
|
|
|
// regular p2wkh output and p2wkh outputs nested within a regular p2sh output.
|
2016-08-13 00:29:38 +02:00
|
|
|
//
|
|
|
|
// This is a part of the WalletController interface.
|
|
|
|
func (b *BtcWallet) ComputeInputScript(tx *wire.MsgTx,
|
2019-01-16 15:47:43 +01:00
|
|
|
signDesc *input.SignDescriptor) (*input.Script, error) {
|
2016-08-13 00:29:38 +02:00
|
|
|
|
2017-07-30 20:44:48 +02:00
|
|
|
// If a tweak (single or double) is specified, then we'll need to use
|
|
|
|
// this tweak to derive the final private key to be used for signing
|
|
|
|
// this output.
|
2020-10-01 16:21:42 +02:00
|
|
|
privKeyTweaker := func(k *btcec.PrivateKey) (*btcec.PrivateKey, error) {
|
|
|
|
return maybeTweakPrivKey(signDesc, k)
|
2017-07-30 20:44:48 +02:00
|
|
|
}
|
|
|
|
|
2020-10-01 16:21:42 +02:00
|
|
|
// Let the wallet compute the input script now.
|
|
|
|
witness, sigScript, err := b.wallet.ComputeInputScript(
|
|
|
|
tx, signDesc.Output, signDesc.InputIndex, signDesc.SigHashes,
|
|
|
|
signDesc.HashType, privKeyTweaker,
|
2018-11-18 05:46:51 +01:00
|
|
|
)
|
2016-08-13 00:29:38 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-10-01 16:21:42 +02:00
|
|
|
return &input.Script{
|
|
|
|
Witness: witness,
|
|
|
|
SigScript: sigScript,
|
|
|
|
}, nil
|
2016-08-13 00:29:38 +02:00
|
|
|
}
|
2016-12-22 21:53:20 +01:00
|
|
|
|
|
|
|
// A compile time check to ensure that BtcWallet implements the Signer
|
|
|
|
// interface.
|
2019-01-16 15:47:43 +01:00
|
|
|
var _ input.Signer = (*BtcWallet)(nil)
|
2017-03-27 19:13:40 +02:00
|
|
|
|
2017-04-14 20:05:18 +02:00
|
|
|
// SignMessage attempts to sign a target message with the private key that
|
2021-09-23 16:54:30 +02:00
|
|
|
// corresponds to the passed key locator. If the target private key is unable to
|
2017-04-14 20:05:18 +02:00
|
|
|
// be found, then an error will be returned. The actual digest signed is the
|
|
|
|
// double SHA-256 of the passed message.
|
2017-04-01 14:33:17 +02:00
|
|
|
//
|
2017-04-14 20:05:18 +02:00
|
|
|
// NOTE: This is a part of the MessageSigner interface.
|
2021-09-23 16:54:30 +02:00
|
|
|
func (b *BtcWallet) SignMessage(keyLoc keychain.KeyLocator,
|
2022-02-23 14:48:00 +01:00
|
|
|
msg []byte, doubleHash bool) (*ecdsa.Signature, error) {
|
2017-03-27 19:13:40 +02:00
|
|
|
|
|
|
|
// First attempt to fetch the private key which corresponds to the
|
|
|
|
// specified public key.
|
2018-02-18 00:10:51 +01:00
|
|
|
privKey, err := b.fetchPrivKey(&keychain.KeyDescriptor{
|
2021-09-23 16:54:30 +02:00
|
|
|
KeyLocator: keyLoc,
|
2018-02-18 00:10:51 +01:00
|
|
|
})
|
2017-03-27 19:13:40 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-04-14 20:05:18 +02:00
|
|
|
// Double hash and sign the data.
|
2021-10-14 15:42:44 +02:00
|
|
|
var msgDigest []byte
|
|
|
|
if doubleHash {
|
|
|
|
msgDigest = chainhash.DoubleHashB(msg)
|
|
|
|
} else {
|
|
|
|
msgDigest = chainhash.HashB(msg)
|
|
|
|
}
|
2022-02-23 14:48:00 +01:00
|
|
|
return ecdsa.Sign(privKey, msgDigest), nil
|
2017-03-27 19:13:40 +02:00
|
|
|
}
|
2017-04-14 20:05:18 +02:00
|
|
|
|
|
|
|
// A compile time check to ensure that BtcWallet implements the MessageSigner
|
|
|
|
// interface.
|
|
|
|
var _ lnwallet.MessageSigner = (*BtcWallet)(nil)
|