use of org.aion.util.types.ByteArrayWrapper in project aion by aionnetwork.
the class AionBlockchainImpl method load.
/**
* Loads the block chain attempting recovery if necessary and returns the starting block.
*
* @param genesis the expected genesis block
* @param genLOG logger for output messages
*/
public void load(AionGenesis genesis, Logger genLOG) {
// function repurposed for integrity checks since previously not implemented
try {
repository.getBlockStore().load();
} catch (RuntimeException re) {
genLOG.error("Fatal: can't load blockstore; exiting.", re);
System.exit(org.aion.zero.impl.SystemExitCodes.INITIALIZATION_ERROR);
}
// Note: if block DB corruption, the bestBlock may not match with the indexDB.
Block bestBlock = repository.getBestBlock();
// AKI-716
if (bestBlock != null && forkUtility.isSignatureSwapForkActive(bestBlock.getNumber())) {
HashUtil.setAfterSignatureSwap();
}
boolean recovered = true;
boolean bestBlockShifted = true;
int countRecoveryAttempts = 0;
// recover only for non-null blocks
while (bestBlockShifted && countRecoveryAttempts < 5 && bestBlock != null && !repository.isValidRoot(bestBlock.getStateRoot())) {
genLOG.info("Recovery initiated due to corrupt world state at block " + bestBlock.getNumber() + ".");
long bestBlockNumber = bestBlock.getNumber();
byte[] bestBlockRoot = bestBlock.getStateRoot();
// ensure that the genesis state exists before attempting recovery
if (!repository.isValidRoot(genesis.getStateRoot())) {
genLOG.info("Corrupt world state for genesis block hash: " + genesis.getShortHash() + ", number: " + genesis.getNumber() + ".");
repository.buildGenesis(genesis);
if (repository.isValidRoot(genesis.getStateRoot())) {
genLOG.info("Rebuilding genesis block SUCCEEDED.");
} else {
genLOG.info("Rebuilding genesis block FAILED.");
}
}
recovered = recoverWorldState(repository, bestBlock);
if (recovered && !repository.isIndexed(bestBlock.getHash(), bestBlock.getNumber())) {
// correct the index for this block
recovered = recoverIndexEntry(repository, bestBlock);
}
long blockNumber = bestBlock.getNumber();
if (!repository.isValidRoot(bestBlock.getStateRoot())) {
// reverting back one block
genLOG.info("Rebuild state FAILED. Reverting to previous block.");
--blockNumber;
boolean isSuccessful = getRepository().revertTo(blockNumber, genLOG);
recovered = isSuccessful && repository.isValidRoot(getBlockByNumber(blockNumber).getStateRoot());
}
if (recovered) {
// reverting block & index DB
repository.getBlockStore().rollback(blockNumber);
// new best block after recovery
bestBlock = repository.getBestBlock();
if (bestBlock != null) {
bestBlock.setTotalDifficulty(getTotalDifficultyForHash(bestBlock.getHash()));
// TODO : [unity] The publicbestblock is a weird settings, should consider to remove it.
resetPubBestBlock(bestBlock);
} else {
genLOG.error("Recovery failed! please re-import your database by ./aion.sh -n <network> --redo-import, it will take a while.");
throw new IllegalStateException("Recovery failed due to database corruption.");
}
// checking is the best block has changed since attempting recovery
bestBlockShifted = // block number changed
!(bestBlockNumber == bestBlock.getNumber()) || // root hash changed
!(Arrays.equals(bestBlockRoot, bestBlock.getStateRoot()));
if (bestBlockShifted) {
genLOG.info("Rebuilding world state SUCCEEDED by REVERTING to a previous block.");
} else {
genLOG.info("Rebuilding world state SUCCEEDED.");
}
} else {
genLOG.error("Rebuilding world state FAILED. " + "Stop the kernel (Ctrl+C) and use the command line revert option to move back to a valid block. " + "Check the Aion wiki for recommendations on choosing the block number.");
}
countRecoveryAttempts++;
}
// rebuild from genesis if (1) no best block (2) recovery failed
if (bestBlock == null || !recovered) {
if (bestBlock == null) {
genLOG.info("DB is empty - adding Genesis");
} else {
genLOG.info("DB could not be recovered - adding Genesis");
}
repository.buildGenesis(genesis);
setBestBlock(genesis);
setTotalDifficulty(genesis.getDifficultyBI());
if (genesis.getTotalDifficulty().equals(BigInteger.ZERO)) {
// setting the object runtime value
genesis.setTotalDifficulty(genesis.getDifficultyBI());
}
} else {
setBestBlock(bestBlock);
if (bestBlock instanceof StakingBlock) {
loadBestMiningBlock();
} else if (bestBlock instanceof MiningBlock) {
loadBestStakingBlock();
} else {
throw new IllegalStateException();
}
BigInteger totalDifficulty = repository.getBlockStore().getBestBlockWithInfo().getTotalDifficulty();
setTotalDifficulty(totalDifficulty);
if (bestBlock.getTotalDifficulty().equals(BigInteger.ZERO)) {
// setting the object runtime value
bestBlock.setTotalDifficulty(totalDifficulty);
}
genLOG.info("loaded block <num={}, root={}, td={}>", getBestBlock().getNumber(), LogUtil.toHexF8(getBestBlock().getStateRoot()), getTotalDifficulty());
}
ByteArrayWrapper genesisHash = genesis.getHashWrapper();
ByteArrayWrapper databaseGenHash = getBlockByNumber(0) == null ? null : getBlockByNumber(0).getHashWrapper();
// this indicates that DB and genesis are inconsistent
if (genesisHash == null || databaseGenHash == null || !genesisHash.equals(databaseGenHash)) {
if (genesisHash == null) {
genLOG.error("failed to load genesis from config");
}
if (databaseGenHash == null) {
genLOG.error("failed to load block 0 from database");
}
genLOG.error("genesis json rootHash {} is inconsistent with database rootHash {}\n" + "your configuration and genesis are incompatible, please do the following:\n" + "\t1) Remove your database folder\n" + "\t2) Verify that your genesis is correct by re-downloading the binary or checking online\n" + "\t3) Reboot with correct genesis and empty database\n", genesisHash == null ? "null" : genesisHash, databaseGenHash == null ? "null" : databaseGenHash);
System.exit(org.aion.zero.impl.SystemExitCodes.INITIALIZATION_ERROR);
}
if (!Arrays.equals(getBestBlock().getStateRoot(), ConstantUtil.EMPTY_TRIE_HASH)) {
repository.syncToRoot(getBestBlock().getStateRoot());
}
long bestNumber = getBestBlock().getNumber();
if (forkUtility.isNonceForkActive(bestNumber + 1)) {
// Reset the PoS difficulty as part of the fork logic.
if (bestNumber == forkUtility.getNonceForkBlockHeight()) {
// If this is the trigger for the fork calculate the new difficulty.
Block block = getBestBlock();
BigInteger newDiff = calculateFirstPoSDifficultyAtBlock(block);
forkUtility.setNonceForkResetDiff(newDiff);
} else {
// Otherwise, assume that it was already calculated and validated during import.
// The difficulty cannot be calculated here due to possible pruning of the world state.
Block firstStaked = getBlockByNumber(forkUtility.getNonceForkBlockHeight() + 1);
forkUtility.setNonceForkResetDiff(firstStaked.getDifficultyBI());
}
}
}
use of org.aion.util.types.ByteArrayWrapper in project aion by aionnetwork.
the class AionBlockchainImpl method getTransactionInfo.
@Override
public /* NOTE: only returns receipts from the main chain */
AionTxInfo getTransactionInfo(byte[] hash) {
// Try to get info if the hash is from an invokable transaction
Map<ByteArrayWrapper, AionTxInfo> infos = getTransactionInfoByAlias(hash);
// If we didn't find the alias for an invokable
if (infos == null || infos.isEmpty()) {
infos = transactionStore.getTxInfo(hash);
}
if (infos == null || infos.isEmpty()) {
return null;
}
AionTxInfo txInfo = null;
// pick up the receipt from the block on the main chain
for (ByteArrayWrapper blockHash : infos.keySet()) {
if (!isMainChain(blockHash.toBytes())) {
continue;
} else {
txInfo = infos.get(blockHash);
break;
}
}
if (txInfo == null) {
LOG.warn("Can't find block from main chain for transaction " + toHexString(hash));
return null;
}
AionTransaction tx = this.getBlockByHash(txInfo.getBlockHash()).getTransactionsList().get(txInfo.getIndex());
txInfo.setTransaction(tx);
return txInfo;
}
use of org.aion.util.types.ByteArrayWrapper in project aion by aionnetwork.
the class AionImpl method getStorageValue.
@Override
public Optional<ByteArrayWrapper> getStorageValue(AionAddress address, ByteArrayWrapper key) {
Objects.requireNonNull(address);
Objects.requireNonNull(key);
ByteArrayWrapper values = aionHub.getRepository().getStorageValue(address, key);
return values == null ? Optional.empty() : Optional.of(values);
}
use of org.aion.util.types.ByteArrayWrapper in project aion by aionnetwork.
the class ResponseTrieDataTest method correctParameters.
/**
* Parameters for testing:
*
* <ul>
* <li>{@link #testDecode_correct(ByteArrayWrapper, byte[], Map, DatabaseType)}
* <li>{@link #testEncode_4Parameters_correct(ByteArrayWrapper, byte[], Map, DatabaseType)}
* <li>{@link #testEncodeDecode(ByteArrayWrapper, byte[], Map, DatabaseType)}
* </ul>
*/
@SuppressWarnings("unused")
private Object correctParameters() {
List<Object> parameters = new ArrayList<>();
ByteArrayWrapper[] keyOptions = new ByteArrayWrapper[] { wrappedNodeKey, wrappedAltNodeKey, wrappedZeroNodeKey };
byte[][] valueOptions = new byte[][] { leafValue, branchValue, extensionValue };
Object[] refOptions = new Object[] { emptyReferences, singleReference, multipleReferences };
DatabaseType[] dbOptions = new DatabaseType[] { STATE, STORAGE, DETAILS };
// network and directory
String[] net_values = new String[] { "mainnet", "invalid" };
for (ByteArrayWrapper key : keyOptions) {
for (byte[] value : valueOptions) {
for (Object refs : refOptions) {
for (DatabaseType db : dbOptions) {
parameters.add(new Object[] { key, value, refs, db });
}
}
}
}
return parameters.toArray();
}
use of org.aion.util.types.ByteArrayWrapper in project aion by aionnetwork.
the class ResponseTrieDataTest method correct3Parameters.
/**
* Parameters for testing:
*
* <ul>
* <li>{@link #testEncode_3Parameters_correct(ByteArrayWrapper, byte[], DatabaseType)}
* <li>{@link #testEncodeDecode_3Parameters(ByteArrayWrapper, byte[], DatabaseType)}
* </ul>
*/
@SuppressWarnings("unused")
private Object correct3Parameters() {
List<Object> parameters = new ArrayList<>();
ByteArrayWrapper[] keyOptions = new ByteArrayWrapper[] { wrappedNodeKey, wrappedAltNodeKey, wrappedZeroNodeKey };
byte[][] valueOptions = new byte[][] { leafValue, branchValue, extensionValue };
DatabaseType[] dbOptions = new DatabaseType[] { STATE, STORAGE, DETAILS };
// network and directory
String[] net_values = new String[] { "mainnet", "invalid" };
for (ByteArrayWrapper key : keyOptions) {
for (byte[] value : valueOptions) {
for (DatabaseType db : dbOptions) {
parameters.add(new Object[] { key, value, db });
}
}
}
return parameters.toArray();
}
Aggregations