Skip to content

Commit 12563d3

Browse files
committed
premium: enable flexible settings with bbolt db
Remove policy.conf management and switch to bbolt db for managing premium settings. Provide APIs for cln and lnd to interact with premium. - Implement Observer for premium updates - Poller detects updates and performs polling
1 parent 8e569e8 commit 12563d3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+3800
-2579
lines changed

.envrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
use flake

clightning/clightning.go

+9-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"time"
1212

1313
"github.com/elementsproject/peerswap/log"
14+
"github.com/elementsproject/peerswap/premium"
1415

1516
"github.com/btcsuite/btcd/chaincfg"
1617
"github.com/elementsproject/glightning/gbitcoin"
@@ -46,6 +47,10 @@ var methods = []peerswaprpcMethod{
4647
&ReloadPolicyFile{},
4748
&GetRequestedSwaps{},
4849
&ListConfig{},
50+
&GetPremiumRateRequest{},
51+
&SetPremiumRateRequest{},
52+
&GetDefaultPremiumRateRequest{},
53+
&SetDefaultPremiumRateRequest{},
4954
}
5055

5156
var devmethods = []peerswaprpcMethod{}
@@ -70,6 +75,7 @@ type ClightningClient struct {
7075
requestedSwaps *swap.RequestedSwapsPrinter
7176
policy PolicyReloader
7277
pollService *poll.Service
78+
ps *premium.Setting
7379

7480
gbitcoin *gbitcoin.Bitcoin
7581
bitcoinChain *onchain.BitcoinOnChain
@@ -326,14 +332,16 @@ func (cl *ClightningClient) GetPreimage() (lightning.Preimage, error) {
326332
func (cl *ClightningClient) SetupClients(liquidWallet wallet.Wallet,
327333
swaps *swap.SwapService,
328334
policy PolicyReloader, requestedSwaps *swap.RequestedSwapsPrinter,
329-
bitcoin *gbitcoin.Bitcoin, bitcoinChain *onchain.BitcoinOnChain, pollService *poll.Service) {
335+
bitcoin *gbitcoin.Bitcoin, bitcoinChain *onchain.BitcoinOnChain, pollService *poll.Service,
336+
ps *premium.Setting) {
330337
cl.liquidWallet = liquidWallet
331338
cl.requestedSwaps = requestedSwaps
332339
cl.swaps = swaps
333340
cl.policy = policy
334341
cl.gbitcoin = bitcoin
335342
cl.pollService = pollService
336343
cl.bitcoinChain = bitcoinChain
344+
cl.ps = ps
337345
if cl.bitcoinChain != nil {
338346
cl.bitcoinNetwork = bitcoinChain.GetChain()
339347
}

clightning/clightning_commands.go

+225-16
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
"github.com/elementsproject/peerswap/log"
1414
"github.com/elementsproject/peerswap/peerswaprpc"
15+
"github.com/elementsproject/peerswap/premium"
1516

1617
"github.com/elementsproject/glightning/glightning"
1718
"github.com/elementsproject/glightning/jrpc2"
@@ -628,11 +629,13 @@ func (l *ListPeers) Call() (jrpc2.Result, error) {
628629
SatsOut: ReceiverSatsOut,
629630
SatsIn: ReceiverSatsIn,
630631
},
631-
PaidFee: paidFees,
632-
BTCSwapInPremiumRatePPM: p.BTCSwapInPremiumRatePPM,
633-
BTCSwapOutPremiumRatePPM: p.BTCSwapOutPremiumRatePPM,
634-
LBTCSwapInPremiumRatePPM: p.LBTCSwapInPremiumRatePPM,
635-
LBTCSwapOutPremiumRatePPM: p.LBTCSwapOutPremiumRatePPM,
632+
PaidFee: paidFees,
633+
PeerPremium: &Premium{
634+
BTCSwapInPremiumRatePPM: p.BTCSwapInPremiumRatePPM,
635+
BTCSwapOutPremiumRatePPM: p.BTCSwapOutPremiumRatePPM,
636+
LBTCSwapInPremiumRatePPM: p.LBTCSwapInPremiumRatePPM,
637+
LBTCSwapOutPremiumRatePPM: p.LBTCSwapOutPremiumRatePPM,
638+
},
636639
}
637640
channels, err := l.cl.glightning.ListChannelsBySource(peer.Id)
638641
if err != nil {
@@ -1102,6 +1105,208 @@ func (c ListConfig) LongDescription() string {
11021105
return c.Description()
11031106
}
11041107

1108+
func toPremiumAssetType(asset string) premium.AssetType {
1109+
switch strings.ToUpper(asset) {
1110+
case "BTC":
1111+
return premium.BTC
1112+
case "LBTC":
1113+
return premium.LBTC
1114+
default:
1115+
return premium.AsserUnspecified
1116+
}
1117+
}
1118+
1119+
func toPremiumOperationType(operation string) premium.OperationType {
1120+
switch strings.ToUpper(operation) {
1121+
case "SWAP_IN":
1122+
return premium.SwapIn
1123+
case "SWAP_OUT":
1124+
return premium.SwapOut
1125+
default:
1126+
return premium.OperationUnspecified
1127+
}
1128+
}
1129+
1130+
type GetPremiumRateRequest struct {
1131+
PeerID string `json:"peer_id"`
1132+
Asset string `json:"asset"`
1133+
OperationType string `json:"operation"`
1134+
cl *ClightningClient
1135+
}
1136+
1137+
func (c *GetPremiumRateRequest) Name() string {
1138+
return "peerswap-getpremiumrate"
1139+
}
1140+
1141+
func (c *GetPremiumRateRequest) New() interface{} {
1142+
return &GetPremiumRateRequest{
1143+
cl: c.cl,
1144+
}
1145+
}
1146+
1147+
func (c *GetPremiumRateRequest) Call() (jrpc2.Result, error) {
1148+
if !c.cl.isReady {
1149+
return nil, ErrWaitingForReady
1150+
}
1151+
e, err := c.cl.ps.GetRate(c.PeerID, toPremiumAssetType(c.Asset),
1152+
toPremiumOperationType(c.OperationType))
1153+
if err != nil {
1154+
return nil, fmt.Errorf("error getting premium rate: %v", err)
1155+
}
1156+
return e.PremiumRatePPM().Value(), nil
1157+
}
1158+
1159+
func (c *GetPremiumRateRequest) Get(client *ClightningClient) jrpc2.ServerMethod {
1160+
return &GetPremiumRateRequest{
1161+
cl: client,
1162+
}
1163+
}
1164+
1165+
func (c GetPremiumRateRequest) Description() string {
1166+
return "Get the premium rate for a peer"
1167+
}
1168+
1169+
func (c GetPremiumRateRequest) LongDescription() string {
1170+
return c.Description()
1171+
}
1172+
1173+
type SetPremiumRateRequest struct {
1174+
PeerID string `json:"peer_id"`
1175+
Asset string `json:"asset"`
1176+
OperationType string `json:"operation"`
1177+
PremiumRatePPM int64 `json:"premium_rate_ppm"`
1178+
cl *ClightningClient
1179+
}
1180+
1181+
func (c *SetPremiumRateRequest) Name() string {
1182+
return "peerswap-setpremiumrate"
1183+
}
1184+
1185+
func (c *SetPremiumRateRequest) New() interface{} {
1186+
return &SetPremiumRateRequest{
1187+
cl: c.cl,
1188+
}
1189+
}
1190+
1191+
func (c *SetPremiumRateRequest) Call() (jrpc2.Result, error) {
1192+
if !c.cl.isReady {
1193+
return nil, ErrWaitingForReady
1194+
}
1195+
rate, err := premium.NewPremiumRate(toPremiumAssetType(c.Asset),
1196+
toPremiumOperationType(c.OperationType), premium.NewPPM(c.PremiumRatePPM))
1197+
if err != nil {
1198+
return nil, fmt.Errorf("error creating premium rate: %v", err)
1199+
}
1200+
err = c.cl.ps.SetRate(c.PeerID, rate)
1201+
if err != nil {
1202+
return nil, fmt.Errorf("error setting premium rate: %v", err)
1203+
}
1204+
return rate, nil
1205+
}
1206+
1207+
func (c *SetPremiumRateRequest) Get(client *ClightningClient) jrpc2.ServerMethod {
1208+
return &SetPremiumRateRequest{
1209+
cl: client,
1210+
}
1211+
}
1212+
1213+
func (c SetPremiumRateRequest) Description() string {
1214+
return "Set the premium rate for a peer"
1215+
}
1216+
1217+
func (c SetPremiumRateRequest) LongDescription() string {
1218+
return c.Description()
1219+
}
1220+
1221+
type SetDefaultPremiumRateRequest struct {
1222+
Asset string `json:"asset"`
1223+
OperationType string `json:"operation"`
1224+
PremiumRatePPM int64 `json:"premium_rate_ppm"`
1225+
cl *ClightningClient
1226+
}
1227+
1228+
func (c *SetDefaultPremiumRateRequest) Name() string {
1229+
return "peerswap-setdefaultpremiumrate"
1230+
}
1231+
1232+
func (c *SetDefaultPremiumRateRequest) New() interface{} {
1233+
return &SetDefaultPremiumRateRequest{
1234+
cl: c.cl,
1235+
}
1236+
}
1237+
1238+
func (c *SetDefaultPremiumRateRequest) Call() (jrpc2.Result, error) {
1239+
if !c.cl.isReady {
1240+
return nil, ErrWaitingForReady
1241+
}
1242+
rate, err := premium.NewPremiumRate(toPremiumAssetType(c.Asset),
1243+
toPremiumOperationType(c.OperationType), premium.NewPPM(c.PremiumRatePPM))
1244+
if err != nil {
1245+
return nil, fmt.Errorf("error creating premium rate: %v", err)
1246+
}
1247+
err = c.cl.ps.SetDefaultRate(rate)
1248+
if err != nil {
1249+
return nil, fmt.Errorf("error setting default premium rate: %v", err)
1250+
}
1251+
return rate, nil
1252+
}
1253+
1254+
func (c *SetDefaultPremiumRateRequest) Get(client *ClightningClient) jrpc2.ServerMethod {
1255+
return &SetDefaultPremiumRateRequest{
1256+
cl: client,
1257+
}
1258+
}
1259+
1260+
func (c SetDefaultPremiumRateRequest) Description() string {
1261+
return "Set the default premium rate"
1262+
}
1263+
1264+
func (c SetDefaultPremiumRateRequest) LongDescription() string {
1265+
return c.Description()
1266+
}
1267+
1268+
type GetDefaultPremiumRateRequest struct {
1269+
Asset string `json:"asset"`
1270+
OperationType string `json:"operation"`
1271+
cl *ClightningClient
1272+
}
1273+
1274+
func (c *GetDefaultPremiumRateRequest) Name() string {
1275+
return "peerswap-getdefaultpremiumrate"
1276+
}
1277+
1278+
func (c *GetDefaultPremiumRateRequest) New() interface{} {
1279+
return &GetDefaultPremiumRateRequest{
1280+
cl: c.cl,
1281+
}
1282+
}
1283+
1284+
func (c *GetDefaultPremiumRateRequest) Call() (jrpc2.Result, error) {
1285+
if !c.cl.isReady {
1286+
return nil, ErrWaitingForReady
1287+
}
1288+
rate, err := c.cl.ps.GetDefaultRate(toPremiumAssetType(c.Asset),
1289+
toPremiumOperationType(c.OperationType))
1290+
if err != nil {
1291+
return nil, fmt.Errorf("error getting default premium rate: %v", err)
1292+
}
1293+
return rate.PremiumRatePPM().Value(), nil
1294+
}
1295+
1296+
func (c *GetDefaultPremiumRateRequest) Get(client *ClightningClient) jrpc2.ServerMethod {
1297+
return &GetDefaultPremiumRateRequest{
1298+
cl: client,
1299+
}
1300+
}
1301+
1302+
func (c GetDefaultPremiumRateRequest) Description() string {
1303+
return "Get the default premium rate"
1304+
}
1305+
1306+
func (c GetDefaultPremiumRateRequest) LongDescription() string {
1307+
return c.Description()
1308+
}
1309+
11051310
type PeerSwapPeerChannel struct {
11061311
ChannelId string `json:"short_channel_id"`
11071312
LocalBalance uint64 `json:"local_balance"`
@@ -1116,18 +1321,22 @@ type SwapStats struct {
11161321
SatsIn uint64 `json:"total_sats_swapped_in"`
11171322
}
11181323

1324+
type Premium struct {
1325+
BTCSwapInPremiumRatePPM int64 `json:"btc_swap_in_premium_rate_ppm"`
1326+
BTCSwapOutPremiumRatePPM int64 `json:"btc_swap_out_premium_rate_ppm"`
1327+
LBTCSwapInPremiumRatePPM int64 `json:"lbtc_swap_in_premium_rate_ppm"`
1328+
LBTCSwapOutPremiumRatePPM int64 `json:"lbtc_swap_out_premium_rate_ppm"`
1329+
}
1330+
11191331
type PeerSwapPeer struct {
1120-
NodeId string `json:"nodeid"`
1121-
SwapsAllowed bool `json:"swaps_allowed"`
1122-
SupportedAssets []string `json:"supported_assets"`
1123-
Channels []*PeerSwapPeerChannel `json:"channels"`
1124-
AsSender *SwapStats `json:"sent,omitempty"`
1125-
AsReceiver *SwapStats `json:"received,omitempty"`
1126-
PaidFee uint64 `json:"total_fee_paid,omitempty"`
1127-
BTCSwapInPremiumRatePPM int64 `json:"btc_swap_in_premium_rate_ppm"`
1128-
BTCSwapOutPremiumRatePPM int64 `json:"btc_swap_out_premium_rate_ppm"`
1129-
LBTCSwapInPremiumRatePPM int64 `json:"lbtc_swap_in_premium_rate_ppm"`
1130-
LBTCSwapOutPremiumRatePPM int64 `json:"lbtc_swap_out_premium_rate_ppm"`
1332+
NodeId string `json:"nodeid"`
1333+
SwapsAllowed bool `json:"swaps_allowed"`
1334+
SupportedAssets []string `json:"supported_assets"`
1335+
Channels []*PeerSwapPeerChannel `json:"channels"`
1336+
AsSender *SwapStats `json:"sent,omitempty"`
1337+
AsReceiver *SwapStats `json:"received,omitempty"`
1338+
PaidFee uint64 `json:"total_fee_paid,omitempty"`
1339+
PeerPremium *Premium `json:"premium,omitempty"`
11311340
}
11321341

11331342
// checkFeatures checks if a node runs the peerswap Plugin

cmd/peerswap-plugin/main.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/elementsproject/peerswap/onchain"
2929
"github.com/elementsproject/peerswap/policy"
3030
"github.com/elementsproject/peerswap/poll"
31+
"github.com/elementsproject/peerswap/premium"
3132
"github.com/elementsproject/peerswap/swap"
3233
"github.com/elementsproject/peerswap/txwatcher"
3334
"github.com/elementsproject/peerswap/wallet"
@@ -327,6 +328,10 @@ func run(ctx context.Context, lightningPlugin *clightning.ClightningClient) erro
327328

328329
// Manager for send message retry.
329330
mesmgr := messages.NewManager()
331+
ps, err := premium.NewSetting(swapDb)
332+
if err != nil {
333+
return err
334+
}
330335

331336
swapServices := swap.NewSwapServices(swapStore,
332337
requestedSwapStore,
@@ -342,6 +347,7 @@ func run(ctx context.Context, lightningPlugin *clightning.ClightningClient) erro
342347
liquidOnChainService,
343348
liquidOnChainService,
344349
liquidTxWatcher,
350+
ps,
345351
)
346352
swapService := swap.NewSwapService(swapServices)
347353

@@ -370,12 +376,12 @@ func run(ctx context.Context, lightningPlugin *clightning.ClightningClient) erro
370376
if err != nil {
371377
return err
372378
}
373-
pollService := poll.NewService(1*time.Hour, 2*time.Hour, pollStore, lightningPlugin, pol, lightningPlugin, supportedAssets)
379+
pollService := poll.NewService(1*time.Hour, 2*time.Hour, pollStore, lightningPlugin, pol, lightningPlugin, supportedAssets, ps)
374380
pollService.Start()
375381
defer pollService.Stop()
376382

377383
sp := swap.NewRequestedSwapsPrinter(requestedSwapStore)
378-
lightningPlugin.SetupClients(liquidRpcWallet, swapService, pol, sp, bitcoinCli, bitcoinOnChainService, pollService)
384+
lightningPlugin.SetupClients(liquidRpcWallet, swapService, pol, sp, bitcoinCli, bitcoinOnChainService, pollService, ps)
379385

380386
// We are ready to accept and handle requests.
381387
// FIXME: Once we reworked the recovery service (non-blocking) we want to

0 commit comments

Comments
 (0)