diff --git a/dkgNode/appsettings.Development.json b/dkgNode/appsettings.Development.json index 469ef73..2d26af3 100644 --- a/dkgNode/appsettings.Development.json +++ b/dkgNode/appsettings.Development.json @@ -2,7 +2,8 @@ "Logging": { "LogLevel": { "Default": "Warning", - "Microsoft.Hosting.Lifetime": "Warning" + "Microsoft.Hosting.Lifetime": "Warning", + "Microsoft.EntityFrameworkCore": "Warning" } } } diff --git a/dkgNodeLibrary/Services/DkgNodeService.cs b/dkgNodeLibrary/Services/DkgNodeService.cs index d44c95f..815e263 100644 --- a/dkgNodeLibrary/Services/DkgNodeService.cs +++ b/dkgNodeLibrary/Services/DkgNodeService.cs @@ -263,7 +263,10 @@ public async Task RunDkg(HttpClient httpClient, string[] encodedPublicKeys, Canc Name, PublicKeys.Length, Round); Status = WaitingStepTwo; - if (dieOnStep2) Thread.Sleep(4000); + if (dieOnStep2) + { + Thread.Sleep(40000); + } statusResponse = await ReportStatus(httpClient, encodedDeals); if (!ShallContinue([WaitingStepTwo, RunningStepTwo], stoppingToken)) return; @@ -282,7 +285,7 @@ public async Task RunDkg(HttpClient httpClient, string[] encodedPublicKeys, Canc Name, PublicKeys.Length, Round); Status = WaitingStepThree; - if (dieOnStep2) Thread.Sleep(4000); + if (dieOnStep2) Thread.Sleep(40000); statusResponse = await ReportStatus(httpClient, encodedResponses); if (!ShallContinue([WaitingStepThree, RunningStepThree], stoppingToken)) return; @@ -296,7 +299,10 @@ public async Task RunDkg(HttpClient httpClient, string[] encodedPublicKeys, Canc if (!await ReportStatusAndCheck(httpClient, [RunningStepThree], stoppingToken)) return; RunDkgStepThree(statusResponse.Data); - + if (dieOnStep3) + { + Thread.Sleep(4000); + } DistributedPublicKey = null; string[] encodedResult = []; diff --git a/dkgServiceNode/Controllers/OpsController.cs b/dkgServiceNode/Controllers/OpsController.cs index 4319a58..1e1541a 100644 --- a/dkgServiceNode/Controllers/OpsController.cs +++ b/dkgServiceNode/Controllers/OpsController.cs @@ -36,6 +36,7 @@ using Solnet.Wallet; using System.Diagnostics; +using Microsoft.Extensions.Logging; namespace dkgServiceNode.Controllers { @@ -315,21 +316,28 @@ internal async Task AcceptFinished(Round? round, Node node, NodesR return _500UndefinedRound(); } - runner.SetResultWaitingTime(round); - - if (stReport.Data.Length != 0) + if (node.Status == NStatus.Finished) { - runner.SetResult(round, node, stReport.Data); - await UpdateNodeState(dkgContext, node, (short)stReport.Status, round.Id); - await UpdateRoundState(round); - return Accepted(CreateStatusResponse(round, lastRoundHistory, stReport.Status, node.Random)); + return Ok(CreateStatusResponse(round, lastRoundHistory, NStatus.Finished, node.Random)); } else { - runner.SetNoResult(round, node); - await UpdateNodeState(dkgContext, node, (short)stReport.Status, round.Id); - await UpdateRoundState(round); - return _400NoResult(round.Id, node.Name, node.PublicKey); + runner.SetResultWaitingTime(round); + + if (stReport.Data.Length != 0) + { + runner.SetResult(round, node, stReport.Data); + await UpdateNodeState(dkgContext, node, (short)stReport.Status, round.Id); + await UpdateRoundState(round); + return Accepted(CreateStatusResponse(round, lastRoundHistory, stReport.Status, node.Random)); + } + else + { + runner.SetNoResult(round, node); + await UpdateNodeState(dkgContext, node, (short)stReport.Status, round.Id); + await UpdateRoundState(round); + return _400NoResult(round.Id, node.Name, node.PublicKey); + } } } internal async Task AcceptFailed(Round? round, Node node, NodesRoundHistory? lastRoundHistory, StatusReport stReport) @@ -514,15 +522,16 @@ public async Task> Status(StatusReport statusReport RStatus? rStatus = null; if (round != null) { - await UpdateRoundState(round); + // await UpdateRoundState(round); rStatus = round.Status; } if (actionMap.TryGetValue((rStatus, statusReport.Status), out var function)) { - logger.LogDebug("State transition round [{id}] node [{name}] : ({rStatus}, {nStatus}) -> {f}", + logger.LogDebug("State transition round [{id}] node [{name}] : ({rStatus}, {nStatus}) -> {f}(Data: {data})", (round != null ? round.Id.ToString() : "null"), - node.Name, rStatus, statusReport.Status, function.Method.Name); + node.Name, rStatus, statusReport.Status, function.Method.Name, + statusReport.Data.Length == 0 ? "empty" : statusReport.Data); res = await function(round, node, lastRoundHistory, statusReport); } diff --git a/dkgServiceNode/Data/DbEnsure.cs b/dkgServiceNode/Data/DbEnsure.cs index 9e37d4c..9cf497c 100644 --- a/dkgServiceNode/Data/DbEnsure.cs +++ b/dkgServiceNode/Data/DbEnsure.cs @@ -29,7 +29,7 @@ namespace dkgServiceNode.Data { public static class DbEnsure { - readonly static string sqlScript_0_8_0 = @" + readonly static string sqlScript_0_12_1 = @" START TRANSACTION; DROP TABLE IF EXISTS ""users""; @@ -91,50 +91,6 @@ public static class DbEnsure CREATE INDEX ""idx_nodes_round_history_round_id"" ON ""nodes_round_history"" (""round_id""); CREATE INDEX ""idx_nodes_round_history_node_id"" ON ""nodes_round_history"" (""node_id""); - CREATE OR REPLACE FUNCTION update_nodes_round_history() RETURNS TRIGGER AS $$ - BEGIN - IF OLD.round_id IS NOT NULL AND NEW.round_id IS NULL THEN - -- Check if a record already exists in nodes_round_history - IF EXISTS (SELECT 1 FROM nodes_round_history WHERE node_id = OLD.id AND round_id = OLD.round_id) THEN - -- Update the existing record - UPDATE nodes_round_history - SET node_final_status = OLD.status - WHERE node_id = OLD.id AND round_id = OLD.round_id; - ELSE - -- Insert a new record - INSERT INTO nodes_round_history (round_id, node_id, node_final_status, node_random) - VALUES (OLD.round_id, OLD.id, OLD.status, OLD.random); - END IF; - END IF; - RETURN NEW; - END; - $$ LANGUAGE plpgsql; - - CREATE TRIGGER nodes_before_update_trigger - BEFORE UPDATE ON nodes - FOR EACH ROW - EXECUTE PROCEDURE update_nodes_round_history(); - - DROP TABLE IF EXISTS ""versions""; - - CREATE TABLE ""versions"" ( - ""id"" SERIAL PRIMARY KEY, - ""version"" VARCHAR(16) NOT NULL, - ""date"" DATE NOT NULL DEFAULT now() - ); - - INSERT INTO ""versions"" (""version"", ""date"") VALUES - ('0.8.0', '" + DateTime.Now.ToString("yyyy-MM-dd") + @"'); - - COMMIT; - "; - - readonly static string sqlScript_0_12_1 = @" - START TRANSACTION; - - DROP TRIGGER IF EXISTS nodes_before_update_trigger ON nodes; - DROP FUNCTION IF EXISTS update_nodes_round_history(); - CREATE OR REPLACE PROCEDURE upsert_node_round_history( p_node_id INT, p_round_id INT, @@ -163,6 +119,14 @@ INSERT INTO nodes_round_history(node_id, round_id, node_final_status, node_rando END; $$; + DROP TABLE IF EXISTS ""versions""; + + CREATE TABLE ""versions"" ( + ""id"" SERIAL PRIMARY KEY, + ""version"" VARCHAR(16) NOT NULL, + ""date"" DATE NOT NULL DEFAULT now() + ); + INSERT INTO ""versions"" (""version"", ""date"") VALUES ('0.12.1', '" + DateTime.Now.ToString("yyyy-MM-dd") + @"'); @@ -199,6 +163,16 @@ GROUP BY COMMIT; "; + readonly static string sqlScript_0_13_1 = @" + START TRANSACTION; + + DROP FUNCTION IF EXISTS update_nodes_round_history(); + + INSERT INTO ""versions"" (""version"", ""date"") VALUES + ('0.13.1', '" + DateTime.Now.ToString("yyyy-MM-dd") + @"'); + COMMIT; + "; + private static string PuVersionUpdateQuery(string v) { return @" @@ -220,7 +194,7 @@ private static bool VCheck(string v, NpgsqlConnection connection) return (rows != null && (long)rows != 0); } - public static int Ensure_0_8_0(NpgsqlConnection connection) + public static int Ensure_0_12_1(NpgsqlConnection connection) { // Check if table 'versions' exists var sql = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'versions';"; @@ -231,14 +205,14 @@ public static int Ensure_0_8_0(NpgsqlConnection connection) if (rows != null && (long)rows != 0) { - sql = "SELECT COUNT(*) FROM versions WHERE version = '0.8.0';"; + sql = "SELECT COUNT(*) FROM versions WHERE version = '0.12.1';"; command = new NpgsqlCommand(sql, connection); rows = command.ExecuteScalar(); } if (rows == null || (long)rows == 0) { - var scriptCommand = new NpgsqlCommand(sqlScript_0_8_0, connection); + var scriptCommand = new NpgsqlCommand(sqlScript_0_12_1, connection); r = scriptCommand.ExecuteNonQuery(); } @@ -265,12 +239,12 @@ public static void Ensure(NpgsqlConnection connection, ILogger logger) { try { - logger.LogInformation("Initializing database at 0.8.0"); - Ensure_0_8_0(connection); - logger.LogInformation("Update to 0.12.1"); - EnsureVersion("0.12.1", sqlScript_0_12_1, connection); + logger.LogInformation("Initializing database at 0.12.1"); + Ensure_0_12_1(connection); logger.LogInformation("Update to 0.13.0"); EnsureVersion("0.13.0", sqlScript_0_13_0, connection); + logger.LogInformation("Update to 0.13.1"); + EnsureVersion("0.13.1", sqlScript_0_13_1, connection); } catch (Exception ex) { diff --git a/dkgServiceNode/Data/DkgContext.cs b/dkgServiceNode/Data/DkgContext.cs index 0cfc78c..3f07e3b 100644 --- a/dkgServiceNode/Data/DkgContext.cs +++ b/dkgServiceNode/Data/DkgContext.cs @@ -128,7 +128,7 @@ public async Task AddRoundAsync(Round round) { Rounds.Add(round); await SaveChangesAsync(); - roundsCache.AddRoundToCache(round); + roundsCache.SaveRoundToCache(round); } catch (Exception ex) { diff --git a/dkgServiceNode/Services/Cache/NodesCache.cs b/dkgServiceNode/Services/Cache/NodesCache.cs index d5ca934..e933f54 100644 --- a/dkgServiceNode/Services/Cache/NodesCache.cs +++ b/dkgServiceNode/Services/Cache/NodesCache.cs @@ -33,7 +33,7 @@ namespace dkgServiceNode.Services.Cache public class NodesCache { private readonly Dictionary _cacheNodes = new(); - private readonly ConcurrentDictionary _addressToId = new(); + private readonly Dictionary _addressToId = new(); private readonly object _cacheNodesLock = new(); public void SaveNodeToCacheNoLock(Node node) { @@ -65,11 +65,18 @@ public void SaveNodeToCache(Node node) public Node? GetNodeByAddress(string address) { - if (_addressToId.TryGetValue(address, out var id)) + Node? res = null; + lock (_cacheNodesLock) { - return GetNodeById(id); + if (_addressToId.TryGetValue(address, out var id)) + { + if (_cacheNodes.TryGetValue(id, out Node? node)) + { + res = new Node(node); + } + } } - return null; + return res; } public List GetAllNodes() @@ -128,32 +135,25 @@ private List GetFilteredNodesInternal(string search) return filteredNodes; } - private void RemoveToIdEntries(ConcurrentDictionary dictionary, int nodeId) + public void UpdateNodeInCache(Node node) { - var keysToRemove = dictionary - .Where(kvp => kvp.Value == nodeId) - .Select(kvp => kvp.Key) - .ToList(); - - foreach (var key in keysToRemove) + lock (_cacheNodesLock) { - dictionary.TryRemove(key, out _); + var addr = _cacheNodes[node.Id]?.Address; + if (addr != null && addr!= node.Address) + { + _addressToId.Remove(addr); + } + _cacheNodes[node.Id] = new Node(node); + _addressToId[node.Address] = node.Id; } } - public void UpdateNodeInCache(Node node) - { - // Update node in the cache - RemoveToIdEntries(_addressToId, node.Id); - SaveNodeToCache(node); - } - public void DeleteNodeFromCache(Node node) { - // Remove node from the cache - RemoveToIdEntries(_addressToId, node.Id); lock (_cacheNodesLock) { + _addressToId.Remove(node.Address); _cacheNodes.Remove(node.Id); } } diff --git a/dkgServiceNode/Services/Cache/RoundsCache.cs b/dkgServiceNode/Services/Cache/RoundsCache.cs index a96f183..7109c3a 100644 --- a/dkgServiceNode/Services/Cache/RoundsCache.cs +++ b/dkgServiceNode/Services/Cache/RoundsCache.cs @@ -31,20 +31,28 @@ public class RoundsCache { private readonly Dictionary _cacheRounds = new(); private readonly object _cacheRoundsLock = new(); - public void SaveRoundToCache(Round round) - { - _cacheRounds[round.Id] = new Round(round); - } public Round? GetRoundById(int id) { - _cacheRounds.TryGetValue(id, out var round); - return round; + Round? res = null; + lock (_cacheRoundsLock) + { + if (_cacheRounds.TryGetValue(id, out Round? round)) + { + res = new Round(round); + } + } + return res; } public List GetAllRounds() { - return new List(_cacheRounds.Values); + List copiedRounds; + lock (_cacheRoundsLock) + { + copiedRounds = _cacheRounds.Values.Select(round => new Round(round)).ToList(); + } + return copiedRounds; } public List GetAllRoundsSortedByIdDescending() @@ -52,15 +60,19 @@ public List GetAllRoundsSortedByIdDescending() return new List(_cacheRounds.Values.OrderByDescending(r => r.Id)); } - public void AddRoundToCache(Round round) + public void SaveRoundToCache(Round round) { lock (_cacheRoundsLock) { - SaveRoundToCache(round); + _cacheRounds[round.Id] = new Round(round); } } + public void SaveRoundToCacheNoLock(Round round) + { + _cacheRounds[round.Id] = new Round(round); + } - public void UpdateRoundInCache(Round round) + public void UpdateRoundInCache(Round round) { lock (_cacheRoundsLock) { @@ -78,12 +90,22 @@ public void DeleteRoundFromCache(int id) public bool RoundExists(int id) { - return _cacheRounds.ContainsKey(id); + bool res = false; + lock (_cacheRoundsLock) + { + res = _cacheRounds.ContainsKey(id); + } + return res; } public int? LastRoundResult() { - return _cacheRounds.Values.OrderByDescending(r => r.Id).FirstOrDefault()?.Result; + int? res = null; + lock (_cacheRoundsLock) + { + res = _cacheRounds.Values.OrderByDescending(r => r.Id).FirstOrDefault()?.Result; + } + return res; } } } diff --git a/dkgServiceNode/Services/Initialization/Initializer.cs b/dkgServiceNode/Services/Initialization/Initializer.cs index 99844ed..1332547 100644 --- a/dkgServiceNode/Services/Initialization/Initializer.cs +++ b/dkgServiceNode/Services/Initialization/Initializer.cs @@ -152,7 +152,7 @@ private void InitializeRoundsCache(NpgsqlConnection connection) ModifiedOn = reader.GetDateTime(reader.GetOrdinal("modified")) }; - _roundsCache.SaveRoundToCache(round); + _roundsCache.SaveRoundToCacheNoLock(round); counter++; } _logger.LogInformation("Populated cache with {counter} rounds from database", counter); diff --git a/dkgServiceNode/Services/RoundRunner/ActiveRound.cs b/dkgServiceNode/Services/RoundRunner/ActiveRound.cs index 60c04b7..40adad9 100644 --- a/dkgServiceNode/Services/RoundRunner/ActiveRound.cs +++ b/dkgServiceNode/Services/RoundRunner/ActiveRound.cs @@ -31,6 +31,7 @@ using dkgServiceNode.Services.CRandom; using System.Runtime.CompilerServices; +using System.Xml.Linq; [assembly: InternalsVisibleTo("dkgNodesTests")] @@ -170,6 +171,9 @@ public bool IsResultReady() int finished = Nodes.Count(node => node.Finished); int timedOut = Nodes.Count(node => node.TimedOut); + Logger.LogDebug("Finished: {finished}; failed: {failed}; timed-out {timedOut}; minimum T: {minimumT}", + finished, failed, timedOut, VssTools.MinimumT(Nodes.Length)); + if ((ResultStartWaitingTime == DateTime.MinValue || DateTime.Now - ResultStartWaitingTime < TimeSpan.FromSeconds(Round.TimeoutR)) && VssTools.MinimumT(Nodes.Length) >= finished && finished + failed + timedOut < Nodes.Length) diff --git a/dkgServiceNode/appsettings.Development.json b/dkgServiceNode/appsettings.Development.json index bbc9de0..5016258 100644 --- a/dkgServiceNode/appsettings.Development.json +++ b/dkgServiceNode/appsettings.Development.json @@ -1,7 +1,7 @@ { "Logging": { "LogLevel": { - "Default": "Information", + "Default": "Warning", "Microsoft.AspNetCore": "Warning", "Microsoft.EntityFrameworkCore": "Warning" } diff --git a/docker-compose.yml b/docker-compose.yml index ec589d0..8f91053 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -61,6 +61,7 @@ services: container_name: dkg_node_4 image: ${DOCKER_REGISTRY-}dkgnode environment: +# - DKG_NODE_DIE_ON_STEP_THREE=ON - DKG_SERVICE_NODE_URL=http://dkg_service_node:8080 - DKG_NODE_NAME=Fourth node - DKG_SOLANA_KEYSTORE=eyJjcnlwdG8iOnsiY2lwaGVyIjoiYWVzLTEyOC1jdHIiLCJjaXBoZXJ0ZXh0IjoiNzRmYjhmOWM3YjJhOTMyOGI2NmYyMTEwOThhNGQ3YjQ3MjFmYWY4NDNkZDNhNDEzODI2ZDZjMjNmYjJiMWNiN2NlYzZlN2JhMmZhMGU2YzY5NTllNzUyNzdjNWY1YThmNjMwZWQ1Y2Q1NDRhMmJmMDEwYzU4NzFhZTViN2EwMzgiLCJjaXBoZXJwYXJhbXMiOnsiaXYiOiJiZWY3MzYwNjc5YzZmYjE1NDVhMDdjMjk0ZmJkZDRhNiJ9LCJrZGYiOiJzY3J5cHQiLCJtYWMiOiIwNjgyMmM2YzE0ZjE1MDZjYzI4MzcyZWFlZTVhNDA4OWJjMDk3NzgxMzAzZWQ0ZjRiZDJmYmRkMTliOTgyM2FhIiwia2RmcGFyYW1zIjp7Im4iOjI2MjE0NCwiciI6MSwicCI6OCwiZGtsZW4iOjMyLCJzYWx0IjoiNTZhNDRjZDViYWMwZWNkNGYxM2I1MTBmNzI0N2FmMzY1MmI4MDQ1ZTg2ODA3ZDhmMjkwNjQ4YjE1NzE5OTZiNiJ9fSwiaWQiOiIyZjBjZjdhOS1lMjJkLTRkMTEtYTQwYy01YTQ3OTZkNzc1YTUiLCJhZGRyZXNzIjoiOXFFaFNwREtpV0daSHVORWZtaVhMMVFjS1lpaWdxenQ2UWVwNnBQaUNlc0wiLCJ2ZXJzaW9uIjozfQ==