### Question:
ShardHashCalculatorImpl implements ShardHashCalculator { @Override public byte[] calculateHash(int shardStartHeight, int shardEndHeight) { if (shardStartHeight >= shardEndHeight) { throw new IllegalArgumentException("shard start height should be less than shard end height " + String.format("start - %d, finish - %d", shardStartHeight, shardEndHeight)); } long startTime = System.currentTimeMillis(); List<byte[]> blockSignatures = retrieveBlockSignatures(shardStartHeight, shardEndHeight); int blocks = blockSignatures.size(); log.trace("Retrieved {} block signatures in {} ms", blocks, System.currentTimeMillis() - startTime); if (blocks == 0) { return null; } long merkleTreeStartTime = System.currentTimeMillis(); byte[] prevHash = getPrevShardHash(shardStartHeight); blockSignatures.add(prevHash); byte[] hash = calculateMerkleRoot(blockSignatures); log.debug("Built merkle tree in {} ms", System.currentTimeMillis() - merkleTreeStartTime); long time = System.currentTimeMillis() - startTime; log.debug("Hash calculated in {} s for [{}] blocks, speed {} bpms", time / 1000, blocks, (shardEndHeight - shardStartHeight) / Math.max(time, 1)); return hash; } @Inject  ShardHashCalculatorImpl(Blockchain blockchain, BlockchainConfig blockchainConfig, ShardDao shardDao);  ShardHashCalculatorImpl(Blockchain blockchain, BlockchainConfig blockchainConfig, ShardDao shardDao, int blockSelectLimit); @Override byte[] calculateHash(int shardStartHeight, int shardEndHeight);  }### Answer:
@Test void testCalculateHashForAllBlocks() throws IOException { byte[] expectedFullMerkleRoot = hash(td.BLOCKS); byte[] merkleRoot1 = shardHashCalculator.calculateHash(td.GENESIS_BLOCK.getHeight(), td.LAST_BLOCK.getHeight() + 1); byte[] merkleRoot2 = shardHashCalculator.calculateHash(td.GENESIS_BLOCK.getHeight() - 100, td.LAST_BLOCK.getHeight() + 200); byte[] merkleRoot3 = shardHashCalculator.calculateHash(td.GENESIS_BLOCK.getHeight(), td.LAST_BLOCK.getHeight() + 20000); assertArrayEquals(expectedFullMerkleRoot, merkleRoot1); assertArrayEquals(expectedFullMerkleRoot, merkleRoot2); assertArrayEquals(expectedFullMerkleRoot, merkleRoot3); }
@Test void testCalculateHashWhenNoBlocks() throws IOException { byte[] merkleRoot = shardHashCalculator.calculateHash(td.LAST_BLOCK.getHeight() + 1, td.LAST_BLOCK.getHeight() + 100_000); Assertions.assertNull(merkleRoot); }
@Test void testCalculateHashForMiddleBlocks() throws IOException { byte[] merkleRoot = shardHashCalculator.calculateHash(td.BLOCK_1.getHeight(), td.BLOCK_5.getHeight()); assertArrayEquals(PARTIAL_MERKLE_ROOT_2_6, merkleRoot); }
@Test void testCalculateHashForFirstBlocks() throws IOException { byte[] merkleRoot = shardHashCalculator.calculateHash(0, td.BLOCK_8.getHeight()); assertArrayEquals(PARTIAL_MERKLE_ROOT_1_8, merkleRoot); }
@Test void testCalculateHashForLastBlocks() throws IOException { byte[] merkleRoot = shardHashCalculator.calculateHash(td.BLOCK_6.getHeight(), td.BLOCK_11.getHeight() + 1); assertArrayEquals(PARTIAL_MERKLE_ROOT_7_12, merkleRoot); }