Search in sources :

Example 1 with BlockCachingContext

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()));
}
Also used : ImportResult(org.aion.zero.impl.core.ImportResult) BlockCachingContext(org.aion.zero.impl.vm.common.BlockCachingContext) MiningBlock(org.aion.zero.impl.types.MiningBlock) MiningBlockHeader(org.aion.zero.impl.types.MiningBlockHeader) MiningBlock(org.aion.zero.impl.types.MiningBlock) Block(org.aion.zero.impl.types.Block) BigInteger(java.math.BigInteger) Test(org.junit.Test)

Example 2 with BlockCachingContext

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);
}
Also used : ImportResult(org.aion.zero.impl.core.ImportResult) BlockCachingContext(org.aion.zero.impl.vm.common.BlockCachingContext) MiningBlock(org.aion.zero.impl.types.MiningBlock) Test(org.junit.Test)

Example 3 with BlockCachingContext

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);
}
Also used : MiningBlockHeader(org.aion.zero.impl.types.MiningBlockHeader) BlockCachingContext(org.aion.zero.impl.vm.common.BlockCachingContext) MiningBlock(org.aion.zero.impl.types.MiningBlock) Test(org.junit.Test)

Aggregations

MiningBlock (org.aion.zero.impl.types.MiningBlock)3 BlockCachingContext (org.aion.zero.impl.vm.common.BlockCachingContext)3 Test (org.junit.Test)3 ImportResult (org.aion.zero.impl.core.ImportResult)2 MiningBlockHeader (org.aion.zero.impl.types.MiningBlockHeader)2 BigInteger (java.math.BigInteger)1 Block (org.aion.zero.impl.types.Block)1