From 33e82b336b41578e1ddbea5f1cb4076f8659e543 Mon Sep 17 00:00:00 2001 From: CalDescent Date: Sun, 9 Jan 2022 11:22:27 +0000 Subject: [PATCH 1/7] Limit arbitrary signature requests to 3 hops, just in case a bug caused any kind of circular broadcasting. --- .../arbitrary/ArbitraryDataFileManager.java | 2 +- .../arbitrary/ArbitraryDataManager.java | 13 ++++++++--- .../message/ArbitrarySignaturesMessage.java | 23 +++++++++++++++---- .../transaction/ArbitraryTransaction.java | 2 +- 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataFileManager.java b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataFileManager.java index 1b1cb945..2b0b8143 100644 --- a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataFileManager.java +++ b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataFileManager.java @@ -159,7 +159,7 @@ public class ArbitraryDataFileManager { // but only if these files are in accordance with our storage policy if (ArbitraryDataStorageManager.getInstance().canStoreData(arbitraryTransactionData)) { // Use a null peer address to indicate our own - Message newArbitrarySignatureMessage = new ArbitrarySignaturesMessage(null, Arrays.asList(signature)); + Message newArbitrarySignatureMessage = new ArbitrarySignaturesMessage(null, 0, Arrays.asList(signature)); Network.getInstance().broadcast(broadcastPeer -> newArbitrarySignatureMessage); } } diff --git a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataManager.java b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataManager.java index e2d62f6a..a0ef02bb 100644 --- a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataManager.java +++ b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataManager.java @@ -43,6 +43,9 @@ public class ArbitraryDataManager extends Thread { /** Maximum time to hold information about an in-progress relay */ public static final long ARBITRARY_RELAY_TIMEOUT = 30 * 1000L; // ms + /** Maximum number of hops that an arbitrary signatures request is allowed to make */ + private static int ARBITRARY_SIGNATURES_REQUEST_MAX_HOPS = 3; + private static ArbitraryDataManager instance; private final Object peerDataLock = new Object(); @@ -369,7 +372,7 @@ public class ArbitraryDataManager extends Thread { // Broadcast the list, using null to represent our peer address LOGGER.info("Broadcasting list of hosted signatures..."); - Message arbitrarySignatureMessage = new ArbitrarySignaturesMessage(null, hostedSignatures); + Message arbitrarySignatureMessage = new ArbitrarySignaturesMessage(null, 0, hostedSignatures); Network.getInstance().broadcast(broadcastPeer -> arbitrarySignatureMessage); } catch (DataException e) { @@ -429,8 +432,12 @@ public class ArbitraryDataManager extends Thread { // If at least one signature in this batch was new to us, we should rebroadcast the message to the // network in case some peers haven't received it yet if (containsNewEntry) { - LOGGER.debug("Rebroadcasting arbitrary signature list for peer {}", peerAddress); - Network.getInstance().broadcast(broadcastPeer -> broadcastPeer == peer ? null : arbitrarySignaturesMessage); + int requestHops = arbitrarySignaturesMessage.getRequestHops(); + arbitrarySignaturesMessage.setRequestHops(++requestHops); + if (requestHops < ARBITRARY_SIGNATURES_REQUEST_MAX_HOPS) { + LOGGER.debug("Rebroadcasting arbitrary signature list for peer {}", peerAddress); + Network.getInstance().broadcast(broadcastPeer -> broadcastPeer == peer ? null : arbitrarySignaturesMessage); + } } else { // Don't rebroadcast as otherwise we could get into a loop } diff --git a/src/main/java/org/qortal/network/message/ArbitrarySignaturesMessage.java b/src/main/java/org/qortal/network/message/ArbitrarySignaturesMessage.java index 379eeb47..16af9649 100644 --- a/src/main/java/org/qortal/network/message/ArbitrarySignaturesMessage.java +++ b/src/main/java/org/qortal/network/message/ArbitrarySignaturesMessage.java @@ -2,7 +2,6 @@ package org.qortal.network.message; import com.google.common.primitives.Ints; import org.qortal.data.network.PeerData; -import org.qortal.transaction.DeployAtTransaction; import org.qortal.transform.TransformationException; import org.qortal.transform.Transformer; import org.qortal.utils.Serialization; @@ -19,16 +18,18 @@ public class ArbitrarySignaturesMessage extends Message { private static final int SIGNATURE_LENGTH = Transformer.SIGNATURE_LENGTH; private String peerAddress; + private int requestHops; private List signatures; - public ArbitrarySignaturesMessage(String peerAddress, List signatures) { - this(-1, peerAddress, signatures); + public ArbitrarySignaturesMessage(String peerAddress, int requestHops, List signatures) { + this(-1, peerAddress, requestHops, signatures); } - private ArbitrarySignaturesMessage(int id, String peerAddress, List signatures) { + private ArbitrarySignaturesMessage(int id, String peerAddress, int requestHops, List signatures) { super(id, MessageType.ARBITRARY_SIGNATURES); this.peerAddress = peerAddress; + this.requestHops = requestHops; this.signatures = signatures; } @@ -40,9 +41,19 @@ public class ArbitrarySignaturesMessage extends Message { return this.signatures; } + public int getRequestHops() { + return this.requestHops; + } + + public void setRequestHops(int requestHops) { + this.requestHops = requestHops; + } + public static Message fromByteBuffer(int id, ByteBuffer bytes) throws UnsupportedEncodingException, TransformationException { String peerAddress = Serialization.deserializeSizedString(bytes, PeerData.MAX_PEER_ADDRESS_SIZE); + int requestHops = bytes.getInt(); + int signatureCount = bytes.getInt(); if (bytes.remaining() != signatureCount * SIGNATURE_LENGTH) @@ -55,7 +66,7 @@ public class ArbitrarySignaturesMessage extends Message { signatures.add(signature); } - return new ArbitrarySignaturesMessage(id, peerAddress, signatures); + return new ArbitrarySignaturesMessage(id, peerAddress, requestHops, signatures); } @Override @@ -65,6 +76,8 @@ public class ArbitrarySignaturesMessage extends Message { Serialization.serializeSizedString(bytes, this.peerAddress); + bytes.write(Ints.toByteArray(this.requestHops)); + bytes.write(Ints.toByteArray(this.signatures.size())); for (byte[] signature : this.signatures) diff --git a/src/main/java/org/qortal/transaction/ArbitraryTransaction.java b/src/main/java/org/qortal/transaction/ArbitraryTransaction.java index d8740351..ddb58b0e 100644 --- a/src/main/java/org/qortal/transaction/ArbitraryTransaction.java +++ b/src/main/java/org/qortal/transaction/ArbitraryTransaction.java @@ -228,7 +228,7 @@ public class ArbitraryTransaction extends Transaction { if (ArbitraryDataStorageManager.getInstance().canStoreData(arbitraryTransactionData)) { // Use a null peer address to indicate our own byte[] signature = arbitraryTransactionData.getSignature(); - Message arbitrarySignatureMessage = new ArbitrarySignaturesMessage(null, Arrays.asList(signature)); + Message arbitrarySignatureMessage = new ArbitrarySignaturesMessage(null, 0, Arrays.asList(signature)); Network.getInstance().broadcast(broadcastPeer -> arbitrarySignatureMessage); } } From f58a16905f3efb1a7469b20c9e5a753c10583558 Mon Sep 17 00:00:00 2001 From: CalDescent Date: Sun, 9 Jan 2022 13:19:07 +0000 Subject: [PATCH 2/7] Removed unused setting. --- src/main/java/org/qortal/settings/Settings.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/org/qortal/settings/Settings.java b/src/main/java/org/qortal/settings/Settings.java index a40b14a9..f35a4364 100644 --- a/src/main/java/org/qortal/settings/Settings.java +++ b/src/main/java/org/qortal/settings/Settings.java @@ -214,8 +214,6 @@ public class Settings { private int maxBlocksPerRequest = 100; /** Maximum number of blocks this node will serve in a single response */ private int maxBlocksPerResponse = 200; - /** Maximum number of untrimmed blocks this node will serve in a single response */ - private int maxUntrimmedBlocksPerResponse = 10; // Which blockchains this node is running private String blockchainConfig = null; // use default from resources @@ -709,8 +707,6 @@ public class Settings { public int getMaxBlocksPerResponse() { return this.maxBlocksPerResponse; } - public int getMaxUntrimmedBlocksPerResponse() { return this.maxUntrimmedBlocksPerResponse; } - public boolean isAutoUpdateEnabled() { return this.autoUpdateEnabled; } From 5ba6f6f53ef00fdfd1ed406f1a111509d1a14a96 Mon Sep 17 00:00:00 2001 From: CalDescent Date: Sun, 9 Jan 2022 13:25:49 +0000 Subject: [PATCH 3/7] FOLLOWED_AND_VIEWED renamed to FOLLOWED_OR_VIEWED, since it's technically an OR not an AND. --- .../qortal/controller/arbitrary/ArbitraryDataManager.java | 2 +- .../controller/arbitrary/ArbitraryDataStorageManager.java | 8 ++++---- .../test/arbitrary/ArbitraryDataStoragePolicyTests.java | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataManager.java b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataManager.java index a0ef02bb..e968d64c 100644 --- a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataManager.java +++ b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataManager.java @@ -102,7 +102,7 @@ public class ArbitraryDataManager extends Thread { // Fetch data according to storage policy switch (Settings.getInstance().getStoragePolicy()) { case FOLLOWED: - case FOLLOWED_AND_VIEWED: + case FOLLOWED_OR_VIEWED: this.processNames(); break; diff --git a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataStorageManager.java b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataStorageManager.java index bd686355..c95b0694 100644 --- a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataStorageManager.java +++ b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataStorageManager.java @@ -26,7 +26,7 @@ import java.util.stream.Collectors; public class ArbitraryDataStorageManager extends Thread { public enum StoragePolicy { - FOLLOWED_AND_VIEWED, + FOLLOWED_OR_VIEWED, FOLLOWED, VIEWED, ALL, @@ -126,7 +126,7 @@ public class ArbitraryDataStorageManager extends Thread { // Check if our storage policy and and lists allow us to host data for this name switch (Settings.getInstance().getStoragePolicy()) { - case FOLLOWED_AND_VIEWED: + case FOLLOWED_OR_VIEWED: case ALL: case VIEWED: // If the policy includes viewed data, we can host it as long as it's not blocked @@ -189,7 +189,7 @@ public class ArbitraryDataStorageManager extends Thread { switch (Settings.getInstance().getStoragePolicy()) { case FOLLOWED: - case FOLLOWED_AND_VIEWED: + case FOLLOWED_OR_VIEWED: return this.isFollowingName(name); case ALL: @@ -217,7 +217,7 @@ public class ArbitraryDataStorageManager extends Thread { case NONE: case VIEWED: case FOLLOWED: - case FOLLOWED_AND_VIEWED: + case FOLLOWED_OR_VIEWED: default: return false; } diff --git a/src/test/java/org/qortal/test/arbitrary/ArbitraryDataStoragePolicyTests.java b/src/test/java/org/qortal/test/arbitrary/ArbitraryDataStoragePolicyTests.java index 5c88956e..30abc9f5 100644 --- a/src/test/java/org/qortal/test/arbitrary/ArbitraryDataStoragePolicyTests.java +++ b/src/test/java/org/qortal/test/arbitrary/ArbitraryDataStoragePolicyTests.java @@ -68,7 +68,7 @@ public class ArbitraryDataStoragePolicyTests extends Common { assertTrue(ResourceListManager.getInstance().addToList("followedNames", name, false)); // We should store and pre-fetch data for this transaction - assertEquals(StoragePolicy.FOLLOWED_AND_VIEWED, Settings.getInstance().getStoragePolicy()); + assertEquals(StoragePolicy.FOLLOWED_OR_VIEWED, Settings.getInstance().getStoragePolicy()); assertTrue(storageManager.canStoreData(transactionData)); assertTrue(storageManager.shouldPreFetchData(repository, transactionData)); From 11e194292cfaa8803c30ec9072315d06648b292b Mon Sep 17 00:00:00 2001 From: CalDescent Date: Sun, 9 Jan 2022 16:26:23 +0000 Subject: [PATCH 4/7] Removed API key requirement from GET /admin/status and GET /admin/mintingaccounts --- src/main/java/org/qortal/api/resource/AdminResource.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/org/qortal/api/resource/AdminResource.java b/src/main/java/org/qortal/api/resource/AdminResource.java index 1d127b93..d2bc8fb3 100644 --- a/src/main/java/org/qortal/api/resource/AdminResource.java +++ b/src/main/java/org/qortal/api/resource/AdminResource.java @@ -137,10 +137,7 @@ public class AdminResource { ) } ) - @SecurityRequirement(name = "apiKey") public NodeStatus status() { - Security.checkApiCallAllowed(request); - NodeStatus nodeStatus = new NodeStatus(); return nodeStatus; @@ -252,9 +249,7 @@ public class AdminResource { } ) @ApiErrors({ApiError.REPOSITORY_ISSUE}) - @SecurityRequirement(name = "apiKey") public List getMintingAccounts() { - Security.checkApiCallAllowed(request); try (final Repository repository = RepositoryManager.getRepository()) { List mintingAccounts = repository.getAccountRepository().getMintingAccounts(); From b4d2fae27f6dfda7d02d7a902e4e141f0265f1f4 Mon Sep 17 00:00:00 2001 From: CalDescent Date: Sun, 9 Jan 2022 16:26:52 +0000 Subject: [PATCH 5/7] Fixed a couple of FOLLOWED_AND_VIEWED references that were missed --- src/main/java/org/qortal/settings/Settings.java | 2 +- src/test/resources/test-settings-v2.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/qortal/settings/Settings.java b/src/main/java/org/qortal/settings/Settings.java index f35a4364..505ed6be 100644 --- a/src/main/java/org/qortal/settings/Settings.java +++ b/src/main/java/org/qortal/settings/Settings.java @@ -282,7 +282,7 @@ public class Settings { private String tempDataPath = null; /** Storage policy to indicate which data should be hosted */ - private String storagePolicy = "FOLLOWED_AND_VIEWED"; + private String storagePolicy = "FOLLOWED_OR_VIEWED"; /** Whether to allow data outside of the storage policy to be relayed between other peers */ private boolean relayModeEnabled = true; diff --git a/src/test/resources/test-settings-v2.json b/src/test/resources/test-settings-v2.json index d9f6cb0c..7802f598 100644 --- a/src/test/resources/test-settings-v2.json +++ b/src/test/resources/test-settings-v2.json @@ -14,7 +14,7 @@ "dataPath": "data-test", "tempDataPath": "data-test/_temp", "listsPath": "lists-test", - "storagePolicy": "FOLLOWED_AND_VIEWED", + "storagePolicy": "FOLLOWED_OR_VIEWED", "maxStorageCapacity": 104857600, "localAuthBypassEnabled": true } From 940304b4c2029611729e80c0322be557b96b5e30 Mon Sep 17 00:00:00 2001 From: CalDescent Date: Sun, 9 Jan 2022 20:08:45 +0000 Subject: [PATCH 6/7] Added GET /admin/apikey/test endpoint, so that we have a dedicated place to test if authentication works. --- .../org/qortal/api/resource/AdminResource.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/main/java/org/qortal/api/resource/AdminResource.java b/src/main/java/org/qortal/api/resource/AdminResource.java index d2bc8fb3..d9f35550 100644 --- a/src/main/java/org/qortal/api/resource/AdminResource.java +++ b/src/main/java/org/qortal/api/resource/AdminResource.java @@ -746,4 +746,22 @@ public class AdminResource { return apiKey.toString(); } + @GET + @Path("/apikey/test") + @Operation( + summary = "Test an API key", + responses = { + @ApiResponse( + description = "true if authenticated", + content = @Content(mediaType = MediaType.TEXT_PLAIN, schema = @Schema(type = "boolean")) + ) + } + ) + @SecurityRequirement(name = "apiKey") + public String testApiKey() { + Security.checkApiCallAllowed(request); + + return "true"; + } + } From 051052fdd23adbe4a47b4692e2b1e122e2999b37 Mon Sep 17 00:00:00 2001 From: CalDescent Date: Sun, 9 Jan 2022 21:09:20 +0000 Subject: [PATCH 7/7] Removed authentication for GET /peers/summary endpoint --- src/main/java/org/qortal/api/resource/PeersResource.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/org/qortal/api/resource/PeersResource.java b/src/main/java/org/qortal/api/resource/PeersResource.java index 77c11b99..1cf5ff16 100644 --- a/src/main/java/org/qortal/api/resource/PeersResource.java +++ b/src/main/java/org/qortal/api/resource/PeersResource.java @@ -352,10 +352,7 @@ public class PeersResource { ) } ) - @SecurityRequirement(name = "apiKey") public PeersSummary peersSummary() { - Security.checkApiCallAllowed(request); - PeersSummary peersSummary = new PeersSummary(); List connectedPeers = Network.getInstance().getConnectedPeers().stream().collect(Collectors.toList());