From 278201e87ca779c2b44107611e1aa7aae3c044d6 Mon Sep 17 00:00:00 2001 From: CalDescent Date: Sun, 5 Sep 2021 21:24:02 +0100 Subject: [PATCH 01/10] Workaround for block 535658 problem --- src/main/java/org/qortal/block/Block.java | 7 +- .../java/org/qortal/block/Block535658.java | 78 +++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/qortal/block/Block535658.java diff --git a/src/main/java/org/qortal/block/Block.java b/src/main/java/org/qortal/block/Block.java index 11aab89c..91e1b2e6 100644 --- a/src/main/java/org/qortal/block/Block.java +++ b/src/main/java/org/qortal/block/Block.java @@ -1092,9 +1092,14 @@ public class Block { // Create repository savepoint here so we can rollback to it after testing transactions repository.setSavepoint(); - if (this.blockData.getHeight() == 212937) + if (this.blockData.getHeight() == 212937) { // Apply fix for block 212937 but fix will be rolled back before we exit method Block212937.processFix(this); + } + else if (this.blockData.getHeight() == 535658) { + // Apply fix for block 535658 but fix will be rolled back before we exit method + Block535658.processFix(this); + } for (Transaction transaction : this.getTransactions()) { TransactionData transactionData = transaction.getTransactionData(); diff --git a/src/main/java/org/qortal/block/Block535658.java b/src/main/java/org/qortal/block/Block535658.java new file mode 100644 index 00000000..5404cc54 --- /dev/null +++ b/src/main/java/org/qortal/block/Block535658.java @@ -0,0 +1,78 @@ +package org.qortal.block; + +import org.qortal.naming.Name; +import org.qortal.repository.DataException; + +/** + * Block 535658 + *

+ * A node minted a version of block 535658 that contained one transaction: + * a REGISTER_NAME transaction that attempted to register a name that was already registered. + *

+ * This invalid transaction made block 535658 (rightly) invalid to several nodes, + * which refused to use that block. + * However, it seems there were no other nodes minting an alternative, valid block at that time + * and so the chain stalled for several nodes in the network. + *

+ * Additionally, the invalid block 535658 affected all new installations, regardless of whether + * they synchronized from scratch (block 1) or used an 'official release' bootstrap. + *

+ * The diagnosis found the following: + * - The original problem occurred in block 535205 where for some unknown reason many nodes didn't + * add the name from a REGISTER_NAME transaction to their Names table. + * - As a result, those nodes had a corrupt db, because they weren't holding a record of the name. + * - This invalid db then caused them to treat a candidate for block 535658 as valid when it + * should have been invalid. + * - As such, the chain continued on with a technically invalid block in it, for a subset of the network + *

+ * As with block 212937, there were three options, but the only feasible one was to apply edits to block + * 535658 to make it valid. There were several cross-chain trades completed after this block, so doing + * any kind of rollback was out of the question. + *

+ * To complicate things further, a custom data field was used for the first REGISTER_NAME transaction, + * and the default data field was used for the second. So it was important that all nodes ended up with + * the exact same data regardless of how they arrived there. + *

+ * The invalid block 535658 signature is: 3oiuDhok...NdXvCLEV. + *

+ * The invalid transaction in block 212937 is: + *

+ *

+	 {
+		 "type": "REGISTER_NAME",
+		 "timestamp": 1630739437517,
+		 "reference": "4peRechwSPxP6UkRj9Y8ox9YxkWb34sWk5zyMc1WyMxEsACxD4Gmm7LZVsQ6Skpze8QCSBMZasvEZg6RgdqkyADW",
+		 "fee": "0.00100000",
+		 "signature": "2t1CryCog8KPDBarzY5fDCKu499nfnUcGrz4Lz4w5wNb5nWqm7y126P48dChYY7huhufcBV3RJPkgKP4Ywxc1gXx",
+		 "txGroupId": 0,
+		 "blockHeight": 535658,
+		 "approvalStatus": "NOT_REQUIRED",
+		 "creatorAddress": "Qbx9ojxv7XNi1xDMWzzw7xDvd1zYW6SKFB",
+		 "registrantPublicKey": "HJqGEf6cW695Xun4ydhkB2excGFwsDxznhNCRHZStyyx",
+		 "name": "Qplay",
+		 "data": "Registered Name on the Qortal Chain"
+	 }
+   
+ *

+ * Account Qbx9ojxv7XNi1xDMWzzw7xDvd1zYW6SKFB attempted to register the name Qplay + * when they had already registered it 12 hours before in block 535205. + *

+ * However, on the broken DB nodes, their Names table was missing a record for the `Qplay` name + * which was sufficient to make the transaction valid. + */ +public final class Block535658 { + + private Block535658() { + /* Do not instantiate */ + } + + public static void processFix(Block block) throws DataException { + // Unregister the existing name record if it exists + // This ensures that the duplicate name is considered valid, and therefore + // the second (i.e. duplicate) REGISTER_NAME transaction data is applied. + // Both were issued by the same user account, so there is no conflict. + Name name = new Name(block.repository, "Qplay"); + name.unregister(); + } + +} From 6b74ef77e610bb693c92655fc2dc0e0837e050ea Mon Sep 17 00:00:00 2001 From: CalDescent Date: Sun, 5 Sep 2021 21:25:38 +0100 Subject: [PATCH 02/10] Increased log level of invalid transaction message. --- src/main/java/org/qortal/block/Block.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/qortal/block/Block.java b/src/main/java/org/qortal/block/Block.java index 91e1b2e6..e84250ca 100644 --- a/src/main/java/org/qortal/block/Block.java +++ b/src/main/java/org/qortal/block/Block.java @@ -1138,7 +1138,7 @@ public class Block { // Check transaction can even be processed validationResult = transaction.isProcessable(); if (validationResult != Transaction.ValidationResult.OK) { - LOGGER.debug(String.format("Error during transaction validation, tx %s: %s", Base58.encode(transactionData.getSignature()), validationResult.name())); + LOGGER.info(String.format("Error during transaction validation, tx %s: %s", Base58.encode(transactionData.getSignature()), validationResult.name())); return ValidationResult.TRANSACTION_INVALID; } From 25b787f6f2391d1899af1705bbf22e08fafc5682 Mon Sep 17 00:00:00 2001 From: QuickMythril Date: Sun, 5 Sep 2021 18:06:32 -0400 Subject: [PATCH 03/10] Add files via upload --- .../java/org/qortal/block/Block536140.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/main/java/org/qortal/block/Block536140.java diff --git a/src/main/java/org/qortal/block/Block536140.java b/src/main/java/org/qortal/block/Block536140.java new file mode 100644 index 00000000..ab1ca58f --- /dev/null +++ b/src/main/java/org/qortal/block/Block536140.java @@ -0,0 +1,21 @@ +package org.qortal.block; + +import org.qortal.naming.Name; +import org.qortal.repository.DataException; + +public final class Block536140 { + + private Block536140() { + /* Do not instantiate */ + } + + public static void processFix(Block block) throws DataException { + // Unregister the existing name record if it exists + // This ensures that the duplicate name is considered valid, and therefore + // the second (i.e. duplicate) REGISTER_NAME transaction data is applied. + // Both were issued by the same user account, so there is no conflict. + Name name = new Name(block.repository, "Qweb"); + name.unregister(); + } + +} From 673ee4aeed99d77f4f9561e244265879762fc681 Mon Sep 17 00:00:00 2001 From: QuickMythril Date: Sun, 5 Sep 2021 18:07:11 -0400 Subject: [PATCH 04/10] Update Block.java --- src/main/java/org/qortal/block/Block.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/org/qortal/block/Block.java b/src/main/java/org/qortal/block/Block.java index e84250ca..7b155a53 100644 --- a/src/main/java/org/qortal/block/Block.java +++ b/src/main/java/org/qortal/block/Block.java @@ -1100,6 +1100,10 @@ public class Block { // Apply fix for block 535658 but fix will be rolled back before we exit method Block535658.processFix(this); } + else if (this.blockData.getHeight() == 536140) { + // Apply fix for block 536140 but fix will be rolled back before we exit method + Block536140.processFix(this); + } for (Transaction transaction : this.getTransactions()) { TransactionData transactionData = transaction.getTransactionData(); From 172a629da3400dc38fb14948adeee612bf8ede4a Mon Sep 17 00:00:00 2001 From: CalDescent Date: Sun, 5 Sep 2021 23:32:11 +0100 Subject: [PATCH 05/10] Added comments --- .../java/org/qortal/block/Block536140.java | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/main/java/org/qortal/block/Block536140.java b/src/main/java/org/qortal/block/Block536140.java index ab1ca58f..c731f95a 100644 --- a/src/main/java/org/qortal/block/Block536140.java +++ b/src/main/java/org/qortal/block/Block536140.java @@ -3,6 +3,47 @@ package org.qortal.block; import org.qortal.naming.Name; import org.qortal.repository.DataException; +/** + * Block 536140 + *

+ * This block had the same problem as block 535658. + *

+ * Original transaction: + *

+ {
+     "type": "REGISTER_NAME",
+     "timestamp": 1630701955448,
+     "reference": "5CytqtRzhP1irQjiJfKBwNkKBVM9gfvkWQEwqT49VNAofcyNHtSpqrVKB9v44NkhxytHwvfneCndCQTp3J8wU9p7",
+     "fee": "0.00100000",
+     "signature": "sPhiAfQ7MenpJAarTZ99neQHBrmyQ3jDFxRp79BTDmkRf7fMsQinuZJvWbsCzGeihr6zEjuPCD2k9srNGkzLhSS",
+     "txGroupId": 0,
+     "blockHeight": 535172,
+     "approvalStatus": "NOT_REQUIRED",
+     "creatorAddress": "QSUnyUZugWanhDtPaySLdaAGyKLzN3SurS",
+     "registrantPublicKey": "C83r2taaX3pGQTgjmb7QNnFN8GWJqZxnhwptJEViJSqM",
+     "name": "Qweb",
+     "data": "{\"age\":30}"
+ }
+ 
+ *

+ * Duplicate transaction: + *

+ {
+	 "type": "REGISTER_NAME",
+	 "timestamp": 1630777397713,
+	 "reference": "sPhiAfQ7MenpJAarTZ99neQHBrmyQ3jDFxRp79BTDmkRf7fMsQinuZJvWbsCzGeihr6zEjuPCD2k9srNGkzLhSS",
+	 "fee": "0.00100000",
+	 "signature": "45knBoCoKxraJaJWuwANTyM75Su9TAz45bvU8mQLj9wxwNvkVwrFXneLQtiNzN6ctcmNcGLTR4npiJ7PdxtxbJQA",
+	 "txGroupId": 0,
+	 "blockHeight": 536140,
+	 "approvalStatus": "NOT_REQUIRED",
+	 "creatorAddress": "QSUnyUZugWanhDtPaySLdaAGyKLzN3SurS",
+	 "registrantPublicKey": "C83r2taaX3pGQTgjmb7QNnFN8GWJqZxnhwptJEViJSqM",
+	 "name": "Qweb",
+	 "data": "Registered Name on the Qortal Chain"
+ }
+ 
+ */ public final class Block536140 { private Block536140() { From b800fb5846c8cddb949ad03588df1b6da438b04d Mon Sep 17 00:00:00 2001 From: CalDescent Date: Thu, 9 Sep 2021 12:54:01 +0100 Subject: [PATCH 06/10] Treat a REGISTER_NAME transaction as an UPDATE_NAME if the creator matches. Whilst not ideal, this is necessary to prevent the chain from getting stuck on future blocks due to duplicate name registrations. See Block535658.java for full details on this problem - this is simply a "catch-all" implementation of that class in order to futureproof this fix. There is still a database inconsistency to be solved, as some nodes are failing to add a registered name to their Names table the first time around, but this will take some time. Once fixed, this commit could potentially be reverted. Also added unit tests for both scenarios (same and different creator). TLDR: this allows all past and future invalid blocks caused by NAME_ALREADY_REGISTERED (by the same creator) to now be valid. --- .../transaction/RegisterNameTransaction.java | 22 +++++++++++++- .../org/qortal/test/naming/MiscTests.java | 30 +++++++++++++++++-- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/qortal/transaction/RegisterNameTransaction.java b/src/main/java/org/qortal/transaction/RegisterNameTransaction.java index 66c1fc8b..ad20ef1c 100644 --- a/src/main/java/org/qortal/transaction/RegisterNameTransaction.java +++ b/src/main/java/org/qortal/transaction/RegisterNameTransaction.java @@ -2,11 +2,13 @@ package org.qortal.transaction; import java.util.Collections; import java.util.List; +import java.util.Objects; 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.RegisterNameTransactionData; import org.qortal.data.transaction.TransactionData; import org.qortal.naming.Name; @@ -77,14 +79,32 @@ public class RegisterNameTransaction extends Transaction { @Override public ValidationResult isProcessable() throws DataException { // Check the name isn't already taken - if (this.repository.getNameRepository().reducedNameExists(this.registerNameTransactionData.getReducedName())) + if (this.repository.getNameRepository().reducedNameExists(this.registerNameTransactionData.getReducedName())) { + // Name exists, but we'll allow the transaction if it has the same creator + // This is necessary to workaround an issue due to inconsistent data in the Names table on some nodes. + // Without this, the chain can get stuck for a subset of nodes when the name is registered + // for the second time. It's simplest to just treat REGISTER_NAME as UPDATE_NAME if the creator + // matches that of the original registration. + + NameData nameData = this.repository.getNameRepository().fromReducedName(this.registerNameTransactionData.getReducedName()); + if (Objects.equals(this.getCreator().getAddress(), nameData.getOwner())) { + // Transaction creator already owns the name, so it's safe to update it + // Treat this as valid, which also requires skipping the "one name per account" check below. + // Given that the name matches one already registered, we know that it won't exceed the limit. + return ValidationResult.OK; + } + + // Name is already registered to someone else return ValidationResult.NAME_ALREADY_REGISTERED; + } // If accounts are only allowed one registered name then check for this if (BlockChain.getInstance().oneNamePerAccount() && !this.repository.getNameRepository().getNamesByOwner(getRegistrant().getAddress()).isEmpty()) return ValidationResult.MULTIPLE_NAMES_FORBIDDEN; + // FUTURE: when adding more validation, make sure to check the `return ValidationResult.OK` above + return ValidationResult.OK; } diff --git a/src/test/java/org/qortal/test/naming/MiscTests.java b/src/test/java/org/qortal/test/naming/MiscTests.java index c46cbfab..23e0e720 100644 --- a/src/test/java/org/qortal/test/naming/MiscTests.java +++ b/src/test/java/org/qortal/test/naming/MiscTests.java @@ -44,9 +44,9 @@ public class MiscTests extends Common { } } - // test trying to register same name twice + // test trying to register same name twice (with same creator) @Test - public void testDuplicateRegisterName() throws DataException { + public void testDuplicateRegisterNameWithSameCreator() throws DataException { try (final Repository repository = RepositoryManager.getRepository()) { // Register-name PrivateKeyAccount alice = Common.getTestAccount(repository, "alice"); @@ -63,7 +63,31 @@ public class MiscTests extends Common { transaction.sign(alice); ValidationResult result = transaction.importAsUnconfirmed(); - assertTrue("Transaction should be invalid", ValidationResult.OK != result); + assertTrue("Transaction should be valid because it has the same creator", ValidationResult.OK == result); + } + } + + // test trying to register same name twice (with different creator) + @Test + public void testDuplicateRegisterNameWithDifferentCreator() throws DataException { + try (final Repository repository = RepositoryManager.getRepository()) { + // Register-name + PrivateKeyAccount alice = Common.getTestAccount(repository, "alice"); + String name = "test-name"; + String data = "{}"; + + RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data); + TransactionUtils.signAndMint(repository, transactionData, alice); + + // duplicate (this time registered by Bob) + PrivateKeyAccount bob = Common.getTestAccount(repository, "bob"); + String duplicateName = "TEST-nÁme"; + transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(bob), duplicateName, data); + Transaction transaction = Transaction.fromData(repository, transactionData); + transaction.sign(alice); + + ValidationResult result = transaction.importAsUnconfirmed(); + assertTrue("Transaction should be invalid because it has a different creator", ValidationResult.OK != result); } } From a6bbc819620b37a14b0c172d2513689ac34f3d30 Mon Sep 17 00:00:00 2001 From: CalDescent Date: Thu, 9 Sep 2021 12:55:08 +0100 Subject: [PATCH 07/10] Revert "Merge pull request #58 from QuickMythril/536140-fix" This reverts commit 6d1f7b36a7af3d0c12085f5718b2d42162ffd521, reversing changes made to 6b74ef77e610bb693c92655fc2dc0e0837e050ea. # Conflicts: # src/main/java/org/qortal/block/Block536140.java --- src/main/java/org/qortal/block/Block.java | 4 -- .../java/org/qortal/block/Block536140.java | 62 ------------------- 2 files changed, 66 deletions(-) delete mode 100644 src/main/java/org/qortal/block/Block536140.java diff --git a/src/main/java/org/qortal/block/Block.java b/src/main/java/org/qortal/block/Block.java index 7b155a53..e84250ca 100644 --- a/src/main/java/org/qortal/block/Block.java +++ b/src/main/java/org/qortal/block/Block.java @@ -1100,10 +1100,6 @@ public class Block { // Apply fix for block 535658 but fix will be rolled back before we exit method Block535658.processFix(this); } - else if (this.blockData.getHeight() == 536140) { - // Apply fix for block 536140 but fix will be rolled back before we exit method - Block536140.processFix(this); - } for (Transaction transaction : this.getTransactions()) { TransactionData transactionData = transaction.getTransactionData(); diff --git a/src/main/java/org/qortal/block/Block536140.java b/src/main/java/org/qortal/block/Block536140.java deleted file mode 100644 index c731f95a..00000000 --- a/src/main/java/org/qortal/block/Block536140.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.qortal.block; - -import org.qortal.naming.Name; -import org.qortal.repository.DataException; - -/** - * Block 536140 - *

- * This block had the same problem as block 535658. - *

- * Original transaction: - *

- {
-     "type": "REGISTER_NAME",
-     "timestamp": 1630701955448,
-     "reference": "5CytqtRzhP1irQjiJfKBwNkKBVM9gfvkWQEwqT49VNAofcyNHtSpqrVKB9v44NkhxytHwvfneCndCQTp3J8wU9p7",
-     "fee": "0.00100000",
-     "signature": "sPhiAfQ7MenpJAarTZ99neQHBrmyQ3jDFxRp79BTDmkRf7fMsQinuZJvWbsCzGeihr6zEjuPCD2k9srNGkzLhSS",
-     "txGroupId": 0,
-     "blockHeight": 535172,
-     "approvalStatus": "NOT_REQUIRED",
-     "creatorAddress": "QSUnyUZugWanhDtPaySLdaAGyKLzN3SurS",
-     "registrantPublicKey": "C83r2taaX3pGQTgjmb7QNnFN8GWJqZxnhwptJEViJSqM",
-     "name": "Qweb",
-     "data": "{\"age\":30}"
- }
- 
- *

- * Duplicate transaction: - *

- {
-	 "type": "REGISTER_NAME",
-	 "timestamp": 1630777397713,
-	 "reference": "sPhiAfQ7MenpJAarTZ99neQHBrmyQ3jDFxRp79BTDmkRf7fMsQinuZJvWbsCzGeihr6zEjuPCD2k9srNGkzLhSS",
-	 "fee": "0.00100000",
-	 "signature": "45knBoCoKxraJaJWuwANTyM75Su9TAz45bvU8mQLj9wxwNvkVwrFXneLQtiNzN6ctcmNcGLTR4npiJ7PdxtxbJQA",
-	 "txGroupId": 0,
-	 "blockHeight": 536140,
-	 "approvalStatus": "NOT_REQUIRED",
-	 "creatorAddress": "QSUnyUZugWanhDtPaySLdaAGyKLzN3SurS",
-	 "registrantPublicKey": "C83r2taaX3pGQTgjmb7QNnFN8GWJqZxnhwptJEViJSqM",
-	 "name": "Qweb",
-	 "data": "Registered Name on the Qortal Chain"
- }
- 
- */ -public final class Block536140 { - - private Block536140() { - /* Do not instantiate */ - } - - public static void processFix(Block block) throws DataException { - // Unregister the existing name record if it exists - // This ensures that the duplicate name is considered valid, and therefore - // the second (i.e. duplicate) REGISTER_NAME transaction data is applied. - // Both were issued by the same user account, so there is no conflict. - Name name = new Name(block.repository, "Qweb"); - name.unregister(); - } - -} From 63c9bc5c1cc047239be6dc17111b7fa6a36b577b Mon Sep 17 00:00:00 2001 From: CalDescent Date: Thu, 9 Sep 2021 12:55:21 +0100 Subject: [PATCH 08/10] Revert "Workaround for block 535658 problem" This reverts commit 278201e87ca779c2b44107611e1aa7aae3c044d6. --- src/main/java/org/qortal/block/Block.java | 7 +- .../java/org/qortal/block/Block535658.java | 78 ------------------- 2 files changed, 1 insertion(+), 84 deletions(-) delete mode 100644 src/main/java/org/qortal/block/Block535658.java diff --git a/src/main/java/org/qortal/block/Block.java b/src/main/java/org/qortal/block/Block.java index e84250ca..1a7b48fe 100644 --- a/src/main/java/org/qortal/block/Block.java +++ b/src/main/java/org/qortal/block/Block.java @@ -1092,14 +1092,9 @@ public class Block { // Create repository savepoint here so we can rollback to it after testing transactions repository.setSavepoint(); - if (this.blockData.getHeight() == 212937) { + if (this.blockData.getHeight() == 212937) // Apply fix for block 212937 but fix will be rolled back before we exit method Block212937.processFix(this); - } - else if (this.blockData.getHeight() == 535658) { - // Apply fix for block 535658 but fix will be rolled back before we exit method - Block535658.processFix(this); - } for (Transaction transaction : this.getTransactions()) { TransactionData transactionData = transaction.getTransactionData(); diff --git a/src/main/java/org/qortal/block/Block535658.java b/src/main/java/org/qortal/block/Block535658.java deleted file mode 100644 index 5404cc54..00000000 --- a/src/main/java/org/qortal/block/Block535658.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.qortal.block; - -import org.qortal.naming.Name; -import org.qortal.repository.DataException; - -/** - * Block 535658 - *

- * A node minted a version of block 535658 that contained one transaction: - * a REGISTER_NAME transaction that attempted to register a name that was already registered. - *

- * This invalid transaction made block 535658 (rightly) invalid to several nodes, - * which refused to use that block. - * However, it seems there were no other nodes minting an alternative, valid block at that time - * and so the chain stalled for several nodes in the network. - *

- * Additionally, the invalid block 535658 affected all new installations, regardless of whether - * they synchronized from scratch (block 1) or used an 'official release' bootstrap. - *

- * The diagnosis found the following: - * - The original problem occurred in block 535205 where for some unknown reason many nodes didn't - * add the name from a REGISTER_NAME transaction to their Names table. - * - As a result, those nodes had a corrupt db, because they weren't holding a record of the name. - * - This invalid db then caused them to treat a candidate for block 535658 as valid when it - * should have been invalid. - * - As such, the chain continued on with a technically invalid block in it, for a subset of the network - *

- * As with block 212937, there were three options, but the only feasible one was to apply edits to block - * 535658 to make it valid. There were several cross-chain trades completed after this block, so doing - * any kind of rollback was out of the question. - *

- * To complicate things further, a custom data field was used for the first REGISTER_NAME transaction, - * and the default data field was used for the second. So it was important that all nodes ended up with - * the exact same data regardless of how they arrived there. - *

- * The invalid block 535658 signature is: 3oiuDhok...NdXvCLEV. - *

- * The invalid transaction in block 212937 is: - *

- *

-	 {
-		 "type": "REGISTER_NAME",
-		 "timestamp": 1630739437517,
-		 "reference": "4peRechwSPxP6UkRj9Y8ox9YxkWb34sWk5zyMc1WyMxEsACxD4Gmm7LZVsQ6Skpze8QCSBMZasvEZg6RgdqkyADW",
-		 "fee": "0.00100000",
-		 "signature": "2t1CryCog8KPDBarzY5fDCKu499nfnUcGrz4Lz4w5wNb5nWqm7y126P48dChYY7huhufcBV3RJPkgKP4Ywxc1gXx",
-		 "txGroupId": 0,
-		 "blockHeight": 535658,
-		 "approvalStatus": "NOT_REQUIRED",
-		 "creatorAddress": "Qbx9ojxv7XNi1xDMWzzw7xDvd1zYW6SKFB",
-		 "registrantPublicKey": "HJqGEf6cW695Xun4ydhkB2excGFwsDxznhNCRHZStyyx",
-		 "name": "Qplay",
-		 "data": "Registered Name on the Qortal Chain"
-	 }
-   
- *

- * Account Qbx9ojxv7XNi1xDMWzzw7xDvd1zYW6SKFB attempted to register the name Qplay - * when they had already registered it 12 hours before in block 535205. - *

- * However, on the broken DB nodes, their Names table was missing a record for the `Qplay` name - * which was sufficient to make the transaction valid. - */ -public final class Block535658 { - - private Block535658() { - /* Do not instantiate */ - } - - public static void processFix(Block block) throws DataException { - // Unregister the existing name record if it exists - // This ensures that the duplicate name is considered valid, and therefore - // the second (i.e. duplicate) REGISTER_NAME transaction data is applied. - // Both were issued by the same user account, so there is no conflict. - Name name = new Name(block.repository, "Qplay"); - name.unregister(); - } - -} From e90c3a78d10863d20a9c3f05d50a70afeef5ca99 Mon Sep 17 00:00:00 2001 From: CalDescent Date: Thu, 9 Sep 2021 15:12:28 +0100 Subject: [PATCH 09/10] Updated default "data" field text in the API documentation, to match the value the UI uses. --- .../qortal/data/transaction/RegisterNameTransactionData.java | 2 +- .../org/qortal/data/transaction/UpdateNameTransactionData.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/qortal/data/transaction/RegisterNameTransactionData.java b/src/main/java/org/qortal/data/transaction/RegisterNameTransactionData.java index d4455da1..c2b06fd2 100644 --- a/src/main/java/org/qortal/data/transaction/RegisterNameTransactionData.java +++ b/src/main/java/org/qortal/data/transaction/RegisterNameTransactionData.java @@ -26,7 +26,7 @@ public class RegisterNameTransactionData extends TransactionData { @Schema(description = "requested name", example = "my-name") private String name; - @Schema(description = "simple name-related info in JSON format", example = "{ \"age\": 30 }") + @Schema(description = "simple name-related info in JSON or text format", example = "Registered Name on the Qortal Chain") private String data; // For internal use diff --git a/src/main/java/org/qortal/data/transaction/UpdateNameTransactionData.java b/src/main/java/org/qortal/data/transaction/UpdateNameTransactionData.java index 43c8da59..b43361db 100644 --- a/src/main/java/org/qortal/data/transaction/UpdateNameTransactionData.java +++ b/src/main/java/org/qortal/data/transaction/UpdateNameTransactionData.java @@ -26,7 +26,7 @@ public class UpdateNameTransactionData extends TransactionData { @Schema(description = "new name", example = "my-new-name") private String newName; - @Schema(description = "replacement simple name-related info in JSON format", example = "{ \"age\": 30 }") + @Schema(description = "replacement simple name-related info in JSON or text format", example = "Registered Name on the Qortal Chain") private String newData; // For internal use From ce60ab8e0071daddaf502b72566f5f2b5a8bdc81 Mon Sep 17 00:00:00 2001 From: CalDescent Date: Sun, 12 Sep 2021 10:16:07 +0100 Subject: [PATCH 10/10] Updated naming unit tests - Use the "{\"age\":30}" data to make the tests more similar to some real world data. - Added tests to ensure that registering and orphaning works as expected. --- .../org/qortal/test/naming/MiscTests.java | 68 +++++++++++++++++-- .../org/qortal/test/naming/UpdateTests.java | 14 ++-- 2 files changed, 70 insertions(+), 12 deletions(-) diff --git a/src/test/java/org/qortal/test/naming/MiscTests.java b/src/test/java/org/qortal/test/naming/MiscTests.java index 23e0e720..625130e1 100644 --- a/src/test/java/org/qortal/test/naming/MiscTests.java +++ b/src/test/java/org/qortal/test/naming/MiscTests.java @@ -13,6 +13,7 @@ import org.qortal.data.transaction.UpdateNameTransactionData; import org.qortal.repository.DataException; import org.qortal.repository.Repository; import org.qortal.repository.RepositoryManager; +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; @@ -32,7 +33,7 @@ public class MiscTests extends Common { // Register-name PrivateKeyAccount alice = Common.getTestAccount(repository, "alice"); String name = "initial-name"; - String data = "initial-data"; + String data = "{\"age\":30}"; RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data); TransactionUtils.signAndMint(repository, transactionData, alice); @@ -51,7 +52,7 @@ public class MiscTests extends Common { // Register-name PrivateKeyAccount alice = Common.getTestAccount(repository, "alice"); String name = "test-name"; - String data = "{}"; + String data = "{\"age\":30}"; RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data); TransactionUtils.signAndMint(repository, transactionData, alice); @@ -98,7 +99,7 @@ public class MiscTests extends Common { // Register-name PrivateKeyAccount alice = Common.getTestAccount(repository, "alice"); String name = "test-name"; - String data = "{}"; + String data = "{\"age\":30}"; TransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data); TransactionUtils.signAndMint(repository, transactionData, alice); @@ -127,7 +128,7 @@ public class MiscTests extends Common { // Register-name PrivateKeyAccount alice = Common.getTestAccount(repository, "alice"); String name = alice.getAddress(); - String data = "{}"; + String data = "{\"age\":30}"; RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data); Transaction transaction = Transaction.fromData(repository, transactionData); @@ -145,7 +146,7 @@ public class MiscTests extends Common { // Register-name PrivateKeyAccount alice = Common.getTestAccount(repository, "alice"); String name = "test-name"; - String data = "{}"; + String data = "{\"age\":30}"; TransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data); TransactionUtils.signAndMint(repository, transactionData, alice); @@ -162,4 +163,61 @@ public class MiscTests extends Common { } } + // test registering and then orphaning + @Test + public void testRegisterNameAndOrphan() throws DataException { + try (final Repository repository = RepositoryManager.getRepository()) { + + PrivateKeyAccount alice = Common.getTestAccount(repository, "alice"); + String name = "test-name"; + String data = "{\"age\":30}"; + + // Ensure the name doesn't exist + assertNull(repository.getNameRepository().fromName(name)); + + // Register the name + RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data); + TransactionUtils.signAndMint(repository, transactionData, alice); + + // Ensure the name exists and the data is correct + assertEquals(data, repository.getNameRepository().fromName(name).getData()); + + // Orphan the latest block + BlockUtils.orphanBlocks(repository, 1); + + // Ensure the name doesn't exist once again + assertNull(repository.getNameRepository().fromName(name)); + } + } + + // test registering and then orphaning multiple times (to simulate several re-orgs) + @Test + public void testMultipleRegisterNameAndOrphan() throws DataException { + try (final Repository repository = RepositoryManager.getRepository()) { + + PrivateKeyAccount alice = Common.getTestAccount(repository, "alice"); + String name = "test-name"; + String data = "{\"age\":30}"; + + for (int i = 0; i < 10; i++) { + + // Ensure the name doesn't exist + assertNull(repository.getNameRepository().fromName(name)); + + // Register the name + RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data); + TransactionUtils.signAndMint(repository, transactionData, alice); + + // Ensure the name exists and the data is correct + assertEquals(data, repository.getNameRepository().fromName(name).getData()); + + // Orphan the latest block + BlockUtils.orphanBlocks(repository, 1); + + // Ensure the name doesn't exist once again + assertNull(repository.getNameRepository().fromName(name)); + } + } + } + } diff --git a/src/test/java/org/qortal/test/naming/UpdateTests.java b/src/test/java/org/qortal/test/naming/UpdateTests.java index ffbf7177..134d3358 100644 --- a/src/test/java/org/qortal/test/naming/UpdateTests.java +++ b/src/test/java/org/qortal/test/naming/UpdateTests.java @@ -29,7 +29,7 @@ public class UpdateTests extends Common { // Register-name PrivateKeyAccount alice = Common.getTestAccount(repository, "alice"); String initialName = "initial-name"; - String initialData = "initial-data"; + String initialData = "{\"age\":30}"; TransactionData initialTransactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), initialName, initialData); TransactionUtils.signAndMint(repository, initialTransactionData, alice); @@ -68,7 +68,7 @@ public class UpdateTests extends Common { // Register-name PrivateKeyAccount alice = Common.getTestAccount(repository, "alice"); String initialName = "initial-name"; - String initialData = "initial-data"; + String initialData = "{\"age\":30}"; TransactionData initialTransactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), initialName, initialData); TransactionUtils.signAndMint(repository, initialTransactionData, alice); @@ -108,7 +108,7 @@ public class UpdateTests extends Common { // Register-name PrivateKeyAccount alice = Common.getTestAccount(repository, "alice"); String initialName = "initial-name"; - String initialData = "initial-data"; + String initialData = "{\"age\":30}"; TransactionData initialTransactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), initialName, initialData); TransactionUtils.signAndMint(repository, initialTransactionData, alice); @@ -171,7 +171,7 @@ public class UpdateTests extends Common { // Register-name PrivateKeyAccount alice = Common.getTestAccount(repository, "alice"); String initialName = "initial-name"; - String initialData = "initial-data"; + String initialData = "{\"age\":30}"; TransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), initialName, initialData); TransactionUtils.signAndMint(repository, transactionData, alice); @@ -217,7 +217,7 @@ public class UpdateTests extends Common { // Register-name PrivateKeyAccount alice = Common.getTestAccount(repository, "alice"); String initialName = "initial-name"; - String initialData = "initial-data"; + String initialData = "{\"age\":30}"; TransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), initialName, initialData); TransactionUtils.signAndMint(repository, transactionData, alice); @@ -251,7 +251,7 @@ public class UpdateTests extends Common { // Register-name PrivateKeyAccount alice = Common.getTestAccount(repository, "alice"); String initialName = "initial-name"; - String initialData = "initial-data"; + String initialData = "{\"age\":30}"; TransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), initialName, initialData); TransactionUtils.signAndMint(repository, transactionData, alice); @@ -294,7 +294,7 @@ public class UpdateTests extends Common { // Register-name PrivateKeyAccount alice = Common.getTestAccount(repository, "alice"); String initialName = "initial-name"; - String initialData = "initial-data"; + String initialData = "{\"age\":30}"; TransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), initialName, initialData); TransactionUtils.signAndMint(repository, transactionData, alice);