From cbd1018ecfc01c518aa674cbc1451d620f6bf497 Mon Sep 17 00:00:00 2001 From: CalDescent Date: Tue, 1 Mar 2022 20:22:47 +0000 Subject: [PATCH] Allow identical data to be published if the metadata differs. --- .../ArbitraryDataTransactionBuilder.java | 53 +++++++++++++++---- .../ArbitraryDataTransactionMetadata.java | 9 +++- 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/qortal/arbitrary/ArbitraryDataTransactionBuilder.java b/src/main/java/org/qortal/arbitrary/ArbitraryDataTransactionBuilder.java index 13e5808c..0f3d4357 100644 --- a/src/main/java/org/qortal/arbitrary/ArbitraryDataTransactionBuilder.java +++ b/src/main/java/org/qortal/arbitrary/ArbitraryDataTransactionBuilder.java @@ -6,6 +6,7 @@ import org.qortal.arbitrary.exception.MissingDataException; import org.qortal.arbitrary.ArbitraryDataFile.ResourceIdType; import org.qortal.arbitrary.ArbitraryDataDiff.*; import org.qortal.arbitrary.metadata.ArbitraryDataMetadataPatch; +import org.qortal.arbitrary.metadata.ArbitraryDataTransactionMetadata; import org.qortal.arbitrary.misc.Category; import org.qortal.arbitrary.misc.Service; import org.qortal.crypto.Crypto; @@ -27,6 +28,7 @@ import java.io.IOException; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Random; public class ArbitraryDataTransactionBuilder { @@ -79,9 +81,9 @@ public class ArbitraryDataTransactionBuilder { this.identifier = identifier; // Metadata (optional) - this.title = title; - this.description = description; - this.tags = tags; + this.title = ArbitraryDataTransactionMetadata.limitTitle(title); + this.description = ArbitraryDataTransactionMetadata.limitDescription(description); + this.tags = ArbitraryDataTransactionMetadata.limitTags(tags); this.category = category; } @@ -121,6 +123,10 @@ public class ArbitraryDataTransactionBuilder { return Method.PUT; } + // Get existing metadata and see if it matches the new metadata + ArbitraryDataResource resource = new ArbitraryDataResource(this.name, ResourceIdType.NAME, this.service, this.identifier); + ArbitraryDataTransactionMetadata existingMetadata = resource.getLatestTransactionMetadata(); + try { // Check layer count int layerCount = reader.getLayerCount(); @@ -131,7 +137,23 @@ public class ArbitraryDataTransactionBuilder { // Check size of differences between this layer and previous layer ArbitraryDataCreatePatch patch = new ArbitraryDataCreatePatch(reader.getFilePath(), this.path, reader.getLatestSignature()); - patch.create(); + try { + patch.create(); + } + catch (DataException | IOException e) { + // Handle matching states separately, as it's best to block transactions with duplicate states + if (e.getMessage().equals("Current state matches previous state. Nothing to do.")) { + // Only throw an exception if the metadata is also identical, as well as the data + if (this.isMetadataEqual(existingMetadata)) { + throw new DataException(e.getMessage()); + } + } + + LOGGER.info("Caught exception when creating patch: {}", e.getMessage()); + LOGGER.info("Unable to load existing resource - using PUT to overwrite it."); + return Method.PUT; + } + long diffSize = FilesystemUtils.getDirectorySize(patch.getFinalPath()); long existingStateSize = FilesystemUtils.getDirectorySize(reader.getFilePath()); double difference = (double) diffSize / (double) existingStateSize; @@ -168,11 +190,8 @@ public class ArbitraryDataTransactionBuilder { // State is appropriate for a PATCH transaction return Method.PATCH; } - catch (IOException | DataException e) { - // Handle matching states separately, as it's best to block transactions with duplicate states - if (e.getMessage().equals("Current state matches previous state. Nothing to do.")) { - throw new DataException(e.getMessage()); - } + catch (IOException e) { + // IMPORTANT: Don't catch DataException here, as they must be passed to the caller LOGGER.info("Caught exception: {}", e.getMessage()); LOGGER.info("Unable to load existing resource - using PUT to overwrite it."); return Method.PUT; @@ -267,6 +286,22 @@ public class ArbitraryDataTransactionBuilder { } + private boolean isMetadataEqual(ArbitraryDataTransactionMetadata existingMetadata) { + if (!Objects.equals(existingMetadata.getTitle(), this.title)) { + return false; + } + if (!Objects.equals(existingMetadata.getDescription(), this.description)) { + return false; + } + if (!Objects.equals(existingMetadata.getCategory(), this.category)) { + return false; + } + if (!Objects.equals(existingMetadata.getTags(), this.tags)) { + return false; + } + return true; + } + public void computeNonce() throws DataException { if (this.arbitraryTransactionData == null) { throw new DataException("Arbitrary transaction data is required to compute nonce"); diff --git a/src/main/java/org/qortal/arbitrary/metadata/ArbitraryDataTransactionMetadata.java b/src/main/java/org/qortal/arbitrary/metadata/ArbitraryDataTransactionMetadata.java index 1657fe05..0f8b676b 100644 --- a/src/main/java/org/qortal/arbitrary/metadata/ArbitraryDataTransactionMetadata.java +++ b/src/main/java/org/qortal/arbitrary/metadata/ArbitraryDataTransactionMetadata.java @@ -172,6 +172,9 @@ public class ArbitraryDataTransactionMetadata extends ArbitraryDataMetadata { if (title == null) { return null; } + if (title.isEmpty()) { + return null; + } return title.substring(0, Math.min(title.length(), MAX_TITLE_LENGTH)); } @@ -180,6 +183,9 @@ public class ArbitraryDataTransactionMetadata extends ArbitraryDataMetadata { if (description == null) { return null; } + if (description.isEmpty()) { + return null; + } return description.substring(0, Math.min(description.length(), MAX_DESCRIPTION_LENGTH)); } @@ -199,10 +205,11 @@ public class ArbitraryDataTransactionMetadata extends ArbitraryDataMetadata { // Remove tags over the limit // This is cleaner than truncating, which results in malformed tags + // Also remove tags that are empty Iterator iterator = mutableTags.iterator(); while (iterator.hasNext()) { String tag = (String) iterator.next(); - if (tag == null || tag.length() > MAX_TAG_LENGTH) { + if (tag == null || tag.length() > MAX_TAG_LENGTH || tag.isEmpty()) { iterator.remove(); } }