mirror of
https://github.com/Qortal/qortal.git
synced 2025-07-19 10:51:23 +00:00
primary names implementation
This commit is contained in:
parent
f5a4a0a16c
commit
88fe3b0af6
@ -2,12 +2,14 @@ package org.qortal.account;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.qortal.api.resource.TransactionsResource;
|
||||
import org.qortal.block.BlockChain;
|
||||
import org.qortal.controller.LiteNode;
|
||||
import org.qortal.data.account.AccountBalanceData;
|
||||
import org.qortal.data.account.AccountData;
|
||||
import org.qortal.data.account.RewardShareData;
|
||||
import org.qortal.data.naming.NameData;
|
||||
import org.qortal.data.transaction.TransactionData;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.repository.GroupRepository;
|
||||
import org.qortal.repository.NameRepository;
|
||||
@ -19,7 +21,11 @@ import org.qortal.utils.Groups;
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.qortal.utils.Amounts.prettyAmount;
|
||||
|
||||
@ -361,6 +367,142 @@ public class Account {
|
||||
return accountData.getLevel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Primary Name
|
||||
*
|
||||
* @return the primary name for this address if present, otherwise empty
|
||||
*
|
||||
* @throws DataException
|
||||
*/
|
||||
public Optional<String> getPrimaryName() throws DataException {
|
||||
|
||||
return this.repository.getNameRepository().getPrimaryName(this.address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Primary Name
|
||||
*
|
||||
* @throws DataException
|
||||
*/
|
||||
public void removePrimaryName() throws DataException {
|
||||
this.repository.getNameRepository().removePrimaryName(this.address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset Primary Name
|
||||
*
|
||||
* Set primary name based on the names (and their history) this account owns.
|
||||
*
|
||||
* @param confirmationStatus the status of the transactions for the determining the primary name
|
||||
*
|
||||
* @return the primary name, empty if their isn't one
|
||||
*
|
||||
* @throws DataException
|
||||
*/
|
||||
public Optional<String> resetPrimaryName(TransactionsResource.ConfirmationStatus confirmationStatus) throws DataException {
|
||||
Optional<String> primaryName = determinePrimaryName(confirmationStatus);
|
||||
|
||||
if(primaryName.isPresent()) {
|
||||
return setPrimaryName(primaryName.get());
|
||||
}
|
||||
else {
|
||||
return primaryName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine Primary Name
|
||||
*
|
||||
* Determine primary name based on a list of registered names.
|
||||
*
|
||||
* @param confirmationStatus the status of the transactions for this determination
|
||||
*
|
||||
* @return the primary name, empty if there is no primary name
|
||||
*
|
||||
* @throws DataException
|
||||
*/
|
||||
public Optional<String> determinePrimaryName(TransactionsResource.ConfirmationStatus confirmationStatus) throws DataException {
|
||||
|
||||
// all registered names for the owner
|
||||
List<NameData> names = this.repository.getNameRepository().getNamesByOwner(this.address);
|
||||
|
||||
Optional<String> primaryName;
|
||||
|
||||
// if no registered names, the no primary name possible
|
||||
if (names.isEmpty()) {
|
||||
primaryName = Optional.empty();
|
||||
}
|
||||
// if names
|
||||
else {
|
||||
// if one name, then that is the primary name
|
||||
if (names.size() == 1) {
|
||||
primaryName = Optional.of( names.get(0).getName() );
|
||||
}
|
||||
// if more than one name, then seek the earliest name acquisition that was never released
|
||||
else {
|
||||
Map<String, TransactionData> txByName = new HashMap<>(names.size());
|
||||
|
||||
// for each name, get the latest transaction
|
||||
for (NameData nameData : names) {
|
||||
|
||||
// since the name is currently registered to the owner,
|
||||
// we assume the latest transaction involving this name was the transaction that the acquired
|
||||
// name through registration, purchase or update
|
||||
Optional<TransactionData> latestTransaction
|
||||
= this.repository
|
||||
.getTransactionRepository()
|
||||
.getTransactionsInvolvingName(
|
||||
nameData.getName(),
|
||||
confirmationStatus
|
||||
)
|
||||
.stream()
|
||||
.sorted(Comparator.comparing(
|
||||
TransactionData::getTimestamp).reversed()
|
||||
)
|
||||
.findFirst(); // first is the last, since it was reversed
|
||||
|
||||
// if there is a latest transaction, expected for all registered names
|
||||
if (latestTransaction.isPresent()) {
|
||||
txByName.put(nameData.getName(), latestTransaction.get());
|
||||
}
|
||||
// if there is no latest transaction, then
|
||||
else {
|
||||
LOGGER.warn("No matching transaction for name: " + nameData.getName());
|
||||
}
|
||||
}
|
||||
|
||||
// get the first name aqcuistion for this address
|
||||
Optional<Map.Entry<String, TransactionData>> firstNameEntry
|
||||
= txByName.entrySet().stream().sorted(Comparator.comparing(entry -> entry.getValue().getTimestamp())).findFirst();
|
||||
|
||||
// if their is a name acquisition, then the first one is the primary name
|
||||
if (firstNameEntry.isPresent()) {
|
||||
primaryName = Optional.of( firstNameEntry.get().getKey() );
|
||||
}
|
||||
// if there is no nameacquistion, then there is no primary name
|
||||
else {
|
||||
primaryName = Optional.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
return primaryName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Primary Name
|
||||
*
|
||||
* @param primaryName the primary to set to this address
|
||||
*
|
||||
* @return the primary name if successful, empty if unsuccessful
|
||||
*
|
||||
* @throws DataException
|
||||
*/
|
||||
public Optional<String> setPrimaryName( String primaryName ) throws DataException {
|
||||
int changed = this.repository.getNameRepository().setPrimaryName(this.address, primaryName);
|
||||
|
||||
return changed > 0 ? Optional.of(primaryName) : Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns reward-share minting address, or unknown if reward-share does not exist.
|
||||
*
|
||||
|
@ -33,6 +33,7 @@ import javax.ws.rs.*;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Path("/names")
|
||||
@ -104,6 +105,45 @@ public class NamesResource {
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/primary/{address}")
|
||||
@Operation(
|
||||
summary = "primary name owned by address",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "registered primary name info",
|
||||
content = @Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(implementation = NameSummary.class)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_ADDRESS, ApiError.REPOSITORY_ISSUE, ApiError.UNAUTHORIZED})
|
||||
public NameSummary getPrimaryNameByAddress(@PathParam("address") String address) {
|
||||
if (!Crypto.isValidAddress(address))
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ADDRESS);
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
|
||||
if (Settings.getInstance().isLite()) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.UNAUTHORIZED);
|
||||
}
|
||||
else {
|
||||
Optional<String> primaryName = repository.getNameRepository().getPrimaryName(address);
|
||||
|
||||
if(primaryName.isPresent()) {
|
||||
return new NameSummary(new NameData(primaryName.get(), address));
|
||||
}
|
||||
else {
|
||||
return new NameSummary((new NameData(null, address)));
|
||||
}
|
||||
}
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{name}")
|
||||
@Operation(
|
||||
|
@ -1640,6 +1640,8 @@ public class Block {
|
||||
SelfSponsorshipAlgoV2Block.processAccountPenalties(this);
|
||||
} else if (this.blockData.getHeight() == BlockChain.getInstance().getSelfSponsorshipAlgoV3Height()) {
|
||||
SelfSponsorshipAlgoV3Block.processAccountPenalties(this);
|
||||
} else if (this.blockData.getHeight() == BlockChain.getInstance().getMultipleNamesPerAccountHeight()) {
|
||||
PrimaryNamesBlock.processNames(this.repository);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1952,6 +1954,8 @@ public class Block {
|
||||
SelfSponsorshipAlgoV2Block.orphanAccountPenalties(this);
|
||||
} else if (this.blockData.getHeight() == BlockChain.getInstance().getSelfSponsorshipAlgoV3Height()) {
|
||||
SelfSponsorshipAlgoV3Block.orphanAccountPenalties(this);
|
||||
} else if (this.blockData.getHeight() == BlockChain.getInstance().getMultipleNamesPerAccountHeight()) {
|
||||
PrimaryNamesBlock.orphanNames( this.repository );
|
||||
}
|
||||
}
|
||||
|
||||
|
47
src/main/java/org/qortal/block/PrimaryNamesBlock.java
Normal file
47
src/main/java/org/qortal/block/PrimaryNamesBlock.java
Normal file
@ -0,0 +1,47 @@
|
||||
package org.qortal.block;
|
||||
|
||||
import org.qortal.account.Account;
|
||||
import org.qortal.api.resource.TransactionsResource;
|
||||
import org.qortal.data.naming.NameData;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.repository.Repository;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Class PrimaryNamesBlock
|
||||
*/
|
||||
public class PrimaryNamesBlock {
|
||||
|
||||
/**
|
||||
* Process Primary Names
|
||||
*
|
||||
* @param repository
|
||||
* @throws DataException
|
||||
*/
|
||||
public static void processNames(Repository repository) throws DataException {
|
||||
|
||||
Set<String> addressesWithNames
|
||||
= repository.getNameRepository().getAllNames().stream()
|
||||
.map(NameData::getOwner).collect(Collectors.toSet());
|
||||
|
||||
// for each address with a name, set primary name to the address
|
||||
for( String address : addressesWithNames ) {
|
||||
|
||||
Account account = new Account(repository, address);
|
||||
account.resetPrimaryName(TransactionsResource.ConfirmationStatus.CONFIRMED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Orphan the Primary Names Block
|
||||
*
|
||||
* @param repository
|
||||
* @throws DataException
|
||||
*/
|
||||
public static void orphanNames(Repository repository) throws DataException {
|
||||
|
||||
repository.getNameRepository().clearPrimaryNames();
|
||||
}
|
||||
}
|
@ -67,6 +67,11 @@ public class NameData {
|
||||
this(name, reducedName, owner, data, registered, null, false, null, reference, creationGroupId);
|
||||
}
|
||||
|
||||
// Typically used for name summsry
|
||||
public NameData(String name, String owner) {
|
||||
this(name, null, owner, null, 0L, null, false, null, null, 0);
|
||||
}
|
||||
|
||||
// Getters / setters
|
||||
|
||||
public String getName() {
|
||||
|
@ -3,6 +3,7 @@ package org.qortal.repository;
|
||||
import org.qortal.data.naming.NameData;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface NameRepository {
|
||||
|
||||
@ -34,10 +35,17 @@ public interface NameRepository {
|
||||
return getNamesByOwner(address, null, null, null);
|
||||
}
|
||||
|
||||
public int setPrimaryName(String address, String primaryName) throws DataException;
|
||||
|
||||
public void removePrimaryName(String address) throws DataException;
|
||||
|
||||
public Optional<String> getPrimaryName(String address) throws DataException;
|
||||
|
||||
public int clearPrimaryNames() throws DataException;
|
||||
|
||||
public List<String> getRecentNames(long startTimestamp) throws DataException;
|
||||
|
||||
public void save(NameData nameData) throws DataException;
|
||||
|
||||
public void delete(String name) throws DataException;
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
package org.qortal.repository.hsqldb;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.qortal.block.BlockChain;
|
||||
import org.qortal.data.chat.ActiveChats;
|
||||
import org.qortal.data.chat.ActiveChats.DirectChat;
|
||||
import org.qortal.data.chat.ActiveChats.GroupChat;
|
||||
@ -18,6 +21,8 @@ import static org.qortal.data.chat.ChatMessage.Encoding;
|
||||
|
||||
public class HSQLDBChatRepository implements ChatRepository {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger(HSQLDBChatRepository.class);
|
||||
|
||||
protected HSQLDBRepository repository;
|
||||
|
||||
public HSQLDBChatRepository(HSQLDBRepository repository) {
|
||||
@ -142,10 +147,23 @@ public class HSQLDBChatRepository implements ChatRepository {
|
||||
|
||||
@Override
|
||||
public ChatMessage toChatMessage(ChatTransactionData chatTransactionData, Encoding encoding) throws DataException {
|
||||
|
||||
String tableName;
|
||||
|
||||
// if the PrimaryTable is available, then use it
|
||||
if( this.repository.getBlockRepository().getBlockchainHeight() > BlockChain.getInstance().getMultipleNamesPerAccountHeight()) {
|
||||
LOGGER.info("using PrimaryNames for chat transactions");
|
||||
tableName = "PrimaryNames";
|
||||
}
|
||||
else {
|
||||
LOGGER.info("using Names for chat transactions");
|
||||
tableName = "Names";
|
||||
}
|
||||
|
||||
String sql = "SELECT SenderNames.name, RecipientNames.name "
|
||||
+ "FROM ChatTransactions "
|
||||
+ "LEFT OUTER JOIN Names AS SenderNames ON SenderNames.owner = sender "
|
||||
+ "LEFT OUTER JOIN Names AS RecipientNames ON RecipientNames.owner = recipient "
|
||||
+ "LEFT OUTER JOIN " + tableName + " AS SenderNames ON SenderNames.owner = sender "
|
||||
+ "LEFT OUTER JOIN " + tableName + " AS RecipientNames ON RecipientNames.owner = recipient "
|
||||
+ "WHERE signature = ?";
|
||||
|
||||
try (ResultSet resultSet = this.repository.checkedExecute(sql, chatTransactionData.getSignature())) {
|
||||
|
@ -1053,6 +1053,12 @@ public class HSQLDBDatabaseUpdates {
|
||||
stmt.execute("UPDATE Accounts SET blocks_minted_penalty = -5000000 WHERE blocks_minted_penalty < 0");
|
||||
break;
|
||||
|
||||
case 50:
|
||||
// Primary name for a Qortal Address, 0-1 for any address
|
||||
stmt.execute("CREATE TABLE PrimaryNames (owner QortalAddress, name RegisteredName, "
|
||||
+ "PRIMARY KEY (owner), FOREIGN KEY (name) REFERENCES Names (name) ON DELETE CASCADE)");
|
||||
break;
|
||||
|
||||
default:
|
||||
// nothing to do
|
||||
return false;
|
||||
|
@ -8,6 +8,7 @@ import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class HSQLDBNameRepository implements NameRepository {
|
||||
|
||||
@ -333,6 +334,55 @@ public class HSQLDBNameRepository implements NameRepository {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePrimaryName(String address) throws DataException {
|
||||
try {
|
||||
this.repository.delete("PrimaryNames", "owner = ?", address);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to delete primary name from repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getPrimaryName(String address) throws DataException {
|
||||
String sql = "SELECT name FROM PrimaryNames WHERE owner = ?";
|
||||
|
||||
List<String> names = new ArrayList<>();
|
||||
|
||||
try (ResultSet resultSet = this.repository.checkedExecute(sql, address)) {
|
||||
if (resultSet == null)
|
||||
return Optional.empty();
|
||||
|
||||
String name = resultSet.getString(1);
|
||||
|
||||
return Optional.of(name);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to fetch recent names from repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int setPrimaryName(String address, String primaryName) throws DataException {
|
||||
|
||||
String sql = "INSERT INTO PrimaryNames (owner, name) VALUES (?, ?) ON DUPLICATE KEY UPDATE name = ?";
|
||||
|
||||
try{
|
||||
return this.repository.executeCheckedUpdate(sql, address, primaryName, primaryName);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to set primary name", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int clearPrimaryNames() throws DataException {
|
||||
|
||||
try {
|
||||
return this.repository.delete("PrimaryNames");
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to clear primary names from repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(NameData nameData) throws DataException {
|
||||
HSQLDBSaver saveHelper = new HSQLDBSaver("Names");
|
||||
|
@ -16,6 +16,7 @@ import org.qortal.utils.Unicode;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class BuyNameTransaction extends Transaction {
|
||||
|
||||
@ -117,6 +118,25 @@ public class BuyNameTransaction extends Transaction {
|
||||
|
||||
// Save transaction with updated "name reference" pointing to previous transaction that changed name
|
||||
this.repository.getTransactionRepository().save(this.buyNameTransactionData);
|
||||
|
||||
// if multiple names feature is activated, then check the buyer and seller's primary name status
|
||||
if( this.repository.getBlockRepository().getBlockchainHeight() > BlockChain.getInstance().getMultipleNamesPerAccountHeight()) {
|
||||
|
||||
Account seller = new Account(this.repository, this.buyNameTransactionData.getSeller());
|
||||
Optional<String> sellerPrimaryName = seller.getPrimaryName();
|
||||
|
||||
// if the seller sold their primary name, then remove their primary name
|
||||
if (sellerPrimaryName.isPresent() && sellerPrimaryName.get().equals(buyNameTransactionData.getName())) {
|
||||
seller.removePrimaryName();
|
||||
}
|
||||
|
||||
Account buyer = new Account(this.repository, this.getBuyer().getAddress());
|
||||
|
||||
// if the buyer had no primary name, then set the primary name to the name bought
|
||||
if( buyer.getPrimaryName().isEmpty() ) {
|
||||
buyer.setPrimaryName(this.buyNameTransactionData.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -127,6 +147,24 @@ public class BuyNameTransaction extends Transaction {
|
||||
|
||||
// Save this transaction, with previous "name reference"
|
||||
this.repository.getTransactionRepository().save(this.buyNameTransactionData);
|
||||
}
|
||||
|
||||
// if multiple names feature is activated, then check the buyer and seller's primary name status
|
||||
if( this.repository.getBlockRepository().getBlockchainHeight() > BlockChain.getInstance().getMultipleNamesPerAccountHeight()) {
|
||||
|
||||
Account seller = new Account(this.repository, this.buyNameTransactionData.getSeller());
|
||||
|
||||
// if the seller lost their primary name, then set their primary name back
|
||||
if (seller.getPrimaryName().isEmpty()) {
|
||||
seller.setPrimaryName(this.buyNameTransactionData.getName());
|
||||
}
|
||||
|
||||
Account buyer = new Account(this.repository, this.getBuyer().getAddress());
|
||||
Optional<String> buyerPrimaryName = buyer.getPrimaryName();
|
||||
|
||||
// if the buyer bought their primary, then remove it
|
||||
if( buyerPrimaryName.isPresent() && this.buyNameTransactionData.getName().equals(buyerPrimaryName.get()) ) {
|
||||
buyer.removePrimaryName();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,12 @@ package org.qortal.transaction;
|
||||
|
||||
import com.google.common.base.Utf8;
|
||||
import org.qortal.account.Account;
|
||||
import org.qortal.api.resource.TransactionsResource;
|
||||
import org.qortal.asset.Asset;
|
||||
import org.qortal.block.BlockChain;
|
||||
import org.qortal.controller.repository.NamesDatabaseIntegrityCheck;
|
||||
import org.qortal.crypto.Crypto;
|
||||
import org.qortal.data.naming.NameData;
|
||||
import org.qortal.data.transaction.RegisterNameTransactionData;
|
||||
import org.qortal.data.transaction.TransactionData;
|
||||
import org.qortal.naming.Name;
|
||||
@ -15,6 +17,7 @@ import org.qortal.utils.Unicode;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class RegisterNameTransaction extends Transaction {
|
||||
|
||||
@ -54,6 +57,15 @@ public class RegisterNameTransaction extends Transaction {
|
||||
Account registrant = getRegistrant();
|
||||
String name = this.registerNameTransactionData.getName();
|
||||
|
||||
Optional<String> registrantPrimaryName = registrant.getPrimaryName();
|
||||
if( registrantPrimaryName.isPresent() ) {
|
||||
|
||||
NameData nameData = repository.getNameRepository().fromName(registrantPrimaryName.get());
|
||||
if (nameData.isForSale()) {
|
||||
return ValidationResult.NOT_SUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
int blockchainHeight = this.repository.getBlockRepository().getBlockchainHeight();
|
||||
final int start = BlockChain.getInstance().getSelfSponsorshipAlgoV2Height() - 1180;
|
||||
final int end = BlockChain.getInstance().getSelfSponsorshipAlgoV3Height();
|
||||
@ -117,6 +129,16 @@ public class RegisterNameTransaction extends Transaction {
|
||||
// Register Name
|
||||
Name name = new Name(this.repository, this.registerNameTransactionData);
|
||||
name.register();
|
||||
|
||||
if( this.repository.getBlockRepository().getBlockchainHeight() > BlockChain.getInstance().getMultipleNamesPerAccountHeight()) {
|
||||
|
||||
Account account = new Account(this.repository, this.getCreator().getAddress());
|
||||
|
||||
// if there is no primary name established, then the new registered name is the primary name
|
||||
if (account.getPrimaryName().isEmpty()) {
|
||||
account.setPrimaryName(this.registerNameTransactionData.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -3,6 +3,7 @@ package org.qortal.transaction;
|
||||
import com.google.common.base.Utf8;
|
||||
import org.qortal.account.Account;
|
||||
import org.qortal.asset.Asset;
|
||||
import org.qortal.block.BlockChain;
|
||||
import org.qortal.controller.repository.NamesDatabaseIntegrityCheck;
|
||||
import org.qortal.crypto.Crypto;
|
||||
import org.qortal.data.naming.NameData;
|
||||
@ -49,6 +50,12 @@ public class UpdateNameTransaction extends Transaction {
|
||||
public ValidationResult isValid() throws DataException {
|
||||
String name = this.updateNameTransactionData.getName();
|
||||
|
||||
// if the account has more than one name, then they cannot update their primary name
|
||||
if( this.repository.getNameRepository().getNamesByOwner(this.getOwner().getAddress()).size() > 1 &&
|
||||
this.getOwner().getPrimaryName().get().equals(name) ) {
|
||||
return ValidationResult.NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
// Check name size bounds
|
||||
int nameLength = Utf8.encodedLength(name);
|
||||
if (nameLength < Name.MIN_NAME_SIZE || nameLength > Name.MAX_NAME_SIZE)
|
||||
@ -152,6 +159,16 @@ public class UpdateNameTransaction extends Transaction {
|
||||
|
||||
// Save this transaction, now with updated "name reference" to previous transaction that changed name
|
||||
this.repository.getTransactionRepository().save(this.updateNameTransactionData);
|
||||
|
||||
if( this.repository.getBlockRepository().getBlockchainHeight() > BlockChain.getInstance().getMultipleNamesPerAccountHeight()) {
|
||||
|
||||
Account account = new Account(this.repository, this.getCreator().getAddress());
|
||||
|
||||
// if updating the primary name, then set primary name to new name
|
||||
if( account.getPrimaryName().isEmpty() || account.getPrimaryName().get().equals(this.updateNameTransactionData.getName())) {
|
||||
account.setPrimaryName(this.updateNameTransactionData.getNewName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -167,6 +184,16 @@ public class UpdateNameTransaction extends Transaction {
|
||||
|
||||
// Save this transaction, with previous "name reference"
|
||||
this.repository.getTransactionRepository().save(this.updateNameTransactionData);
|
||||
|
||||
if( this.repository.getBlockRepository().getBlockchainHeight() > BlockChain.getInstance().getMultipleNamesPerAccountHeight()) {
|
||||
|
||||
Account account = new Account(this.repository, this.getCreator().getAddress());
|
||||
|
||||
// if the primary name is the new updated name, then it needs to be set back to the previous name
|
||||
if (account.getPrimaryName().isPresent() && account.getPrimaryName().get().equals(this.updateNameTransactionData.getNewName())) {
|
||||
account.setPrimaryName(this.updateNameTransactionData.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.qortal.account.PrivateKeyAccount;
|
||||
import org.qortal.api.resource.TransactionsResource;
|
||||
import org.qortal.block.BlockChain;
|
||||
import org.qortal.data.naming.NameData;
|
||||
import org.qortal.data.transaction.BuyNameTransactionData;
|
||||
@ -22,6 +23,7 @@ import org.qortal.transaction.Transaction;
|
||||
import org.qortal.utils.Amounts;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
@ -135,13 +137,26 @@ public class BuySellTests extends Common {
|
||||
|
||||
@Test
|
||||
public void testSellName() throws DataException {
|
||||
// mint passed the feature trigger block
|
||||
BlockUtils.mintBlocks(repository, BlockChain.getInstance().getMultipleNamesPerAccountHeight());
|
||||
|
||||
// Register-name
|
||||
testRegisterName();
|
||||
|
||||
// assert primary name for alice
|
||||
Optional<String> alicePrimaryName1 = alice.getPrimaryName();
|
||||
assertTrue(alicePrimaryName1.isPresent());
|
||||
assertTrue(alicePrimaryName1.get().equals(name));
|
||||
|
||||
// Sell-name
|
||||
SellNameTransactionData transactionData = new SellNameTransactionData(TestTransaction.generateBase(alice), name, price);
|
||||
TransactionUtils.signAndMint(repository, transactionData, alice);
|
||||
|
||||
// assert primary name for alice
|
||||
Optional<String> alicePrimaryName2 = alice.getPrimaryName();
|
||||
assertTrue(alicePrimaryName2.isPresent());
|
||||
assertTrue(alicePrimaryName2.get().equals(name));
|
||||
|
||||
NameData nameData;
|
||||
|
||||
// Check name is for sale
|
||||
@ -149,6 +164,14 @@ public class BuySellTests extends Common {
|
||||
assertTrue(nameData.isForSale());
|
||||
assertEquals("price incorrect", price, nameData.getSalePrice());
|
||||
|
||||
// assert alice cannot register another name while primary name is for sale
|
||||
final String name2 = "another name";
|
||||
RegisterNameTransactionData registerSecondNameData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name2, "{}");
|
||||
Transaction.ValidationResult registrationResult = TransactionUtils.signAndImport(repository, registerSecondNameData, alice);
|
||||
|
||||
// check that registering is not supported while primary name is for sale
|
||||
assertTrue(Transaction.ValidationResult.NOT_SUPPORTED.equals(registrationResult));
|
||||
|
||||
// Orphan sell-name
|
||||
BlockUtils.orphanLastBlock(repository);
|
||||
|
||||
@ -168,6 +191,10 @@ public class BuySellTests extends Common {
|
||||
// Orphan sell-name and register-name
|
||||
BlockUtils.orphanBlocks(repository, 2);
|
||||
|
||||
// assert primary name for alice
|
||||
Optional<String> alicePrimaryName3 = alice.getPrimaryName();
|
||||
assertTrue(alicePrimaryName3.isEmpty());
|
||||
|
||||
// Check name no longer exists
|
||||
assertFalse(repository.getNameRepository().nameExists(name));
|
||||
nameData = repository.getNameRepository().fromName(name);
|
||||
@ -261,15 +288,36 @@ public class BuySellTests extends Common {
|
||||
|
||||
@Test
|
||||
public void testBuyName() throws DataException {
|
||||
// move passed primary initiation
|
||||
BlockUtils.mintBlocks(repository, BlockChain.getInstance().getMultipleNamesPerAccountHeight());
|
||||
|
||||
// Register-name and sell-name
|
||||
testSellName();
|
||||
|
||||
String seller = alice.getAddress();
|
||||
|
||||
// assert alice has the name as primary
|
||||
Optional<String> alicePrimaryName1 = alice.getPrimaryName();
|
||||
assertTrue(alicePrimaryName1.isPresent());
|
||||
assertEquals(name, alicePrimaryName1.get());
|
||||
|
||||
// assert bob does not have a primary name
|
||||
Optional<String> bobPrimaryName1 = bob.getPrimaryName();
|
||||
assertTrue(bobPrimaryName1.isEmpty());
|
||||
|
||||
// Buy-name
|
||||
BuyNameTransactionData transactionData = new BuyNameTransactionData(TestTransaction.generateBase(bob), name, price, seller);
|
||||
TransactionUtils.signAndMint(repository, transactionData, bob);
|
||||
|
||||
// assert alice does not have a primary name anymore
|
||||
Optional<String> alicePrimaryName2 = alice.getPrimaryName();
|
||||
assertTrue(alicePrimaryName2.isEmpty());
|
||||
|
||||
// assert bob does have the name as primary
|
||||
Optional<String> bobPrimaryName2 = bob.getPrimaryName();
|
||||
assertTrue(bobPrimaryName2.isPresent());
|
||||
assertEquals(name, bobPrimaryName2.get());
|
||||
|
||||
NameData nameData;
|
||||
|
||||
// Check name is sold
|
||||
@ -280,6 +328,15 @@ public class BuySellTests extends Common {
|
||||
// Orphan buy-name
|
||||
BlockUtils.orphanLastBlock(repository);
|
||||
|
||||
// assert alice has the name as primary
|
||||
Optional<String> alicePrimaryNameOrphaned = alice.getPrimaryName();
|
||||
assertTrue(alicePrimaryNameOrphaned.isPresent());
|
||||
assertEquals(name, alicePrimaryNameOrphaned.get());
|
||||
|
||||
// assert bob does not have a primary name
|
||||
Optional<String> bobPrimaryNameOrphaned = bob.getPrimaryName();
|
||||
assertTrue(bobPrimaryNameOrphaned.isEmpty());
|
||||
|
||||
// Check name is for sale (not sold)
|
||||
nameData = repository.getNameRepository().fromName(name);
|
||||
assertTrue(nameData.isForSale());
|
||||
@ -314,6 +371,9 @@ public class BuySellTests extends Common {
|
||||
assertFalse(nameData.isForSale());
|
||||
// Not concerned about price
|
||||
assertEquals(bob.getAddress(), nameData.getOwner());
|
||||
|
||||
assertEquals(alice.getPrimaryName(), alice.determinePrimaryName(TransactionsResource.ConfirmationStatus.CONFIRMED));
|
||||
assertEquals(bob.getPrimaryName(), bob.determinePrimaryName(TransactionsResource.ConfirmationStatus.CONFIRMED));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -373,6 +433,9 @@ public class BuySellTests extends Common {
|
||||
assertTrue(nameData.isForSale());
|
||||
assertEquals("price incorrect", newPrice, nameData.getSalePrice());
|
||||
assertEquals(bob.getAddress(), nameData.getOwner());
|
||||
|
||||
assertEquals(alice.getPrimaryName(), alice.determinePrimaryName(TransactionsResource.ConfirmationStatus.CONFIRMED));
|
||||
assertEquals(bob.getPrimaryName(), bob.determinePrimaryName(TransactionsResource.ConfirmationStatus.CONFIRMED));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.qortal.account.PrivateKeyAccount;
|
||||
import org.qortal.api.resource.TransactionsResource;
|
||||
import org.qortal.block.BlockChain;
|
||||
import org.qortal.controller.repository.NamesDatabaseIntegrityCheck;
|
||||
import org.qortal.data.naming.NameData;
|
||||
import org.qortal.data.transaction.*;
|
||||
@ -13,6 +15,7 @@ import org.qortal.repository.RepositoryFactory;
|
||||
import org.qortal.repository.RepositoryManager;
|
||||
import org.qortal.repository.hsqldb.HSQLDBRepositoryFactory;
|
||||
import org.qortal.settings.Settings;
|
||||
import org.qortal.test.common.BlockUtils;
|
||||
import org.qortal.test.common.Common;
|
||||
import org.qortal.test.common.TransactionUtils;
|
||||
import org.qortal.test.common.transaction.TestTransaction;
|
||||
@ -385,6 +388,8 @@ public class IntegrityTests extends Common {
|
||||
@Test
|
||||
public void testUpdateToMissingName() throws DataException {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
BlockUtils.mintBlocks(repository, BlockChain.getInstance().getMultipleNamesPerAccountHeight());
|
||||
|
||||
// Register-name
|
||||
PrivateKeyAccount alice = Common.getTestAccount(repository, "alice");
|
||||
String initialName = "test-name";
|
||||
@ -422,7 +427,12 @@ public class IntegrityTests extends Common {
|
||||
// Therefore the name that we are trying to rename TO already exists
|
||||
Transaction.ValidationResult result = transaction.importAsUnconfirmed();
|
||||
assertTrue("Transaction should be invalid", Transaction.ValidationResult.OK != result);
|
||||
assertTrue("Destination name should already exist", Transaction.ValidationResult.NAME_ALREADY_REGISTERED == result);
|
||||
|
||||
// this assertion has been updated, because the primary name logic now comes into play and you cannot update a primary name when there
|
||||
// is other names registered and if your try a NOT SUPPORTED result will be given
|
||||
assertTrue("Destination name should already exist", Transaction.ValidationResult.NOT_SUPPORTED == result);
|
||||
|
||||
assertEquals(alice.getPrimaryName(), alice.determinePrimaryName(TransactionsResource.ConfirmationStatus.CONFIRMED));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.qortal.test.naming;
|
||||
|
||||
import org.apache.commons.lang3.reflect.FieldUtils;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.qortal.account.PrivateKeyAccount;
|
||||
@ -8,6 +9,7 @@ import org.qortal.api.AmountTypeAdapter;
|
||||
import org.qortal.block.BlockChain;
|
||||
import org.qortal.block.BlockChain.UnitFeesByTimestamp;
|
||||
import org.qortal.controller.BlockMinter;
|
||||
import org.qortal.data.naming.NameData;
|
||||
import org.qortal.data.transaction.PaymentTransactionData;
|
||||
import org.qortal.data.transaction.RegisterNameTransactionData;
|
||||
import org.qortal.data.transaction.TransactionData;
|
||||
@ -28,6 +30,7 @@ import org.qortal.utils.NTP;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
@ -121,6 +124,8 @@ public class MiscTests extends Common {
|
||||
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(transactionData.getTimestamp()));
|
||||
TransactionUtils.signAndMint(repository, transactionData, alice);
|
||||
|
||||
BlockUtils.mintBlocks(repository, BlockChain.getInstance().getMultipleNamesPerAccountHeight());
|
||||
|
||||
// Register another name that we will later attempt to rename to first name (above)
|
||||
String otherName = "new-name";
|
||||
String otherData = "";
|
||||
@ -335,6 +340,8 @@ public class MiscTests extends Common {
|
||||
public void testRegisterNameFeeIncrease() throws Exception {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
|
||||
BlockUtils.mintBlocks(repository, BlockChain.getInstance().getMultipleNamesPerAccountHeight());
|
||||
|
||||
// Add original fee to nameRegistrationUnitFees
|
||||
UnitFeesByTimestamp originalFee = new UnitFeesByTimestamp();
|
||||
originalFee.timestamp = 0L;
|
||||
@ -517,4 +524,168 @@ public class MiscTests extends Common {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@Test
|
||||
public void testPrimaryNameEmpty() throws DataException {
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
|
||||
PrivateKeyAccount alice = Common.getTestAccount(repository, "alice");
|
||||
|
||||
// mint passed the feature trigger block
|
||||
BlockUtils.mintBlocks(repository, BlockChain.getInstance().getMultipleNamesPerAccountHeight());
|
||||
|
||||
Optional<String> primaryName = repository.getNameRepository().getPrimaryName(alice.getAddress());
|
||||
|
||||
Assert.assertNotNull(primaryName);
|
||||
Assert.assertTrue(primaryName.isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrimaryNameSingle() throws DataException {
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
String name = "alice 1";
|
||||
PrivateKeyAccount alice = Common.getTestAccount(repository, "alice");
|
||||
|
||||
// register name 1
|
||||
RegisterNameTransactionData transactionData1 = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, "{}");
|
||||
transactionData1.setFee(new RegisterNameTransaction(null, null).getUnitFee(transactionData1.getTimestamp()));
|
||||
TransactionUtils.signAndMint(repository, transactionData1, alice);
|
||||
|
||||
String name1 = transactionData1.getName();
|
||||
|
||||
// check name does exist
|
||||
assertTrue(repository.getNameRepository().nameExists(name1));
|
||||
|
||||
|
||||
// mint passed the feature trigger block
|
||||
BlockUtils.mintBlocks(repository, BlockChain.getInstance().getMultipleNamesPerAccountHeight() + 1);
|
||||
|
||||
Optional<String> primaryName = repository.getNameRepository().getPrimaryName(alice.getAddress());
|
||||
|
||||
Assert.assertNotNull(primaryName);
|
||||
Assert.assertTrue(primaryName.isPresent());
|
||||
Assert.assertEquals(name, primaryName.get());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrimaryNameSingleAfterFeature() throws DataException {
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
String name = "alice 1";
|
||||
PrivateKeyAccount alice = Common.getTestAccount(repository, "alice");
|
||||
|
||||
// mint passed the feature trigger block
|
||||
BlockUtils.mintBlocks(repository, BlockChain.getInstance().getMultipleNamesPerAccountHeight());
|
||||
|
||||
// register name 1
|
||||
RegisterNameTransactionData transactionData1 = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, "{}");
|
||||
transactionData1.setFee(new RegisterNameTransaction(null, null).getUnitFee(transactionData1.getTimestamp()));
|
||||
TransactionUtils.signAndMint(repository, transactionData1, alice);
|
||||
|
||||
String name1 = transactionData1.getName();
|
||||
|
||||
// check name does exist
|
||||
assertTrue(repository.getNameRepository().nameExists(name1));
|
||||
|
||||
|
||||
Optional<String> primaryName = repository.getNameRepository().getPrimaryName(alice.getAddress());
|
||||
|
||||
Assert.assertNotNull(primaryName);
|
||||
Assert.assertTrue(primaryName.isPresent());
|
||||
Assert.assertEquals(name, primaryName.get());
|
||||
|
||||
BlockUtils.orphanLastBlock(repository);
|
||||
|
||||
Optional<String> primaryNameOrpaned = repository.getNameRepository().getPrimaryName(alice.getAddress());
|
||||
|
||||
Assert.assertNotNull(primaryNameOrpaned);
|
||||
Assert.assertTrue(primaryNameOrpaned.isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateNameMultiple() throws DataException {
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
String name = "alice 1";
|
||||
PrivateKeyAccount alice = Common.getTestAccount(repository, "alice");
|
||||
|
||||
// register name 1
|
||||
RegisterNameTransactionData transactionData1 = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, "{}");
|
||||
transactionData1.setFee(new RegisterNameTransaction(null, null).getUnitFee(transactionData1.getTimestamp()));
|
||||
TransactionUtils.signAndMint(repository, transactionData1, alice);
|
||||
|
||||
String name1 = transactionData1.getName();
|
||||
|
||||
// check name does exist
|
||||
assertTrue(repository.getNameRepository().nameExists(name1));
|
||||
|
||||
// register another name, second registered name should fail before the feature trigger
|
||||
final String name2 = "another name";
|
||||
RegisterNameTransactionData transactionData2 = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name2, "{}");
|
||||
Transaction.ValidationResult resultBeforeFeatureTrigger = TransactionUtils.signAndImport(repository, transactionData2, alice);
|
||||
|
||||
// check that that multiple names is forbidden
|
||||
assertTrue(Transaction.ValidationResult.MULTIPLE_NAMES_FORBIDDEN.equals(resultBeforeFeatureTrigger));
|
||||
|
||||
// mint passed the feature trigger block
|
||||
BlockUtils.mintBlocks(repository, BlockChain.getInstance().getMultipleNamesPerAccountHeight());
|
||||
|
||||
// register again, now that we are passed the feature trigger
|
||||
RegisterNameTransactionData transactionData3 = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name2, "{}");
|
||||
Transaction.ValidationResult resultAfterFeatureTrigger = TransactionUtils.signAndImport(repository, transactionData3, alice);
|
||||
|
||||
// check that multiple names is ok
|
||||
assertTrue(Transaction.ValidationResult.OK.equals(resultAfterFeatureTrigger));
|
||||
|
||||
// mint block, confirm transaction
|
||||
BlockUtils.mintBlock(repository);
|
||||
|
||||
// check name does exist
|
||||
assertTrue(repository.getNameRepository().nameExists(name2));
|
||||
|
||||
// check that there are 2 names for one account
|
||||
List<NameData> namesByOwner = repository.getNameRepository().getNamesByOwner(alice.getAddress(), 0, 0, false);
|
||||
|
||||
assertEquals(2, namesByOwner.size());
|
||||
|
||||
// check that the order is correct
|
||||
assertEquals(name1, namesByOwner.get(0).getName());
|
||||
|
||||
String newestName = "newest-name";
|
||||
String newestReducedName = "newest-name";
|
||||
String newestData = "newest-data";
|
||||
TransactionData newestTransactionData = new UpdateNameTransactionData(TestTransaction.generateBase(alice), name2, newestName, newestData);
|
||||
TransactionUtils.signAndMint(repository, newestTransactionData, alice);
|
||||
|
||||
// Check previous name no longer exists
|
||||
assertFalse(repository.getNameRepository().nameExists(name2));
|
||||
|
||||
// Check newest name exists
|
||||
assertTrue(repository.getNameRepository().nameExists(newestName));
|
||||
|
||||
Optional<String> alicePrimaryName1 = alice.getPrimaryName();
|
||||
|
||||
assertTrue( alicePrimaryName1.isPresent() );
|
||||
assertEquals( name1, alicePrimaryName1.get() );
|
||||
|
||||
// orphan and recheck
|
||||
BlockUtils.orphanLastBlock(repository);
|
||||
|
||||
Optional<String> alicePrimaryName2 = alice.getPrimaryName();
|
||||
|
||||
assertTrue( alicePrimaryName2.isPresent() );
|
||||
assertEquals( name1, alicePrimaryName2.get() );
|
||||
|
||||
// Check newest name no longer exists
|
||||
assertFalse(repository.getNameRepository().nameExists(newestName));
|
||||
assertNull(repository.getNameRepository().fromReducedName(newestReducedName));
|
||||
|
||||
// Check previous name exists again
|
||||
assertTrue(repository.getNameRepository().nameExists(name2));
|
||||
}
|
||||
}
|
||||
}
|
@ -3,8 +3,12 @@ package org.qortal.test.naming;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.qortal.account.PrivateKeyAccount;
|
||||
import org.qortal.api.resource.TransactionsResource;
|
||||
import org.qortal.block.BlockChain;
|
||||
import org.qortal.data.naming.NameData;
|
||||
import org.qortal.data.transaction.BuyNameTransactionData;
|
||||
import org.qortal.data.transaction.RegisterNameTransactionData;
|
||||
import org.qortal.data.transaction.SellNameTransactionData;
|
||||
import org.qortal.data.transaction.TransactionData;
|
||||
import org.qortal.data.transaction.UpdateNameTransactionData;
|
||||
import org.qortal.repository.DataException;
|
||||
@ -15,6 +19,9 @@ import org.qortal.test.common.Common;
|
||||
import org.qortal.test.common.TransactionUtils;
|
||||
import org.qortal.test.common.transaction.TestTransaction;
|
||||
import org.qortal.transaction.RegisterNameTransaction;
|
||||
import org.qortal.transaction.Transaction;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
@ -395,6 +402,13 @@ public class UpdateTests extends Common {
|
||||
assertTrue(repository.getNameRepository().nameExists(initialName));
|
||||
assertNotNull(repository.getNameRepository().fromReducedName(initialReducedName));
|
||||
|
||||
// move passed primary initiation
|
||||
BlockUtils.mintBlocks(repository, BlockChain.getInstance().getMultipleNamesPerAccountHeight());
|
||||
|
||||
// check primary name
|
||||
assertTrue(alice.getPrimaryName().isPresent());
|
||||
assertEquals(initialName, alice.getPrimaryName().get());
|
||||
|
||||
// Update data
|
||||
String middleName = "middle-name";
|
||||
String middleReducedName = "midd1e-name";
|
||||
@ -402,6 +416,11 @@ public class UpdateTests extends Common {
|
||||
transactionData = new UpdateNameTransactionData(TestTransaction.generateBase(alice), initialName, middleName, middleData);
|
||||
TransactionUtils.signAndMint(repository, transactionData, alice);
|
||||
|
||||
// check primary name
|
||||
Optional<String> alicePrimaryName1 = alice.getPrimaryName();
|
||||
assertTrue(alicePrimaryName1.isPresent());
|
||||
assertEquals(middleName, alicePrimaryName1.get());
|
||||
|
||||
// Check data is correct
|
||||
assertEquals(middleData, repository.getNameRepository().fromName(middleName).getData());
|
||||
|
||||
@ -414,6 +433,11 @@ public class UpdateTests extends Common {
|
||||
// Check data is correct
|
||||
assertEquals(newestData, repository.getNameRepository().fromName(newestName).getData());
|
||||
|
||||
// check primary name
|
||||
Optional<String> alicePrimaryName2 = alice.getPrimaryName();
|
||||
assertTrue(alicePrimaryName2.isPresent());
|
||||
assertEquals(newestName, alicePrimaryName2.get());
|
||||
|
||||
// Check initial name no longer exists
|
||||
assertFalse(repository.getNameRepository().nameExists(initialName));
|
||||
assertNull(repository.getNameRepository().fromReducedName(initialReducedName));
|
||||
@ -516,4 +540,101 @@ public class UpdateTests extends Common {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdatePrimaryName() throws DataException {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
// mint passed the feature trigger block
|
||||
BlockUtils.mintBlocks(repository, BlockChain.getInstance().getMultipleNamesPerAccountHeight());
|
||||
|
||||
PrivateKeyAccount alice = Common.getTestAccount(repository, "alice");
|
||||
PrivateKeyAccount bob = Common.getTestAccount(repository, "bob");
|
||||
|
||||
// register name 1
|
||||
String initialName = "initial-name";
|
||||
RegisterNameTransactionData registerNameTransactionData1 = new RegisterNameTransactionData(TestTransaction.generateBase(alice), initialName, "{}");
|
||||
registerNameTransactionData1.setFee(new RegisterNameTransaction(null, null).getUnitFee(registerNameTransactionData1.getTimestamp()));
|
||||
TransactionUtils.signAndMint(repository, registerNameTransactionData1, alice);
|
||||
|
||||
// assert name 1 registration, assert primary name
|
||||
assertTrue(repository.getNameRepository().nameExists(initialName));
|
||||
|
||||
Optional<String> primaryNameOptional = alice.getPrimaryName();
|
||||
assertTrue(primaryNameOptional.isPresent());
|
||||
assertEquals(initialName, primaryNameOptional.get());
|
||||
|
||||
// register name 2
|
||||
String secondName = "second-name";
|
||||
RegisterNameTransactionData registerNameTransactionData2 = new RegisterNameTransactionData(TestTransaction.generateBase(alice), secondName, "{}");
|
||||
registerNameTransactionData2.setFee(new RegisterNameTransaction(null, null).getUnitFee(registerNameTransactionData2.getTimestamp()));
|
||||
TransactionUtils.signAndMint(repository, registerNameTransactionData2, alice);
|
||||
|
||||
// assert name 2 registration, assert primary has not changed
|
||||
assertTrue(repository.getNameRepository().nameExists(secondName));
|
||||
|
||||
// the name alice is trying to update to
|
||||
String newName = "updated-name";
|
||||
|
||||
// update name, assert invalid
|
||||
updateName(repository, initialName, newName, Transaction.ValidationResult.NOT_SUPPORTED, alice);
|
||||
|
||||
// check primary name did not update
|
||||
// check primary name update
|
||||
Optional<String> primaryNameNotUpdateOptional = alice.getPrimaryName();
|
||||
assertTrue(primaryNameNotUpdateOptional.isPresent());
|
||||
assertEquals(initialName, primaryNameNotUpdateOptional.get());
|
||||
|
||||
// sell name 2, assert valid
|
||||
Long amount = 1000000L;
|
||||
SellNameTransactionData transactionData = new SellNameTransactionData(TestTransaction.generateBase(alice), secondName, amount);
|
||||
TransactionUtils.signAndMint(repository, transactionData, alice);
|
||||
|
||||
// Check name is for sale
|
||||
NameData nameData = repository.getNameRepository().fromName(secondName);
|
||||
assertTrue(nameData.isForSale());
|
||||
assertEquals("price incorrect", amount, nameData.getSalePrice());
|
||||
|
||||
// bob buys name 2, assert
|
||||
BuyNameTransactionData bobBuysName2Data = new BuyNameTransactionData(TestTransaction.generateBase(bob), secondName, amount, alice.getAddress());
|
||||
TransactionUtils.signAndMint(repository, bobBuysName2Data, bob);
|
||||
|
||||
// update name, assert valid, assert primary name change
|
||||
updateName(repository, initialName, newName, Transaction.ValidationResult.OK, alice);
|
||||
|
||||
// check primary name update
|
||||
Optional<String> primaryNameUpdateOptional = alice.getPrimaryName();
|
||||
assertTrue(primaryNameUpdateOptional.isPresent());
|
||||
assertEquals(newName, primaryNameUpdateOptional.get());
|
||||
|
||||
assertEquals(alice.getPrimaryName(), alice.determinePrimaryName(TransactionsResource.ConfirmationStatus.CONFIRMED));
|
||||
assertEquals(bob.getPrimaryName(), bob.determinePrimaryName(TransactionsResource.ConfirmationStatus.CONFIRMED));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Name
|
||||
*
|
||||
* @param repository
|
||||
* @param initialName the name before the update
|
||||
* @param newName the name after the update
|
||||
* @param expectedValidationResult the validation result expected from the update
|
||||
* @param account the account for the update
|
||||
*
|
||||
* @throws DataException
|
||||
*/
|
||||
private static void updateName(Repository repository, String initialName, String newName, Transaction.ValidationResult expectedValidationResult, PrivateKeyAccount account) throws DataException {
|
||||
TransactionData data = new UpdateNameTransactionData(TestTransaction.generateBase(account), initialName, newName, "{}");
|
||||
Transaction.ValidationResult result = TransactionUtils.signAndImport(repository,data, account);
|
||||
|
||||
assertEquals("Transaction invalid", expectedValidationResult, result);
|
||||
|
||||
BlockUtils.mintBlock(repository);
|
||||
|
||||
if( Transaction.ValidationResult.OK.equals(expectedValidationResult) ) {
|
||||
assertTrue(repository.getNameRepository().nameExists(newName));
|
||||
}
|
||||
else {
|
||||
// the new name should not exist, because the update was invalid
|
||||
assertFalse(repository.getNameRepository().nameExists(newName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user