private SemaphoreSlim semaphore = new SemaphoreSlim(1);      // we need to run tests in serial

        public static TransitWallet Restore(string privateKey)
        {
            var accountId = Signatures.GetAccountIdFromPrivateKey(privateKey);

            return(new TransitWallet(accountId, privateKey, LyraRestClient.Create(TestConfig.networkId, "Windows", "UnitTest", "1.0")));
        }
Exemple #2
0
        // args: [number] the tps to simulate
        //
        static async System.Threading.Tasks.Task Main(string[] args)
        {
            var workingFolder = @"C:\working\Friday";

            var lyraFolder = BaseAccount.GetFullFolderName("Lyra-CLI-" + network_id);

            Console.WriteLine("Press enter to begin.");
            Console.ReadLine();

            // create and save wallets
            //var tt = new TransactionTester();
            //var wlts = tt.CreateWallet(1000);
            //var json = JsonConvert.SerializeObject(wlts);
            //File.WriteAllText(workingFolder + @"\wallets.json", json);

            // key is account id
            var wallets = JsonConvert.DeserializeObject <Dictionary <string, string> >(File.ReadAllText(workingFolder + @"\\wallets.json"));

            //var rpcClient = await LyraRestClient.CreateAsync(network_id, "Windows", "Lyra Client Cli", "1.0a", "https://192.168.3.62:4505/api/LyraNode/");
            var rpcClient = await LyraRestClient.CreateAsync(network_id, "Windows", "Lyra Client Cli", "1.0a");

            var tt = new TransactionTester(rpcClient);

            var masterWallet = new Wallet(new LiteAccountDatabase(), network_id);

            masterWallet.AccountName = "My Account";
            masterWallet.OpenAccount(lyraFolder, masterWallet.AccountName);
            await masterWallet.Sync(rpcClient);

            _ = Task.Run(async() =>
            {
                while (true)
                {
                    var state = await rpcClient.GetSyncState();
                    await Task.Delay(10000);
                    var state2 = await rpcClient.GetSyncState();

                    //var tps = state2.NewestBlockUIndex - state.NewestBlockUIndex;

                    //Console.WriteLine($"\n============> TPS: {tps} / 10\n");
                }
            });

            //var all = await tt.RefreshBalancesAsync(wallets.Select(a => new KeyPair(Base58Encoding.DecodePrivateKey(a.Value))).ToArray());
            //File.WriteAllText(workingFolder + @"\balances.json", JsonConvert.SerializeObject(all));

            var rich10     = JsonConvert.DeserializeObject <List <WalletBalance> >(File.ReadAllText(workingFolder + @"\balances.json"));
            var realRich10 = rich10.Where(a => a.balance.ContainsKey(lyraCoin) && a.balance.ContainsKey(testCoin))
                             .Where(a => a.balance[testCoin] >= 10000).ToDictionary(a => a.privateKey, a => a.balance);

            //var rich90 = wallets.Where(a => !realRich10.ContainsKey(a.Value)).Take(90);
            var rich90 = JsonConvert.DeserializeObject <List <KeyValuePair <string, string> > >(File.ReadAllText(workingFolder + @"\\rich90.json"));
            //File.WriteAllText(workingFolder + @"\rich90.json", JsonConvert.SerializeObject(rich90));

            var poors = wallets.Where(a => !rich90.Any(x => x.Key == a.Key));

            var testGroup1 = rich90.Take(350);
            await tt.MultiThreadedSendAsync(new [] { masterWallet.PrivateKey }, testGroup1.Select(a => a.Key).ToArray(), new Dictionary <string, decimal> {
                { lyraCoin, 5000 }
            }, true);

            Console.WriteLine("Coin distribute OK. Press Enter to continue...");
            Console.ReadLine();

            await tt.MultiThreadedSendAsync(testGroup1.Select(a => a.Value).ToArray(), poors.Select(a => a.Key).ToArray(), new Dictionary <string, decimal> {
                { lyraCoin, 1 }
            });

            Console.ReadLine();

            //foreach(var b in masterWallet.GetLatestBlock().Balances)
            //{
            //    Console.WriteLine($"{b.Key}: {b.Value}");
            //}
            //Console.WriteLine("Hello Lyra!");

            //var top10 = wallets.Take(10).ToDictionary(a => a.Key, a => a.Value);

            //await tt.SingleThreadedSendAsync(10, masterWallet, top10.Keys.ToArray(), new Dictionary<string, decimal> {
            //    { lyraCoin, 10000 }, {testCoin, 1000000}
            //});

            //var top100 = wallets.Skip(10).Take(100).ToDictionary(a => a.Key, a => a.Value);
            //await tt.MultiThreadedSendAsync(10, top10.Select(a => new KeyPair(Base58Encoding.DecodePrivateKey(a.Value))).ToArray(),
            //    top100.Values.ToArray(), new Dictionary<string, decimal> {
            //        { lyraCoin, 100 }, {testCoin, 10000} }
            //    );
        }
