Merge branch 'master' into Abstract-and-Update-Deps

This commit is contained in:
Ice 2025-06-11 03:15:22 -04:00 committed by GitHub
commit 47e5c473b3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 49 additions and 57 deletions

View File

@ -15,20 +15,31 @@ Building the future one block at a time. Welcome to Qortal.
# Building the Qortal Core from Source # Building the Qortal Core from Source
## Build / run ## Build / Run
- Requires Java 11. OpenJDK 11 recommended over Java SE. - Requires Java 11. OpenJDK 11 recommended over Java SE.
- Install Maven - Install Maven
- Use Maven to fetch dependencies and build: `mvn clean package` - Use Maven to fetch dependencies and build: `mvn clean package`
- Update Maven dependencies: `mvn install`
- Built JAR should be something like `target/qortal-1.0.jar` - Built JAR should be something like `target/qortal-1.0.jar`
- Create basic *settings.json* file: `echo '{}' > settings.json` - Create basic *settings.json* file: `echo '{}' > settings.json`
- Run JAR in same working directory as *settings.json*: `java -jar target/qortal-1.0.jar` - Run JAR in same working directory as *settings.json*: `java -jar target/qortal-1.0.jar`
- Wrap in shell script, add JVM flags, redirection, backgrounding, etc. as necessary. - Wrap in shell script, add JVM flags, redirection, backgrounding, etc. as necessary.
- Or use supplied example shell script: *start.sh* - Or use supplied example shell script: *start.sh*
## IntelliJ IDEA Configuration
- Run -> Edit Configurations
- Add New Application
- Name: qortal
- SDK: java 11
- Main Class: org.qortal.controller.Controller
- Program arguments: settings.json -Dlog4j.configurationFile=log4j2.properties -ea
- Environment variables: Djava.net.preferIPv4Stack=false
# Using a pre-built Qortal 'jar' binary # Using a pre-built Qortal 'jar' binary
If you would prefer to utilize a released version of Qortal, you may do so by downloading one of the available releases from the releases page, that are also linked on https://qortal.org and https://qortal.dev. If you prefer to utilize a released version of Qortal, you may do so by downloading one of the available releases from the releases page, that are also linked on https://qortal.org and https://qortal.dev.
# Learning Q-App Development # Learning Q-App Development

View File

