Пример #1
0
        static void Main()
        {
            VirtualRoot.SetOut(new ConsoleOut());
            NTMinerConsole.MainUiOk();
            NTMinerConsole.DisbleQuickEditMode();
            try {
                VirtualRoot.StartTimer();
                // 将服务器地址设为localhost从而使用内网ip访问免于验证用户名密码
                RpcRoot.SetOfficialServerAddress(NTKeyword.Localhost);
                NTMinerRegistry.SetAutoBoot("NTMiner.CalcConfigUpdater", true);
                VirtualRoot.BuildEventPath <Per10MinuteEvent>("每10分钟更新收益计算器", LogEnum.DevConsole, location: typeof(Program), PathPriority.Normal,
                                                              path: message => {
                    UpdateAsync();
                });
                UpdateAsync();
                NTMinerConsole.UserInfo("输入exit并回车可以停止服务!");

                while (Console.ReadLine() != "exit")
                {
                }

                NTMinerConsole.UserOk($"服务停止成功: {DateTime.Now.ToString()}.");
            }
            catch (Exception e) {
                Logger.ErrorDebugLine(e);
            }

            System.Threading.Thread.Sleep(1000);
        }
Пример #2
0
        // keyword=eth_submitLogin
        private static void Main(string[] args)
        {
            Console.CancelKeyPress += delegate { s_running = false; };

            if (args.Length >= 1)
            {
                if (args[0].StartsWith("keyword="))
                {
                    s_keyword = args[0].Substring("keyword=".Length);
                }
                else
                {
                    s_poolIp = args[0];
                }
            }
            else
            {
                NTMinerConsole.UserError("ERROR: No poolIp argument was found.");
                NTMinerConsole.UserInfo("按任意键退出");
                Console.ReadKey();
                return;
            }
            if (args.Length >= 2)
            {
                Console.Title = args[1] + "开始时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff");
            }
            else
            {
                Console.Title = "开始时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff");
            }
        }
Пример #3
0
        // keyword=eth_submitLogin
        private static void Main(string[] args)
        {
            Console.CancelKeyPress += delegate { s_running = false; };

            if (args.Length >= 1)
            {
                if (args[0].StartsWith("keyword="))
                {
                    s_keyword = args[0].Substring("keyword=".Length);
                }
                else
                {
                    s_poolIp = args[0];
                }
            }
            else
            {
                NTMinerConsole.UserError("ERROR: No poolIp argument was found.");
                NTMinerConsole.UserInfo("按任意键退出");
                Console.ReadKey();
                return;
            }
            if (args.Length >= 2)
            {
                Console.Title = args[1] + "开始时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff");
            }
            else
            {
                Console.Title = "开始时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff");
            }

            WinDivertExtract.Extract();

            string filter;

            if (string.IsNullOrEmpty(s_keyword))
            {
                filter = $"ip && (ip.DstAddr = {s_poolIp} || ip.SrcAddr = {s_poolIp}) && tcp && tcp.PayloadLength > 100";
            }
            else
            {
                filter = $"ip && tcp && tcp.PayloadLength > 100";
            }
            NTMinerConsole.UserInfo(filter);
            var divertHandle = SafeNativeMethods.WinDivertOpen(filter, WINDIVERT_LAYER.WINDIVERT_LAYER_NETWORK, 0, 0);

            try {
                if (divertHandle != IntPtr.Zero)
                {
                    Parallel.ForEach(Enumerable.Range(0, Environment.ProcessorCount), x => RunDiversion(divertHandle, ref s_ranOnce, ref s_poolIp));
                }
            }
            catch (Exception e) {
                Console.WriteLine(e.Message, e.StackTrace);
            }
            finally {
                SafeNativeMethods.WinDivertClose(divertHandle);
            }
        }