Exemple #3
0
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            _waitOrder = new AutoResetEvent(false);
            try
            {
                var networkId = LyraNodeConfig.GetNetworkId();

                _log.LogInformation($"{LyraGlobal.PRODUCTNAME} {LyraGlobal.NODE_VERSION} Mode: {Neo.Settings.Default.LyraNode.Lyra.Mode}: Starting node daemon for {networkId}.");

                // something must be initialized first

                Wallet PosWallet;

                string lyrawalletfolder = Wallet.GetFullFolderName(networkId, "wallets");

                if (!Directory.Exists(lyrawalletfolder))
                {
                    Directory.CreateDirectory(lyrawalletfolder);
                }

                var walletStore = new SecuredWalletStore(lyrawalletfolder);
                if (!walletStore.Exists(Neo.Settings.Default.LyraNode.Lyra.Wallet.Name))
                {
                    _log.LogInformation($"Creating wallet for {networkId}.");

                    (var privateKey, var publicKey) = Signatures.GenerateWallet();

                    _log.LogInformation($"The new wallet {Neo.Settings.Default.LyraNode.Lyra.Wallet.Name} for {networkId} was created.");
                    //Console.WriteLine($"Private Key: {privateKey}");
                    _log.LogInformation($"Account ID: {publicKey}");

                    walletStore.Create(Neo.Settings.Default.LyraNode.Lyra.Wallet.Name, Neo.Settings.Default.LyraNode.Lyra.Wallet.Password, networkId, privateKey, publicKey, "");
                    _log.LogInformation($"Wallet saved to: {lyrawalletfolder}{Neo.Settings.Default.LyraNode.Lyra.Wallet.Name}.lyrawallet");
                }

                PosWallet = Wallet.Open(walletStore,
                                        Neo.Settings.Default.LyraNode.Lyra.Wallet.Name,
                                        Neo.Settings.Default.LyraNode.Lyra.Wallet.Password,
                                        LyraRestClient.Create(networkId, "", "NodeService", "1.0", LyraGlobal.SelectNode(networkId) + "Node/"));
                _log.LogInformation($"Staking wallet: {PosWallet.AccountId}");
                PosWallet.SetVoteFor(PosWallet.AccountId);

                var blcokcount = await _store.GetBlockCountAsync();

                if (blcokcount > 0 && networkId == "devnet") // not genesis
                {
                    try
                    {
                        await PosWallet.SyncAsync(null);
                    }
                    catch { }
                }

                var localNode = DagSystem.ActorSystem.ActorOf(Neo.Network.P2P.LocalNode.Props());
                Dag = new DagSystem(_hostEnv, _store, _lyraEventContext, PosWallet, localNode);
                _   = Task.Run(async() => await Dag.StartAsync()).ConfigureAwait(false);
                await Task.Delay(30000);
            }
            catch (Exception ex)
            {
                _log.LogCritical($"NodeService: Error Initialize! {ex}");
            }

            while (!stoppingToken.IsCancellationRequested)
            {
                // do work
                if (_waitOrder.WaitOne(1000))
                {
                    _waitOrder.Reset();
                }
                else
                {
                    // no new order. do house keeping.
                }
            }
        }
