Added duplicate message filter for chat transactions.

This tracks the last 3 unique (unencrypted) messages for each address and fails validation if a duplicate is sent.
This commit is contained in:
CalDescent 2021-08-07 14:59:32 +01:00
parent 2f3e10e15a
commit b0f963cca7
3 changed files with 80 additions and 0 deletions

View File

@ -0,0 +1,69 @@
package org.qortal.chat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ChatDuplicateMessageFilter {
private static ChatDuplicateMessageFilter instance;
private volatile boolean isStopping = false;
private static final int numberOfUniqueMessagesToMonitor = 3;
// Maintain a short list of recent chat messages for each address, to save having to query the database every time
private Map<String, List<String>> recentMessages = new ConcurrentHashMap<>();
public ChatDuplicateMessageFilter() {
}
public static synchronized ChatDuplicateMessageFilter getInstance() {
if (instance == null) {
instance = new ChatDuplicateMessageFilter();
}
return instance;
}
public boolean isDuplicateMessage(String address, String message) {
boolean isDuplicateMessage;
boolean messagesUpdated = false;
// Add timestamp to array for address
List<String> messages = new ArrayList<>();
if (this.recentMessages.containsKey(address)) {
messages = this.recentMessages.get(address);
}
if (!messages.contains(message)) {
messages.add(message);
this.recentMessages.put(address, messages);
messagesUpdated = true;
isDuplicateMessage = false;
}
else {
// Can't add message because it already exists
isDuplicateMessage = true;
}
// Ensure we're not tracking more messages than intended
while (messages.size() > numberOfUniqueMessagesToMonitor) {
messages.remove(0);
messagesUpdated = true;
}
if (messagesUpdated) {
if (messages.size() > 0) {
this.recentMessages.put(address, messages);
}
else {
this.recentMessages.remove(address);
}
}
return isDuplicateMessage;
}
}

View File

@ -6,6 +6,7 @@ import java.util.List;
import org.qortal.account.Account;
import org.qortal.account.PublicKeyAccount;
import org.qortal.asset.Asset;
import org.qortal.chat.ChatDuplicateMessageFilter;
import org.qortal.chat.ChatRateLimiter;
import org.qortal.crypto.Crypto;
import org.qortal.crypto.MemoryPoW;
@ -19,6 +20,7 @@ import org.qortal.repository.Repository;
import org.qortal.transform.TransformationException;
import org.qortal.transform.transaction.ChatTransactionTransformer;
import org.qortal.transform.transaction.TransactionTransformer;
import org.qortal.utils.Base58;
public class ChatTransaction extends Transaction {
@ -166,6 +168,14 @@ public class ChatTransaction extends Transaction {
if (rateLimiter.isAddressAboveRateLimit(chatTransactionData.getSender()))
return ValidationResult.ADDRESS_ABOVE_RATE_LIMIT;
// Check for duplicate messages (unencrypted text messages only)
if (!chatTransactionData.getIsEncrypted() && chatTransactionData.getIsText()) {
ChatDuplicateMessageFilter duplicateFilter = ChatDuplicateMessageFilter.getInstance();
String message58 = Base58.encode(chatTransactionData.getData());
if (duplicateFilter.isDuplicateMessage(chatTransactionData.getSender(), message58))
return ValidationResult.DUPLICATE_MESSAGE;
}
return ValidationResult.OK;
}

View File

@ -249,6 +249,7 @@ public abstract class Transaction {
INVALID_TIMESTAMP_SIGNATURE(95),
ADDRESS_IN_BLACKLIST(96),
ADDRESS_ABOVE_RATE_LIMIT(97),
DUPLICATE_MESSAGE(98),
INVALID_BUT_OK(999),
NOT_YET_RELEASED(1000);