Search in sources :

Example 1 with ByteArrayWrapper

use of org.aion.util.types.ByteArrayWrapper in project aion by aionnetwork.

the class TaskImportBlocks method processBatch.

/**
 * @implNote This method is called only when state is not null.
 */
private static SyncMode processBatch(AionBlockchainImpl chain, Map<ByteArrayWrapper, Object> importedBlockHashes, SyncStats syncStats, SyncMode syncMode, List<Block> batch, String displayId) {
    // for runtime survey information
    long startTime, duration;
    // interpreted as repeated work
    if (batch.isEmpty()) {
        log.debug("Empty batch received from node = {} in mode = {}.", displayId, syncMode);
        // we therefore reset this peer to (possibly) do something other than its previous mode
        return NORMAL;
    }
    // check last block in batch to see if we can skip batch
    if (syncMode != BACKWARD) {
        Block b = batch.get(batch.size() - 1);
        // implies the full batch was already imported (but not filtered by the queue)
        if (chain.isBlockStored(b.getHash(), b.getNumber())) {
            // keeping track of the last block check
            importedBlockHashes.put(ByteArrayWrapper.wrap(b.getHash()), true);
            // skipping the batch
            log.debug("Skip {} blocks from node = {} in mode = {}.", batch.size(), displayId, syncMode);
            batch.clear();
            if (syncMode == FORWARD) {
                return FORWARD;
            } else {
                return NORMAL;
            }
        }
    }
    // remembering imported range
    Block firstInBatch = batch.get(0);
    long first = firstInBatch.getNumber(), last = -1L, currentBest;
    ImportResult importResult = null;
    SyncMode returnMode = syncMode;
    startTime = System.nanoTime();
    try {
        long importDuration = System.currentTimeMillis();
        Triple<Long, Set<ByteArrayWrapper>, ImportResult> resultTriple = chain.tryToConnect(batch, displayId);
        importDuration = System.currentTimeMillis() - importDuration;
        currentBest = resultTriple.getLeft();
        Set<ByteArrayWrapper> importedHashes = resultTriple.getMiddle();
        importResult = resultTriple.getRight();
        int count = importedHashes.size();
        if (currentBest >= first) {
            last = currentBest + 1;
            importedHashes.stream().forEach(v -> importedBlockHashes.put(v, true));
            syncStats.updatePeerBlocks(displayId, count, BlockType.IMPORTED);
            log.info("<import-status: node = {}, from = #{}, to = #{}, time elapsed = {} ms>", displayId, first, currentBest, importDuration);
        }
    } catch (Exception e) {
        log.error("<import-block throw> ", e);
        if (e.getMessage() != null && e.getMessage().contains("No space left on device")) {
            log.error("Shutdown due to lack of disk space.", e);
            System.exit(SystemExitCodes.OUT_OF_DISK_SPACE);
        }
    }
    // if any block results in NO_PARENT, all subsequent blocks will too
    if (importResult == ImportResult.NO_PARENT) {
        int stored = chain.storePendingBlockRange(batch, log);
        syncStats.updatePeerBlocks(displayId, stored, BlockType.STORED);
        // check if it is below the current importable blocks
        if (firstInBatch.getNumber() <= getBestBlockNumber(chain) + 1) {
            duration = System.nanoTime() - startTime;
            surveyLog.debug("Import Stage 4.A: import received batch, duration = {} ns.", duration);
            return BACKWARD;
        }
        duration = System.nanoTime() - startTime;
        surveyLog.debug("Import Stage 4.A: import received batch, duration = {} ns.", duration);
        return returnMode;
    } else if (importResult.isStored()) {
        if (syncMode == BACKWARD) {
            returnMode = FORWARD;
        } else if (syncMode == FORWARD && importResult.isBest()) {
            returnMode = NORMAL;
        }
    }
    duration = System.nanoTime() - startTime;
    surveyLog.debug("Import Stage 4.A: import received batch, duration = {} ns.", duration);
    startTime = System.nanoTime();
    // check for stored blocks
    if (first < last) {
        returnMode = importFromStorage(chain, importedBlockHashes, returnMode, first, last);
    }
    duration = System.nanoTime() - startTime;
    surveyLog.debug("Import Stage 4.B: process all disk batches, duration = {} ns.", duration);
    return returnMode;
}
Also used : ImportResult(org.aion.zero.impl.core.ImportResult) Set(java.util.Set) ByteArrayWrapper(org.aion.util.types.ByteArrayWrapper) Block(org.aion.zero.impl.types.Block) SyncMode(org.aion.zero.impl.sync.SyncHeaderRequestManager.SyncMode)

Example 2 with ByteArrayWrapper

use of org.aion.util.types.ByteArrayWrapper in project aion by aionnetwork.

