diff --git a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataFileListManager.java b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataFileListManager.java index 40ce2d9a..9a56dcf5 100644 --- a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataFileListManager.java +++ b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataFileListManager.java @@ -56,6 +56,12 @@ public class ArbitraryDataFileListManager { private Map> arbitraryDataSignatureRequests = Collections.synchronizedMap(new HashMap<>()); + /** Maximum number of seconds that a file list relay request is able to exist on the network */ + private static long RELAY_REQUEST_MAX_DURATION = 5000L; + /** Maximum number of hops that a file list relay request is allowed to make */ + private static int RELAY_REQUEST_MAX_HOPS = 3; + + private ArbitraryDataFileListManager() { } @@ -223,6 +229,11 @@ public class ArbitraryDataFileListManager { byte[] signature = arbitraryTransactionData.getSignature(); String signature58 = Base58.encode(signature); + Long now = NTP.getTime(); + if (now == null) { + return false; + } + // If we've already tried too many times in a short space of time, make sure to give up if (!this.shouldMakeFileListRequestForSignature(signature58)) { // Check if we should make direct connections to peers @@ -238,7 +249,7 @@ public class ArbitraryDataFileListManager { LOGGER.debug(String.format("Sending data file list request for signature %s...", Base58.encode(signature))); // Build request - Message getArbitraryDataFileListMessage = new GetArbitraryDataFileListMessage(signature); + Message getArbitraryDataFileListMessage = new GetArbitraryDataFileListMessage(signature, now, 0); // Save our request into requests map Triple requestEntry = new Triple<>(signature58, null, NTP.getTime()); @@ -386,8 +397,8 @@ public class ArbitraryDataFileListManager { GetArbitraryDataFileListMessage getArbitraryDataFileListMessage = (GetArbitraryDataFileListMessage) message; byte[] signature = getArbitraryDataFileListMessage.getSignature(); String signature58 = Base58.encode(signature); - Long timestamp = NTP.getTime(); - Triple newEntry = new Triple<>(signature58, peer, timestamp); + Long now = NTP.getTime(); + Triple newEntry = new Triple<>(signature58, peer, now); // If we've seen this request recently, then ignore if (arbitraryDataFileListRequests.putIfAbsent(message.getId(), newEntry) != null) { @@ -446,7 +457,7 @@ public class ArbitraryDataFileListManager { if (hashes.size() > 0) { // Update requests map to reflect that we've sent it - newEntry = new Triple<>(signature58, null, timestamp); + newEntry = new Triple<>(signature58, null, now); arbitraryDataFileListRequests.put(message.getId(), newEntry); ArbitraryDataFileListMessage arbitraryDataFileListMessage = new ArbitraryDataFileListMessage(signature, hashes); @@ -462,11 +473,31 @@ public class ArbitraryDataFileListManager { boolean isBlocked = (transactionData == null || ArbitraryDataStorageManager.getInstance().isNameBlocked(transactionData.getName())); if (Settings.getInstance().isRelayModeEnabled() && !isBlocked) { // In relay mode - so ask our other peers if they have it - LOGGER.debug("Rebroadcasted hash list request from peer {} for signature {} to our other peers", peer, Base58.encode(signature)); - Network.getInstance().broadcast( - broadcastPeer -> broadcastPeer == peer || - Objects.equals(broadcastPeer.getPeerData().getAddress().getHost(), peer.getPeerData().getAddress().getHost()) - ? null : message); + + long requestTime = getArbitraryDataFileListMessage.getRequestTime(); + int requestHops = getArbitraryDataFileListMessage.getRequestHops(); + getArbitraryDataFileListMessage.setRequestHops(++requestHops); + long totalRequestTime = now - requestTime; + + if (totalRequestTime < RELAY_REQUEST_MAX_DURATION) { + // Relay request hasn't timed out yet, so can potentially be rebroadcast + if (requestHops < RELAY_REQUEST_MAX_HOPS) { + // Relay request hasn't reached the maximum number of hops yet, so can be rebroadcast + + LOGGER.info("Rebroadcasting hash list request from peer {} for signature {} to our other peers... totalRequestTime: {}, requestHops: {}", peer, Base58.encode(signature), totalRequestTime, requestHops); + Network.getInstance().broadcast( + broadcastPeer -> broadcastPeer == peer || + Objects.equals(broadcastPeer.getPeerData().getAddress().getHost(), peer.getPeerData().getAddress().getHost()) + ? null : getArbitraryDataFileListMessage); + + } + else { + // This relay request has reached the maximum number of allowed hops + } + } + else { + // This relay request has timed out + } } } } diff --git a/src/main/java/org/qortal/network/message/GetArbitraryDataFileListMessage.java b/src/main/java/org/qortal/network/message/GetArbitraryDataFileListMessage.java index 49fd60cb..e19bbb25 100644 --- a/src/main/java/org/qortal/network/message/GetArbitraryDataFileListMessage.java +++ b/src/main/java/org/qortal/network/message/GetArbitraryDataFileListMessage.java @@ -1,5 +1,7 @@ package org.qortal.network.message; +import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; import org.qortal.transform.Transformer; import java.io.ByteArrayOutputStream; @@ -7,20 +9,27 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; +import static org.qortal.transform.Transformer.INT_LENGTH; +import static org.qortal.transform.Transformer.LONG_LENGTH; + public class GetArbitraryDataFileListMessage extends Message { private static final int SIGNATURE_LENGTH = Transformer.SIGNATURE_LENGTH; private final byte[] signature; + private final long requestTime; + private int requestHops; - public GetArbitraryDataFileListMessage(byte[] signature) { - this(-1, signature); + public GetArbitraryDataFileListMessage(byte[] signature, long requestTime, int requestHops) { + this(-1, signature, requestTime, requestHops); } - private GetArbitraryDataFileListMessage(int id, byte[] signature) { + private GetArbitraryDataFileListMessage(int id, byte[] signature, long requestTime, int requestHops) { super(id, MessageType.GET_ARBITRARY_DATA_FILE_LIST); this.signature = signature; + this.requestTime = requestTime; + this.requestHops = requestHops; } public byte[] getSignature() { @@ -28,14 +37,18 @@ public class GetArbitraryDataFileListMessage extends Message { } public static Message fromByteBuffer(int id, ByteBuffer bytes) throws UnsupportedEncodingException { - if (bytes.remaining() != SIGNATURE_LENGTH) + if (bytes.remaining() != SIGNATURE_LENGTH + LONG_LENGTH + INT_LENGTH) return null; byte[] signature = new byte[SIGNATURE_LENGTH]; bytes.get(signature); - return new GetArbitraryDataFileListMessage(id, signature); + long requestTime = bytes.getLong(); + + int requestHops = bytes.getInt(); + + return new GetArbitraryDataFileListMessage(id, signature, requestTime, requestHops); } @Override @@ -45,10 +58,25 @@ public class GetArbitraryDataFileListMessage extends Message { bytes.write(this.signature); + bytes.write(Longs.toByteArray(this.requestTime)); + + bytes.write(Ints.toByteArray(this.requestHops)); + return bytes.toByteArray(); } catch (IOException e) { return null; } } + public long getRequestTime() { + return this.requestTime; + } + + public int getRequestHops() { + return this.requestHops; + } + public void setRequestHops(int requestHops) { + this.requestHops = requestHops; + } + }