use of org.aion.zero.impl.types.BlockContext in project aion by aionnetwork.
the class AionBlockchainImpl method createNewMiningBlockInternal.
BlockContext createNewMiningBlockInternal(Block parent, List<AionTransaction> txs, boolean waitUntilBlockTime, long currTimeSeconds) {
BlockHeader parentHdr = parent.getHeader();
long time = currTimeSeconds;
if (parentHdr.getTimestamp() >= time) {
time = parentHdr.getTimestamp() + 1;
while (waitUntilBlockTime && TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) <= time) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
break;
}
}
}
long energyLimit = this.energyLimitStrategy.getEnergyLimit(parentHdr);
MiningBlock block;
try {
MiningBlockHeader.Builder headerBuilder = MiningBlockHeader.Builder.newInstance().withParentHash(parent.getHash()).withCoinbase(minerCoinbase).withNumber(parentHdr.getNumber() + 1).withTimestamp(time).withExtraData(minerExtraData).withTxTrieRoot(calcTxTrieRoot(txs)).withEnergyLimit(energyLimit).withDefaultStateRoot().withDefaultReceiptTrieRoot().withDefaultLogsBloom().withDefaultDifficulty().withDefaultNonce().withDefaultSolution();
block = new MiningBlock(headerBuilder.build(), txs);
} catch (Exception e) {
LOG.error("Construct new mining block header exception:", e);
return null;
}
BlockHeader parentMiningBlock;
BlockHeader parentMiningBlocksParent = null;
byte[] newDiff;
IDifficultyCalculator diffCalculator;
// so we use a strict greater than here
if (forkUtility.isUnityForkActive(block.getNumber())) {
if (parentHdr.getSealType() == Seal.PROOF_OF_WORK) {
LOG.warn("Tried to create 2 PoW blocks in a row");
return null;
} else {
Block[] blockFamily = repository.getBlockStore().getTwoGenerationBlocksByHashWithInfo(parentHdr.getParentHash());
Objects.requireNonNull(blockFamily[0]);
parentMiningBlock = blockFamily[0].getHeader();
parentMiningBlocksParent = blockFamily[1].getHeader();
diffCalculator = chainConfiguration.getUnityDifficultyCalculator();
}
} else {
parentMiningBlock = parentHdr;
if (!parentMiningBlock.isGenesis()) {
parentMiningBlocksParent = getParent(parentMiningBlock).getHeader();
}
diffCalculator = chainConfiguration.getPreUnityDifficultyCalculator();
}
newDiff = ByteUtil.bigIntegerToBytes(diffCalculator.calculateDifficulty(parentMiningBlock, parentMiningBlocksParent), DIFFICULTY_BYTES);
block.updateHeaderDifficulty(newDiff);
BigInteger totalTransactionFee = blockPreSeal(parentHdr, block);
if (totalTransactionFee == null) {
return null;
}
// derive base block reward
BigInteger baseBlockReward;
if (forkUtility.isSignatureSwapForkActive(block.getNumber())) {
baseBlockReward = TimeVaryingRewardsCalculator.calculateReward(block.getTimestamp() - parentHdr.getTimestamp());
} else {
baseBlockReward = this.chainConfiguration.getRewardsCalculatorBeforeSignatureSchemeSwap(forkUtility.isUnityForkActive(block.getNumber())).calculateReward(block.getNumber());
}
return new BlockContext(block, baseBlockReward, totalTransactionFee);
}
use of org.aion.zero.impl.types.BlockContext in project aion by aionnetwork.
the class RPCMethods method getBlockTemplate.
@Override
public BlockTemplate getBlockTemplate() {
BlockContext context = chainHolder.getBlockTemplate();
MiningBlock block = context.block;
return new BlockTemplate(ByteArray.wrap(block.getParentHash()), block.getNumber(), block.getHeader().getPowBoundaryBI(), ByteArray.wrap(block.getHeader().getMineHash()), context.baseBlockReward, context.transactionFee);
}
use of org.aion.zero.impl.types.BlockContext in project aion by aionnetwork.
the class BlockchainImplementationTest method testIsPruneRestricted_wFullState.
/**
* In FULL mode the state is stored for all blocks. There are no restrictions due to pruning.
*/
@Test
public void testIsPruneRestricted_wFullState() {
// the maximum height considered by this test
int height = 200;
StandaloneBlockchain.Bundle bundle = new StandaloneBlockchain.Builder().withValidatorConfiguration("simple").withRepoConfig(new MockRepositoryConfig(new CfgPrune(false))).withDefaultAccounts(accounts).build();
StandaloneBlockchain chain = bundle.bc;
AionRepositoryImpl repo = chain.getRepository();
BlockContext context;
List<AionTransaction> txs;
// creating (height) blocks
long time = System.currentTimeMillis();
for (int i = 0; i < height; 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);
}
// testing restriction for unrestricted blocks
for (int i = height; i >= 0; i--) {
assertThat(chain.isPruneRestricted(i)).isFalse();
// ensure the state exists
assertThat(repo.isValidRoot(chain.getBlockByNumber(i).getStateRoot())).isTrue();
}
}
use of org.aion.zero.impl.types.BlockContext in project aion by aionnetwork.
the class BlockchainDataRecoveryTest method testRecoverIndexWithoutGenesis.
/**
* Test the index recovery when the index database is empty.
*
* <p>Under these circumstances the recovery process will fail.
*/
@Test
public void testRecoverIndexWithoutGenesis() {
// 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;
long time = System.currentTimeMillis();
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);
blocksToImport.add(context.block);
}
Block bestBlock = chain.getBestBlock();
assertThat(bestBlock.getNumber()).isEqualTo(NUMBER_OF_BLOCKS);
ByteArrayKeyValueDatabase indexDatabase = repo.getIndexDatabase();
// 1: direct recovery call
repo.flush();
// deleting the entire index database
indexDatabase.drop();
// 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 best block is unchanged
assertThat(chain.getBestBlockHash()).isEqualTo(bestBlock.getHash());
// check that the index recovery failed
assertThat(worked).isFalse();
assertThat(repo.isIndexed(bestBlock.getHash(), bestBlock.getNumber())).isFalse();
// 2: recovery at import
repo.flush();
// deleting the entire index database
indexDatabase.drop();
// 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 missing after import
assertThat(repo.isIndexed(block.getHash(), block.getNumber())).isFalse();
}
// ensure that the best block is unchanged
assertThat(chain.getBestBlockHash()).isEqualTo(bestBlock.getHash());
// check that the index recovery failed
assertThat(repo.isIndexed(bestBlock.getHash(), bestBlock.getNumber())).isFalse();
}
use of org.aion.zero.impl.types.BlockContext in project aion by aionnetwork.
the class BlockchainDataRecoveryTest method testRecoverIndexWithPartialIndex_ShorterSideChain.
/**
* Test the recovery of the index with start from the index of an ancestor block.
*/
@Test
public void testRecoverIndexWithPartialIndex_ShorterSideChain() {
final int EVEN_NUMBER_OF_BLOCKS = NUMBER_OF_BLOCKS % 2 == 0 ? NUMBER_OF_BLOCKS : NUMBER_OF_BLOCKS + 1;
// 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;
// adding common blocks
long time = System.currentTimeMillis();
for (int i = 0; i < EVEN_NUMBER_OF_BLOCKS / 2 - 1; 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);
}
// splitting chains
Block mainChainBlock, sideChainBlock;
mainChainBlock = chain.getBestBlock();
sideChainBlock = mainChainBlock;
txs = BlockchainTestUtils.generateTransactions(MAX_TX_PER_BLOCK, accounts, repo);
context = chain.createNewMiningBlockInternal(mainChainBlock, txs, true, time / 10000L);
assertThat(chain.tryToConnect(context.block)).isEqualTo(ImportResult.IMPORTED_BEST);
mainChainBlock = context.block;
// starting side chain
txs = BlockchainTestUtils.generateTransactions(MAX_TX_PER_BLOCK, accounts, repo);
context = chain.createNewMiningBlockInternal(sideChainBlock, txs, true, time / 10000L);
MiningBlockHeader newHeader = MiningBlockHeader.Builder.newInstance().withHeader(context.block.getHeader()).withExtraData("other".getBytes()).build();
context.block.updateHeader(newHeader);
assertThat(chain.tryToConnect(context.block)).isEqualTo(ImportResult.IMPORTED_NOT_BEST);
sideChainBlock = context.block;
// building chains; sidechain will have missing index
Map<Long, byte[]> blocksToDelete = new HashMap<>();
List<MiningBlock> blocksToImportMainChain = new ArrayList<>();
List<MiningBlock> blocksToImportSideChain = new ArrayList<>();
for (int i = 0; i < EVEN_NUMBER_OF_BLOCKS / 2 - 1; i++) {
txs = BlockchainTestUtils.generateTransactions(MAX_TX_PER_BLOCK, accounts, repo);
context = chain.createNewMiningBlockInternal(mainChainBlock, txs, true, time / 10000L);
assertThat(chain.tryToConnect(context.block)).isEqualTo(ImportResult.IMPORTED_BEST);
mainChainBlock = context.block;
blocksToImportMainChain.add(context.block);
// adding side chain
txs = BlockchainTestUtils.generateTransactions(MAX_TX_PER_BLOCK, accounts, repo);
context = chain.createNewMiningBlockInternal(sideChainBlock, txs, true, time / 10000L);
assertThat(chain.tryToConnect(context.block)).isEqualTo(ImportResult.IMPORTED_NOT_BEST);
sideChainBlock = context.block;
blocksToDelete.put(sideChainBlock.getNumber(), sideChainBlock.getHash());
blocksToImportSideChain.add(context.block);
}
// making the main chain longer
txs = BlockchainTestUtils.generateTransactions(MAX_TX_PER_BLOCK, accounts, repo);
context = chain.createNewMiningBlockInternal(mainChainBlock, txs, true, time / 10000L);
assertThat(chain.tryToConnect(context.block)).isEqualTo(ImportResult.IMPORTED_BEST);
mainChainBlock = context.block;
Block bestBlock = chain.getBestBlock();
assertThat(bestBlock.getNumber()).isEqualTo(EVEN_NUMBER_OF_BLOCKS);
assertThat(bestBlock.getHash()).isEqualTo(mainChainBlock.getHash());
// 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();
}
// call the recovery functionality for the main chain subsection
boolean worked = chain.recoverIndexEntry(repo, chain.getBlockByHash(mainChainBlock.getParentHash()));
// ensure that the index was corrupted only for the side chain
assertThat(repo.isIndexed(sideChainBlock.getHash(), sideChainBlock.getNumber())).isFalse();
assertThat(repo.isIndexed(mainChainBlock.getHash(), mainChainBlock.getNumber())).isTrue();
assertThat(worked).isTrue();
// call the recovery functionality
worked = chain.recoverIndexEntry(repo, sideChainBlock);
// ensure that the blockchain is ok
assertThat(chain.getBestBlockHash()).isEqualTo(bestBlock.getHash());
// ensure that the index was recovered
assertThat(worked).isTrue();
assertThat(repo.isIndexed(sideChainBlock.getHash(), sideChainBlock.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());
// NOTE: this checks the correction of both main chain and side chain recovery
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();
}
// call the recovery functionality indirectly for main chain
for (MiningBlock block : blocksToImportMainChain) {
// 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 index was corrupted only for the side chain
assertThat(repo.isIndexed(sideChainBlock.getHash(), sideChainBlock.getNumber())).isFalse();
assertThat(repo.isIndexed(mainChainBlock.getHash(), mainChainBlock.getNumber())).isTrue();
// call the recovery functionality indirectly for side chain
for (MiningBlock block : blocksToImportSideChain) {
// 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 index was corrupted only for the side chain
assertThat(repo.isIndexed(sideChainBlock.getHash(), sideChainBlock.getNumber())).isTrue();
assertThat(repo.isIndexed(mainChainBlock.getHash(), mainChainBlock.getNumber())).isTrue();
// ensure that the blockchain is ok
assertThat(chain.getBestBlockHash()).isEqualTo(bestBlock.getHash());
// 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());
// NOTE: this checks the correction of both main chain and side chain recovery
assertThat(deletedInfo.get(key)).isEqualTo(indexDatabase.get(indexKey).get());
}
}
Aggregations