Browse Source

Verify that each data file matches the size reported by transaction.

This discourages an incorrect file size being included with a transaction, as the system will reject it and won't even serve it to other peers.

FUTURE: we could introduce some kind of blacklist to track invalid files like this, and avoid repeated attempts to retrieve them. It is okay for now as the system will backoff after a few attempts.
qdn
CalDescent 3 years ago
parent
commit
f7ed3eefc8
  1. 15
      src/main/java/org/qortal/arbitrary/ArbitraryDataFile.java
  2. 3
      src/main/java/org/qortal/arbitrary/ArbitraryDataReader.java
  3. 24
      src/main/java/org/qortal/repository/hsqldb/HSQLDBArbitraryRepository.java

15
src/main/java/org/qortal/arbitrary/ArbitraryDataFile.java

@ -225,6 +225,21 @@ public class ArbitraryDataFile {
return ValidationResult.OK;
}
public void validateFileSize(long expectedSize) throws DataException {
// Verify that we can determine the file's size
long fileSize = 0;
try {
fileSize = Files.size(this.getFilePath());
} catch (IOException e) {
throw new DataException(String.format("Couldn't get file size for transaction %s", Base58.encode(signature)));
}
// Ensure the file's size matches the size reported by the transaction
if (fileSize != expectedSize) {
throw new DataException(String.format("File size mismatch for transaction %s", Base58.encode(signature)));
}
}
private void addChunk(ArbitraryDataFileChunk chunk) {
this.chunks.add(chunk);
}

3
src/main/java/org/qortal/arbitrary/ArbitraryDataReader.java

@ -357,6 +357,9 @@ public class ArbitraryDataReader {
if (!Arrays.equals(arbitraryDataFile.digest(), digest)) {
throw new DataException("Unable to validate complete file hash");
}
// Ensure the file's size matches the size reported by the transaction (throws a DataException if not)
arbitraryDataFile.validateFileSize(transactionData.getSize());
// Set filePath to the location of the ArbitraryDataFile
this.filePath = arbitraryDataFile.getFilePath();
}

24
src/main/java/org/qortal/repository/hsqldb/HSQLDBArbitraryRepository.java

@ -1,5 +1,7 @@
package org.qortal.repository.hsqldb;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.qortal.arbitrary.misc.Service;
import org.qortal.data.arbitrary.ArbitraryResourceInfo;
import org.qortal.crypto.Crypto;
@ -13,6 +15,7 @@ import org.qortal.repository.ArbitraryRepository;
import org.qortal.repository.DataException;
import org.qortal.arbitrary.ArbitraryDataFile;
import org.qortal.transaction.Transaction.ApprovalStatus;
import org.qortal.utils.Base58;
import java.sql.ResultSet;
import java.sql.SQLException;
@ -21,6 +24,8 @@ import java.util.List;
public class HSQLDBArbitraryRepository implements ArbitraryRepository {
private static final Logger LOGGER = LogManager.getLogger(HSQLDBArbitraryRepository.class);
private static final int MAX_RAW_DATA_SIZE = 255; // size of VARBINARY
protected HSQLDBRepository repository;
@ -66,7 +71,8 @@ public class HSQLDBArbitraryRepository implements ArbitraryRepository {
}
@Override
public byte[] fetchData(byte[] signature) throws DataException {
public byte[] fetchData(byte[] signature) {
try {
ArbitraryTransactionData transactionData = getTransactionData(signature);
if (transactionData == null) {
return null;
@ -87,6 +93,9 @@ public class HSQLDBArbitraryRepository implements ArbitraryRepository {
// If we have the complete data file, return it
if (arbitraryDataFile.exists()) {
// Ensure the file's size matches the size reported by the transaction (throws a DataException if not)
arbitraryDataFile.validateFileSize(transactionData.getSize());
return arbitraryDataFile.getBytes();
}
@ -95,9 +104,20 @@ public class HSQLDBArbitraryRepository implements ArbitraryRepository {
arbitraryDataFile.join();
// Verify that the combined hash matches the expected hash
if (digest.equals(arbitraryDataFile.digest())) {
if (!digest.equals(arbitraryDataFile.digest())) {
LOGGER.info(String.format("Hash mismatch for transaction: %s", Base58.encode(signature)));
return null;
}
// Ensure the file's size matches the size reported by the transaction
arbitraryDataFile.validateFileSize(transactionData.getSize());
return arbitraryDataFile.getBytes();
}
} catch (DataException e) {
LOGGER.info("Unable to fetch data for transaction {}: {}", Base58.encode(signature), e.getMessage());
return null;
}
return null;

Loading…
Cancel
Save