use of org.aion.zero.impl.types.BlockContext in project aion by aionnetwork.
the class BlockchainDataRecoveryTest method testRecoverIndexWithStartFromGenesis.
/**
* Test the index recovery when the index database contains only the size and genesis index.
*/
@Test
public void testRecoverIndexWithStartFromGenesis() {
// build a blockchain with a few blocks
StandaloneBlockchain.Builder builder = new StandaloneBlockchain.Builder();
StandaloneBlockchain.Bundle bundle = builder.withValidatorConfiguration("simple").withDefaultAccounts(accounts).build();
StandaloneBlockchain chain = bundle.bc;
AionRepositoryImpl repo = chain.getRepository();
BlockContext context;
List<AionTransaction> txs;
// all blocks will be incorrect
long time = System.currentTimeMillis();
Map<Long, byte[]> blocksToDelete = new HashMap<>();
List<MiningBlock> blocksToImport = new ArrayList<>();
for (int i = 0; i < NUMBER_OF_BLOCKS; i++) {
txs = BlockchainTestUtils.generateTransactions(MAX_TX_PER_BLOCK, accounts, repo);
context = chain.createNewMiningBlockInternal(chain.getBestBlock(), txs, true, time / 10000L);
assertThat(chain.tryToConnect(context.block)).isEqualTo(ImportResult.IMPORTED_BEST);
blocksToDelete.put(context.block.getNumber(), context.block.getHash());
blocksToImport.add(context.block);
}
Block bestBlock = chain.getBestBlock();
assertThat(bestBlock.getNumber()).isEqualTo(NUMBER_OF_BLOCKS);
// delete index entries from the database
MockDB indexDatabase = (MockDB) repo.getIndexDatabase();
// 1: direct recovery call
repo.flush();
Map<Long, byte[]> deletedInfo = new HashMap<>();
for (Map.Entry<Long, byte[]> entry : blocksToDelete.entrySet()) {
byte[] indexKey = ByteUtil.intToBytes(entry.getKey().intValue());
// saving the data for checking recovery
deletedInfo.put(entry.getKey(), indexDatabase.get(indexKey).get());
// deleting the block info
indexDatabase.deleteAndCommit(indexKey);
// ensure that the index was corrupted
assertThat(repo.isIndexed(entry.getValue(), entry.getKey())).isFalse();
}
// ensure that the index was corrupted
assertThat(repo.isIndexed(bestBlock.getHash(), bestBlock.getNumber())).isFalse();
// call the recovery functionality
boolean worked = chain.recoverIndexEntry(repo, bestBlock);
// ensure that the blockchain is ok
assertThat(chain.getBestBlockHash()).isEqualTo(bestBlock.getHash());
// ensure that the index was recovered
assertThat(worked).isTrue();
assertThat(repo.isIndexed(bestBlock.getHash(), bestBlock.getNumber())).isTrue();
// check that the index information is correct
for (Map.Entry<Long, byte[]> entry : blocksToDelete.entrySet()) {
long level = entry.getKey();
byte[] hash = entry.getValue();
// checking at block store level
assertThat(repo.isIndexed(hash, level)).isTrue();
// checking at database level
byte[] indexKey = ByteUtil.intToBytes(entry.getKey().intValue());
assertThat(deletedInfo.get(level)).isEqualTo(indexDatabase.get(indexKey).get());
}
// 2: recovery at import
repo.flush();
for (Map.Entry<Long, byte[]> entry : blocksToDelete.entrySet()) {
byte[] indexKey = ByteUtil.intToBytes(entry.getKey().intValue());
// deleting the block info
indexDatabase.deleteAndCommit(indexKey);
// ensure that the index was corrupted
assertThat(repo.isIndexed(entry.getValue(), entry.getKey())).isFalse();
}
// ensure that the index was corrupted
assertThat(repo.isIndexed(bestBlock.getHash(), bestBlock.getNumber())).isFalse();
// call the recovery functionality indirectly
for (MiningBlock block : blocksToImport) {
// index missing before import
assertThat(repo.isIndexed(block.getHash(), block.getNumber())).isFalse();
assertThat(chain.tryToConnect(block)).isEqualTo(ImportResult.EXIST);
// index present after import
assertThat(repo.isIndexed(block.getHash(), block.getNumber())).isTrue();
}
// ensure that the blockchain is ok
assertThat(chain.getBestBlockHash()).isEqualTo(bestBlock.getHash());
// ensure that the index was recovered
assertThat(repo.isIndexed(bestBlock.getHash(), bestBlock.getNumber())).isTrue();
// check that the index information is correct at database level
for (Long key : blocksToDelete.keySet()) {
// checking at database level
byte[] indexKey = ByteUtil.intToBytes(key.intValue());
assertThat(deletedInfo.get(key)).isEqualTo(indexDatabase.get(indexKey).get());
}
}
use of org.aion.zero.impl.types.BlockContext in project aion by aionnetwork.
the class BlockchainDataRecoveryTest method testRecoverIndexWithStartFromGenesisWithoutSize.
/**
* Test the index recovery when the index database contains only the genesis index and is
* missing the size key.
*/
@Test
public void testRecoverIndexWithStartFromGenesisWithoutSize() {
// build a blockchain with a few blocks
StandaloneBlockchain.Builder builder = new StandaloneBlockchain.Builder();
StandaloneBlockchain.Bundle bundle = builder.withValidatorConfiguration("simple").withDefaultAccounts(accounts).build();
StandaloneBlockchain chain = bundle.bc;
AionRepositoryImpl repo = chain.getRepository();
BlockContext context;
List<AionTransaction> txs;
// all blocks will be incorrect
long time = System.currentTimeMillis();
Map<Long, byte[]> blocksToDelete = new HashMap<>();
List<MiningBlock> blocksToImport = new ArrayList<>();
for (int i = 0; i < NUMBER_OF_BLOCKS; i++) {
txs = BlockchainTestUtils.generateTransactions(MAX_TX_PER_BLOCK, accounts, repo);
context = chain.createNewMiningBlockInternal(chain.getBestBlock(), txs, true, time / 10000L);
assertThat(chain.tryToConnect(context.block)).isEqualTo(ImportResult.IMPORTED_BEST);
blocksToDelete.put(context.block.getNumber(), context.block.getHash());
blocksToImport.add(context.block);
}
Block bestBlock = chain.getBestBlock();
assertThat(bestBlock.getNumber()).isEqualTo(NUMBER_OF_BLOCKS);
// delete index entries from the database
MockDB indexDatabase = (MockDB) repo.getIndexDatabase();
// 1: direct recovery call
repo.flush();
Map<Long, byte[]> deletedInfo = new HashMap<>();
for (Map.Entry<Long, byte[]> entry : blocksToDelete.entrySet()) {
byte[] indexKey = ByteUtil.intToBytes(entry.getKey().intValue());
// saving the data for checking recovery
deletedInfo.put(entry.getKey(), indexDatabase.get(indexKey).get());
// deleting the block info
indexDatabase.deleteAndCommit(indexKey);
// ensure that the index was corrupted
assertThat(repo.isIndexed(entry.getValue(), entry.getKey())).isFalse();
}
// ensure that the index was corrupted
assertThat(repo.isIndexed(bestBlock.getHash(), bestBlock.getNumber())).isFalse();
// call the recovery functionality
boolean worked = chain.recoverIndexEntry(repo, bestBlock);
// ensure that the blockchain is ok
assertThat(chain.getBestBlockHash()).isEqualTo(bestBlock.getHash());
// ensure that the index was recovered
assertThat(worked).isTrue();
assertThat(repo.isIndexed(bestBlock.getHash(), bestBlock.getNumber())).isTrue();
// check that the index information is correct
for (Map.Entry<Long, byte[]> entry : blocksToDelete.entrySet()) {
long level = entry.getKey();
byte[] hash = entry.getValue();
// checking at block store level
assertThat(repo.isIndexed(hash, level)).isTrue();
// checking at database level
byte[] indexKey = ByteUtil.intToBytes(entry.getKey().intValue());
assertThat(deletedInfo.get(level)).isEqualTo(indexDatabase.get(indexKey).get());
}
// 2: recovery at import
repo.flush();
for (Map.Entry<Long, byte[]> entry : blocksToDelete.entrySet()) {
byte[] indexKey = ByteUtil.intToBytes(entry.getKey().intValue());
// deleting the block info
indexDatabase.deleteAndCommit(indexKey);
// ensure that the index was corrupted
assertThat(repo.isIndexed(entry.getValue(), entry.getKey())).isFalse();
}
// ensure that the index was corrupted
assertThat(repo.isIndexed(bestBlock.getHash(), bestBlock.getNumber())).isFalse();
// call the recovery functionality indirectly
for (MiningBlock block : blocksToImport) {
// index missing before import
assertThat(repo.isIndexed(block.getHash(), block.getNumber())).isFalse();
assertThat(chain.tryToConnect(block)).isEqualTo(ImportResult.EXIST);
// index present after import
assertThat(repo.isIndexed(block.getHash(), block.getNumber())).isTrue();
}
// ensure that the blockchain is ok
assertThat(chain.getBestBlockHash()).isEqualTo(bestBlock.getHash());
// ensure that the index was recovered
assertThat(repo.isIndexed(bestBlock.getHash(), bestBlock.getNumber())).isTrue();
// check that the index information is correct at database level
for (Long key : blocksToDelete.keySet()) {
// checking at database level
byte[] indexKey = ByteUtil.intToBytes(key.intValue());
assertThat(deletedInfo.get(key)).isEqualTo(indexDatabase.get(indexKey).get());
}
}
use of org.aion.zero.impl.types.BlockContext in project aion by aionnetwork.
the class BlockchainDataRecoveryTest method testRecoverIndexWithPartialIndex_MainChain.
/**
* Test the recovery of the index with start from the index of an ancestor block.
*/
@Test
public void testRecoverIndexWithPartialIndex_MainChain() {
// build a blockchain with a few blocks
StandaloneBlockchain.Builder builder = new StandaloneBlockchain.Builder();
StandaloneBlockchain.Bundle bundle = builder.withValidatorConfiguration("simple").withDefaultAccounts(accounts).build();
StandaloneBlockchain chain = bundle.bc;
AionRepositoryImpl repo = chain.getRepository();
BlockContext context;
List<AionTransaction> txs;
// first half of blocks will be correct
long time = System.currentTimeMillis();
for (int i = 0; i < NUMBER_OF_BLOCKS / 2; i++) {
txs = BlockchainTestUtils.generateTransactions(MAX_TX_PER_BLOCK, accounts, repo);
context = chain.createNewMiningBlockInternal(chain.getBestBlock(), txs, true, time / 10000L);
assertThat(chain.tryToConnect(context.block)).isEqualTo(ImportResult.IMPORTED_BEST);
}
// second half of blocks will miss the index
Map<Long, byte[]> blocksToDelete = new HashMap<>();
List<MiningBlock> blocksToImport = new ArrayList<>();
for (int i = 0; i < NUMBER_OF_BLOCKS / 2; i++) {
txs = BlockchainTestUtils.generateTransactions(MAX_TX_PER_BLOCK, accounts, repo);
context = chain.createNewMiningBlockInternal(chain.getBestBlock(), txs, true, time / 10000L);
assertThat(chain.tryToConnect(context.block)).isEqualTo(ImportResult.IMPORTED_BEST);
blocksToImport.add(context.block);
blocksToDelete.put(context.block.getNumber(), context.block.getHash());
}
Block bestBlock = chain.getBestBlock();
assertThat(bestBlock.getNumber()).isEqualTo(NUMBER_OF_BLOCKS);
// delete index entries from the database
MockDB indexDatabase = (MockDB) repo.getIndexDatabase();
// 1: direct recovery call
repo.flush();
Map<Long, byte[]> deletedInfo = new HashMap<>();
for (Map.Entry<Long, byte[]> entry : blocksToDelete.entrySet()) {
byte[] indexKey = ByteUtil.intToBytes(entry.getKey().intValue());
// saving the data for checking recovery
deletedInfo.put(entry.getKey(), indexDatabase.get(indexKey).get());
// deleting the block info
indexDatabase.deleteAndCommit(indexKey);
// ensure that the index was corrupted
assertThat(repo.isIndexed(entry.getValue(), entry.getKey())).isFalse();
}
// ensure that the index was corrupted
assertThat(repo.isIndexed(bestBlock.getHash(), bestBlock.getNumber())).isFalse();
// call the recovery functionality
boolean worked = chain.recoverIndexEntry(repo, bestBlock);
// ensure that the blockchain is ok
assertThat(chain.getBestBlockHash()).isEqualTo(bestBlock.getHash());
// ensure that the index was recovered
assertThat(worked).isTrue();
assertThat(repo.isIndexed(bestBlock.getHash(), bestBlock.getNumber())).isTrue();
// check that the index information is correct
for (Map.Entry<Long, byte[]> entry : blocksToDelete.entrySet()) {
long level = entry.getKey();
byte[] hash = entry.getValue();
// checking at block store level
assertThat(repo.isIndexed(hash, level)).isTrue();
// checking at database level
byte[] indexKey = ByteUtil.intToBytes(entry.getKey().intValue());
assertThat(deletedInfo.get(level)).isEqualTo(indexDatabase.get(indexKey).get());
}
// 2: recovery at import
repo.flush();
for (Map.Entry<Long, byte[]> entry : blocksToDelete.entrySet()) {
byte[] indexKey = ByteUtil.intToBytes(entry.getKey().intValue());
// deleting the block info
indexDatabase.deleteAndCommit(indexKey);
// ensure that the index was corrupted
assertThat(repo.isIndexed(entry.getValue(), entry.getKey())).isFalse();
}
// ensure that the index was corrupted
assertThat(repo.isIndexed(bestBlock.getHash(), bestBlock.getNumber())).isFalse();
// call the recovery functionality indirectly
for (MiningBlock block : blocksToImport) {
// index missing before import
assertThat(repo.isIndexed(block.getHash(), block.getNumber())).isFalse();
assertThat(chain.tryToConnect(block)).isEqualTo(ImportResult.EXIST);
// index present after import
assertThat(repo.isIndexed(block.getHash(), block.getNumber())).isTrue();
}
// ensure that the blockchain is ok
assertThat(chain.getBestBlockHash()).isEqualTo(bestBlock.getHash());
// ensure that the index was recovered
assertThat(repo.isIndexed(bestBlock.getHash(), bestBlock.getNumber())).isTrue();
// check that the index information is correct at database level
for (Long key : blocksToDelete.keySet()) {
// checking at database level
byte[] indexKey = ByteUtil.intToBytes(key.intValue());
assertThat(deletedInfo.get(key)).isEqualTo(indexDatabase.get(indexKey).get());
}
}
use of org.aion.zero.impl.types.BlockContext in project aion by aionnetwork.
the class BlockchainDataRecoveryTest method testRecoverWorldState_wRepositorySnapshot.
@Test
public void testRecoverWorldState_wRepositorySnapshot() {
StandaloneBlockchain.Builder builder = new StandaloneBlockchain.Builder();
StandaloneBlockchain.Bundle bundle = builder.withValidatorConfiguration("simple").withDefaultAccounts(accounts).build();
StandaloneBlockchain chain = bundle.bc;
AionRepositoryImpl repo = chain.getRepository();
BlockContext context;
List<AionTransaction> txs;
// all blocks will be incorrect
long time = System.currentTimeMillis();
for (int i = 0; i < NUMBER_OF_BLOCKS; i++) {
txs = BlockchainTestUtils.generateTransactions(MAX_TX_PER_BLOCK, accounts, repo);
context = chain.createNewMiningBlockInternal(chain.getBestBlock(), txs, true, time / 10000L);
assertThat(chain.tryToConnect(context.block)).isEqualTo(ImportResult.IMPORTED_BEST);
}
repo.flush();
assertThat(chain.recoverWorldState(repo.getSnapshotTo(repo.getRoot()), chain.getBestBlock())).isFalse();
}
use of org.aion.zero.impl.types.BlockContext in project aion by aionnetwork.
the class BlockchainDataRecoveryTest method testRecoverWorldStateWithPartialWorldState.
/**
* Test the recovery of the world state with start from the state of an ancestor block.
*/
@Test
public void testRecoverWorldStateWithPartialWorldState() {
// build a blockchain with a few blocks
StandaloneBlockchain.Builder builder = new StandaloneBlockchain.Builder();
StandaloneBlockchain.Bundle bundle = builder.withValidatorConfiguration("simple").withDefaultAccounts(accounts).build();
StandaloneBlockchain chain = bundle.bc;
AionRepositoryImpl repo = chain.getRepository();
BlockContext context;
List<AionTransaction> txs;
// first half of blocks will be correct
long time = System.currentTimeMillis();
for (int i = 0; i < NUMBER_OF_BLOCKS / 2; i++) {
txs = BlockchainTestUtils.generateTransactions(MAX_TX_PER_BLOCK, accounts, repo);
context = chain.createNewMiningBlockInternal(chain.getBestBlock(), txs, true, time / 10000L);
assertThat(chain.tryToConnect(context.block)).isEqualTo(ImportResult.IMPORTED_BEST);
}
// second half of blocks will miss the state root
List<byte[]> statesToDelete = new ArrayList<>();
List<MiningBlock> blocksToImport = new ArrayList<>();
for (int i = 0; i < NUMBER_OF_BLOCKS / 2; i++) {
txs = BlockchainTestUtils.generateTransactions(MAX_TX_PER_BLOCK, accounts, repo);
context = chain.createNewMiningBlockInternal(chain.getBestBlock(), txs, true, time / 10000L);
assertThat(chain.tryToConnect(context.block)).isEqualTo(ImportResult.IMPORTED_BEST);
statesToDelete.add(context.block.getStateRoot());
blocksToImport.add(context.block);
}
Block bestBlock = chain.getBestBlock();
assertThat(bestBlock.getNumber()).isEqualTo(NUMBER_OF_BLOCKS);
// delete some world state root entries from the database
TrieImpl trie = (TrieImpl) repo.getWorldState();
MockDB database = (MockDB) repo.getStateDatabase();
// 1: direct recovery call
repo.flush();
for (byte[] key : statesToDelete) {
database.deleteAndCommit(key);
assertThat(trie.isValidRoot(key)).isFalse();
}
// ensure that the world state was corrupted
assertThat(trie.isValidRoot(chain.getBestBlock().getStateRoot())).isFalse();
// call the recovery functionality
boolean worked = chain.recoverWorldState(repo, bestBlock);
// ensure that the blockchain is ok
assertThat(chain.getBestBlockHash()).isEqualTo(bestBlock.getHash());
// ensure that the world state is ok
assertThat(worked).isTrue();
assertThat(trie.isValidRoot(chain.getBestBlock().getStateRoot())).isTrue();
// 2: recovery at import
repo.flush();
for (byte[] key : statesToDelete) {
database.deleteAndCommit(key);
assertThat(trie.isValidRoot(key)).isFalse();
}
// ensure that the world state was corrupted
assertThat(trie.isValidRoot(chain.getBestBlock().getStateRoot())).isFalse();
// call the recovery functionality indirectly
for (MiningBlock block : blocksToImport) {
// state missing before import
assertThat(trie.isValidRoot(block.getStateRoot())).isFalse();
assertThat(chain.tryToConnect(block)).isEqualTo(ImportResult.EXIST);
// state present after import
assertThat(trie.isValidRoot(block.getStateRoot())).isTrue();
}
// ensure that the blockchain is ok
assertThat(chain.getBestBlockHash()).isEqualTo(bestBlock.getHash());
// ensure that the world state is ok
assertThat(trie.isValidRoot(chain.getBestBlock().getStateRoot())).isTrue();
}
Aggregations