use of org.aion.zero.impl.vm.common.BlockCachingContext in project aion by aionnetwork.
the class BlockchainForkingTest method testInvalidFirstBlockDifficulty.
/*-
* Test the general forking case, where an incoming block (b) has a greater total
* difficulty than our current block. In this scenario, we should switch to
* the branch (sub-tree) that has (b) at its tip.
*
* This is the simplest case, where the distance between (a) (our current head) and
* (b) is 2. This implies that the common ancestor is directly adjacent to both blocks.
*
* (common ancestor)
* / \
* / \
* / \
* (a)x(low td) (b)o(higher td)
*
* In this simple case:
* b.td > a.td
* a_worldState === b_worldState
*
*/
@Test
public void testInvalidFirstBlockDifficulty() {
StandaloneBlockchain.Builder builder = new StandaloneBlockchain.Builder();
StandaloneBlockchain.Bundle b = builder.withValidatorConfiguration("simple").build();
StandaloneBlockchain bc = b.bc;
Block bestBlock = bc.getBestBlock();
MiningBlock standardBlock = bc.createNewMiningBlock(bc.getBestBlock(), Collections.emptyList(), true);
ChainConfiguration cc = new ChainConfiguration();
MiningBlock higherDifficultyBlock = new MiningBlock(standardBlock);
MiningBlockHeader newBlockHeader = MiningBlockHeader.Builder.newInstance().withHeader(higherDifficultyBlock.getHeader()).withTimestamp(bestBlock.getTimestamp() + 1).build();
higherDifficultyBlock.updateHeader(newBlockHeader);
BigInteger difficulty = cc.getPreUnityDifficultyCalculator().calculateDifficulty(higherDifficultyBlock.getHeader(), bestBlock.getHeader());
assertThat(difficulty).isGreaterThan(standardBlock.getDifficultyBI());
newBlockHeader = MiningBlockHeader.Builder.newInstance().withHeader(higherDifficultyBlock.getHeader()).withDifficulty(difficulty.toByteArray()).build();
higherDifficultyBlock.updateHeader(newBlockHeader);
System.out.println("before any processing: " + Hex.toHexString(bc.getRepository().getRoot()));
System.out.println("trie: " + bc.getRepository().getWorldState().getTrieDump());
ImportResult result = bc.tryToConnect(standardBlock);
assertThat(result).isEqualTo(ImportResult.IMPORTED_BEST);
// check that the correct caching context was used
Pair<Long, BlockCachingContext> cacheContext = bc.getAvmCachingContext();
assertThat(cacheContext.getLeft()).isEqualTo(standardBlock.getNumber() - 1);
assertThat(cacheContext.getRight()).isEqualTo(BlockCachingContext.MAINCHAIN);
// assert that the block we just inserted (best) is the instance that is returned
assertThat(bc.getBestBlock() == standardBlock).isTrue();
System.out.println(Hex.toHexString(bc.getRepository().getRoot()));
ImportResult higherDifficultyResult = bc.tryToConnect(higherDifficultyBlock);
/**
* With our updates to difficulty verification and calculation, this block is now invalid
*/
assertThat(higherDifficultyResult).isEqualTo(ImportResult.INVALID_BLOCK);
assertThat(bc.getBestBlockHash()).isEqualTo(standardBlock.getHash());
// since the block is second for that height, it is assumed as sidechain
cacheContext = bc.getAvmCachingContext();
assertThat(cacheContext.getLeft()).isEqualTo(standardBlock.getNumber() - 1);
assertThat(cacheContext.getRight()).isEqualTo(BlockCachingContext.SIDECHAIN);
// the object reference here is intentional
assertThat(bc.getBestBlock() == standardBlock).isTrue();
// check for correct state rollback
assertThat(bc.getRepository().getRoot()).isEqualTo(standardBlock.getStateRoot());
assertThat(bc.getTotalDifficulty()).isEqualTo(bc.getTotalDifficultyForHash(standardBlock.getHash()));
}
use of org.aion.zero.impl.vm.common.BlockCachingContext in project aion by aionnetwork.
the class BlockchainForkingTest method testSameBlockDifferentNonceAndSolutionSimple.
/*-
* Tests the case where multiple threads submit a single block (content) but
* with different mining nonces and solutions. In this case our rules dictate
* that all subsequent blocks are considered invalid.
*
* (common ancestor)
* / \
* / \
* / \
* (a)o (b)x
*
* Given:
* a.td == b.td
*/
@Test
public void testSameBlockDifferentNonceAndSolutionSimple() {
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);
MiningBlock sameBlock = (MiningBlock) BlockUtil.newBlockFromRlp(block.getEncoded());
ImportResult firstRes = bc.tryToConnect(block);
// check that the returned block is the first block
assertThat(bc.getBestBlock() == block).isTrue();
assertThat(firstRes).isEqualTo(ImportResult.IMPORTED_BEST);
// check that the correct caching context was used
Pair<Long, BlockCachingContext> cacheContext = bc.getAvmCachingContext();
assertThat(cacheContext.getLeft()).isEqualTo(block.getNumber() - 1);
assertThat(cacheContext.getRight()).isEqualTo(BlockCachingContext.MAINCHAIN);
ImportResult secondRes = bc.tryToConnect(sameBlock);
// the second block should get rejected, so check that the reference still refers
// to the first block (we dont change the published reference)
assertThat(bc.getBestBlock() == block).isTrue();
assertThat(secondRes).isEqualTo(ImportResult.EXIST);
// the caching context does not change for already known blocks
cacheContext = bc.getAvmCachingContext();
assertThat(cacheContext.getLeft()).isEqualTo(block.getNumber() - 1);
assertThat(cacheContext.getRight()).isEqualTo(BlockCachingContext.MAINCHAIN);
}
use of org.aion.zero.impl.vm.common.BlockCachingContext 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);
}
Aggregations