@@ -54,19 +54,90 @@ type Manager struct {
5454 cfg * ManagerConfig
5555
5656 currentHeight atomic.Int32
57+
58+ // addrRequest is a channel used to request new static addresses from
59+ // the manager. The manager employs a go worker routine that handles the
60+ // requests.
61+ addrRequest chan request
62+ }
63+
64+ type request struct {
65+ ctx context.Context
66+ respChan chan response
67+ }
68+
69+ type response struct {
70+ addr * btcutil.AddressTaproot
71+ expiry int64
72+ err error
5773}
5874
5975// NewManager creates a new address manager.
6076func NewManager (cfg * ManagerConfig , currentHeight int32 ) * Manager {
6177 m := & Manager {
62- cfg : cfg ,
78+ cfg : cfg ,
79+ addrRequest : make (chan request ),
6380 }
6481 m .currentHeight .Store (currentHeight )
6582
6683 return m
6784}
6885
69- // Run runs the address manager.
86+ // addrWorker is a worker that handles address creation requests. It calls
87+ // m.newAddress which blocks on server I/O and returns the address and expiry.
88+ func (m * Manager ) addrWorker (ctx context.Context ) {
89+ // We detect panics in the worker in recover() which in this case also
90+ // restores normal execution. We then return an error to the caller and
91+ // continue the address flow.
92+ panicRecovery := func (req request ) {
93+ if r := recover (); r != nil {
94+ var err error
95+ if e , ok := r .(error ); ok {
96+ err = e
97+ } else {
98+ err = fmt .Errorf ("addrWorker panic: %v" , r )
99+ }
100+
101+ // Best-effort notifies caller; don't block shutdown.
102+ select {
103+ case req .respChan <- response {err : err }:
104+ case <- req .ctx .Done ():
105+ case <- ctx .Done ():
106+ }
107+ }
108+ }
109+
110+ for {
111+ select {
112+ case req := <- m .addrRequest :
113+ func (req request ) {
114+ // If the worker goroutine panics, we want to
115+ // recover and return an error to the caller.
116+ defer panicRecovery (req )
117+
118+ addr , expiry , e := m .newAddress (req .ctx )
119+
120+ resp := response {
121+ addr : addr ,
122+ expiry : expiry ,
123+ err : e ,
124+ }
125+
126+ select {
127+ case req .respChan <- resp :
128+ case <- req .ctx .Done ():
129+ case <- ctx .Done ():
130+ }
131+ }(req )
132+
133+ case <- ctx .Done ():
134+ return
135+ }
136+ }
137+ }
138+
139+ // Run runs the address manager. It keeps track of the current block height and
140+ // creates new static addresses as needed.
70141func (m * Manager ) Run (ctx context.Context , initChan chan struct {}) error {
71142 newBlockChan , newBlockErrChan , err :=
72143 m .cfg .ChainNotifier .RegisterBlockEpochNtfn (ctx )
@@ -75,6 +146,10 @@ func (m *Manager) Run(ctx context.Context, initChan chan struct{}) error {
75146 return err
76147 }
77148
149+ // The address worker offloads the I/O heavy address creation with the
150+ // server to a separate go routine.
151+ go m .addrWorker (ctx )
152+
78153 // Communicate to the caller that the address manager has completed its
79154 // initialization.
80155 close (initChan )
@@ -95,10 +170,40 @@ func (m *Manager) Run(ctx context.Context, initChan chan struct{}) error {
95170}
96171
97172// NewAddress creates a new static address with the server or returns an
98- // existing one.
173+ // existing one. It now sends a request to the manager's Run loop which
174+ // executes the actual address creation logic.
99175func (m * Manager ) NewAddress (ctx context.Context ) (* btcutil.AddressTaproot ,
100176 int64 , error ) {
101177
178+ respChan := make (chan response , 1 )
179+ req := request {
180+ ctx : ctx ,
181+ respChan : respChan ,
182+ }
183+
184+ // Send the new address request to the manager run loop.
185+ select {
186+ case m .addrRequest <- req :
187+
188+ case <- ctx .Done ():
189+ return nil , 0 , ctx .Err ()
190+ }
191+
192+ // Wait for the response from the manager run loop.
193+ select {
194+ case resp := <- respChan :
195+ return resp .addr , resp .expiry , resp .err
196+
197+ case <- ctx .Done ():
198+ return nil , 0 , ctx .Err ()
199+ }
200+ }
201+
202+ // newAddress contains the body of the former NewAddress method and performs the
203+ // actual address creation/lookup according to the requested type.
204+ func (m * Manager ) newAddress (ctx context.Context ) (* btcutil.AddressTaproot ,
205+ int64 , error ) {
206+
102207 // If there's already a static address in the database, we can return
103208 // it.
104209 m .Lock ()
0 commit comments