Merge pull request #3419 from Roasbeef/sign-create-key-if-not-found

lnwallet: when signing create account if not found
This commit is contained in:
Olaoluwa Osuntokun 2019-08-20 20:53:01 -07:00 committed by GitHub
commit 216d1e8017
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 87 additions and 11 deletions

@ -83,6 +83,25 @@ func (b *BtcWallet) fetchOutputAddr(script []byte) (waddrmgr.ManagedAddress, err
return nil, lnwallet.ErrNotMine
}
// 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{
Account: uint32(keyLoc.Family),
Branch: 0,
Index: uint32(keyLoc.Index),
}
addr, err := scopedMgr.DeriveFromKeyPath(addrmgrNs, path)
if err != nil {
return nil, err
}
return addr.(waddrmgr.ManagedPubKeyAddress).PrivKey()
}
// deriveKeyByLocator attempts to derive a key stored in the wallet given a
// valid key locator.
func (b *BtcWallet) deriveKeyByLocator(keyLoc keychain.KeyLocator) (*btcec.PrivateKey, error) {
@ -95,20 +114,31 @@ func (b *BtcWallet) deriveKeyByLocator(keyLoc keychain.KeyLocator) (*btcec.Priva
}
var key *btcec.PrivateKey
err = walletdb.View(b.db, func(tx walletdb.ReadTx) error {
addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
err = walletdb.Update(b.db, func(tx walletdb.ReadWriteTx) error {
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
path := waddrmgr.DerivationPath{
Account: uint32(keyLoc.Family),
Branch: 0,
Index: uint32(keyLoc.Index),
}
addr, err := scopedMgr.DeriveFromKeyPath(addrmgrNs, path)
if err != nil {
return err
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
}
}
key, err = addr.(waddrmgr.ManagedPubKeyAddress).PrivKey()
return err
})
if err != nil {

@ -2427,6 +2427,48 @@ func testCreateSimpleTx(r *rpctest.Harness, w *lnwallet.LightningWallet,
}
}
// testSignOutputCreateAccount tests that we're able to properly sign for an
// output if the target account hasn't yet been created on disk. In this case,
// we'll create the account, then sign.
func testSignOutputCreateAccount(r *rpctest.Harness, w *lnwallet.LightningWallet,
_ *lnwallet.LightningWallet, t *testing.T) {
// First, we'll create a sign desc that references a non-default key
// family. Under the hood, key families are actually accounts, so this
// should force create of the account so we can sign with it.
fakeTx := wire.NewMsgTx(2)
fakeTx.AddTxIn(&wire.TxIn{
PreviousOutPoint: wire.OutPoint{
Hash: chainhash.Hash{},
Index: 0,
},
})
signDesc := &input.SignDescriptor{
KeyDesc: keychain.KeyDescriptor{
KeyLocator: keychain.KeyLocator{
Family: 99,
Index: 1,
},
},
WitnessScript: []byte{},
Output: &wire.TxOut{
Value: 1000,
},
HashType: txscript.SigHashAll,
SigHashes: txscript.NewTxSigHashes(fakeTx),
InputIndex: 0,
}
// We'll now sign and expect this to succeed, as even though the
// account doesn't exist atm, it should be created in order to process
// the inbound signing request.
_, err := w.Cfg.Signer.SignOutputRaw(fakeTx, signDesc)
if err != nil {
t.Fatalf("unable to sign for output with non-existent "+
"account: %v", err)
}
}
type walletTestCase struct {
name string
test func(miner *rpctest.Harness, alice, bob *lnwallet.LightningWallet,
@ -2493,6 +2535,10 @@ var walletTests = []walletTestCase{
name: "create simple tx",
test: testCreateSimpleTx,
},
{
name: "test sign create account",
test: testSignOutputCreateAccount,
},
}
func clearWalletStates(a, b *lnwallet.LightningWallet) error {