Skip to content

Commit d0bc3c3

Browse files
committed
firewalldb: add FetchAllPairs to PrivacyMapTx interface
This commit adds a new FetchAllPairs to the PrivacyMapTx interface. This method returns a new PrivacyMapPairs struct which is an in-memory privacy map DB. The PrivacyMapPairs struct implements a new PrivacyMapReader interface which can be used to pass around read only access to the PrivacyMapPairs struct.
1 parent 9b7ed4a commit d0bc3c3

File tree

3 files changed

+204
-0
lines changed

3 files changed

+204
-0
lines changed

firewall/privacy_mapper_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,12 @@ func (m *mockPrivacyMapDB) RealToPseudo(real string) (string, error) {
555555
return p, nil
556556
}
557557

558+
func (m *mockPrivacyMapDB) FetchAllPairs() (*firewalldb.PrivacyMapPairs,
559+
error) {
560+
561+
return firewalldb.NewPrivacyMapPairs(m.r2p), nil
562+
}
563+
558564
var _ firewalldb.PrivacyMapDB = (*mockPrivacyMapDB)(nil)
559565

560566
// TestRandBetween tests random number generation for numbers in an interval.

firewalldb/privacy_mapper.go

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"math/big"
99
"strconv"
1010
"strings"
11+
"sync"
1112

1213
"github.com/lightninglabs/lightning-terminal/session"
1314
"go.etcd.io/bbolt"
@@ -78,6 +79,10 @@ type PrivacyMapTx interface {
7879
// RealToPseudo returns the pseudo value associated with the given real
7980
// value. If no such pair is found, then ErrNoSuchKeyFound is returned.
8081
RealToPseudo(real string) (string, error)
82+
83+
// FetchAllPairs loads and returns the real-to-pseudo pairs in the form
84+
// of a PrivacyMapPairs struct.
85+
FetchAllPairs() (*PrivacyMapPairs, error)
8186
}
8287

8388
// privacyMapDB is an implementation of PrivacyMapDB.
@@ -169,6 +174,8 @@ type privacyMapTx struct {
169174
}
170175

