diff --git a/ProjectPlugins/CodexContractsPlugin/Marketplace/Customizations.cs b/ProjectPlugins/CodexContractsPlugin/Marketplace/Customizations.cs index 96490b7..8e8edb2 100644 --- a/ProjectPlugins/CodexContractsPlugin/Marketplace/Customizations.cs +++ b/ProjectPlugins/CodexContractsPlugin/Marketplace/Customizations.cs @@ -1,10 +1,12 @@ #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. using GethPlugin; +using Newtonsoft.Json; namespace CodexContractsPlugin.Marketplace { public partial class Request : RequestBase { + [JsonIgnore] public ulong BlockNumber { get; set; } public byte[] RequestId { get; set; } @@ -13,22 +15,26 @@ namespace CodexContractsPlugin.Marketplace public partial class RequestFulfilledEventDTO { + [JsonIgnore] public ulong BlockNumber { get; set; } } public partial class RequestCancelledEventDTO { + [JsonIgnore] public ulong BlockNumber { get; set; } } public partial class SlotFilledEventDTO { + [JsonIgnore] public ulong BlockNumber { get; set; } public EthAddress Host { get; set; } } public partial class SlotFreedEventDTO { + [JsonIgnore] public ulong BlockNumber { get; set; } } } diff --git a/Tools/TestNetRewarder/ChainState.cs b/Tools/TestNetRewarder/ChainState.cs index b57c39b..ce7f850 100644 --- a/Tools/TestNetRewarder/ChainState.cs +++ b/Tools/TestNetRewarder/ChainState.cs @@ -1,5 +1,6 @@ using CodexContractsPlugin; using CodexContractsPlugin.Marketplace; +using Newtonsoft.Json; using Utils; namespace TestNetRewarder @@ -11,11 +12,13 @@ namespace TestNetRewarder public ChainState(HistoricState historicState, ICodexContracts contracts, BlockInterval blockRange) { NewRequests = contracts.GetStorageRequests(blockRange); + historicState.CleanUpOldRequests(); historicState.ProcessNewRequests(NewRequests); historicState.UpdateStorageRequests(contracts); StartedRequests = historicState.StorageRequests.Where(r => r.RecentlyStarted).ToArray(); FinishedRequests = historicState.StorageRequests.Where(r => r.RecentlyFinished).ToArray(); + ChangedRequests = historicState.StorageRequests.Where(r => r.RecentlyChanged).ToArray(); RequestFulfilledEvents = contracts.GetRequestFulfilledEvents(blockRange); RequestCancelledEvents = contracts.GetRequestCancelledEvents(blockRange); SlotFilledEvents = contracts.GetSlotFilledEvents(blockRange); @@ -27,9 +30,78 @@ namespace TestNetRewarder public StorageRequest[] AllRequests => historicState.StorageRequests; public StorageRequest[] StartedRequests { get; private set; } public StorageRequest[] FinishedRequests { get; private set; } + public StorageRequest[] ChangedRequests { get; private set; } public RequestFulfilledEventDTO[] RequestFulfilledEvents { get; } public RequestCancelledEventDTO[] RequestCancelledEvents { get; } public SlotFilledEventDTO[] SlotFilledEvents { get; } public SlotFreedEventDTO[] SlotFreedEvents { get; } + + public string[] GenerateOverview() + { + var entries = new List(); + + entries.AddRange(ChangedRequests.Select(ToPair)); + entries.AddRange(RequestFulfilledEvents.Select(ToPair)); + entries.AddRange(RequestCancelledEvents.Select(ToPair)); + entries.AddRange(SlotFilledEvents.Select(ToPair)); + entries.AddRange(SlotFreedEvents.Select(ToPair)); + + entries.Sort(new StringUtcComparer()); + + return entries.Select(ToLine).ToArray(); + } + + private StringBlockNumberPair ToPair(StorageRequest r) + { + return new StringBlockNumberPair(JsonConvert.SerializeObject(r), r.Request.BlockNumber); + } + + private StringBlockNumberPair ToPair(RequestFulfilledEventDTO r) + { + return new StringBlockNumberPair(JsonConvert.SerializeObject(r), r.BlockNumber); + } + + private StringBlockNumberPair ToPair(RequestCancelledEventDTO r) + { + return new StringBlockNumberPair(JsonConvert.SerializeObject(r), r.BlockNumber); + } + + private StringBlockNumberPair ToPair(SlotFilledEventDTO r) + { + return new StringBlockNumberPair(JsonConvert.SerializeObject(r), r.BlockNumber); + } + + private StringBlockNumberPair ToPair(SlotFreedEventDTO r) + { + return new StringBlockNumberPair(JsonConvert.SerializeObject(r), r.BlockNumber); + } + + private string ToLine(StringBlockNumberPair pair) + { + return $"[{pair.Number}] {pair.Str}"; + } + + public class StringBlockNumberPair + { + public StringBlockNumberPair(string str, ulong number) + { + Str = str; + Number = number; + } + + public string Str { get; } + public ulong Number { get; } + } + + public class StringUtcComparer : IComparer + { + public int Compare(StringBlockNumberPair? x, StringBlockNumberPair? y) + { + if (x == null && y == null) return 0; + if (x == null) return 1; + if (y == null) return -1; + return x.Number.CompareTo(y.Number); + } + } } } diff --git a/Tools/TestNetRewarder/Configuration.cs b/Tools/TestNetRewarder/Configuration.cs index ed885d1..c180648 100644 --- a/Tools/TestNetRewarder/Configuration.cs +++ b/Tools/TestNetRewarder/Configuration.cs @@ -22,6 +22,9 @@ namespace TestNetRewarder [Uniform("market-insights", "mi", "MARKETINSIGHTS", false, "Semi-colon separated integers. Each represents a multiple of intervals, for which a market insights average will be generated.")] public string MarketInsights { get; set; } = "1;96"; + [Uniform("events-overview", "eo", "EVENTSOVERVIEW", false, "When greater than zero, chain event summary will be generated. (default 1)")] + public int CreateChainEventsOverview { get; set; } = 1; + public string LogPath { get diff --git a/Tools/TestNetRewarder/HistoricState.cs b/Tools/TestNetRewarder/HistoricState.cs index 53fd1ba..316a80e 100644 --- a/Tools/TestNetRewarder/HistoricState.cs +++ b/Tools/TestNetRewarder/HistoricState.cs @@ -1,6 +1,7 @@ using CodexContractsPlugin; using CodexContractsPlugin.Marketplace; using GethPlugin; +using Newtonsoft.Json; namespace TestNetRewarder { @@ -19,6 +20,17 @@ namespace TestNetRewarder { foreach (var r in storageRequests) r.Update(contracts); } + + public void CleanUpOldRequests() + { + storageRequests.RemoveAll(r => + r.State == RequestState.Cancelled || + r.State == RequestState.Finished || + r.State == RequestState.Failed + ); + + foreach (var r in storageRequests) r.IsNew = false; + } } public class StorageRequest @@ -27,17 +39,26 @@ namespace TestNetRewarder { Request = request; Hosts = Array.Empty(); + IsNew = true; } public Request Request { get; } public EthAddress[] Hosts { get; private set; } public RequestState State { get; private set; } + public bool IsNew { get; set; } + + [JsonIgnore] public bool RecentlyStarted { get; private set; } + + [JsonIgnore] public bool RecentlyFinished { get; private set; } + [JsonIgnore] + public bool RecentlyChanged { get; private set; } + public void Update(ICodexContracts contracts) { - Hosts = GetHosts(contracts); + var newHosts = GetHosts(contracts); var newState = contracts.GetRequestState(Request); @@ -49,7 +70,25 @@ namespace TestNetRewarder State == RequestState.Started && newState == RequestState.Finished; + RecentlyChanged = + IsNew || + State != newState || + HostsChanged(newHosts); + State = newState; + Hosts = newHosts; + } + + private bool HostsChanged(EthAddress[] newHosts) + { + if (newHosts.Length != Hosts.Length) return true; + + foreach (var newHost in newHosts) + { + if (!Hosts.Contains(newHost)) return true; + } + + return false; } private EthAddress[] GetHosts(ICodexContracts contracts) diff --git a/Tools/TestNetRewarder/Processor.cs b/Tools/TestNetRewarder/Processor.cs index 8530330..a293495 100644 --- a/Tools/TestNetRewarder/Processor.cs +++ b/Tools/TestNetRewarder/Processor.cs @@ -65,29 +65,36 @@ namespace TestNetRewarder } var marketAverages = GetMarketAverages(chainState); + var eventsOverview = GenerateEventsOverview(chainState); log.Log($"Found {outgoingRewards.Count} rewards to send. Found {marketAverages.Length} market averages."); if (outgoingRewards.Any()) { - if (!await SendRewardsCommand(outgoingRewards, marketAverages)) + if (!await SendRewardsCommand(outgoingRewards, marketAverages, eventsOverview)) { log.Error("Failed to send reward command."); } } } + private string[] GenerateEventsOverview(ChainState chainState) + { + return chainState.GenerateOverview(); + } + private MarketAverage[] GetMarketAverages(ChainState chainState) { return marketTracker.ProcessChainState(chainState); } - private async Task SendRewardsCommand(List outgoingRewards, MarketAverage[] marketAverages) + private async Task SendRewardsCommand(List outgoingRewards, MarketAverage[] marketAverages, string[] eventsOverview) { var cmd = new GiveRewardsCommand { Rewards = outgoingRewards.ToArray(), - Averages = marketAverages.ToArray() + Averages = marketAverages.ToArray(), + EventsOverview = eventsOverview }; log.Debug("Sending rewards: " + JsonConvert.SerializeObject(cmd));