use of org.aion.zero.impl.types.MiningBlockHeader in project aion by aionnetwork.
the class BlockchainForkingTest method testSecondBlockHigherDifficultyFork.
/*-
*
* Recall previous forking logic worked as follows:
*
* [ parent block ]
* / \
* / \
* / \
* [block_1] [block_2]
* TD=101 TD=102
*
* Where if block_1 had a greater timestamp (and thus lower TD) than
* block_2, then block_2 would be accepted as the best block for
* that particular level (until later re-orgs prove this to be untrue)
*
* With our changes to difficulty calculations, difficulty is calculated
* with respect to the two previous blocks (parent, grandParent) blocks
* in the sequence.
*
* This leads to the following:
*
* [ parent block - 1] TD = 50
* |
* |
* [ parent block ] D = 50
* / \
* / \
* / \
* [block_1] [block_2]
* TD=100 TD=100
*
* Where both blocks are guaranteed to have the same TD if they directly
* branch off of the same parent. In fact, this guarantees us that the
* first block coming in from the network (for a particular level) is
* the de-facto best block for a short period of time.
*
* It is only when the block after comes in (for both chains) that a re-org
* will happen on one of the chains (possibly)
*
*
* ...prev
* [block_1] [block_2]
* T(n) = T(n-1) + 4 T(n) = T(n-1) + 20
* | |
* | |
* | |
* [block_1_2] [block_1_2]
* TD = 160 TD = 140
*
* At which point a re-org should occur on most blocks. Remember that this reorg
* is a minimum, given equal hashing power and particularily bad luck, two parties
* could indefinitely stay on their respective chains, but the chances of this is
* extraordinarily small.
*/
@Test
public void testSecondBlockHigherDifficultyFork() {
StandaloneBlockchain.Builder builder = new StandaloneBlockchain.Builder();
StandaloneBlockchain.Bundle bundle = builder.withValidatorConfiguration("simple").withDefaultAccounts().build();
long time = System.currentTimeMillis();
StandaloneBlockchain bc = bundle.bc;
// generate three blocks, on the third block we get flexibility
// for what difficulties can occur
MiningBlock firstBlock = bc.createNewMiningBlockInternal(bc.getGenesis(), Collections.emptyList(), true, time / 1000L).block;
assertThat(bc.tryToConnect(firstBlock)).isEqualTo(ImportResult.IMPORTED_BEST);
// check that the correct caching context was used
Pair<Long, BlockCachingContext> cacheContext = bc.getAvmCachingContext();
assertThat(cacheContext.getLeft()).isEqualTo(firstBlock.getNumber() - 1);
assertThat(cacheContext.getRight()).isEqualTo(BlockCachingContext.MAINCHAIN);
// now connect the second block
MiningBlock secondBlock = bc.createNewMiningBlockInternal(firstBlock, Collections.emptyList(), true, time / 1000L).block;
assertThat(bc.tryToConnect(secondBlock)).isEqualTo(ImportResult.IMPORTED_BEST);
// check that the correct caching context was used
cacheContext = bc.getAvmCachingContext();
// the parent
assertThat(cacheContext.getLeft()).isEqualTo(firstBlock.getNumber());
assertThat(cacheContext.getRight()).isEqualTo(BlockCachingContext.MAINCHAIN);
// now on the third block, we diverge with one block having higher TD than the other
MiningBlock fasterSecondBlock = bc.createNewMiningBlockInternal(secondBlock, Collections.emptyList(), true, time / 1000L).block;
MiningBlock slowerSecondBlock = new MiningBlock(fasterSecondBlock);
MiningBlockHeader newBlockHeader = MiningBlockHeader.Builder.newInstance().withHeader(slowerSecondBlock.getHeader()).withTimestamp(time / 1000L + 100).build();
slowerSecondBlock.updateHeader(newBlockHeader);
assertThat(bc.tryToConnect(fasterSecondBlock)).isEqualTo(ImportResult.IMPORTED_BEST);
// check that the correct caching context was used
cacheContext = bc.getAvmCachingContext();
// the parent
assertThat(cacheContext.getLeft()).isEqualTo(secondBlock.getNumber());
assertThat(cacheContext.getRight()).isEqualTo(BlockCachingContext.MAINCHAIN);
assertThat(bc.tryToConnect(slowerSecondBlock)).isEqualTo(ImportResult.IMPORTED_NOT_BEST);
// check that the correct caching context was used
cacheContext = bc.getAvmCachingContext();
// the parent
assertThat(cacheContext.getLeft()).isEqualTo(secondBlock.getNumber());
assertThat(cacheContext.getRight()).isEqualTo(BlockCachingContext.SIDECHAIN);
time += 100;
MiningBlock fastBlockDescendant = bc.createNewMiningBlockInternal(fasterSecondBlock, Collections.emptyList(), true, time / 1000L).block;
MiningBlock slowerBlockDescendant = bc.createNewMiningBlockInternal(slowerSecondBlock, Collections.emptyList(), true, time / 1000L + 100 + 1).block;
// increment by another hundred (this is supposed to be when the slower block descendant is
// completed)
time += 100;
assertThat(fastBlockDescendant.getDifficultyBI()).isGreaterThan(slowerBlockDescendant.getDifficultyBI());
System.out.println("faster block descendant TD: " + fastBlockDescendant.getDifficultyBI());
System.out.println("slower block descendant TD: " + slowerBlockDescendant.getDifficultyBI());
assertThat(bc.tryToConnect(slowerBlockDescendant)).isEqualTo(ImportResult.IMPORTED_BEST);
// check that the correct caching context was used
cacheContext = bc.getAvmCachingContext();
// no known parent
assertThat(cacheContext.getLeft()).isEqualTo(0);
assertThat(cacheContext.getRight()).isEqualTo(BlockCachingContext.DEEP_SIDECHAIN);
assertThat(bc.tryToConnect(fastBlockDescendant)).isEqualTo(ImportResult.IMPORTED_BEST);
// check that the correct caching context was used
cacheContext = bc.getAvmCachingContext();
// parent had been made side chain
assertThat(cacheContext.getLeft()).isEqualTo(0);
assertThat(cacheContext.getRight()).isEqualTo(BlockCachingContext.DEEP_SIDECHAIN);
assertThat(bc.getBestBlock()).isEqualTo(fastBlockDescendant);
// ensuring that the caching is correct for the nest block to be added
MiningBlock switchBlock = bc.createNewMiningBlockInternal(fastBlockDescendant, Collections.emptyList(), true, time / 1000L).block;
assertThat(bc.tryToConnect(switchBlock)).isEqualTo(ImportResult.IMPORTED_BEST);
// check that the correct caching context was used
cacheContext = bc.getAvmCachingContext();
// common ancestor
assertThat(cacheContext.getLeft()).isEqualTo(secondBlock.getNumber());
assertThat(cacheContext.getRight()).isEqualTo(BlockCachingContext.SWITCHING_MAINCHAIN);
// ensuring that the caching is correct for the nest block to be added
MiningBlock lastBlock = bc.createNewMiningBlockInternal(switchBlock, Collections.emptyList(), true, time / 1000L).block;
assertThat(bc.tryToConnect(lastBlock)).isEqualTo(ImportResult.IMPORTED_BEST);
// check that the correct caching context was used
cacheContext = bc.getAvmCachingContext();
// parent
assertThat(cacheContext.getLeft()).isEqualTo(switchBlock.getNumber());
assertThat(cacheContext.getRight()).isEqualTo(BlockCachingContext.MAINCHAIN);
}
use of org.aion.zero.impl.types.MiningBlockHeader in project aion by aionnetwork.
the class BlockchainForkingTest method testSecondBlockHigherDifficultyFork_wExceptionOnFasterBlockAdd.
/**
* Test fork with exception.
*/
@Test
public void testSecondBlockHigherDifficultyFork_wExceptionOnFasterBlockAdd() {
StandaloneBlockchain.Builder builder = new StandaloneBlockchain.Builder();
StandaloneBlockchain.Bundle bundle = builder.withValidatorConfiguration("simple").withDefaultAccounts().build();
long time = System.currentTimeMillis();
StandaloneBlockchain bc = bundle.bc;
// generate three blocks, on the third block we get flexibility
// for what difficulties can occur
BlockContext firstBlock = bc.createNewMiningBlockInternal(bc.getGenesis(), Collections.emptyList(), true, time / 1000L);
assertThat(bc.tryToConnect(firstBlock.block)).isEqualTo(ImportResult.IMPORTED_BEST);
// now connect the second block
BlockContext secondBlock = bc.createNewMiningBlockInternal(firstBlock.block, Collections.emptyList(), true, time / 1000L);
assertThat(bc.tryToConnect(secondBlock.block)).isEqualTo(ImportResult.IMPORTED_BEST);
// now on the third block, we diverge with one block having higher TD than the other
BlockContext fasterSecondBlock = bc.createNewMiningBlockInternal(secondBlock.block, Collections.emptyList(), true, time / 1000L);
MiningBlock slowerSecondBlock = new MiningBlock(fasterSecondBlock.block);
MiningBlockHeader newBlockHeader = MiningBlockHeader.Builder.newInstance().withHeader(slowerSecondBlock.getHeader()).withTimestamp(time / 1000L + 100).build();
slowerSecondBlock.updateHeader(newBlockHeader);
assertThat(bc.tryToConnect(fasterSecondBlock.block)).isEqualTo(ImportResult.IMPORTED_BEST);
assertThat(bc.tryToConnect(slowerSecondBlock)).isEqualTo(ImportResult.IMPORTED_NOT_BEST);
time += 100;
BlockContext fastBlockDescendant = bc.createNewMiningBlockInternal(fasterSecondBlock.block, Collections.emptyList(), true, time / 1000L);
BlockContext slowerBlockDescendant = bc.createNewMiningBlockInternal(slowerSecondBlock, Collections.emptyList(), true, time / 1000L + 100 + 1);
assertThat(fastBlockDescendant.block.getDifficultyBI()).isGreaterThan(slowerBlockDescendant.block.getDifficultyBI());
System.out.println("faster block descendant TD: " + fastBlockDescendant.block.getDifficultyBI());
System.out.println("slower block descendant TD: " + slowerBlockDescendant.block.getDifficultyBI());
assertThat(bc.tryToConnect(slowerBlockDescendant.block)).isEqualTo(ImportResult.IMPORTED_BEST);
// corrupt the parent for the fast block descendant
((MockDB) bc.getRepository().getStateDatabase()).deleteAndCommit(fasterSecondBlock.block.getStateRoot());
assertThat(bc.getRepository().isValidRoot(fasterSecondBlock.block.getStateRoot())).isFalse();
// attempt adding the fastBlockDescendant
assertThat(bc.tryToConnect(fastBlockDescendant.block)).isEqualTo(ImportResult.INVALID_BLOCK);
// check for correct state rollback
assertThat(bc.getBestBlock()).isEqualTo(slowerBlockDescendant.block);
assertThat(bc.getRepository().getRoot()).isEqualTo(slowerBlockDescendant.block.getStateRoot());
assertThat(bc.getTotalDifficulty()).isEqualTo(bc.getTotalDifficultyForHash(slowerBlockDescendant.block.getHash()));
}
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 _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
* @param _startHeight blocks are created only if a minimum height is reached
*/
private void addThread_createNewBlock(List<Runnable> _threads, StandaloneBlockchain _chain, int _id, ConcurrentLinkedQueue<MiningBlock> _queue, int _startHeight) {
_threads.add(() -> {
// parent will be main chain block
Block _parent = _chain.getBestBlock();
if (_parent.getNumber() >= _startHeight) {
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();
// 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 (using getBestBlock) %n", block.getShortHash(), block.getNumber(), new String(block.getExtraData()), block.getTransactionsList().size(), _parent.getShortHash(), Thread.currentThread().getName());
}
} else {
if (DISPLAY_MESSAGES) {
System.out.format("%51sParent not at minimum height. Skipping create in thread: %20s %n", " ", Thread.currentThread().getName());
}
}
});
}
use of org.aion.zero.impl.types.MiningBlockHeader in project aion by aionnetwork.
the class EquihashSolutionsGenerationTest210_9 method testMine_wBlockData.
@Test
@Parameters(method = "blocks")
public void testMine_wBlockData(MiningBlock block) {
MiningBlockHeader header = block.getHeader();
Equihash spy = spy(new Equihash(n, k));
// mock return the known solution
byte[] nonce = header.getNonce();
when(spy.getSolutionsForNonce(header.getMineHash(), nonce)).thenReturn(new int[][] { EquiUtils.getIndicesFromMinimal(header.getSolution(), n / (k + 1)) });
// use real method for mine call
when(spy.mine(block, nonce)).thenCallRealMethod();
AionPowSolution sol = spy.mine(block, nonce);
assertNotNull(sol);
assertArrayEquals(header.getNonce(), sol.getNonce());
assertArrayEquals(header.getSolution(), sol.getSolution());
assertArrayEquals(header.getMineHash(), sol.getBlock().getHeader().getMineHash());
}
Aggregations