Commit graph

661 commits

Author SHA1 Message Date
Julian Knutsen
e7673407f1
[REFACTOR] Remove duplication in filtering functions
Introduce a generic function that can be used to filter
Map<ByteArray, PersistableNetworkPayload> or
Map<ByteArray, ProtectedStorageEntry>.

Used to deduplicate the GetData code paths and ensure the logic is the
same between the two payload types.
2019-12-03 12:20:37 -08:00
Julian Knutsen
4c5d8184b7
[REFACTOR] Inline filtering functions
Removes unnecessary calculations converting Set<byte[]> into
Set<ByteArray> and allows additional deduplication of stream operations.
2019-12-03 12:20:37 -08:00
Julian Knutsen
3aaf8a285e
[REFACTOR] Inline capability check for ProtectedStorageEntries
Move the capability check inside the stream operation. This should
improve performance slightly, but more importantly it makes the
two filter functions almost identical so they can be combined.
2019-12-03 12:20:37 -08:00
Julian Knutsen
703a9a0ddd
[REFACTOR] Move required capabilities log
Move the logging function to the common capabilities check
so it can run on both ProtectedStoragePayload and
PersistableNetworkPayload objects
2019-12-03 12:20:36 -08:00
Julian Knutsen
caf946dfe0
Remove redundant HashSet lookups in filter functions
The appendOnlyDataStoreService and map already have unique keys that
are based on the hash of the payload. This would catch instances
where:

PersistableNetworkPayload
- None: The key is based on ByteArray(payload.getHash()) which is the
        same as this check.

ProtectedStorageEntry
- Cases where multiple PSEs contain payloads that have equivalent
  hashCode(), but different data.toProtoMessage().toByteArray().
  I don't think it is a good idea to keep 2 "unique" methods on
  payloads. This is likely left over from a time when
  Payload hashCode() needed to be different than the hash of
  the payload.
