Skip to content

Commit e4b5853

Browse files
committed
assets: add manager
1 parent 2ff22f2 commit e4b5853

File tree

2 files changed

+229
-0
lines changed

2 files changed

+229
-0
lines changed

assets/manager.go

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
package assets
2+
3+
import (
4+
"context"
5+
"sync"
6+
"time"
7+
8+
"github.com/lightninglabs/lndclient"
9+
"github.com/lightninglabs/loop/fsm"
10+
loop_rpc "github.com/lightninglabs/loop/swapserverrpc"
11+
"github.com/lightninglabs/loop/utils"
12+
"github.com/lightninglabs/taproot-assets/taprpc"
13+
"github.com/lightningnetwork/lnd/lntypes"
14+
)
15+
16+
const (
17+
// ClientKeyFamily is the key family for the assets swap client.
18+
ClientKeyFamily = 696969
19+
)
20+
21+
// Config holds the configuration for the assets swap manager.
22+
type Config struct {
23+
AssetClient *TapdClient
24+
Wallet lndclient.WalletKitClient
25+
// ExchangeRateProvider is the exchange rate provider.
26+
ExchangeRateProvider *FixedExchangeRateProvider
27+
Signer lndclient.SignerClient
28+
ChainNotifier lndclient.ChainNotifierClient
29+
Router lndclient.RouterClient
30+
LndClient lndclient.LightningClient
31+
Store *PostgresStore
32+
ServerClient loop_rpc.AssetsSwapServerClient
33+
}
34+
35+
// AssetsSwapManager handles the lifecycle of asset swaps.
36+
type AssetsSwapManager struct {
37+
cfg *Config
38+
39+
expiryManager *utils.ExpiryManager
40+
txConfManager *utils.TxSubscribeConfirmationManager
41+
42+
blockHeight int32
43+
runCtx context.Context
44+
activeSwapOuts map[lntypes.Hash]*OutFSM
45+
46+
sync.Mutex
47+
}
48+
49+
// NewAssetSwapServer creates a new assets swap manager.
50+
func NewAssetSwapServer(config *Config) *AssetsSwapManager {
51+
return &AssetsSwapManager{
52+
cfg: config,
53+
54+
activeSwapOuts: make(map[lntypes.Hash]*OutFSM),
55+
}
56+
}
57+
58+
// Run is the main loop for the assets swap manager.
59+
func (m *AssetsSwapManager) Run(ctx context.Context, blockHeight int32) error {
60+
m.runCtx = ctx
61+
m.blockHeight = blockHeight
62+
63+
// Get our tapd client info.
64+
tapdInfo, err := m.cfg.AssetClient.GetInfo(
65+
ctx, &taprpc.GetInfoRequest{},
66+
)
67+
if err != nil {
68+
return err
69+
}
70+
log.Infof("Tapd info: %v", tapdInfo)
71+
72+
// Create our subscriptionManagers.
73+
m.expiryManager = utils.NewExpiryManager(m.cfg.ChainNotifier)
74+
m.txConfManager = utils.NewTxSubscribeConfirmationManager(
75+
m.cfg.ChainNotifier,
76+
)
77+
78+
// Start the expiry manager.
79+
errChan := make(chan error, 1)
80+
wg := &sync.WaitGroup{}
81+
wg.Add(1)
82+
go func() {
83+
defer wg.Done()
84+
err := m.expiryManager.Start(ctx, blockHeight)
85+
if err != nil {
86+
log.Errorf("Expiry manager failed: %v", err)
87+
errChan <- err
88+
}
89+
}()
90+
91+
// Recover all the active asset swap outs from the database.
92+
err = m.recoverSwapOuts(ctx)
93+
if err != nil {
94+
return err
95+
}
96+
97+
for {
98+
select {
99+
case err := <-errChan:
100+
return err
101+
102+
case <-ctx.Done():
103+
wg.Wait()
104+
return nil
105+
}
106+
}
107+
}
108+
109+
// NewSwapOut creates a new asset swap out using the amount and asset id.
110+
// It will wait for the fsm to be in the payprepay state before returning.
111+
func (m *AssetsSwapManager) NewSwapOut(ctx context.Context,
112+
amt uint64, asset []byte) (*OutFSM, error) {
113+
114+
// Create a new out fsm.
115+
outFSM := NewOutFSM(m.getFSMOutConfig())
116+
117+
// Send the initial event to the fsm.
118+
err := outFSM.SendEvent(
119+
m.runCtx, OnRequestAssetOut, &InitSwapOutContext{
120+
Amount: amt,
121+
AssetId: asset,
122+
BlockHeightHint: uint32(m.blockHeight),
123+
},
124+
)
125+
if err != nil {
126+
return nil, err
127+
}
128+
// Check if the fsm has an error.
129+
if outFSM.LastActionError != nil {
130+
return nil, outFSM.LastActionError
131+
}
132+
133+
// Wait for the fsm to be in the state we expect.
134+
err = outFSM.DefaultObserver.WaitForState(
135+
ctx, time.Second*15, PayPrepay,
136+
fsm.WithAbortEarlyOnErrorOption(),
137+
)
138+
if err != nil {
139+
return nil, err
140+
}
141+
142+
// Add the swap to the active swap outs.
143+
m.Lock()
144+
m.activeSwapOuts[outFSM.SwapOut.SwapHash] = outFSM
145+
m.Unlock()
146+
147+
return outFSM, nil
148+
}
149+
150+
// recoverSwapOuts recovers all the active asset swap outs from the database.
151+
func (m *AssetsSwapManager) recoverSwapOuts(ctx context.Context) error {
152+
// Fetch all the active asset swap outs from the database.
153+
activeSwapOuts, err := m.cfg.Store.GetActiveAssetOuts(ctx)
154+
if err != nil {
155+
return err
156+
}
157+
158+
for _, swapOut := range activeSwapOuts {
159+
log.Debugf("Recovering asset out %v with state %v",
160+
swapOut.SwapHash, swapOut.State)
161+
162+
swapOutFSM := NewOutFSMFromSwap(
163+
m.getFSMOutConfig(), swapOut,
164+
)
165+
166+
m.Lock()
167+
m.activeSwapOuts[swapOut.SwapHash] = swapOutFSM
168+
m.Unlock()
169+
170+
// As SendEvent can block, we'll start a goroutine to process
171+
// the event.
172+
go func() {
173+
err := swapOutFSM.SendEvent(ctx, OnRecover, nil)
174+
if err != nil {
175+
log.Errorf("FSM %v Error sending recover "+
176+
"event %v, state: %v",
177+
swapOutFSM.SwapOut.SwapHash,
178+
err, swapOutFSM.SwapOut.State)
179+
}
180+
}()
181+
}
182+
183+
return nil
184+
}
185+
186+
// getFSMOutConfig returns a fsmconfig from the manager.
187+
func (m *AssetsSwapManager) getFSMOutConfig() *FSMConfig {
188+
return &FSMConfig{
189+
TapdClient: m.cfg.AssetClient,
190+
AssetClient: m.cfg.ServerClient,
191+
BlockHeightSubscriber: m.expiryManager,
192+
TxConfSubscriber: m.txConfManager,
193+
ExchangeRateProvider: m.cfg.ExchangeRateProvider,
194+
Wallet: m.cfg.Wallet,
195+
196+
Store: m.cfg.Store,
197+
Signer: m.cfg.Signer,
198+
}
199+
}
200+
201+
// ListSwapOuts lists all the asset swap outs in the database.
202+
func (m *AssetsSwapManager) ListSwapOuts(ctx context.Context) ([]*SwapOut,
203+
error) {
204+
205+
return m.cfg.Store.GetAllAssetOuts(ctx)
206+
}

assets/rateprovider.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package assets
2+
3+
import "github.com/btcsuite/btcd/btcutil"
4+
5+
const (
6+
fixedPrice = 100
7+
)
8+
9+
// FixedExchangeRateProvider is a fixed exchange rate provider.
10+
type FixedExchangeRateProvider struct {
11+
}
12+
13+
// NewFixedExchangeRateProvider creates a new fixed exchange rate provider.
14+
func NewFixedExchangeRateProvider() *FixedExchangeRateProvider {
15+
return &FixedExchangeRateProvider{}
16+
}
17+
18+
// GetSatsPerAssetUnit returns the fixed price in sats per asset unit.
19+
func (e *FixedExchangeRateProvider) GetSatsPerAssetUnit(assetId []byte) (
20+
btcutil.Amount, error) {
21+
22+
return btcutil.Amount(fixedPrice), nil
23+
}

0 commit comments

Comments
 (0)