@ -22,28 +22,23 @@ public class CancelSellNameTransaction extends Transaction {
private CancelSellNameTransactionData cancelSellNameTransactionData; private CancelSellNameTransactionData cancelSellNameTransactionData;
// Constructors // Constructors
public CancelSellNameTransaction(Repository repository, TransactionData transactionData) { public CancelSellNameTransaction(Repository repository, TransactionData transactionData) {
super(repository, transactionData); super(repository, transactionData);
this.cancelSellNameTransactionData = (CancelSellNameTransactionData) this.transactionData; this.cancelSellNameTransactionData = (CancelSellNameTransactionData) this.transactionData;
} }
// More information // More information
@Override @Override
public List<String> getRecipientAddresses() throws DataException { public List<String> getRecipientAddresses() throws DataException {
return Collections.emptyList(); return Collections.emptyList(); // No recipient address for this transaction
} }
// Navigation // Navigation
public Account getOwner() { public Account getOwner() {
return this.getCreator(); return this.getCreator(); // The creator of the transaction is the owner
} }
// Processing // Processing
@Override @Override
public ValidationResult isValid() throws DataException { public ValidationResult isValid() throws DataException {
String name = this.cancelSellNameTransactionData.getName(); String name = this.cancelSellNameTransactionData.getName();
@ -57,61 +52,56 @@ public class CancelSellNameTransaction extends Transaction {
if (!name.equals(Unicode.normalize(name))) if (!name.equals(Unicode.normalize(name)))
return ValidationResult.NAME_NOT_NORMALIZED; return ValidationResult.NAME_NOT_NORMALIZED;
// Retrieve name data from repository
NameData nameData = this.repository.getNameRepository().fromName(name); NameData nameData = this.repository.getNameRepository().fromName(name);
// Check name exists // Check if name exists
if (nameData == null) if (nameData == null)
return ValidationResult.NAME_DOES_NOT_EXIST; return ValidationResult.NAME_DOES_NOT_EXIST;
// Check name is currently for sale // Check name is currently for sale
if (!nameData.isForSale()) { if (!nameData.isForSale()) {
// Only validate after feature-trigger timestamp, due to a small number of double cancelations in the chain history // Validate after feature-trigger timestamp, due to potential double cancellations
if (this.cancelSellNameTransactionData.getTimestamp() > BlockChain.getInstance().getCancelSellNameValidationTimestamp()) if (this.cancelSellNameTransactionData.getTimestamp() > BlockChain.getInstance().getCancelSellNameValidationTimestamp())
return ValidationResult.NAME_NOT_FOR_SALE; return ValidationResult.NAME_NOT_FOR_SALE;
} }
// Check transaction creator matches name's current owner // Check if transaction creator matches the name's current owner
Account owner = getOwner(); Account owner = getOwner();
if (!owner.getAddress().equals(nameData.getOwner())) if (!owner.getAddress().equals(nameData.getOwner()))
return ValidationResult.INVALID_NAME_OWNER; return ValidationResult.INVALID_NAME_OWNER;
// Check issuer has enough funds // Check if issuer has enough balance for the transaction fee
if (owner.getConfirmedBalance(Asset.QORT) < cancelSellNameTransactionData.getFee()) if (owner.getConfirmedBalance(Asset.QORT) < cancelSellNameTransactionData.getFee())
return ValidationResult.NO_BALANCE; return ValidationResult.NO_BALANCE;
return ValidationResult.OK; return ValidationResult.OK; // All validations passed
} }
@Override @Override
public void preProcess() throws DataException { public void preProcess() throws DataException {
CancelSellNameTransactionData cancelSellNameTransactionData = (CancelSellNameTransactionData) transactionData; // Direct access to class field, no need to redeclare
// 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 namesDatabaseIntegrityCheck = new NamesDatabaseIntegrityCheck();
namesDatabaseIntegrityCheck.rebuildName(cancelSellNameTransactionData.getName(), this.repository); namesDatabaseIntegrityCheck.rebuildName(this.cancelSellNameTransactionData.getName(), this.repository);
} }
@Override @Override
public void process() throws DataException { public void process() throws DataException {
// Update Name // Update the Name to reflect the cancellation of the sale
Name name = new Name(this.repository, cancelSellNameTransactionData.getName()); Name name = new Name(this.repository, cancelSellNameTransactionData.getName());
name.cancelSell(cancelSellNameTransactionData); name.cancelSell(cancelSellNameTransactionData);
// Save this transaction, with updated "name reference" to previous transaction that updated name // Save this transaction with updated "name reference"
this.repository.getTransactionRepository().save(cancelSellNameTransactionData); this.repository.getTransactionRepository().save(cancelSellNameTransactionData);
} }
@Override @Override
public void orphan() throws DataException { public void orphan() throws DataException {
// Revert name // Revert the cancellation of the name sale
Name name = new Name(this.repository, cancelSellNameTransactionData.getName()); Name name = new Name(this.repository, cancelSellNameTransactionData.getName());
name.uncancelSell(cancelSellNameTransactionData); name.uncancelSell(cancelSellNameTransactionData);
// Save this transaction, with removed "name reference" // Save the transaction with the reverted "name reference"
this.repository.getTransactionRepository().save(cancelSellNameTransactionData); this.repository.getTransactionRepository().save(cancelSellNameTransactionData);
} }
} }

View File

