use of org.aion.zero.impl.types.StakingBlock in project aion by aionnetwork.
the class AionChainHolder method submitSeed.
@Override
public byte[] submitSeed(byte[] newSeed, byte[] signingPublicKey, byte[] coinBase) {
if (!isUnityForkEnabled())
throw new UnsupportedOperationException();
else {
Pair<Long, StakingBlock> template = stakingBlockTemplate.get();
StakingBlock blockTemplate = template == null ? null : template.getRight();
if (blockTemplate != null && Arrays.equals(blockTemplate.getSeed(), newSeed) && Arrays.equals(blockTemplate.getHeader().getSigningPublicKey(), signingPublicKey) && blockTemplate.getCoinbase().equals(new AionAddress(coinBase)) && System.currentTimeMillis() <= template.getLeft() + templateRefreshRate) {
return blockTemplate.getHeader().getMineHash();
} else {
blockTemplate = this.chain.getStakingBlockTemplate(newSeed, signingPublicKey, coinBase);
stakingBlockTemplate.set(Pair.of(System.currentTimeMillis(), blockTemplate));
if (blockTemplate == null) {
return ByteUtil.EMPTY_BYTE_ARRAY;
} else {
return blockTemplate.getHeader().getMineHash();
}
}
}
}
use of org.aion.zero.impl.types.StakingBlock 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.zero.impl.types.StakingBlock in project aion by aionnetwork.
the class AionBlockchainImpl method recoverWorldState.
/**
* Recovery functionality for rebuilding the world state.
*
* @return {@code true} if the recovery was successful, {@code false} otherwise
*/
public boolean recoverWorldState(Repository repository, Block block) {
lock.lock();
try {
if (block == null) {
LOG.error("World state recovery attempted with null block.");
return false;
}
if (repository.isSnapshot()) {
LOG.error("World state recovery attempted with snapshot repository.");
return false;
}
long blockNumber = block.getNumber();
LOG.info("Pruned or corrupt world state at block hash: {}, number: {}." + " Looking for ancestor block with valid world state ...", block.getShortHash(), blockNumber);
AionRepositoryImpl repo = (AionRepositoryImpl) repository;
// keeping track of the original root
byte[] originalRoot = repo.getRoot();
Deque<Block> dirtyBlocks = new ArrayDeque<>();
// already known to be missing the state
dirtyBlocks.push(block);
Block other = block;
// find all the blocks missing a world state
do {
other = getBlockByHash(other.getParentHash());
// cannot recover if no valid states exist (must build from genesis)
if (other == null) {
return false;
} else {
dirtyBlocks.push(other);
}
} while (!repo.isValidRoot(other.getStateRoot()) && other.getNumber() > 0);
if (other.getNumber() == 0 && !repo.isValidRoot(other.getStateRoot())) {
LOG.info("Rebuild state FAILED because a valid state could not be found.");
return false;
}
// sync to the last correct state
repo.syncToRoot(other.getStateRoot());
// remove the last added block because it has a correct world state
dirtyBlocks.pop();
LOG.info("Valid state found at block hash: {}, number: {}.", other.getShortHash(), other.getNumber());
// rebuild world state for dirty blocks
while (!dirtyBlocks.isEmpty()) {
other = dirtyBlocks.pop();
LOG.info("Rebuilding block hash: {}, number: {}, txs: {}.", other.getShortHash(), other.getNumber(), other.getTransactionsList().size());
// Load bestblock for executing the CLI command.
if (bestBlock == null) {
bestBlock = repo.getBestBlock();
if (bestBlock instanceof MiningBlock) {
bestMiningBlock = (MiningBlock) bestBlock;
} else if (bestBlock instanceof StakingBlock) {
bestStakingBlock = (StakingBlock) bestBlock;
} else {
throw new IllegalStateException("Invalid best block!");
}
}
this.add(new BlockWrapper(other, false, true, true, false));
}
// update the repository
repo.flush();
// setting the root back to its correct value
repo.syncToRoot(originalRoot);
// return a flag indicating if the recovery worked
return repo.isValidRoot(block.getStateRoot());
} finally {
lock.unlock();
}
}
use of org.aion.zero.impl.types.StakingBlock in project aion by aionnetwork.
the class AionBlockchainImpl method pushState.
private State pushState(byte[] bestBlockHash) {
State push = stateStack.push(new State());
Block block = repository.getBlockStore().getBlockByHashWithInfo(bestBlockHash);
;
if (block == null) {
throw new IllegalStateException("BlockStore error, cannot find the block byHash: " + ByteUtil.toHexString(bestBlockHash));
}
this.bestBlock = block;
LOG.debug("pushState bestBlock:{}", bestBlock);
if (bestBlock.getHeader().getSealType() == Seal.PROOF_OF_WORK) {
bestMiningBlock = (MiningBlock) bestBlock;
if (forkUtility.isUnityForkActive(bestBlock.getNumber())) {
bestStakingBlock = (StakingBlock) getBlockByHash(bestBlock.getParentHash());
} else {
bestStakingBlock = null;
}
} else if (bestBlock.getHeader().getSealType() == Seal.PROOF_OF_STAKE) {
bestStakingBlock = (StakingBlock) bestBlock;
bestMiningBlock = (MiningBlock) getBlockByHash(bestBlock.getParentHash());
} else {
throw new IllegalStateException("Invalid best block data!");
}
totalDifficulty.set(bestBlock.getTotalDifficulty());
this.repository = (AionRepositoryImpl) this.repository.getSnapshotTo(this.bestBlock.getStateRoot());
return push;
}
use of org.aion.zero.impl.types.StakingBlock in project aion by aionnetwork.
the class ApiWeb3Aion method ops_getBlock.
public RpcMsg ops_getBlock(Object _params) {
String _bnOrHash;
boolean _fullTx;
if (_params instanceof JSONArray) {
_bnOrHash = ((JSONArray) _params).get(0) + "";
_fullTx = ((JSONArray) _params).optBoolean(1, false);
} else if (_params instanceof JSONObject) {
_bnOrHash = ((JSONObject) _params).get("block") + "";
_fullTx = ((JSONObject) _params).optBoolean("fullTransaction", false);
} else {
return new RpcMsg(null, RpcError.INVALID_PARAMS, "Invalid parameters");
}
Block block = null;
Long bn = this.parseBnOrId(_bnOrHash);
AionBlockchainImpl blockchainImpl = (AionBlockchainImpl) this.ac.getBlockchain();
// user passed a Long block number
if (bn != null) {
if (bn >= 0) {
block = blockchainImpl.getBlockByNumber(bn);
if (block == null) {
return new RpcMsg(JSONObject.NULL);
}
} else {
return new RpcMsg(JSONObject.NULL);
}
}
// see if the user passed in a hash
if (block == null) {
block = blockchainImpl.getBlockByHash(ByteUtil.hexStringToBytes(_bnOrHash));
if (block == null) {
return new RpcMsg(JSONObject.NULL);
}
}
Block mainBlock = blockchainImpl.getBlockByNumber(block.getNumber());
if (mainBlock == null) {
return new RpcMsg(JSONObject.NULL);
}
if (!Arrays.equals(block.getHash(), mainBlock.getHash())) {
return new RpcMsg(JSONObject.NULL);
}
// ok so now we have a mainchain block
BigInteger blkReward = blockchainImpl.calculateBlockRewards(block.getHeader().getNumber());
BigInteger totalDiff = this.ac.getAionHub().getTotalDifficultyForHash(block.getHash());
JSONObject blk = new JSONObject();
blk.put("timestampVal", block.getTimestamp());
blk.put("blockNumber", block.getNumber());
blk.put("numTransactions", block.getTransactionsList().size());
blk.put("blockHash", StringUtils.toJsonHex(block.getHash()));
blk.put("parentHash", StringUtils.toJsonHex(block.getParentHash()));
blk.put("minerAddress", StringUtils.toJsonHex(block.getCoinbase().toByteArray()));
blk.put("receiptTxRoot", StringUtils.toJsonHex(block.getReceiptsRoot()));
blk.put("txTrieRoot", StringUtils.toJsonHex(block.getTxTrieRoot()));
blk.put("stateRoot", StringUtils.toJsonHex(block.getStateRoot()));
blk.put("difficulty", StringUtils.toJsonHex(block.getDifficulty()));
blk.put("totalDifficulty", totalDiff.toString(16));
blk.put("blockReward", blkReward);
blk.put("nrgConsumed", block.getNrgConsumed());
blk.put("nrgLimit", block.getNrgLimit());
blk.put("size", block.size());
blk.put("bloom", StringUtils.toJsonHex(block.getLogBloom()));
blk.put("extraData", StringUtils.toJsonHex(block.getExtraData()));
blk.put("sealType", StringUtils.toJsonHex(block.getHeader().getSealType().getSealId()));
if (isPowBlock(block)) {
MiningBlock powBlock = (MiningBlock) block;
blk.put("nonce", StringUtils.toJsonHex(powBlock.getNonce()));
blk.put("solution", StringUtils.toJsonHex(powBlock.getHeader().getSolution()));
} else if (isPosBlock(block)) {
StakingBlock posBlock = (StakingBlock) block;
byte[] seedOrProof = posBlock.getHeader().getSeedOrProof();
if (seedOrProof.length == StakingBlockHeader.SEED_LENGTH) {
blk.put("seed", StringUtils.toJsonHex(seedOrProof));
} else if (seedOrProof.length == StakingBlockHeader.PROOF_LENGTH) {
blk.put("proof", StringUtils.toJsonHex(seedOrProof));
} else {
blk.put("seedOrProofError", StringUtils.toJsonHex(seedOrProof));
}
blk.put("publicKey", StringUtils.toJsonHex(posBlock.getHeader().getSigningPublicKey()));
blk.put("signature", StringUtils.toJsonHex(posBlock.getHeader().getSignature()));
}
JSONObject result = new JSONObject();
result.put("blk", blk);
if (_fullTx) {
JSONArray txn = new JSONArray();
for (AionTransaction tx : block.getTransactionsList()) {
// transactionHash, fromAddr, toAddr, value, timestampVal, blockNumber, blockHash
JSONArray t = new JSONArray();
t.put(StringUtils.toJsonHex(tx.getTransactionHash()));
t.put(StringUtils.toJsonHex(tx.getSenderAddress().toByteArray()));
t.put(StringUtils.toJsonHex(tx.getDestinationAddress() == null ? EMPTY_BYTE_ARRAY : tx.getDestinationAddress().toByteArray()));
t.put(StringUtils.toJsonHex(tx.getValue()));
t.put(block.getTimestamp());
t.put(block.getNumber());
txn.put(t);
}
result.put("txn", txn);
}
return new RpcMsg(result);
}
Aggregations