Exemple #4
0
        /// <summary>
        /// if this node is seed0 then sync with seeds others (random choice the one that is in normal state)
        /// if this node is seed1+ then sync with seed0
        /// otherwise sync with any seed node
        /// </summary>
        private void SyncBlocksFromSeeds(long ToUIndex)
        {
            InSyncing = true;
            Task.Run(async() => {
                while (true)
                {
                    _log.LogInformation("BlockChain Doing Sync...");
                    string syncWithUrl    = null;
                    LyraRestClient client = null;
                    long syncToUIndex     = ToUIndex;
#if DEBUG
                    for (int i = 0; i < 2; i++)         // save time
#else
                    for (int i = 0; i < ProtocolSettings.Default.SeedList.Length; i++)
#endif
                    {
                        if (NodeService.Instance.PosWallet.AccountId == ProtocolSettings.Default.StandbyValidators[i])  // self
                        {
                            continue;
                        }

                        try
                        {
                            var addr   = ProtocolSettings.Default.SeedList[i].Split(':')[0];
                            var apiUrl = $"https://{addr}:4505/api/LyraNode/";
                            _log.LogInformation("Platform {1} Use seed node of {0}", apiUrl, Environment.OSVersion.Platform);
                            client   = await LyraRestClient.CreateAsync(NetworkID, Environment.OSVersion.Platform.ToString(), "LyraNode2", "1.0", apiUrl);
                            var mode = await client.GetSyncState();
                            if (mode.ResultCode == APIResultCodes.Success && mode.Mode == ConsensusWorkingMode.Normal)
                            {
                                syncWithUrl = apiUrl;
                                if (syncToUIndex == 0)
                                {
                                    syncToUIndex = mode.NewestBlockUIndex;
                                }
                                break;
                            }
                        }
                        catch (Exception ex)
                        {
                            _log.LogWarning($"Trying to sync.. {ex.Message}");
                        }
                    }

                    if (syncWithUrl == null)
                    {
                        // no node to sync.
                        if (NodeService.Instance.PosWallet.AccountId == ProtocolSettings.Default.StandbyValidators[0])
                        {
                            // seed0. no seed to sync. this seed must have the NORMAL blockchain
                            break;
                        }
                        else
                        {
                            _log.LogError("No seed node in normal state. Wait...");
                            await Task.Delay(300 * 1000);
                        }
                    }
                    else
                    {
                        // update latest billboard
                        var board = await client.GetBillBoardAsync();
                        LyraSystem.Singleton.Consensus.Tell(board);

                        // do sync with node
                        long startUIndex = await _store.GetNewestBlockUIndexAsync() + 1;

                        _log.LogInformation($"BlockChain Doing sync from {startUIndex} to {syncToUIndex} from node {syncWithUrl}");

                        async Task <bool> DoCopyBlock(long fromUIndex, long toUIndex)
                        {
                            var authorizers = new AuthorizersFactory();

                            for (long j = fromUIndex; j <= toUIndex; j++)
                            {
                                var blockResult = await client.GetBlockByUIndex(j).ConfigureAwait(false);
                                if (blockResult.ResultCode == APIResultCodes.Success)
                                {
                                    var blockX = blockResult.GetBlock() as TransactionBlock;
                                    if (blockX.UIndex <= 2)      // the two genesis service block
                                    {
                                        await AddBlockAsync(blockX);
                                        continue;
                                    }

                                    var stopwatch = Stopwatch.StartNew();

                                    var authorizer      = authorizers.Create(blockX.BlockType);
                                    var localAuthResult = await authorizer.AuthorizeAsync(blockX, false);

                                    stopwatch.Stop();
                                    _log.LogInformation($"Authorize takes {stopwatch.ElapsedMilliseconds} ms");

                                    if (localAuthResult.Item1 == APIResultCodes.Success)
                                    {
                                        await AddBlockAsync(blockX);
                                        fromUIndex = j + 1;
                                        _log.LogInformation($"BlockChain Synced Block Number: {j}");
                                    }
                                    else
                                    {
                                        _log.LogInformation($"BlockChain Block Number: {j} verify failed for {localAuthResult.Item1}");
                                        return(false);
                                    }
                                }
                                else
                                {
                                    // error
                                    _log.LogInformation($"Error syncing block: {blockResult.ResultCode}");
                                    continue;
                                }
                            }
                            return(true);
                        }

                        var copyOK = await DoCopyBlock(startUIndex, syncToUIndex).ConfigureAwait(false);
                        if (copyOK)
                        {
                            //// check missing block
                            //for(long k = 1; k <= startUIndex; k++)
                            //{
                            //    if(await BlockChain.Singleton.GetBlockByUIndex(k) == null)
                            //    {
                            //        _log.LogInformation($"syncing one missing block: {k}");
                            //        await DoCopyBlock(k, k).ConfigureAwait(false);
                            //    }
                            //}
                            break;
                        }
                        else
                        {
                            await Task.Delay(5000).ConfigureAwait(false);
                        }
                    }
                }

                InSyncing = false;
                LyraSystem.Singleton.Consensus.Tell(new ConsensusService.BlockChainSynced());
                _log.LogInformation("BlockChain Sync Completed.");
            });
        }
