github.com/decred/dcrlnd@v0.7.6/lnwallet/remotedcrwallet/keychain.go (about) 1 package remotedcrwallet 2 3 import ( 4 "errors" 5 6 "github.com/decred/dcrd/hdkeychain/v3" 7 "github.com/decred/dcrd/txscript/v4/stdaddr" 8 "github.com/decred/dcrlnd/channeldb" 9 "github.com/decred/dcrlnd/keychain" 10 "github.com/decred/dcrlnd/lnwallet" 11 ) 12 13 // onchainAddrSourcer is an interface for the required operations needed to 14 // derive keys that also correspond to regular onchain wallet addresses. 15 type onchainAddrSourcer interface { 16 // NewAddress must return the next usable onchain address for the 17 // wallet. 18 NewAddress(t lnwallet.AddressType, change bool, accountName string) (stdaddr.Address, error) 19 20 // Bip44AddressInfo returns the respective account, branch and index 21 // for the given wallet address. 22 Bip44AddressInfo(addr stdaddr.Address) (uint32, uint32, uint32, error) 23 } 24 25 // remoteWalletKeyRing is an implementation of both the KeyRing and 26 // SecretKeyRing interfaces backed by a root master HD key. 27 type remoteWalletKeyRing struct { 28 *keychain.HDKeyRing 29 30 onchainAddrs onchainAddrSourcer 31 32 rootXPriv *hdkeychain.ExtendedKey 33 multiSigKFXpriv *hdkeychain.ExtendedKey 34 paymentBaseKFXpriv *hdkeychain.ExtendedKey 35 36 // db is a pointer to a channeldb.DB instance that stores the indices 37 // of used keys of the keyring. This is used to track the next 38 // available key to prevent key reuse when establishing channels. 39 db *channeldb.DB 40 } 41 42 // Compile time type assertions to ensure remoteWalletKeyRing fulfills the desired 43 // interfaces. 44 var _ keychain.KeyRing = (*remoteWalletKeyRing)(nil) 45 var _ keychain.SecretKeyRing = (*remoteWalletKeyRing)(nil) 46 47 // newRemoteWalletKeyRing creates a new implementation of the 48 // keychain.SecretKeyRing interface backed by the given root extended private 49 // key. 50 func newRemoteWalletKeyRing(rootAccountXPriv *hdkeychain.ExtendedKey, 51 db *channeldb.DB, onchainAddrs onchainAddrSourcer) (*remoteWalletKeyRing, error) { 52 53 if !rootAccountXPriv.IsPrivate() { 54 return nil, errors.New("Provided key is not an extended private key") 55 } 56 57 // By convention, the root LN key is a hardened key derived from index 58 // 1017 of a wallet account. 59 idx := uint32(hdkeychain.HardenedKeyStart + keychain.BIP0043Purpose) 60 rootXPriv, err := rootAccountXPriv.Child(idx) 61 if err != nil { 62 return nil, err 63 } 64 65 // This assumes that there are no discontinuities within the KeyFamily 66 // constants. 67 lastKeyFam := uint32(keychain.KeyFamilyLastKF) 68 masterPubs := make(map[keychain.KeyFamily]*hdkeychain.ExtendedKey, 69 lastKeyFam+1) 70 71 // The masterpub for the multisig key family is still the external 72 // branch for the root account xpriv provided. This allows the wallet 73 // itself to watch for on-chain spends from it and correctly account 74 // for its funds. 75 multiSigXPriv, err := rootAccountXPriv.Child(0) 76 if err != nil { 77 return nil, err 78 } 79 multiSigXPub := multiSigXPriv.Neuter() 80 masterPubs[keychain.KeyFamilyMultiSig] = multiSigXPub 81 82 // The masterpub for the payment base key family (i.e. addresses used 83 // for our non-encumbered output in remote commitments) is the internal 84 // branch for the root account xpriv provided. This allows the wallet 85 // to directly spend these funds when a breach or DLP scenario is 86 // triggered without requiring any other off-chain state. 87 paymentBaseXPriv, err := rootAccountXPriv.Child(0) 88 if err != nil { 89 return nil, err 90 } 91 paymentBaseXPub := paymentBaseXPriv.Neuter() 92 masterPubs[keychain.KeyFamilyPaymentBase] = paymentBaseXPub 93 94 // Derive the master pubs for the other key families. 95 for i := uint32(0); i <= lastKeyFam; i++ { 96 switch keychain.KeyFamily(i) { 97 case keychain.KeyFamilyMultiSig: 98 continue 99 case keychain.KeyFamilyPaymentBase: 100 continue 101 } 102 103 // Errors here cause fatal failures due to the wallet not 104 // attempting to generate the next account. 105 famKey, err := rootXPriv.Child(hdkeychain.HardenedKeyStart + i) 106 if err != nil { 107 return nil, err 108 } 109 110 famPub := famKey.Neuter() 111 masterPubs[keychain.KeyFamily(i)] = famPub 112 } 113 114 wkr := &remoteWalletKeyRing{ 115 rootXPriv: rootXPriv, 116 multiSigKFXpriv: multiSigXPriv, 117 paymentBaseKFXpriv: paymentBaseXPriv, 118 db: db, 119 onchainAddrs: onchainAddrs, 120 } 121 wkr.HDKeyRing = keychain.NewHDKeyRing(masterPubs, wkr.fetchMasterPriv, 122 wkr.nextIndex) 123 return wkr, nil 124 } 125 126 func (kr *remoteWalletKeyRing) nextIndex(keyFam keychain.KeyFamily) (uint32, error) { 127 switch keyFam { 128 case keychain.KeyFamilyMultiSig, 129 keychain.KeyFamilyPaymentBase: 130 131 // For these key families, instead of using the channel 132 // database to track indices we request the next available 133 // address from the wallet (with the wrap gap policy) and 134 // decode its index. 135 // 136 // The net result is that the wallet should always be able to 137 // find these addresses and their respective keys during 138 // address discovery instead of requiring input from the 139 // lightning wallet. 140 branchInternal := keyFam == keychain.KeyFamilyPaymentBase 141 addr, err := kr.onchainAddrs.NewAddress( 142 lnwallet.PubKeyHash, branchInternal, lnwallet.DefaultAccountName, 143 ) 144 if err != nil { 145 return 0, err 146 } 147 148 _, _, addrIndex, err := kr.onchainAddrs.Bip44AddressInfo(addr) 149 if err != nil { 150 return 0, err 151 } 152 return addrIndex, nil 153 154 } 155 156 return kr.db.NextKeyFamilyIndex(uint32(keyFam)) 157 } 158 159 func (kr *remoteWalletKeyRing) fetchMasterPriv(keyFam keychain.KeyFamily) (*hdkeychain.ExtendedKey, 160 error) { 161 162 // The master priv of the special key families that correspond to 163 // regular on-chain branches are stored separately. 164 switch keyFam { 165 case keychain.KeyFamilyMultiSig: 166 return kr.multiSigKFXpriv, nil 167 case keychain.KeyFamilyPaymentBase: 168 return kr.paymentBaseKFXpriv, nil 169 } 170 171 // For the other keyfamilies, derive from the alternative root branch. 172 idx := uint32(hdkeychain.HardenedKeyStart + keyFam) 173 famKey, err := kr.rootXPriv.Child(idx) 174 if err != nil { 175 return nil, err 176 } 177 178 return famKey, nil 179 }