the class TaskImportBlocks method importFromStorage.

/**
 * Imports blocks from storage as long as there are blocks to import.
 *
 * @return the total number of imported blocks from all iterations
 */
private static SyncMode importFromStorage(AionBlockchainImpl chain, Map<ByteArrayWrapper, Object> importedBlockHashes, SyncMode givenMode, long first, long last) {
    // for runtime survey information
    long startTime, duration;
    ImportResult importResult = ImportResult.NO_PARENT;
    int imported = 0, batch;
    long level = first;
    while (level <= last) {
        startTime = System.nanoTime();
        // get blocks stored for level
        Map<ByteArrayWrapper, List<Block>> levelFromDisk = chain.loadPendingBlocksAtLevel(level, log);
        duration = System.nanoTime() - startTime;
        surveyLog.debug("Import Stage 4.B.i: load batch from disk, duration = {} ns.", duration);
        if (levelFromDisk.isEmpty()) {
            // move on to next level
            level++;
            continue;
        }
        List<ByteArrayWrapper> importedQueues = new ArrayList<>(levelFromDisk.keySet());
        for (Map.Entry<ByteArrayWrapper, List<Block>> entry : levelFromDisk.entrySet()) {
            // initialize batch counter
            batch = 0;
            List<Block> batchFromDisk = entry.getValue();
            startTime = System.nanoTime();
            // filter already imported blocks
            batchFromDisk = filterBatch(batchFromDisk, chain, importedBlockHashes);
            duration = System.nanoTime() - startTime;
            surveyLog.debug("Import Stage 4.B.ii: filter batch from disk, duration = {} ns.", duration);
            if (!batchFromDisk.isEmpty()) {
                if (log.isDebugEnabled()) {
                    log.debug("{} {} left after filtering out imported blocks.", batchFromDisk.size(), (batchFromDisk.size() == 1 ? "block" : "blocks"));
                }
            } else {
                if (log.isDebugEnabled()) {
                    log.debug("No blocks left after filtering out imported blocks.");
                }
                // this queue will be deleted from storage
                continue;
            }
            startTime = System.nanoTime();
            try {
                first = batchFromDisk.get(0).getNumber();
                long importDuration = System.currentTimeMillis();
                Triple<Long, Set<ByteArrayWrapper>, ImportResult> resultTriple = chain.tryToConnect(batchFromDisk, "STORAGE");
                importDuration = System.currentTimeMillis() - importDuration;
                long currentBest = resultTriple.getLeft();
                Set<ByteArrayWrapper> importedHashes = resultTriple.getMiddle();
                importResult = resultTriple.getRight();
                batch = importedHashes.size();
                if (currentBest >= first) {
                    last = currentBest + 1;
                    importedHashes.stream().forEach(v -> importedBlockHashes.put(v, true));
                    log.info("<import-status: node = {}, from = #{}, to = #{}, time elapsed = {} ms>", "STORAGE", first, currentBest, importDuration);
                } else {
                    // do not delete queue from storage
                    importedQueues.remove(entry.getKey());
                    // stop importing this queue
                    break;
                }
            } catch (Exception e) {
                log.error("<import-block throw> ", e);
                if (e.getMessage() != null && e.getMessage().contains("No space left on device")) {
                    log.error("Shutdown due to lack of disk space.", e);
                    System.exit(SystemExitCodes.OUT_OF_DISK_SPACE);
                }
            }
            duration = System.nanoTime() - startTime;
            surveyLog.debug("Import Stage 4.B.iii: import batch from disk, duration = {} ns.", duration);
            imported += batch;
        }
        // remove imported data from storage
        chain.dropImported(level, importedQueues, levelFromDisk, log);
        // increment level
        level++;
    }
    log.debug("Imported {} blocks from storage.", imported);
    // switch to NORMAL if in FORWARD mode
    if (importResult.isBest()) {
        return NORMAL;
    } else if (importResult.isStored() && givenMode == BACKWARD) {
        return FORWARD;
    }
    return givenMode;
}
Also used : ImportResult(org.aion.zero.impl.core.ImportResult) Set(java.util.Set) ArrayList(java.util.ArrayList) ByteArrayWrapper(org.aion.util.types.ByteArrayWrapper) Block(org.aion.zero.impl.types.Block) ArrayList(java.util.ArrayList) List(java.util.List) Map(java.util.Map)

Example 3 with ByteArrayWrapper

use of org.aion.util.types.ByteArrayWrapper in project aion by aionnetwork.

the class BlockPropagationHandler method processIncomingBlock.

