Skip to content

Commit 25aae89

Browse files
committed
rules: let RealToPseudo take in a priv map DB
This commit expands the RealToPseudo methods to take in a privacy map db. This allows the methods to check if the privacy map db already contains an entry for a "real" string before generating a new one.
1 parent ac5e019 commit 25aae89

File tree

9 files changed

+261
-16
lines changed

9 files changed

+261
-16
lines changed

rules/chan_policy_bounds.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,8 @@ func (f *ChanPolicyBounds) PseudoToReal(_ firewalldb.PrivacyMapDB) (Values,
329329
// that should be persisted. This is a no-op for the ChanPolicyBounds rule.
330330
//
331331
// NOTE: this is part of the Values interface.
332-
func (f *ChanPolicyBounds) RealToPseudo() (Values, map[string]string, error) {
332+
func (f *ChanPolicyBounds) RealToPseudo(_ firewalldb.PrivacyMapDB) (Values,
333+
map[string]string, error) {
334+
333335
return f, nil, nil
334336
}

rules/channel_restrictions.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -360,17 +360,24 @@ func (c *ChannelRestrict) PseudoToReal(db firewalldb.PrivacyMapDB) (Values,
360360
}, nil
361361
}
362362

363-
// RealToPseudo converts all the channel IDs into pseudo IDs.
363+
// RealToPseudo converts all the channel IDs into pseudo IDs. It returns a map
364+
// of any _new_ real to pseudo strings that should be persisted.
364365
//
365366
// NOTE: this is part of the Values interface.
366-
func (c *ChannelRestrict) RealToPseudo() (Values, map[string]string, error) {
367+
func (c *ChannelRestrict) RealToPseudo(db firewalldb.PrivacyMapDB) (Values,
368+
map[string]string, error) {
369+
367370
pseudoIDs := make([]uint64, len(c.DenyList))
368371
privMapPairs := make(map[string]string)
369372
for i, c := range c.DenyList {
370373
// TODO(elle): check that this channel actually exists
371374

372375
chanID := firewalldb.Uint64ToStr(c)
373-
if pseudo, ok := privMapPairs[chanID]; ok {
376+
377+
pseudo, ok, err := pseudoFromReal(db, privMapPairs, chanID)
378+
if err != nil {
379+
return nil, nil, err
380+
} else if ok {
374381
p, err := firewalldb.StrToUint64(pseudo)
375382
if err != nil {
376383
return nil, nil, err

rules/channel_restrictions_test.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"testing"
99

1010
"github.com/btcsuite/btcd/chaincfg/chainhash"
11+
"github.com/lightninglabs/lightning-terminal/firewall/mock"
1112
"github.com/lightninglabs/lightning-terminal/firewalldb"
1213
"github.com/lightninglabs/lndclient"
1314
"github.com/lightningnetwork/lnd/lnrpc"
@@ -161,3 +162,91 @@ func (m *mockLndClient) ListChannels(_ context.Context, _, _ bool) (
161162

162163
return m.channels, nil
163164
}
165+
166+
// TestChannelRestrictRealToPseudo tests that the ChannelRestrict's RealToPseudo
167+
// method correctly determines which real strings to generate pseudo pairs for
168+
// based on the privacy map db passed to it.
169+
func TestChannelRestrictRealToPseudo(t *testing.T) {
170+
chanID1 := firewalldb.Uint64ToStr(1)
171+
chanID2 := firewalldb.Uint64ToStr(2)
172+
chanID3 := firewalldb.Uint64ToStr(3)
173+
chanID2Obfuscated := firewalldb.Uint64ToStr(200)
174+
175+
tests := []struct {
176+
name string
177+
dbPreLoad map[string]string
178+
expectNewPairs map[string]bool
179+
}{
180+
{
181+
// If there is no preloaded DB, then we expect all the
182+
// values in the deny list to be returned from the
183+
// RealToPseudo method.
184+
name: "no pre loaded db",
185+
expectNewPairs: map[string]bool{
186+
chanID1: true,
187+
chanID2: true,
188+
chanID3: true,
189+
},
190+
},
191+
{
192+
// If the DB is preloaded with an entry for "channel 2"
193+
// then we don't expect that entry to be returned in the
194+
// set of new pairs.
195+
name: "partially pre-loaded DB",
196+
dbPreLoad: map[string]string{
197+
chanID2: chanID2Obfuscated,
198+
},
199+
expectNewPairs: map[string]bool{
200+
chanID1: true,
201+
chanID3: true,
202+
},
203+
},
204+
}
205+
206+
cr := &ChannelRestrict{
207+
DenyList: []uint64{
208+
1,
209+
2,
210+
3,
211+
},
212+
}
213+
214+
for _, test := range tests {
215+
test := test
216+
t.Run(test.name, func(t *testing.T) {
217+
t.Parallel()
218+
219+
var privDB firewalldb.PrivacyMapDB
220+
if len(test.dbPreLoad) != 0 {
221+
privDB = mock.NewPrivacyMapDB()
222+
}
223+
224+
var expectedDenyList []string
225+
for r, p := range test.dbPreLoad {
226+
err := privDB.View(
227+
func(tx firewalldb.PrivacyMapTx) error {
228+
return tx.NewPair(r, p)
229+
},
230+
)
231+
require.NoError(t, err)
232+
233+
expectedDenyList = append(expectedDenyList, p)
234+
}
235+
236+
v, newPairs, err := cr.RealToPseudo(privDB)
237+
require.NoError(t, err)
238+
require.Len(t, newPairs, len(test.expectNewPairs))
239+
240+
for r, p := range newPairs {
241+
require.True(t, test.expectNewPairs[r])
242+
243+
expectedDenyList = append(expectedDenyList, p)
244+
}
245+
246+
denyList, ok := v.(*ChannelRestrict)
247+
require.True(t, ok)
248+
249+
require.EqualValues(t, v, denyList)
250+
})
251+
}
252+
}

rules/history_limit.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,8 @@ func (h *HistoryLimit) PseudoToReal(_ firewalldb.PrivacyMapDB) (Values,
266266
// that should be persisted. This is a no-op for the HistoryLimit rule.
267267
//
268268
// NOTE: this is part of the Values interface.
269-
func (h *HistoryLimit) RealToPseudo() (Values, map[string]string, error) {
269+
func (h *HistoryLimit) RealToPseudo(_ firewalldb.PrivacyMapDB) (Values,
270+
map[string]string, error) {
271+
270272
return h, nil, nil
271273
}

rules/interfaces.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,10 @@ type Values interface {
5959
ToProto() *litrpc.RuleValue
6060

6161
// RealToPseudo converts the rule Values to a new one that uses pseudo
62-
// keys, channel IDs, channel points etc. It returns a map of real to
63-
// pseudo strings that should be persisted.
64-
RealToPseudo() (Values, map[string]string, error)
62+
// keys, channel IDs, channel points etc. It returns a map of any _new_
63+
// real to pseudo strings that should be persisted.
64+
RealToPseudo(db firewalldb.PrivacyMapDB) (Values, map[string]string,
65+
error)
6566

6667
// PseudoToReal attempts to convert any appropriate pseudo fields in
6768
// the rule Values to their corresponding real values. It uses the

rules/peer_restrictions.go

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package rules
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"sync"
78

@@ -367,22 +368,28 @@ func (c *PeerRestrict) PseudoToReal(db firewalldb.PrivacyMapDB) (Values,
367368
}, nil
368369
}
369370

370-
// RealToPseudo converts all the real peer IDs into pseudo IDs.
371+
// RealToPseudo converts all the real peer IDs into pseudo IDs. It returns a map
372+
// of any _new_ real to pseudo strings that should be persisted.
371373
//
372374
// NOTE: this is part of the Values interface.
373-
func (c *PeerRestrict) RealToPseudo() (Values, map[string]string, error) {
375+
func (c *PeerRestrict) RealToPseudo(db firewalldb.PrivacyMapDB) (Values,
376+
map[string]string, error) {
377+
374378
pseudoIDs := make([]string, len(c.DenyList))
375379
privMapPairs := make(map[string]string)
376380
for i, id := range c.DenyList {
377381
// TODO(elle): check that this peer is actually one of our
378382
// channel peers.
379383

380-
if pseudo, ok := privMapPairs[id]; ok {
384+
pseudo, ok, err := pseudoFromReal(db, privMapPairs, id)
385+
if err != nil {
386+
return nil, nil, err
387+
} else if ok {
381388
pseudoIDs[i] = pseudo
382389
continue
383390
}
384391

385-
pseudo, err := firewalldb.NewPseudoStr(len(id))
392+
pseudo, err = firewalldb.NewPseudoStr(len(id))
386393
if err != nil {
387394
return nil, nil, err
388395
}
@@ -393,3 +400,52 @@ func (c *PeerRestrict) RealToPseudo() (Values, map[string]string, error) {
393400

394401
return &PeerRestrict{DenyList: pseudoIDs}, privMapPairs, nil
395402
}
403+
404+
// pseudoFromReal is a helper that can be used to get the associated pseudo
405+
// value for a given real value from either the privacy map db if it is defined
406+
// or from a set of real-to-pseudo pairs.
407+
func pseudoFromReal(db firewalldb.PrivacyMapDB,
408+
privMapPairs map[string]string, real string) (string, bool, error) {
409+
410+
pseudo, ok := privMapPairs[real]
411+
412+
switch {
413+
// If the db is nil, then only the map needs to be checked.
414+
case db == nil:
415+
return pseudo, ok, nil
416+
417+
// If the value was found in the map, then return that.
418+
case ok:
419+
return pseudo, true, nil
420+
421+
// Otherwise, the DB is checked.
422+
default:
423+
}
424+
425+
err := db.View(func(tx firewalldb.PrivacyMapTx) error {
426+
var err error
427+
pseudo, err = tx.RealToPseudo(real)
428+
switch {
429+
// If the error is nil, an entry exists.
430+
case err == nil:
431+
ok = true
432+
433+
return nil
434+
435+
// Return any unexpected error.
436+
case !errors.Is(err, firewalldb.ErrNoSuchKeyFound):
437+
return err
438+
439+
// No key found.
440+
default:
441+
ok = false
442+
}
443+
444+
return nil
445+
})
446+
if err != nil {
447+
return "", false, err
448+
}
449+
450+
return pseudo, ok, nil
451+
}

rules/peer_restrictions_test.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"testing"
88

99
"github.com/btcsuite/btcd/chaincfg/chainhash"
10+
"github.com/lightninglabs/lightning-terminal/firewall/mock"
1011
"github.com/lightninglabs/lightning-terminal/firewalldb"
1112
"github.com/lightninglabs/lndclient"
1213
"github.com/lightningnetwork/lnd/lnrpc"
@@ -150,3 +151,86 @@ func TestPeerRestrictCheckRequest(t *testing.T) {
150151
)
151152
require.NoError(t, err)
152153
}
154+
155+
// TestPeerRestrictionRealToPseudo tests that the PeerRestriction's RealToPseudo
156+
// method correctly determines which real strings to generate pseudo pairs for
157+
// based on the privacy map db passed to it.
158+
func TestPeerRestrictRealToPseudo(t *testing.T) {
159+
tests := []struct {
160+
name string
161+
dbPreLoad map[string]string
162+
expectNewPairs map[string]bool
163+
}{
164+
{
165+
// If there is no preloaded DB, then we expect all the
166+
// values in the deny list to be returned from the
167+
// RealToPseudo method.
168+
name: "no pre loaded db",
169+
expectNewPairs: map[string]bool{
170+
"peer 1": true,
171+
"peer 2": true,
172+
"peer 3": true,
173+
},
174+
},
175+
{
176+
// If the DB is preloaded with an entry for "peer 2"
177+
// then we don't expect that entry to be returned in the
178+
// set of new pairs.
179+
name: "partially pre-loaded DB",
180+
dbPreLoad: map[string]string{
181+
"peer 2": "obfuscated peer 2",
182+
},
183+
expectNewPairs: map[string]bool{
184+
"peer 1": true,
185+
"peer 3": true,
186+
},
187+
},
188+
}
189+
190+
pr := &PeerRestrict{
191+
DenyList: []string{
192+
"peer 1",
193+
"peer 2",
194+
"peer 3",
195+
},
196+
}
197+
198+
for _, test := range tests {
199+
test := test
200+
t.Run(test.name, func(t *testing.T) {
201+
t.Parallel()
202+
203+
var privDB firewalldb.PrivacyMapDB
204+
if len(test.dbPreLoad) != 0 {
205+
privDB = mock.NewPrivacyMapDB()
206+
}
207+
208+
var expectedDenyList []string
209+
for r, p := range test.dbPreLoad {
210+
err := privDB.View(
211+
func(tx firewalldb.PrivacyMapTx) error {
212+
return tx.NewPair(r, p)
213+
},
214+
)
215+
require.NoError(t, err)
216+
217+
expectedDenyList = append(expectedDenyList, p)
218+
}
219+
220+
v, newPairs, err := pr.RealToPseudo(privDB)
221+
require.NoError(t, err)
222+
require.Len(t, newPairs, len(test.expectNewPairs))
223+
224+
for r, p := range newPairs {
225+
require.True(t, test.expectNewPairs[r])
226+
227+
expectedDenyList = append(expectedDenyList, p)
228+
}
229+
230+
denyList, ok := v.(*PeerRestrict)
231+
require.True(t, ok)
232+
233+
require.EqualValues(t, v, denyList)
234+
})
235+
}
236+
}

rules/rate_limit.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -273,10 +273,12 @@ func (r *RateLimit) PseudoToReal(_ firewalldb.PrivacyMapDB) (Values,
273273
}
274274

275275
// RealToPseudo converts the rule Values to a new one that uses pseudo keys,
276-
// channel IDs, channel points etc. It returns a map of real to pseudo strings
277-
// that should be persisted. This is a no-op for the RateLimit rule.
276+
// channel IDs, channel points etc. It returns a map of any new real to pseudo
277+
// strings that should be persisted. This is a no-op for the RateLimit rule.
278278
//
279279
// NOTE: this is part of the Values interface.
280-
func (r *RateLimit) RealToPseudo() (Values, map[string]string, error) {
280+
func (r *RateLimit) RealToPseudo(_ firewalldb.PrivacyMapDB) (Values,
281+
map[string]string, error) {
282+
281283
return r, nil, nil
282284
}

session_rpcserver.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -882,7 +882,9 @@ func (s *sessionRpcServer) AddAutopilotSession(ctx context.Context,
882882

883883
if privacy {
884884
var privMapPairs map[string]string
885-
v, privMapPairs, err = v.RealToPseudo()
885+
v, privMapPairs, err = v.RealToPseudo(
886+
nil,
887+
)
886888
if err != nil {
887889
return nil, err
888890
}

0 commit comments

Comments
 (0)