private bool handleSignature(InventoryItem item, RemoteEndpoint endpoint) { InventoryItemSignature iis = (InventoryItemSignature)item; ulong last_block_height = IxianHandler.getLastBlockHeight(); byte[] address = iis.address; ulong block_num = iis.blockNum; if (block_num + 5 > last_block_height && block_num <= last_block_height + 1) { if (block_num == last_block_height + 1) { lock (Node.blockProcessor.localBlockLock) { Block local_block = Node.blockProcessor.localNewBlock; if (local_block == null || local_block.blockNum != block_num) { return(false); } if (!local_block.blockChecksum.SequenceEqual(iis.blockHash) || local_block.hasNodeSignature(address)) { return(false); } } } else { Block sf_block = Node.blockChain.getBlock(block_num); if (!sf_block.blockChecksum.SequenceEqual(iis.blockHash) || sf_block.hasNodeSignature(address)) { return(false); } } byte[] block_num_bytes = block_num.GetIxiVarIntBytes(); byte[] addr_len_bytes = ((ulong)address.Length).GetIxiVarIntBytes(); byte[] data = new byte[block_num_bytes.Length + 1 + addr_len_bytes.Length + address.Length]; Array.Copy(block_num_bytes, data, block_num_bytes.Length); data[block_num_bytes.Length] = 1; Array.Copy(addr_len_bytes, 0, data, block_num_bytes.Length + 1, addr_len_bytes.Length); Array.Copy(address, 0, data, block_num_bytes.Length + 1 + addr_len_bytes.Length, address.Length); if (endpoint == null) { CoreProtocolMessage.broadcastProtocolMessageToSingleRandomNode(new char[] { 'M', 'H' }, ProtocolMessageCode.getSignatures, data, block_num); } else { endpoint.sendData(ProtocolMessageCode.getSignatures, data, null); } return(true); } return(false); }
public static void broadcastGetSignatures(ulong block_num, List <InventoryItemSignature> sig_list, RemoteEndpoint endpoint) { int sig_count = sig_list.Count; int max_sig_per_chunk = ConsensusConfig.maximumBlockSigners; for (int i = 0; i < sig_count;) { using (MemoryStream mOut = new MemoryStream(max_sig_per_chunk * 570)) { using (BinaryWriter writer = new BinaryWriter(mOut)) { writer.WriteIxiVarInt(block_num); int next_sig_count; if (sig_count - i > max_sig_per_chunk) { next_sig_count = max_sig_per_chunk; } else { next_sig_count = sig_count - i; } writer.WriteIxiVarInt(next_sig_count); for (int j = 0; j < next_sig_count && i < sig_count; j++) { InventoryItemSignature sig = sig_list[i]; i++; long out_rollback_len = mOut.Length; writer.WriteIxiVarInt(sig.address.Length); writer.Write(sig.address); if (mOut.Length > CoreConfig.maxMessageSize) { mOut.SetLength(out_rollback_len); i--; break; } } } endpoint.sendData(ProtocolMessageCode.getSignatures, mOut.ToArray(), null); } } }
public bool refreshSignatures(Block b, bool forceRefresh = false, RemoteEndpoint endpoint = null) { if (!forceRefresh) { // we refuse to change sig numbers older than 4 blocks ulong sigLockHeight = getLastBlockNum() > 5 ? getLastBlockNum() - 3 : 1; if (b.blockNum <= sigLockHeight) { return(false); } } Block updatestorage_block = null; int beforeSigs = 0; int afterSigs = 0; lock (blocks) { int idx = blocks.FindIndex(x => x.blockNum == b.blockNum && x.blockChecksum.SequenceEqual(b.blockChecksum)); if (idx > 0) { if (blocks[idx].compacted) { Logging.error("Trying to refresh signatures on compacted block {0}", blocks[idx].blockNum); return(false); } byte[] beforeSigsChecksum = blocks[idx].calculateSignatureChecksum(); beforeSigs = blocks[idx].getFrozenSignatureCount(); var added_sigs = blocks[idx].addSignaturesFrom(b); if (added_sigs != null && Node.isMasterNode()) { foreach (var sig in added_sigs) { Node.inventoryCache.setProcessedFlag(InventoryItemTypes.blockSignature, InventoryItemSignature.getHash(sig[1], b.blockChecksum), true); SignatureProtocolMessages.broadcastBlockSignature(sig[0], sig[1], b.blockNum, b.blockChecksum, endpoint, null); } } if (forceRefresh) { if (!b.verifyBlockProposer()) { Logging.error("Error verifying block proposer while force refreshing signatures on block {0} ({1})", b.blockNum, Crypto.hashToString(b.blockChecksum)); return(false); } blocks[idx].setFrozenSignatures(b.signatures); afterSigs = b.signatures.Count; } else { afterSigs = blocks[idx].signatures.Count; } byte[] afterSigsChecksum = blocks[idx].calculateSignatureChecksum(); if (!beforeSigsChecksum.SequenceEqual(afterSigsChecksum)) { updatestorage_block = blocks[idx]; } } } // Check if the block needs to be refreshed if (updatestorage_block != null) { updateBlock(updatestorage_block); Logging.info("Refreshed block #{0}: Updated signatures {1} -> {2}", b.blockNum, beforeSigs, afterSigs); return(true); } return(false); }
// Removes event subscriptions for the provided endpoint public static void handleBlockSignature(byte[] data, RemoteEndpoint endpoint) { if (Node.blockSync.synchronizing) { return; } if (data == null) { Logging.warn(string.Format("Invalid protocol message signature data")); return; } using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { ulong block_num = reader.ReadUInt64(); int checksum_len = reader.ReadInt32(); byte[] checksum = reader.ReadBytes(checksum_len); int sig_len = reader.ReadInt32(); byte[] sig = reader.ReadBytes(sig_len); int sig_addr_len = reader.ReadInt32(); byte[] sig_addr = reader.ReadBytes(sig_addr_len); ulong last_bh = IxianHandler.getLastBlockHeight(); lock (Node.blockProcessor.localBlockLock) { if (last_bh + 1 < block_num || (last_bh + 1 == block_num && Node.blockProcessor.getLocalBlock() == null)) { Logging.info("Received signature for block {0} which is missing", block_num); // future block, request the next block BlockProtocolMessages.broadcastGetBlock(last_bh + 1, null, endpoint); return; } } if (PresenceList.getPresenceByAddress(sig_addr) == null) { Logging.info("Received signature for block {0} whose signer isn't in the PL", block_num); return; } Node.inventoryCache.setProcessedFlag(InventoryItemTypes.blockSignature, InventoryItemSignature.getHash(sig_addr, checksum), true); if (Node.blockProcessor.addSignatureToBlock(block_num, checksum, sig, sig_addr, endpoint)) { Node.blockProcessor.acceptLocalNewBlock(); if (Node.isMasterNode()) { broadcastBlockSignature(sig, sig_addr, block_num, checksum, endpoint); } } else { // discard - it might have already been applied } } } }
public static void handleSignaturesChunk(byte[] data, RemoteEndpoint endpoint) { using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { ulong block_num = reader.ReadIxiVarUInt(); int checksum_len = (int)reader.ReadIxiVarUInt(); byte[] checksum = reader.ReadBytes(checksum_len); ulong last_block_height = IxianHandler.getLastBlockHeight() + 1; if (block_num > last_block_height) { return; } Block block = null; if (block_num == last_block_height) { bool haveLock = false; try { Monitor.TryEnter(Node.blockProcessor.localBlockLock, 1000, ref haveLock); if (!haveLock) { throw new TimeoutException(); } Block tmp = Node.blockProcessor.getLocalBlock(); if (tmp != null && tmp.blockNum == last_block_height) { block = tmp; } } finally { if (haveLock) { Monitor.Exit(Node.blockProcessor.localBlockLock); } } } else { block = Node.blockChain.getBlock(block_num, false, false); } if (block == null) { // target block missing Logging.warn("Target block {0} for adding sigs is missing", block_num); return; } else if (!block.blockChecksum.SequenceEqual(checksum)) { // incorrect target block Logging.warn("Incorrect target block {0} - {1}, possibly forked", block_num, Crypto.hashToString(checksum)); return; } if (block_num + 5 < last_block_height) { // block already sigfreezed, do nothing return; } int sig_count = (int)reader.ReadIxiVarUInt(); if (sig_count > ConsensusConfig.maximumBlockSigners) { sig_count = ConsensusConfig.maximumBlockSigners; } if (block_num + 5 == last_block_height) { // handle currently sigfreezing block differently Block dummy_block = new Block(); dummy_block.blockNum = block_num; dummy_block.blockChecksum = checksum; dummy_block.blockProposer = block.blockProposer; for (int i = 0; i < sig_count; i++) { if (m.Position == m.Length) { break; } int sig_len = (int)reader.ReadIxiVarUInt(); byte[] sig = reader.ReadBytes(sig_len); int addr_len = (int)reader.ReadIxiVarUInt(); byte[] addr = reader.ReadBytes(addr_len); Node.inventoryCache.setProcessedFlag(InventoryItemTypes.blockSignature, InventoryItemSignature.getHash(addr, checksum), true); dummy_block.addSignature(sig, addr); } Node.blockProcessor.handleSigFreezedBlock(dummy_block, endpoint); } else { for (int i = 0; i < sig_count; i++) { if (m.Position == m.Length) { break; } int sig_len = (int)reader.ReadIxiVarUInt(); byte[] sig = reader.ReadBytes(sig_len); int addr_len = (int)reader.ReadIxiVarUInt(); byte[] addr = reader.ReadBytes(addr_len); Node.inventoryCache.setProcessedFlag(InventoryItemTypes.blockSignature, InventoryItemSignature.getHash(addr, checksum), true); if (PresenceList.getPresenceByAddress(addr) == null) { Logging.info("Received signature for block {0} whose signer isn't in the PL", block_num); continue; } if (Node.blockProcessor.addSignatureToBlock(block_num, checksum, sig, addr, endpoint)) { if (Node.isMasterNode()) { broadcastBlockSignature(sig, addr, block_num, checksum, endpoint); } } } } Node.blockProcessor.acceptLocalNewBlock(); } } }
public static void handleSigfreezedBlockSignatures(byte[] data, RemoteEndpoint endpoint) { using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { ulong block_num = reader.ReadUInt64(); int checksum_len = reader.ReadInt32(); byte[] checksum = reader.ReadBytes(checksum_len); ulong last_block_height = IxianHandler.getLastBlockHeight(); Block target_block = Node.blockChain.getBlock(block_num, true); if (target_block == null) { if (block_num == last_block_height + 1) { // target block missing, request the next block Logging.warn("Target block {0} missing, requesting...", block_num); BlockProtocolMessages.broadcastGetBlock(block_num, null, endpoint); } else { // target block missing Logging.warn("Target block {0} missing", block_num); } return; } else if (!target_block.blockChecksum.SequenceEqual(checksum)) { // incorrect target block Logging.warn("Incorrect target block {0} - {1}, possibly forked", block_num, Crypto.hashToString(checksum)); return; } Block sf_block = null; if (block_num + 4 == last_block_height) { sf_block = Node.blockProcessor.getLocalBlock(); } else if (block_num + 4 > last_block_height) { Logging.warn("Sigfreezing block {0} missing", block_num + 5); return; } else { // block already sigfreezed, do nothing return; } lock (target_block) { if (sf_block != null) { if (target_block.calculateSignatureChecksum().SequenceEqual(sf_block.signatureFreezeChecksum)) { // we already have the correct sigfreeze return; } } else { // sf_block missing Logging.warn("Sigfreezing block {0} missing", block_num + 5); return; } int sig_count = reader.ReadInt32(); if (sig_count > ConsensusConfig.maximumBlockSigners) { sig_count = ConsensusConfig.maximumBlockSigners; } Block dummy_block = new Block(); dummy_block.blockNum = block_num; dummy_block.blockChecksum = checksum; dummy_block.blockProposer = sf_block.blockProposer; for (int i = 0; i < sig_count; i++) { if (m.Position == m.Length) { break; } int sig_len = reader.ReadInt32(); byte[] sig = reader.ReadBytes(sig_len); int addr_len = reader.ReadInt32(); byte[] addr = reader.ReadBytes(addr_len); Node.inventoryCache.setProcessedFlag(InventoryItemTypes.blockSignature, InventoryItemSignature.getHash(addr, checksum), true); dummy_block.addSignature(sig, addr); } Node.blockProcessor.handleSigFreezedBlock(dummy_block, endpoint); } } } }
private void rollForward() { bool sleep = false; ulong lowestBlockNum = getLowestBlockNum(); ulong syncToBlock = syncTargetBlockNum; if (Node.blockChain.Count > 5) { lock (pendingBlocks) { pendingBlocks.RemoveAll(x => x.blockNum < Node.blockChain.getLastBlockNum() - 5); } } lock (pendingBlocks) { // Loop until we have no more pending blocks do { ulong next_to_apply = lowestBlockNum; if (Node.blockChain.Count > 0) { next_to_apply = Node.blockChain.getLastBlockNum() + 1; } if (next_to_apply > syncToBlock) { // we have everything, clear pending blocks and break pendingBlocks.Clear(); lock (requestedBlockTimes) { requestedBlockTimes.Clear(); } break; } Block b = pendingBlocks.Find(x => x.blockNum == next_to_apply); if (b == null) { lock (requestedBlockTimes) { if (requestBlockAgain(next_to_apply)) { // the node isn't connected yet, wait a while sleep = true; } } break; } b = new Block(b); if (Node.blockChain.Count == 0 && b.blockNum > 1) { Block tmp_b = Node.blockChain.getBlock(b.blockNum - 1, true, true); if (tmp_b != null) { Node.blockChain.setLastBlockVersion(tmp_b.version); } else { Node.blockChain.setLastBlockVersion(b.version); } } if (b.version > Block.maxVersion) { Logging.error("Received block {0} with a version higher than this node can handle, discarding the block.", b.blockNum); pendingBlocks.RemoveAll(x => x.blockNum == b.blockNum); Node.blockProcessor.networkUpgraded = true; sleep = true; break; } else { Node.blockProcessor.networkUpgraded = false; } if (next_to_apply > 5) { ulong targetBlock = next_to_apply - 5; Block tb = pendingBlocks.Find(x => x.blockNum == targetBlock); if (tb != null) { Block local_block = Node.blockChain.getBlock(tb.blockNum); if (local_block != null && tb.blockChecksum.SequenceEqual(local_block.blockChecksum) && Node.blockProcessor.verifyBlockBasic(tb) == BlockVerifyStatus.Valid) { if (Node.blockProcessor.verifyBlockSignatures(tb, null)) { Node.blockChain.refreshSignatures(tb, true); } else { Logging.warn("Target block " + tb.blockNum + " does not have the required consensus."); } } pendingBlocks.RemoveAll(x => x.blockNum == tb.blockNum); } } try { Logging.info("Sync: Applying block #{0}/{1}.", b.blockNum, syncToBlock); bool ignoreWalletState = true; if (b.blockNum > wsSyncConfirmedBlockNum || Config.fullStorageDataVerification) { ignoreWalletState = false; } b.powField = null; // wallet state is correct as of wsConfirmedBlockNumber, so before that we call // verify with a parameter to ignore WS tests, but do all the others BlockVerifyStatus b_status = BlockVerifyStatus.Valid; if (b.fromLocalStorage) { // TODO TODO improve this section with NodeStorage.getTransactionsInBlock once rocksdb switch happens bool missing = false; foreach (byte[] txid in b.transactions) { if (!running) { break; } Transaction t = TransactionPool.getUnappliedTransaction(txid); if (t == null) { t = Node.storage.getTransaction(txid, b.blockNum); if (t != null) { t.applied = 0; TransactionPool.addTransaction(t, true, null, Config.fullStorageDataVerification); } else { CoreProtocolMessage.broadcastGetTransaction(txid, b.blockNum); missing = true; } } } if (missing) { Logging.info("Requesting missing transactions for block {0}", b.blockNum); Thread.Sleep(100); break; } } if (b.blockNum > wsSyncConfirmedBlockNum || b.fromLocalStorage == false || Config.fullStorageDataVerification) { b_status = Node.blockProcessor.verifyBlock(b, ignoreWalletState); } else { b_status = Node.blockProcessor.verifyBlockBasic(b, false); } if (b_status == BlockVerifyStatus.Indeterminate) { if (lastProcessedBlockNum < b.blockNum) { lastProcessedBlockNum = b.blockNum; lastProcessedBlockTime = Clock.getTimestamp(); } if (Clock.getTimestamp() - lastProcessedBlockTime > ConsensusConfig.blockGenerationInterval * 2) { Logging.info("Sync: Discarding indeterminate block #{0}, due to timeout...", b.blockNum); Node.blockProcessor.blacklistBlock(b); pendingBlocks.RemoveAll(x => x.blockNum == b.blockNum); requestBlockAgain(b.blockNum); } else { Logging.info("Sync: Waiting for missing transactions from block #{0}...", b.blockNum); } Thread.Sleep(100); return; } if (b_status != BlockVerifyStatus.Valid) { Logging.warn("Block #{0} {1} is invalid. Discarding and requesting a new one.", b.blockNum, Crypto.hashToString(b.blockChecksum)); Node.blockProcessor.blacklistBlock(b); pendingBlocks.RemoveAll(x => x.blockNum == b.blockNum); requestBlockAgain(b.blockNum); if (b_status == BlockVerifyStatus.PotentiallyForkedBlock && b.blockNum + 7 > lastBlockToReadFromStorage) { Node.blockProcessor.handleForkedFlag(); } return; } if (!b.fromLocalStorage && !Node.blockProcessor.verifyBlockSignatures(b, null) && Node.blockChain.Count > 16) { Logging.warn("Block #{0} {1} doesn't have the required consensus. Discarding and requesting a new one.", b.blockNum, Crypto.hashToString(b.blockChecksum)); pendingBlocks.RemoveAll(x => x.blockNum == b.blockNum); requestBlockAgain(b.blockNum); return; } bool sigFreezeCheck = Node.blockProcessor.verifySignatureFreezeChecksum(b, null); // Apply transactions when rolling forward from a recover file without a synced WS if (b.blockNum > wsSyncConfirmedBlockNum) { if (Node.blockChain.Count <= 5 || sigFreezeCheck) { Node.walletState.beginTransaction(b.blockNum, false); bool applied = false; try { applied = Node.blockProcessor.applyAcceptedBlock(b); if (applied) { Node.walletState.commitTransaction(b.blockNum); } }catch (Exception e) { Logging.error("Error occured during block sync, while applying/commiting transactions: " + e); } if (!applied) { Logging.warn("Error applying Block #{0} {1}. Reverting, discarding and requesting a new one.", b.blockNum, Crypto.hashToString(b.blockChecksum)); Node.walletState.revertTransaction(b.blockNum); Node.blockChain.revertBlockTransactions(b); Node.blockProcessor.blacklistBlock(b); pendingBlocks.RemoveAll(x => x.blockNum == b.blockNum); requestBlockAgain(b.blockNum); return; } else { if (b.version >= BlockVer.v5 && b.lastSuperBlockChecksum == null) { // skip WS checksum check } else { if (b.lastSuperBlockChecksum != null) { byte[] wsChecksum = Node.walletState.calculateWalletStateChecksum(); if (wsChecksum == null || !wsChecksum.SequenceEqual(b.walletStateChecksum)) { Logging.error("After applying block #{0}, walletStateChecksum is incorrect!. Block's WS: {1}, actual WS: {2}", b.blockNum, Crypto.hashToString(b.walletStateChecksum), Crypto.hashToString(wsChecksum)); Node.walletState.revertTransaction(b.blockNum); Node.blockChain.revertBlockTransactions(b); Node.blockProcessor.blacklistBlock(b); pendingBlocks.RemoveAll(x => x.blockNum == b.blockNum); requestBlockAgain(b.blockNum); return; } } } if (b.blockNum % Config.saveWalletStateEveryBlock == 0) { DLT.Meta.WalletStateStorage.saveWalletState(b.blockNum); } } } } else { if (syncToBlock == b.blockNum) { if (b.version >= BlockVer.v5 && b.lastSuperBlockChecksum == null) { // skip WS checksum check } else { byte[] wsChecksum = Node.walletState.calculateWalletStateChecksum(); if (wsChecksum == null || !wsChecksum.SequenceEqual(b.walletStateChecksum)) { Logging.warn("Block #{0} is last and has an invalid WSChecksum. Discarding and requesting a new one.", b.blockNum); Node.blockProcessor.blacklistBlock(b); pendingBlocks.RemoveAll(x => x.blockNum == b.blockNum); requestBlockAgain(b.blockNum); return; } } } } if (Node.blockChain.Count <= 5 || sigFreezeCheck) { //Logging.info(String.Format("Appending block #{0} to blockChain.", b.blockNum)); if (b.blockNum <= wsSyncConfirmedBlockNum) { if (!TransactionPool.setAppliedFlagToTransactionsFromBlock(b)) { Node.blockChain.revertBlockTransactions(b); Node.blockProcessor.blacklistBlock(b); pendingBlocks.RemoveAll(x => x.blockNum == b.blockNum); requestBlockAgain(b.blockNum); return; } } if (b.blockNum > 12 && b.blockNum + 5 >= IxianHandler.getHighestKnownNetworkBlockHeight()) { if (Node.isMasterNode() && b.blockNum > 7) { byte[][] signature_data = b.applySignature(); // applySignature() will return signature_data, if signature was applied and null, if signature was already present from before if (signature_data != null) { Node.inventoryCache.setProcessedFlag(InventoryItemTypes.blockSignature, InventoryItemSignature.getHash(signature_data[1], b.blockChecksum), true); // ProtocolMessage.broadcastNewBlock(localNewBlock); SignatureProtocolMessages.broadcastBlockSignature(signature_data[0], signature_data[1], b.blockNum, b.blockChecksum, null, null); } } } Node.blockChain.appendBlock(b, !b.fromLocalStorage); } else if (Node.blockChain.Count > 5 && !sigFreezeCheck) { if (CoreConfig.preventNetworkOperations || Config.recoverFromFile) { var last_block = lastBlocks.Find(x => x.blockNum == b.blockNum - 5); if (last_block != null) { pendingBlocks.Add(last_block); } return; } // invalid sigfreeze, waiting for the correct block Logging.warn("Block #{0} {1} doesn't have the correct sigfreezed block. Discarding and requesting a new one.", b.blockNum, Crypto.hashToString(b.blockChecksum)); pendingBlocks.RemoveAll(x => x.blockNum == b.blockNum); requestBlockAgain(b.blockNum); return; } } catch (Exception e) { Logging.error("Exception occured while syncing block #{0}: {1}", b.blockNum, e); } if (Config.enableChainReorgTest) { if (!Node.blockProcessor.chainReorgTest(b.blockNum)) { pendingBlocks.RemoveAll(x => x.blockNum + 6 < b.blockNum); } } else { pendingBlocks.RemoveAll(x => x.blockNum == b.blockNum); } Node.blockProcessor.cleanupBlockBlacklist(); } while (pendingBlocks.Count > 0 && running); } if (!sleep && Node.blockChain.getLastBlockNum() >= syncToBlock) { if (verifyLastBlock()) { sleep = false; } else { sleep = true; } } if (sleep) { Thread.Sleep(500); } }