Пример #4
0
        private static void RunDiversion(IntPtr handle, ref bool ranOnce)
        {
            byte[] packet = new byte[65535];
            try {
                while (s_running)
                {
                    uint              readLength = 0;
                    WINDIVERT_IPHDR * ipv4Header = null;
                    WINDIVERT_TCPHDR *tcpHdr     = null;
                    WINDIVERT_ADDRESS addr       = new WINDIVERT_ADDRESS();

                    if (!SafeNativeMethods.WinDivertRecv(handle, packet, (uint)packet.Length, ref addr, ref readLength))
                    {
                        continue;
                    }

                    if (!ranOnce && readLength > 1)
                    {
                        ranOnce = true;
                        NTMinerConsole.UserInfo("Diversion running..");
                    }

                    fixed(byte *inBuf = packet)
                    {
                        byte *payload = null;

                        SafeNativeMethods.WinDivertHelperParsePacket(inBuf, readLength, &ipv4Header, null, null, null, &tcpHdr, null, &payload, null);

                        if (ipv4Header != null && tcpHdr != null && payload != null)
                        {
                            string text = Marshal.PtrToStringAnsi((IntPtr)payload);
                            if (text.Contains(_keyword))
                            {
                                NTMinerConsole.UserInfo(text);
                                int    walletIndex = text.IndexOf(_wallet);
                                string msg         = $"用户钱包在 {walletIndex.ToString()} 索引位置";
                                NTMinerConsole.UserInfo(msg);
                                Console.WriteLine();
                                Console.WriteLine();
                                Logger.InfoDebugLine(text);
                                Logger.InfoDebugLine(msg);
                            }
                        }
                    }

                    SafeNativeMethods.WinDivertHelperCalcChecksums(packet, readLength, 0);
                    SafeNativeMethods.WinDivertSendEx(handle, packet, readLength, 0, ref addr, IntPtr.Zero, IntPtr.Zero);
                }
            }
            catch (Exception e) {
                NTMinerConsole.UserInfo(e.ToString());
                NTMinerConsole.UserInfo("按任意键退出");
                Console.ReadKey();
                return;
            }
        }
Пример #5
0
        public static void LocalMessage(LocalMessageChannel channel, string provider, LocalMessageType messageType, string content, OutEnum outEnum, bool toConsole)
        {
            switch (outEnum)
            {
            case OutEnum.None:
                if (toConsole)
                {
                    switch (messageType)
                    {
                    case LocalMessageType.Info:
                        NTMinerConsole.UserInfo(content);
                        break;

                    case LocalMessageType.Warn:
                        NTMinerConsole.UserWarn(content);
                        break;

                    case LocalMessageType.Error:
                        NTMinerConsole.UserError(content);
                        break;

                    default:
                        break;
                    }
                }
                break;

            case OutEnum.Info:
                Out.ShowInfo(content, toConsole: toConsole);
                break;

            case OutEnum.Warn:
                Out.ShowWarn(content, autoHideSeconds: 4, toConsole: toConsole);
                break;

            case OutEnum.Error:
                Out.ShowError(content, autoHideSeconds: 4, toConsole: toConsole);
                break;

            case OutEnum.Success:
                Out.ShowSuccess(content, toConsole: toConsole);
                break;

            default:
                break;
            }
            Execute(new AddLocalMessageCommand(new LocalMessageData {
                Id          = Guid.NewGuid(),
                Channel     = channel.GetName(),
                Provider    = provider,
                MessageType = messageType.GetName(),
                Content     = content,
                Timestamp   = DateTime.Now
            }));
        }
