private async Task SendNodesInfoToGroupAsync(ChatId chatid) { var wc = new WebClient(); var json = wc.DownloadString(LyraGlobal.SelectNode(_network) + "LyraNode/GetBillboard"); var bb = JsonConvert.DeserializeObject<BillBoardData>(json); var sb = new StringBuilder(); sb.AppendLine($"*Consensus Algorithm (PBFT) Settings*"); sb.AppendLine($"Total Needed Minimal Node Number: {ProtocolSettings.Default.ConsensusTotalNumber}"); sb.AppendLine($"Consensus Win Number: {ProtocolSettings.Default.ConsensusWinNumber}"); sb.AppendLine($"Maxmimum Failed Node Number: {ProtocolSettings.Default.ConsensusNumber}"); sb.AppendLine($"Current Running Node Count: {bb.AllNodes.Count}"); sb.AppendLine($"Current Nodes can do Authorizing: {bb.AllNodes.Count(a => a.Value.ableToAuthorize)}"); var cando = bb.canDoConsensus ? "Yes" : "No"; sb.AppendLine($"Consensus Can be Made Now: {cando}"); sb.AppendLine("\n*Fully Functional Nodes List*\n"); sb.AppendLine("`" + bb.AllNodes.Values.Where(a => a.ableToAuthorize) .Select(b => b.accountID.Shorten()).Aggregate((c, d) => c + "\n" + d) + "`"); sb.AppendLine("\n*Nodes which has trouble*\n"); sb.AppendLine("`" + bb.AllNodes.Values.Where(a => !a.ableToAuthorize) .Aggregate(new StringBuilder(), (c, n) => c.AppendLine(n.accountID.Shorten() + ": " + n.FailReasons.Select(a => "[" + a + "]").Aggregate((a, b) => a + b)), sb => sb.ToString()) + "`"); await SendGroupMessageAsync(chatid, sb.ToString()); }
public override async Task <APIResultCodes> PreSendAuthAsync(DagSystem sys, SendTransferBlock block, TransactionBlock last) { var dexid = block.Tags.ContainsKey("dexid") ? block.Tags["dexid"] : null; if (dexid == null) { return(APIResultCodes.InvalidAccountId); } decimal mintamount = 0; var mintamountstr = block.Tags.ContainsKey("amount") ? block.Tags["amount"] : null; if (mintamountstr == null || !decimal.TryParse(mintamountstr, out mintamount) || mintamount <= 0) { return(APIResultCodes.InvalidAmount); } // verify if sender is dex server if (block.AccountID != LyraGlobal.GetDexServerAccountID(LyraNodeConfig.GetNetworkId())) { return(APIResultCodes.InvalidDexServer); } // verify dex wallet owner var brkr = await sys.Storage.FindLatestBlockAsync(dexid) as IBrokerAccount; if (brkr == null) { return(APIResultCodes.InvalidBrokerAcount); } return(APIResultCodes.Success); }
private async Task SendHeight(ChatId chatid) { var wc = new WebClient(); var json = wc.DownloadString(LyraGlobal.SelectNode(_network) + "LyraNode/GetSyncState"); var bb = JsonConvert.DeserializeObject<GetSyncStateAPIResult>(json); await SendGroupMessageAsync(chatid, $"Current Height: *{bb.NewestBlockUIndex}*"); }
private async Task SendNodesInfoToGroupAsync() { var wc = new WebClient(); var json = wc.DownloadString(LyraGlobal.SelectNode("devnet") + "LyraNode/GetBillboard"); var bb = JsonConvert.DeserializeObject <BillBoard>(json); var sb = new StringBuilder(); foreach (var node in bb.AllNodes.Values) { sb.AppendLine($"{node.AccountID}"); sb.AppendLine($"Staking Balance: {node.Balance}"); sb.AppendLine($"Last Staking Time: {node.LastStaking}"); sb.AppendLine(); } await SendGroupMessageAsync(sb.ToString()); }
protected override async Task <APIResultCodes> AuthorizeImplAsync <T>(DagSystem sys, T tblock) { if (!(tblock is TokenMintBlock)) { return(APIResultCodes.InvalidBlockType); } var block = tblock as TokenMintBlock; if (string.IsNullOrWhiteSpace(block.MintBy)) { return(APIResultCodes.InvalidTokenMint); } if (string.IsNullOrWhiteSpace(block.GenesisHash)) { return(APIResultCodes.InvalidTokenMint); } if (block.MintAmount <= 0) { return(APIResultCodes.InvalidTokenMint); } if (block.MintBy != LyraGlobal.GetDexServerAccountID(LyraNodeConfig.GetNetworkId())) { return(APIResultCodes.InvalidDexServer); } // IDexWallet interface var brkauth = new DexWalletAuthorizer(); var brkret = await brkauth.AuthorizeAsync(sys, tblock); if (brkret == APIResultCodes.Success) { return(await Lyra.Shared.StopWatcher.TrackAsync(() => base.AuthorizeImplAsync(sys, tblock), "DexTokenMintAuthorizer->TransactionAuthorizer")); } else { return(brkret); } }
/// <summary> /// two ways to begin view changing: either one third of all voters requested, or local requested. /// /// </summary> /// <returns></returns> internal async Task BeginChangeViewAsync(ViewChangeReason reason) { _log.LogInformation($"BeginChangeViewAsync: VID: {ViewId} Req: {reqMsgs.Count} Reply: {replyMsgs.Count} Commit: {commitMsgs.Count} Votes {commitMsgs.Count}/{LyraGlobal.GetMajority(_context.Board.AllVoters.Count)}/{_context.Board.AllVoters.Count} Replyed: {replySent} Commited: {commitSent}"); _reason = reason; var lastSb = await _sys.Storage.GetLastServiceBlockAsync(); if (lastSb == null) { // genesis? _log.LogCritical($"BeginChangeViewAsync has null service block. should not happend. error."); return; } _isViewChanging = true; ShiftView(lastSb.Height + 1); selectedSuccess = false; _log.LogInformation($"View change for ViewId {ViewId} begin at {TimeStarted}"); CalculateLeaderCandidate(); var lastCons = await _sys.Storage.GetLastConsolidationBlockAsync(); var req = new ViewChangeRequestMessage { From = _sys.PosWallet.AccountId, ViewID = ViewId, prevViewID = lastSb.Height, requestSignature = Signatures.GetSignature(_sys.PosWallet.PrivateKey, $"{lastSb.Hash}|{lastCons.Hash}", _sys.PosWallet.AccountId), }; _context.Send2P2pNetwork(req); await CheckRequestAsync(req); }
private async Task CheckAllStatsAsync() { if (!IsViewChanging) { RemoveOutDatedMsgs(); if (reqMsgs.Count > _context.Board.AllVoters.Count - LyraGlobal.GetMajority(_context.Board.AllVoters.Count)) { var sb = new StringBuilder(); foreach (var msg in reqMsgs) { sb.Append($"{msg.Key.Shorten()}, "); } _log.LogInformation($"too many view change request, {sb.ToString()}. force into view change mode"); // too many view change request. force into view change mode await _context.GotViewChangeRequestAsync(ViewId, reqMsgs.Count); } return; } // request if (!replySent && reqMsgs.Count >= LyraGlobal.GetMajority(_context.Board.AllVoters.Count)) { _log.LogInformation($"CheckAllStats VID: {ViewId} Time: {TimeStarted} Req: {reqMsgs.Count} Reply: {replyMsgs.Count} Commit: {commitMsgs.Count} Votes {commitMsgs.Count}/{LyraGlobal.GetMajority(_context.Board.AllVoters.Count)}/{_context.Board.AllVoters.Count} Replyed: {replySent} Commited: {commitSent}"); if (string.IsNullOrEmpty(nextLeader)) { CalculateLeaderCandidate(); } var reply = new ViewChangeReplyMessage { From = _sys.PosWallet.AccountId, ViewID = ViewId, Result = Blocks.APIResultCodes.Success, Candidate = nextLeader }; _context.Send2P2pNetwork(reply); replySent = true; await CheckReplyAsync(reply); } if (!commitSent) { if (replyMsgs.Count >= LyraGlobal.GetMajority(_context.Board.AllVoters.Count)) { _log.LogInformation($"CheckAllStats VID: {ViewId} Time: {TimeStarted} Req: {reqMsgs.Count} Reply: {replyMsgs.Count} Commit: {commitMsgs.Count} Votes {commitMsgs.Count}/{LyraGlobal.GetMajority(_context.Board.AllVoters.Count)}/{_context.Board.AllVoters.Count} Replyed: {replySent} Commited: {commitSent}"); // reply // only if we have enough reply var decQr = from rep in replyMsgs.Values where rep.msg.Result == Blocks.APIResultCodes.Success group rep by rep.msg.Candidate into g select new { Candidate = g.Key, Count = g.Count() }; var decisions = decQr.OrderByDescending(x => x.Count).ToList(); var candidateQR = decisions.FirstOrDefault(); var sb = new StringBuilder(); sb.AppendLine($"Decisions for View ID: {ViewId}"); foreach (var x in decisions) { sb.AppendLine($"\t{x.Candidate.Shorten()}: {x.Count}"); } _log.LogInformation(sb.ToString()); if (candidateQR?.Count >= LyraGlobal.GetMajority(_context.Board.AllVoters.Count)) { var commit = new ViewChangeCommitMessage { From = _sys.PosWallet.AccountId, ViewID = ViewId, Candidate = candidateQR.Candidate, Consensus = ConsensusResult.Yea }; _context.Send2P2pNetwork(commit); commitSent = true; await CheckCommitAsync(commit); } else { //_log.LogInformation($"CheckAllStats, By ReplyMsgs, not commit, top candidate {candidateQR?.Candidate.Shorten()} has {candidateQR?.Count} votes"); } } } if (!selectedSuccess) { // commit var q = from rep in commitMsgs.Values group rep by rep.msg.Candidate into g select new { Candidate = g.Key, Count = g.Count() }; var candidate = q.OrderByDescending(x => x.Count).FirstOrDefault(); if (candidate != null) { if (nextLeader != candidate.Candidate) { _log.LogWarning($"Next Leader {nextLeader} not {candidate.Candidate}"); } nextLeader = candidate.Candidate; if (candidate?.Count >= LyraGlobal.GetMajority(_context.Board.AllVoters.Count)) { _log.LogInformation($"CheckAllStats, By CommitMsgs, leader selected {candidate.Candidate} with {candidate.Count} votes."); selectedSuccess = true; _leaderSelected(this, ViewId, candidate.Candidate, candidate.Count, _context.Board.AllVoters); } } } }
protected override async Task <APIResultCodes> AuthorizeImplAsync <T>(DagSystem sys, T tblock) { if (!(tblock is TokenGenesisBlock)) { return(APIResultCodes.InvalidBlockType); } var block = tblock as TokenGenesisBlock; // Local node validations - before it sends it out to the authorization sample: // 1. check if the account already exists if (!(tblock is LyraTokenGenesisBlock)) { if (!await sys.Storage.AccountExistsAsync(block.AccountID)) { return(APIResultCodes.AccountDoesNotExist); // } TransactionBlock lastBlock = await sys.Storage.FindLatestBlockAsync(block.AccountID) as TransactionBlock; if (lastBlock == null) { return(APIResultCodes.CouldNotFindLatestBlock); } // check LYR balance if (lastBlock.Balances[LyraGlobal.OFFICIALTICKERCODE] != block.Balances[LyraGlobal.OFFICIALTICKERCODE] + block.Fee.ToBalanceLong()) { return(APIResultCodes.InvalidNewAccountBalance); } // check ticker name // https://www.quora.com/What-characters-are-not-allowed-in-a-domain-name var r = new Regex(@"[^\w-]|^-|-$"); if (!block.Ticker.Contains(block.DomainName + "/") || r.IsMatch(block.DomainName)) { return(APIResultCodes.InvalidDomainName); } if (r.IsMatch(block.Ticker.Replace(block.DomainName + "/", ""))) { return(APIResultCodes.InvalidTickerName); } if (block.RenewalDate > DateTime.UtcNow.Add(TimeSpan.FromDays(3660)) || block.RenewalDate < DateTime.UtcNow) { return(APIResultCodes.InvalidTokenRenewalDate); } if (string.IsNullOrWhiteSpace(block.DomainName)) { return(APIResultCodes.EmptyDomainName); } bool tokenIssuerIsSeed0 = block.AccountID == ProtocolSettings.Default.StandbyValidators[0]; if (!tokenIssuerIsSeed0 && block.AccountID != LyraGlobal.GetDexServerAccountID(LyraNodeConfig.GetNetworkId())) { if (block.DomainName.Length < 6) { return(APIResultCodes.DomainNameTooShort); } if (_reservedDomains.Any(a => a.Equals(block.DomainName, StringComparison.InvariantCultureIgnoreCase))) { return(APIResultCodes.DomainNameReserved); } } } if (await sys.Storage.WasAccountImportedAsync(block.AccountID)) { return(APIResultCodes.CannotModifyImportedAccount); } var tresult = await VerifyTransactionBlockAsync(sys, block); if (tresult != APIResultCodes.Success) { return(tresult); } // check length if (string.IsNullOrWhiteSpace(block.DomainName) || string.IsNullOrWhiteSpace(block.Ticker)) { return(APIResultCodes.InvalidTickerName); } if (block.DomainName.Length >= 64 || block.Ticker.Length >= 64) { return(APIResultCodes.InvalidTickerName); } // check if this token already exists //AccountData genesis_blocks = _accountCollection.GetAccount(AccountCollection.GENESIS_BLOCKS); //if (genesis_blocks.FindTokenGenesisBlock(testTokenGenesisBlock) != null) if (await sys.Storage.FindTokenGenesisBlockAsync(block.Hash, block.Ticker) != null) { return(APIResultCodes.TokenGenesisBlockAlreadyExists); } if (block.Fee != (await sys.Storage.GetLastServiceBlockAsync()).TokenGenerationFee) { return(APIResultCodes.InvalidFeeAmount); } if (block.NonFungibleType == NonFungibleTokenTypes.Collectible && !block.IsNonFungible) { return(APIResultCodes.InvalidNFT); } if (block.IsNonFungible) { if (!Signatures.ValidateAccountId(block.NonFungibleKey)) { return(APIResultCodes.InvalidNonFungiblePublicKey); } // Validate Collectible NFT if (block.ContractType == ContractTypes.Collectible) { if (block.Precision != 0) { return(APIResultCodes.InvalidNFT); } if (block.NonFungibleType != NonFungibleTokenTypes.Collectible) { return(APIResultCodes.InvalidNFT); } } } return(await Lyra.Shared.StopWatcher.TrackAsync(() => base.AuthorizeImplAsync(sys, tblock), "TokenGenesisAuthorizer->ReceiveTransferAuthorizer")); }
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. } } }