Beispiel #1
0
        public static JoinBlock FromID(byte[] id)
        {
            var offset = 0;

            var ip_bytes   = id.Skip(offset).Take(4).ToArray();
            var port_bytes = id.Skip(offset + 4).Take(2).ToArray();

            var addr = new IPAddress(ip_bytes);
            var port = BitConverter.ToUInt16(port_bytes, 0);

            var block = new JoinBlock(addr, port);

            if (!block.Verify(id))
            {
                return(null);
            }

            return(block);
        }
Beispiel #2
0
        public override INode Connect(byte[] id)
        {
            var join_block = JoinBlock.FromID(id);

            if (join_block == null)
            {
                return(null);
            }

            var endpoint = new IPEndPoint(join_block.Address, join_block.Port);
            var node     = Self.CreateRemoteNode(endpoint) as HarmonyRemoteNode;

            if (node == null)
            {
                MarkUnreachable(id);
                return(null);
            }

            node.DisconnectEvent += HandleNodeDisconnect;

            Add(node);

            return(node);
        }
Beispiel #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);