Skip to content

Commit

Permalink
Merge pull request #3419 from Roasbeef/sign-create-key-if-not-found
Browse files Browse the repository at this point in the history
lnwallet: when signing create account if not found
  • Loading branch information
Roasbeef committed Aug 21, 2019
2 parents 2f8d3c4 + cc4daa0 commit 216d1e8
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 12 deletions.
54 changes: 42 additions & 12 deletions lnwallet/btcwallet/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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)

path := waddrmgr.DerivationPath{
Account: uint32(keyLoc.Family),
Branch: 0,
Index: uint32(keyLoc.Index),
}
addr, err := scopedMgr.DeriveFromKeyPath(addrmgrNs, path)
if err != nil {
return err
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
}
}

key, err = addr.(waddrmgr.ManagedPubKeyAddress).PrivKey()
return err
})
if err != nil {
Expand Down
46 changes: 46 additions & 0 deletions lnwallet/interface_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit 216d1e8

Please sign in to comment.