Exemple #5
0
        public async Task <int> RunWallet(Options options)
        {
            Console.WriteLine("Personal and Business Banking, Payments, and Digital Asset Management");
            Console.WriteLine("");
            Console.WriteLine("Banking: Store, transfer, and receive interest on multiple digital assets");
            Console.WriteLine("Payments: Make or accept instant payments using various currencies, online and in store");
            Console.WriteLine("Digital Asset Management: Issue your own tokens within seconds");
            Console.WriteLine("");

            string network_id = options.NetworkId;
            bool   INMEMORY   = options.Database == Options.INMEMORY_DATABASE;
            bool   WEB        = options.Protocol == Options.WEBAPI_PROTOCOL;

            Wallet wallet;

            if (INMEMORY)
            {
                var inmemory_storage = new AccountInMemoryStorage();
                wallet = new Wallet(inmemory_storage, network_id);
            }
            else
            {
                wallet = new Wallet(new LiteAccountDatabase(), network_id);
            }

            string lyra_folder = BaseAccount.GetFullFolderName("Lyra-CLI-" + network_id);

            if (!Directory.Exists(lyra_folder))
            {
                Directory.CreateDirectory(lyra_folder);
            }

            Console.WriteLine("Storage Location: " + lyra_folder);

            if (options.GenWalletName != null)
            {
                wallet.AccountName = options.GenWalletName;
                wallet.CreateAccount(lyra_folder, wallet.AccountName, AccountTypes.Standard);
                var ep = Neo.Cryptography.ECC.ECPoint.FromBytes(Base58Encoding.DecodeAccountId(wallet.AccountId), Neo.Cryptography.ECC.ECCurve.Secp256r1);
                Console.WriteLine($"The new wallet {wallet.AccountName} for {network_id}: ");
                Console.WriteLine(ep.ToString());
                Console.WriteLine(wallet.AccountId);
                return(0);
            }

            CommandProcessor command = new CommandProcessor(wallet);
            string           input   = null;

            try
            {
                while (!wallet.AccountExistsLocally(lyra_folder, input))
                {
                    Console.WriteLine("Press Enter for default account, or enter account name: ");
                    input = Console.ReadLine();

                    if (string.IsNullOrEmpty(input))
                    {
                        input = "My Account";
                    }

                    wallet.AccountName = input;

                    string fileName = "";
                    if (INMEMORY)
                    {
                        fileName = lyra_folder + wallet.AccountName + ".key";

                        if (System.IO.File.Exists(fileName))
                        {
                            string private_key = System.IO.File.ReadAllText(fileName);
                            if (wallet.ValidatePrivateKey(private_key))
                            {
                                var result = await wallet.RestoreAccountAsync(lyra_folder, private_key);

                                if (!result.Successful())
                                {
                                    Console.WriteLine("Could not restore account from file: " + result.ResultMessage);
                                    continue;
                                }
                            }
                        }
                    }


                    if (!wallet.AccountExistsLocally(lyra_folder, wallet.AccountName))
                    {
                        Console.WriteLine("Local account data not found. Would you like to create a new account? (Y/n): ");
                        if (command.ReadYesNoAnswer())
                        {
                            wallet.CreateAccount(lyra_folder, wallet.AccountName, AccountTypes.Standard);
                        }
                        else
                        {
                            Console.WriteLine("Please enter private key to restore account: ");
                            string privatekey = Console.ReadLine();

                            if (!wallet.ValidatePrivateKey(privatekey))
                            {
                                continue;
                            }

                            var result = await wallet.RestoreAccountAsync(lyra_folder, privatekey);

                            if (!result.Successful())
                            {
                                Console.WriteLine("Could not restore account from file: " + result.ResultMessage);
                                continue;
                            }
                        }
                        if (INMEMORY)
                        {
                            System.IO.File.WriteAllText(fileName, wallet.PrivateKey);
                        }
                    }
                    else
                    {
                        wallet.OpenAccount(lyra_folder, wallet.AccountName);
                    }
                }

                LyraRestClient rpcClient;
                if (!string.IsNullOrWhiteSpace(options.Node))
                {
                    var apiUrl = $"https://{options.Node}:4505/api/LyraNode/";
                    rpcClient = await LyraRestClient.CreateAsync(network_id, "Windows", "Lyra Client Cli", "1.0a", apiUrl);
                }
                else
                {
                    rpcClient = await LyraRestClient.CreateAsync(network_id, "Windows", "Lyra Client Cli", "1.0a");//await LyraRpcClient.CreateAsync(network_id, "Lyra Client Cli", "1.0");
                }
                Console.WriteLine("Type 'help' to see the list of available commands");
                Console.WriteLine("");

                await wallet.Sync(rpcClient);

                //timer1 = new Timer(async _ =>
                //{
                //    if (timer_busy1)
                //        return;
                //    try
                //    {
                //        timer_busy1 = true;
                //        var sync_result = await wallet.Sync(rpcClient);
                //    }
                //    finally
                //    {
                //        timer_busy1 = false;
                //    }
                //},
                //null, 2000, 30000);


                input = CommandProcessor.COMMAND_STATUS;

                while (input != CommandProcessor.COMMAND_STOP)
                {
                    var result = await command.Execute(input);

                    Console.Write(string.Format("{0}> ", wallet.AccountName));
                    //Console.Write
                    input = Console.ReadLine();
                }

                Console.WriteLine("Lyra Client is shutting down");
            }
            catch (Exception ex)
            {
                Console.WriteLine(string.Format("Exception: {0}", ex.Message));
                Console.WriteLine("Lyra Client is shutting down");
            }
            finally
            {
                if (wallet != null)
                {
                    wallet.Dispose();
                }
            }

            return(0);
        }
