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;
}
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;
}
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;
}
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);
}
}
}
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);
}
Aggregations