Esempio n. 1
0
 public static void AnnounceTo(Uri tracker, HarmonyNode node)
 {
     try
     {
         Log.Info($"Attempting to announce our presence to {tracker}...");
         var client = new WebClient();
         client.UploadString(new Uri(tracker, "/announce"), JsonConvert.SerializeObject(Announcement.Create(node)));
         Log.Info("Successfully announced ourselves to tracker");
     }
     catch (Exception ex)
     {
         Log.Warn($"Couldn't announce ourselves to tracker {tracker}: {ex}");
     }
 }
Esempio n. 2
0
 public HarmonyNetwork(Node self) : base(self, HashSingleton.Hash.HashSize)
 {
     Self = self as HarmonyNode;
 }
Esempio n. 3
0
        static void Main(string[] args)
        {
            // initialize NLog
            var console_target = new ColoredConsoleTarget("console")
            {
                Layout = @"${date:format=HH\:mm\:ss.fff} [${level}] ${message} ${exception}"
            };

            var nlog_config = new LoggingConfiguration();

            nlog_config.AddTarget(console_target);
            nlog_config.AddRuleForAllLevels(console_target);

            LogManager.Configuration = nlog_config;

            // register IPAddressResolver for the serializer to work
            CompositeResolver.RegisterAndSetAsDefault(IPAddressResolver.Instance, StandardResolver.Instance);

            // create data store
            var data_store = new DataStore();

            // create option set for command line argument parsing
            var  bootstrap_list   = new List <string>();
            var  listen_arg       = "";
            var  api_listen_arg   = "";
            var  tracker_arg      = "";
            bool test_mode        = false;
            bool daemon_mode      = false;
            bool metrics          = false;
            var  tracker_interval = 60;
            var  max_peers        = 8;

            OptionSet set = null;

            set = new OptionSet()
            {
                { "b|bootstrap=", "A comma-separated list of Harmony IDs or IP endpoints. (can be mixed)", b => bootstrap_list.AddRange(b.Split(',')) },
                { "l|listen=", "Starts listening for Harmony connections on the given IP endpoint. " +
                  "If only an integer is specified, treats the argument as 127.0.0.1:<port>." +
                  "If only an address is specified, treats the argument as <addr>:<random_port>", l => listen_arg = l },
                { "api=", "Starts listening for HTTP requests on the given IP endpoint. Implements the Harmony REST API." +
                  "If only an integer is specified, treats the argument as 127.0.0.1:<port>." +
                  "If only an address is specified, treats the argument as <addr>:<random_port>", l => api_listen_arg = l },
                { "test", "Starts an interactive test session after boot.", t => test_mode = true },
                { "c|cache=", "Instructs Harmony to read cached pieces from the given cache directory.", c => data_store.CachePath = c },
                { "daemon", "Replaces stdin reads with indefinite waits and never updates title display, useful for when running as a daemon", d => daemon_mode = true },
                { "t|tracker=", "Announces and asks for peers from a tracker Zgibe server.", t => tracker_arg = t },
                { "tracker-interval=", "Sets the announcement and node stability check interval in seconds.", i => tracker_interval = int.Parse(i) },
                { "n|name=", "Sets the node name, announced to other peers and added to the response headers of the HTTP API.", n => Name = n },
                { "m|max-peers=", "The maximum number of peers to connect to. Recommended to be set above 4.", m => max_peers = int.Parse(m) },
                { "metrics", "Prints node metrics every stabilization cycle.", m => metrics = true },
                { "?|h|help", "Shows this text.", h =>
                  {
                      Console.WriteLine("usage: harmony [--listen [<IP>:]<port>] [--api [<IP>:]port] [--bootstrap ...]\n" +
                                        "               [--tracker <URI>] [--name <name>] [--tracker-interval <seconds>]\n" +
                                        "               [--cache <path>] [--max-peers <N>] [--test] [--help] [--daemon]\n" +
                                        "               [--metrics]\n");
                      Console.WriteLine();

                      set.WriteOptionDescriptions(Console.Out);
                      Environment.Exit(0);
                  } }
            };

            var cli_leftovers = set.Parse(args);

            Log.Info("Harmony starting.");
            Log.Debug($"args: [{string.Join(", ", args.Select(arg => $"\"{arg}\""))}]");

            // interpret command line arguments
            IPEndPoint listen_ep     = new IPEndPoint(IPAddress.None, 0);
            IPEndPoint api_listen_ep = new IPEndPoint(IPAddress.None, 0);

            if (ushort.TryParse(listen_arg, out ushort listen_port))
            {
                listen_ep = new IPEndPoint(IPAddress.Loopback, listen_port);
            }
            else if (IPAddress.TryParse(listen_arg, out IPAddress listen_addr))
            {
                listen_ep = new IPEndPoint(listen_addr, 30000 + Random.Next(1000));
            }
            else if (Utilities.TryParseIPEndPoint(listen_arg, out IPEndPoint temp_ep))
            {
                listen_ep = temp_ep;
            }

            if (ushort.TryParse(api_listen_arg, out ushort api_listen_port))
            {
                api_listen_ep = new IPEndPoint(IPAddress.Loopback, api_listen_port);
            }
            else if (IPAddress.TryParse(api_listen_arg, out IPAddress api_listen_addr))
            {
                api_listen_ep = new IPEndPoint(api_listen_addr, 8000 + Random.Next(1000));
            }
            else if (Utilities.TryParseIPEndPoint(api_listen_arg, out IPEndPoint temp_ep))
            {
                api_listen_ep = temp_ep;
            }

            if (listen_ep.Address == IPAddress.None && listen_ep.Port == 0)
            {
                if (listen_arg.Any())
                {
                    Log.Warn($"Invalid argument passed to --listen: \"{listen_arg}\" is not a parsable IP endpoint. Try something in the form of 127.0.0.1:30001.");
                }

                listen_ep = new IPEndPoint(IPAddress.Loopback, 30000 + Random.Next(1000));
            }

            if (!string.IsNullOrWhiteSpace(tracker_arg))
            {
                if (Uri.TryCreate(tracker_arg, UriKind.Absolute, out Uri tracker))
                {
                    Tracker = tracker;
                }
                else if (Uri.TryCreate($"http://{tracker_arg}/", UriKind.Absolute, out tracker))
                {
                    Tracker = tracker;
                }
                else
                {
                    Log.Warn($"Invalid argument passed to --tracker: \"{tracker_arg}\" is not a parsable HTTP URI. Try something in the form of http://tracker.example.com/.");
                }
            }

            ListenEP = listen_ep;
            Log.Info($"Listening for Harmony connections on {listen_ep}");

            DaemonMode = daemon_mode;

            // start HTTP server if needed
            if (api_listen_ep.Port != 0)
            {
                Log.Info($"Listening for HTTP requests on {api_listen_ep}");

                var host = new WebHostBuilder()
                           .UseUrls($"http://{api_listen_ep}/")
                           .UseKestrel()
                           .ConfigureLogging((logging) => { logging.ClearProviders(); })
                           .SuppressStatusMessages(true)
                           .UseStartup <Startup>()
                           .Build();

                host.RunAsync();

                Log.Info("Started API server");
            }

            // initialize network parameters
            HashSingleton.Hash = SHA256.Create();
            Node = new HarmonyNode(listen_ep);
            Node.Network.MaximumPeers = max_peers;
            Node.LocalDataStore       = data_store;
            Node.PrintMetricOutput    = metrics;

            HarmonyModule.Node = Node; // Nancy module for the HTTP API

            // announce self to tracker if configured
            if (Tracker != default)
            {
                Announcer.AnnounceTo(Tracker, Node);
            }

            // configure title display
            if (!DaemonMode)
            {
                Node.PredecessorChanged += (e, s) => { UpdateDisplay(); };
                Node.SuccessorChanged   += (e, s) => { UpdateDisplay(); };
                Task.Run(() => { while (true)
                                 {
                                     UpdateDisplay(); Thread.Sleep(1000);
                                 }
                         }).ConfigureAwait(false);
            }

            // start node
            Node.Start();
            Log.Info($"Started node, our ID is {Node.ID.ToUsefulString()}");
            Log.Info($"Piece cache is located at {Node.LocalDataStore.CachePath}");

            // join network
            if (bootstrap_list.Any())
            {
                Log.Info($"Bootstrapping from {bootstrap_list.Count} sources...");

                foreach (var bootstrap_node in bootstrap_list)
                {
                    bool bootstrap_result = false;

                    if (bootstrap_node.Length == (HashSingleton.Hash.HashSize / 4)) // if bootstrap_node is an ID
                    {
                        var bootstrap_id = Utilities.ParseBytesFromString(bootstrap_node);
                        bootstrap_result = Node.Join(bootstrap_id);
                    }
                    else if (Utilities.TryParseIPEndPoint(bootstrap_node, out IPEndPoint bootstrap_ep))
                    {
                        var join_block = new JoinBlock(bootstrap_ep);
                        bootstrap_result = Node.Join(join_block.GenerateID());
                    }
                    else
                    {
                        Log.Warn($"Couldn't parse bootstrap node descriptor \"{bootstrap_node}\". Bootstrap descriptors can be in 2 forms: " +
                                 $"IP end point (ex.: 127.0.0.1:30303) or node ID (abbv. ex.: 7f00...34fc)");
                    }

                    if (bootstrap_result)
                    {
                        Log.Info($"Successfully connected to {bootstrap_node}");
                    }
                    else
                    {
                        Log.Warn($"Failed to connect to {bootstrap_node}");
                    }
                }
            }
            else
            {
                Log.Warn($"No bootstrap nodes specified, we're alone. You can form an actual network by passing either " +
                         $"{Node.ID.ToUsefulString()} or {listen_ep} to another Harmony instance.");

                Node.Join(default);
Esempio n. 4
0
 public static Announcement Create(HarmonyNode self) =>
 new Announcement()
 {
     Address = self.ListenEndPoint.Address, Port = (ushort)self.ListenEndPoint.Port, ID = self.ID
 };