diff --git a/src/main/java/org/qortal/block/Block.java b/src/main/java/org/qortal/block/Block.java index af5c6b01..1be6991a 100644 --- a/src/main/java/org/qortal/block/Block.java +++ b/src/main/java/org/qortal/block/Block.java @@ -41,14 +41,12 @@ import org.qortal.data.block.BlockData; import org.qortal.data.block.BlockSummaryData; import org.qortal.data.block.BlockTransactionData; import org.qortal.data.network.OnlineAccountData; -import org.qortal.data.transaction.RegisterNameTransactionData; import org.qortal.data.transaction.TransactionData; import org.qortal.repository.ATRepository; import org.qortal.repository.DataException; import org.qortal.repository.Repository; import org.qortal.repository.TransactionRepository; import org.qortal.transaction.AtTransaction; -import org.qortal.transaction.RegisterNameTransaction; import org.qortal.transaction.Transaction; import org.qortal.transaction.Transaction.ApprovalStatus; import org.qortal.transaction.Transaction.TransactionType; @@ -1094,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 (InvalidNameRegistrationBlocks.isAffectedBlock(this.blockData.getHeight())) { + // Apply fix for affected name registration blocks, but fix will be rolled back before we exit method + InvalidNameRegistrationBlocks.processFix(this); + } for (Transaction transaction : this.getTransactions()) { TransactionData transactionData = transaction.getTransactionData(); diff --git a/src/main/java/org/qortal/block/InvalidNameRegistrationBlocks.java b/src/main/java/org/qortal/block/InvalidNameRegistrationBlocks.java new file mode 100644 index 00000000..ebef366f --- /dev/null +++ b/src/main/java/org/qortal/block/InvalidNameRegistrationBlocks.java @@ -0,0 +1,114 @@ +package org.qortal.block; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.qortal.naming.Name; +import org.qortal.repository.DataException; + +import java.util.HashMap; +import java.util.Map; + +/** + * Invalid Name Registration Blocks + *
+ * 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.
+ *
+ * This problem then occurred two more times, in blocks 536140 and 541334
+ * To reduce duplication, I have combined all three block fixes into a single class
+ *
+ */
+public final class InvalidNameRegistrationBlocks {
+
+ private static final Logger LOGGER = LogManager.getLogger(InvalidNameRegistrationBlocks.class);
+
+ public static Map