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; } } }
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); } }
private async Task ReadStream() { while (!Closing) { try { //try to read a header var hdata = new byte[24]; await Stream.ReadAsyncExact(hdata, 0, hdata.Length); var h = new MessageHeader(); h.ReadFromPayload(hdata, 0); if (h != null) { //read the payload var pl = new byte[h.PayloadSize]; await Stream.ReadAsyncExact(pl, 0, pl.Length); bool checksumOk = false; //verify hash using (var sha = SHA256.Create()) { var h1 = sha.ComputeHash(pl); var h2 = sha.ComputeHash(h1); checksumOk = h2[0] == h.Checksum[0] && h2[1] == h.Checksum[1] && h2[2] == h.Checksum[2] && h2[3] == h.Checksum[3]; } if (checksumOk) { switch (h.Command) { case "addr\0\0\0\0\0\0\0\0": { if (OnAddr != null) { var a = new Addr(); a.ReadFromPayload(pl, 0); await OnAddr?.Invoke(this, a); } break; } case "alert\0\0\0\0\0\0\0": { if (OnAlert != null) { var a = new Alert(); a.ReadFromPayload(pl, 0); await OnAlert?.Invoke(this, a); } break; } case "feefilter\0\0\0": { if (OnFeeFilter != null) { var f = new FeeFilter(); f.ReadFromPayload(pl, 0); await OnFeeFilter?.Invoke(this, f); } break; } case "filteradd\0\0\0": { if (OnFilterAdd != null) { var f = new FilterAdd(); f.ReadFromPayload(pl, 0); await OnFilterAdd?.Invoke(this, f); } break; } case "filterclear\0": { if (OnFilterClear != null) { var f = new FilterClear(); f.ReadFromPayload(pl, 0); await OnFilterClear?.Invoke(this, f); } break; } case "filterload\0\0": { if (OnFilterLoad != null) { var f = new FilterLoad(); f.ReadFromPayload(pl, 0); await OnFilterLoad?.Invoke(this, f); } break; } case "getaddr\0\0\0\0\0": { if (OnGetAddr != null) { var ga = new GetAddr(); ga.ReadFromPayload(pl, 0); await OnGetAddr?.Invoke(this, ga); } break; } case "getblocks\0\0\0": { if (OnGetBlocks != null) { var gb = new GetBlocks(); gb.ReadFromPayload(pl, 0); await OnGetBlocks?.Invoke(this, gb); } break; } case "getdata\0\0\0\0\0": { if (OnGetData != null) { var gd = new GetData(); gd.ReadFromPayload(pl, 0); await OnGetData?.Invoke(this, gd); } break; } case "getheaders\0\0": { if (OnGetHeaders != null) { var gh = new GetHeaders(); gh.ReadFromPayload(pl, 0); await OnGetHeaders?.Invoke(this, gh); } break; } case "headers\0\0\0\0\0": { if (OnHeaders != null) { var hd = new Headers(); hd.ReadFromPayload(pl, 0); await OnHeaders?.Invoke(this, hd); } break; } case "inv\0\0\0\0\0\0\0\0\0": { if (OnInv != null) { var iv = new Inv(); iv.ReadFromPayload(pl, 0); await OnInv?.Invoke(this, iv); } break; } case "mempool\0\0\0\0\0": { if (OnMemPool != null) { var mp = new MemPool(); mp.ReadFromPayload(pl, 0); await OnMemPool?.Invoke(this, mp); } break; } case "notfound\0\0\0\0": { if (OnNotFound != null) { var nf = new NotFound(); nf.ReadFromPayload(pl, 0); await OnNotFound?.Invoke(this, nf); } break; } case "ping\0\0\0\0\0\0\0\0": { if (OnPing != null) { var ping = new Ping(); ping.ReadFromPayload(pl, 0); await OnPing?.Invoke(this, ping); } break; } case "pong\0\0\0\0\0\0\0\0": { if (OnPong != null) { var pong = new Pong(); pong.ReadFromPayload(pl, 0); await OnPong?.Invoke(this, pong); } break; } case "reject\0\0\0\0\0\0": { if (OnReject != null) { var re = new Reject(); re.ReadFromPayload(pl, 0); await OnReject?.Invoke(this, re); } break; } case "sendheaders\0": { if (OnSendHeaders != null) { var sh = new SendHeaders(); sh.ReadFromPayload(pl, 0); await OnSendHeaders?.Invoke(this, sh); } break; } case "verack\0\0\0\0\0\0": { if (OnVerAck != null) { var va = new VerAck(); va.ReadFromPayload(pl, 0); await OnVerAck.Invoke(this, va); } break; } case "version\0\0\0\0\0": { if (OnVersion != null) { var v = new bitcoin_lib.P2P.Version(""); v.ReadFromPayload(pl, 0); await OnVersion?.Invoke(this, v); } break; } default: { //Console.WriteLine($"Got cmd: {h.Command}"); break; } } } else { Closing = true; } } } catch (Exception ex) { Closing = true; } } }
private Task Peer_OnGetAddr(BitcoinPeer s, GetAddr a) { throw new NotImplementedException(); }
private static async Task RunNode() { try { BlockChain.Init(); var cp_btc = new ChainParams(); Node = new BitcoinNode <BitcoinNodePeer>(cp_btc, new IPEndPoint(IPAddress.Any, 8336)); Node.OnLog += Node_OnLog; Node.Start(); //var ct = node.AddPeer(new IPEndPoint(IPAddress.Parse("192.168.254.6"), 8333)); //var ct2 = node.AddPeer(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8333)); //if no peers, add seeds if (BlockChain.Peers.Count == 0) { Console.WriteLine("No peers found, adding seed nodes.."); foreach (var seed in DNSSeeds) { try { var ips = await Dns.GetHostAddressesAsync(seed); foreach (var ip in ips) { var ep = new IPEndPoint(ip, 8333); BlockChain.Peers.Add(ep.AsHash(), new PeerInfo() { Ip = ep, LastSeen = DateTime.Now, LastVersion = new bitcoin_lib.P2P.Version() }); } } catch (Exception ex) { Console.WriteLine($"No ips found for seed: {seed} ({ex.Message})"); } } } //connect to last 8 peers await TryAddLastPeers(); bool exit = false; Console.CancelKeyPress += (s, e) => { Console.WriteLine($"Shutting down.."); exit = true; //doesnt work in .net core it seems.. //https://github.com/dotnet/coreclr/issues/8565 }; while (!exit) { //ping the peers every 60s if ((DateTime.Now - LastPingSent).TotalSeconds >= 60) { //disconnect a high ping node BitcoinNodePeer hpn = Node.EnumeratePeers().Where(a => a.LastPing != TimeSpan.Zero).OrderByDescending(a => a.LastPing.TotalMilliseconds).FirstOrDefault(); if (hpn != default) { hpn.Disconnect(); } var pt = new List <Task>(); foreach (var n in Node.EnumeratePeers()) { pt.Add(n.StartPing()); } await Task.WhenAll(pt); LastPingSent = DateTime.Now; } //try to get 8 peers if (Node.EnumeratePeers().Count() < 8) { //ask for more peers if we dont have enough if (BlockChain.Peers.Count < 100) { Console.WriteLine($"Not enough known peers, asking for more.."); foreach (var n in Node.EnumeratePeers()) { var addr = new GetAddr(); await n.WriteMessage(addr); } //wait 2s for nodes to reply await Task.Delay(2000); } await TryAddLastPeers(); } await Task.Delay(100); } await ShutdownNode(); } catch (Exception ex) { Console.WriteLine(ex); } }