mirror of
https://github.com/Qortal/qortal.git
synced 2025-04-23 19:37:51 +00:00
Merge branch 'master' into rebuild-archive
This commit is contained in:
commit
e1771dbaea
2
pom.xml
2
pom.xml
@ -3,7 +3,7 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>org.qortal</groupId>
|
<groupId>org.qortal</groupId>
|
||||||
<artifactId>qortal</artifactId>
|
<artifactId>qortal</artifactId>
|
||||||
<version>3.8.5</version>
|
<version>3.8.9</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<properties>
|
<properties>
|
||||||
<skipTests>true</skipTests>
|
<skipTests>true</skipTests>
|
||||||
|
@ -60,7 +60,7 @@ public class BootstrapResource {
|
|||||||
bootstrap.validateBlockchain();
|
bootstrap.validateBlockchain();
|
||||||
return bootstrap.create();
|
return bootstrap.create();
|
||||||
|
|
||||||
} catch (DataException | InterruptedException | IOException e) {
|
} catch (Exception e) {
|
||||||
LOGGER.info("Unable to create bootstrap", e);
|
LOGGER.info("Unable to create bootstrap", e);
|
||||||
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.REPOSITORY_ISSUE, e.getMessage());
|
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.REPOSITORY_ISSUE, e.getMessage());
|
||||||
}
|
}
|
||||||
|
@ -84,6 +84,8 @@ public enum Service {
|
|||||||
QCHAT_IMAGE(420, true, 500*1024L, null),
|
QCHAT_IMAGE(420, true, 500*1024L, null),
|
||||||
VIDEO(500, false, null, null),
|
VIDEO(500, false, null, null),
|
||||||
AUDIO(600, 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(700, false, null, null),
|
||||||
BLOG_POST(777, false, null, null),
|
BLOG_POST(777, false, null, null),
|
||||||
BLOG_COMMENT(778, false, null, null),
|
BLOG_COMMENT(778, false, null, null),
|
||||||
|
@ -204,7 +204,7 @@ public class ArbitraryDataCleanupManager extends Thread {
|
|||||||
|
|
||||||
if (completeFileExists && !allChunksExist) {
|
if (completeFileExists && !allChunksExist) {
|
||||||
// We have the complete file but not the chunks, so let's convert it
|
// 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())));
|
Base58.encode(arbitraryTransactionData.getSignature())));
|
||||||
|
|
||||||
ArbitraryTransactionUtils.convertFileToChunks(arbitraryTransactionData, now, STALE_FILE_TIMEOUT);
|
ArbitraryTransactionUtils.convertFileToChunks(arbitraryTransactionData, now, STALE_FILE_TIMEOUT);
|
||||||
|
@ -45,6 +45,7 @@ public class Digibyte extends Bitcoiny {
|
|||||||
return Arrays.asList(
|
return Arrays.asList(
|
||||||
// Servers chosen on NO BASIS WHATSOEVER from various sources!
|
// Servers chosen on NO BASIS WHATSOEVER from various sources!
|
||||||
// Status verified at https://1209k.com/bitcoin-eye/ele.php?chain=dgb
|
// Status verified at https://1209k.com/bitcoin-eye/ele.php?chain=dgb
|
||||||
|
new Server("electrum.qortal.link", Server.ConnectionType.SSL, 55002),
|
||||||
new Server("electrum-dgb.qortal.online", ConnectionType.SSL, 50002),
|
new Server("electrum-dgb.qortal.online", ConnectionType.SSL, 50002),
|
||||||
new Server("electrum1-dgb.qortal.online", ConnectionType.SSL, 50002),
|
new Server("electrum1-dgb.qortal.online", ConnectionType.SSL, 50002),
|
||||||
new Server("electrum1.cipig.net", ConnectionType.SSL, 20059),
|
new Server("electrum1.cipig.net", ConnectionType.SSL, 20059),
|
||||||
|
@ -46,6 +46,7 @@ public class Dogecoin extends Bitcoiny {
|
|||||||
return Arrays.asList(
|
return Arrays.asList(
|
||||||
// Servers chosen on NO BASIS WHATSOEVER from various sources!
|
// Servers chosen on NO BASIS WHATSOEVER from various sources!
|
||||||
// Status verified at https://1209k.com/bitcoin-eye/ele.php?chain=doge
|
// Status verified at https://1209k.com/bitcoin-eye/ele.php?chain=doge
|
||||||
|
new Server("electrum.qortal.link", Server.ConnectionType.SSL, 54002),
|
||||||
new Server("electrum-doge.qortal.online", ConnectionType.SSL, 50002),
|
new Server("electrum-doge.qortal.online", ConnectionType.SSL, 50002),
|
||||||
new Server("electrum1-doge.qortal.online", ConnectionType.SSL, 50002),
|
new Server("electrum1-doge.qortal.online", ConnectionType.SSL, 50002),
|
||||||
new Server("electrum1.cipig.net", ConnectionType.SSL, 20060),
|
new Server("electrum1.cipig.net", ConnectionType.SSL, 20060),
|
||||||
|
@ -50,6 +50,7 @@ public class Litecoin extends Bitcoiny {
|
|||||||
//BEHIND new Server("62.171.169.176", Server.ConnectionType.SSL, 50002),
|
//BEHIND new Server("62.171.169.176", Server.ConnectionType.SSL, 50002),
|
||||||
//PHISHY new Server("electrum-ltc.bysh.me", Server.ConnectionType.SSL, 50002),
|
//PHISHY new Server("electrum-ltc.bysh.me", Server.ConnectionType.SSL, 50002),
|
||||||
new Server("backup.electrum-ltc.org", Server.ConnectionType.SSL, 443),
|
new Server("backup.electrum-ltc.org", Server.ConnectionType.SSL, 443),
|
||||||
|
new Server("electrum.qortal.link", Server.ConnectionType.SSL, 50002),
|
||||||
new Server("electrum.ltc.xurious.com", Server.ConnectionType.SSL, 50002),
|
new Server("electrum.ltc.xurious.com", Server.ConnectionType.SSL, 50002),
|
||||||
new Server("electrum-ltc.petrkr.net", Server.ConnectionType.SSL, 60002),
|
new Server("electrum-ltc.petrkr.net", Server.ConnectionType.SSL, 60002),
|
||||||
new Server("electrum-ltc.qortal.online", Server.ConnectionType.SSL, 50002),
|
new Server("electrum-ltc.qortal.online", Server.ConnectionType.SSL, 50002),
|
||||||
|
@ -48,6 +48,7 @@ public class Ravencoin extends Bitcoiny {
|
|||||||
//CLOSED new Server("aethyn.com", ConnectionType.SSL, 50002),
|
//CLOSED new Server("aethyn.com", ConnectionType.SSL, 50002),
|
||||||
//CLOSED new Server("electrum2.rvn.rocks", ConnectionType.SSL, 50002),
|
//CLOSED new Server("electrum2.rvn.rocks", ConnectionType.SSL, 50002),
|
||||||
//BEHIND new Server("electrum3.rvn.rocks", ConnectionType.SSL, 50002),
|
//BEHIND new Server("electrum3.rvn.rocks", ConnectionType.SSL, 50002),
|
||||||
|
new Server("electrum.qortal.link", Server.ConnectionType.SSL, 56002),
|
||||||
new Server("electrum-rvn.qortal.online", ConnectionType.SSL, 50002),
|
new Server("electrum-rvn.qortal.online", ConnectionType.SSL, 50002),
|
||||||
new Server("electrum1-rvn.qortal.online", ConnectionType.SSL, 50002),
|
new Server("electrum1-rvn.qortal.online", ConnectionType.SSL, 50002),
|
||||||
new Server("electrum1.cipig.net", ConnectionType.SSL, 20051),
|
new Server("electrum1.cipig.net", ConnectionType.SSL, 20051),
|
||||||
|
@ -17,17 +17,21 @@ public class ActiveChats {
|
|||||||
private Long timestamp;
|
private Long timestamp;
|
||||||
private String sender;
|
private String sender;
|
||||||
private String senderName;
|
private String senderName;
|
||||||
|
private byte[] signature;
|
||||||
|
private byte[] data;
|
||||||
|
|
||||||
protected GroupChat() {
|
protected GroupChat() {
|
||||||
/* JAXB */
|
/* JAXB */
|
||||||
}
|
}
|
||||||
|
|
||||||
public GroupChat(int groupId, String groupName, Long timestamp, String sender, String senderName) {
|
public GroupChat(int groupId, String groupName, Long timestamp, String sender, String senderName, byte[] signature, byte[] data) {
|
||||||
this.groupId = groupId;
|
this.groupId = groupId;
|
||||||
this.groupName = groupName;
|
this.groupName = groupName;
|
||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
this.sender = sender;
|
this.sender = sender;
|
||||||
this.senderName = senderName;
|
this.senderName = senderName;
|
||||||
|
this.signature = signature;
|
||||||
|
this.data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getGroupId() {
|
public int getGroupId() {
|
||||||
@ -49,6 +53,14 @@ public class ActiveChats {
|
|||||||
public String getSenderName() {
|
public String getSenderName() {
|
||||||
return this.senderName;
|
return this.senderName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public byte[] getSignature() {
|
||||||
|
return this.signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getData() {
|
||||||
|
return this.data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@XmlAccessorType(XmlAccessType.FIELD)
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
@ -16,6 +16,8 @@ import org.qortal.repository.Repository;
|
|||||||
import org.qortal.transaction.Transaction.TransactionType;
|
import org.qortal.transaction.Transaction.TransactionType;
|
||||||
import org.qortal.utils.Unicode;
|
import org.qortal.utils.Unicode;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public class Name {
|
public class Name {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
@ -116,7 +118,7 @@ public class Name {
|
|||||||
|
|
||||||
this.repository.getNameRepository().save(this.nameData);
|
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
|
// Name has changed, delete old entry
|
||||||
this.repository.getNameRepository().delete(updateNameTransactionData.getNewName());
|
this.repository.getNameRepository().delete(updateNameTransactionData.getNewName());
|
||||||
|
|
||||||
|
@ -279,7 +279,9 @@ public class Bootstrap {
|
|||||||
|
|
||||||
LOGGER.info("Generating checksum file...");
|
LOGGER.info("Generating checksum file...");
|
||||||
String checksum = Crypto.digestHexString(compressedOutputPath.toFile(), 1024*1024);
|
String checksum = Crypto.digestHexString(compressedOutputPath.toFile(), 1024*1024);
|
||||||
|
LOGGER.info("checksum: {}", checksum);
|
||||||
Path checksumPath = Paths.get(String.format("%s.sha256", compressedOutputPath.toString()));
|
Path checksumPath = Paths.get(String.format("%s.sha256", compressedOutputPath.toString()));
|
||||||
|
LOGGER.info("Writing checksum to path: {}", checksumPath);
|
||||||
Files.writeString(checksumPath, checksum, StandardOpenOption.CREATE);
|
Files.writeString(checksumPath, checksum, StandardOpenOption.CREATE);
|
||||||
|
|
||||||
// Return the path to the compressed bootstrap file
|
// Return the path to the compressed bootstrap file
|
||||||
|
@ -177,11 +177,11 @@ public class HSQLDBChatRepository implements ChatRepository {
|
|||||||
|
|
||||||
private List<GroupChat> getActiveGroupChats(String address) throws DataException {
|
private List<GroupChat> getActiveGroupChats(String address) throws DataException {
|
||||||
// Find groups where address is a member and potential latest message details
|
// Find groups where address is a member and potential latest message details
|
||||||
String groupsSql = "SELECT group_id, group_name, latest_timestamp, sender, sender_name "
|
String groupsSql = "SELECT group_id, group_name, latest_timestamp, sender, sender_name, signature, data "
|
||||||
+ "FROM GroupMembers "
|
+ "FROM GroupMembers "
|
||||||
+ "JOIN Groups USING (group_id) "
|
+ "JOIN Groups USING (group_id) "
|
||||||
+ "LEFT OUTER JOIN LATERAL("
|
+ "LEFT OUTER JOIN LATERAL("
|
||||||
+ "SELECT created_when AS latest_timestamp, sender, name AS sender_name "
|
+ "SELECT created_when AS latest_timestamp, sender, name AS sender_name, signature, data "
|
||||||
+ "FROM ChatTransactions "
|
+ "FROM ChatTransactions "
|
||||||
+ "JOIN Transactions USING (signature) "
|
+ "JOIN Transactions USING (signature) "
|
||||||
+ "LEFT OUTER JOIN Names AS SenderNames ON SenderNames.owner = sender "
|
+ "LEFT OUTER JOIN Names AS SenderNames ON SenderNames.owner = sender "
|
||||||
@ -205,8 +205,10 @@ public class HSQLDBChatRepository implements ChatRepository {
|
|||||||
|
|
||||||
String sender = resultSet.getString(4);
|
String sender = resultSet.getString(4);
|
||||||
String senderName = resultSet.getString(5);
|
String senderName = resultSet.getString(5);
|
||||||
|
byte[] signature = resultSet.getBytes(6);
|
||||||
|
byte[] data = resultSet.getBytes(7);
|
||||||
|
|
||||||
GroupChat groupChat = new GroupChat(groupId, groupName, timestamp, sender, senderName);
|
GroupChat groupChat = new GroupChat(groupId, groupName, timestamp, sender, senderName, signature, data);
|
||||||
groupChats.add(groupChat);
|
groupChats.add(groupChat);
|
||||||
} while (resultSet.next());
|
} while (resultSet.next());
|
||||||
}
|
}
|
||||||
@ -215,7 +217,7 @@ public class HSQLDBChatRepository implements ChatRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We need different SQL to handle group-less chat
|
// We need different SQL to handle group-less chat
|
||||||
String grouplessSql = "SELECT created_when, sender, SenderNames.name "
|
String grouplessSql = "SELECT created_when, sender, SenderNames.name, signature, data "
|
||||||
+ "FROM ChatTransactions "
|
+ "FROM ChatTransactions "
|
||||||
+ "JOIN Transactions USING (signature) "
|
+ "JOIN Transactions USING (signature) "
|
||||||
+ "LEFT OUTER JOIN Names AS SenderNames ON SenderNames.owner = sender "
|
+ "LEFT OUTER JOIN Names AS SenderNames ON SenderNames.owner = sender "
|
||||||
@ -228,15 +230,19 @@ public class HSQLDBChatRepository implements ChatRepository {
|
|||||||
Long timestamp = null;
|
Long timestamp = null;
|
||||||
String sender = null;
|
String sender = null;
|
||||||
String senderName = null;
|
String senderName = null;
|
||||||
|
byte[] signature = null;
|
||||||
|
byte[] data = null;
|
||||||
|
|
||||||
if (resultSet != null) {
|
if (resultSet != null) {
|
||||||
// We found a recipient-less, group-less CHAT message, so report its details
|
// We found a recipient-less, group-less CHAT message, so report its details
|
||||||
timestamp = resultSet.getLong(1);
|
timestamp = resultSet.getLong(1);
|
||||||
sender = resultSet.getString(2);
|
sender = resultSet.getString(2);
|
||||||
senderName = resultSet.getString(3);
|
senderName = resultSet.getString(3);
|
||||||
|
signature = resultSet.getBytes(4);
|
||||||
|
data = resultSet.getBytes(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupChat groupChat = new GroupChat(0, null, timestamp, sender, senderName);
|
GroupChat groupChat = new GroupChat(0, null, timestamp, sender, senderName, signature, data);
|
||||||
groupChats.add(groupChat);
|
groupChats.add(groupChat);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new DataException("Unable to fetch active group chats from repository", e);
|
throw new DataException("Unable to fetch active group chats from repository", e);
|
||||||
|
@ -215,7 +215,7 @@ public class Settings {
|
|||||||
public long recoveryModeTimeout = 10 * 60 * 1000L;
|
public long recoveryModeTimeout = 10 * 60 * 1000L;
|
||||||
|
|
||||||
/** Minimum peer version number required in order to sync with them */
|
/** 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
|
/** 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 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 */
|
* If false, sync will be blocked both ways, and they will not appear in the peers list */
|
||||||
|
@ -5,6 +5,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import org.qortal.account.Account;
|
import org.qortal.account.Account;
|
||||||
import org.qortal.asset.Asset;
|
import org.qortal.asset.Asset;
|
||||||
|
import org.qortal.controller.repository.NamesDatabaseIntegrityCheck;
|
||||||
import org.qortal.data.naming.NameData;
|
import org.qortal.data.naming.NameData;
|
||||||
import org.qortal.data.transaction.CancelSellNameTransactionData;
|
import org.qortal.data.transaction.CancelSellNameTransactionData;
|
||||||
import org.qortal.data.transaction.TransactionData;
|
import org.qortal.data.transaction.TransactionData;
|
||||||
@ -81,7 +82,13 @@ public class CancelSellNameTransaction extends Transaction {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void preProcess() throws DataException {
|
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
|
@Override
|
||||||
|
@ -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 that reverting using previous UPDATE_NAME works as expected
|
||||||
@Test
|
@Test
|
||||||
public void testIntermediateUpdateName() throws DataException {
|
public void testIntermediateUpdateName() throws DataException {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user