diff --git a/src/main/java/org/qortal/arbitrary/ArbitraryDataBuildQueueItem.java b/src/main/java/org/qortal/arbitrary/ArbitraryDataBuildQueueItem.java index e7fd1c93..b4a75162 100644 --- a/src/main/java/org/qortal/arbitrary/ArbitraryDataBuildQueueItem.java +++ b/src/main/java/org/qortal/arbitrary/ArbitraryDataBuildQueueItem.java @@ -36,7 +36,7 @@ public class ArbitraryDataBuildQueueItem { public void build() throws IOException, DataException, MissingDataException { Long now = NTP.getTime(); if (now == null) { - throw new IllegalStateException("NTP time hasn't synced yet"); + throw new DataException("NTP time hasn't synced yet"); } this.buildStartTimestamp = now; diff --git a/src/main/java/org/qortal/arbitrary/ArbitraryDataBuilder.java b/src/main/java/org/qortal/arbitrary/ArbitraryDataBuilder.java index 0d1fb377..f5ada3cd 100644 --- a/src/main/java/org/qortal/arbitrary/ArbitraryDataBuilder.java +++ b/src/main/java/org/qortal/arbitrary/ArbitraryDataBuilder.java @@ -88,7 +88,7 @@ public class ArbitraryDataBuilder { if (latestPut == null) { String message = String.format("Couldn't find PUT transaction for name %s, service %s and identifier %s", this.name, this.service, this.identifierString()); - throw new IllegalStateException(message); + throw new DataException(message); } this.latestPutTransaction = latestPut; @@ -101,25 +101,25 @@ public class ArbitraryDataBuilder { } } - private void validateTransactions() { + private void validateTransactions() throws DataException { List transactionDataList = new ArrayList<>(this.transactions); ArbitraryTransactionData latestPut = this.latestPutTransaction; if (latestPut == null) { - throw new IllegalStateException("Cannot PATCH without existing PUT. Deploy using PUT first."); + throw new DataException("Cannot PATCH without existing PUT. Deploy using PUT first."); } if (latestPut.getMethod() != Method.PUT) { - throw new IllegalStateException("Expected PUT but received PATCH"); + throw new DataException("Expected PUT but received PATCH"); } if (transactionDataList.size() == 0) { - throw new IllegalStateException(String.format("No transactions found for name %s, service %s, " + + throw new DataException(String.format("No transactions found for name %s, service %s, " + "identifier: %s, since %d", name, service, this.identifierString(), latestPut.getTimestamp())); } // Verify that the signature of the first transaction matches the latest PUT ArbitraryTransactionData firstTransaction = transactionDataList.get(0); if (!Arrays.equals(firstTransaction.getSignature(), latestPut.getSignature())) { - throw new IllegalStateException("First transaction did not match latest PUT transaction"); + throw new DataException("First transaction did not match latest PUT transaction"); } // Remove the first transaction, as it should be the only PUT @@ -127,10 +127,10 @@ public class ArbitraryDataBuilder { for (ArbitraryTransactionData transactionData : transactionDataList) { if (transactionData == null) { - throw new IllegalStateException("Transaction not found"); + throw new DataException("Transaction not found"); } if (transactionData.getMethod() != Method.PATCH) { - throw new IllegalStateException("Expected PATCH but received PUT"); + throw new DataException("Expected PATCH but received PUT"); } } } @@ -173,32 +173,32 @@ public class ArbitraryDataBuilder { // By this point we should have all data needed to build the layers Path path = arbitraryDataReader.getFilePath(); if (path == null) { - throw new IllegalStateException(String.format("Null path when building data from transaction %s", sig58)); + throw new DataException(String.format("Null path when building data from transaction %s", sig58)); } if (!Files.exists(path)) { - throw new IllegalStateException(String.format("Path doesn't exist when building data from transaction %s", sig58)); + throw new DataException(String.format("Path doesn't exist when building data from transaction %s", sig58)); } paths.add(path); } } - private void findLatestSignature() { + private void findLatestSignature() throws DataException { if (this.transactions.size() == 0) { - throw new IllegalStateException("Unable to find latest signature from empty transaction list"); + throw new DataException("Unable to find latest signature from empty transaction list"); } // Find the latest signature ArbitraryTransactionData latestTransaction = this.transactions.get(this.transactions.size() - 1); if (latestTransaction == null) { - throw new IllegalStateException("Unable to find latest signature from null transaction"); + throw new DataException("Unable to find latest signature from null transaction"); } this.latestSignature = latestTransaction.getSignature(); } - private void validatePaths() { + private void validatePaths() throws DataException { if (this.paths.isEmpty()) { - throw new IllegalStateException("No paths available from which to build latest state"); + throw new DataException("No paths available from which to build latest state"); } } @@ -238,7 +238,7 @@ public class ArbitraryDataBuilder { private void cacheLatestSignature() throws IOException, DataException { byte[] latestTransactionSignature = this.transactions.get(this.transactions.size()-1).getSignature(); if (latestTransactionSignature == null) { - throw new IllegalStateException("Missing latest transaction signature"); + throw new DataException("Missing latest transaction signature"); } Long now = NTP.getTime(); if (now == null) { diff --git a/src/main/java/org/qortal/arbitrary/ArbitraryDataCache.java b/src/main/java/org/qortal/arbitrary/ArbitraryDataCache.java index 194b6bcc..baedb7b2 100644 --- a/src/main/java/org/qortal/arbitrary/ArbitraryDataCache.java +++ b/src/main/java/org/qortal/arbitrary/ArbitraryDataCache.java @@ -153,7 +153,7 @@ public class ArbitraryDataCache { cache.read(); return cache.getSignature(); - } catch (IOException e) { + } catch (IOException | DataException e) { return null; } } diff --git a/src/main/java/org/qortal/arbitrary/ArbitraryDataCombiner.java b/src/main/java/org/qortal/arbitrary/ArbitraryDataCombiner.java index 38e1cbd7..e92db8bc 100644 --- a/src/main/java/org/qortal/arbitrary/ArbitraryDataCombiner.java +++ b/src/main/java/org/qortal/arbitrary/ArbitraryDataCombiner.java @@ -79,12 +79,12 @@ public class ArbitraryDataCombiner { } } - private void preExecute() { + private void preExecute() throws DataException { if (this.pathBefore == null || this.pathAfter == null) { - throw new IllegalStateException("No paths available to build patch"); + throw new DataException("No paths available to build patch"); } if (!Files.exists(this.pathBefore) || !Files.exists(this.pathAfter)) { - throw new IllegalStateException("Unable to create patch because at least one path doesn't exist"); + throw new DataException("Unable to create patch because at least one path doesn't exist"); } } @@ -92,35 +92,35 @@ public class ArbitraryDataCombiner { } - private void readMetadata() throws IOException { + private void readMetadata() throws IOException, DataException { this.metadata = new ArbitraryDataMetadataPatch(this.pathAfter); this.metadata.read(); } - private void validatePreviousSignature() { + private void validatePreviousSignature() throws DataException { if (this.signatureBefore == null) { - throw new IllegalStateException("No previous signature passed to the combiner"); + throw new DataException("No previous signature passed to the combiner"); } byte[] previousSignature = this.metadata.getPreviousSignature(); if (previousSignature == null) { - throw new IllegalStateException("Unable to extract previous signature from patch metadata"); + throw new DataException("Unable to extract previous signature from patch metadata"); } // Compare the signatures if (!Arrays.equals(previousSignature, this.signatureBefore)) { - throw new IllegalStateException("Previous signatures do not match - transactions out of order?"); + throw new DataException("Previous signatures do not match - transactions out of order?"); } } - private void validatePreviousHash() throws IOException { + private void validatePreviousHash() throws IOException, DataException { if (!Settings.getInstance().shouldValidateAllDataLayers()) { return; } byte[] previousHash = this.metadata.getPreviousHash(); if (previousHash == null) { - throw new IllegalStateException("Unable to extract previous hash from patch metadata"); + throw new DataException("Unable to extract previous hash from patch metadata"); } ArbitraryDataDigest digest = new ArbitraryDataDigest(this.pathBefore); @@ -139,14 +139,14 @@ public class ArbitraryDataCombiner { this.finalPath = merge.getMergePath(); } - private void validateCurrentHash() throws IOException { + private void validateCurrentHash() throws IOException, DataException { if (!this.shouldValidateHashes) { return; } byte[] currentHash = this.metadata.getCurrentHash(); if (currentHash == null) { - throw new IllegalStateException("Unable to extract current hash from patch metadata"); + throw new DataException("Unable to extract current hash from patch metadata"); } ArbitraryDataDigest digest = new ArbitraryDataDigest(this.finalPath); diff --git a/src/main/java/org/qortal/arbitrary/ArbitraryDataCreatePatch.java b/src/main/java/org/qortal/arbitrary/ArbitraryDataCreatePatch.java index 52b4adee..d5ff0eb7 100644 --- a/src/main/java/org/qortal/arbitrary/ArbitraryDataCreatePatch.java +++ b/src/main/java/org/qortal/arbitrary/ArbitraryDataCreatePatch.java @@ -48,12 +48,12 @@ public class ArbitraryDataCreatePatch { } } - private void preExecute() { + private void preExecute() throws DataException { if (this.pathBefore == null || this.pathAfter == null) { - throw new IllegalStateException("No paths available to build patch"); + throw new DataException("No paths available to build patch"); } if (!Files.exists(this.pathBefore) || !Files.exists(this.pathAfter)) { - throw new IllegalStateException("Unable to create patch because at least one path doesn't exist"); + throw new DataException("Unable to create patch because at least one path doesn't exist"); } this.createRandomIdentifier(); @@ -84,14 +84,14 @@ public class ArbitraryDataCreatePatch { this.identifier = UUID.randomUUID().toString(); } - private void createWorkingDirectory() { + private void createWorkingDirectory() throws DataException { // Use the user-specified temp dir, as it is deterministic, and is more likely to be located on reusable storage hardware String baseDir = Settings.getInstance().getTempDataPath(); Path tempDir = Paths.get(baseDir, "patch", this.identifier); try { Files.createDirectories(tempDir); } catch (IOException e) { - throw new IllegalStateException("Unable to create temp directory"); + throw new DataException("Unable to create temp directory"); } this.workingPath = tempDir; } @@ -114,7 +114,7 @@ public class ArbitraryDataCreatePatch { } } - private void process() throws IOException { + private void process() throws IOException, DataException { ArbitraryDataDiff diff = new ArbitraryDataDiff(this.pathBefore, this.pathAfter, this.previousSignature); this.finalPath = diff.getDiffPath(); diff --git a/src/main/java/org/qortal/arbitrary/ArbitraryDataDiff.java b/src/main/java/org/qortal/arbitrary/ArbitraryDataDiff.java index d7e45d73..6a68e276 100644 --- a/src/main/java/org/qortal/arbitrary/ArbitraryDataDiff.java +++ b/src/main/java/org/qortal/arbitrary/ArbitraryDataDiff.java @@ -6,6 +6,7 @@ import org.json.JSONObject; import org.qortal.arbitrary.metadata.ArbitraryDataMetadataPatch; import org.qortal.arbitrary.patch.UnifiedDiffPatch; import org.qortal.crypto.Crypto; +import org.qortal.repository.DataException; import org.qortal.settings.Settings; import java.io.*; @@ -75,7 +76,7 @@ public class ArbitraryDataDiff { private int totalFileCount; - public ArbitraryDataDiff(Path pathBefore, Path pathAfter, byte[] previousSignature) { + public ArbitraryDataDiff(Path pathBefore, Path pathAfter, byte[] previousSignature) throws DataException { this.pathBefore = pathBefore; this.pathAfter = pathAfter; this.previousSignature = previousSignature; @@ -88,7 +89,7 @@ public class ArbitraryDataDiff { this.createOutputDirectory(); } - public void compute() throws IOException { + public void compute() throws IOException, DataException { try { this.preExecute(); this.hashPreviousState(); @@ -115,19 +116,19 @@ public class ArbitraryDataDiff { this.identifier = UUID.randomUUID().toString(); } - private void createOutputDirectory() { + private void createOutputDirectory() throws DataException { // Use the user-specified temp dir, as it is deterministic, and is more likely to be located on reusable storage hardware String baseDir = Settings.getInstance().getTempDataPath(); Path tempDir = Paths.get(baseDir, "diff", this.identifier); try { Files.createDirectories(tempDir); } catch (IOException e) { - throw new IllegalStateException("Unable to create temp directory"); + throw new DataException("Unable to create temp directory"); } this.diffPath = tempDir; } - private void hashPreviousState() throws IOException { + private void hashPreviousState() throws IOException, DataException { ArbitraryDataDigest digest = new ArbitraryDataDigest(this.pathBefore); digest.compute(); this.previousHash = digest.getHash(); @@ -181,7 +182,12 @@ public class ArbitraryDataDiff { diff.copyFilePathToBaseDir(afterPathAbsolute, diffPathAbsolute, afterPathRelative); } if (wasModified) { - diff.pathModified(beforePathAbsolute, afterPathAbsolute, afterPathRelative, diffPathAbsolute); + try { + diff.pathModified(beforePathAbsolute, afterPathAbsolute, afterPathRelative, diffPathAbsolute); + } catch (DataException e) { + // We can only throw IOExceptions because we are overriding FileVisitor.visitFile() + throw new IOException(e); + } } // Keep a tally of the total number of files to help with decision making @@ -273,19 +279,19 @@ public class ArbitraryDataDiff { } } - private void validate() { + private void validate() throws DataException { if (this.addedPaths.isEmpty() && this.modifiedPaths.isEmpty() && this.removedPaths.isEmpty()) { - throw new IllegalStateException("Current state matches previous state. Nothing to do."); + throw new DataException("Current state matches previous state. Nothing to do."); } } - private void hashCurrentState() throws IOException { + private void hashCurrentState() throws IOException, DataException { ArbitraryDataDigest digest = new ArbitraryDataDigest(this.pathAfter); digest.compute(); this.currentHash = digest.getHash(); } - private void writeMetadata() throws IOException { + private void writeMetadata() throws IOException, DataException { ArbitraryDataMetadataPatch metadata = new ArbitraryDataMetadataPatch(this.diffPath); metadata.setAddedPaths(this.addedPaths); metadata.setModifiedPaths(this.modifiedPaths); @@ -298,7 +304,7 @@ public class ArbitraryDataDiff { private void pathModified(Path beforePathAbsolute, Path afterPathAbsolute, Path afterPathRelative, - Path destinationBasePathAbsolute) throws IOException { + Path destinationBasePathAbsolute) throws IOException, DataException { Path destination = Paths.get(destinationBasePathAbsolute.toString(), afterPathRelative.toString()); long beforeSize = Files.size(beforePathAbsolute); diff --git a/src/main/java/org/qortal/arbitrary/ArbitraryDataDigest.java b/src/main/java/org/qortal/arbitrary/ArbitraryDataDigest.java index 6f762863..9703b231 100644 --- a/src/main/java/org/qortal/arbitrary/ArbitraryDataDigest.java +++ b/src/main/java/org/qortal/arbitrary/ArbitraryDataDigest.java @@ -1,5 +1,6 @@ package org.qortal.arbitrary; +import org.qortal.repository.DataException; import org.qortal.utils.Base58; import java.io.IOException; @@ -21,7 +22,7 @@ public class ArbitraryDataDigest { this.path = path; } - public void compute() throws IOException { + public void compute() throws IOException, DataException { List allPaths = Files.walk(path).filter(Files::isRegularFile).sorted().collect(Collectors.toList()); Path basePathAbsolute = this.path.toAbsolutePath(); @@ -29,7 +30,7 @@ public class ArbitraryDataDigest { try { sha256 = MessageDigest.getInstance("SHA-256"); } catch (NoSuchAlgorithmException e) { - throw new IllegalStateException("SHA-256 hashing algorithm unavailable"); + throw new DataException("SHA-256 hashing algorithm unavailable"); } for (Path path : allPaths) { diff --git a/src/main/java/org/qortal/arbitrary/ArbitraryDataFile.java b/src/main/java/org/qortal/arbitrary/ArbitraryDataFile.java index 38f9d78e..6e82257a 100644 --- a/src/main/java/org/qortal/arbitrary/ArbitraryDataFile.java +++ b/src/main/java/org/qortal/arbitrary/ArbitraryDataFile.java @@ -3,6 +3,7 @@ package org.qortal.arbitrary; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.qortal.crypto.Crypto; +import org.qortal.repository.DataException; import org.qortal.settings.Settings; import org.qortal.transform.transaction.TransactionTransformer; import org.qortal.utils.Base58; @@ -64,14 +65,14 @@ public class ArbitraryDataFile { public ArbitraryDataFile() { } - public ArbitraryDataFile(String hash58) { + public ArbitraryDataFile(String hash58) throws DataException { this.createDataDirectory(); this.filePath = ArbitraryDataFile.getOutputFilePath(hash58, false); this.chunks = new ArrayList<>(); this.hash58 = hash58; } - public ArbitraryDataFile(byte[] fileContent) { + public ArbitraryDataFile(byte[] fileContent) throws DataException { if (fileContent == null) { LOGGER.error("fileContent is null"); return; @@ -89,18 +90,18 @@ public class ArbitraryDataFile { if (!this.hash58.equals(this.digest58())) { LOGGER.error("Hash {} does not match file digest {}", this.hash58, this.digest58()); this.delete(); - throw new IllegalStateException("Data file digest validation failed"); + throw new DataException("Data file digest validation failed"); } } catch (IOException e) { - throw new IllegalStateException("Unable to write data to file"); + throw new DataException("Unable to write data to file"); } } - public static ArbitraryDataFile fromHash58(String hash58) { + public static ArbitraryDataFile fromHash58(String hash58) throws DataException { return new ArbitraryDataFile(hash58); } - public static ArbitraryDataFile fromHash(byte[] hash) { + public static ArbitraryDataFile fromHash(byte[] hash) throws DataException { return ArbitraryDataFile.fromHash58(Base58.encode(hash)); } @@ -126,7 +127,7 @@ public class ArbitraryDataFile { } return arbitraryDataFile; - } catch (IOException e) { + } catch (IOException | DataException e) { LOGGER.error("Couldn't compute digest for ArbitraryDataFile"); } } @@ -150,7 +151,7 @@ public class ArbitraryDataFile { return true; } - private Path copyToDataDirectory(Path sourcePath) { + private Path copyToDataDirectory(Path sourcePath) throws DataException { if (this.hash58 == null || this.filePath == null) { return null; } @@ -160,11 +161,11 @@ public class ArbitraryDataFile { try { return Files.copy(sourcePath, destPath, StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { - throw new IllegalStateException(String.format("Unable to copy file %s to data directory %s", sourcePath, destPath)); + throw new DataException(String.format("Unable to copy file %s to data directory %s", sourcePath, destPath)); } } - public static Path getOutputFilePath(String hash58, boolean createDirectories) { + public static Path getOutputFilePath(String hash58, boolean createDirectories) throws DataException { if (hash58 == null) { return null; } @@ -176,7 +177,7 @@ public class ArbitraryDataFile { try { Files.createDirectories(directory); } catch (IOException e) { - throw new IllegalStateException("Unable to create data subdirectory"); + throw new DataException("Unable to create data subdirectory"); } } return Paths.get(directory.toString(), hash58); @@ -208,7 +209,7 @@ public class ArbitraryDataFile { this.chunks.add(chunk); } - public void addChunkHashes(byte[] chunks) { + public void addChunkHashes(byte[] chunks) throws DataException { if (chunks == null || chunks.length == 0) { return; } @@ -234,7 +235,7 @@ public class ArbitraryDataFile { return hashes; } - public int split(int chunkSize) { + public int split(int chunkSize) throws DataException { try { File file = this.getFile(); @@ -256,14 +257,14 @@ public class ArbitraryDataFile { if (validationResult == ValidationResult.OK) { this.chunks.add(chunk); } else { - throw new IllegalStateException(String.format("Chunk %s is invalid", chunk)); + throw new DataException(String.format("Chunk %s is invalid", chunk)); } } } } } } catch (Exception e) { - throw new IllegalStateException("Unable to split file into chunks"); + throw new DataException("Unable to split file into chunks"); } return this.chunks.size(); @@ -308,7 +309,7 @@ public class ArbitraryDataFile { return true; } catch (FileNotFoundException e) { return false; - } catch (IOException e) { + } catch (IOException | DataException e) { return false; } } @@ -416,7 +417,7 @@ public class ArbitraryDataFile { return false; } - public boolean allChunksExist(byte[] chunks) { + public boolean allChunksExist(byte[] chunks) throws DataException { if (chunks == null) { return true; } @@ -432,7 +433,7 @@ public class ArbitraryDataFile { return true; } - public boolean anyChunksExist(byte[] chunks) { + public boolean anyChunksExist(byte[] chunks) throws DataException { if (chunks == null) { return false; } @@ -473,7 +474,7 @@ public class ArbitraryDataFile { return this.chunks; } - public byte[] chunkHashes() { + public byte[] chunkHashes() throws DataException { if (this.chunks != null && this.chunks.size() > 0) { // Return null if we only have one chunk, with the same hash as the parent if (Arrays.equals(this.digest(), this.chunks.get(0).digest())) { @@ -486,7 +487,7 @@ public class ArbitraryDataFile { byte[] chunkHash = chunk.digest(); if (chunkHash.length != 32) { LOGGER.info("Invalid chunk hash length: {}", chunkHash.length); - throw new IllegalStateException("Invalid chunk hash length"); + throw new DataException("Invalid chunk hash length"); } outputStream.write(chunk.digest()); } diff --git a/src/main/java/org/qortal/arbitrary/ArbitraryDataFileChunk.java b/src/main/java/org/qortal/arbitrary/ArbitraryDataFileChunk.java index 1e35a628..b5adcace 100644 --- a/src/main/java/org/qortal/arbitrary/ArbitraryDataFileChunk.java +++ b/src/main/java/org/qortal/arbitrary/ArbitraryDataFileChunk.java @@ -2,6 +2,7 @@ package org.qortal.arbitrary; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.qortal.repository.DataException; import org.qortal.utils.Base58; import java.io.IOException; @@ -12,19 +13,19 @@ public class ArbitraryDataFileChunk extends ArbitraryDataFile { private static final Logger LOGGER = LogManager.getLogger(ArbitraryDataFileChunk.class); - public ArbitraryDataFileChunk(String hash58) { + public ArbitraryDataFileChunk(String hash58) throws DataException { super(hash58); } - public ArbitraryDataFileChunk(byte[] fileContent) { + public ArbitraryDataFileChunk(byte[] fileContent) throws DataException { super(fileContent); } - public static ArbitraryDataFileChunk fromHash58(String hash58) { + public static ArbitraryDataFileChunk fromHash58(String hash58) throws DataException { return new ArbitraryDataFileChunk(hash58); } - public static ArbitraryDataFileChunk fromHash(byte[] hash) { + public static ArbitraryDataFileChunk fromHash(byte[] hash) throws DataException { return ArbitraryDataFileChunk.fromHash58(Base58.encode(hash)); } diff --git a/src/main/java/org/qortal/arbitrary/ArbitraryDataMerge.java b/src/main/java/org/qortal/arbitrary/ArbitraryDataMerge.java index d61bd55d..eca1b8e4 100644 --- a/src/main/java/org/qortal/arbitrary/ArbitraryDataMerge.java +++ b/src/main/java/org/qortal/arbitrary/ArbitraryDataMerge.java @@ -44,7 +44,7 @@ public class ArbitraryDataMerge { } } - private void preExecute() { + private void preExecute() throws DataException { this.createRandomIdentifier(); this.createOutputDirectory(); } @@ -57,14 +57,14 @@ public class ArbitraryDataMerge { this.identifier = UUID.randomUUID().toString(); } - private void createOutputDirectory() { + private void createOutputDirectory() throws DataException { // Use the user-specified temp dir, as it is deterministic, and is more likely to be located on reusable storage hardware String baseDir = Settings.getInstance().getTempDataPath(); Path tempDir = Paths.get(baseDir, "merge", this.identifier); try { Files.createDirectories(tempDir); } catch (IOException e) { - throw new IllegalStateException("Unable to create temp directory"); + throw new DataException("Unable to create temp directory"); } this.mergePath = tempDir; } @@ -73,7 +73,7 @@ public class ArbitraryDataMerge { ArbitraryDataMerge.copyDirPathToBaseDir(this.pathBefore, this.mergePath, Paths.get("")); } - private void loadMetadata() throws IOException { + private void loadMetadata() throws IOException, DataException { this.metadata = new ArbitraryDataMetadataPatch(this.pathAfter); this.metadata.read(); } diff --git a/src/main/java/org/qortal/arbitrary/ArbitraryDataReader.java b/src/main/java/org/qortal/arbitrary/ArbitraryDataReader.java index dd50f067..5c487800 100644 --- a/src/main/java/org/qortal/arbitrary/ArbitraryDataReader.java +++ b/src/main/java/org/qortal/arbitrary/ArbitraryDataReader.java @@ -131,12 +131,11 @@ public class ArbitraryDataReader { * If no exception is thrown, you can then use getFilePath() to access the data immediately after returning * * @param overwrite - set to true to force rebuild an existing cache - * @throws IllegalStateException * @throws IOException * @throws DataException * @throws MissingDataException */ - public void loadSynchronously(boolean overwrite) throws IllegalStateException, IOException, DataException, MissingDataException { + public void loadSynchronously(boolean overwrite) throws DataException, IOException, MissingDataException { try { ArbitraryDataCache cache = new ArbitraryDataCache(this.uncompressedPath, overwrite, this.resourceId, this.resourceIdType, this.service, this.identifier); @@ -158,7 +157,7 @@ public class ArbitraryDataReader { } } - private void preExecute() { + private void preExecute() throws DataException { ArbitraryDataBuildManager.getInstance().setBuildInProgress(true); this.createWorkingDirectory(); this.createUncompressedDirectory(); @@ -168,15 +167,15 @@ public class ArbitraryDataReader { ArbitraryDataBuildManager.getInstance().setBuildInProgress(false); } - private void createWorkingDirectory() { + private void createWorkingDirectory() throws DataException { try { Files.createDirectories(this.workingPath); } catch (IOException e) { - throw new IllegalStateException("Unable to create temp directory"); + throw new DataException("Unable to create temp directory"); } } - private void createUncompressedDirectory() { + private void createUncompressedDirectory() throws DataException { try { // Create parent directory Files.createDirectories(this.uncompressedPath.getParent()); @@ -184,7 +183,7 @@ public class ArbitraryDataReader { FileUtils.deleteDirectory(this.uncompressedPath.toFile()); } catch (IOException e) { - throw new IllegalStateException("Unable to create uncompressed directory"); + throw new DataException("Unable to create uncompressed directory"); } } @@ -225,7 +224,7 @@ public class ArbitraryDataReader { } } - private void fetch() throws IllegalStateException, IOException, DataException, MissingDataException { + private void fetch() throws DataException, IOException, MissingDataException { switch (resourceIdType) { case FILE_HASH: @@ -245,18 +244,18 @@ public class ArbitraryDataReader { break; default: - throw new IllegalStateException(String.format("Unknown resource ID type specified: %s", resourceIdType.toString())); + throw new DataException(String.format("Unknown resource ID type specified: %s", resourceIdType.toString())); } } - private void fetchFromFileHash() { + private void fetchFromFileHash() throws DataException { // Load data file directly from the hash ArbitraryDataFile arbitraryDataFile = ArbitraryDataFile.fromHash58(resourceId); // Set filePath to the location of the ArbitraryDataFile this.filePath = arbitraryDataFile.getFilePath(); } - private void fetchFromName() throws IllegalStateException, IOException, DataException, MissingDataException { + private void fetchFromName() throws DataException, IOException, MissingDataException { try { // Build the existing state using past transactions @@ -264,7 +263,7 @@ public class ArbitraryDataReader { builder.build(); Path builtPath = builder.getFinalPath(); if (builtPath == null) { - throw new IllegalStateException("Unable to build path"); + throw new DataException("Unable to build path"); } // Update stats @@ -282,7 +281,7 @@ public class ArbitraryDataReader { } } - private void fetchFromSignature() throws IllegalStateException, IOException, DataException, MissingDataException { + private void fetchFromSignature() throws DataException, IOException, MissingDataException { // Load the full transaction data from the database so we can access the file hashes ArbitraryTransactionData transactionData; @@ -290,15 +289,15 @@ public class ArbitraryDataReader { transactionData = (ArbitraryTransactionData) repository.getTransactionRepository().fromSignature(Base58.decode(resourceId)); } if (transactionData == null) { - throw new IllegalStateException(String.format("Transaction data not found for signature %s", this.resourceId)); + throw new DataException(String.format("Transaction data not found for signature %s", this.resourceId)); } this.fetchFromTransactionData(transactionData); } - private void fetchFromTransactionData(ArbitraryTransactionData transactionData) throws IllegalStateException, IOException, MissingDataException { + private void fetchFromTransactionData(ArbitraryTransactionData transactionData) throws DataException, IOException, MissingDataException { if (transactionData == null) { - throw new IllegalStateException(String.format("Transaction data not found for signature %s", this.resourceId)); + throw new DataException(String.format("Transaction data not found for signature %s", this.resourceId)); } // Load hashes @@ -316,7 +315,7 @@ public class ArbitraryDataReader { if (!arbitraryDataFile.exists()) { if (!arbitraryDataFile.allChunksExist(chunkHashes) || chunkHashes == null) { if (ArbitraryDataStorageManager.getInstance().isNameInBlacklist(transactionData.getName())) { - throw new IllegalStateException( + throw new DataException( String.format("Unable to request missing data for file %s due to blacklist", arbitraryDataFile)); } else { @@ -352,13 +351,13 @@ public class ArbitraryDataReader { } // Ensure the complete hash matches the joined chunks if (!Arrays.equals(arbitraryDataFile.digest(), digest)) { - throw new IllegalStateException("Unable to validate complete file hash"); + throw new DataException("Unable to validate complete file hash"); } // Set filePath to the location of the ArbitraryDataFile this.filePath = arbitraryDataFile.getFilePath(); } - private void decrypt() { + private void decrypt() throws DataException { // Decrypt if we have the secret key. byte[] secret = this.secret58 != null ? Base58.decode(this.secret58) : null; if (secret != null && secret.length == Transformer.AES256_LENGTH) { @@ -373,7 +372,7 @@ public class ArbitraryDataReader { } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException | IOException | InvalidKeyException e) { - throw new IllegalStateException(String.format("Unable to decrypt file at path %s: %s", this.filePath, e.getMessage())); + throw new DataException(String.format("Unable to decrypt file at path %s: %s", this.filePath, e.getMessage())); } } else { // Assume it is unencrypted. This will be the case when we have built a custom path by combining @@ -381,9 +380,9 @@ public class ArbitraryDataReader { } } - private void uncompress() throws IOException { + private void uncompress() throws IOException, DataException { if (this.filePath == null || !Files.exists(this.filePath)) { - throw new IllegalStateException("Can't uncompress non-existent file path"); + throw new DataException("Can't uncompress non-existent file path"); } File file = new File(this.filePath.toString()); if (file.isDirectory()) { @@ -407,10 +406,10 @@ public class ArbitraryDataReader { this.filePath.toFile().renameTo(finalPath.toFile()); } else { - throw new IllegalStateException(String.format("Unrecognized compression type: %s", transactionData.getCompression())); + throw new DataException(String.format("Unrecognized compression type: %s", transactionData.getCompression())); } } catch (IOException e) { - throw new IllegalStateException(String.format("Unable to unzip file: %s", e.getMessage())); + throw new DataException(String.format("Unable to unzip file: %s", e.getMessage())); } // If unzipped data was a file not a directory, move it into a data/ directory so that the .qortal @@ -449,12 +448,12 @@ public class ArbitraryDataReader { } - private void moveFilePathToFinalDestination() throws IOException { + private void moveFilePathToFinalDestination() throws IOException, DataException { if (this.filePath.compareTo(this.uncompressedPath) != 0) { File source = new File(this.filePath.toString()); File dest = new File(this.uncompressedPath.toString()); if (!source.exists()) { - throw new IllegalStateException("Source directory doesn't exist"); + throw new DataException("Source directory doesn't exist"); } // Ensure destination directory doesn't exist FileUtils.deleteDirectory(dest); diff --git a/src/main/java/org/qortal/arbitrary/ArbitraryDataResource.java b/src/main/java/org/qortal/arbitrary/ArbitraryDataResource.java index 0a68b0fe..afd93b9b 100644 --- a/src/main/java/org/qortal/arbitrary/ArbitraryDataResource.java +++ b/src/main/java/org/qortal/arbitrary/ArbitraryDataResource.java @@ -65,7 +65,7 @@ public class ArbitraryDataResource { } catch (MissingDataException e) { return new ArbitraryResourceSummary(ArbitraryResourceStatus.DOWNLOADING); - } catch (IOException | DataException | IllegalStateException e) { + } catch (IOException | DataException e) { // Ignore for now } diff --git a/src/main/java/org/qortal/arbitrary/ArbitraryDataTransactionBuilder.java b/src/main/java/org/qortal/arbitrary/ArbitraryDataTransactionBuilder.java index ff1d7374..3b8a93dc 100644 --- a/src/main/java/org/qortal/arbitrary/ArbitraryDataTransactionBuilder.java +++ b/src/main/java/org/qortal/arbitrary/ArbitraryDataTransactionBuilder.java @@ -122,7 +122,7 @@ public class ArbitraryDataTransactionBuilder { // State is appropriate for a PATCH transaction return Method.PATCH; } - catch (IOException | DataException | MissingDataException | IllegalStateException e) { + catch (IOException | DataException | MissingDataException 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()); diff --git a/src/main/java/org/qortal/arbitrary/ArbitraryDataWriter.java b/src/main/java/org/qortal/arbitrary/ArbitraryDataWriter.java index 8c48f57a..d5b55f2b 100644 --- a/src/main/java/org/qortal/arbitrary/ArbitraryDataWriter.java +++ b/src/main/java/org/qortal/arbitrary/ArbitraryDataWriter.java @@ -56,7 +56,7 @@ public class ArbitraryDataWriter { this.compression = compression; } - public void save() throws IllegalStateException, IOException, DataException, InterruptedException, MissingDataException { + public void save() throws DataException, IOException, DataException, InterruptedException, MissingDataException { try { this.preExecute(); this.validateService(); @@ -71,11 +71,11 @@ public class ArbitraryDataWriter { } } - private void preExecute() { + private void preExecute() throws DataException { // Enforce compression when uploading a directory File file = new File(this.filePath.toString()); if (file.isDirectory() && compression == Compression.NONE) { - throw new IllegalStateException("Unable to upload a directory without compression"); + throw new DataException("Unable to upload a directory without compression"); } // Create temporary working directory @@ -86,7 +86,7 @@ public class ArbitraryDataWriter { this.cleanupFilesystem(); } - private void createWorkingDirectory() { + private void createWorkingDirectory() throws DataException { // Use the user-specified temp dir, as it is deterministic, and is more likely to be located on reusable storage hardware String baseDir = Settings.getInstance().getTempDataPath(); String identifier = Base58.encode(Crypto.digest(this.filePath.toString().getBytes())); @@ -94,7 +94,7 @@ public class ArbitraryDataWriter { try { Files.createDirectories(tempDir); } catch (IOException e) { - throw new IllegalStateException("Unable to create temp directory"); + throw new DataException("Unable to create temp directory"); } this.workingPath = tempDir; } @@ -124,7 +124,7 @@ public class ArbitraryDataWriter { break; default: - throw new IllegalStateException(String.format("Unknown method specified: %s", method.toString())); + throw new DataException(String.format("Unknown method specified: %s", method.toString())); } } @@ -154,29 +154,29 @@ public class ArbitraryDataWriter { this.validatePatch(); } - private void validatePatch() { + private void validatePatch() throws DataException { if (this.filePath == null) { - throw new IllegalStateException("Null path after creating patch"); + throw new DataException("Null path after creating patch"); } File qortalMetadataDirectoryFile = Paths.get(this.filePath.toString(), ".qortal").toFile(); if (!qortalMetadataDirectoryFile.exists()) { - throw new IllegalStateException("Qortal metadata folder doesn't exist in patch"); + throw new DataException("Qortal metadata folder doesn't exist in patch"); } if (!qortalMetadataDirectoryFile.isDirectory()) { - throw new IllegalStateException("Qortal metadata folder isn't a directory"); + throw new DataException("Qortal metadata folder isn't a directory"); } File qortalPatchMetadataFile = Paths.get(this.filePath.toString(), ".qortal", "patch").toFile(); if (!qortalPatchMetadataFile.exists()) { - throw new IllegalStateException("Qortal patch metadata file doesn't exist in patch"); + throw new DataException("Qortal patch metadata file doesn't exist in patch"); } if (!qortalPatchMetadataFile.isFile()) { - throw new IllegalStateException("Qortal patch metadata file isn't a file"); + throw new DataException("Qortal patch metadata file isn't a file"); } } - private void compress() throws InterruptedException { + private void compress() throws InterruptedException, DataException { // Compress the data if requested if (this.compression != Compression.NONE) { this.compressedPath = Paths.get(this.workingPath.toString() + File.separator + "data.zip"); @@ -187,7 +187,7 @@ public class ArbitraryDataWriter { ZipUtils.zip(this.filePath.toString(), this.compressedPath.toString(), "data"); } else { - throw new IllegalStateException(String.format("Unknown compression type specified: %s", compression.toString())); + throw new DataException(String.format("Unknown compression type specified: %s", compression.toString())); } // FUTURE: other compression types @@ -199,13 +199,13 @@ public class ArbitraryDataWriter { // Replace filePath pointer with the zipped file path this.filePath = this.compressedPath; - } catch (IOException e) { - throw new IllegalStateException("Unable to zip directory", e); + } catch (IOException | DataException e) { + throw new DataException("Unable to zip directory", e); } } } - private void encrypt() { + private void encrypt() throws DataException { this.encryptedPath = Paths.get(this.workingPath.toString() + File.separator + "data.zip.encrypted"); try { // Encrypt the file with AES @@ -222,11 +222,11 @@ public class ArbitraryDataWriter { } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException | IOException | InvalidKeyException e) { - throw new IllegalStateException(String.format("Unable to encrypt file %s: %s", this.filePath, e.getMessage())); + throw new DataException(String.format("Unable to encrypt file %s: %s", this.filePath, e.getMessage())); } } - private void validate() throws IOException { + private void validate() throws IOException, DataException { if (this.arbitraryDataFile == null) { throw new IOException("No file available when validating"); } @@ -235,7 +235,7 @@ public class ArbitraryDataWriter { // Validate the file ValidationResult validationResult = this.arbitraryDataFile.isValid(); if (validationResult != ValidationResult.OK) { - throw new IllegalStateException(String.format("File %s failed validation: %s", this.arbitraryDataFile, validationResult)); + throw new DataException(String.format("File %s failed validation: %s", this.arbitraryDataFile, validationResult)); } LOGGER.info("Whole file hash is valid: {}", this.arbitraryDataFile.digest58()); @@ -243,14 +243,14 @@ public class ArbitraryDataWriter { for (ArbitraryDataFileChunk chunk : this.arbitraryDataFile.getChunks()) { validationResult = chunk.isValid(); if (validationResult != ValidationResult.OK) { - throw new IllegalStateException(String.format("Chunk %s failed validation: %s", chunk, validationResult)); + throw new DataException(String.format("Chunk %s failed validation: %s", chunk, validationResult)); } } LOGGER.info("Chunk hashes are valid"); } - private void split() throws IOException { + private void split() throws IOException, DataException { this.arbitraryDataFile = ArbitraryDataFile.fromPath(this.filePath); if (this.arbitraryDataFile == null) { throw new IOException("No file available when trying to split"); @@ -261,7 +261,7 @@ public class ArbitraryDataWriter { LOGGER.info(String.format("Successfully split into %d chunk%s", chunkCount, (chunkCount == 1 ? "" : "s"))); } else { - throw new IllegalStateException("Unable to split file into chunks"); + throw new DataException("Unable to split file into chunks"); } } diff --git a/src/main/java/org/qortal/arbitrary/metadata/ArbitraryDataMetadata.java b/src/main/java/org/qortal/arbitrary/metadata/ArbitraryDataMetadata.java index 7dcfd1b5..1f6b1657 100644 --- a/src/main/java/org/qortal/arbitrary/metadata/ArbitraryDataMetadata.java +++ b/src/main/java/org/qortal/arbitrary/metadata/ArbitraryDataMetadata.java @@ -2,6 +2,7 @@ package org.qortal.arbitrary.metadata; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.qortal.repository.DataException; import java.io.BufferedWriter; import java.io.File; @@ -30,7 +31,7 @@ public class ArbitraryDataMetadata { return null; } - protected void readJson() { + protected void readJson() throws DataException { // To be overridden } @@ -39,12 +40,12 @@ public class ArbitraryDataMetadata { } - public void read() throws IOException { + public void read() throws IOException, DataException { this.loadJson(); this.readJson(); } - public void write() throws IOException { + public void write() throws IOException, DataException { this.buildJson(); this.createQortalDirectory(); this.writeToQortalPath(); @@ -61,11 +62,11 @@ public class ArbitraryDataMetadata { this.jsonString = new String(Files.readAllBytes(path)); } - protected void createQortalDirectory() { + protected void createQortalDirectory() throws DataException { try { Files.createDirectories(this.qortalDirectoryPath); } catch (IOException e) { - throw new IllegalStateException("Unable to create .qortal directory"); + throw new DataException("Unable to create .qortal directory"); } } diff --git a/src/main/java/org/qortal/arbitrary/metadata/ArbitraryDataMetadataCache.java b/src/main/java/org/qortal/arbitrary/metadata/ArbitraryDataMetadataCache.java index bedbddee..afd3aea5 100644 --- a/src/main/java/org/qortal/arbitrary/metadata/ArbitraryDataMetadataCache.java +++ b/src/main/java/org/qortal/arbitrary/metadata/ArbitraryDataMetadataCache.java @@ -1,6 +1,7 @@ package org.qortal.arbitrary.metadata; import org.json.JSONObject; +import org.qortal.repository.DataException; import org.qortal.utils.Base58; import java.nio.file.Path; @@ -21,9 +22,9 @@ public class ArbitraryDataMetadataCache extends ArbitraryDataMetadata { } @Override - protected void readJson() { + protected void readJson() throws DataException { if (this.jsonString == null) { - throw new IllegalStateException("Patch JSON string is null"); + throw new DataException("Patch JSON string is null"); } JSONObject cache = new JSONObject(this.jsonString); diff --git a/src/main/java/org/qortal/arbitrary/metadata/ArbitraryDataMetadataPatch.java b/src/main/java/org/qortal/arbitrary/metadata/ArbitraryDataMetadataPatch.java index 888503ab..9555bdfa 100644 --- a/src/main/java/org/qortal/arbitrary/metadata/ArbitraryDataMetadataPatch.java +++ b/src/main/java/org/qortal/arbitrary/metadata/ArbitraryDataMetadataPatch.java @@ -5,6 +5,7 @@ import org.apache.logging.log4j.Logger; import org.json.JSONArray; import org.json.JSONObject; import org.qortal.arbitrary.ArbitraryDataDiff.*; +import org.qortal.repository.DataException; import org.qortal.utils.Base58; import java.lang.reflect.Field; @@ -39,9 +40,9 @@ public class ArbitraryDataMetadataPatch extends ArbitraryDataMetadata { } @Override - protected void readJson() { + protected void readJson() throws DataException { if (this.jsonString == null) { - throw new IllegalStateException("Patch JSON string is null"); + throw new DataException("Patch JSON string is null"); } JSONObject patch = new JSONObject(this.jsonString); diff --git a/src/main/java/org/qortal/arbitrary/patch/UnifiedDiffPatch.java b/src/main/java/org/qortal/arbitrary/patch/UnifiedDiffPatch.java index 78705760..d35d9f4a 100644 --- a/src/main/java/org/qortal/arbitrary/patch/UnifiedDiffPatch.java +++ b/src/main/java/org/qortal/arbitrary/patch/UnifiedDiffPatch.java @@ -8,6 +8,7 @@ import org.apache.commons.io.FileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.qortal.crypto.Crypto; +import org.qortal.repository.DataException; import org.qortal.settings.Settings; import java.io.BufferedWriter; @@ -96,7 +97,7 @@ public class UnifiedDiffPatch { * * @return true if valid, false if invalid */ - public boolean isValid() { + public boolean isValid() throws DataException { this.createRandomIdentifier(); this.createTempValidationDirectory(); @@ -147,7 +148,7 @@ public class UnifiedDiffPatch { * @param pathSuffix - a file path to append to the base paths, or null if the base paths are already files * @throws IOException */ - public void apply(Path pathSuffix) throws IOException { + public void apply(Path pathSuffix) throws IOException, DataException { Path originalPath = this.before; Path patchPath = this.after; Path mergePath = this.destination; @@ -160,7 +161,7 @@ public class UnifiedDiffPatch { } if (!patchPath.toFile().exists()) { - throw new IllegalStateException("Patch file doesn't exist, but its path was included in modifiedPaths"); + throw new DataException("Patch file doesn't exist, but its path was included in modifiedPaths"); } // Delete an existing file, as we are starting from a duplicate of pathBefore @@ -190,7 +191,7 @@ public class UnifiedDiffPatch { writer.close(); } catch (PatchFailedException e) { - throw new IllegalStateException(String.format("Failed to apply patch for path %s: %s", pathSuffix, e.getMessage())); + throw new DataException(String.format("Failed to apply patch for path %s: %s", pathSuffix, e.getMessage())); } } @@ -198,14 +199,14 @@ public class UnifiedDiffPatch { this.identifier = UUID.randomUUID().toString(); } - private void createTempValidationDirectory() { + private void createTempValidationDirectory() throws DataException { // Use the user-specified temp dir, as it is deterministic, and is more likely to be located on reusable storage hardware String baseDir = Settings.getInstance().getTempDataPath(); Path tempDir = Paths.get(baseDir, "diff", "validate"); try { Files.createDirectories(tempDir); } catch (IOException e) { - throw new IllegalStateException("Unable to create temp directory"); + throw new DataException("Unable to create temp directory"); } this.validationPath = tempDir; } diff --git a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataManager.java b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataManager.java index 066edc87..2b0e9936 100644 --- a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataManager.java +++ b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataManager.java @@ -779,33 +779,39 @@ public class ArbitraryDataManager extends Thread { byte[] hash = getArbitraryDataFileMessage.getHash(); Controller.getInstance().stats.getArbitraryDataFileMessageStats.requests.incrementAndGet(); - ArbitraryDataFile arbitraryDataFile = ArbitraryDataFile.fromHash(hash); - if (arbitraryDataFile.exists()) { - ArbitraryDataFileMessage arbitraryDataFileMessage = new ArbitraryDataFileMessage(arbitraryDataFile); - arbitraryDataFileMessage.setId(message.getId()); - if (!peer.sendMessage(arbitraryDataFileMessage)) { - LOGGER.info("Couldn't sent file"); - peer.disconnect("failed to send file"); + try { + ArbitraryDataFile arbitraryDataFile = ArbitraryDataFile.fromHash(hash); + + if (arbitraryDataFile.exists()) { + ArbitraryDataFileMessage arbitraryDataFileMessage = new ArbitraryDataFileMessage(arbitraryDataFile); + arbitraryDataFileMessage.setId(message.getId()); + if (!peer.sendMessage(arbitraryDataFileMessage)) { + LOGGER.info("Couldn't sent file"); + peer.disconnect("failed to send file"); + } + LOGGER.info("Sent file {}", arbitraryDataFile); + } + else { + + // We don't have this file + Controller.getInstance().stats.getArbitraryDataFileMessageStats.unknownFiles.getAndIncrement(); + + // Send valid, yet unexpected message type in response, so peer's synchronizer doesn't have to wait for timeout + LOGGER.debug(() -> String.format("Sending 'file unknown' response to peer %s for GET_FILE request for unknown file %s", peer, arbitraryDataFile)); + + // We'll send empty block summaries message as it's very short + // TODO: use a different message type here + Message fileUnknownMessage = new BlockSummariesMessage(Collections.emptyList()); + fileUnknownMessage.setId(message.getId()); + if (!peer.sendMessage(fileUnknownMessage)) { + LOGGER.info("Couldn't sent file-unknown response"); + peer.disconnect("failed to send file-unknown response"); + } + LOGGER.info("Sent file-unknown response for file {}", arbitraryDataFile); } - LOGGER.info("Sent file {}", arbitraryDataFile); } - else { - - // We don't have this file - Controller.getInstance().stats.getArbitraryDataFileMessageStats.unknownFiles.getAndIncrement(); - - // Send valid, yet unexpected message type in response, so peer's synchronizer doesn't have to wait for timeout - LOGGER.debug(() -> String.format("Sending 'file unknown' response to peer %s for GET_FILE request for unknown file %s", peer, arbitraryDataFile)); - - // We'll send empty block summaries message as it's very short - // TODO: use a different message type here - Message fileUnknownMessage = new BlockSummariesMessage(Collections.emptyList()); - fileUnknownMessage.setId(message.getId()); - if (!peer.sendMessage(fileUnknownMessage)) { - LOGGER.info("Couldn't sent file-unknown response"); - peer.disconnect("failed to send file-unknown response"); - } - LOGGER.info("Sent file-unknown response for file {}", arbitraryDataFile); + catch (DataException e) { + LOGGER.info("Unable to handle request for arbitrary data file: {}", Base58.encode(hash)); } } diff --git a/src/main/java/org/qortal/network/message/ArbitraryDataFileMessage.java b/src/main/java/org/qortal/network/message/ArbitraryDataFileMessage.java index 289dcb49..b1fc21eb 100644 --- a/src/main/java/org/qortal/network/message/ArbitraryDataFileMessage.java +++ b/src/main/java/org/qortal/network/message/ArbitraryDataFileMessage.java @@ -2,6 +2,7 @@ package org.qortal.network.message; import com.google.common.primitives.Ints; import org.qortal.arbitrary.ArbitraryDataFile; +import org.qortal.repository.DataException; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -36,9 +37,14 @@ public class ArbitraryDataFileMessage extends Message { byte[] data = new byte[dataLength]; byteBuffer.get(data); - ArbitraryDataFile arbitraryDataFile = new ArbitraryDataFile(data); - return new ArbitraryDataFileMessage(id, arbitraryDataFile); + try { + ArbitraryDataFile arbitraryDataFile = new ArbitraryDataFile(data); + return new ArbitraryDataFileMessage(id, arbitraryDataFile); + } + catch (DataException e) { + return null; + } } @Override diff --git a/src/main/java/org/qortal/utils/ArbitraryTransactionUtils.java b/src/main/java/org/qortal/utils/ArbitraryTransactionUtils.java index 0709a595..f9cb5f83 100644 --- a/src/main/java/org/qortal/utils/ArbitraryTransactionUtils.java +++ b/src/main/java/org/qortal/utils/ArbitraryTransactionUtils.java @@ -77,7 +77,7 @@ public class ArbitraryTransactionUtils { return hasNewerPut; } - public static boolean completeFileExists(ArbitraryTransactionData transactionData) { + public static boolean completeFileExists(ArbitraryTransactionData transactionData) throws DataException { if (transactionData == null) { return false; } @@ -90,7 +90,7 @@ public class ArbitraryTransactionUtils { } - public static boolean allChunksExist(ArbitraryTransactionData transactionData) { + public static boolean allChunksExist(ArbitraryTransactionData transactionData) throws DataException { if (transactionData == null) { return false; } @@ -111,7 +111,7 @@ public class ArbitraryTransactionUtils { return arbitraryDataFile.allChunksExist(chunkHashes); } - public static boolean anyChunksExist(ArbitraryTransactionData transactionData) { + public static boolean anyChunksExist(ArbitraryTransactionData transactionData) throws DataException { if (transactionData == null) { return false; } @@ -132,7 +132,7 @@ public class ArbitraryTransactionUtils { return arbitraryDataFile.anyChunksExist(chunkHashes); } - public static int ourChunkCount(ArbitraryTransactionData transactionData) { + public static int ourChunkCount(ArbitraryTransactionData transactionData) throws DataException { if (transactionData == null) { return 0; } @@ -174,7 +174,7 @@ public class ArbitraryTransactionUtils { return true; } - public static boolean isFileHashRecent(byte[] hash, long now, long cleanupAfter) { + public static boolean isFileHashRecent(byte[] hash, long now, long cleanupAfter) throws DataException { ArbitraryDataFile arbitraryDataFile = ArbitraryDataFile.fromHash(hash); if (arbitraryDataFile == null || !arbitraryDataFile.exists()) { // No hash, or file doesn't exist, so it's not recent @@ -185,7 +185,7 @@ public class ArbitraryTransactionUtils { return ArbitraryTransactionUtils.isFileRecent(filePath, now, cleanupAfter); } - public static void deleteCompleteFile(ArbitraryTransactionData arbitraryTransactionData, long now, long cleanupAfter) { + public static void deleteCompleteFile(ArbitraryTransactionData arbitraryTransactionData, long now, long cleanupAfter) throws DataException { byte[] completeHash = arbitraryTransactionData.getData(); byte[] chunkHashes = arbitraryTransactionData.getChunkHashes(); @@ -200,7 +200,7 @@ public class ArbitraryTransactionUtils { } } - public static void deleteCompleteFileAndChunks(ArbitraryTransactionData arbitraryTransactionData) { + public static void deleteCompleteFileAndChunks(ArbitraryTransactionData arbitraryTransactionData) throws DataException { byte[] completeHash = arbitraryTransactionData.getData(); byte[] chunkHashes = arbitraryTransactionData.getChunkHashes(); @@ -209,7 +209,7 @@ public class ArbitraryTransactionUtils { arbitraryDataFile.deleteAll(); } - public static void convertFileToChunks(ArbitraryTransactionData arbitraryTransactionData, long now, long cleanupAfter) { + public static void convertFileToChunks(ArbitraryTransactionData arbitraryTransactionData, long now, long cleanupAfter) throws DataException { byte[] completeHash = arbitraryTransactionData.getData(); byte[] chunkHashes = arbitraryTransactionData.getChunkHashes(); diff --git a/src/test/java/org/qortal/test/arbitrary/ArbitraryDataDigestTests.java b/src/test/java/org/qortal/test/arbitrary/ArbitraryDataDigestTests.java index 738a5221..1c8afc2e 100644 --- a/src/test/java/org/qortal/test/arbitrary/ArbitraryDataDigestTests.java +++ b/src/test/java/org/qortal/test/arbitrary/ArbitraryDataDigestTests.java @@ -22,7 +22,7 @@ public class ArbitraryDataDigestTests extends Common { } @Test - public void testDirectoryDigest() throws IOException { + public void testDirectoryDigest() throws IOException, DataException { Path dataPath = Paths.get("src/test/resources/arbitrary/demo1"); String expectedHash58 = "DKyMuonWKoneJqiVHgw26Vk1ytrZG9PGsE9xfBg3GKDp"; diff --git a/src/test/java/org/qortal/test/arbitrary/ArbitraryDataFileTests.java b/src/test/java/org/qortal/test/arbitrary/ArbitraryDataFileTests.java index bb016920..2bbbc776 100644 --- a/src/test/java/org/qortal/test/arbitrary/ArbitraryDataFileTests.java +++ b/src/test/java/org/qortal/test/arbitrary/ArbitraryDataFileTests.java @@ -18,7 +18,7 @@ public class ArbitraryDataFileTests extends Common { } @Test - public void testSplitAndJoin() { + public void testSplitAndJoin() throws DataException { String dummyDataString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; ArbitraryDataFile arbitraryDataFile = new ArbitraryDataFile(dummyDataString.getBytes()); assertTrue(arbitraryDataFile.exists()); @@ -45,7 +45,7 @@ public class ArbitraryDataFileTests extends Common { } @Test - public void testSplitAndJoinWithLargeFiles() { + public void testSplitAndJoinWithLargeFiles() throws DataException { int fileSize = (int) (5.5f * 1024 * 1024); // 5.5MiB byte[] randomData = new byte[fileSize]; new Random().nextBytes(randomData); // No need for SecureRandom here diff --git a/src/test/java/org/qortal/test/arbitrary/ArbitraryDataMergeTests.java b/src/test/java/org/qortal/test/arbitrary/ArbitraryDataMergeTests.java index 5bfe298d..5be15ec1 100644 --- a/src/test/java/org/qortal/test/arbitrary/ArbitraryDataMergeTests.java +++ b/src/test/java/org/qortal/test/arbitrary/ArbitraryDataMergeTests.java @@ -148,7 +148,7 @@ public class ArbitraryDataMergeTests extends Common { patch.create(); fail("Creating patch should fail due to matching states"); - } catch (IllegalStateException expectedException) { + } catch (DataException expectedException) { assertEquals("Current state matches previous state. Nothing to do.", expectedException.getMessage()); } diff --git a/src/test/java/org/qortal/test/arbitrary/ArbitraryDataTests.java b/src/test/java/org/qortal/test/arbitrary/ArbitraryDataTests.java index 38bb7cc4..797ad5ae 100644 --- a/src/test/java/org/qortal/test/arbitrary/ArbitraryDataTests.java +++ b/src/test/java/org/qortal/test/arbitrary/ArbitraryDataTests.java @@ -234,7 +234,7 @@ public class ArbitraryDataTests extends Common { arbitraryDataReader1a.loadSynchronously(true); fail("Loading data with null identifier should fail due to nonexistent PUT transaction"); - } catch (IllegalStateException expectedException) { + } catch (DataException expectedException) { assertEquals(String.format("Couldn't find PUT transaction for name %s, service %s " + "and identifier ", name.toLowerCase(), service), expectedException.getMessage()); } @@ -246,7 +246,7 @@ public class ArbitraryDataTests extends Common { arbitraryDataReader1b.loadSynchronously(true); fail("Loading data with incorrect identifier should fail due to nonexistent PUT transaction"); - } catch (IllegalStateException expectedException) { + } catch (DataException expectedException) { assertEquals(String.format("Couldn't find PUT transaction for name %s, service %s " + "and identifier %s", name.toLowerCase(), service, differentIdentifier), expectedException.getMessage()); } @@ -321,7 +321,7 @@ public class ArbitraryDataTests extends Common { arbitraryDataReader1c.loadSynchronously(true); fail("Loading data with incorrect identifier should fail due to nonexistent PUT transaction"); - } catch (IllegalStateException expectedException) { + } catch (DataException expectedException) { assertEquals(String.format("Couldn't find PUT transaction for name %s, service %s " + "and identifier %s", name.toLowerCase(), service, differentIdentifier), expectedException.getMessage()); }