use of org.aion.zero.impl.types.MiningBlockHeader in project aion by aionnetwork.
the class BlockchainForkingTest method testRollbackWithAddInvalidBlock.
@Test
public void testRollbackWithAddInvalidBlock() {
StandaloneBlockchain.Builder builder = new StandaloneBlockchain.Builder();
StandaloneBlockchain.Bundle b = builder.withValidatorConfiguration("simple").build();
StandaloneBlockchain bc = b.bc;
MiningBlock block = bc.createNewMiningBlock(bc.getBestBlock(), Collections.emptyList(), true);
assertThat(bc.tryToConnect(block)).isEqualTo(ImportResult.IMPORTED_BEST);
// check that the returned block is the first block
assertThat(bc.getBestBlock() == block).isTrue();
MiningBlock invalidBlock = bc.createNewMiningBlock(bc.getBestBlock(), Collections.emptyList(), true);
MiningBlockHeader newBlockHeader = MiningBlockHeader.Builder.newInstance().withHeader(invalidBlock.getHeader()).withDifficulty(BigInteger.ONE.toByteArray()).build();
invalidBlock.updateHeader(newBlockHeader);
// attempting to add invalid block
assertThat(bc.tryToConnect(invalidBlock)).isEqualTo(ImportResult.INVALID_BLOCK);
// check for correct state rollback
assertThat(bc.getBestBlock()).isEqualTo(block);
assertThat(bc.getRepository().getRoot()).isEqualTo(block.getStateRoot());
assertThat(bc.getTotalDifficulty()).isEqualTo(bc.getTotalDifficultyForHash(block.getHash()));
}
use of org.aion.zero.impl.types.MiningBlockHeader in project aion by aionnetwork.
the class BlockchainForkingTest method testVmTypeRetrieval_ForkWithConflictingContractVM.
/**
* Ensures that if a side-chain block is imported after a main-chain block creating the same
* contract address X but using different VMs, then each chain will operate on the correct VM.
*/
@Test
public void testVmTypeRetrieval_ForkWithConflictingContractVM() throws Exception {
TestResourceProvider resourceProvider = TestResourceProvider.initializeAndCreateNewProvider(AvmPathManager.getPathOfProjectRootDirectory());
IAvmResourceFactory resourceFactory = resourceProvider.factoryForVersion1;
// blocks to be built
MiningBlock block, fastBlock, slowBlock, lowBlock, highBlock;
// transactions used in blocks
AionTransaction deployOnAVM, deployOnFVM, callTxOnFVM;
// for processing block results
Pair<ImportResult, AionBlockSummary> connectResult;
ImportResult result;
AionTxReceipt receipt;
// build a blockchain
TransactionTypeRule.allowAVMContractTransaction();
List<ECKey> accounts = generateAccounts(10);
StandaloneBlockchain.Builder builder = new StandaloneBlockchain.Builder();
StandaloneBlockchain sourceChain = builder.withValidatorConfiguration("simple").withDefaultAccounts(accounts).build().bc;
StandaloneBlockchain testChain = builder.withValidatorConfiguration("simple").withDefaultAccounts(accounts).build().bc;
ECKey sender = accounts.remove(0);
assertThat(testChain).isNotEqualTo(sourceChain);
assertThat(testChain.genesis).isEqualTo(sourceChain.genesis);
long time = System.currentTimeMillis();
// add a block to both chains
block = sourceChain.createNewMiningBlockInternal(sourceChain.getBestBlock(), Collections.emptyList(), true, time / 10_000L).block;
assertThat(sourceChain.tryToConnect(block)).isEqualTo(ImportResult.IMPORTED_BEST);
assertThat(testChain.tryToConnect(block)).isEqualTo(ImportResult.IMPORTED_BEST);
// ****** setup side chain ******
// deploy contracts on different VMs for the two chains
deployOnAVM = deployStatefulnessAVMContract(resourceFactory, sender);
fastBlock = sourceChain.createNewMiningBlockInternal(sourceChain.getBestBlock(), Arrays.asList(deployOnAVM), true, time / 10_000L).block;
deployOnFVM = deployContract(sender);
slowBlock = new MiningBlock(sourceChain.createNewMiningBlockInternal(sourceChain.getBestBlock(), Arrays.asList(deployOnFVM), true, time / 10_000L).block);
MiningBlockHeader newBlockHeader = MiningBlockHeader.Builder.newInstance().withHeader(slowBlock.getHeader()).withTimestamp(time / 10_000L + 100).build();
slowBlock.updateHeader(newBlockHeader);
time += 100;
// sourceChain imports only fast block
connectResult = sourceChain.tryToConnectAndFetchSummary(fastBlock);
result = connectResult.getLeft();
receipt = connectResult.getRight().getReceipts().get(0);
assertThat(result).isEqualTo(ImportResult.IMPORTED_BEST);
assertThat(receipt.isSuccessful()).isTrue();
AionAddress contract = TxUtil.calculateContractAddress(receipt.getTransaction());
// testChain imports both blocks
connectResult = testChain.tryToConnectAndFetchSummary(fastBlock);
result = connectResult.getLeft();
receipt = connectResult.getRight().getReceipts().get(0);
assertThat(result).isEqualTo(ImportResult.IMPORTED_BEST);
assertThat(receipt.isSuccessful()).isTrue();
assertThat(TxUtil.calculateContractAddress(receipt.getTransaction())).isEqualTo(contract);
connectResult = testChain.tryToConnectAndFetchSummary(slowBlock);
result = connectResult.getLeft();
receipt = connectResult.getRight().getReceipts().get(0);
assertThat(result).isEqualTo(ImportResult.IMPORTED_NOT_BEST);
assertThat(receipt.isSuccessful()).isTrue();
assertThat(TxUtil.calculateContractAddress(receipt.getTransaction())).isEqualTo(contract);
// ****** check that the correct contract details are kept ******
// check that both chains have the correct vm type for the AVM contract
byte[] codeHashAVM = sourceChain.getRepository().getAccountState(contract).getCodeHash();
assertThat(testChain.getRepository().getVMUsed(contract, codeHashAVM)).isEqualTo(sourceChain.getRepository().getVMUsed(contract, codeHashAVM));
// check that only the second chain has the vm type for the FVM contract
byte[] codeHashFVM = ((AionRepositoryImpl) testChain.getRepository().getSnapshotTo(slowBlock.getStateRoot())).getAccountState(contract).getCodeHash();
assertThat(sourceChain.getRepository().getVMUsed(contract, codeHashFVM)).isEqualTo(InternalVmType.UNKNOWN);
assertThat(testChain.getRepository().getVMUsed(contract, codeHashFVM)).isEqualTo(InternalVmType.FVM);
// check the stored information details
ContractInformation infoSingleImport = sourceChain.getRepository().getIndexedContractInformation(contract);
System.out.println("without side chain:" + infoSingleImport);
assertThat(infoSingleImport.getVmUsed(codeHashAVM)).isEqualTo(InternalVmType.AVM);
assertThat(infoSingleImport.getInceptionBlocks(codeHashAVM)).isEqualTo(Set.of(fastBlock.getHashWrapper()));
assertThat(infoSingleImport.getVmUsed(codeHashFVM)).isEqualTo(InternalVmType.UNKNOWN);
assertThat(infoSingleImport.getInceptionBlocks(codeHashFVM)).isEmpty();
ContractInformation infoMultiImport = testChain.getRepository().getIndexedContractInformation(contract);
System.out.println("with side chain:" + infoMultiImport);
assertThat(infoMultiImport.getVmUsed(codeHashAVM)).isEqualTo(InternalVmType.AVM);
assertThat(infoMultiImport.getInceptionBlocks(codeHashAVM)).isEqualTo(Set.of(fastBlock.getHashWrapper()));
assertThat(infoMultiImport.getVmUsed(codeHashFVM)).isEqualTo(InternalVmType.FVM);
assertThat(infoMultiImport.getInceptionBlocks(codeHashFVM)).isEqualTo(Set.of(slowBlock.getHashWrapper()));
// build two blocks where the second block has a higher total difficulty
callTxOnFVM = callSetValue(sender, contract, 9, BigInteger.ONE);
lowBlock = testChain.createNewMiningBlockInternal(slowBlock, Arrays.asList(callTxOnFVM), true, time / 10_000L + 101).block;
int expectedCount = 3;
List<AionTransaction> callTxOnAVM = callStatefulnessAVM(resourceFactory, sender, expectedCount, BigInteger.ONE, contract);
highBlock = sourceChain.createNewMiningBlockInternal(fastBlock, callTxOnAVM, true, time / 10_000L).block;
assertThat(highBlock.getDifficultyBI()).isGreaterThan(lowBlock.getDifficultyBI());
// build first chain with highBlock applied directly
connectResult = sourceChain.tryToConnectAndFetchSummary(highBlock);
// get last tx
receipt = connectResult.getRight().getReceipts().get(expectedCount);
assertThat(receipt.isSuccessful()).isTrue();
result = connectResult.getLeft();
assertThat(result).isEqualTo(ImportResult.IMPORTED_BEST);
// collect the consensus information from the block & receipt
AionBlockSummary blockSummary = connectResult.getRight();
byte[] stateRoot = blockSummary.getBlock().getStateRoot();
byte[] blockReceiptsRoot = blockSummary.getBlock().getReceiptsRoot();
byte[] receiptTrieEncoded = receipt.getReceiptTrieEncoded();
int returnedCount = resourceFactory.newDecoder(blockSummary.getReceipts().get(expectedCount).getTransactionOutput()).decodeOneInteger();
assertThat(returnedCount).isEqualTo(expectedCount);
// ****** test fork behavior ******
// first import lowBlock
assertThat(testChain.tryToConnect(lowBlock)).isEqualTo(ImportResult.IMPORTED_BEST);
// next import highBlock causing the fork
connectResult = testChain.tryToConnectAndFetchSummary(highBlock);
receipt = connectResult.getRight().getReceipts().get(expectedCount);
assertThat(receipt.isSuccessful()).isTrue();
System.out.println(receipt);
result = connectResult.getLeft();
assertThat(result).isEqualTo(ImportResult.IMPORTED_BEST);
// collect the consensus information from the block & receipt
blockSummary = connectResult.getRight();
assertThat(testChain.getBestBlock()).isEqualTo(sourceChain.getBestBlock());
assertThat(blockSummary.getBlock().getStateRoot()).isEqualTo(stateRoot);
assertThat(blockSummary.getBlock().getReceiptsRoot()).isEqualTo(blockReceiptsRoot);
assertThat(receipt.getReceiptTrieEncoded()).isEqualTo(receiptTrieEncoded);
returnedCount = resourceFactory.newDecoder(blockSummary.getReceipts().get(expectedCount).getTransactionOutput()).decodeOneInteger();
assertThat(returnedCount).isEqualTo(expectedCount);
}
use of org.aion.zero.impl.types.MiningBlockHeader in project aion by aionnetwork.
the class BlockchainConcurrentImportTest method addThread_createNewBlock.
/**
* Adds a new thread for creating a new block with a parent among the already known blocks.
*
* @param _threads list of threads to be executed; the current thread will be added to this list
* @param _chain the blockchain where the blocks will be imported
* @param _parent the block that will be the parent of the newly created block
* @param _id number used for identifying the block; added as extra data
* @param _queue a queue for storing the new blocks; to be imported by a separate thread
*/
private void addThread_createNewBlock(List<Runnable> _threads, StandaloneBlockchain _chain, MiningBlock _parent, int _id, ConcurrentLinkedQueue<MiningBlock> _queue) {
_threads.add(() -> {
// creating block only if parent already imported
if (_chain.isBlockStored(_parent.getHash(), _parent.getNumber())) {
testChain.assertEqualTotalDifficulty();
// only some of these txs may be valid
// cannot syncToRoot due to concurrency issues
AionRepositoryImpl repo = _chain.getRepository();
List<AionTransaction> txs = generateTransactions(MAX_TX_PER_BLOCK, accounts, repo);
MiningBlock block = null;
try {
block = _chain.createNewMiningBlock(_parent, txs, true);
MiningBlockHeader newBlockHeader = MiningBlockHeader.Builder.newInstance().withHeader(block.getHeader()).withExtraData(String.valueOf(_id).getBytes()).build();
block.updateHeader(newBlockHeader);
} catch (Exception e) {
e.printStackTrace();
}
testChain.assertEqualTotalDifficulty();
// checking if the new block was already imported
if (!_chain.isBlockStored(block.getHash(), block.getNumber())) {
// still adding this block
_queue.add(block);
if (DISPLAY_MESSAGES) {
System.out.format("Create block with hash: %s, number: %6d, extra data: %6s, txs: %3d, parent: %20s in thread: %20s %n", block.getShortHash(), block.getNumber(), new String(block.getExtraData()), block.getTransactionsList().size(), _parent.getShortHash(), Thread.currentThread().getName());
}
} else {
if (DISPLAY_MESSAGES) {
System.out.format("%57sBlock already imported. Skipping create in thread: %20s %n", " ", Thread.currentThread().getName());
}
}
} else {
if (DISPLAY_MESSAGES) {
System.out.format("%60sParent not imported. Skipping create in thread: %20s %n", " ", Thread.currentThread().getName());
}
}
});
}
use of org.aion.zero.impl.types.MiningBlockHeader in project aion by aionnetwork.
the class BlockchainConcurrentImportTest method generateBlocks.
private static void generateBlocks() {
System.out.format("%nGenerating %d input blocks...%n", CONCURRENT_THREADS_PER_TYPE);
Random rand = new Random();
MiningBlock parent, block, mainChain;
mainChain = sourceChain.getGenesis();
knownBlocks.add(mainChain);
List<AionTransaction> txs;
AionRepositoryImpl sourceRepo = sourceChain.getRepository();
long time = System.currentTimeMillis();
for (int i = 0; i < CONCURRENT_THREADS_PER_TYPE; i++) {
// ensuring that we add to the main chain at least every MAIN_CHAIN_FREQUENCY block
if (i % MAIN_CHAIN_FREQUENCY == 0) {
// the parent will be the main chain
parent = mainChain;
} else {
// the parent is a random already imported block
parent = knownBlocks.get(rand.nextInt(knownBlocks.size()));
}
// generate transactions for correct root
byte[] originalRoot = sourceRepo.getRoot();
sourceRepo.syncToRoot(parent.getStateRoot());
txs = generateTransactions(MAX_TX_PER_BLOCK, accounts, sourceRepo);
sourceRepo.syncToRoot(originalRoot);
block = sourceChain.createNewMiningBlockInternal(parent, txs, true, time / 10000L).block;
MiningBlockHeader newBlockHeader = MiningBlockHeader.Builder.newInstance().withHeader(block.getHeader()).withExtraData(String.valueOf(i).getBytes()).build();
block.updateHeader(newBlockHeader);
ImportResult result = sourceChain.tryToConnect(block);
knownBlocks.add(block);
if (result == ImportResult.IMPORTED_BEST) {
mainChain = block;
}
if (DISPLAY_MESSAGES) {
System.out.format("Created block with hash: %s, number: %6d, extra data: %6s, txs: %3d, import status: %20s %n", block.getShortHash(), block.getNumber(), new String(block.getExtraData()), block.getTransactionsList().size(), result.toString());
}
}
// all blocks except the genesis will be imported by the other chain
knownBlocks.remove(sourceChain.getGenesis());
}
use of org.aion.zero.impl.types.MiningBlockHeader in project aion by aionnetwork.
the class BlockchainIndexIntegrityTest method testIndexIntegrityWithCorrectData.
/**
* Test the index integrity check and recovery when the index database is correct.
*/
@Test
public void testIndexIntegrityWithCorrectData() {
final int NUMBER_OF_BLOCKS = 5;
// build a blockchain with a few blocks
StandaloneBlockchain.Builder builder = new StandaloneBlockchain.Builder();
StandaloneBlockchain.Bundle bundle = builder.withValidatorConfiguration("simple").build();
StandaloneBlockchain chain = bundle.bc;
Block bestBlock;
ImportResult result;
for (int i = 0; i < NUMBER_OF_BLOCKS; i++) {
bestBlock = chain.getBestBlock();
MiningBlock next = chain.createNewMiningBlock(bestBlock, Collections.emptyList(), true);
result = chain.tryToConnect(next);
assertThat(result).isEqualTo(ImportResult.IMPORTED_BEST);
// adding side chain
next = chain.createNewMiningBlock(bestBlock, Collections.emptyList(), true);
MiningBlockHeader newBlockHeader = MiningBlockHeader.Builder.newInstance().withHeader(next.getHeader()).withExtraData("other".getBytes()).build();
next.updateHeader(newBlockHeader);
result = chain.tryToConnect(next);
assertThat(result).isEqualTo(ImportResult.IMPORTED_NOT_BEST);
}
bestBlock = chain.getBestBlock();
assertThat(bestBlock.getNumber()).isEqualTo(NUMBER_OF_BLOCKS);
chain.getRepository().flush();
AionRepositoryImpl repo = chain.getRepository();
AionBlockStore blockStore = repo.getBlockStore();
// check that the index recovery succeeded
assertThat(blockStore.indexIntegrityCheck()).isEqualTo(AionBlockStore.IntegrityCheckResult.CORRECT);
}
Aggregations