Browse Source

Added checkpoint lookup on startup.

Currently enabled for topOnly nodes only. This will detect if the node is on a divergent chain, and will force a bootstrap or resync (depending on settings) in order to rejoin the main chain.
at-states-fix
CalDescent 2 years ago
parent
commit
e91e612b55
  1. 60
      src/main/java/org/qortal/block/BlockChain.java
  2. 3
      src/main/resources/blockchain.json

60
src/main/java/org/qortal/block/BlockChain.java

@ -100,6 +100,13 @@ public class BlockChain {
/** Whether only one registered name is allowed per account. */ /** Whether only one registered name is allowed per account. */
private boolean oneNamePerAccount = false; private boolean oneNamePerAccount = false;
/** Checkpoints */
public static class Checkpoint {
public int height;
public String signature;
}
private List<Checkpoint> checkpoints;
/** Block rewards by block height */ /** Block rewards by block height */
public static class RewardByHeight { public static class RewardByHeight {
public int height; public int height;
@ -381,6 +388,10 @@ public class BlockChain {
return this.oneNamePerAccount; return this.oneNamePerAccount;
} }
public List<Checkpoint> getCheckpoints() {
return this.checkpoints;
}
public List<RewardByHeight> getBlockRewardsByHeight() { public List<RewardByHeight> getBlockRewardsByHeight() {
return this.rewardsByHeight; return this.rewardsByHeight;
} }
@ -679,6 +690,7 @@ public class BlockChain {
boolean isTopOnly = Settings.getInstance().isTopOnly(); boolean isTopOnly = Settings.getInstance().isTopOnly();
boolean archiveEnabled = Settings.getInstance().isArchiveEnabled(); boolean archiveEnabled = Settings.getInstance().isArchiveEnabled();
boolean isLite = Settings.getInstance().isLite();
boolean canBootstrap = Settings.getInstance().getBootstrap(); boolean canBootstrap = Settings.getInstance().getBootstrap();
boolean needsArchiveRebuild = false; boolean needsArchiveRebuild = false;
BlockData chainTip; BlockData chainTip;
@ -699,23 +711,45 @@ public class BlockChain {
} }
} }
} }
}
boolean hasBlocks = (chainTip != null && chainTip.getHeight() > 1); // Validate checkpoints
// Limited to topOnly nodes for now, in order to reduce risk, and to solve a real-world problem with divergent topOnly nodes
if (isTopOnly && hasBlocks) { // TODO: remove the isTopOnly conditional below once this feature has had more testing time
// Top-only mode is enabled and we have blocks, so it's possible that the genesis block has been pruned if (isTopOnly && !isLite) {
// It's best not to validate it, and there's no real need to List<Checkpoint> checkpoints = BlockChain.getInstance().getCheckpoints();
} else { for (Checkpoint checkpoint : checkpoints) {
// Check first block is Genesis Block BlockData blockData = repository.getBlockRepository().fromHeight(checkpoint.height);
if (!isGenesisBlockValid() || needsArchiveRebuild) { if (blockData == null) {
try { // Try the archive
rebuildBlockchain(); blockData = repository.getBlockArchiveRepository().fromHeight(checkpoint.height);
}
if (blockData == null) {
LOGGER.trace("Couldn't find block for height {}", checkpoint.height);
// This is likely due to the block being pruned, so is safe to ignore.
// Continue, as there might be other blocks we can check more definitively.
continue;
}
} catch (InterruptedException e) { byte[] signature = Base58.decode(checkpoint.signature);
throw new DataException(String.format("Interrupted when trying to rebuild blockchain: %s", e.getMessage())); if (!Arrays.equals(signature, blockData.getSignature())) {
LOGGER.info("Error: block at height {} with signature {} doesn't match checkpoint sig: {}. Bootstrapping...", checkpoint.height, Base58.encode(blockData.getSignature()), checkpoint.signature);
needsArchiveRebuild = true;
break;
}
LOGGER.info("Block at height {} matches checkpoint signature", blockData.getHeight());
} }
} }
}
// Check first block is Genesis Block
if (!isGenesisBlockValid() || needsArchiveRebuild) {
try {
rebuildBlockchain();
} catch (InterruptedException e) {
throw new DataException(String.format("Interrupted when trying to rebuild blockchain: %s", e.getMessage()));
}
} }
// We need to create a new connection, as the previous repository and its connections may be been // We need to create a new connection, as the previous repository and its connections may be been

3
src/main/resources/blockchain.json

@ -87,6 +87,9 @@
"feeValidationFixTimestamp": 1671918000000, "feeValidationFixTimestamp": 1671918000000,
"chatReferenceTimestamp": 1674316800000 "chatReferenceTimestamp": 1674316800000
}, },
"checkpoints": [
{ "height": 1131800, "signature": "EpRam4PLdKzULMp7xNU7XG964AKfioG3g1k7cxwxWXnXspPwnjfF6UncEz4feuSA9mr1vW5d3YQPGruXYjj4vciSh4SPj5iWRxkHRWFeRpQnmVUyaVumuBTwM8nnLKJTdtkZnd6d8Mc5mVFdHs6EwLBTY4HECoRcbo4e4FwkfqVon4M" }
],
"genesisInfo": { "genesisInfo": {
"version": 4, "version": 4,
"timestamp": "1593450000000", "timestamp": "1593450000000",

Loading…
Cancel
Save