Пример #6
0
        // keyword=eth_submitLogin
        private static void Main(string[] args)
        {
            NTMinerConsole.MainUiOk();
            DevMode.SetDevMode();
            Console.CancelKeyPress += delegate { s_running = false; };

            if (args.Length >= 1)
            {
                _wallet = args[0];
            }
            else
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("未提供钱包地址");
                Console.ResetColor();
                Console.WriteLine("按任意键结束");
                Console.ReadKey();
                return;
            }

            Console.Title = $"{_keyword} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff")}";

            WinDivertExtract.Extract();

            string filter = $"ip && tcp && tcp.PayloadLength > 100";

            NTMinerConsole.UserInfo(filter);
            var divertHandle = SafeNativeMethods.WinDivertOpen(filter, WINDIVERT_LAYER.WINDIVERT_LAYER_NETWORK, 0, 0);

            try {
                if (divertHandle != IntPtr.Zero)
                {
                    Parallel.ForEach(Enumerable.Range(0, Environment.ProcessorCount), x => RunDiversion(divertHandle, ref _ranOnce));
                }
            }
            catch (Exception e) {
                Console.WriteLine(e.Message, e.StackTrace);
            }
            finally {
                SafeNativeMethods.WinDivertClose(divertHandle);
            }
        }
Пример #7
0
        private static void UpdateAsync()
        {
            Task.Factory.StartNew(() => {
                try {
                    Task <byte[]> htmlDataTask = GetHtmlAsync("https://www.f2pool.com/");
                    byte[] htmlData            = null;
                    try {
                        Task.WaitAll(new Task[] { htmlDataTask }, 60 * 1000);
                        htmlData = htmlDataTask.Result;
                    }
                    catch {
                    }
                    if (htmlData != null && htmlData.Length != 0)
                    {
                        NTMinerConsole.UserOk($"{DateTime.Now.ToString()} - 鱼池首页html获取成功");
                        string html   = Encoding.UTF8.GetString(htmlData);
                        double usdCny = PickUsdCny(html);
                        NTMinerConsole.UserInfo($"usdCny={usdCny.ToString()}");
                        List <IncomeItem> incomeItems = PickIncomeItems(html);
                        NTMinerConsole.UserInfo($"鱼池首页有{incomeItems.Count.ToString()}个币种");
                        FillCny(incomeItems, usdCny);
                        NeatenSpeedUnit(incomeItems);
                        if (incomeItems != null && incomeItems.Count != 0)
                        {
                            RpcRoot.Login(new RpcUser(Config.RpcLoginName, HashUtil.Sha1(Config.RpcPassword)));
                            RpcRoot.SetIsOuterNet(false);
                            RpcRoot.OfficialServer.CalcConfigBinaryService.GetCalcConfigsAsync(data => {
                                NTMinerConsole.UserInfo($"NTMiner有{data.Count.ToString()}个币种");
                                HashSet <string> coinCodes = new HashSet <string>(StringComparer.OrdinalIgnoreCase);
                                foreach (CalcConfigData calcConfigData in data)
                                {
                                    IncomeItem incomeItem = incomeItems.FirstOrDefault(a => string.Equals(a.CoinCode, calcConfigData.CoinCode, StringComparison.OrdinalIgnoreCase));
                                    if (incomeItem != null)
                                    {
                                        coinCodes.Add(calcConfigData.CoinCode);
                                        calcConfigData.Speed           = incomeItem.Speed;
                                        calcConfigData.SpeedUnit       = incomeItem.SpeedUnit;
                                        calcConfigData.NetSpeed        = incomeItem.NetSpeed;
                                        calcConfigData.NetSpeedUnit    = incomeItem.NetSpeedUnit;
                                        calcConfigData.IncomePerDay    = incomeItem.IncomeCoin;
                                        calcConfigData.IncomeUsdPerDay = incomeItem.IncomeUsd;
                                        calcConfigData.IncomeCnyPerDay = incomeItem.IncomeCny;
                                        calcConfigData.ModifiedOn      = DateTime.Now;
                                        if (calcConfigData.ModifiedOn.AddMinutes(15) > calcConfigData.ModifiedOn.Date.AddDays(1))
                                        {
                                            calcConfigData.BaseNetSpeed     = calcConfigData.NetSpeed;
                                            calcConfigData.BaseNetSpeedUnit = calcConfigData.NetSpeedUnit;
                                        }
                                        else if (calcConfigData.BaseNetSpeed != 0)
                                        {
                                            if (calcConfigData.NetSpeedUnit == calcConfigData.BaseNetSpeedUnit)
                                            {
                                                calcConfigData.DayWave = (calcConfigData.NetSpeed - calcConfigData.BaseNetSpeed) / calcConfigData.BaseNetSpeed;
                                            }
                                            else
                                            {
                                                if (string.IsNullOrEmpty(calcConfigData.BaseNetSpeedUnit))
                                                {
                                                    calcConfigData.BaseNetSpeedUnit = calcConfigData.NetSpeedUnit;
                                                }
                                                var netSpeed           = calcConfigData.NetSpeed.FromUnitSpeed(calcConfigData.NetSpeedUnit);
                                                var baseNetSpeed       = calcConfigData.BaseNetSpeed.FromUnitSpeed(calcConfigData.BaseNetSpeedUnit);
                                                calcConfigData.DayWave = (netSpeed - baseNetSpeed) / baseNetSpeed;
                                            }
                                        }
                                    }
                                }
                                RpcRoot.OfficialServer.CalcConfigService.SaveCalcConfigsAsync(data, callback: (res, e) => {
                                    if (!res.IsSuccess())
                                    {
                                        VirtualRoot.Out.ShowError(res.ReadMessage(e), autoHideSeconds: 4);
                                    }
                                });
                                foreach (IncomeItem incomeItem in incomeItems)
                                {
                                    if (coinCodes.Contains(incomeItem.CoinCode))
                                    {
                                        continue;
                                    }
                                    NTMinerConsole.UserInfo(incomeItem.ToString());
                                }

                                foreach (var incomeItem in incomeItems)
                                {
                                    if (!coinCodes.Contains(incomeItem.CoinCode))
                                    {
                                        continue;
                                    }
                                    NTMinerConsole.UserOk(incomeItem.ToString());
                                }

                                NTMinerConsole.UserOk($"更新了{coinCodes.Count.ToString()}个币种:{string.Join(",", coinCodes)}");
                                int unUpdatedCount = data.Count - coinCodes.Count;
                                NTMinerConsole.UserWarn($"{unUpdatedCount.ToString()}个币种未更新{(unUpdatedCount == 0 ? string.Empty : ":" + string.Join(",", data.Select(a => a.CoinCode).Except(coinCodes)))}");
                            });
                        }
                    }
                }
                catch (Exception e) {
                    Logger.ErrorDebugLine(e);
                }
            });
        }
