mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 01:41:11 +01:00
Merge pull request #3747 from julianknutsen/clean-up-pse-objs
(6/6) Clean up technical debt in P2PDataStorage and ProtectedStorageEntry objects
This commit is contained in:
commit
3fe84975ad
@ -20,7 +20,7 @@ package bisq.core.account.sign;
|
||||
import bisq.network.p2p.storage.P2PDataStorage;
|
||||
import bisq.network.p2p.storage.payload.CapabilityRequiringPayload;
|
||||
import bisq.network.p2p.storage.payload.DateTolerantPayload;
|
||||
import bisq.network.p2p.storage.payload.LazyProcessedPayload;
|
||||
import bisq.network.p2p.storage.payload.ProcessOncePersistableNetworkPayload;
|
||||
import bisq.network.p2p.storage.payload.PersistableNetworkPayload;
|
||||
|
||||
import bisq.common.app.Capabilities;
|
||||
@ -45,7 +45,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
// Supports signatures made from EC key (arbitrators) and signature created with DSA key.
|
||||
@Slf4j
|
||||
@Value
|
||||
public class SignedWitness implements LazyProcessedPayload, PersistableNetworkPayload, PersistableEnvelope,
|
||||
public class SignedWitness implements ProcessOncePersistableNetworkPayload, PersistableNetworkPayload, PersistableEnvelope,
|
||||
DateTolerantPayload, CapabilityRequiringPayload {
|
||||
|
||||
public enum VerificationMethod {
|
||||
|
@ -19,7 +19,7 @@ package bisq.core.account.witness;
|
||||
|
||||
import bisq.network.p2p.storage.P2PDataStorage;
|
||||
import bisq.network.p2p.storage.payload.DateTolerantPayload;
|
||||
import bisq.network.p2p.storage.payload.LazyProcessedPayload;
|
||||
import bisq.network.p2p.storage.payload.ProcessOncePersistableNetworkPayload;
|
||||
import bisq.network.p2p.storage.payload.PersistableNetworkPayload;
|
||||
|
||||
import bisq.common.proto.persistable.PersistableEnvelope;
|
||||
@ -40,7 +40,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
// so only the newly added objects since the last release will be retrieved over the P2P network.
|
||||
@Slf4j
|
||||
@Value
|
||||
public class AccountAgeWitness implements LazyProcessedPayload, PersistableNetworkPayload, PersistableEnvelope, DateTolerantPayload {
|
||||
public class AccountAgeWitness implements ProcessOncePersistableNetworkPayload, PersistableNetworkPayload, PersistableEnvelope, DateTolerantPayload {
|
||||
private static final long TOLERANCE = TimeUnit.DAYS.toMillis(1);
|
||||
|
||||
private final byte[] hash; // Ripemd160(Sha256(concatenated accountHash, signature and sigPubKey)); 20 bytes
|
||||
|
@ -127,7 +127,7 @@ public class AlertManager {
|
||||
if (isKeyValid) {
|
||||
signAndAddSignatureToAlertMessage(alert);
|
||||
user.setDevelopersAlert(alert);
|
||||
boolean result = p2PService.addProtectedStorageEntry(alert, true);
|
||||
boolean result = p2PService.addProtectedStorageEntry(alert);
|
||||
if (result) {
|
||||
log.trace("Add alertMessage to network was successful. AlertMessage={}", alert);
|
||||
}
|
||||
@ -139,7 +139,7 @@ public class AlertManager {
|
||||
public boolean removeAlertMessageIfKeyIsValid(String privKeyString) {
|
||||
Alert alert = user.getDevelopersAlert();
|
||||
if (isKeyValid(privKeyString) && alert != null) {
|
||||
if (p2PService.removeData(alert, true))
|
||||
if (p2PService.removeData(alert))
|
||||
log.trace("Remove alertMessage from network was successful. AlertMessage={}", alert);
|
||||
|
||||
user.setDevelopersAlert(null);
|
||||
|
@ -163,7 +163,7 @@ public class MyProposalListService implements PersistedDataHost, DaoStateListene
|
||||
|
||||
public boolean remove(Proposal proposal) {
|
||||
if (canRemoveProposal(proposal, daoStateService, periodService)) {
|
||||
boolean success = p2PService.removeData(new TempProposalPayload(proposal, signaturePubKey), true);
|
||||
boolean success = p2PService.removeData(new TempProposalPayload(proposal, signaturePubKey));
|
||||
if (!success)
|
||||
log.warn("Removal of proposal from p2p network failed. proposal={}", proposal);
|
||||
|
||||
@ -214,7 +214,7 @@ public class MyProposalListService implements PersistedDataHost, DaoStateListene
|
||||
}
|
||||
|
||||
private boolean addToP2PNetworkAsProtectedData(Proposal proposal) {
|
||||
return p2PService.addProtectedStorageEntry(new TempProposalPayload(proposal, signaturePubKey), true);
|
||||
return p2PService.addProtectedStorageEntry(new TempProposalPayload(proposal, signaturePubKey));
|
||||
}
|
||||
|
||||
private void rePublishMyProposalsOnceWellConnected() {
|
||||
|
@ -20,7 +20,7 @@ package bisq.core.dao.governance.proposal.storage.temp;
|
||||
import bisq.core.dao.state.model.governance.Proposal;
|
||||
|
||||
import bisq.network.p2p.storage.payload.ExpirablePayload;
|
||||
import bisq.network.p2p.storage.payload.LazyProcessedPayload;
|
||||
import bisq.network.p2p.storage.payload.ProcessOncePersistableNetworkPayload;
|
||||
import bisq.network.p2p.storage.payload.ProtectedStoragePayload;
|
||||
|
||||
import bisq.common.crypto.Sig;
|
||||
@ -55,7 +55,7 @@ import javax.annotation.concurrent.Immutable;
|
||||
@Getter
|
||||
@EqualsAndHashCode
|
||||
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
|
||||
public class TempProposalPayload implements LazyProcessedPayload, ProtectedStoragePayload,
|
||||
public class TempProposalPayload implements ProcessOncePersistableNetworkPayload, ProtectedStoragePayload,
|
||||
ExpirablePayload, PersistablePayload {
|
||||
|
||||
protected final Proposal proposal;
|
||||
|
@ -156,7 +156,7 @@ public abstract class StateNetworkService<Msg extends NewStateHashMessage,
|
||||
|
||||
public void broadcastMyStateHash(StH myStateHash) {
|
||||
NewStateHashMessage newStateHashMessage = getNewStateHashMessage(myStateHash);
|
||||
broadcaster.broadcast(newStateHashMessage, networkNode.getNodeAddress(), null, true);
|
||||
broadcaster.broadcast(newStateHashMessage, networkNode.getNodeAddress(), null);
|
||||
}
|
||||
|
||||
public void requestHashes(int fromHeight, String peersAddress) {
|
||||
|
@ -105,7 +105,7 @@ public class FullNodeNetworkService implements MessageListener, PeerManager.List
|
||||
log.info("Publish new block at height={} and block hash={}", block.getHeight(), block.getHash());
|
||||
RawBlock rawBlock = RawBlock.fromBlock(block);
|
||||
NewBlockBroadcastMessage newBlockBroadcastMessage = new NewBlockBroadcastMessage(rawBlock);
|
||||
broadcaster.broadcast(newBlockBroadcastMessage, networkNode.getNodeAddress(), null, true);
|
||||
broadcaster.broadcast(newBlockBroadcastMessage, networkNode.getNodeAddress(), null);
|
||||
}
|
||||
|
||||
|
||||
|
@ -238,7 +238,7 @@ public class LiteNodeNetworkService implements MessageListener, ConnectionListen
|
||||
log.debug("We received a new message from peer {} and broadcast it to our peers. extBlockId={}",
|
||||
connection.getPeersNodeAddressOptional().orElse(null), extBlockId);
|
||||
receivedBlocks.add(extBlockId);
|
||||
broadcaster.broadcast(newBlockBroadcastMessage, networkNode.getNodeAddress(), null, false);
|
||||
broadcaster.broadcast(newBlockBroadcastMessage, connection.getPeersNodeAddressOptional().orElse(null), null);
|
||||
listeners.forEach(listener -> listener.onNewBlockReceived(newBlockBroadcastMessage));
|
||||
} else {
|
||||
log.debug("We had that message already and do not further broadcast it. extBlockId={}", extBlockId);
|
||||
|
@ -265,7 +265,7 @@ public class FilterManager {
|
||||
signAndAddSignatureToFilter(filter);
|
||||
user.setDevelopersFilter(filter);
|
||||
|
||||
boolean result = p2PService.addProtectedStorageEntry(filter, true);
|
||||
boolean result = p2PService.addProtectedStorageEntry(filter);
|
||||
if (result)
|
||||
log.trace("Add filter to network was successful. FilterMessage = {}", filter);
|
||||
|
||||
@ -278,7 +278,7 @@ public class FilterManager {
|
||||
Filter filter = user.getDevelopersFilter();
|
||||
if (filter == null) {
|
||||
log.warn("Developers filter is null");
|
||||
} else if (p2PService.removeData(filter, true)) {
|
||||
} else if (p2PService.removeData(filter)) {
|
||||
log.trace("Remove filter from network was successful. FilterMessage = {}", filter);
|
||||
user.setDevelopersFilter(null);
|
||||
} else {
|
||||
|
@ -148,7 +148,7 @@ public class OfferBookService {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean result = p2PService.addProtectedStorageEntry(offer.getOfferPayload(), true);
|
||||
boolean result = p2PService.addProtectedStorageEntry(offer.getOfferPayload());
|
||||
if (result) {
|
||||
resultHandler.handleResult();
|
||||
} else {
|
||||
@ -164,7 +164,7 @@ public class OfferBookService {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean result = p2PService.refreshTTL(offerPayload, true);
|
||||
boolean result = p2PService.refreshTTL(offerPayload);
|
||||
if (result) {
|
||||
resultHandler.handleResult();
|
||||
} else {
|
||||
@ -187,7 +187,7 @@ public class OfferBookService {
|
||||
public void removeOffer(OfferPayload offerPayload,
|
||||
@Nullable ResultHandler resultHandler,
|
||||
@Nullable ErrorMessageHandler errorMessageHandler) {
|
||||
if (p2PService.removeData(offerPayload, true)) {
|
||||
if (p2PService.removeData(offerPayload)) {
|
||||
if (resultHandler != null)
|
||||
resultHandler.handleResult();
|
||||
} else {
|
||||
|
@ -64,7 +64,7 @@ public abstract class DisputeAgentService<T extends DisputeAgent> {
|
||||
log.debug("addDisputeAgent disputeAgent.hashCode() " + disputeAgent.hashCode());
|
||||
if (!BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ||
|
||||
!Utilities.encodeToHex(disputeAgent.getRegistrationPubKey()).equals(DevEnv.DEV_PRIVILEGE_PUB_KEY)) {
|
||||
boolean result = p2PService.addProtectedStorageEntry(disputeAgent, true);
|
||||
boolean result = p2PService.addProtectedStorageEntry(disputeAgent);
|
||||
if (result) {
|
||||
log.trace("Add disputeAgent to network was successful. DisputeAgent.hashCode() = " + disputeAgent.hashCode());
|
||||
resultHandler.handleResult();
|
||||
@ -81,7 +81,7 @@ public abstract class DisputeAgentService<T extends DisputeAgent> {
|
||||
ResultHandler resultHandler,
|
||||
ErrorMessageHandler errorMessageHandler) {
|
||||
log.debug("removeDisputeAgent disputeAgent.hashCode() " + disputeAgent.hashCode());
|
||||
if (p2PService.removeData(disputeAgent, true)) {
|
||||
if (p2PService.removeData(disputeAgent)) {
|
||||
log.trace("Remove disputeAgent from network was successful. DisputeAgent.hashCode() = " + disputeAgent.hashCode());
|
||||
resultHandler.handleResult();
|
||||
} else {
|
||||
|
@ -24,7 +24,7 @@ import bisq.core.monetary.Volume;
|
||||
import bisq.core.offer.OfferPayload;
|
||||
|
||||
import bisq.network.p2p.storage.payload.ExpirablePayload;
|
||||
import bisq.network.p2p.storage.payload.LazyProcessedPayload;
|
||||
import bisq.network.p2p.storage.payload.ProcessOncePersistableNetworkPayload;
|
||||
import bisq.network.p2p.storage.payload.ProtectedStoragePayload;
|
||||
|
||||
import bisq.common.crypto.Sig;
|
||||
@ -60,7 +60,7 @@ import javax.annotation.Nullable;
|
||||
@Slf4j
|
||||
@EqualsAndHashCode(exclude = {"signaturePubKeyBytes"})
|
||||
@Value
|
||||
public final class TradeStatistics implements LazyProcessedPayload, ProtectedStoragePayload, ExpirablePayload, PersistablePayload {
|
||||
public final class TradeStatistics implements ProcessOncePersistableNetworkPayload, ProtectedStoragePayload, ExpirablePayload, PersistablePayload {
|
||||
private final OfferPayload.Direction direction;
|
||||
private final String baseCurrency;
|
||||
private final String counterCurrency;
|
||||
|
@ -25,7 +25,7 @@ import bisq.core.offer.OfferPayload;
|
||||
import bisq.core.offer.OfferUtil;
|
||||
|
||||
import bisq.network.p2p.storage.payload.CapabilityRequiringPayload;
|
||||
import bisq.network.p2p.storage.payload.LazyProcessedPayload;
|
||||
import bisq.network.p2p.storage.payload.ProcessOncePersistableNetworkPayload;
|
||||
import bisq.network.p2p.storage.payload.PersistableNetworkPayload;
|
||||
|
||||
import bisq.common.app.Capabilities;
|
||||
@ -63,7 +63,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
@Slf4j
|
||||
@Value
|
||||
public final class TradeStatistics2 implements LazyProcessedPayload, PersistableNetworkPayload, PersistableEnvelope, CapabilityRequiringPayload {
|
||||
public final class TradeStatistics2 implements ProcessOncePersistableNetworkPayload, PersistableNetworkPayload, PersistableEnvelope, CapabilityRequiringPayload {
|
||||
|
||||
//We don't support arbitrators anymore so this entry will be only for pre v1.2. trades
|
||||
@Deprecated
|
||||
|
@ -221,20 +221,15 @@ public class P2PMarketStats extends P2PSeedNodeSnapshotBase {
|
||||
versions.log(protectedStoragePayload);
|
||||
});
|
||||
|
||||
Set<PersistableNetworkPayload> persistableNetworkPayloadSet = dataResponse
|
||||
.getPersistableNetworkPayloadSet();
|
||||
if (persistableNetworkPayloadSet != null) {
|
||||
persistableNetworkPayloadSet.forEach(persistableNetworkPayload -> {
|
||||
dataResponse.getPersistableNetworkPayloadSet().forEach(persistableNetworkPayload -> {
|
||||
// memorize message hashes
|
||||
//Byte[] bytes = new Byte[persistableNetworkPayload.getHash().length];
|
||||
//Arrays.setAll(bytes, n -> persistableNetworkPayload.getHash()[n]);
|
||||
|
||||
// memorize message hashes
|
||||
//Byte[] bytes = new Byte[persistableNetworkPayload.getHash().length];
|
||||
//Arrays.setAll(bytes, n -> persistableNetworkPayload.getHash()[n]);
|
||||
//hashes.add(bytes);
|
||||
|
||||
//hashes.add(bytes);
|
||||
|
||||
hashes.add(persistableNetworkPayload.getHash());
|
||||
});
|
||||
}
|
||||
hashes.add(persistableNetworkPayload.getHash());
|
||||
});
|
||||
|
||||
bucketsPerHost.put(connection.getPeersNodeAddressProperty().getValue(), result);
|
||||
versionBucketsPerHost.put(connection.getPeersNodeAddressProperty().getValue(), versions);
|
||||
|
@ -333,20 +333,15 @@ public class P2PSeedNodeSnapshot extends P2PSeedNodeSnapshotBase {
|
||||
result.log(protectedStoragePayload);
|
||||
});
|
||||
|
||||
Set<PersistableNetworkPayload> persistableNetworkPayloadSet = dataResponse
|
||||
.getPersistableNetworkPayloadSet();
|
||||
if (persistableNetworkPayloadSet != null) {
|
||||
persistableNetworkPayloadSet.forEach(persistableNetworkPayload -> {
|
||||
dataResponse.getPersistableNetworkPayloadSet().forEach(persistableNetworkPayload -> {
|
||||
// memorize message hashes
|
||||
//Byte[] bytes = new Byte[persistableNetworkPayload.getHash().length];
|
||||
//Arrays.setAll(bytes, n -> persistableNetworkPayload.getHash()[n]);
|
||||
|
||||
// memorize message hashes
|
||||
//Byte[] bytes = new Byte[persistableNetworkPayload.getHash().length];
|
||||
//Arrays.setAll(bytes, n -> persistableNetworkPayload.getHash()[n]);
|
||||
//hashes.add(bytes);
|
||||
|
||||
//hashes.add(bytes);
|
||||
|
||||
hashes.add(persistableNetworkPayload.getHash());
|
||||
});
|
||||
}
|
||||
hashes.add(persistableNetworkPayload.getHash());
|
||||
});
|
||||
|
||||
bucketsPerHost.put(connection.getPeersNodeAddressProperty().getValue(), result);
|
||||
return true;
|
||||
|
@ -699,7 +699,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||
// to the logic from BroadcastHandler.sendToPeer
|
||||
}
|
||||
};
|
||||
boolean result = p2PDataStorage.addProtectedStorageEntry(protectedMailboxStorageEntry, networkNode.getNodeAddress(), listener, true);
|
||||
boolean result = p2PDataStorage.addProtectedStorageEntry(protectedMailboxStorageEntry, networkNode.getNodeAddress(), listener);
|
||||
if (!result) {
|
||||
sendMailboxMessageListener.onFault("Data already exists in our local database");
|
||||
|
||||
@ -760,7 +760,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||
expirableMailboxStoragePayload,
|
||||
keyRing.getSignatureKeyPair(),
|
||||
receiversPubKey);
|
||||
p2PDataStorage.remove(protectedMailboxStorageEntry, networkNode.getNodeAddress(), true);
|
||||
p2PDataStorage.remove(protectedMailboxStorageEntry, networkNode.getNodeAddress());
|
||||
} catch (CryptoException e) {
|
||||
log.error("Signing at getDataWithSignedSeqNr failed. That should never happen.");
|
||||
}
|
||||
@ -779,14 +779,14 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public boolean addPersistableNetworkPayload(PersistableNetworkPayload payload, boolean reBroadcast) {
|
||||
return p2PDataStorage.addPersistableNetworkPayload(payload, networkNode.getNodeAddress(), true, true, reBroadcast, false);
|
||||
return p2PDataStorage.addPersistableNetworkPayload(payload, networkNode.getNodeAddress(), reBroadcast);
|
||||
}
|
||||
|
||||
public boolean addProtectedStorageEntry(ProtectedStoragePayload protectedStoragePayload, boolean isDataOwner) {
|
||||
public boolean addProtectedStorageEntry(ProtectedStoragePayload protectedStoragePayload) {
|
||||
if (isBootstrapped()) {
|
||||
try {
|
||||
ProtectedStorageEntry protectedStorageEntry = p2PDataStorage.getProtectedStorageEntry(protectedStoragePayload, keyRing.getSignatureKeyPair());
|
||||
return p2PDataStorage.addProtectedStorageEntry(protectedStorageEntry, networkNode.getNodeAddress(), null, isDataOwner);
|
||||
return p2PDataStorage.addProtectedStorageEntry(protectedStorageEntry, networkNode.getNodeAddress(), null);
|
||||
} catch (CryptoException e) {
|
||||
log.error("Signing at getDataWithSignedSeqNr failed. That should never happen.");
|
||||
return false;
|
||||
@ -796,11 +796,11 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||
}
|
||||
}
|
||||
|
||||
public boolean refreshTTL(ProtectedStoragePayload protectedStoragePayload, boolean isDataOwner) {
|
||||
public boolean refreshTTL(ProtectedStoragePayload protectedStoragePayload) {
|
||||
if (isBootstrapped()) {
|
||||
try {
|
||||
RefreshOfferMessage refreshTTLMessage = p2PDataStorage.getRefreshTTLMessage(protectedStoragePayload, keyRing.getSignatureKeyPair());
|
||||
return p2PDataStorage.refreshTTL(refreshTTLMessage, networkNode.getNodeAddress(), isDataOwner);
|
||||
return p2PDataStorage.refreshTTL(refreshTTLMessage, networkNode.getNodeAddress());
|
||||
} catch (CryptoException e) {
|
||||
log.error("Signing at getDataWithSignedSeqNr failed. That should never happen.");
|
||||
return false;
|
||||
@ -810,11 +810,11 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||
}
|
||||
}
|
||||
|
||||
public boolean removeData(ProtectedStoragePayload protectedStoragePayload, boolean isDataOwner) {
|
||||
public boolean removeData(ProtectedStoragePayload protectedStoragePayload) {
|
||||
if (isBootstrapped()) {
|
||||
try {
|
||||
ProtectedStorageEntry protectedStorageEntry = p2PDataStorage.getProtectedStorageEntry(protectedStoragePayload, keyRing.getSignatureKeyPair());
|
||||
return p2PDataStorage.remove(protectedStorageEntry, networkNode.getNodeAddress(), isDataOwner);
|
||||
return p2PDataStorage.remove(protectedStorageEntry, networkNode.getNodeAddress());
|
||||
} catch (CryptoException e) {
|
||||
log.error("Signing at getDataWithSignedSeqNr failed. That should never happen.");
|
||||
return false;
|
||||
|
@ -111,7 +111,7 @@ public class BroadcastHandler implements PeerManager.Listener {
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void broadcast(BroadcastMessage message, @Nullable NodeAddress sender, ResultHandler resultHandler,
|
||||
@Nullable Listener listener, boolean isDataOwner) {
|
||||
@Nullable Listener listener) {
|
||||
this.message = message;
|
||||
this.resultHandler = resultHandler;
|
||||
this.listener = listener;
|
||||
@ -127,6 +127,8 @@ public class BroadcastHandler implements PeerManager.Listener {
|
||||
Collections.shuffle(connectedPeersList);
|
||||
numPeers = connectedPeersList.size();
|
||||
int delay = 50;
|
||||
|
||||
boolean isDataOwner = (sender != null) && sender.equals(networkNode.getNodeAddress());
|
||||
if (!isDataOwner) {
|
||||
// for not data owner (relay nodes) we send to max. 7 nodes and use a longer delay
|
||||
numPeers = Math.min(7, connectedPeersList.size());
|
||||
|
@ -56,9 +56,9 @@ public class Broadcaster implements BroadcastHandler.ResultHandler {
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void broadcast(BroadcastMessage message, @Nullable NodeAddress sender,
|
||||
@Nullable BroadcastHandler.Listener listener, boolean isDataOwner) {
|
||||
@Nullable BroadcastHandler.Listener listener) {
|
||||
BroadcastHandler broadcastHandler = new BroadcastHandler(networkNode, peerManager);
|
||||
broadcastHandler.broadcast(message, sender, this, listener, isDataOwner);
|
||||
broadcastHandler.broadcast(message, sender, this, listener);
|
||||
broadcastHandlers.add(broadcastHandler);
|
||||
}
|
||||
|
||||
|
@ -22,27 +22,17 @@ import bisq.network.p2p.network.Connection;
|
||||
import bisq.network.p2p.network.NetworkNode;
|
||||
import bisq.network.p2p.peers.getdata.messages.GetDataRequest;
|
||||
import bisq.network.p2p.peers.getdata.messages.GetDataResponse;
|
||||
import bisq.network.p2p.peers.getdata.messages.GetUpdatedDataRequest;
|
||||
import bisq.network.p2p.storage.P2PDataStorage;
|
||||
import bisq.network.p2p.storage.payload.CapabilityRequiringPayload;
|
||||
import bisq.network.p2p.storage.payload.PersistableNetworkPayload;
|
||||
import bisq.network.p2p.storage.payload.ProtectedStorageEntry;
|
||||
import bisq.network.p2p.storage.payload.ProtectedStoragePayload;
|
||||
|
||||
import bisq.common.Timer;
|
||||
import bisq.common.UserThread;
|
||||
import bisq.common.util.Utilities;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@ -51,8 +41,8 @@ import org.jetbrains.annotations.NotNull;
|
||||
@Slf4j
|
||||
public class GetDataRequestHandler {
|
||||
private static final long TIMEOUT = 90;
|
||||
private static final int MAX_ENTRIES = 10000;
|
||||
|
||||
private static final int MAX_ENTRIES = 10000;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Listener
|
||||
@ -93,10 +83,35 @@ public class GetDataRequestHandler {
|
||||
|
||||
public void handle(GetDataRequest getDataRequest, final Connection connection) {
|
||||
long ts = System.currentTimeMillis();
|
||||
GetDataResponse getDataResponse = new GetDataResponse(getFilteredProtectedStorageEntries(getDataRequest, connection),
|
||||
getFilteredPersistableNetworkPayload(getDataRequest, connection),
|
||||
getDataRequest.getNonce(),
|
||||
getDataRequest instanceof GetUpdatedDataRequest);
|
||||
String connectionInfo = "connectionInfo" + connection.getPeersNodeAddressOptional()
|
||||
.map(e -> "node address " + e.getFullAddress())
|
||||
.orElseGet(() -> "connection UID " + connection.getUid());
|
||||
|
||||
AtomicBoolean outPersistableNetworkPayloadOutputTruncated = new AtomicBoolean(false);
|
||||
AtomicBoolean outProtectedStoragePayloadOutputTruncated = new AtomicBoolean(false);
|
||||
GetDataResponse getDataResponse = dataStorage.buildGetDataResponse(
|
||||
getDataRequest,
|
||||
MAX_ENTRIES,
|
||||
outPersistableNetworkPayloadOutputTruncated,
|
||||
outProtectedStoragePayloadOutputTruncated,
|
||||
connection.getCapabilities());
|
||||
|
||||
if (outPersistableNetworkPayloadOutputTruncated.get()) {
|
||||
log.warn("The getData request from peer with {} caused too much PersistableNetworkPayload " +
|
||||
"entries to get delivered. We limited the entries for the response to {} entries",
|
||||
connectionInfo, MAX_ENTRIES);
|
||||
}
|
||||
|
||||
if (outProtectedStoragePayloadOutputTruncated.get()) {
|
||||
log.warn("The getData request from peer with {} caused too much ProtectedStorageEntry " +
|
||||
"entries to get delivered. We limited the entries for the response to {} entries",
|
||||
connectionInfo, MAX_ENTRIES);
|
||||
}
|
||||
|
||||
log.info("The getDataResponse to peer with {} contains {} ProtectedStorageEntries and {} PersistableNetworkPayloads",
|
||||
connectionInfo,
|
||||
getDataResponse.getDataSet().size(),
|
||||
getDataResponse.getPersistableNetworkPayloadSet().size());
|
||||
|
||||
if (timeoutTimer == null) {
|
||||
timeoutTimer = UserThread.runAfter(() -> { // setup before sending to avoid race conditions
|
||||
@ -136,81 +151,6 @@ public class GetDataRequestHandler {
|
||||
log.info("handle GetDataRequest took {} ms", System.currentTimeMillis() - ts);
|
||||
}
|
||||
|
||||
private Set<PersistableNetworkPayload> getFilteredPersistableNetworkPayload(GetDataRequest getDataRequest,
|
||||
Connection connection) {
|
||||
Set<P2PDataStorage.ByteArray> tempLookupSet = new HashSet<>();
|
||||
String connectionInfo = "connectionInfo" + connection.getPeersNodeAddressOptional()
|
||||
.map(e -> "node address " + e.getFullAddress())
|
||||
.orElseGet(() -> "connection UID " + connection.getUid());
|
||||
|
||||
Set<P2PDataStorage.ByteArray> excludedKeysAsByteArray = P2PDataStorage.ByteArray.convertBytesSetToByteArraySet(getDataRequest.getExcludedKeys());
|
||||
AtomicInteger maxSize = new AtomicInteger(MAX_ENTRIES);
|
||||
Set<PersistableNetworkPayload> result = dataStorage.getAppendOnlyDataStoreMap().entrySet().stream()
|
||||
.filter(e -> !excludedKeysAsByteArray.contains(e.getKey()))
|
||||
.filter(e -> maxSize.decrementAndGet() >= 0)
|
||||
.map(Map.Entry::getValue)
|
||||
.filter(connection::noCapabilityRequiredOrCapabilityIsSupported)
|
||||
.filter(payload -> {
|
||||
boolean notContained = tempLookupSet.add(new P2PDataStorage.ByteArray(payload.getHash()));
|
||||
return notContained;
|
||||
})
|
||||
.collect(Collectors.toSet());
|
||||
if (maxSize.get() <= 0) {
|
||||
log.warn("The getData request from peer with {} caused too much PersistableNetworkPayload " +
|
||||
"entries to get delivered. We limited the entries for the response to {} entries",
|
||||
connectionInfo, MAX_ENTRIES);
|
||||
}
|
||||
log.info("The getData request from peer with {} contains {} PersistableNetworkPayload entries ",
|
||||
connectionInfo, result.size());
|
||||
return result;
|
||||
}
|
||||
|
||||
private Set<ProtectedStorageEntry> getFilteredProtectedStorageEntries(GetDataRequest getDataRequest,
|
||||
Connection connection) {
|
||||
Set<ProtectedStorageEntry> filteredDataSet = new HashSet<>();
|
||||
Set<Integer> lookupSet = new HashSet<>();
|
||||
String connectionInfo = "connectionInfo" + connection.getPeersNodeAddressOptional()
|
||||
.map(e -> "node address " + e.getFullAddress())
|
||||
.orElseGet(() -> "connection UID " + connection.getUid());
|
||||
|
||||
AtomicInteger maxSize = new AtomicInteger(MAX_ENTRIES);
|
||||
Set<P2PDataStorage.ByteArray> excludedKeysAsByteArray = P2PDataStorage.ByteArray.convertBytesSetToByteArraySet(getDataRequest.getExcludedKeys());
|
||||
Set<ProtectedStorageEntry> filteredSet = dataStorage.getMap().entrySet().stream()
|
||||
.filter(e -> !excludedKeysAsByteArray.contains(e.getKey()))
|
||||
.filter(e -> maxSize.decrementAndGet() >= 0)
|
||||
.map(Map.Entry::getValue)
|
||||
.collect(Collectors.toSet());
|
||||
if (maxSize.get() <= 0) {
|
||||
log.warn("The getData request from peer with {} caused too much ProtectedStorageEntry " +
|
||||
"entries to get delivered. We limited the entries for the response to {} entries",
|
||||
connectionInfo, MAX_ENTRIES);
|
||||
}
|
||||
log.info("getFilteredProtectedStorageEntries " + filteredSet.size());
|
||||
|
||||
for (ProtectedStorageEntry protectedStorageEntry : filteredSet) {
|
||||
final ProtectedStoragePayload protectedStoragePayload = protectedStorageEntry.getProtectedStoragePayload();
|
||||
boolean doAdd = false;
|
||||
if (protectedStoragePayload instanceof CapabilityRequiringPayload) {
|
||||
if (connection.getCapabilities().containsAll(((CapabilityRequiringPayload) protectedStoragePayload).getRequiredCapabilities()))
|
||||
doAdd = true;
|
||||
else
|
||||
log.debug("We do not send the message to the peer because they do not support the required capability for that message type.\n" +
|
||||
"storagePayload is: " + Utilities.toTruncatedString(protectedStoragePayload));
|
||||
} else {
|
||||
doAdd = true;
|
||||
}
|
||||
if (doAdd) {
|
||||
boolean notContained = lookupSet.add(protectedStoragePayload.hashCode());
|
||||
if (notContained)
|
||||
filteredDataSet.add(protectedStorageEntry);
|
||||
}
|
||||
}
|
||||
|
||||
log.info("The getData request from peer with {} contains {} ProtectedStorageEntry entries ",
|
||||
connectionInfo, filteredDataSet.size());
|
||||
return filteredDataSet;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
cleanup();
|
||||
}
|
||||
|
@ -25,10 +25,7 @@ import bisq.network.p2p.network.NetworkNode;
|
||||
import bisq.network.p2p.peers.PeerManager;
|
||||
import bisq.network.p2p.peers.getdata.messages.GetDataRequest;
|
||||
import bisq.network.p2p.peers.getdata.messages.GetDataResponse;
|
||||
import bisq.network.p2p.peers.getdata.messages.GetUpdatedDataRequest;
|
||||
import bisq.network.p2p.peers.getdata.messages.PreliminaryGetDataRequest;
|
||||
import bisq.network.p2p.storage.P2PDataStorage;
|
||||
import bisq.network.p2p.storage.payload.LazyProcessedPayload;
|
||||
import bisq.network.p2p.storage.payload.PersistableNetworkPayload;
|
||||
import bisq.network.p2p.storage.payload.ProtectedStorageEntry;
|
||||
import bisq.network.p2p.storage.payload.ProtectedStoragePayload;
|
||||
@ -47,8 +44,6 @@ import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@ -58,7 +53,6 @@ import org.jetbrains.annotations.Nullable;
|
||||
@Slf4j
|
||||
class RequestDataHandler implements MessageListener {
|
||||
private static final long TIMEOUT = 90;
|
||||
private static boolean initialRequestApplied = false;
|
||||
|
||||
private NodeAddress peersNodeAddress;
|
||||
/*
|
||||
@ -124,25 +118,10 @@ class RequestDataHandler implements MessageListener {
|
||||
if (!stopped) {
|
||||
GetDataRequest getDataRequest;
|
||||
|
||||
// We collect the keys of the PersistableNetworkPayload items so we exclude them in our request.
|
||||
// PersistedStoragePayload items don't get removed, so we don't have an issue with the case that
|
||||
// an object gets removed in between PreliminaryGetDataRequest and the GetUpdatedDataRequest and we would
|
||||
// miss that event if we do not load the full set or use some delta handling.
|
||||
Set<byte[]> excludedKeys = dataStorage.getAppendOnlyDataStoreMap().keySet().stream()
|
||||
.map(e -> e.bytes)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
Set<byte[]> excludedKeysFromPersistedEntryMap = dataStorage.getMap().keySet()
|
||||
.stream()
|
||||
.map(e -> e.bytes)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
excludedKeys.addAll(excludedKeysFromPersistedEntryMap);
|
||||
|
||||
if (isPreliminaryDataRequest)
|
||||
getDataRequest = new PreliminaryGetDataRequest(nonce, excludedKeys);
|
||||
getDataRequest = dataStorage.buildPreliminaryGetDataRequest(nonce);
|
||||
else
|
||||
getDataRequest = new GetUpdatedDataRequest(networkNode.getNodeAddress(), nonce, excludedKeys);
|
||||
getDataRequest = dataStorage.buildGetUpdatedDataRequest(networkNode.getNodeAddress(), nonce);
|
||||
|
||||
if (timeoutTimer == null) {
|
||||
timeoutTimer = UserThread.runAfter(() -> { // setup before sending to avoid race conditions
|
||||
@ -207,7 +186,6 @@ class RequestDataHandler implements MessageListener {
|
||||
GetDataResponse getDataResponse = (GetDataResponse) networkEnvelope;
|
||||
final Set<ProtectedStorageEntry> dataSet = getDataResponse.getDataSet();
|
||||
Set<PersistableNetworkPayload> persistableNetworkPayloadSet = getDataResponse.getPersistableNetworkPayloadSet();
|
||||
|
||||
logContents(networkEnvelope, dataSet, persistableNetworkPayloadSet);
|
||||
|
||||
if (getDataResponse.getRequestNonce() == nonce) {
|
||||
@ -218,50 +196,8 @@ class RequestDataHandler implements MessageListener {
|
||||
return;
|
||||
}
|
||||
|
||||
final NodeAddress sender = connection.getPeersNodeAddressOptional().get();
|
||||
|
||||
long ts2 = System.currentTimeMillis();
|
||||
AtomicInteger counter = new AtomicInteger();
|
||||
dataSet.forEach(e -> {
|
||||
// We don't broadcast here (last param) as we are only connected to the seed node and would be pointless
|
||||
dataStorage.addProtectedStorageEntry(e, sender, null, false, false);
|
||||
counter.getAndIncrement();
|
||||
|
||||
});
|
||||
log.info("Processing {} protectedStorageEntries took {} ms.", counter.get(), System.currentTimeMillis() - ts2);
|
||||
|
||||
/* // engage the firstRequest logic only if we are a seed node. Normal clients get here twice at most.
|
||||
if (!Capabilities.app.containsAll(Capability.SEED_NODE))
|
||||
firstRequest = true;*/
|
||||
|
||||
if (persistableNetworkPayloadSet != null /*&& firstRequest*/) {
|
||||
ts2 = System.currentTimeMillis();
|
||||
persistableNetworkPayloadSet.forEach(e -> {
|
||||
if (e instanceof LazyProcessedPayload) {
|
||||
// We use an optimized method as many checks are not required in that case to avoid
|
||||
// performance issues.
|
||||
// Processing 82645 items took now 61 ms compared to earlier version where it took ages (> 2min).
|
||||
// Usually we only get about a few hundred or max. a few 1000 items. 82645 is all
|
||||
// trade stats stats and all account age witness data.
|
||||
|
||||
// We only apply it once from first response
|
||||
if (!initialRequestApplied) {
|
||||
dataStorage.addPersistableNetworkPayloadFromInitialRequest(e);
|
||||
|
||||
}
|
||||
} else {
|
||||
// We don't broadcast here as we are only connected to the seed node and would be pointless
|
||||
dataStorage.addPersistableNetworkPayload(e, sender, false,
|
||||
false, false, false);
|
||||
}
|
||||
});
|
||||
|
||||
// We set initialRequestApplied to true after the loop, otherwise we would only process 1 entry
|
||||
initialRequestApplied = true;
|
||||
|
||||
log.info("Processing {} persistableNetworkPayloads took {} ms.",
|
||||
persistableNetworkPayloadSet.size(), System.currentTimeMillis() - ts2);
|
||||
}
|
||||
dataStorage.processGetDataResponse(getDataResponse,
|
||||
connection.getPeersNodeAddressOptional().get());
|
||||
|
||||
cleanup();
|
||||
listener.onComplete();
|
||||
@ -310,24 +246,20 @@ class RequestDataHandler implements MessageListener {
|
||||
payloadByClassName.get(className).add(protectedStoragePayload);
|
||||
});
|
||||
|
||||
persistableNetworkPayloadSet.forEach(persistableNetworkPayload -> {
|
||||
// For logging different data types
|
||||
String className = persistableNetworkPayload.getClass().getSimpleName();
|
||||
if (!payloadByClassName.containsKey(className))
|
||||
payloadByClassName.put(className, new HashSet<>());
|
||||
|
||||
if (persistableNetworkPayloadSet != null) {
|
||||
persistableNetworkPayloadSet.forEach(persistableNetworkPayload -> {
|
||||
// For logging different data types
|
||||
String className = persistableNetworkPayload.getClass().getSimpleName();
|
||||
if (!payloadByClassName.containsKey(className))
|
||||
payloadByClassName.put(className, new HashSet<>());
|
||||
|
||||
payloadByClassName.get(className).add(persistableNetworkPayload);
|
||||
});
|
||||
}
|
||||
payloadByClassName.get(className).add(persistableNetworkPayload);
|
||||
});
|
||||
|
||||
// Log different data types
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("\n#################################################################\n");
|
||||
sb.append("Connected to node: " + peersNodeAddress.getFullAddress() + "\n");
|
||||
final int items = dataSet.size() +
|
||||
(persistableNetworkPayloadSet != null ? persistableNetworkPayloadSet.size() : 0);
|
||||
final int items = dataSet.size() + persistableNetworkPayloadSet.size();
|
||||
sb.append("Received ").append(items).append(" instances\n");
|
||||
payloadByClassName.forEach((key, value) -> sb.append(key)
|
||||
.append(": ")
|
||||
|
@ -29,7 +29,6 @@ import bisq.common.proto.network.NetworkEnvelope;
|
||||
import bisq.common.proto.network.NetworkProtoResolver;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -37,7 +36,7 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.Value;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@Slf4j
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ -47,17 +46,15 @@ public final class GetDataResponse extends NetworkEnvelope implements SupportedC
|
||||
private final Set<ProtectedStorageEntry> dataSet;
|
||||
|
||||
// Set of PersistableNetworkPayload objects
|
||||
// We added that in v 0.6 and we would get a null object from older peers, so keep it annotated with @Nullable
|
||||
@Nullable
|
||||
// We added that in v 0.6 and the fromProto code will create an empty HashSet if it doesn't exist
|
||||
private final Set<PersistableNetworkPayload> persistableNetworkPayloadSet;
|
||||
|
||||
private final int requestNonce;
|
||||
private final boolean isGetUpdatedDataResponse;
|
||||
@Nullable
|
||||
private final Capabilities supportedCapabilities;
|
||||
|
||||
public GetDataResponse(Set<ProtectedStorageEntry> dataSet,
|
||||
@Nullable Set<PersistableNetworkPayload> persistableNetworkPayloadSet,
|
||||
public GetDataResponse(@NotNull Set<ProtectedStorageEntry> dataSet,
|
||||
@NotNull Set<PersistableNetworkPayload> persistableNetworkPayloadSet,
|
||||
int requestNonce,
|
||||
boolean isGetUpdatedDataResponse) {
|
||||
this(dataSet,
|
||||
@ -72,11 +69,11 @@ public final class GetDataResponse extends NetworkEnvelope implements SupportedC
|
||||
// PROTO BUFFER
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private GetDataResponse(Set<ProtectedStorageEntry> dataSet,
|
||||
@Nullable Set<PersistableNetworkPayload> persistableNetworkPayloadSet,
|
||||
private GetDataResponse(@NotNull Set<ProtectedStorageEntry> dataSet,
|
||||
@NotNull Set<PersistableNetworkPayload> persistableNetworkPayloadSet,
|
||||
int requestNonce,
|
||||
boolean isGetUpdatedDataResponse,
|
||||
@Nullable Capabilities supportedCapabilities,
|
||||
@NotNull Capabilities supportedCapabilities,
|
||||
int messageVersion) {
|
||||
super(messageVersion);
|
||||
|
||||
@ -100,13 +97,12 @@ public final class GetDataResponse extends NetworkEnvelope implements SupportedC
|
||||
.setProtectedStorageEntry((protobuf.ProtectedStorageEntry) protectedStorageEntry.toProtoMessage())
|
||||
.build())
|
||||
.collect(Collectors.toList()))
|
||||
.addAllPersistableNetworkPayloadItems(persistableNetworkPayloadSet.stream()
|
||||
.map(PersistableNetworkPayload::toProtoMessage)
|
||||
.collect(Collectors.toList()))
|
||||
.setRequestNonce(requestNonce)
|
||||
.setIsGetUpdatedDataResponse(isGetUpdatedDataResponse);
|
||||
|
||||
Optional.ofNullable(supportedCapabilities).ifPresent(e -> builder.addAllSupportedCapabilities(Capabilities.toIntList(supportedCapabilities)));
|
||||
Optional.ofNullable(persistableNetworkPayloadSet).ifPresent(set -> builder.addAllPersistableNetworkPayloadItems(set.stream()
|
||||
.map(PersistableNetworkPayload::toProtoMessage)
|
||||
.collect(Collectors.toList())));
|
||||
.setIsGetUpdatedDataResponse(isGetUpdatedDataResponse)
|
||||
.addAllSupportedCapabilities(Capabilities.toIntList(supportedCapabilities));
|
||||
|
||||
protobuf.NetworkEnvelope proto = getNetworkEnvelopeBuilder()
|
||||
.setGetDataResponse(builder)
|
||||
@ -124,14 +120,11 @@ public final class GetDataResponse extends NetworkEnvelope implements SupportedC
|
||||
.map(entry -> (ProtectedStorageEntry) resolver.fromProto(entry))
|
||||
.collect(Collectors.toSet()));
|
||||
|
||||
Set<PersistableNetworkPayload> persistableNetworkPayloadSet = proto.getPersistableNetworkPayloadItemsList().isEmpty() ?
|
||||
null :
|
||||
new HashSet<>(
|
||||
proto.getPersistableNetworkPayloadItemsList().stream()
|
||||
Set<PersistableNetworkPayload> persistableNetworkPayloadSet = new HashSet<>(
|
||||
proto.getPersistableNetworkPayloadItemsList().stream()
|
||||
.map(e -> (PersistableNetworkPayload) resolver.fromProto(e))
|
||||
.collect(Collectors.toSet()));
|
||||
|
||||
//PersistableNetworkPayload
|
||||
return new GetDataResponse(dataSet,
|
||||
persistableNetworkPayloadSet,
|
||||
proto.getRequestNonce(),
|
||||
|
@ -26,7 +26,6 @@ import bisq.common.proto.ProtoUtil;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -34,9 +33,7 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.Value;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import protobuf.NetworkEnvelope;
|
||||
|
||||
@ -44,11 +41,10 @@ import protobuf.NetworkEnvelope;
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Value
|
||||
public final class PreliminaryGetDataRequest extends GetDataRequest implements AnonymousMessage, SupportedCapabilitiesMessage {
|
||||
@Nullable
|
||||
private final Capabilities supportedCapabilities;
|
||||
|
||||
public PreliminaryGetDataRequest(int nonce,
|
||||
Set<byte[]> excludedKeys) {
|
||||
@NotNull Set<byte[]> excludedKeys) {
|
||||
this(nonce, excludedKeys, Capabilities.app, Version.getP2PMessageVersion());
|
||||
}
|
||||
|
||||
@ -58,8 +54,8 @@ public final class PreliminaryGetDataRequest extends GetDataRequest implements A
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private PreliminaryGetDataRequest(int nonce,
|
||||
Set<byte[]> excludedKeys,
|
||||
@Nullable Capabilities supportedCapabilities,
|
||||
@NotNull Set<byte[]> excludedKeys,
|
||||
@NotNull Capabilities supportedCapabilities,
|
||||
int messageVersion) {
|
||||
super(messageVersion, nonce, excludedKeys);
|
||||
|
||||
@ -69,13 +65,12 @@ public final class PreliminaryGetDataRequest extends GetDataRequest implements A
|
||||
@Override
|
||||
public protobuf.NetworkEnvelope toProtoNetworkEnvelope() {
|
||||
final protobuf.PreliminaryGetDataRequest.Builder builder = protobuf.PreliminaryGetDataRequest.newBuilder()
|
||||
.addAllSupportedCapabilities(Capabilities.toIntList(supportedCapabilities))
|
||||
.setNonce(nonce)
|
||||
.addAllExcludedKeys(excludedKeys.stream()
|
||||
.map(ByteString::copyFrom)
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
Optional.ofNullable(supportedCapabilities).ifPresent(e -> builder.addAllSupportedCapabilities(Capabilities.toIntList(supportedCapabilities)));
|
||||
|
||||
NetworkEnvelope proto = getNetworkEnvelopeBuilder()
|
||||
.setPreliminaryGetDataRequest(builder)
|
||||
.build();
|
||||
@ -85,13 +80,9 @@ public final class PreliminaryGetDataRequest extends GetDataRequest implements A
|
||||
|
||||
public static PreliminaryGetDataRequest fromProto(protobuf.PreliminaryGetDataRequest proto, int messageVersion) {
|
||||
log.info("Received a PreliminaryGetDataRequest with {} kB", proto.getSerializedSize() / 1000d);
|
||||
Capabilities supportedCapabilities = proto.getSupportedCapabilitiesList().isEmpty() ?
|
||||
null :
|
||||
Capabilities.fromIntList(proto.getSupportedCapabilitiesList());
|
||||
|
||||
return new PreliminaryGetDataRequest(proto.getNonce(),
|
||||
ProtoUtil.byteSetFromProtoByteStringList(proto.getExcludedKeysList()),
|
||||
supportedCapabilities,
|
||||
Capabilities.fromIntList(proto.getSupportedCapabilitiesList()),
|
||||
messageVersion);
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,10 @@ import bisq.network.p2p.network.MessageListener;
|
||||
import bisq.network.p2p.network.NetworkNode;
|
||||
import bisq.network.p2p.peers.BroadcastHandler;
|
||||
import bisq.network.p2p.peers.Broadcaster;
|
||||
import bisq.network.p2p.peers.getdata.messages.GetDataRequest;
|
||||
import bisq.network.p2p.peers.getdata.messages.GetDataResponse;
|
||||
import bisq.network.p2p.peers.getdata.messages.GetUpdatedDataRequest;
|
||||
import bisq.network.p2p.peers.getdata.messages.PreliminaryGetDataRequest;
|
||||
import bisq.network.p2p.storage.messages.AddDataMessage;
|
||||
import bisq.network.p2p.storage.messages.AddOncePayload;
|
||||
import bisq.network.p2p.storage.messages.AddPersistableNetworkPayloadMessage;
|
||||
@ -32,8 +36,10 @@ import bisq.network.p2p.storage.messages.BroadcastMessage;
|
||||
import bisq.network.p2p.storage.messages.RefreshOfferMessage;
|
||||
import bisq.network.p2p.storage.messages.RemoveDataMessage;
|
||||
import bisq.network.p2p.storage.messages.RemoveMailboxDataMessage;
|
||||
import bisq.network.p2p.storage.payload.CapabilityRequiringPayload;
|
||||
import bisq.network.p2p.storage.payload.DateTolerantPayload;
|
||||
import bisq.network.p2p.storage.payload.ExpirablePayload;
|
||||
import bisq.network.p2p.storage.payload.ProcessOncePersistableNetworkPayload;
|
||||
import bisq.network.p2p.storage.payload.MailboxStoragePayload;
|
||||
import bisq.network.p2p.storage.payload.PersistableNetworkPayload;
|
||||
import bisq.network.p2p.storage.payload.ProtectedMailboxStorageEntry;
|
||||
@ -48,6 +54,7 @@ import bisq.network.p2p.storage.persistence.SequenceNumberMap;
|
||||
|
||||
import bisq.common.Timer;
|
||||
import bisq.common.UserThread;
|
||||
import bisq.common.app.Capabilities;
|
||||
import bisq.common.crypto.CryptoException;
|
||||
import bisq.common.crypto.Hash;
|
||||
import bisq.common.crypto.Sig;
|
||||
@ -88,6 +95,9 @@ import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
@ -106,7 +116,9 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
|
||||
public static final int PURGE_AGE_DAYS = 10;
|
||||
|
||||
@VisibleForTesting
|
||||
public static int CHECK_TTL_INTERVAL_SEC = 60;
|
||||
public static final int CHECK_TTL_INTERVAL_SEC = 60;
|
||||
|
||||
private boolean initialRequestApplied = false;
|
||||
|
||||
private final Broadcaster broadcaster;
|
||||
private final AppendOnlyDataStoreService appendOnlyDataStoreService;
|
||||
@ -177,6 +189,179 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
|
||||
map.putAll(protectedDataStoreService.getMap());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// RequestData API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Returns a PreliminaryGetDataRequest that can be sent to a peer node to request missing Payload data.
|
||||
*/
|
||||
public PreliminaryGetDataRequest buildPreliminaryGetDataRequest(int nonce) {
|
||||
return new PreliminaryGetDataRequest(nonce, this.getKnownPayloadHashes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a GetUpdatedDataRequest that can be sent to a peer node to request missing Payload data.
|
||||
*/
|
||||
public GetUpdatedDataRequest buildGetUpdatedDataRequest(NodeAddress senderNodeAddress, int nonce) {
|
||||
return new GetUpdatedDataRequest(senderNodeAddress, nonce, this.getKnownPayloadHashes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of known payload hashes. This is used in the GetData path to request missing data from peer nodes
|
||||
*/
|
||||
private Set<byte[]> getKnownPayloadHashes() {
|
||||
// We collect the keys of the PersistableNetworkPayload items so we exclude them in our request.
|
||||
// PersistedStoragePayload items don't get removed, so we don't have an issue with the case that
|
||||
// an object gets removed in between PreliminaryGetDataRequest and the GetUpdatedDataRequest and we would
|
||||
// miss that event if we do not load the full set or use some delta handling.
|
||||
Set<byte[]> excludedKeys =this.appendOnlyDataStoreService.getMap().keySet().stream()
|
||||
.map(e -> e.bytes)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
Set<byte[]> excludedKeysFromPersistedEntryMap = this.map.keySet()
|
||||
.stream()
|
||||
.map(e -> e.bytes)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
excludedKeys.addAll(excludedKeysFromPersistedEntryMap);
|
||||
|
||||
return excludedKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic function that can be used to filter a Map<ByteArray, ProtectedStorageEntry || PersistableNetworkPayload>
|
||||
* by a given set of keys and peer capabilities.
|
||||
*/
|
||||
static private <T extends NetworkPayload> Set<T> filterKnownHashes(
|
||||
Map<ByteArray, T> toFilter,
|
||||
Function<T, ? extends NetworkPayload> objToPayload,
|
||||
Set<ByteArray> knownHashes,
|
||||
Capabilities peerCapabilities,
|
||||
int maxEntries,
|
||||
AtomicBoolean outTruncated) {
|
||||
|
||||
AtomicInteger limit = new AtomicInteger(maxEntries);
|
||||
|
||||
Set<T> filteredResults = toFilter.entrySet().stream()
|
||||
.filter(e -> !knownHashes.contains(e.getKey()))
|
||||
.filter(e -> limit.decrementAndGet() >= 0)
|
||||
.map(Map.Entry::getValue)
|
||||
.filter(networkPayload -> shouldTransmitPayloadToPeer(peerCapabilities,
|
||||
objToPayload.apply(networkPayload)))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
if (limit.get() < 0)
|
||||
outTruncated.set(true);
|
||||
|
||||
return filteredResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a GetDataResponse object that contains the Payloads known locally, but not remotely.
|
||||
*/
|
||||
public GetDataResponse buildGetDataResponse(
|
||||
GetDataRequest getDataRequest,
|
||||
int maxEntriesPerType,
|
||||
AtomicBoolean outPersistableNetworkPayloadOutputTruncated,
|
||||
AtomicBoolean outProtectedStorageEntryOutputTruncated,
|
||||
Capabilities peerCapabilities) {
|
||||
|
||||
Set<P2PDataStorage.ByteArray> excludedKeysAsByteArray =
|
||||
P2PDataStorage.ByteArray.convertBytesSetToByteArraySet(getDataRequest.getExcludedKeys());
|
||||
|
||||
Set<PersistableNetworkPayload> filteredPersistableNetworkPayloads =
|
||||
filterKnownHashes(
|
||||
this.appendOnlyDataStoreService.getMap(),
|
||||
Function.identity(),
|
||||
excludedKeysAsByteArray,
|
||||
peerCapabilities,
|
||||
maxEntriesPerType,
|
||||
outPersistableNetworkPayloadOutputTruncated);
|
||||
|
||||
Set<ProtectedStorageEntry> filteredProtectedStorageEntries =
|
||||
filterKnownHashes(
|
||||
this.map,
|
||||
ProtectedStorageEntry::getProtectedStoragePayload,
|
||||
excludedKeysAsByteArray,
|
||||
peerCapabilities,
|
||||
maxEntriesPerType,
|
||||
outProtectedStorageEntryOutputTruncated);
|
||||
|
||||
return new GetDataResponse(
|
||||
filteredProtectedStorageEntries,
|
||||
filteredPersistableNetworkPayloads,
|
||||
getDataRequest.getNonce(),
|
||||
getDataRequest instanceof GetUpdatedDataRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a Payload should be transmit to a peer given the peer's supported capabilities.
|
||||
*/
|
||||
private static boolean shouldTransmitPayloadToPeer(Capabilities peerCapabilities, NetworkPayload payload) {
|
||||
|
||||
// Sanity check to ensure this isn't used outside P2PDataStorage
|
||||
if (!(payload instanceof ProtectedStoragePayload || payload instanceof PersistableNetworkPayload))
|
||||
return false;
|
||||
|
||||
// If the payload doesn't have a required capability, we should transmit it
|
||||
if (!(payload instanceof CapabilityRequiringPayload))
|
||||
return true;
|
||||
|
||||
// Otherwise, only transmit the Payload if the peer supports all capabilities required by the payload
|
||||
boolean shouldTransmit = peerCapabilities.containsAll(((CapabilityRequiringPayload) payload).getRequiredCapabilities());
|
||||
|
||||
if (!shouldTransmit) {
|
||||
log.debug("We do not send the message to the peer because they do not support the required capability for that message type.\n" +
|
||||
"storagePayload is: " + Utilities.toTruncatedString(payload));
|
||||
}
|
||||
|
||||
return shouldTransmit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a GetDataResponse message and updates internal state. Does not broadcast updates to the P2P network
|
||||
* or domain listeners.
|
||||
*/
|
||||
public void processGetDataResponse(GetDataResponse getDataResponse, NodeAddress sender) {
|
||||
final Set<ProtectedStorageEntry> dataSet = getDataResponse.getDataSet();
|
||||
Set<PersistableNetworkPayload> persistableNetworkPayloadSet = getDataResponse.getPersistableNetworkPayloadSet();
|
||||
|
||||
long ts2 = System.currentTimeMillis();
|
||||
dataSet.forEach(e -> {
|
||||
// We don't broadcast here (last param) as we are only connected to the seed node and would be pointless
|
||||
addProtectedStorageEntry(e, sender, null, false);
|
||||
|
||||
});
|
||||
log.info("Processing {} protectedStorageEntries took {} ms.", dataSet.size(), this.clock.millis() - ts2);
|
||||
|
||||
ts2 = this.clock.millis();
|
||||
persistableNetworkPayloadSet.forEach(e -> {
|
||||
if (e instanceof ProcessOncePersistableNetworkPayload) {
|
||||
// We use an optimized method as many checks are not required in that case to avoid
|
||||
// performance issues.
|
||||
// Processing 82645 items took now 61 ms compared to earlier version where it took ages (> 2min).
|
||||
// Usually we only get about a few hundred or max. a few 1000 items. 82645 is all
|
||||
// trade stats stats and all account age witness data.
|
||||
|
||||
// We only apply it once from first response
|
||||
if (!initialRequestApplied) {
|
||||
addPersistableNetworkPayloadFromInitialRequest(e);
|
||||
|
||||
}
|
||||
} else {
|
||||
// We don't broadcast here as we are only connected to the seed node and would be pointless
|
||||
addPersistableNetworkPayload(e, sender,false, false, false);
|
||||
}
|
||||
});
|
||||
log.info("Processing {} persistableNetworkPayloads took {} ms.",
|
||||
persistableNetworkPayloadSet.size(), this.clock.millis() - ts2);
|
||||
|
||||
// We only process PersistableNetworkPayloads implementing ProcessOncePersistableNetworkPayload once. It can cause performance
|
||||
// issues and since the data is rarely out of sync it is not worth it to apply them from multiple peers during
|
||||
// startup.
|
||||
initialRequestApplied = true;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
@ -229,16 +414,16 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
|
||||
if (networkEnvelope instanceof BroadcastMessage) {
|
||||
connection.getPeersNodeAddressOptional().ifPresent(peersNodeAddress -> {
|
||||
if (networkEnvelope instanceof AddDataMessage) {
|
||||
addProtectedStorageEntry(((AddDataMessage) networkEnvelope).getProtectedStorageEntry(), peersNodeAddress, null, false);
|
||||
addProtectedStorageEntry(((AddDataMessage) networkEnvelope).getProtectedStorageEntry(), peersNodeAddress, null, true);
|
||||
} else if (networkEnvelope instanceof RemoveDataMessage) {
|
||||
remove(((RemoveDataMessage) networkEnvelope).getProtectedStorageEntry(), peersNodeAddress, false);
|
||||
remove(((RemoveDataMessage) networkEnvelope).getProtectedStorageEntry(), peersNodeAddress);
|
||||
} else if (networkEnvelope instanceof RemoveMailboxDataMessage) {
|
||||
remove(((RemoveMailboxDataMessage) networkEnvelope).getProtectedMailboxStorageEntry(), peersNodeAddress, false);
|
||||
remove(((RemoveMailboxDataMessage) networkEnvelope).getProtectedMailboxStorageEntry(), peersNodeAddress);
|
||||
} else if (networkEnvelope instanceof RefreshOfferMessage) {
|
||||
refreshTTL((RefreshOfferMessage) networkEnvelope, peersNodeAddress, false);
|
||||
refreshTTL((RefreshOfferMessage) networkEnvelope, peersNodeAddress);
|
||||
} else if (networkEnvelope instanceof AddPersistableNetworkPayloadMessage) {
|
||||
addPersistableNetworkPayload(((AddPersistableNetworkPayloadMessage) networkEnvelope).getPersistableNetworkPayload(),
|
||||
peersNodeAddress, false, true, false, true);
|
||||
peersNodeAddress, true, false, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -255,46 +440,31 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
|
||||
|
||||
@Override
|
||||
public void onDisconnect(CloseConnectionReason closeConnectionReason, Connection connection) {
|
||||
if (connection.hasPeersNodeAddress() && !closeConnectionReason.isIntended) {
|
||||
map.values()
|
||||
.forEach(protectedStorageEntry -> {
|
||||
NetworkPayload networkPayload = protectedStorageEntry.getProtectedStoragePayload();
|
||||
if (networkPayload instanceof ExpirablePayload && networkPayload instanceof RequiresOwnerIsOnlinePayload) {
|
||||
NodeAddress ownerNodeAddress = ((RequiresOwnerIsOnlinePayload) networkPayload).getOwnerNodeAddress();
|
||||
if (connection.getPeersNodeAddressOptional().isPresent() &&
|
||||
ownerNodeAddress.equals(connection.getPeersNodeAddressOptional().get())) {
|
||||
// We have a RequiresLiveOwnerData data object with the node address of the
|
||||
// disconnected peer. We remove that data from our map.
|
||||
if (closeConnectionReason.isIntended)
|
||||
return;
|
||||
|
||||
// Check if we have the data (e.g. OfferPayload)
|
||||
ByteArray hashOfPayload = get32ByteHashAsByteArray(networkPayload);
|
||||
boolean containsKey = map.containsKey(hashOfPayload);
|
||||
if (containsKey) {
|
||||
log.debug("We remove the data as the data owner got disconnected with " +
|
||||
"closeConnectionReason=" + closeConnectionReason);
|
||||
if (!connection.getPeersNodeAddressOptional().isPresent())
|
||||
return;
|
||||
|
||||
// We only set the data back by half of the TTL and remove the data only if is has
|
||||
// expired after that back dating.
|
||||
// We might get connection drops which are not caused by the node going offline, so
|
||||
// we give more tolerance with that approach, giving the node the change to
|
||||
// refresh the TTL with a refresh message.
|
||||
// We observed those issues during stress tests, but it might have been caused by the
|
||||
// test set up (many nodes/connections over 1 router)
|
||||
// TODO investigate what causes the disconnections.
|
||||
// Usually the are: SOCKET_TIMEOUT ,TERMINATED (EOFException)
|
||||
protectedStorageEntry.backDate();
|
||||
if (protectedStorageEntry.isExpired(this.clock)) {
|
||||
log.info("We found an expired data entry which we have already back dated. " +
|
||||
"We remove the protectedStoragePayload:\n\t" + Utilities.toTruncatedString(protectedStorageEntry.getProtectedStoragePayload(), 100));
|
||||
removeFromMapAndDataStore(protectedStorageEntry, hashOfPayload);
|
||||
}
|
||||
} else {
|
||||
log.debug("Remove data ignored as we don't have an entry for that data.");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
NodeAddress peersNodeAddress = connection.getPeersNodeAddressOptional().get();
|
||||
|
||||
// Backdate all the eligible payloads based on the node that disconnected
|
||||
map.values().stream()
|
||||
.filter(protectedStorageEntry -> protectedStorageEntry.getProtectedStoragePayload() instanceof RequiresOwnerIsOnlinePayload)
|
||||
.filter(protectedStorageEntry -> ((RequiresOwnerIsOnlinePayload) protectedStorageEntry.getProtectedStoragePayload()).getOwnerNodeAddress().equals(peersNodeAddress))
|
||||
.forEach(protectedStorageEntry -> {
|
||||
// We only set the data back by half of the TTL and remove the data only if is has
|
||||
// expired after that back dating.
|
||||
// We might get connection drops which are not caused by the node going offline, so
|
||||
// we give more tolerance with that approach, giving the node the chance to
|
||||
// refresh the TTL with a refresh message.
|
||||
// We observed those issues during stress tests, but it might have been caused by the
|
||||
// test set up (many nodes/connections over 1 router)
|
||||
// TODO investigate what causes the disconnections.
|
||||
// Usually the are: SOCKET_TIMEOUT ,TERMINATED (EOFException)
|
||||
log.debug("Backdating {} due to closeConnectionReason={}", protectedStorageEntry, closeConnectionReason);
|
||||
protectedStorageEntry.backDate();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -304,15 +474,31 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
// Client API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Adds a PersistableNetworkPayload to the local P2P data storage. If it does not already exist locally, it will
|
||||
* be broadcast to the P2P network.
|
||||
* @param payload PersistableNetworkPayload to add to the network
|
||||
* @param sender local NodeAddress, if available
|
||||
* @param allowReBroadcast <code>true</code> if the PersistableNetworkPayload should be rebroadcast even if it
|
||||
* already exists locally
|
||||
* @return <code>true</code> if the PersistableNetworkPayload passes all validation and exists in the P2PDataStore
|
||||
* on completion
|
||||
*/
|
||||
public boolean addPersistableNetworkPayload(PersistableNetworkPayload payload,
|
||||
@Nullable NodeAddress sender,
|
||||
boolean isDataOwner,
|
||||
boolean allowBroadcast,
|
||||
boolean reBroadcast,
|
||||
boolean checkDate) {
|
||||
boolean allowReBroadcast) {
|
||||
return addPersistableNetworkPayload(
|
||||
payload, sender, true, allowReBroadcast, false);
|
||||
}
|
||||
|
||||
private boolean addPersistableNetworkPayload(PersistableNetworkPayload payload,
|
||||
@Nullable NodeAddress sender,
|
||||
boolean allowBroadcast,
|
||||
boolean reBroadcast,
|
||||
boolean checkDate) {
|
||||
log.trace("addPersistableNetworkPayload payload={}", payload);
|
||||
|
||||
// Payload hash size does not match expectation for that type of message.
|
||||
@ -346,7 +532,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
|
||||
|
||||
// Broadcast the payload if requested by caller
|
||||
if (allowBroadcast)
|
||||
broadcaster.broadcast(new AddPersistableNetworkPayloadMessage(payload), sender, null, isDataOwner);
|
||||
broadcaster.broadcast(new AddPersistableNetworkPayloadMessage(payload), sender, null);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -356,27 +542,33 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
|
||||
// Overwriting an entry would be also no issue. We also skip notifying listeners as we get called before the domain
|
||||
// is ready so no listeners are set anyway. We might get called twice from a redundant call later, so listeners
|
||||
// might be added then but as we have the data already added calling them would be irrelevant as well.
|
||||
public boolean addPersistableNetworkPayloadFromInitialRequest(PersistableNetworkPayload payload) {
|
||||
private void addPersistableNetworkPayloadFromInitialRequest(PersistableNetworkPayload payload) {
|
||||
byte[] hash = payload.getHash();
|
||||
if (payload.verifyHashSize()) {
|
||||
ByteArray hashAsByteArray = new ByteArray(hash);
|
||||
appendOnlyDataStoreService.put(hashAsByteArray, payload);
|
||||
return true;
|
||||
} else {
|
||||
log.warn("We got a hash exceeding our permitted size");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a ProtectedStorageEntry to the local P2P data storage. If it does not already exist locally, it will be
|
||||
* broadcast to the P2P network.
|
||||
*
|
||||
* @param protectedStorageEntry ProtectedStorageEntry to add to the network
|
||||
* @param sender local NodeAddress, if available
|
||||
* @param listener optional listener that can be used to receive events on broadcast
|
||||
* @return <code>true</code> if the ProtectedStorageEntry was added to the local P2P data storage and broadcast
|
||||
*/
|
||||
public boolean addProtectedStorageEntry(ProtectedStorageEntry protectedStorageEntry, @Nullable NodeAddress sender,
|
||||
@Nullable BroadcastHandler.Listener listener, boolean isDataOwner) {
|
||||
return addProtectedStorageEntry(protectedStorageEntry, sender, listener, isDataOwner, true);
|
||||
@Nullable BroadcastHandler.Listener listener) {
|
||||
return addProtectedStorageEntry(protectedStorageEntry, sender, listener, true);
|
||||
}
|
||||
|
||||
public boolean addProtectedStorageEntry(ProtectedStorageEntry protectedStorageEntry,
|
||||
private boolean addProtectedStorageEntry(ProtectedStorageEntry protectedStorageEntry,
|
||||
@Nullable NodeAddress sender,
|
||||
@Nullable BroadcastHandler.Listener listener,
|
||||
boolean isDataOwner,
|
||||
boolean allowBroadcast) {
|
||||
ProtectedStoragePayload protectedStoragePayload = protectedStorageEntry.getProtectedStoragePayload();
|
||||
ByteArray hashOfPayload = get32ByteHashAsByteArray(protectedStoragePayload);
|
||||
@ -422,7 +614,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
|
||||
|
||||
// Optionally, broadcast the add/update depending on the calling environment
|
||||
if (allowBroadcast)
|
||||
broadcastProtectedStorageEntry(protectedStorageEntry, sender, listener, isDataOwner);
|
||||
broadcaster.broadcast(new AddDataMessage(protectedStorageEntry), sender, listener);
|
||||
|
||||
// Persist ProtectedStorageEntrys carrying PersistablePayload payloads
|
||||
if (protectedStoragePayload instanceof PersistablePayload)
|
||||
@ -431,16 +623,15 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
|
||||
return true;
|
||||
}
|
||||
|
||||
private void broadcastProtectedStorageEntry(ProtectedStorageEntry protectedStorageEntry,
|
||||
@Nullable NodeAddress sender,
|
||||
@Nullable BroadcastHandler.Listener broadcastListener,
|
||||
boolean isDataOwner) {
|
||||
broadcast(new AddDataMessage(protectedStorageEntry), sender, broadcastListener, isDataOwner);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a local RefreshOffer with TTL changes and broadcasts those changes to the network
|
||||
*
|
||||
* @param refreshTTLMessage refreshTTLMessage containing the update
|
||||
* @param sender local NodeAddress, if available
|
||||
* @return <code>true</code> if the RefreshOffer was successfully updated and changes broadcast
|
||||
*/
|
||||
public boolean refreshTTL(RefreshOfferMessage refreshTTLMessage,
|
||||
@Nullable NodeAddress sender,
|
||||
boolean isDataOwner) {
|
||||
@Nullable NodeAddress sender) {
|
||||
|
||||
ByteArray hashOfPayload = new ByteArray(refreshTTLMessage.getHashOfPayload());
|
||||
ProtectedStorageEntry storedData = map.get(hashOfPayload);
|
||||
@ -476,14 +667,21 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
|
||||
sequenceNumberMapStorage.queueUpForSave(SequenceNumberMap.clone(sequenceNumberMap), 1000);
|
||||
|
||||
// Always broadcast refreshes
|
||||
broadcast(refreshTTLMessage, sender, null, isDataOwner);
|
||||
broadcaster.broadcast(refreshTTLMessage, sender, null);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a ProtectedStorageEntry from the local P2P data storage. If it is successful, it will broadcast that
|
||||
* change to the P2P network.
|
||||
*
|
||||
* @param protectedStorageEntry ProtectedStorageEntry to add to the network
|
||||
* @param sender local NodeAddress, if available
|
||||
* @return <code>true</code> if the ProtectedStorageEntry was removed from the local P2P data storage and broadcast
|
||||
*/
|
||||
public boolean remove(ProtectedStorageEntry protectedStorageEntry,
|
||||
@Nullable NodeAddress sender,
|
||||
boolean isDataOwner) {
|
||||
@Nullable NodeAddress sender) {
|
||||
ProtectedStoragePayload protectedStoragePayload = protectedStorageEntry.getProtectedStoragePayload();
|
||||
ByteArray hashOfPayload = get32ByteHashAsByteArray(protectedStoragePayload);
|
||||
|
||||
@ -504,7 +702,9 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
|
||||
sequenceNumberMap.put(hashOfPayload, new MapValue(protectedStorageEntry.getSequenceNumber(), this.clock.millis()));
|
||||
sequenceNumberMapStorage.queueUpForSave(SequenceNumberMap.clone(sequenceNumberMap), 300);
|
||||
|
||||
maybeAddToRemoveAddOncePayloads(protectedStoragePayload, hashOfPayload);
|
||||
// Update that we have seen this AddOncePayload so the next time it is seen it fails verification
|
||||
if (protectedStoragePayload instanceof AddOncePayload)
|
||||
removedAddOncePayloads.add(hashOfPayload);
|
||||
|
||||
if (storedEntry != null) {
|
||||
// Valid remove entry, do the remove and signal listeners
|
||||
@ -517,9 +717,9 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
|
||||
printData("after remove");
|
||||
|
||||
if (protectedStorageEntry instanceof ProtectedMailboxStorageEntry) {
|
||||
broadcast(new RemoveMailboxDataMessage((ProtectedMailboxStorageEntry) protectedStorageEntry), sender, null, isDataOwner);
|
||||
broadcaster.broadcast(new RemoveMailboxDataMessage((ProtectedMailboxStorageEntry) protectedStorageEntry), sender, null);
|
||||
} else {
|
||||
broadcast(new RemoveDataMessage(protectedStorageEntry), sender, null, isDataOwner);
|
||||
broadcaster.broadcast(new RemoveDataMessage(protectedStorageEntry), sender, null);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -556,13 +756,6 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
|
||||
// source (network).
|
||||
}
|
||||
|
||||
private void maybeAddToRemoveAddOncePayloads(ProtectedStoragePayload protectedStoragePayload,
|
||||
ByteArray hashOfData) {
|
||||
if (protectedStoragePayload instanceof AddOncePayload) {
|
||||
removedAddOncePayloads.add(hashOfData);
|
||||
}
|
||||
}
|
||||
|
||||
public ProtectedStorageEntry getProtectedStorageEntry(ProtectedStoragePayload protectedStoragePayload,
|
||||
KeyPair ownerStoragePubKey)
|
||||
throws CryptoException {
|
||||
@ -689,11 +882,6 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
|
||||
}
|
||||
}
|
||||
|
||||
private void broadcast(BroadcastMessage message, @Nullable NodeAddress sender,
|
||||
@Nullable BroadcastHandler.Listener listener, boolean isDataOwner) {
|
||||
broadcaster.broadcast(message, sender, listener, isDataOwner);
|
||||
}
|
||||
|
||||
public static ByteArray get32ByteHashAsByteArray(NetworkPayload data) {
|
||||
return new ByteArray(P2PDataStorage.get32ByteHash(data));
|
||||
}
|
||||
|
@ -37,6 +37,8 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
@ -63,7 +65,7 @@ public final class MailboxStoragePayload implements ProtectedStoragePayload, Exp
|
||||
private Map<String, String> extraDataMap;
|
||||
|
||||
public MailboxStoragePayload(PrefixedSealedAndSignedMessage prefixedSealedAndSignedMessage,
|
||||
PublicKey senderPubKeyForAddOperation,
|
||||
@NotNull PublicKey senderPubKeyForAddOperation,
|
||||
PublicKey ownerPubKey) {
|
||||
this.prefixedSealedAndSignedMessage = prefixedSealedAndSignedMessage;
|
||||
this.senderPubKeyForAddOperation = senderPubKeyForAddOperation;
|
||||
|
@ -20,8 +20,9 @@ package bisq.network.p2p.storage.payload;
|
||||
import bisq.common.Payload;
|
||||
|
||||
/**
|
||||
* Marker interface for payload which gets delayed processed at startup so we don't hit performance too much.
|
||||
* Used for TradeStatistics and AccountAgeWitness.
|
||||
* Marker interface for PersistableNetworkPayloads that are only added during the FIRST call to
|
||||
* P2PDataStorage::processDataResponse. This improves performance for objects that don't go out
|
||||
* of sync frequently.
|
||||
*/
|
||||
public interface LazyProcessedPayload extends Payload {
|
||||
public interface ProcessOncePersistableNetworkPayload extends Payload {
|
||||
}
|
@ -101,13 +101,12 @@ public class ProtectedMailboxStorageEntry extends ProtectedStorageEntry {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean result = mailboxStoragePayload.getSenderPubKeyForAddOperation() != null &&
|
||||
mailboxStoragePayload.getSenderPubKeyForAddOperation().equals(this.getOwnerPubKey());
|
||||
boolean result = mailboxStoragePayload.getSenderPubKeyForAddOperation().equals(this.getOwnerPubKey());
|
||||
|
||||
if (!result) {
|
||||
String res1 = this.toString();
|
||||
String res2 = "null";
|
||||
if (mailboxStoragePayload != null && mailboxStoragePayload.getOwnerPubKey() != null)
|
||||
if (mailboxStoragePayload.getOwnerPubKey() != null)
|
||||
res2 = Utilities.encodeToHex(mailboxStoragePayload.getSenderPubKeyForAddOperation().getEncoded(),true);
|
||||
|
||||
log.warn("ProtectedMailboxStorageEntry::isValidForAddOperation() failed. " +
|
||||
@ -141,7 +140,7 @@ public class ProtectedMailboxStorageEntry extends ProtectedStorageEntry {
|
||||
if (!result) {
|
||||
String res1 = this.toString();
|
||||
String res2 = "null";
|
||||
if (mailboxStoragePayload != null && mailboxStoragePayload.getOwnerPubKey() != null)
|
||||
if (mailboxStoragePayload.getOwnerPubKey() != null)
|
||||
res2 = Utilities.encodeToHex(mailboxStoragePayload.getOwnerPubKey().getEncoded(), true);
|
||||
|
||||
log.warn("ProtectedMailboxStorageEntry::isValidForRemoveOperation() failed. " +
|
||||
|
@ -39,6 +39,8 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@Getter
|
||||
@EqualsAndHashCode
|
||||
@Slf4j
|
||||
@ -47,14 +49,14 @@ public class ProtectedStorageEntry implements NetworkPayload, PersistablePayload
|
||||
private final byte[] ownerPubKeyBytes;
|
||||
transient private final PublicKey ownerPubKey;
|
||||
private final int sequenceNumber;
|
||||
private byte[] signature;
|
||||
private final byte[] signature;
|
||||
private long creationTimeStamp;
|
||||
|
||||
public ProtectedStorageEntry(ProtectedStoragePayload protectedStoragePayload,
|
||||
PublicKey ownerPubKey,
|
||||
int sequenceNumber,
|
||||
byte[] signature,
|
||||
Clock clock) {
|
||||
public ProtectedStorageEntry(@NotNull ProtectedStoragePayload protectedStoragePayload,
|
||||
@NotNull PublicKey ownerPubKey,
|
||||
int sequenceNumber,
|
||||
byte[] signature,
|
||||
Clock clock) {
|
||||
this(protectedStoragePayload,
|
||||
Sig.getPublicKeyBytes(ownerPubKey),
|
||||
ownerPubKey,
|
||||
@ -64,13 +66,13 @@ public class ProtectedStorageEntry implements NetworkPayload, PersistablePayload
|
||||
clock);
|
||||
}
|
||||
|
||||
protected ProtectedStorageEntry(ProtectedStoragePayload protectedStoragePayload,
|
||||
byte[] ownerPubKeyBytes,
|
||||
PublicKey ownerPubKey,
|
||||
int sequenceNumber,
|
||||
byte[] signature,
|
||||
long creationTimeStamp,
|
||||
Clock clock) {
|
||||
protected ProtectedStorageEntry(@NotNull ProtectedStoragePayload protectedStoragePayload,
|
||||
byte[] ownerPubKeyBytes,
|
||||
@NotNull PublicKey ownerPubKey,
|
||||
int sequenceNumber,
|
||||
byte[] signature,
|
||||
long creationTimeStamp,
|
||||
Clock clock) {
|
||||
|
||||
Preconditions.checkArgument(!(protectedStoragePayload instanceof PersistableNetworkPayload));
|
||||
|
||||
@ -80,21 +82,21 @@ public class ProtectedStorageEntry implements NetworkPayload, PersistablePayload
|
||||
|
||||
this.sequenceNumber = sequenceNumber;
|
||||
this.signature = signature;
|
||||
this.creationTimeStamp = creationTimeStamp;
|
||||
|
||||
maybeAdjustCreationTimeStamp(clock);
|
||||
// We don't allow creation date in the future, but we cannot be too strict as clocks are not synced
|
||||
this.creationTimeStamp = Math.min(creationTimeStamp, clock.millis());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PROTO BUFFER
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private ProtectedStorageEntry(ProtectedStoragePayload protectedStoragePayload,
|
||||
byte[] ownerPubKeyBytes,
|
||||
int sequenceNumber,
|
||||
byte[] signature,
|
||||
long creationTimeStamp,
|
||||
Clock clock) {
|
||||
private ProtectedStorageEntry(@NotNull ProtectedStoragePayload protectedStoragePayload,
|
||||
byte[] ownerPubKeyBytes,
|
||||
int sequenceNumber,
|
||||
byte[] signature,
|
||||
long creationTimeStamp,
|
||||
Clock clock) {
|
||||
this(protectedStoragePayload,
|
||||
ownerPubKeyBytes,
|
||||
Sig.getPublicKeyFromBytes(ownerPubKeyBytes),
|
||||
@ -135,22 +137,11 @@ public class ProtectedStorageEntry implements NetworkPayload, PersistablePayload
|
||||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void maybeAdjustCreationTimeStamp(Clock clock) {
|
||||
// We don't allow creation date in the future, but we cannot be too strict as clocks are not synced
|
||||
if (creationTimeStamp > clock.millis())
|
||||
creationTimeStamp = clock.millis();
|
||||
}
|
||||
|
||||
public void backDate() {
|
||||
if (protectedStoragePayload instanceof ExpirablePayload)
|
||||
creationTimeStamp -= ((ExpirablePayload) protectedStoragePayload).getTTL() / 2;
|
||||
}
|
||||
|
||||
// TODO: only used in tests so find a better way to test and delete public API
|
||||
public void updateSignature(byte[] signature) {
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public boolean isExpired(Clock clock) {
|
||||
return protectedStoragePayload instanceof ExpirablePayload &&
|
||||
(clock.millis() - creationTimeStamp) > ((ExpirablePayload) protectedStoragePayload).getTTL();
|
||||
@ -167,18 +158,15 @@ public class ProtectedStorageEntry implements NetworkPayload, PersistablePayload
|
||||
// TODO: The code currently supports MailboxStoragePayload objects inside ProtectedStorageEntry. Fix this.
|
||||
if (protectedStoragePayload instanceof MailboxStoragePayload) {
|
||||
MailboxStoragePayload mailboxStoragePayload = (MailboxStoragePayload) this.getProtectedStoragePayload();
|
||||
return mailboxStoragePayload.getSenderPubKeyForAddOperation() != null &&
|
||||
mailboxStoragePayload.getSenderPubKeyForAddOperation().equals(this.getOwnerPubKey());
|
||||
return mailboxStoragePayload.getSenderPubKeyForAddOperation().equals(this.getOwnerPubKey());
|
||||
|
||||
} else {
|
||||
boolean result = this.ownerPubKey != null &&
|
||||
this.protectedStoragePayload != null &&
|
||||
this.ownerPubKey.equals(protectedStoragePayload.getOwnerPubKey());
|
||||
boolean result = this.ownerPubKey.equals(protectedStoragePayload.getOwnerPubKey());
|
||||
|
||||
if (!result) {
|
||||
String res1 = this.toString();
|
||||
String res2 = "null";
|
||||
if (protectedStoragePayload != null && protectedStoragePayload.getOwnerPubKey() != null)
|
||||
if (protectedStoragePayload.getOwnerPubKey() != null)
|
||||
res2 = Utilities.encodeToHex(protectedStoragePayload.getOwnerPubKey().getEncoded(), true);
|
||||
|
||||
log.warn("ProtectedStorageEntry::isValidForAddOperation() failed. Entry owner does not match Payload owner:\n" +
|
||||
@ -201,7 +189,7 @@ public class ProtectedStorageEntry implements NetworkPayload, PersistablePayload
|
||||
if (!result) {
|
||||
String res1 = this.toString();
|
||||
String res2 = "null";
|
||||
if (protectedStoragePayload != null && protectedStoragePayload.getOwnerPubKey() != null)
|
||||
if (protectedStoragePayload.getOwnerPubKey() != null)
|
||||
res2 = Utilities.encodeToHex(protectedStoragePayload.getOwnerPubKey().getEncoded(), true);
|
||||
|
||||
log.warn("ProtectedStorageEntry::isValidForRemoveOperation() failed. Entry owner does not match Payload owner:\n" +
|
||||
@ -239,9 +227,8 @@ public class ProtectedStorageEntry implements NetworkPayload, PersistablePayload
|
||||
boolean result = protectedStorageEntry.getOwnerPubKey().equals(this.ownerPubKey);
|
||||
|
||||
if (!result) {
|
||||
log.warn("New data entry does not match our stored data. storedData.ownerPubKey=" +
|
||||
(protectedStorageEntry.getOwnerPubKey() != null ? protectedStorageEntry.getOwnerPubKey().toString() : "null") +
|
||||
", ownerPubKey=" + this.ownerPubKey);
|
||||
log.warn("New data entry does not match our stored data. storedData.ownerPubKey={}, ownerPubKey={}}",
|
||||
protectedStorageEntry.getOwnerPubKey().toString(), this.ownerPubKey);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -0,0 +1,481 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.network.p2p.storage;
|
||||
|
||||
import bisq.network.p2p.NodeAddress;
|
||||
import bisq.network.p2p.TestUtils;
|
||||
import bisq.network.p2p.network.NetworkNode;
|
||||
import bisq.network.p2p.peers.getdata.messages.GetDataRequest;
|
||||
import bisq.network.p2p.peers.getdata.messages.GetDataResponse;
|
||||
import bisq.network.p2p.peers.getdata.messages.GetUpdatedDataRequest;
|
||||
import bisq.network.p2p.peers.getdata.messages.PreliminaryGetDataRequest;
|
||||
import bisq.network.p2p.storage.mocks.PersistableNetworkPayloadStub;
|
||||
import bisq.network.p2p.storage.payload.CapabilityRequiringPayload;
|
||||
import bisq.network.p2p.storage.payload.PersistableNetworkPayload;
|
||||
import bisq.network.p2p.storage.payload.ProtectedStorageEntry;
|
||||
import bisq.network.p2p.storage.payload.ProtectedStoragePayload;
|
||||
|
||||
import bisq.common.app.Capabilities;
|
||||
import bisq.common.app.Capability;
|
||||
import bisq.common.crypto.Sig;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.withSettings;
|
||||
|
||||
|
||||
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
public class P2PDataStorageBuildGetDataResponseTest {
|
||||
abstract static class P2PDataStorageBuildGetDataResponseTestBase {
|
||||
// GIVEN null & non-null supportedCapabilities
|
||||
private TestState testState;
|
||||
|
||||
abstract GetDataRequest buildGetDataRequest(int nonce, Set<byte[]> knownKeys);
|
||||
|
||||
@Mock
|
||||
NetworkNode networkNode;
|
||||
|
||||
private NodeAddress localNodeAddress;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
this.testState = new TestState();
|
||||
|
||||
this.localNodeAddress = new NodeAddress("localhost", 8080);
|
||||
when(networkNode.getNodeAddress()).thenReturn(this.localNodeAddress);
|
||||
|
||||
// Set up basic capabilities to ensure message contains it
|
||||
Capabilities.app.addAll(Capability.MEDIATION);
|
||||
}
|
||||
|
||||
static class RequiredCapabilitiesPNPStub extends PersistableNetworkPayloadStub
|
||||
implements CapabilityRequiringPayload {
|
||||
Capabilities capabilities;
|
||||
|
||||
RequiredCapabilitiesPNPStub(Capabilities capabilities, byte[] hash) {
|
||||
super(hash);
|
||||
this.capabilities = capabilities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Capabilities getRequiredCapabilities() {
|
||||
return capabilities;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a unique ProtectedStorageEntry that is valid for add. This is used to initialize P2PDataStorage state
|
||||
* so the tests can validate the correct behavior. Adds of identical payloads with different sequence numbers
|
||||
* is not supported.
|
||||
*/
|
||||
private ProtectedStorageEntry getProtectedStorageEntryForAdd() throws NoSuchAlgorithmException {
|
||||
return getProtectedStorageEntryForAdd(null);
|
||||
}
|
||||
|
||||
private ProtectedStorageEntry getProtectedStorageEntryForAdd(Capabilities requiredCapabilities)
|
||||
throws NoSuchAlgorithmException {
|
||||
KeyPair ownerKeys = TestUtils.generateKeyPair();
|
||||
|
||||
// Payload stub
|
||||
ProtectedStoragePayload protectedStoragePayload;
|
||||
|
||||
if (requiredCapabilities == null)
|
||||
protectedStoragePayload = mock(ProtectedStoragePayload.class);
|
||||
else {
|
||||
protectedStoragePayload = mock(ProtectedStoragePayload.class,
|
||||
withSettings().extraInterfaces(CapabilityRequiringPayload.class));
|
||||
when(((CapabilityRequiringPayload) protectedStoragePayload).getRequiredCapabilities())
|
||||
.thenReturn(requiredCapabilities);
|
||||
}
|
||||
|
||||
Message messageMock = mock(Message.class);
|
||||
when(messageMock.toByteArray()).thenReturn(Sig.getPublicKeyBytes(ownerKeys.getPublic()));
|
||||
when(protectedStoragePayload.toProtoMessage()).thenReturn(messageMock);
|
||||
|
||||
// Entry stub
|
||||
ProtectedStorageEntry stub = mock(ProtectedStorageEntry.class);
|
||||
when(stub.getOwnerPubKey()).thenReturn(ownerKeys.getPublic());
|
||||
when(stub.isValidForAddOperation()).thenReturn(true);
|
||||
when(stub.matchesRelevantPubKey(any(ProtectedStorageEntry.class))).thenReturn(true);
|
||||
when(stub.getSequenceNumber()).thenReturn(1);
|
||||
when(stub.getProtectedStoragePayload()).thenReturn(protectedStoragePayload);
|
||||
|
||||
return stub;
|
||||
}
|
||||
|
||||
// TESTCASE: Given a GetDataRequest w/ unknown PNP, nothing is sent back
|
||||
@Test
|
||||
public void buildGetDataResponse_unknownPNPDoNothing() {
|
||||
PersistableNetworkPayload fromPeer = new PersistableNetworkPayloadStub(new byte[]{1});
|
||||
|
||||
GetDataRequest getDataRequest =
|
||||
this.buildGetDataRequest(1, new HashSet<>(Collections.singletonList(fromPeer.getHash())));
|
||||
|
||||
AtomicBoolean outPNPTruncated = new AtomicBoolean(false);
|
||||
AtomicBoolean outPSETruncated = new AtomicBoolean(false);
|
||||
Capabilities peerCapabilities = new Capabilities();
|
||||
GetDataResponse getDataResponse = this.testState.mockedStorage.buildGetDataResponse(
|
||||
getDataRequest, 1, outPNPTruncated, outPSETruncated, peerCapabilities);
|
||||
|
||||
Assert.assertFalse(outPNPTruncated.get());
|
||||
Assert.assertFalse(outPSETruncated.get());
|
||||
Assert.assertEquals(1, getDataResponse.getRequestNonce());
|
||||
Assert.assertEquals(getDataRequest instanceof GetUpdatedDataRequest, getDataResponse.isGetUpdatedDataResponse());
|
||||
Assert.assertEquals(getDataResponse.getSupportedCapabilities(), Capabilities.app);
|
||||
Assert.assertTrue(getDataResponse.getPersistableNetworkPayloadSet().isEmpty());
|
||||
Assert.assertTrue(getDataResponse.getDataSet().isEmpty());
|
||||
}
|
||||
|
||||
// TESTCASE: Given a GetDataRequest w/ known PNP, nothing is sent back
|
||||
@Test
|
||||
public void buildGetDataResponse_knownPNPDoNothing() {
|
||||
PersistableNetworkPayload fromPeerAndLocal = new PersistableNetworkPayloadStub(new byte[]{1});
|
||||
|
||||
this.testState.mockedStorage.addPersistableNetworkPayload(
|
||||
fromPeerAndLocal, this.localNodeAddress, false);
|
||||
|
||||
GetDataRequest getDataRequest =
|
||||
this.buildGetDataRequest(
|
||||
1,
|
||||
new HashSet<>(Collections.singletonList(fromPeerAndLocal.getHash())));
|
||||
|
||||
AtomicBoolean outPNPTruncated = new AtomicBoolean(false);
|
||||
AtomicBoolean outPSETruncated = new AtomicBoolean(false);
|
||||
Capabilities peerCapabilities = new Capabilities();
|
||||
GetDataResponse getDataResponse = this.testState.mockedStorage.buildGetDataResponse(
|
||||
getDataRequest, 1, outPNPTruncated, outPSETruncated, peerCapabilities);
|
||||
|
||||
Assert.assertFalse(outPNPTruncated.get());
|
||||
Assert.assertFalse(outPSETruncated.get());
|
||||
Assert.assertEquals(1, getDataResponse.getRequestNonce());
|
||||
Assert.assertEquals(getDataRequest instanceof GetUpdatedDataRequest, getDataResponse.isGetUpdatedDataResponse());
|
||||
Assert.assertEquals(getDataResponse.getSupportedCapabilities(), Capabilities.app);
|
||||
Assert.assertTrue(getDataResponse.getPersistableNetworkPayloadSet().isEmpty());
|
||||
Assert.assertTrue(getDataResponse.getDataSet().isEmpty());
|
||||
}
|
||||
|
||||
// TESTCASE: Given a GetDataRequest w/o known PNP, send it back
|
||||
@Test
|
||||
public void buildGetDataResponse_unknownPNPSendBack() {
|
||||
PersistableNetworkPayload onlyLocal = new PersistableNetworkPayloadStub(new byte[]{1});
|
||||
|
||||
this.testState.mockedStorage.addPersistableNetworkPayload(
|
||||
onlyLocal, this.localNodeAddress, false);
|
||||
|
||||
GetDataRequest getDataRequest =
|
||||
this.buildGetDataRequest(1, new HashSet<>());
|
||||
|
||||
AtomicBoolean outPNPTruncated = new AtomicBoolean(false);
|
||||
AtomicBoolean outPSETruncated = new AtomicBoolean(false);
|
||||
Capabilities peerCapabilities = new Capabilities();
|
||||
GetDataResponse getDataResponse = this.testState.mockedStorage.buildGetDataResponse(
|
||||
getDataRequest, 1, outPNPTruncated, outPSETruncated, peerCapabilities);
|
||||
|
||||
Assert.assertFalse(outPNPTruncated.get());
|
||||
Assert.assertFalse(outPSETruncated.get());
|
||||
Assert.assertEquals(1, getDataResponse.getRequestNonce());
|
||||
Assert.assertEquals(getDataRequest instanceof GetUpdatedDataRequest, getDataResponse.isGetUpdatedDataResponse());
|
||||
Assert.assertEquals(getDataResponse.getSupportedCapabilities(), Capabilities.app);
|
||||
Assert.assertTrue(getDataResponse.getPersistableNetworkPayloadSet().contains(onlyLocal));
|
||||
Assert.assertTrue(getDataResponse.getDataSet().isEmpty());
|
||||
}
|
||||
|
||||
// TESTCASE: Given a GetDataRequest w/o known PNP, don't send more than truncation limit
|
||||
@Test
|
||||
public void buildGetDataResponse_unknownPNPSendBackTruncation() {
|
||||
PersistableNetworkPayload onlyLocal1 = new PersistableNetworkPayloadStub(new byte[]{1});
|
||||
PersistableNetworkPayload onlyLocal2 = new PersistableNetworkPayloadStub(new byte[]{2});
|
||||
|
||||
this.testState.mockedStorage.addPersistableNetworkPayload(
|
||||
onlyLocal1, this.localNodeAddress, false);
|
||||
this.testState.mockedStorage.addPersistableNetworkPayload(
|
||||
onlyLocal2, this.localNodeAddress, false);
|
||||
|
||||
GetDataRequest getDataRequest =
|
||||
this.buildGetDataRequest(1, new HashSet<>());
|
||||
|
||||
AtomicBoolean outPNPTruncated = new AtomicBoolean(false);
|
||||
AtomicBoolean outPSETruncated = new AtomicBoolean(false);
|
||||
Capabilities peerCapabilities = new Capabilities();
|
||||
GetDataResponse getDataResponse = this.testState.mockedStorage.buildGetDataResponse(
|
||||
getDataRequest, 1, outPNPTruncated, outPSETruncated, peerCapabilities);
|
||||
|
||||
Assert.assertTrue(outPNPTruncated.get());
|
||||
Assert.assertFalse(outPSETruncated.get());
|
||||
Assert.assertEquals(1, getDataResponse.getRequestNonce());
|
||||
Assert.assertEquals(getDataRequest instanceof GetUpdatedDataRequest, getDataResponse.isGetUpdatedDataResponse());
|
||||
Assert.assertEquals(getDataResponse.getSupportedCapabilities(), Capabilities.app);
|
||||
Assert.assertEquals(1, getDataResponse.getPersistableNetworkPayloadSet().size());
|
||||
Assert.assertTrue(getDataResponse.getPersistableNetworkPayloadSet().contains(onlyLocal1));
|
||||
Assert.assertTrue(getDataResponse.getDataSet().isEmpty());
|
||||
}
|
||||
|
||||
// TESTCASE: Given a GetDataRequest w/o known PNP, but missing required capabilities, nothing is sent back
|
||||
@Test
|
||||
public void buildGetDataResponse_unknownPNPCapabilitiesMismatchDontSendBack() {
|
||||
PersistableNetworkPayload onlyLocal =
|
||||
new RequiredCapabilitiesPNPStub(new Capabilities(Collections.singletonList(Capability.MEDIATION)),
|
||||
new byte[]{1});
|
||||
|
||||
this.testState.mockedStorage.addPersistableNetworkPayload(
|
||||
onlyLocal, this.localNodeAddress, false);
|
||||
|
||||
GetDataRequest getDataRequest =
|
||||
this.buildGetDataRequest(1, new HashSet<>());
|
||||
|
||||
AtomicBoolean outPNPTruncated = new AtomicBoolean(false);
|
||||
AtomicBoolean outPSETruncated = new AtomicBoolean(false);
|
||||
Capabilities peerCapabilities = new Capabilities();
|
||||
GetDataResponse getDataResponse = this.testState.mockedStorage.buildGetDataResponse(
|
||||
getDataRequest, 2, outPNPTruncated, outPSETruncated, peerCapabilities);
|
||||
|
||||
Assert.assertFalse(outPNPTruncated.get());
|
||||
Assert.assertFalse(outPSETruncated.get());
|
||||
Assert.assertEquals(1, getDataResponse.getRequestNonce());
|
||||
Assert.assertEquals(getDataRequest instanceof GetUpdatedDataRequest, getDataResponse.isGetUpdatedDataResponse());
|
||||
Assert.assertEquals(getDataResponse.getSupportedCapabilities(), Capabilities.app);
|
||||
Assert.assertTrue(getDataResponse.getPersistableNetworkPayloadSet().isEmpty());
|
||||
Assert.assertTrue(getDataResponse.getDataSet().isEmpty());
|
||||
}
|
||||
|
||||
// TESTCASE: Given a GetDataRequest w/o known PNP that requires capabilities (and they match) send it back
|
||||
@Test
|
||||
public void buildGetDataResponse_unknownPNPCapabilitiesMatch() {
|
||||
PersistableNetworkPayload onlyLocal =
|
||||
new RequiredCapabilitiesPNPStub(new Capabilities(Collections.singletonList(Capability.MEDIATION)),
|
||||
new byte[]{1});
|
||||
|
||||
this.testState.mockedStorage.addPersistableNetworkPayload(
|
||||
onlyLocal, this.localNodeAddress, false);
|
||||
|
||||
GetDataRequest getDataRequest =
|
||||
this.buildGetDataRequest(1, new HashSet<>());
|
||||
|
||||
AtomicBoolean outPNPTruncated = new AtomicBoolean(false);
|
||||
AtomicBoolean outPSETruncated = new AtomicBoolean(false);
|
||||
Capabilities peerCapabilities = new Capabilities(Collections.singletonList(Capability.MEDIATION));
|
||||
GetDataResponse getDataResponse = this.testState.mockedStorage.buildGetDataResponse(
|
||||
getDataRequest, 2, outPNPTruncated, outPSETruncated, peerCapabilities);
|
||||
|
||||
Assert.assertFalse(outPNPTruncated.get());
|
||||
Assert.assertFalse(outPSETruncated.get());
|
||||
Assert.assertEquals(1, getDataResponse.getRequestNonce());
|
||||
Assert.assertEquals(getDataRequest instanceof GetUpdatedDataRequest, getDataResponse.isGetUpdatedDataResponse());
|
||||
Assert.assertEquals(getDataResponse.getSupportedCapabilities(), Capabilities.app);
|
||||
Assert.assertTrue(getDataResponse.getPersistableNetworkPayloadSet().contains(onlyLocal));
|
||||
Assert.assertTrue(getDataResponse.getDataSet().isEmpty());
|
||||
}
|
||||
|
||||
// TESTCASE: Given a GetDataRequest w/ unknown PSE, nothing is sent back
|
||||
@Test
|
||||
public void buildGetDataResponse_unknownPSEDoNothing() throws NoSuchAlgorithmException {
|
||||
ProtectedStorageEntry fromPeer = getProtectedStorageEntryForAdd();
|
||||
|
||||
GetDataRequest getDataRequest =
|
||||
this.buildGetDataRequest(1,
|
||||
new HashSet<>(Collections.singletonList(
|
||||
P2PDataStorage.get32ByteHash(fromPeer.getProtectedStoragePayload()))));
|
||||
|
||||
AtomicBoolean outPNPTruncated = new AtomicBoolean(false);
|
||||
AtomicBoolean outPSETruncated = new AtomicBoolean(false);
|
||||
Capabilities peerCapabilities = new Capabilities();
|
||||
GetDataResponse getDataResponse = this.testState.mockedStorage.buildGetDataResponse(
|
||||
getDataRequest, 1, outPNPTruncated, outPSETruncated, peerCapabilities);
|
||||
|
||||
Assert.assertFalse(outPNPTruncated.get());
|
||||
Assert.assertFalse(outPSETruncated.get());
|
||||
Assert.assertEquals(1, getDataResponse.getRequestNonce());
|
||||
Assert.assertEquals(getDataRequest instanceof GetUpdatedDataRequest, getDataResponse.isGetUpdatedDataResponse());
|
||||
Assert.assertEquals(getDataResponse.getSupportedCapabilities(), Capabilities.app);
|
||||
Assert.assertTrue(getDataResponse.getPersistableNetworkPayloadSet().isEmpty());
|
||||
Assert.assertTrue(getDataResponse.getDataSet().isEmpty());
|
||||
}
|
||||
|
||||
// TESTCASE: Given a GetDataRequest w/ known PSE, nothing is sent back
|
||||
@Test
|
||||
public void buildGetDataResponse_knownPSEDoNothing() throws NoSuchAlgorithmException {
|
||||
ProtectedStorageEntry fromPeerAndLocal = getProtectedStorageEntryForAdd();
|
||||
|
||||
GetDataRequest getDataRequest =
|
||||
this.buildGetDataRequest(1,
|
||||
new HashSet<>(Collections.singletonList(
|
||||
P2PDataStorage.get32ByteHash(fromPeerAndLocal.getProtectedStoragePayload()))));
|
||||
|
||||
this.testState.mockedStorage.addProtectedStorageEntry(
|
||||
fromPeerAndLocal, this.localNodeAddress, null);
|
||||
|
||||
AtomicBoolean outPNPTruncated = new AtomicBoolean(false);
|
||||
AtomicBoolean outPSETruncated = new AtomicBoolean(false);
|
||||
Capabilities peerCapabilities = new Capabilities();
|
||||
GetDataResponse getDataResponse = this.testState.mockedStorage.buildGetDataResponse(
|
||||
getDataRequest, 1, outPNPTruncated, outPSETruncated, peerCapabilities);
|
||||
|
||||
Assert.assertFalse(outPNPTruncated.get());
|
||||
Assert.assertFalse(outPSETruncated.get());
|
||||
Assert.assertEquals(1, getDataResponse.getRequestNonce());
|
||||
Assert.assertEquals(getDataRequest instanceof GetUpdatedDataRequest, getDataResponse.isGetUpdatedDataResponse());
|
||||
Assert.assertEquals(getDataResponse.getSupportedCapabilities(), Capabilities.app);
|
||||
Assert.assertTrue(getDataResponse.getPersistableNetworkPayloadSet().isEmpty());
|
||||
Assert.assertTrue(getDataResponse.getDataSet().isEmpty());
|
||||
}
|
||||
|
||||
// TESTCASE: Given a GetDataRequest w/o known PSE, send it back
|
||||
@Test
|
||||
public void buildGetDataResponse_unknownPSESendBack() throws NoSuchAlgorithmException {
|
||||
ProtectedStorageEntry onlyLocal = getProtectedStorageEntryForAdd();
|
||||
|
||||
GetDataRequest getDataRequest = this.buildGetDataRequest(1, new HashSet<>());
|
||||
|
||||
this.testState.mockedStorage.addProtectedStorageEntry(
|
||||
onlyLocal, this.localNodeAddress, null);
|
||||
|
||||
AtomicBoolean outPNPTruncated = new AtomicBoolean(false);
|
||||
AtomicBoolean outPSETruncated = new AtomicBoolean(false);
|
||||
Capabilities peerCapabilities = new Capabilities();
|
||||
GetDataResponse getDataResponse = this.testState.mockedStorage.buildGetDataResponse(
|
||||
getDataRequest, 1, outPNPTruncated, outPSETruncated, peerCapabilities);
|
||||
|
||||
Assert.assertFalse(outPNPTruncated.get());
|
||||
Assert.assertFalse(outPSETruncated.get());
|
||||
Assert.assertEquals(1, getDataResponse.getRequestNonce());
|
||||
Assert.assertEquals(getDataRequest instanceof GetUpdatedDataRequest, getDataResponse.isGetUpdatedDataResponse());
|
||||
Assert.assertEquals(getDataResponse.getSupportedCapabilities(), Capabilities.app);
|
||||
Assert.assertTrue(getDataResponse.getPersistableNetworkPayloadSet().isEmpty());
|
||||
Assert.assertTrue(getDataResponse.getDataSet().contains(onlyLocal));
|
||||
}
|
||||
|
||||
// TESTCASE: Given a GetDataRequest w/o known PNP, don't send more than truncation limit
|
||||
@Test
|
||||
public void buildGetDataResponse_unknownPSESendBackTruncation() throws NoSuchAlgorithmException {
|
||||
ProtectedStorageEntry onlyLocal1 = getProtectedStorageEntryForAdd();
|
||||
ProtectedStorageEntry onlyLocal2 = getProtectedStorageEntryForAdd();
|
||||
|
||||
GetDataRequest getDataRequest = this.buildGetDataRequest(1, new HashSet<>());
|
||||
|
||||
this.testState.mockedStorage.addProtectedStorageEntry(
|
||||
onlyLocal1, this.localNodeAddress, null);
|
||||
this.testState.mockedStorage.addProtectedStorageEntry(
|
||||
onlyLocal2, this.localNodeAddress, null);
|
||||
|
||||
AtomicBoolean outPNPTruncated = new AtomicBoolean(false);
|
||||
AtomicBoolean outPSETruncated = new AtomicBoolean(false);
|
||||
Capabilities peerCapabilities = new Capabilities();
|
||||
GetDataResponse getDataResponse = this.testState.mockedStorage.buildGetDataResponse(
|
||||
getDataRequest, 1, outPNPTruncated, outPSETruncated, peerCapabilities);
|
||||
|
||||
Assert.assertFalse(outPNPTruncated.get());
|
||||
Assert.assertTrue(outPSETruncated.get());
|
||||
Assert.assertEquals(1, getDataResponse.getRequestNonce());
|
||||
Assert.assertEquals(getDataRequest instanceof GetUpdatedDataRequest, getDataResponse.isGetUpdatedDataResponse());
|
||||
Assert.assertEquals(getDataResponse.getSupportedCapabilities(), Capabilities.app);
|
||||
Assert.assertTrue(getDataResponse.getPersistableNetworkPayloadSet().isEmpty());
|
||||
Assert.assertEquals(1, getDataResponse.getDataSet().size());
|
||||
Assert.assertTrue(
|
||||
getDataResponse.getDataSet().contains(onlyLocal1)
|
||||
|| getDataResponse.getDataSet().contains(onlyLocal2));
|
||||
}
|
||||
|
||||
// TESTCASE: Given a GetDataRequest w/o known PNP, but missing required capabilities, nothing is sent back
|
||||
@Test
|
||||
public void buildGetDataResponse_unknownPSECapabilitiesMismatchDontSendBack() throws NoSuchAlgorithmException {
|
||||
ProtectedStorageEntry onlyLocal =
|
||||
getProtectedStorageEntryForAdd(new Capabilities(Collections.singletonList(Capability.MEDIATION)));
|
||||
|
||||
this.testState.mockedStorage.addProtectedStorageEntry(
|
||||
onlyLocal, this.localNodeAddress, null);
|
||||
|
||||
GetDataRequest getDataRequest = this.buildGetDataRequest(1, new HashSet<>());
|
||||
|
||||
AtomicBoolean outPNPTruncated = new AtomicBoolean(false);
|
||||
AtomicBoolean outPSETruncated = new AtomicBoolean(false);
|
||||
Capabilities peerCapabilities = new Capabilities();
|
||||
GetDataResponse getDataResponse = this.testState.mockedStorage.buildGetDataResponse(
|
||||
getDataRequest, 2, outPNPTruncated, outPSETruncated, peerCapabilities);
|
||||
|
||||
Assert.assertFalse(outPNPTruncated.get());
|
||||
Assert.assertFalse(outPSETruncated.get());
|
||||
Assert.assertEquals(1, getDataResponse.getRequestNonce());
|
||||
Assert.assertEquals(getDataRequest instanceof GetUpdatedDataRequest, getDataResponse.isGetUpdatedDataResponse());
|
||||
Assert.assertEquals(getDataResponse.getSupportedCapabilities(), Capabilities.app);
|
||||
Assert.assertTrue(getDataResponse.getPersistableNetworkPayloadSet().isEmpty());
|
||||
Assert.assertTrue(getDataResponse.getDataSet().isEmpty());
|
||||
}
|
||||
|
||||
// TESTCASE: Given a GetDataRequest w/o known PNP that requires capabilities (and they match) send it back
|
||||
@Test
|
||||
public void buildGetDataResponse_unknownPSECapabilitiesMatch() throws NoSuchAlgorithmException {
|
||||
ProtectedStorageEntry onlyLocal =
|
||||
getProtectedStorageEntryForAdd(new Capabilities(Collections.singletonList(Capability.MEDIATION)));
|
||||
|
||||
this.testState.mockedStorage.addProtectedStorageEntry(
|
||||
onlyLocal, this.localNodeAddress, null);
|
||||
|
||||
GetDataRequest getDataRequest =
|
||||
this.buildGetDataRequest(1, new HashSet<>());
|
||||
|
||||
AtomicBoolean outPNPTruncated = new AtomicBoolean(false);
|
||||
AtomicBoolean outPSETruncated = new AtomicBoolean(false);
|
||||
Capabilities peerCapabilities = new Capabilities(Collections.singletonList(Capability.MEDIATION));
|
||||
GetDataResponse getDataResponse = this.testState.mockedStorage.buildGetDataResponse(
|
||||
getDataRequest, 2, outPNPTruncated, outPSETruncated, peerCapabilities);
|
||||
|
||||
Assert.assertFalse(outPNPTruncated.get());
|
||||
Assert.assertFalse(outPSETruncated.get());
|
||||
Assert.assertEquals(1, getDataResponse.getRequestNonce());
|
||||
Assert.assertEquals(getDataRequest instanceof GetUpdatedDataRequest, getDataResponse.isGetUpdatedDataResponse());
|
||||
Assert.assertEquals(getDataResponse.getSupportedCapabilities(), Capabilities.app);
|
||||
Assert.assertTrue(getDataResponse.getPersistableNetworkPayloadSet().isEmpty());
|
||||
Assert.assertTrue(getDataResponse.getDataSet().contains(onlyLocal));
|
||||
}
|
||||
}
|
||||
|
||||
public static class P2PDataStorageBuildGetDataResponseTestPreliminary extends P2PDataStorageBuildGetDataResponseTestBase {
|
||||
|
||||
@Override
|
||||
GetDataRequest buildGetDataRequest(int nonce, Set<byte[]> knownKeys) {
|
||||
return new PreliminaryGetDataRequest(nonce, knownKeys);
|
||||
}
|
||||
}
|
||||
|
||||
public static class P2PDataStorageBuildGetDataResponseTestUpdated extends P2PDataStorageBuildGetDataResponseTestBase {
|
||||
|
||||
@Override
|
||||
GetDataRequest buildGetDataRequest(int nonce, Set<byte[]> knownKeys) {
|
||||
return new GetUpdatedDataRequest(new NodeAddress("peer", 10), nonce, knownKeys);
|
||||
}
|
||||
}
|
||||
}
|
@ -71,9 +71,9 @@ public class P2PDataStorageClientAPITest {
|
||||
ProtectedStorageEntry protectedStorageEntry = this.testState.mockedStorage.getProtectedStorageEntry(protectedStoragePayload, ownerKeys);
|
||||
|
||||
SavedTestState beforeState = this.testState.saveTestState(protectedStorageEntry);
|
||||
Assert.assertTrue(this.testState.mockedStorage.addProtectedStorageEntry(protectedStorageEntry, TestState.getTestNodeAddress(), null, true));
|
||||
Assert.assertTrue(this.testState.mockedStorage.addProtectedStorageEntry(protectedStorageEntry, TestState.getTestNodeAddress(), null));
|
||||
|
||||
this.testState.verifyProtectedStorageAdd(beforeState, protectedStorageEntry, true, true);
|
||||
this.testState.verifyProtectedStorageAdd(beforeState, protectedStorageEntry, true, true, true, true);
|
||||
}
|
||||
|
||||
// TESTCASE: Adding an entry from the getProtectedStorageEntry API of an existing item correctly updates the item
|
||||
@ -84,13 +84,13 @@ public class P2PDataStorageClientAPITest {
|
||||
ProtectedStoragePayload protectedStoragePayload = new ExpirableProtectedStoragePayloadStub(ownerKeys.getPublic());
|
||||
ProtectedStorageEntry protectedStorageEntry = this.testState.mockedStorage.getProtectedStorageEntry(protectedStoragePayload, ownerKeys);
|
||||
|
||||
Assert.assertTrue(this.testState.mockedStorage.addProtectedStorageEntry(protectedStorageEntry, TestState.getTestNodeAddress(), null, true));
|
||||
Assert.assertTrue(this.testState.mockedStorage.addProtectedStorageEntry(protectedStorageEntry, TestState.getTestNodeAddress(), null));
|
||||
|
||||
SavedTestState beforeState = this.testState.saveTestState(protectedStorageEntry);
|
||||
protectedStorageEntry = this.testState.mockedStorage.getProtectedStorageEntry(protectedStoragePayload, ownerKeys);
|
||||
this.testState.mockedStorage.addProtectedStorageEntry(protectedStorageEntry, TestState.getTestNodeAddress(), null, true);
|
||||
this.testState.mockedStorage.addProtectedStorageEntry(protectedStorageEntry, TestState.getTestNodeAddress(), null);
|
||||
|
||||
this.testState.verifyProtectedStorageAdd(beforeState, protectedStorageEntry, true, true);
|
||||
this.testState.verifyProtectedStorageAdd(beforeState, protectedStorageEntry, true, true, true, true);
|
||||
}
|
||||
|
||||
// TESTCASE: Adding an entry from the getProtectedStorageEntry API of an existing item (added from onMessage path) correctly updates the item
|
||||
@ -108,9 +108,9 @@ public class P2PDataStorageClientAPITest {
|
||||
|
||||
SavedTestState beforeState = this.testState.saveTestState(protectedStorageEntry);
|
||||
protectedStorageEntry = this.testState.mockedStorage.getProtectedStorageEntry(protectedStoragePayload, ownerKeys);
|
||||
Assert.assertTrue(this.testState.mockedStorage.addProtectedStorageEntry(protectedStorageEntry, TestState.getTestNodeAddress(), null, true));
|
||||
Assert.assertTrue(this.testState.mockedStorage.addProtectedStorageEntry(protectedStorageEntry, TestState.getTestNodeAddress(), null));
|
||||
|
||||
this.testState.verifyProtectedStorageAdd(beforeState, protectedStorageEntry, true, true);
|
||||
this.testState.verifyProtectedStorageAdd(beforeState, protectedStorageEntry, true, true, true, true);
|
||||
}
|
||||
|
||||
// TESTCASE: Updating an entry from the getRefreshTTLMessage API correctly errors if the item hasn't been seen
|
||||
@ -123,9 +123,9 @@ public class P2PDataStorageClientAPITest {
|
||||
RefreshOfferMessage refreshOfferMessage = this.testState.mockedStorage.getRefreshTTLMessage(protectedStoragePayload, ownerKeys);
|
||||
|
||||
SavedTestState beforeState = this.testState.saveTestState(refreshOfferMessage);
|
||||
Assert.assertFalse(this.testState.mockedStorage.refreshTTL(refreshOfferMessage, TestState.getTestNodeAddress(), true));
|
||||
Assert.assertFalse(this.testState.mockedStorage.refreshTTL(refreshOfferMessage, TestState.getTestNodeAddress()));
|
||||
|
||||
this.testState.verifyRefreshTTL(beforeState, refreshOfferMessage, false, true);
|
||||
this.testState.verifyRefreshTTL(beforeState, refreshOfferMessage, false);
|
||||
}
|
||||
|
||||
// TESTCASE: Updating an entry from the getRefreshTTLMessage API correctly "refreshes" the item
|
||||
@ -135,19 +135,19 @@ public class P2PDataStorageClientAPITest {
|
||||
|
||||
ProtectedStoragePayload protectedStoragePayload = new ExpirableProtectedStoragePayloadStub(ownerKeys.getPublic());
|
||||
ProtectedStorageEntry protectedStorageEntry = this.testState.mockedStorage.getProtectedStorageEntry(protectedStoragePayload, ownerKeys);
|
||||
this.testState.mockedStorage.addProtectedStorageEntry(protectedStorageEntry, TestState.getTestNodeAddress(), null, true);
|
||||
this.testState.mockedStorage.addProtectedStorageEntry(protectedStorageEntry, TestState.getTestNodeAddress(), null);
|
||||
|
||||
RefreshOfferMessage refreshOfferMessage = this.testState.mockedStorage.getRefreshTTLMessage(protectedStoragePayload, ownerKeys);
|
||||
this.testState.mockedStorage.refreshTTL(refreshOfferMessage, TestState.getTestNodeAddress(), true);
|
||||
this.testState.mockedStorage.refreshTTL(refreshOfferMessage, TestState.getTestNodeAddress());
|
||||
|
||||
refreshOfferMessage = this.testState.mockedStorage.getRefreshTTLMessage(protectedStoragePayload, ownerKeys);
|
||||
|
||||
this.testState.incrementClock();
|
||||
|
||||
SavedTestState beforeState = this.testState.saveTestState(refreshOfferMessage);
|
||||
Assert.assertTrue(this.testState.mockedStorage.refreshTTL(refreshOfferMessage, TestState.getTestNodeAddress(), true));
|
||||
Assert.assertTrue(this.testState.mockedStorage.refreshTTL(refreshOfferMessage, TestState.getTestNodeAddress()));
|
||||
|
||||
this.testState.verifyRefreshTTL(beforeState, refreshOfferMessage, true, true);
|
||||
this.testState.verifyRefreshTTL(beforeState, refreshOfferMessage, true);
|
||||
}
|
||||
|
||||
// TESTCASE: Updating an entry from the getRefreshTTLMessage API correctly "refreshes" the item when it was originally added from onMessage path
|
||||
@ -157,7 +157,7 @@ public class P2PDataStorageClientAPITest {
|
||||
|
||||
ProtectedStoragePayload protectedStoragePayload = new ExpirableProtectedStoragePayloadStub(ownerKeys.getPublic());
|
||||
ProtectedStorageEntry protectedStorageEntry = this.testState.mockedStorage.getProtectedStorageEntry(protectedStoragePayload, ownerKeys);
|
||||
this.testState.mockedStorage.addProtectedStorageEntry(protectedStorageEntry, TestState.getTestNodeAddress(), null, true);
|
||||
this.testState.mockedStorage.addProtectedStorageEntry(protectedStorageEntry, TestState.getTestNodeAddress(), null);
|
||||
|
||||
Connection mockedConnection = mock(Connection.class);
|
||||
when(mockedConnection.getPeersNodeAddressOptional()).thenReturn(Optional.of(TestState.getTestNodeAddress()));
|
||||
@ -169,9 +169,9 @@ public class P2PDataStorageClientAPITest {
|
||||
this.testState.incrementClock();
|
||||
|
||||
SavedTestState beforeState = this.testState.saveTestState(refreshOfferMessage);
|
||||
Assert.assertTrue(this.testState.mockedStorage.refreshTTL(refreshOfferMessage, TestState.getTestNodeAddress(), true));
|
||||
Assert.assertTrue(this.testState.mockedStorage.refreshTTL(refreshOfferMessage, TestState.getTestNodeAddress()));
|
||||
|
||||
this.testState.verifyRefreshTTL(beforeState, refreshOfferMessage, true, true);
|
||||
this.testState.verifyRefreshTTL(beforeState, refreshOfferMessage, true);
|
||||
}
|
||||
|
||||
// TESTCASE: Removing a non-existent mailbox entry from the getMailboxDataWithSignedSeqNr API
|
||||
@ -186,9 +186,9 @@ public class P2PDataStorageClientAPITest {
|
||||
this.testState.mockedStorage.getMailboxDataWithSignedSeqNr(mailboxStoragePayload, receiverKeys, receiverKeys.getPublic());
|
||||
|
||||
SavedTestState beforeState = this.testState.saveTestState(protectedMailboxStorageEntry);
|
||||
Assert.assertTrue(this.testState.mockedStorage.remove(protectedMailboxStorageEntry, TestState.getTestNodeAddress(), true));
|
||||
Assert.assertTrue(this.testState.mockedStorage.remove(protectedMailboxStorageEntry, TestState.getTestNodeAddress()));
|
||||
|
||||
this.testState.verifyProtectedStorageRemove(beforeState, protectedMailboxStorageEntry, false, false, false, true, true);
|
||||
this.testState.verifyProtectedStorageRemove(beforeState, protectedMailboxStorageEntry, false, false, true, true);
|
||||
}
|
||||
|
||||
// TESTCASE: Adding, then removing a mailbox message from the getMailboxDataWithSignedSeqNr API
|
||||
@ -202,15 +202,15 @@ public class P2PDataStorageClientAPITest {
|
||||
ProtectedMailboxStorageEntry protectedMailboxStorageEntry =
|
||||
this.testState.mockedStorage.getMailboxDataWithSignedSeqNr(mailboxStoragePayload, senderKeys, receiverKeys.getPublic());
|
||||
|
||||
Assert.assertTrue(this.testState.mockedStorage.addProtectedStorageEntry(protectedMailboxStorageEntry, TestState.getTestNodeAddress(), null, true));
|
||||
Assert.assertTrue(this.testState.mockedStorage.addProtectedStorageEntry(protectedMailboxStorageEntry, TestState.getTestNodeAddress(), null));
|
||||
|
||||
protectedMailboxStorageEntry =
|
||||
this.testState.mockedStorage.getMailboxDataWithSignedSeqNr(mailboxStoragePayload, receiverKeys, receiverKeys.getPublic());
|
||||
|
||||
SavedTestState beforeState = this.testState.saveTestState(protectedMailboxStorageEntry);
|
||||
Assert.assertTrue(this.testState.mockedStorage.remove(protectedMailboxStorageEntry, TestState.getTestNodeAddress(), true));
|
||||
Assert.assertTrue(this.testState.mockedStorage.remove(protectedMailboxStorageEntry, TestState.getTestNodeAddress()));
|
||||
|
||||
this.testState.verifyProtectedStorageRemove(beforeState, protectedMailboxStorageEntry, true, true, true, true,true);
|
||||
this.testState.verifyProtectedStorageRemove(beforeState, protectedMailboxStorageEntry, true, true, true, true);
|
||||
}
|
||||
|
||||
// TESTCASE: Removing a mailbox message that was added from the onMessage handler
|
||||
@ -235,8 +235,8 @@ public class P2PDataStorageClientAPITest {
|
||||
this.testState.mockedStorage.getMailboxDataWithSignedSeqNr(mailboxStoragePayload, receiverKeys, receiverKeys.getPublic());
|
||||
|
||||
SavedTestState beforeState = this.testState.saveTestState(protectedMailboxStorageEntry);
|
||||
Assert.assertTrue(this.testState.mockedStorage.remove(protectedMailboxStorageEntry, TestState.getTestNodeAddress(), true));
|
||||
Assert.assertTrue(this.testState.mockedStorage.remove(protectedMailboxStorageEntry, TestState.getTestNodeAddress()));
|
||||
|
||||
this.testState.verifyProtectedStorageRemove(beforeState, protectedMailboxStorageEntry, true, true, true, true,true);
|
||||
this.testState.verifyProtectedStorageRemove(beforeState, protectedMailboxStorageEntry, true, true, true, true);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,193 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.network.p2p.storage;
|
||||
|
||||
import bisq.network.p2p.TestUtils;
|
||||
import bisq.network.p2p.peers.getdata.messages.GetDataRequest;
|
||||
import bisq.network.p2p.peers.getdata.messages.GetDataResponse;
|
||||
import bisq.network.p2p.storage.mocks.PersistableExpirableProtectedStoragePayloadStub;
|
||||
import bisq.network.p2p.storage.mocks.ProtectedStoragePayloadStub;
|
||||
import bisq.network.p2p.storage.payload.ProtectedStorageEntry;
|
||||
import bisq.network.p2p.storage.payload.ProtectedStoragePayload;
|
||||
|
||||
import bisq.common.app.Capabilities;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PublicKey;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class P2PDataStorageGetDataIntegrationTest {
|
||||
|
||||
/**
|
||||
* Generates a unique ProtectedStorageEntry that is valid for add and remove.
|
||||
*/
|
||||
private ProtectedStorageEntry getProtectedStorageEntry() throws NoSuchAlgorithmException {
|
||||
KeyPair ownerKeys = TestUtils.generateKeyPair();
|
||||
|
||||
return getProtectedStorageEntry(
|
||||
ownerKeys.getPublic(), new ProtectedStoragePayloadStub(ownerKeys.getPublic()), 1);
|
||||
}
|
||||
|
||||
private ProtectedStorageEntry getProtectedStorageEntry(
|
||||
PublicKey ownerPubKey,
|
||||
ProtectedStoragePayload protectedStoragePayload,
|
||||
int sequenceNumber) {
|
||||
ProtectedStorageEntry stub = mock(ProtectedStorageEntry.class);
|
||||
when(stub.getOwnerPubKey()).thenReturn(ownerPubKey);
|
||||
when(stub.isValidForAddOperation()).thenReturn(true);
|
||||
when(stub.isValidForRemoveOperation()).thenReturn(true);
|
||||
when(stub.matchesRelevantPubKey(any(ProtectedStorageEntry.class))).thenReturn(true);
|
||||
when(stub.getSequenceNumber()).thenReturn(sequenceNumber);
|
||||
when(stub.getProtectedStoragePayload()).thenReturn(protectedStoragePayload);
|
||||
|
||||
return stub;
|
||||
}
|
||||
|
||||
// TESTCASE: Basic synchronization of a ProtectedStorageEntry works between a seed node and client node
|
||||
@Test
|
||||
public void basicSynchronizationWorks() throws NoSuchAlgorithmException {
|
||||
TestState seedNodeTestState = new TestState();
|
||||
P2PDataStorage seedNode = seedNodeTestState.mockedStorage;
|
||||
|
||||
TestState clientNodeTestState = new TestState();
|
||||
P2PDataStorage clientNode = clientNodeTestState.mockedStorage;
|
||||
|
||||
ProtectedStorageEntry onSeedNode = getProtectedStorageEntry();
|
||||
seedNode.addProtectedStorageEntry(onSeedNode, null, null);
|
||||
|
||||
GetDataRequest getDataRequest = clientNode.buildPreliminaryGetDataRequest(1);
|
||||
|
||||
GetDataResponse getDataResponse = seedNode.buildGetDataResponse(
|
||||
getDataRequest, 1, new AtomicBoolean(), new AtomicBoolean(), new Capabilities());
|
||||
|
||||
TestState.SavedTestState beforeState = clientNodeTestState.saveTestState(onSeedNode);
|
||||
clientNode.processGetDataResponse(getDataResponse, null);
|
||||
|
||||
clientNodeTestState.verifyProtectedStorageAdd(
|
||||
beforeState, onSeedNode, true, true, false, true);
|
||||
}
|
||||
|
||||
// TESTCASE: Synchronization after peer restart works for in-memory ProtectedStorageEntrys
|
||||
@Test
|
||||
public void basicSynchronizationWorksAfterRestartTransient() throws NoSuchAlgorithmException {
|
||||
ProtectedStorageEntry transientEntry = getProtectedStorageEntry();
|
||||
|
||||
TestState seedNodeTestState = new TestState();
|
||||
P2PDataStorage seedNode = seedNodeTestState.mockedStorage;
|
||||
|
||||
TestState clientNodeTestState = new TestState();
|
||||
P2PDataStorage clientNode = clientNodeTestState.mockedStorage;
|
||||
|
||||
seedNode.addProtectedStorageEntry(transientEntry, null, null);
|
||||
|
||||
clientNode.addProtectedStorageEntry(transientEntry, null, null);
|
||||
|
||||
clientNodeTestState.simulateRestart();
|
||||
clientNode = clientNodeTestState.mockedStorage;
|
||||
|
||||
GetDataRequest getDataRequest = clientNode.buildPreliminaryGetDataRequest(1);
|
||||
|
||||
GetDataResponse getDataResponse = seedNode.buildGetDataResponse(
|
||||
getDataRequest, 1, new AtomicBoolean(), new AtomicBoolean(), new Capabilities());
|
||||
|
||||
TestState.SavedTestState beforeState = clientNodeTestState.saveTestState(transientEntry);
|
||||
clientNode.processGetDataResponse(getDataResponse, null);
|
||||
|
||||
clientNodeTestState.verifyProtectedStorageAdd(
|
||||
beforeState, transientEntry, true, true, false, true);
|
||||
}
|
||||
|
||||
// TESTCASE: Synchronization after peer restart works for in-memory ProtectedStorageEntrys
|
||||
@Test
|
||||
public void basicSynchronizationWorksAfterRestartPersistent() throws NoSuchAlgorithmException {
|
||||
KeyPair ownerKeys = TestUtils.generateKeyPair();
|
||||
ProtectedStoragePayload persistentPayload =
|
||||
new PersistableExpirableProtectedStoragePayloadStub(ownerKeys.getPublic());
|
||||
ProtectedStorageEntry persistentEntry = getProtectedStorageEntry(ownerKeys.getPublic(), persistentPayload, 1);
|
||||
|
||||
TestState seedNodeTestState = new TestState();
|
||||
P2PDataStorage seedNode = seedNodeTestState.mockedStorage;
|
||||
|
||||
TestState clientNodeTestState = new TestState();
|
||||
P2PDataStorage clientNode = clientNodeTestState.mockedStorage;
|
||||
|
||||
seedNode.addProtectedStorageEntry(persistentEntry, null, null);
|
||||
|
||||
clientNode.addProtectedStorageEntry(persistentEntry, null, null);
|
||||
|
||||
clientNodeTestState.simulateRestart();
|
||||
clientNode = clientNodeTestState.mockedStorage;
|
||||
|
||||
GetDataRequest getDataRequest = clientNode.buildPreliminaryGetDataRequest(1);
|
||||
|
||||
GetDataResponse getDataResponse = seedNode.buildGetDataResponse(
|
||||
getDataRequest, 1, new AtomicBoolean(), new AtomicBoolean(), new Capabilities());
|
||||
|
||||
TestState.SavedTestState beforeState = clientNodeTestState.saveTestState(persistentEntry);
|
||||
clientNode.processGetDataResponse(getDataResponse, null);
|
||||
|
||||
clientNodeTestState.verifyProtectedStorageAdd(
|
||||
beforeState, persistentEntry, false, false, false, false);
|
||||
Assert.assertTrue(clientNodeTestState.mockedStorage.getMap().containsValue(persistentEntry));
|
||||
}
|
||||
|
||||
// TESTCASE: Removes seen only by the seednode should be replayed on the client node
|
||||
// during startup
|
||||
// XXXBUGXXX: #3610 Lost removes are never replayed.
|
||||
@Test
|
||||
public void lostRemoveNeverUpdated() throws NoSuchAlgorithmException {
|
||||
TestState seedNodeTestState = new TestState();
|
||||
P2PDataStorage seedNode = seedNodeTestState.mockedStorage;
|
||||
|
||||
TestState clientNodeTestState = new TestState();
|
||||
P2PDataStorage clientNode = clientNodeTestState.mockedStorage;
|
||||
|
||||
// Both nodes see the add
|
||||
KeyPair ownerKeys = TestUtils.generateKeyPair();
|
||||
ProtectedStoragePayload protectedStoragePayload = new ProtectedStoragePayloadStub(ownerKeys.getPublic());
|
||||
ProtectedStorageEntry onSeedNodeAndClientNode = getProtectedStorageEntry(
|
||||
ownerKeys.getPublic(), protectedStoragePayload, 1);
|
||||
seedNode.addProtectedStorageEntry(onSeedNodeAndClientNode, null, null);
|
||||
clientNode.addProtectedStorageEntry(onSeedNodeAndClientNode, null, null);
|
||||
|
||||
// Seed node sees the remove, but client node does not
|
||||
seedNode.remove(getProtectedStorageEntry(
|
||||
ownerKeys.getPublic(), protectedStoragePayload, 2), null);
|
||||
|
||||
GetDataRequest getDataRequest = clientNode.buildPreliminaryGetDataRequest(1);
|
||||
|
||||
GetDataResponse getDataResponse = seedNode.buildGetDataResponse(
|
||||
getDataRequest, 1, new AtomicBoolean(), new AtomicBoolean(), new Capabilities());
|
||||
|
||||
TestState.SavedTestState beforeState = clientNodeTestState.saveTestState(onSeedNodeAndClientNode);
|
||||
clientNode.processGetDataResponse(getDataResponse, null);
|
||||
|
||||
// Should succeed
|
||||
clientNodeTestState.verifyProtectedStorageRemove(
|
||||
beforeState, onSeedNodeAndClientNode, false, false, false, false);
|
||||
}
|
||||
}
|
@ -69,7 +69,7 @@ public class P2PDataStorageOnMessageHandlerTest {
|
||||
this.testState.mockedStorage.onMessage(envelope, mockedConnection);
|
||||
|
||||
verify(this.testState.appendOnlyDataStoreListener, never()).onAdded(any(PersistableNetworkPayload.class));
|
||||
verify(this.testState.mockBroadcaster, never()).broadcast(any(BroadcastMessage.class), any(NodeAddress.class), eq(null), anyBoolean());
|
||||
verify(this.testState.mockBroadcaster, never()).broadcast(any(BroadcastMessage.class), any(NodeAddress.class), eq(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -82,7 +82,7 @@ public class P2PDataStorageOnMessageHandlerTest {
|
||||
this.testState.mockedStorage.onMessage(envelope, mockedConnection);
|
||||
|
||||
verify(this.testState.appendOnlyDataStoreListener, never()).onAdded(any(PersistableNetworkPayload.class));
|
||||
verify(this.testState.mockBroadcaster, never()).broadcast(any(BroadcastMessage.class), any(NodeAddress.class), eq(null), anyBoolean());
|
||||
verify(this.testState.mockBroadcaster, never()).broadcast(any(BroadcastMessage.class), any(NodeAddress.class), eq(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -96,6 +96,6 @@ public class P2PDataStorageOnMessageHandlerTest {
|
||||
this.testState.mockedStorage.onMessage(envelope, mockedConnection);
|
||||
|
||||
verify(this.testState.appendOnlyDataStoreListener, never()).onAdded(any(PersistableNetworkPayload.class));
|
||||
verify(this.testState.mockBroadcaster, never()).broadcast(any(BroadcastMessage.class), any(NodeAddress.class), eq(null), anyBoolean());
|
||||
verify(this.testState.mockBroadcaster, never()).broadcast(any(BroadcastMessage.class), any(NodeAddress.class), eq(null));
|
||||
}
|
||||
}
|
||||
|
@ -49,11 +49,10 @@ import static bisq.network.p2p.storage.TestState.*;
|
||||
* Each subclass (Payload type) can optionally add additional tests that verify functionality only relevant
|
||||
* to that payload.
|
||||
*
|
||||
* Each test case is run through 4 entry points to verify the correct behavior:
|
||||
* Each test case is run through 3 entry points to verify the correct behavior:
|
||||
*
|
||||
* 1. RequestData path [addPersistableNetworkPayloadFromInitialRequest]
|
||||
* 2 & 3 Client API [addPersistableNetworkPayload(reBroadcast=(true && false))]
|
||||
* 4. onMessage() [onMessage(AddPersistableNetworkPayloadMessage)]
|
||||
* 1 & 2 Client API [addPersistableNetworkPayload(reBroadcast=(true && false))]
|
||||
* 3. onMessage() [onMessage(AddPersistableNetworkPayloadMessage)]
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class P2PDataStoragePersistableNetworkPayloadTest {
|
||||
@ -66,14 +65,8 @@ public class P2PDataStoragePersistableNetworkPayloadTest {
|
||||
public TestCase testCase;
|
||||
|
||||
@Parameterized.Parameter(1)
|
||||
public boolean allowBroadcast;
|
||||
|
||||
@Parameterized.Parameter(2)
|
||||
public boolean reBroadcast;
|
||||
|
||||
@Parameterized.Parameter(3)
|
||||
public boolean checkDate;
|
||||
|
||||
PersistableNetworkPayload persistableNetworkPayload;
|
||||
|
||||
abstract PersistableNetworkPayload createInstance();
|
||||
@ -81,25 +74,18 @@ public class P2PDataStoragePersistableNetworkPayloadTest {
|
||||
enum TestCase {
|
||||
PUBLIC_API,
|
||||
ON_MESSAGE,
|
||||
INIT,
|
||||
}
|
||||
|
||||
boolean expectBroadcastOnStateChange() {
|
||||
return this.testCase != TestCase.INIT;
|
||||
}
|
||||
|
||||
boolean expectedIsDataOwner() {
|
||||
return this.testCase == TestCase.PUBLIC_API;
|
||||
}
|
||||
|
||||
void doAddAndVerify(PersistableNetworkPayload persistableNetworkPayload, boolean expectedReturnValue, boolean expectedStateChange) {
|
||||
void doAddAndVerify(PersistableNetworkPayload persistableNetworkPayload,
|
||||
boolean expectedReturnValue,
|
||||
boolean expectedHashMapAndDataStoreUpdated,
|
||||
boolean expectedListenersSignaled,
|
||||
boolean expectedBroadcast) {
|
||||
SavedTestState beforeState = this.testState.saveTestState(persistableNetworkPayload);
|
||||
|
||||
if (this.testCase == TestCase.INIT) {
|
||||
Assert.assertEquals(expectedReturnValue, this.testState.mockedStorage.addPersistableNetworkPayloadFromInitialRequest(persistableNetworkPayload));
|
||||
} else if (this.testCase == TestCase.PUBLIC_API) {
|
||||
if (this.testCase == TestCase.PUBLIC_API) {
|
||||
Assert.assertEquals(expectedReturnValue,
|
||||
this.testState.mockedStorage.addPersistableNetworkPayload(persistableNetworkPayload, TestState.getTestNodeAddress(), true, this.allowBroadcast, this.reBroadcast, this.checkDate));
|
||||
this.testState.mockedStorage.addPersistableNetworkPayload(persistableNetworkPayload, TestState.getTestNodeAddress(), this.reBroadcast));
|
||||
} else { // onMessage
|
||||
Connection mockedConnection = mock(Connection.class);
|
||||
when(mockedConnection.getPeersNodeAddressOptional()).thenReturn(Optional.of(TestState.getTestNodeAddress()));
|
||||
@ -107,7 +93,7 @@ public class P2PDataStoragePersistableNetworkPayloadTest {
|
||||
testState.mockedStorage.onMessage(new AddPersistableNetworkPayloadMessage(persistableNetworkPayload), mockedConnection);
|
||||
}
|
||||
|
||||
this.testState.verifyPersistableAdd(beforeState, persistableNetworkPayload, expectedStateChange, this.expectBroadcastOnStateChange(), this.expectedIsDataOwner());
|
||||
this.testState.verifyPersistableAdd(beforeState, persistableNetworkPayload, expectedHashMapAndDataStoreUpdated, expectedListenersSignaled, expectedBroadcast);
|
||||
}
|
||||
|
||||
@Before
|
||||
@ -121,18 +107,15 @@ public class P2PDataStoragePersistableNetworkPayloadTest {
|
||||
public static Collection<Object[]> data() {
|
||||
List<Object[]> data = new ArrayList<>();
|
||||
|
||||
// Init doesn't use other parameters
|
||||
data.add(new Object[] { TestCase.INIT, false, false, false });
|
||||
|
||||
// onMessage doesn't use other parameters
|
||||
data.add(new Object[] { TestCase.ON_MESSAGE, false, false, false });
|
||||
data.add(new Object[] { TestCase.ON_MESSAGE, false });
|
||||
|
||||
// Client API uses two permutations
|
||||
// Normal path
|
||||
data.add(new Object[] { TestCase.PUBLIC_API, true, true, false });
|
||||
data.add(new Object[] { TestCase.PUBLIC_API, true });
|
||||
|
||||
// Refresh path
|
||||
data.add(new Object[] { TestCase.PUBLIC_API, true, false, false });
|
||||
data.add(new Object[] { TestCase.PUBLIC_API, false });
|
||||
|
||||
return data;
|
||||
}
|
||||
@ -140,17 +123,15 @@ public class P2PDataStoragePersistableNetworkPayloadTest {
|
||||
@Test
|
||||
public void addPersistableNetworkPayload() {
|
||||
// First add should succeed regardless of parameters
|
||||
doAddAndVerify(this.persistableNetworkPayload, true, true);
|
||||
doAddAndVerify(this.persistableNetworkPayload, true, true, true, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addPersistableNetworkPayloadDuplicate() {
|
||||
doAddAndVerify(this.persistableNetworkPayload, true, true);
|
||||
doAddAndVerify(this.persistableNetworkPayload, true, true, true, true);
|
||||
|
||||
// Second call only succeeds if reBroadcast was set or we are adding through the init
|
||||
// path which just overwrites
|
||||
boolean expectedReturnValue = this.reBroadcast || this.testCase == TestCase.INIT;
|
||||
doAddAndVerify(this.persistableNetworkPayload, expectedReturnValue, false);
|
||||
// We return true and broadcast if reBroadcast is set
|
||||
doAddAndVerify(this.persistableNetworkPayload, this.reBroadcast, false, false, this.reBroadcast);
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,7 +148,7 @@ public class P2PDataStoragePersistableNetworkPayloadTest {
|
||||
public void invalidHash() {
|
||||
PersistableNetworkPayload persistableNetworkPayload = new PersistableNetworkPayloadStub(false);
|
||||
|
||||
doAddAndVerify(persistableNetworkPayload, false, false);
|
||||
doAddAndVerify(persistableNetworkPayload, false, false, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -190,7 +171,7 @@ public class P2PDataStoragePersistableNetworkPayloadTest {
|
||||
// The onMessage path checks for tolerance
|
||||
boolean expectedReturn = this.testCase != TestCase.ON_MESSAGE;
|
||||
|
||||
doAddAndVerify(persistableNetworkPayload, expectedReturn, expectedReturn);
|
||||
doAddAndVerify(persistableNetworkPayload, expectedReturn, expectedReturn, expectedReturn, expectedReturn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,263 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.network.p2p.storage;
|
||||
|
||||
import bisq.network.p2p.NodeAddress;
|
||||
import bisq.network.p2p.TestUtils;
|
||||
import bisq.network.p2p.peers.getdata.messages.GetDataResponse;
|
||||
import bisq.network.p2p.storage.mocks.PersistableNetworkPayloadStub;
|
||||
import bisq.network.p2p.storage.mocks.ProtectedStoragePayloadStub;
|
||||
import bisq.network.p2p.storage.payload.ProcessOncePersistableNetworkPayload;
|
||||
import bisq.network.p2p.storage.payload.PersistableNetworkPayload;
|
||||
import bisq.network.p2p.storage.payload.ProtectedStorageEntry;
|
||||
import bisq.network.p2p.storage.payload.ProtectedStoragePayload;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
public class P2PDataStorageProcessGetDataResponse {
|
||||
private TestState testState;
|
||||
|
||||
private NodeAddress peerNodeAddress;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
this.testState = new TestState();
|
||||
|
||||
this.peerNodeAddress = new NodeAddress("peer", 8080);
|
||||
}
|
||||
|
||||
static private GetDataResponse buildGetDataResponse(PersistableNetworkPayload persistableNetworkPayload) {
|
||||
return buildGetDataResponse(Collections.emptyList(), Collections.singletonList(persistableNetworkPayload));
|
||||
}
|
||||
|
||||
static private GetDataResponse buildGetDataResponse(ProtectedStorageEntry protectedStorageEntry) {
|
||||
return buildGetDataResponse(Collections.singletonList(protectedStorageEntry), Collections.emptyList());
|
||||
}
|
||||
|
||||
static private GetDataResponse buildGetDataResponse(
|
||||
List<ProtectedStorageEntry> protectedStorageEntries,
|
||||
List<PersistableNetworkPayload> persistableNetworkPayloads) {
|
||||
return new GetDataResponse(
|
||||
new HashSet<>(protectedStorageEntries),
|
||||
new HashSet<>(persistableNetworkPayloads),
|
||||
1,
|
||||
false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a unique ProtectedStorageEntry that is valid for add. This is used to initialize P2PDataStorage state
|
||||
* so the tests can validate the correct behavior. Adds of identical payloads with different sequence numbers
|
||||
* is not supported.
|
||||
*/
|
||||
private ProtectedStorageEntry getProtectedStorageEntryForAdd() throws NoSuchAlgorithmException {
|
||||
KeyPair ownerKeys = TestUtils.generateKeyPair();
|
||||
|
||||
ProtectedStoragePayload protectedStoragePayload = new ProtectedStoragePayloadStub(ownerKeys.getPublic());
|
||||
|
||||
ProtectedStorageEntry stub = mock(ProtectedStorageEntry.class);
|
||||
when(stub.getOwnerPubKey()).thenReturn(ownerKeys.getPublic());
|
||||
when(stub.isValidForAddOperation()).thenReturn(true);
|
||||
when(stub.matchesRelevantPubKey(any(ProtectedStorageEntry.class))).thenReturn(true);
|
||||
when(stub.getSequenceNumber()).thenReturn(1);
|
||||
when(stub.getProtectedStoragePayload()).thenReturn(protectedStoragePayload);
|
||||
|
||||
return stub;
|
||||
}
|
||||
|
||||
static class LazyPersistableNetworkPayloadStub extends PersistableNetworkPayloadStub
|
||||
implements ProcessOncePersistableNetworkPayload {
|
||||
|
||||
LazyPersistableNetworkPayloadStub(byte[] hash) {
|
||||
super(hash);
|
||||
}
|
||||
|
||||
LazyPersistableNetworkPayloadStub(boolean validHashSize) {
|
||||
super(validHashSize);
|
||||
}
|
||||
}
|
||||
|
||||
// TESTCASE: GetDataResponse w/ missing PNP is added with no broadcast or listener signal
|
||||
// XXXBUGXXX: We signal listeners w/ non ProcessOncePersistableNetworkPayloads
|
||||
@Test
|
||||
public void processGetDataResponse_newPNPUpdatesState() {
|
||||
PersistableNetworkPayload persistableNetworkPayload = new PersistableNetworkPayloadStub(new byte[] { 1 });
|
||||
|
||||
GetDataResponse getDataResponse = buildGetDataResponse(persistableNetworkPayload);
|
||||
|
||||
TestState.SavedTestState beforeState = this.testState.saveTestState(persistableNetworkPayload);
|
||||
this.testState.mockedStorage.processGetDataResponse(getDataResponse, this.peerNodeAddress);
|
||||
this.testState.verifyPersistableAdd(
|
||||
beforeState, persistableNetworkPayload, true, true, false);
|
||||
}
|
||||
|
||||
// TESTCASE: GetDataResponse w/ invalid PNP does nothing (LazyProcessed)
|
||||
@Test
|
||||
public void processGetDataResponse_newInvalidPNPDoesNothing() {
|
||||
PersistableNetworkPayload persistableNetworkPayload = new LazyPersistableNetworkPayloadStub(false);
|
||||
|
||||
GetDataResponse getDataResponse = buildGetDataResponse(persistableNetworkPayload);
|
||||
|
||||
TestState.SavedTestState beforeState = this.testState.saveTestState(persistableNetworkPayload);
|
||||
this.testState.mockedStorage.processGetDataResponse(getDataResponse, this.peerNodeAddress);
|
||||
this.testState.verifyPersistableAdd(
|
||||
beforeState, persistableNetworkPayload, false, false, false);
|
||||
}
|
||||
|
||||
// TESTCASE: GetDataResponse w/ existing PNP changes no state
|
||||
@Test
|
||||
public void processGetDataResponse_duplicatePNPDoesNothing() {
|
||||
PersistableNetworkPayload persistableNetworkPayload = new PersistableNetworkPayloadStub(new byte[] { 1 });
|
||||
this.testState.mockedStorage.addPersistableNetworkPayload(persistableNetworkPayload,
|
||||
this.peerNodeAddress, false);
|
||||
|
||||
GetDataResponse getDataResponse = buildGetDataResponse(persistableNetworkPayload);
|
||||
|
||||
TestState.SavedTestState beforeState = this.testState.saveTestState(persistableNetworkPayload);
|
||||
this.testState.mockedStorage.processGetDataResponse(getDataResponse, this.peerNodeAddress);
|
||||
this.testState.verifyPersistableAdd(
|
||||
beforeState, persistableNetworkPayload, false, false, false);
|
||||
}
|
||||
|
||||
// TESTCASE: GetDataResponse w/ missing PNP is added with no broadcast or listener signal (ProcessOncePersistableNetworkPayload)
|
||||
@Test
|
||||
public void processGetDataResponse_newPNPUpdatesState_LazyProcessed() {
|
||||
PersistableNetworkPayload persistableNetworkPayload = new LazyPersistableNetworkPayloadStub(new byte[] { 1 });
|
||||
|
||||
GetDataResponse getDataResponse = buildGetDataResponse(persistableNetworkPayload);
|
||||
|
||||
TestState.SavedTestState beforeState = this.testState.saveTestState(persistableNetworkPayload);
|
||||
this.testState.mockedStorage.processGetDataResponse(getDataResponse, this.peerNodeAddress);
|
||||
this.testState.verifyPersistableAdd(
|
||||
beforeState, persistableNetworkPayload, true, false, false);
|
||||
}
|
||||
|
||||
// TESTCASE: GetDataResponse w/ existing PNP changes no state (ProcessOncePersistableNetworkPayload)
|
||||
@Test
|
||||
public void processGetDataResponse_duplicatePNPDoesNothing_LazyProcessed() {
|
||||
PersistableNetworkPayload persistableNetworkPayload = new LazyPersistableNetworkPayloadStub(new byte[] { 1 });
|
||||
this.testState.mockedStorage.addPersistableNetworkPayload(persistableNetworkPayload,
|
||||
this.peerNodeAddress, false);
|
||||
|
||||
GetDataResponse getDataResponse = buildGetDataResponse(persistableNetworkPayload);
|
||||
|
||||
TestState.SavedTestState beforeState = this.testState.saveTestState(persistableNetworkPayload);
|
||||
this.testState.mockedStorage.processGetDataResponse(getDataResponse, this.peerNodeAddress);
|
||||
this.testState.verifyPersistableAdd(
|
||||
beforeState, persistableNetworkPayload, false, false, false);
|
||||
}
|
||||
|
||||
// TESTCASE: Second call to processGetDataResponse adds PNP for non-ProcessOncePersistableNetworkPayloads
|
||||
@Test
|
||||
public void processGetDataResponse_secondProcessNewPNPUpdatesState() {
|
||||
PersistableNetworkPayload addFromFirstProcess = new PersistableNetworkPayloadStub(new byte[] { 1 });
|
||||
GetDataResponse getDataResponse = buildGetDataResponse(addFromFirstProcess);
|
||||
|
||||
TestState.SavedTestState beforeState = this.testState.saveTestState(addFromFirstProcess);
|
||||
this.testState.mockedStorage.processGetDataResponse(getDataResponse, this.peerNodeAddress);
|
||||
this.testState.verifyPersistableAdd(
|
||||
beforeState, addFromFirstProcess, true, true, false);
|
||||
|
||||
PersistableNetworkPayload addFromSecondProcess = new PersistableNetworkPayloadStub(new byte[] { 2 });
|
||||
getDataResponse = buildGetDataResponse(addFromSecondProcess);
|
||||
beforeState = this.testState.saveTestState(addFromSecondProcess);
|
||||
this.testState.mockedStorage.processGetDataResponse(getDataResponse, this.peerNodeAddress);
|
||||
this.testState.verifyPersistableAdd(
|
||||
beforeState, addFromSecondProcess, true, true, false);
|
||||
}
|
||||
|
||||
// TESTCASE: Second call to processGetDataResponse does not add any PNP (LazyProcessed)
|
||||
@Test
|
||||
public void processGetDataResponse_secondProcessNoPNPUpdates_LazyProcessed() {
|
||||
PersistableNetworkPayload addFromFirstProcess = new LazyPersistableNetworkPayloadStub(new byte[] { 1 });
|
||||
GetDataResponse getDataResponse = buildGetDataResponse(addFromFirstProcess);
|
||||
|
||||
TestState.SavedTestState beforeState = this.testState.saveTestState(addFromFirstProcess);
|
||||
this.testState.mockedStorage.processGetDataResponse(getDataResponse, this.peerNodeAddress);
|
||||
this.testState.verifyPersistableAdd(
|
||||
beforeState, addFromFirstProcess, true, false, false);
|
||||
|
||||
PersistableNetworkPayload addFromSecondProcess = new LazyPersistableNetworkPayloadStub(new byte[] { 2 });
|
||||
getDataResponse = buildGetDataResponse(addFromSecondProcess);
|
||||
beforeState = this.testState.saveTestState(addFromSecondProcess);
|
||||
this.testState.mockedStorage.processGetDataResponse(getDataResponse, this.peerNodeAddress);
|
||||
this.testState.verifyPersistableAdd(
|
||||
beforeState, addFromSecondProcess, false, false, false);
|
||||
}
|
||||
|
||||
// TESTCASE: GetDataResponse w/ missing PSE is added with no broadcast or listener signal
|
||||
// XXXBUGXXX: We signal listeners for all ProtectedStorageEntrys
|
||||
@Test
|
||||
public void processGetDataResponse_newPSEUpdatesState() throws NoSuchAlgorithmException {
|
||||
ProtectedStorageEntry protectedStorageEntry = getProtectedStorageEntryForAdd();
|
||||
GetDataResponse getDataResponse = buildGetDataResponse(protectedStorageEntry);
|
||||
|
||||
TestState.SavedTestState beforeState = this.testState.saveTestState(protectedStorageEntry);
|
||||
this.testState.mockedStorage.processGetDataResponse(getDataResponse, this.peerNodeAddress);
|
||||
this.testState.verifyProtectedStorageAdd(
|
||||
beforeState, protectedStorageEntry, true, true, false, true);
|
||||
}
|
||||
|
||||
// TESTCASE: GetDataResponse w/ existing PSE changes no state
|
||||
@Test
|
||||
public void processGetDataResponse_duplicatePSEDoesNothing() throws NoSuchAlgorithmException {
|
||||
ProtectedStorageEntry protectedStorageEntry = getProtectedStorageEntryForAdd();
|
||||
this.testState.mockedStorage.addProtectedStorageEntry(protectedStorageEntry, this.peerNodeAddress, null);
|
||||
|
||||
GetDataResponse getDataResponse = buildGetDataResponse(protectedStorageEntry);
|
||||
|
||||
this.testState.mockedStorage.processGetDataResponse(getDataResponse, this.peerNodeAddress);
|
||||
TestState.SavedTestState beforeState = this.testState.saveTestState(protectedStorageEntry);
|
||||
this.testState.verifyProtectedStorageAdd(
|
||||
beforeState, protectedStorageEntry, false, false, false, false);
|
||||
}
|
||||
|
||||
// TESTCASE: GetDataResponse w/ missing PSE is added with no broadcast or listener signal
|
||||
// XXXBUGXXX: We signal listeners for all ProtectedStorageEntrys
|
||||
@Test
|
||||
public void processGetDataResponse_secondCallNewPSEUpdatesState() throws NoSuchAlgorithmException {
|
||||
ProtectedStorageEntry protectedStorageEntry = getProtectedStorageEntryForAdd();
|
||||
GetDataResponse getDataResponse = buildGetDataResponse(protectedStorageEntry);
|
||||
|
||||
TestState.SavedTestState beforeState = this.testState.saveTestState(protectedStorageEntry);
|
||||
this.testState.mockedStorage.processGetDataResponse(getDataResponse, this.peerNodeAddress);
|
||||
this.testState.verifyProtectedStorageAdd(
|
||||
beforeState, protectedStorageEntry, true, true, false, true);
|
||||
|
||||
protectedStorageEntry = getProtectedStorageEntryForAdd();
|
||||
getDataResponse = buildGetDataResponse(protectedStorageEntry);
|
||||
beforeState = this.testState.saveTestState(protectedStorageEntry);
|
||||
this.testState.mockedStorage.processGetDataResponse(getDataResponse, this.peerNodeAddress);
|
||||
this.testState.verifyProtectedStorageAdd(
|
||||
beforeState, protectedStorageEntry, true, true, false, true);
|
||||
}
|
||||
}
|
@ -83,12 +83,6 @@ public class P2PDataStorageProtectedStorageEntryTest {
|
||||
@Parameterized.Parameter(0)
|
||||
public boolean useMessageHandler;
|
||||
|
||||
boolean expectIsDataOwner() {
|
||||
// The onMessage handler variant should always broadcast with isDataOwner == false
|
||||
// The Client API should always broadcast with isDataOwner == true
|
||||
return !useMessageHandler;
|
||||
}
|
||||
|
||||
@Parameterized.Parameters(name = "{index}: Test with useMessageHandler={0}")
|
||||
public static Collection<Object[]> data() {
|
||||
List<Object[]> data = new ArrayList<>();
|
||||
@ -119,8 +113,7 @@ public class P2PDataStorageProtectedStorageEntryTest {
|
||||
|
||||
return true;
|
||||
} else {
|
||||
// XXX: All callers just pass in true, a future patch can remove the argument.
|
||||
return testState.mockedStorage.remove(entry, TestState.getTestNodeAddress(), true);
|
||||
return testState.mockedStorage.remove(entry, TestState.getTestNodeAddress());
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,10 +126,8 @@ public class P2PDataStorageProtectedStorageEntryTest {
|
||||
|
||||
return true;
|
||||
} else {
|
||||
// XXX: All external callers just pass in true for isDataOwner and allowBroadcast a future patch can
|
||||
// remove the argument.
|
||||
return this.testState.mockedStorage.addProtectedStorageEntry(protectedStorageEntry,
|
||||
TestState.getTestNodeAddress(), null, true);
|
||||
TestState.getTestNodeAddress(), null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,8 +140,7 @@ public class P2PDataStorageProtectedStorageEntryTest {
|
||||
|
||||
return true;
|
||||
} else {
|
||||
// XXX: All external callers just pass in true for isDataOwner a future patch can remove the argument.
|
||||
return this.testState.mockedStorage.refreshTTL(refreshOfferMessage, TestState.getTestNodeAddress(), true);
|
||||
return this.testState.mockedStorage.refreshTTL(refreshOfferMessage, TestState.getTestNodeAddress());
|
||||
}
|
||||
}
|
||||
|
||||
@ -197,7 +187,13 @@ public class P2PDataStorageProtectedStorageEntryTest {
|
||||
if (!this.useMessageHandler)
|
||||
Assert.assertEquals(expectedReturnValue, addResult);
|
||||
|
||||
this.testState.verifyProtectedStorageAdd(beforeState, protectedStorageEntry, expectedStateChange, this.expectIsDataOwner());
|
||||
if (expectedStateChange) {
|
||||
this.testState.verifyProtectedStorageAdd(
|
||||
beforeState, protectedStorageEntry, true, true, true, true);
|
||||
} else{
|
||||
this.testState.verifyProtectedStorageAdd(
|
||||
beforeState, protectedStorageEntry, false, false, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
void doProtectedStorageRemoveAndVerify(ProtectedStorageEntry entry,
|
||||
@ -214,7 +210,7 @@ public class P2PDataStorageProtectedStorageEntryTest {
|
||||
if (!this.useMessageHandler)
|
||||
Assert.assertEquals(expectedReturnValue, addResult);
|
||||
|
||||
this.testState.verifyProtectedStorageRemove(beforeState, entry, expectedHashMapAndDataStoreUpdated, expectedListenersSignaled, expectedBroadcast, expectedSeqNrWrite, this.expectIsDataOwner());
|
||||
this.testState.verifyProtectedStorageRemove(beforeState, entry, expectedHashMapAndDataStoreUpdated, expectedListenersSignaled, expectedBroadcast, expectedSeqNrWrite);
|
||||
}
|
||||
|
||||
/// Valid Add Tests (isValidForAdd() and matchesRelevantPubKey() return true)
|
||||
@ -490,7 +486,7 @@ public class P2PDataStorageProtectedStorageEntryTest {
|
||||
if (!this.useMessageHandler)
|
||||
Assert.assertEquals(expectedReturnValue, returnValue);
|
||||
|
||||
this.testState.verifyRefreshTTL(beforeState, refreshOfferMessage, expectStateChange, this.expectIsDataOwner());
|
||||
this.testState.verifyRefreshTTL(beforeState, refreshOfferMessage, expectStateChange);
|
||||
}
|
||||
|
||||
// TESTCASE: Refresh an entry that doesn't exist
|
||||
@ -686,8 +682,7 @@ public class P2PDataStorageProtectedStorageEntryTest {
|
||||
|
||||
return true;
|
||||
} else {
|
||||
// XXX: All external callers just pass in true, a future patch can remove the argument.
|
||||
return testState.mockedStorage.remove(entry, TestState.getTestNodeAddress(), true);
|
||||
return testState.mockedStorage.remove(entry, TestState.getTestNodeAddress());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,12 +62,12 @@ public class P2PDataStorageRemoveExpiredTest {
|
||||
KeyPair ownerKeys = TestUtils.generateKeyPair();
|
||||
ProtectedStoragePayload protectedStoragePayload = new ProtectedStoragePayloadStub(ownerKeys.getPublic());
|
||||
ProtectedStorageEntry protectedStorageEntry = this.testState.mockedStorage.getProtectedStorageEntry(protectedStoragePayload, ownerKeys);
|
||||
Assert.assertTrue(this.testState.mockedStorage.addProtectedStorageEntry(protectedStorageEntry, TestState.getTestNodeAddress(), null, true));
|
||||
Assert.assertTrue(this.testState.mockedStorage.addProtectedStorageEntry(protectedStorageEntry, TestState.getTestNodeAddress(), null));
|
||||
|
||||
SavedTestState beforeState = this.testState.saveTestState(protectedStorageEntry);
|
||||
this.testState.mockedStorage.removeExpiredEntries();
|
||||
|
||||
this.testState.verifyProtectedStorageRemove(beforeState, protectedStorageEntry, false, false, false, false, false);
|
||||
this.testState.verifyProtectedStorageRemove(beforeState, protectedStorageEntry, false, false, false, false);
|
||||
}
|
||||
|
||||
// TESTCASE: Correctly skips all PersistableNetworkPayloads since they are not expirable
|
||||
@ -75,7 +75,7 @@ public class P2PDataStorageRemoveExpiredTest {
|
||||
public void removeExpiredEntries_skipsPersistableNetworkPayload() {
|
||||
PersistableNetworkPayload persistableNetworkPayload = new PersistableNetworkPayloadStub(true);
|
||||
|
||||
Assert.assertTrue(this.testState.mockedStorage.addPersistableNetworkPayload(persistableNetworkPayload,getTestNodeAddress(), true, true, false, false));
|
||||
Assert.assertTrue(this.testState.mockedStorage.addPersistableNetworkPayload(persistableNetworkPayload,getTestNodeAddress(), false));
|
||||
|
||||
this.testState.mockedStorage.removeExpiredEntries();
|
||||
|
||||
@ -88,12 +88,12 @@ public class P2PDataStorageRemoveExpiredTest {
|
||||
KeyPair ownerKeys = TestUtils.generateKeyPair();
|
||||
ProtectedStoragePayload protectedStoragePayload = new ExpirableProtectedStoragePayloadStub(ownerKeys.getPublic());
|
||||
ProtectedStorageEntry protectedStorageEntry = this.testState.mockedStorage.getProtectedStorageEntry(protectedStoragePayload, ownerKeys);
|
||||
Assert.assertTrue(this.testState.mockedStorage.addProtectedStorageEntry(protectedStorageEntry, TestState.getTestNodeAddress(), null, true));
|
||||
Assert.assertTrue(this.testState.mockedStorage.addProtectedStorageEntry(protectedStorageEntry, TestState.getTestNodeAddress(), null));
|
||||
|
||||
SavedTestState beforeState = this.testState.saveTestState(protectedStorageEntry);
|
||||
this.testState.mockedStorage.removeExpiredEntries();
|
||||
|
||||
this.testState.verifyProtectedStorageRemove(beforeState, protectedStorageEntry, false, false, false, false, false);
|
||||
this.testState.verifyProtectedStorageRemove(beforeState, protectedStorageEntry, false, false, false, false);
|
||||
}
|
||||
|
||||
// TESTCASE: Correctly expires non-persistable entries that are expired
|
||||
@ -102,7 +102,7 @@ public class P2PDataStorageRemoveExpiredTest {
|
||||
KeyPair ownerKeys = TestUtils.generateKeyPair();
|
||||
ProtectedStoragePayload protectedStoragePayload = new ExpirableProtectedStoragePayloadStub(ownerKeys.getPublic(), 0);
|
||||
ProtectedStorageEntry protectedStorageEntry = this.testState.mockedStorage.getProtectedStorageEntry(protectedStoragePayload, ownerKeys);
|
||||
Assert.assertTrue(this.testState.mockedStorage.addProtectedStorageEntry(protectedStorageEntry, TestState.getTestNodeAddress(), null, true));
|
||||
Assert.assertTrue(this.testState.mockedStorage.addProtectedStorageEntry(protectedStorageEntry, TestState.getTestNodeAddress(), null));
|
||||
|
||||
// Increment the clock by an hour which will cause the Payloads to be outside the TTL range
|
||||
this.testState.incrementClock();
|
||||
@ -110,7 +110,7 @@ public class P2PDataStorageRemoveExpiredTest {
|
||||
SavedTestState beforeState = this.testState.saveTestState(protectedStorageEntry);
|
||||
this.testState.mockedStorage.removeExpiredEntries();
|
||||
|
||||
this.testState.verifyProtectedStorageRemove(beforeState, protectedStorageEntry, true, true, false, false, false);
|
||||
this.testState.verifyProtectedStorageRemove(beforeState, protectedStorageEntry, true, true, false, false);
|
||||
}
|
||||
|
||||
// TESTCASE: Correctly skips persistable entries that are not expired
|
||||
@ -119,12 +119,12 @@ public class P2PDataStorageRemoveExpiredTest {
|
||||
KeyPair ownerKeys = TestUtils.generateKeyPair();
|
||||
ProtectedStoragePayload protectedStoragePayload = new PersistableExpirableProtectedStoragePayloadStub(ownerKeys.getPublic());
|
||||
ProtectedStorageEntry protectedStorageEntry = this.testState.mockedStorage.getProtectedStorageEntry(protectedStoragePayload, ownerKeys);
|
||||
Assert.assertTrue(this.testState.mockedStorage.addProtectedStorageEntry(protectedStorageEntry, TestState.getTestNodeAddress(), null, true));
|
||||
Assert.assertTrue(this.testState.mockedStorage.addProtectedStorageEntry(protectedStorageEntry, TestState.getTestNodeAddress(), null));
|
||||
|
||||
SavedTestState beforeState = this.testState.saveTestState(protectedStorageEntry);
|
||||
this.testState.mockedStorage.removeExpiredEntries();
|
||||
|
||||
this.testState.verifyProtectedStorageRemove(beforeState, protectedStorageEntry, false, false, false, false, false);
|
||||
this.testState.verifyProtectedStorageRemove(beforeState, protectedStorageEntry, false, false, false, false);
|
||||
}
|
||||
|
||||
// TESTCASE: Correctly expires persistable entries that are expired
|
||||
@ -133,7 +133,7 @@ public class P2PDataStorageRemoveExpiredTest {
|
||||
KeyPair ownerKeys = TestUtils.generateKeyPair();
|
||||
ProtectedStoragePayload protectedStoragePayload = new PersistableExpirableProtectedStoragePayloadStub(ownerKeys.getPublic(), 0);
|
||||
ProtectedStorageEntry protectedStorageEntry = testState.mockedStorage.getProtectedStorageEntry(protectedStoragePayload, ownerKeys);
|
||||
Assert.assertTrue(testState.mockedStorage.addProtectedStorageEntry(protectedStorageEntry, TestState.getTestNodeAddress(), null, true));
|
||||
Assert.assertTrue(testState.mockedStorage.addProtectedStorageEntry(protectedStorageEntry, TestState.getTestNodeAddress(), null));
|
||||
|
||||
// Increment the clock by an hour which will cause the Payloads to be outside the TTL range
|
||||
this.testState.incrementClock();
|
||||
@ -141,7 +141,7 @@ public class P2PDataStorageRemoveExpiredTest {
|
||||
SavedTestState beforeState = this.testState.saveTestState(protectedStorageEntry);
|
||||
this.testState.mockedStorage.removeExpiredEntries();
|
||||
|
||||
this.testState.verifyProtectedStorageRemove(beforeState, protectedStorageEntry, true, true, false, false, false);
|
||||
this.testState.verifyProtectedStorageRemove(beforeState, protectedStorageEntry, true, true, false, false);
|
||||
}
|
||||
|
||||
// TESTCASE: Ensure we try to purge old entries sequence number map when size exceeds the maximum size
|
||||
@ -158,14 +158,14 @@ public class P2PDataStorageRemoveExpiredTest {
|
||||
ProtectedStorageEntry purgedProtectedStorageEntry = testState.mockedStorage.getProtectedStorageEntry(purgedProtectedStoragePayload, purgedOwnerKeys);
|
||||
expectedRemoves.add(purgedProtectedStorageEntry);
|
||||
|
||||
Assert.assertTrue(testState.mockedStorage.addProtectedStorageEntry(purgedProtectedStorageEntry, TestState.getTestNodeAddress(), null, true));
|
||||
Assert.assertTrue(testState.mockedStorage.addProtectedStorageEntry(purgedProtectedStorageEntry, TestState.getTestNodeAddress(), null));
|
||||
|
||||
for (int i = 0; i < MAX_SEQUENCE_NUMBER_MAP_SIZE_BEFORE_PURGE - 1; ++i) {
|
||||
KeyPair ownerKeys = TestUtils.generateKeyPair();
|
||||
ProtectedStoragePayload protectedStoragePayload = new PersistableExpirableProtectedStoragePayloadStub(ownerKeys.getPublic(), 0);
|
||||
ProtectedStorageEntry tmpEntry = testState.mockedStorage.getProtectedStorageEntry(protectedStoragePayload, ownerKeys);
|
||||
expectedRemoves.add(tmpEntry);
|
||||
Assert.assertTrue(testState.mockedStorage.addProtectedStorageEntry(tmpEntry, TestState.getTestNodeAddress(), null, true));
|
||||
Assert.assertTrue(testState.mockedStorage.addProtectedStorageEntry(tmpEntry, TestState.getTestNodeAddress(), null));
|
||||
}
|
||||
|
||||
// Increment the time by 5 days which is less than the purge requirement. This will allow the map to have
|
||||
@ -178,7 +178,7 @@ public class P2PDataStorageRemoveExpiredTest {
|
||||
ProtectedStorageEntry keepProtectedStorageEntry = testState.mockedStorage.getProtectedStorageEntry(keepProtectedStoragePayload, keepOwnerKeys);
|
||||
expectedRemoves.add(keepProtectedStorageEntry);
|
||||
|
||||
Assert.assertTrue(testState.mockedStorage.addProtectedStorageEntry(keepProtectedStorageEntry, TestState.getTestNodeAddress(), null, true));
|
||||
Assert.assertTrue(testState.mockedStorage.addProtectedStorageEntry(keepProtectedStorageEntry, TestState.getTestNodeAddress(), null));
|
||||
|
||||
// P2PDataStorage::PURGE_AGE_DAYS == 10 days
|
||||
// Advance time past it so they will be valid purge targets
|
||||
@ -187,6 +187,6 @@ public class P2PDataStorageRemoveExpiredTest {
|
||||
// The first 4 entries (11 days old) should be purged from the SequenceNumberMap
|
||||
SavedTestState beforeState = this.testState.saveTestState(purgedProtectedStorageEntry);
|
||||
this.testState.mockedStorage.removeExpiredEntries();
|
||||
this.testState.verifyProtectedStorageRemove(beforeState, expectedRemoves, true, true, false, false, false);
|
||||
this.testState.verifyProtectedStorageRemove(beforeState, expectedRemoves, true, true, false, false);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.network.p2p.storage;
|
||||
|
||||
import bisq.network.p2p.NodeAddress;
|
||||
import bisq.network.p2p.TestUtils;
|
||||
import bisq.network.p2p.peers.getdata.messages.GetUpdatedDataRequest;
|
||||
import bisq.network.p2p.peers.getdata.messages.PreliminaryGetDataRequest;
|
||||
import bisq.network.p2p.storage.mocks.PersistableNetworkPayloadStub;
|
||||
import bisq.network.p2p.storage.mocks.ProtectedStoragePayloadStub;
|
||||
import bisq.network.p2p.storage.payload.PersistableNetworkPayload;
|
||||
import bisq.network.p2p.storage.payload.ProtectedStorageEntry;
|
||||
import bisq.network.p2p.storage.payload.ProtectedStoragePayload;
|
||||
|
||||
import bisq.common.app.Capabilities;
|
||||
import bisq.common.app.Capability;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
public class P2PDataStorageRequestDataTest {
|
||||
private TestState testState;
|
||||
|
||||
private NodeAddress localNodeAddress;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
this.testState = new TestState();
|
||||
|
||||
this.localNodeAddress = new NodeAddress("localhost", 8080);
|
||||
|
||||
// Set up basic capabilities to ensure message contains it
|
||||
Capabilities.app.addAll(Capability.MEDIATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the target bytes are found in the container set.
|
||||
*/
|
||||
private boolean byteSetContains(Set<byte[]> container, byte[] target) {
|
||||
// Set<byte[]>.contains() doesn't do a deep compare, so generate a Set<ByteArray> so equals() does what
|
||||
// we want
|
||||
Set<P2PDataStorage.ByteArray> translatedContainer =
|
||||
P2PDataStorage.ByteArray.convertBytesSetToByteArraySet(container);
|
||||
|
||||
return translatedContainer.contains(new P2PDataStorage.ByteArray(target));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a unique ProtectedStorageEntry that is valid for add. This is used to initialize P2PDataStorage state
|
||||
* so the tests can validate the correct behavior. Adds of identical payloads with different sequence numbers
|
||||
* is not supported.
|
||||
*/
|
||||
private ProtectedStorageEntry getProtectedStorageEntryForAdd() throws NoSuchAlgorithmException {
|
||||
KeyPair ownerKeys = TestUtils.generateKeyPair();
|
||||
|
||||
ProtectedStoragePayload protectedStoragePayload = new ProtectedStoragePayloadStub(ownerKeys.getPublic());
|
||||
|
||||
ProtectedStorageEntry stub = mock(ProtectedStorageEntry.class);
|
||||
when(stub.getOwnerPubKey()).thenReturn(ownerKeys.getPublic());
|
||||
when(stub.isValidForAddOperation()).thenReturn(true);
|
||||
when(stub.matchesRelevantPubKey(any(ProtectedStorageEntry.class))).thenReturn(true);
|
||||
when(stub.getSequenceNumber()).thenReturn(1);
|
||||
when(stub.getProtectedStoragePayload()).thenReturn(protectedStoragePayload);
|
||||
|
||||
return stub;
|
||||
}
|
||||
|
||||
// TESTCASE: P2PDataStorage with no entries returns an empty PreliminaryGetDataRequest
|
||||
@Test
|
||||
public void buildPreliminaryGetDataRequest_EmptyP2PDataStore() {
|
||||
PreliminaryGetDataRequest getDataRequest = this.testState.mockedStorage.buildPreliminaryGetDataRequest(1);
|
||||
|
||||
Assert.assertEquals(getDataRequest.getNonce(), 1);
|
||||
Assert.assertEquals(getDataRequest.getSupportedCapabilities(), Capabilities.app);
|
||||
Assert.assertTrue(getDataRequest.getExcludedKeys().isEmpty());
|
||||
}
|
||||
|
||||
// TESTCASE: P2PDataStorage with no entries returns an empty PreliminaryGetDataRequest
|
||||
@Test
|
||||
public void buildGetUpdatedDataRequest_EmptyP2PDataStore() {
|
||||
GetUpdatedDataRequest getDataRequest =
|
||||
this.testState.mockedStorage.buildGetUpdatedDataRequest(this.localNodeAddress, 1);
|
||||
|
||||
Assert.assertEquals(getDataRequest.getNonce(), 1);
|
||||
Assert.assertEquals(getDataRequest.getSenderNodeAddress(), this.localNodeAddress);
|
||||
Assert.assertTrue(getDataRequest.getExcludedKeys().isEmpty());
|
||||
}
|
||||
|
||||
// TESTCASE: P2PDataStorage with PersistableNetworkPayloads and ProtectedStorageEntry generates
|
||||
// correct GetDataRequestMessage with both sets of keys.
|
||||
@Test
|
||||
public void buildPreliminaryGetDataRequest_FilledP2PDataStore() throws NoSuchAlgorithmException {
|
||||
PersistableNetworkPayload toAdd1 = new PersistableNetworkPayloadStub(new byte[] { 1 });
|
||||
PersistableNetworkPayload toAdd2 = new PersistableNetworkPayloadStub(new byte[] { 2 });
|
||||
ProtectedStorageEntry toAdd3 = getProtectedStorageEntryForAdd();
|
||||
ProtectedStorageEntry toAdd4 = getProtectedStorageEntryForAdd();
|
||||
|
||||
this.testState.mockedStorage.addPersistableNetworkPayload(toAdd1, this.localNodeAddress, false);
|
||||
this.testState.mockedStorage.addPersistableNetworkPayload(toAdd2, this.localNodeAddress, false);
|
||||
|
||||
this.testState.mockedStorage.addProtectedStorageEntry(toAdd3, this.localNodeAddress, null);
|
||||
this.testState.mockedStorage.addProtectedStorageEntry(toAdd4, this.localNodeAddress, null);
|
||||
|
||||
PreliminaryGetDataRequest getDataRequest = this.testState.mockedStorage.buildPreliminaryGetDataRequest(1);
|
||||
|
||||
Assert.assertEquals(getDataRequest.getNonce(), 1);
|
||||
Assert.assertEquals(getDataRequest.getSupportedCapabilities(), Capabilities.app);
|
||||
Assert.assertEquals(4, getDataRequest.getExcludedKeys().size());
|
||||
Assert.assertTrue(byteSetContains(getDataRequest.getExcludedKeys(), toAdd1.getHash()));
|
||||
Assert.assertTrue(byteSetContains(getDataRequest.getExcludedKeys(), toAdd2.getHash()));
|
||||
Assert.assertTrue(byteSetContains(getDataRequest.getExcludedKeys(),
|
||||
P2PDataStorage.get32ByteHash(toAdd3.getProtectedStoragePayload())));
|
||||
Assert.assertTrue(byteSetContains(getDataRequest.getExcludedKeys(),
|
||||
P2PDataStorage.get32ByteHash(toAdd4.getProtectedStoragePayload())));
|
||||
}
|
||||
|
||||
// TESTCASE: P2PDataStorage with PersistableNetworkPayloads and ProtectedStorageEntry generates
|
||||
// correct GetDataRequestMessage with both sets of keys.
|
||||
@Test
|
||||
public void requestData_FilledP2PDataStore_GetUpdatedDataRequest() throws NoSuchAlgorithmException {
|
||||
PersistableNetworkPayload toAdd1 = new PersistableNetworkPayloadStub(new byte[] { 1 });
|
||||
PersistableNetworkPayload toAdd2 = new PersistableNetworkPayloadStub(new byte[] { 2 });
|
||||
ProtectedStorageEntry toAdd3 = getProtectedStorageEntryForAdd();
|
||||
ProtectedStorageEntry toAdd4 = getProtectedStorageEntryForAdd();
|
||||
|
||||
this.testState.mockedStorage.addPersistableNetworkPayload(toAdd1, this.localNodeAddress, false);
|
||||
this.testState.mockedStorage.addPersistableNetworkPayload(toAdd2, this.localNodeAddress, false);
|
||||
|
||||
this.testState.mockedStorage.addProtectedStorageEntry(toAdd3, this.localNodeAddress, null);
|
||||
this.testState.mockedStorage.addProtectedStorageEntry(toAdd4, this.localNodeAddress, null);
|
||||
|
||||
GetUpdatedDataRequest getDataRequest =
|
||||
this.testState.mockedStorage.buildGetUpdatedDataRequest(this.localNodeAddress, 1);
|
||||
|
||||
Assert.assertEquals(getDataRequest.getNonce(), 1);
|
||||
Assert.assertEquals(getDataRequest.getSenderNodeAddress(), this.localNodeAddress);
|
||||
Assert.assertEquals(4, getDataRequest.getExcludedKeys().size());
|
||||
Assert.assertTrue(byteSetContains(getDataRequest.getExcludedKeys(), toAdd1.getHash()));
|
||||
Assert.assertTrue(byteSetContains(getDataRequest.getExcludedKeys(), toAdd2.getHash()));
|
||||
Assert.assertTrue(byteSetContains(getDataRequest.getExcludedKeys(),
|
||||
P2PDataStorage.get32ByteHash(toAdd3.getProtectedStoragePayload())));
|
||||
Assert.assertTrue(byteSetContains(getDataRequest.getExcludedKeys(),
|
||||
P2PDataStorage.get32ByteHash(toAdd4.getProtectedStoragePayload())));
|
||||
}
|
||||
}
|
@ -26,11 +26,9 @@ import bisq.network.p2p.storage.payload.ProtectedStorageEntry;
|
||||
import bisq.network.p2p.storage.payload.ProtectedStoragePayload;
|
||||
|
||||
import bisq.common.crypto.CryptoException;
|
||||
import bisq.common.proto.persistable.PersistablePayload;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PublicKey;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -57,19 +55,18 @@ public class P2PDataStoreDisconnectTest {
|
||||
ProtectedStoragePayload protectedStoragePayload = new ExpirableProtectedStoragePayloadStub(ownerKeys.getPublic(), ttl);
|
||||
|
||||
ProtectedStorageEntry protectedStorageEntry = testState.mockedStorage.getProtectedStorageEntry(protectedStoragePayload, ownerKeys);
|
||||
testState.mockedStorage.addProtectedStorageEntry(protectedStorageEntry, TestState.getTestNodeAddress(), null, false);
|
||||
testState.mockedStorage.addProtectedStorageEntry(protectedStorageEntry, getTestNodeAddress(), null);
|
||||
|
||||
return protectedStorageEntry;
|
||||
}
|
||||
|
||||
private static void verifyStateAfterDisconnect(TestState currentState,
|
||||
SavedTestState beforeState,
|
||||
boolean wasRemoved,
|
||||
boolean wasTTLReduced) {
|
||||
ProtectedStorageEntry protectedStorageEntry = beforeState.protectedStorageEntryBeforeOp;
|
||||
|
||||
currentState.verifyProtectedStorageRemove(beforeState, protectedStorageEntry,
|
||||
wasRemoved, wasRemoved, false, false, false);
|
||||
false, false, false, false);
|
||||
|
||||
if (wasTTLReduced)
|
||||
Assert.assertTrue(protectedStorageEntry.getCreationTimeStamp() < beforeState.creationTimestampBeforeUpdate);
|
||||
@ -85,39 +82,48 @@ public class P2PDataStoreDisconnectTest {
|
||||
|
||||
// TESTCASE: Bad peer info
|
||||
@Test
|
||||
public void peerConnectionUnknown() {
|
||||
when(this.mockedConnection.hasPeersNodeAddress()).thenReturn(false);
|
||||
|
||||
this.testState.mockedStorage.onDisconnect(CloseConnectionReason.SOCKET_CLOSED, mockedConnection);
|
||||
}
|
||||
|
||||
// TESTCASE: Intended disconnects don't trigger expiration
|
||||
@Test
|
||||
public void connectionClosedIntended() {
|
||||
when(this.mockedConnection.hasPeersNodeAddress()).thenReturn(true);
|
||||
this.testState.mockedStorage.onDisconnect(CloseConnectionReason.CLOSE_REQUESTED_BY_PEER, mockedConnection);
|
||||
}
|
||||
|
||||
// TESTCASE: Peer NodeAddress unknown
|
||||
@Test
|
||||
public void connectionClosedSkipsItemsPeerInfoBadState() throws NoSuchAlgorithmException, CryptoException {
|
||||
when(this.mockedConnection.hasPeersNodeAddress()).thenReturn(true);
|
||||
when(mockedConnection.getPeersNodeAddressOptional()).thenReturn(Optional.empty());
|
||||
|
||||
ProtectedStorageEntry protectedStorageEntry = populateTestState(testState, 1);
|
||||
public void peerConnectionUnknown() throws CryptoException, NoSuchAlgorithmException {
|
||||
when(this.mockedConnection.getPeersNodeAddressOptional()).thenReturn(Optional.empty());
|
||||
ProtectedStorageEntry protectedStorageEntry = populateTestState(testState, 2);
|
||||
|
||||
SavedTestState beforeState = this.testState.saveTestState(protectedStorageEntry);
|
||||
|
||||
this.testState.mockedStorage.onDisconnect(CloseConnectionReason.SOCKET_CLOSED, mockedConnection);
|
||||
|
||||
verifyStateAfterDisconnect(this.testState, beforeState, false, false);
|
||||
verifyStateAfterDisconnect(this.testState, beforeState, false);
|
||||
}
|
||||
|
||||
// TESTCASE: Intended disconnects don't trigger expiration
|
||||
@Test
|
||||
public void connectionClosedIntended() throws CryptoException, NoSuchAlgorithmException {
|
||||
when(this.mockedConnection.getPeersNodeAddressOptional()).thenReturn(Optional.of(getTestNodeAddress()));
|
||||
ProtectedStorageEntry protectedStorageEntry = populateTestState(testState, 2);
|
||||
|
||||
SavedTestState beforeState = this.testState.saveTestState(protectedStorageEntry);
|
||||
|
||||
this.testState.mockedStorage.onDisconnect(CloseConnectionReason.CLOSE_REQUESTED_BY_PEER, mockedConnection);
|
||||
|
||||
verifyStateAfterDisconnect(this.testState, beforeState, false);
|
||||
}
|
||||
|
||||
// TESTCASE: Peer NodeAddress unknown
|
||||
@Test
|
||||
public void connectionClosedSkipsItemsPeerInfoBadState() throws NoSuchAlgorithmException, CryptoException {
|
||||
when(this.mockedConnection.getPeersNodeAddressOptional()).thenReturn(Optional.empty());
|
||||
|
||||
ProtectedStorageEntry protectedStorageEntry = populateTestState(testState, 2);
|
||||
|
||||
SavedTestState beforeState = this.testState.saveTestState(protectedStorageEntry);
|
||||
|
||||
this.testState.mockedStorage.onDisconnect(CloseConnectionReason.SOCKET_CLOSED, mockedConnection);
|
||||
|
||||
verifyStateAfterDisconnect(this.testState, beforeState, false);
|
||||
}
|
||||
|
||||
// TESTCASE: Unintended disconnects reduce the TTL for entrys that match disconnected peer
|
||||
@Test
|
||||
public void connectionClosedReduceTTL() throws NoSuchAlgorithmException, CryptoException {
|
||||
when(this.mockedConnection.hasPeersNodeAddress()).thenReturn(true);
|
||||
when(mockedConnection.getPeersNodeAddressOptional()).thenReturn(Optional.of(TestState.getTestNodeAddress()));
|
||||
when(this.mockedConnection.getPeersNodeAddressOptional()).thenReturn(Optional.of(getTestNodeAddress()));
|
||||
|
||||
ProtectedStorageEntry protectedStorageEntry = populateTestState(testState, TimeUnit.DAYS.toMillis(90));
|
||||
|
||||
@ -125,76 +131,20 @@ public class P2PDataStoreDisconnectTest {
|
||||
|
||||
this.testState.mockedStorage.onDisconnect(CloseConnectionReason.SOCKET_CLOSED, mockedConnection);
|
||||
|
||||
verifyStateAfterDisconnect(this.testState, beforeState, false, true);
|
||||
verifyStateAfterDisconnect(this.testState, beforeState, true);
|
||||
}
|
||||
|
||||
// TESTCASE: Unintended disconnects don't reduce TTL for entrys that are not from disconnected peer
|
||||
@Test
|
||||
public void connectionClosedSkipsItemsNotFromPeer() throws NoSuchAlgorithmException, CryptoException {
|
||||
when(this.mockedConnection.hasPeersNodeAddress()).thenReturn(true);
|
||||
when(mockedConnection.getPeersNodeAddressOptional()).thenReturn(Optional.of(new NodeAddress("notTestNode", 2020)));
|
||||
when(this.mockedConnection.getPeersNodeAddressOptional()).thenReturn(Optional.of(new NodeAddress("notTestNode", 2020)));
|
||||
|
||||
ProtectedStorageEntry protectedStorageEntry = populateTestState(testState, 1);
|
||||
ProtectedStorageEntry protectedStorageEntry = populateTestState(testState, 2);
|
||||
|
||||
SavedTestState beforeState = this.testState.saveTestState(protectedStorageEntry);
|
||||
|
||||
this.testState.mockedStorage.onDisconnect(CloseConnectionReason.SOCKET_CLOSED, mockedConnection);
|
||||
|
||||
verifyStateAfterDisconnect(this.testState, beforeState, false, false);
|
||||
}
|
||||
|
||||
// TESTCASE: Unintended disconnects expire entrys that match disconnected peer and TTL is low enough for expire
|
||||
@Test
|
||||
public void connectionClosedReduceTTLAndExpireItemsFromPeer() throws NoSuchAlgorithmException, CryptoException {
|
||||
when(this.mockedConnection.hasPeersNodeAddress()).thenReturn(true);
|
||||
when(mockedConnection.getPeersNodeAddressOptional()).thenReturn(Optional.of(TestState.getTestNodeAddress()));
|
||||
|
||||
ProtectedStorageEntry protectedStorageEntry = populateTestState(testState, 1);
|
||||
|
||||
SavedTestState beforeState = this.testState.saveTestState(protectedStorageEntry);
|
||||
|
||||
// Increment the time by 1 hour which will put the protectedStorageState outside TTL
|
||||
this.testState.incrementClock();
|
||||
|
||||
this.testState.mockedStorage.onDisconnect(CloseConnectionReason.SOCKET_CLOSED, mockedConnection);
|
||||
|
||||
verifyStateAfterDisconnect(this.testState, beforeState, true, false);
|
||||
}
|
||||
|
||||
// TESTCASE: ProtectedStoragePayloads implementing the PersistablePayload interface are correctly removed
|
||||
// from the persistent store during the onDisconnect path.
|
||||
@Test
|
||||
public void connectionClosedReduceTTLAndExpireItemsFromPeerPersistable()
|
||||
throws NoSuchAlgorithmException, CryptoException {
|
||||
|
||||
class ExpirablePersistentProtectedStoragePayloadStub
|
||||
extends ExpirableProtectedStoragePayloadStub implements PersistablePayload {
|
||||
|
||||
private ExpirablePersistentProtectedStoragePayloadStub(PublicKey ownerPubKey) {
|
||||
super(ownerPubKey, 0);
|
||||
}
|
||||
}
|
||||
|
||||
when(this.mockedConnection.hasPeersNodeAddress()).thenReturn(true);
|
||||
when(mockedConnection.getPeersNodeAddressOptional()).thenReturn(Optional.of(TestState.getTestNodeAddress()));
|
||||
|
||||
KeyPair ownerKeys = TestUtils.generateKeyPair();
|
||||
ProtectedStoragePayload protectedStoragePayload =
|
||||
new ExpirablePersistentProtectedStoragePayloadStub(ownerKeys.getPublic());
|
||||
|
||||
ProtectedStorageEntry protectedStorageEntry =
|
||||
testState.mockedStorage.getProtectedStorageEntry(protectedStoragePayload, ownerKeys);
|
||||
|
||||
testState.mockedStorage.addProtectedStorageEntry(
|
||||
protectedStorageEntry, TestState.getTestNodeAddress(), null, false);
|
||||
|
||||
SavedTestState beforeState = this.testState.saveTestState(protectedStorageEntry);
|
||||
|
||||
// Increment the time by 1 hour which will put the protectedStorageState outside TTL
|
||||
this.testState.incrementClock();
|
||||
|
||||
this.testState.mockedStorage.onDisconnect(CloseConnectionReason.SOCKET_CLOSED, mockedConnection);
|
||||
|
||||
verifyStateAfterDisconnect(this.testState, beforeState, true, false);
|
||||
verifyStateAfterDisconnect(this.testState, beforeState, false);
|
||||
}
|
||||
}
|
||||
|
@ -171,69 +171,66 @@ public class TestState {
|
||||
|
||||
void verifyPersistableAdd(SavedTestState beforeState,
|
||||
PersistableNetworkPayload persistableNetworkPayload,
|
||||
boolean expectedStateChange,
|
||||
boolean expectedBroadcastAndListenersSignaled,
|
||||
boolean expectedIsDataOwner) {
|
||||
boolean expectedHashMapAndDataStoreUpdated,
|
||||
boolean expectedListenersSignaled,
|
||||
boolean expectedBroadcast) {
|
||||
P2PDataStorage.ByteArray hash = new P2PDataStorage.ByteArray(persistableNetworkPayload.getHash());
|
||||
|
||||
if (expectedStateChange) {
|
||||
// Payload is accessible from get()
|
||||
if (expectedHashMapAndDataStoreUpdated)
|
||||
Assert.assertEquals(persistableNetworkPayload, this.mockedStorage.getAppendOnlyDataStoreMap().get(hash));
|
||||
} else {
|
||||
// On failure, just ensure the state remained the same as before the add
|
||||
if (beforeState.persistableNetworkPayloadBeforeOp != null)
|
||||
Assert.assertEquals(beforeState.persistableNetworkPayloadBeforeOp, this.mockedStorage.getAppendOnlyDataStoreMap().get(hash));
|
||||
else
|
||||
Assert.assertNull(this.mockedStorage.getAppendOnlyDataStoreMap().get(hash));
|
||||
}
|
||||
else
|
||||
Assert.assertEquals(beforeState.persistableNetworkPayloadBeforeOp, this.mockedStorage.getAppendOnlyDataStoreMap().get(hash));
|
||||
|
||||
if (expectedStateChange && expectedBroadcastAndListenersSignaled) {
|
||||
// Broadcast Called
|
||||
verify(this.mockBroadcaster).broadcast(any(AddPersistableNetworkPayloadMessage.class), any(NodeAddress.class),
|
||||
eq(null), eq(expectedIsDataOwner));
|
||||
|
||||
// Verify the listeners were updated once
|
||||
if (expectedListenersSignaled)
|
||||
verify(this.appendOnlyDataStoreListener).onAdded(persistableNetworkPayload);
|
||||
|
||||
} else {
|
||||
verify(this.mockBroadcaster, never()).broadcast(any(BroadcastMessage.class), any(NodeAddress.class), any(BroadcastHandler.Listener.class), anyBoolean());
|
||||
|
||||
// Verify the listeners were never updated
|
||||
else
|
||||
verify(this.appendOnlyDataStoreListener, never()).onAdded(persistableNetworkPayload);
|
||||
}
|
||||
|
||||
if (expectedBroadcast)
|
||||
verify(this.mockBroadcaster).broadcast(any(AddPersistableNetworkPayloadMessage.class),
|
||||
nullable(NodeAddress.class), isNull());
|
||||
else
|
||||
verify(this.mockBroadcaster, never()).broadcast(any(BroadcastMessage.class), nullable(NodeAddress.class), nullable(BroadcastHandler.Listener.class));
|
||||
}
|
||||
|
||||
void verifyProtectedStorageAdd(SavedTestState beforeState,
|
||||
ProtectedStorageEntry protectedStorageEntry,
|
||||
boolean expectedStateChange,
|
||||
boolean expectedIsDataOwner) {
|
||||
boolean expectedHashMapAndDataStoreUpdated,
|
||||
boolean expectedListenersSignaled,
|
||||
boolean expectedBroadcast,
|
||||
boolean expectedSequenceNrMapWrite) {
|
||||
P2PDataStorage.ByteArray hashMapHash = P2PDataStorage.get32ByteHashAsByteArray(protectedStorageEntry.getProtectedStoragePayload());
|
||||
|
||||
if (expectedStateChange) {
|
||||
if (expectedHashMapAndDataStoreUpdated) {
|
||||
Assert.assertEquals(protectedStorageEntry, this.mockedStorage.getMap().get(hashMapHash));
|
||||
|
||||
if (protectedStorageEntry.getProtectedStoragePayload() instanceof PersistablePayload)
|
||||
Assert.assertEquals(protectedStorageEntry, this.protectedDataStoreService.getMap().get(hashMapHash));
|
||||
} else {
|
||||
Assert.assertEquals(beforeState.protectedStorageEntryBeforeOp, this.mockedStorage.getMap().get(hashMapHash));
|
||||
Assert.assertEquals(beforeState.protectedStorageEntryBeforeOpDataStoreMap, this.protectedDataStoreService.getMap().get(hashMapHash));
|
||||
}
|
||||
|
||||
if (expectedListenersSignaled) {
|
||||
verify(this.hashMapChangedListener).onAdded(Collections.singletonList(protectedStorageEntry));
|
||||
} else {
|
||||
verify(this.hashMapChangedListener, never()).onAdded(Collections.singletonList(protectedStorageEntry));
|
||||
}
|
||||
|
||||
if (expectedBroadcast) {
|
||||
final ArgumentCaptor<BroadcastMessage> captor = ArgumentCaptor.forClass(BroadcastMessage.class);
|
||||
verify(this.mockBroadcaster).broadcast(captor.capture(), any(NodeAddress.class),
|
||||
eq(null), eq(expectedIsDataOwner));
|
||||
verify(this.mockBroadcaster).broadcast(captor.capture(), nullable(NodeAddress.class), isNull());
|
||||
|
||||
BroadcastMessage broadcastMessage = captor.getValue();
|
||||
Assert.assertTrue(broadcastMessage instanceof AddDataMessage);
|
||||
Assert.assertEquals(protectedStorageEntry, ((AddDataMessage) broadcastMessage).getProtectedStorageEntry());
|
||||
} else {
|
||||
verify(this.mockBroadcaster, never()).broadcast(any(BroadcastMessage.class), nullable(NodeAddress.class), nullable(BroadcastHandler.Listener.class));
|
||||
}
|
||||
|
||||
if (expectedSequenceNrMapWrite) {
|
||||
this.verifySequenceNumberMapWriteContains(P2PDataStorage.get32ByteHashAsByteArray(protectedStorageEntry.getProtectedStoragePayload()), protectedStorageEntry.getSequenceNumber());
|
||||
} else {
|
||||
Assert.assertEquals(beforeState.protectedStorageEntryBeforeOp, this.mockedStorage.getMap().get(hashMapHash));
|
||||
Assert.assertEquals(beforeState.protectedStorageEntryBeforeOpDataStoreMap, this.protectedDataStoreService.getMap().get(hashMapHash));
|
||||
|
||||
verify(this.mockBroadcaster, never()).broadcast(any(BroadcastMessage.class), any(NodeAddress.class), any(BroadcastHandler.Listener.class), anyBoolean());
|
||||
|
||||
// Internal state didn't change... nothing should be notified
|
||||
verify(this.hashMapChangedListener, never()).onAdded(Collections.singletonList(protectedStorageEntry));
|
||||
verify(this.mockSeqNrStorage, never()).queueUpForSave(any(SequenceNumberMap.class), anyLong());
|
||||
}
|
||||
}
|
||||
@ -243,12 +240,11 @@ public class TestState {
|
||||
boolean expectedHashMapAndDataStoreUpdated,
|
||||
boolean expectedListenersSignaled,
|
||||
boolean expectedBroadcast,
|
||||
boolean expectedSeqNrWrite,
|
||||
boolean expectedIsDataOwner) {
|
||||
boolean expectedSeqNrWrite) {
|
||||
|
||||
verifyProtectedStorageRemove(beforeState, Collections.singletonList(protectedStorageEntry),
|
||||
expectedHashMapAndDataStoreUpdated, expectedListenersSignaled, expectedBroadcast,
|
||||
expectedSeqNrWrite, expectedIsDataOwner);
|
||||
expectedSeqNrWrite);
|
||||
}
|
||||
|
||||
void verifyProtectedStorageRemove(SavedTestState beforeState,
|
||||
@ -256,8 +252,7 @@ public class TestState {
|
||||
boolean expectedHashMapAndDataStoreUpdated,
|
||||
boolean expectedListenersSignaled,
|
||||
boolean expectedBroadcast,
|
||||
boolean expectedSeqNrWrite,
|
||||
boolean expectedIsDataOwner) {
|
||||
boolean expectedSeqNrWrite) {
|
||||
|
||||
// The default matcher expects orders to stay the same. So, create a custom matcher function since
|
||||
// we don't care about the order.
|
||||
@ -280,7 +275,7 @@ public class TestState {
|
||||
verify(this.mockSeqNrStorage, never()).queueUpForSave(any(SequenceNumberMap.class), anyLong());
|
||||
|
||||
if (!expectedBroadcast)
|
||||
verify(this.mockBroadcaster, never()).broadcast(any(BroadcastMessage.class), any(NodeAddress.class), any(BroadcastHandler.Listener.class), anyBoolean());
|
||||
verify(this.mockBroadcaster, never()).broadcast(any(BroadcastMessage.class), nullable(NodeAddress.class), nullable(BroadcastHandler.Listener.class));
|
||||
|
||||
|
||||
protectedStorageEntries.forEach(protectedStorageEntry -> {
|
||||
@ -292,9 +287,9 @@ public class TestState {
|
||||
|
||||
if (expectedBroadcast) {
|
||||
if (protectedStorageEntry instanceof ProtectedMailboxStorageEntry)
|
||||
verify(this.mockBroadcaster).broadcast(any(RemoveMailboxDataMessage.class), any(NodeAddress.class), eq(null), eq(expectedIsDataOwner));
|
||||
verify(this.mockBroadcaster).broadcast(any(RemoveMailboxDataMessage.class), nullable(NodeAddress.class), isNull());
|
||||
else
|
||||
verify(this.mockBroadcaster).broadcast(any(RemoveDataMessage.class), any(NodeAddress.class), eq(null), eq(expectedIsDataOwner));
|
||||
verify(this.mockBroadcaster).broadcast(any(RemoveDataMessage.class), nullable(NodeAddress.class), isNull());
|
||||
}
|
||||
|
||||
|
||||
@ -312,8 +307,7 @@ public class TestState {
|
||||
|
||||
void verifyRefreshTTL(SavedTestState beforeState,
|
||||
RefreshOfferMessage refreshOfferMessage,
|
||||
boolean expectedStateChange,
|
||||
boolean expectedIsDataOwner) {
|
||||
boolean expectedStateChange) {
|
||||
P2PDataStorage.ByteArray payloadHash = new P2PDataStorage.ByteArray(refreshOfferMessage.getHashOfPayload());
|
||||
|
||||
ProtectedStorageEntry entryAfterRefresh = this.mockedStorage.getMap().get(payloadHash);
|
||||
@ -325,8 +319,7 @@ public class TestState {
|
||||
Assert.assertTrue(entryAfterRefresh.getCreationTimeStamp() > beforeState.creationTimestampBeforeUpdate);
|
||||
|
||||
final ArgumentCaptor<BroadcastMessage> captor = ArgumentCaptor.forClass(BroadcastMessage.class);
|
||||
verify(this.mockBroadcaster).broadcast(captor.capture(), any(NodeAddress.class),
|
||||
eq(null), eq(expectedIsDataOwner));
|
||||
verify(this.mockBroadcaster).broadcast(captor.capture(), nullable(NodeAddress.class), isNull());
|
||||
|
||||
BroadcastMessage broadcastMessage = captor.getValue();
|
||||
Assert.assertTrue(broadcastMessage instanceof RefreshOfferMessage);
|
||||
@ -343,7 +336,7 @@ public class TestState {
|
||||
Assert.assertEquals(beforeState.creationTimestampBeforeUpdate, entryAfterRefresh.getCreationTimeStamp());
|
||||
}
|
||||
|
||||
verify(this.mockBroadcaster, never()).broadcast(any(BroadcastMessage.class), any(NodeAddress.class), any(BroadcastHandler.Listener.class), anyBoolean());
|
||||
verify(this.mockBroadcaster, never()).broadcast(any(BroadcastMessage.class), nullable(NodeAddress.class), nullable(BroadcastHandler.Listener.class));
|
||||
verify(this.mockSeqNrStorage, never()).queueUpForSave(any(SequenceNumberMap.class), anyLong());
|
||||
}
|
||||
}
|
||||
|
@ -28,9 +28,19 @@ import bisq.network.p2p.storage.payload.PersistableNetworkPayload;
|
||||
*/
|
||||
public class PersistableNetworkPayloadStub implements PersistableNetworkPayload {
|
||||
private final boolean hashSizeValid;
|
||||
private final byte[] hash;
|
||||
|
||||
public PersistableNetworkPayloadStub(boolean hashSizeValid) {
|
||||
this(hashSizeValid, new byte[] { 1 });
|
||||
}
|
||||
|
||||
public PersistableNetworkPayloadStub(byte[] hash) {
|
||||
this(true, hash);
|
||||
}
|
||||
|
||||
private PersistableNetworkPayloadStub(boolean hashSizeValid, byte[] hash) {
|
||||
this.hashSizeValid = hashSizeValid;
|
||||
this.hash = hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -40,7 +50,7 @@ public class PersistableNetworkPayloadStub implements PersistableNetworkPayload
|
||||
|
||||
@Override
|
||||
public byte[] getHash() {
|
||||
return new byte[] { 1 };
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -107,14 +107,13 @@ public class ProtectedMailboxStorageEntryTest {
|
||||
|
||||
// TESTCASE: validForAddOperation() should fail if the signature isn't valid
|
||||
@Test
|
||||
public void isValidForAddOperation_BadSignature() throws NoSuchAlgorithmException, CryptoException {
|
||||
public void isValidForAddOperation_BadSignature() throws NoSuchAlgorithmException {
|
||||
KeyPair senderKeys = TestUtils.generateKeyPair();
|
||||
KeyPair receiverKeys = TestUtils.generateKeyPair();
|
||||
|
||||
MailboxStoragePayload mailboxStoragePayload = buildMailboxStoragePayload(senderKeys.getPublic(), receiverKeys.getPublic());
|
||||
ProtectedStorageEntry protectedStorageEntry = buildProtectedMailboxStorageEntry(mailboxStoragePayload, senderKeys, receiverKeys.getPublic(), 1);
|
||||
|
||||
protectedStorageEntry.updateSignature( new byte[] { 0 });
|
||||
ProtectedStorageEntry protectedStorageEntry = new ProtectedMailboxStorageEntry(
|
||||
mailboxStoragePayload, senderKeys.getPublic(), 1, new byte[] { 0 }, receiverKeys.getPublic(), Clock.systemDefaultZone());
|
||||
|
||||
Assert.assertFalse(protectedStorageEntry.isValidForAddOperation());
|
||||
}
|
||||
@ -145,14 +144,14 @@ public class ProtectedMailboxStorageEntryTest {
|
||||
|
||||
// TESTCASE: isValidForRemoveOperation() should fail if the signature is bad
|
||||
@Test
|
||||
public void isValidForRemoveOperation_BadSignature() throws NoSuchAlgorithmException, CryptoException {
|
||||
public void isValidForRemoveOperation_BadSignature() throws NoSuchAlgorithmException {
|
||||
KeyPair senderKeys = TestUtils.generateKeyPair();
|
||||
KeyPair receiverKeys = TestUtils.generateKeyPair();
|
||||
|
||||
MailboxStoragePayload mailboxStoragePayload = buildMailboxStoragePayload(senderKeys.getPublic(), receiverKeys.getPublic());
|
||||
ProtectedStorageEntry protectedStorageEntry = buildProtectedMailboxStorageEntry(mailboxStoragePayload, receiverKeys, receiverKeys.getPublic(), 1);
|
||||
|
||||
protectedStorageEntry.updateSignature(new byte[] { 0 });
|
||||
ProtectedStorageEntry protectedStorageEntry =
|
||||
new ProtectedMailboxStorageEntry(mailboxStoragePayload, receiverKeys.getPublic(),
|
||||
1, new byte[] { 0 }, receiverKeys.getPublic(), Clock.systemDefaultZone());
|
||||
|
||||
Assert.assertFalse(protectedStorageEntry.isValidForRemoveOperation());
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PublicKey;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.time.Duration;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
@ -125,11 +126,13 @@ public class ProtectedStorageEntryTest {
|
||||
|
||||
// TESTCASE: validForAddOperation() should fail if the signature isn't valid
|
||||
@Test
|
||||
public void isValidForAddOperation_BadSignature() throws NoSuchAlgorithmException, CryptoException {
|
||||
public void isValidForAddOperation_BadSignature() throws NoSuchAlgorithmException {
|
||||
KeyPair ownerKeys = TestUtils.generateKeyPair();
|
||||
ProtectedStorageEntry protectedStorageEntry = buildProtectedStorageEntry(ownerKeys, ownerKeys, 1);
|
||||
|
||||
protectedStorageEntry.updateSignature( new byte[] { 0 });
|
||||
ProtectedStoragePayload protectedStoragePayload = new ProtectedStoragePayloadStub(ownerKeys.getPublic());
|
||||
ProtectedStorageEntry protectedStorageEntry =
|
||||
new ProtectedStorageEntry(protectedStoragePayload, ownerKeys.getPublic(),
|
||||
1, new byte[] { 0 }, Clock.systemDefaultZone());
|
||||
|
||||
Assert.assertFalse(protectedStorageEntry.isValidForAddOperation());
|
||||
}
|
||||
@ -181,11 +184,13 @@ public class ProtectedStorageEntryTest {
|
||||
|
||||
// TESTCASE: isValidForRemoveOperation() should fail if the signature is bad
|
||||
@Test
|
||||
public void isValidForRemoveOperation_BadSignature() throws NoSuchAlgorithmException, CryptoException {
|
||||
public void isValidForRemoveOperation_BadSignature() throws NoSuchAlgorithmException {
|
||||
KeyPair ownerKeys = TestUtils.generateKeyPair();
|
||||
ProtectedStorageEntry protectedStorageEntry = buildProtectedStorageEntry(ownerKeys, ownerKeys, 1);
|
||||
|
||||
protectedStorageEntry.updateSignature(new byte[] { 0 });
|
||||
ProtectedStoragePayload protectedStoragePayload = new ProtectedStoragePayloadStub(ownerKeys.getPublic());
|
||||
ProtectedStorageEntry protectedStorageEntry =
|
||||
new ProtectedStorageEntry(protectedStoragePayload, ownerKeys.getPublic(),
|
||||
1, new byte[] { 0 }, Clock.systemDefaultZone());
|
||||
|
||||
Assert.assertFalse(protectedStorageEntry.isValidForRemoveOperation());
|
||||
}
|
||||
@ -249,4 +254,20 @@ public class ProtectedStorageEntryTest {
|
||||
new ProtectedStorageEntry(incompatiblePayload,ownerKeys.getPublic(), 1,
|
||||
new byte[] { 0 }, Clock.systemDefaultZone());
|
||||
}
|
||||
|
||||
// TESTCASE: PSEs received with future-dated timestamps are updated to be min(currentTime, creationTimeStamp)
|
||||
@Test
|
||||
public void futureTimestampIsSanitized() throws NoSuchAlgorithmException {
|
||||
KeyPair ownerKeys = TestUtils.generateKeyPair();
|
||||
|
||||
Clock baseClock = Clock.systemDefaultZone();
|
||||
Clock futureClock = Clock.offset(baseClock, Duration.ofDays(1));
|
||||
|
||||
ProtectedStoragePayload protectedStoragePayload = new ProtectedStoragePayloadStub(ownerKeys.getPublic());
|
||||
ProtectedStorageEntry protectedStorageEntry =
|
||||
new ProtectedStorageEntry(protectedStoragePayload, Sig.getPublicKeyBytes(ownerKeys.getPublic()),
|
||||
ownerKeys.getPublic(), 1, new byte[] { 0 }, futureClock.millis(), baseClock);
|
||||
|
||||
Assert.assertTrue(protectedStorageEntry.getCreationTimeStamp() <= baseClock.millis());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user