From b917da765c9406adacbb5abd261c3b6ed236ec74 Mon Sep 17 00:00:00 2001 From: CalDescent Date: Tue, 15 Jun 2021 09:29:07 +0100 Subject: [PATCH] Removed block 212937 --- src/main/java/org/qortal/block/Block.java | 11 -- .../java/org/qortal/block/Block212937.java | 153 ------------------ 2 files changed, 164 deletions(-) delete mode 100644 src/main/java/org/qortal/block/Block212937.java diff --git a/src/main/java/org/qortal/block/Block.java b/src/main/java/org/qortal/block/Block.java index 798a4f91..5f6e1641 100644 --- a/src/main/java/org/qortal/block/Block.java +++ b/src/main/java/org/qortal/block/Block.java @@ -1104,10 +1104,6 @@ public class Block { // Create repository savepoint here so we can rollback to it after testing transactions repository.setSavepoint(); - if (this.blockData.getHeight() == 212937) - // Apply fix for block 212937 but fix will be rolled back before we exit method - Block212937.processFix(this); - for (Transaction transaction : this.getTransactions()) { TransactionData transactionData = transaction.getTransactionData(); @@ -1312,9 +1308,6 @@ public class Block { // Distribute block rewards, including transaction fees, before transactions processed processBlockRewards(); - if (this.blockData.getHeight() == 212937) - // Apply fix for block 212937 - Block212937.processFix(this); } // We're about to (test-)process a batch of transactions, @@ -1549,10 +1542,6 @@ public class Block { // Invalidate expandedAccounts as they may have changed due to orphaning TRANSFER_PRIVS transactions, etc. this.cachedExpandedAccounts = null; - if (this.blockData.getHeight() == 212937) - // Revert fix for block 212937 - Block212937.orphanFix(this); - // Block rewards, including transaction fees, removed after transactions undone orphanBlockRewards(); diff --git a/src/main/java/org/qortal/block/Block212937.java b/src/main/java/org/qortal/block/Block212937.java deleted file mode 100644 index a53c9d31..00000000 --- a/src/main/java/org/qortal/block/Block212937.java +++ /dev/null @@ -1,153 +0,0 @@ -package org.qortal.block; - -import java.io.InputStream; -import java.util.List; -import java.util.stream.Collectors; - -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBException; -import javax.xml.bind.UnmarshalException; -import javax.xml.bind.Unmarshaller; -import javax.xml.transform.stream.StreamSource; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.eclipse.persistence.jaxb.JAXBContextFactory; -import org.eclipse.persistence.jaxb.UnmarshallerProperties; -import org.qortal.data.account.AccountBalanceData; -import org.qortal.repository.DataException; - -/** - * Block 212937 - *

- * Somehow a node minted a version of block 212937 that contained one transaction: - * a PAYMENT transaction that attempted to spend more QORT than that account had as QORT balance. - *

- * This invalid transaction made block 212937 (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 212937 affected all new installations, regardless of whether - * they synchronized from scratch (block 1) or used an 'official release' bootstrap. - *

- * After lengthy diagnosis, it was discovered that - * the invalid transaction seemed to rely on incorrect balances in a corrupted database. - * Copies of DB files containing the broken chain were also shared around, exacerbating the problem. - *

- * There were three options: - *

    - *
  1. roll back the chain to last known valid block 212936 and re-mint empty blocks to current height
  2. - *
  3. keep existing chain, but apply database edits at block 212937 to allow current chain to be valid
  4. - *
  5. attempt to mint an alternative chain, retaining as many valid transactions as possible
  6. - *
- *

- * Option 1 was highly undesirable due to knock-on effects from wiping 700+ transactions, some of which - * might have affect cross-chain trades, although there were no cross-chain trade completed during - * the decision period. - *

- * Option 3 was essentially a slightly better version of option 1 and rejected for similar reasons. - * Attempts at option 3 also rapidly hit cumulative problems with every replacement block due to - * differing block timestamps making some transactions, and then even some blocks themselves, invalid. - *

- * This class is the implementation of option 2. - *

- * The change in account balances are relatively small, see block-212937-deltas.json resource - * for actual values. These values were obtained by exporting the AccountBalances table from - * both versions of the database with chain at block 212936, and then comparing. The values were also - * tested by syncing both databases up to block 225500, re-exporting and re-comparing. - *

- * The invalid block 212937 signature is: 2J3GVJjv...qavh6KkQ. - *

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

- *

-   {
-      "amount" : "0.10788294",
-      "approvalStatus" : "NOT_REQUIRED",
-      "blockHeight" : 212937,
-      "creatorAddress" : "QLdw5uabviLJgRGkRiydAFmAtZzxHfNXSs",
-      "fee" : "0.00100000",
-      "recipient" : "QZi1mNHDbiLvsytxTgxDr9nhJe4pNZaWpw",
-      "reference" : "J6JukdTVuXZ3JYbHatfZzwxG2vSiZwVCPDzW5K7PsVQKRj8XZeDtqnkGCGGjaSQZ9bQMtV44ky88NnGM4YBQKU6",
-      "senderPublicKey" : "DBFfbD2M3uh4jPE5PaUcZVvNPfrrJzVB7seeEtBn5SPs",
-      "signature" : "qkitxdCEEnKt8w6wRfFixtErbXsxWE6zG2ESNhpqBdScikV1WxeA6WZTTMJVV4tCeZdBFXw3V1X5NVztv6LirWK",
-      "timestamp" : 1607863074904,
-      "txGroupId" : 0,
-      "type" : "PAYMENT"
-   }
-   
- *

- * Account QLdw5uabviLJgRGkRiydAFmAtZzxHfNXSs attempted to spend 0.10888294 (including fees) - * when their QORT balance was really only 0.10886665. - *

- * However, on the broken DB nodes, their balance - * seemed to be 0.10890293 which was sufficient to make the transaction valid. - */ -public final class Block212937 { - - private static final Logger LOGGER = LogManager.getLogger(Block212937.class); - private static final String ACCOUNT_DELTAS_SOURCE = "block-212937-deltas.json"; - - private static final List accountDeltas = readAccountDeltas(); - - private Block212937() { - /* Do not instantiate */ - } - - @SuppressWarnings("unchecked") - private static List readAccountDeltas() { - Unmarshaller unmarshaller; - - try { - // Create JAXB context aware of classes we need to unmarshal - JAXBContext jc = JAXBContextFactory.createContext(new Class[] { - AccountBalanceData.class - }, null); - - // Create unmarshaller - unmarshaller = jc.createUnmarshaller(); - - // Set the unmarshaller media type to JSON - unmarshaller.setProperty(UnmarshallerProperties.MEDIA_TYPE, "application/json"); - - // Tell unmarshaller that there's no JSON root element in the JSON input - unmarshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, false); - } catch (JAXBException e) { - String message = "Failed to setup unmarshaller to read block 212937 deltas"; - LOGGER.error(message, e); - throw new RuntimeException(message, e); - } - - ClassLoader classLoader = BlockChain.class.getClassLoader(); - InputStream in = classLoader.getResourceAsStream(ACCOUNT_DELTAS_SOURCE); - StreamSource jsonSource = new StreamSource(in); - - try { - // Attempt to unmarshal JSON stream to BlockChain config - return (List) unmarshaller.unmarshal(jsonSource, AccountBalanceData.class).getValue(); - } catch (UnmarshalException e) { - String message = "Failed to parse block 212937 deltas"; - LOGGER.error(message, e); - throw new RuntimeException(message, e); - } catch (JAXBException e) { - String message = "Unexpected JAXB issue while processing block 212937 deltas"; - LOGGER.error(message, e); - throw new RuntimeException(message, e); - } - } - - public static void processFix(Block block) throws DataException { - block.repository.getAccountRepository().modifyAssetBalances(accountDeltas); - } - - public static void orphanFix(Block block) throws DataException { - // Create inverse deltas - List inverseDeltas = accountDeltas.stream() - .map(delta -> new AccountBalanceData(delta.getAddress(), delta.getAssetId(), 0 - delta.getBalance())) - .collect(Collectors.toList()); - - block.repository.getAccountRepository().modifyAssetBalances(inverseDeltas); - } - -}