Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions loopd/swapclient_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1632,14 +1632,21 @@ func (s *swapClientServer) NewStaticAddress(ctx context.Context,
_ *looprpc.NewStaticAddressRequest) (
*looprpc.NewStaticAddressResponse, error) {

staticAddress, expiry, err := s.staticAddressManager.NewAddress(ctx)
params, err := s.staticAddressManager.NewAddress(ctx)
if err != nil {
return nil, err
}

address, err := s.staticAddressManager.GetTaprootAddress(
params.ClientPubkey, params.ServerPubkey, int64(params.Expiry),
)
if err != nil {
return nil, err
}

return &looprpc.NewStaticAddressResponse{
Address: staticAddress.String(),
Expiry: uint32(expiry),
Address: address.String(),
Expiry: params.Expiry,
}, nil
}

Expand Down
136 changes: 89 additions & 47 deletions staticaddr/address/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"bytes"
"context"
"fmt"
"sync"
"sync/atomic"

"github.com/btcsuite/btcd/btcec/v2"
Expand Down Expand Up @@ -49,11 +48,24 @@ type ManagerConfig struct {

// Manager manages the address state machines.
type Manager struct {
sync.Mutex

cfg *ManagerConfig

currentHeight atomic.Int32

// addrRequest is a channel used to request new static addresses from
// the manager. The manager employs a go worker routine that handles the
// requests.
addrRequest chan request
}

type request struct {
ctx context.Context
respChan chan response
}

type response struct {
parameters *Parameters
err error
}

// NewManager creates a new address manager.
Expand All @@ -64,14 +76,48 @@ func NewManager(cfg *ManagerConfig, currentHeight int32) (*Manager, error) {
}

m := &Manager{
cfg: cfg,
cfg: cfg,
addrRequest: make(chan request),
}
m.currentHeight.Store(currentHeight)

return m, nil
}

// Run runs the address manager.
// addrWorker is a worker that handles address creation requests. It calls
// m.newAddress which blocks on server I/O and returns the address and expiry.
func (m *Manager) addrWorker(ctx context.Context) {
for {
select {
case req := <-m.addrRequest:
m.handleAddrRequest(ctx, req)

case <-ctx.Done():
return
}
}
}

// handleAddrRequest is responsible for processing a single address request.
func (m *Manager) handleAddrRequest(managerCtx context.Context, req request) {
addrParams, err := m.newAddress(req.ctx)

resp := response{
parameters: addrParams,
err: err,
}

select {
case req.respChan <- resp:

case <-req.ctx.Done():

case <-managerCtx.Done():
}
}