@ -24,28 +24,23 @@ public class SellNameTransaction extends Transaction {
private SellNameTransactionData sellNameTransactionData; private SellNameTransactionData sellNameTransactionData;
// Constructors // Constructors
public SellNameTransaction(Repository repository, TransactionData transactionData) { public SellNameTransaction(Repository repository, TransactionData transactionData) {
super(repository, transactionData); super(repository, transactionData);
this.sellNameTransactionData = (SellNameTransactionData) this.transactionData; this.sellNameTransactionData = (SellNameTransactionData) this.transactionData;
} }
// More information // More information
@Override @Override
public List<String> getRecipientAddresses() throws DataException { public List<String> getRecipientAddresses() throws DataException {
return Collections.emptyList(); return Collections.emptyList(); // No direct recipient address for this transaction
} }
// Navigation // Navigation
public Account getOwner() { public Account getOwner() {
return this.getCreator(); return this.getCreator(); // Owner is the creator of the transaction
} }
// Processing // Processing
@Override @Override
public ValidationResult isValid() throws DataException { public ValidationResult isValid() throws DataException {
String name = this.sellNameTransactionData.getName(); String name = this.sellNameTransactionData.getName();
@ -59,59 +54,54 @@ public class SellNameTransaction extends Transaction {
if (!name.equals(Unicode.normalize(name))) if (!name.equals(Unicode.normalize(name)))
return ValidationResult.NAME_NOT_NORMALIZED; return ValidationResult.NAME_NOT_NORMALIZED;
// Retrieve name data from repository
NameData nameData = this.repository.getNameRepository().fromName(name); NameData nameData = this.repository.getNameRepository().fromName(name);
// Check name exists // Check if name exists
if (nameData == null) if (nameData == null)
return ValidationResult.NAME_DOES_NOT_EXIST; return ValidationResult.NAME_DOES_NOT_EXIST;
// Check name isn't currently for sale // Check name is not already for sale
if (nameData.isForSale()) if (nameData.isForSale())
return ValidationResult.NAME_ALREADY_FOR_SALE; return ValidationResult.NAME_ALREADY_FOR_SALE;
// Check transaction's public key matches name's current owner // Validate transaction's public key matches name's current owner
Account owner = getOwner(); Account owner = getOwner();
if (!owner.getAddress().equals(nameData.getOwner())) if (!owner.getAddress().equals(nameData.getOwner()))
return ValidationResult.INVALID_NAME_OWNER; return ValidationResult.INVALID_NAME_OWNER;
// Check amount is positive // Check amount is positive and within valid range
if (this.sellNameTransactionData.getAmount() <= 0) long amount = this.sellNameTransactionData.getAmount();
if (amount <= 0)
return ValidationResult.NEGATIVE_AMOUNT; return ValidationResult.NEGATIVE_AMOUNT;
if (amount >= MAX_AMOUNT)
// Check amount within bounds
if (this.sellNameTransactionData.getAmount() >= MAX_AMOUNT)
return ValidationResult.INVALID_AMOUNT; return ValidationResult.INVALID_AMOUNT;
// Check issuer has enough funds // Check if owner has enough balance for the transaction fee
if (owner.getConfirmedBalance(Asset.QORT) < this.sellNameTransactionData.getFee()) if (owner.getConfirmedBalance(Asset.QORT) < this.sellNameTransactionData.getFee())
return ValidationResult.NO_BALANCE; return ValidationResult.NO_BALANCE;
return ValidationResult.OK; return ValidationResult.OK; // All validation checks passed
} }
@Override @Override
public void preProcess() throws DataException { public void preProcess() throws DataException {
SellNameTransactionData sellNameTransactionData = (SellNameTransactionData) transactionData; // Directly access class field rather than local variable for clarity
// 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 namesDatabaseIntegrityCheck = new NamesDatabaseIntegrityCheck();
namesDatabaseIntegrityCheck.rebuildName(sellNameTransactionData.getName(), this.repository); namesDatabaseIntegrityCheck.rebuildName(this.sellNameTransactionData.getName(), this.repository);
} }
@Override @Override
public void process() throws DataException { public void process() throws DataException {
// Sell Name // Sell the name
Name name = new Name(this.repository, this.sellNameTransactionData.getName()); Name name = new Name(this.repository, this.sellNameTransactionData.getName());
name.sell(this.sellNameTransactionData); name.sell(this.sellNameTransactionData);
} }
@Override @Override
public void orphan() throws DataException { public void orphan() throws DataException {
// Revert name // Revert the name sale in case of orphaning
Name name = new Name(this.repository, this.sellNameTransactionData.getName()); Name name = new Name(this.repository, this.sellNameTransactionData.getName());
name.unsell(this.sellNameTransactionData); name.unsell(this.sellNameTransactionData);
} }
} }

View File

@ -45,10 +45,10 @@ SYNCHRONIZING_BLOCKCHAIN = Bezig met synchronizeren
SYNCHRONIZING_CLOCK = Klok wordt gesynchronizeerd SYNCHRONIZING_CLOCK = Klok wordt gesynchronizeerd
RESTARTING_NODE = Knooppunt opnieuw starten RESTARTING_NODE = Node opnieuw starten
APPLYING_RESTARTING_NODE = Herstartknooppunt toepassen. Wees alstublieft geduldig... APPLYING_RESTARTING_NODE = Node wordt herstart. Even geduld alstublieft...
BOOTSTRAP_NODE = Opstartknooppunt BOOTSTRAP_NODE = Opstarten van node (bootstrap)
APPLYING_BOOTSTRAP_AND_RESTARTING = Bootstrap toepassen en knooppunt opnieuw starten. Wees alstublieft geduldig... APPLYING_BOOTSTRAP_AND_RESTARTING = Bootstrap toepassen en node opnieuw starten. Even geduld alstublieft...

View File

@ -196,6 +196,7 @@ TX_GROUP_ID_MISMATCH = groep-ID komt niet overeen
TRANSFER_PRIVS_DISABLED = overdrachtsrechten uitgeschakeld TRANSFER_PRIVS_DISABLED = overdrachtsrechten uitgeschakeld
TEMPORARY_DISABLED = Naamregistratie tijdelijk uitgeschakeld TEMPORARY_DISABLED = naamregistratie tijdelijk uitgeschakeld
GENERAL_TEMPORARY_DISABLED = Tijdelijk uitgeschakeld GENERAL_TEMPORARY_DISABLED = Tijdelijk uitgeschakeld