Skip to content

Commit 106690b

Browse files
committed
rfq: add support for quote policy persistence and restoration
1 parent 2b3ac4f commit 106690b

File tree

4 files changed

+306
-67
lines changed

4 files changed

+306
-67
lines changed

rfq/manager.go

Lines changed: 30 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ type ManagerCfg struct {
118118
// helps us communicate custom feature bits with our peer.
119119
AuxChanNegotiator *tapfeatures.AuxChannelNegotiator
120120

121+
// PolicyStore provides persistence for agreed RFQ policies.
122+
PolicyStore PolicyStore
123+
121124
// AcceptPriceDeviationPpm is the price deviation in
122125
// parts per million that is accepted by the RFQ negotiator.
123126
//
@@ -179,34 +182,6 @@ type Manager struct {
179182
// events.
180183
acceptHtlcEvents chan *AcceptHtlcEvent
181184

182-
// peerAcceptedBuyQuotes holds buy quotes for assets that our node has
183-
// requested and that have been accepted by peer nodes. These quotes are
184-
// exclusively used by our node for the acquisition of assets, as they
185-
// represent agreed-upon terms for purchase transactions with our peers.
186-
peerAcceptedBuyQuotes lnutils.SyncMap[SerialisedScid, rfqmsg.BuyAccept]
187-
188-
// peerAcceptedSellQuotes holds sell quotes for assets that our node has
189-
// requested and that have been accepted by peer nodes. These quotes are
190-
// exclusively used by our node for the sale of assets, as they
191-
// represent agreed-upon terms for sale transactions with our peers.
192-
peerAcceptedSellQuotes lnutils.SyncMap[
193-
SerialisedScid, rfqmsg.SellAccept,
194-
]
195-
196-
// localAcceptedBuyQuotes holds buy quotes for assets that our node has
197-
// accepted and that have been requested by peer nodes. These quotes are
198-
// exclusively used by our node for the acquisition of assets, as they
199-
// represent agreed-upon terms for purchase transactions with our peers.
200-
localAcceptedBuyQuotes lnutils.SyncMap[SerialisedScid, rfqmsg.BuyAccept]
201-
202-
// localAcceptedSellQuotes holds sell quotes for assets that our node
203-
// has accepted and that have been requested by peer nodes. These quotes
204-
// are exclusively used by our node for the sale of assets, as they
205-
// represent agreed-upon terms for sale transactions with our peers.
206-
localAcceptedSellQuotes lnutils.SyncMap[
207-
SerialisedScid, rfqmsg.SellAccept,
208-
]
209-
210185
// groupKeyLookupCache is a map that helps us quickly perform an
211186
// in-memory look up of the group an asset belongs to. Since this
212187
// information is static and generated during minting, it is not
@@ -234,10 +209,6 @@ func NewManager(cfg ManagerCfg) (*Manager, error) {
234209
outgoingMessages: make(chan rfqmsg.OutgoingMsg),
235210

236211
acceptHtlcEvents: make(chan *AcceptHtlcEvent),
237-
peerAcceptedBuyQuotes: lnutils.SyncMap[
238-
SerialisedScid, rfqmsg.BuyAccept]{},
239-
peerAcceptedSellQuotes: lnutils.SyncMap[
240-
SerialisedScid, rfqmsg.SellAccept]{},
241212

242213
subscribers: lnutils.SyncMap[
243214
uint64, *fn.EventReceiver[fn.Event]]{},
@@ -264,13 +235,14 @@ func (m *Manager) startSubsystems(ctx context.Context) error {
264235
SpecifierChecker: m.AssetMatchesSpecifier,
265236
NoOpHTLCs: m.cfg.NoOpHTLCs,
266237
AuxChanNegotiator: m.cfg.AuxChanNegotiator,
238+
PolicyStore: m.cfg.PolicyStore,
267239
})
268240
if err != nil {
269241
return fmt.Errorf("error initializing RFQ order handler: %w",
270242
err)
271243
}
272244

273-
if err := m.orderHandler.Start(); err != nil {
245+
if err := m.orderHandler.Start(ctx); err != nil {
274246
return fmt.Errorf("unable to start RFQ order handler: %w", err)
275247
}
276248

@@ -435,7 +407,7 @@ func (m *Manager) handleIncomingMessage(incomingMsg rfqmsg.IncomingMsg) error {
435407
// quote so that it can be used to send a payment by our
436408
// lightning node.
437409
scid := msg.ShortChannelId()
438-
m.peerAcceptedBuyQuotes.Store(scid, msg)
410+
m.orderHandler.peerAcceptedBuyQuotes.Store(scid, msg)
439411

440412
// Since we're going to buy assets from our peer, we
441413
// need to make sure we can identify the incoming asset
@@ -488,7 +460,7 @@ func (m *Manager) handleIncomingMessage(incomingMsg rfqmsg.IncomingMsg) error {
488460
// quote so that it can be used to send a payment by our
489461
// lightning node.
490462
scid := msg.ShortChannelId()
491-
m.peerAcceptedSellQuotes.Store(scid, msg)
463+
m.orderHandler.peerAcceptedSellQuotes.Store(scid, msg)
492464

493465
// Notify subscribers of the incoming peer accepted
494466
// asset sell quote.
@@ -522,16 +494,16 @@ func (m *Manager) handleOutgoingMessage(outgoingMsg rfqmsg.OutgoingMsg) error {
522494
// we inform our peer of our decision, we inform the order
523495
// handler that we are willing to sell the asset subject to a
524496
// sale policy.
525-
m.orderHandler.RegisterAssetSalePolicy(*msg)
526-
527-
// We want to store that we accepted the buy quote, in case we
528-
// need to look it up for a direct peer payment.
529-
m.localAcceptedBuyQuotes.Store(msg.ShortChannelId(), *msg)
497+
err := m.orderHandler.RegisterAssetSalePolicy(*msg)
498+
if err != nil {
499+
return fmt.Errorf("error registering asset sale "+
500+
"policy: %w", err)
501+
}
530502

531503
// Since our peer is going to buy assets from us, we need to
532504
// make sure we can identify the forwarded asset payment by the
533505
// outgoing SCID alias within the onion packet.
534-
err := m.addScidAlias(
506+
err = m.addScidAlias(
535507
uint64(msg.ShortChannelId()),
536508
msg.Request.AssetSpecifier, msg.Peer,
537509
)
@@ -545,11 +517,11 @@ func (m *Manager) handleOutgoingMessage(outgoingMsg rfqmsg.OutgoingMsg) error {
545517
// we inform our peer of our decision, we inform the order
546518
// handler that we are willing to buy the asset subject to a
547519
// purchase policy.
548-
m.orderHandler.RegisterAssetPurchasePolicy(*msg)
549-
550-
// We want to store that we accepted the sell quote, in case we
551-
// need to look it up for a direct peer payment.
552-
m.localAcceptedSellQuotes.Store(msg.ShortChannelId(), *msg)
520+
err := m.orderHandler.RegisterAssetPurchasePolicy(*msg)
521+
if err != nil {
522+
return fmt.Errorf("error registering asset purchase "+
523+
"policy: %w", err)
524+
}
553525
}
554526

555527
// Send the outgoing message to the peer.
@@ -842,10 +814,11 @@ func (m *Manager) PeerAcceptedBuyQuotes() BuyAcceptMap {
842814
// Returning the map directly is not thread safe. We will therefore
843815
// create a copy.
844816
buyQuotesCopy := make(map[SerialisedScid]rfqmsg.BuyAccept)
845-
m.peerAcceptedBuyQuotes.ForEach(
817+
m.orderHandler.peerAcceptedBuyQuotes.ForEach(
846818
func(scid SerialisedScid, accept rfqmsg.BuyAccept) error {
847819
if time.Now().After(accept.AssetRate.Expiry) {
848-
m.peerAcceptedBuyQuotes.Delete(scid)
820+
//nolint:lll
821+
m.orderHandler.peerAcceptedBuyQuotes.Delete(scid)
849822
return nil
850823
}
851824

@@ -864,10 +837,11 @@ func (m *Manager) PeerAcceptedSellQuotes() SellAcceptMap {
864837
// Returning the map directly is not thread safe. We will therefore
865838
// create a copy.
866839
sellQuotesCopy := make(map[SerialisedScid]rfqmsg.SellAccept)
867-
m.peerAcceptedSellQuotes.ForEach(
840+
m.orderHandler.peerAcceptedSellQuotes.ForEach(
868841
func(scid SerialisedScid, accept rfqmsg.SellAccept) error {
869842
if time.Now().After(accept.AssetRate.Expiry) {
870-
m.peerAcceptedSellQuotes.Delete(scid)
843+
//nolint:lll
844+
m.orderHandler.peerAcceptedSellQuotes.Delete(scid)
871845
return nil
872846
}
873847

@@ -886,10 +860,11 @@ func (m *Manager) LocalAcceptedBuyQuotes() BuyAcceptMap {
886860
// Returning the map directly is not thread safe. We will therefore
887861
// create a copy.
888862
buyQuotesCopy := make(map[SerialisedScid]rfqmsg.BuyAccept)
889-
m.localAcceptedBuyQuotes.ForEach(
863+
m.orderHandler.localAcceptedBuyQuotes.ForEach(
890864
func(scid SerialisedScid, accept rfqmsg.BuyAccept) error {
891865
if time.Now().After(accept.AssetRate.Expiry) {
892-
m.localAcceptedBuyQuotes.Delete(scid)
866+
//nolint:lll
867+
m.orderHandler.localAcceptedBuyQuotes.Delete(scid)
893868
return nil
894869
}
895870

@@ -908,10 +883,11 @@ func (m *Manager) LocalAcceptedSellQuotes() SellAcceptMap {
908883
// Returning the map directly is not thread safe. We will therefore
909884
// create a copy.
910885
sellQuotesCopy := make(map[SerialisedScid]rfqmsg.SellAccept)
911-
m.localAcceptedSellQuotes.ForEach(
886+
m.orderHandler.localAcceptedSellQuotes.ForEach(
912887
func(scid SerialisedScid, accept rfqmsg.SellAccept) error {
913888
if time.Now().After(accept.AssetRate.Expiry) {
914-
m.localAcceptedSellQuotes.Delete(scid)
889+
//nolint:lll
890+
m.orderHandler.localAcceptedSellQuotes.Delete(scid)
915891
return nil
916892
}
917893

rfq/manager_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import (
44
"context"
55
"encoding/binary"
66
"testing"
7+
"time"
78

89
"github.com/btcsuite/btcd/btcec/v2"
910
"github.com/decred/dcrd/dcrec/secp256k1/v4"
1011
"github.com/lightninglabs/lndclient"
1112
"github.com/lightninglabs/taproot-assets/address"
1213
"github.com/lightninglabs/taproot-assets/asset"
1314
"github.com/lightninglabs/taproot-assets/proof"
15+
"github.com/lightninglabs/taproot-assets/rfqmsg"
1416
tpchmsg "github.com/lightninglabs/taproot-assets/tapchannelmsg"
1517
"github.com/lightningnetwork/lnd/lnwallet"
1618
"github.com/lightningnetwork/lnd/routing/route"
@@ -59,6 +61,32 @@ var (
5961
peer2 = route.Vertex{77}
6062
)
6163

64+
type mockPolicyStore struct{}
65+
66+
func (mockPolicyStore) StoreSalePolicy(context.Context,
67+
rfqmsg.BuyAccept) error {
68+
69+
return nil
70+
}
71+
72+
func (mockPolicyStore) StorePurchasePolicy(context.Context,
73+
rfqmsg.SellAccept) error {
74+
75+
return nil
76+
}
77+
78+
func (mockPolicyStore) FetchAcceptedQuotes(context.Context) (
79+
[]rfqmsg.BuyAccept, []rfqmsg.SellAccept, error) {
80+
81+
return nil, nil, nil
82+
}
83+
84+
func (mockPolicyStore) DeactivateQuote(context.Context,
85+
rfqmsg.ID, time.Time) error {
86+
87+
return nil
88+
}
89+
6290
// GroupLookupMock mocks the GroupLookup interface that is required by the
6391
// rfq manager to check asset IDs against asset specifiers.
6492
type GroupLookupMock struct{}
@@ -141,6 +169,7 @@ func assertComputeChannelAssetBalance(t *testing.T,
141169
mockGroupLookup := &GroupLookupMock{}
142170
cfg := ManagerCfg{
143171
GroupLookup: mockGroupLookup,
172+
PolicyStore: mockPolicyStore{},
144173
}
145174
manager, err := NewManager(cfg)
146175
require.NoError(t, err)

0 commit comments

Comments
 (0)