From eb9d438f0f89b77035e71707952d62e3f947ff59 Mon Sep 17 00:00:00 2001 From: Maxim Samsonov Date: Wed, 29 May 2024 00:03:32 +0300 Subject: [PATCH] Fixed #14, created statistics for timed out nodes, Admin user is now created on startup, default timeouts changed to 30/30/120 --- dkgCommon/Constants/NodeStatus.cs | 8 ++ dkgServiceNode/Controllers/DControllerBase.cs | 38 +++++++- dkgServiceNode/Controllers/NodesController.cs | 94 ++++++++++--------- .../Controllers/RoundsController.cs | 49 +++------- dkgServiceNode/Data/DbEnsure.cs | 17 ++++ dkgServiceNode/Models/Round.cs | 4 +- .../Services/RoundRunner/ActiveRound.cs | 20 ++++ dkgServiceNode/Services/RoundRunner/Runner.cs | 16 ++++ 8 files changed, 165 insertions(+), 81 deletions(-) diff --git a/dkgCommon/Constants/NodeStatus.cs b/dkgCommon/Constants/NodeStatus.cs index e43a530..2bbf2d1 100644 --- a/dkgCommon/Constants/NodeStatus.cs +++ b/dkgCommon/Constants/NodeStatus.cs @@ -38,6 +38,7 @@ public enum NStatus RunningStepFour = 27, Finished = 30, Failed = 40, + TimedOut = 41, Unknown = 255 } public sealed class NodeStatus @@ -121,6 +122,12 @@ public static class NodeStatusConstants Name = "Failed [no round result]", }; + public static readonly NodeStatus TimedOut = new() + { + NodeStatusId = NStatus.TimedOut, + Name = "Timed out [no round result]", + }; + public static readonly NodeStatus[] NodeStatusArray = [ NotRegistered, WaitingRoundStart, @@ -131,6 +138,7 @@ public static class NodeStatusConstants RunningStepThree, Finished, Failed, + TimedOut, ]; public static NodeStatus GetNodeStatusById(short id) { diff --git a/dkgServiceNode/Controllers/DControllerBase.cs b/dkgServiceNode/Controllers/DControllerBase.cs index 56c0fee..bc2e7ec 100644 --- a/dkgServiceNode/Controllers/DControllerBase.cs +++ b/dkgServiceNode/Controllers/DControllerBase.cs @@ -30,6 +30,7 @@ using dkgServiceNode.Services.RoundRunner; using System.Security.Cryptography.X509Certificates; using System.Xml.Linq; +using Microsoft.EntityFrameworkCore; namespace dkgServiceNode.Controllers { @@ -116,7 +117,7 @@ protected ObjectResult _500MisssingStepOneData(int id, string status) return _500($"Round [{id}] status is [{status}] but step one data is missing"); } - protected ObjectResult _500UnknownStateTransition(string rState, string nState ) + protected ObjectResult _500UnknownStateTransition(string rState, string nState) { return _500($"Unknown state transition [rState = {rState}, nState = {nState}]"); } @@ -131,5 +132,38 @@ protected DControllerBase(IHttpContextAccessor httpContextAccessor, UserContext if (uid != null) curUserId = (int)uid; } } + + protected async Task ResetNodeState(DkgContext dkgContext, Node node) + { + bool needsUpdate = false; + if (node.StatusValue != (short)NStatus.NotRegistered) + { + node.StatusValue = (short)NStatus.NotRegistered; + needsUpdate = true; + } + + if (node.RoundId != null) + { + node.RoundId = null; + needsUpdate = true; + } + + if (needsUpdate) + { + dkgContext.Entry(node).State = EntityState.Modified; + await dkgContext.SaveChangesAsync(); + } + } + + protected async Task UpdateNodeState(DkgContext dkgContext, Node node, short nStatus, int? roundId) + { + if (node.StatusValue != nStatus || node.RoundId != roundId) + { + node.StatusValue = nStatus; + node.RoundId = roundId; + dkgContext.Entry(node).State = EntityState.Modified; + await dkgContext.SaveChangesAsync(); + } + } } -} +} \ No newline at end of file diff --git a/dkgServiceNode/Controllers/NodesController.cs b/dkgServiceNode/Controllers/NodesController.cs index dfd39c2..168015b 100644 --- a/dkgServiceNode/Controllers/NodesController.cs +++ b/dkgServiceNode/Controllers/NodesController.cs @@ -124,7 +124,7 @@ public async Task> RegisterNode(Node node) // Acknowledges that the status report has been received internal async Task Accept(Round? round, Node node, StatusReport stReport) { - await UpdateNodeState(node, (short)stReport.Status, round?.Id); + await UpdateNodeState(dkgContext, node, (short)stReport.Status, round?.Id); if (round != null) { await UpdateRoundState(round); @@ -141,7 +141,7 @@ internal async Task TrToNotRegistered(Round? round, Node node, Sta runner.SetNoResult(round, node); } - await ResetNodeState(node); + await ResetNodeState(dkgContext, node); var response = new StatusResponse(0, NStatus.NotRegistered); if (stReport.Status != NStatus.NotRegistered || stReport.RoundId != 0) { @@ -150,13 +150,23 @@ internal async Task TrToNotRegistered(Round? round, Node node, Sta return Accepted(response); } - internal async Task TrToRunningStepOne(Round? round, Node _node, StatusReport _stReport) + internal async Task TrToRunningStepOne(Round? round, Node node, StatusReport stReport) { if (round == null) { return _500UndefinedRound(); } + if (!runner.CheckNode(round, node)) + { + await ResetNodeState(dkgContext, node); + var response = new StatusResponse(0, NStatus.NotRegistered); + if (stReport.Status != NStatus.NotRegistered || stReport.RoundId != 0) + { + return Ok(response); + } + } + await Task.Delay(0); string[] data = runner.GetStepOneData(round!); if (data.Length == 0) @@ -171,9 +181,19 @@ internal async Task TrToRunningStepTwoConditional(Round? round, No { return _500UndefinedRound(); } - + + if (runner.CheckTimedOutNode(round, node)) + { + await UpdateNodeState(dkgContext, node, (short)NStatus.TimedOut, round.Id); + var response = new StatusResponse(round.Id, NStatus.TimedOut); + if (stReport.Status != NStatus.TimedOut) + { + return Ok(response); + } + } + runner.SetStepTwoWaitingTime(round); - await UpdateNodeState(node, (short)stReport.Status, round?.Id); + await UpdateNodeState(dkgContext, node, (short)stReport.Status, round?.Id); if (stReport.Data.Length != 0) { @@ -193,8 +213,18 @@ internal async Task TrToRunningStepThreeConditional(Round? round, return _500UndefinedRound(); } + if (runner.CheckTimedOutNode(round, node)) + { + await UpdateNodeState(dkgContext, node, (short)NStatus.TimedOut, round.Id); + var response = new StatusResponse(round.Id, NStatus.TimedOut); + if (stReport.Status != NStatus.TimedOut) + { + return Ok(response); + } + } + runner.SetStepThreeWaitingTime(round); - await UpdateNodeState(node, (short)stReport.Status, round?.Id); + await UpdateNodeState(dkgContext, node, (short)stReport.Status, round?.Id); if (stReport.Data.Length != 0) { @@ -214,7 +244,7 @@ internal async Task WrongStatus(Round? round, Node node, StatusRep runner.SetNoResult(round, node); } - await ResetNodeState(node); + await ResetNodeState(dkgContext, node); string rStatus = round == null ? "null" : GetRoundStatusById(round.StatusValue).ToString(); return _409Status(stReport.PublicKey, stReport.Name, GetNodeStatusById(stReport.Status).ToString(), rStatus); } @@ -230,14 +260,14 @@ internal async Task AcceptFinished(Round? round, Node node, Status if (stReport.Data.Length != 0) { runner.SetResult(round, node, stReport.Data); - await UpdateNodeState(node, (short)stReport.Status, round.Id); + await UpdateNodeState(dkgContext, node, (short)stReport.Status, round.Id); await UpdateRoundState(round); return Accepted(new StatusResponse(round.Id, stReport.Status)); } else { runner.SetNoResult(round, node); - await UpdateNodeState(node, (short)stReport.Status, round.Id); + await UpdateNodeState(dkgContext, node, (short)stReport.Status, round.Id); await UpdateRoundState(round); return _400NoResult(round.Id, node.Name, node.PublicKey); } @@ -252,43 +282,11 @@ internal async Task AcceptFailed(Round? round, Node node, StatusRe runner.SetResultWaitingTime(round); runner.SetNoResult(round, node); - await UpdateNodeState(node, (short)stReport.Status, round.Id); + await UpdateNodeState(dkgContext, node, (short)stReport.Status, round.Id); await UpdateRoundState(round); return Accepted(new StatusResponse(round.Id, stReport.Status)); } - internal async Task ResetNodeState(Node node) - { - bool needsUpdate = false; - if (node.StatusValue != (short)NStatus.NotRegistered) - { - node.StatusValue = (short)NStatus.NotRegistered; - needsUpdate = true; - } - - if (node.RoundId != null) - { - node.RoundId = null; - needsUpdate = true; - } - - if (needsUpdate) - { - dkgContext.Entry(node).State = EntityState.Modified; - await dkgContext.SaveChangesAsync(); - } - } - - internal async Task UpdateNodeState(Node node, short nStatus, int? roundId) - { - if (node.StatusValue != nStatus || node.RoundId != roundId) - { - node.StatusValue = nStatus; - node.RoundId = roundId; - dkgContext.Entry(node).State = EntityState.Modified; - await dkgContext.SaveChangesAsync(); - } - } // POST: api/Nodes/status [HttpPost("status")] @@ -399,6 +397,18 @@ public async Task> Status(StatusReport statusReport) { (RStatus.Cancelled, NStatus.Failed), TrToNotRegistered }, { (RStatus.Failed, NStatus.Failed), TrToNotRegistered }, { (RStatus.Unknown, NStatus.Failed), TrToNotRegistered }, + + { (null, NStatus.TimedOut), WrongStatus }, + { (RStatus.NotStarted, NStatus.TimedOut), WrongStatus }, + { (RStatus.Registration, NStatus.TimedOut), TrToNotRegistered }, + { (RStatus.CreatingDeals, NStatus.TimedOut), TrToNotRegistered }, + { (RStatus.ProcessingDeals, NStatus.TimedOut), TrToNotRegistered }, + { (RStatus.ProcessingResponses, NStatus.TimedOut), TrToNotRegistered }, + { (RStatus.Finished, NStatus.TimedOut), TrToNotRegistered }, + { (RStatus.Cancelled, NStatus.TimedOut), TrToNotRegistered }, + { (RStatus.Failed, NStatus.TimedOut), TrToNotRegistered }, + { (RStatus.Unknown, NStatus.TimedOut), TrToNotRegistered }, + }; var node = await dkgContext.FindNodeByPublicKeyAsync(statusReport.PublicKey); diff --git a/dkgServiceNode/Controllers/RoundsController.cs b/dkgServiceNode/Controllers/RoundsController.cs index 8202c26..6339d7b 100644 --- a/dkgServiceNode/Controllers/RoundsController.cs +++ b/dkgServiceNode/Controllers/RoundsController.cs @@ -180,6 +180,11 @@ public async Task> NextRoundStep(int id) reNodes = rNodes.Skip(round.MaxNodes).ToList(); } runner.RunRound(round, fiNodes); + foreach (Node node in reNodes ?? []) + { + await ResetNodeState(dkgContext, node); + } + break; case (short)RStatus.ProcessingDeals: runner.ProcessDeals(round); @@ -201,38 +206,7 @@ public async Task> NextRoundStep(int id) break; } - dkgContext.Entry(round).State = EntityState.Modified; - try - { - await dkgContext.SaveChangesAsync(); - } - catch (DbUpdateConcurrencyException) - { - if (!await dkgContext.RoundExistsAsync(id)) - { - return _404Round(id); - } - else - { - throw; - } - } - - foreach (var node in reNodes) - { - node.RoundId = null; - node.Status = NStatus.NotRegistered; - dkgContext.Entry(node).State = EntityState.Modified; - } - try - { - await dkgContext.SaveChangesAsync(); - } - catch - { - } - - return NoContent(); + return await UpdateRoundState(dkgContext, round); } // POST: api/rounds/cancel/5 @@ -253,6 +227,12 @@ public async Task> CancelRound(int id) round.CreatedOn = round.CreatedOn.ToUniversalTime(); round.Status = RoundStatusConstants.Cancelled; + runner.CancelRound(round); + return await UpdateRoundState(dkgContext, round); + } + + internal async Task> UpdateRoundState(DkgContext dkgContext, Round round) + { dkgContext.Entry(round).State = EntityState.Modified; try { @@ -260,16 +240,15 @@ public async Task> CancelRound(int id) } catch (DbUpdateConcurrencyException) { - if (! await dkgContext.RoundExistsAsync(id)) + if (!await dkgContext.RoundExistsAsync(round.Id)) { - return _404Round(id); + return _404Round(round.Id); } else { throw; } } - runner.CancelRound(round); return NoContent(); } } diff --git a/dkgServiceNode/Data/DbEnsure.cs b/dkgServiceNode/Data/DbEnsure.cs index 979b299..192369e 100644 --- a/dkgServiceNode/Data/DbEnsure.cs +++ b/dkgServiceNode/Data/DbEnsure.cs @@ -187,6 +187,22 @@ FOR EACH ROW INSERT INTO ""versions"" (""version"", ""date"") VALUES ('0.6.0', '" + DateTime.Now.ToString("yyyy-MM-dd") + @"'); + COMMIT; + "; + + readonly static string sqlScript_0_6_2 = @" + START TRANSACTION; + + ALTER TABLE ""rounds"" ALTER COLUMN ""timeout2"" SET DEFAULT 30; + ALTER TABLE ""rounds"" ALTER COLUMN ""timeout3"" SET DEFAULT 30; + + INSERT INTO ""users"" (""name"", ""email"", ""password"", ""is_enabled"", ""is_admin"") + SELECT 'Admin', 'admin@example.com', '$2a$11$YygO9mUKjDioWY0CPj35LeCGY4SRnVHNdT2cFdVAGTSRwSpYHhytu', TRUE, TRUE + WHERE NOT EXISTS(SELECT 1 FROM ""users"" WHERE ""email"" = 'admin@example.com'); + + INSERT INTO ""versions"" (""version"", ""date"") VALUES + ('0.6.2', '" + DateTime.Now.ToString("yyyy-MM-dd") + @"'); + COMMIT; "; private static string PuVersionUpdateQuery(string v) @@ -279,6 +295,7 @@ public static void Ensure(string connectionString) PuVersionUpdate("0.5.2", connection); EnsureVersion("0.6.0", sqlScript_0_6_0, connection); PuVersionUpdate("0.6.1", connection); + EnsureVersion("0.6.2", sqlScript_0_6_2, connection); } } diff --git a/dkgServiceNode/Models/Round.cs b/dkgServiceNode/Models/Round.cs index 50e0ffa..b937683 100644 --- a/dkgServiceNode/Models/Round.cs +++ b/dkgServiceNode/Models/Round.cs @@ -46,10 +46,10 @@ public class Round public int MaxNodes { get; set; } = 256; [Column("timeout2")] - public int Timeout2 { get; set; } = 120; + public int Timeout2 { get; set; } = 30; [Column("timeout3")] - public int Timeout3 { get; set; } = 120; + public int Timeout3 { get; set; } = 30; [Column("timeoutr")] public int TimeoutR { get; set; } = 120; diff --git a/dkgServiceNode/Services/RoundRunner/ActiveRound.cs b/dkgServiceNode/Services/RoundRunner/ActiveRound.cs index 1b7c025..b2708ab 100644 --- a/dkgServiceNode/Services/RoundRunner/ActiveRound.cs +++ b/dkgServiceNode/Services/RoundRunner/ActiveRound.cs @@ -66,6 +66,8 @@ public void Clear() Step3StartWaitingTime = DateTime.MinValue; ResultStartWaitingTime = DateTime.MinValue; } + public bool CheckNode(Node node) => CheckNodeCondition(node, (activeNode, node) => activeNode == node); + public bool CheckTimedOutNode(Node node) => CheckNodeCondition(node, (activeNode, node) => activeNode == node && activeNode.TimedOut); public int? GetResult() { int? result = null; @@ -294,6 +296,24 @@ public void SetStepThreeWaitingTime() Step3StartWaitingTime = DateTime.Now; } } + + public bool CheckNodeCondition(Node node, Func condition) + { + bool res = false; + if (Nodes != null) + { + foreach (var activeNode in Nodes) + { + if (condition(activeNode, node)) + { + res = true; + break; + } + } + } + return res; + } + private ActiveNode? FindNode(Node node) { ActiveNode? result = null; diff --git a/dkgServiceNode/Services/RoundRunner/Runner.cs b/dkgServiceNode/Services/RoundRunner/Runner.cs index 3a3b2d2..ac2122a 100644 --- a/dkgServiceNode/Services/RoundRunner/Runner.cs +++ b/dkgServiceNode/Services/RoundRunner/Runner.cs @@ -41,6 +41,8 @@ public Runner(ILogger logger) { Logger = logger; } + public bool CheckNode(Round round, Node node) => CheckNodeCondition(round, node, (roundToRun, node) => roundToRun.CheckNode(node)); + public bool CheckTimedOutNode(Round round, Node node) => CheckNodeCondition(round, node, (roundToRun, node) => roundToRun.CheckTimedOutNode(node)); public void StartRound(Round round) { lock (lockObject) @@ -133,6 +135,20 @@ public string[] GetStepThreeData(Round round, Node node) => // Private methods + internal bool CheckNodeCondition(Round round, Node node, Func condition) + { + bool res = false; + lock (lockObject) + { + ActiveRound? roundToRun = ActiveRounds.FirstOrDefault(r => r.Id == round.Id); + if (roundToRun != null) + { + res = condition(roundToRun, node); + } + } + return res; + } + internal void SetStepData(Round round, Node node, string[] data, Action setDataAction) { lock (lockObject)