Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.util.stream.Stream;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
Expand All @@ -44,7 +45,13 @@ public class BlockchainUtilParameterizedTest {

private static final int chainHeight = 89;
private static Block genesisBlock;
private static MutableBlockchain localBlockchain;
// Pre-generated canonical chain data (crypto happens once in @BeforeAll)
private static final List<Block> canonicalBlocks = new ArrayList<>(chainHeight);
private static final List<List<TransactionReceipt>> canonicalReceipts =
new ArrayList<>(chainHeight);

// Rebuilt cheaply from canonical data before each test — never accumulates fork chains
private MutableBlockchain localBlockchain;
Comment on lines +48 to +54
Copy link

Copilot AI Mar 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cached canonical data structures are static and mutable. If JUnit parallel execution is enabled (or if future edits accidentally mutate these lists), tests can become order-dependent or flaky. Consider making the cached collections immutable after @BeforeAll completes (e.g., wrap with Collections.unmodifiableList(...) / use ImmutableList) and, for receipts, consider storing unmodifiable copies of the inner lists as well.

Copilot uses AI. Check for mistakes.

private MutableBlockchain remoteBlockchain;

Expand All @@ -54,16 +61,27 @@ public class BlockchainUtilParameterizedTest {
@BeforeAll
public static void setupClass() {
genesisBlock = blockDataGenerator.genesisBlock();
localBlockchain = InMemoryKeyValueStorageProvider.createInMemoryBlockchain(genesisBlock);
// Setup local chain.
// Use a temporary chain only to thread parent hashes; store blocks for reuse
final MutableBlockchain tempChain =
InMemoryKeyValueStorageProvider.createInMemoryBlockchain(genesisBlock);
for (int i = 1; i <= chainHeight; i++) {
final BlockDataGenerator.BlockOptions options =
new BlockDataGenerator.BlockOptions()
.setBlockNumber(i)
.setParentHash(localBlockchain.getBlockHashByNumber(i - 1).get());
.setParentHash(tempChain.getBlockHashByNumber(i - 1).get());
final Block block = blockDataGenerator.block(options);
final List<TransactionReceipt> receipts = blockDataGenerator.receipts(block);
localBlockchain.appendBlock(block, receipts);
tempChain.appendBlock(block, receipts);
canonicalBlocks.add(block);
canonicalReceipts.add(receipts);
}
}

@BeforeEach
public void setupInstance() {
localBlockchain = InMemoryKeyValueStorageProvider.createInMemoryBlockchain(genesisBlock);
for (int i = 0; i < canonicalBlocks.size(); i++) {
localBlockchain.appendBlock(canonicalBlocks.get(i), canonicalReceipts.get(i));
}
}

Expand All @@ -78,16 +96,9 @@ public void setup(final int commonAncestorHeight) {
final BlockBody commonBody = localBlockchain.getBlockBody(commonHeader.getHash()).get();
remoteBlockchain.appendBlock(new Block(commonHeader, commonBody), receipts);
}
// Remaining blocks are disparate.
// Remaining blocks are disparate on the remote chain only.
// Local canonical chain already has blocks up to chainHeight from @BeforeEach.
for (long i = commonAncestorHeight + 1L; i <= chainHeight; i++) {
final BlockDataGenerator.BlockOptions localOptions =
new BlockDataGenerator.BlockOptions()
.setBlockNumber(i)
.setParentHash(localBlockchain.getBlockHashByNumber(i - 1).get());
final Block localBlock = blockDataGenerator.block(localOptions);
final List<TransactionReceipt> localReceipts = blockDataGenerator.receipts(localBlock);
localBlockchain.appendBlock(localBlock, localReceipts);

final BlockDataGenerator.BlockOptions remoteOptions =
new BlockDataGenerator.BlockOptions()
.setDifficulty(Difficulty.ONE) // differentiator
Expand All @@ -97,7 +108,7 @@ public void setup(final int commonAncestorHeight) {
final List<TransactionReceipt> remoteReceipts = blockDataGenerator.receipts(remoteBlock);
remoteBlockchain.appendBlock(remoteBlock, remoteReceipts);
}
headers = new ArrayList<>();
headers = new ArrayList<>(chainHeight + 1);
for (long i = 0L; i <= remoteBlockchain.getChainHeadBlockNumber(); i++) {
headers.add(remoteBlockchain.getBlockHeader(i).get());
}
Expand Down
Loading