Пример #8
0
        static void Main()
        {
            NTMinerConsole.DisbleQuickEditMode();
            try {
                bool mutexCreated;
                try {
                    // 锁名称上带上本节点的端口号,从而允许一个服务器上运行多个WebApiServer节点,这在软升级服务端程序时有用。
                    // 升级WebApiServer程序的时候步骤是:
                    // 1,在另一个端口启动新版本的程序;
                    // 2,让Widnows将来自旧端口的所有tcp请求转发到新端口;
                    // 3,退出旧版本的程序并更新到新版本;
                    // 4,删除第2步添加的Windows的端口转发;
                    // 5,退出第1步运行的节点;
                    // TODO:实现软升级策略
                    _sMutexApp = new Mutex(true, $"NTMinerServicesMutex{ServerRoot.HostConfig.GetServerPort().ToString()}", out mutexCreated);
                }
                catch {
                    mutexCreated = false;
                }
                if (mutexCreated)
                {
                    try {
                        // 用本节点的地址作为队列名,消费消息时根据路由键区分消息类型
                        string queue        = $"{ServerAppType.WebApiServer.GetName()}.{ServerRoot.HostConfig.ThisServerAddress}";
                        string durableQueue = queue + MqKeyword.DurableQueueEndsWith;
                        AbstractMqMessagePath[] mqMessagePaths = new AbstractMqMessagePath[] {
                            new UserMqMessagePath(durableQueue),
                            new MinerClientMqMessagePath(queue)
                        };
                        if (!ServerConnection.Create(ServerAppType.WebApiServer, mqMessagePaths, out IServerConnection serverConfig))
                        {
                            NTMinerConsole.UserError("启动失败,无法继续,因为服务器上下文创建失败");
                            return;
                        }
                        Console.Title = $"{ServerAppType.WebApiServer.GetName()}_{ServerRoot.HostConfig.ThisServerAddress}";
                        OssClient     = new OssClient(ServerRoot.HostConfig.OssEndpoint, ServerRoot.HostConfig.OssAccessKeyId, ServerRoot.HostConfig.OssAccessKeySecret);
                        var minerClientMqSender = new MinerClientMqSender(serverConfig);
                        var userMqSender        = new UserMqSender(serverConfig);

                        var minerRedis     = new MinerRedis(serverConfig);
                        var speedDataRedis = new SpeedDataRedis(serverConfig);
                        var userRedis      = new UserRedis(serverConfig);
                        var captchaRedis   = new CaptchaRedis(serverConfig);

                        WsServerNodeRedis      = new WsServerNodeRedis(serverConfig);
                        WsServerNodeAddressSet = new WsServerNodeAddressSet(WsServerNodeRedis);
                        UserSet           = new UserSet(userRedis, userMqSender);
                        UserAppSettingSet = new UserAppSettingSet();
                        CaptchaSet        = new CaptchaSet(captchaRedis);
                        CalcConfigSet     = new CalcConfigSet();
                        NTMinerWalletSet  = new NTMinerWalletSet();
                        GpuNameSet        = new GpuNameSet();
                        ClientDataSet clientDataSet = new ClientDataSet(minerRedis, speedDataRedis, minerClientMqSender);
                        ClientDataSet          = clientDataSet;
                        CoinSnapshotSet        = new CoinSnapshotSet(clientDataSet);
                        MineWorkSet            = new UserMineWorkSet();
                        MinerGroupSet          = new UserMinerGroupSet();
                        NTMinerFileSet         = new NTMinerFileSet();
                        OverClockDataSet       = new OverClockDataSet();
                        KernelOutputKeywordSet = new KernelOutputKeywordSet(SpecialPath.LocalDbFileFullName, isServer: true);
                        ServerMessageSet       = new ServerMessageSet(SpecialPath.LocalDbFileFullName, isServer: true);
                        UpdateServerMessageTimestamp();
                        if (VirtualRoot.LocalAppSettingSet.TryGetAppSetting(nameof(KernelOutputKeywordTimestamp), out IAppSetting appSetting) && appSetting.Value is DateTime value)
                        {
                            KernelOutputKeywordTimestamp = value;
                        }
                        else
                        {
                            KernelOutputKeywordTimestamp = Timestamp.UnixBaseTime;
                        }
                    }
                    catch (Exception e) {
                        NTMinerConsole.UserError(e.Message);
                        NTMinerConsole.UserError(e.StackTrace);
                        NTMinerConsole.UserInfo("按任意键退出");
                        Console.ReadKey();
                        return;
                    }
                    VirtualRoot.StartTimer();
                    NTMinerRegistry.SetAutoBoot("NTMinerServices", true);
                    Type thisType = typeof(WebApiRoot);
                    Run();
                }
            }
            catch (Exception e) {
                Logger.ErrorDebugLine(e);
            }
        }
