wip
This commit is contained in:
parent
d3488dc907
commit
c38a2242ba
|
@ -20,37 +20,29 @@ namespace CodexContractsPlugin.ChainMonitor
|
|||
{
|
||||
private readonly List<ChainStateRequest> requests = new List<ChainStateRequest>();
|
||||
private readonly ILog log;
|
||||
private readonly ICodexContracts contracts;
|
||||
private readonly IChainStateChangeHandler handler;
|
||||
|
||||
private ChainState(ILog log, IChainStateChangeHandler changeHandler, TimeRange timeRange)
|
||||
public ChainState(ILog log, ICodexContracts contracts, IChainStateChangeHandler changeHandler, DateTime startUtc)
|
||||
{
|
||||
this.log = new LogPrefixer(log, "(ChainState) ");
|
||||
this.contracts = contracts;
|
||||
handler = changeHandler;
|
||||
TotalSpan = timeRange;
|
||||
}
|
||||
|
||||
public static ChainState FromTimeRange(ILog log, ICodexContracts contracts, TimeRange timeRange, IChainStateChangeHandler changeHandler)
|
||||
{
|
||||
var events = ChainEvents.FromTimeRange(contracts, timeRange);
|
||||
return FromEvents(log, events, changeHandler);
|
||||
}
|
||||
|
||||
public static ChainState FromEvents(ILog log, ChainEvents events, IChainStateChangeHandler changeHandler)
|
||||
{
|
||||
var state = new ChainState(log, changeHandler, events.BlockInterval.TimeRange);
|
||||
state.Apply(events);
|
||||
return state;
|
||||
StartUtc = startUtc;
|
||||
TotalSpan = new TimeRange(startUtc, startUtc);
|
||||
}
|
||||
|
||||
public TimeRange TotalSpan { get; private set; }
|
||||
public IChainStateRequest[] Requests => requests.ToArray();
|
||||
|
||||
public void Update(ICodexContracts contracts)
|
||||
public DateTime StartUtc { get; }
|
||||
|
||||
public void Update()
|
||||
{
|
||||
Update(contracts, DateTime.UtcNow);
|
||||
Update(DateTime.UtcNow);
|
||||
}
|
||||
|
||||
public void Update(ICodexContracts contracts, DateTime toUtc)
|
||||
public void Update(DateTime toUtc)
|
||||
{
|
||||
var span = new TimeRange(TotalSpan.To, toUtc);
|
||||
var events = ChainEvents.FromTimeRange(contracts, span);
|
||||
|
|
|
@ -6,9 +6,8 @@ using Utils;
|
|||
|
||||
namespace TestNetRewarder
|
||||
{
|
||||
public class ChainState
|
||||
public class Keepers
|
||||
{
|
||||
private HistoricState historicState;
|
||||
private readonly string[] colorIcons = new[]
|
||||
{
|
||||
"🔴",
|
||||
|
@ -32,155 +31,5 @@ namespace TestNetRewarder
|
|||
"🔶",
|
||||
"🔷"
|
||||
};
|
||||
|
||||
public ChainState(HistoricState historicState, ICodexContracts contracts, BlockInterval blockRange)
|
||||
{
|
||||
this.historicState = historicState;
|
||||
|
||||
throw new Exception("This is getting a rewrite");
|
||||
|
||||
//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();
|
||||
//RequestFulfilledEvents = contracts.GetRequestFulfilledEvents(blockRange);
|
||||
//RequestCancelledEvents = contracts.GetRequestCancelledEvents(blockRange);
|
||||
//SlotFilledEvents = contracts.GetSlotFilledEvents(blockRange);
|
||||
//SlotFreedEvents = contracts.GetSlotFreedEvents(blockRange);
|
||||
}
|
||||
|
||||
public ChainState(
|
||||
Request[] newRequests,
|
||||
RequestFulfilledEventDTO[] requestFulfilledEvents,
|
||||
RequestCancelledEventDTO[] requestCancelledEvents,
|
||||
SlotFilledEventDTO[] slotFilledEvents,
|
||||
SlotFreedEventDTO[] slotFreedEvents)
|
||||
{
|
||||
NewRequests = newRequests;
|
||||
RequestFulfilledEvents = requestFulfilledEvents;
|
||||
RequestCancelledEvents = requestCancelledEvents;
|
||||
SlotFilledEvents = slotFilledEvents;
|
||||
SlotFreedEvents = slotFreedEvents;
|
||||
|
||||
historicState = new HistoricState();
|
||||
StartedRequests = Array.Empty<StorageRequest>();
|
||||
FinishedRequests = Array.Empty<StorageRequest>();
|
||||
}
|
||||
|
||||
public Request[] NewRequests { get; }
|
||||
[JsonIgnore]
|
||||
public StorageRequest[] AllRequests => historicState.StorageRequests;
|
||||
[JsonIgnore]
|
||||
public StorageRequest[] StartedRequests { get; private set; }
|
||||
[JsonIgnore]
|
||||
public StorageRequest[] FinishedRequests { get; private set; }
|
||||
public RequestFulfilledEventDTO[] RequestFulfilledEvents { get; }
|
||||
public RequestCancelledEventDTO[] RequestCancelledEvents { get; }
|
||||
public SlotFilledEventDTO[] SlotFilledEvents { get; }
|
||||
public SlotFreedEventDTO[] SlotFreedEvents { get; }
|
||||
|
||||
public string EntireString()
|
||||
{
|
||||
return
|
||||
$"ChainState=[{JsonConvert.SerializeObject(this)}]" +
|
||||
$"HistoricState=[{historicState.EntireString()}]";
|
||||
}
|
||||
|
||||
public void Set(HistoricState h)
|
||||
{
|
||||
historicState = h;
|
||||
}
|
||||
|
||||
public string[] GenerateOverview()
|
||||
{
|
||||
var entries = new List<StringBlockNumberPair>();
|
||||
|
||||
entries.AddRange(NewRequests.Select(ToPair));
|
||||
entries.AddRange(RequestFulfilledEvents.Select(ToPair));
|
||||
entries.AddRange(RequestCancelledEvents.Select(ToPair));
|
||||
entries.AddRange(SlotFilledEvents.Select(ToPair));
|
||||
entries.AddRange(SlotFreedEvents.Select(ToPair));
|
||||
entries.AddRange(FinishedRequests.Select(ToPair));
|
||||
|
||||
entries.Sort(new StringUtcComparer());
|
||||
|
||||
return entries.Select(ToLine).ToArray();
|
||||
}
|
||||
|
||||
private StringBlockNumberPair ToPair(Request r)
|
||||
{
|
||||
return new StringBlockNumberPair("NewRequest", JsonConvert.SerializeObject(r), r.Block, r.RequestId);
|
||||
}
|
||||
|
||||
public StringBlockNumberPair ToPair(StorageRequest r)
|
||||
{
|
||||
return new StringBlockNumberPair("FinishedRequest", JsonConvert.SerializeObject(r), r.Request.Block, r.Request.RequestId);
|
||||
}
|
||||
|
||||
private StringBlockNumberPair ToPair(RequestFulfilledEventDTO r)
|
||||
{
|
||||
return new StringBlockNumberPair("Fulfilled", JsonConvert.SerializeObject(r), r.Block, r.RequestId);
|
||||
}
|
||||
|
||||
private StringBlockNumberPair ToPair(RequestCancelledEventDTO r)
|
||||
{
|
||||
return new StringBlockNumberPair("Cancelled", JsonConvert.SerializeObject(r), r.Block, r.RequestId);
|
||||
}
|
||||
|
||||
private StringBlockNumberPair ToPair(SlotFilledEventDTO r)
|
||||
{
|
||||
return new StringBlockNumberPair("SlotFilled", JsonConvert.SerializeObject(r), r.Block, r.RequestId);
|
||||
}
|
||||
|
||||
private StringBlockNumberPair ToPair(SlotFreedEventDTO r)
|
||||
{
|
||||
return new StringBlockNumberPair("SlotFreed", JsonConvert.SerializeObject(r), r.Block, r.RequestId);
|
||||
}
|
||||
|
||||
private string ToLine(StringBlockNumberPair pair)
|
||||
{
|
||||
var nl = Environment.NewLine;
|
||||
var colorIcon = GetColorIcon(pair.RequestId);
|
||||
return $"{colorIcon} {pair.Block} ({pair.Name}){nl}" +
|
||||
$"```json{nl}" +
|
||||
$"{pair.Str}{nl}" +
|
||||
$"```";
|
||||
}
|
||||
|
||||
private string GetColorIcon(byte[] requestId)
|
||||
{
|
||||
var index = requestId[0] % colorIcons.Length;
|
||||
return colorIcons[index];
|
||||
}
|
||||
|
||||
public class StringBlockNumberPair
|
||||
{
|
||||
public StringBlockNumberPair(string name, string str, BlockTimeEntry block, byte[] requestId)
|
||||
{
|
||||
Name = name;
|
||||
Str = str;
|
||||
Block = block;
|
||||
RequestId = requestId;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
public string Str { get; }
|
||||
public BlockTimeEntry Block { get; }
|
||||
public byte[] RequestId { get; }
|
||||
}
|
||||
|
||||
public class StringUtcComparer : IComparer<StringBlockNumberPair>
|
||||
{
|
||||
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.Block.BlockNumber.CompareTo(y.Block.BlockNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,5 +40,14 @@ namespace TestNetRewarder
|
|||
return TimeSpan.FromMinutes(IntervalMinutes);
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime HistoryStartUtc
|
||||
{
|
||||
get
|
||||
{
|
||||
if (CheckHistoryTimestamp == 0) throw new Exception("'check-history' unix timestamp is required. Set it to the start/launch moment of the testnet.");
|
||||
return DateTimeOffset.FromUnixTimeSeconds(CheckHistoryTimestamp).UtcDateTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
using CodexContractsPlugin.Marketplace;
|
||||
using CodexContractsPlugin.ChainMonitor;
|
||||
using CodexContractsPlugin.Marketplace;
|
||||
using DiscordRewards;
|
||||
using System.Numerics;
|
||||
|
||||
namespace TestNetRewarder
|
||||
{
|
||||
public class MarketTracker
|
||||
public class MarketTracker : IChainStateChangeHandler
|
||||
{
|
||||
private readonly List<ChainState> buffer = new List<ChainState>();
|
||||
|
||||
|
@ -120,5 +121,40 @@ namespace TestNetRewarder
|
|||
}
|
||||
return Array.Empty<int>();
|
||||
}
|
||||
|
||||
public void OnNewRequest(IChainStateRequest request)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void OnRequestStarted(IChainStateRequest request)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void OnRequestFinished(IChainStateRequest request)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void OnRequestFulfilled(IChainStateRequest request)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void OnRequestCancelled(IChainStateRequest request)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void OnSlotFilled(IChainStateRequest request, BigInteger slotIndex)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void OnSlotFreed(IChainStateRequest request, BigInteger slotIndex)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,39 +1,37 @@
|
|||
using DiscordRewards;
|
||||
using CodexContractsPlugin;
|
||||
using CodexContractsPlugin.ChainMonitor;
|
||||
using DiscordRewards;
|
||||
using GethPlugin;
|
||||
using Logging;
|
||||
using Newtonsoft.Json;
|
||||
using System.Numerics;
|
||||
using Utils;
|
||||
|
||||
namespace TestNetRewarder
|
||||
{
|
||||
public class Processor
|
||||
public class Processor : ITimeSegmentHandler, IChainStateChangeHandler
|
||||
{
|
||||
private static readonly HistoricState historicState = new HistoricState();
|
||||
private static readonly RewardRepo rewardRepo = new RewardRepo();
|
||||
private static readonly MarketTracker marketTracker = new MarketTracker();
|
||||
private readonly RewardChecker rewardChecker = new RewardChecker();
|
||||
private readonly MarketTracker marketTracker = new MarketTracker();
|
||||
private readonly ChainState chainState;
|
||||
private readonly Configuration config;
|
||||
private readonly ILog log;
|
||||
private BlockInterval? lastBlockRange;
|
||||
|
||||
public Processor(ILog log)
|
||||
public Processor(Configuration config, ICodexContracts contracts, ILog log)
|
||||
{
|
||||
this.config = config;
|
||||
this.log = log;
|
||||
|
||||
chainState = new ChainState(log, contracts, this, config.HistoryStartUtc);
|
||||
}
|
||||
|
||||
public async Task ProcessTimeSegment(TimeRange timeRange)
|
||||
public async Task OnNewSegment(TimeRange timeRange)
|
||||
{
|
||||
var connector = GethConnector.GethConnector.Initialize(log);
|
||||
if (connector == null) throw new Exception("Invalid Geth information");
|
||||
|
||||
try
|
||||
{
|
||||
var blockRange = connector.GethNode.ConvertTimeRangeToBlockRange(timeRange);
|
||||
if (!IsNewBlockRange(blockRange))
|
||||
{
|
||||
log.Log($"Block range {blockRange} was previously processed. Skipping...");
|
||||
return;
|
||||
}
|
||||
chainState.Update(timeRange.To);
|
||||
|
||||
var chainState = new ChainState(historicState, connector.CodexContracts, blockRange);
|
||||
await ProcessChainState(chainState);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -43,19 +41,6 @@ namespace TestNetRewarder
|
|||
}
|
||||
}
|
||||
|
||||
private bool IsNewBlockRange(BlockInterval blockRange)
|
||||
{
|
||||
if (lastBlockRange == null ||
|
||||
lastBlockRange.From != blockRange.From ||
|
||||
lastBlockRange.To != blockRange.To)
|
||||
{
|
||||
lastBlockRange = blockRange;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task ProcessChainState(ChainState chainState)
|
||||
{
|
||||
log.Log(chainState.EntireString());
|
||||
|
@ -92,57 +77,39 @@ namespace TestNetRewarder
|
|||
return marketTracker.ProcessChainState(chainState);
|
||||
}
|
||||
|
||||
private async Task<bool> SendRewardsCommand(List<RewardUsersCommand> outgoingRewards, MarketAverage[] marketAverages, string[] eventsOverview)
|
||||
public void OnNewRequest(IChainStateRequest request)
|
||||
{
|
||||
var cmd = new GiveRewardsCommand
|
||||
{
|
||||
Rewards = outgoingRewards.ToArray(),
|
||||
Averages = marketAverages.ToArray(),
|
||||
EventsOverview = eventsOverview
|
||||
};
|
||||
|
||||
log.Debug("Sending rewards: " + JsonConvert.SerializeObject(cmd));
|
||||
return await Program.BotClient.SendRewards(cmd);
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private void ProcessReward(List<RewardUsersCommand> outgoingRewards, RewardConfig reward, ChainState chainState)
|
||||
public void OnRequestStarted(IChainStateRequest request)
|
||||
{
|
||||
var winningAddresses = PerformCheck(reward, chainState);
|
||||
foreach (var win in winningAddresses)
|
||||
{
|
||||
log.Log($"Address '{win.Address}' wins '{reward.Message}'");
|
||||
}
|
||||
if (winningAddresses.Any())
|
||||
{
|
||||
outgoingRewards.Add(new RewardUsersCommand
|
||||
{
|
||||
RewardId = reward.RoleId,
|
||||
UserAddresses = winningAddresses.Select(a => a.Address).ToArray()
|
||||
});
|
||||
}
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private EthAddress[] PerformCheck(RewardConfig reward, ChainState chainState)
|
||||
public void OnRequestFinished(IChainStateRequest request)
|
||||
{
|
||||
var check = GetCheck(reward.CheckConfig);
|
||||
return check.Check(chainState).Distinct().ToArray();
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private ICheck GetCheck(CheckConfig config)
|
||||
public void OnRequestFulfilled(IChainStateRequest request)
|
||||
{
|
||||
switch (config.Type)
|
||||
{
|
||||
case CheckType.FilledSlot:
|
||||
return new FilledAnySlotCheck();
|
||||
case CheckType.FinishedSlot:
|
||||
return new FinishedSlotCheck(config.MinSlotSize, config.MinDuration);
|
||||
case CheckType.PostedContract:
|
||||
return new PostedContractCheck(config.MinNumberOfHosts, config.MinSlotSize, config.MinDuration);
|
||||
case CheckType.StartedContract:
|
||||
return new StartedContractCheck(config.MinNumberOfHosts, config.MinSlotSize, config.MinDuration);
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
throw new Exception("Unknown check type: " + config.Type);
|
||||
public void OnRequestCancelled(IChainStateRequest request)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void OnSlotFilled(IChainStateRequest request, BigInteger slotIndex)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void OnSlotFreed(IChainStateRequest request, BigInteger slotIndex)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using ArgsUniform;
|
||||
using Logging;
|
||||
using Nethereum.Model;
|
||||
using Utils;
|
||||
|
||||
namespace TestNetRewarder
|
||||
|
@ -27,8 +28,11 @@ namespace TestNetRewarder
|
|||
new ConsoleLog()
|
||||
);
|
||||
|
||||
var connector = GethConnector.GethConnector.Initialize(Log);
|
||||
if (connector == null) throw new Exception("Invalid Geth information");
|
||||
|
||||
BotClient = new BotClient(Config, Log);
|
||||
processor = new Processor(Log);
|
||||
processor = new Processor(Config, connector.CodexContracts, Log);
|
||||
|
||||
EnsurePath(Config.DataPath);
|
||||
EnsurePath(Config.LogPath);
|
||||
|
@ -41,12 +45,12 @@ namespace TestNetRewarder
|
|||
EnsureGethOnline();
|
||||
|
||||
Log.Log("Starting TestNet Rewarder...");
|
||||
var segmenter = new TimeSegmenter(Log, Config);
|
||||
var segmenter = new TimeSegmenter(Log, Config, processor);
|
||||
|
||||
while (!CancellationToken.IsCancellationRequested)
|
||||
{
|
||||
await EnsureBotOnline();
|
||||
await segmenter.WaitForNextSegment(processor.ProcessTimeSegment);
|
||||
await segmenter.ProcessNextSegment();
|
||||
await Task.Delay(100, CancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
using CodexContractsPlugin.ChainMonitor;
|
||||
using DiscordRewards;
|
||||
using GethPlugin;
|
||||
using Nethereum.Model;
|
||||
using Newtonsoft.Json;
|
||||
using System.Numerics;
|
||||
|
||||
namespace TestNetRewarder
|
||||
{
|
||||
public class RewardChecker : IChainStateChangeHandler
|
||||
{
|
||||
private static readonly RewardRepo rewardRepo = new RewardRepo();
|
||||
|
||||
private async Task<bool> SendRewardsCommand(List<RewardUsersCommand> outgoingRewards, MarketAverage[] marketAverages, string[] eventsOverview)
|
||||
{
|
||||
var cmd = new GiveRewardsCommand
|
||||
{
|
||||
Rewards = outgoingRewards.ToArray(),
|
||||
Averages = marketAverages.ToArray(),
|
||||
EventsOverview = eventsOverview
|
||||
};
|
||||
|
||||
log.Debug("Sending rewards: " + JsonConvert.SerializeObject(cmd));
|
||||
return await Program.BotClient.SendRewards(cmd);
|
||||
}
|
||||
|
||||
private void ProcessReward(List<RewardUsersCommand> outgoingRewards, RewardConfig reward, ChainState chainState)
|
||||
{
|
||||
var winningAddresses = PerformCheck(reward, chainState);
|
||||
foreach (var win in winningAddresses)
|
||||
{
|
||||
log.Log($"Address '{win.Address}' wins '{reward.Message}'");
|
||||
}
|
||||
if (winningAddresses.Any())
|
||||
{
|
||||
outgoingRewards.Add(new RewardUsersCommand
|
||||
{
|
||||
RewardId = reward.RoleId,
|
||||
UserAddresses = winningAddresses.Select(a => a.Address).ToArray()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private EthAddress[] PerformCheck(RewardConfig reward, ChainState chainState)
|
||||
{
|
||||
var check = GetCheck(reward.CheckConfig);
|
||||
return check.Check(chainState).Distinct().ToArray();
|
||||
}
|
||||
|
||||
private ICheck GetCheck(CheckConfig config)
|
||||
{
|
||||
switch (config.Type)
|
||||
{
|
||||
case CheckType.FilledSlot:
|
||||
return new FilledAnySlotCheck();
|
||||
case CheckType.FinishedSlot:
|
||||
return new FinishedSlotCheck(config.MinSlotSize, config.MinDuration);
|
||||
case CheckType.PostedContract:
|
||||
return new PostedContractCheck(config.MinNumberOfHosts, config.MinSlotSize, config.MinDuration);
|
||||
case CheckType.StartedContract:
|
||||
return new StartedContractCheck(config.MinNumberOfHosts, config.MinSlotSize, config.MinDuration);
|
||||
}
|
||||
|
||||
throw new Exception("Unknown check type: " + config.Type);
|
||||
}
|
||||
|
||||
public void OnNewRequest(IChainStateRequest request)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void OnRequestStarted(IChainStateRequest request)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void OnRequestFinished(IChainStateRequest request)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void OnRequestFulfilled(IChainStateRequest request)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void OnRequestCancelled(IChainStateRequest request)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void OnSlotFilled(IChainStateRequest request, BigInteger slotIndex)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void OnSlotFreed(IChainStateRequest request, BigInteger slotIndex)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,51 +3,60 @@ using Utils;
|
|||
|
||||
namespace TestNetRewarder
|
||||
{
|
||||
public interface ITimeSegmentHandler
|
||||
{
|
||||
Task OnNewSegment(TimeRange timeRange);
|
||||
}
|
||||
|
||||
public class TimeSegmenter
|
||||
{
|
||||
private readonly ILog log;
|
||||
private readonly ITimeSegmentHandler handler;
|
||||
private readonly TimeSpan segmentSize;
|
||||
private DateTime start;
|
||||
private DateTime latest;
|
||||
|
||||
public TimeSegmenter(ILog log, Configuration configuration)
|
||||
public TimeSegmenter(ILog log, Configuration configuration, ITimeSegmentHandler handler)
|
||||
{
|
||||
this.log = log;
|
||||
|
||||
this.handler = handler;
|
||||
if (configuration.IntervalMinutes < 0) configuration.IntervalMinutes = 1;
|
||||
if (configuration.CheckHistoryTimestamp == 0) throw new Exception("'check-history' unix timestamp is required. Set it to the start/launch moment of the testnet.");
|
||||
|
||||
segmentSize = configuration.Interval;
|
||||
start = DateTimeOffset.FromUnixTimeSeconds(configuration.CheckHistoryTimestamp).UtcDateTime;
|
||||
latest = configuration.HistoryStartUtc;
|
||||
|
||||
log.Log("Starting time segments at " + start);
|
||||
log.Log("Starting time segments at " + latest);
|
||||
log.Log("Segment size: " + Time.FormatDuration(segmentSize));
|
||||
}
|
||||
|
||||
public async Task WaitForNextSegment(Func<TimeRange, Task> onSegment)
|
||||
public async Task ProcessNextSegment()
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
var end = start + segmentSize;
|
||||
var waited = false;
|
||||
if (end > now)
|
||||
{
|
||||
// Wait for the entire time segment to be in the past.
|
||||
var delay = end - now;
|
||||
waited = true;
|
||||
log.Log($"Waiting till time segment is in the past... {Time.FormatDuration(delay)}");
|
||||
await Task.Delay(delay, Program.CancellationToken);
|
||||
}
|
||||
await Task.Delay(TimeSpan.FromSeconds(3), Program.CancellationToken);
|
||||
var end = latest + segmentSize;
|
||||
var waited = await WaitUntilTimeSegmentInPast(end);
|
||||
|
||||
if (Program.CancellationToken.IsCancellationRequested) return;
|
||||
|
||||
var postfix = "(Catching up...)";
|
||||
if (waited) postfix = "(Real-time)";
|
||||
log.Log($"Time segment [{latest} to {end}] {postfix}");
|
||||
|
||||
log.Log($"Time segment [{start} to {end}] {postfix}");
|
||||
var range = new TimeRange(start, end);
|
||||
start = end;
|
||||
var range = new TimeRange(latest, end);
|
||||
latest = end;
|
||||
|
||||
await onSegment(range);
|
||||
await handler.OnNewSegment(range);
|
||||
}
|
||||
|
||||
private async Task<bool> WaitUntilTimeSegmentInPast(DateTime end)
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(3), Program.CancellationToken);
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
while (end > now)
|
||||
{
|
||||
var delay = (end - now) + TimeSpan.FromSeconds(3);
|
||||
await Task.Delay(delay, Program.CancellationToken);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue