From cc2513bd2f0b0fbfdeeddf9042986bcc3b43950a Mon Sep 17 00:00:00 2001 From: Ben Date: Mon, 10 Jun 2024 14:04:25 +0200 Subject: [PATCH] Better chain state representation --- .../ChainMonitor/ChainState.cs | 32 +++- .../ChainMonitor/ChainStateRequest.cs | 9 +- .../Marketplace/Customizations.cs | 11 ++ .../CodexPlugin/StoragePurchaseContract.cs | 1 + .../UtilityTests/DiscordBotTests.cs | 158 ++---------------- 5 files changed, 63 insertions(+), 148 deletions(-) diff --git a/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainState.cs b/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainState.cs index b3870dc..e3993f2 100644 --- a/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainState.cs +++ b/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainState.cs @@ -24,7 +24,7 @@ namespace CodexContractsPlugin.ChainMonitor private ChainState(ILog log, IChainStateChangeHandler changeHandler, TimeRange timeRange) { - this.log = log; + this.log = new LogPrefixer(log, "(ChainState) "); handler = changeHandler; TotalSpan = timeRange; } @@ -39,7 +39,21 @@ namespace CodexContractsPlugin.ChainMonitor public TimeRange TotalSpan { get; private set; } public IChainStateRequest[] Requests => requests.ToArray(); - public void Apply(ChainEvents events) + public void Update(ICodexContracts contracts) + { + Update(contracts, DateTime.UtcNow); + } + + public void Update(ICodexContracts contracts, DateTime toUtc) + { + var span = new TimeRange(TotalSpan.To, toUtc); + var events = ChainEvents.FromTimeRange(contracts, span); + Apply(events); + + TotalSpan = new TimeRange(TotalSpan.From, span.To); + } + + 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."); @@ -52,7 +66,7 @@ namespace CodexContractsPlugin.ChainMonitor var spanPerBlock = span / numBlocks; var eventUtc = events.BlockInterval.TimeRange.From; - for (var b = events.BlockInterval.From; b < events.BlockInterval.To; b++) + for (var b = events.BlockInterval.From; b <= events.BlockInterval.To; b++) { var blockEvents = events.All.Where(e => e.Block.BlockNumber == b).ToArray(); ApplyEvents(blockEvents, eventUtc); @@ -86,6 +100,7 @@ namespace CodexContractsPlugin.ChainMonitor private void ApplyEvent(RequestFulfilledEventDTO request) { var r = FindRequest(request.RequestId); + if (r == null) return; r.UpdateState(RequestState.Started); handler.OnRequestFulfilled(r); } @@ -93,6 +108,7 @@ namespace CodexContractsPlugin.ChainMonitor private void ApplyEvent(RequestCancelledEventDTO request) { var r = FindRequest(request.RequestId); + if (r == null) return; r.UpdateState(RequestState.Cancelled); handler.OnRequestCancelled(r); } @@ -100,12 +116,16 @@ namespace CodexContractsPlugin.ChainMonitor private void ApplyEvent(SlotFilledEventDTO request) { var r = FindRequest(request.RequestId); + if (r == null) return; + r.Log("SlotFilled"); handler.OnSlotFilled(r, request.SlotIndex); } private void ApplyEvent(SlotFreedEventDTO request) { var r = FindRequest(request.RequestId); + if (r == null) return; + r.Log("SlotFreed"); handler.OnSlotFreed(r, request.SlotIndex); } @@ -122,9 +142,11 @@ namespace CodexContractsPlugin.ChainMonitor } } - private ChainStateRequest FindRequest(byte[] requestId) + private ChainStateRequest? FindRequest(byte[] requestId) { - return requests.Single(r => Equal(r.Request.RequestId, requestId)); + var r = requests.SingleOrDefault(r => Equal(r.Request.RequestId, requestId)); + if (r == null) log.Log("Unable to find request by ID!"); + return r; } private bool Equal(byte[] a, byte[] b) diff --git a/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainStateRequest.cs b/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainStateRequest.cs index ae735b4..4783e3a 100644 --- a/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainStateRequest.cs +++ b/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainStateRequest.cs @@ -24,7 +24,7 @@ namespace CodexContractsPlugin.ChainMonitor ExpiryUtc = request.Block.Utc + TimeSpan.FromSeconds((double)request.Expiry); FinishedUtc = request.Block.Utc + TimeSpan.FromSeconds((double)request.Ask.Duration); - log.Log($"Request created as {State}."); + log.Log($"Created as {State}."); } public Request Request { get; } @@ -34,8 +34,13 @@ namespace CodexContractsPlugin.ChainMonitor public void UpdateState(RequestState newState) { - log.Log($"Request transit: {State} -> {newState}"); + Log($"Transit: {State} -> {newState}"); State = newState; } + + public void Log(string msg) + { + log.Log($"Request '{Request.Id}': {msg}"); + } } } diff --git a/ProjectPlugins/CodexContractsPlugin/Marketplace/Customizations.cs b/ProjectPlugins/CodexContractsPlugin/Marketplace/Customizations.cs index 5094e12..557e021 100644 --- a/ProjectPlugins/CodexContractsPlugin/Marketplace/Customizations.cs +++ b/ProjectPlugins/CodexContractsPlugin/Marketplace/Customizations.cs @@ -17,6 +17,17 @@ namespace CodexContractsPlugin.Marketplace public byte[] RequestId { get; set; } public EthAddress ClientAddress { get { return new EthAddress(Client); } } + + [JsonIgnore] + public string Id + { + get + { + var id = ""; + foreach (var b in RequestId) id += b.ToString(); + return id; + } + } } public partial class RequestFulfilledEventDTO : IHasBlock diff --git a/ProjectPlugins/CodexPlugin/StoragePurchaseContract.cs b/ProjectPlugins/CodexPlugin/StoragePurchaseContract.cs index 3a9e0b9..d812f51 100644 --- a/ProjectPlugins/CodexPlugin/StoragePurchaseContract.cs +++ b/ProjectPlugins/CodexPlugin/StoragePurchaseContract.cs @@ -6,6 +6,7 @@ namespace CodexPlugin { public interface IStoragePurchaseContract { + string PurchaseId { get; } void WaitForStorageContractSubmitted(); void WaitForStorageContractStarted(); void WaitForStorageContractFinished(); diff --git a/Tests/CodexTests/UtilityTests/DiscordBotTests.cs b/Tests/CodexTests/UtilityTests/DiscordBotTests.cs index c73cd23..3ae9474 100644 --- a/Tests/CodexTests/UtilityTests/DiscordBotTests.cs +++ b/Tests/CodexTests/UtilityTests/DiscordBotTests.cs @@ -1,16 +1,13 @@ using CodexContractsPlugin; using CodexContractsPlugin.ChainMonitor; -using CodexContractsPlugin.Marketplace; using CodexDiscordBotPlugin; using CodexPlugin; using Core; using DiscordRewards; using GethPlugin; using KubernetesWorkflow.Types; -using Logging; using Newtonsoft.Json; using NUnit.Framework; -using TestNetRewarder; using Utils; namespace CodexTests.UtilityTests @@ -33,9 +30,6 @@ namespace CodexTests.UtilityTests var contracts = Ci.StartCodexContracts(geth); var gethInfo = CreateGethInfo(geth, contracts); - var monitor = new ChainMonitor(contracts, geth, GetTestLog()); - monitor.Start(); - var botContainer = StartDiscordBot(gethInfo); StartHosts(geth, contracts); @@ -44,49 +38,45 @@ namespace CodexTests.UtilityTests var client = StartClient(geth, contracts); - var events = ChainEvents.FromTimeRange(contracts, GetTestRunTimeRange()); - var chainState = CodexContractsPlugin.ChainMonitor.ChainState.FromEvents( + var chainState = ChainState.FromEvents( GetTestLog(), events, new DoNothingChainEventHandler()); - var apiCalls = new RewardApiCalls(Ci, botContainer); apiCalls.Start(OnCommand); - var rewarderLog = new RewarderLogMonitor(Ci, rewarderContainer.Containers.Single()); - rewarderLog.Start(l => Log("Rewarder ChainState: " + l)); var purchaseContract = ClientPurchasesStorage(client); + chainState.Update(contracts); + Assert.That(chainState.Requests.Length, Is.EqualTo(1)); + + purchaseContract.WaitForStorageContractStarted(); + chainState.Update(contracts); + purchaseContract.WaitForStorageContractFinished(); - rewarderLog.Stop(); - apiCalls.Stop(); - monitor.Stop(); - - Log("Done!"); - Thread.Sleep(rewarderInterval * 2); - - chainState.Apply(ChainEvents.FromTimeRange(contracts, GetTestRunTimeRange())); - - Log("Seen:"); - foreach (var seen in rewardsSeen) - { - Log(seen.ToString()); - } - Log(""); + + apiCalls.Stop(); + chainState.Update(contracts); foreach (var r in repo.Rewards) { var seen = rewardsSeen.Any(s => r.RoleId == s); - Log($"{r.RoleId} = {seen}"); + Log($"{Lookup(r.RoleId)} = {seen}"); } Assert.That(repo.Rewards.All(r => rewardsSeen.Contains(r.RoleId))); } + private string Lookup(ulong rewardId) + { + var reward = repo.Rewards.Single(r => r.RoleId == rewardId); + return $"({rewardId})'{reward.Message}'"; + } + private void OnCommand(GiveRewardsCommand call) { if (call.Averages.Any()) Log($"API call: {call.Averages.Length} average."); @@ -292,55 +282,6 @@ namespace CodexTests.UtilityTests } } - public class RewarderLogMonitor - { - private readonly ContainerFileMonitor monitor; - private readonly Dictionary commands = new Dictionary(); - - public RewarderLogMonitor(CoreInterface ci, RunningContainer botContainer) - { - monitor = new ContainerFileMonitor(ci, botContainer, "/app/datapath/logs/testnetrewarder.log"); - } - - public void Start(Action onCommand) - { - monitor.Start(l => ProcessLine(l, onCommand)); - } - - public void Stop() - { - monitor.Stop(); - } - - private void ProcessLine(string line, Action log) - { - //$"ChainState=[{JsonConvert.SerializeObject(this)}]" + - //$"HistoricState=[{historicState.EntireString()}]"; - - //var stateOpenTag = "ChainState=["; - //var historicOpenTag = "]HistoricState=["; - - //if (!line.Contains(stateOpenTag)) return; - //if (!line.Contains(historicOpenTag)) return; - - //var stateStr = Between(line, stateOpenTag, historicOpenTag); - //var historicStr = Between(line, historicOpenTag, "]"); - - //var chainState = JsonConvert.DeserializeObject(stateStr); - //var historicState = JsonConvert.DeserializeObject(historicStr)!; - //chainState!.Set(new HistoricState(historicState)); - - //log(string.Join(",", chainState!.GenerateOverview())); - } - - private string Between(string s, string open, string close) - { - var start = s.IndexOf(open) + open.Length; - var end = s.LastIndexOf(close); - return s.Substring(start, end - start); - } - } - public class ContainerFileMonitor { private readonly CoreInterface ci; @@ -395,70 +336,5 @@ namespace CodexTests.UtilityTests } } } - - public class ChainMonitor - { - private readonly ICodexContracts contracts; - private readonly IGethNode geth; - private readonly ILog log; - private readonly CancellationTokenSource cts = new CancellationTokenSource(); - private Task worker = Task.CompletedTask; - private DateTime last = DateTime.UtcNow; - - public ChainMonitor(ICodexContracts contracts, IGethNode geth, ILog log) - { - this.contracts = contracts; - this.geth = geth; - this.log = log; - } - - public void Start() - { - last = DateTime.UtcNow; - worker = Task.Run(Worker); - } - - public void Stop() - { - cts.Cancel(); - worker.Wait(); - } - - private void Worker() - { - while (!cts.IsCancellationRequested) - { - Thread.Sleep(TimeSpan.FromSeconds(10)); - if (cts.IsCancellationRequested) return; - - Update(); - - } - } - - private void Update() - { - //var start = last; - //var stop = DateTime.UtcNow; - //last = stop; - - //var range = geth.ConvertTimeRangeToBlockRange(new TimeRange(start, stop)); - - - //throw new Exception(); - //LogEvents(nameof(contracts.GetStorageRequests), contracts.GetStorageRequests, range); - //LogEvents(nameof(contracts.GetRequestFulfilledEvents), contracts.GetRequestFulfilledEvents, range); - //LogEvents(nameof(contracts.GetRequestCancelledEvents), contracts.GetRequestCancelledEvents, range); - //LogEvents(nameof(contracts.GetSlotFilledEvents), contracts.GetSlotFilledEvents, range); - //LogEvents(nameof(contracts.GetSlotFreedEvents), contracts.GetSlotFreedEvents, range); - } - - private void LogEvents(string n, Func f, BlockInterval r) - { - var a = (object[])f(r); - - a.ToList().ForEach(request => log.Log(n + " - " + JsonConvert.SerializeObject(request))); - } - } } }