@@ -3,7 +3,9 @@ package address
33import (
44 "bytes"
55 "context"
6+ "errors"
67 "fmt"
8+ "strings"
79 "sync"
810 "sync/atomic"
911
@@ -21,6 +23,12 @@ import (
2123 "github.com/lightningnetwork/lnd/lnwallet"
2224)
2325
26+ var (
27+ // ErrNoStaticAddress is returned when no static address parameters are
28+ // present in the store.
29+ ErrNoStaticAddress = errors .New ("no static address parameters found" )
30+ )
31+
2432// ManagerConfig holds the configuration for the address manager.
2533type ManagerConfig struct {
2634 // AddressClient is the client that communicates with the loop server
@@ -199,27 +207,143 @@ func (m *Manager) NewAddress(ctx context.Context) (*btcutil.AddressTaproot,
199207 return nil , 0 , err
200208 }
201209
210+ _ , err = m .importAddressTapscript (ctx , staticAddress )
211+ if err != nil {
212+ return nil , 0 , err
213+ }
214+
215+ address , err := m .GetTaprootAddress (
216+ clientPubKey .PubKey , serverPubKey , int64 (serverParams .Expiry ),
217+ )
218+ if err != nil {
219+ return nil , 0 , err
220+ }
221+
222+ return address , int64 (serverParams .Expiry ), nil
223+ }
224+
225+ // RestoreAddress recreates a static address record locally and makes sure the
226+ // corresponding tapscript is imported into lnd. If the same address already
227+ // exists locally, the call is idempotent.
228+ func (m * Manager ) RestoreAddress (ctx context.Context ,
229+ addrParams * Parameters ) (* btcutil.AddressTaproot , bool , error ) {
230+
231+ if addrParams == nil {
232+ return nil , false , fmt .Errorf ("missing static address parameters" )
233+ }
234+
235+ staticAddress , err := script .NewStaticAddress (
236+ input .MuSig2Version100RC2 , int64 (addrParams .Expiry ),
237+ addrParams .ClientPubkey , addrParams .ServerPubkey ,
238+ )
239+ if err != nil {
240+ return nil , false , err
241+ }
242+
243+ pkScript , err := staticAddress .StaticAddressScript ()
244+ if err != nil {
245+ return nil , false , err
246+ }
247+
248+ if len (addrParams .PkScript ) != 0 &&
249+ ! bytes .Equal (addrParams .PkScript , pkScript ) {
250+
251+ return nil , false , fmt .Errorf ("static address pk script mismatch" )
252+ }
253+
254+ addrParams .PkScript = pkScript
255+ if addrParams .InitiationHeight <= 0 {
256+ addrParams .InitiationHeight = m .currentHeight .Load ()
257+ }
258+
259+ m .Lock ()
260+ existing , err := m .cfg .Store .GetAllStaticAddresses (ctx )
261+ if err != nil {
262+ m .Unlock ()
263+
264+ return nil , false , err
265+ }
266+
267+ changed := false
268+ switch {
269+ case len (existing ) == 0 :
270+ err = m .cfg .Store .CreateStaticAddress (ctx , addrParams )
271+ if err != nil {
272+ m .Unlock ()
273+
274+ return nil , false , err
275+ }
276+ changed = true
277+
278+ case len (existing ) > 1 :
279+ m .Unlock ()
280+
281+ return nil , false , fmt .Errorf ("more than one static address found" )
282+
283+ case ! sameAddressParameters (existing [0 ], addrParams ):
284+ m .Unlock ()
285+
286+ return nil , false , fmt .Errorf ("existing static address differs from " +
287+ "backup" )
288+ }
289+ m .Unlock ()
290+
291+ imported , err := m .importAddressTapscript (ctx , staticAddress )
292+ if err != nil {
293+ return nil , false , err
294+ }
295+
296+ changed = changed || imported
297+
298+ addr , err := m .GetTaprootAddress (
299+ addrParams .ClientPubkey , addrParams .ServerPubkey ,
300+ int64 (addrParams .Expiry ),
301+ )
302+ if err != nil {
303+ return nil , false , err
304+ }
305+
306+ return addr , changed , nil
307+ }
308+
309+ func (m * Manager ) importAddressTapscript (ctx context.Context ,
310+ staticAddress * script.StaticAddress ) (bool , error ) {
311+
202312 // Import the static address tapscript into our lnd wallet, so we can
203313 // track unspent outputs of it.
204314 tapScript := input .TapscriptFullTree (
205315 staticAddress .InternalPubKey , * staticAddress .TimeoutLeaf ,
206316 )
207317 addr , err := m .cfg .WalletKit .ImportTaprootScript (ctx , tapScript )
208318 if err != nil {
209- return nil , 0 , err
319+ // Restoring into an lnd instance that already imported the script is
320+ // expected. Treat the duplicate import as success.
321+ if strings .Contains (err .Error (), "already exists" ) {
322+ log .Infof ("Static address tapscript already imported" )
323+ return false , nil
324+ }
325+
326+ return false , err
210327 }
211328
212329 log .Infof ("Imported static address taproot script to lnd wallet: %v" ,
213330 addr )
214331
215- address , err := m .GetTaprootAddress (
216- clientPubKey .PubKey , serverPubKey , int64 (serverParams .Expiry ),
217- )
218- if err != nil {
219- return nil , 0 , err
332+ return true , nil
333+ }
334+
335+ func sameAddressParameters (a , b * Parameters ) bool {
336+ if a == nil || b == nil {
337+ return false
220338 }
221339
222- return address , int64 (serverParams .Expiry ), nil
340+ return a .ClientPubkey .IsEqual (b .ClientPubkey ) &&
341+ a .ServerPubkey .IsEqual (b .ServerPubkey ) &&
342+ a .Expiry == b .Expiry &&
343+ bytes .Equal (a .PkScript , b .PkScript ) &&
344+ a .KeyLocator == b .KeyLocator &&
345+ a .ProtocolVersion == b .ProtocolVersion &&
346+ a .InitiationHeight == b .InitiationHeight
223347}
224348
225349// GetTaprootAddress returns a taproot address for the given client and server
@@ -297,7 +421,7 @@ func (m *Manager) GetStaticAddressParameters(ctx context.Context) (*Parameters,
297421 }
298422
299423 if len (params ) == 0 {
300- return nil , fmt . Errorf ( "no static address parameters found" )
424+ return nil , ErrNoStaticAddress
301425 }
302426
303427 return params [0 ], nil
0 commit comments