diff --git a/Framework/DiscordRewards/GiveRewardsCommand.cs b/Framework/DiscordRewards/GiveRewardsCommand.cs index 6ca5aa4..3aae088 100644 --- a/Framework/DiscordRewards/GiveRewardsCommand.cs +++ b/Framework/DiscordRewards/GiveRewardsCommand.cs @@ -4,6 +4,7 @@ { public RewardUsersCommand[] Rewards { get; set; } = Array.Empty(); public ChainEventMessage[] EventsOverview { get; set; } = Array.Empty(); + public string[] Errors { get; set; } = Array.Empty(); public bool HasAny() { diff --git a/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainState.cs b/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainState.cs index 4377b22..30756c4 100644 --- a/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainState.cs +++ b/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainState.cs @@ -17,6 +17,8 @@ namespace CodexContractsPlugin.ChainMonitor void OnSlotFilled(RequestEvent requestEvent, EthAddress host, BigInteger slotIndex); void OnSlotFreed(RequestEvent requestEvent, BigInteger slotIndex); void OnSlotReservationsFull(RequestEvent requestEvent, BigInteger slotIndex); + + void OnError(string msg); } public class RequestEvent @@ -67,7 +69,11 @@ namespace CodexContractsPlugin.ChainMonitor private void Apply(ChainEvents events) { if (events.BlockInterval.TimeRange.From < TotalSpan.From) - throw new Exception("Attempt to update ChainState with set of events from before its current record."); + { + var msg = "Attempt to update ChainState with set of events from before its current record."; + handler.OnError(msg); + throw new Exception(msg); + } log.Log($"ChainState updating: {events.BlockInterval}"); @@ -110,7 +116,7 @@ namespace CodexContractsPlugin.ChainMonitor private void ApplyEvent(RequestFulfilledEventDTO @event) { - var r = FindRequest(@event.RequestId); + var r = FindRequest(@event); if (r == null) return; r.UpdateState(@event.Block.BlockNumber, RequestState.Started); handler.OnRequestFulfilled(new RequestEvent(@event.Block, r)); @@ -118,7 +124,7 @@ namespace CodexContractsPlugin.ChainMonitor private void ApplyEvent(RequestCancelledEventDTO @event) { - var r = FindRequest(@event.RequestId); + var r = FindRequest(@event); if (r == null) return; r.UpdateState(@event.Block.BlockNumber, RequestState.Cancelled); handler.OnRequestCancelled(new RequestEvent(@event.Block, r)); @@ -126,7 +132,7 @@ namespace CodexContractsPlugin.ChainMonitor private void ApplyEvent(RequestFailedEventDTO @event) { - var r = FindRequest(@event.RequestId); + var r = FindRequest(@event); if (r == null) return; r.UpdateState(@event.Block.BlockNumber, RequestState.Failed); handler.OnRequestFailed(new RequestEvent(@event.Block, r)); @@ -134,7 +140,7 @@ namespace CodexContractsPlugin.ChainMonitor private void ApplyEvent(SlotFilledEventDTO @event) { - var r = FindRequest(@event.RequestId); + var r = FindRequest(@event); if (r == null) return; r.Hosts.Add(@event.Host, (int)@event.SlotIndex); r.Log($"[{@event.Block.BlockNumber}] SlotFilled (host:'{@event.Host}', slotIndex:{@event.SlotIndex})"); @@ -143,7 +149,7 @@ namespace CodexContractsPlugin.ChainMonitor private void ApplyEvent(SlotFreedEventDTO @event) { - var r = FindRequest(@event.RequestId); + var r = FindRequest(@event); if (r == null) return; r.Hosts.RemoveHost((int)@event.SlotIndex); r.Log($"[{@event.Block.BlockNumber}] SlotFreed (slotIndex:{@event.SlotIndex})"); @@ -152,7 +158,7 @@ namespace CodexContractsPlugin.ChainMonitor private void ApplyEvent(SlotReservationsFullEventDTO @event) { - var r = FindRequest(@event.RequestId); + var r = FindRequest(@event); if (r == null) return; r.Log($"[{@event.Block.BlockNumber}] SlotReservationsFull (slotIndex:{@event.SlotIndex})"); handler.OnSlotReservationsFull(new RequestEvent(@event.Block, r), @event.SlotIndex); @@ -171,10 +177,23 @@ namespace CodexContractsPlugin.ChainMonitor } } - private ChainStateRequest? FindRequest(byte[] requestId) + private ChainStateRequest? FindRequest(IHasRequestId request) { - var r = requests.SingleOrDefault(r => Equal(r.Request.RequestId, requestId)); - if (r == null) log.Log("Unable to find request by ID!"); + var r = requests.SingleOrDefault(r => Equal(r.Request.RequestId, request.RequestId)); + if (r == null) + { + var blockNumber = "unknown"; + if (request is IHasBlock blk) + { + blockNumber = blk.Block.BlockNumber.ToString(); + } + + var msg = $"Received event of type '{request.GetType()}' in block '{blockNumber}' for request by Id: '{request.RequestId}'. " + + $"Failed to find request. Request creation event not seen! (Tracker start time: {TotalSpan.From})"; + + log.Error(msg); + handler.OnError(msg); + } return r; } diff --git a/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainStateChangeHandlerMux.cs b/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainStateChangeHandlerMux.cs index 597cb86..799573f 100644 --- a/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainStateChangeHandlerMux.cs +++ b/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainStateChangeHandlerMux.cs @@ -1,10 +1,5 @@ using GethPlugin; -using System; -using System.Collections.Generic; -using System.Linq; using System.Numerics; -using System.Text; -using System.Threading.Tasks; namespace CodexContractsPlugin.ChainMonitor { @@ -56,5 +51,10 @@ namespace CodexContractsPlugin.ChainMonitor { foreach (var handler in Handlers) handler.OnSlotReservationsFull(requestEvent, slotIndex); } + + public void OnError(string msg) + { + foreach (var handler in Handlers) handler.OnError(msg); + } } } diff --git a/ProjectPlugins/CodexContractsPlugin/ChainMonitor/DoNothingChainEventHandler.cs b/ProjectPlugins/CodexContractsPlugin/ChainMonitor/DoNothingChainEventHandler.cs index 1b478ae..749f30c 100644 --- a/ProjectPlugins/CodexContractsPlugin/ChainMonitor/DoNothingChainEventHandler.cs +++ b/ProjectPlugins/CodexContractsPlugin/ChainMonitor/DoNothingChainEventHandler.cs @@ -36,5 +36,9 @@ namespace CodexContractsPlugin.ChainMonitor public void OnSlotReservationsFull(RequestEvent requestEvent, BigInteger slotIndex) { } + + public void OnError(string msg) + { + } } } diff --git a/ProjectPlugins/CodexContractsPlugin/Marketplace/Customizations.cs b/ProjectPlugins/CodexContractsPlugin/Marketplace/Customizations.cs index ba1e168..413d340 100644 --- a/ProjectPlugins/CodexContractsPlugin/Marketplace/Customizations.cs +++ b/ProjectPlugins/CodexContractsPlugin/Marketplace/Customizations.cs @@ -10,7 +10,12 @@ namespace CodexContractsPlugin.Marketplace BlockTimeEntry Block { get; set; } } - public partial class Request : RequestBase, IHasBlock + public interface IHasRequestId + { + byte[] RequestId { get; set; } + } + + public partial class Request : RequestBase, IHasBlock, IHasRequestId { [JsonIgnore] public BlockTimeEntry Block { get; set; } @@ -28,38 +33,38 @@ namespace CodexContractsPlugin.Marketplace } } - public partial class RequestFulfilledEventDTO : IHasBlock + public partial class RequestFulfilledEventDTO : IHasBlock, IHasRequestId { [JsonIgnore] public BlockTimeEntry Block { get; set; } } - public partial class RequestCancelledEventDTO : IHasBlock + public partial class RequestCancelledEventDTO : IHasBlock, IHasRequestId { [JsonIgnore] public BlockTimeEntry Block { get; set; } } - public partial class RequestFailedEventDTO : IHasBlock + public partial class RequestFailedEventDTO : IHasBlock, IHasRequestId { [JsonIgnore] public BlockTimeEntry Block { get; set; } } - public partial class SlotFilledEventDTO : IHasBlock + public partial class SlotFilledEventDTO : IHasBlock, IHasRequestId { [JsonIgnore] public BlockTimeEntry Block { get; set; } public EthAddress Host { get; set; } } - public partial class SlotFreedEventDTO : IHasBlock + public partial class SlotFreedEventDTO : IHasBlock, IHasRequestId { [JsonIgnore] public BlockTimeEntry Block { get; set; } } - public partial class SlotReservationsFullEventDTO : IHasBlock + public partial class SlotReservationsFullEventDTO : IHasBlock, IHasRequestId { [JsonIgnore] public BlockTimeEntry Block { get; set; } diff --git a/Tools/BiblioTech/Rewards/ChainEventsSender.cs b/Tools/BiblioTech/Rewards/ChainEventsSender.cs index 5cb6aed..7e27328 100644 --- a/Tools/BiblioTech/Rewards/ChainEventsSender.cs +++ b/Tools/BiblioTech/Rewards/ChainEventsSender.cs @@ -17,8 +17,10 @@ namespace BiblioTech.Rewards this.eventsChannel = eventsChannel; } - public async Task ProcessChainEvents(ChainEventMessage[] eventsOverview) + public async Task ProcessChainEvents(ChainEventMessage[] eventsOverview, string[] errors) { + await SendErrorsToAdminChannel(errors); + if (eventsChannel == null || eventsOverview == null || !eventsOverview.Any()) return; try { @@ -34,6 +36,22 @@ namespace BiblioTech.Rewards } } + private async Task SendErrorsToAdminChannel(string[] errors) + { + try + { + foreach (var error in errors) + { + await Program.AdminChecker.SendInAdminChannel(error); + } + } + catch (Exception exc) + { + log.Error("Failed to send error messages to admin channel. " + exc); + Environment.Exit(1); + } + } + private async Task SendChainEventsInOrder(ChainEventMessage[] eventsOverview, SocketTextChannel eventsChannel, UserData[] users) { eventsOverview = eventsOverview.OrderBy(e => e.BlockNumber).ToArray(); diff --git a/Tools/BiblioTech/Rewards/RoleDriver.cs b/Tools/BiblioTech/Rewards/RoleDriver.cs index 71019b7..b3bf707 100644 --- a/Tools/BiblioTech/Rewards/RoleDriver.cs +++ b/Tools/BiblioTech/Rewards/RoleDriver.cs @@ -31,7 +31,7 @@ namespace BiblioTech.Rewards await ProcessRewards(rewards); } - await eventsSender.ProcessChainEvents(rewards.EventsOverview); + await eventsSender.ProcessChainEvents(rewards.EventsOverview, rewards.Errors); } private async Task ProcessRewards(GiveRewardsCommand rewards) diff --git a/Tools/MarketInsights/AverageHistory.cs b/Tools/MarketInsights/AverageHistory.cs index af752d1..5af2010 100644 --- a/Tools/MarketInsights/AverageHistory.cs +++ b/Tools/MarketInsights/AverageHistory.cs @@ -1,6 +1,5 @@ using CodexContractsPlugin; using CodexContractsPlugin.ChainMonitor; -using Nethereum.Model; using TestNetRewarder; using Utils; @@ -40,7 +39,7 @@ namespace MarketInsights private MarketTimeSegment BuildContribution(TimeRange timeRange) { - var builder = new ContributionBuilder(timeRange); + var builder = new ContributionBuilder(appState.Log, timeRange); mux.Handlers.Add(builder); chainState.Update(timeRange.To); mux.Handlers.Remove(builder); diff --git a/Tools/MarketInsights/ContributionBuilder.cs b/Tools/MarketInsights/ContributionBuilder.cs index 63ab4da..5d1c4f7 100644 --- a/Tools/MarketInsights/ContributionBuilder.cs +++ b/Tools/MarketInsights/ContributionBuilder.cs @@ -1,5 +1,6 @@ using CodexContractsPlugin.ChainMonitor; using GethPlugin; +using Logging; using System.Numerics; using Utils; @@ -8,14 +9,16 @@ namespace MarketInsights public class ContributionBuilder : IChainStateChangeHandler { private readonly MarketTimeSegment segment = new MarketTimeSegment(); + private readonly ILog log; - public ContributionBuilder(TimeRange timeRange) + public ContributionBuilder(ILog log, TimeRange timeRange) { segment = new MarketTimeSegment { FromUtc = timeRange.From, ToUtc = timeRange.To }; + this.log = log; } public void OnNewRequest(RequestEvent requestEvent) @@ -55,6 +58,11 @@ namespace MarketInsights { } + public void OnError(string msg) + { + log.Error(msg); + } + public MarketTimeSegment GetSegment() { return segment; diff --git a/Tools/TestNetRewarder/EventsFormatter.cs b/Tools/TestNetRewarder/EventsFormatter.cs index 651615d..924645d 100644 --- a/Tools/TestNetRewarder/EventsFormatter.cs +++ b/Tools/TestNetRewarder/EventsFormatter.cs @@ -12,6 +12,7 @@ namespace TestNetRewarder { private static readonly string nl = Environment.NewLine; private readonly List events = new List(); + private readonly List errors = new List(); private readonly EmojiMaps emojiMaps = new EmojiMaps(); public ChainEventMessage[] GetEvents() @@ -21,9 +22,11 @@ namespace TestNetRewarder return result; } - public void AddError(string error) + public string[] GetErrors() { - AddBlock(1, "📢 **Error**", error); + var result = errors.ToArray(); + errors.Clear(); + return result; } public void OnNewRequest(RequestEvent requestEvent) @@ -83,6 +86,11 @@ namespace TestNetRewarder $"Slot Index: {slotIndex}" ); } + + public void OnError(string msg) + { + errors.Add(msg); + } private void AddRequestBlock(RequestEvent requestEvent, string eventName, params string[] content) { diff --git a/Tools/TestNetRewarder/Processor.cs b/Tools/TestNetRewarder/Processor.cs index a6d2ccd..40651be 100644 --- a/Tools/TestNetRewarder/Processor.cs +++ b/Tools/TestNetRewarder/Processor.cs @@ -48,7 +48,7 @@ namespace TestNetRewarder { var msg = "Exception processing time segment: " + ex; log.Error(msg); - eventsFormatter.AddError(msg); + eventsFormatter.OnError(msg); throw; } } @@ -58,8 +58,9 @@ namespace TestNetRewarder var numberOfChainEvents = chainState.Update(timeRange.To); var events = eventsFormatter.GetEvents(); + var errors = eventsFormatter.GetErrors(); - var request = builder.Build(events); + var request = builder.Build(events, errors); if (request.HasAny()) { await client.SendRewards(request); diff --git a/Tools/TestNetRewarder/RequestBuilder.cs b/Tools/TestNetRewarder/RequestBuilder.cs index 72403b9..6ea25f3 100644 --- a/Tools/TestNetRewarder/RequestBuilder.cs +++ b/Tools/TestNetRewarder/RequestBuilder.cs @@ -19,7 +19,7 @@ namespace TestNetRewarder } } - public GiveRewardsCommand Build(ChainEventMessage[] lines) + public GiveRewardsCommand Build(ChainEventMessage[] lines, string[] errors) { var result = new GiveRewardsCommand { @@ -28,7 +28,8 @@ namespace TestNetRewarder RewardId = p.Key, UserAddresses = p.Value.Select(v => v.Address).ToArray() }).ToArray(), - EventsOverview = lines + EventsOverview = lines, + Errors = errors }; rewards.Clear(); diff --git a/Tools/TestNetRewarder/RewardCheck.cs b/Tools/TestNetRewarder/RewardCheck.cs index e6662cb..abfafda 100644 --- a/Tools/TestNetRewarder/RewardCheck.cs +++ b/Tools/TestNetRewarder/RewardCheck.cs @@ -76,6 +76,10 @@ namespace TestNetRewarder { } + public void OnError(string msg) + { + } + private void GiveReward(RewardConfig reward, EthAddress receiver) { giver.Give(reward, receiver);