public static void handleGetRandomPresences(byte[] data, RemoteEndpoint endpoint) { if (!endpoint.isConnected()) { return; } using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { char type = reader.ReadChar(); List <Presence> presences = PresenceList.getPresencesByType(type); int presence_count = presences.Count(); if (presence_count > 10) { Random rnd = new Random(); presences = presences.Skip(rnd.Next(presence_count - 10)).Take(10).ToList(); } foreach (Presence presence in presences) { byte[][] presence_chunks = presence.getByteChunks(); foreach (byte[] presence_chunk in presence_chunks) { endpoint.sendData(ProtocolMessageCode.updatePresence, presence_chunk, null); } } } } }
public static bool broadcastNewBlock(Block b, RemoteEndpoint skipEndpoint = null, RemoteEndpoint endpoint = null, bool force_broadcast = false) { if (!Node.isMasterNode()) { return(true); } if (endpoint != null) { if (endpoint.isConnected()) { endpoint.sendData(ProtocolMessageCode.blockData, b.getBytes(false), BitConverter.GetBytes(b.blockNum)); return(true); } return(false); } else { if (force_broadcast) { return(CoreProtocolMessage.broadcastProtocolMessage(new char[] { 'M', 'H' }, ProtocolMessageCode.blockData, b.getBytes(false), BitConverter.GetBytes(b.blockNum), skipEndpoint)); } else { return(CoreProtocolMessage.addToInventory(new char[] { 'M', 'H' }, new InventoryItemBlock(b.blockChecksum, b.blockNum), skipEndpoint, ProtocolMessageCode.blockData, b.getBytes(false), BitConverter.GetBytes(b.blockNum))); } } }
public static bool broadcastGetTransaction(string txid, ulong block_num, RemoteEndpoint endpoint = null) { using (MemoryStream mw = new MemoryStream()) { using (BinaryWriter writerw = new BinaryWriter(mw)) { writerw.Write(txid); writerw.Write(block_num); #if TRACE_MEMSTREAM_SIZES Logging.info(String.Format("NetworkProtocol::broadcastGetTransaction: {0}", mw.Length)); #endif if (endpoint != null) { if (endpoint.isConnected()) { endpoint.sendData(ProtocolMessageCode.getTransaction, mw.ToArray()); return(true); } } // TODO TODO TODO TODO TODO determine if historic transaction and send to 'H' instead of 'M' return(broadcastProtocolMessageToSingleRandomNode(new char[] { 'M', 'H' }, ProtocolMessageCode.getTransaction, mw.ToArray(), block_num)); } } }
public static bool broadcastGetBlockSignatures(ulong block_num, byte[] block_checksum, RemoteEndpoint endpoint) { using (MemoryStream mw = new MemoryStream()) { using (BinaryWriter writerw = new BinaryWriter(mw)) { writerw.WriteIxiVarInt(block_num); writerw.WriteIxiVarInt(block_checksum.Length); writerw.Write(block_checksum); #if TRACE_MEMSTREAM_SIZES Logging.info(String.Format("NetworkProtocol::broadcastGetBlockSignatures: {0}", mw.Length)); #endif if (endpoint != null) { if (endpoint.isConnected()) { endpoint.sendData(ProtocolMessageCode.getBlockSignatures2, mw.ToArray()); return(true); } } return(CoreProtocolMessage.broadcastProtocolMessageToSingleRandomNode(new char[] { 'M', 'H' }, ProtocolMessageCode.getBlockSignatures2, mw.ToArray(), block_num)); } } }
public static bool broadcastGetBlock(ulong block_num, RemoteEndpoint skipEndpoint = null, RemoteEndpoint endpoint = null, byte include_transactions = 0, bool full_header = false) { using (MemoryStream mw = new MemoryStream()) { using (BinaryWriter writerw = new BinaryWriter(mw)) { writerw.WriteIxiVarInt(block_num); writerw.Write(include_transactions); writerw.Write(full_header); #if TRACE_MEMSTREAM_SIZES Logging.info(String.Format("NetworkProtocol::broadcastGetBlock: {0}", mw.Length)); #endif if (endpoint != null) { if (endpoint.isConnected()) { endpoint.sendData(ProtocolMessageCode.getBlock3, mw.ToArray()); return(true); } } return(CoreProtocolMessage.broadcastProtocolMessageToSingleRandomNode(new char[] { 'M', 'H' }, ProtocolMessageCode.getBlock3, mw.ToArray(), block_num, skipEndpoint)); } } }
private static void broadcastBlockHeaderTransactions(Block b, RemoteEndpoint endpoint) { if (!endpoint.isConnected()) { return; } foreach (var txid in b.transactions) { Transaction t = TransactionPool.getAppliedTransaction(txid, b.blockNum, true); if (endpoint.isSubscribedToAddress(NetworkEvents.Type.transactionFrom, new Address(t.pubKey).address)) { endpoint.sendData(ProtocolMessageCode.transactionData, t.getBytes(true), null); } else { foreach (var entry in t.toList) { if (endpoint.isSubscribedToAddress(NetworkEvents.Type.transactionTo, entry.Key)) { endpoint.sendData(ProtocolMessageCode.transactionData, t.getBytes(true), null); } } } } }
public static bool broadcastBlockSignature(byte[] signature, byte[] sig_address, ulong block_num, byte[] block_hash, RemoteEndpoint skipEndpoint = null, RemoteEndpoint endpoint = null) { byte[] signature_data = null; using (MemoryStream m = new MemoryStream(1152)) { using (BinaryWriter writer = new BinaryWriter(m)) { writer.WriteIxiVarInt(block_num); writer.WriteIxiVarInt(block_hash.Length); writer.Write(block_hash); writer.WriteIxiVarInt(signature.Length); writer.Write(signature); writer.WriteIxiVarInt(sig_address.Length); writer.Write(sig_address); #if TRACE_MEMSTREAM_SIZES Logging.info(String.Format("NetworkProtocol::broadcastNewBlockSignature: {0}", m.Length)); #endif signature_data = m.ToArray(); } } if (endpoint != null) { if (endpoint.isConnected()) { endpoint.sendData(ProtocolMessageCode.blockSignature2, signature_data); return(true); } return(false); } else { return(CoreProtocolMessage.addToInventory(new char[] { 'M', 'H' }, new InventoryItemSignature(sig_address, block_num, block_hash), skipEndpoint, ProtocolMessageCode.blockSignature2, signature_data, null)); } }
// Called when receiving a keepalive network message. The PresenceList will update the appropriate entry based on the timestamp. // Returns TRUE if it updated an entry in the PL // Sets the out address parameter to be the KA wallet's address or null if an error occured public static bool receiveKeepAlive(byte[] bytes, out byte[] address, RemoteEndpoint endpoint) { address = null; // Get the current timestamp long currentTime = Clock.getNetworkTimestamp(); try { using (MemoryStream m = new MemoryStream(bytes)) { using (BinaryReader reader = new BinaryReader(m)) { int keepAliveVersion = reader.ReadInt32(); int walletLen = reader.ReadInt32(); byte[] wallet = reader.ReadBytes(walletLen); // Assign the out address parameter address = wallet; string deviceid = reader.ReadString(); long timestamp = reader.ReadInt64(); string hostname = reader.ReadString(); char node_type = '0'; node_type = reader.ReadChar(); int sigLen = reader.ReadInt32(); byte[] signature = reader.ReadBytes(sigLen); //Logging.info(String.Format("[PL] KEEPALIVE request from {0}", hostname)); if (node_type == 'C' || node_type == 'R') { // all good, continue } else if (node_type == 'M' || node_type == 'H') { if (myPresenceType == 'M' || myPresenceType == 'H') { // check balance if (IxianHandler.getWalletBalance(wallet) < ConsensusConfig.minimumMasterNodeFunds) { return(false); } } } else { // reject everything else return(false); } lock (presences) { Presence listEntry = presences.Find(x => x.wallet.SequenceEqual(wallet)); if (listEntry == null && wallet.SequenceEqual(IxianHandler.getWalletStorage().getPrimaryAddress())) { Logging.warn("My entry was removed from local PL, readding."); curNodePresence.addresses.Clear(); curNodePresence.addresses.Add(curNodePresenceAddress); updateEntry(curNodePresence); listEntry = presences.Find(x => x.wallet.SequenceEqual(wallet)); } // Check if no such wallet found in presence list if (listEntry == null) { // request for additional data using (MemoryStream mw = new MemoryStream()) { using (BinaryWriter writer = new BinaryWriter(mw)) { writer.Write(wallet.Length); writer.Write(wallet); if (endpoint != null && endpoint.isConnected()) { endpoint.sendData(ProtocolMessageCode.getPresence, mw.ToArray(), wallet); } else { CoreProtocolMessage.broadcastProtocolMessageToSingleRandomNode(new char[] { 'M', 'R', 'H' }, ProtocolMessageCode.getPresence, mw.ToArray(), 0, null); } } } return(false); } // Verify the signature if (CryptoManager.lib.verifySignature(bytes.Take(bytes.Length - sigLen - 4).ToArray(), listEntry.pubkey, signature) == false) { Logging.warn(string.Format("[PL] KEEPALIVE tampering for {0} {1}, incorrect Sig.", Base58Check.Base58CheckEncoding.EncodePlain(listEntry.wallet), hostname)); return(false); } PresenceAddress pa = listEntry.addresses.Find(x => x.address == hostname && x.device == deviceid); if (pa != null) { // Check the node type if (pa.lastSeenTime != timestamp) { // Check for outdated timestamp if (timestamp < pa.lastSeenTime) { // We already have a newer timestamp for this entry return(false); } int expiration_time = CoreConfig.serverPresenceExpiration; if (pa.type == 'C') { expiration_time = CoreConfig.clientPresenceExpiration; } // Check for tampering. Includes a +300, -30 second synchronization zone if ((currentTime - timestamp) > expiration_time) { Logging.warn(string.Format("[PL] Received expired KEEPALIVE for {0} {1}. Timestamp {2}", Base58Check.Base58CheckEncoding.EncodePlain(listEntry.wallet), pa.address, timestamp)); return(false); } if ((currentTime - timestamp) < -30) { Logging.warn(string.Format("[PL] Potential KEEPALIVE tampering for {0} {1}. Timestamp {2}", Base58Check.Base58CheckEncoding.EncodePlain(listEntry.wallet), pa.address, timestamp)); return(false); } // Update the timestamp pa.lastSeenTime = timestamp; pa.signature = signature; pa.version = keepAliveVersion; if (pa.type != node_type) { lock (presenceCount) { presenceCount[pa.type]--; if (!presenceCount.ContainsKey(node_type)) { presenceCount.Add(node_type, 0); } presenceCount[node_type]++; } } pa.type = node_type; //Console.WriteLine("[PL] LASTSEEN for {0} - {1} set to {2}", hostname, deviceid, pa.lastSeenTime); return(true); } } else { if (wallet.SequenceEqual(IxianHandler.getWalletStorage().getPrimaryAddress())) { curNodePresence.addresses.Clear(); curNodePresence.addresses.Add(curNodePresenceAddress); updateEntry(curNodePresence); return(true); } else { using (MemoryStream mw = new MemoryStream()) { using (BinaryWriter writer = new BinaryWriter(mw)) { writer.Write(wallet.Length); writer.Write(wallet); if (endpoint != null && endpoint.isConnected()) { endpoint.sendData(ProtocolMessageCode.getPresence, mw.ToArray(), wallet); } else { CoreProtocolMessage.broadcastProtocolMessageToSingleRandomNode(new char[] { 'M', 'R', 'H' }, ProtocolMessageCode.getPresence, mw.ToArray(), 0, null); } } } return(false); } } } } } } catch (Exception e) { Logging.error("Exception occured in receiveKeepAlive: " + e); return(false); } return(false); }
// Broadcasts protocol message to a single random node with block height higher than the one specified with parameter block_num /// <summary> /// Sends the specified protocol message to one of the connected remote endpoints, chosen randomly. /// </summary> /// <remarks> /// The payload `data` should be properly formatted for the given `code` - this function will not ensure that this is so and /// the caller must provide a valid message to this function. /// The `skipEndpoint` parameter is useful when re-broadcasting a message received from a specific endpoint and do not wish to echo the same /// data back to the sender. /// The `block_num` parameter is used to filter the remote endpoints based on their latest known block height. /// </remarks> /// <param name="types">Types of the nodes where the message should be sent.</param> /// <param name="code">Ixian protocol code.</param> /// <param name="data">Payload data.</param> /// <param name="block_num">Block which should be searched for the endpoint addresses.</param> /// <param name="skipEndpoint">Minimum block height for endpoints which should receive this message.</param> /// <returns>True, if at least one message was sent to at least one remote endpoint. False if no messages were sent.</returns> public static bool broadcastProtocolMessageToSingleRandomNode(char[] types, ProtocolMessageCode code, byte[] data, ulong block_num, RemoteEndpoint skipEndpoint = null) { if (data == null) { Logging.warn(string.Format("Invalid protocol message data for {0}", code)); return(false); } lock (NetworkClientManager.networkClients) { lock (NetworkServer.connectedClients) { int serverCount = 0; int clientCount = 0; List <NetworkClient> servers = null; List <RemoteEndpoint> clients = null; if (types == null) { servers = NetworkClientManager.networkClients.FindAll(x => x.blockHeight > block_num && x.isConnected() && x.helloReceived); clients = NetworkServer.connectedClients.FindAll(x => x.blockHeight > block_num && x.isConnected() && x.helloReceived); serverCount = servers.Count(); clientCount = clients.Count(); if (serverCount == 0 && clientCount == 0) { servers = NetworkClientManager.networkClients.FindAll(x => x.blockHeight == block_num && x.isConnected() && x.helloReceived); clients = NetworkServer.connectedClients.FindAll(x => x.blockHeight == block_num && x.isConnected() && x.helloReceived); } } else { servers = NetworkClientManager.networkClients.FindAll(x => x.blockHeight > block_num && x.presenceAddress != null && types.Contains(x.presenceAddress.type) && x.isConnected() && x.helloReceived); clients = NetworkServer.connectedClients.FindAll(x => x.blockHeight > block_num && x.presenceAddress != null && types.Contains(x.presenceAddress.type) && x.isConnected() && x.helloReceived); serverCount = servers.Count(); clientCount = clients.Count(); if (serverCount == 0 && clientCount == 0) { servers = NetworkClientManager.networkClients.FindAll(x => x.blockHeight == block_num && x.presenceAddress != null && types.Contains(x.presenceAddress.type) && x.isConnected() && x.helloReceived); clients = NetworkServer.connectedClients.FindAll(x => x.blockHeight == block_num && x.presenceAddress != null && types.Contains(x.presenceAddress.type) && x.isConnected() && x.helloReceived); } } serverCount = servers.Count(); clientCount = clients.Count(); if (serverCount == 0 && clientCount == 0) { return(false); } Random r = new Random(); int rIdx = r.Next(serverCount + clientCount); RemoteEndpoint re = null; if (rIdx < serverCount) { re = servers[rIdx]; } else { re = clients[rIdx - serverCount]; } if (re == skipEndpoint && serverCount + clientCount > 1) { if (rIdx + 1 < serverCount) { re = servers[rIdx + 1]; } else if (rIdx + 1 < serverCount + clientCount) { re = clients[rIdx + 1 - serverCount]; } else if (serverCount > 0) { re = servers[0]; } else if (clientCount > 0) { re = clients[0]; } } if (re != null && re.isConnected()) { re.sendData(code, data); return(true); } return(false); } } }
public static void broadcastBlockSignatures(ulong block_num, byte[] block_checksum, List <byte[][]> signatures, RemoteEndpoint skip_endpoint = null, RemoteEndpoint endpoint = null) { int max_sigs_per_chunk = ConsensusConfig.maximumBlockSigners; int sig_count = signatures.Count(); if (sig_count == 0) { return; } for (int i = 0; i < sig_count;) { using (MemoryStream mOut = new MemoryStream()) { using (BinaryWriter writer = new BinaryWriter(mOut)) { writer.WriteIxiVarInt(block_num); writer.WriteIxiVarInt(block_checksum.Length); writer.Write(block_checksum); int next_sig_count; if (sig_count - i > max_sigs_per_chunk) { next_sig_count = max_sigs_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++) { byte[][] sig = signatures[i]; i++; if (sig == null) { continue; } // sig writer.WriteIxiVarInt(sig[0].Length); writer.Write(sig[0]); // address/pubkey writer.WriteIxiVarInt(sig[1].Length); writer.Write(sig[1]); } } #if TRACE_MEMSTREAM_SIZES Logging.info(String.Format("NetworkProtocol::broadcastBlockSignatures: {0}", mOut.Length)); #endif // Send a chunk if (endpoint != null) { if (!endpoint.isConnected()) { return; } endpoint.sendData(ProtocolMessageCode.signaturesChunk, mOut.ToArray(), BitConverter.GetBytes(block_num)); } else { CoreProtocolMessage.broadcastProtocolMessage(new char[] { 'M', 'H' }, ProtocolMessageCode.signaturesChunk, mOut.ToArray(), BitConverter.GetBytes(block_num), skip_endpoint); } } } }
public static void handleGetBlockHeaders(byte[] data, RemoteEndpoint endpoint) { using (MemoryStream m = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(m)) { ulong from = reader.ReadUInt64(); ulong to = reader.ReadUInt64(); ulong totalCount = to - from; if (totalCount < 1) { return; } ulong lastBlockNum = Node.blockChain.getLastBlockNum(); if (from > lastBlockNum - 1) { return; } if (to > lastBlockNum) { to = lastBlockNum; } // Adjust total count if necessary totalCount = to - from; if (totalCount < 1) { return; } // Cap total block headers sent if (totalCount > (ulong)CoreConfig.maximumBlockHeadersPerChunk) { totalCount = (ulong)CoreConfig.maximumBlockHeadersPerChunk; } if (endpoint == null) { return; } if (!endpoint.isConnected()) { return; } // TODO TODO TODO block headers should be read from a separate storage and every node should keep a full copy for (ulong i = 0; i < totalCount;) { bool found = false; using (MemoryStream mOut = new MemoryStream()) { using (BinaryWriter writer = new BinaryWriter(mOut)) { for (int j = 0; j < CoreConfig.maximumBlockHeadersPerChunk && i < totalCount; j++) { Block block = Node.blockChain.getBlock(from + i, true, true); i++; if (block == null) { break; } long rollback_len = mOut.Length; found = true; BlockHeader header = new BlockHeader(block); byte[] headerBytes = header.getBytes(); writer.Write(headerBytes.Length); writer.Write(headerBytes); if (mOut.Length > CoreConfig.maxMessageSize) { mOut.SetLength(rollback_len); i--; break; } broadcastBlockHeaderTransactions(block, endpoint); } } if (!found) { break; } endpoint.sendData(ProtocolMessageCode.blockHeaders, mOut.ToArray()); } } } } }