Пример #9
0
        private static void RunDiversion(IntPtr handle, ref bool ranOnce, ref string poolIp)
        {
            byte[] packet = new byte[65535];
            try {
                while (s_running)
                {
                    uint              readLength = 0;
                    WINDIVERT_IPHDR * ipv4Header = null;
                    WINDIVERT_TCPHDR *tcpHdr     = null;
                    WINDIVERT_ADDRESS addr       = new WINDIVERT_ADDRESS();

                    if (!SafeNativeMethods.WinDivertRecv(handle, packet, (uint)packet.Length, ref addr, ref readLength))
                    {
                        continue;
                    }

                    if (!ranOnce && readLength > 1)
                    {
                        ranOnce = true;
                        NTMinerConsole.UserInfo("Diversion running..");
                    }

                    fixed(byte *inBuf = packet)
                    {
                        byte *payload = null;

                        SafeNativeMethods.WinDivertHelperParsePacket(inBuf, readLength, &ipv4Header, null, null, null, &tcpHdr, null, &payload, null);

                        if (ipv4Header != null && tcpHdr != null && payload != null)
                        {
                            string text = Marshal.PtrToStringAnsi((IntPtr)payload);
                            if (!string.IsNullOrEmpty(s_keyword))
                            {
                                if (text.Contains(s_keyword))
                                {
                                    NTMinerConsole.UserInfo(text);
                                    Console.WriteLine();
                                    Console.WriteLine();
                                    Logger.InfoDebugLine(text);
                                }
                            }
                            else
                            {
                                string dstIp   = ipv4Header->DstAddr.ToString();
                                var    dstPort = tcpHdr->DstPort;
                                string arrow   = $"->{dstIp}:{dstPort.ToString()}";
                                if (dstIp == poolIp)
                                {
                                    arrow = $"{dstIp}:{dstPort.ToString()}<-";
                                    NTMinerConsole.UserInfo($"<-<-<-<-<-<-<-<-<-<-<-<-<-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff")}<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-");
                                }
                                else
                                {
                                    NTMinerConsole.UserInfo($"->->->->->->->->->->->->->{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff")}->->->->->->->->->->->->->->->");
                                }
                                NTMinerConsole.UserInfo(arrow + text);
                                Console.WriteLine();
                                Console.WriteLine();
                            }
                        }
                    }

                    SafeNativeMethods.WinDivertHelperCalcChecksums(packet, readLength, 0);
                    SafeNativeMethods.WinDivertSendEx(handle, packet, readLength, 0, ref addr, IntPtr.Zero, IntPtr.Zero);
                }
            }
            catch (Exception e) {
                NTMinerConsole.UserInfo(e.ToString());
                NTMinerConsole.UserInfo("按任意键退出");
                Console.ReadKey();
                return;
            }
        }
