mirror of https://github.com/lightninglabs/loop
staticaddr: static address manager
parent
126b2a3583
commit
a16258e568
@ -0,0 +1,188 @@
|
||||
package staticaddr
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/btcec/v2/schnorr"
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/lightninglabs/lndclient"
|
||||
"github.com/lightninglabs/loop"
|
||||
"github.com/lightninglabs/loop/staticaddr/script"
|
||||
"github.com/lightninglabs/loop/swap"
|
||||
staticaddressrpc "github.com/lightninglabs/loop/swapserverrpc"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
)
|
||||
|
||||
// ManagerConfig holds the configuration for the address manager.
|
||||
type ManagerConfig struct {
|
||||
// AddressClient is the client that communicates with the loop server
|
||||
// to manage static addresses.
|
||||
AddressClient staticaddressrpc.StaticAddressServerClient
|
||||
|
||||
// SwapClient provides loop rpc functionality.
|
||||
SwapClient *loop.Client
|
||||
|
||||
// Store is the database store that is used to store static address
|
||||
// related records.
|
||||
Store AddressStore
|
||||
|
||||
// WalletKit is the wallet client that is used to derive new keys from
|
||||
// lnd's wallet.
|
||||
WalletKit lndclient.WalletKitClient
|
||||
|
||||
// ChainParams is the chain configuration(mainnet, testnet...) this
|
||||
// manager uses.
|
||||
ChainParams *chaincfg.Params
|
||||
}
|
||||
|
||||
// Manager manages the address state machines.
|
||||
type Manager struct {
|
||||
cfg *ManagerConfig
|
||||
|
||||
initChan chan struct{}
|
||||
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// NewAddressManager creates a new address manager.
|
||||
func NewAddressManager(cfg *ManagerConfig) *Manager {
|
||||
return &Manager{
|
||||
cfg: cfg,
|
||||
initChan: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Run runs the address manager.
|
||||
func (m *Manager) Run(ctx context.Context) error {
|
||||
log.Debugf("Starting address manager.")
|
||||
defer log.Debugf("Address manager stopped.")
|
||||
|
||||
// Communicate to the caller that the address manager has completed its
|
||||
// initialization.
|
||||
close(m.initChan)
|
||||
|
||||
<-ctx.Done()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewAddress starts a new address creation flow.
|
||||
func (m *Manager) NewAddress(ctx context.Context) (*btcutil.AddressTaproot,
|
||||
error) {
|
||||
|
||||
// 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()
|
||||
|
||||
return nil, err
|
||||
}
|
||||
if len(addresses) > 0 {
|
||||
clientPubKey := addresses[0].ClientPubkey
|
||||
serverPubKey := addresses[0].ServerPubkey
|
||||
expiry := int64(addresses[0].Expiry)
|
||||
|
||||
m.Unlock()
|
||||
|
||||
return m.getTaprootAddress(clientPubKey, serverPubKey, expiry)
|
||||
}
|
||||
m.Unlock()
|
||||
|
||||
// We are fetching a new L402 token from the server. There is one static
|
||||
// address per L402 token allowed.
|
||||
err = m.cfg.SwapClient.Server.FetchL402(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clientPubKey, err := m.cfg.WalletKit.DeriveNextKey(
|
||||
ctx, swap.StaticAddressKeyFamily,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Send our clientPubKey to the server and wait for the server to
|
||||
// respond with he serverPubKey and the static address CSV expiry.
|
||||
protocolVersion := CurrentRPCProtocolVersion()
|
||||
resp, err := m.cfg.AddressClient.ServerNewAddress(
|
||||
ctx, &staticaddressrpc.ServerNewAddressRequest{
|
||||
ProtocolVersion: protocolVersion,
|
||||
ClientKey: clientPubKey.PubKey.SerializeCompressed(), //nolint:lll
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serverParams := resp.GetParams()
|
||||
|
||||
serverPubKey, err := btcec.ParsePubKey(serverParams.ServerKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
staticAddress, err := script.NewStaticAddress(
|
||||
input.MuSig2Version100RC2, int64(serverParams.Expiry),
|
||||
clientPubKey.PubKey, serverPubKey,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pkScript, err := staticAddress.StaticAddressScript()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create the static address from the parameters the server provided and
|
||||
// store all parameters in the database.
|
||||
addrParams := &AddressParameters{
|
||||
ClientPubkey: clientPubKey.PubKey,
|
||||
ServerPubkey: serverPubKey,
|
||||
PkScript: pkScript,
|
||||
Expiry: serverParams.Expiry,
|
||||
KeyLocator: keychain.KeyLocator{
|
||||
Family: clientPubKey.Family,
|
||||
Index: clientPubKey.Index,
|
||||
},
|
||||
ProtocolVersion: AddressProtocolVersion(protocolVersion),
|
||||
}
|
||||
err = m.cfg.Store.CreateStaticAddress(ctx, addrParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return m.getTaprootAddress(
|
||||
clientPubKey.PubKey, serverPubKey, int64(serverParams.Expiry),
|
||||
)
|
||||
}
|
||||
|
||||
func (m *Manager) getTaprootAddress(clientPubkey,
|
||||
serverPubkey *btcec.PublicKey, expiry int64) (*btcutil.AddressTaproot,
|
||||
error) {
|
||||
|
||||
staticAddress, err := script.NewStaticAddress(
|
||||
input.MuSig2Version100RC2, expiry, clientPubkey, serverPubkey,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return btcutil.NewAddressTaproot(
|
||||
schnorr.SerializePubKey(staticAddress.TaprootKey),
|
||||
m.cfg.ChainParams,
|
||||
)
|
||||
}
|
||||
|
||||
// WaitInitComplete waits until the address manager has completed its setup.
|
||||
func (m *Manager) WaitInitComplete() {
|
||||
defer log.Debugf("Address manager initiation complete.")
|
||||
<-m.initChan
|
||||
}
|
Loading…
Reference in New Issue