Activate checkpoints

This commit is contained in:
AlphaX-Reloaded 2024-07-07 12:12:16 +02:00
parent e4134a769b
commit 708a978de0

View File

@ -806,18 +806,17 @@ public class BlockChain {
* @throws SQLException * @throws SQLException
*/ */
public static void validate() throws DataException { public static void validate() throws DataException {
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 isLite = Settings.getInstance().isLite();
boolean canBootstrap = Settings.getInstance().getBootstrap(); boolean canBootstrap = Settings.getInstance().getBootstrap();
boolean needsArchiveRebuild = false; boolean needsArchiveRebuild = false;
int checkHeight = 0; boolean checkPointFailed = false;
BlockData chainTip; int currentChainHeight = 0;
int checkPointHeight = 0;
try (final Repository repository = RepositoryManager.getRepository()) { try (final Repository repository = RepositoryManager.getRepository()) {
chainTip = repository.getBlockRepository().getLastBlock(); currentChainHeight = repository.getBlockRepository().getBlockchainHeight();
checkHeight = repository.getBlockRepository().getBlockchainHeight();
// Ensure archive is (at least partially) intact, and force a bootstrap if it isn't // Ensure archive is (at least partially) intact, and force a bootstrap if it isn't
if (!isTopOnly && archiveEnabled && canBootstrap) { if (!isTopOnly && archiveEnabled && canBootstrap) {
@ -833,50 +832,57 @@ public class BlockChain {
} }
} }
// Validate bootstrap
if (!canBootstrap) { if (!canBootstrap) {
if (checkHeight > 2) { if (currentChainHeight > 2) {
LOGGER.info("Retrieved block 2 from archive. Syncing from genesis block resumed!"); LOGGER.info("Retrieved block 2 from archive. Syncing from genesis block resumed!");
} else { } else {
needsArchiveRebuild = (repository.getBlockArchiveRepository().fromHeight(2) == null); needsArchiveRebuild = (repository.getBlockArchiveRepository().fromHeight(2) == null);
if (needsArchiveRebuild) { if (needsArchiveRebuild) {
LOGGER.info("Couldn't retrieve block 2 from archive. Bootstrapping is disabled. Syncing from genesis block!"); LOGGER.info("Couldn't retrieve block 2 from archive. Bootstrapping is disabled. Syncing from genesis block!");
// If there are minting accounts, make sure to back them up
// Don't backup if there are no minting accounts, as this can cause problems
if (!repository.getAccountRepository().getMintingAccounts().isEmpty()) {
Controller.getInstance().exportRepositoryData();
}
} }
} }
} }
// Validate checkpoints // 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 && !isLite) {
// TODO: remove the isTopOnly conditional below once this feature has had more testing time
if (isTopOnly && !isLite) {
List<Checkpoint> checkpoints = BlockChain.getInstance().getCheckpoints(); List<Checkpoint> checkpoints = BlockChain.getInstance().getCheckpoints();
for (Checkpoint checkpoint : checkpoints) { for (Checkpoint checkpoint : checkpoints) {
BlockData blockData = repository.getBlockRepository().fromHeight(checkpoint.height); checkPointHeight = checkpoint.height;
if (blockData == null) { if (currentChainHeight > checkPointHeight) {
// Try the archive BlockData blockData = repository.getBlockRepository().fromHeight(checkpoint.height);
blockData = repository.getBlockArchiveRepository().fromHeight(checkpoint.height); if (blockData == null) {
} // Try the archive
if (blockData == null) { blockData = repository.getBlockArchiveRepository().fromHeight(checkpoint.height);
LOGGER.trace("Couldn't find block for height {}", checkpoint.height); }
// This is likely due to the block being pruned, so is safe to ignore. if (blockData == null) {
// Continue, as there might be other blocks we can check more definitively. LOGGER.trace("Couldn't find block for height {}", checkpoint.height);
continue; // 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;
}
byte[] signature = Base58.decode(checkpoint.signature); byte[] signature = Base58.decode(checkpoint.signature);
if (!Arrays.equals(signature, blockData.getSignature())) { 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); LOGGER.info("Error: block at height {} with sig: {} doesn't match checkpoint sig: {}. Bootstrapping / Resync...", checkpoint.height, Base58.encode(blockData.getSignature()), checkpoint.signature);
needsArchiveRebuild = true; checkPointFailed = true;
break; break;
}
LOGGER.info("Block at height {} matches checkpoint signature", blockData.getHeight());
} }
LOGGER.info("Block at height {} matches checkpoint signature", blockData.getHeight());
} }
} }
} }
// Check first block is Genesis Block // Check first block is Genesis Block
if (!isGenesisBlockValid() || needsArchiveRebuild) { if (!isGenesisBlockValid() || needsArchiveRebuild) {
if (checkHeight < 3) { if (!canBootstrap && currentChainHeight < 3) {
try { try {
rebuildBlockchain(); rebuildBlockchain();
} catch (InterruptedException e) { } catch (InterruptedException e) {
@ -885,6 +891,20 @@ public class BlockChain {
} }
} }
// If checkpoint failed resync or bootstrap
if (checkPointFailed) {
try (final Repository repository = RepositoryManager.getRepository()) {
// If there are minting accounts, make sure to back them up
// Don't backup if there are no minting accounts, as this can cause problems
if (!repository.getAccountRepository().getMintingAccounts().isEmpty()) {
Controller.getInstance().exportRepositoryData();
}
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
// closed by rebuildBlockchain() if a bootstrap was applied // closed by rebuildBlockchain() if a bootstrap was applied
try (final Repository repository = RepositoryManager.getRepository()) { try (final Repository repository = RepositoryManager.getRepository()) {