public void Init <T>(BitcoinNode <T> node, BitcoinPeer peer, Guid id) where T : PeerHandler, new() { Node = node as BitcoinNode <BitcoinNodePeer>; Id = id; Peer = peer; Peer.OnMessage += Peer_OnMessage; Peer.OnStopping += Peer_OnStopping; Peer.Start(); }
private async Task Peer_OnMessage(BitcoinPeer s, IStreamable msg) { switch (msg) { case Addr a: { foreach (var ip in a.Ips) { var ep = new IPEndPoint(ip.Ip.MapToIPv6(), ip.Port); var epb = ep.ToByteArray(); if (!await db.SetContainsAsync("hs:nodes:good-nodes", epb) && !await db.SetContainsAsync("hs:nodes:bad-nodes", epb)) { QueueNewNode(ep); } } break; } case Ping a: { var pong = new Pong(); pong.Nonce = a.Nonce; await s.WriteMessage(pong); break; } case bitcoin_lib.P2P.Version a: { GotVersion = true; //send verack and log await SetNodeDetails(a); var va = new VerAck(); await s.WriteMessage(va); var ga = new GetAddr(); await s.WriteMessage(ga); break; } } }
public async Task Connect() { try { var ns = new Socket(SocketType.Stream, ProtocolType.Tcp); #if NET461 var sem_conn_net461 = new SemaphoreSlim(1); var sca = new SocketAsyncEventArgs(); if (ns.ConnectAsync(sca)) { await sem_conn_net461.WaitAsync(); } #else await ns.ConnectAsync(IP); #endif Peer = new BitcoinPeer(ChainParams, ns); Peer.OnMessage += Peer_OnMessage; Peer.Start(); Ver.Timestamp = (UInt64)DateTimeOffset.Now.ToUnixTimeSeconds(); await Peer.WriteMessage(Ver); //disconnect after 5s await Task.Factory.StartNew(async() => { await Task.Delay(TimeSpan.FromSeconds(5)); if (!GotVersion) { await SetAsBadNode(); } Close(); }); } catch (Exception ex) { //Console.WriteLine($"Connection failed {ex.Message}, setting as bad node."); await SetAsBadNode(); } }
private async Task Peer_OnMessage(BitcoinPeer s, IStreamable msg) { switch (msg) { case bitcoin_lib.P2P.Version a: { PeerVersion = a; if (Peer.IsInbound) { await SendVersion(); } var va = new VerAck(); await s.WriteMessage(va); if (Peer.IsInbound && PeerVersion.HighestVersion >= 70014) { //send cmpct (i only want my version, i dont care about another version) //no version 1 msg will be sent if version 2 is supported var nVersion = (Peer.ChainParams.Services & (ulong)Services.NODE_WITNESS) != 0 ? 2ul : 1ul; var cmp = new SendCMPCT(); cmp.Version = nVersion; cmp.Enabled = true; await s.WriteMessage(cmp); } if (BlockChain.Mempool.Count == 0) { //ask for mempool var mp = new MemPool(); await s.WriteMessage(mp); } var ph = ((IPEndPoint)Peer.RemoteEndpoint).AsHash(); if (BlockChain.Peers.ContainsKey(ph)) { //update peer info BlockChain.Peers[ph].LastSeen = DateTime.Now; BlockChain.Peers[ph].LastVersion = a; } else { //ask the peer for addr info if this is the first time we seen them var ga = new GetAddr(); await s.WriteMessage(ga); } break; } case Inv a: { var gd = new GetData(); var to_get = new List <Inventory>(); var sw = new Stopwatch(); sw.Start(); to_get.AddRange(a.Inventory.Where(b => b.Type == InventoryType.MSG_TX && !BlockChain.Mempool.ContainsKey(b.Hash))); if ((PeerVersion.Services & (ulong)Services.NODE_WITNESS) == 1) { to_get.ForEach(b => b.Type = InventoryType.MSG_WITNESS_TX); } gd.Inventory = to_get.ToArray(); if (gd.Inventory.Length > 0) { //Console.WriteLine($"Asking for {gd.Count} tnxs, {sw.Elapsed.TotalMilliseconds.ToString("0.00")}ms"); await s.WriteMessage(gd); } break; } case Tx a: { if (!BlockChain.Mempool.ContainsKey(a.Hash)) { BlockChain.Mempool.Add(a.Hash, a); } break; } case VerAck a: { if (Peer.IsInbound) { await SendAddr(); } await s.WriteMessage(new Ping()); break; } case Addr a: { foreach (var ip in a.Ips) { var ph = new IPEndPoint(ip.Ip, ip.Port).AsHash(); if (!BlockChain.Peers.ContainsKey(ph)) { BlockChain.Peers.Add(ph, new PeerInfo() { Ip = Peer.RemoteEndpoint, LastSeen = DateTime.Now, LastVersion = new bitcoin_lib.P2P.Version() //add empty version }); } } break; } case Pong a: { if (PingTimer.IsRunning) { PingTimer.Stop(); } //Console.WriteLine($"[{RemoteEndpoint}]{PeerVersion.UserAgent} ping is: {LastPing.TotalMilliseconds.ToString("0.00")}ms"); break; } case Ping a: { var pong = new Pong(); pong.Nonce = a.Nonce; await s.WriteMessage(pong); break; } case MemPool a: { var inv = new Inv(); inv.Inventory = BlockChain.Mempool.Keys.Select(b => new Inventory() { Type = InventoryType.MSG_TX, Hash = b }).ToArray(); if (inv.Inventory.Length > 0) { await s.WriteMessage(inv); } break; } case SendCMPCT a: { if (CMPCTBlockVersion == 0 && a.Enabled && a.Version <= 2) { if (Peer.IsInbound) { //lock in cmpct var nVersion = (Peer.ChainParams.Services & (ulong)Services.NODE_WITNESS) != 0 ? 2ul : 1ul; if (a.Version == nVersion) { CMPCTBlockVersion = a.Version; Console.WriteLine($"Locking version {a.Version} cmpct block"); } } else { //reply to cmpct negotiation, if the node advertises witness use version 2 var nVersion = (PeerVersion.Services & (ulong)Services.NODE_WITNESS) != 0 ? 2ul : 1ul; if (a.Version == nVersion) { CMPCTBlockVersion = a.Version; } else if (nVersion != a.Version && CMPCTBlockVersion == 0) //if they only sent version 1 & they advertise NODE_WITNESS, use this version { CMPCTBlockVersion = a.Version; } var rsp = new SendCMPCT(); rsp.Version = CMPCTBlockVersion; rsp.Enabled = true; Console.WriteLine($"Sending version {rsp.Version} cmpct block"); await s.WriteMessage(rsp); } Console.WriteLine($"Peer is asking for version {a.Version} cmpct block"); } break; } } }
public async Task UpdateNodeInfo(Node n) { //Console.WriteLine($"Update node info: {n.IP}"); try { //try to connect to the node to get version info and to get its peers var ns = new Socket(SocketType.Stream, ProtocolType.Tcp); await ns.ConnectAsync(n.IP); var p = new BitcoinPeer(ns); //Console.WriteLine($"Connected to {n.IP}"); var ss = new SemaphoreSlim(0, 1); var db = Redis.GetDatabase(); p.OnVersion += async(s, v) => { //send verack and log var ip = new IPEndPoint(((IPEndPoint)s.RemoteEndpoint).Address.MapToIPv6(), ((IPEndPoint)s.RemoteEndpoint).Port); var ipb = ip.ToByteArray(); await db.HashSetAsync($"hs:nodes:detail:{ip.ToString()}", "version", v.ToArray()); if (await db.SortedSetAddAsync("hs:nodes:all-nodes", ipb, DateTime.Now.Ticks)) { //Console.WriteLine($"Got new node: {ip}"); await db.SetRemoveAsync("hs:nodes:new-nodes", ipb); var gloc = GeoIp.City(ip.Address); if (gloc.Location.HasCoordinates) { await db.GeoAddAsync("hs:nodes:geo", new GeoEntry(gloc.Location.Longitude.Value, gloc.Location.Latitude.Value, ipb)); } } var va = new VerAck(); await s.WriteMessage(va); var ga = new GetAddr(); await s.WriteMessage(ga); }; p.OnAddr += async(s, a) => { //Console.WriteLine($"Got {a.IpCount.Value} ips"); foreach (var ip in a.Ips) { var ep = new IPEndPoint(ip.Ip.MapToIPv6(), ip.Port); var epb = ep.ToByteArray(); if (await db.SetAddAsync("hs:nodes:new-nodes", epb)) { //Console.WriteLine($"Got new node: {ep}"); } } s.Stop(); ss.Release(); //Console.WriteLine($"Disconnected from {n.IP}"); }; p.Start(); Ver.Timestamp = (UInt64)DateTimeOffset.Now.ToUnixTimeSeconds(); await p.WriteMessage(Ver); await ss.WaitAsync(TimeSpan.FromSeconds(5)); } catch (Exception ex) { //Console.WriteLine(ex); } }