From 1676098abeda7b0d3c0726c8d7ef93199c01fbd7 Mon Sep 17 00:00:00 2001 From: QuickMythril Date: Wed, 13 Nov 2024 03:06:58 -0500 Subject: [PATCH 1/9] Add missing feature triggers to unit tests --- src/test/resources/test-chain-v2-block-timestamps.json | 7 ++++++- .../resources/test-chain-v2-disable-reference.json | 7 ++++++- src/test/resources/test-chain-v2-founder-rewards.json | 7 ++++++- src/test/resources/test-chain-v2-leftover-reward.json | 7 ++++++- src/test/resources/test-chain-v2-minting.json | 7 ++++++- src/test/resources/test-chain-v2-penalty-fix.json | 10 ++++++++-- .../resources/test-chain-v2-qora-holder-extremes.json | 7 ++++++- .../resources/test-chain-v2-qora-holder-reduction.json | 7 ++++++- src/test/resources/test-chain-v2-qora-holder.json | 7 ++++++- src/test/resources/test-chain-v2-reward-levels.json | 7 ++++++- src/test/resources/test-chain-v2-reward-scaling.json | 7 ++++++- src/test/resources/test-chain-v2-reward-shares.json | 7 ++++++- .../test-chain-v2-self-sponsorship-algo-v1.json | 7 ++++++- .../test-chain-v2-self-sponsorship-algo-v2.json | 7 ++++++- .../test-chain-v2-self-sponsorship-algo-v3.json | 7 ++++++- 15 files changed, 92 insertions(+), 16 deletions(-) diff --git a/src/test/resources/test-chain-v2-block-timestamps.json b/src/test/resources/test-chain-v2-block-timestamps.json index 17fc80c4..b2f0119d 100644 --- a/src/test/resources/test-chain-v2-block-timestamps.json +++ b/src/test/resources/test-chain-v2-block-timestamps.json @@ -91,7 +91,12 @@ "arbitraryOptionalFeeTimestamp": 0, "unconfirmableRewardSharesHeight": 99999999, "disableTransferPrivsTimestamp": 9999999999500, - "enableTransferPrivsTimestamp": 9999999999950 + "enableTransferPrivsTimestamp": 9999999999950, + "cancelSellNameValidationTimestamp": 9999999999999, + "disableRewardshareHeight": 9999999999990, + "enableRewardshareHeight": 9999999999999, + "onlyMintWithNameHeight": 9999999999999, + "groupMemberCheckHeight": 9999999999999 }, "genesisInfo": { "version": 4, diff --git a/src/test/resources/test-chain-v2-disable-reference.json b/src/test/resources/test-chain-v2-disable-reference.json index 33054732..86ed264f 100644 --- a/src/test/resources/test-chain-v2-disable-reference.json +++ b/src/test/resources/test-chain-v2-disable-reference.json @@ -94,7 +94,12 @@ "arbitraryOptionalFeeTimestamp": 0, "unconfirmableRewardSharesHeight": 99999999, "disableTransferPrivsTimestamp": 9999999999500, - "enableTransferPrivsTimestamp": 9999999999950 + "enableTransferPrivsTimestamp": 9999999999950, + "cancelSellNameValidationTimestamp": 9999999999999, + "disableRewardshareHeight": 9999999999990, + "enableRewardshareHeight": 9999999999999, + "onlyMintWithNameHeight": 9999999999999, + "groupMemberCheckHeight": 9999999999999 }, "genesisInfo": { "version": 4, diff --git a/src/test/resources/test-chain-v2-founder-rewards.json b/src/test/resources/test-chain-v2-founder-rewards.json index 577a07f1..d1b9c3c4 100644 --- a/src/test/resources/test-chain-v2-founder-rewards.json +++ b/src/test/resources/test-chain-v2-founder-rewards.json @@ -95,7 +95,12 @@ "arbitraryOptionalFeeTimestamp": 0, "unconfirmableRewardSharesHeight": 99999999, "disableTransferPrivsTimestamp": 9999999999500, - "enableTransferPrivsTimestamp": 9999999999950 + "enableTransferPrivsTimestamp": 9999999999950, + "cancelSellNameValidationTimestamp": 9999999999999, + "disableRewardshareHeight": 9999999999990, + "enableRewardshareHeight": 9999999999999, + "onlyMintWithNameHeight": 9999999999999, + "groupMemberCheckHeight": 9999999999999 }, "genesisInfo": { "version": 4, diff --git a/src/test/resources/test-chain-v2-leftover-reward.json b/src/test/resources/test-chain-v2-leftover-reward.json index 82e4ace7..106ac7dd 100644 --- a/src/test/resources/test-chain-v2-leftover-reward.json +++ b/src/test/resources/test-chain-v2-leftover-reward.json @@ -95,7 +95,12 @@ "arbitraryOptionalFeeTimestamp": 0, "unconfirmableRewardSharesHeight": 99999999, "disableTransferPrivsTimestamp": 9999999999500, - "enableTransferPrivsTimestamp": 9999999999950 + "enableTransferPrivsTimestamp": 9999999999950, + "cancelSellNameValidationTimestamp": 9999999999999, + "disableRewardshareHeight": 9999999999990, + "enableRewardshareHeight": 9999999999999, + "onlyMintWithNameHeight": 9999999999999, + "groupMemberCheckHeight": 9999999999999 }, "genesisInfo": { "version": 4, diff --git a/src/test/resources/test-chain-v2-minting.json b/src/test/resources/test-chain-v2-minting.json index 16032a9c..159b2dd7 100644 --- a/src/test/resources/test-chain-v2-minting.json +++ b/src/test/resources/test-chain-v2-minting.json @@ -95,7 +95,12 @@ "arbitraryOptionalFeeTimestamp": 9999999999999, "unconfirmableRewardSharesHeight": 99999999, "disableTransferPrivsTimestamp": 9999999999500, - "enableTransferPrivsTimestamp": 9999999999950 + "enableTransferPrivsTimestamp": 9999999999950, + "cancelSellNameValidationTimestamp": 9999999999999, + "disableRewardshareHeight": 9999999999990, + "enableRewardshareHeight": 9999999999999, + "onlyMintWithNameHeight": 9999999999999, + "groupMemberCheckHeight": 9999999999999 }, "genesisInfo": { "version": 4, diff --git a/src/test/resources/test-chain-v2-penalty-fix.json b/src/test/resources/test-chain-v2-penalty-fix.json index e62fc9f2..2266b032 100644 --- a/src/test/resources/test-chain-v2-penalty-fix.json +++ b/src/test/resources/test-chain-v2-penalty-fix.json @@ -85,14 +85,20 @@ "disableReferenceTimestamp": 9999999999999, "increaseOnlineAccountsDifficultyTimestamp": 9999999999999, "onlineAccountMinterLevelValidationHeight": 0, - "selfSponsorshipAlgoV1Height": 99999999, + "selfSponsorshipAlgoV1Height": 999999999, + "selfSponsorshipAlgoV2Height": 999999999, + "selfSponsorshipAlgoV3Height": 999999999, "feeValidationFixTimestamp": 0, "chatReferenceTimestamp": 0, "arbitraryOptionalFeeTimestamp": 0, "unconfirmableRewardSharesHeight": 99999999, - "selfSponsorshipAlgoV2Height": 9999999, "disableTransferPrivsTimestamp": 9999999999500, "enableTransferPrivsTimestamp": 9999999999950, + "cancelSellNameValidationTimestamp": 9999999999999, + "disableRewardshareHeight": 9999999999990, + "enableRewardshareHeight": 9999999999999, + "onlyMintWithNameHeight": 9999999999999, + "groupMemberCheckHeight": 9999999999999, "penaltyFixHeight": 5 }, "genesisInfo": { diff --git a/src/test/resources/test-chain-v2-qora-holder-extremes.json b/src/test/resources/test-chain-v2-qora-holder-extremes.json index 3ec11942..6043f15c 100644 --- a/src/test/resources/test-chain-v2-qora-holder-extremes.json +++ b/src/test/resources/test-chain-v2-qora-holder-extremes.json @@ -95,7 +95,12 @@ "arbitraryOptionalFeeTimestamp": 0, "unconfirmableRewardSharesHeight": 99999999, "disableTransferPrivsTimestamp": 9999999999500, - "enableTransferPrivsTimestamp": 9999999999950 + "enableTransferPrivsTimestamp": 9999999999950, + "cancelSellNameValidationTimestamp": 9999999999999, + "disableRewardshareHeight": 9999999999990, + "enableRewardshareHeight": 9999999999999, + "onlyMintWithNameHeight": 9999999999999, + "groupMemberCheckHeight": 9999999999999 }, "genesisInfo": { "version": 4, diff --git a/src/test/resources/test-chain-v2-qora-holder-reduction.json b/src/test/resources/test-chain-v2-qora-holder-reduction.json index 2b8834ce..7727a283 100644 --- a/src/test/resources/test-chain-v2-qora-holder-reduction.json +++ b/src/test/resources/test-chain-v2-qora-holder-reduction.json @@ -96,7 +96,12 @@ "arbitraryOptionalFeeTimestamp": 0, "unconfirmableRewardSharesHeight": 99999999, "disableTransferPrivsTimestamp": 9999999999500, - "enableTransferPrivsTimestamp": 9999999999950 + "enableTransferPrivsTimestamp": 9999999999950, + "cancelSellNameValidationTimestamp": 9999999999999, + "disableRewardshareHeight": 9999999999990, + "enableRewardshareHeight": 9999999999999, + "onlyMintWithNameHeight": 9999999999999, + "groupMemberCheckHeight": 9999999999999 }, "genesisInfo": { "version": 4, diff --git a/src/test/resources/test-chain-v2-qora-holder.json b/src/test/resources/test-chain-v2-qora-holder.json index ab96a243..6b9f9d54 100644 --- a/src/test/resources/test-chain-v2-qora-holder.json +++ b/src/test/resources/test-chain-v2-qora-holder.json @@ -95,7 +95,12 @@ "arbitraryOptionalFeeTimestamp": 0, "unconfirmableRewardSharesHeight": 99999999, "disableTransferPrivsTimestamp": 9999999999500, - "enableTransferPrivsTimestamp": 9999999999950 + "enableTransferPrivsTimestamp": 9999999999950, + "cancelSellNameValidationTimestamp": 9999999999999, + "disableRewardshareHeight": 9999999999990, + "enableRewardshareHeight": 9999999999999, + "onlyMintWithNameHeight": 9999999999999, + "groupMemberCheckHeight": 9999999999999 }, "genesisInfo": { "version": 4, diff --git a/src/test/resources/test-chain-v2-reward-levels.json b/src/test/resources/test-chain-v2-reward-levels.json index 35535c75..6f0993d0 100644 --- a/src/test/resources/test-chain-v2-reward-levels.json +++ b/src/test/resources/test-chain-v2-reward-levels.json @@ -95,7 +95,12 @@ "arbitraryOptionalFeeTimestamp": 0, "unconfirmableRewardSharesHeight": 99999999, "disableTransferPrivsTimestamp": 9999999999500, - "enableTransferPrivsTimestamp": 9999999999950 + "enableTransferPrivsTimestamp": 9999999999950, + "cancelSellNameValidationTimestamp": 9999999999999, + "disableRewardshareHeight": 9999999999990, + "enableRewardshareHeight": 9999999999999, + "onlyMintWithNameHeight": 9999999999999, + "groupMemberCheckHeight": 9999999999999 }, "genesisInfo": { "version": 4, diff --git a/src/test/resources/test-chain-v2-reward-scaling.json b/src/test/resources/test-chain-v2-reward-scaling.json index 616d0925..d1d4519a 100644 --- a/src/test/resources/test-chain-v2-reward-scaling.json +++ b/src/test/resources/test-chain-v2-reward-scaling.json @@ -95,7 +95,12 @@ "arbitraryOptionalFeeTimestamp": 0, "unconfirmableRewardSharesHeight": 500, "disableTransferPrivsTimestamp": 9999999999500, - "enableTransferPrivsTimestamp": 9999999999950 + "enableTransferPrivsTimestamp": 9999999999950, + "cancelSellNameValidationTimestamp": 9999999999999, + "disableRewardshareHeight": 9999999999990, + "enableRewardshareHeight": 9999999999999, + "onlyMintWithNameHeight": 9999999999999, + "groupMemberCheckHeight": 9999999999999 }, "genesisInfo": { "version": 4, diff --git a/src/test/resources/test-chain-v2-reward-shares.json b/src/test/resources/test-chain-v2-reward-shares.json index ec6ffd2e..69edc540 100644 --- a/src/test/resources/test-chain-v2-reward-shares.json +++ b/src/test/resources/test-chain-v2-reward-shares.json @@ -95,7 +95,12 @@ "arbitraryOptionalFeeTimestamp": 0, "unconfirmableRewardSharesHeight": 99999999, "disableTransferPrivsTimestamp": 9999999999500, - "enableTransferPrivsTimestamp": 9999999999950 + "enableTransferPrivsTimestamp": 9999999999950, + "cancelSellNameValidationTimestamp": 9999999999999, + "disableRewardshareHeight": 9999999999990, + "enableRewardshareHeight": 9999999999999, + "onlyMintWithNameHeight": 9999999999999, + "groupMemberCheckHeight": 9999999999999 }, "genesisInfo": { "version": 4, diff --git a/src/test/resources/test-chain-v2-self-sponsorship-algo-v1.json b/src/test/resources/test-chain-v2-self-sponsorship-algo-v1.json index d0d989cf..ad2ad4b1 100644 --- a/src/test/resources/test-chain-v2-self-sponsorship-algo-v1.json +++ b/src/test/resources/test-chain-v2-self-sponsorship-algo-v1.json @@ -95,7 +95,12 @@ "arbitraryOptionalFeeTimestamp": 0, "unconfirmableRewardSharesHeight": 99999999, "disableTransferPrivsTimestamp": 9999999999500, - "enableTransferPrivsTimestamp": 9999999999950 + "enableTransferPrivsTimestamp": 9999999999950, + "cancelSellNameValidationTimestamp": 9999999999999, + "disableRewardshareHeight": 9999999999990, + "enableRewardshareHeight": 9999999999999, + "onlyMintWithNameHeight": 9999999999999, + "groupMemberCheckHeight": 9999999999999 }, "genesisInfo": { "version": 4, diff --git a/src/test/resources/test-chain-v2-self-sponsorship-algo-v2.json b/src/test/resources/test-chain-v2-self-sponsorship-algo-v2.json index 5f09cb47..b2812c05 100644 --- a/src/test/resources/test-chain-v2-self-sponsorship-algo-v2.json +++ b/src/test/resources/test-chain-v2-self-sponsorship-algo-v2.json @@ -95,7 +95,12 @@ "arbitraryOptionalFeeTimestamp": 0, "unconfirmableRewardSharesHeight": 99999999, "disableTransferPrivsTimestamp": 9999999999500, - "enableTransferPrivsTimestamp": 9999999999950 + "enableTransferPrivsTimestamp": 9999999999950, + "cancelSellNameValidationTimestamp": 9999999999999, + "disableRewardshareHeight": 9999999999990, + "enableRewardshareHeight": 9999999999999, + "onlyMintWithNameHeight": 9999999999999, + "groupMemberCheckHeight": 9999999999999 }, "genesisInfo": { "version": 4, diff --git a/src/test/resources/test-chain-v2-self-sponsorship-algo-v3.json b/src/test/resources/test-chain-v2-self-sponsorship-algo-v3.json index f7d1faa2..d65aa48e 100644 --- a/src/test/resources/test-chain-v2-self-sponsorship-algo-v3.json +++ b/src/test/resources/test-chain-v2-self-sponsorship-algo-v3.json @@ -95,7 +95,12 @@ "arbitraryOptionalFeeTimestamp": 0, "unconfirmableRewardSharesHeight": 99999999, "disableTransferPrivsTimestamp": 9999999999500, - "enableTransferPrivsTimestamp": 9999999999950 + "enableTransferPrivsTimestamp": 9999999999950, + "cancelSellNameValidationTimestamp": 9999999999999, + "disableRewardshareHeight": 9999999999990, + "enableRewardshareHeight": 9999999999999, + "onlyMintWithNameHeight": 9999999999999, + "groupMemberCheckHeight": 9999999999999 }, "genesisInfo": { "version": 4, From 82d5d25c597d6d3f9f9e8c9a9c176c35df8c759b Mon Sep 17 00:00:00 2001 From: QuickMythril Date: Wed, 13 Nov 2024 06:16:39 -0500 Subject: [PATCH 2/9] Add logging to block archive unit tests --- .../org/qortal/test/BlockArchiveV1Tests.java | 1049 ++++++++++------- .../org/qortal/test/BlockArchiveV2Tests.java | 876 ++++++++------ 2 files changed, 1112 insertions(+), 813 deletions(-) diff --git a/src/test/java/org/qortal/test/BlockArchiveV1Tests.java b/src/test/java/org/qortal/test/BlockArchiveV1Tests.java index a28bd28d..60eefa8e 100644 --- a/src/test/java/org/qortal/test/BlockArchiveV1Tests.java +++ b/src/test/java/org/qortal/test/BlockArchiveV1Tests.java @@ -34,441 +34,574 @@ import static org.junit.Assert.*; public class BlockArchiveV1Tests extends Common { - @Before - public void beforeTest() throws DataException, IllegalAccessException { - Common.useSettings("test-settings-v2-block-archive.json"); - NTP.setFixedOffset(Settings.getInstance().getTestNtpOffset()); - this.deleteArchiveDirectory(); + @Before + public void beforeTest() throws DataException, IllegalAccessException { + Common.useSettings("test-settings-v2-block-archive.json"); + NTP.setFixedOffset(Settings.getInstance().getTestNtpOffset()); + this.deleteArchiveDirectory(); - // Set default archive version to 1, so that archive builds in these tests use V2 - FieldUtils.writeField(Settings.getInstance(), "defaultArchiveVersion", 1, true); - } + // Set default archive version to 1, so that archive builds in these tests use V2 + FieldUtils.writeField(Settings.getInstance(), "defaultArchiveVersion", 1, true); + } - @After - public void afterTest() throws DataException { - this.deleteArchiveDirectory(); - } + @After + public void afterTest() throws DataException { + this.deleteArchiveDirectory(); + } + @Test + public void testWriter() throws DataException, InterruptedException, TransformationException, IOException { + try (final Repository repository = RepositoryManager.getRepository()) { + + System.out.println("Starting testWriter"); + + // Mint some blocks so that we are able to archive them later + System.out.println("Minting 1000 blocks..."); + for (int i = 0; i < 1000; i++) { + BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); + // Log every 100 blocks + if ((i + 1) % 100 == 0) { + System.out.println("Minted block " + (i + 1)); + } + } + System.out.println("Finished minting blocks."); + + // 900 blocks are trimmed (this specifies the first untrimmed height) + repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(901); + repository.getATRepository().setAtTrimHeight(901); + System.out.println("Set trim heights to 901."); + + // Check the max archive height - this should be one less than the first untrimmed height + final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); + System.out.println("Maximum archive height (Expected 900): " + maximumArchiveHeight); + assertEquals(900, maximumArchiveHeight); + + // Write blocks 2-900 to the archive + System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); + BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); + writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes + BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); + System.out.println("Finished writing blocks to archive. Result: " + result); + assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); + + // Make sure that the archive contains the correct number of blocks + System.out.println("Archive contains " + writer.getWrittenCount() + " blocks. (Expected 899)"); + assertEquals(900 - 1, writer.getWrittenCount()); + + // Increment block archive height + repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); + repository.saveChanges(); + assertEquals(900 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); + + // Ensure the file exists + File outputFile = writer.getOutputPath().toFile(); + assertTrue(outputFile.exists()); + System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); + + System.out.println("testWriter completed successfully."); + } + } + + @Test + public void testWriterAndReader() throws DataException, InterruptedException, TransformationException, IOException { + try (final Repository repository = RepositoryManager.getRepository()) { + + System.out.println("Starting testWriterAndReader"); + + // Mint some blocks so that we are able to archive them later + System.out.println("Minting 1000 blocks..."); + for (int i = 0; i < 1000; i++) { + BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); + // Log every 100 blocks + if ((i + 1) % 100 == 0) { + System.out.println("Minted block " + (i + 1)); + } + } + System.out.println("Finished minting blocks."); + + // 900 blocks are trimmed (this specifies the first untrimmed height) + repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(901); + repository.getATRepository().setAtTrimHeight(901); + System.out.println("Set trim heights to 901."); + + // Check the max archive height - this should be one less than the first untrimmed height + final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); + System.out.println("Maximum archive height (Expected 900): " + maximumArchiveHeight); + assertEquals(900, maximumArchiveHeight); + + // Write blocks 2-900 to the archive + System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); + BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); + writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes + BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); + System.out.println("Finished writing blocks to archive. Result: " + result); + assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); + + // Make sure that the archive contains the correct number of blocks + System.out.println("Archive contains " + writer.getWrittenCount() + " blocks. (Expected 899)"); + assertEquals(900 - 1, writer.getWrittenCount()); + + // Increment block archive height + repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); + repository.saveChanges(); + assertEquals(900 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); + + // Ensure the file exists + File outputFile = writer.getOutputPath().toFile(); + assertTrue(outputFile.exists()); + System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); + + // Read block 2 from the archive + System.out.println("Reading block 2 from the archive..."); + BlockArchiveReader reader = BlockArchiveReader.getInstance(); + BlockTransformation block2Info = reader.fetchBlockAtHeight(2); + BlockData block2ArchiveData = block2Info.getBlockData(); + + // Read block 2 from the repository + BlockData block2RepositoryData = repository.getBlockRepository().fromHeight(2); + + // Ensure the values match + System.out.println("Comparing block 2 data..."); + assertEquals(block2ArchiveData.getHeight(), block2RepositoryData.getHeight()); + assertArrayEquals(block2ArchiveData.getSignature(), block2RepositoryData.getSignature()); + + // Test some values in the archive + assertEquals(1, block2ArchiveData.getOnlineAccountsCount()); + + // Read block 900 from the archive + System.out.println("Reading block 900 from the archive..."); + BlockTransformation block900Info = reader.fetchBlockAtHeight(900); + BlockData block900ArchiveData = block900Info.getBlockData(); + + // Read block 900 from the repository + BlockData block900RepositoryData = repository.getBlockRepository().fromHeight(900); + + // Ensure the values match + System.out.println("Comparing block 900 data..."); + assertEquals(block900ArchiveData.getHeight(), block900RepositoryData.getHeight()); + assertArrayEquals(block900ArchiveData.getSignature(), block900RepositoryData.getSignature()); + + // Test some values in the archive + assertEquals(1, block900ArchiveData.getOnlineAccountsCount()); + + System.out.println("testWriterAndReader completed successfully."); + } + } + + @Test + public void testArchivedAtStates() throws DataException, InterruptedException, TransformationException, IOException { + try (final Repository repository = RepositoryManager.getRepository()) { + + System.out.println("Starting testArchivedAtStates"); + + // Deploy an AT so that we have AT state data + System.out.println("Deploying AT..."); + PrivateKeyAccount deployer = Common.getTestAccount(repository, "alice"); + byte[] creationBytes = AtUtils.buildSimpleAT(); + long fundingAmount = 1_00000000L; + DeployAtTransaction deployAtTransaction = AtUtils.doDeployAT(repository, deployer, creationBytes, fundingAmount); + String atAddress = deployAtTransaction.getATAccount().getAddress(); + System.out.println("AT deployed at address: " + atAddress); + + // Mint some blocks so that we are able to archive them later + System.out.println("Minting 1000 blocks..."); + for (int i = 0; i < 1000; i++) { + BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); + // Log every 100 blocks + if ((i + 1) % 100 == 0) { + System.out.println("Minted block " + (i + 1)); + } + } + System.out.println("Finished minting blocks."); + + // 9 blocks are trimmed (this specifies the first untrimmed height) + repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(10); + repository.getATRepository().setAtTrimHeight(10); + System.out.println("Set trim heights to 10."); + + // Check the max archive height + final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); + System.out.println("Maximum archive height (Expected 9): " + maximumArchiveHeight); + assertEquals(9, maximumArchiveHeight); + + // Write blocks 2-9 to the archive + System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); + BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); + writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes + BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); + System.out.println("Finished writing blocks to archive. Result: " + result); + assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); + + // Make sure that the archive contains the correct number of blocks + System.out.println("Archive contains " + writer.getWrittenCount() + " blocks. (Expected 8)"); + assertEquals(9 - 1, writer.getWrittenCount()); + + // Increment block archive height + repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); + repository.saveChanges(); + assertEquals(9 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); + + // Ensure the file exists + File outputFile = writer.getOutputPath().toFile(); + assertTrue(outputFile.exists()); + System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); + + // Check blocks 3-9 + System.out.println("Checking blocks 3 to 9..."); + for (Integer testHeight = 2; testHeight <= 9; testHeight++) { + + System.out.println("Reading block " + testHeight + " from the archive..."); + // Read a block from the archive + BlockArchiveReader reader = BlockArchiveReader.getInstance(); + BlockTransformation blockInfo = reader.fetchBlockAtHeight(testHeight); + BlockData archivedBlockData = blockInfo.getBlockData(); + ATStateData archivedAtStateData = blockInfo.getAtStates().isEmpty() ? null : blockInfo.getAtStates().get(0); + List archivedTransactions = blockInfo.getTransactions(); + + // Read the same block from the repository + BlockData repositoryBlockData = repository.getBlockRepository().fromHeight(testHeight); + ATStateData repositoryAtStateData = repository.getATRepository().getATStateAtHeight(atAddress, testHeight); + + // Ensure the repository has full AT state data + assertNotNull(repositoryAtStateData.getStateHash()); + assertNotNull(repositoryAtStateData.getStateData()); + + // Check the archived AT state + if (testHeight == 2) { + System.out.println("Checking block " + testHeight + " AT state data (expected null)..."); + // Block 2 won't have an AT state hash because it's initial (and has the DEPLOY_AT in the same block) + assertNull(archivedAtStateData); + + assertEquals(1, archivedTransactions.size()); + assertEquals(Transaction.TransactionType.DEPLOY_AT, archivedTransactions.get(0).getType()); + } + else { + System.out.println("Checking block " + testHeight + " AT state data..."); + // For blocks 3+, ensure the archive has the AT state data, but not the hashes + assertNotNull(archivedAtStateData.getStateHash()); + assertNull(archivedAtStateData.getStateData()); + + // They also shouldn't have any transactions + assertTrue(archivedTransactions.isEmpty()); + } + + // Also check the online accounts count and height + assertEquals(1, archivedBlockData.getOnlineAccountsCount()); + assertEquals(testHeight, archivedBlockData.getHeight()); + + // Ensure the values match + assertEquals(archivedBlockData.getHeight(), repositoryBlockData.getHeight()); + assertArrayEquals(archivedBlockData.getSignature(), repositoryBlockData.getSignature()); + assertEquals(archivedBlockData.getOnlineAccountsCount(), repositoryBlockData.getOnlineAccountsCount()); + assertArrayEquals(archivedBlockData.getMinterSignature(), repositoryBlockData.getMinterSignature()); + assertEquals(archivedBlockData.getATCount(), repositoryBlockData.getATCount()); + assertEquals(archivedBlockData.getOnlineAccountsCount(), repositoryBlockData.getOnlineAccountsCount()); + assertArrayEquals(archivedBlockData.getReference(), repositoryBlockData.getReference()); + assertEquals(archivedBlockData.getTimestamp(), repositoryBlockData.getTimestamp()); + assertEquals(archivedBlockData.getATFees(), repositoryBlockData.getATFees()); + assertEquals(archivedBlockData.getTotalFees(), repositoryBlockData.getTotalFees()); + assertEquals(archivedBlockData.getTransactionCount(), repositoryBlockData.getTransactionCount()); + assertArrayEquals(archivedBlockData.getTransactionsSignature(), repositoryBlockData.getTransactionsSignature()); + + if (testHeight != 2) { + assertArrayEquals(archivedAtStateData.getStateHash(), repositoryAtStateData.getStateHash()); + } + } + + // Check block 10 (unarchived) + System.out.println("Checking block 10 (should not be in archive)..."); + BlockArchiveReader reader = BlockArchiveReader.getInstance(); + BlockTransformation blockInfo = reader.fetchBlockAtHeight(10); + assertNull(blockInfo); + + System.out.println("testArchivedAtStates completed successfully."); + } + + } + + @Test + public void testArchiveAndPrune() throws DataException, InterruptedException, TransformationException, IOException { + try (final Repository repository = RepositoryManager.getRepository()) { + + System.out.println("Starting testArchiveAndPrune"); + + // Deploy an AT so that we have AT state data + System.out.println("Deploying AT..."); + PrivateKeyAccount deployer = Common.getTestAccount(repository, "alice"); + byte[] creationBytes = AtUtils.buildSimpleAT(); + long fundingAmount = 1_00000000L; + AtUtils.doDeployAT(repository, deployer, creationBytes, fundingAmount); + + // Mint some blocks so that we are able to archive them later + System.out.println("Minting 1000 blocks..."); + for (int i = 0; i < 1000; i++) { + BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); + // Log every 100 blocks + if ((i + 1) % 100 == 0) { + System.out.println("Minted block " + (i + 1)); + } + } + System.out.println("Finished minting blocks."); + + // Assume 900 blocks are trimmed (this specifies the first untrimmed height) + repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(901); + repository.getATRepository().setAtTrimHeight(901); + System.out.println("Set trim heights to 901."); + + // Check the max archive height - this should be one less than the first untrimmed height + final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); + System.out.println("Maximum archive height (Expected 900): " + maximumArchiveHeight); + assertEquals(900, maximumArchiveHeight); + + // Write blocks 2-900 to the archive + System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); + BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); + writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes + BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); + System.out.println("Finished writing blocks to archive. Result: " + result); + assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); + + // Make sure that the archive contains the correct number of blocks + System.out.println("Archive contains " + writer.getWrittenCount() + " blocks. (Expected 899)"); + assertEquals(900 - 1, writer.getWrittenCount()); + + // Increment block archive height + repository.getBlockArchiveRepository().setBlockArchiveHeight(901); + repository.saveChanges(); + assertEquals(901, repository.getBlockArchiveRepository().getBlockArchiveHeight()); + + // Ensure the file exists + File outputFile = writer.getOutputPath().toFile(); + assertTrue(outputFile.exists()); + System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); + + // Ensure the SQL repository contains blocks 2 and 900... + assertNotNull(repository.getBlockRepository().fromHeight(2)); + assertNotNull(repository.getBlockRepository().fromHeight(900)); + System.out.println("Blocks 2 and 900 exist in the repository."); + + // Prune all the archived blocks + System.out.println("Pruning blocks 2 to 900..."); + int numBlocksPruned = repository.getBlockRepository().pruneBlocks(0, 900); + assertEquals(900-1, numBlocksPruned); + repository.getBlockRepository().setBlockPruneHeight(901); + + // Prune the AT states for the archived blocks + System.out.println("Pruning AT states up to height 900..."); + repository.getATRepository().rebuildLatestAtStates(900); + repository.saveChanges(); + int numATStatesPruned = repository.getATRepository().pruneAtStates(0, 900); + assertEquals(900-2, numATStatesPruned); // Minus 1 for genesis block, and another for the latest AT state + repository.getATRepository().setAtPruneHeight(901); + + // Now ensure the SQL repository is missing blocks 2 and 900... + assertNull(repository.getBlockRepository().fromHeight(2)); + assertNull(repository.getBlockRepository().fromHeight(900)); + System.out.println("Blocks 2 and 900 have been pruned from the repository."); + + // ... but it's not missing blocks 1 and 901 (we don't prune the genesis block) + assertNotNull(repository.getBlockRepository().fromHeight(1)); + assertNotNull(repository.getBlockRepository().fromHeight(901)); + System.out.println("Blocks 1 and 901 still exist in the repository."); + + // Validate the latest block height in the repository + int lastBlockHeight = repository.getBlockRepository().getLastBlock().getHeight(); + System.out.println("Latest block height in repository (Expected 1002): " + lastBlockHeight); + assertEquals(1002, lastBlockHeight); + + System.out.println("testArchiveAndPrune completed successfully."); + } + } + @Test - public void testWriter() throws DataException, InterruptedException, TransformationException, IOException { - try (final Repository repository = RepositoryManager.getRepository()) { - - // Mint some blocks so that we are able to archive them later - for (int i = 0; i < 1000; i++) { - BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); - } - - // 900 blocks are trimmed (this specifies the first untrimmed height) - repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(901); - repository.getATRepository().setAtTrimHeight(901); - - // Check the max archive height - this should be one less than the first untrimmed height - final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); - assertEquals(900, maximumArchiveHeight); - - // Write blocks 2-900 to the archive - BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); - writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes - BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); - assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); - - // Make sure that the archive contains the correct number of blocks - assertEquals(900 - 1, writer.getWrittenCount()); - - // Increment block archive height - repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); - repository.saveChanges(); - assertEquals(900 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); - - // Ensure the file exists - File outputFile = writer.getOutputPath().toFile(); - assertTrue(outputFile.exists()); - } - } - - @Test - public void testWriterAndReader() throws DataException, InterruptedException, TransformationException, IOException { - try (final Repository repository = RepositoryManager.getRepository()) { - - // Mint some blocks so that we are able to archive them later - for (int i = 0; i < 1000; i++) { - BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); - } - - // 900 blocks are trimmed (this specifies the first untrimmed height) - repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(901); - repository.getATRepository().setAtTrimHeight(901); - - // Check the max archive height - this should be one less than the first untrimmed height - final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); - assertEquals(900, maximumArchiveHeight); - - // Write blocks 2-900 to the archive - BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); - writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes - BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); - assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); - - // Make sure that the archive contains the correct number of blocks - assertEquals(900 - 1, writer.getWrittenCount()); - - // Increment block archive height - repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); - repository.saveChanges(); - assertEquals(900 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); - - // Ensure the file exists - File outputFile = writer.getOutputPath().toFile(); - assertTrue(outputFile.exists()); - - // Read block 2 from the archive - BlockArchiveReader reader = BlockArchiveReader.getInstance(); - BlockTransformation block2Info = reader.fetchBlockAtHeight(2); - BlockData block2ArchiveData = block2Info.getBlockData(); - - // Read block 2 from the repository - BlockData block2RepositoryData = repository.getBlockRepository().fromHeight(2); - - // Ensure the values match - assertEquals(block2ArchiveData.getHeight(), block2RepositoryData.getHeight()); - assertArrayEquals(block2ArchiveData.getSignature(), block2RepositoryData.getSignature()); - - // Test some values in the archive - assertEquals(1, block2ArchiveData.getOnlineAccountsCount()); - - // Read block 900 from the archive - BlockTransformation block900Info = reader.fetchBlockAtHeight(900); - BlockData block900ArchiveData = block900Info.getBlockData(); - - // Read block 900 from the repository - BlockData block900RepositoryData = repository.getBlockRepository().fromHeight(900); - - // Ensure the values match - assertEquals(block900ArchiveData.getHeight(), block900RepositoryData.getHeight()); - assertArrayEquals(block900ArchiveData.getSignature(), block900RepositoryData.getSignature()); - - // Test some values in the archive - assertEquals(1, block900ArchiveData.getOnlineAccountsCount()); - - } - } - - @Test - public void testArchivedAtStates() throws DataException, InterruptedException, TransformationException, IOException { - try (final Repository repository = RepositoryManager.getRepository()) { - - // Deploy an AT so that we have AT state data - PrivateKeyAccount deployer = Common.getTestAccount(repository, "alice"); - byte[] creationBytes = AtUtils.buildSimpleAT(); - long fundingAmount = 1_00000000L; - DeployAtTransaction deployAtTransaction = AtUtils.doDeployAT(repository, deployer, creationBytes, fundingAmount); - String atAddress = deployAtTransaction.getATAccount().getAddress(); - - // Mint some blocks so that we are able to archive them later - for (int i = 0; i < 1000; i++) { - BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); - } - - // 9 blocks are trimmed (this specifies the first untrimmed height) - repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(10); - repository.getATRepository().setAtTrimHeight(10); - - // Check the max archive height - final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); - assertEquals(9, maximumArchiveHeight); - - // Write blocks 2-9 to the archive - BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); - writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes - BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); - assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); - - // Make sure that the archive contains the correct number of blocks - assertEquals(9 - 1, writer.getWrittenCount()); - - // Increment block archive height - repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); - repository.saveChanges(); - assertEquals(9 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); - - // Ensure the file exists - File outputFile = writer.getOutputPath().toFile(); - assertTrue(outputFile.exists()); - - // Check blocks 3-9 - for (Integer testHeight = 2; testHeight <= 9; testHeight++) { - - // Read a block from the archive - BlockArchiveReader reader = BlockArchiveReader.getInstance(); - BlockTransformation blockInfo = reader.fetchBlockAtHeight(testHeight); - BlockData archivedBlockData = blockInfo.getBlockData(); - ATStateData archivedAtStateData = blockInfo.getAtStates().isEmpty() ? null : blockInfo.getAtStates().get(0); - List archivedTransactions = blockInfo.getTransactions(); - - // Read the same block from the repository - BlockData repositoryBlockData = repository.getBlockRepository().fromHeight(testHeight); - ATStateData repositoryAtStateData = repository.getATRepository().getATStateAtHeight(atAddress, testHeight); - - // Ensure the repository has full AT state data - assertNotNull(repositoryAtStateData.getStateHash()); - assertNotNull(repositoryAtStateData.getStateData()); - - // Check the archived AT state - if (testHeight == 2) { - // Block 2 won't have an AT state hash because it's initial (and has the DEPLOY_AT in the same block) - assertNull(archivedAtStateData); - - assertEquals(1, archivedTransactions.size()); - assertEquals(Transaction.TransactionType.DEPLOY_AT, archivedTransactions.get(0).getType()); - } - else { - // For blocks 3+, ensure the archive has the AT state data, but not the hashes - assertNotNull(archivedAtStateData.getStateHash()); - assertNull(archivedAtStateData.getStateData()); - - // They also shouldn't have any transactions - assertTrue(archivedTransactions.isEmpty()); - } - - // Also check the online accounts count and height - assertEquals(1, archivedBlockData.getOnlineAccountsCount()); - assertEquals(testHeight, archivedBlockData.getHeight()); - - // Ensure the values match - assertEquals(archivedBlockData.getHeight(), repositoryBlockData.getHeight()); - assertArrayEquals(archivedBlockData.getSignature(), repositoryBlockData.getSignature()); - assertEquals(archivedBlockData.getOnlineAccountsCount(), repositoryBlockData.getOnlineAccountsCount()); - assertArrayEquals(archivedBlockData.getMinterSignature(), repositoryBlockData.getMinterSignature()); - assertEquals(archivedBlockData.getATCount(), repositoryBlockData.getATCount()); - assertEquals(archivedBlockData.getOnlineAccountsCount(), repositoryBlockData.getOnlineAccountsCount()); - assertArrayEquals(archivedBlockData.getReference(), repositoryBlockData.getReference()); - assertEquals(archivedBlockData.getTimestamp(), repositoryBlockData.getTimestamp()); - assertEquals(archivedBlockData.getATFees(), repositoryBlockData.getATFees()); - assertEquals(archivedBlockData.getTotalFees(), repositoryBlockData.getTotalFees()); - assertEquals(archivedBlockData.getTransactionCount(), repositoryBlockData.getTransactionCount()); - assertArrayEquals(archivedBlockData.getTransactionsSignature(), repositoryBlockData.getTransactionsSignature()); - - if (testHeight != 2) { - assertArrayEquals(archivedAtStateData.getStateHash(), repositoryAtStateData.getStateHash()); - } - } - - // Check block 10 (unarchived) - BlockArchiveReader reader = BlockArchiveReader.getInstance(); - BlockTransformation blockInfo = reader.fetchBlockAtHeight(10); - assertNull(blockInfo); - - } - - } - - @Test - public void testArchiveAndPrune() throws DataException, InterruptedException, TransformationException, IOException { - try (final Repository repository = RepositoryManager.getRepository()) { - - // Deploy an AT so that we have AT state data - PrivateKeyAccount deployer = Common.getTestAccount(repository, "alice"); - byte[] creationBytes = AtUtils.buildSimpleAT(); - long fundingAmount = 1_00000000L; - AtUtils.doDeployAT(repository, deployer, creationBytes, fundingAmount); - - // Mint some blocks so that we are able to archive them later - for (int i = 0; i < 1000; i++) { - BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); - } - - // Assume 900 blocks are trimmed (this specifies the first untrimmed height) - repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(901); - repository.getATRepository().setAtTrimHeight(901); - - // Check the max archive height - this should be one less than the first untrimmed height - final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); - assertEquals(900, maximumArchiveHeight); - - // Write blocks 2-900 to the archive - BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); - writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes - BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); - assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); - - // Make sure that the archive contains the correct number of blocks - assertEquals(900 - 1, writer.getWrittenCount()); - - // Increment block archive height - repository.getBlockArchiveRepository().setBlockArchiveHeight(901); - repository.saveChanges(); - assertEquals(901, repository.getBlockArchiveRepository().getBlockArchiveHeight()); - - // Ensure the file exists - File outputFile = writer.getOutputPath().toFile(); - assertTrue(outputFile.exists()); - - // Ensure the SQL repository contains blocks 2 and 900... - assertNotNull(repository.getBlockRepository().fromHeight(2)); - assertNotNull(repository.getBlockRepository().fromHeight(900)); - - // Prune all the archived blocks - int numBlocksPruned = repository.getBlockRepository().pruneBlocks(0, 900); - assertEquals(900-1, numBlocksPruned); - repository.getBlockRepository().setBlockPruneHeight(901); - - // Prune the AT states for the archived blocks - repository.getATRepository().rebuildLatestAtStates(900); - repository.saveChanges(); - int numATStatesPruned = repository.getATRepository().pruneAtStates(0, 900); - assertEquals(900-2, numATStatesPruned); // Minus 1 for genesis block, and another for the latest AT state - repository.getATRepository().setAtPruneHeight(901); - - // Now ensure the SQL repository is missing blocks 2 and 900... - assertNull(repository.getBlockRepository().fromHeight(2)); - assertNull(repository.getBlockRepository().fromHeight(900)); - - // ... but it's not missing blocks 1 and 901 (we don't prune the genesis block) - assertNotNull(repository.getBlockRepository().fromHeight(1)); - assertNotNull(repository.getBlockRepository().fromHeight(901)); - - // Validate the latest block height in the repository - assertEquals(1002, (int) repository.getBlockRepository().getLastBlock().getHeight()); - - } - } - - @Test - public void testTrimArchivePruneAndOrphan() throws DataException, InterruptedException, TransformationException, IOException { - try (final Repository repository = RepositoryManager.getRepository()) { - - // Deploy an AT so that we have AT state data - PrivateKeyAccount deployer = Common.getTestAccount(repository, "alice"); - byte[] creationBytes = AtUtils.buildSimpleAT(); - long fundingAmount = 1_00000000L; - AtUtils.doDeployAT(repository, deployer, creationBytes, fundingAmount); - - // Mint some blocks so that we are able to archive them later - for (int i = 0; i < 1000; i++) { - BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); - } - - // Make sure that block 500 has full AT state data and data hash - List block500AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(500); - ATStateData atStatesData = repository.getATRepository().getATStateAtHeight(block500AtStatesData.get(0).getATAddress(), 500); - assertNotNull(atStatesData.getStateHash()); - assertNotNull(atStatesData.getStateData()); - - // Trim the first 500 blocks - repository.getBlockRepository().trimOldOnlineAccountsSignatures(0, 500); - repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(501); - repository.getATRepository().rebuildLatestAtStates(500); - repository.getATRepository().trimAtStates(0, 500, 1000); - repository.getATRepository().setAtTrimHeight(501); - - // Now block 499 should only have the AT state data hash - List block499AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(499); - atStatesData = repository.getATRepository().getATStateAtHeight(block499AtStatesData.get(0).getATAddress(), 499); - assertNotNull(atStatesData.getStateHash()); - assertNull(atStatesData.getStateData()); - - // ... but block 500 should have the full data (due to being retained as the "latest" AT state in the trimmed range - block500AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(500); - atStatesData = repository.getATRepository().getATStateAtHeight(block500AtStatesData.get(0).getATAddress(), 500); - assertNotNull(atStatesData.getStateHash()); - assertNotNull(atStatesData.getStateData()); - - // ... and block 501 should also have the full data - List block501AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(501); - atStatesData = repository.getATRepository().getATStateAtHeight(block501AtStatesData.get(0).getATAddress(), 501); - assertNotNull(atStatesData.getStateHash()); - assertNotNull(atStatesData.getStateData()); - - // Check the max archive height - this should be one less than the first untrimmed height - final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); - assertEquals(500, maximumArchiveHeight); - - BlockData block3DataPreArchive = repository.getBlockRepository().fromHeight(3); - - // Write blocks 2-500 to the archive - BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); - writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes - BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); - assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); - - // Make sure that the archive contains the correct number of blocks - assertEquals(500 - 1, writer.getWrittenCount()); // -1 for the genesis block - - // Increment block archive height - repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); - repository.saveChanges(); - assertEquals(500 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); - - // Ensure the file exists - File outputFile = writer.getOutputPath().toFile(); - assertTrue(outputFile.exists()); - - // Ensure the SQL repository contains blocks 2 and 500... - assertNotNull(repository.getBlockRepository().fromHeight(2)); - assertNotNull(repository.getBlockRepository().fromHeight(500)); - - // Prune all the archived blocks - int numBlocksPruned = repository.getBlockRepository().pruneBlocks(0, 500); - assertEquals(500-1, numBlocksPruned); - repository.getBlockRepository().setBlockPruneHeight(501); - - // Prune the AT states for the archived blocks - repository.getATRepository().rebuildLatestAtStates(500); - repository.saveChanges(); - int numATStatesPruned = repository.getATRepository().pruneAtStates(2, 500); - assertEquals(498, numATStatesPruned); // Minus 1 for genesis block, and another for the latest AT state - repository.getATRepository().setAtPruneHeight(501); - - // Now ensure the SQL repository is missing blocks 2 and 500... - assertNull(repository.getBlockRepository().fromHeight(2)); - assertNull(repository.getBlockRepository().fromHeight(500)); - - // ... but it's not missing blocks 1 and 501 (we don't prune the genesis block) - assertNotNull(repository.getBlockRepository().fromHeight(1)); - assertNotNull(repository.getBlockRepository().fromHeight(501)); - - // Validate the latest block height in the repository - assertEquals(1002, (int) repository.getBlockRepository().getLastBlock().getHeight()); - - // Now orphan some unarchived blocks. - BlockUtils.orphanBlocks(repository, 500); - assertEquals(502, (int) repository.getBlockRepository().getLastBlock().getHeight()); - - // We're close to the lower limit of the SQL database now, so - // we need to import some blocks from the archive - BlockArchiveUtils.importFromArchive(401, 500, repository); - - // Ensure the SQL repository now contains block 401 but not 400... - assertNotNull(repository.getBlockRepository().fromHeight(401)); - assertNull(repository.getBlockRepository().fromHeight(400)); - - // Import the remaining 399 blocks - BlockArchiveUtils.importFromArchive(2, 400, repository); - - // Verify that block 3 matches the original - BlockData block3DataPostArchive = repository.getBlockRepository().fromHeight(3); - assertArrayEquals(block3DataPreArchive.getSignature(), block3DataPostArchive.getSignature()); - assertEquals(block3DataPreArchive.getHeight(), block3DataPostArchive.getHeight()); - - // Orphan 1 more block, which should be the last one that is possible to be orphaned - BlockUtils.orphanBlocks(repository, 1); - - // Orphan another block, which should fail - Exception exception = null; - try { - BlockUtils.orphanBlocks(repository, 1); - } catch (DataException e) { - exception = e; - } - - // Ensure that a DataException is thrown because there is no more AT states data available - assertNotNull(exception); - assertEquals(DataException.class, exception.getClass()); - - // FUTURE: we may be able to retain unique AT states when trimming, to avoid this exception - // and allow orphaning back through blocks with trimmed AT states. - + public void testTrimArchivePruneAndOrphan() throws DataException, InterruptedException, TransformationException, IOException { + try (final Repository repository = RepositoryManager.getRepository()) { + + System.out.println("Starting testTrimArchivePruneAndOrphan"); + + // Deploy an AT so that we have AT state data + System.out.println("Deploying AT..."); + PrivateKeyAccount deployer = Common.getTestAccount(repository, "alice"); + byte[] creationBytes = AtUtils.buildSimpleAT(); + long fundingAmount = 1_00000000L; + AtUtils.doDeployAT(repository, deployer, creationBytes, fundingAmount); + System.out.println("AT deployed successfully."); + + // Mint some blocks so that we are able to archive them later + System.out.println("Minting 1000 blocks..."); + for (int i = 0; i < 1000; i++) { + BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); + // Log every 100 blocks + if ((i + 1) % 100 == 0) { + System.out.println("Minted block " + (i + 1)); + } + } + System.out.println("Finished minting blocks."); + + // Make sure that block 500 has full AT state data and data hash + System.out.println("Verifying block 500 AT state data..."); + List block500AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(500); + ATStateData atStatesData = repository.getATRepository().getATStateAtHeight(block500AtStatesData.get(0).getATAddress(), 500); + assertNotNull(atStatesData.getStateHash()); + assertNotNull(atStatesData.getStateData()); + System.out.println("Block 500 AT state data verified."); + + // Trim the first 500 blocks + System.out.println("Trimming first 500 blocks..."); + repository.getBlockRepository().trimOldOnlineAccountsSignatures(0, 500); + repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(501); + repository.getATRepository().rebuildLatestAtStates(500); + repository.getATRepository().trimAtStates(0, 500, 1000); + repository.getATRepository().setAtTrimHeight(501); + System.out.println("Trimming completed."); + + // Now block 499 should only have the AT state data hash + System.out.println("Checking block 499 AT state data..."); + List block499AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(499); + atStatesData = repository.getATRepository().getATStateAtHeight(block499AtStatesData.get(0).getATAddress(), 499); + assertNotNull(atStatesData.getStateHash()); + assertNull(atStatesData.getStateData()); + System.out.println("Block 499 AT state data contains only state hash as expected."); + + // ... but block 500 should have the full data (due to being retained as the "latest" AT state in the trimmed range + System.out.println("Verifying block 500 AT state data again..."); + block500AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(500); + atStatesData = repository.getATRepository().getATStateAtHeight(block500AtStatesData.get(0).getATAddress(), 500); + assertNotNull(atStatesData.getStateHash()); + assertNotNull(atStatesData.getStateData()); + System.out.println("Block 500 AT state data contains full data."); + + // ... and block 501 should also have the full data + System.out.println("Verifying block 501 AT state data..."); + List block501AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(501); + atStatesData = repository.getATRepository().getATStateAtHeight(block501AtStatesData.get(0).getATAddress(), 501); + assertNotNull(atStatesData.getStateHash()); + assertNotNull(atStatesData.getStateData()); + System.out.println("Block 501 AT state data contains full data."); + + // Check the max archive height - this should be one less than the first untrimmed height + final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); + System.out.println("Maximum archive height determined (Expected 500): " + maximumArchiveHeight); + assertEquals(500, maximumArchiveHeight); + + BlockData block3DataPreArchive = repository.getBlockRepository().fromHeight(3); + + // Write blocks 2-500 to the archive + System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); + BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); + writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes + BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); + System.out.println("Finished writing blocks to archive. Result: " + result); + assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); + + // Make sure that the archive contains the correct number of blocks + System.out.println("Number of blocks written to archive (Expected 499): " + writer.getWrittenCount()); + assertEquals(500 - 1, writer.getWrittenCount()); // -1 for the genesis block + + // Increment block archive height + repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); + repository.saveChanges(); + assertEquals(500 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); + System.out.println("Block archive height updated to: " + (500 - 1)); + + // Ensure the file exists + File outputFile = writer.getOutputPath().toFile(); + assertTrue(outputFile.exists()); + System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); + + // Ensure the SQL repository contains blocks 2 and 500... + System.out.println("Verifying that blocks 2 and 500 exist in the repository..."); + assertNotNull(repository.getBlockRepository().fromHeight(2)); + assertNotNull(repository.getBlockRepository().fromHeight(500)); + System.out.println("Blocks 2 and 500 are present in the repository."); + + // Prune all the archived blocks + System.out.println("Pruning blocks 2 to 500..."); + int numBlocksPruned = repository.getBlockRepository().pruneBlocks(0, 500); + System.out.println("Number of blocks pruned (Expected 499): " + numBlocksPruned); + assertEquals(500-1, numBlocksPruned); + repository.getBlockRepository().setBlockPruneHeight(501); + + // Prune the AT states for the archived blocks + System.out.println("Pruning AT states up to height 500..."); + repository.getATRepository().rebuildLatestAtStates(500); + repository.saveChanges(); + int numATStatesPruned = repository.getATRepository().pruneAtStates(2, 500); + System.out.println("Number of AT states pruned (Expected 498): " + numATStatesPruned); + assertEquals(498, numATStatesPruned); // Minus 1 for genesis block, and another for the latest AT state + repository.getATRepository().setAtPruneHeight(501); + + // Now ensure the SQL repository is missing blocks 2 and 500... + System.out.println("Verifying that blocks 2 and 500 have been pruned..."); + assertNull(repository.getBlockRepository().fromHeight(2)); + assertNull(repository.getBlockRepository().fromHeight(500)); + System.out.println("Blocks 2 and 500 have been successfully pruned."); + + // ... but it's not missing blocks 1 and 501 (we don't prune the genesis block) + System.out.println("Verifying that blocks 1 and 501 still exist..."); + assertNotNull(repository.getBlockRepository().fromHeight(1)); + assertNotNull(repository.getBlockRepository().fromHeight(501)); + System.out.println("Blocks 1 and 501 are present in the repository."); + + // Validate the latest block height in the repository + int lastBlockHeight = repository.getBlockRepository().getLastBlock().getHeight(); + System.out.println("Latest block height in repository (Expected 1002): " + lastBlockHeight); + assertEquals(1002, lastBlockHeight); + + // Now orphan some unarchived blocks. + System.out.println("Orphaning 500 blocks..."); + BlockUtils.orphanBlocks(repository, 500); + int currentLastBlockHeight = repository.getBlockRepository().getLastBlock().getHeight(); + System.out.println("New last block height after orphaning (Expected 502): " + currentLastBlockHeight); + assertEquals(502, currentLastBlockHeight); + + // We're close to the lower limit of the SQL database now, so + // we need to import some blocks from the archive + System.out.println("Importing blocks 401 to 500 from the archive..."); + BlockArchiveUtils.importFromArchive(401, 500, repository); + + // Ensure the SQL repository now contains block 401 but not 400... + System.out.println("Verifying that block 401 exists and block 400 does not..."); + assertNotNull(repository.getBlockRepository().fromHeight(401)); + assertNull(repository.getBlockRepository().fromHeight(400)); + System.out.println("Block 401 exists, block 400 does not."); + + // Import the remaining 399 blocks + System.out.println("Importing blocks 2 to 400 from the archive..."); + BlockArchiveUtils.importFromArchive(2, 400, repository); + + // Verify that block 3 matches the original + System.out.println("Verifying that block 3 matches the original data..."); + BlockData block3DataPostArchive = repository.getBlockRepository().fromHeight(3); + assertArrayEquals(block3DataPreArchive.getSignature(), block3DataPostArchive.getSignature()); + assertEquals(block3DataPreArchive.getHeight(), block3DataPostArchive.getHeight()); + System.out.println("Block 3 data matches the original."); + + // Orphan 1 more block, which should be the last one that is possible to be orphaned + System.out.println("Orphaning 1 more block..."); + BlockUtils.orphanBlocks(repository, 1); + System.out.println("Orphaned 1 block successfully."); + + // Orphan another block, which should fail + System.out.println("Attempting to orphan another block, which should fail..."); + Exception exception = null; + try { + BlockUtils.orphanBlocks(repository, 1); + } catch (DataException e) { + exception = e; + System.out.println("Caught expected DataException: " + e.getMessage()); + } + + // Ensure that a DataException is thrown because there is no more AT states data available + assertNotNull(exception); + assertEquals(DataException.class, exception.getClass()); + System.out.println("DataException confirmed due to lack of AT states data."); + + // FUTURE: we may be able to retain unique AT states when trimming, to avoid this exception + // and allow orphaning back through blocks with trimmed AT states. + + System.out.println("testTrimArchivePruneAndOrphan completed successfully."); } } @@ -478,32 +611,44 @@ public class BlockArchiveV1Tests extends Common { * In these cases we disable archiving and pruning as this index is a * very essential component in these processes. */ - @Test - public void testMissingAtStatesHeightIndex() throws DataException, SQLException { - try (final HSQLDBRepository repository = (HSQLDBRepository) RepositoryManager.getRepository()) { + @Test + public void testMissingAtStatesHeightIndex() throws DataException, SQLException { + try (final HSQLDBRepository repository = (HSQLDBRepository) RepositoryManager.getRepository()) { - // Firstly check that we're able to prune or archive when the index exists - assertTrue(repository.getATRepository().hasAtStatesHeightIndex()); - assertTrue(RepositoryManager.canArchiveOrPrune()); + System.out.println("Starting testMissingAtStatesHeightIndex"); - // Delete the index - repository.prepareStatement("DROP INDEX ATSTATESHEIGHTINDEX").execute(); + // Firstly check that we're able to prune or archive when the index exists + System.out.println("Checking existence of ATStatesHeightIndex..."); + assertTrue(repository.getATRepository().hasAtStatesHeightIndex()); + assertTrue(RepositoryManager.canArchiveOrPrune()); + System.out.println("ATStatesHeightIndex exists. Archiving and pruning are possible."); - // Ensure check that we're unable to prune or archive when the index doesn't exist - assertFalse(repository.getATRepository().hasAtStatesHeightIndex()); - assertFalse(RepositoryManager.canArchiveOrPrune()); - } - } + // Delete the index + System.out.println("Dropping ATStatesHeightIndex..."); + repository.prepareStatement("DROP INDEX ATSTATESHEIGHTINDEX").execute(); + System.out.println("ATStatesHeightIndex dropped."); + + // Ensure check that we're unable to prune or archive when the index doesn't exist + System.out.println("Verifying that ATStatesHeightIndex no longer exists..."); + assertFalse(repository.getATRepository().hasAtStatesHeightIndex()); + assertFalse(RepositoryManager.canArchiveOrPrune()); + System.out.println("ATStatesHeightIndex does not exist. Archiving and pruning are disabled."); + + System.out.println("testMissingAtStatesHeightIndex completed successfully."); + } + } - private void deleteArchiveDirectory() { - // Delete archive directory if exists - Path archivePath = Paths.get(Settings.getInstance().getRepositoryPath(), "archive").toAbsolutePath(); - try { - FileUtils.deleteDirectory(archivePath.toFile()); - } catch (IOException e) { - - } - } + private void deleteArchiveDirectory() { + // Delete archive directory if exists + Path archivePath = Paths.get(Settings.getInstance().getRepositoryPath(), "archive").toAbsolutePath(); + try { + FileUtils.deleteDirectory(archivePath.toFile()); + System.out.println("Deleted archive directory at: " + archivePath); + } catch (IOException e) { + + System.out.println("Failed to delete archive directory: " + e.getMessage()); + } + } } diff --git a/src/test/java/org/qortal/test/BlockArchiveV2Tests.java b/src/test/java/org/qortal/test/BlockArchiveV2Tests.java index 3b1d12d3..784ac3d3 100644 --- a/src/test/java/org/qortal/test/BlockArchiveV2Tests.java +++ b/src/test/java/org/qortal/test/BlockArchiveV2Tests.java @@ -34,471 +34,625 @@ import static org.junit.Assert.*; public class BlockArchiveV2Tests extends Common { - @Before - public void beforeTest() throws DataException, IllegalAccessException { - Common.useSettings("test-settings-v2-block-archive.json"); - NTP.setFixedOffset(Settings.getInstance().getTestNtpOffset()); - this.deleteArchiveDirectory(); + @Before + public void beforeTest() throws DataException, IllegalAccessException { + Common.useSettings("test-settings-v2-block-archive.json"); + NTP.setFixedOffset(Settings.getInstance().getTestNtpOffset()); + this.deleteArchiveDirectory(); - // Set default archive version to 2, so that archive builds in these tests use V2 - FieldUtils.writeField(Settings.getInstance(), "defaultArchiveVersion", 2, true); - } + // Set default archive version to 2, so that archive builds in these tests use V2 + FieldUtils.writeField(Settings.getInstance(), "defaultArchiveVersion", 2, true); + } - @After - public void afterTest() throws DataException { - this.deleteArchiveDirectory(); - } + @After + public void afterTest() throws DataException { + this.deleteArchiveDirectory(); + } - @Test - public void testWriter() throws DataException, InterruptedException, TransformationException, IOException { - try (final Repository repository = RepositoryManager.getRepository()) { + @Test + public void testWriter() throws DataException, InterruptedException, TransformationException, IOException { + try (final Repository repository = RepositoryManager.getRepository()) { - // Mint some blocks so that we are able to archive them later - for (int i = 0; i < 1000; i++) { - BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); - } + System.out.println("Starting testWriter"); - // 900 blocks are trimmed (this specifies the first untrimmed height) - repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(901); - repository.getATRepository().setAtTrimHeight(901); + // Mint some blocks so that we are able to archive them later + System.out.println("Minting 1000 blocks..."); + for (int i = 0; i < 1000; i++) { + BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); + // Log every 100 blocks + if ((i + 1) % 100 == 0) { + System.out.println("Minted block " + (i + 1)); + } + } + System.out.println("Finished minting blocks."); - // Check the max archive height - this should be one less than the first untrimmed height - final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); - assertEquals(900, maximumArchiveHeight); + // 900 blocks are trimmed (this specifies the first untrimmed height) + repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(901); + repository.getATRepository().setAtTrimHeight(901); + System.out.println("Set trim heights to 901."); - // Write blocks 2-900 to the archive - BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); - writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes - BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); - assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); + // Check the max archive height - this should be one less than the first untrimmed height + final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); + System.out.println("Maximum archive height (Expected 900): " + maximumArchiveHeight); + assertEquals(900, maximumArchiveHeight); - // Make sure that the archive contains the correct number of blocks - assertEquals(900 - 1, writer.getWrittenCount()); + // Write blocks 2-900 to the archive + System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); + BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); + writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes + BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); + System.out.println("Finished writing blocks to archive. Result: " + result); + assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); - // Increment block archive height - repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); - repository.saveChanges(); - assertEquals(900 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); + // Make sure that the archive contains the correct number of blocks + System.out.println("Archive contains " + writer.getWrittenCount() + " blocks. (Expected 899)"); + assertEquals(900 - 1, writer.getWrittenCount()); - // Ensure the file exists - File outputFile = writer.getOutputPath().toFile(); - assertTrue(outputFile.exists()); - } - } + // Increment block archive height + repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); + repository.saveChanges(); + assertEquals(900 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); - @Test - public void testWriterAndReader() throws DataException, InterruptedException, TransformationException, IOException { - try (final Repository repository = RepositoryManager.getRepository()) { + // Ensure the file exists + File outputFile = writer.getOutputPath().toFile(); + assertTrue(outputFile.exists()); + System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); - // Mint some blocks so that we are able to archive them later - for (int i = 0; i < 1000; i++) { - BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); - } + System.out.println("testWriter completed successfully."); + } + } - // 900 blocks are trimmed (this specifies the first untrimmed height) - repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(901); - repository.getATRepository().setAtTrimHeight(901); + @Test + public void testWriterAndReader() throws DataException, InterruptedException, TransformationException, IOException { + try (final Repository repository = RepositoryManager.getRepository()) { - // Check the max archive height - this should be one less than the first untrimmed height - final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); - assertEquals(900, maximumArchiveHeight); + System.out.println("Starting testWriterAndReader"); - // Write blocks 2-900 to the archive - BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); - writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes - BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); - assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); + // Mint some blocks so that we are able to archive them later + System.out.println("Minting 1000 blocks..."); + for (int i = 0; i < 1000; i++) { + BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); + // Log every 100 blocks + if ((i + 1) % 100 == 0) { + System.out.println("Minted block " + (i + 1)); + } + } + System.out.println("Finished minting blocks."); - // Make sure that the archive contains the correct number of blocks - assertEquals(900 - 1, writer.getWrittenCount()); + // 900 blocks are trimmed (this specifies the first untrimmed height) + repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(901); + repository.getATRepository().setAtTrimHeight(901); + System.out.println("Set trim heights to 901."); - // Increment block archive height - repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); - repository.saveChanges(); - assertEquals(900 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); + // Check the max archive height - this should be one less than the first untrimmed height + final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); + System.out.println("Maximum archive height (Expected 900): " + maximumArchiveHeight); + assertEquals(900, maximumArchiveHeight); - // Ensure the file exists - File outputFile = writer.getOutputPath().toFile(); - assertTrue(outputFile.exists()); + // Write blocks 2-900 to the archive + System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); + BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); + writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes + BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); + System.out.println("Finished writing blocks to archive. Result: " + result); + assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); - // Read block 2 from the archive - BlockArchiveReader reader = BlockArchiveReader.getInstance(); - BlockTransformation block2Info = reader.fetchBlockAtHeight(2); - BlockData block2ArchiveData = block2Info.getBlockData(); + // Make sure that the archive contains the correct number of blocks + System.out.println("Archive contains " + writer.getWrittenCount() + " blocks. (Expected 899)"); + assertEquals(900 - 1, writer.getWrittenCount()); - // Read block 2 from the repository - BlockData block2RepositoryData = repository.getBlockRepository().fromHeight(2); + // Increment block archive height + repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); + repository.saveChanges(); + assertEquals(900 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); - // Ensure the values match - assertEquals(block2ArchiveData.getHeight(), block2RepositoryData.getHeight()); - assertArrayEquals(block2ArchiveData.getSignature(), block2RepositoryData.getSignature()); + // Ensure the file exists + File outputFile = writer.getOutputPath().toFile(); + assertTrue(outputFile.exists()); + System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); - // Test some values in the archive - assertEquals(1, block2ArchiveData.getOnlineAccountsCount()); + // Read block 2 from the archive + System.out.println("Reading block 2 from the archive..."); + BlockArchiveReader reader = BlockArchiveReader.getInstance(); + BlockTransformation block2Info = reader.fetchBlockAtHeight(2); + BlockData block2ArchiveData = block2Info.getBlockData(); - // Read block 900 from the archive - BlockTransformation block900Info = reader.fetchBlockAtHeight(900); - BlockData block900ArchiveData = block900Info.getBlockData(); + // Read block 2 from the repository + BlockData block2RepositoryData = repository.getBlockRepository().fromHeight(2); - // Read block 900 from the repository - BlockData block900RepositoryData = repository.getBlockRepository().fromHeight(900); + // Ensure the values match + System.out.println("Comparing block 2 data..."); + assertEquals(block2ArchiveData.getHeight(), block2RepositoryData.getHeight()); + assertArrayEquals(block2ArchiveData.getSignature(), block2RepositoryData.getSignature()); - // Ensure the values match - assertEquals(block900ArchiveData.getHeight(), block900RepositoryData.getHeight()); - assertArrayEquals(block900ArchiveData.getSignature(), block900RepositoryData.getSignature()); + // Test some values in the archive + assertEquals(1, block2ArchiveData.getOnlineAccountsCount()); - // Test some values in the archive - assertEquals(1, block900ArchiveData.getOnlineAccountsCount()); + // Read block 900 from the archive + System.out.println("Reading block 900 from the archive..."); + BlockTransformation block900Info = reader.fetchBlockAtHeight(900); + BlockData block900ArchiveData = block900Info.getBlockData(); - } - } + // Read block 900 from the repository + BlockData block900RepositoryData = repository.getBlockRepository().fromHeight(900); - @Test - public void testArchivedAtStates() throws DataException, InterruptedException, TransformationException, IOException { - try (final Repository repository = RepositoryManager.getRepository()) { + // Ensure the values match + System.out.println("Comparing block 900 data..."); + assertEquals(block900ArchiveData.getHeight(), block900RepositoryData.getHeight()); + assertArrayEquals(block900ArchiveData.getSignature(), block900RepositoryData.getSignature()); - // Deploy an AT so that we have AT state data - PrivateKeyAccount deployer = Common.getTestAccount(repository, "alice"); - byte[] creationBytes = AtUtils.buildSimpleAT(); - long fundingAmount = 1_00000000L; - DeployAtTransaction deployAtTransaction = AtUtils.doDeployAT(repository, deployer, creationBytes, fundingAmount); - String atAddress = deployAtTransaction.getATAccount().getAddress(); + // Test some values in the archive + assertEquals(1, block900ArchiveData.getOnlineAccountsCount()); - // Mint some blocks so that we are able to archive them later - for (int i = 0; i < 1000; i++) { - BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); - } + System.out.println("testWriterAndReader completed successfully."); + } + } - // 9 blocks are trimmed (this specifies the first untrimmed height) - repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(10); - repository.getATRepository().setAtTrimHeight(10); + @Test + public void testArchivedAtStates() throws DataException, InterruptedException, TransformationException, IOException { + try (final Repository repository = RepositoryManager.getRepository()) { - // Check the max archive height - final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); - assertEquals(9, maximumArchiveHeight); + System.out.println("Starting testArchivedAtStates"); - // Write blocks 2-9 to the archive - BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); - writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes - BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); - assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); + // Deploy an AT so that we have AT state data + System.out.println("Deploying AT..."); + PrivateKeyAccount deployer = Common.getTestAccount(repository, "alice"); + byte[] creationBytes = AtUtils.buildSimpleAT(); + long fundingAmount = 1_00000000L; + DeployAtTransaction deployAtTransaction = AtUtils.doDeployAT(repository, deployer, creationBytes, fundingAmount); + String atAddress = deployAtTransaction.getATAccount().getAddress(); + System.out.println("AT deployed at address: " + atAddress); - // Make sure that the archive contains the correct number of blocks - assertEquals(9 - 1, writer.getWrittenCount()); + // Mint some blocks so that we are able to archive them later + System.out.println("Minting 1000 blocks..."); + for (int i = 0; i < 1000; i++) { + BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); + // Log every 100 blocks + if ((i + 1) % 100 == 0) { + System.out.println("Minted block " + (i + 1)); + } + } + System.out.println("Finished minting blocks."); - // Increment block archive height - repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); - repository.saveChanges(); - assertEquals(9 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); + // 9 blocks are trimmed (this specifies the first untrimmed height) + repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(10); + repository.getATRepository().setAtTrimHeight(10); + System.out.println("Set trim heights to 10."); - // Ensure the file exists - File outputFile = writer.getOutputPath().toFile(); - assertTrue(outputFile.exists()); + // Check the max archive height + final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); + System.out.println("Maximum archive height (Expected 9): " + maximumArchiveHeight); + assertEquals(9, maximumArchiveHeight); - // Check blocks 3-9 - for (Integer testHeight = 2; testHeight <= 9; testHeight++) { + // Write blocks 2-9 to the archive + System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); + BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); + writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes + BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); + System.out.println("Finished writing blocks to archive. Result: " + result); + assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); - // Read a block from the archive - BlockArchiveReader reader = BlockArchiveReader.getInstance(); - BlockTransformation blockInfo = reader.fetchBlockAtHeight(testHeight); - BlockData archivedBlockData = blockInfo.getBlockData(); - byte[] archivedAtStateHash = blockInfo.getAtStatesHash(); - List archivedTransactions = blockInfo.getTransactions(); + // Make sure that the archive contains the correct number of blocks + System.out.println("Archive contains " + writer.getWrittenCount() + " blocks. (Expected 8)"); + assertEquals(9 - 1, writer.getWrittenCount()); - // Read the same block from the repository - BlockData repositoryBlockData = repository.getBlockRepository().fromHeight(testHeight); - ATStateData repositoryAtStateData = repository.getATRepository().getATStateAtHeight(atAddress, testHeight); + // Increment block archive height + repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); + repository.saveChanges(); + assertEquals(9 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); + System.out.println("Block archive height updated to: " + (9 - 1)); - // Ensure the repository has full AT state data - assertNotNull(repositoryAtStateData.getStateHash()); - assertNotNull(repositoryAtStateData.getStateData()); + // Ensure the file exists + File outputFile = writer.getOutputPath().toFile(); + assertTrue(outputFile.exists()); + System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); - // Check the archived AT state - if (testHeight == 2) { - assertEquals(1, archivedTransactions.size()); - assertEquals(Transaction.TransactionType.DEPLOY_AT, archivedTransactions.get(0).getType()); - } + // Check blocks 3-9 + System.out.println("Checking blocks 2 to 9..."); + for (Integer testHeight = 2; testHeight <= 9; testHeight++) { + + System.out.println("Reading block " + testHeight + " from the archive..."); + // Read a block from the archive + BlockArchiveReader reader = BlockArchiveReader.getInstance(); + BlockTransformation blockInfo = reader.fetchBlockAtHeight(testHeight); + BlockData archivedBlockData = blockInfo.getBlockData(); + byte[] archivedAtStateHash = blockInfo.getAtStatesHash(); + List archivedTransactions = blockInfo.getTransactions(); + + // Read the same block from the repository + BlockData repositoryBlockData = repository.getBlockRepository().fromHeight(testHeight); + ATStateData repositoryAtStateData = repository.getATRepository().getATStateAtHeight(atAddress, testHeight); + + // Ensure the repository has full AT state data + assertNotNull(repositoryAtStateData.getStateHash()); + assertNotNull(repositoryAtStateData.getStateData()); + + // Check the archived AT state + if (testHeight == 2) { + System.out.println("Checking block " + testHeight + " AT state data (expected transactions)..."); + assertEquals(1, archivedTransactions.size()); + assertEquals(Transaction.TransactionType.DEPLOY_AT, archivedTransactions.get(0).getType()); + } else { - // Blocks 3+ shouldn't have any transactions - assertTrue(archivedTransactions.isEmpty()); - } + System.out.println("Checking block " + testHeight + " AT state data (no transactions expected)..."); + // Blocks 3+ shouldn't have any transactions + assertTrue(archivedTransactions.isEmpty()); + } - // Ensure the archive has the AT states hash - assertNotNull(archivedAtStateHash); + // Ensure the archive has the AT states hash + System.out.println("Checking block " + testHeight + " AT states hash..."); + assertNotNull(archivedAtStateHash); - // Also check the online accounts count and height - assertEquals(1, archivedBlockData.getOnlineAccountsCount()); - assertEquals(testHeight, archivedBlockData.getHeight()); + // Also check the online accounts count and height + assertEquals(1, archivedBlockData.getOnlineAccountsCount()); + assertEquals(testHeight, archivedBlockData.getHeight()); - // Ensure the values match - assertEquals(archivedBlockData.getHeight(), repositoryBlockData.getHeight()); - assertArrayEquals(archivedBlockData.getSignature(), repositoryBlockData.getSignature()); - assertEquals(archivedBlockData.getOnlineAccountsCount(), repositoryBlockData.getOnlineAccountsCount()); - assertArrayEquals(archivedBlockData.getMinterSignature(), repositoryBlockData.getMinterSignature()); - assertEquals(archivedBlockData.getATCount(), repositoryBlockData.getATCount()); - assertEquals(archivedBlockData.getOnlineAccountsCount(), repositoryBlockData.getOnlineAccountsCount()); - assertArrayEquals(archivedBlockData.getReference(), repositoryBlockData.getReference()); - assertEquals(archivedBlockData.getTimestamp(), repositoryBlockData.getTimestamp()); - assertEquals(archivedBlockData.getATFees(), repositoryBlockData.getATFees()); - assertEquals(archivedBlockData.getTotalFees(), repositoryBlockData.getTotalFees()); - assertEquals(archivedBlockData.getTransactionCount(), repositoryBlockData.getTransactionCount()); - assertArrayEquals(archivedBlockData.getTransactionsSignature(), repositoryBlockData.getTransactionsSignature()); + // Ensure the values match + System.out.println("Comparing block " + testHeight + " data..."); + assertEquals(archivedBlockData.getHeight(), repositoryBlockData.getHeight()); + assertArrayEquals(archivedBlockData.getSignature(), repositoryBlockData.getSignature()); + assertEquals(archivedBlockData.getOnlineAccountsCount(), repositoryBlockData.getOnlineAccountsCount()); + assertArrayEquals(archivedBlockData.getMinterSignature(), repositoryBlockData.getMinterSignature()); + assertEquals(archivedBlockData.getATCount(), repositoryBlockData.getATCount()); + assertEquals(archivedBlockData.getOnlineAccountsCount(), repositoryBlockData.getOnlineAccountsCount()); + assertArrayEquals(archivedBlockData.getReference(), repositoryBlockData.getReference()); + assertEquals(archivedBlockData.getTimestamp(), repositoryBlockData.getTimestamp()); + assertEquals(archivedBlockData.getATFees(), repositoryBlockData.getATFees()); + assertEquals(archivedBlockData.getTotalFees(), repositoryBlockData.getTotalFees()); + assertEquals(archivedBlockData.getTransactionCount(), repositoryBlockData.getTransactionCount()); + assertArrayEquals(archivedBlockData.getTransactionsSignature(), repositoryBlockData.getTransactionsSignature()); - // TODO: build atStatesHash and compare against value in archive - } + // TODO: build atStatesHash and compare against value in archive + } - // Check block 10 (unarchived) - BlockArchiveReader reader = BlockArchiveReader.getInstance(); - BlockTransformation blockInfo = reader.fetchBlockAtHeight(10); - assertNull(blockInfo); + // Check block 10 (unarchived) + System.out.println("Checking block 10 (should not be in archive)..."); + BlockArchiveReader reader = BlockArchiveReader.getInstance(); + BlockTransformation blockInfo = reader.fetchBlockAtHeight(10); + assertNull(blockInfo); - } + System.out.println("testArchivedAtStates completed successfully."); + } - } + } - @Test - public void testArchiveAndPrune() throws DataException, InterruptedException, TransformationException, IOException { - try (final Repository repository = RepositoryManager.getRepository()) { + @Test + public void testArchiveAndPrune() throws DataException, InterruptedException, TransformationException, IOException { + try (final Repository repository = RepositoryManager.getRepository()) { - // Deploy an AT so that we have AT state data - PrivateKeyAccount deployer = Common.getTestAccount(repository, "alice"); - byte[] creationBytes = AtUtils.buildSimpleAT(); - long fundingAmount = 1_00000000L; - AtUtils.doDeployAT(repository, deployer, creationBytes, fundingAmount); + System.out.println("Starting testArchiveAndPrune"); - // Mint some blocks so that we are able to archive them later - for (int i = 0; i < 1000; i++) { - BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); - } + // Deploy an AT so that we have AT state data + System.out.println("Deploying AT..."); + PrivateKeyAccount deployer = Common.getTestAccount(repository, "alice"); + byte[] creationBytes = AtUtils.buildSimpleAT(); + long fundingAmount = 1_00000000L; + AtUtils.doDeployAT(repository, deployer, creationBytes, fundingAmount); + System.out.println("AT deployed successfully."); - // Assume 900 blocks are trimmed (this specifies the first untrimmed height) - repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(901); - repository.getATRepository().setAtTrimHeight(901); + // Mint some blocks so that we are able to archive them later + System.out.println("Minting 1000 blocks..."); + for (int i = 0; i < 1000; i++) { + BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); + // Log every 100 blocks + if ((i + 1) % 100 == 0) { + System.out.println("Minted block " + (i + 1)); + } + } + System.out.println("Finished minting blocks."); - // Check the max archive height - this should be one less than the first untrimmed height - final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); - assertEquals(900, maximumArchiveHeight); + // Assume 900 blocks are trimmed (this specifies the first untrimmed height) + repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(901); + repository.getATRepository().setAtTrimHeight(901); + System.out.println("Set trim heights to 901."); - // Write blocks 2-900 to the archive - BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); - writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes - BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); - assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); + // Check the max archive height - this should be one less than the first untrimmed height + final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); + System.out.println("Maximum archive height (Expected 900): " + maximumArchiveHeight); + assertEquals(900, maximumArchiveHeight); - // Make sure that the archive contains the correct number of blocks - assertEquals(900 - 1, writer.getWrittenCount()); + // Write blocks 2-900 to the archive + System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); + BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); + writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes + BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); + System.out.println("Finished writing blocks to archive. Result: " + result); + assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); - // Increment block archive height - repository.getBlockArchiveRepository().setBlockArchiveHeight(901); - repository.saveChanges(); - assertEquals(901, repository.getBlockArchiveRepository().getBlockArchiveHeight()); + // Make sure that the archive contains the correct number of blocks + System.out.println("Archive contains " + writer.getWrittenCount() + " blocks. (Expected 899)"); + assertEquals(900 - 1, writer.getWrittenCount()); - // Ensure the file exists - File outputFile = writer.getOutputPath().toFile(); - assertTrue(outputFile.exists()); + // Increment block archive height + repository.getBlockArchiveRepository().setBlockArchiveHeight(901); + repository.saveChanges(); + assertEquals(901, repository.getBlockArchiveRepository().getBlockArchiveHeight()); - // Ensure the SQL repository contains blocks 2 and 900... - assertNotNull(repository.getBlockRepository().fromHeight(2)); - assertNotNull(repository.getBlockRepository().fromHeight(900)); + // Ensure the file exists + File outputFile = writer.getOutputPath().toFile(); + assertTrue(outputFile.exists()); + System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); - // Prune all the archived blocks - int numBlocksPruned = repository.getBlockRepository().pruneBlocks(0, 900); - assertEquals(900-1, numBlocksPruned); - repository.getBlockRepository().setBlockPruneHeight(901); + // Ensure the SQL repository contains blocks 2 and 900... + System.out.println("Verifying that blocks 2 and 900 exist in the repository..."); + assertNotNull(repository.getBlockRepository().fromHeight(2)); + assertNotNull(repository.getBlockRepository().fromHeight(900)); + System.out.println("Blocks 2 and 900 are present in the repository."); - // Prune the AT states for the archived blocks - repository.getATRepository().rebuildLatestAtStates(900); - repository.saveChanges(); - int numATStatesPruned = repository.getATRepository().pruneAtStates(0, 900); - assertEquals(900-2, numATStatesPruned); // Minus 1 for genesis block, and another for the latest AT state - repository.getATRepository().setAtPruneHeight(901); + // Prune all the archived blocks + System.out.println("Pruning blocks 2 to 900..."); + int numBlocksPruned = repository.getBlockRepository().pruneBlocks(0, 900); + System.out.println("Number of blocks pruned (Expected 899): " + numBlocksPruned); + assertEquals(900-1, numBlocksPruned); + repository.getBlockRepository().setBlockPruneHeight(901); - // Now ensure the SQL repository is missing blocks 2 and 900... - assertNull(repository.getBlockRepository().fromHeight(2)); - assertNull(repository.getBlockRepository().fromHeight(900)); + // Prune the AT states for the archived blocks + System.out.println("Pruning AT states up to height 900..."); + repository.getATRepository().rebuildLatestAtStates(900); + repository.saveChanges(); + int numATStatesPruned = repository.getATRepository().pruneAtStates(0, 900); + System.out.println("Number of AT states pruned (Expected 898): " + numATStatesPruned); + assertEquals(900-2, numATStatesPruned); // Minus 1 for genesis block, and another for the latest AT state + repository.getATRepository().setAtPruneHeight(901); - // ... but it's not missing blocks 1 and 901 (we don't prune the genesis block) - assertNotNull(repository.getBlockRepository().fromHeight(1)); - assertNotNull(repository.getBlockRepository().fromHeight(901)); + // Now ensure the SQL repository is missing blocks 2 and 900... + System.out.println("Verifying that blocks 2 and 900 have been pruned..."); + assertNull(repository.getBlockRepository().fromHeight(2)); + assertNull(repository.getBlockRepository().fromHeight(900)); + System.out.println("Blocks 2 and 900 have been successfully pruned."); - // Validate the latest block height in the repository - assertEquals(1002, (int) repository.getBlockRepository().getLastBlock().getHeight()); + // ... but it's not missing blocks 1 and 901 (we don't prune the genesis block) + System.out.println("Verifying that blocks 1 and 901 still exist..."); + assertNotNull(repository.getBlockRepository().fromHeight(1)); + assertNotNull(repository.getBlockRepository().fromHeight(901)); + System.out.println("Blocks 1 and 901 are present in the repository."); - } - } + // Validate the latest block height in the repository + int lastBlockHeight = repository.getBlockRepository().getLastBlock().getHeight(); + System.out.println("Latest block height in repository (Expected 1002): " + lastBlockHeight); + assertEquals(1002, lastBlockHeight); - @Test - public void testTrimArchivePruneAndOrphan() throws DataException, InterruptedException, TransformationException, IOException { - try (final Repository repository = RepositoryManager.getRepository()) { + System.out.println("testArchiveAndPrune completed successfully."); + } + } - // Deploy an AT so that we have AT state data - PrivateKeyAccount deployer = Common.getTestAccount(repository, "alice"); - byte[] creationBytes = AtUtils.buildSimpleAT(); - long fundingAmount = 1_00000000L; - AtUtils.doDeployAT(repository, deployer, creationBytes, fundingAmount); + @Test + public void testTrimArchivePruneAndOrphan() throws DataException, InterruptedException, TransformationException, IOException { + try (final Repository repository = RepositoryManager.getRepository()) { - // Mint some blocks so that we are able to archive them later - for (int i = 0; i < 1000; i++) { - BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); - } + System.out.println("Starting testTrimArchivePruneAndOrphan"); - // Make sure that block 500 has full AT state data and data hash - List block500AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(500); - ATStateData atStatesData = repository.getATRepository().getATStateAtHeight(block500AtStatesData.get(0).getATAddress(), 500); - assertNotNull(atStatesData.getStateHash()); - assertNotNull(atStatesData.getStateData()); + // Deploy an AT so that we have AT state data + System.out.println("Deploying AT..."); + PrivateKeyAccount deployer = Common.getTestAccount(repository, "alice"); + byte[] creationBytes = AtUtils.buildSimpleAT(); + long fundingAmount = 1_00000000L; + AtUtils.doDeployAT(repository, deployer, creationBytes, fundingAmount); + System.out.println("AT deployed successfully."); - // Trim the first 500 blocks - repository.getBlockRepository().trimOldOnlineAccountsSignatures(0, 500); - repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(501); - repository.getATRepository().rebuildLatestAtStates(500); - repository.getATRepository().trimAtStates(0, 500, 1000); - repository.getATRepository().setAtTrimHeight(501); + // Mint some blocks so that we are able to archive them later + System.out.println("Minting 1000 blocks..."); + for (int i = 0; i < 1000; i++) { + BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); + // Log every 100 blocks + if ((i + 1) % 100 == 0) { + System.out.println("Minted block " + (i + 1)); + } + } + System.out.println("Finished minting blocks."); - // Now block 499 should only have the AT state data hash - List block499AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(499); - atStatesData = repository.getATRepository().getATStateAtHeight(block499AtStatesData.get(0).getATAddress(), 499); - assertNotNull(atStatesData.getStateHash()); - assertNull(atStatesData.getStateData()); + // Make sure that block 500 has full AT state data and data hash + System.out.println("Verifying block 500 AT state data..."); + List block500AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(500); + ATStateData atStatesData = repository.getATRepository().getATStateAtHeight(block500AtStatesData.get(0).getATAddress(), 500); + assertNotNull(atStatesData.getStateHash()); + assertNotNull(atStatesData.getStateData()); + System.out.println("Block 500 AT state data verified."); - // ... but block 500 should have the full data (due to being retained as the "latest" AT state in the trimmed range - block500AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(500); - atStatesData = repository.getATRepository().getATStateAtHeight(block500AtStatesData.get(0).getATAddress(), 500); - assertNotNull(atStatesData.getStateHash()); - assertNotNull(atStatesData.getStateData()); + // Trim the first 500 blocks + System.out.println("Trimming first 500 blocks..."); + repository.getBlockRepository().trimOldOnlineAccountsSignatures(0, 500); + repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(501); + repository.getATRepository().rebuildLatestAtStates(500); + repository.getATRepository().trimAtStates(0, 500, 1000); + repository.getATRepository().setAtTrimHeight(501); + System.out.println("Trimming completed."); - // ... and block 501 should also have the full data - List block501AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(501); - atStatesData = repository.getATRepository().getATStateAtHeight(block501AtStatesData.get(0).getATAddress(), 501); - assertNotNull(atStatesData.getStateHash()); - assertNotNull(atStatesData.getStateData()); + // Now block 499 should only have the AT state data hash + System.out.println("Checking block 499 AT state data..."); + List block499AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(499); + atStatesData = repository.getATRepository().getATStateAtHeight(block499AtStatesData.get(0).getATAddress(), 499); + assertNotNull(atStatesData.getStateHash()); + assertNull(atStatesData.getStateData()); + System.out.println("Block 499 AT state data contains only state hash as expected."); - // Check the max archive height - this should be one less than the first untrimmed height - final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); - assertEquals(500, maximumArchiveHeight); + // ... but block 500 should have the full data (due to being retained as the "latest" AT state in the trimmed range + System.out.println("Verifying block 500 AT state data again..."); + block500AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(500); + atStatesData = repository.getATRepository().getATStateAtHeight(block500AtStatesData.get(0).getATAddress(), 500); + assertNotNull(atStatesData.getStateHash()); + assertNotNull(atStatesData.getStateData()); + System.out.println("Block 500 AT state data contains full data."); - BlockData block3DataPreArchive = repository.getBlockRepository().fromHeight(3); + // ... and block 501 should also have the full data + System.out.println("Verifying block 501 AT state data..."); + List block501AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(501); + atStatesData = repository.getATRepository().getATStateAtHeight(block501AtStatesData.get(0).getATAddress(), 501); + assertNotNull(atStatesData.getStateHash()); + assertNotNull(atStatesData.getStateData()); + System.out.println("Block 501 AT state data contains full data."); - // Write blocks 2-500 to the archive - BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); - writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes - BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); - assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); + // Check the max archive height - this should be one less than the first untrimmed height + final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); + System.out.println("Maximum archive height determined (Expected 500): " + maximumArchiveHeight); + assertEquals(500, maximumArchiveHeight); - // Make sure that the archive contains the correct number of blocks - assertEquals(500 - 1, writer.getWrittenCount()); // -1 for the genesis block + BlockData block3DataPreArchive = repository.getBlockRepository().fromHeight(3); - // Increment block archive height - repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); - repository.saveChanges(); - assertEquals(500 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); + // Write blocks 2-500 to the archive + System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); + BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); + writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes + BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); + System.out.println("Finished writing blocks to archive. Result: " + result); + assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); - // Ensure the file exists - File outputFile = writer.getOutputPath().toFile(); - assertTrue(outputFile.exists()); + // Make sure that the archive contains the correct number of blocks + System.out.println("Number of blocks written to archive (Expected 499): " + writer.getWrittenCount()); + assertEquals(500 - 1, writer.getWrittenCount()); // -1 for the genesis block - // Ensure the SQL repository contains blocks 2 and 500... - assertNotNull(repository.getBlockRepository().fromHeight(2)); - assertNotNull(repository.getBlockRepository().fromHeight(500)); + // Increment block archive height + repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); + repository.saveChanges(); + assertEquals(500 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); + System.out.println("Block archive height updated to: " + (500 - 1)); - // Prune all the archived blocks - int numBlocksPruned = repository.getBlockRepository().pruneBlocks(0, 500); - assertEquals(500-1, numBlocksPruned); - repository.getBlockRepository().setBlockPruneHeight(501); + // Ensure the file exists + File outputFile = writer.getOutputPath().toFile(); + assertTrue(outputFile.exists()); + System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); - // Prune the AT states for the archived blocks - repository.getATRepository().rebuildLatestAtStates(500); - repository.saveChanges(); - int numATStatesPruned = repository.getATRepository().pruneAtStates(2, 500); - assertEquals(498, numATStatesPruned); // Minus 1 for genesis block, and another for the latest AT state - repository.getATRepository().setAtPruneHeight(501); + // Ensure the SQL repository contains blocks 2 and 500... + System.out.println("Verifying that blocks 2 and 500 exist in the repository..."); + assertNotNull(repository.getBlockRepository().fromHeight(2)); + assertNotNull(repository.getBlockRepository().fromHeight(500)); + System.out.println("Blocks 2 and 500 are present in the repository."); - // Now ensure the SQL repository is missing blocks 2 and 500... - assertNull(repository.getBlockRepository().fromHeight(2)); - assertNull(repository.getBlockRepository().fromHeight(500)); + // Prune all the archived blocks + System.out.println("Pruning blocks 2 to 500..."); + int numBlocksPruned = repository.getBlockRepository().pruneBlocks(0, 500); + System.out.println("Number of blocks pruned (Expected 499): " + numBlocksPruned); + assertEquals(500-1, numBlocksPruned); + repository.getBlockRepository().setBlockPruneHeight(501); - // ... but it's not missing blocks 1 and 501 (we don't prune the genesis block) - assertNotNull(repository.getBlockRepository().fromHeight(1)); - assertNotNull(repository.getBlockRepository().fromHeight(501)); + // Prune the AT states for the archived blocks + System.out.println("Pruning AT states up to height 500..."); + repository.getATRepository().rebuildLatestAtStates(500); + repository.saveChanges(); + int numATStatesPruned = repository.getATRepository().pruneAtStates(2, 500); + System.out.println("Number of AT states pruned (Expected 498): " + numATStatesPruned); + assertEquals(498, numATStatesPruned); // Minus 1 for genesis block, and another for the latest AT state + repository.getATRepository().setAtPruneHeight(501); - // Validate the latest block height in the repository - assertEquals(1002, (int) repository.getBlockRepository().getLastBlock().getHeight()); + // Now ensure the SQL repository is missing blocks 2 and 500... + System.out.println("Verifying that blocks 2 and 500 have been pruned..."); + assertNull(repository.getBlockRepository().fromHeight(2)); + assertNull(repository.getBlockRepository().fromHeight(500)); + System.out.println("Blocks 2 and 500 have been successfully pruned."); - // Now orphan some unarchived blocks. - BlockUtils.orphanBlocks(repository, 500); - assertEquals(502, (int) repository.getBlockRepository().getLastBlock().getHeight()); + // ... but it's not missing blocks 1 and 501 (we don't prune the genesis block) + System.out.println("Verifying that blocks 1 and 501 still exist..."); + assertNotNull(repository.getBlockRepository().fromHeight(1)); + assertNotNull(repository.getBlockRepository().fromHeight(501)); + System.out.println("Blocks 1 and 501 are present in the repository."); - // We're close to the lower limit of the SQL database now, so - // we need to import some blocks from the archive - BlockArchiveUtils.importFromArchive(401, 500, repository); + // Validate the latest block height in the repository + int lastBlockHeight = repository.getBlockRepository().getLastBlock().getHeight(); + System.out.println("Latest block height in repository (Expected 1002): " + lastBlockHeight); + assertEquals(1002, lastBlockHeight); - // Ensure the SQL repository now contains block 401 but not 400... - assertNotNull(repository.getBlockRepository().fromHeight(401)); - assertNull(repository.getBlockRepository().fromHeight(400)); + // Now orphan some unarchived blocks. + System.out.println("Orphaning 500 blocks..."); + BlockUtils.orphanBlocks(repository, 500); + int currentLastBlockHeight = repository.getBlockRepository().getLastBlock().getHeight(); + System.out.println("New last block height after orphaning (Expected 502): " + currentLastBlockHeight); + assertEquals(502, currentLastBlockHeight); - // Import the remaining 399 blocks - BlockArchiveUtils.importFromArchive(2, 400, repository); + // We're close to the lower limit of the SQL database now, so + // we need to import some blocks from the archive + System.out.println("Importing blocks 401 to 500 from the archive..."); + BlockArchiveUtils.importFromArchive(401, 500, repository); - // Verify that block 3 matches the original - BlockData block3DataPostArchive = repository.getBlockRepository().fromHeight(3); - assertArrayEquals(block3DataPreArchive.getSignature(), block3DataPostArchive.getSignature()); - assertEquals(block3DataPreArchive.getHeight(), block3DataPostArchive.getHeight()); + // Ensure the SQL repository now contains block 401 but not 400... + System.out.println("Verifying that block 401 exists and block 400 does not..."); + assertNotNull(repository.getBlockRepository().fromHeight(401)); + assertNull(repository.getBlockRepository().fromHeight(400)); + System.out.println("Block 401 exists, block 400 does not."); - // Orphan 2 more block, which should be the last one that is possible to be orphaned + // Import the remaining 399 blocks + System.out.println("Importing blocks 2 to 400 from the archive..."); + BlockArchiveUtils.importFromArchive(2, 400, repository); + + // Verify that block 3 matches the original + System.out.println("Verifying that block 3 matches the original data..."); + BlockData block3DataPostArchive = repository.getBlockRepository().fromHeight(3); + assertArrayEquals(block3DataPreArchive.getSignature(), block3DataPostArchive.getSignature()); + assertEquals(block3DataPreArchive.getHeight(), block3DataPostArchive.getHeight()); + System.out.println("Block 3 data matches the original."); + + // Orphan 2 more block, which should be the last one that is possible to be orphaned // TODO: figure out why this is 1 block more than in the equivalent block archive V1 test - BlockUtils.orphanBlocks(repository, 2); + System.out.println("Orphaning 2 more blocks..."); + BlockUtils.orphanBlocks(repository, 2); + System.out.println("Orphaned 2 blocks successfully."); - // Orphan another block, which should fail - Exception exception = null; - try { - BlockUtils.orphanBlocks(repository, 1); - } catch (DataException e) { - exception = e; - } + // Orphan another block, which should fail + System.out.println("Attempting to orphan another block, which should fail..."); + Exception exception = null; + try { + BlockUtils.orphanBlocks(repository, 1); + } catch (DataException e) { + exception = e; + System.out.println("Caught expected DataException: " + e.getMessage()); + } - // Ensure that a DataException is thrown because there is no more AT states data available - assertNotNull(exception); - assertEquals(DataException.class, exception.getClass()); + // Ensure that a DataException is thrown because there is no more AT states data available + assertNotNull(exception); + assertEquals(DataException.class, exception.getClass()); + System.out.println("DataException confirmed due to lack of AT states data."); - // FUTURE: we may be able to retain unique AT states when trimming, to avoid this exception - // and allow orphaning back through blocks with trimmed AT states. + // FUTURE: we may be able to retain unique AT states when trimming, to avoid this exception + // and allow orphaning back through blocks with trimmed AT states. - } - } + System.out.println("testTrimArchivePruneAndOrphan completed successfully."); + } + } - /** - * Many nodes are missing an ATStatesHeightIndex due to an earlier bug - * In these cases we disable archiving and pruning as this index is a - * very essential component in these processes. - */ - @Test - public void testMissingAtStatesHeightIndex() throws DataException, SQLException { - try (final HSQLDBRepository repository = (HSQLDBRepository) RepositoryManager.getRepository()) { + /** + * Many nodes are missing an ATStatesHeightIndex due to an earlier bug + * In these cases we disable archiving and pruning as this index is a + * very essential component in these processes. + */ + @Test + public void testMissingAtStatesHeightIndex() throws DataException, SQLException { + try (final HSQLDBRepository repository = (HSQLDBRepository) RepositoryManager.getRepository()) { - // Firstly check that we're able to prune or archive when the index exists - assertTrue(repository.getATRepository().hasAtStatesHeightIndex()); - assertTrue(RepositoryManager.canArchiveOrPrune()); + System.out.println("Starting testMissingAtStatesHeightIndex"); - // Delete the index - repository.prepareStatement("DROP INDEX ATSTATESHEIGHTINDEX").execute(); + // Firstly check that we're able to prune or archive when the index exists + System.out.println("Checking existence of ATStatesHeightIndex..."); + assertTrue(repository.getATRepository().hasAtStatesHeightIndex()); + assertTrue(RepositoryManager.canArchiveOrPrune()); + System.out.println("ATStatesHeightIndex exists. Archiving and pruning are possible."); - // Ensure check that we're unable to prune or archive when the index doesn't exist - assertFalse(repository.getATRepository().hasAtStatesHeightIndex()); - assertFalse(RepositoryManager.canArchiveOrPrune()); - } - } + // Delete the index + System.out.println("Dropping ATStatesHeightIndex..."); + repository.prepareStatement("DROP INDEX ATSTATESHEIGHTINDEX").execute(); + System.out.println("ATStatesHeightIndex dropped."); + + // Ensure check that we're unable to prune or archive when the index doesn't exist + System.out.println("Verifying that ATStatesHeightIndex no longer exists..."); + assertFalse(repository.getATRepository().hasAtStatesHeightIndex()); + assertFalse(RepositoryManager.canArchiveOrPrune()); + System.out.println("ATStatesHeightIndex does not exist. Archiving and pruning are disabled."); + + System.out.println("testMissingAtStatesHeightIndex completed successfully."); + } + } - private void deleteArchiveDirectory() { - // Delete archive directory if exists - Path archivePath = Paths.get(Settings.getInstance().getRepositoryPath(), "archive").toAbsolutePath(); - try { - FileUtils.deleteDirectory(archivePath.toFile()); - } catch (IOException e) { + private void deleteArchiveDirectory() { + // Delete archive directory if exists + Path archivePath = Paths.get(Settings.getInstance().getRepositoryPath(), "archive").toAbsolutePath(); + try { + FileUtils.deleteDirectory(archivePath.toFile()); + System.out.println("Deleted archive directory at: " + archivePath); + } catch (IOException e) { - } - } + System.out.println("Failed to delete archive directory: " + e.getMessage()); + } + } } From 3d83a79014966423798a9257eacdc849c8804499 Mon Sep 17 00:00:00 2001 From: QuickMythril Date: Wed, 13 Nov 2024 06:28:23 -0500 Subject: [PATCH 3/9] Fix whitespace only --- .../org/qortal/test/BlockArchiveV1Tests.java | 1128 ++++++++--------- .../org/qortal/test/BlockArchiveV2Tests.java | 1014 +++++++-------- 2 files changed, 1071 insertions(+), 1071 deletions(-) diff --git a/src/test/java/org/qortal/test/BlockArchiveV1Tests.java b/src/test/java/org/qortal/test/BlockArchiveV1Tests.java index 60eefa8e..2cf8ef79 100644 --- a/src/test/java/org/qortal/test/BlockArchiveV1Tests.java +++ b/src/test/java/org/qortal/test/BlockArchiveV1Tests.java @@ -34,574 +34,574 @@ import static org.junit.Assert.*; public class BlockArchiveV1Tests extends Common { - @Before - public void beforeTest() throws DataException, IllegalAccessException { - Common.useSettings("test-settings-v2-block-archive.json"); - NTP.setFixedOffset(Settings.getInstance().getTestNtpOffset()); - this.deleteArchiveDirectory(); + @Before + public void beforeTest() throws DataException, IllegalAccessException { + Common.useSettings("test-settings-v2-block-archive.json"); + NTP.setFixedOffset(Settings.getInstance().getTestNtpOffset()); + this.deleteArchiveDirectory(); - // Set default archive version to 1, so that archive builds in these tests use V2 - FieldUtils.writeField(Settings.getInstance(), "defaultArchiveVersion", 1, true); - } + // Set default archive version to 1, so that archive builds in these tests use V2 + FieldUtils.writeField(Settings.getInstance(), "defaultArchiveVersion", 1, true); + } - @After - public void afterTest() throws DataException { - this.deleteArchiveDirectory(); - } + @After + public void afterTest() throws DataException { + this.deleteArchiveDirectory(); + } - @Test - public void testWriter() throws DataException, InterruptedException, TransformationException, IOException { - try (final Repository repository = RepositoryManager.getRepository()) { - - System.out.println("Starting testWriter"); - - // Mint some blocks so that we are able to archive them later - System.out.println("Minting 1000 blocks..."); - for (int i = 0; i < 1000; i++) { - BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); - // Log every 100 blocks - if ((i + 1) % 100 == 0) { - System.out.println("Minted block " + (i + 1)); - } - } - System.out.println("Finished minting blocks."); - - // 900 blocks are trimmed (this specifies the first untrimmed height) - repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(901); - repository.getATRepository().setAtTrimHeight(901); - System.out.println("Set trim heights to 901."); - - // Check the max archive height - this should be one less than the first untrimmed height - final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); - System.out.println("Maximum archive height (Expected 900): " + maximumArchiveHeight); - assertEquals(900, maximumArchiveHeight); - - // Write blocks 2-900 to the archive - System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); - BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); - writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes - BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); - System.out.println("Finished writing blocks to archive. Result: " + result); - assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); - - // Make sure that the archive contains the correct number of blocks - System.out.println("Archive contains " + writer.getWrittenCount() + " blocks. (Expected 899)"); - assertEquals(900 - 1, writer.getWrittenCount()); - - // Increment block archive height - repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); - repository.saveChanges(); - assertEquals(900 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); - - // Ensure the file exists - File outputFile = writer.getOutputPath().toFile(); - assertTrue(outputFile.exists()); - System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); - - System.out.println("testWriter completed successfully."); - } - } - - @Test - public void testWriterAndReader() throws DataException, InterruptedException, TransformationException, IOException { - try (final Repository repository = RepositoryManager.getRepository()) { - - System.out.println("Starting testWriterAndReader"); - - // Mint some blocks so that we are able to archive them later - System.out.println("Minting 1000 blocks..."); - for (int i = 0; i < 1000; i++) { - BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); - // Log every 100 blocks - if ((i + 1) % 100 == 0) { - System.out.println("Minted block " + (i + 1)); - } - } - System.out.println("Finished minting blocks."); - - // 900 blocks are trimmed (this specifies the first untrimmed height) - repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(901); - repository.getATRepository().setAtTrimHeight(901); - System.out.println("Set trim heights to 901."); - - // Check the max archive height - this should be one less than the first untrimmed height - final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); - System.out.println("Maximum archive height (Expected 900): " + maximumArchiveHeight); - assertEquals(900, maximumArchiveHeight); - - // Write blocks 2-900 to the archive - System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); - BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); - writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes - BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); - System.out.println("Finished writing blocks to archive. Result: " + result); - assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); - - // Make sure that the archive contains the correct number of blocks - System.out.println("Archive contains " + writer.getWrittenCount() + " blocks. (Expected 899)"); - assertEquals(900 - 1, writer.getWrittenCount()); - - // Increment block archive height - repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); - repository.saveChanges(); - assertEquals(900 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); - - // Ensure the file exists - File outputFile = writer.getOutputPath().toFile(); - assertTrue(outputFile.exists()); - System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); - - // Read block 2 from the archive - System.out.println("Reading block 2 from the archive..."); - BlockArchiveReader reader = BlockArchiveReader.getInstance(); - BlockTransformation block2Info = reader.fetchBlockAtHeight(2); - BlockData block2ArchiveData = block2Info.getBlockData(); - - // Read block 2 from the repository - BlockData block2RepositoryData = repository.getBlockRepository().fromHeight(2); - - // Ensure the values match - System.out.println("Comparing block 2 data..."); - assertEquals(block2ArchiveData.getHeight(), block2RepositoryData.getHeight()); - assertArrayEquals(block2ArchiveData.getSignature(), block2RepositoryData.getSignature()); - - // Test some values in the archive - assertEquals(1, block2ArchiveData.getOnlineAccountsCount()); - - // Read block 900 from the archive - System.out.println("Reading block 900 from the archive..."); - BlockTransformation block900Info = reader.fetchBlockAtHeight(900); - BlockData block900ArchiveData = block900Info.getBlockData(); - - // Read block 900 from the repository - BlockData block900RepositoryData = repository.getBlockRepository().fromHeight(900); - - // Ensure the values match - System.out.println("Comparing block 900 data..."); - assertEquals(block900ArchiveData.getHeight(), block900RepositoryData.getHeight()); - assertArrayEquals(block900ArchiveData.getSignature(), block900RepositoryData.getSignature()); - - // Test some values in the archive - assertEquals(1, block900ArchiveData.getOnlineAccountsCount()); - - System.out.println("testWriterAndReader completed successfully."); - } - } - - @Test - public void testArchivedAtStates() throws DataException, InterruptedException, TransformationException, IOException { - try (final Repository repository = RepositoryManager.getRepository()) { - - System.out.println("Starting testArchivedAtStates"); - - // Deploy an AT so that we have AT state data - System.out.println("Deploying AT..."); - PrivateKeyAccount deployer = Common.getTestAccount(repository, "alice"); - byte[] creationBytes = AtUtils.buildSimpleAT(); - long fundingAmount = 1_00000000L; - DeployAtTransaction deployAtTransaction = AtUtils.doDeployAT(repository, deployer, creationBytes, fundingAmount); - String atAddress = deployAtTransaction.getATAccount().getAddress(); - System.out.println("AT deployed at address: " + atAddress); - - // Mint some blocks so that we are able to archive them later - System.out.println("Minting 1000 blocks..."); - for (int i = 0; i < 1000; i++) { - BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); - // Log every 100 blocks - if ((i + 1) % 100 == 0) { - System.out.println("Minted block " + (i + 1)); - } - } - System.out.println("Finished minting blocks."); - - // 9 blocks are trimmed (this specifies the first untrimmed height) - repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(10); - repository.getATRepository().setAtTrimHeight(10); - System.out.println("Set trim heights to 10."); - - // Check the max archive height - final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); - System.out.println("Maximum archive height (Expected 9): " + maximumArchiveHeight); - assertEquals(9, maximumArchiveHeight); - - // Write blocks 2-9 to the archive - System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); - BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); - writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes - BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); - System.out.println("Finished writing blocks to archive. Result: " + result); - assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); - - // Make sure that the archive contains the correct number of blocks - System.out.println("Archive contains " + writer.getWrittenCount() + " blocks. (Expected 8)"); - assertEquals(9 - 1, writer.getWrittenCount()); - - // Increment block archive height - repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); - repository.saveChanges(); - assertEquals(9 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); - - // Ensure the file exists - File outputFile = writer.getOutputPath().toFile(); - assertTrue(outputFile.exists()); - System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); - - // Check blocks 3-9 - System.out.println("Checking blocks 3 to 9..."); - for (Integer testHeight = 2; testHeight <= 9; testHeight++) { - - System.out.println("Reading block " + testHeight + " from the archive..."); - // Read a block from the archive - BlockArchiveReader reader = BlockArchiveReader.getInstance(); - BlockTransformation blockInfo = reader.fetchBlockAtHeight(testHeight); - BlockData archivedBlockData = blockInfo.getBlockData(); - ATStateData archivedAtStateData = blockInfo.getAtStates().isEmpty() ? null : blockInfo.getAtStates().get(0); - List archivedTransactions = blockInfo.getTransactions(); - - // Read the same block from the repository - BlockData repositoryBlockData = repository.getBlockRepository().fromHeight(testHeight); - ATStateData repositoryAtStateData = repository.getATRepository().getATStateAtHeight(atAddress, testHeight); - - // Ensure the repository has full AT state data - assertNotNull(repositoryAtStateData.getStateHash()); - assertNotNull(repositoryAtStateData.getStateData()); - - // Check the archived AT state - if (testHeight == 2) { - System.out.println("Checking block " + testHeight + " AT state data (expected null)..."); - // Block 2 won't have an AT state hash because it's initial (and has the DEPLOY_AT in the same block) - assertNull(archivedAtStateData); - - assertEquals(1, archivedTransactions.size()); - assertEquals(Transaction.TransactionType.DEPLOY_AT, archivedTransactions.get(0).getType()); - } - else { - System.out.println("Checking block " + testHeight + " AT state data..."); - // For blocks 3+, ensure the archive has the AT state data, but not the hashes - assertNotNull(archivedAtStateData.getStateHash()); - assertNull(archivedAtStateData.getStateData()); - - // They also shouldn't have any transactions - assertTrue(archivedTransactions.isEmpty()); - } - - // Also check the online accounts count and height - assertEquals(1, archivedBlockData.getOnlineAccountsCount()); - assertEquals(testHeight, archivedBlockData.getHeight()); - - // Ensure the values match - assertEquals(archivedBlockData.getHeight(), repositoryBlockData.getHeight()); - assertArrayEquals(archivedBlockData.getSignature(), repositoryBlockData.getSignature()); - assertEquals(archivedBlockData.getOnlineAccountsCount(), repositoryBlockData.getOnlineAccountsCount()); - assertArrayEquals(archivedBlockData.getMinterSignature(), repositoryBlockData.getMinterSignature()); - assertEquals(archivedBlockData.getATCount(), repositoryBlockData.getATCount()); - assertEquals(archivedBlockData.getOnlineAccountsCount(), repositoryBlockData.getOnlineAccountsCount()); - assertArrayEquals(archivedBlockData.getReference(), repositoryBlockData.getReference()); - assertEquals(archivedBlockData.getTimestamp(), repositoryBlockData.getTimestamp()); - assertEquals(archivedBlockData.getATFees(), repositoryBlockData.getATFees()); - assertEquals(archivedBlockData.getTotalFees(), repositoryBlockData.getTotalFees()); - assertEquals(archivedBlockData.getTransactionCount(), repositoryBlockData.getTransactionCount()); - assertArrayEquals(archivedBlockData.getTransactionsSignature(), repositoryBlockData.getTransactionsSignature()); - - if (testHeight != 2) { - assertArrayEquals(archivedAtStateData.getStateHash(), repositoryAtStateData.getStateHash()); - } - } - - // Check block 10 (unarchived) - System.out.println("Checking block 10 (should not be in archive)..."); - BlockArchiveReader reader = BlockArchiveReader.getInstance(); - BlockTransformation blockInfo = reader.fetchBlockAtHeight(10); - assertNull(blockInfo); - - System.out.println("testArchivedAtStates completed successfully."); - } - - } - - @Test - public void testArchiveAndPrune() throws DataException, InterruptedException, TransformationException, IOException { - try (final Repository repository = RepositoryManager.getRepository()) { - - System.out.println("Starting testArchiveAndPrune"); - - // Deploy an AT so that we have AT state data - System.out.println("Deploying AT..."); - PrivateKeyAccount deployer = Common.getTestAccount(repository, "alice"); - byte[] creationBytes = AtUtils.buildSimpleAT(); - long fundingAmount = 1_00000000L; - AtUtils.doDeployAT(repository, deployer, creationBytes, fundingAmount); - - // Mint some blocks so that we are able to archive them later - System.out.println("Minting 1000 blocks..."); - for (int i = 0; i < 1000; i++) { - BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); - // Log every 100 blocks - if ((i + 1) % 100 == 0) { - System.out.println("Minted block " + (i + 1)); - } - } - System.out.println("Finished minting blocks."); - - // Assume 900 blocks are trimmed (this specifies the first untrimmed height) - repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(901); - repository.getATRepository().setAtTrimHeight(901); - System.out.println("Set trim heights to 901."); - - // Check the max archive height - this should be one less than the first untrimmed height - final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); - System.out.println("Maximum archive height (Expected 900): " + maximumArchiveHeight); - assertEquals(900, maximumArchiveHeight); - - // Write blocks 2-900 to the archive - System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); - BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); - writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes - BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); - System.out.println("Finished writing blocks to archive. Result: " + result); - assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); - - // Make sure that the archive contains the correct number of blocks - System.out.println("Archive contains " + writer.getWrittenCount() + " blocks. (Expected 899)"); - assertEquals(900 - 1, writer.getWrittenCount()); - - // Increment block archive height - repository.getBlockArchiveRepository().setBlockArchiveHeight(901); - repository.saveChanges(); - assertEquals(901, repository.getBlockArchiveRepository().getBlockArchiveHeight()); - - // Ensure the file exists - File outputFile = writer.getOutputPath().toFile(); - assertTrue(outputFile.exists()); - System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); - - // Ensure the SQL repository contains blocks 2 and 900... - assertNotNull(repository.getBlockRepository().fromHeight(2)); - assertNotNull(repository.getBlockRepository().fromHeight(900)); - System.out.println("Blocks 2 and 900 exist in the repository."); - - // Prune all the archived blocks - System.out.println("Pruning blocks 2 to 900..."); - int numBlocksPruned = repository.getBlockRepository().pruneBlocks(0, 900); - assertEquals(900-1, numBlocksPruned); - repository.getBlockRepository().setBlockPruneHeight(901); - - // Prune the AT states for the archived blocks - System.out.println("Pruning AT states up to height 900..."); - repository.getATRepository().rebuildLatestAtStates(900); - repository.saveChanges(); - int numATStatesPruned = repository.getATRepository().pruneAtStates(0, 900); - assertEquals(900-2, numATStatesPruned); // Minus 1 for genesis block, and another for the latest AT state - repository.getATRepository().setAtPruneHeight(901); - - // Now ensure the SQL repository is missing blocks 2 and 900... - assertNull(repository.getBlockRepository().fromHeight(2)); - assertNull(repository.getBlockRepository().fromHeight(900)); - System.out.println("Blocks 2 and 900 have been pruned from the repository."); - - // ... but it's not missing blocks 1 and 901 (we don't prune the genesis block) - assertNotNull(repository.getBlockRepository().fromHeight(1)); - assertNotNull(repository.getBlockRepository().fromHeight(901)); - System.out.println("Blocks 1 and 901 still exist in the repository."); - - // Validate the latest block height in the repository - int lastBlockHeight = repository.getBlockRepository().getLastBlock().getHeight(); - System.out.println("Latest block height in repository (Expected 1002): " + lastBlockHeight); - assertEquals(1002, lastBlockHeight); - - System.out.println("testArchiveAndPrune completed successfully."); - } - } - @Test - public void testTrimArchivePruneAndOrphan() throws DataException, InterruptedException, TransformationException, IOException { - try (final Repository repository = RepositoryManager.getRepository()) { + public void testWriter() throws DataException, InterruptedException, TransformationException, IOException { + try (final Repository repository = RepositoryManager.getRepository()) { - System.out.println("Starting testTrimArchivePruneAndOrphan"); + System.out.println("Starting testWriter"); - // Deploy an AT so that we have AT state data - System.out.println("Deploying AT..."); - PrivateKeyAccount deployer = Common.getTestAccount(repository, "alice"); - byte[] creationBytes = AtUtils.buildSimpleAT(); - long fundingAmount = 1_00000000L; - AtUtils.doDeployAT(repository, deployer, creationBytes, fundingAmount); - System.out.println("AT deployed successfully."); + // Mint some blocks so that we are able to archive them later + System.out.println("Minting 1000 blocks..."); + for (int i = 0; i < 1000; i++) { + BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); + // Log every 100 blocks + if ((i + 1) % 100 == 0) { + System.out.println("Minted block " + (i + 1)); + } + } + System.out.println("Finished minting blocks."); - // Mint some blocks so that we are able to archive them later - System.out.println("Minting 1000 blocks..."); - for (int i = 0; i < 1000; i++) { - BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); - // Log every 100 blocks - if ((i + 1) % 100 == 0) { - System.out.println("Minted block " + (i + 1)); - } - } - System.out.println("Finished minting blocks."); + // 900 blocks are trimmed (this specifies the first untrimmed height) + repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(901); + repository.getATRepository().setAtTrimHeight(901); + System.out.println("Set trim heights to 901."); - // Make sure that block 500 has full AT state data and data hash - System.out.println("Verifying block 500 AT state data..."); - List block500AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(500); - ATStateData atStatesData = repository.getATRepository().getATStateAtHeight(block500AtStatesData.get(0).getATAddress(), 500); - assertNotNull(atStatesData.getStateHash()); - assertNotNull(atStatesData.getStateData()); - System.out.println("Block 500 AT state data verified."); + // Check the max archive height - this should be one less than the first untrimmed height + final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); + System.out.println("Maximum archive height (Expected 900): " + maximumArchiveHeight); + assertEquals(900, maximumArchiveHeight); - // Trim the first 500 blocks - System.out.println("Trimming first 500 blocks..."); - repository.getBlockRepository().trimOldOnlineAccountsSignatures(0, 500); - repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(501); - repository.getATRepository().rebuildLatestAtStates(500); - repository.getATRepository().trimAtStates(0, 500, 1000); - repository.getATRepository().setAtTrimHeight(501); - System.out.println("Trimming completed."); + // Write blocks 2-900 to the archive + System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); + BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); + writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes + BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); + System.out.println("Finished writing blocks to archive. Result: " + result); + assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); - // Now block 499 should only have the AT state data hash - System.out.println("Checking block 499 AT state data..."); - List block499AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(499); - atStatesData = repository.getATRepository().getATStateAtHeight(block499AtStatesData.get(0).getATAddress(), 499); - assertNotNull(atStatesData.getStateHash()); - assertNull(atStatesData.getStateData()); - System.out.println("Block 499 AT state data contains only state hash as expected."); + // Make sure that the archive contains the correct number of blocks + System.out.println("Archive contains " + writer.getWrittenCount() + " blocks. (Expected 899)"); + assertEquals(900 - 1, writer.getWrittenCount()); - // ... but block 500 should have the full data (due to being retained as the "latest" AT state in the trimmed range - System.out.println("Verifying block 500 AT state data again..."); - block500AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(500); - atStatesData = repository.getATRepository().getATStateAtHeight(block500AtStatesData.get(0).getATAddress(), 500); - assertNotNull(atStatesData.getStateHash()); - assertNotNull(atStatesData.getStateData()); - System.out.println("Block 500 AT state data contains full data."); + // Increment block archive height + repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); + repository.saveChanges(); + assertEquals(900 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); - // ... and block 501 should also have the full data - System.out.println("Verifying block 501 AT state data..."); - List block501AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(501); - atStatesData = repository.getATRepository().getATStateAtHeight(block501AtStatesData.get(0).getATAddress(), 501); - assertNotNull(atStatesData.getStateHash()); - assertNotNull(atStatesData.getStateData()); - System.out.println("Block 501 AT state data contains full data."); + // Ensure the file exists + File outputFile = writer.getOutputPath().toFile(); + assertTrue(outputFile.exists()); + System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); - // Check the max archive height - this should be one less than the first untrimmed height - final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); - System.out.println("Maximum archive height determined (Expected 500): " + maximumArchiveHeight); - assertEquals(500, maximumArchiveHeight); + System.out.println("testWriter completed successfully."); + } + } - BlockData block3DataPreArchive = repository.getBlockRepository().fromHeight(3); + @Test + public void testWriterAndReader() throws DataException, InterruptedException, TransformationException, IOException { + try (final Repository repository = RepositoryManager.getRepository()) { - // Write blocks 2-500 to the archive - System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); - BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); - writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes - BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); - System.out.println("Finished writing blocks to archive. Result: " + result); - assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); + System.out.println("Starting testWriterAndReader"); - // Make sure that the archive contains the correct number of blocks - System.out.println("Number of blocks written to archive (Expected 499): " + writer.getWrittenCount()); - assertEquals(500 - 1, writer.getWrittenCount()); // -1 for the genesis block + // Mint some blocks so that we are able to archive them later + System.out.println("Minting 1000 blocks..."); + for (int i = 0; i < 1000; i++) { + BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); + // Log every 100 blocks + if ((i + 1) % 100 == 0) { + System.out.println("Minted block " + (i + 1)); + } + } + System.out.println("Finished minting blocks."); - // Increment block archive height - repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); - repository.saveChanges(); - assertEquals(500 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); - System.out.println("Block archive height updated to: " + (500 - 1)); + // 900 blocks are trimmed (this specifies the first untrimmed height) + repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(901); + repository.getATRepository().setAtTrimHeight(901); + System.out.println("Set trim heights to 901."); - // Ensure the file exists - File outputFile = writer.getOutputPath().toFile(); - assertTrue(outputFile.exists()); - System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); + // Check the max archive height - this should be one less than the first untrimmed height + final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); + System.out.println("Maximum archive height (Expected 900): " + maximumArchiveHeight); + assertEquals(900, maximumArchiveHeight); - // Ensure the SQL repository contains blocks 2 and 500... - System.out.println("Verifying that blocks 2 and 500 exist in the repository..."); - assertNotNull(repository.getBlockRepository().fromHeight(2)); - assertNotNull(repository.getBlockRepository().fromHeight(500)); - System.out.println("Blocks 2 and 500 are present in the repository."); + // Write blocks 2-900 to the archive + System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); + BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); + writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes + BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); + System.out.println("Finished writing blocks to archive. Result: " + result); + assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); - // Prune all the archived blocks - System.out.println("Pruning blocks 2 to 500..."); - int numBlocksPruned = repository.getBlockRepository().pruneBlocks(0, 500); - System.out.println("Number of blocks pruned (Expected 499): " + numBlocksPruned); - assertEquals(500-1, numBlocksPruned); - repository.getBlockRepository().setBlockPruneHeight(501); + // Make sure that the archive contains the correct number of blocks + System.out.println("Archive contains " + writer.getWrittenCount() + " blocks. (Expected 899)"); + assertEquals(900 - 1, writer.getWrittenCount()); - // Prune the AT states for the archived blocks - System.out.println("Pruning AT states up to height 500..."); - repository.getATRepository().rebuildLatestAtStates(500); - repository.saveChanges(); - int numATStatesPruned = repository.getATRepository().pruneAtStates(2, 500); - System.out.println("Number of AT states pruned (Expected 498): " + numATStatesPruned); - assertEquals(498, numATStatesPruned); // Minus 1 for genesis block, and another for the latest AT state - repository.getATRepository().setAtPruneHeight(501); + // Increment block archive height + repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); + repository.saveChanges(); + assertEquals(900 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); - // Now ensure the SQL repository is missing blocks 2 and 500... - System.out.println("Verifying that blocks 2 and 500 have been pruned..."); - assertNull(repository.getBlockRepository().fromHeight(2)); - assertNull(repository.getBlockRepository().fromHeight(500)); - System.out.println("Blocks 2 and 500 have been successfully pruned."); + // Ensure the file exists + File outputFile = writer.getOutputPath().toFile(); + assertTrue(outputFile.exists()); + System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); - // ... but it's not missing blocks 1 and 501 (we don't prune the genesis block) - System.out.println("Verifying that blocks 1 and 501 still exist..."); - assertNotNull(repository.getBlockRepository().fromHeight(1)); - assertNotNull(repository.getBlockRepository().fromHeight(501)); - System.out.println("Blocks 1 and 501 are present in the repository."); + // Read block 2 from the archive + System.out.println("Reading block 2 from the archive..."); + BlockArchiveReader reader = BlockArchiveReader.getInstance(); + BlockTransformation block2Info = reader.fetchBlockAtHeight(2); + BlockData block2ArchiveData = block2Info.getBlockData(); - // Validate the latest block height in the repository - int lastBlockHeight = repository.getBlockRepository().getLastBlock().getHeight(); - System.out.println("Latest block height in repository (Expected 1002): " + lastBlockHeight); - assertEquals(1002, lastBlockHeight); + // Read block 2 from the repository + BlockData block2RepositoryData = repository.getBlockRepository().fromHeight(2); - // Now orphan some unarchived blocks. - System.out.println("Orphaning 500 blocks..."); - BlockUtils.orphanBlocks(repository, 500); - int currentLastBlockHeight = repository.getBlockRepository().getLastBlock().getHeight(); - System.out.println("New last block height after orphaning (Expected 502): " + currentLastBlockHeight); - assertEquals(502, currentLastBlockHeight); + // Ensure the values match + System.out.println("Comparing block 2 data..."); + assertEquals(block2ArchiveData.getHeight(), block2RepositoryData.getHeight()); + assertArrayEquals(block2ArchiveData.getSignature(), block2RepositoryData.getSignature()); - // We're close to the lower limit of the SQL database now, so - // we need to import some blocks from the archive - System.out.println("Importing blocks 401 to 500 from the archive..."); - BlockArchiveUtils.importFromArchive(401, 500, repository); + // Test some values in the archive + assertEquals(1, block2ArchiveData.getOnlineAccountsCount()); - // Ensure the SQL repository now contains block 401 but not 400... - System.out.println("Verifying that block 401 exists and block 400 does not..."); - assertNotNull(repository.getBlockRepository().fromHeight(401)); - assertNull(repository.getBlockRepository().fromHeight(400)); - System.out.println("Block 401 exists, block 400 does not."); + // Read block 900 from the archive + System.out.println("Reading block 900 from the archive..."); + BlockTransformation block900Info = reader.fetchBlockAtHeight(900); + BlockData block900ArchiveData = block900Info.getBlockData(); - // Import the remaining 399 blocks - System.out.println("Importing blocks 2 to 400 from the archive..."); - BlockArchiveUtils.importFromArchive(2, 400, repository); + // Read block 900 from the repository + BlockData block900RepositoryData = repository.getBlockRepository().fromHeight(900); - // Verify that block 3 matches the original - System.out.println("Verifying that block 3 matches the original data..."); - BlockData block3DataPostArchive = repository.getBlockRepository().fromHeight(3); - assertArrayEquals(block3DataPreArchive.getSignature(), block3DataPostArchive.getSignature()); - assertEquals(block3DataPreArchive.getHeight(), block3DataPostArchive.getHeight()); - System.out.println("Block 3 data matches the original."); + // Ensure the values match + System.out.println("Comparing block 900 data..."); + assertEquals(block900ArchiveData.getHeight(), block900RepositoryData.getHeight()); + assertArrayEquals(block900ArchiveData.getSignature(), block900RepositoryData.getSignature()); - // Orphan 1 more block, which should be the last one that is possible to be orphaned - System.out.println("Orphaning 1 more block..."); - BlockUtils.orphanBlocks(repository, 1); - System.out.println("Orphaned 1 block successfully."); + // Test some values in the archive + assertEquals(1, block900ArchiveData.getOnlineAccountsCount()); - // Orphan another block, which should fail - System.out.println("Attempting to orphan another block, which should fail..."); - Exception exception = null; - try { - BlockUtils.orphanBlocks(repository, 1); - } catch (DataException e) { - exception = e; - System.out.println("Caught expected DataException: " + e.getMessage()); - } + System.out.println("testWriterAndReader completed successfully."); + } + } - // Ensure that a DataException is thrown because there is no more AT states data available - assertNotNull(exception); - assertEquals(DataException.class, exception.getClass()); - System.out.println("DataException confirmed due to lack of AT states data."); + @Test + public void testArchivedAtStates() throws DataException, InterruptedException, TransformationException, IOException { + try (final Repository repository = RepositoryManager.getRepository()) { - // FUTURE: we may be able to retain unique AT states when trimming, to avoid this exception - // and allow orphaning back through blocks with trimmed AT states. + System.out.println("Starting testArchivedAtStates"); - System.out.println("testTrimArchivePruneAndOrphan completed successfully."); + // Deploy an AT so that we have AT state data + System.out.println("Deploying AT..."); + PrivateKeyAccount deployer = Common.getTestAccount(repository, "alice"); + byte[] creationBytes = AtUtils.buildSimpleAT(); + long fundingAmount = 1_00000000L; + DeployAtTransaction deployAtTransaction = AtUtils.doDeployAT(repository, deployer, creationBytes, fundingAmount); + String atAddress = deployAtTransaction.getATAccount().getAddress(); + System.out.println("AT deployed at address: " + atAddress); + + // Mint some blocks so that we are able to archive them later + System.out.println("Minting 1000 blocks..."); + for (int i = 0; i < 1000; i++) { + BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); + // Log every 100 blocks + if ((i + 1) % 100 == 0) { + System.out.println("Minted block " + (i + 1)); + } + } + System.out.println("Finished minting blocks."); + + // 9 blocks are trimmed (this specifies the first untrimmed height) + repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(10); + repository.getATRepository().setAtTrimHeight(10); + System.out.println("Set trim heights to 10."); + + // Check the max archive height + final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); + System.out.println("Maximum archive height (Expected 9): " + maximumArchiveHeight); + assertEquals(9, maximumArchiveHeight); + + // Write blocks 2-9 to the archive + System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); + BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); + writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes + BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); + System.out.println("Finished writing blocks to archive. Result: " + result); + assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); + + // Make sure that the archive contains the correct number of blocks + System.out.println("Archive contains " + writer.getWrittenCount() + " blocks. (Expected 8)"); + assertEquals(9 - 1, writer.getWrittenCount()); + + // Increment block archive height + repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); + repository.saveChanges(); + assertEquals(9 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); + + // Ensure the file exists + File outputFile = writer.getOutputPath().toFile(); + assertTrue(outputFile.exists()); + System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); + + // Check blocks 3-9 + System.out.println("Checking blocks 3 to 9..."); + for (Integer testHeight = 2; testHeight <= 9; testHeight++) { + + System.out.println("Reading block " + testHeight + " from the archive..."); + // Read a block from the archive + BlockArchiveReader reader = BlockArchiveReader.getInstance(); + BlockTransformation blockInfo = reader.fetchBlockAtHeight(testHeight); + BlockData archivedBlockData = blockInfo.getBlockData(); + ATStateData archivedAtStateData = blockInfo.getAtStates().isEmpty() ? null : blockInfo.getAtStates().get(0); + List archivedTransactions = blockInfo.getTransactions(); + + // Read the same block from the repository + BlockData repositoryBlockData = repository.getBlockRepository().fromHeight(testHeight); + ATStateData repositoryAtStateData = repository.getATRepository().getATStateAtHeight(atAddress, testHeight); + + // Ensure the repository has full AT state data + assertNotNull(repositoryAtStateData.getStateHash()); + assertNotNull(repositoryAtStateData.getStateData()); + + // Check the archived AT state + if (testHeight == 2) { + System.out.println("Checking block " + testHeight + " AT state data (expected null)..."); + // Block 2 won't have an AT state hash because it's initial (and has the DEPLOY_AT in the same block) + assertNull(archivedAtStateData); + + assertEquals(1, archivedTransactions.size()); + assertEquals(Transaction.TransactionType.DEPLOY_AT, archivedTransactions.get(0).getType()); + } + else { + System.out.println("Checking block " + testHeight + " AT state data..."); + // For blocks 3+, ensure the archive has the AT state data, but not the hashes + assertNotNull(archivedAtStateData.getStateHash()); + assertNull(archivedAtStateData.getStateData()); + + // They also shouldn't have any transactions + assertTrue(archivedTransactions.isEmpty()); + } + + // Also check the online accounts count and height + assertEquals(1, archivedBlockData.getOnlineAccountsCount()); + assertEquals(testHeight, archivedBlockData.getHeight()); + + // Ensure the values match + assertEquals(archivedBlockData.getHeight(), repositoryBlockData.getHeight()); + assertArrayEquals(archivedBlockData.getSignature(), repositoryBlockData.getSignature()); + assertEquals(archivedBlockData.getOnlineAccountsCount(), repositoryBlockData.getOnlineAccountsCount()); + assertArrayEquals(archivedBlockData.getMinterSignature(), repositoryBlockData.getMinterSignature()); + assertEquals(archivedBlockData.getATCount(), repositoryBlockData.getATCount()); + assertEquals(archivedBlockData.getOnlineAccountsCount(), repositoryBlockData.getOnlineAccountsCount()); + assertArrayEquals(archivedBlockData.getReference(), repositoryBlockData.getReference()); + assertEquals(archivedBlockData.getTimestamp(), repositoryBlockData.getTimestamp()); + assertEquals(archivedBlockData.getATFees(), repositoryBlockData.getATFees()); + assertEquals(archivedBlockData.getTotalFees(), repositoryBlockData.getTotalFees()); + assertEquals(archivedBlockData.getTransactionCount(), repositoryBlockData.getTransactionCount()); + assertArrayEquals(archivedBlockData.getTransactionsSignature(), repositoryBlockData.getTransactionsSignature()); + + if (testHeight != 2) { + assertArrayEquals(archivedAtStateData.getStateHash(), repositoryAtStateData.getStateHash()); + } + } + + // Check block 10 (unarchived) + System.out.println("Checking block 10 (should not be in archive)..."); + BlockArchiveReader reader = BlockArchiveReader.getInstance(); + BlockTransformation blockInfo = reader.fetchBlockAtHeight(10); + assertNull(blockInfo); + + System.out.println("testArchivedAtStates completed successfully."); + } + + } + + @Test + public void testArchiveAndPrune() throws DataException, InterruptedException, TransformationException, IOException { + try (final Repository repository = RepositoryManager.getRepository()) { + + System.out.println("Starting testArchiveAndPrune"); + + // Deploy an AT so that we have AT state data + System.out.println("Deploying AT..."); + PrivateKeyAccount deployer = Common.getTestAccount(repository, "alice"); + byte[] creationBytes = AtUtils.buildSimpleAT(); + long fundingAmount = 1_00000000L; + AtUtils.doDeployAT(repository, deployer, creationBytes, fundingAmount); + + // Mint some blocks so that we are able to archive them later + System.out.println("Minting 1000 blocks..."); + for (int i = 0; i < 1000; i++) { + BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); + // Log every 100 blocks + if ((i + 1) % 100 == 0) { + System.out.println("Minted block " + (i + 1)); + } + } + System.out.println("Finished minting blocks."); + + // Assume 900 blocks are trimmed (this specifies the first untrimmed height) + repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(901); + repository.getATRepository().setAtTrimHeight(901); + System.out.println("Set trim heights to 901."); + + // Check the max archive height - this should be one less than the first untrimmed height + final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); + System.out.println("Maximum archive height (Expected 900): " + maximumArchiveHeight); + assertEquals(900, maximumArchiveHeight); + + // Write blocks 2-900 to the archive + System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); + BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); + writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes + BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); + System.out.println("Finished writing blocks to archive. Result: " + result); + assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); + + // Make sure that the archive contains the correct number of blocks + System.out.println("Archive contains " + writer.getWrittenCount() + " blocks. (Expected 899)"); + assertEquals(900 - 1, writer.getWrittenCount()); + + // Increment block archive height + repository.getBlockArchiveRepository().setBlockArchiveHeight(901); + repository.saveChanges(); + assertEquals(901, repository.getBlockArchiveRepository().getBlockArchiveHeight()); + + // Ensure the file exists + File outputFile = writer.getOutputPath().toFile(); + assertTrue(outputFile.exists()); + System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); + + // Ensure the SQL repository contains blocks 2 and 900... + assertNotNull(repository.getBlockRepository().fromHeight(2)); + assertNotNull(repository.getBlockRepository().fromHeight(900)); + System.out.println("Blocks 2 and 900 exist in the repository."); + + // Prune all the archived blocks + System.out.println("Pruning blocks 2 to 900..."); + int numBlocksPruned = repository.getBlockRepository().pruneBlocks(0, 900); + assertEquals(900-1, numBlocksPruned); + repository.getBlockRepository().setBlockPruneHeight(901); + + // Prune the AT states for the archived blocks + System.out.println("Pruning AT states up to height 900..."); + repository.getATRepository().rebuildLatestAtStates(900); + repository.saveChanges(); + int numATStatesPruned = repository.getATRepository().pruneAtStates(0, 900); + assertEquals(900-2, numATStatesPruned); // Minus 1 for genesis block, and another for the latest AT state + repository.getATRepository().setAtPruneHeight(901); + + // Now ensure the SQL repository is missing blocks 2 and 900... + assertNull(repository.getBlockRepository().fromHeight(2)); + assertNull(repository.getBlockRepository().fromHeight(900)); + System.out.println("Blocks 2 and 900 have been pruned from the repository."); + + // ... but it's not missing blocks 1 and 901 (we don't prune the genesis block) + assertNotNull(repository.getBlockRepository().fromHeight(1)); + assertNotNull(repository.getBlockRepository().fromHeight(901)); + System.out.println("Blocks 1 and 901 still exist in the repository."); + + // Validate the latest block height in the repository + int lastBlockHeight = repository.getBlockRepository().getLastBlock().getHeight(); + System.out.println("Latest block height in repository (Expected 1002): " + lastBlockHeight); + assertEquals(1002, lastBlockHeight); + + System.out.println("testArchiveAndPrune completed successfully."); + } + } + + @Test + public void testTrimArchivePruneAndOrphan() throws DataException, InterruptedException, TransformationException, IOException { + try (final Repository repository = RepositoryManager.getRepository()) { + + System.out.println("Starting testTrimArchivePruneAndOrphan"); + + // Deploy an AT so that we have AT state data + System.out.println("Deploying AT..."); + PrivateKeyAccount deployer = Common.getTestAccount(repository, "alice"); + byte[] creationBytes = AtUtils.buildSimpleAT(); + long fundingAmount = 1_00000000L; + AtUtils.doDeployAT(repository, deployer, creationBytes, fundingAmount); + System.out.println("AT deployed successfully."); + + // Mint some blocks so that we are able to archive them later + System.out.println("Minting 1000 blocks..."); + for (int i = 0; i < 1000; i++) { + BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); + // Log every 100 blocks + if ((i + 1) % 100 == 0) { + System.out.println("Minted block " + (i + 1)); + } + } + System.out.println("Finished minting blocks."); + + // Make sure that block 500 has full AT state data and data hash + System.out.println("Verifying block 500 AT state data..."); + List block500AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(500); + ATStateData atStatesData = repository.getATRepository().getATStateAtHeight(block500AtStatesData.get(0).getATAddress(), 500); + assertNotNull(atStatesData.getStateHash()); + assertNotNull(atStatesData.getStateData()); + System.out.println("Block 500 AT state data verified."); + + // Trim the first 500 blocks + System.out.println("Trimming first 500 blocks..."); + repository.getBlockRepository().trimOldOnlineAccountsSignatures(0, 500); + repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(501); + repository.getATRepository().rebuildLatestAtStates(500); + repository.getATRepository().trimAtStates(0, 500, 1000); + repository.getATRepository().setAtTrimHeight(501); + System.out.println("Trimming completed."); + + // Now block 499 should only have the AT state data hash + System.out.println("Checking block 499 AT state data..."); + List block499AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(499); + atStatesData = repository.getATRepository().getATStateAtHeight(block499AtStatesData.get(0).getATAddress(), 499); + assertNotNull(atStatesData.getStateHash()); + assertNull(atStatesData.getStateData()); + System.out.println("Block 499 AT state data contains only state hash as expected."); + + // ... but block 500 should have the full data (due to being retained as the "latest" AT state in the trimmed range + System.out.println("Verifying block 500 AT state data again..."); + block500AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(500); + atStatesData = repository.getATRepository().getATStateAtHeight(block500AtStatesData.get(0).getATAddress(), 500); + assertNotNull(atStatesData.getStateHash()); + assertNotNull(atStatesData.getStateData()); + System.out.println("Block 500 AT state data contains full data."); + + // ... and block 501 should also have the full data + System.out.println("Verifying block 501 AT state data..."); + List block501AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(501); + atStatesData = repository.getATRepository().getATStateAtHeight(block501AtStatesData.get(0).getATAddress(), 501); + assertNotNull(atStatesData.getStateHash()); + assertNotNull(atStatesData.getStateData()); + System.out.println("Block 501 AT state data contains full data."); + + // Check the max archive height - this should be one less than the first untrimmed height + final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); + System.out.println("Maximum archive height determined (Expected 500): " + maximumArchiveHeight); + assertEquals(500, maximumArchiveHeight); + + BlockData block3DataPreArchive = repository.getBlockRepository().fromHeight(3); + + // Write blocks 2-500 to the archive + System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); + BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); + writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes + BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); + System.out.println("Finished writing blocks to archive. Result: " + result); + assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); + + // Make sure that the archive contains the correct number of blocks + System.out.println("Number of blocks written to archive (Expected 499): " + writer.getWrittenCount()); + assertEquals(500 - 1, writer.getWrittenCount()); // -1 for the genesis block + + // Increment block archive height + repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); + repository.saveChanges(); + assertEquals(500 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); + System.out.println("Block archive height updated to: " + (500 - 1)); + + // Ensure the file exists + File outputFile = writer.getOutputPath().toFile(); + assertTrue(outputFile.exists()); + System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); + + // Ensure the SQL repository contains blocks 2 and 500... + System.out.println("Verifying that blocks 2 and 500 exist in the repository..."); + assertNotNull(repository.getBlockRepository().fromHeight(2)); + assertNotNull(repository.getBlockRepository().fromHeight(500)); + System.out.println("Blocks 2 and 500 are present in the repository."); + + // Prune all the archived blocks + System.out.println("Pruning blocks 2 to 500..."); + int numBlocksPruned = repository.getBlockRepository().pruneBlocks(0, 500); + System.out.println("Number of blocks pruned (Expected 499): " + numBlocksPruned); + assertEquals(500-1, numBlocksPruned); + repository.getBlockRepository().setBlockPruneHeight(501); + + // Prune the AT states for the archived blocks + System.out.println("Pruning AT states up to height 500..."); + repository.getATRepository().rebuildLatestAtStates(500); + repository.saveChanges(); + int numATStatesPruned = repository.getATRepository().pruneAtStates(2, 500); + System.out.println("Number of AT states pruned (Expected 498): " + numATStatesPruned); + assertEquals(498, numATStatesPruned); // Minus 1 for genesis block, and another for the latest AT state + repository.getATRepository().setAtPruneHeight(501); + + // Now ensure the SQL repository is missing blocks 2 and 500... + System.out.println("Verifying that blocks 2 and 500 have been pruned..."); + assertNull(repository.getBlockRepository().fromHeight(2)); + assertNull(repository.getBlockRepository().fromHeight(500)); + System.out.println("Blocks 2 and 500 have been successfully pruned."); + + // ... but it's not missing blocks 1 and 501 (we don't prune the genesis block) + System.out.println("Verifying that blocks 1 and 501 still exist..."); + assertNotNull(repository.getBlockRepository().fromHeight(1)); + assertNotNull(repository.getBlockRepository().fromHeight(501)); + System.out.println("Blocks 1 and 501 are present in the repository."); + + // Validate the latest block height in the repository + int lastBlockHeight = repository.getBlockRepository().getLastBlock().getHeight(); + System.out.println("Latest block height in repository (Expected 1002): " + lastBlockHeight); + assertEquals(1002, lastBlockHeight); + + // Now orphan some unarchived blocks. + System.out.println("Orphaning 500 blocks..."); + BlockUtils.orphanBlocks(repository, 500); + int currentLastBlockHeight = repository.getBlockRepository().getLastBlock().getHeight(); + System.out.println("New last block height after orphaning (Expected 502): " + currentLastBlockHeight); + assertEquals(502, currentLastBlockHeight); + + // We're close to the lower limit of the SQL database now, so + // we need to import some blocks from the archive + System.out.println("Importing blocks 401 to 500 from the archive..."); + BlockArchiveUtils.importFromArchive(401, 500, repository); + + // Ensure the SQL repository now contains block 401 but not 400... + System.out.println("Verifying that block 401 exists and block 400 does not..."); + assertNotNull(repository.getBlockRepository().fromHeight(401)); + assertNull(repository.getBlockRepository().fromHeight(400)); + System.out.println("Block 401 exists, block 400 does not."); + + // Import the remaining 399 blocks + System.out.println("Importing blocks 2 to 400 from the archive..."); + BlockArchiveUtils.importFromArchive(2, 400, repository); + + // Verify that block 3 matches the original + System.out.println("Verifying that block 3 matches the original data..."); + BlockData block3DataPostArchive = repository.getBlockRepository().fromHeight(3); + assertArrayEquals(block3DataPreArchive.getSignature(), block3DataPostArchive.getSignature()); + assertEquals(block3DataPreArchive.getHeight(), block3DataPostArchive.getHeight()); + System.out.println("Block 3 data matches the original."); + + // Orphan 1 more block, which should be the last one that is possible to be orphaned + System.out.println("Orphaning 1 more block..."); + BlockUtils.orphanBlocks(repository, 1); + System.out.println("Orphaned 1 block successfully."); + + // Orphan another block, which should fail + System.out.println("Attempting to orphan another block, which should fail..."); + Exception exception = null; + try { + BlockUtils.orphanBlocks(repository, 1); + } catch (DataException e) { + exception = e; + System.out.println("Caught expected DataException: " + e.getMessage()); + } + + // Ensure that a DataException is thrown because there is no more AT states data available + assertNotNull(exception); + assertEquals(DataException.class, exception.getClass()); + System.out.println("DataException confirmed due to lack of AT states data."); + + // FUTURE: we may be able to retain unique AT states when trimming, to avoid this exception + // and allow orphaning back through blocks with trimmed AT states. + + System.out.println("testTrimArchivePruneAndOrphan completed successfully."); } } @@ -611,44 +611,44 @@ public class BlockArchiveV1Tests extends Common { * In these cases we disable archiving and pruning as this index is a * very essential component in these processes. */ - @Test - public void testMissingAtStatesHeightIndex() throws DataException, SQLException { - try (final HSQLDBRepository repository = (HSQLDBRepository) RepositoryManager.getRepository()) { + @Test + public void testMissingAtStatesHeightIndex() throws DataException, SQLException { + try (final HSQLDBRepository repository = (HSQLDBRepository) RepositoryManager.getRepository()) { - System.out.println("Starting testMissingAtStatesHeightIndex"); + System.out.println("Starting testMissingAtStatesHeightIndex"); - // Firstly check that we're able to prune or archive when the index exists - System.out.println("Checking existence of ATStatesHeightIndex..."); - assertTrue(repository.getATRepository().hasAtStatesHeightIndex()); - assertTrue(RepositoryManager.canArchiveOrPrune()); - System.out.println("ATStatesHeightIndex exists. Archiving and pruning are possible."); + // Firstly check that we're able to prune or archive when the index exists + System.out.println("Checking existence of ATStatesHeightIndex..."); + assertTrue(repository.getATRepository().hasAtStatesHeightIndex()); + assertTrue(RepositoryManager.canArchiveOrPrune()); + System.out.println("ATStatesHeightIndex exists. Archiving and pruning are possible."); - // Delete the index - System.out.println("Dropping ATStatesHeightIndex..."); - repository.prepareStatement("DROP INDEX ATSTATESHEIGHTINDEX").execute(); - System.out.println("ATStatesHeightIndex dropped."); + // Delete the index + System.out.println("Dropping ATStatesHeightIndex..."); + repository.prepareStatement("DROP INDEX ATSTATESHEIGHTINDEX").execute(); + System.out.println("ATStatesHeightIndex dropped."); - // Ensure check that we're unable to prune or archive when the index doesn't exist - System.out.println("Verifying that ATStatesHeightIndex no longer exists..."); - assertFalse(repository.getATRepository().hasAtStatesHeightIndex()); - assertFalse(RepositoryManager.canArchiveOrPrune()); - System.out.println("ATStatesHeightIndex does not exist. Archiving and pruning are disabled."); + // Ensure check that we're unable to prune or archive when the index doesn't exist + System.out.println("Verifying that ATStatesHeightIndex no longer exists..."); + assertFalse(repository.getATRepository().hasAtStatesHeightIndex()); + assertFalse(RepositoryManager.canArchiveOrPrune()); + System.out.println("ATStatesHeightIndex does not exist. Archiving and pruning are disabled."); - System.out.println("testMissingAtStatesHeightIndex completed successfully."); - } - } + System.out.println("testMissingAtStatesHeightIndex completed successfully."); + } + } - private void deleteArchiveDirectory() { - // Delete archive directory if exists - Path archivePath = Paths.get(Settings.getInstance().getRepositoryPath(), "archive").toAbsolutePath(); - try { - FileUtils.deleteDirectory(archivePath.toFile()); - System.out.println("Deleted archive directory at: " + archivePath); - } catch (IOException e) { + private void deleteArchiveDirectory() { + // Delete archive directory if exists + Path archivePath = Paths.get(Settings.getInstance().getRepositoryPath(), "archive").toAbsolutePath(); + try { + FileUtils.deleteDirectory(archivePath.toFile()); + System.out.println("Deleted archive directory at: " + archivePath); + } catch (IOException e) { - System.out.println("Failed to delete archive directory: " + e.getMessage()); - } - } + System.out.println("Failed to delete archive directory: " + e.getMessage()); + } + } } diff --git a/src/test/java/org/qortal/test/BlockArchiveV2Tests.java b/src/test/java/org/qortal/test/BlockArchiveV2Tests.java index 784ac3d3..8ab02b40 100644 --- a/src/test/java/org/qortal/test/BlockArchiveV2Tests.java +++ b/src/test/java/org/qortal/test/BlockArchiveV2Tests.java @@ -34,625 +34,625 @@ import static org.junit.Assert.*; public class BlockArchiveV2Tests extends Common { - @Before - public void beforeTest() throws DataException, IllegalAccessException { - Common.useSettings("test-settings-v2-block-archive.json"); - NTP.setFixedOffset(Settings.getInstance().getTestNtpOffset()); - this.deleteArchiveDirectory(); + @Before + public void beforeTest() throws DataException, IllegalAccessException { + Common.useSettings("test-settings-v2-block-archive.json"); + NTP.setFixedOffset(Settings.getInstance().getTestNtpOffset()); + this.deleteArchiveDirectory(); - // Set default archive version to 2, so that archive builds in these tests use V2 - FieldUtils.writeField(Settings.getInstance(), "defaultArchiveVersion", 2, true); - } + // Set default archive version to 2, so that archive builds in these tests use V2 + FieldUtils.writeField(Settings.getInstance(), "defaultArchiveVersion", 2, true); + } - @After - public void afterTest() throws DataException { - this.deleteArchiveDirectory(); - } + @After + public void afterTest() throws DataException { + this.deleteArchiveDirectory(); + } - @Test - public void testWriter() throws DataException, InterruptedException, TransformationException, IOException { - try (final Repository repository = RepositoryManager.getRepository()) { + @Test + public void testWriter() throws DataException, InterruptedException, TransformationException, IOException { + try (final Repository repository = RepositoryManager.getRepository()) { - System.out.println("Starting testWriter"); + System.out.println("Starting testWriter"); - // Mint some blocks so that we are able to archive them later - System.out.println("Minting 1000 blocks..."); - for (int i = 0; i < 1000; i++) { - BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); - // Log every 100 blocks - if ((i + 1) % 100 == 0) { - System.out.println("Minted block " + (i + 1)); - } - } - System.out.println("Finished minting blocks."); + // Mint some blocks so that we are able to archive them later + System.out.println("Minting 1000 blocks..."); + for (int i = 0; i < 1000; i++) { + BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); + // Log every 100 blocks + if ((i + 1) % 100 == 0) { + System.out.println("Minted block " + (i + 1)); + } + } + System.out.println("Finished minting blocks."); - // 900 blocks are trimmed (this specifies the first untrimmed height) - repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(901); - repository.getATRepository().setAtTrimHeight(901); - System.out.println("Set trim heights to 901."); + // 900 blocks are trimmed (this specifies the first untrimmed height) + repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(901); + repository.getATRepository().setAtTrimHeight(901); + System.out.println("Set trim heights to 901."); - // Check the max archive height - this should be one less than the first untrimmed height - final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); - System.out.println("Maximum archive height (Expected 900): " + maximumArchiveHeight); - assertEquals(900, maximumArchiveHeight); + // Check the max archive height - this should be one less than the first untrimmed height + final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); + System.out.println("Maximum archive height (Expected 900): " + maximumArchiveHeight); + assertEquals(900, maximumArchiveHeight); - // Write blocks 2-900 to the archive - System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); - BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); - writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes - BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); - System.out.println("Finished writing blocks to archive. Result: " + result); - assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); + // Write blocks 2-900 to the archive + System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); + BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); + writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes + BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); + System.out.println("Finished writing blocks to archive. Result: " + result); + assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); - // Make sure that the archive contains the correct number of blocks - System.out.println("Archive contains " + writer.getWrittenCount() + " blocks. (Expected 899)"); - assertEquals(900 - 1, writer.getWrittenCount()); + // Make sure that the archive contains the correct number of blocks + System.out.println("Archive contains " + writer.getWrittenCount() + " blocks. (Expected 899)"); + assertEquals(900 - 1, writer.getWrittenCount()); - // Increment block archive height - repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); - repository.saveChanges(); - assertEquals(900 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); + // Increment block archive height + repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); + repository.saveChanges(); + assertEquals(900 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); - // Ensure the file exists - File outputFile = writer.getOutputPath().toFile(); - assertTrue(outputFile.exists()); - System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); + // Ensure the file exists + File outputFile = writer.getOutputPath().toFile(); + assertTrue(outputFile.exists()); + System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); - System.out.println("testWriter completed successfully."); - } - } + System.out.println("testWriter completed successfully."); + } + } - @Test - public void testWriterAndReader() throws DataException, InterruptedException, TransformationException, IOException { - try (final Repository repository = RepositoryManager.getRepository()) { + @Test + public void testWriterAndReader() throws DataException, InterruptedException, TransformationException, IOException { + try (final Repository repository = RepositoryManager.getRepository()) { - System.out.println("Starting testWriterAndReader"); + System.out.println("Starting testWriterAndReader"); - // Mint some blocks so that we are able to archive them later - System.out.println("Minting 1000 blocks..."); - for (int i = 0; i < 1000; i++) { - BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); - // Log every 100 blocks - if ((i + 1) % 100 == 0) { - System.out.println("Minted block " + (i + 1)); - } - } - System.out.println("Finished minting blocks."); + // Mint some blocks so that we are able to archive them later + System.out.println("Minting 1000 blocks..."); + for (int i = 0; i < 1000; i++) { + BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); + // Log every 100 blocks + if ((i + 1) % 100 == 0) { + System.out.println("Minted block " + (i + 1)); + } + } + System.out.println("Finished minting blocks."); - // 900 blocks are trimmed (this specifies the first untrimmed height) - repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(901); - repository.getATRepository().setAtTrimHeight(901); - System.out.println("Set trim heights to 901."); + // 900 blocks are trimmed (this specifies the first untrimmed height) + repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(901); + repository.getATRepository().setAtTrimHeight(901); + System.out.println("Set trim heights to 901."); - // Check the max archive height - this should be one less than the first untrimmed height - final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); - System.out.println("Maximum archive height (Expected 900): " + maximumArchiveHeight); - assertEquals(900, maximumArchiveHeight); + // Check the max archive height - this should be one less than the first untrimmed height + final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); + System.out.println("Maximum archive height (Expected 900): " + maximumArchiveHeight); + assertEquals(900, maximumArchiveHeight); - // Write blocks 2-900 to the archive - System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); - BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); - writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes - BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); - System.out.println("Finished writing blocks to archive. Result: " + result); - assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); + // Write blocks 2-900 to the archive + System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); + BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); + writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes + BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); + System.out.println("Finished writing blocks to archive. Result: " + result); + assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); - // Make sure that the archive contains the correct number of blocks - System.out.println("Archive contains " + writer.getWrittenCount() + " blocks. (Expected 899)"); - assertEquals(900 - 1, writer.getWrittenCount()); + // Make sure that the archive contains the correct number of blocks + System.out.println("Archive contains " + writer.getWrittenCount() + " blocks. (Expected 899)"); + assertEquals(900 - 1, writer.getWrittenCount()); - // Increment block archive height - repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); - repository.saveChanges(); - assertEquals(900 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); + // Increment block archive height + repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); + repository.saveChanges(); + assertEquals(900 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); - // Ensure the file exists - File outputFile = writer.getOutputPath().toFile(); - assertTrue(outputFile.exists()); - System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); + // Ensure the file exists + File outputFile = writer.getOutputPath().toFile(); + assertTrue(outputFile.exists()); + System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); - // Read block 2 from the archive - System.out.println("Reading block 2 from the archive..."); - BlockArchiveReader reader = BlockArchiveReader.getInstance(); - BlockTransformation block2Info = reader.fetchBlockAtHeight(2); - BlockData block2ArchiveData = block2Info.getBlockData(); + // Read block 2 from the archive + System.out.println("Reading block 2 from the archive..."); + BlockArchiveReader reader = BlockArchiveReader.getInstance(); + BlockTransformation block2Info = reader.fetchBlockAtHeight(2); + BlockData block2ArchiveData = block2Info.getBlockData(); - // Read block 2 from the repository - BlockData block2RepositoryData = repository.getBlockRepository().fromHeight(2); + // Read block 2 from the repository + BlockData block2RepositoryData = repository.getBlockRepository().fromHeight(2); - // Ensure the values match - System.out.println("Comparing block 2 data..."); - assertEquals(block2ArchiveData.getHeight(), block2RepositoryData.getHeight()); - assertArrayEquals(block2ArchiveData.getSignature(), block2RepositoryData.getSignature()); + // Ensure the values match + System.out.println("Comparing block 2 data..."); + assertEquals(block2ArchiveData.getHeight(), block2RepositoryData.getHeight()); + assertArrayEquals(block2ArchiveData.getSignature(), block2RepositoryData.getSignature()); - // Test some values in the archive - assertEquals(1, block2ArchiveData.getOnlineAccountsCount()); + // Test some values in the archive + assertEquals(1, block2ArchiveData.getOnlineAccountsCount()); - // Read block 900 from the archive - System.out.println("Reading block 900 from the archive..."); - BlockTransformation block900Info = reader.fetchBlockAtHeight(900); - BlockData block900ArchiveData = block900Info.getBlockData(); + // Read block 900 from the archive + System.out.println("Reading block 900 from the archive..."); + BlockTransformation block900Info = reader.fetchBlockAtHeight(900); + BlockData block900ArchiveData = block900Info.getBlockData(); - // Read block 900 from the repository - BlockData block900RepositoryData = repository.getBlockRepository().fromHeight(900); + // Read block 900 from the repository + BlockData block900RepositoryData = repository.getBlockRepository().fromHeight(900); - // Ensure the values match - System.out.println("Comparing block 900 data..."); - assertEquals(block900ArchiveData.getHeight(), block900RepositoryData.getHeight()); - assertArrayEquals(block900ArchiveData.getSignature(), block900RepositoryData.getSignature()); + // Ensure the values match + System.out.println("Comparing block 900 data..."); + assertEquals(block900ArchiveData.getHeight(), block900RepositoryData.getHeight()); + assertArrayEquals(block900ArchiveData.getSignature(), block900RepositoryData.getSignature()); - // Test some values in the archive - assertEquals(1, block900ArchiveData.getOnlineAccountsCount()); + // Test some values in the archive + assertEquals(1, block900ArchiveData.getOnlineAccountsCount()); - System.out.println("testWriterAndReader completed successfully."); - } - } + System.out.println("testWriterAndReader completed successfully."); + } + } - @Test - public void testArchivedAtStates() throws DataException, InterruptedException, TransformationException, IOException { - try (final Repository repository = RepositoryManager.getRepository()) { + @Test + public void testArchivedAtStates() throws DataException, InterruptedException, TransformationException, IOException { + try (final Repository repository = RepositoryManager.getRepository()) { - System.out.println("Starting testArchivedAtStates"); + System.out.println("Starting testArchivedAtStates"); - // Deploy an AT so that we have AT state data - System.out.println("Deploying AT..."); - PrivateKeyAccount deployer = Common.getTestAccount(repository, "alice"); - byte[] creationBytes = AtUtils.buildSimpleAT(); - long fundingAmount = 1_00000000L; - DeployAtTransaction deployAtTransaction = AtUtils.doDeployAT(repository, deployer, creationBytes, fundingAmount); - String atAddress = deployAtTransaction.getATAccount().getAddress(); - System.out.println("AT deployed at address: " + atAddress); + // Deploy an AT so that we have AT state data + System.out.println("Deploying AT..."); + PrivateKeyAccount deployer = Common.getTestAccount(repository, "alice"); + byte[] creationBytes = AtUtils.buildSimpleAT(); + long fundingAmount = 1_00000000L; + DeployAtTransaction deployAtTransaction = AtUtils.doDeployAT(repository, deployer, creationBytes, fundingAmount); + String atAddress = deployAtTransaction.getATAccount().getAddress(); + System.out.println("AT deployed at address: " + atAddress); - // Mint some blocks so that we are able to archive them later - System.out.println("Minting 1000 blocks..."); - for (int i = 0; i < 1000; i++) { - BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); - // Log every 100 blocks - if ((i + 1) % 100 == 0) { - System.out.println("Minted block " + (i + 1)); - } - } - System.out.println("Finished minting blocks."); + // Mint some blocks so that we are able to archive them later + System.out.println("Minting 1000 blocks..."); + for (int i = 0; i < 1000; i++) { + BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); + // Log every 100 blocks + if ((i + 1) % 100 == 0) { + System.out.println("Minted block " + (i + 1)); + } + } + System.out.println("Finished minting blocks."); - // 9 blocks are trimmed (this specifies the first untrimmed height) - repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(10); - repository.getATRepository().setAtTrimHeight(10); - System.out.println("Set trim heights to 10."); + // 9 blocks are trimmed (this specifies the first untrimmed height) + repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(10); + repository.getATRepository().setAtTrimHeight(10); + System.out.println("Set trim heights to 10."); - // Check the max archive height - final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); - System.out.println("Maximum archive height (Expected 9): " + maximumArchiveHeight); - assertEquals(9, maximumArchiveHeight); + // Check the max archive height + final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); + System.out.println("Maximum archive height (Expected 9): " + maximumArchiveHeight); + assertEquals(9, maximumArchiveHeight); - // Write blocks 2-9 to the archive - System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); - BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); - writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes - BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); - System.out.println("Finished writing blocks to archive. Result: " + result); - assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); + // Write blocks 2-9 to the archive + System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); + BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); + writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes + BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); + System.out.println("Finished writing blocks to archive. Result: " + result); + assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); - // Make sure that the archive contains the correct number of blocks - System.out.println("Archive contains " + writer.getWrittenCount() + " blocks. (Expected 8)"); - assertEquals(9 - 1, writer.getWrittenCount()); + // Make sure that the archive contains the correct number of blocks + System.out.println("Archive contains " + writer.getWrittenCount() + " blocks. (Expected 8)"); + assertEquals(9 - 1, writer.getWrittenCount()); - // Increment block archive height - repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); - repository.saveChanges(); - assertEquals(9 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); - System.out.println("Block archive height updated to: " + (9 - 1)); + // Increment block archive height + repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); + repository.saveChanges(); + assertEquals(9 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); + System.out.println("Block archive height updated to: " + (9 - 1)); - // Ensure the file exists - File outputFile = writer.getOutputPath().toFile(); - assertTrue(outputFile.exists()); - System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); + // Ensure the file exists + File outputFile = writer.getOutputPath().toFile(); + assertTrue(outputFile.exists()); + System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); - // Check blocks 3-9 - System.out.println("Checking blocks 2 to 9..."); - for (Integer testHeight = 2; testHeight <= 9; testHeight++) { + // Check blocks 3-9 + System.out.println("Checking blocks 2 to 9..."); + for (Integer testHeight = 2; testHeight <= 9; testHeight++) { - System.out.println("Reading block " + testHeight + " from the archive..."); - // Read a block from the archive - BlockArchiveReader reader = BlockArchiveReader.getInstance(); - BlockTransformation blockInfo = reader.fetchBlockAtHeight(testHeight); - BlockData archivedBlockData = blockInfo.getBlockData(); - byte[] archivedAtStateHash = blockInfo.getAtStatesHash(); - List archivedTransactions = blockInfo.getTransactions(); + System.out.println("Reading block " + testHeight + " from the archive..."); + // Read a block from the archive + BlockArchiveReader reader = BlockArchiveReader.getInstance(); + BlockTransformation blockInfo = reader.fetchBlockAtHeight(testHeight); + BlockData archivedBlockData = blockInfo.getBlockData(); + byte[] archivedAtStateHash = blockInfo.getAtStatesHash(); + List archivedTransactions = blockInfo.getTransactions(); - // Read the same block from the repository - BlockData repositoryBlockData = repository.getBlockRepository().fromHeight(testHeight); - ATStateData repositoryAtStateData = repository.getATRepository().getATStateAtHeight(atAddress, testHeight); + // Read the same block from the repository + BlockData repositoryBlockData = repository.getBlockRepository().fromHeight(testHeight); + ATStateData repositoryAtStateData = repository.getATRepository().getATStateAtHeight(atAddress, testHeight); - // Ensure the repository has full AT state data - assertNotNull(repositoryAtStateData.getStateHash()); - assertNotNull(repositoryAtStateData.getStateData()); + // Ensure the repository has full AT state data + assertNotNull(repositoryAtStateData.getStateHash()); + assertNotNull(repositoryAtStateData.getStateData()); - // Check the archived AT state - if (testHeight == 2) { - System.out.println("Checking block " + testHeight + " AT state data (expected transactions)..."); - assertEquals(1, archivedTransactions.size()); - assertEquals(Transaction.TransactionType.DEPLOY_AT, archivedTransactions.get(0).getType()); - } + // Check the archived AT state + if (testHeight == 2) { + System.out.println("Checking block " + testHeight + " AT state data (expected transactions)..."); + assertEquals(1, archivedTransactions.size()); + assertEquals(Transaction.TransactionType.DEPLOY_AT, archivedTransactions.get(0).getType()); + } else { - System.out.println("Checking block " + testHeight + " AT state data (no transactions expected)..."); - // Blocks 3+ shouldn't have any transactions - assertTrue(archivedTransactions.isEmpty()); - } + System.out.println("Checking block " + testHeight + " AT state data (no transactions expected)..."); + // Blocks 3+ shouldn't have any transactions + assertTrue(archivedTransactions.isEmpty()); + } - // Ensure the archive has the AT states hash - System.out.println("Checking block " + testHeight + " AT states hash..."); - assertNotNull(archivedAtStateHash); + // Ensure the archive has the AT states hash + System.out.println("Checking block " + testHeight + " AT states hash..."); + assertNotNull(archivedAtStateHash); - // Also check the online accounts count and height - assertEquals(1, archivedBlockData.getOnlineAccountsCount()); - assertEquals(testHeight, archivedBlockData.getHeight()); + // Also check the online accounts count and height + assertEquals(1, archivedBlockData.getOnlineAccountsCount()); + assertEquals(testHeight, archivedBlockData.getHeight()); - // Ensure the values match - System.out.println("Comparing block " + testHeight + " data..."); - assertEquals(archivedBlockData.getHeight(), repositoryBlockData.getHeight()); - assertArrayEquals(archivedBlockData.getSignature(), repositoryBlockData.getSignature()); - assertEquals(archivedBlockData.getOnlineAccountsCount(), repositoryBlockData.getOnlineAccountsCount()); - assertArrayEquals(archivedBlockData.getMinterSignature(), repositoryBlockData.getMinterSignature()); - assertEquals(archivedBlockData.getATCount(), repositoryBlockData.getATCount()); - assertEquals(archivedBlockData.getOnlineAccountsCount(), repositoryBlockData.getOnlineAccountsCount()); - assertArrayEquals(archivedBlockData.getReference(), repositoryBlockData.getReference()); - assertEquals(archivedBlockData.getTimestamp(), repositoryBlockData.getTimestamp()); - assertEquals(archivedBlockData.getATFees(), repositoryBlockData.getATFees()); - assertEquals(archivedBlockData.getTotalFees(), repositoryBlockData.getTotalFees()); - assertEquals(archivedBlockData.getTransactionCount(), repositoryBlockData.getTransactionCount()); - assertArrayEquals(archivedBlockData.getTransactionsSignature(), repositoryBlockData.getTransactionsSignature()); + // Ensure the values match + System.out.println("Comparing block " + testHeight + " data..."); + assertEquals(archivedBlockData.getHeight(), repositoryBlockData.getHeight()); + assertArrayEquals(archivedBlockData.getSignature(), repositoryBlockData.getSignature()); + assertEquals(archivedBlockData.getOnlineAccountsCount(), repositoryBlockData.getOnlineAccountsCount()); + assertArrayEquals(archivedBlockData.getMinterSignature(), repositoryBlockData.getMinterSignature()); + assertEquals(archivedBlockData.getATCount(), repositoryBlockData.getATCount()); + assertEquals(archivedBlockData.getOnlineAccountsCount(), repositoryBlockData.getOnlineAccountsCount()); + assertArrayEquals(archivedBlockData.getReference(), repositoryBlockData.getReference()); + assertEquals(archivedBlockData.getTimestamp(), repositoryBlockData.getTimestamp()); + assertEquals(archivedBlockData.getATFees(), repositoryBlockData.getATFees()); + assertEquals(archivedBlockData.getTotalFees(), repositoryBlockData.getTotalFees()); + assertEquals(archivedBlockData.getTransactionCount(), repositoryBlockData.getTransactionCount()); + assertArrayEquals(archivedBlockData.getTransactionsSignature(), repositoryBlockData.getTransactionsSignature()); - // TODO: build atStatesHash and compare against value in archive - } + // TODO: build atStatesHash and compare against value in archive + } - // Check block 10 (unarchived) - System.out.println("Checking block 10 (should not be in archive)..."); - BlockArchiveReader reader = BlockArchiveReader.getInstance(); - BlockTransformation blockInfo = reader.fetchBlockAtHeight(10); - assertNull(blockInfo); + // Check block 10 (unarchived) + System.out.println("Checking block 10 (should not be in archive)..."); + BlockArchiveReader reader = BlockArchiveReader.getInstance(); + BlockTransformation blockInfo = reader.fetchBlockAtHeight(10); + assertNull(blockInfo); - System.out.println("testArchivedAtStates completed successfully."); - } + System.out.println("testArchivedAtStates completed successfully."); + } - } + } - @Test - public void testArchiveAndPrune() throws DataException, InterruptedException, TransformationException, IOException { - try (final Repository repository = RepositoryManager.getRepository()) { + @Test + public void testArchiveAndPrune() throws DataException, InterruptedException, TransformationException, IOException { + try (final Repository repository = RepositoryManager.getRepository()) { - System.out.println("Starting testArchiveAndPrune"); + System.out.println("Starting testArchiveAndPrune"); - // Deploy an AT so that we have AT state data - System.out.println("Deploying AT..."); - PrivateKeyAccount deployer = Common.getTestAccount(repository, "alice"); - byte[] creationBytes = AtUtils.buildSimpleAT(); - long fundingAmount = 1_00000000L; - AtUtils.doDeployAT(repository, deployer, creationBytes, fundingAmount); - System.out.println("AT deployed successfully."); + // Deploy an AT so that we have AT state data + System.out.println("Deploying AT..."); + PrivateKeyAccount deployer = Common.getTestAccount(repository, "alice"); + byte[] creationBytes = AtUtils.buildSimpleAT(); + long fundingAmount = 1_00000000L; + AtUtils.doDeployAT(repository, deployer, creationBytes, fundingAmount); + System.out.println("AT deployed successfully."); - // Mint some blocks so that we are able to archive them later - System.out.println("Minting 1000 blocks..."); - for (int i = 0; i < 1000; i++) { - BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); - // Log every 100 blocks - if ((i + 1) % 100 == 0) { - System.out.println("Minted block " + (i + 1)); - } - } - System.out.println("Finished minting blocks."); + // Mint some blocks so that we are able to archive them later + System.out.println("Minting 1000 blocks..."); + for (int i = 0; i < 1000; i++) { + BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); + // Log every 100 blocks + if ((i + 1) % 100 == 0) { + System.out.println("Minted block " + (i + 1)); + } + } + System.out.println("Finished minting blocks."); - // Assume 900 blocks are trimmed (this specifies the first untrimmed height) - repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(901); - repository.getATRepository().setAtTrimHeight(901); - System.out.println("Set trim heights to 901."); + // Assume 900 blocks are trimmed (this specifies the first untrimmed height) + repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(901); + repository.getATRepository().setAtTrimHeight(901); + System.out.println("Set trim heights to 901."); - // Check the max archive height - this should be one less than the first untrimmed height - final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); - System.out.println("Maximum archive height (Expected 900): " + maximumArchiveHeight); - assertEquals(900, maximumArchiveHeight); + // Check the max archive height - this should be one less than the first untrimmed height + final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); + System.out.println("Maximum archive height (Expected 900): " + maximumArchiveHeight); + assertEquals(900, maximumArchiveHeight); - // Write blocks 2-900 to the archive - System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); - BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); - writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes - BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); - System.out.println("Finished writing blocks to archive. Result: " + result); - assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); + // Write blocks 2-900 to the archive + System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); + BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); + writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes + BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); + System.out.println("Finished writing blocks to archive. Result: " + result); + assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); - // Make sure that the archive contains the correct number of blocks - System.out.println("Archive contains " + writer.getWrittenCount() + " blocks. (Expected 899)"); - assertEquals(900 - 1, writer.getWrittenCount()); + // Make sure that the archive contains the correct number of blocks + System.out.println("Archive contains " + writer.getWrittenCount() + " blocks. (Expected 899)"); + assertEquals(900 - 1, writer.getWrittenCount()); - // Increment block archive height - repository.getBlockArchiveRepository().setBlockArchiveHeight(901); - repository.saveChanges(); - assertEquals(901, repository.getBlockArchiveRepository().getBlockArchiveHeight()); + // Increment block archive height + repository.getBlockArchiveRepository().setBlockArchiveHeight(901); + repository.saveChanges(); + assertEquals(901, repository.getBlockArchiveRepository().getBlockArchiveHeight()); - // Ensure the file exists - File outputFile = writer.getOutputPath().toFile(); - assertTrue(outputFile.exists()); - System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); + // Ensure the file exists + File outputFile = writer.getOutputPath().toFile(); + assertTrue(outputFile.exists()); + System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); - // Ensure the SQL repository contains blocks 2 and 900... - System.out.println("Verifying that blocks 2 and 900 exist in the repository..."); - assertNotNull(repository.getBlockRepository().fromHeight(2)); - assertNotNull(repository.getBlockRepository().fromHeight(900)); - System.out.println("Blocks 2 and 900 are present in the repository."); + // Ensure the SQL repository contains blocks 2 and 900... + System.out.println("Verifying that blocks 2 and 900 exist in the repository..."); + assertNotNull(repository.getBlockRepository().fromHeight(2)); + assertNotNull(repository.getBlockRepository().fromHeight(900)); + System.out.println("Blocks 2 and 900 are present in the repository."); - // Prune all the archived blocks - System.out.println("Pruning blocks 2 to 900..."); - int numBlocksPruned = repository.getBlockRepository().pruneBlocks(0, 900); - System.out.println("Number of blocks pruned (Expected 899): " + numBlocksPruned); - assertEquals(900-1, numBlocksPruned); - repository.getBlockRepository().setBlockPruneHeight(901); + // Prune all the archived blocks + System.out.println("Pruning blocks 2 to 900..."); + int numBlocksPruned = repository.getBlockRepository().pruneBlocks(0, 900); + System.out.println("Number of blocks pruned (Expected 899): " + numBlocksPruned); + assertEquals(900-1, numBlocksPruned); + repository.getBlockRepository().setBlockPruneHeight(901); - // Prune the AT states for the archived blocks - System.out.println("Pruning AT states up to height 900..."); - repository.getATRepository().rebuildLatestAtStates(900); - repository.saveChanges(); - int numATStatesPruned = repository.getATRepository().pruneAtStates(0, 900); - System.out.println("Number of AT states pruned (Expected 898): " + numATStatesPruned); - assertEquals(900-2, numATStatesPruned); // Minus 1 for genesis block, and another for the latest AT state - repository.getATRepository().setAtPruneHeight(901); + // Prune the AT states for the archived blocks + System.out.println("Pruning AT states up to height 900..."); + repository.getATRepository().rebuildLatestAtStates(900); + repository.saveChanges(); + int numATStatesPruned = repository.getATRepository().pruneAtStates(0, 900); + System.out.println("Number of AT states pruned (Expected 898): " + numATStatesPruned); + assertEquals(900-2, numATStatesPruned); // Minus 1 for genesis block, and another for the latest AT state + repository.getATRepository().setAtPruneHeight(901); - // Now ensure the SQL repository is missing blocks 2 and 900... - System.out.println("Verifying that blocks 2 and 900 have been pruned..."); - assertNull(repository.getBlockRepository().fromHeight(2)); - assertNull(repository.getBlockRepository().fromHeight(900)); - System.out.println("Blocks 2 and 900 have been successfully pruned."); + // Now ensure the SQL repository is missing blocks 2 and 900... + System.out.println("Verifying that blocks 2 and 900 have been pruned..."); + assertNull(repository.getBlockRepository().fromHeight(2)); + assertNull(repository.getBlockRepository().fromHeight(900)); + System.out.println("Blocks 2 and 900 have been successfully pruned."); - // ... but it's not missing blocks 1 and 901 (we don't prune the genesis block) - System.out.println("Verifying that blocks 1 and 901 still exist..."); - assertNotNull(repository.getBlockRepository().fromHeight(1)); - assertNotNull(repository.getBlockRepository().fromHeight(901)); - System.out.println("Blocks 1 and 901 are present in the repository."); + // ... but it's not missing blocks 1 and 901 (we don't prune the genesis block) + System.out.println("Verifying that blocks 1 and 901 still exist..."); + assertNotNull(repository.getBlockRepository().fromHeight(1)); + assertNotNull(repository.getBlockRepository().fromHeight(901)); + System.out.println("Blocks 1 and 901 are present in the repository."); - // Validate the latest block height in the repository - int lastBlockHeight = repository.getBlockRepository().getLastBlock().getHeight(); - System.out.println("Latest block height in repository (Expected 1002): " + lastBlockHeight); - assertEquals(1002, lastBlockHeight); + // Validate the latest block height in the repository + int lastBlockHeight = repository.getBlockRepository().getLastBlock().getHeight(); + System.out.println("Latest block height in repository (Expected 1002): " + lastBlockHeight); + assertEquals(1002, lastBlockHeight); - System.out.println("testArchiveAndPrune completed successfully."); - } - } + System.out.println("testArchiveAndPrune completed successfully."); + } + } - @Test - public void testTrimArchivePruneAndOrphan() throws DataException, InterruptedException, TransformationException, IOException { - try (final Repository repository = RepositoryManager.getRepository()) { + @Test + public void testTrimArchivePruneAndOrphan() throws DataException, InterruptedException, TransformationException, IOException { + try (final Repository repository = RepositoryManager.getRepository()) { - System.out.println("Starting testTrimArchivePruneAndOrphan"); + System.out.println("Starting testTrimArchivePruneAndOrphan"); - // Deploy an AT so that we have AT state data - System.out.println("Deploying AT..."); - PrivateKeyAccount deployer = Common.getTestAccount(repository, "alice"); - byte[] creationBytes = AtUtils.buildSimpleAT(); - long fundingAmount = 1_00000000L; - AtUtils.doDeployAT(repository, deployer, creationBytes, fundingAmount); - System.out.println("AT deployed successfully."); + // Deploy an AT so that we have AT state data + System.out.println("Deploying AT..."); + PrivateKeyAccount deployer = Common.getTestAccount(repository, "alice"); + byte[] creationBytes = AtUtils.buildSimpleAT(); + long fundingAmount = 1_00000000L; + AtUtils.doDeployAT(repository, deployer, creationBytes, fundingAmount); + System.out.println("AT deployed successfully."); - // Mint some blocks so that we are able to archive them later - System.out.println("Minting 1000 blocks..."); - for (int i = 0; i < 1000; i++) { - BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); - // Log every 100 blocks - if ((i + 1) % 100 == 0) { - System.out.println("Minted block " + (i + 1)); - } - } - System.out.println("Finished minting blocks."); + // Mint some blocks so that we are able to archive them later + System.out.println("Minting 1000 blocks..."); + for (int i = 0; i < 1000; i++) { + BlockMinter.mintTestingBlock(repository, Common.getTestAccount(repository, "alice-reward-share")); + // Log every 100 blocks + if ((i + 1) % 100 == 0) { + System.out.println("Minted block " + (i + 1)); + } + } + System.out.println("Finished minting blocks."); - // Make sure that block 500 has full AT state data and data hash - System.out.println("Verifying block 500 AT state data..."); - List block500AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(500); - ATStateData atStatesData = repository.getATRepository().getATStateAtHeight(block500AtStatesData.get(0).getATAddress(), 500); - assertNotNull(atStatesData.getStateHash()); - assertNotNull(atStatesData.getStateData()); - System.out.println("Block 500 AT state data verified."); + // Make sure that block 500 has full AT state data and data hash + System.out.println("Verifying block 500 AT state data..."); + List block500AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(500); + ATStateData atStatesData = repository.getATRepository().getATStateAtHeight(block500AtStatesData.get(0).getATAddress(), 500); + assertNotNull(atStatesData.getStateHash()); + assertNotNull(atStatesData.getStateData()); + System.out.println("Block 500 AT state data verified."); - // Trim the first 500 blocks - System.out.println("Trimming first 500 blocks..."); - repository.getBlockRepository().trimOldOnlineAccountsSignatures(0, 500); - repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(501); - repository.getATRepository().rebuildLatestAtStates(500); - repository.getATRepository().trimAtStates(0, 500, 1000); - repository.getATRepository().setAtTrimHeight(501); - System.out.println("Trimming completed."); + // Trim the first 500 blocks + System.out.println("Trimming first 500 blocks..."); + repository.getBlockRepository().trimOldOnlineAccountsSignatures(0, 500); + repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(501); + repository.getATRepository().rebuildLatestAtStates(500); + repository.getATRepository().trimAtStates(0, 500, 1000); + repository.getATRepository().setAtTrimHeight(501); + System.out.println("Trimming completed."); - // Now block 499 should only have the AT state data hash - System.out.println("Checking block 499 AT state data..."); - List block499AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(499); - atStatesData = repository.getATRepository().getATStateAtHeight(block499AtStatesData.get(0).getATAddress(), 499); - assertNotNull(atStatesData.getStateHash()); - assertNull(atStatesData.getStateData()); - System.out.println("Block 499 AT state data contains only state hash as expected."); + // Now block 499 should only have the AT state data hash + System.out.println("Checking block 499 AT state data..."); + List block499AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(499); + atStatesData = repository.getATRepository().getATStateAtHeight(block499AtStatesData.get(0).getATAddress(), 499); + assertNotNull(atStatesData.getStateHash()); + assertNull(atStatesData.getStateData()); + System.out.println("Block 499 AT state data contains only state hash as expected."); - // ... but block 500 should have the full data (due to being retained as the "latest" AT state in the trimmed range - System.out.println("Verifying block 500 AT state data again..."); - block500AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(500); - atStatesData = repository.getATRepository().getATStateAtHeight(block500AtStatesData.get(0).getATAddress(), 500); - assertNotNull(atStatesData.getStateHash()); - assertNotNull(atStatesData.getStateData()); - System.out.println("Block 500 AT state data contains full data."); + // ... but block 500 should have the full data (due to being retained as the "latest" AT state in the trimmed range + System.out.println("Verifying block 500 AT state data again..."); + block500AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(500); + atStatesData = repository.getATRepository().getATStateAtHeight(block500AtStatesData.get(0).getATAddress(), 500); + assertNotNull(atStatesData.getStateHash()); + assertNotNull(atStatesData.getStateData()); + System.out.println("Block 500 AT state data contains full data."); - // ... and block 501 should also have the full data - System.out.println("Verifying block 501 AT state data..."); - List block501AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(501); - atStatesData = repository.getATRepository().getATStateAtHeight(block501AtStatesData.get(0).getATAddress(), 501); - assertNotNull(atStatesData.getStateHash()); - assertNotNull(atStatesData.getStateData()); - System.out.println("Block 501 AT state data contains full data."); + // ... and block 501 should also have the full data + System.out.println("Verifying block 501 AT state data..."); + List block501AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(501); + atStatesData = repository.getATRepository().getATStateAtHeight(block501AtStatesData.get(0).getATAddress(), 501); + assertNotNull(atStatesData.getStateHash()); + assertNotNull(atStatesData.getStateData()); + System.out.println("Block 501 AT state data contains full data."); - // Check the max archive height - this should be one less than the first untrimmed height - final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); - System.out.println("Maximum archive height determined (Expected 500): " + maximumArchiveHeight); - assertEquals(500, maximumArchiveHeight); + // Check the max archive height - this should be one less than the first untrimmed height + final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository); + System.out.println("Maximum archive height determined (Expected 500): " + maximumArchiveHeight); + assertEquals(500, maximumArchiveHeight); - BlockData block3DataPreArchive = repository.getBlockRepository().fromHeight(3); + BlockData block3DataPreArchive = repository.getBlockRepository().fromHeight(3); - // Write blocks 2-500 to the archive - System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); - BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); - writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes - BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); - System.out.println("Finished writing blocks to archive. Result: " + result); - assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); + // Write blocks 2-500 to the archive + System.out.println("Writing blocks 2 to " + maximumArchiveHeight + " to the archive..."); + BlockArchiveWriter writer = new BlockArchiveWriter(0, maximumArchiveHeight, repository); + writer.setShouldEnforceFileSizeTarget(false); // To avoid the need to pre-calculate file sizes + BlockArchiveWriter.BlockArchiveWriteResult result = writer.write(); + System.out.println("Finished writing blocks to archive. Result: " + result); + assertEquals(BlockArchiveWriter.BlockArchiveWriteResult.OK, result); - // Make sure that the archive contains the correct number of blocks - System.out.println("Number of blocks written to archive (Expected 499): " + writer.getWrittenCount()); - assertEquals(500 - 1, writer.getWrittenCount()); // -1 for the genesis block + // Make sure that the archive contains the correct number of blocks + System.out.println("Number of blocks written to archive (Expected 499): " + writer.getWrittenCount()); + assertEquals(500 - 1, writer.getWrittenCount()); // -1 for the genesis block - // Increment block archive height - repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); - repository.saveChanges(); - assertEquals(500 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); - System.out.println("Block archive height updated to: " + (500 - 1)); + // Increment block archive height + repository.getBlockArchiveRepository().setBlockArchiveHeight(writer.getWrittenCount()); + repository.saveChanges(); + assertEquals(500 - 1, repository.getBlockArchiveRepository().getBlockArchiveHeight()); + System.out.println("Block archive height updated to: " + (500 - 1)); - // Ensure the file exists - File outputFile = writer.getOutputPath().toFile(); - assertTrue(outputFile.exists()); - System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); + // Ensure the file exists + File outputFile = writer.getOutputPath().toFile(); + assertTrue(outputFile.exists()); + System.out.println("Archive file exists at: " + outputFile.getAbsolutePath()); - // Ensure the SQL repository contains blocks 2 and 500... - System.out.println("Verifying that blocks 2 and 500 exist in the repository..."); - assertNotNull(repository.getBlockRepository().fromHeight(2)); - assertNotNull(repository.getBlockRepository().fromHeight(500)); - System.out.println("Blocks 2 and 500 are present in the repository."); + // Ensure the SQL repository contains blocks 2 and 500... + System.out.println("Verifying that blocks 2 and 500 exist in the repository..."); + assertNotNull(repository.getBlockRepository().fromHeight(2)); + assertNotNull(repository.getBlockRepository().fromHeight(500)); + System.out.println("Blocks 2 and 500 are present in the repository."); - // Prune all the archived blocks - System.out.println("Pruning blocks 2 to 500..."); - int numBlocksPruned = repository.getBlockRepository().pruneBlocks(0, 500); - System.out.println("Number of blocks pruned (Expected 499): " + numBlocksPruned); - assertEquals(500-1, numBlocksPruned); - repository.getBlockRepository().setBlockPruneHeight(501); + // Prune all the archived blocks + System.out.println("Pruning blocks 2 to 500..."); + int numBlocksPruned = repository.getBlockRepository().pruneBlocks(0, 500); + System.out.println("Number of blocks pruned (Expected 499): " + numBlocksPruned); + assertEquals(500-1, numBlocksPruned); + repository.getBlockRepository().setBlockPruneHeight(501); - // Prune the AT states for the archived blocks - System.out.println("Pruning AT states up to height 500..."); - repository.getATRepository().rebuildLatestAtStates(500); - repository.saveChanges(); - int numATStatesPruned = repository.getATRepository().pruneAtStates(2, 500); - System.out.println("Number of AT states pruned (Expected 498): " + numATStatesPruned); - assertEquals(498, numATStatesPruned); // Minus 1 for genesis block, and another for the latest AT state - repository.getATRepository().setAtPruneHeight(501); + // Prune the AT states for the archived blocks + System.out.println("Pruning AT states up to height 500..."); + repository.getATRepository().rebuildLatestAtStates(500); + repository.saveChanges(); + int numATStatesPruned = repository.getATRepository().pruneAtStates(2, 500); + System.out.println("Number of AT states pruned (Expected 498): " + numATStatesPruned); + assertEquals(498, numATStatesPruned); // Minus 1 for genesis block, and another for the latest AT state + repository.getATRepository().setAtPruneHeight(501); - // Now ensure the SQL repository is missing blocks 2 and 500... - System.out.println("Verifying that blocks 2 and 500 have been pruned..."); - assertNull(repository.getBlockRepository().fromHeight(2)); - assertNull(repository.getBlockRepository().fromHeight(500)); - System.out.println("Blocks 2 and 500 have been successfully pruned."); + // Now ensure the SQL repository is missing blocks 2 and 500... + System.out.println("Verifying that blocks 2 and 500 have been pruned..."); + assertNull(repository.getBlockRepository().fromHeight(2)); + assertNull(repository.getBlockRepository().fromHeight(500)); + System.out.println("Blocks 2 and 500 have been successfully pruned."); - // ... but it's not missing blocks 1 and 501 (we don't prune the genesis block) - System.out.println("Verifying that blocks 1 and 501 still exist..."); - assertNotNull(repository.getBlockRepository().fromHeight(1)); - assertNotNull(repository.getBlockRepository().fromHeight(501)); - System.out.println("Blocks 1 and 501 are present in the repository."); + // ... but it's not missing blocks 1 and 501 (we don't prune the genesis block) + System.out.println("Verifying that blocks 1 and 501 still exist..."); + assertNotNull(repository.getBlockRepository().fromHeight(1)); + assertNotNull(repository.getBlockRepository().fromHeight(501)); + System.out.println("Blocks 1 and 501 are present in the repository."); - // Validate the latest block height in the repository - int lastBlockHeight = repository.getBlockRepository().getLastBlock().getHeight(); - System.out.println("Latest block height in repository (Expected 1002): " + lastBlockHeight); - assertEquals(1002, lastBlockHeight); + // Validate the latest block height in the repository + int lastBlockHeight = repository.getBlockRepository().getLastBlock().getHeight(); + System.out.println("Latest block height in repository (Expected 1002): " + lastBlockHeight); + assertEquals(1002, lastBlockHeight); - // Now orphan some unarchived blocks. - System.out.println("Orphaning 500 blocks..."); - BlockUtils.orphanBlocks(repository, 500); - int currentLastBlockHeight = repository.getBlockRepository().getLastBlock().getHeight(); - System.out.println("New last block height after orphaning (Expected 502): " + currentLastBlockHeight); - assertEquals(502, currentLastBlockHeight); + // Now orphan some unarchived blocks. + System.out.println("Orphaning 500 blocks..."); + BlockUtils.orphanBlocks(repository, 500); + int currentLastBlockHeight = repository.getBlockRepository().getLastBlock().getHeight(); + System.out.println("New last block height after orphaning (Expected 502): " + currentLastBlockHeight); + assertEquals(502, currentLastBlockHeight); - // We're close to the lower limit of the SQL database now, so - // we need to import some blocks from the archive - System.out.println("Importing blocks 401 to 500 from the archive..."); - BlockArchiveUtils.importFromArchive(401, 500, repository); + // We're close to the lower limit of the SQL database now, so + // we need to import some blocks from the archive + System.out.println("Importing blocks 401 to 500 from the archive..."); + BlockArchiveUtils.importFromArchive(401, 500, repository); - // Ensure the SQL repository now contains block 401 but not 400... - System.out.println("Verifying that block 401 exists and block 400 does not..."); - assertNotNull(repository.getBlockRepository().fromHeight(401)); - assertNull(repository.getBlockRepository().fromHeight(400)); - System.out.println("Block 401 exists, block 400 does not."); + // Ensure the SQL repository now contains block 401 but not 400... + System.out.println("Verifying that block 401 exists and block 400 does not..."); + assertNotNull(repository.getBlockRepository().fromHeight(401)); + assertNull(repository.getBlockRepository().fromHeight(400)); + System.out.println("Block 401 exists, block 400 does not."); - // Import the remaining 399 blocks - System.out.println("Importing blocks 2 to 400 from the archive..."); - BlockArchiveUtils.importFromArchive(2, 400, repository); + // Import the remaining 399 blocks + System.out.println("Importing blocks 2 to 400 from the archive..."); + BlockArchiveUtils.importFromArchive(2, 400, repository); - // Verify that block 3 matches the original - System.out.println("Verifying that block 3 matches the original data..."); - BlockData block3DataPostArchive = repository.getBlockRepository().fromHeight(3); - assertArrayEquals(block3DataPreArchive.getSignature(), block3DataPostArchive.getSignature()); - assertEquals(block3DataPreArchive.getHeight(), block3DataPostArchive.getHeight()); - System.out.println("Block 3 data matches the original."); + // Verify that block 3 matches the original + System.out.println("Verifying that block 3 matches the original data..."); + BlockData block3DataPostArchive = repository.getBlockRepository().fromHeight(3); + assertArrayEquals(block3DataPreArchive.getSignature(), block3DataPostArchive.getSignature()); + assertEquals(block3DataPreArchive.getHeight(), block3DataPostArchive.getHeight()); + System.out.println("Block 3 data matches the original."); - // Orphan 2 more block, which should be the last one that is possible to be orphaned + // Orphan 2 more block, which should be the last one that is possible to be orphaned // TODO: figure out why this is 1 block more than in the equivalent block archive V1 test - System.out.println("Orphaning 2 more blocks..."); - BlockUtils.orphanBlocks(repository, 2); - System.out.println("Orphaned 2 blocks successfully."); + System.out.println("Orphaning 2 more blocks..."); + BlockUtils.orphanBlocks(repository, 2); + System.out.println("Orphaned 2 blocks successfully."); - // Orphan another block, which should fail - System.out.println("Attempting to orphan another block, which should fail..."); - Exception exception = null; - try { - BlockUtils.orphanBlocks(repository, 1); - } catch (DataException e) { - exception = e; - System.out.println("Caught expected DataException: " + e.getMessage()); - } + // Orphan another block, which should fail + System.out.println("Attempting to orphan another block, which should fail..."); + Exception exception = null; + try { + BlockUtils.orphanBlocks(repository, 1); + } catch (DataException e) { + exception = e; + System.out.println("Caught expected DataException: " + e.getMessage()); + } - // Ensure that a DataException is thrown because there is no more AT states data available - assertNotNull(exception); - assertEquals(DataException.class, exception.getClass()); - System.out.println("DataException confirmed due to lack of AT states data."); + // Ensure that a DataException is thrown because there is no more AT states data available + assertNotNull(exception); + assertEquals(DataException.class, exception.getClass()); + System.out.println("DataException confirmed due to lack of AT states data."); - // FUTURE: we may be able to retain unique AT states when trimming, to avoid this exception - // and allow orphaning back through blocks with trimmed AT states. + // FUTURE: we may be able to retain unique AT states when trimming, to avoid this exception + // and allow orphaning back through blocks with trimmed AT states. - System.out.println("testTrimArchivePruneAndOrphan completed successfully."); - } - } + System.out.println("testTrimArchivePruneAndOrphan completed successfully."); + } + } - /** - * Many nodes are missing an ATStatesHeightIndex due to an earlier bug - * In these cases we disable archiving and pruning as this index is a - * very essential component in these processes. - */ - @Test - public void testMissingAtStatesHeightIndex() throws DataException, SQLException { - try (final HSQLDBRepository repository = (HSQLDBRepository) RepositoryManager.getRepository()) { + /** + * Many nodes are missing an ATStatesHeightIndex due to an earlier bug + * In these cases we disable archiving and pruning as this index is a + * very essential component in these processes. + */ + @Test + public void testMissingAtStatesHeightIndex() throws DataException, SQLException { + try (final HSQLDBRepository repository = (HSQLDBRepository) RepositoryManager.getRepository()) { - System.out.println("Starting testMissingAtStatesHeightIndex"); + System.out.println("Starting testMissingAtStatesHeightIndex"); - // Firstly check that we're able to prune or archive when the index exists - System.out.println("Checking existence of ATStatesHeightIndex..."); - assertTrue(repository.getATRepository().hasAtStatesHeightIndex()); - assertTrue(RepositoryManager.canArchiveOrPrune()); - System.out.println("ATStatesHeightIndex exists. Archiving and pruning are possible."); + // Firstly check that we're able to prune or archive when the index exists + System.out.println("Checking existence of ATStatesHeightIndex..."); + assertTrue(repository.getATRepository().hasAtStatesHeightIndex()); + assertTrue(RepositoryManager.canArchiveOrPrune()); + System.out.println("ATStatesHeightIndex exists. Archiving and pruning are possible."); - // Delete the index - System.out.println("Dropping ATStatesHeightIndex..."); - repository.prepareStatement("DROP INDEX ATSTATESHEIGHTINDEX").execute(); - System.out.println("ATStatesHeightIndex dropped."); + // Delete the index + System.out.println("Dropping ATStatesHeightIndex..."); + repository.prepareStatement("DROP INDEX ATSTATESHEIGHTINDEX").execute(); + System.out.println("ATStatesHeightIndex dropped."); - // Ensure check that we're unable to prune or archive when the index doesn't exist - System.out.println("Verifying that ATStatesHeightIndex no longer exists..."); - assertFalse(repository.getATRepository().hasAtStatesHeightIndex()); - assertFalse(RepositoryManager.canArchiveOrPrune()); - System.out.println("ATStatesHeightIndex does not exist. Archiving and pruning are disabled."); + // Ensure check that we're unable to prune or archive when the index doesn't exist + System.out.println("Verifying that ATStatesHeightIndex no longer exists..."); + assertFalse(repository.getATRepository().hasAtStatesHeightIndex()); + assertFalse(RepositoryManager.canArchiveOrPrune()); + System.out.println("ATStatesHeightIndex does not exist. Archiving and pruning are disabled."); - System.out.println("testMissingAtStatesHeightIndex completed successfully."); - } - } + System.out.println("testMissingAtStatesHeightIndex completed successfully."); + } + } - private void deleteArchiveDirectory() { - // Delete archive directory if exists - Path archivePath = Paths.get(Settings.getInstance().getRepositoryPath(), "archive").toAbsolutePath(); - try { - FileUtils.deleteDirectory(archivePath.toFile()); - System.out.println("Deleted archive directory at: " + archivePath); - } catch (IOException e) { + private void deleteArchiveDirectory() { + // Delete archive directory if exists + Path archivePath = Paths.get(Settings.getInstance().getRepositoryPath(), "archive").toAbsolutePath(); + try { + FileUtils.deleteDirectory(archivePath.toFile()); + System.out.println("Deleted archive directory at: " + archivePath); + } catch (IOException e) { - System.out.println("Failed to delete archive directory: " + e.getMessage()); - } - } + System.out.println("Failed to delete archive directory: " + e.getMessage()); + } + } } From 652c9026072339b0908ecbdd181ff43dbbe70586 Mon Sep 17 00:00:00 2001 From: QuickMythril Date: Sun, 17 Nov 2024 16:45:39 -0500 Subject: [PATCH 4/9] Add missing feature triggers to unit tests --- src/test/resources/test-chain-v2-block-timestamps.json | 8 +++++--- src/test/resources/test-chain-v2-disable-reference.json | 8 +++++--- src/test/resources/test-chain-v2-founder-rewards.json | 8 +++++--- src/test/resources/test-chain-v2-leftover-reward.json | 8 +++++--- src/test/resources/test-chain-v2-minting.json | 8 +++++--- src/test/resources/test-chain-v2-penalty-fix.json | 6 ++++-- .../resources/test-chain-v2-qora-holder-extremes.json | 8 +++++--- .../resources/test-chain-v2-qora-holder-reduction.json | 8 +++++--- src/test/resources/test-chain-v2-qora-holder.json | 8 +++++--- src/test/resources/test-chain-v2-reward-levels.json | 8 +++++--- src/test/resources/test-chain-v2-reward-scaling.json | 8 +++++--- src/test/resources/test-chain-v2-reward-shares.json | 8 +++++--- .../resources/test-chain-v2-self-sponsorship-algo-v1.json | 8 +++++--- .../resources/test-chain-v2-self-sponsorship-algo-v2.json | 8 +++++--- .../resources/test-chain-v2-self-sponsorship-algo-v3.json | 8 +++++--- src/test/resources/test-chain-v2.json | 8 +++++--- 16 files changed, 79 insertions(+), 47 deletions(-) diff --git a/src/test/resources/test-chain-v2-block-timestamps.json b/src/test/resources/test-chain-v2-block-timestamps.json index b2f0119d..4e49e86d 100644 --- a/src/test/resources/test-chain-v2-block-timestamps.json +++ b/src/test/resources/test-chain-v2-block-timestamps.json @@ -81,7 +81,7 @@ "transactionV5Timestamp": 0, "transactionV6Timestamp": 9999999999999, "disableReferenceTimestamp": 9999999999999, - "increaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "increaseOnlineAccountsDifficultyTimestamp": 9999999999990, "onlineAccountMinterLevelValidationHeight": 0, "selfSponsorshipAlgoV1Height": 999999999, "selfSponsorshipAlgoV2Height": 999999999, @@ -95,8 +95,10 @@ "cancelSellNameValidationTimestamp": 9999999999999, "disableRewardshareHeight": 9999999999990, "enableRewardshareHeight": 9999999999999, - "onlyMintWithNameHeight": 9999999999999, - "groupMemberCheckHeight": 9999999999999 + "onlyMintWithNameHeight": 9999999999990, + "groupMemberCheckHeight": 9999999999999, + "decreaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "removeOnlyMintWithNameHeight": 9999999999999 }, "genesisInfo": { "version": 4, diff --git a/src/test/resources/test-chain-v2-disable-reference.json b/src/test/resources/test-chain-v2-disable-reference.json index 86ed264f..9ad59d79 100644 --- a/src/test/resources/test-chain-v2-disable-reference.json +++ b/src/test/resources/test-chain-v2-disable-reference.json @@ -84,7 +84,7 @@ "transactionV5Timestamp": 0, "transactionV6Timestamp": 0, "disableReferenceTimestamp": 0, - "increaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "increaseOnlineAccountsDifficultyTimestamp": 9999999999990, "onlineAccountMinterLevelValidationHeight": 0, "selfSponsorshipAlgoV1Height": 999999999, "selfSponsorshipAlgoV2Height": 999999999, @@ -98,8 +98,10 @@ "cancelSellNameValidationTimestamp": 9999999999999, "disableRewardshareHeight": 9999999999990, "enableRewardshareHeight": 9999999999999, - "onlyMintWithNameHeight": 9999999999999, - "groupMemberCheckHeight": 9999999999999 + "onlyMintWithNameHeight": 9999999999990, + "groupMemberCheckHeight": 9999999999999, + "decreaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "removeOnlyMintWithNameHeight": 9999999999999 }, "genesisInfo": { "version": 4, diff --git a/src/test/resources/test-chain-v2-founder-rewards.json b/src/test/resources/test-chain-v2-founder-rewards.json index d1b9c3c4..e4182d7d 100644 --- a/src/test/resources/test-chain-v2-founder-rewards.json +++ b/src/test/resources/test-chain-v2-founder-rewards.json @@ -85,7 +85,7 @@ "transactionV5Timestamp": 0, "transactionV6Timestamp": 0, "disableReferenceTimestamp": 9999999999999, - "increaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "increaseOnlineAccountsDifficultyTimestamp": 9999999999990, "onlineAccountMinterLevelValidationHeight": 0, "selfSponsorshipAlgoV1Height": 999999999, "selfSponsorshipAlgoV2Height": 999999999, @@ -99,8 +99,10 @@ "cancelSellNameValidationTimestamp": 9999999999999, "disableRewardshareHeight": 9999999999990, "enableRewardshareHeight": 9999999999999, - "onlyMintWithNameHeight": 9999999999999, - "groupMemberCheckHeight": 9999999999999 + "onlyMintWithNameHeight": 9999999999990, + "groupMemberCheckHeight": 9999999999999, + "decreaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "removeOnlyMintWithNameHeight": 9999999999999 }, "genesisInfo": { "version": 4, diff --git a/src/test/resources/test-chain-v2-leftover-reward.json b/src/test/resources/test-chain-v2-leftover-reward.json index 106ac7dd..04005b2b 100644 --- a/src/test/resources/test-chain-v2-leftover-reward.json +++ b/src/test/resources/test-chain-v2-leftover-reward.json @@ -85,7 +85,7 @@ "transactionV5Timestamp": 0, "transactionV6Timestamp": 0, "disableReferenceTimestamp": 9999999999999, - "increaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "increaseOnlineAccountsDifficultyTimestamp": 9999999999990, "onlineAccountMinterLevelValidationHeight": 0, "selfSponsorshipAlgoV1Height": 999999999, "selfSponsorshipAlgoV2Height": 999999999, @@ -99,8 +99,10 @@ "cancelSellNameValidationTimestamp": 9999999999999, "disableRewardshareHeight": 9999999999990, "enableRewardshareHeight": 9999999999999, - "onlyMintWithNameHeight": 9999999999999, - "groupMemberCheckHeight": 9999999999999 + "onlyMintWithNameHeight": 9999999999990, + "groupMemberCheckHeight": 9999999999999, + "decreaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "removeOnlyMintWithNameHeight": 9999999999999 }, "genesisInfo": { "version": 4, diff --git a/src/test/resources/test-chain-v2-minting.json b/src/test/resources/test-chain-v2-minting.json index 159b2dd7..ddb29ca5 100644 --- a/src/test/resources/test-chain-v2-minting.json +++ b/src/test/resources/test-chain-v2-minting.json @@ -85,7 +85,7 @@ "transactionV5Timestamp": 0, "transactionV6Timestamp": 0, "disableReferenceTimestamp": 0, - "increaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "increaseOnlineAccountsDifficultyTimestamp": 9999999999990, "onlineAccountMinterLevelValidationHeight": 0, "selfSponsorshipAlgoV1Height": 999999999, "selfSponsorshipAlgoV2Height": 999999999, @@ -99,8 +99,10 @@ "cancelSellNameValidationTimestamp": 9999999999999, "disableRewardshareHeight": 9999999999990, "enableRewardshareHeight": 9999999999999, - "onlyMintWithNameHeight": 9999999999999, - "groupMemberCheckHeight": 9999999999999 + "onlyMintWithNameHeight": 9999999999990, + "groupMemberCheckHeight": 9999999999999, + "decreaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "removeOnlyMintWithNameHeight": 9999999999999 }, "genesisInfo": { "version": 4, diff --git a/src/test/resources/test-chain-v2-penalty-fix.json b/src/test/resources/test-chain-v2-penalty-fix.json index 2266b032..cac92c16 100644 --- a/src/test/resources/test-chain-v2-penalty-fix.json +++ b/src/test/resources/test-chain-v2-penalty-fix.json @@ -83,7 +83,7 @@ "transactionV5Timestamp": 0, "transactionV6Timestamp": 0, "disableReferenceTimestamp": 9999999999999, - "increaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "increaseOnlineAccountsDifficultyTimestamp": 9999999999990, "onlineAccountMinterLevelValidationHeight": 0, "selfSponsorshipAlgoV1Height": 999999999, "selfSponsorshipAlgoV2Height": 999999999, @@ -97,8 +97,10 @@ "cancelSellNameValidationTimestamp": 9999999999999, "disableRewardshareHeight": 9999999999990, "enableRewardshareHeight": 9999999999999, - "onlyMintWithNameHeight": 9999999999999, + "onlyMintWithNameHeight": 9999999999990, "groupMemberCheckHeight": 9999999999999, + "decreaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "removeOnlyMintWithNameHeight": 9999999999999, "penaltyFixHeight": 5 }, "genesisInfo": { diff --git a/src/test/resources/test-chain-v2-qora-holder-extremes.json b/src/test/resources/test-chain-v2-qora-holder-extremes.json index 6043f15c..566d8515 100644 --- a/src/test/resources/test-chain-v2-qora-holder-extremes.json +++ b/src/test/resources/test-chain-v2-qora-holder-extremes.json @@ -85,7 +85,7 @@ "transactionV5Timestamp": 0, "transactionV6Timestamp": 0, "disableReferenceTimestamp": 9999999999999, - "increaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "increaseOnlineAccountsDifficultyTimestamp": 9999999999990, "onlineAccountMinterLevelValidationHeight": 0, "selfSponsorshipAlgoV1Height": 999999999, "selfSponsorshipAlgoV2Height": 999999999, @@ -99,8 +99,10 @@ "cancelSellNameValidationTimestamp": 9999999999999, "disableRewardshareHeight": 9999999999990, "enableRewardshareHeight": 9999999999999, - "onlyMintWithNameHeight": 9999999999999, - "groupMemberCheckHeight": 9999999999999 + "onlyMintWithNameHeight": 9999999999990, + "groupMemberCheckHeight": 9999999999999, + "decreaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "removeOnlyMintWithNameHeight": 9999999999999 }, "genesisInfo": { "version": 4, diff --git a/src/test/resources/test-chain-v2-qora-holder-reduction.json b/src/test/resources/test-chain-v2-qora-holder-reduction.json index 7727a283..c7ed2270 100644 --- a/src/test/resources/test-chain-v2-qora-holder-reduction.json +++ b/src/test/resources/test-chain-v2-qora-holder-reduction.json @@ -86,7 +86,7 @@ "transactionV6Timestamp": 0, "disableReferenceTimestamp": 9999999999999, "aggregateSignatureTimestamp": 0, - "increaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "increaseOnlineAccountsDifficultyTimestamp": 9999999999990, "onlineAccountMinterLevelValidationHeight": 0, "selfSponsorshipAlgoV1Height": 999999999, "selfSponsorshipAlgoV2Height": 999999999, @@ -100,8 +100,10 @@ "cancelSellNameValidationTimestamp": 9999999999999, "disableRewardshareHeight": 9999999999990, "enableRewardshareHeight": 9999999999999, - "onlyMintWithNameHeight": 9999999999999, - "groupMemberCheckHeight": 9999999999999 + "onlyMintWithNameHeight": 9999999999990, + "groupMemberCheckHeight": 9999999999999, + "decreaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "removeOnlyMintWithNameHeight": 9999999999999 }, "genesisInfo": { "version": 4, diff --git a/src/test/resources/test-chain-v2-qora-holder.json b/src/test/resources/test-chain-v2-qora-holder.json index 6b9f9d54..1c4f0d93 100644 --- a/src/test/resources/test-chain-v2-qora-holder.json +++ b/src/test/resources/test-chain-v2-qora-holder.json @@ -85,7 +85,7 @@ "transactionV5Timestamp": 0, "transactionV6Timestamp": 0, "disableReferenceTimestamp": 9999999999999, - "increaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "increaseOnlineAccountsDifficultyTimestamp": 9999999999990, "onlineAccountMinterLevelValidationHeight": 0, "selfSponsorshipAlgoV1Height": 999999999, "selfSponsorshipAlgoV2Height": 999999999, @@ -99,8 +99,10 @@ "cancelSellNameValidationTimestamp": 9999999999999, "disableRewardshareHeight": 9999999999990, "enableRewardshareHeight": 9999999999999, - "onlyMintWithNameHeight": 9999999999999, - "groupMemberCheckHeight": 9999999999999 + "onlyMintWithNameHeight": 9999999999990, + "groupMemberCheckHeight": 9999999999999, + "decreaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "removeOnlyMintWithNameHeight": 9999999999999 }, "genesisInfo": { "version": 4, diff --git a/src/test/resources/test-chain-v2-reward-levels.json b/src/test/resources/test-chain-v2-reward-levels.json index 6f0993d0..30d952e1 100644 --- a/src/test/resources/test-chain-v2-reward-levels.json +++ b/src/test/resources/test-chain-v2-reward-levels.json @@ -85,7 +85,7 @@ "transactionV5Timestamp": 0, "transactionV6Timestamp": 0, "disableReferenceTimestamp": 9999999999999, - "increaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "increaseOnlineAccountsDifficultyTimestamp": 9999999999990, "onlineAccountMinterLevelValidationHeight": 0, "selfSponsorshipAlgoV1Height": 999999999, "selfSponsorshipAlgoV2Height": 999999999, @@ -99,8 +99,10 @@ "cancelSellNameValidationTimestamp": 9999999999999, "disableRewardshareHeight": 9999999999990, "enableRewardshareHeight": 9999999999999, - "onlyMintWithNameHeight": 9999999999999, - "groupMemberCheckHeight": 9999999999999 + "onlyMintWithNameHeight": 9999999999990, + "groupMemberCheckHeight": 9999999999999, + "decreaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "removeOnlyMintWithNameHeight": 9999999999999 }, "genesisInfo": { "version": 4, diff --git a/src/test/resources/test-chain-v2-reward-scaling.json b/src/test/resources/test-chain-v2-reward-scaling.json index d1d4519a..612f02a5 100644 --- a/src/test/resources/test-chain-v2-reward-scaling.json +++ b/src/test/resources/test-chain-v2-reward-scaling.json @@ -85,7 +85,7 @@ "transactionV5Timestamp": 0, "transactionV6Timestamp": 0, "disableReferenceTimestamp": 9999999999999, - "increaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "increaseOnlineAccountsDifficultyTimestamp": 9999999999990, "onlineAccountMinterLevelValidationHeight": 0, "selfSponsorshipAlgoV1Height": 999999999, "selfSponsorshipAlgoV2Height": 999999999, @@ -99,8 +99,10 @@ "cancelSellNameValidationTimestamp": 9999999999999, "disableRewardshareHeight": 9999999999990, "enableRewardshareHeight": 9999999999999, - "onlyMintWithNameHeight": 9999999999999, - "groupMemberCheckHeight": 9999999999999 + "onlyMintWithNameHeight": 9999999999990, + "groupMemberCheckHeight": 9999999999999, + "decreaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "removeOnlyMintWithNameHeight": 9999999999999 }, "genesisInfo": { "version": 4, diff --git a/src/test/resources/test-chain-v2-reward-shares.json b/src/test/resources/test-chain-v2-reward-shares.json index 69edc540..2f332233 100644 --- a/src/test/resources/test-chain-v2-reward-shares.json +++ b/src/test/resources/test-chain-v2-reward-shares.json @@ -85,7 +85,7 @@ "transactionV5Timestamp": 0, "transactionV6Timestamp": 0, "disableReferenceTimestamp": 9999999999999, - "increaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "increaseOnlineAccountsDifficultyTimestamp": 9999999999990, "onlineAccountMinterLevelValidationHeight": 0, "selfSponsorshipAlgoV1Height": 999999999, "selfSponsorshipAlgoV2Height": 999999999, @@ -99,8 +99,10 @@ "cancelSellNameValidationTimestamp": 9999999999999, "disableRewardshareHeight": 9999999999990, "enableRewardshareHeight": 9999999999999, - "onlyMintWithNameHeight": 9999999999999, - "groupMemberCheckHeight": 9999999999999 + "onlyMintWithNameHeight": 9999999999990, + "groupMemberCheckHeight": 9999999999999, + "decreaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "removeOnlyMintWithNameHeight": 9999999999999 }, "genesisInfo": { "version": 4, diff --git a/src/test/resources/test-chain-v2-self-sponsorship-algo-v1.json b/src/test/resources/test-chain-v2-self-sponsorship-algo-v1.json index ad2ad4b1..3ea8bc70 100644 --- a/src/test/resources/test-chain-v2-self-sponsorship-algo-v1.json +++ b/src/test/resources/test-chain-v2-self-sponsorship-algo-v1.json @@ -85,7 +85,7 @@ "transactionV5Timestamp": 0, "transactionV6Timestamp": 0, "disableReferenceTimestamp": 9999999999999, - "increaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "increaseOnlineAccountsDifficultyTimestamp": 9999999999990, "onlineAccountMinterLevelValidationHeight": 0, "selfSponsorshipAlgoV1Height": 20, "selfSponsorshipAlgoV2Height": 999999999, @@ -99,8 +99,10 @@ "cancelSellNameValidationTimestamp": 9999999999999, "disableRewardshareHeight": 9999999999990, "enableRewardshareHeight": 9999999999999, - "onlyMintWithNameHeight": 9999999999999, - "groupMemberCheckHeight": 9999999999999 + "onlyMintWithNameHeight": 9999999999990, + "groupMemberCheckHeight": 9999999999999, + "decreaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "removeOnlyMintWithNameHeight": 9999999999999 }, "genesisInfo": { "version": 4, diff --git a/src/test/resources/test-chain-v2-self-sponsorship-algo-v2.json b/src/test/resources/test-chain-v2-self-sponsorship-algo-v2.json index b2812c05..ae424704 100644 --- a/src/test/resources/test-chain-v2-self-sponsorship-algo-v2.json +++ b/src/test/resources/test-chain-v2-self-sponsorship-algo-v2.json @@ -85,7 +85,7 @@ "transactionV5Timestamp": 0, "transactionV6Timestamp": 0, "disableReferenceTimestamp": 9999999999999, - "increaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "increaseOnlineAccountsDifficultyTimestamp": 9999999999990, "onlineAccountMinterLevelValidationHeight": 0, "selfSponsorshipAlgoV1Height": 999999999, "selfSponsorshipAlgoV2Height": 30, @@ -99,8 +99,10 @@ "cancelSellNameValidationTimestamp": 9999999999999, "disableRewardshareHeight": 9999999999990, "enableRewardshareHeight": 9999999999999, - "onlyMintWithNameHeight": 9999999999999, - "groupMemberCheckHeight": 9999999999999 + "onlyMintWithNameHeight": 9999999999990, + "groupMemberCheckHeight": 9999999999999, + "decreaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "removeOnlyMintWithNameHeight": 9999999999999 }, "genesisInfo": { "version": 4, diff --git a/src/test/resources/test-chain-v2-self-sponsorship-algo-v3.json b/src/test/resources/test-chain-v2-self-sponsorship-algo-v3.json index d65aa48e..2a24473b 100644 --- a/src/test/resources/test-chain-v2-self-sponsorship-algo-v3.json +++ b/src/test/resources/test-chain-v2-self-sponsorship-algo-v3.json @@ -85,7 +85,7 @@ "transactionV5Timestamp": 0, "transactionV6Timestamp": 0, "disableReferenceTimestamp": 9999999999999, - "increaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "increaseOnlineAccountsDifficultyTimestamp": 9999999999990, "onlineAccountMinterLevelValidationHeight": 0, "selfSponsorshipAlgoV1Height": 999999999, "selfSponsorshipAlgoV2Height": 999999999, @@ -99,8 +99,10 @@ "cancelSellNameValidationTimestamp": 9999999999999, "disableRewardshareHeight": 9999999999990, "enableRewardshareHeight": 9999999999999, - "onlyMintWithNameHeight": 9999999999999, - "groupMemberCheckHeight": 9999999999999 + "onlyMintWithNameHeight": 9999999999990, + "groupMemberCheckHeight": 9999999999999, + "decreaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "removeOnlyMintWithNameHeight": 9999999999999 }, "genesisInfo": { "version": 4, diff --git a/src/test/resources/test-chain-v2.json b/src/test/resources/test-chain-v2.json index 086c126e..c829975b 100644 --- a/src/test/resources/test-chain-v2.json +++ b/src/test/resources/test-chain-v2.json @@ -86,7 +86,7 @@ "transactionV5Timestamp": 0, "transactionV6Timestamp": 0, "disableReferenceTimestamp": 9999999999999, - "increaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "increaseOnlineAccountsDifficultyTimestamp": 9999999999990, "onlineAccountMinterLevelValidationHeight": 0, "selfSponsorshipAlgoV1Height": 999999999, "selfSponsorshipAlgoV2Height": 999999999, @@ -100,8 +100,10 @@ "cancelSellNameValidationTimestamp": 9999999999999, "disableRewardshareHeight": 9999999999990, "enableRewardshareHeight": 9999999999999, - "onlyMintWithNameHeight": 9999999999999, - "groupMemberCheckHeight": 9999999999999 + "onlyMintWithNameHeight": 9999999999990, + "groupMemberCheckHeight": 9999999999999, + "decreaseOnlineAccountsDifficultyTimestamp": 9999999999999, + "removeOnlyMintWithNameHeight": 9999999999999 }, "genesisInfo": { "version": 4, From 7803d6c8f5db2530e9b51489b5c3b5402f9f2817 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Mon, 25 Nov 2024 09:36:11 +0200 Subject: [PATCH 5/9] adjust timeouts for qortalrequests --- src/main/resources/q-apps/q-apps.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/resources/q-apps/q-apps.js b/src/main/resources/q-apps/q-apps.js index e8a42537..25656370 100644 --- a/src/main/resources/q-apps/q-apps.js +++ b/src/main/resources/q-apps/q-apps.js @@ -614,6 +614,7 @@ function getDefaultTimeout(action) { switch (action) { case "GET_USER_ACCOUNT": case "SAVE_FILE": + case "SIGN_TRANSACTION": case "DECRYPT_DATA": // User may take a long time to accept/deny the popup return 60 * 60 * 1000; @@ -635,6 +636,11 @@ function getDefaultTimeout(action) { // Chat messages rely on PoW computations, so allow extra time return 60 * 1000; + case "CREATE_TRADE_BUY_ORDER": + case "CREATE_TRADE_SELL_ORDER": + case "CANCEL_TRADE_SELL_ORDER": + case "VOTE_ON_POLL": + case "CREATE_POLL": case "JOIN_GROUP": case "DEPLOY_AT": case "SEND_COIN": @@ -649,7 +655,7 @@ function getDefaultTimeout(action) { break; } } - return 10 * 1000; + return 30 * 1000; } /** From adbba0f94767cda6251668c5206015dfccb44941 Mon Sep 17 00:00:00 2001 From: AlphaX-Qortal Date: Mon, 2 Dec 2024 14:22:05 +0100 Subject: [PATCH 6/9] Various changes - Added real address to API results - Added group member check to validations - Network changes --- src/main/java/org/qortal/account/Account.java | 20 +++- .../qortal/api/model/ApiOnlineAccount.java | 33 +++++++ .../qortal/api/model/BlockMintingInfo.java | 2 +- .../qortal/api/resource/BlocksResource.java | 3 +- src/main/java/org/qortal/block/Block.java | 51 +++++++--- .../java/org/qortal/data/block/BlockData.java | 29 +++++- .../java/org/qortal/network/Handshake.java | 26 ++--- src/main/java/org/qortal/network/Network.java | 98 +++++++++---------- 8 files changed, 180 insertions(+), 82 deletions(-) diff --git a/src/main/java/org/qortal/account/Account.java b/src/main/java/org/qortal/account/Account.java index 537f0788..99fa5217 100644 --- a/src/main/java/org/qortal/account/Account.java +++ b/src/main/java/org/qortal/account/Account.java @@ -349,10 +349,28 @@ public class Account { } /** - * Returns 'effective' minting level, or zero if reward-share does not exist. + * Returns reward-share minting address, or unknown if reward-share does not exist. * * @param repository * @param rewardSharePublicKey + * @return address or unknown + * @throws DataException + */ + public static String getRewardShareMintingAddress(Repository repository, byte[] rewardSharePublicKey) throws DataException { + // Find actual minter address + RewardShareData rewardShareData = repository.getAccountRepository().getRewardShare(rewardSharePublicKey); + + if (rewardShareData == null) + return "Unknown"; + + return rewardShareData.getMinter(); + } + + /** + * Returns 'effective' minting level, or zero if reward-share does not exist. + * + * @param repository + * @param rewardSharePublicKey * @return 0+ * @throws DataException */ diff --git a/src/main/java/org/qortal/api/model/ApiOnlineAccount.java b/src/main/java/org/qortal/api/model/ApiOnlineAccount.java index 08b697aa..e26eb816 100644 --- a/src/main/java/org/qortal/api/model/ApiOnlineAccount.java +++ b/src/main/java/org/qortal/api/model/ApiOnlineAccount.java @@ -1,7 +1,13 @@ package org.qortal.api.model; +import org.qortal.account.Account; +import org.qortal.repository.DataException; +import org.qortal.repository.RepositoryManager; +import org.qortal.repository.Repository; + import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; // All properties to be converted to JSON via JAXB @XmlAccessorType(XmlAccessType.FIELD) @@ -47,4 +53,31 @@ public class ApiOnlineAccount { return this.recipientAddress; } + public int getMinterLevelFromPublicKey() { + try (final Repository repository = RepositoryManager.getRepository()) { + return Account.getRewardShareEffectiveMintingLevel(repository, this.rewardSharePublicKey); + } catch (DataException e) { + return 0; + } + } + + public boolean getIsMember() { + try (final Repository repository = RepositoryManager.getRepository()) { + return repository.getGroupRepository().memberExists(694, getMinterAddress()); + } catch (DataException e) { + return false; + } + } + + // JAXB special + + @XmlElement(name = "minterLevel") + protected int getMinterLevel() { + return getMinterLevelFromPublicKey(); + } + + @XmlElement(name = "isMinterMember") + protected boolean getMinterMember() { + return getIsMember(); + } } diff --git a/src/main/java/org/qortal/api/model/BlockMintingInfo.java b/src/main/java/org/qortal/api/model/BlockMintingInfo.java index f84e179e..02765a89 100644 --- a/src/main/java/org/qortal/api/model/BlockMintingInfo.java +++ b/src/main/java/org/qortal/api/model/BlockMintingInfo.java @@ -9,6 +9,7 @@ import java.math.BigInteger; public class BlockMintingInfo { public byte[] minterPublicKey; + public String minterAddress; public int minterLevel; public int onlineAccountsCount; public BigDecimal maxDistance; @@ -19,5 +20,4 @@ public class BlockMintingInfo { public BlockMintingInfo() { } - } diff --git a/src/main/java/org/qortal/api/resource/BlocksResource.java b/src/main/java/org/qortal/api/resource/BlocksResource.java index 01d8d2ab..ff0bb979 100644 --- a/src/main/java/org/qortal/api/resource/BlocksResource.java +++ b/src/main/java/org/qortal/api/resource/BlocksResource.java @@ -542,6 +542,7 @@ public class BlocksResource { } } + String minterAddress = Account.getRewardShareMintingAddress(repository, blockData.getMinterPublicKey()); int minterLevel = Account.getRewardShareEffectiveMintingLevel(repository, blockData.getMinterPublicKey()); if (minterLevel == 0) // This may be unavailable when requesting a trimmed block @@ -554,6 +555,7 @@ public class BlocksResource { BlockMintingInfo blockMintingInfo = new BlockMintingInfo(); blockMintingInfo.minterPublicKey = blockData.getMinterPublicKey(); + blockMintingInfo.minterAddress = minterAddress; blockMintingInfo.minterLevel = minterLevel; blockMintingInfo.onlineAccountsCount = blockData.getOnlineAccountsCount(); blockMintingInfo.maxDistance = new BigDecimal(block.MAX_DISTANCE); @@ -887,5 +889,4 @@ public class BlocksResource { throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e); } } - } diff --git a/src/main/java/org/qortal/block/Block.java b/src/main/java/org/qortal/block/Block.java index 918a20ae..c9353d70 100644 --- a/src/main/java/org/qortal/block/Block.java +++ b/src/main/java/org/qortal/block/Block.java @@ -145,7 +145,7 @@ public class Block { private final Account recipientAccount; private final AccountData recipientAccountData; - + final BlockChain blockChain = BlockChain.getInstance(); ExpandedAccount(Repository repository, RewardShareData rewardShareData) throws DataException { @@ -414,6 +414,21 @@ public class Block { }); } + // After feature trigger, remove any online accounts that are not minter group member + if (height >= BlockChain.getInstance().getGroupMemberCheckHeight()) { + onlineAccounts.removeIf(a -> { + try { + int groupId = BlockChain.getInstance().getMintingGroupId(); + String address = Account.getRewardShareMintingAddress(repository, a.getPublicKey()); + boolean isMinterGroupMember = repository.getGroupRepository().memberExists(groupId, address); + return !isMinterGroupMember; + } catch (DataException e) { + // Something went wrong, so remove the account + return true; + } + }); + } + if (onlineAccounts.isEmpty()) { LOGGER.debug("No online accounts - not even our own?"); return null; @@ -721,19 +736,19 @@ public class Block { List expandedAccounts = new ArrayList<>(); for (RewardShareData rewardShare : this.cachedOnlineRewardShares) { - if (this.getBlockData().getHeight() < BlockChain.getInstance().getFixBatchRewardHeight()) { + int groupId = BlockChain.getInstance().getMintingGroupId(); + String address = rewardShare.getMinter(); + boolean isMinterGroupMember = repository.getGroupRepository().memberExists(groupId, address); + + if (this.getBlockData().getHeight() < BlockChain.getInstance().getFixBatchRewardHeight()) + expandedAccounts.add(new ExpandedAccount(repository, rewardShare)); + + if (this.getBlockData().getHeight() >= BlockChain.getInstance().getFixBatchRewardHeight() && isMinterGroupMember) expandedAccounts.add(new ExpandedAccount(repository, rewardShare)); - } - if (this.getBlockData().getHeight() >= BlockChain.getInstance().getFixBatchRewardHeight()) { - boolean isMinterGroupMember = repository.getGroupRepository().memberExists(BlockChain.getInstance().getMintingGroupId(), rewardShare.getMinter()); - if (isMinterGroupMember) { - expandedAccounts.add(new ExpandedAccount(repository, rewardShare)); - } - } } - this.cachedExpandedAccounts = expandedAccounts; + LOGGER.trace(() -> String.format("Online reward-shares after expanded accounts %s", this.cachedOnlineRewardShares)); return this.cachedExpandedAccounts; } @@ -1143,8 +1158,17 @@ public class Block { if (this.getBlockData().getHeight() >= BlockChain.getInstance().getOnlineAccountMinterLevelValidationHeight()) { List expandedAccounts = this.getExpandedAccounts(); for (ExpandedAccount account : expandedAccounts) { + int groupId = BlockChain.getInstance().getMintingGroupId(); + String address = account.getMintingAccount().getAddress(); + boolean isMinterGroupMember = repository.getGroupRepository().memberExists(groupId, address); + if (account.getMintingAccount().getEffectiveMintingLevel() == 0) return ValidationResult.ONLINE_ACCOUNTS_INVALID; + + if (this.getBlockData().getHeight() >= BlockChain.getInstance().getFixBatchRewardHeight()) { + if (!isMinterGroupMember) + return ValidationResult.ONLINE_ACCOUNTS_INVALID; + } } } @@ -1273,6 +1297,7 @@ public class Block { // Online Accounts ValidationResult onlineAccountsResult = this.areOnlineAccountsValid(); + LOGGER.trace("Accounts valid = {}", onlineAccountsResult); if (onlineAccountsResult != ValidationResult.OK) return onlineAccountsResult; @@ -1361,7 +1386,7 @@ public class Block { // Check transaction can even be processed validationResult = transaction.isProcessable(); if (validationResult != Transaction.ValidationResult.OK) { - LOGGER.info(String.format("Error during transaction validation, tx %s: %s", Base58.encode(transactionData.getSignature()), validationResult.name())); + LOGGER.debug(String.format("Error during transaction validation, tx %s: %s", Base58.encode(transactionData.getSignature()), validationResult.name())); return ValidationResult.TRANSACTION_INVALID; } @@ -1562,6 +1587,7 @@ public class Block { this.blockData.setHeight(blockchainHeight + 1); LOGGER.trace(() -> String.format("Processing block %d", this.blockData.getHeight())); + LOGGER.trace(() -> String.format("Online Reward Shares in process %s", this.cachedOnlineRewardShares)); if (this.blockData.getHeight() > 1) { @@ -2280,7 +2306,6 @@ public class Block { // Select the correct set of share bins based on block height List accountLevelShareBinsForBlock = (this.blockData.getHeight() >= BlockChain.getInstance().getSharesByLevelV2Height()) ? BlockChain.getInstance().getAccountLevelShareBinsV2() : BlockChain.getInstance().getAccountLevelShareBinsV1(); - // Determine reward candidates based on account level // This needs a deep copy, so the shares can be modified when tiers aren't activated yet List accountLevelShareBins = new ArrayList<>(); @@ -2570,9 +2595,11 @@ public class Block { return; int minterLevel = Account.getRewardShareEffectiveMintingLevel(this.repository, this.getMinter().getPublicKey()); + String minterAddress = Account.getRewardShareMintingAddress(this.repository, this.getMinter().getPublicKey()); LOGGER.debug(String.format("======= BLOCK %d (%.8s) =======", this.getBlockData().getHeight(), Base58.encode(this.getSignature()))); LOGGER.debug(String.format("Timestamp: %d", this.getBlockData().getTimestamp())); + LOGGER.debug(String.format("Minter address: %s", minterAddress)); LOGGER.debug(String.format("Minter level: %d", minterLevel)); LOGGER.debug(String.format("Online accounts: %d", this.getBlockData().getOnlineAccountsCount())); LOGGER.debug(String.format("AT count: %d", this.getBlockData().getATCount())); diff --git a/src/main/java/org/qortal/data/block/BlockData.java b/src/main/java/org/qortal/data/block/BlockData.java index 34df0f9a..7e2a1872 100644 --- a/src/main/java/org/qortal/data/block/BlockData.java +++ b/src/main/java/org/qortal/data/block/BlockData.java @@ -1,8 +1,11 @@ package org.qortal.data.block; import com.google.common.primitives.Bytes; +import org.qortal.account.Account; import org.qortal.block.BlockChain; -import org.qortal.crypto.Crypto; +import org.qortal.repository.DataException; +import org.qortal.repository.Repository; +import org.qortal.repository.RepositoryManager; import org.qortal.settings.Settings; import org.qortal.utils.NTP; @@ -224,7 +227,7 @@ public class BlockData implements Serializable { } return 0; } - + public boolean isTrimmed() { long onlineAccountSignaturesTrimmedTimestamp = NTP.getTime() - BlockChain.getInstance().getOnlineAccountSignaturesMaxLifetime(); long currentTrimmableTimestamp = NTP.getTime() - Settings.getInstance().getAtStatesMaxLifetime(); @@ -232,11 +235,31 @@ public class BlockData implements Serializable { return blockTimestamp < onlineAccountSignaturesTrimmedTimestamp && blockTimestamp < currentTrimmableTimestamp; } + public String getMinterAddressFromPublicKey() { + try (final Repository repository = RepositoryManager.getRepository()) { + return Account.getRewardShareMintingAddress(repository, this.minterPublicKey); + } catch (DataException e) { + return "Unknown"; + } + } + + public int getMinterLevelFromPublicKey() { + try (final Repository repository = RepositoryManager.getRepository()) { + return Account.getRewardShareEffectiveMintingLevel(repository, this.minterPublicKey); + } catch (DataException e) { + return 0; + } + } + // JAXB special @XmlElement(name = "minterAddress") protected String getMinterAddress() { - return Crypto.toAddress(this.minterPublicKey); + return getMinterAddressFromPublicKey(); } + @XmlElement(name = "minterLevel") + protected int getMinterLevel() { + return getMinterLevelFromPublicKey(); + } } diff --git a/src/main/java/org/qortal/network/Handshake.java b/src/main/java/org/qortal/network/Handshake.java index 081e79e6..07f14702 100644 --- a/src/main/java/org/qortal/network/Handshake.java +++ b/src/main/java/org/qortal/network/Handshake.java @@ -48,7 +48,7 @@ public enum Handshake { String versionString = helloMessage.getVersionString(); - Matcher matcher = peer.VERSION_PATTERN.matcher(versionString); + Matcher matcher = Peer.VERSION_PATTERN.matcher(versionString); if (!matcher.lookingAt()) { LOGGER.debug(() -> String.format("Peer %s sent invalid HELLO version string '%s'", peer, versionString)); return null; @@ -71,7 +71,7 @@ public enum Handshake { // Ensure the peer is running at least the version specified in MIN_PEER_VERSION if (!peer.isAtLeastVersion(MIN_PEER_VERSION)) { - LOGGER.debug(String.format("Ignoring peer %s because it is on an old version (%s)", peer, versionString)); + LOGGER.debug("Ignoring peer {} because it is on an old version ({})", peer, versionString); return null; } @@ -79,7 +79,7 @@ public enum Handshake { // Ensure the peer is running at least the minimum version allowed for connections final String minPeerVersion = Settings.getInstance().getMinPeerVersion(); if (!peer.isAtLeastVersion(minPeerVersion)) { - LOGGER.debug(String.format("Ignoring peer %s because it is on an old version (%s)", peer, versionString)); + LOGGER.debug("Ignoring peer {} because it is on an old version ({})", peer, versionString); return null; } } @@ -106,7 +106,7 @@ public enum Handshake { byte[] peersPublicKey = challengeMessage.getPublicKey(); byte[] peersChallenge = challengeMessage.getChallenge(); - // If public key matches our public key then we've connected to self + // If public key matches our public key, then we've connected to self byte[] ourPublicKey = Network.getInstance().getOurPublicKey(); if (Arrays.equals(ourPublicKey, peersPublicKey)) { // If outgoing connection then record destination as self so we don't try again @@ -121,11 +121,11 @@ public enum Handshake { peer.disconnect("failed to send CHALLENGE to self"); /* - * We return CHALLENGE here to prevent us from closing connection. Closing - * connection currently preempts remote end from reading any pending messages, + * We return the CHALLENGE here to prevent us from closing the connection. + * Closing the connection currently preempts the remote end from reading any pending messages, * specifically the CHALLENGE message we just sent above. When our 'remote' * outbound counterpart reads our message, they will close both connections. - * Failing that, our connection will timeout or a future handshake error will + * Failing that, our connection will time out or a future handshake error will * occur. */ return CHALLENGE; @@ -135,7 +135,7 @@ public enum Handshake { // Are we already connected to this peer? Peer existingPeer = Network.getInstance().getHandshakedPeerWithPublicKey(peersPublicKey); if (existingPeer != null) { - LOGGER.info(() -> String.format("We already have a connection with peer %s - discarding", peer)); + LOGGER.debug(() -> String.format("We already have a connection with peer %s - discarding", peer)); // Handshake failure - caller will deal with disconnect return null; } @@ -148,7 +148,7 @@ public enum Handshake { @Override public void action(Peer peer) { - // Send challenge + // Send a challenge byte[] publicKey = Network.getInstance().getOurPublicKey(); byte[] challenge = peer.getOurChallenge(); @@ -254,16 +254,17 @@ public enum Handshake { private static final Logger LOGGER = LogManager.getLogger(Handshake.class); - /** Maximum allowed difference between peer's reported timestamp and when they connected, in milliseconds. */ + /** The Maximum allowed difference between peer's reported timestamp and when they connected, in milliseconds. */ private static final long MAX_TIMESTAMP_DELTA = 30 * 1000L; // ms private static final long PEER_VERSION_131 = 0x0100030001L; /** Minimum peer version that we are allowed to communicate with */ - private static final String MIN_PEER_VERSION = "4.1.1"; + private static final String MIN_PEER_VERSION = "4.6.5"; private static final int POW_BUFFER_SIZE_PRE_131 = 8 * 1024 * 1024; // bytes private static final int POW_DIFFICULTY_PRE_131 = 8; // leading zero bits + // Can always be made harder in the future... private static final int POW_BUFFER_SIZE_POST_131 = 2 * 1024 * 1024; // bytes private static final int POW_DIFFICULTY_POST_131 = 2; // leading zero bits @@ -275,12 +276,11 @@ public enum Handshake { public final MessageType expectedMessageType; - private Handshake(MessageType expectedMessageType) { + Handshake(MessageType expectedMessageType) { this.expectedMessageType = expectedMessageType; } public abstract Handshake onMessage(Peer peer, Message message); public abstract void action(Peer peer); - } diff --git a/src/main/java/org/qortal/network/Network.java b/src/main/java/org/qortal/network/Network.java index f500b2e8..d8777eec 100644 --- a/src/main/java/org/qortal/network/Network.java +++ b/src/main/java/org/qortal/network/Network.java @@ -80,7 +80,7 @@ public class Network { "node.qortal.ru", "node2.qortal.ru", "node3.qortal.ru", "node.qortal.uk", "node22.qortal.org", "cinfu1.crowetic.com", "node.cwd.systems", "bootstrap.cwd.systems", "node1.qortalnodes.live", "node2.qortalnodes.live", "node3.qortalnodes.live", "node4.qortalnodes.live", "node5.qortalnodes.live", - "node6.qortalnodes.live", "node7.qortalnodes.live", "node8.qortalnodes.live" + "node.qortalnodes.live", "qortex.live", }; private static final long NETWORK_EPC_KEEPALIVE = 5L; // seconds @@ -149,7 +149,7 @@ public class Network { private final Lock mergePeersLock = new ReentrantLock(); - private List ourExternalIpAddressHistory = new ArrayList<>(); + private final List ourExternalIpAddressHistory = new ArrayList<>(); private String ourExternalIpAddress = null; private int ourExternalPort = Settings.getInstance().getListenPort(); @@ -167,7 +167,7 @@ public class Network { ExecutorService networkExecutor = new ThreadPoolExecutor(2, Settings.getInstance().getMaxNetworkThreadPoolSize(), NETWORK_EPC_KEEPALIVE, TimeUnit.SECONDS, - new SynchronousQueue(), + new SynchronousQueue<>(), new NamedThreadFactory("Network-EPC", Settings.getInstance().getNetworkThreadPriority())); networkEPC = new NetworkProcessor(networkExecutor); } @@ -314,7 +314,7 @@ public class Network { public List getImmutableConnectedDataPeers() { return this.getImmutableConnectedPeers().stream() - .filter(p -> p.isDataPeer()) + .filter(Peer::isDataPeer) .collect(Collectors.toList()); } @@ -346,7 +346,7 @@ public class Network { public boolean requestDataFromPeer(String peerAddressString, byte[] signature) { if (peerAddressString != null) { PeerAddress peerAddress = PeerAddress.fromString(peerAddressString); - PeerData peerData = null; + PeerData peerData; // Reuse an existing PeerData instance if it's already in the known peers list synchronized (this.allKnownPeers) { @@ -370,9 +370,9 @@ public class Network { // Check if we're already connected to and handshaked with this peer Peer connectedPeer = this.getImmutableConnectedPeers().stream() - .filter(p -> p.getPeerData().getAddress().equals(peerAddress)) - .findFirst() - .orElse(null); + .filter(p -> p.getPeerData().getAddress().equals(peerAddress)) + .findFirst() + .orElse(null); boolean isConnected = (connectedPeer != null); @@ -710,7 +710,7 @@ public class Network { return true; } - private Peer getConnectablePeer(final Long now) throws InterruptedException { + private Peer getConnectablePeer(final Long now) { // We can't block here so use tryRepository(). We don't NEED to connect a new peer. try (Repository repository = RepositoryManager.tryRepository()) { if (repository == null) { @@ -807,7 +807,7 @@ public class Network { // Find peers that have reached their maximum connection age, and disconnect them List peersToDisconnect = this.getImmutableConnectedPeers().stream() .filter(peer -> !peer.isSyncInProgress()) - .filter(peer -> peer.hasReachedMaxConnectionAge()) + .filter(Peer::hasReachedMaxConnectionAge) .collect(Collectors.toList()); if (peersToDisconnect != null && !peersToDisconnect.isEmpty()) { @@ -996,9 +996,9 @@ public class Network { } // Add to per-message thread count (first initializing to 0 if not already present) - threadsPerMessageType.computeIfAbsent(message.getType(), key -> 0); + threadsPerMessageType.putIfAbsent(message.getType(), 0); threadsPerMessageType.computeIfPresent(message.getType(), (key, value) -> value + 1); - + // Add to total thread count synchronized (this) { totalThreadCount++; @@ -1037,7 +1037,7 @@ public class Network { } // Remove from per-message thread count (first initializing to 0 if not already present) - threadsPerMessageType.computeIfAbsent(message.getType(), key -> 0); + threadsPerMessageType.putIfAbsent(message.getType(), 0); threadsPerMessageType.computeIfPresent(message.getType(), (key, value) -> value - 1); // Remove from total thread count @@ -1135,7 +1135,7 @@ public class Network { Peer existingPeer = getHandshakedPeerWithPublicKey(peer.getPeersPublicKey()); // NOTE: actual object reference compare, not Peer.equals() if (existingPeer != peer) { - LOGGER.info("[{}] We already have a connection with peer {} - discarding", + LOGGER.debug("[{}] We already have a connection with peer {} - discarding", peer.getPeerConnectionId(), peer); peer.disconnect("existing connection"); return; @@ -1216,29 +1216,7 @@ public class Network { * Returns PEERS message made from peers we've connected to recently, and this node's details */ public Message buildPeersMessage(Peer peer) { - List knownPeers = this.getAllKnownPeers(); - - // Filter out peers that we've not connected to ever or within X milliseconds - final long connectionThreshold = NTP.getTime() - RECENT_CONNECTION_THRESHOLD; - Predicate notRecentlyConnected = peerData -> { - final Long lastAttempted = peerData.getLastAttempted(); - final Long lastConnected = peerData.getLastConnected(); - - if (lastAttempted == null || lastConnected == null) { - return true; - } - - if (lastConnected < lastAttempted) { - return true; - } - - if (lastConnected < connectionThreshold) { - return true; - } - - return false; - }; - knownPeers.removeIf(notRecentlyConnected); + final var knownPeers = getPeerData(); List peerAddresses = new ArrayList<>(); @@ -1262,6 +1240,29 @@ public class Network { return new PeersV2Message(peerAddresses); } + private List getPeerData() { + List knownPeers = this.getAllKnownPeers(); + + // Filter out peers that we've not connected to ever or within X milliseconds + final long connectionThreshold = NTP.getTime() - RECENT_CONNECTION_THRESHOLD; + Predicate notRecentlyConnected = peerData -> { + final Long lastAttempted = peerData.getLastAttempted(); + final Long lastConnected = peerData.getLastConnected(); + + if (lastAttempted == null || lastConnected == null) { + return true; + } + + if (lastConnected < lastAttempted) { + return true; + } + + return lastConnected < connectionThreshold; + }; + knownPeers.removeIf(notRecentlyConnected); + return knownPeers; + } + /** Builds either (legacy) HeightV2Message or (newer) BlockSummariesV2Message, depending on peer version. * * @return Message, or null if DataException was thrown. @@ -1328,7 +1329,7 @@ public class Network { return; } String host = parts[0]; - + try { InetAddress addr = InetAddress.getByName(host); if (addr.isAnyLocalAddress() || addr.isSiteLocalAddress()) { @@ -1369,12 +1370,12 @@ public class Network { for (int i = size-1; i >= 0; i--) { String reading = ipAddressHistory.get(i); if (lastReading != null) { - if (Objects.equals(reading, lastReading)) { + if (Objects.equals(reading, lastReading)) { consecutiveReadings++; - } - else { - consecutiveReadings = 0; - } + } + else { + consecutiveReadings = 0; + } } lastReading = reading; } @@ -1515,12 +1516,8 @@ public class Network { return true; } - if (peerData.getLastConnected() == null - || peerData.getLastConnected() > now - OLD_PEER_CONNECTION_PERIOD) { - return true; - } - - return false; + return peerData.getLastConnected() == null + || peerData.getLastConnected() > now - OLD_PEER_CONNECTION_PERIOD; }; // Disregard peers that are NOT 'old' @@ -1655,7 +1652,7 @@ public class Network { // Stop processing threads try { - if (!this.networkEPC.shutdown(5000)) { + if (!this.networkEPC.shutdown(10000)) { LOGGER.warn("Network threads failed to terminate"); } } catch (InterruptedException e) { @@ -1667,5 +1664,4 @@ public class Network { peer.shutdown(); } } - } From e3a85786e7ebd7fa78f8bf711ee5493e136fc149 Mon Sep 17 00:00:00 2001 From: AlphaX-Qortal Date: Mon, 2 Dec 2024 15:06:46 +0100 Subject: [PATCH 7/9] Update dependencies --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index bde846a0..11c06bc0 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ 1.2.2 0.12.3 4.9.10 - 1.68.1 + 1.68.2 33.3.1-jre 2.2 1.2.1 @@ -35,7 +35,7 @@ 9.4.56.v20240826 1.1.1 20240303 - 1.18.1 + 1.18.3 5.11.0-M2 1.0.0 2.23.1 From 8d6830135c4bd98f251fe761a8da94b540053960 Mon Sep 17 00:00:00 2001 From: crowetic Date: Wed, 4 Dec 2024 14:07:43 -0800 Subject: [PATCH 8/9] Changes need to be reverted prior to new PR from crowetic repo. Revert "Update dependencies" This reverts commit e3a85786e7ebd7fa78f8bf711ee5493e136fc149. --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 11c06bc0..bde846a0 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ 1.2.2 0.12.3 4.9.10 - 1.68.2 + 1.68.1 33.3.1-jre 2.2 1.2.1 @@ -35,7 +35,7 @@ 9.4.56.v20240826 1.1.1 20240303 - 1.18.3 + 1.18.1 5.11.0-M2 1.0.0 2.23.1 From 9b20192b3057eb0b147a2240a923504eb6068fa8 Mon Sep 17 00:00:00 2001 From: crowetic Date: Wed, 4 Dec 2024 14:08:25 -0800 Subject: [PATCH 9/9] Changes need to be reverted prior to the PR from crowetic repo being merged. All of these changes aside from those in the 'network' folder, will be re-applied with crowetic's PR. Revert "Various changes" This reverts commit adbba0f94767cda6251668c5206015dfccb44941. --- src/main/java/org/qortal/account/Account.java | 20 +--- .../qortal/api/model/ApiOnlineAccount.java | 33 ------- .../qortal/api/model/BlockMintingInfo.java | 2 +- .../qortal/api/resource/BlocksResource.java | 3 +- src/main/java/org/qortal/block/Block.java | 51 +++------- .../java/org/qortal/data/block/BlockData.java | 29 +----- .../java/org/qortal/network/Handshake.java | 26 ++--- src/main/java/org/qortal/network/Network.java | 98 ++++++++++--------- 8 files changed, 82 insertions(+), 180 deletions(-) diff --git a/src/main/java/org/qortal/account/Account.java b/src/main/java/org/qortal/account/Account.java index 99fa5217..537f0788 100644 --- a/src/main/java/org/qortal/account/Account.java +++ b/src/main/java/org/qortal/account/Account.java @@ -348,27 +348,9 @@ public class Account { return accountData.getLevel(); } - /** - * Returns reward-share minting address, or unknown if reward-share does not exist. - * - * @param repository - * @param rewardSharePublicKey - * @return address or unknown - * @throws DataException - */ - public static String getRewardShareMintingAddress(Repository repository, byte[] rewardSharePublicKey) throws DataException { - // Find actual minter address - RewardShareData rewardShareData = repository.getAccountRepository().getRewardShare(rewardSharePublicKey); - - if (rewardShareData == null) - return "Unknown"; - - return rewardShareData.getMinter(); - } - /** * Returns 'effective' minting level, or zero if reward-share does not exist. - * + * * @param repository * @param rewardSharePublicKey * @return 0+ diff --git a/src/main/java/org/qortal/api/model/ApiOnlineAccount.java b/src/main/java/org/qortal/api/model/ApiOnlineAccount.java index e26eb816..08b697aa 100644 --- a/src/main/java/org/qortal/api/model/ApiOnlineAccount.java +++ b/src/main/java/org/qortal/api/model/ApiOnlineAccount.java @@ -1,13 +1,7 @@ package org.qortal.api.model; -import org.qortal.account.Account; -import org.qortal.repository.DataException; -import org.qortal.repository.RepositoryManager; -import org.qortal.repository.Repository; - import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlElement; // All properties to be converted to JSON via JAXB @XmlAccessorType(XmlAccessType.FIELD) @@ -53,31 +47,4 @@ public class ApiOnlineAccount { return this.recipientAddress; } - public int getMinterLevelFromPublicKey() { - try (final Repository repository = RepositoryManager.getRepository()) { - return Account.getRewardShareEffectiveMintingLevel(repository, this.rewardSharePublicKey); - } catch (DataException e) { - return 0; - } - } - - public boolean getIsMember() { - try (final Repository repository = RepositoryManager.getRepository()) { - return repository.getGroupRepository().memberExists(694, getMinterAddress()); - } catch (DataException e) { - return false; - } - } - - // JAXB special - - @XmlElement(name = "minterLevel") - protected int getMinterLevel() { - return getMinterLevelFromPublicKey(); - } - - @XmlElement(name = "isMinterMember") - protected boolean getMinterMember() { - return getIsMember(); - } } diff --git a/src/main/java/org/qortal/api/model/BlockMintingInfo.java b/src/main/java/org/qortal/api/model/BlockMintingInfo.java index 02765a89..f84e179e 100644 --- a/src/main/java/org/qortal/api/model/BlockMintingInfo.java +++ b/src/main/java/org/qortal/api/model/BlockMintingInfo.java @@ -9,7 +9,6 @@ import java.math.BigInteger; public class BlockMintingInfo { public byte[] minterPublicKey; - public String minterAddress; public int minterLevel; public int onlineAccountsCount; public BigDecimal maxDistance; @@ -20,4 +19,5 @@ public class BlockMintingInfo { public BlockMintingInfo() { } + } diff --git a/src/main/java/org/qortal/api/resource/BlocksResource.java b/src/main/java/org/qortal/api/resource/BlocksResource.java index ff0bb979..01d8d2ab 100644 --- a/src/main/java/org/qortal/api/resource/BlocksResource.java +++ b/src/main/java/org/qortal/api/resource/BlocksResource.java @@ -542,7 +542,6 @@ public class BlocksResource { } } - String minterAddress = Account.getRewardShareMintingAddress(repository, blockData.getMinterPublicKey()); int minterLevel = Account.getRewardShareEffectiveMintingLevel(repository, blockData.getMinterPublicKey()); if (minterLevel == 0) // This may be unavailable when requesting a trimmed block @@ -555,7 +554,6 @@ public class BlocksResource { BlockMintingInfo blockMintingInfo = new BlockMintingInfo(); blockMintingInfo.minterPublicKey = blockData.getMinterPublicKey(); - blockMintingInfo.minterAddress = minterAddress; blockMintingInfo.minterLevel = minterLevel; blockMintingInfo.onlineAccountsCount = blockData.getOnlineAccountsCount(); blockMintingInfo.maxDistance = new BigDecimal(block.MAX_DISTANCE); @@ -889,4 +887,5 @@ public class BlocksResource { throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e); } } + } diff --git a/src/main/java/org/qortal/block/Block.java b/src/main/java/org/qortal/block/Block.java index c9353d70..918a20ae 100644 --- a/src/main/java/org/qortal/block/Block.java +++ b/src/main/java/org/qortal/block/Block.java @@ -145,7 +145,7 @@ public class Block { private final Account recipientAccount; private final AccountData recipientAccountData; - + final BlockChain blockChain = BlockChain.getInstance(); ExpandedAccount(Repository repository, RewardShareData rewardShareData) throws DataException { @@ -414,21 +414,6 @@ public class Block { }); } - // After feature trigger, remove any online accounts that are not minter group member - if (height >= BlockChain.getInstance().getGroupMemberCheckHeight()) { - onlineAccounts.removeIf(a -> { - try { - int groupId = BlockChain.getInstance().getMintingGroupId(); - String address = Account.getRewardShareMintingAddress(repository, a.getPublicKey()); - boolean isMinterGroupMember = repository.getGroupRepository().memberExists(groupId, address); - return !isMinterGroupMember; - } catch (DataException e) { - // Something went wrong, so remove the account - return true; - } - }); - } - if (onlineAccounts.isEmpty()) { LOGGER.debug("No online accounts - not even our own?"); return null; @@ -736,19 +721,19 @@ public class Block { List expandedAccounts = new ArrayList<>(); for (RewardShareData rewardShare : this.cachedOnlineRewardShares) { - int groupId = BlockChain.getInstance().getMintingGroupId(); - String address = rewardShare.getMinter(); - boolean isMinterGroupMember = repository.getGroupRepository().memberExists(groupId, address); - - if (this.getBlockData().getHeight() < BlockChain.getInstance().getFixBatchRewardHeight()) - expandedAccounts.add(new ExpandedAccount(repository, rewardShare)); - - if (this.getBlockData().getHeight() >= BlockChain.getInstance().getFixBatchRewardHeight() && isMinterGroupMember) + if (this.getBlockData().getHeight() < BlockChain.getInstance().getFixBatchRewardHeight()) { expandedAccounts.add(new ExpandedAccount(repository, rewardShare)); + } + if (this.getBlockData().getHeight() >= BlockChain.getInstance().getFixBatchRewardHeight()) { + boolean isMinterGroupMember = repository.getGroupRepository().memberExists(BlockChain.getInstance().getMintingGroupId(), rewardShare.getMinter()); + if (isMinterGroupMember) { + expandedAccounts.add(new ExpandedAccount(repository, rewardShare)); + } + } } + this.cachedExpandedAccounts = expandedAccounts; - LOGGER.trace(() -> String.format("Online reward-shares after expanded accounts %s", this.cachedOnlineRewardShares)); return this.cachedExpandedAccounts; } @@ -1158,17 +1143,8 @@ public class Block { if (this.getBlockData().getHeight() >= BlockChain.getInstance().getOnlineAccountMinterLevelValidationHeight()) { List expandedAccounts = this.getExpandedAccounts(); for (ExpandedAccount account : expandedAccounts) { - int groupId = BlockChain.getInstance().getMintingGroupId(); - String address = account.getMintingAccount().getAddress(); - boolean isMinterGroupMember = repository.getGroupRepository().memberExists(groupId, address); - if (account.getMintingAccount().getEffectiveMintingLevel() == 0) return ValidationResult.ONLINE_ACCOUNTS_INVALID; - - if (this.getBlockData().getHeight() >= BlockChain.getInstance().getFixBatchRewardHeight()) { - if (!isMinterGroupMember) - return ValidationResult.ONLINE_ACCOUNTS_INVALID; - } } } @@ -1297,7 +1273,6 @@ public class Block { // Online Accounts ValidationResult onlineAccountsResult = this.areOnlineAccountsValid(); - LOGGER.trace("Accounts valid = {}", onlineAccountsResult); if (onlineAccountsResult != ValidationResult.OK) return onlineAccountsResult; @@ -1386,7 +1361,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; } @@ -1587,7 +1562,6 @@ public class Block { this.blockData.setHeight(blockchainHeight + 1); LOGGER.trace(() -> String.format("Processing block %d", this.blockData.getHeight())); - LOGGER.trace(() -> String.format("Online Reward Shares in process %s", this.cachedOnlineRewardShares)); if (this.blockData.getHeight() > 1) { @@ -2306,6 +2280,7 @@ public class Block { // Select the correct set of share bins based on block height List accountLevelShareBinsForBlock = (this.blockData.getHeight() >= BlockChain.getInstance().getSharesByLevelV2Height()) ? BlockChain.getInstance().getAccountLevelShareBinsV2() : BlockChain.getInstance().getAccountLevelShareBinsV1(); + // Determine reward candidates based on account level // This needs a deep copy, so the shares can be modified when tiers aren't activated yet List accountLevelShareBins = new ArrayList<>(); @@ -2595,11 +2570,9 @@ public class Block { return; int minterLevel = Account.getRewardShareEffectiveMintingLevel(this.repository, this.getMinter().getPublicKey()); - String minterAddress = Account.getRewardShareMintingAddress(this.repository, this.getMinter().getPublicKey()); LOGGER.debug(String.format("======= BLOCK %d (%.8s) =======", this.getBlockData().getHeight(), Base58.encode(this.getSignature()))); LOGGER.debug(String.format("Timestamp: %d", this.getBlockData().getTimestamp())); - LOGGER.debug(String.format("Minter address: %s", minterAddress)); LOGGER.debug(String.format("Minter level: %d", minterLevel)); LOGGER.debug(String.format("Online accounts: %d", this.getBlockData().getOnlineAccountsCount())); LOGGER.debug(String.format("AT count: %d", this.getBlockData().getATCount())); diff --git a/src/main/java/org/qortal/data/block/BlockData.java b/src/main/java/org/qortal/data/block/BlockData.java index 7e2a1872..34df0f9a 100644 --- a/src/main/java/org/qortal/data/block/BlockData.java +++ b/src/main/java/org/qortal/data/block/BlockData.java @@ -1,11 +1,8 @@ package org.qortal.data.block; import com.google.common.primitives.Bytes; -import org.qortal.account.Account; import org.qortal.block.BlockChain; -import org.qortal.repository.DataException; -import org.qortal.repository.Repository; -import org.qortal.repository.RepositoryManager; +import org.qortal.crypto.Crypto; import org.qortal.settings.Settings; import org.qortal.utils.NTP; @@ -227,7 +224,7 @@ public class BlockData implements Serializable { } return 0; } - + public boolean isTrimmed() { long onlineAccountSignaturesTrimmedTimestamp = NTP.getTime() - BlockChain.getInstance().getOnlineAccountSignaturesMaxLifetime(); long currentTrimmableTimestamp = NTP.getTime() - Settings.getInstance().getAtStatesMaxLifetime(); @@ -235,31 +232,11 @@ public class BlockData implements Serializable { return blockTimestamp < onlineAccountSignaturesTrimmedTimestamp && blockTimestamp < currentTrimmableTimestamp; } - public String getMinterAddressFromPublicKey() { - try (final Repository repository = RepositoryManager.getRepository()) { - return Account.getRewardShareMintingAddress(repository, this.minterPublicKey); - } catch (DataException e) { - return "Unknown"; - } - } - - public int getMinterLevelFromPublicKey() { - try (final Repository repository = RepositoryManager.getRepository()) { - return Account.getRewardShareEffectiveMintingLevel(repository, this.minterPublicKey); - } catch (DataException e) { - return 0; - } - } - // JAXB special @XmlElement(name = "minterAddress") protected String getMinterAddress() { - return getMinterAddressFromPublicKey(); + return Crypto.toAddress(this.minterPublicKey); } - @XmlElement(name = "minterLevel") - protected int getMinterLevel() { - return getMinterLevelFromPublicKey(); - } } diff --git a/src/main/java/org/qortal/network/Handshake.java b/src/main/java/org/qortal/network/Handshake.java index 07f14702..081e79e6 100644 --- a/src/main/java/org/qortal/network/Handshake.java +++ b/src/main/java/org/qortal/network/Handshake.java @@ -48,7 +48,7 @@ public enum Handshake { String versionString = helloMessage.getVersionString(); - Matcher matcher = Peer.VERSION_PATTERN.matcher(versionString); + Matcher matcher = peer.VERSION_PATTERN.matcher(versionString); if (!matcher.lookingAt()) { LOGGER.debug(() -> String.format("Peer %s sent invalid HELLO version string '%s'", peer, versionString)); return null; @@ -71,7 +71,7 @@ public enum Handshake { // Ensure the peer is running at least the version specified in MIN_PEER_VERSION if (!peer.isAtLeastVersion(MIN_PEER_VERSION)) { - LOGGER.debug("Ignoring peer {} because it is on an old version ({})", peer, versionString); + LOGGER.debug(String.format("Ignoring peer %s because it is on an old version (%s)", peer, versionString)); return null; } @@ -79,7 +79,7 @@ public enum Handshake { // Ensure the peer is running at least the minimum version allowed for connections final String minPeerVersion = Settings.getInstance().getMinPeerVersion(); if (!peer.isAtLeastVersion(minPeerVersion)) { - LOGGER.debug("Ignoring peer {} because it is on an old version ({})", peer, versionString); + LOGGER.debug(String.format("Ignoring peer %s because it is on an old version (%s)", peer, versionString)); return null; } } @@ -106,7 +106,7 @@ public enum Handshake { byte[] peersPublicKey = challengeMessage.getPublicKey(); byte[] peersChallenge = challengeMessage.getChallenge(); - // If public key matches our public key, then we've connected to self + // If public key matches our public key then we've connected to self byte[] ourPublicKey = Network.getInstance().getOurPublicKey(); if (Arrays.equals(ourPublicKey, peersPublicKey)) { // If outgoing connection then record destination as self so we don't try again @@ -121,11 +121,11 @@ public enum Handshake { peer.disconnect("failed to send CHALLENGE to self"); /* - * We return the CHALLENGE here to prevent us from closing the connection. - * Closing the connection currently preempts the remote end from reading any pending messages, + * We return CHALLENGE here to prevent us from closing connection. Closing + * connection currently preempts remote end from reading any pending messages, * specifically the CHALLENGE message we just sent above. When our 'remote' * outbound counterpart reads our message, they will close both connections. - * Failing that, our connection will time out or a future handshake error will + * Failing that, our connection will timeout or a future handshake error will * occur. */ return CHALLENGE; @@ -135,7 +135,7 @@ public enum Handshake { // Are we already connected to this peer? Peer existingPeer = Network.getInstance().getHandshakedPeerWithPublicKey(peersPublicKey); if (existingPeer != null) { - LOGGER.debug(() -> String.format("We already have a connection with peer %s - discarding", peer)); + LOGGER.info(() -> String.format("We already have a connection with peer %s - discarding", peer)); // Handshake failure - caller will deal with disconnect return null; } @@ -148,7 +148,7 @@ public enum Handshake { @Override public void action(Peer peer) { - // Send a challenge + // Send challenge byte[] publicKey = Network.getInstance().getOurPublicKey(); byte[] challenge = peer.getOurChallenge(); @@ -254,17 +254,16 @@ public enum Handshake { private static final Logger LOGGER = LogManager.getLogger(Handshake.class); - /** The Maximum allowed difference between peer's reported timestamp and when they connected, in milliseconds. */ + /** Maximum allowed difference between peer's reported timestamp and when they connected, in milliseconds. */ private static final long MAX_TIMESTAMP_DELTA = 30 * 1000L; // ms private static final long PEER_VERSION_131 = 0x0100030001L; /** Minimum peer version that we are allowed to communicate with */ - private static final String MIN_PEER_VERSION = "4.6.5"; + private static final String MIN_PEER_VERSION = "4.1.1"; private static final int POW_BUFFER_SIZE_PRE_131 = 8 * 1024 * 1024; // bytes private static final int POW_DIFFICULTY_PRE_131 = 8; // leading zero bits - // Can always be made harder in the future... private static final int POW_BUFFER_SIZE_POST_131 = 2 * 1024 * 1024; // bytes private static final int POW_DIFFICULTY_POST_131 = 2; // leading zero bits @@ -276,11 +275,12 @@ public enum Handshake { public final MessageType expectedMessageType; - Handshake(MessageType expectedMessageType) { + private Handshake(MessageType expectedMessageType) { this.expectedMessageType = expectedMessageType; } public abstract Handshake onMessage(Peer peer, Message message); public abstract void action(Peer peer); + } diff --git a/src/main/java/org/qortal/network/Network.java b/src/main/java/org/qortal/network/Network.java index d8777eec..f500b2e8 100644 --- a/src/main/java/org/qortal/network/Network.java +++ b/src/main/java/org/qortal/network/Network.java @@ -80,7 +80,7 @@ public class Network { "node.qortal.ru", "node2.qortal.ru", "node3.qortal.ru", "node.qortal.uk", "node22.qortal.org", "cinfu1.crowetic.com", "node.cwd.systems", "bootstrap.cwd.systems", "node1.qortalnodes.live", "node2.qortalnodes.live", "node3.qortalnodes.live", "node4.qortalnodes.live", "node5.qortalnodes.live", - "node.qortalnodes.live", "qortex.live", + "node6.qortalnodes.live", "node7.qortalnodes.live", "node8.qortalnodes.live" }; private static final long NETWORK_EPC_KEEPALIVE = 5L; // seconds @@ -149,7 +149,7 @@ public class Network { private final Lock mergePeersLock = new ReentrantLock(); - private final List ourExternalIpAddressHistory = new ArrayList<>(); + private List ourExternalIpAddressHistory = new ArrayList<>(); private String ourExternalIpAddress = null; private int ourExternalPort = Settings.getInstance().getListenPort(); @@ -167,7 +167,7 @@ public class Network { ExecutorService networkExecutor = new ThreadPoolExecutor(2, Settings.getInstance().getMaxNetworkThreadPoolSize(), NETWORK_EPC_KEEPALIVE, TimeUnit.SECONDS, - new SynchronousQueue<>(), + new SynchronousQueue(), new NamedThreadFactory("Network-EPC", Settings.getInstance().getNetworkThreadPriority())); networkEPC = new NetworkProcessor(networkExecutor); } @@ -314,7 +314,7 @@ public class Network { public List getImmutableConnectedDataPeers() { return this.getImmutableConnectedPeers().stream() - .filter(Peer::isDataPeer) + .filter(p -> p.isDataPeer()) .collect(Collectors.toList()); } @@ -346,7 +346,7 @@ public class Network { public boolean requestDataFromPeer(String peerAddressString, byte[] signature) { if (peerAddressString != null) { PeerAddress peerAddress = PeerAddress.fromString(peerAddressString); - PeerData peerData; + PeerData peerData = null; // Reuse an existing PeerData instance if it's already in the known peers list synchronized (this.allKnownPeers) { @@ -370,9 +370,9 @@ public class Network { // Check if we're already connected to and handshaked with this peer Peer connectedPeer = this.getImmutableConnectedPeers().stream() - .filter(p -> p.getPeerData().getAddress().equals(peerAddress)) - .findFirst() - .orElse(null); + .filter(p -> p.getPeerData().getAddress().equals(peerAddress)) + .findFirst() + .orElse(null); boolean isConnected = (connectedPeer != null); @@ -710,7 +710,7 @@ public class Network { return true; } - private Peer getConnectablePeer(final Long now) { + private Peer getConnectablePeer(final Long now) throws InterruptedException { // We can't block here so use tryRepository(). We don't NEED to connect a new peer. try (Repository repository = RepositoryManager.tryRepository()) { if (repository == null) { @@ -807,7 +807,7 @@ public class Network { // Find peers that have reached their maximum connection age, and disconnect them List peersToDisconnect = this.getImmutableConnectedPeers().stream() .filter(peer -> !peer.isSyncInProgress()) - .filter(Peer::hasReachedMaxConnectionAge) + .filter(peer -> peer.hasReachedMaxConnectionAge()) .collect(Collectors.toList()); if (peersToDisconnect != null && !peersToDisconnect.isEmpty()) { @@ -996,9 +996,9 @@ public class Network { } // Add to per-message thread count (first initializing to 0 if not already present) - threadsPerMessageType.putIfAbsent(message.getType(), 0); + threadsPerMessageType.computeIfAbsent(message.getType(), key -> 0); threadsPerMessageType.computeIfPresent(message.getType(), (key, value) -> value + 1); - + // Add to total thread count synchronized (this) { totalThreadCount++; @@ -1037,7 +1037,7 @@ public class Network { } // Remove from per-message thread count (first initializing to 0 if not already present) - threadsPerMessageType.putIfAbsent(message.getType(), 0); + threadsPerMessageType.computeIfAbsent(message.getType(), key -> 0); threadsPerMessageType.computeIfPresent(message.getType(), (key, value) -> value - 1); // Remove from total thread count @@ -1135,7 +1135,7 @@ public class Network { Peer existingPeer = getHandshakedPeerWithPublicKey(peer.getPeersPublicKey()); // NOTE: actual object reference compare, not Peer.equals() if (existingPeer != peer) { - LOGGER.debug("[{}] We already have a connection with peer {} - discarding", + LOGGER.info("[{}] We already have a connection with peer {} - discarding", peer.getPeerConnectionId(), peer); peer.disconnect("existing connection"); return; @@ -1216,7 +1216,29 @@ public class Network { * Returns PEERS message made from peers we've connected to recently, and this node's details */ public Message buildPeersMessage(Peer peer) { - final var knownPeers = getPeerData(); + List knownPeers = this.getAllKnownPeers(); + + // Filter out peers that we've not connected to ever or within X milliseconds + final long connectionThreshold = NTP.getTime() - RECENT_CONNECTION_THRESHOLD; + Predicate notRecentlyConnected = peerData -> { + final Long lastAttempted = peerData.getLastAttempted(); + final Long lastConnected = peerData.getLastConnected(); + + if (lastAttempted == null || lastConnected == null) { + return true; + } + + if (lastConnected < lastAttempted) { + return true; + } + + if (lastConnected < connectionThreshold) { + return true; + } + + return false; + }; + knownPeers.removeIf(notRecentlyConnected); List peerAddresses = new ArrayList<>(); @@ -1240,29 +1262,6 @@ public class Network { return new PeersV2Message(peerAddresses); } - private List getPeerData() { - List knownPeers = this.getAllKnownPeers(); - - // Filter out peers that we've not connected to ever or within X milliseconds - final long connectionThreshold = NTP.getTime() - RECENT_CONNECTION_THRESHOLD; - Predicate notRecentlyConnected = peerData -> { - final Long lastAttempted = peerData.getLastAttempted(); - final Long lastConnected = peerData.getLastConnected(); - - if (lastAttempted == null || lastConnected == null) { - return true; - } - - if (lastConnected < lastAttempted) { - return true; - } - - return lastConnected < connectionThreshold; - }; - knownPeers.removeIf(notRecentlyConnected); - return knownPeers; - } - /** Builds either (legacy) HeightV2Message or (newer) BlockSummariesV2Message, depending on peer version. * * @return Message, or null if DataException was thrown. @@ -1329,7 +1328,7 @@ public class Network { return; } String host = parts[0]; - + try { InetAddress addr = InetAddress.getByName(host); if (addr.isAnyLocalAddress() || addr.isSiteLocalAddress()) { @@ -1370,12 +1369,12 @@ public class Network { for (int i = size-1; i >= 0; i--) { String reading = ipAddressHistory.get(i); if (lastReading != null) { - if (Objects.equals(reading, lastReading)) { + if (Objects.equals(reading, lastReading)) { consecutiveReadings++; - } - else { - consecutiveReadings = 0; - } + } + else { + consecutiveReadings = 0; + } } lastReading = reading; } @@ -1516,8 +1515,12 @@ public class Network { return true; } - return peerData.getLastConnected() == null - || peerData.getLastConnected() > now - OLD_PEER_CONNECTION_PERIOD; + if (peerData.getLastConnected() == null + || peerData.getLastConnected() > now - OLD_PEER_CONNECTION_PERIOD) { + return true; + } + + return false; }; // Disregard peers that are NOT 'old' @@ -1652,7 +1655,7 @@ public class Network { // Stop processing threads try { - if (!this.networkEPC.shutdown(10000)) { + if (!this.networkEPC.shutdown(5000)) { LOGGER.warn("Network threads failed to terminate"); } } catch (InterruptedException e) { @@ -1664,4 +1667,5 @@ public class Network { peer.shutdown(); } } + }