public PropStatus processIncomingBlock(final int nodeId, final String displayId, final Block block) {
    if (block == null)
        return PropStatus.DROPPED;
    ByteArrayWrapper hashWrapped = block.getHashWrapper();
    if (!this.blockHeaderValidator.validate(block.getHeader(), log))
        return PropStatus.DROPPED;
    // guarantees if multiple requests of same block appears, only one goes through
    synchronized (this.cacheMap) {
        if (this.cacheMap.get(hashWrapped) != null) {
            if (log.isTraceEnabled()) {
                log.trace("block {} already cached", block.getShortHash());
            }
            return PropStatus.DROPPED;
        }
        // regardless if block processing is successful, place into cache
        this.cacheMap.put(hashWrapped, true);
    }
    // process
    long t1 = System.currentTimeMillis();
    ImportResult result;
    if (this.blockchain.skipTryToConnect(block.getNumber())) {
        result = ImportResult.NO_PARENT;
        if (log.isInfoEnabled()) {
            log.info("<import-status: node = {}, hash = {}, number = {}, txs = {}, result = NOT_IN_RANGE>", displayId, block.getShortHash(), block.getNumber(), block.getTransactionsList().size(), result);
        } else if (log.isDebugEnabled()) {
            log.debug("<import-status: node = {}, hash = {}, number = {}, txs = {}, block time = {}, result = NOT_IN_RANGE>", displayId, block.getShortHash(), block.getNumber(), block.getTransactionsList().size(), block.getTimestamp(), result);
        }
    } else {
        result = this.blockchain.tryToConnect(new BlockWrapper(block));
        long t2 = System.currentTimeMillis();
        if (result.isStored()) {
            this.syncStats.updatePeerBlocks(displayId, 1, BlockType.IMPORTED);
        }
        if (log.isInfoEnabled()) {
            log.info("<import-status: node = {}, hash = {}, number = {}, txs = {}, result = {}, time elapsed = {} ms>", displayId, block.getShortHash(), block.getNumber(), block.getTransactionsList().size(), result, t2 - t1);
        } else if (log.isDebugEnabled()) {
            log.debug("<import-status: node = {}, hash = {}, number = {}, td = {}, txs = {}, block time = {}, result = {}, time elapsed = {} ms>", displayId, block.getShortHash(), block.getNumber(), blockchain.getTotalDifficulty(), block.getTransactionsList().size(), block.getTimestamp(), result, t2 - t1);
        }
    }
    // send
    boolean sent = result.isBest() && send(block, nodeId);
    // notify higher td peers in order to limit the rebroadcast on delay of res status updating
    if (result.isBest()) {
        Block bestBlock = blockchain.getBestBlock();
        BigInteger td = bestBlock.getTotalDifficulty();
        ResStatus rs = new ResStatus(bestBlock.getNumber(), td.toByteArray(), bestBlock.getHash(), genesis, apiVersion, (short) p2pManager.getActiveNodes().size(), BigInteger.valueOf(this.pendingState.getPendingTxSize()).toByteArray(), p2pManager.getAvgLatency());
        this.p2pManager.getActiveNodes().values().stream().filter(n -> n.getIdHash() != nodeId).filter(n -> n.getTotalDifficulty().compareTo(td) >= 0).forEach(n -> {
            log.debug("<push-status blk={} hash={} to-node={} dd={} import-result={}>", block.getNumber(), block.getShortHash(), n.getIdShort(), td.longValue() - n.getTotalDifficulty().longValue(), result.name());
            this.p2pManager.send(n.getIdHash(), n.getIdShort(), rs);
        });
    }
    // process resulting state
    if (sent && result.isSuccessful())
        return PropStatus.PROP_CONNECTED;
    if (result.isSuccessful())
        return PropStatus.CONNECTED;
    if (sent)
        return PropStatus.PROPAGATED;
    // gets dropped when the result is not valid
    return PropStatus.DROPPED;
}
Also used : BlockWrapper(org.aion.zero.impl.blockchain.BlockWrapper) AionLoggerFactory(org.aion.log.AionLoggerFactory) IP2pMgr(org.aion.p2p.IP2pMgr) BlockHeaderValidator(org.aion.zero.impl.valid.BlockHeaderValidator) Logger(org.slf4j.Logger) BlockWrapper(org.aion.zero.impl.blockchain.BlockWrapper) IAionBlockchain(org.aion.zero.impl.blockchain.IAionBlockchain) ImportResult(org.aion.zero.impl.core.ImportResult) BroadcastNewBlock(org.aion.zero.impl.sync.msg.BroadcastNewBlock) BlockType(org.aion.zero.impl.sync.statistics.BlockType) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) LRUMap(org.apache.commons.collections4.map.LRUMap) AionPendingStateImpl(org.aion.zero.impl.pendingState.AionPendingStateImpl) ByteArrayWrapper(org.aion.util.types.ByteArrayWrapper) LogEnum(org.aion.log.LogEnum) SyncStats(org.aion.zero.impl.sync.SyncStats) Map(java.util.Map) ResStatus(org.aion.zero.impl.sync.msg.ResStatus) BigInteger(java.math.BigInteger) Block(org.aion.zero.impl.types.Block) CfgAion(org.aion.zero.impl.config.CfgAion) ImportResult(org.aion.zero.impl.core.ImportResult) ResStatus(org.aion.zero.impl.sync.msg.ResStatus) ByteArrayWrapper(org.aion.util.types.ByteArrayWrapper) BroadcastNewBlock(org.aion.zero.impl.sync.msg.BroadcastNewBlock) Block(org.aion.zero.impl.types.Block) BigInteger(java.math.BigInteger)

