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.
This commit is contained in:
Elle Mouton 2023-08-31 13:50:11 +02:00
parent 9b7ed4a4d9
commit d0bc3c37f1
No known key found for this signature in database
GPG key ID: D7D916376026F177
3 changed files with 204 additions and 0 deletions

View file

@ -555,6 +555,12 @@ func (m *mockPrivacyMapDB) RealToPseudo(real string) (string, error) {
return p, nil
}
func (m *mockPrivacyMapDB) FetchAllPairs() (*firewalldb.PrivacyMapPairs,
error) {
return firewalldb.NewPrivacyMapPairs(m.r2p), nil
}
var _ firewalldb.PrivacyMapDB = (*mockPrivacyMapDB)(nil)
// TestRandBetween tests random number generation for numbers in an interval.

View file

@ -8,6 +8,7 @@ import (
"math/big"
"strconv"
"strings"
"sync"
"github.com/lightninglabs/lightning-terminal/session"
"go.etcd.io/bbolt"
@ -78,6 +79,10 @@ type PrivacyMapTx interface {
// RealToPseudo returns the pseudo value associated with the given real
// value. If no such pair is found, then ErrNoSuchKeyFound is returned.
RealToPseudo(real string) (string, error)
// FetchAllPairs loads and returns the real-to-pseudo pairs in the form
// of a PrivacyMapPairs struct.
FetchAllPairs() (*PrivacyMapPairs, error)
}
// privacyMapDB is an implementation of PrivacyMapDB.
@ -169,6 +174,8 @@ type privacyMapTx struct {
}
// NewPair inserts a new real-pseudo pair into the db.
//
// NOTE: this is part of the PrivacyMapTx interface.
func (p *privacyMapTx) NewPair(real, pseudo string) error {
privacyBucket, err := getBucket(p.boltTx, privacyBucketKey)
if err != nil {
@ -214,6 +221,8 @@ func (p *privacyMapTx) NewPair(real, pseudo string) error {
// PseudoToReal will check the db to see if the given pseudo key exists. If
// it does then the real value is returned, else an error is returned.
//
// NOTE: this is part of the PrivacyMapTx interface.
func (p *privacyMapTx) PseudoToReal(pseudo string) (string, error) {
privacyBucket, err := getBucket(p.boltTx, privacyBucketKey)
if err != nil {
@ -240,6 +249,8 @@ func (p *privacyMapTx) PseudoToReal(pseudo string) (string, error) {
// RealToPseudo will check the db to see if the given real key exists. If
// it does then the pseudo value is returned, else an error is returned.
//
// NOTE: this is part of the PrivacyMapTx interface.
func (p *privacyMapTx) RealToPseudo(real string) (string, error) {
privacyBucket, err := getBucket(p.boltTx, privacyBucketKey)
if err != nil {
@ -264,6 +275,38 @@ func (p *privacyMapTx) RealToPseudo(real string) (string, error) {
return string(pseudo), nil
}
// FetchAllPairs loads and returns the real-to-pseudo pairs.
//
// NOTE: this is part of the PrivacyMapTx interface.
func (p *privacyMapTx) FetchAllPairs() (*PrivacyMapPairs, error) {
privacyBucket, err := getBucket(p.boltTx, privacyBucketKey)
if err != nil {
return nil, err
}
sessBucket := privacyBucket.Bucket(p.groupID[:])
if sessBucket == nil {
return nil, ErrNoSuchKeyFound
}
realToPseudoBucket := sessBucket.Bucket(realToPseudoKey)
if realToPseudoBucket == nil {
return nil, ErrNoSuchKeyFound
}
pairs := make(map[string]string)
err = realToPseudoBucket.ForEach(func(r, p []byte) error {
pairs[string(r)] = string(p)
return nil
})
if err != nil {
return nil, err
}
return NewPrivacyMapPairs(pairs), nil
}
func HideString(tx PrivacyMapTx, real string) (string, error) {
pseudo, err := tx.RealToPseudo(real)
if err != nil && err != ErrNoSuchKeyFound {
@ -470,3 +513,73 @@ func decodeChannelPoint(cp string) (string, uint32, error) {
return parts[0], uint32(index), nil
}
// PrivacyMapReader is an interface that gives read access to a privacy map
// DB.
type PrivacyMapReader interface {
// GetPseudo returns the associated pseudo value for a given real value.
// If no such real value exists in the DB, then false is returned.
GetPseudo(real string) (string, bool)
}
// PrivacyMapPairs is an in memory implementation of the PrivacyMapReader.
type PrivacyMapPairs struct {
// pairs is a map from real to psuedo strings.
pairs map[string]string
mu sync.Mutex
}
// NewPrivacyMapPairs constructs a new PrivacyMapPairs struct. It may be
// initialised with either a nil map or a pre-defined real-to-pseudo strings
// map.
func NewPrivacyMapPairs(m map[string]string) *PrivacyMapPairs {
if m != nil {
return &PrivacyMapPairs{
pairs: m,
}
}
return &PrivacyMapPairs{
pairs: make(map[string]string),
}
}
// GetPseudo returns the associated pseudo value for a given real value. If no
// such real value exists in the DB, then false is returned.
//
// NOTE: this is part of the PrivacyMapReader interface.
func (p *PrivacyMapPairs) GetPseudo(real string) (string, bool) {
p.mu.Lock()
defer p.mu.Unlock()
pseudo, ok := p.pairs[real]
return pseudo, ok
}
// Add adds the passed set of real-to-pseudo pairs to the PrivacyMapPairs
// structure. It will throw an error if the new pairs conflict with any of the
// existing pairs.
func (p *PrivacyMapPairs) Add(pairs map[string]string) error {
p.mu.Lock()
defer p.mu.Unlock()
// Do a first pass to ensure that none of the new entries conflict with
// the existing entries. We do this so that we don't mutate the set of
// pairs before we know that the new set is valid.
for realStr, pseudoStr := range pairs {
ps, ok := p.pairs[realStr]
if ok && ps != pseudoStr {
return fmt.Errorf("cannot replace existing pseudo "+
"entry for real value: %s", realStr)
}
}
// In our second pass, we can add the new pairs to our set.
for realStr, pseudoStr := range pairs {
p.pairs[realStr] = pseudoStr
}
return nil
}

View file

@ -36,6 +36,13 @@ func TestPrivacyMapStorage(t *testing.T) {
require.NoError(t, err)
require.Equal(t, "real", real)
pairs, err := tx.FetchAllPairs()
require.NoError(t, err)
require.EqualValues(t, pairs.pairs, map[string]string{
"real": "pseudo",
})
return nil
})
@ -59,6 +66,13 @@ func TestPrivacyMapStorage(t *testing.T) {
require.NoError(t, err)
require.Equal(t, "real 2", real)
pairs, err := tx.FetchAllPairs()
require.NoError(t, err)
require.EqualValues(t, pairs.pairs, map[string]string{
"real 2": "pseudo 2",
})
return nil
})
@ -81,6 +95,77 @@ func TestPrivacyMapStorage(t *testing.T) {
require.ErrorContains(t, err, "an entry already exists for "+
"pseudo value")
// Add a few more pairs.
err = tx.NewPair("real 2", "pseudo 2")
require.NoError(t, err)
err = tx.NewPair("real 3", "pseudo 3")
require.NoError(t, err)
err = tx.NewPair("real 4", "pseudo 4")
require.NoError(t, err)
// Check that FetchAllPairs correctly returns all the pairs.
pairs, err := tx.FetchAllPairs()
require.NoError(t, err)
require.EqualValues(t, pairs.pairs, map[string]string{
"real 1": "pseudo 1",
"real 2": "pseudo 2",
"real 3": "pseudo 3",
"real 4": "pseudo 4",
})
// Do a few tests to ensure that the PrivacyMapPairs struct
// returned from FetchAllPairs also works as expected.
pseudo, ok := pairs.GetPseudo("real 1")
require.True(t, ok)
require.Equal(t, "pseudo 1", pseudo)
// Fetch a real value that is not present.
_, ok = pairs.GetPseudo("real 5")
require.False(t, ok)
// Try to add a conflicting pair.
err = pairs.Add(map[string]string{"real 2": "pseudo 10"})
require.ErrorContains(t, err, "cannot replace existing "+
"pseudo entry for real value")
// Add a new pair.
err = pairs.Add(map[string]string{"real 5": "pseudo 5"})
require.NoError(t, err)
pseudo, ok = pairs.GetPseudo("real 5")
require.True(t, ok)
require.Equal(t, "pseudo 5", pseudo)
// Finally, also test adding multiple new pairs with some
// overlapping with previously added pairs.
err = pairs.Add(map[string]string{
// Add some pairs that already exist.
"real 1": "pseudo 1",
"real 3": "pseudo 3",
// Add some new pairs.
"real 6": "pseudo 6",
"real 7": "pseudo 7",
})
require.NoError(t, err)
// Verify that all the expected pairs can be found.
for r, p := range map[string]string{
"real 1": "pseudo 1",
"real 2": "pseudo 2",
"real 3": "pseudo 3",
"real 4": "pseudo 4",
"real 5": "pseudo 5",
"real 6": "pseudo 6",
"real 7": "pseudo 7",
} {
pseudo, ok = pairs.GetPseudo(r)
require.True(t, ok)
require.Equal(t, p, pseudo)
}
return nil
})
}