parameterize BlockchainUtilTest (#57)

S. Matthew English 6 years ago committed by GitHub
parent 92fefe60e5
commit d38a5a3f84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 11
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/util/BlockchainUtil.java
  2. 135
      ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/util/BlockchainUtilParameterizedTest.java
  3. 119
      ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/util/BlockchainUtilTest.java

@ -12,6 +12,17 @@ public class BlockchainUtil {
private BlockchainUtil() {} private BlockchainUtil() {}
/**
* General 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 purpose of which being to determine the point of departure in
* fork scenarios.
*
* @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( public static OptionalInt findHighestKnownBlockIndex(
final Blockchain blockchain, final Blockchain blockchain,
final List<BlockHeader> headers, final List<BlockHeader> headers,

@ -0,0 +1,135 @@
package tech.pegasys.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()));
}
}

@ -1,119 +0,0 @@
package tech.pegasys.pantheon.ethereum.util;
import static org.assertj.core.api.Assertions.assertThat;
import static sun.security.krb5.Confounder.bytes;
import tech.pegasys.pantheon.ethereum.core.Address;
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.BlockHeaderBuilder;
import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.ethereum.core.LogsBloomFilter;
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.bytes.Bytes32;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import tech.pegasys.pantheon.util.uint.UInt256;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.OptionalInt;
import org.junit.Test;
public class BlockchainUtilTest {
@Test
public void shouldReturnIndexOfCommonBlockForAscendingOrder() {
BlockDataGenerator blockDataGenerator = new BlockDataGenerator();
BlockHeader genesisHeader =
BlockHeaderBuilder.create()
.parentHash(Hash.ZERO)
.ommersHash(Hash.ZERO)
.coinbase(Address.fromHexString("0x0000000000000000000000000000000000000000"))
.stateRoot(Hash.ZERO)
.transactionsRoot(Hash.ZERO)
.receiptsRoot(Hash.ZERO)
.logsBloom(new LogsBloomFilter(BytesValue.of(bytes(LogsBloomFilter.BYTE_SIZE))))
.difficulty(UInt256.ZERO)
.number(0L)
.gasLimit(1L)
.gasUsed(1L)
.timestamp(Instant.now().truncatedTo(ChronoUnit.SECONDS).getEpochSecond())
.extraData(Bytes32.wrap(bytes(Bytes32.SIZE)))
.mixHash(Hash.ZERO)
.nonce(0L)
.blockHashFunction(MainnetBlockHashFunction::createHash)
.buildBlockHeader();
BlockBody genesisBody = new BlockBody(Collections.emptyList(), Collections.emptyList());
Block genesisBlock = new Block(genesisHeader, genesisBody);
KeyValueStorage kvStoreLocal = new InMemoryKeyValueStorage();
KeyValueStorage kvStoreRemote = new InMemoryKeyValueStorage();
DefaultMutableBlockchain blockchainLocal =
new DefaultMutableBlockchain(
genesisBlock, kvStoreLocal, MainnetBlockHashFunction::createHash);
DefaultMutableBlockchain blockchainRemote =
new DefaultMutableBlockchain(
genesisBlock, kvStoreRemote, MainnetBlockHashFunction::createHash);
// Common chain segment
Block commonBlock = null;
for (long i = 1; i <= 3; i++) {
BlockDataGenerator.BlockOptions options =
new BlockDataGenerator.BlockOptions()
.setBlockNumber(i)
.setParentHash(blockchainLocal.getBlockHashByNumber(i - 1).get());
commonBlock = blockDataGenerator.block(options);
List<TransactionReceipt> receipts = blockDataGenerator.receipts(commonBlock);
blockchainLocal.appendBlock(commonBlock, receipts);
blockchainRemote.appendBlock(commonBlock, receipts);
}
// Populate local chain
for (long i = 4; i <= 9; i++) {
BlockDataGenerator.BlockOptions optionsLocal =
new BlockDataGenerator.BlockOptions()
.setDifficulty(UInt256.ZERO) // differentiator
.setBlockNumber(i)
.setParentHash(blockchainLocal.getBlockHashByNumber(i - 1).get());
Block blockLocal = blockDataGenerator.block(optionsLocal);
List<TransactionReceipt> receiptsLocal = blockDataGenerator.receipts(blockLocal);
blockchainLocal.appendBlock(blockLocal, receiptsLocal);
}
// Populate remote chain
for (long i = 4; i <= 9; i++) {
BlockDataGenerator.BlockOptions optionsRemote =
new BlockDataGenerator.BlockOptions()
.setDifficulty(UInt256.ONE)
.setBlockNumber(i)
.setParentHash(blockchainRemote.getBlockHashByNumber(i - 1).get());
Block blockRemote = blockDataGenerator.block(optionsRemote);
List<TransactionReceipt> receiptsRemote = blockDataGenerator.receipts(blockRemote);
blockchainRemote.appendBlock(blockRemote, receiptsRemote);
}
// Create a list of headers...
List<BlockHeader> headers = new ArrayList<>();
for (long i = 0L; i < blockchainRemote.getChainHeadBlockNumber(); i++) {
headers.add(blockchainRemote.getBlockHeader(i).get());
}
OptionalInt maybeAncestorNumber =
BlockchainUtil.findHighestKnownBlockIndex(blockchainLocal, headers, true);
assertThat(maybeAncestorNumber.getAsInt())
.isEqualTo(Math.toIntExact(commonBlock.getHeader().getNumber()));
}
}
Loading…
Cancel
Save