// Run runs the address manager. It keeps track of the current block height and
// creates new static addresses as needed.
func (m *Manager) Run(ctx context.Context, initChan chan struct{}) error {
newBlockChan, newBlockErrChan, err :=
m.cfg.ChainNotifier.RegisterBlockEpochNtfn(ctx)
Expand All @@ -80,6 +126,10 @@ func (m *Manager) Run(ctx context.Context, initChan chan struct{}) error {
return err
}

// The address worker offloads the address creation with the server to a
// separate go routine.
go m.addrWorker(ctx)

// Communicate to the caller that the address manager has completed its
// initialization.
close(initChan)
Expand All @@ -100,49 +150,48 @@ func (m *Manager) Run(ctx context.Context, initChan chan struct{}) error {
}

// NewAddress creates a new static address with the server or returns an
// existing one.
func (m *Manager) NewAddress(ctx context.Context) (*btcutil.AddressTaproot,
int64, error) {
// existing one. It now sends a request to the manager's Run loop which
// executes the actual address creation logic.
func (m *Manager) NewAddress(ctx context.Context) (*Parameters, error) {
respChan := make(chan response, 1)
req := request{
ctx: ctx,
respChan: respChan,
}

// If there's already a static address in the database, we can return
// it.
m.Lock()
addresses, err := m.cfg.Store.GetAllStaticAddresses(ctx)
if err != nil {
m.Unlock()
// Send the new address request to the manager run loop.
select {
case m.addrRequest <- req:

return nil, 0, err
case <-ctx.Done():
return nil, ctx.Err()
}
if len(addresses) > 0 {
clientPubKey := addresses[0].ClientPubkey
serverPubKey := addresses[0].ServerPubkey
expiry := int64(addresses[0].Expiry)

defer m.Unlock()

address, err := m.GetTaprootAddress(
clientPubKey, serverPubKey, expiry,
)
if err != nil {
return nil, 0, err
}

return address, expiry, nil
// Wait for the response from the manager run loop.
select {
case resp := <-respChan:
return resp.parameters, resp.err

case <-ctx.Done():
return nil, ctx.Err()
}
m.Unlock()
}

// newAddress contains the body of the former NewAddress method and performs the
// actual address creation/lookup according to the requested type.
func (m *Manager) newAddress(ctx context.Context) (*Parameters, error) {
// We are fetching a new L402 token from the server. There is one static
// address per L402 token allowed.
err = m.cfg.FetchL402(ctx)
err := m.cfg.FetchL402(ctx)
if err != nil {
return nil, 0, err
return nil, err
}

clientPubKey, err := m.cfg.WalletKit.DeriveNextKey(
ctx, swap.StaticAddressKeyFamily,
)
if err != nil {
return nil, 0, err
return nil, err
}

// Send our clientPubKey to the server and wait for the server to
Expand All @@ -155,27 +204,27 @@ func (m *Manager) NewAddress(ctx context.Context) (*btcutil.AddressTaproot,
},
)
if err != nil {
return nil, 0, err
return nil, err
}

serverParams := resp.GetParams()

serverPubKey, err := btcec.ParsePubKey(serverParams.ServerKey)
if err != nil {
return nil, 0, err
return nil, err
}

staticAddress, err := script.NewStaticAddress(
input.MuSig2Version100RC2, int64(serverParams.Expiry),
clientPubKey.PubKey, serverPubKey,
)
if err != nil {
return nil, 0, err
return nil, err
}

pkScript, err := staticAddress.StaticAddressScript()
if err != nil {
return nil, 0, err
return nil, err
}

// Create the static address from the parameters the server provided and
Expand All @@ -196,7 +245,7 @@ func (m *Manager) NewAddress(ctx context.Context) (*btcutil.AddressTaproot,
}
err = m.cfg.Store.CreateStaticAddress(ctx, addrParams)
if err != nil {
return nil, 0, err
return nil, err
}

// Import the static address tapscript into our lnd wallet, so we can
Expand All @@ -206,20 +255,13 @@ func (m *Manager) NewAddress(ctx context.Context) (*btcutil.AddressTaproot,
)
addr, err := m.cfg.WalletKit.ImportTaprootScript(ctx, tapScript)
if err != nil {
return nil, 0, err
return nil, err
}

log.Infof("Imported static address taproot script to lnd wallet: %v",
addr)

address, err := m.GetTaprootAddress(
clientPubKey.PubKey, serverPubKey, int64(serverParams.Expiry),
)
if err != nil {
return nil, 0, err
}

return address, int64(serverParams.Expiry), nil
return addrParams, nil
}

// GetTaprootAddress returns a taproot address for the given client and server
Expand Down
11 changes: 8 additions & 3 deletions staticaddr/address/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,19 @@ func TestManager(t *testing.T) {
require.NoError(t, err)

// Create a new static address.
taprootAddress, expiry, err := testContext.manager.NewAddress(ctxb)
params, err := testContext.manager.NewAddress(ctxb)
require.NoError(t, err)

address, err := testContext.manager.GetTaprootAddress(
params.ClientPubkey, params.ServerPubkey, int64(params.Expiry),
)
require.NoError(t, err)

// The addresses have to match.
require.Equal(t, expectedAddress.String(), taprootAddress.String())
require.Equal(t, expectedAddress.String(), address.String())

// The expiry has to match.
require.EqualValues(t, defaultExpiry, expiry)
require.EqualValues(t, defaultExpiry, params.Expiry)
}

// GenerateExpectedTaprootAddress generates the expected taproot address that
Expand Down
Loading