From d9f784ed2b6a92c687eaea582d9822461fa6a06f Mon Sep 17 00:00:00 2001 From: catbref Date: Wed, 13 May 2020 10:19:56 +0100 Subject: [PATCH] Registered names: changing 'owner' and allowing renaming. REGISTER_NAME has an "owner" field which can be different from the actual registrant (transaction creator's public key, used for signing transaction). This allowed people to register names to be owned by someone else, thus breaking the whole "one name per account" aspect. So now "owner" is removed from REGISTER_NAME, and the actual owner address is derived from transaction creator's public key, as you would expect. Similarly, UPDATE_NAME has a corresponding "newOwner" field which has been removed. In addition, UPDATE_NAME now allows users to change their registered name using a new "newName" field. Various changes made to DB, Name class, etc. to accomodate above, along with some minor bug-fixes and comment improvements/corrections. Needs new unit tests to cover both new functionality and old! --- .../java/org/qortal/data/naming/NameData.java | 4 ++ .../RegisterNameTransactionData.java | 9 +--- .../UpdateNameTransactionData.java | 20 ++++---- src/main/java/org/qortal/naming/Name.java | 38 ++++++++++----- .../hsqldb/HSQLDBDatabaseUpdates.java | 4 +- ...QLDBRegisterNameTransactionRepository.java | 12 ++--- ...HSQLDBUpdateNameTransactionRepository.java | 10 ++-- .../transaction/BuyNameTransaction.java | 18 ++++--- .../transaction/RegisterNameTransaction.java | 21 ++++---- .../transaction/UpdateNameTransaction.java | 48 ++++++++++++------- .../RegisterNameTransactionTransformer.java | 7 +-- .../UpdateNameTransactionTransformer.java | 22 +++++---- .../org/qortal/test/api/NamesApiTests.java | 6 +-- .../RegisterNameTestTransaction.java | 3 +- .../org/qortal/test/naming/BuySellTests.java | 2 +- .../org/qortal/test/naming/MiscTests.java | 2 +- 16 files changed, 123 insertions(+), 103 deletions(-) diff --git a/src/main/java/org/qortal/data/naming/NameData.java b/src/main/java/org/qortal/data/naming/NameData.java index dffe830e..8fc05c79 100644 --- a/src/main/java/org/qortal/data/naming/NameData.java +++ b/src/main/java/org/qortal/data/naming/NameData.java @@ -66,6 +66,10 @@ public class NameData { return this.name; } + public void setName(String name) { + this.name = name; + } + public String getData() { return this.data; } diff --git a/src/main/java/org/qortal/data/transaction/RegisterNameTransactionData.java b/src/main/java/org/qortal/data/transaction/RegisterNameTransactionData.java index 37cc65d2..f111acf6 100644 --- a/src/main/java/org/qortal/data/transaction/RegisterNameTransactionData.java +++ b/src/main/java/org/qortal/data/transaction/RegisterNameTransactionData.java @@ -19,8 +19,6 @@ public class RegisterNameTransactionData extends TransactionData { // Properties @Schema(description = "registrant's public key", example = "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP") private byte[] registrantPublicKey; - @Schema(description = "new owner's address", example = "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v") - private String owner; @Schema(description = "requested name", example = "my-name") private String name; @Schema(description = "simple name-related info in JSON format", example = "{ \"age\": 30 }") @@ -38,11 +36,10 @@ public class RegisterNameTransactionData extends TransactionData { } /** From repository */ - public RegisterNameTransactionData(BaseTransactionData baseTransactionData, String owner, String name, String data) { + public RegisterNameTransactionData(BaseTransactionData baseTransactionData, String name, String data) { super(TransactionType.REGISTER_NAME, baseTransactionData); this.registrantPublicKey = baseTransactionData.creatorPublicKey; - this.owner = owner; this.name = name; this.data = data; } @@ -53,10 +50,6 @@ public class RegisterNameTransactionData extends TransactionData { return this.registrantPublicKey; } - public String getOwner() { - return this.owner; - } - public String getName() { return this.name; } diff --git a/src/main/java/org/qortal/data/transaction/UpdateNameTransactionData.java b/src/main/java/org/qortal/data/transaction/UpdateNameTransactionData.java index 70c7404d..93c94c8e 100644 --- a/src/main/java/org/qortal/data/transaction/UpdateNameTransactionData.java +++ b/src/main/java/org/qortal/data/transaction/UpdateNameTransactionData.java @@ -17,10 +17,10 @@ public class UpdateNameTransactionData extends TransactionData { // Properties @Schema(description = "owner's public key", example = "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP") private byte[] ownerPublicKey; - @Schema(description = "new owner's address", example = "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v") - private String newOwner; @Schema(description = "which name to update", example = "my-name") private String name; + @Schema(description = "new name", example = "my-new-name") + private String newName; @Schema(description = "replacement simple name-related info in JSON format", example = "{ \"age\": 30 }") private String newData; // For internal use when orphaning @@ -40,19 +40,19 @@ public class UpdateNameTransactionData extends TransactionData { } /** From repository */ - public UpdateNameTransactionData(BaseTransactionData baseTransactionData, String newOwner, String name, String newData, byte[] nameReference) { + public UpdateNameTransactionData(BaseTransactionData baseTransactionData, String name, String newName, String newData, byte[] nameReference) { super(TransactionType.UPDATE_NAME, baseTransactionData); this.ownerPublicKey = baseTransactionData.creatorPublicKey; - this.newOwner = newOwner; this.name = name; + this.newName = newName; this.newData = newData; this.nameReference = nameReference; } /** From network/API */ - public UpdateNameTransactionData(BaseTransactionData baseTransactionData, String newOwner, String name, String newData) { - this(baseTransactionData, newOwner, name, newData, null); + public UpdateNameTransactionData(BaseTransactionData baseTransactionData, String name, String newName, String newData) { + this(baseTransactionData, name, newName, newData, null); } // Getters / setters @@ -61,14 +61,14 @@ public class UpdateNameTransactionData extends TransactionData { return this.ownerPublicKey; } - public String getNewOwner() { - return this.newOwner; - } - public String getName() { return this.name; } + public String getNewName() { + return this.newName; + } + public String getNewData() { return this.newData; } diff --git a/src/main/java/org/qortal/naming/Name.java b/src/main/java/org/qortal/naming/Name.java index b377907f..17d1aae2 100644 --- a/src/main/java/org/qortal/naming/Name.java +++ b/src/main/java/org/qortal/naming/Name.java @@ -3,6 +3,7 @@ package org.qortal.naming; import org.qortal.account.Account; import org.qortal.account.PublicKeyAccount; import org.qortal.asset.Asset; +import org.qortal.crypto.Crypto; import org.qortal.data.naming.NameData; import org.qortal.data.transaction.BuyNameTransactionData; import org.qortal.data.transaction.CancelSellNameTransactionData; @@ -20,6 +21,7 @@ public class Name { private NameData nameData; // Useful constants + public static final int MIN_NAME_SIZE = 3; public static final int MAX_NAME_SIZE = 400; public static final int MAX_DATA_SIZE = 4000; @@ -33,7 +35,10 @@ public class Name { */ public Name(Repository repository, RegisterNameTransactionData registerNameTransactionData) { this.repository = repository; - this.nameData = new NameData(registerNameTransactionData.getOwner(), + + String owner = Crypto.toAddress(registerNameTransactionData.getRegistrantPublicKey()); + + this.nameData = new NameData(owner, registerNameTransactionData.getName(), registerNameTransactionData.getData(), registerNameTransactionData.getTimestamp(), registerNameTransactionData.getSignature(), registerNameTransactionData.getTxGroupId()); } @@ -66,23 +71,31 @@ public class Name { throw new DataException("Unable to revert name transaction as referenced transaction not found in repository"); switch (previousTransactionData.getType()) { - case REGISTER_NAME: + case REGISTER_NAME: { RegisterNameTransactionData previousRegisterNameTransactionData = (RegisterNameTransactionData) previousTransactionData; - this.nameData.setOwner(previousRegisterNameTransactionData.getOwner()); + this.nameData.setName(previousRegisterNameTransactionData.getName()); this.nameData.setData(previousRegisterNameTransactionData.getData()); break; + } - case UPDATE_NAME: + case UPDATE_NAME: { UpdateNameTransactionData previousUpdateNameTransactionData = (UpdateNameTransactionData) previousTransactionData; - this.nameData.setData(previousUpdateNameTransactionData.getNewData()); - this.nameData.setOwner(previousUpdateNameTransactionData.getNewOwner()); - break; - case BUY_NAME: + if (!previousUpdateNameTransactionData.getNewName().isBlank()) + this.nameData.setName(previousUpdateNameTransactionData.getNewName()); + + if (!previousUpdateNameTransactionData.getNewData().isEmpty()) + this.nameData.setData(previousUpdateNameTransactionData.getNewData()); + + break; + } + + case BUY_NAME: { BuyNameTransactionData previousBuyNameTransactionData = (BuyNameTransactionData) previousTransactionData; Account buyer = new PublicKeyAccount(this.repository, previousBuyNameTransactionData.getBuyerPublicKey()); this.nameData.setOwner(buyer.getAddress()); break; + } default: throw new IllegalStateException("Unable to revert name transaction due to unsupported referenced transaction"); @@ -96,9 +109,12 @@ public class Name { // New name reference is this transaction's signature this.nameData.setReference(updateNameTransactionData.getSignature()); - // Update Name's owner and data - this.nameData.setOwner(updateNameTransactionData.getNewOwner()); - this.nameData.setData(updateNameTransactionData.getNewData()); + // Update name and data where appropriate + if (!updateNameTransactionData.getNewName().isEmpty()) + this.nameData.setOwner(updateNameTransactionData.getNewName()); + + if (!updateNameTransactionData.getNewData().isEmpty()) + this.nameData.setData(updateNameTransactionData.getNewData()); // Save updated name data this.repository.getNameRepository().save(this.nameData); diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabaseUpdates.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabaseUpdates.java index 730740cd..58af6c3d 100644 --- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabaseUpdates.java +++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabaseUpdates.java @@ -294,11 +294,11 @@ public class HSQLDBDatabaseUpdates { // Register Name Transactions stmt.execute("CREATE TABLE RegisterNameTransactions (signature Signature, registrant QortalPublicKey NOT NULL, name RegisteredName NOT NULL, " - + "owner QortalAddress NOT NULL, data NameData NOT NULL, " + TRANSACTION_KEYS + ")"); + + "data NameData NOT NULL, " + TRANSACTION_KEYS + ")"); // Update Name Transactions stmt.execute("CREATE TABLE UpdateNameTransactions (signature Signature, owner QortalPublicKey NOT NULL, name RegisteredName NOT NULL, " - + "new_owner QortalAddress NOT NULL, new_data NameData NOT NULL, name_reference Signature, " + TRANSACTION_KEYS + ")"); + + "new_name RegisteredName NOT NULL, new_data NameData NOT NULL, name_reference Signature, " + TRANSACTION_KEYS + ")"); // Sell Name Transactions stmt.execute("CREATE TABLE SellNameTransactions (signature Signature, owner QortalPublicKey NOT NULL, name RegisteredName NOT NULL, " diff --git a/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBRegisterNameTransactionRepository.java b/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBRegisterNameTransactionRepository.java index bddeaf07..bf13aeb3 100644 --- a/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBRegisterNameTransactionRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBRegisterNameTransactionRepository.java @@ -17,17 +17,16 @@ public class HSQLDBRegisterNameTransactionRepository extends HSQLDBTransactionRe } TransactionData fromBase(BaseTransactionData baseTransactionData) throws DataException { - String sql = "SELECT owner, name, data FROM RegisterNameTransactions WHERE signature = ?"; + String sql = "SELECT name, data FROM RegisterNameTransactions WHERE signature = ?"; try (ResultSet resultSet = this.repository.checkedExecute(sql, baseTransactionData.getSignature())) { if (resultSet == null) return null; - String owner = resultSet.getString(1); - String name = resultSet.getString(2); - String data = resultSet.getString(3); + String name = resultSet.getString(1); + String data = resultSet.getString(2); - return new RegisterNameTransactionData(baseTransactionData, owner, name, data); + return new RegisterNameTransactionData(baseTransactionData, name, data); } catch (SQLException e) { throw new DataException("Unable to fetch register name transaction from repository", e); } @@ -40,8 +39,7 @@ public class HSQLDBRegisterNameTransactionRepository extends HSQLDBTransactionRe HSQLDBSaver saveHelper = new HSQLDBSaver("RegisterNameTransactions"); saveHelper.bind("signature", registerNameTransactionData.getSignature()).bind("registrant", registerNameTransactionData.getRegistrantPublicKey()) - .bind("owner", registerNameTransactionData.getOwner()).bind("name", registerNameTransactionData.getName()) - .bind("data", registerNameTransactionData.getData()); + .bind("name", registerNameTransactionData.getName()).bind("data", registerNameTransactionData.getData()); try { saveHelper.execute(this.repository); diff --git a/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBUpdateNameTransactionRepository.java b/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBUpdateNameTransactionRepository.java index d0df6ff9..773ed5e4 100644 --- a/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBUpdateNameTransactionRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/transaction/HSQLDBUpdateNameTransactionRepository.java @@ -17,18 +17,18 @@ public class HSQLDBUpdateNameTransactionRepository extends HSQLDBTransactionRepo } TransactionData fromBase(BaseTransactionData baseTransactionData) throws DataException { - String sql = "SELECT new_owner, name, new_data, name_reference FROM UpdateNameTransactions WHERE signature = ?"; + String sql = "SELECT name, new_name, new_data, name_reference FROM UpdateNameTransactions WHERE signature = ?"; try (ResultSet resultSet = this.repository.checkedExecute(sql, baseTransactionData.getSignature())) { if (resultSet == null) return null; - String newOwner = resultSet.getString(1); - String name = resultSet.getString(2); + String name = resultSet.getString(1); + String newName = resultSet.getString(2); String newData = resultSet.getString(3); byte[] nameReference = resultSet.getBytes(4); - return new UpdateNameTransactionData(baseTransactionData, newOwner, name, newData, nameReference); + return new UpdateNameTransactionData(baseTransactionData, name, newName, newData, nameReference); } catch (SQLException e) { throw new DataException("Unable to fetch update name transaction from repository", e); } @@ -41,7 +41,7 @@ public class HSQLDBUpdateNameTransactionRepository extends HSQLDBTransactionRepo HSQLDBSaver saveHelper = new HSQLDBSaver("UpdateNameTransactions"); saveHelper.bind("signature", updateNameTransactionData.getSignature()).bind("owner", updateNameTransactionData.getOwnerPublicKey()) - .bind("new_owner", updateNameTransactionData.getNewOwner()).bind("name", updateNameTransactionData.getName()) + .bind("name", updateNameTransactionData.getName()).bind("new_name", updateNameTransactionData.getNewName()) .bind("new_data", updateNameTransactionData.getNewData()).bind("name_reference", updateNameTransactionData.getNameReference()); try { diff --git a/src/main/java/org/qortal/transaction/BuyNameTransaction.java b/src/main/java/org/qortal/transaction/BuyNameTransaction.java index 63b6447e..e2be539f 100644 --- a/src/main/java/org/qortal/transaction/BuyNameTransaction.java +++ b/src/main/java/org/qortal/transaction/BuyNameTransaction.java @@ -5,6 +5,7 @@ import java.util.List; import org.qortal.account.Account; import org.qortal.asset.Asset; +import org.qortal.block.BlockChain; import org.qortal.crypto.Crypto; import org.qortal.data.naming.NameData; import org.qortal.data.transaction.BuyNameTransactionData; @@ -54,7 +55,7 @@ public class BuyNameTransaction extends Transaction { // Check name size bounds int nameLength = Utf8.encodedLength(name); - if (nameLength < 1 || nameLength > Name.MAX_NAME_SIZE) + if (nameLength < Name.MIN_NAME_SIZE || nameLength > Name.MAX_NAME_SIZE) return ValidationResult.INVALID_NAME_LENGTH; // Check name is lowercase @@ -76,6 +77,11 @@ public class BuyNameTransaction extends Transaction { if (buyer.getAddress().equals(nameData.getOwner())) return ValidationResult.BUYER_ALREADY_OWNER; + // If accounts are only allowed one registered name then check for this + if (BlockChain.getInstance().oneNamePerAccount() + && !this.repository.getNameRepository().getNamesByOwner(buyer.getAddress()).isEmpty()) + return ValidationResult.MULTIPLE_NAMES_FORBIDDEN; + // Check expected seller currently owns name if (!this.buyNameTransactionData.getSeller().equals(nameData.getOwner())) return ValidationResult.INVALID_SELLER; @@ -84,7 +90,7 @@ public class BuyNameTransaction extends Transaction { if (this.buyNameTransactionData.getAmount() != nameData.getSalePrice()) return ValidationResult.INVALID_AMOUNT; - // Check issuer has enough funds + // Check buyer has enough funds if (buyer.getConfirmedBalance(Asset.QORT) < this.buyNameTransactionData.getFee()) return ValidationResult.NO_BALANCE; @@ -93,21 +99,21 @@ public class BuyNameTransaction extends Transaction { @Override public void process() throws DataException { - // Update Name + // Buy Name Name name = new Name(this.repository, this.buyNameTransactionData.getName()); name.buy(this.buyNameTransactionData); - // Save transaction with updated "name reference" pointing to previous transaction that updated name + // Save transaction with updated "name reference" pointing to previous transaction that changed name this.repository.getTransactionRepository().save(this.buyNameTransactionData); } @Override public void orphan() throws DataException { - // Revert name + // Un-buy name Name name = new Name(this.repository, this.buyNameTransactionData.getName()); name.unbuy(this.buyNameTransactionData); - // Save this transaction, with removed "name reference" + // Save this transaction, with previous "name reference" this.repository.getTransactionRepository().save(this.buyNameTransactionData); } diff --git a/src/main/java/org/qortal/transaction/RegisterNameTransaction.java b/src/main/java/org/qortal/transaction/RegisterNameTransaction.java index 053413eb..5587d0ec 100644 --- a/src/main/java/org/qortal/transaction/RegisterNameTransaction.java +++ b/src/main/java/org/qortal/transaction/RegisterNameTransaction.java @@ -6,7 +6,6 @@ import java.util.List; import org.qortal.account.Account; import org.qortal.asset.Asset; import org.qortal.block.BlockChain; -import org.qortal.crypto.Crypto; import org.qortal.data.transaction.RegisterNameTransactionData; import org.qortal.data.transaction.TransactionData; import org.qortal.naming.Name; @@ -32,7 +31,7 @@ public class RegisterNameTransaction extends Transaction { @Override public List getRecipientAddresses() throws DataException { - return Collections.singletonList(this.registerNameTransactionData.getOwner()); + return Collections.emptyList(); } // Navigation @@ -46,23 +45,20 @@ public class RegisterNameTransaction extends Transaction { @Override public ValidationResult isValid() throws DataException { Account registrant = getRegistrant(); - - // Check owner address is valid - if (!Crypto.isValidAddress(this.registerNameTransactionData.getOwner())) - return ValidationResult.INVALID_ADDRESS; + String name = this.registerNameTransactionData.getName(); // Check name size bounds - int nameLength = Utf8.encodedLength(this.registerNameTransactionData.getName()); - if (nameLength < 1 || nameLength > Name.MAX_NAME_SIZE) + int nameLength = Utf8.encodedLength(name); + if (nameLength < Name.MIN_NAME_SIZE || nameLength > Name.MAX_NAME_SIZE) return ValidationResult.INVALID_NAME_LENGTH; // Check data size bounds int dataLength = Utf8.encodedLength(this.registerNameTransactionData.getData()); - if (dataLength < 1 || dataLength > Name.MAX_DATA_SIZE) + if (dataLength > Name.MAX_DATA_SIZE) return ValidationResult.INVALID_DATA_LENGTH; // Check name is lowercase - if (!this.registerNameTransactionData.getName().equals(this.registerNameTransactionData.getName().toLowerCase())) + if (!name.equals(name.toLowerCase())) return ValidationResult.NAME_NOT_LOWER_CASE; // Check registrant has enough funds @@ -78,10 +74,9 @@ public class RegisterNameTransaction extends Transaction { if (this.repository.getNameRepository().nameExists(this.registerNameTransactionData.getName())) return ValidationResult.NAME_ALREADY_REGISTERED; - Account registrant = getRegistrant(); - // If accounts are only allowed one registered name then check for this - if (BlockChain.getInstance().oneNamePerAccount() && !this.repository.getNameRepository().getNamesByOwner(registrant.getAddress()).isEmpty()) + if (BlockChain.getInstance().oneNamePerAccount() + && !this.repository.getNameRepository().getNamesByOwner(getRegistrant().getAddress()).isEmpty()) return ValidationResult.MULTIPLE_NAMES_FORBIDDEN; return ValidationResult.OK; diff --git a/src/main/java/org/qortal/transaction/UpdateNameTransaction.java b/src/main/java/org/qortal/transaction/UpdateNameTransaction.java index 2b731bd1..8ecc7472 100644 --- a/src/main/java/org/qortal/transaction/UpdateNameTransaction.java +++ b/src/main/java/org/qortal/transaction/UpdateNameTransaction.java @@ -5,7 +5,6 @@ import java.util.List; import org.qortal.account.Account; import org.qortal.asset.Asset; -import org.qortal.crypto.Crypto; import org.qortal.data.naming.NameData; import org.qortal.data.transaction.TransactionData; import org.qortal.data.transaction.UpdateNameTransactionData; @@ -32,7 +31,7 @@ public class UpdateNameTransaction extends Transaction { @Override public List getRecipientAddresses() throws DataException { - return Collections.singletonList(this.updateNameTransactionData.getNewOwner()); + return Collections.emptyList(); } // Navigation @@ -41,30 +40,17 @@ public class UpdateNameTransaction extends Transaction { return this.getCreator(); } - public Account getNewOwner() { - return new Account(this.repository, this.updateNameTransactionData.getNewOwner()); - } - // Processing @Override public ValidationResult isValid() throws DataException { String name = this.updateNameTransactionData.getName(); - // Check new owner address is valid - if (!Crypto.isValidAddress(this.updateNameTransactionData.getNewOwner())) - return ValidationResult.INVALID_ADDRESS; - // Check name size bounds int nameLength = Utf8.encodedLength(name); - if (nameLength < 1 || nameLength > Name.MAX_NAME_SIZE) + if (nameLength < Name.MIN_NAME_SIZE || nameLength > Name.MAX_NAME_SIZE) return ValidationResult.INVALID_NAME_LENGTH; - // Check new data size bounds - int newDataLength = Utf8.encodedLength(this.updateNameTransactionData.getNewData()); - if (newDataLength < 1 || newDataLength > Name.MAX_DATA_SIZE) - return ValidationResult.INVALID_DATA_LENGTH; - // Check name is lowercase if (!name.equals(name.toLowerCase())) return ValidationResult.NAME_NOT_LOWER_CASE; @@ -79,6 +65,24 @@ public class UpdateNameTransaction extends Transaction { if (nameData.getCreationGroupId() != this.updateNameTransactionData.getTxGroupId()) return ValidationResult.TX_GROUP_ID_MISMATCH; + // Check new name (0 length means don't update name) + String newName = this.updateNameTransactionData.getNewName(); + int newNameLength = Utf8.encodedLength(newName); + if (newNameLength != 0) { + // Check new name size bounds + if (newNameLength < Name.MIN_NAME_SIZE || newNameLength > Name.MAX_NAME_SIZE) + return ValidationResult.INVALID_NAME_LENGTH; + + // Check new name is lowercase + if (!newName.equals(newName.toLowerCase())) + return ValidationResult.NAME_NOT_LOWER_CASE; + } + + // Check new data size bounds (0 length means don't update data) + int newDataLength = Utf8.encodedLength(this.updateNameTransactionData.getNewData()); + if (newDataLength > Name.MAX_DATA_SIZE) + return ValidationResult.INVALID_DATA_LENGTH; + Account owner = getOwner(); // Check owner has enough funds @@ -92,6 +96,10 @@ public class UpdateNameTransaction extends Transaction { public ValidationResult isProcessable() throws DataException { NameData nameData = this.repository.getNameRepository().fromName(this.updateNameTransactionData.getName()); + // Check name still exists + if (nameData == null) + return ValidationResult.NAME_DOES_NOT_EXIST; + // Check name isn't currently for sale if (nameData.getIsForSale()) return ValidationResult.NAME_ALREADY_FOR_SALE; @@ -102,6 +110,10 @@ public class UpdateNameTransaction extends Transaction { if (!owner.getAddress().equals(nameData.getOwner())) return ValidationResult.INVALID_NAME_OWNER; + // Check new name isn't already taken + if (this.repository.getNameRepository().nameExists(this.updateNameTransactionData.getNewName())) + return ValidationResult.NAME_ALREADY_REGISTERED; + return ValidationResult.OK; } @@ -111,7 +123,7 @@ public class UpdateNameTransaction extends Transaction { Name name = new Name(this.repository, this.updateNameTransactionData.getName()); name.update(this.updateNameTransactionData); - // Save this transaction, now with updated "name reference" to previous transaction that updated name + // Save this transaction, now with updated "name reference" to previous transaction that changed name this.repository.getTransactionRepository().save(this.updateNameTransactionData); } @@ -121,7 +133,7 @@ public class UpdateNameTransaction extends Transaction { Name name = new Name(this.repository, this.updateNameTransactionData.getName()); name.revert(this.updateNameTransactionData); - // Save this transaction, now with removed "name reference" + // Save this transaction, with previous "name reference" this.repository.getTransactionRepository().save(this.updateNameTransactionData); } diff --git a/src/main/java/org/qortal/transform/transaction/RegisterNameTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/RegisterNameTransactionTransformer.java index 1bf4057e..6722c601 100644 --- a/src/main/java/org/qortal/transform/transaction/RegisterNameTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/RegisterNameTransactionTransformer.java @@ -33,7 +33,6 @@ public class RegisterNameTransactionTransformer extends TransactionTransformer { layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("name registrant's public key", TransformationType.PUBLIC_KEY); - layout.add("name owner", TransformationType.ADDRESS); layout.add("name length", TransformationType.INT); layout.add("name", TransformationType.STRING); layout.add("data length", TransformationType.INT); @@ -52,8 +51,6 @@ public class RegisterNameTransactionTransformer extends TransactionTransformer { byte[] registrantPublicKey = Serialization.deserializePublicKey(byteBuffer); - String owner = Serialization.deserializeAddress(byteBuffer); - String name = Serialization.deserializeSizedString(byteBuffer, Name.MAX_NAME_SIZE); String data = Serialization.deserializeSizedString(byteBuffer, Name.MAX_DATA_SIZE); @@ -65,7 +62,7 @@ public class RegisterNameTransactionTransformer extends TransactionTransformer { BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, registrantPublicKey, fee, signature); - return new RegisterNameTransactionData(baseTransactionData, owner, name, data); + return new RegisterNameTransactionData(baseTransactionData, name, data); } public static int getDataLength(TransactionData transactionData) throws TransformationException { @@ -83,8 +80,6 @@ public class RegisterNameTransactionTransformer extends TransactionTransformer { transformCommonBytes(transactionData, bytes); - Serialization.serializeAddress(bytes, registerNameTransactionData.getOwner()); - Serialization.serializeSizedString(bytes, registerNameTransactionData.getName()); Serialization.serializeSizedString(bytes, registerNameTransactionData.getData()); diff --git a/src/main/java/org/qortal/transform/transaction/UpdateNameTransactionTransformer.java b/src/main/java/org/qortal/transform/transaction/UpdateNameTransactionTransformer.java index 56a4f988..6e3aa06b 100644 --- a/src/main/java/org/qortal/transform/transaction/UpdateNameTransactionTransformer.java +++ b/src/main/java/org/qortal/transform/transaction/UpdateNameTransactionTransformer.java @@ -18,11 +18,11 @@ import com.google.common.primitives.Longs; public class UpdateNameTransactionTransformer extends TransactionTransformer { // Property lengths - private static final int OWNER_LENGTH = ADDRESS_LENGTH; private static final int NAME_SIZE_LENGTH = INT_LENGTH; - private static final int DATA_SIZE_LENGTH = INT_LENGTH; + private static final int NEW_NAME_SIZE_LENGTH = INT_LENGTH; + private static final int NEW_DATA_SIZE_LENGTH = INT_LENGTH; - private static final int EXTRAS_LENGTH = OWNER_LENGTH + NAME_SIZE_LENGTH + DATA_SIZE_LENGTH; + private static final int EXTRAS_LENGTH = NAME_SIZE_LENGTH + NEW_NAME_SIZE_LENGTH + NEW_DATA_SIZE_LENGTH; protected static final TransactionLayout layout; @@ -33,10 +33,11 @@ public class UpdateNameTransactionTransformer extends TransactionTransformer { layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); layout.add("name owner's public key", TransformationType.PUBLIC_KEY); - layout.add("name's new owner", TransformationType.ADDRESS); layout.add("name length", TransformationType.INT); layout.add("name", TransformationType.STRING); - layout.add("new data length", TransformationType.INT); + layout.add("new name's length (0 for no change)", TransformationType.INT); + layout.add("new name", TransformationType.STRING); + layout.add("new data length (0 for no change)", TransformationType.INT); layout.add("new data", TransformationType.STRING); layout.add("fee", TransformationType.AMOUNT); layout.add("signature", TransformationType.SIGNATURE); @@ -52,10 +53,10 @@ public class UpdateNameTransactionTransformer extends TransactionTransformer { byte[] ownerPublicKey = Serialization.deserializePublicKey(byteBuffer); - String newOwner = Serialization.deserializeAddress(byteBuffer); - String name = Serialization.deserializeSizedString(byteBuffer, Name.MAX_NAME_SIZE); + String newName = Serialization.deserializeSizedString(byteBuffer, Name.MAX_NAME_SIZE); + String newData = Serialization.deserializeSizedString(byteBuffer, Name.MAX_DATA_SIZE); long fee = byteBuffer.getLong(); @@ -65,13 +66,14 @@ public class UpdateNameTransactionTransformer extends TransactionTransformer { BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, ownerPublicKey, fee, signature); - return new UpdateNameTransactionData(baseTransactionData, newOwner, name, newData); + return new UpdateNameTransactionData(baseTransactionData, name, newName, newData); } public static int getDataLength(TransactionData transactionData) throws TransformationException { UpdateNameTransactionData updateNameTransactionData = (UpdateNameTransactionData) transactionData; return getBaseLength(transactionData) + EXTRAS_LENGTH + Utf8.encodedLength(updateNameTransactionData.getName()) + + Utf8.encodedLength(updateNameTransactionData.getNewName()) + Utf8.encodedLength(updateNameTransactionData.getNewData()); } @@ -83,10 +85,10 @@ public class UpdateNameTransactionTransformer extends TransactionTransformer { transformCommonBytes(transactionData, bytes); - Serialization.serializeAddress(bytes, updateNameTransactionData.getNewOwner()); - Serialization.serializeSizedString(bytes, updateNameTransactionData.getName()); + Serialization.serializeSizedString(bytes, updateNameTransactionData.getNewName()); + Serialization.serializeSizedString(bytes, updateNameTransactionData.getNewData()); bytes.write(Longs.toByteArray(updateNameTransactionData.getFee())); diff --git a/src/test/java/org/qortal/test/api/NamesApiTests.java b/src/test/java/org/qortal/test/api/NamesApiTests.java index ae7248b4..2d31c8c2 100644 --- a/src/test/java/org/qortal/test/api/NamesApiTests.java +++ b/src/test/java/org/qortal/test/api/NamesApiTests.java @@ -46,7 +46,7 @@ public class NamesApiTests extends ApiCommon { PrivateKeyAccount alice = Common.getTestAccount(repository, "alice"); String name = "test-name"; - RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), alice.getAddress(), name, "{}"); + RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, "{}"); TransactionUtils.signAndMint(repository, transactionData, alice); assertNotNull(this.namesResource.getNamesByAddress(alice.getAddress(), null, null, null)); @@ -61,7 +61,7 @@ public class NamesApiTests extends ApiCommon { PrivateKeyAccount alice = Common.getTestAccount(repository, "alice"); String name = "test-name"; - RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), alice.getAddress(), name, "{}"); + RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, "{}"); TransactionUtils.signAndMint(repository, transactionData, alice); assertNotNull(this.namesResource.getName(name)); @@ -76,7 +76,7 @@ public class NamesApiTests extends ApiCommon { String name = "test-name"; long price = 1_23456789L; - TransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), alice.getAddress(), name, "{}"); + TransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, "{}"); TransactionUtils.signAndMint(repository, transactionData, alice); // Sell-name diff --git a/src/test/java/org/qortal/test/common/transaction/RegisterNameTestTransaction.java b/src/test/java/org/qortal/test/common/transaction/RegisterNameTestTransaction.java index 14f93657..0450fbee 100644 --- a/src/test/java/org/qortal/test/common/transaction/RegisterNameTestTransaction.java +++ b/src/test/java/org/qortal/test/common/transaction/RegisterNameTestTransaction.java @@ -9,14 +9,13 @@ import org.qortal.repository.Repository; public class RegisterNameTestTransaction extends TestTransaction { public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException { - String owner = account.getAddress(); String name = "test name"; if (!wantValid) name += " " + random.nextInt(1_000_000); String data = "{ \"key\": \"value\" }"; - return new RegisterNameTransactionData(generateBase(account), owner, name, data); + return new RegisterNameTransactionData(generateBase(account), name, data); } } diff --git a/src/test/java/org/qortal/test/naming/BuySellTests.java b/src/test/java/org/qortal/test/naming/BuySellTests.java index 0d323c5b..23022613 100644 --- a/src/test/java/org/qortal/test/naming/BuySellTests.java +++ b/src/test/java/org/qortal/test/naming/BuySellTests.java @@ -61,7 +61,7 @@ public class BuySellTests extends Common { @Test public void testRegisterName() throws DataException { // Register-name - RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), alice.getAddress(), name, "{}"); + RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, "{}"); TransactionUtils.signAndMint(repository, transactionData, alice); String name = transactionData.getName(); diff --git a/src/test/java/org/qortal/test/naming/MiscTests.java b/src/test/java/org/qortal/test/naming/MiscTests.java index d9cbf6fc..d183ed82 100644 --- a/src/test/java/org/qortal/test/naming/MiscTests.java +++ b/src/test/java/org/qortal/test/naming/MiscTests.java @@ -29,7 +29,7 @@ public class MiscTests extends Common { PrivateKeyAccount alice = Common.getTestAccount(repository, "alice"); String name = "test-name"; - RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), alice.getAddress(), name, "{}"); + RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, "{}"); TransactionUtils.signAndMint(repository, transactionData, alice); List recentNames = repository.getNameRepository().getRecentNames(0L);