mirror of
https://github.com/Qortal/qortal.git
synced 2025-04-29 14:27:51 +00:00
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:
parent
2f3e10e15a
commit
b0f963cca7
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -6,6 +6,7 @@ import java.util.List;
|
|||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.account.PublicKeyAccount;
|
import org.qortal.account.PublicKeyAccount;
|
||||||
import org.qortal.asset.Asset;
|
import org.qortal.asset.Asset;
|
||||||
|
import org.qortal.chat.ChatDuplicateMessageFilter;
|
||||||
import org.qortal.chat.ChatRateLimiter;
|
import org.qortal.chat.ChatRateLimiter;
|
||||||
import org.qortal.crypto.Crypto;
|
import org.qortal.crypto.Crypto;
|
||||||
import org.qortal.crypto.MemoryPoW;
|
import org.qortal.crypto.MemoryPoW;
|
||||||
@ -19,6 +20,7 @@ import org.qortal.repository.Repository;
|
|||||||
import org.qortal.transform.TransformationException;
|
import org.qortal.transform.TransformationException;
|
||||||
import org.qortal.transform.transaction.ChatTransactionTransformer;
|
import org.qortal.transform.transaction.ChatTransactionTransformer;
|
||||||
import org.qortal.transform.transaction.TransactionTransformer;
|
import org.qortal.transform.transaction.TransactionTransformer;
|
||||||
|
import org.qortal.utils.Base58;
|
||||||
|
|
||||||
public class ChatTransaction extends Transaction {
|
public class ChatTransaction extends Transaction {
|
||||||
|
|
||||||
@ -166,6 +168,14 @@ public class ChatTransaction extends Transaction {
|
|||||||
if (rateLimiter.isAddressAboveRateLimit(chatTransactionData.getSender()))
|
if (rateLimiter.isAddressAboveRateLimit(chatTransactionData.getSender()))
|
||||||
return ValidationResult.ADDRESS_ABOVE_RATE_LIMIT;
|
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;
|
return ValidationResult.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,6 +249,7 @@ public abstract class Transaction {
|
|||||||
INVALID_TIMESTAMP_SIGNATURE(95),
|
INVALID_TIMESTAMP_SIGNATURE(95),
|
||||||
ADDRESS_IN_BLACKLIST(96),
|
ADDRESS_IN_BLACKLIST(96),
|
||||||
ADDRESS_ABOVE_RATE_LIMIT(97),
|
ADDRESS_ABOVE_RATE_LIMIT(97),
|
||||||
|
DUPLICATE_MESSAGE(98),
|
||||||
INVALID_BUT_OK(999),
|
INVALID_BUT_OK(999),
|
||||||
NOT_YET_RELEASED(1000);
|
NOT_YET_RELEASED(1000);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user