2019-12-03 12:20:36 -08:00
Julian Knutsen
5630b35755
[TESTS] Unit tests of buildGetDataResponse
Write a full set of unit tests for buildGetDataResponse. This provides
a safety net with additional refactoring work.
2019-12-03 12:20:36 -08:00
Julian Knutsen
dafc76200a
[REFACTOR] Pass peerCapabilities into buildGetDataResponse
Remove the dependence on the connection object by having the handler
pass in the peer's capabilities. This now allows unit testing of
buildGetDataResponse without any connection dependencies.
2019-12-03 12:20:35 -08:00
Julian Knutsen
a6e8868563
[REFACTOR] Extract truncation logging
Move the logging that utilizes connection information into the request
handler. Now, buildGetDataResponse just returns whether or not the list
is truncated which will make it easier to test.
2019-12-03 12:20:35 -08:00
Julian Knutsen
54021551bf
[REFACTOR] Extract getDataResponse logging
Changed the log to reference getDataResponse instead of getData. Now
that we might truncate the response, it ins't true that this is exactly
what the peer asked.
2019-12-03 12:20:35 -08:00
Julian Knutsen
8208f78837
[REFACTOR] Extract connectionInfo String 2019-12-03 12:20:34 -08:00
Julian Knutsen
944b3fffbc
[REFACTOR] Introduce buildGetDataResponse
This is just a strict move of code to reduce errors.
2019-12-03 12:20:34 -08:00
Julian Knutsen
daffe6dc38
[TESTS] Add tests of GetDataRequestHandler
Add some basic sanity tests prior to the refactor to help catch issues.
2019-12-03 12:20:34 -08:00
Julian Knutsen
a927ed42ac
[TESTS] Add tests of new RequestData APIs
These are identical test cases to the requestHandler tests, but with much
fewer dependencies. The requestHandler tests will eventually be deleted,
but they are going to remain throughout development as an extra safety
net.
2019-12-03 12:20:33 -08:00
Julian Knutsen
1e814d9f1d
[REFACTOR] Introduce buildGetDataRequest variants
As part of changing the GetData path, we want to move all creation
and processing of GetData messages inside P2PDataStorage. This will allow
easier unit testing of the behavior as well as cleaner code in the
request handlers that can just focus on nonces, connections, etc.
2019-12-03 12:20:33 -08:00
Julian Knutsen
5fcd18c4b5
[TESTS] Add tests of requestData
This will allow us to push the GetData creation inside P2PDataStorage
safely.
2019-12-03 12:20:28 -08:00
Christoph Atteneder
3b384172a4
Update data stores 2019-11-27 10:26:24 +01:00
Christoph Atteneder
9c3c6182c2
Refactor checkMaxConnections (#3126)
* Change access level for checkMaxConnections to be tested

* Refactor checkMaxConnections

Fix connection limit checks so as to prevent the following warning:

> WARN  b.n.p2p.peers.PeerManager: No candidates found to remove (That
case should not be possible as we use in the last case all
connections).

* Add MockNode that allows for simulating connections

* Add PeerManagerTest

The old PeerManagerTest was located under network/p2p/routing, which is
no longer the correct location. Additionally, it was outdated so I
just removed it and added a new file under network/p2p/peers containing
tests for checkMaxConnections.

* Add testCompile dependency to core

This is necessary because bisq.network.p2p.MockNode imports
bisq.core.network.p2p.seed.DefaultSeedNodeRepository.

* Update based on review feedback

Mock the SeedNodeRepository superclass, thus eliminating the dependency
to core.
2019-11-26 15:16:07 +01:00
Christoph Atteneder
b15eb70485
(8/8) Persist changes to ProtectedStoragePayload objects implementing PersistablePayload (#3640)
* [PR COMMENTS] Make maxSequenceNumberBeforePurge final

Instead of using a subclass that overwrites a value, utilize Guice
to inject the real value of 10000 in the app and let the tests overwrite
it with their own.

* [TESTS] Clean up 'Analyze Code' warnings

Remove unused imports and clean up some access modifiers now that
the final test structure is complete

* [REFACTOR] HashMapListener::onAdded/onRemoved

Previously, this interface was called each time an item was changed. This
required listeners to understand performance implications of multiple
adds or removes in a short time span.

Instead, give each listener the ability to process a list of added or
removed entrys which can help them avoid performance issues.

This patch is just a refactor. Each listener is called once for each
ProtectedStorageEntry. Future patches will change this.

* [REFACTOR] removeFromMapAndDataStore can operate on Collections

Minor performance overhead for constructing MapEntry and Collections
of one element, but keeps the code cleaner and all removes can still
use the same logic to remove from map, delete from data store, signal
listeners, etc.

The MapEntry type is used instead of Pair since it will require less
operations when this is eventually used in the removeExpiredEntries path.

* Change removeFromMapAndDataStore to signal listeners at the end in a batch

All current users still call this one-at-a-time. But, it gives the ability
for the expire code path to remove in a batch.

* Update removeExpiredEntries to remove all items in a batch

This will cause HashMapChangedListeners to receive just one onRemoved()
call for the expire work instead of multiple onRemoved() calls for each
item.

This required a bit of updating for the remove validation in tests so
that it correctly compares onRemoved with multiple items.

* ProposalService::onProtectedDataRemoved signals listeners once on batch removes

#3143 identified an issue that tempProposals listeners were being
signaled once for each item that was removed during the P2PDataStore
operation that expired old TempProposal objects. Some of the listeners
are very expensive (ProposalListPresentation::updateLists()) which results
in large UI performance issues.

Now that the infrastructure is in place to receive updates from the
P2PDataStore in a batch, the ProposalService can apply all of the removes
received from the P2PDataStore at once. This results in only 1 onChanged()
callback for each listener.

The end result is that updateLists() is only called once and the performance
problems are reduced.

This removes the need for #3148 and those interfaces will be removed in
the next patch.

* Remove HashmapChangedListener::onBatch operations

Now that the only user of this interface has been removed, go ahead
and delete it. This is a partial revert of
f5d75c4f60 that includes the code that was
added into ProposalService that subscribed to the P2PDataStore.

* [TESTS] Regression test for #3629

Write a test that shows the incorrect behavior for #3629, the hashmap
is rebuilt from disk using the 20-byte key instead of the 32-byte key.

* [BUGFIX] Reconstruct HashMap using 32-byte key

Addresses the first half of #3629 by ensuring that the reconstructed
HashMap always has the 32-byte key for each payload.

It turns out, the TempProposalStore persists the ProtectedStorageEntrys
on-disk as a List and doesn't persist the key at all. Then, on
reconstruction, it creates the 20-byte key for its internal map.

The fix is to update the TempProposalStore to use the 32-byte key instead.
This means that all writes, reads, and reconstrution of the TempProposalStore
uses the 32-byte key which matches perfectly with the in-memory map
of the P2PDataStorage that expects 32-byte keys.

Important to note that until all seednodes receive this update, nodes
will continue to have both the 20-byte and 32-byte keys in their HashMap.

* [BUGFIX] Use 32-byte key in requestData path

Addresses the second half of #3629 by using the HashMap, not the
protectedDataStore to generate the known keys in the requestData path.

This won't have any bandwidth reduction until all seednodes have the
update and only have the 32-byte key in their HashMap.

fixes #3629

* [DEAD CODE] Remove getProtectedDataStoreMap

The only user has been migrated to getMap(). Delete it so future
development doesn't have the same 20-byte vs 32-byte key issue.

* [TESTS] Allow tests to validate SequenceNumberMap write separately

In order to implement remove-before-add behavior, we need a way to
verify that the SequenceNumberMap was the only item updated.

* Implement remove-before-add message sequence behavior

It is possible to receive a RemoveData or RemoveMailboxData message
before the relevant AddData, but the current code does not handle
it.

This results in internal state updates and signal handler's being called
when an Add is received with a lower sequence number than a previously
seen Remove.

Minor test validation changes to allow tests to specify that only the
SequenceNumberMap should be written during an operation.

* [TESTS] Allow remove() verification to be more flexible

Now that we have introduced remove-before-add, we need a way
to validate that the SequenceNumberMap was written, but nothing
else. Add this feature to the validation path.

* Broadcast remove-before-add messages to P2P network

In order to aid in propagation of remove() messages, broadcast them
in the event the remove is seen before the add.

* [TESTS] Clean up remove verification helpers

Now that there are cases where the SequenceNumberMap and Broadcast
are called, but no other internal state is updated, the existing helper
functions conflate too many decisions. Remove them in favor of explicitly
defining each state change expected.

* [BUGFIX] Fix duplicate sequence number use case (startup)

Fix a bug introduced in d484617385 that
did not properly handle a valid use case for duplicate sequence numbers.

For in-memory-only ProtectedStoragePayloads, the client nodes need a way
to reconstruct the Payloads after startup from peer and seed nodes. This
involves sending a ProtectedStorageEntry with a sequence number that
is equal to the last one the client had already seen.

This patch adds tests to confirm the bug and fix as well as the changes
necessary to allow adding of Payloads that were previously seen, but
removed during a restart.

* Clean up AtomicBoolean usage in FileManager

Although the code was correct, it was hard to understand the relationship
between the to-be-written object and the savePending flag.

Trade two dependent atomics for one and comment the code to make it more
clear for the next reader.

* [DEADCODE] Clean up FileManager.java

* [BUGFIX] Shorter delay values not taking precedence

Fix a bug in the FileManager where a saveLater called with a low delay
won't execute until the delay specified by a previous saveLater call.

The trade off here is the execution of a task that returns early vs.
losing the requested delay.

* [REFACTOR] Inline saveNowInternal

Only one caller after deadcode removal.

* [TESTS] Introduce MapStoreServiceFake

Now that we want to make changes to the MapStoreService,
it isn't sufficient to have a Fake of the ProtectedDataStoreService.

Tests now use a REAL ProtectedDataStoreService and a FAKE MapStoreService
to exercise more of the production code and allow future testing of
changes to MapStoreService.

* Persist changes to ProtectedStorageEntrys

With the addition of ProtectedStorageEntrys, there are now persistable
maps that have different payloads and the same keys. In the
ProtectedDataStoreService case, the value is the ProtectedStorageEntry
which has a createdTimeStamp, sequenceNumber, and signature that can
all change, but still contain an identical payload.

Previously, the service was only updating the on-disk representation on
the first object and never again. So, when it was recreated from disk it
would not have any of the updated metadata. This was just copied from the
append-only implementation where the value was the Payload
which was immutable.

This hasn't caused any issues to this point, but it causes strange behavior
such as always receiving seqNr==1 items from seednodes on startup. It
is good practice to keep the in-memory objects and on-disk objects in
sync and removes an unexpected failure in future dev work that expects
the same behavior as the append-only on-disk objects.

* [DEADCODE] Remove protectedDataStoreListener

There were no users.

* [DEADCODE] Remove unused methods in ProtectedDataStoreService
2019-11-26 14:47:38 +01:00
Christoph Atteneder
62aea83308
Cleanup fmxlview and javax imports (#3661)
* Remove @FxmlView from abstract view classes

* Use generic javax imports for DI

* Additional cleanup of redundant DI annotations
2019-11-26 14:36:01 +01:00
Julian Knutsen
44a11a0619
[DEADCODE] Remove unused methods in ProtectedDataStoreService 2019-11-25 17:54:52 -08:00
Julian Knutsen
3503fe3b10
[DEADCODE] Remove protectedDataStoreListener
There were no users.
2019-11-25 17:54:52 -08:00
Julian Knutsen
f3faf4bb63
Persist changes to ProtectedStorageEntrys
With the addition of ProtectedStorageEntrys, there are now persistable
maps that have different payloads and the same keys. In the
ProtectedDataStoreService case, the value is the ProtectedStorageEntry
which has a createdTimeStamp, sequenceNumber, and signature that can
all change, but still contain an identical payload.

Previously, the service was only updating the on-disk representation on
the first object and never again. So, when it was recreated from disk it
would not have any of the updated metadata. This was just copied from the
append-only implementation where the value was the Payload
which was immutable.

This hasn't caused any issues to this point, but it causes strange behavior
such as always receiving seqNr==1 items from seednodes on startup. It
is good practice to keep the in-memory objects and on-disk objects in
sync and removes an unexpected failure in future dev work that expects
the same behavior as the append-only on-disk objects.
2019-11-25 17:54:52 -08:00
Julian Knutsen
66f71e59f8
[TESTS] Introduce MapStoreServiceFake
Now that we want to make changes to the MapStoreService,
it isn't sufficient to have a Fake of the ProtectedDataStoreService.

Tests now use a REAL ProtectedDataStoreService and a FAKE MapStoreService
to exercise more of the production code and allow future testing of
changes to MapStoreService.
2019-11-25 17:54:44 -08:00
Julian Knutsen
3d571c4ca3
[BUGFIX] Fix duplicate sequence number use case (startup)
Fix a bug introduced in d484617385 that
did not properly handle a valid use case for duplicate sequence numbers.

For in-memory-only ProtectedStoragePayloads, the client nodes need a way
to reconstruct the Payloads after startup from peer and seed nodes. This
involves sending a ProtectedStorageEntry with a sequence number that
is equal to the last one the client had already seen.

This patch adds tests to confirm the bug and fix as well as the changes
necessary to allow adding of Payloads that were previously seen, but
removed during a restart.
2019-11-25 08:13:49 -08:00
Julian Knutsen
6e2ea6e3ed
[TESTS] Clean up remove verification helpers
Now that there are cases where the SequenceNumberMap and Broadcast
are called, but no other internal state is updated, the existing helper
functions conflate too many decisions. Remove them in favor of explicitly
defining each state change expected.
2019-11-22 08:42:58 -08:00
Julian Knutsen
0472ffc794
Broadcast remove-before-add messages to P2P network
In order to aid in propagation of remove() messages, broadcast them
in the event the remove is seen before the add.
2019-11-22 08:35:57 -08:00
Julian Knutsen
931c1f47b4
[TESTS] Allow remove() verification to be more flexible
Now that we have introduced remove-before-add, we need a way
to validate that the SequenceNumberMap was written, but nothing
else. Add this feature to the validation path.
2019-11-22 08:16:02 -08:00
Justin Carter
d4e7f86ff6
Use generic javax imports for DI 2019-11-22 14:50:21 +01:00
Julian Knutsen
372c26de74
Implement remove-before-add message sequence behavior
It is possible to receive a RemoveData or RemoveMailboxData message
before the relevant AddData, but the current code does not handle
it.

This results in internal state updates and signal handler's being called
when an Add is received with a lower sequence number than a previously
seen Remove.

Minor test validation changes to allow tests to specify that only the
SequenceNumberMap should be written during an operation.
2019-11-20 16:31:56 -08:00
Julian Knutsen
526aee5ed4
[TESTS] Allow tests to validate SequenceNumberMap write separately
In order to implement remove-before-add behavior, we need a way to
verify that the SequenceNumberMap was the only item updated.
2019-11-20 16:31:56 -08:00
Devin Bileck
e99d450725
Update based on review feedback
Mock the SeedNodeRepository superclass, thus eliminating the dependency
to core.
2019-11-20 16:29:06 -08:00
Julian Knutsen
793e84d888
[DEAD CODE] Remove getProtectedDataStoreMap
The only user has been migrated to getMap(). Delete it so future
development doesn't have the same 20-byte vs 32-byte key issue.
2019-11-20 16:23:41 -08:00
Julian Knutsen
455f7d2689
[BUGFIX] Use 32-byte key in requestData path
Addresses the second half of #3629 by using the HashMap, not the
protectedDataStore to generate the known keys in the requestData path.

This won't have any bandwidth reduction until all seednodes have the
update and only have the 32-byte key in their HashMap.

fixes #3629
2019-11-20 16:20:38 -08:00
Julian Knutsen
e212240b88
[BUGFIX] Reconstruct HashMap using 32-byte key
Addresses the first half of #3629 by ensuring that the reconstructed
HashMap always has the 32-byte key for each payload.

It turns out, the TempProposalStore persists the ProtectedStorageEntrys
on-disk as a List and doesn't persist the key at all. Then, on
reconstruction, it creates the 20-byte key for its internal map.

The fix is to update the TempProposalStore to use the 32-byte key instead.
This means that all writes, reads, and reconstrution of the TempProposalStore
uses the 32-byte key which matches perfectly with the in-memory map
of the P2PDataStorage that expects 32-byte keys.

Important to note that until all seednodes receive this update, nodes
will continue to have both the 20-byte and 32-byte keys in their HashMap.
2019-11-20 16:15:52 -08:00
Julian Knutsen
849155a92a
[TESTS] Regression test for #3629
Write a test that shows the incorrect behavior for #3629, the hashmap
is rebuilt from disk using the 20-byte key instead of the 32-byte key.
2019-11-19 12:39:43 -08:00
Julian Knutsen
a8139f3a04
Remove HashmapChangedListener::onBatch operations
Now that the only user of this interface has been removed, go ahead
and delete it. This is a partial revert of
f5d75c4f60 that includes the code that was
added into ProposalService that subscribed to the P2PDataStore.
2019-11-19 08:37:40 -08:00
Julian Knutsen
eae641ee73
Update removeExpiredEntries to remove all items in a batch
This will cause HashMapChangedListeners to receive just one onRemoved()
call for the expire work instead of multiple onRemoved() calls for each
item.

This required a bit of updating for the remove validation in tests so
that it correctly compares onRemoved with multiple items.
2019-11-19 08:37:39 -08:00
Julian Knutsen
489b25aa13
Change removeFromMapAndDataStore to signal listeners at the end in a batch
All current users still call this one-at-a-time. But, it gives the ability
for the expire code path to remove in a batch.
2019-11-19 08:37:38 -08:00
Julian Knutsen
4f08588717
[REFACTOR] removeFromMapAndDataStore can operate on Collections
Minor performance overhead for constructing MapEntry and Collections
of one element, but keeps the code cleaner and all removes can still
use the same logic to remove from map, delete from data store, signal
listeners, etc.

The MapEntry type is used instead of Pair since it will require less
operations when this is eventually used in the removeExpiredEntries path.
2019-11-19 08:37:38 -08:00
Julian Knutsen
b281566e14
[REFACTOR] HashMapListener::onAdded/onRemoved
Previously, this interface was called each time an item was changed. This
required listeners to understand performance implications of multiple
adds or removes in a short time span.

Instead, give each listener the ability to process a list of added or
removed entrys which can help them avoid performance issues.

This patch is just a refactor. Each listener is called once for each
ProtectedStorageEntry. Future patches will change this.
2019-11-19 08:37:38 -08:00
Julian Knutsen
3bd67bab05
[TESTS] Clean up 'Analyze Code' warnings
Remove unused imports and clean up some access modifiers now that
the final test structure is complete
2019-11-19 08:30:24 -08:00
Julian Knutsen
617585d859
[PR COMMENTS] Make maxSequenceNumberBeforePurge final
Instead of using a subclass that overwrites a value, utilize Guice
to inject the real value of 10000 in the app and let the tests overwrite
it with their own.
2019-11-19 08:30:24 -08:00
Julian Knutsen
bdfe32bd18
[BUGFIX] Validate Entry.receiversPubKey for MailboxPayloads
The remove code checks to ensure these fields match, but the add code
never did. This could lead to a situation where a MailboxStoragePayload
could be added, but never removed.
2019-11-14 10:19:28 -08:00
Julian Knutsen
9ffbcf795e
[REFACTOR] Use common path for updating map/data store on remove
Previously, the expire path, the remove path, and the onDisconnect
all used separate logic for updating the map, signaling listeners, and
removing PersistablePaylod objects from the data store. This led to a
bug where the onDisconnect path did not update the protectedDataStore.

Combine the three code paths to ensure that the same state is updated
regardless of the context.
2019-11-14 10:08:08 -08:00
Julian Knutsen
b10a603ead
[BUGFIX] Correctly remove PersistablePayload in onDisconnect path
The code to remove expired Entrys in the onDisconnect path was not
correctly removing the Entry from the protectedDataStore.

This patch adds a test that failed and fixes the bug.
2019-11-14 10:08:07 -08:00
Julian Knutsen
8ecb5b9cb1
[REFACTOR] Clean up removeExpiredEntries
* All of this work is done on the UserThread so there is no need to
clone the map.

* ArrayList objects are faster to iterate than HashSets and the data is
  guaranteed to be unique since the source is a ConcurrentHashMap

* Finding all items to remove first, then removing them all is an easier
  to read code pattern instead of removing during iteration.
2019-11-14 10:04:59 -08:00
Julian Knutsen
bdef1e46ea
Add payload safety checks in ProtectedStorageEntry
It is currently possible to construct a valid Payload object
that implements both the ProtectedStoragePayload and
PersistableNetworkPayload interfaces even though this combination is
invalid.

Instead of depending on future reviewers to catch an error, assert that
ProtectedStoragePayloads and PersistableNetworkPayloads are incompatible
as objects inside a ProtectedStorageEntry.

This allows cleanup of removeExpiredEntries that branched on this
behavior.
2019-11-14 08:42:47 -08:00
Devin Bileck
b45765feb5
Add PeerManagerTest
The old PeerManagerTest was located under network/p2p/routing, which is
no longer the correct location. Additionally, it was outdated so I
just removed it and added a new file under network/p2p/peers containing
tests for checkMaxConnections.
2019-11-12 23:30:31 -08:00
Devin Bileck
6f80b8a964
Add MockNode that allows for simulating connections 2019-11-12 23:15:57 -08:00
Julian Knutsen
c81f8a6da3
[TESTS] Rename P2PDataStoreTest
File name now reflects the actual tests run after everything
was split up.
2019-11-12 18:06:47 -08:00