171176
// NewPair inserts a new real-pseudo pair into the db.
177+
//
178+
// NOTE: this is part of the PrivacyMapTx interface.
172179
func (p *privacyMapTx) NewPair(real, pseudo string) error {
173180
privacyBucket, err := getBucket(p.boltTx, privacyBucketKey)
174181
if err != nil {
@@ -214,6 +221,8 @@ func (p *privacyMapTx) NewPair(real, pseudo string) error {
214221

215222
// PseudoToReal will check the db to see if the given pseudo key exists. If
216223
// it does then the real value is returned, else an error is returned.
224+
//
225+
// NOTE: this is part of the PrivacyMapTx interface.
217226
func (p *privacyMapTx) PseudoToReal(pseudo string) (string, error) {
218227
privacyBucket, err := getBucket(p.boltTx, privacyBucketKey)
219228
if err != nil {
@@ -240,6 +249,8 @@ func (p *privacyMapTx) PseudoToReal(pseudo string) (string, error) {
240249

241250
// RealToPseudo will check the db to see if the given real key exists. If
242251
// it does then the pseudo value is returned, else an error is returned.
252+
//
253+
// NOTE: this is part of the PrivacyMapTx interface.
243254
func (p *privacyMapTx) RealToPseudo(real string) (string, error) {
244255
privacyBucket, err := getBucket(p.boltTx, privacyBucketKey)
245256
if err != nil {
@@ -264,6 +275,38 @@ func (p *privacyMapTx) RealToPseudo(real string) (string, error) {
264275
return string(pseudo), nil
265276
}
266277

278+
// FetchAllPairs loads and returns the real-to-pseudo pairs.
279+
//
280+
// NOTE: this is part of the PrivacyMapTx interface.
281+
func (p *privacyMapTx) FetchAllPairs() (*PrivacyMapPairs, error) {
282+
privacyBucket, err := getBucket(p.boltTx, privacyBucketKey)
283+
if err != nil {
284+
return nil, err
285+
}
286+
287+
sessBucket := privacyBucket.Bucket(p.groupID[:])
288+
if sessBucket == nil {
289+
return nil, ErrNoSuchKeyFound
290+
}
291+
292+
realToPseudoBucket := sessBucket.Bucket(realToPseudoKey)
293+
if realToPseudoBucket == nil {
294+
return nil, ErrNoSuchKeyFound
295+
}
296+
297+
pairs := make(map[string]string)
298+
err = realToPseudoBucket.ForEach(func(r, p []byte) error {
299+
pairs[string(r)] = string(p)
300+
301+
return nil
302+
})
303+
if err != nil {
304+
return nil, err
305+
}
306+
307+
return NewPrivacyMapPairs(pairs), nil
308+
}
309+
267310
func HideString(tx PrivacyMapTx, real string) (string, error) {
268311
pseudo, err := tx.RealToPseudo(real)
269312
if err != nil && err != ErrNoSuchKeyFound {
@@ -470,3 +513,73 @@ func decodeChannelPoint(cp string) (string, uint32, error) {
470513

471514
return parts[0], uint32(index), nil
472515
}
516+
517+
// PrivacyMapReader is an interface that gives read access to a privacy map
518+
// DB.
519+
type PrivacyMapReader interface {
520+
// GetPseudo returns the associated pseudo value for a given real value.
521+
// If no such real value exists in the DB, then false is returned.
522+
GetPseudo(real string) (string, bool)
523+
}
524+
525+
// PrivacyMapPairs is an in memory implementation of the PrivacyMapReader.
526+
type PrivacyMapPairs struct {
527+
// pairs is a map from real to psuedo strings.
528+
pairs map[string]string
529+
530+
mu sync.Mutex
531+
}
532+
533+
// NewPrivacyMapPairs constructs a new PrivacyMapPairs struct. It may be
534+
// initialised with either a nil map or a pre-defined real-to-pseudo strings
535+
// map.
536+
func NewPrivacyMapPairs(m map[string]string) *PrivacyMapPairs {
537+
if m != nil {
538+
return &PrivacyMapPairs{
539+
pairs: m,
540+
}
541+
}
542+
543+
return &PrivacyMapPairs{
544+
pairs: make(map[string]string),
545+
}
546+
}
547+
548+
// GetPseudo returns the associated pseudo value for a given real value. If no
549+
// such real value exists in the DB, then false is returned.
550+
//
551+
// NOTE: this is part of the PrivacyMapReader interface.
552+
func (p *PrivacyMapPairs) GetPseudo(real string) (string, bool) {
553+
p.mu.Lock()
554+
defer p.mu.Unlock()
555+
556+
pseudo, ok := p.pairs[real]
557+
558+
return pseudo, ok
559+
}
560+
561+
// Add adds the passed set of real-to-pseudo pairs to the PrivacyMapPairs
562+
// structure. It will throw an error if the new pairs conflict with any of the
563+
// existing pairs.
564+
func (p *PrivacyMapPairs) Add(pairs map[string]string) error {
565+
p.mu.Lock()
566+
defer p.mu.Unlock()
567+
568+
// Do a first pass to ensure that none of the new entries conflict with
569+
// the existing entries. We do this so that we don't mutate the set of
570+
// pairs before we know that the new set is valid.
571+
for realStr, pseudoStr := range pairs {
572+
ps, ok := p.pairs[realStr]
573+
if ok && ps != pseudoStr {
574+
return fmt.Errorf("cannot replace existing pseudo "+
575+
"entry for real value: %s", realStr)
576+
}
577+
}
578+
579+
// In our second pass, we can add the new pairs to our set.
580+
for realStr, pseudoStr := range pairs {
581+
p.pairs[realStr] = pseudoStr
582+
}
583+
584+
return nil
585+
}

firewalldb/privacy_mapper_test.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ func TestPrivacyMapStorage(t *testing.T) {
3636
require.NoError(t, err)
3737
require.Equal(t, "real", real)
3838

39+
pairs, err := tx.FetchAllPairs()
40+
require.NoError(t, err)
41+
42+
require.EqualValues(t, pairs.pairs, map[string]string{
43+
"real": "pseudo",
44+
})
45+
3946
return nil
4047
})
4148

@@ -59,6 +66,13 @@ func TestPrivacyMapStorage(t *testing.T) {
5966
require.NoError(t, err)
6067
require.Equal(t, "real 2", real)
6168

69+
pairs, err := tx.FetchAllPairs()
70+
require.NoError(t, err)
71+
72+
require.EqualValues(t, pairs.pairs, map[string]string{
73+
"real 2": "pseudo 2",
74+
})
75+
6276
return nil
6377
})
6478

@@ -81,6 +95,77 @@ func TestPrivacyMapStorage(t *testing.T) {
8195
require.ErrorContains(t, err, "an entry already exists for "+
8296
"pseudo value")
8397

98+
// Add a few more pairs.
99+
err = tx.NewPair("real 2", "pseudo 2")
100+
require.NoError(t, err)
101+
102+
err = tx.NewPair("real 3", "pseudo 3")
103+
require.NoError(t, err)
104+
105+
err = tx.NewPair("real 4", "pseudo 4")
106+
require.NoError(t, err)
107+
108+
// Check that FetchAllPairs correctly returns all the pairs.
109+
pairs, err := tx.FetchAllPairs()
110+
require.NoError(t, err)
111+
112+
require.EqualValues(t, pairs.pairs, map[string]string{
113+
"real 1": "pseudo 1",
114+
"real 2": "pseudo 2",
115+
"real 3": "pseudo 3",
116+
"real 4": "pseudo 4",
117+
})
118+
119+
// Do a few tests to ensure that the PrivacyMapPairs struct
120+
// returned from FetchAllPairs also works as expected.
121+
pseudo, ok := pairs.GetPseudo("real 1")
122+
require.True(t, ok)
123+
require.Equal(t, "pseudo 1", pseudo)
124+
125+
// Fetch a real value that is not present.
126+
_, ok = pairs.GetPseudo("real 5")
127+
require.False(t, ok)
128+
129+
// Try to add a conflicting pair.
130+
err = pairs.Add(map[string]string{"real 2": "pseudo 10"})
131+
require.ErrorContains(t, err, "cannot replace existing "+
132+
"pseudo entry for real value")
133+
134+
// Add a new pair.
135+
err = pairs.Add(map[string]string{"real 5": "pseudo 5"})
136+
require.NoError(t, err)
137+
138+
pseudo, ok = pairs.GetPseudo("real 5")
139+
require.True(t, ok)
140+
require.Equal(t, "pseudo 5", pseudo)
141+
142+
// Finally, also test adding multiple new pairs with some
143+
// overlapping with previously added pairs.
144+
err = pairs.Add(map[string]string{
145+
// Add some pairs that already exist.
146+
"real 1": "pseudo 1",
147+
"real 3": "pseudo 3",
148+
// Add some new pairs.
149+
"real 6": "pseudo 6",
150+
"real 7": "pseudo 7",
151+
})
152+
require.NoError(t, err)
153+
154+
// Verify that all the expected pairs can be found.
155+
for r, p := range map[string]string{
156+
"real 1": "pseudo 1",
157+
"real 2": "pseudo 2",
158+
"real 3": "pseudo 3",
159+
"real 4": "pseudo 4",
160+
"real 5": "pseudo 5",
161+
"real 6": "pseudo 6",
162+
"real 7": "pseudo 7",
163+
} {
164+
pseudo, ok = pairs.GetPseudo(r)
165+
require.True(t, ok)
166+
require.Equal(t, p, pseudo)
167+
}
168+
84169
return nil
85170
})
86171
}

0 commit comments

Comments
 (0)