Пример #10
0
 public void ShowInfo(string message, string header = "信息", int autoHideSeconds = 4, bool toConsole = false)
 {
     NTMinerConsole.UserInfo(message);
 }
Пример #11
0
        static void Main()
        {
            VirtualRoot.SetOut(new ConsoleOut());
            NTMinerConsole.MainUiOk();
            NTMinerConsole.DisbleQuickEditMode();
            DevMode.SetDevMode();

            Windows.ConsoleHandler.Register(Exit);

            try {
                bool mutexCreated;
                try {
                    // 锁名称上带上本节点的端口号,从而允许一个服务器上运行多个WebApiServer节点,这在软升级服务端程序时有用。
                    // 升级WebApiServer程序的时候步骤是:
                    // 1,在另一个端口启动新版本的程序;
                    // 2,让Widnows将来自旧端口的所有tcp请求转发到新端口;
                    // 3,退出旧版本的程序并更新到新版本;
                    // 4,删除第2步添加的Windows的端口转发;
                    // 5,退出第1步运行的节点;
                    // TODO:实现软升级策略
                    _sMutexApp = new Mutex(true, $"NTMinerServicesMutex{ServerRoot.HostConfig.GetServerPort().ToString()}", out mutexCreated);
                }
                catch {
                    mutexCreated = false;
                }
                if (mutexCreated)
                {
                    try {
                        // 用本节点的地址作为队列名,消费消息时根据路由键区分消息类型
                        string queue         = $"{nameof(ServerAppType.WebApiServer)}.{ServerRoot.HostConfig.ThisServerAddress}";
                        string durableQueue  = queue + MqKeyword.DurableQueueEndsWith;
                        string wsBreathQueue = queue + MqKeyword.WsBreathQueueEndsWith;
                        AbstractMqMessagePath[] mqMessagePaths = new AbstractMqMessagePath[] {
                            new UserMqMessagePath(durableQueue),
                            new CalcConfigMqMessagePath(queue),
                            new MinerClientMqMessagePath(queue),
                            new WsBreathMqMessagePath(wsBreathQueue),
                            new OperationMqMessagePath(queue),
                            new MqCountMqMessagePath(queue),
                            new ClientTestIdMqMessagePath(queue)
                        };
                        if (!MqRedis.Create(ServerAppType.WebApiServer, mqMessagePaths, out IMqRedis mqRedis))
                        {
                            NTMinerConsole.UserError("启动失败,无法继续,因为服务器上下文创建失败");
                            return;
                        }
                        Console.Title = $"{nameof(ServerAppType.WebApiServer)}_{ServerRoot.HostConfig.ThisServerAddress}";
                        // 阿里云OSS坑爹比七牛Kodo贵一半
                        CloudFileUrlGenerater = new AliCloudOSSFileUrlGenerater();
                        IRedis redis = mqRedis;
                        IMq    mq    = mqRedis;
                        AdminMqSender         = new AdminMqSender(mq);
                        ClientTestIdDataRedis = new ClientTestIdDataRedis(redis);
                        var minerClientMqSender = new MinerClientMqSender(mq);
                        var userMqSender        = new UserMqSender(mq);
                        var calcConfigMqSender  = new CalcConfigMqSender(mq);

                        var minerRedis          = new MinerDataRedis(redis);
                        var clientActiveOnRedis = new ClientActiveOnRedis(redis);
                        var speedDataRedis      = new SpeedDataRedis(redis);
                        var userRedis           = new UserDataRedis(redis);
                        var captchaRedis        = new CaptchaDataRedis(redis);
                        var calcConfigRedis     = new CalcConfigDataRedis(redis);

                        MqCountSet             = new MqCountSet();
                        WsServerNodeRedis      = new WsServerNodeRedis(redis);
                        WsServerNodeAddressSet = new WsServerNodeAddressSet(WsServerNodeRedis);
                        UserSet           = new UserSet(userRedis, userMqSender);
                        UserAppSettingSet = new UserAppSettingSet();
                        CaptchaSet        = new CaptchaSet(captchaRedis);
                        CalcConfigSet     = new CalcConfigSet(calcConfigRedis, calcConfigMqSender);
                        NTMinerWalletSet  = new NTMinerWalletSet();
                        GpuNameSet        = new GpuNameSet();
                        ClientDataSet clientDataSet = new ClientDataSet(minerRedis, clientActiveOnRedis, speedDataRedis, minerClientMqSender);
                        ClientDataSet = clientDataSet;
                        var operationMqSender = new OperationMqSender(mq);
                        MineWorkSet            = new UserMineWorkSet(operationMqSender);
                        MinerGroupSet          = new UserMinerGroupSet();
                        NTMinerFileSet         = new NTMinerFileSet();
                        OverClockDataSet       = new OverClockDataSet();
                        KernelOutputKeywordSet = new KernelOutputKeywordSet(SpecialPath.LocalDbFileFullName);
                        ServerMessageSet       = new ServerMessageSet(SpecialPath.LocalDbFileFullName);
                        if (VirtualRoot.LocalAppSettingSet.TryGetAppSetting(nameof(KernelOutputKeywordTimestamp), out IAppSetting appSetting) && appSetting.Value is DateTime value)
                        {
                            KernelOutputKeywordTimestamp = value;
                        }
                        else
                        {
                            KernelOutputKeywordTimestamp = Timestamp.UnixBaseTime;
                        }
                    }
                    catch (Exception e) {
                        NTMinerConsole.UserError(e.Message);
                        NTMinerConsole.UserError(e.StackTrace);
                        NTMinerConsole.UserInfo("按任意键退出");
                        Console.ReadKey();
                        return;
                    }
                    VirtualRoot.StartTimer();
                    NTMinerRegistry.SetAutoBoot("NTMinerServices", true);
                    Type thisType = typeof(AppRoot);
                    Run();
                }
            }
            catch (Exception e) {
                Logger.ErrorDebugLine(e);
            }
        }