Exemple #6
0
        public override async Task <GetTransByHashReply> GetTransByHash(GetTransByHashRequest request, ServerCallContext context)
        {
            try
            {
                var client = LyraRestClient.Create(_config["network"], Environment.OSVersion.ToString(), "LyraBroker", "1.0");

                var result = await client.GetBlockAsync(request.Hash);

                if (result.ResultCode == Lyra.Core.Blocks.APIResultCodes.Success && result.GetBlock() is TransactionBlock block)
                {
                    var tx = new GetTransByHashReply
                    {
                        TxHash = block.Hash,
                        Height = block.Height,
                        Time   = Timestamp.FromDateTime(block.TimeStamp)
                    };

                    tx.TxType = block is SendTransferBlock ? TransactionType.Send : TransactionType.Receive;
                    if (tx.TxType == TransactionType.Send)
                    {
                        tx.OwnerAccountId = block.AccountID;
                        tx.PeerAccountId  = (block as SendTransferBlock).DestinationAccountId;

                        var rcvBlockQuery = await client.GetBlockBySourceHashAsync(block.Hash);

                        if (rcvBlockQuery.ResultCode == APIResultCodes.Success)
                        {
                            tx.IsReceived = true;
                            tx.RecvHash   = rcvBlockQuery.GetBlock().Hash;
                        }
                        else
                        {
                            tx.IsReceived = false;
                            tx.RecvHash   = ""; //gRPC don't like null
                        }
                    }
                    else
                    {
                        tx.OwnerAccountId = block.AccountID;

                        var sndBlockQuery = await client.GetBlockAsync((block as ReceiveTransferBlock).SourceHash);

                        if (sndBlockQuery.ResultCode == APIResultCodes.Success)
                        {
                            tx.PeerAccountId = (sndBlockQuery.GetBlock() as SendTransferBlock).AccountID;
                        }

                        tx.IsReceived = true;
                        tx.RecvHash   = block.Hash;
                    }

                    return(tx);
                }
            }
            catch (Exception ex)
            {
                _logger.LogWarning("In OpenWallet: " + ex.ToString());
            }

            return(new GetTransByHashReply {
                TxHash = ""
            });
        }
