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();
        }
Beispiel #2
0
        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;
            }
            }
        }
Beispiel #3
0
        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;
            }
            }
        }
Beispiel #5
0
        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);
            }
        }