Search in sources :

Example 11 with StakingBlock

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();
            }
        }
    }
}
Also used : AionAddress(org.aion.types.AionAddress) StakingBlock(org.aion.zero.impl.types.StakingBlock)

Example 12 with StakingBlock

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());
        }
    }
}
Also used : ByteArrayWrapper(org.aion.util.types.ByteArrayWrapper) Block(org.aion.zero.impl.types.Block) BlockDetailsValidator.isValidBlock(org.aion.zero.impl.valid.BlockDetailsValidator.isValidBlock) GenesisStakingBlock(org.aion.zero.impl.types.GenesisStakingBlock) RetValidPreBlock(org.aion.zero.impl.types.RetValidPreBlock) MiningBlock(org.aion.zero.impl.types.MiningBlock) EventBlock(org.aion.evtmgr.impl.evt.EventBlock) StakingBlock(org.aion.zero.impl.types.StakingBlock) BigInteger(java.math.BigInteger) MiningBlock(org.aion.zero.impl.types.MiningBlock) GenesisStakingBlock(org.aion.zero.impl.types.GenesisStakingBlock) StakingBlock(org.aion.zero.impl.types.StakingBlock)

Example 13 with StakingBlock

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();
    }
}
Also used : Block(org.aion.zero.impl.types.Block) BlockDetailsValidator.isValidBlock(org.aion.zero.impl.valid.BlockDetailsValidator.isValidBlock) GenesisStakingBlock(org.aion.zero.impl.types.GenesisStakingBlock) RetValidPreBlock(org.aion.zero.impl.types.RetValidPreBlock) MiningBlock(org.aion.zero.impl.types.MiningBlock) EventBlock(org.aion.evtmgr.impl.evt.EventBlock) StakingBlock(org.aion.zero.impl.types.StakingBlock) AionRepositoryImpl(org.aion.zero.impl.db.AionRepositoryImpl) MiningBlock(org.aion.zero.impl.types.MiningBlock) ArrayDeque(java.util.ArrayDeque) GenesisStakingBlock(org.aion.zero.impl.types.GenesisStakingBlock) StakingBlock(org.aion.zero.impl.types.StakingBlock)

Example 14 with StakingBlock

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;
}
Also used : AccountState(org.aion.base.AccountState) Block(org.aion.zero.impl.types.Block) BlockDetailsValidator.isValidBlock(org.aion.zero.impl.valid.BlockDetailsValidator.isValidBlock) GenesisStakingBlock(org.aion.zero.impl.types.GenesisStakingBlock) RetValidPreBlock(org.aion.zero.impl.types.RetValidPreBlock) MiningBlock(org.aion.zero.impl.types.MiningBlock) EventBlock(org.aion.evtmgr.impl.evt.EventBlock) StakingBlock(org.aion.zero.impl.types.StakingBlock) MiningBlock(org.aion.zero.impl.types.MiningBlock) GenesisStakingBlock(org.aion.zero.impl.types.GenesisStakingBlock) StakingBlock(org.aion.zero.impl.types.StakingBlock)

Example 15 with StakingBlock

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);
}
Also used : JSONObject(org.json.JSONObject) JSONArray(org.json.JSONArray) MiningBlock(org.aion.zero.impl.types.MiningBlock) Block(org.aion.zero.impl.types.Block) StakingBlock(org.aion.zero.impl.types.StakingBlock) BigInteger(java.math.BigInteger) AionTransaction(org.aion.base.AionTransaction) Hex.toHexString(org.aion.util.conversions.Hex.toHexString) AionBlockchainImpl(org.aion.zero.impl.blockchain.AionBlockchainImpl) MiningBlock(org.aion.zero.impl.types.MiningBlock) StakingBlock(org.aion.zero.impl.types.StakingBlock)

Aggregations

StakingBlock (org.aion.zero.impl.types.StakingBlock)18 MiningBlock (org.aion.zero.impl.types.MiningBlock)10 Block (org.aion.zero.impl.types.Block)7 AionAddress (org.aion.types.AionAddress)6 BigInteger (java.math.BigInteger)4 AionTransaction (org.aion.base.AionTransaction)4 EventBlock (org.aion.evtmgr.impl.evt.EventBlock)4 GenesisStakingBlock (org.aion.zero.impl.types.GenesisStakingBlock)4 RetValidPreBlock (org.aion.zero.impl.types.RetValidPreBlock)4 BlockDetailsValidator.isValidBlock (org.aion.zero.impl.valid.BlockDetailsValidator.isValidBlock)4 JSONObject (org.json.JSONObject)4 ArrayList (java.util.ArrayList)3 AionBlockchainImpl (org.aion.zero.impl.blockchain.AionBlockchainImpl)3 AccountState (org.aion.base.AccountState)2 ImportResult (org.aion.zero.impl.core.ImportResult)2 AionTxInfo (org.aion.zero.impl.types.AionTxInfo)2 StakingBlockHeader (org.aion.zero.impl.types.StakingBlockHeader)2 JSONArray (org.json.JSONArray)2 ByteString (com.google.protobuf.ByteString)1 ArrayDeque (java.util.ArrayDeque)1