[MINOR] Parameterize BlockchainUtilTest (#7)

* parameterize BlockchainUtilTest

* update javadoc for return

* update method descriptiuon

* randomize tests

* eliminate redundant comment, simplify addition of params

* eliminate unused parameter

* extract redundant code into own method
S. Matthew English 6 years ago committed by GitHub
parent 3e7a54850b
commit daad2995a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 52
      ethereum/core/src/main/java/net/consensys/pantheon/ethereum/util/BlockchainUtil.java
  2. 135
      ethereum/core/src/test/java/net/consensys/pantheon/ethereum/util/BlockchainUtilParameterizedTest.java
  3. 2
      pantheon/src/main/java/tech/pegasys/pantheon/PantheonInfo.java

@ -0,0 +1,52 @@
package net.consensys.pantheon.ethereum.util;
import tech.pegasys.pantheon.ethereum.chain.Blockchain;
import tech.pegasys.pantheon.ethereum.core.BlockHeader;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.OptionalInt;
public class BlockchainUtil {
private BlockchainUtil() {}
/**
* General purpose utility to process a list of headers and a blockchain, sussing out which header
* in the input list is simultaneously the highest order block number and a direct match with one
* of the headers of the local chain. The point being to determine the point of departure in fork
* situations.
*
* @param blockchain our local copy of the blockchain
* @param headers the list of remote headers
* @param ascendingHeaderOrder whether the headers are sorted in ascending or descending order
* @return index of the highest known header, or an empty value if no header is known
*/
public static OptionalInt findHighestKnownBlockIndex(
final Blockchain blockchain,
final List<BlockHeader> headers,
final boolean ascendingHeaderOrder) {
int offset = ascendingHeaderOrder ? -1 : 0;
Comparator<BlockHeader> comparator = knownBlockComparator(blockchain, ascendingHeaderOrder);
int insertionIndex = -Collections.binarySearch(headers, null, comparator) - 1;
int ancestorIndex = insertionIndex + offset;
if (ancestorIndex < 0 || ancestorIndex >= headers.size()) {
return OptionalInt.empty();
}
return OptionalInt.of(ancestorIndex);
}
private static Comparator<BlockHeader> knownBlockComparator(
final Blockchain blockchain, final boolean ascendingHeaderOrder) {
Comparator<BlockHeader> comparator =
(final BlockHeader element0, final BlockHeader element1) -> {
if (element0 == null) {
return blockchain.contains(element1.getHash()) ? -1 : 1;
}
return blockchain.contains(element0.getHash()) ? 1 : -1;
};
return ascendingHeaderOrder ? comparator.reversed() : comparator;
}
}

@ -0,0 +1,135 @@
package net.consensys.pantheon.ethereum.util;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.ethereum.core.Block;
import tech.pegasys.pantheon.ethereum.core.BlockBody;
import tech.pegasys.pantheon.ethereum.core.BlockHeader;
import tech.pegasys.pantheon.ethereum.core.TransactionReceipt;
import tech.pegasys.pantheon.ethereum.db.DefaultMutableBlockchain;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetBlockHashFunction;
import tech.pegasys.pantheon.ethereum.testutil.BlockDataGenerator;
import tech.pegasys.pantheon.services.kvstore.InMemoryKeyValueStorage;
import tech.pegasys.pantheon.services.kvstore.KeyValueStorage;
import tech.pegasys.pantheon.util.uint.UInt256;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.OptionalInt;
import java.util.Random;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class BlockchainUtilParameterizedTest {
private static final BlockDataGenerator blockDataGenerator = new BlockDataGenerator();
private static final Random random = new Random(1337);
private static final int chainHeight = 89;
private final int commonAncestorHeight;
private static Block genesisBlock;
private static KeyValueStorage localKvStore;
private static DefaultMutableBlockchain localBlockchain;
private KeyValueStorage remoteKvStore;
private DefaultMutableBlockchain remoteBlockchain;
private BlockHeader commonHeader;
private List<BlockHeader> headers;
public BlockchainUtilParameterizedTest(final int commonAncestorHeight) {
this.commonAncestorHeight = commonAncestorHeight;
}
@BeforeClass
public static void setupClass() {
genesisBlock = blockDataGenerator.genesisBlock();
localKvStore = new InMemoryKeyValueStorage();
localBlockchain =
new DefaultMutableBlockchain(
genesisBlock, localKvStore, MainnetBlockHashFunction::createHash);
// Setup local chain.
for (int i = 1; i <= chainHeight; i++) {
final BlockDataGenerator.BlockOptions options =
new BlockDataGenerator.BlockOptions()
.setBlockNumber(i)
.setParentHash(localBlockchain.getBlockHashByNumber(i - 1).get());
final Block block = blockDataGenerator.block(options);
final List<TransactionReceipt> receipts = blockDataGenerator.receipts(block);
localBlockchain.appendBlock(block, receipts);
}
}
@Before
public void setup() {
remoteKvStore = new InMemoryKeyValueStorage();
remoteBlockchain =
new DefaultMutableBlockchain(
genesisBlock, remoteKvStore, MainnetBlockHashFunction::createHash);
commonHeader = genesisBlock.getHeader();
for (long i = 1; i <= commonAncestorHeight; i++) {
commonHeader = localBlockchain.getBlockHeader(i).get();
final List<TransactionReceipt> receipts =
localBlockchain.getTxReceipts(commonHeader.getHash()).get();
final BlockBody commonBody = localBlockchain.getBlockBody(commonHeader.getHash()).get();
remoteBlockchain.appendBlock(new Block(commonHeader, commonBody), receipts);
}
// Remaining blocks are disparate.
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(UInt256.ONE) // differentiator
.setBlockNumber(i)
.setParentHash(remoteBlockchain.getBlockHashByNumber(i - 1).get());
final Block remoteBlock = blockDataGenerator.block(remoteOptions);
final List<TransactionReceipt> remoteReceipts = blockDataGenerator.receipts(remoteBlock);
remoteBlockchain.appendBlock(remoteBlock, remoteReceipts);
}
headers = new ArrayList<>();
for (long i = 0L; i <= remoteBlockchain.getChainHeadBlockNumber(); i++) {
headers.add(remoteBlockchain.getBlockHeader(i).get());
}
}
@Parameterized.Parameters(name = "commonAncestor={0}")
public static Collection<Object[]> parameters() {
final List<Object[]> params = new ArrayList<>();
params.add(new Object[] {0});
params.add(new Object[] {chainHeight});
params.add(new Object[] {random.nextInt(chainHeight - 1) + 1});
params.add(new Object[] {random.nextInt(chainHeight - 1) + 1});
params.add(new Object[] {random.nextInt(chainHeight - 1) + 1});
return params;
}
@Test
public void searchesAscending() {
OptionalInt maybeAncestorNumber =
BlockchainUtil.findHighestKnownBlockIndex(localBlockchain, headers, true);
assertThat(maybeAncestorNumber.getAsInt()).isEqualTo(Math.toIntExact(commonHeader.getNumber()));
}
@Test
public void searchesDescending() {
Collections.reverse(headers);
OptionalInt maybeAncestorNumber =
BlockchainUtil.findHighestKnownBlockIndex(localBlockchain, headers, false);
assertThat(maybeAncestorNumber.getAsInt())
.isEqualTo(Math.toIntExact(chainHeight - commonHeader.getNumber()));
}
}

@ -2,7 +2,7 @@ package tech.pegasys.pantheon;
// This file is generated via a gradle task and should not be edited directly.
public class PantheonInfo {
private static final String version = "pantheon/0.8.0-SNAPSHOT";
private static final String version = "pantheon/0.0.8-SNAPSHOT";
private PantheonInfo() {}

Loading…
Cancel
Save