Exemple #7
0
        public override async Task <GetTransactionsReply> GetTransactions(GetTransactionsRequest request, ServerCallContext context)
        {
            var resp = new GetTransactionsReply {
            };

            try
            {
                var client = LyraRestClient.Create(_config["network"], Environment.OSVersion.ToString(), "LyraBroker", "1.0");
                var result = await client.SearchTransactionsAsync(request.AccountId,
                                                                  request.StartTime.ToDateTime().Ticks,
                                                                  request.EndTime.ToDateTime().Ticks,
                                                                  request.Count);

                if (result.ResultCode == APIResultCodes.Success)
                {
                    for (int i = 0; i < result.Transactions.Count; i++)
                    {
                        var txDesc = result.Transactions[i];
                        var tx     = new LyraTransaction
                        {
                            Height    = txDesc.Height,
                            Time      = Timestamp.FromDateTime(txDesc.TimeStamp),
                            IsReceive = txDesc.IsReceive,

                            SendAccountId = txDesc.SendAccountId ?? "",
                            SendHash      = txDesc.SendHash ?? "",
                            RecvAccountId = txDesc.RecvAccountId,
                            RecvHash      = txDesc.RecvHash ?? ""   // protobuf not like null
                        };

                        if (txDesc.Changes == null || !txDesc.Changes.ContainsKey(LyraGlobal.OFFICIALTICKERCODE))
                        {
                            tx.BalanceChange = 0;
                        }
                        else
                        {
                            tx.BalanceChange = txDesc.Changes[LyraGlobal.OFFICIALTICKERCODE] / LyraGlobal.TOKENSTORAGERITO;
                        }

                        if (txDesc.Balances == null || !txDesc.Balances.ContainsKey(LyraGlobal.OFFICIALTICKERCODE))
                        {
                            tx.Balance = 0;
                        }
                        else
                        {
                            tx.Balance = txDesc.Balances[LyraGlobal.OFFICIALTICKERCODE] / LyraGlobal.TOKENSTORAGERITO;
                        }



                        resp.Transactions.Add(tx);
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogWarning("In OpenWallet: " + ex.ToString());
            }

            return(resp);
        }
 public TransactionTester(LyraRestClient client)
 {
     _client = client;
 }
Exemple #9
0
        public async Task InitAsync()
        {
            ServicePointManager.DefaultConnectionLimit = 30;
            var platform = Environment.OSVersion.Platform.ToString();
            var appName  = "LyraAggregatedClient";
            var appVer   = "1.0";

            ushort peerPort = 4504;

            if (_networkId == "mainnet")
            {
                peerPort = 5504;
            }

            // get nodes list (from billboard)
            var seedNodes = GetSeedNodes();

            var seeds = seedNodes.Select(a => LyraRestClient.Create(_networkId, platform, appName, appVer, $"https://{a}:{peerPort}/api/Node/")).ToList();

            BillBoard currentBillBoard = null;

            do
            {
                try
                {
                    Console.WriteLine($"LyraAggregatedClient.InitAsync Seed Only: {_seedsOnly}");
                    var rand = new Random();
                    foreach (var sd in seedNodes.OrderBy(a => rand.Next()))
                    {
                        try
                        {
                            var apiClient = LyraRestClient.Create(_networkId, platform, appName, appVer, $"https://{sd}:{peerPort}/api/Node/");
                            currentBillBoard = await apiClient.GetBillBoardAsync();

                            Console.WriteLine($"LyraAggregatedClient.InitAsync Got billboard from {sd}");
                            break;
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine("LyraAggregatedClient.InitAsync: " + e.Message);
                        }
                    }

                    //var bbtasks = seeds.Select(client => client.GetBillBoardAsync()).ToList();
                    //try
                    //{
                    //    await Task.WhenAll(bbtasks);
                    //}
                    //catch (Exception ex)
                    //{
                    //    Console.WriteLine($"In LyraAggregatedClient.InitAsync: " + ex.Message);
                    //}
                    //var goodbb = bbtasks.Where(a => !(a.IsFaulted || a.IsCanceled) && a.IsCompleted && a.Result != null).Select(a => a.Result).ToList();

                    //if (goodbb.Count == 0)
                    //    continue;

                    //// pickup best result
                    //var best = goodbb
                    //        .GroupBy(b => b.CurrentLeader)
                    //        .Select(g => new
                    //        {
                    //            Data = g.Key,
                    //            Count = g.Count()
                    //        })
                    //        .OrderByDescending(x => x.Count)
                    //        .First();

                    //if (best.Count >= seedNodes.Length - 2 && !string.IsNullOrWhiteSpace(best.Data))
                    //{
                    //    var r = new Random();
                    //    currentBillBoard = goodbb.ElementAt(r.Next(0, goodbb.Count()));
                    //    //currentBillBoard = goodbb.First(a => a.CurrentLeader == best.Data);
                    //}
                    //else
                    //{
                    //    await Task.Delay(2000);
                    //    continue;
                    //}

                    if (_seedsOnly)
                    {
                        // create clients for primary nodes
                        _primaryClients = seedNodes
                                          .Select(c => LyraRestClient.Create(_networkId, platform, appName, appVer, $"https://{c}:{peerPort}/api/Node/"))
                                          .ToList();
                    }
                    else if (currentBillBoard == null)
                    {
                        Console.WriteLine("Error init LyraAggregatedClient. Billboard is null.");
                        await Task.Delay(1000);

                        continue;
                    }
                    else
                    {
                        // create clients for primary nodes
                        _primaryClients = currentBillBoard.NodeAddresses
                                          .Where(a => currentBillBoard.PrimaryAuthorizers.Contains(a.Key))
                                          .Where(a => a.Key != _poswallet)
                                          .Select(c => LyraRestClient.Create(_networkId, platform, appName, appVer, $"https://{c.Value}:{peerPort}/api/Node/"))
                                          //.Take(7)    // don't spam the whole network
                                          .ToList();
                    }

                    if (_primaryClients.Count < 2)      // billboard not harvest address enough
                    {
                        await Task.Delay(2000);
                    }

                    foreach (var primaryClient in _primaryClients)
                    {
                        primaryClient.SetTimeout(TimeSpan.FromSeconds(5));
                    }
                }
                catch (Exception exx)
                {
                    Console.WriteLine("Error init LyraAggregatedClient. Error: " + exx.ToString());
                    await Task.Delay(1000);

                    continue;
                }
            } while (currentBillBoard == null || _primaryClients.Count < 2);
        }