Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
client
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Terraform modules
Analyze
Contributor analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
elixxir
client
Commits
0a142c7b
Commit
0a142c7b
authored
Mar 16, 2022
by
Josh Brooks
Browse files
Options
Downloads
Patches
Plain Diff
Add fingerprinting logic
parent
9d46ce3d
Branches
Branches containing commit
Tags
Tags containing commit
5 merge requests
!510
Release
,
!207
WIP: Client Restructure
,
!203
Symmetric broadcast
,
!187
Xx 3829/triggers
,
!186
Draft: Add fingerprinting logic
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
network/fingerprints.go
+131
-0
131 additions, 0 deletions
network/fingerprints.go
network/fingerprints_test.go
+240
-0
240 additions, 0 deletions
network/fingerprints_test.go
with
371 additions
and
0 deletions
network/fingerprints.go
0 → 100644
+
131
−
0
View file @
0a142c7b
///////////////////////////////////////////////////////////////////////////////
// Copyright © 2020 xx network SEZC //
// //
// Use of this source code is governed by a license that can be found in the //
// LICENSE file //
///////////////////////////////////////////////////////////////////////////////
package
network
import
(
"github.com/pkg/errors"
"gitlab.com/elixxir/client/interfaces"
"gitlab.com/elixxir/primitives/format"
"sync"
)
// Processor is an object which ties an interfaces.MessageProcessorFP
// to a lock. This prevents the processor from being used in multiple
// different threads.
type
Processor
struct
{
interfaces
.
MessageProcessorFP
sync
.
Mutex
}
// NewFingerprints is a constructor function for the Processor object.
func
newProcessor
(
mp
interfaces
.
MessageProcessorFP
)
*
Processor
{
return
&
Processor
{
MessageProcessorFP
:
mp
,
Mutex
:
sync
.
Mutex
{},
}
}
// Fingerprints is a thread-safe map, mapping format.Fingerprint's to
// a Processor object.
type
Fingerprints
struct
{
fingerprints
map
[
format
.
Fingerprint
]
*
Processor
sync
.
RWMutex
}
// NewFingerprints is a constructor function for the Fingerprints tracker.
func
NewFingerprints
()
*
Fingerprints
{
return
&
Fingerprints
{
fingerprints
:
make
(
map
[
format
.
Fingerprint
]
*
Processor
),
RWMutex
:
sync
.
RWMutex
{},
}
}
// Get is a thread-safe getter for the Fingerprints map. Get returns the mapped
// processor and true (representing that it exists in the map) if the provided
// fingerprint has an entry. Otherwise, Get returns nil and false.
func
(
f
*
Fingerprints
)
Get
(
fingerprint
format
.
Fingerprint
)
(
*
Processor
,
bool
)
{
f
.
RLock
()
defer
f
.
RUnlock
()
fp
,
exists
:=
f
.
fingerprints
[
fingerprint
]
if
!
exists
{
return
nil
,
false
}
return
fp
,
true
}
// AddFingerprint is a thread-safe setter for the Fingerprints map. AddFingerprint
// maps the given fingerprint key to the processor value. If there is already
// an entry for this fingerprint, the method returns with no write operation.
func
(
f
*
Fingerprints
)
AddFingerprint
(
fingerprint
format
.
Fingerprint
,
processor
interfaces
.
MessageProcessorFP
)
{
f
.
Lock
()
defer
f
.
Unlock
()
f
.
addFingerprint
(
fingerprint
,
processor
)
}
// AddFingerprints is a thread-safe setter for multiple entries into
// the Fingerprints map. If there is not a 1:1 relationship between
// fingerprints and processors slices (i.e. the lengths of these slices
// are equivalent), an error will be returned.
// Otherwise, each fingerprint is written to the associated processor.
// If there is already an entry for the given fingerprint/processor pair,
// no write operation will occur for this pair.
func
(
f
*
Fingerprints
)
AddFingerprints
(
fps
[]
format
.
Fingerprint
,
processors
[]
interfaces
.
MessageProcessorFP
)
error
{
f
.
Lock
()
defer
f
.
Unlock
()
if
len
(
fps
)
!=
len
(
processors
)
{
return
errors
.
Errorf
(
"Canot perform a batch add when there are "
+
"not an equal amount of fingerprints and processors. "
+
"Given %d fingerprints and %d processors."
,
len
(
fps
),
len
(
processors
))
}
for
i
,
fp
:=
range
fps
{
f
.
addFingerprint
(
fp
,
processors
[
i
])
}
return
nil
}
// addFingerprint is a non-thread-safe helper function which writes a Processor
// to the given fingerprint key. If an entry already exists for this fingerprint key,
// no write operation occurs.
func
(
f
*
Fingerprints
)
addFingerprint
(
fingerprint
format
.
Fingerprint
,
processor
interfaces
.
MessageProcessorFP
)
{
if
_
,
exists
:=
f
.
fingerprints
[
fingerprint
];
exists
{
return
}
newMsgProc
:=
newProcessor
(
processor
)
f
.
fingerprints
[
fingerprint
]
=
newMsgProc
}
// RemoveFingerprint is a thread-safe deletion operation on the Fingerprints map.
// It will remove the entry for the given fingerprint from the map.
func
(
f
*
Fingerprints
)
RemoveFingerprint
(
fingerprint
format
.
Fingerprint
)
{
f
.
Lock
()
defer
f
.
Unlock
()
delete
(
f
.
fingerprints
,
fingerprint
)
}
// RemoveFingerprints is a thread-safe batch deletion operation on the Fingerprints map.
// It will remove the entries for the given fingerprints from the map.
func
(
f
*
Fingerprints
)
RemoveFingerprints
(
fingerprint
[]
format
.
Fingerprint
)
{
f
.
Lock
()
defer
f
.
Unlock
()
for
_
,
fp
:=
range
fingerprint
{
delete
(
f
.
fingerprints
,
fp
)
}
}
This diff is collapsed.
Click to expand it.
network/fingerprints_test.go
0 → 100644
+
240
−
0
View file @
0a142c7b
///////////////////////////////////////////////////////////////////////////////
// Copyright © 2020 xx network SEZC //
// //
// Use of this source code is governed by a license that can be found in the //
// LICENSE file //
///////////////////////////////////////////////////////////////////////////////
package
network
import
(
jww
"github.com/spf13/jwalterweatherman"
"gitlab.com/elixxir/client/interfaces"
"gitlab.com/elixxir/primitives/format"
"reflect"
"strconv"
"sync"
"testing"
)
// Unit test.
func
TestNewFingerprints
(
t
*
testing
.
T
)
{
expected
:=
&
Fingerprints
{
fingerprints
:
make
(
map
[
format
.
Fingerprint
]
*
Processor
),
RWMutex
:
sync
.
RWMutex
{},
}
received
:=
NewFingerprints
()
if
!
reflect
.
DeepEqual
(
expected
,
received
)
{
t
.
Fatalf
(
"NewFingerprint error: Did not construct expected object."
+
"
\n
Expected: %v"
+
"
\n
Received: %v"
,
expected
,
received
)
}
}
// Unit test.
func
TestFingerprints_Get
(
t
*
testing
.
T
)
{
// Construct fingerprint map
fpTracker
:=
NewFingerprints
()
// Construct fingerprint and processor values
fp
:=
format
.
NewFingerprint
([]
byte
(
"test"
))
mp
:=
NewMockMsgProcessor
(
t
)
// Add the values to the tracker
fpTracker
.
AddFingerprint
(
fp
,
mp
)
// Attempt to retrieve value from map
received
,
exists
:=
fpTracker
.
Get
(
fp
)
if
!
exists
{
t
.
Fatalf
(
"Get error: Did not retrieve fingerprint (%s) that "
+
"should have been in map."
,
fp
)
}
// Check that received value contains the expected data
expected
:=
newProcessor
(
mp
)
if
!
reflect
.
DeepEqual
(
received
,
expected
)
{
t
.
Fatalf
(
"Get error: Map does not contain expected data."
+
"
\n
Expected: %v"
+
"
\n
Received: %v"
,
expected
,
received
)
}
}
// Unit test.
func
TestFingerprints_AddFingerprint
(
t
*
testing
.
T
)
{
// Construct fingerprint map
fpTracker
:=
NewFingerprints
()
// Construct fingerprint and processor values
fp
:=
format
.
NewFingerprint
([]
byte
(
"test"
))
mp
:=
NewMockMsgProcessor
(
t
)
// Add the values to the tracker
fpTracker
.
AddFingerprint
(
fp
,
mp
)
// Check that the fingerprint key has a map entry
received
,
exists
:=
fpTracker
.
fingerprints
[
fp
]
if
!
exists
{
t
.
Fatalf
(
"AddFingerprint did not write to map as expected. "
+
"Fingerprint %s not found in map"
,
fp
)
}
// Check that received value contains the expected data
expected
:=
newProcessor
(
mp
)
if
!
reflect
.
DeepEqual
(
received
,
expected
)
{
t
.
Fatalf
(
"AddFingerprint error: Map does not contain expected data."
+
"
\n
Expected: %v"
+
"
\n
Received: %v"
,
expected
,
received
)
}
}
// Unit test.
func
TestFingerprints_AddFingerprints
(
t
*
testing
.
T
)
{
// Construct fingerprints map
fpTracker
:=
NewFingerprints
()
// Construct slices of fingerprints and processors
numTests
:=
100
fingerprints
:=
make
([]
format
.
Fingerprint
,
0
,
numTests
)
processors
:=
make
([]
interfaces
.
MessageProcessorFP
,
0
,
numTests
)
for
i
:=
0
;
i
<
numTests
;
i
++
{
fp
:=
format
.
NewFingerprint
([]
byte
(
strconv
.
Itoa
(
i
)))
mp
:=
NewMockMsgProcessor
(
t
)
fingerprints
=
append
(
fingerprints
,
fp
)
processors
=
append
(
processors
,
mp
)
}
// Add slices to map
err
:=
fpTracker
.
AddFingerprints
(
fingerprints
,
processors
)
if
err
!=
nil
{
t
.
Fatalf
(
"AddFingerprints unexpected error: %v"
,
err
)
}
// Make sure every fingerprint is mapped to it's expected processor
for
i
,
expected
:=
range
fingerprints
{
received
,
exists
:=
fpTracker
.
fingerprints
[
expected
]
if
!
exists
{
t
.
Errorf
(
"AddFingerprints did not write to map as expected. "
+
"Fingerprint number %d (value: %s) not found in map"
,
i
,
expected
)
}
if
!
reflect
.
DeepEqual
(
received
,
expected
)
{
t
.
Fatalf
(
"AddFingerprints error: Map does not contain expected data for "
+
"fingerprint number %d."
+
"
\n
Expected: %v"
+
"
\n
Received: %v"
,
i
,
expected
,
received
)
}
}
}
// Error case: Call Fingerprints.AddFingerprints with fingerprint and processor
// slices of different lengths.
func
TestFingerprints_AddFingerprints_Error
(
t
*
testing
.
T
)
{
// Construct fingerprint map
fpTracker
:=
NewFingerprints
()
// Construct 2 slices of different lengths
fingerprints
:=
[]
format
.
Fingerprint
{
format
.
NewFingerprint
([]
byte
(
"1"
)),
format
.
NewFingerprint
([]
byte
(
"2"
)),
format
.
NewFingerprint
([]
byte
(
"3"
)),
}
processors
:=
[]
interfaces
.
MessageProcessorFP
{
NewMockMsgProcessor
(
t
),
}
// Attempt to add fingerprints
err
:=
fpTracker
.
AddFingerprints
(
fingerprints
,
processors
)
if
err
==
nil
{
t
.
Fatalf
(
"AddFingerprints should have received an error with mismatched "
+
"slices length"
)
}
}
func
TestFingerprints_RemoveFingerprint
(
t
*
testing
.
T
)
{
// Construct fingerprint map
fpTracker
:=
NewFingerprints
()
// Construct fingerprint and processor values
fp
:=
format
.
NewFingerprint
([]
byte
(
"test"
))
mp
:=
NewMockMsgProcessor
(
t
)
// Add the values to the tracker
fpTracker
.
AddFingerprint
(
fp
,
mp
)
// Remove value from tracker
fpTracker
.
RemoveFingerprint
(
fp
)
// Check that value no longer exists within the map
if
_
,
exists
:=
fpTracker
.
fingerprints
[
fp
];
exists
{
t
.
Fatalf
(
"RemoveFingerprint error: "
+
"Fingerprint %s exists in map after a RemoveFingerprint call"
,
fp
)
}
}
// Unit test.
func
TestFingerprints_RemoveFingerprints
(
t
*
testing
.
T
)
{
// Construct fingerprints map
fpTracker
:=
NewFingerprints
()
// Construct slices of fingerprints and processors
numTests
:=
100
fingerprints
:=
make
([]
format
.
Fingerprint
,
0
,
numTests
)
processors
:=
make
([]
interfaces
.
MessageProcessorFP
,
0
,
numTests
)
for
i
:=
0
;
i
<
numTests
;
i
++
{
fp
:=
format
.
NewFingerprint
([]
byte
(
strconv
.
Itoa
(
i
)))
mp
:=
NewMockMsgProcessor
(
t
)
fingerprints
=
append
(
fingerprints
,
fp
)
processors
=
append
(
processors
,
mp
)
}
// Add slices to map
err
:=
fpTracker
.
AddFingerprints
(
fingerprints
,
processors
)
if
err
!=
nil
{
t
.
Fatalf
(
"AddFingerprints unexpected error: %v"
,
err
)
}
fpTracker
.
RemoveFingerprints
(
fingerprints
)
// Make sure every fingerprint is mapped to it's expected processor
for
i
,
expected
:=
range
fingerprints
{
if
received
,
exists
:=
fpTracker
.
fingerprints
[
expected
];
!
exists
{
t
.
Fatalf
(
"RemoveFingerprints error: Map does not contain "
+
"expected data for fingerprint number %d."
+
"
\n
Expected: %v"
+
"
\n
Received: %v"
,
i
,
expected
,
received
)
}
}
}
// todo: consider moving this to a test utils somewhere else.. maybe in the interfaces package?
type
MockMsgProcessor
struct
{}
func
NewMockMsgProcessor
(
face
interface
{})
*
MockMsgProcessor
{
switch
face
.
(
type
)
{
case
*
testing
.
T
,
*
testing
.
M
,
*
testing
.
B
,
*
testing
.
PB
:
break
default
:
jww
.
FATAL
.
Panicf
(
"NewMockMsgProcessor is restricted to testing only. Got %T"
,
face
)
}
return
&
MockMsgProcessor
{}
}
func
(
mock
*
MockMsgProcessor
)
MarkFingerprintUsed
(
fingerprint
format
.
Fingerprint
)
{
return
}
func
(
mock
*
MockMsgProcessor
)
Process
(
message
format
.
Message
,
fingerprint
format
.
Fingerprint
)
{
return
}
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment