mirror of
https://github.com/vacp2p/linea-besu.git
synced 2026-01-09 15:37:54 -05:00
parameterize BlockchainUtilTest (#57)
Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>
This commit is contained in:
committed by
GitHub
parent
da4be65379
commit
56b0a10942
@@ -12,6 +12,17 @@ public class 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(
|
||||
final Blockchain blockchain,
|
||||
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()));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user