@@ -25,7 +25,6 @@ import {
2525 Transaction ,
2626 TransactionQuery ,
2727 PaymentInfo ,
28- BackupResponse ,
2928 PaymentQuery ,
3029} from '../codecs' ;
3130import { LightningPaymentIntent , LightningPaymentRequest } from '@bitgo/public-types' ;
@@ -38,34 +37,145 @@ export type PayInvoiceResponse = {
3837 paymentStatus ?: LndCreatePaymentResponse ;
3938} ;
4039
41- export interface ILightningWallet {
42- /**
43- * Get the lightning keychain for the given wallet.
44- */
45- getLightningKeychain ( ) : Promise < LightningKeychain > ;
40+ /**
41+ * Get the lightning keychain for the given wallet.
42+ */
43+ export async function getLightningKeychain ( wallet : sdkcore . IWallet ) : Promise < LightningKeychain > {
44+ const coin = wallet . baseCoin ;
45+ if ( coin . getFamily ( ) !== 'lnbtc' ) {
46+ throw new Error ( `Invalid coin to get lightning wallet key: ${ coin . getFamily ( ) } ` ) ;
47+ }
48+ const keyIds = wallet . keyIds ( ) ;
49+ if ( keyIds . length !== 1 ) {
50+ throw new Error ( `Invalid number of key in lightning wallet: ${ keyIds . length } ` ) ;
51+ }
52+ const keychain = await coin . keychains ( ) . get ( { id : keyIds [ 0 ] } ) ;
53+ return sdkcore . decodeOrElse ( LightningKeychain . name , LightningKeychain , keychain , ( _ ) => {
54+ throw new Error ( `Invalid user key` ) ;
55+ } ) ;
56+ }
4657
47- /**
48- * Get the lightning auth keychains for the given wallet.
49- */
50- getLightningAuthKeychains ( ) : Promise < { userAuthKey : LightningAuthKeychain ; nodeAuthKey : LightningAuthKeychain } > ;
58+ /**
59+ * Get the lightning auth keychains for the given wallet.
60+ */
61+ export async function getLightningAuthKeychains ( wallet : sdkcore . IWallet ) : Promise < {
62+ userAuthKey : LightningAuthKeychain ;
63+ nodeAuthKey : LightningAuthKeychain ;
64+ } > {
65+ const coin = wallet . baseCoin ;
66+ if ( coin . getFamily ( ) !== 'lnbtc' ) {
67+ throw new Error ( `Invalid coin to get lightning wallet auth keys: ${ coin . getFamily ( ) } ` ) ;
68+ }
69+ const authKeyIds = wallet . coinSpecific ( ) ?. keys ;
70+ if ( authKeyIds ?. length !== 2 ) {
71+ throw new Error ( `Invalid number of auth keys in lightning wallet: ${ authKeyIds ?. length } ` ) ;
72+ }
73+ const keychains = await Promise . all ( authKeyIds . map ( ( id ) => coin . keychains ( ) . get ( { id } ) ) ) ;
74+ const authKeychains = keychains . map ( ( keychain ) => {
75+ return sdkcore . decodeOrElse ( LightningAuthKeychain . name , LightningAuthKeychain , keychain , ( _ ) => {
76+ // DON'T throw errors from decodeOrElse. It could leak sensitive information.
77+ throw new Error ( `Invalid lightning auth key: ${ keychain ?. id } ` ) ;
78+ } ) ;
79+ } ) ;
80+ const [ userAuthKey , nodeAuthKey ] = ( [ 'userAuth' , 'nodeAuth' ] as const ) . map ( ( purpose ) => {
81+ const keychain = authKeychains . find (
82+ ( k ) => unwrapLightningCoinSpecific ( k . coinSpecific , coin . getChain ( ) ) . purpose === purpose
83+ ) ;
84+ if ( ! keychain ) {
85+ throw new Error ( `Missing ${ purpose } key` ) ;
86+ }
87+ return keychain ;
88+ } ) ;
5189
52- /**
53- * Updates the coin-specific configuration for a Lightning Wallet.
54- *
55- * @param {UpdateLightningWalletClientRequest } params - The parameters containing the updated wallet-specific details.
56- * - `encryptedSignerMacaroon` (optional): This macaroon is used by the watch-only node to ask the signer node to sign transactions.
57- * Encrypted with ECDH secret key from private key of wallet's user auth key and public key of lightning service.
58- * - `encryptedSignerAdminMacaroon` (optional): Generated when initializing the wallet of the signer node.
59- * Encrypted with client's wallet passphrase.
60- * - `signerHost` (optional): The host address of the Lightning signer node.
61- * - `encryptedSignerTlsKey` (optional): The wallet passphrase encrypted TLS key of the signer.
62- * - `passphrase` (required): The wallet passphrase.
63- * - `signerTlsCert` (optional): The TLS certificate of the signer.
64- * - `watchOnlyAccounts` (optional): These are the accounts used to initialize the watch-only wallet.
65- * @returns {Promise<unknown> } A promise resolving to the updated wallet response or throwing an error if the update fails.
66- */
67- updateWalletCoinSpecific ( params : UpdateLightningWalletClientRequest ) : Promise < unknown > ;
90+ return { userAuthKey, nodeAuthKey } ;
91+ }
92+
93+ function encryptWalletUpdateRequest (
94+ wallet : sdkcore . IWallet ,
95+ params : UpdateLightningWalletClientRequest ,
96+ userAuthKey : LightningAuthKeychain
97+ ) : UpdateLightningWalletEncryptedRequest {
98+ const coinName = wallet . coin ( ) as 'tlnbtc' | 'lnbtc' ;
99+
100+ const requestWithEncryption : Partial < UpdateLightningWalletClientRequest & UpdateLightningWalletEncryptedRequest > = {
101+ ...params ,
102+ } ;
103+
104+ const userAuthXprv = wallet . bitgo . decrypt ( {
105+ password : params . passphrase ,
106+ input : userAuthKey . encryptedPrv ,
107+ } ) ;
68108
109+ if ( params . signerTlsKey ) {
110+ requestWithEncryption . encryptedSignerTlsKey = wallet . bitgo . encrypt ( {
111+ password : params . passphrase ,
112+ input : params . signerTlsKey ,
113+ } ) ;
114+ }
115+
116+ if ( params . signerAdminMacaroon ) {
117+ requestWithEncryption . encryptedSignerAdminMacaroon = wallet . bitgo . encrypt ( {
118+ password : params . passphrase ,
119+ input : params . signerAdminMacaroon ,
120+ } ) ;
121+ }
122+
123+ if ( params . signerMacaroon ) {
124+ requestWithEncryption . encryptedSignerMacaroon = wallet . bitgo . encrypt ( {
125+ password : deriveLightningServiceSharedSecret ( coinName , userAuthXprv ) . toString ( 'hex' ) ,
126+ input : params . signerMacaroon ,
127+ } ) ;
128+ }
129+
130+ return t . exact ( UpdateLightningWalletEncryptedRequest ) . encode ( requestWithEncryption ) ;
131+ }
132+
133+ /**
134+ * Updates the coin-specific configuration for a Lightning Wallet.
135+ *
136+ * @param {Wallet } wallet - Wallet.
137+ * @param {UpdateLightningWalletClientRequest } params - The parameters containing the updated wallet-specific details.
138+ * - `encryptedSignerMacaroon` (optional): This macaroon is used by the watch-only node to ask the signer node to sign transactions.
139+ * Encrypted with ECDH secret key from private key of wallet's user auth key and public key of lightning service.
140+ * - `encryptedSignerAdminMacaroon` (optional): Generated when initializing the wallet of the signer node.
141+ * Encrypted with client's wallet passphrase.
142+ * - `signerHost` (optional): The host address of the Lightning signer node.
143+ * - `encryptedSignerTlsKey` (optional): The wallet passphrase encrypted TLS key of the signer.
144+ * - `passphrase` (required): The wallet passphrase.
145+ * - `signerTlsCert` (optional): The TLS certificate of the signer.
146+ * - `watchOnlyAccounts` (optional): These are the accounts used to initialize the watch-only wallet.
147+ * @returns {Promise<unknown> } A promise resolving to the updated wallet response or throwing an error if the update fails.
148+ */
149+ export async function updateWalletCoinSpecific (
150+ wallet : sdkcore . IWallet ,
151+ params : UpdateLightningWalletClientRequest
152+ ) : Promise < unknown > {
153+ sdkcore . decodeOrElse (
154+ UpdateLightningWalletClientRequest . name ,
155+ UpdateLightningWalletClientRequest ,
156+ params ,
157+ ( errors ) => {
158+ // DON'T throw errors from decodeOrElse. It could leak sensitive information.
159+ throw new Error ( `Invalid params for lightning specific update wallet` ) ;
160+ }
161+ ) ;
162+
163+ const { userAuthKey } = await getLightningAuthKeychains ( wallet ) ;
164+ const updateRequestWithEncryption = encryptWalletUpdateRequest ( wallet , params , userAuthKey ) ;
165+ const signature = createMessageSignature (
166+ updateRequestWithEncryption ,
167+ wallet . bitgo . decrypt ( { password : params . passphrase , input : userAuthKey . encryptedPrv } )
168+ ) ;
169+ const coinSpecific = {
170+ [ wallet . coin ( ) ] : {
171+ signedRequest : updateRequestWithEncryption ,
172+ signature,
173+ } ,
174+ } ;
175+ return await wallet . bitgo . put ( wallet . url ( ) ) . send ( { coinSpecific } ) . result ( ) ;
176+ }
177+
178+ export interface ILightningWallet {
69179 /**
70180 * Creates a lightning invoice
71181 * @param {object } params Invoice parameters
@@ -138,130 +248,19 @@ export interface ILightningWallet {
138248 * @returns {Promise<Transaction[]> } List of transactions
139249 */
140250 listTransactions ( params : TransactionQuery ) : Promise < Transaction [ ] > ;
141-
142- /**
143- * Get the channel backup for the given wallet.
144- * @returns {Promise<BackupResponse> } A promise resolving to the channel backup
145- */
146- getChannelBackup ( ) : Promise < BackupResponse > ;
147251}
148252
149- export class SelfCustodialLightningWallet implements ILightningWallet {
253+ export class LightningWallet implements ILightningWallet {
150254 public wallet : sdkcore . IWallet ;
151255
152256 constructor ( wallet : sdkcore . IWallet ) {
153257 const coin = wallet . baseCoin ;
154258 if ( coin . getFamily ( ) !== 'lnbtc' ) {
155- throw new Error ( `Invalid coin to update lightning wallet: ${ coin . getFamily ( ) } ` ) ;
259+ throw new Error ( `Invalid coin for lightning wallet: ${ coin . getFamily ( ) } ` ) ;
156260 }
157261 this . wallet = wallet ;
158262 }
159263
160- private encryptWalletUpdateRequest (
161- params : UpdateLightningWalletClientRequest ,
162- userAuthKey : LightningAuthKeychain
163- ) : UpdateLightningWalletEncryptedRequest {
164- const coinName = this . wallet . coin ( ) as 'tlnbtc' | 'lnbtc' ;
165-
166- const requestWithEncryption : Partial < UpdateLightningWalletClientRequest & UpdateLightningWalletEncryptedRequest > = {
167- ...params ,
168- } ;
169-
170- const userAuthXprv = this . wallet . bitgo . decrypt ( {
171- password : params . passphrase ,
172- input : userAuthKey . encryptedPrv ,
173- } ) ;
174-
175- if ( params . signerTlsKey ) {
176- requestWithEncryption . encryptedSignerTlsKey = this . wallet . bitgo . encrypt ( {
177- password : params . passphrase ,
178- input : params . signerTlsKey ,
179- } ) ;
180- }
181-
182- if ( params . signerAdminMacaroon ) {
183- requestWithEncryption . encryptedSignerAdminMacaroon = this . wallet . bitgo . encrypt ( {
184- password : params . passphrase ,
185- input : params . signerAdminMacaroon ,
186- } ) ;
187- }
188-
189- if ( params . signerMacaroon ) {
190- requestWithEncryption . encryptedSignerMacaroon = this . wallet . bitgo . encrypt ( {
191- password : deriveLightningServiceSharedSecret ( coinName , userAuthXprv ) . toString ( 'hex' ) ,
192- input : params . signerMacaroon ,
193- } ) ;
194- }
195-
196- return t . exact ( UpdateLightningWalletEncryptedRequest ) . encode ( requestWithEncryption ) ;
197- }
198-
199- async getLightningKeychain ( ) : Promise < LightningKeychain > {
200- const keyIds = this . wallet . keyIds ( ) ;
201- if ( keyIds . length !== 1 ) {
202- throw new Error ( `Invalid number of key in lightning wallet: ${ keyIds . length } ` ) ;
203- }
204- const keychain = await this . wallet . baseCoin . keychains ( ) . get ( { id : keyIds [ 0 ] } ) ;
205- return sdkcore . decodeOrElse ( LightningKeychain . name , LightningKeychain , keychain , ( _ ) => {
206- throw new Error ( `Invalid user key` ) ;
207- } ) ;
208- }
209-
210- async getLightningAuthKeychains ( ) : Promise < {
211- userAuthKey : LightningAuthKeychain ;
212- nodeAuthKey : LightningAuthKeychain ;
213- } > {
214- const authKeyIds = this . wallet . coinSpecific ( ) ?. keys ;
215- if ( authKeyIds ?. length !== 2 ) {
216- throw new Error ( `Invalid number of auth keys in lightning wallet: ${ authKeyIds ?. length } ` ) ;
217- }
218- const coin = this . wallet . baseCoin ;
219- const keychains = await Promise . all ( authKeyIds . map ( ( id ) => coin . keychains ( ) . get ( { id } ) ) ) ;
220- const authKeychains = keychains . map ( ( keychain ) => {
221- return sdkcore . decodeOrElse ( LightningAuthKeychain . name , LightningAuthKeychain , keychain , ( _ ) => {
222- // DON'T throw errors from decodeOrElse. It could leak sensitive information.
223- throw new Error ( `Invalid lightning auth key: ${ keychain ?. id } ` ) ;
224- } ) ;
225- } ) ;
226- const [ userAuthKey , nodeAuthKey ] = ( [ 'userAuth' , 'nodeAuth' ] as const ) . map ( ( purpose ) => {
227- const keychain = authKeychains . find (
228- ( k ) => unwrapLightningCoinSpecific ( k . coinSpecific , coin . getChain ( ) ) . purpose === purpose
229- ) ;
230- if ( ! keychain ) {
231- throw new Error ( `Missing ${ purpose } key` ) ;
232- }
233- return keychain ;
234- } ) ;
235-
236- return { userAuthKey, nodeAuthKey } ;
237- }
238-
239- async updateWalletCoinSpecific ( params : UpdateLightningWalletClientRequest ) : Promise < unknown > {
240- sdkcore . decodeOrElse (
241- UpdateLightningWalletClientRequest . name ,
242- UpdateLightningWalletClientRequest ,
243- params ,
244- ( errors ) => {
245- // DON'T throw errors from decodeOrElse. It could leak sensitive information.
246- throw new Error ( `Invalid params for lightning specific update wallet` ) ;
247- }
248- ) ;
249-
250- const { userAuthKey } = await this . getLightningAuthKeychains ( ) ;
251- const updateRequestWithEncryption = this . encryptWalletUpdateRequest ( params , userAuthKey ) ;
252- const signature = createMessageSignature (
253- updateRequestWithEncryption ,
254- this . wallet . bitgo . decrypt ( { password : params . passphrase , input : userAuthKey . encryptedPrv } )
255- ) ;
256- const coinSpecific = {
257- [ this . wallet . coin ( ) ] : {
258- signedRequest : updateRequestWithEncryption ,
259- signature,
260- } ,
261- } ;
262- return await this . wallet . bitgo . put ( this . wallet . url ( ) ) . send ( { coinSpecific } ) . result ( ) ;
263- }
264-
265264 async createInvoice ( params : CreateInvoiceBody ) : Promise < Invoice > {
266265 const createInvoiceResponse = await this . wallet . bitgo
267266 . post ( this . wallet . baseCoin . url ( `/wallet/${ this . wallet . id ( ) } /lightning/invoice` ) )
@@ -297,7 +296,7 @@ export class SelfCustodialLightningWallet implements ILightningWallet {
297296 const reqId = new RequestTracer ( ) ;
298297 this . wallet . bitgo . setRequestTracer ( reqId ) ;
299298
300- const { userAuthKey } = await this . getLightningAuthKeychains ( ) ;
299+ const { userAuthKey } = await getLightningAuthKeychains ( this . wallet ) ;
301300 const signature = createMessageSignature (
302301 t . exact ( LightningPaymentRequest ) . encode ( params ) ,
303302 this . wallet . bitgo . decrypt ( { password : params . passphrase , input : userAuthKey . encryptedPrv } )
@@ -387,13 +386,4 @@ export class SelfCustodialLightningWallet implements ILightningWallet {
387386 throw new Error ( `Invalid transaction list response: ${ error } ` ) ;
388387 } ) ;
389388 }
390-
391- async getChannelBackup ( ) : Promise < BackupResponse > {
392- const backupResponse = await this . wallet . bitgo
393- . get ( this . wallet . baseCoin . url ( `/wallet/${ this . wallet . id ( ) } /lightning/backup` ) )
394- . result ( ) ;
395- return sdkcore . decodeOrElse ( BackupResponse . name , BackupResponse , backupResponse , ( error ) => {
396- throw new Error ( `Invalid backup response: ${ error } ` ) ;
397- } ) ;
398- }
399389}
0 commit comments