diff --git a/pom.xml b/pom.xml index 1eb8adb1..35c77bcc 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.qortal qortal - 3.8.5 + 3.8.9 jar true diff --git a/src/main/java/org/qortal/api/resource/BootstrapResource.java b/src/main/java/org/qortal/api/resource/BootstrapResource.java index b9382dcb..78630dfb 100644 --- a/src/main/java/org/qortal/api/resource/BootstrapResource.java +++ b/src/main/java/org/qortal/api/resource/BootstrapResource.java @@ -60,7 +60,7 @@ public class BootstrapResource { bootstrap.validateBlockchain(); return bootstrap.create(); - } catch (DataException | InterruptedException | IOException e) { + } catch (Exception e) { LOGGER.info("Unable to create bootstrap", e); throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.REPOSITORY_ISSUE, e.getMessage()); } diff --git a/src/main/java/org/qortal/arbitrary/misc/Service.java b/src/main/java/org/qortal/arbitrary/misc/Service.java index 01419d2f..5ea1b7aa 100644 --- a/src/main/java/org/qortal/arbitrary/misc/Service.java +++ b/src/main/java/org/qortal/arbitrary/misc/Service.java @@ -84,6 +84,8 @@ public enum Service { QCHAT_IMAGE(420, true, 500*1024L, null), VIDEO(500, false, null, null), AUDIO(600, false, null, null), + QCHAT_AUDIO(610, true, 10*1024*1024L, null), + QCHAT_VOICE(620, true, 10*1024*1024L, null), BLOG(700, false, null, null), BLOG_POST(777, false, null, null), BLOG_COMMENT(778, false, null, null), diff --git a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataCleanupManager.java b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataCleanupManager.java index 39425b7e..34acf0cb 100644 --- a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataCleanupManager.java +++ b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataCleanupManager.java @@ -204,7 +204,7 @@ public class ArbitraryDataCleanupManager extends Thread { if (completeFileExists && !allChunksExist) { // We have the complete file but not the chunks, so let's convert it - LOGGER.info(String.format("Transaction %s has complete file but no chunks", + LOGGER.debug(String.format("Transaction %s has complete file but no chunks", Base58.encode(arbitraryTransactionData.getSignature()))); ArbitraryTransactionUtils.convertFileToChunks(arbitraryTransactionData, now, STALE_FILE_TIMEOUT); diff --git a/src/main/java/org/qortal/naming/Name.java b/src/main/java/org/qortal/naming/Name.java index ecf826a5..1751cca8 100644 --- a/src/main/java/org/qortal/naming/Name.java +++ b/src/main/java/org/qortal/naming/Name.java @@ -16,6 +16,8 @@ import org.qortal.repository.Repository; import org.qortal.transaction.Transaction.TransactionType; import org.qortal.utils.Unicode; +import java.util.Objects; + public class Name { // Properties @@ -116,7 +118,7 @@ public class Name { this.repository.getNameRepository().save(this.nameData); - if (!updateNameTransactionData.getNewName().isEmpty()) + if (!updateNameTransactionData.getNewName().isEmpty() && !Objects.equals(updateNameTransactionData.getName(), updateNameTransactionData.getNewName())) // Name has changed, delete old entry this.repository.getNameRepository().delete(updateNameTransactionData.getNewName()); diff --git a/src/main/java/org/qortal/repository/Bootstrap.java b/src/main/java/org/qortal/repository/Bootstrap.java index 626433e8..2d2605cc 100644 --- a/src/main/java/org/qortal/repository/Bootstrap.java +++ b/src/main/java/org/qortal/repository/Bootstrap.java @@ -279,7 +279,9 @@ public class Bootstrap { LOGGER.info("Generating checksum file..."); String checksum = Crypto.digestHexString(compressedOutputPath.toFile(), 1024*1024); + LOGGER.info("checksum: {}", checksum); Path checksumPath = Paths.get(String.format("%s.sha256", compressedOutputPath.toString())); + LOGGER.info("Writing checksum to path: {}", checksumPath); Files.writeString(checksumPath, checksum, StandardOpenOption.CREATE); // Return the path to the compressed bootstrap file diff --git a/src/main/java/org/qortal/settings/Settings.java b/src/main/java/org/qortal/settings/Settings.java index 5799bd26..ae5dc173 100644 --- a/src/main/java/org/qortal/settings/Settings.java +++ b/src/main/java/org/qortal/settings/Settings.java @@ -215,7 +215,7 @@ public class Settings { public long recoveryModeTimeout = 10 * 60 * 1000L; /** Minimum peer version number required in order to sync with them */ - private String minPeerVersion = "3.8.2"; + private String minPeerVersion = "3.8.7"; /** Whether to allow connections with peers below minPeerVersion * If true, we won't sync with them but they can still sync with us, and will show in the peers list * If false, sync will be blocked both ways, and they will not appear in the peers list */ diff --git a/src/main/java/org/qortal/transaction/CancelSellNameTransaction.java b/src/main/java/org/qortal/transaction/CancelSellNameTransaction.java index 788492a9..876f0aed 100644 --- a/src/main/java/org/qortal/transaction/CancelSellNameTransaction.java +++ b/src/main/java/org/qortal/transaction/CancelSellNameTransaction.java @@ -5,6 +5,7 @@ import java.util.List; import org.qortal.account.Account; import org.qortal.asset.Asset; +import org.qortal.controller.repository.NamesDatabaseIntegrityCheck; import org.qortal.data.naming.NameData; import org.qortal.data.transaction.CancelSellNameTransactionData; import org.qortal.data.transaction.TransactionData; @@ -81,7 +82,13 @@ public class CancelSellNameTransaction extends Transaction { @Override public void preProcess() throws DataException { - // Nothing to do + CancelSellNameTransactionData cancelSellNameTransactionData = (CancelSellNameTransactionData) transactionData; + + // Rebuild this name in the Names table from the transaction history + // This is necessary because in some rare cases names can be missing from the Names table after registration + // but we have been unable to reproduce the issue and track down the root cause + NamesDatabaseIntegrityCheck namesDatabaseIntegrityCheck = new NamesDatabaseIntegrityCheck(); + namesDatabaseIntegrityCheck.rebuildName(cancelSellNameTransactionData.getName(), this.repository); } @Override diff --git a/src/test/java/org/qortal/test/naming/UpdateTests.java b/src/test/java/org/qortal/test/naming/UpdateTests.java index 24af5317..54227e94 100644 --- a/src/test/java/org/qortal/test/naming/UpdateTests.java +++ b/src/test/java/org/qortal/test/naming/UpdateTests.java @@ -219,6 +219,65 @@ public class UpdateTests extends Common { } } + // Test that multiple UPDATE_NAME transactions work as expected, when using a matching name and newName string + @Test + public void testDoubleUpdateNameWithMatchingNewName() throws DataException { + try (final Repository repository = RepositoryManager.getRepository()) { + // Register-name + PrivateKeyAccount alice = Common.getTestAccount(repository, "alice"); + String name = "name"; + String reducedName = "name"; + String data = "{\"age\":30}"; + + TransactionData initialTransactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data); + initialTransactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(initialTransactionData.getTimestamp())); + TransactionUtils.signAndMint(repository, initialTransactionData, alice); + + // Check name exists + assertTrue(repository.getNameRepository().nameExists(name)); + assertNotNull(repository.getNameRepository().fromReducedName(reducedName)); + + // Update name + TransactionData middleTransactionData = new UpdateNameTransactionData(TestTransaction.generateBase(alice), name, name, data); + TransactionUtils.signAndMint(repository, middleTransactionData, alice); + + // Check name still exists + assertTrue(repository.getNameRepository().nameExists(name)); + assertNotNull(repository.getNameRepository().fromReducedName(reducedName)); + + // Update name again + TransactionData newestTransactionData = new UpdateNameTransactionData(TestTransaction.generateBase(alice), name, name, data); + TransactionUtils.signAndMint(repository, newestTransactionData, alice); + + // Check name still exists + assertTrue(repository.getNameRepository().nameExists(name)); + assertNotNull(repository.getNameRepository().fromReducedName(reducedName)); + + // Check updated timestamp is correct + assertEquals((Long) newestTransactionData.getTimestamp(), repository.getNameRepository().fromName(name).getUpdated()); + + // orphan and recheck + BlockUtils.orphanLastBlock(repository); + + // Check name still exists + assertTrue(repository.getNameRepository().nameExists(name)); + assertNotNull(repository.getNameRepository().fromReducedName(reducedName)); + + // Check updated timestamp is correct + assertEquals((Long) middleTransactionData.getTimestamp(), repository.getNameRepository().fromName(name).getUpdated()); + + // orphan and recheck + BlockUtils.orphanLastBlock(repository); + + // Check name still exists + assertTrue(repository.getNameRepository().nameExists(name)); + assertNotNull(repository.getNameRepository().fromReducedName(reducedName)); + + // Check updated timestamp is empty + assertNull(repository.getNameRepository().fromName(name).getUpdated()); + } + } + // Test that reverting using previous UPDATE_NAME works as expected @Test public void testIntermediateUpdateName() throws DataException {