Example 4 with ByteArrayWrapper

use of org.aion.util.types.ByteArrayWrapper in project aion by aionnetwork.

the class RequestTrieDataHandler method receive.

@Override
public void receive(int peerId, String displayId, final byte[] message) {
    if (message == null || message.length == 0) {
        this.log.debug("<req-trie empty message from peer={}>", displayId);
        return;
    }
    RequestTrieData request = RequestTrieData.decode(message);
    if (request != null) {
        DatabaseType dbType = request.getDbType();
        ByteArrayWrapper key = ByteArrayWrapper.wrap(request.getNodeKey());
        int limit = request.getLimit();
        if (log.isDebugEnabled()) {
            this.log.debug("<req-trie from-db={} key={} peer={}>", dbType, key, displayId);
        }
        byte[] value = null;
        try {
            // retrieve from blockchain depending on db type
            value = chain.getTrieNode(key.toBytes(), dbType);
        } catch (Exception e) {
            this.log.error("<req-trie value retrieval failed>", e);
        }
        if (value != null) {
            ResponseTrieData response;
            if (limit == 1) {
                // generate response without referenced nodes
                response = new ResponseTrieData(key, value, dbType);
            } else {
                // check for internal limit on the request
                if (limit == 0) {
                    limit = TRIE_DATA_REQUEST_MAXIMUM_BATCH_SIZE;
                } else {
                    // the first value counts towards the limit
                    limit = Math.min(limit - 1, TRIE_DATA_REQUEST_MAXIMUM_BATCH_SIZE);
                }
                Map<ByteArrayWrapper, byte[]> referencedNodes = Collections.emptyMap();
                try {
                    // determine if the node can be expanded
                    referencedNodes = chain.getReferencedTrieNodes(value, limit, dbType);
                } catch (Exception e) {
                    this.log.error("<req-trie reference retrieval failed>", e);
                }
                // generate response with referenced nodes
                response = new ResponseTrieData(key, value, referencedNodes, dbType);
            }
            // reply to request
            this.p2p.send(peerId, displayId, response);
        }
    } else {
        this.log.error("<req-trie decode-error msg-bytes={} peer={}>", message.length, displayId);
        if (log.isTraceEnabled()) {
            this.log.trace("<req-trie decode-error for msg={} peer={}>", Arrays.toString(message), displayId);
        }
    }
}
Also used : DatabaseType(org.aion.zero.impl.sync.DatabaseType) ByteArrayWrapper(org.aion.util.types.ByteArrayWrapper) ResponseTrieData(org.aion.zero.impl.sync.msg.ResponseTrieData) RequestTrieData(org.aion.zero.impl.sync.msg.RequestTrieData)

Example 5 with ByteArrayWrapper

use of org.aion.util.types.ByteArrayWrapper in project aion by aionnetwork.

the class Cache method markRemoved.

public void markRemoved(byte[] key) {
    ByteArrayWrapper keyW = ByteArrayWrapper.wrap(key);
    removedNodes.add(keyW);
    nodes.remove(keyW);
}
Also used : ByteArrayWrapper(org.aion.util.types.ByteArrayWrapper)

Aggregations

ByteArrayWrapper (org.aion.util.types.ByteArrayWrapper)130 Test (org.junit.Test)51 HashMap (java.util.HashMap)39 ArrayList (java.util.ArrayList)33 AionAddress (org.aion.types.AionAddress)26 Block (org.aion.zero.impl.types.Block)24 Map (java.util.Map)20 BigInteger (java.math.BigInteger)14 MiningBlock (org.aion.zero.impl.types.MiningBlock)14 IOException (java.io.IOException)13 MockDB (org.aion.db.impl.mockdb.MockDB)13 DataWord (org.aion.util.types.DataWord)13 PooledTransaction (org.aion.base.PooledTransaction)11 List (java.util.List)10 AionTransaction (org.aion.base.AionTransaction)10 Properties (java.util.Properties)8 HashSet (java.util.HashSet)5 Optional (java.util.Optional)5 ECKey (org.aion.crypto.ECKey)5 RLPElement (org.aion.rlp.RLPElement)5