public void start(string block_header_storage_path, ulong starting_block_height, byte[] starting_block_checksum) { if (running) { return; } BlockHeaderStorage.init(block_header_storage_path); BlockHeader last_block_header = BlockHeaderStorage.getLastBlockHeader(); if (last_block_header != null && last_block_header.blockNum > starting_block_height) { lastBlockHeader = last_block_header; } else { BlockHeaderStorage.deleteCache(); lastBlockHeader = new BlockHeader() { blockNum = starting_block_height, blockChecksum = starting_block_checksum }; } running = true; // Start the thread tiv_thread = new Thread(onUpdate); tiv_thread.Name = "TIV_Update_Thread"; tiv_thread.Start(); }
private bool processBlockHeader(BlockHeader header) { if (lastBlockHeader != null && lastBlockHeader.blockChecksum != null && !header.lastBlockChecksum.SequenceEqual(lastBlockHeader.blockChecksum)) { Logging.warn("TIV: Invalid last block checksum"); // discard the block // TODO require previous block to get verifications from 3 nodes // if in verification mode, detect liar and flag him // below is an implementation that's good enough for now if (minBlockHeightReorg < lastBlockHeader.blockNum - 7) { minBlockHeightReorg = lastBlockHeader.blockNum - 7; } BlockHeader prev_header = BlockHeaderStorage.getBlockHeader(minBlockHeightReorg); if (prev_header == null) { return(false); } lastBlockHeader = prev_header; ConsensusConfig.redactedWindowSize = ConsensusConfig.getRedactedWindowSize(lastBlockHeader.version); ConsensusConfig.minRedactedWindowSize = ConsensusConfig.getRedactedWindowSize(lastBlockHeader.version); return(false); } if (!header.calculateChecksum().SequenceEqual(header.blockChecksum)) { Logging.warn("TIV: Invalid block checksum"); return(false); } lastBlockHeader = header; ConsensusConfig.redactedWindowSize = ConsensusConfig.getRedactedWindowSize(lastBlockHeader.version); ConsensusConfig.minRedactedWindowSize = ConsensusConfig.getRedactedWindowSize(lastBlockHeader.version); if (BlockHeaderStorage.saveBlockHeader(lastBlockHeader)) { // Cleanup every n blocks if ((header.blockNum > CoreConfig.maxBlockHeadersPerDatabase * 25) && header.blockNum % CoreConfig.maxBlockHeadersPerDatabase == 0) { BlockHeaderStorage.removeAllBlocksBefore(header.blockNum - (CoreConfig.maxBlockHeadersPerDatabase * 25)); } } IxianHandler.receivedBlockHeader(lastBlockHeader, true); return(true); }
public void stop() { if (!running) { return; } running = false; BlockHeaderStorage.stop(); }
public TransactionInclusion(string block_header_storage_path = "", ulong starting_block_height = 1, byte[] starting_block_checksum = null) { BlockHeaderStorage.init(block_header_storage_path); BlockHeader last_block_header = BlockHeaderStorage.getLastBlockHeader(); if (last_block_header != null && last_block_header.blockNum > starting_block_height) { lastBlockHeader = last_block_header; } else { BlockHeaderStorage.deleteCache(); lastBlockHeader = new BlockHeader() { blockNum = starting_block_height, blockChecksum = starting_block_checksum }; } }
/// <summary> /// When a response to a PIT request is received, this function validates and caches it so transactions may be verified in a separate thread. /// </summary> /// <param name="data">PIT response bytes.</param> /// <param name="endpoint">Neighbor, who sent this data.</param> public void receivedPIT2(byte[] data, RemoteEndpoint endpoint) { MemoryStream m = new MemoryStream(data); using (BinaryReader r = new BinaryReader(m)) { ulong block_num = r.ReadIxiVarUInt(); int len = (int)r.ReadIxiVarUInt(); if (len > 0) { byte[] pit_data = r.ReadBytes(len); PrefixInclusionTree pit = new PrefixInclusionTree(44, 3); try { pit.reconstructMinimumTree(pit_data); BlockHeader h = BlockHeaderStorage.getBlockHeader(block_num); if (h == null) { Logging.warn("TIV: Received PIT information for block {0}, but we do not have that block header in storage!", block_num); return; } if (!h.pitHash.SequenceEqual(pit.calculateTreeHash())) { Logging.error("TIV: Received PIT information for block {0}, but the PIT checksum does not match the one in the block header!", block_num); // TODO: more drastic action? Maybe blacklist or something. return; } lock (pitCache) { if (pitCache.ContainsKey(block_num)) { Logging.info("TIV: Received valid PIT information for block {0}", block_num); pitCache[block_num].pit = pit; } } } catch (Exception) { Logging.warn("TIV: Invalid or corrupt data received for block {0}.", block_num); } } } }
private bool processBlockHeader(BlockHeader header) { if (lastBlockHeader != null && lastBlockHeader.blockChecksum != null && !header.lastBlockChecksum.SequenceEqual(lastBlockHeader.blockChecksum)) { Logging.warn("TIV: Invalid last block checksum"); // discard the block // require previous block to get verifications from 3 nodes // if in verification mode, detect liar and flag him return(false); } if (!header.calculateChecksum().SequenceEqual(header.blockChecksum)) { Logging.warn("TIV: Invalid block checksum"); return(false); } lastBlockHeader = header; if (!BlockHeaderStorage.saveBlockHeader(lastBlockHeader)) { return(false); } // Cleanup every n blocks if ((header.blockNum > CoreConfig.maxBlockHeadersPerDatabase * 25) && header.blockNum % CoreConfig.maxBlockHeadersPerDatabase == 0) { BlockHeaderStorage.removeAllBlocksBefore(header.blockNum - (CoreConfig.maxBlockHeadersPerDatabase * 25)); } IxianHandler.receivedBlockHeader(lastBlockHeader, true); return(true); }
private void verifyUnprocessedTransactions() { if (lastBlockHeader == null) { return; } lock (txQueue) { var tmp_txQueue = txQueue.Values.Where(x => x.applied != 0 && x.applied <= lastBlockHeader.blockNum).ToArray(); foreach (var tx in tmp_txQueue) { BlockHeader bh = BlockHeaderStorage.getBlockHeader(tx.applied); if (bh is null) { // TODO: need to wait for the block to arrive, or re-request // maybe something similar to PIT cache, or extend PIT cache to handle older blocks, too continue; } if (bh.version < BlockVer.v6) { txQueue.Remove(tx.id); if (bh.transactions.Contains(tx.id)) { // valid IxianHandler.receivedTransactionInclusionVerificationResponse(tx.id, true); } else { // invalid IxianHandler.receivedTransactionInclusionVerificationResponse(tx.id, false); } } else { lock (pitCache) { // check if we already have the partial tree for this transaction if (pitCache.ContainsKey(tx.applied) && pitCache[tx.applied].pit != null) { // Note: PIT has been verified against the block header when it was received, so additional verification is not needed here. // Note: the PIT we have cached might have been requested for different txids (the current txid could have been added later) // For that reason, the list of TXIDs we requested is stored together with the cached PIT if (pitCache[tx.applied].requestedForTXIDs.Contains(tx.id)) { txQueue.Remove(tx.id); if (pitCache[tx.applied].pit.contains(tx.id)) { // valid IxianHandler.receivedTransactionInclusionVerificationResponse(tx.id, true); } else { // invalid IxianHandler.receivedTransactionInclusionVerificationResponse(tx.id, false); } } else { // PIT cache for the correct block exists, but it was originally requested for different txids // we have to re-request it for any remaining txids in the queue. (We do not need to request the already-verified ids) requestPITForBlock(tx.applied, txQueue.Values .Where(x => x.applied == tx.applied && x.applied <= lastBlockHeader.blockNum) .Select(x => x.id) .ToList()); continue; } } else { // PIT cache has not been received yet, or maybe it has never been requested for this block requestPITForBlock(tx.applied, txQueue.Values .Where(x => x.applied == tx.applied && x.applied <= lastBlockHeader.blockNum) .Select(x => x.id) .ToList()); } } } } } }