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:
commit
216d1e8017
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user