diff --git a/core/src/main/java/bisq/core/proto/persistable/CorePersistenceProtoResolver.java b/core/src/main/java/bisq/core/proto/persistable/CorePersistenceProtoResolver.java index f32bf710ad..539d8ace19 100644 --- a/core/src/main/java/bisq/core/proto/persistable/CorePersistenceProtoResolver.java +++ b/core/src/main/java/bisq/core/proto/persistable/CorePersistenceProtoResolver.java @@ -44,6 +44,7 @@ import bisq.core.user.PreferencesPayload; import bisq.core.user.UserPayload; import bisq.network.p2p.MailboxMessageList; +import bisq.network.p2p.mailbox.IgnoredMailboxMap; import bisq.network.p2p.peers.peerexchange.PeerList; import bisq.network.p2p.storage.persistence.SequenceNumberMap; @@ -132,7 +133,8 @@ public class CorePersistenceProtoResolver extends CoreProtoResolver implements P return TradeStatistics3Store.fromProto(proto.getTradeStatistics3Store()); case MAILBOX_MESSAGE_LIST: return MailboxMessageList.fromProto(proto.getMailboxMessageList(), networkProtoResolver); - + case IGNORED_MAILBOX_MAP: + return IgnoredMailboxMap.fromProto(proto.getIgnoredMailboxMap()); default: throw new ProtobufferRuntimeException("Unknown proto message case(PB.PersistableEnvelope). " + "messageCase=" + proto.getMessageCase() + "; proto raw data=" + proto.toString()); diff --git a/core/src/main/java/bisq/core/setup/CorePersistedDataHost.java b/core/src/main/java/bisq/core/setup/CorePersistedDataHost.java index 43fc7f2fd5..85b3a902b7 100644 --- a/core/src/main/java/bisq/core/setup/CorePersistedDataHost.java +++ b/core/src/main/java/bisq/core/setup/CorePersistedDataHost.java @@ -36,6 +36,7 @@ import bisq.core.user.Preferences; import bisq.core.user.User; import bisq.network.p2p.P2PService; +import bisq.network.p2p.mailbox.IgnoredMailboxService; import bisq.network.p2p.peers.PeerManager; import bisq.network.p2p.storage.P2PDataStorage; @@ -68,6 +69,7 @@ public class CorePersistedDataHost { persistedDataHosts.add(injector.getInstance(P2PDataStorage.class)); persistedDataHosts.add(injector.getInstance(PeerManager.class)); persistedDataHosts.add(injector.getInstance(P2PService.class)); + persistedDataHosts.add(injector.getInstance(IgnoredMailboxService.class)); if (injector.getInstance(Config.class).daoActivated) { persistedDataHosts.add(injector.getInstance(BallotListService.class)); diff --git a/p2p/src/main/java/bisq/network/p2p/P2PService.java b/p2p/src/main/java/bisq/network/p2p/P2PService.java index 3cb04314dd..f80acf6e14 100644 --- a/p2p/src/main/java/bisq/network/p2p/P2PService.java +++ b/p2p/src/main/java/bisq/network/p2p/P2PService.java @@ -19,6 +19,7 @@ package bisq.network.p2p; import bisq.network.Socks5ProxyProvider; import bisq.network.crypto.EncryptionService; +import bisq.network.p2p.mailbox.IgnoredMailboxService; import bisq.network.p2p.messaging.DecryptedMailboxListener; import bisq.network.p2p.network.CloseConnectionReason; import bisq.network.p2p.network.Connection; @@ -50,6 +51,7 @@ import bisq.common.app.Capability; import bisq.common.crypto.CryptoException; import bisq.common.crypto.KeyRing; import bisq.common.crypto.PubKeyRing; +import bisq.common.crypto.SealedAndSigned; import bisq.common.persistence.PersistenceManager; import bisq.common.proto.ProtobufferException; import bisq.common.proto.network.NetworkEnvelope; @@ -110,6 +112,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis private final SeedNodeRepository seedNodeRepository; private final EncryptionService encryptionService; + private final IgnoredMailboxService ignoredMailboxService; private final PersistenceManager persistenceManager; private final KeyRing keyRing; @@ -158,6 +161,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis SeedNodeRepository seedNodeRepository, Socks5ProxyProvider socks5ProxyProvider, EncryptionService encryptionService, + IgnoredMailboxService ignoredMailboxService, PersistenceManager persistenceManager, KeyRing keyRing) { this.networkNode = networkNode; @@ -170,6 +174,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis this.seedNodeRepository = seedNodeRepository; this.socks5ProxyProvider = socks5ProxyProvider; this.encryptionService = encryptionService; + this.ignoredMailboxService = ignoredMailboxService; this.persistenceManager = persistenceManager; this.keyRing = keyRing; @@ -513,15 +518,23 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis @Nullable private MailboxItem decryptProtectedMailboxStorageEntry(ProtectedMailboxStorageEntry protectedMailboxStorageEntry) { + PrefixedSealedAndSignedMessage prefixedSealedAndSignedMessage = protectedMailboxStorageEntry + .getMailboxStoragePayload() + .getPrefixedSealedAndSignedMessage(); + SealedAndSigned sealedAndSigned = prefixedSealedAndSignedMessage.getSealedAndSigned(); + String uid = prefixedSealedAndSignedMessage.getUid(); + if (ignoredMailboxService.isIgnored(uid)) { + // We had persisted a past failed decryption attempt on that message so we don't try again and return early + return null; + } try { - DecryptedMessageWithPubKey decryptedMessageWithPubKey = encryptionService.decryptAndVerify(protectedMailboxStorageEntry - .getMailboxStoragePayload() - .getPrefixedSealedAndSignedMessage() - .getSealedAndSigned()); + DecryptedMessageWithPubKey decryptedMessageWithPubKey = encryptionService.decryptAndVerify(sealedAndSigned); checkArgument(decryptedMessageWithPubKey.getNetworkEnvelope() instanceof MailboxMessage); return new MailboxItem(protectedMailboxStorageEntry, decryptedMessageWithPubKey); } catch (CryptoException ignore) { // Expected if message was not intended for us + // We persist those entries so at the next startup we do not need to try to decrypt it anymore + ignoredMailboxService.ignore(uid, protectedMailboxStorageEntry.getCreationTimeStamp()); } catch (ProtobufferException e) { log.error(e.toString()); e.getStackTrace(); diff --git a/p2p/src/main/java/bisq/network/p2p/mailbox/IgnoredMailboxMap.java b/p2p/src/main/java/bisq/network/p2p/mailbox/IgnoredMailboxMap.java new file mode 100644 index 0000000000..1441f24e62 --- /dev/null +++ b/p2p/src/main/java/bisq/network/p2p/mailbox/IgnoredMailboxMap.java @@ -0,0 +1,71 @@ +/* + * 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 . + */ + +package bisq.network.p2p.mailbox; + + +import bisq.common.proto.persistable.PersistableEnvelope; +import bisq.common.util.CollectionUtils; + +import java.util.HashMap; +import java.util.Map; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@EqualsAndHashCode +public class IgnoredMailboxMap implements PersistableEnvelope { + @Getter + private final Map dataMap; + + public IgnoredMailboxMap() { + this.dataMap = new HashMap<>(); + } + + /////////////////////////////////////////////////////////////////////////////////////////// + // PROTO BUFFER + /////////////////////////////////////////////////////////////////////////////////////////// + + public IgnoredMailboxMap(Map ignored) { + this.dataMap = ignored; + } + + @Override + public protobuf.PersistableEnvelope toProtoMessage() { + return protobuf.PersistableEnvelope.newBuilder() + .setIgnoredMailboxMap(protobuf.IgnoredMailboxMap.newBuilder().putAllData(dataMap)) + .build(); + } + + public static IgnoredMailboxMap fromProto(protobuf.IgnoredMailboxMap proto) { + return new IgnoredMailboxMap(CollectionUtils.isEmpty(proto.getDataMap()) ? new HashMap<>() : proto.getDataMap()); + } + + public void putAll(Map map) { + dataMap.putAll(map); + } + + public boolean containsKey(String uid) { + return dataMap.containsKey(uid); + } + + public void put(String uid, long creationTimeStamp) { + dataMap.put(uid, creationTimeStamp); + } +} diff --git a/p2p/src/main/java/bisq/network/p2p/mailbox/IgnoredMailboxService.java b/p2p/src/main/java/bisq/network/p2p/mailbox/IgnoredMailboxService.java new file mode 100644 index 0000000000..82d11c71b1 --- /dev/null +++ b/p2p/src/main/java/bisq/network/p2p/mailbox/IgnoredMailboxService.java @@ -0,0 +1,71 @@ +/* + * 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 . + */ + +package bisq.network.p2p.mailbox; + +import bisq.network.p2p.storage.payload.MailboxStoragePayload; + +import bisq.common.persistence.PersistenceManager; +import bisq.common.proto.persistable.PersistedDataHost; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * We persist failed attempts to decrypt mailbox messages (expected if mailbox message was not addressed to us). + * This improves performance at processing mailbox messages. + * On a fast 4 core machine 1000 mailbox messages take about 1.5 second. At second start-up using the persisted data + * it only takes about 30 ms. + */ +@Singleton +public class IgnoredMailboxService implements PersistedDataHost { + private final PersistenceManager persistenceManager; + private final IgnoredMailboxMap ignoredMailboxMap = new IgnoredMailboxMap(); + + @Inject + public IgnoredMailboxService(PersistenceManager persistenceManager) { + this.persistenceManager = persistenceManager; + this.persistenceManager.initialize(ignoredMailboxMap, PersistenceManager.Source.PRIVATE_LOW_PRIO); + } + + /////////////////////////////////////////////////////////////////////////////////////////// + // PersistedDataHost + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public void readPersisted(Runnable completeHandler) { + persistenceManager.readPersisted(persisted -> { + // At each load we cleanup outdated entries + long expiredDate = System.currentTimeMillis() - MailboxStoragePayload.TTL; + persisted.getDataMap().entrySet().stream() + .filter(e -> e.getValue() > expiredDate) + .forEach(e -> ignoredMailboxMap.put(e.getKey(), e.getValue())); + persistenceManager.requestPersistence(); + completeHandler.run(); + }, + completeHandler); + } + + public boolean isIgnored(String uid) { + return ignoredMailboxMap.containsKey(uid); + } + + public void ignore(String uid, long creationTimeStamp) { + ignoredMailboxMap.put(uid, creationTimeStamp); + persistenceManager.requestPersistence(); + } +} diff --git a/p2p/src/main/java/bisq/network/p2p/storage/payload/MailboxStoragePayload.java b/p2p/src/main/java/bisq/network/p2p/storage/payload/MailboxStoragePayload.java index ab5d14c182..e169f07a94 100644 --- a/p2p/src/main/java/bisq/network/p2p/storage/payload/MailboxStoragePayload.java +++ b/p2p/src/main/java/bisq/network/p2p/storage/payload/MailboxStoragePayload.java @@ -53,6 +53,8 @@ import javax.annotation.Nullable; @EqualsAndHashCode @Slf4j public final class MailboxStoragePayload implements ProtectedStoragePayload, ExpirablePayload, AddOncePayload { + public static final long TTL = TimeUnit.DAYS.toMillis(15); + private final PrefixedSealedAndSignedMessage prefixedSealedAndSignedMessage; private PublicKey senderPubKeyForAddOperation; private final byte[] senderPubKeyForAddOperationBytes; @@ -118,6 +120,6 @@ public final class MailboxStoragePayload implements ProtectedStoragePayload, Exp @Override public long getTTL() { - return TimeUnit.DAYS.toMillis(15); + return TTL; } } diff --git a/proto/src/main/proto/pb.proto b/proto/src/main/proto/pb.proto index 7d7870c5c6..472c7e0df4 100644 --- a/proto/src/main/proto/pb.proto +++ b/proto/src/main/proto/pb.proto @@ -576,6 +576,10 @@ message MailboxMessageList { repeated MailboxItem mailbox_item = 1; } +message IgnoredMailboxMap { + map data = 1; +} + message MailboxItem { ProtectedMailboxStorageEntry protected_mailbox_storage_entry = 1; DecryptedMessageWithPubKey decrypted_message_with_pub_key = 2; @@ -1211,6 +1215,7 @@ message PersistableEnvelope { RefundDisputeList refund_dispute_list = 30; TradeStatistics3Store trade_statistics3_store = 31; MailboxMessageList mailbox_message_list = 32; + IgnoredMailboxMap ignored_mailbox_map = 33; } }