Finish replace of old chainstate object in rewarder bot
This commit is contained in:
parent
c38a2242ba
commit
bed57dd35b
|
@ -13,9 +13,9 @@ namespace DiscordRewards
|
|||
public enum CheckType
|
||||
{
|
||||
Uninitialized,
|
||||
FilledSlot,
|
||||
FinishedSlot,
|
||||
PostedContract,
|
||||
StartedContract,
|
||||
HostFilledSlot,
|
||||
HostFinishedSlot,
|
||||
ClientPostedContract,
|
||||
ClientStartedContract,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
public RewardUsersCommand[] Rewards { get; set; } = Array.Empty<RewardUsersCommand>();
|
||||
public MarketAverage[] Averages { get; set; } = Array.Empty<MarketAverage>();
|
||||
public string[] EventsOverview { get; set; } = Array.Empty<string>();
|
||||
|
||||
public bool HasAny()
|
||||
{
|
||||
return Rewards.Any() || Averages.Any() || EventsOverview.Any();
|
||||
}
|
||||
}
|
||||
|
||||
public class RewardUsersCommand
|
||||
|
|
|
@ -11,19 +11,19 @@ namespace DiscordRewards
|
|||
// Filled any slot
|
||||
new RewardConfig(1187039439558541498, $"{Tag} successfully filled their first slot!", new CheckConfig
|
||||
{
|
||||
Type = CheckType.FilledSlot
|
||||
Type = CheckType.HostFilledSlot
|
||||
}),
|
||||
|
||||
// Finished any slot
|
||||
new RewardConfig(1202286165630390339, $"{Tag} successfully finished their first slot!", new CheckConfig
|
||||
{
|
||||
Type = CheckType.FinishedSlot
|
||||
Type = CheckType.HostFinishedSlot
|
||||
}),
|
||||
|
||||
// Finished a sizable slot
|
||||
new RewardConfig(1202286218738405418, $"{Tag} finished their first 1GB-24h slot! (10mb/5mins for test)", new CheckConfig
|
||||
{
|
||||
Type = CheckType.FinishedSlot,
|
||||
Type = CheckType.HostFinishedSlot,
|
||||
MinSlotSize = 10.MB(),
|
||||
MinDuration = TimeSpan.FromMinutes(5.0),
|
||||
}),
|
||||
|
@ -31,19 +31,19 @@ namespace DiscordRewards
|
|||
// Posted any contract
|
||||
new RewardConfig(1202286258370383913, $"{Tag} posted their first contract!", new CheckConfig
|
||||
{
|
||||
Type = CheckType.PostedContract
|
||||
Type = CheckType.ClientPostedContract
|
||||
}),
|
||||
|
||||
// Started any contract
|
||||
new RewardConfig(1202286330873126992, $"A contract created by {Tag} reached Started state for the first time!", new CheckConfig
|
||||
{
|
||||
Type = CheckType.StartedContract
|
||||
Type = CheckType.ClientStartedContract
|
||||
}),
|
||||
|
||||
// Started a sizable contract
|
||||
new RewardConfig(1202286381670608909, $"A large contract created by {Tag} reached Started state for the first time! (10mb/5mins for test)", new CheckConfig
|
||||
{
|
||||
Type = CheckType.StartedContract,
|
||||
Type = CheckType.ClientStartedContract,
|
||||
MinNumberOfHosts = 4,
|
||||
MinSlotSize = 10.MB(),
|
||||
MinDuration = TimeSpan.FromMinutes(5.0),
|
||||
|
|
|
@ -8,7 +8,6 @@ namespace CodexContractsPlugin.ChainMonitor
|
|||
public interface IChainStateChangeHandler
|
||||
{
|
||||
void OnNewRequest(IChainStateRequest request);
|
||||
void OnRequestStarted(IChainStateRequest request);
|
||||
void OnRequestFinished(IChainStateRequest request);
|
||||
void OnRequestFulfilled(IChainStateRequest request);
|
||||
void OnRequestCancelled(IChainStateRequest request);
|
||||
|
@ -115,6 +114,7 @@ namespace CodexContractsPlugin.ChainMonitor
|
|||
{
|
||||
var r = FindRequest(request.RequestId);
|
||||
if (r == null) return;
|
||||
r.Hosts.Add(request.Host, (int)request.SlotIndex);
|
||||
r.Log($"[{request.Block.BlockNumber}] SlotFilled");
|
||||
handler.OnSlotFilled(r, request.SlotIndex);
|
||||
}
|
||||
|
@ -123,6 +123,7 @@ namespace CodexContractsPlugin.ChainMonitor
|
|||
{
|
||||
var r = FindRequest(request.RequestId);
|
||||
if (r == null) return;
|
||||
r.Hosts.RemoveHost((int)request.SlotIndex);
|
||||
r.Log($"[{request.Block.BlockNumber}] SlotFreed");
|
||||
handler.OnSlotFreed(r, request.SlotIndex);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using CodexContractsPlugin.Marketplace;
|
||||
using GethPlugin;
|
||||
using Logging;
|
||||
|
||||
namespace CodexContractsPlugin.ChainMonitor
|
||||
|
@ -9,6 +10,8 @@ namespace CodexContractsPlugin.ChainMonitor
|
|||
RequestState State { get; }
|
||||
DateTime ExpiryUtc { get; }
|
||||
DateTime FinishedUtc { get; }
|
||||
EthAddress Client { get; }
|
||||
RequestHosts Hosts { get; }
|
||||
}
|
||||
|
||||
public class ChainStateRequest : IChainStateRequest
|
||||
|
@ -25,12 +28,17 @@ namespace CodexContractsPlugin.ChainMonitor
|
|||
FinishedUtc = request.Block.Utc + TimeSpan.FromSeconds((double)request.Ask.Duration);
|
||||
|
||||
Log($"[{request.Block.BlockNumber}] Created as {State}.");
|
||||
|
||||
Client = new EthAddress(request.Client);
|
||||
Hosts = new RequestHosts();
|
||||
}
|
||||
|
||||
public Request Request { get; }
|
||||
public RequestState State { get; private set; }
|
||||
public DateTime ExpiryUtc { get; }
|
||||
public DateTime FinishedUtc { get; }
|
||||
public EthAddress Client { get; }
|
||||
public RequestHosts Hosts { get; }
|
||||
|
||||
public void UpdateState(ulong blockNumber, RequestState newState)
|
||||
{
|
||||
|
@ -43,4 +51,30 @@ namespace CodexContractsPlugin.ChainMonitor
|
|||
log.Log($"Request '{Request.Id}': {msg}");
|
||||
}
|
||||
}
|
||||
|
||||
public class RequestHosts
|
||||
{
|
||||
private readonly Dictionary<int, EthAddress> hosts = new Dictionary<int, EthAddress>();
|
||||
|
||||
public void Add(EthAddress host, int index)
|
||||
{
|
||||
hosts.Add(index, host);
|
||||
}
|
||||
|
||||
public void RemoveHost(int index)
|
||||
{
|
||||
hosts.Remove(index);
|
||||
}
|
||||
|
||||
public EthAddress? GetHost(int index)
|
||||
{
|
||||
if (!hosts.ContainsKey(index)) return null;
|
||||
return hosts[index];
|
||||
}
|
||||
|
||||
public EthAddress[] GetHosts()
|
||||
{
|
||||
return hosts.Values.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,10 +20,6 @@ namespace CodexContractsPlugin.ChainMonitor
|
|||
{
|
||||
}
|
||||
|
||||
public void OnRequestStarted(IChainStateRequest request)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnSlotFilled(IChainStateRequest request, BigInteger slotIndex)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -39,27 +39,24 @@ namespace CodexTests.UtilityTests
|
|||
var client = StartClient(geth, contracts);
|
||||
|
||||
var events = ChainEvents.FromTimeRange(contracts, GetTestRunTimeRange());
|
||||
var chainState = ChainState.FromEvents(
|
||||
GetTestLog(),
|
||||
events,
|
||||
new DoNothingChainEventHandler());
|
||||
var chainState = new ChainState(GetTestLog(), contracts, new DoNothingChainEventHandler(), GetTestRunTimeRange().From);
|
||||
|
||||
var apiCalls = new RewardApiCalls(Ci, botContainer);
|
||||
apiCalls.Start(OnCommand);
|
||||
|
||||
var purchaseContract = ClientPurchasesStorage(client);
|
||||
chainState.Update(contracts);
|
||||
chainState.Update();
|
||||
Assert.That(chainState.Requests.Length, Is.EqualTo(1));
|
||||
|
||||
purchaseContract.WaitForStorageContractStarted();
|
||||
chainState.Update(contracts);
|
||||
chainState.Update();
|
||||
|
||||
purchaseContract.WaitForStorageContractFinished();
|
||||
|
||||
Thread.Sleep(rewarderInterval * 2);
|
||||
|
||||
apiCalls.Stop();
|
||||
chainState.Update(contracts);
|
||||
chainState.Update();
|
||||
|
||||
foreach (var r in repo.Rewards)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
using Logging;
|
||||
|
||||
namespace TestNetRewarder
|
||||
{
|
||||
public class BufferLogger : ILog
|
||||
{
|
||||
private readonly List<string> lines = new List<string>();
|
||||
|
||||
public void AddStringReplace(string from, string to)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public LogFile CreateSubfile(string ext = "log")
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Debug(string message = "", int skipFrames = 0)
|
||||
{
|
||||
lines.Add(message);
|
||||
Trim();
|
||||
}
|
||||
|
||||
public void Error(string message)
|
||||
{
|
||||
lines.Add($"Error: {message}");
|
||||
Trim();
|
||||
}
|
||||
|
||||
public void Log(string message)
|
||||
{
|
||||
lines.Add(message);
|
||||
Trim();
|
||||
}
|
||||
|
||||
public string[] Get()
|
||||
{
|
||||
var result = lines.ToArray();
|
||||
lines.Clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
private void Trim()
|
||||
{
|
||||
if (lines.Count > 100)
|
||||
{
|
||||
lines.RemoveRange(0, 50);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
using CodexContractsPlugin.ChainMonitor;
|
||||
using System.Numerics;
|
||||
|
||||
namespace TestNetRewarder
|
||||
{
|
||||
public class ChainChangeMux : IChainStateChangeHandler
|
||||
{
|
||||
private readonly IChainStateChangeHandler[] handlers;
|
||||
|
||||
public ChainChangeMux(params IChainStateChangeHandler[] handlers)
|
||||
{
|
||||
this.handlers = handlers;
|
||||
}
|
||||
|
||||
public void OnNewRequest(IChainStateRequest request)
|
||||
{
|
||||
foreach (var handler in handlers) handler.OnNewRequest(request);
|
||||
}
|
||||
|
||||
public void OnRequestCancelled(IChainStateRequest request)
|
||||
{
|
||||
foreach (var handler in handlers) handler.OnRequestCancelled(request);
|
||||
}
|
||||
|
||||
public void OnRequestFinished(IChainStateRequest request)
|
||||
{
|
||||
foreach (var handler in handlers) handler.OnRequestFinished(request);
|
||||
}
|
||||
|
||||
public void OnRequestFulfilled(IChainStateRequest request)
|
||||
{
|
||||
foreach (var handler in handlers) handler.OnRequestFulfilled(request);
|
||||
}
|
||||
|
||||
public void OnSlotFilled(IChainStateRequest request, BigInteger slotIndex)
|
||||
{
|
||||
foreach (var handler in handlers) handler.OnSlotFilled(request, slotIndex);
|
||||
}
|
||||
|
||||
public void OnSlotFreed(IChainStateRequest request, BigInteger slotIndex)
|
||||
{
|
||||
foreach (var handler in handlers) handler.OnSlotFreed(request, slotIndex);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
using CodexContractsPlugin;
|
||||
using CodexContractsPlugin.Marketplace;
|
||||
using NethereumWorkflow.BlockUtils;
|
||||
using Newtonsoft.Json;
|
||||
using Utils;
|
||||
|
||||
namespace TestNetRewarder
|
||||
{
|
||||
public class Keepers
|
||||
{
|
||||
private readonly string[] colorIcons = new[]
|
||||
{
|
||||
"🔴",
|
||||
"🟠",
|
||||
"🟡",
|
||||
"🟢",
|
||||
"🔵",
|
||||
"🟣",
|
||||
"🟤",
|
||||
"⚫",
|
||||
"⚪",
|
||||
"🟥",
|
||||
"🟧",
|
||||
"🟨",
|
||||
"🟩",
|
||||
"🟦",
|
||||
"🟪",
|
||||
"🟫",
|
||||
"⬛",
|
||||
"⬜",
|
||||
"🔶",
|
||||
"🔷"
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,141 +0,0 @@
|
|||
using CodexContractsPlugin.Marketplace;
|
||||
using GethPlugin;
|
||||
using NethereumWorkflow;
|
||||
using Utils;
|
||||
|
||||
namespace TestNetRewarder
|
||||
{
|
||||
public interface ICheck
|
||||
{
|
||||
EthAddress[] Check(ChainState state);
|
||||
}
|
||||
|
||||
public class FilledAnySlotCheck : ICheck
|
||||
{
|
||||
public EthAddress[] Check(ChainState state)
|
||||
{
|
||||
return state.SlotFilledEvents.Select(e => e.Host).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public class FinishedSlotCheck : ICheck
|
||||
{
|
||||
private readonly ByteSize minSize;
|
||||
private readonly TimeSpan minDuration;
|
||||
|
||||
public FinishedSlotCheck(ByteSize minSize, TimeSpan minDuration)
|
||||
{
|
||||
this.minSize = minSize;
|
||||
this.minDuration = minDuration;
|
||||
}
|
||||
|
||||
public EthAddress[] Check(ChainState state)
|
||||
{
|
||||
return state.FinishedRequests
|
||||
.Where(r =>
|
||||
MeetsSizeRequirement(r) &&
|
||||
MeetsDurationRequirement(r))
|
||||
.SelectMany(r => r.Hosts)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private bool MeetsSizeRequirement(StorageRequest r)
|
||||
{
|
||||
var slotSize = r.Request.Ask.SlotSize.ToDecimal();
|
||||
decimal min = minSize.SizeInBytes;
|
||||
return slotSize >= min;
|
||||
}
|
||||
|
||||
private bool MeetsDurationRequirement(StorageRequest r)
|
||||
{
|
||||
var duration = TimeSpan.FromSeconds((double)r.Request.Ask.Duration);
|
||||
return duration >= minDuration;
|
||||
}
|
||||
}
|
||||
|
||||
public class PostedContractCheck : ICheck
|
||||
{
|
||||
private readonly ulong minNumberOfHosts;
|
||||
private readonly ByteSize minSlotSize;
|
||||
private readonly TimeSpan minDuration;
|
||||
|
||||
public PostedContractCheck(ulong minNumberOfHosts, ByteSize minSlotSize, TimeSpan minDuration)
|
||||
{
|
||||
this.minNumberOfHosts = minNumberOfHosts;
|
||||
this.minSlotSize = minSlotSize;
|
||||
this.minDuration = minDuration;
|
||||
}
|
||||
|
||||
public EthAddress[] Check(ChainState state)
|
||||
{
|
||||
return state.NewRequests
|
||||
.Where(r =>
|
||||
MeetsNumSlotsRequirement(r) &&
|
||||
MeetsSizeRequirement(r) &&
|
||||
MeetsDurationRequirement(r))
|
||||
.Select(r => r.ClientAddress)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private bool MeetsNumSlotsRequirement(Request r)
|
||||
{
|
||||
return r.Ask.Slots >= minNumberOfHosts;
|
||||
}
|
||||
|
||||
private bool MeetsSizeRequirement(Request r)
|
||||
{
|
||||
var slotSize = r.Ask.SlotSize.ToDecimal();
|
||||
decimal min = minSlotSize.SizeInBytes;
|
||||
return slotSize >= min;
|
||||
}
|
||||
|
||||
private bool MeetsDurationRequirement(Request r)
|
||||
{
|
||||
var duration = TimeSpan.FromSeconds((double)r.Ask.Duration);
|
||||
return duration >= minDuration;
|
||||
}
|
||||
}
|
||||
|
||||
public class StartedContractCheck : ICheck
|
||||
{
|
||||
private readonly ulong minNumberOfHosts;
|
||||
private readonly ByteSize minSlotSize;
|
||||
private readonly TimeSpan minDuration;
|
||||
|
||||
public StartedContractCheck(ulong minNumberOfHosts, ByteSize minSlotSize, TimeSpan minDuration)
|
||||
{
|
||||
this.minNumberOfHosts = minNumberOfHosts;
|
||||
this.minSlotSize = minSlotSize;
|
||||
this.minDuration = minDuration;
|
||||
}
|
||||
|
||||
public EthAddress[] Check(ChainState state)
|
||||
{
|
||||
return state.StartedRequests
|
||||
.Where(r =>
|
||||
MeetsNumSlotsRequirement(r) &&
|
||||
MeetsSizeRequirement(r) &&
|
||||
MeetsDurationRequirement(r))
|
||||
.Select(r => r.Request.ClientAddress)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private bool MeetsNumSlotsRequirement(StorageRequest r)
|
||||
{
|
||||
return r.Request.Ask.Slots >= minNumberOfHosts;
|
||||
}
|
||||
|
||||
private bool MeetsSizeRequirement(StorageRequest r)
|
||||
{
|
||||
var slotSize = r.Request.Ask.SlotSize.ToDecimal();
|
||||
decimal min = minSlotSize.SizeInBytes;
|
||||
return slotSize >= min;
|
||||
}
|
||||
|
||||
private bool MeetsDurationRequirement(StorageRequest r)
|
||||
{
|
||||
var duration = TimeSpan.FromSeconds((double)r.Request.Ask.Duration);
|
||||
return duration >= minDuration;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
using CodexContractsPlugin.ChainMonitor;
|
||||
using CodexContractsPlugin.Marketplace;
|
||||
using DiscordRewards;
|
||||
using System.Numerics;
|
||||
|
||||
namespace TestNetRewarder
|
||||
{
|
||||
public class MarketBuffer
|
||||
{
|
||||
private readonly List<IChainStateRequest> requests = new List<IChainStateRequest>();
|
||||
private readonly TimeSpan bufferSpan;
|
||||
|
||||
public MarketBuffer(TimeSpan bufferSpan)
|
||||
{
|
||||
this.bufferSpan = bufferSpan;
|
||||
}
|
||||
|
||||
public void Add(IChainStateRequest request)
|
||||
{
|
||||
requests.Add(request);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
requests.RemoveAll(r => (now - r.FinishedUtc) > bufferSpan);
|
||||
}
|
||||
|
||||
public MarketAverage? GetAverage()
|
||||
{
|
||||
if (requests.Count == 0) return null;
|
||||
|
||||
return new MarketAverage
|
||||
{
|
||||
NumberOfFinished = requests.Count,
|
||||
TimeRangeSeconds = (int)bufferSpan.TotalSeconds,
|
||||
Price = Average(s => s.Request.Ask.Reward),
|
||||
Duration = Average(s => s.Request.Ask.Duration),
|
||||
Size = Average(s => GetTotalSize(s.Request.Ask)),
|
||||
Collateral = Average(s => s.Request.Ask.Collateral),
|
||||
ProofProbability = Average(s => s.Request.Ask.ProofProbability)
|
||||
};
|
||||
}
|
||||
|
||||
private float Average(Func<IChainStateRequest, BigInteger> getValue)
|
||||
{
|
||||
return Average(s => Convert.ToInt32(getValue(s)));
|
||||
}
|
||||
|
||||
private float Average(Func<IChainStateRequest, int> getValue)
|
||||
{
|
||||
var sum = 0.0f;
|
||||
float count = requests.Count;
|
||||
foreach (var r in requests)
|
||||
{
|
||||
sum += getValue(r);
|
||||
}
|
||||
|
||||
if (count < 1.0f) return 0.0f;
|
||||
return sum / count;
|
||||
}
|
||||
|
||||
private int GetTotalSize(Ask ask)
|
||||
{
|
||||
var nSlots = Convert.ToInt32(ask.Slots);
|
||||
var slotSize = Convert.ToInt32(ask.SlotSize);
|
||||
return nSlots * slotSize;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,160 +1,73 @@
|
|||
using CodexContractsPlugin.ChainMonitor;
|
||||
using CodexContractsPlugin.Marketplace;
|
||||
using DiscordRewards;
|
||||
using Logging;
|
||||
using System.Numerics;
|
||||
|
||||
namespace TestNetRewarder
|
||||
{
|
||||
public class MarketTracker : IChainStateChangeHandler
|
||||
{
|
||||
private readonly List<ChainState> buffer = new List<ChainState>();
|
||||
private readonly List<MarketBuffer> buffers = new List<MarketBuffer>();
|
||||
private readonly ILog log;
|
||||
|
||||
public MarketAverage[] ProcessChainState(ChainState chainState)
|
||||
public MarketTracker(Configuration config, ILog log)
|
||||
{
|
||||
var intervalCounts = GetInsightCounts();
|
||||
if (!intervalCounts.Any()) return Array.Empty<MarketAverage>();
|
||||
var intervals = GetInsightCounts(config);
|
||||
|
||||
UpdateBuffer(chainState, intervalCounts.Max());
|
||||
var result = intervalCounts
|
||||
.Select(GenerateMarketAverage)
|
||||
.Where(a => a != null)
|
||||
.Cast<MarketAverage>()
|
||||
.ToArray();
|
||||
|
||||
if (!result.Any()) result = Array.Empty<MarketAverage>();
|
||||
return result;
|
||||
}
|
||||
|
||||
private void UpdateBuffer(ChainState chainState, int maxNumberOfIntervals)
|
||||
{
|
||||
buffer.Add(chainState);
|
||||
while (buffer.Count > maxNumberOfIntervals)
|
||||
foreach (var i in intervals)
|
||||
{
|
||||
buffer.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
private MarketAverage? GenerateMarketAverage(int numberOfIntervals)
|
||||
{
|
||||
var states = SelectStates(numberOfIntervals);
|
||||
return CreateAverage(states);
|
||||
}
|
||||
|
||||
private ChainState[] SelectStates(int numberOfIntervals)
|
||||
{
|
||||
if (numberOfIntervals < 1) return Array.Empty<ChainState>();
|
||||
if (numberOfIntervals > buffer.Count) return Array.Empty<ChainState>();
|
||||
return buffer.TakeLast(numberOfIntervals).ToArray();
|
||||
}
|
||||
|
||||
private MarketAverage? CreateAverage(ChainState[] states)
|
||||
{
|
||||
try
|
||||
{
|
||||
return new MarketAverage
|
||||
{
|
||||
NumberOfFinished = CountNumberOfFinishedRequests(states),
|
||||
TimeRangeSeconds = GetTotalTimeRange(states),
|
||||
Price = Average(states, s => s.Request.Ask.Reward),
|
||||
Duration = Average(states, s => s.Request.Ask.Duration),
|
||||
Size = Average(states, s => GetTotalSize(s.Request.Ask)),
|
||||
Collateral = Average(states, s => s.Request.Ask.Collateral),
|
||||
ProofProbability = Average(states, s => s.Request.Ask.ProofProbability)
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Program.Log.Error($"Exception in CreateAverage: {ex}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private int GetTotalSize(Ask ask)
|
||||
{
|
||||
var nSlots = Convert.ToInt32(ask.Slots);
|
||||
var slotSize = Convert.ToInt32(ask.SlotSize);
|
||||
return nSlots * slotSize;
|
||||
}
|
||||
|
||||
private float Average(ChainState[] states, Func<StorageRequest, BigInteger> getValue)
|
||||
{
|
||||
return Average(states, s => Convert.ToInt32(getValue(s)));
|
||||
}
|
||||
|
||||
private float Average(ChainState[] states, Func<StorageRequest, int> getValue)
|
||||
{
|
||||
var sum = 0.0f;
|
||||
var count = 0.0f;
|
||||
foreach (var state in states)
|
||||
{
|
||||
foreach (var finishedRequest in state.FinishedRequests)
|
||||
{
|
||||
sum += getValue(finishedRequest);
|
||||
count++;
|
||||
}
|
||||
buffers.Add(new MarketBuffer(
|
||||
config.Interval * i
|
||||
));
|
||||
}
|
||||
|
||||
if (count < 1.0f) return 0.0f;
|
||||
return sum / count;
|
||||
this.log = log;
|
||||
}
|
||||
|
||||
private int GetTotalTimeRange(ChainState[] states)
|
||||
public MarketAverage[] GetAverages()
|
||||
{
|
||||
return Convert.ToInt32((Program.Config.Interval * states.Length).TotalSeconds);
|
||||
}
|
||||
foreach (var b in buffers) b.Update();
|
||||
|
||||
private int CountNumberOfFinishedRequests(ChainState[] states)
|
||||
{
|
||||
return states.Sum(s => s.FinishedRequests.Length);
|
||||
}
|
||||
|
||||
private int[] GetInsightCounts()
|
||||
{
|
||||
try
|
||||
{
|
||||
var tokens = Program.Config.MarketInsights.Split(';').ToArray();
|
||||
return tokens.Select(t => Convert.ToInt32(t)).ToArray();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Program.Log.Error($"Exception when parsing MarketInsights config parameters: {ex}");
|
||||
}
|
||||
return Array.Empty<int>();
|
||||
return buffers.Select(b => b.GetAverage()).Where(a => a != null).Cast<MarketAverage>().ToArray();
|
||||
}
|
||||
|
||||
public void OnNewRequest(IChainStateRequest request)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void OnRequestStarted(IChainStateRequest request)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void OnRequestFinished(IChainStateRequest request)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
foreach (var b in buffers) b.Add(request);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
private int[] GetInsightCounts(Configuration config)
|
||||
{
|
||||
try
|
||||
{
|
||||
var tokens = config.MarketInsights.Split(';').ToArray();
|
||||
return tokens.Select(t => Convert.ToInt32(t)).ToArray();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.Error($"Exception when parsing MarketInsights config parameters: {ex}");
|
||||
}
|
||||
return Array.Empty<int>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,29 +1,36 @@
|
|||
using CodexContractsPlugin;
|
||||
using CodexContractsPlugin.ChainMonitor;
|
||||
using DiscordRewards;
|
||||
using GethPlugin;
|
||||
using Logging;
|
||||
using Newtonsoft.Json;
|
||||
using System.Numerics;
|
||||
using Utils;
|
||||
|
||||
namespace TestNetRewarder
|
||||
{
|
||||
public class Processor : ITimeSegmentHandler, IChainStateChangeHandler
|
||||
public class Processor : ITimeSegmentHandler
|
||||
{
|
||||
private readonly RewardChecker rewardChecker = new RewardChecker();
|
||||
private readonly MarketTracker marketTracker = new MarketTracker();
|
||||
private readonly RequestBuilder builder;
|
||||
private readonly RewardChecker rewardChecker;
|
||||
private readonly MarketTracker marketTracker;
|
||||
private readonly BufferLogger bufferLogger;
|
||||
private readonly ChainState chainState;
|
||||
private readonly Configuration config;
|
||||
private readonly BotClient client;
|
||||
private readonly ILog log;
|
||||
private BlockInterval? lastBlockRange;
|
||||
|
||||
public Processor(Configuration config, ICodexContracts contracts, ILog log)
|
||||
public Processor(Configuration config, BotClient client, ICodexContracts contracts, ILog log)
|
||||
{
|
||||
this.config = config;
|
||||
this.client = client;
|
||||
this.log = log;
|
||||
|
||||
chainState = new ChainState(log, contracts, this, config.HistoryStartUtc);
|
||||
builder = new RequestBuilder();
|
||||
rewardChecker = new RewardChecker(builder);
|
||||
marketTracker = new MarketTracker(config, log);
|
||||
bufferLogger = new BufferLogger();
|
||||
|
||||
var handler = new ChainChangeMux(
|
||||
rewardChecker.Handler,
|
||||
marketTracker
|
||||
);
|
||||
|
||||
chainState = new ChainState(new LogSplitter(log, bufferLogger), contracts, handler, config.HistoryStartUtc);
|
||||
}
|
||||
|
||||
public async Task OnNewSegment(TimeRange timeRange)
|
||||
|
@ -31,8 +38,15 @@ namespace TestNetRewarder
|
|||
try
|
||||
{
|
||||
chainState.Update(timeRange.To);
|
||||
|
||||
await ProcessChainState(chainState);
|
||||
|
||||
var averages = marketTracker.GetAverages();
|
||||
var lines = bufferLogger.Get();
|
||||
|
||||
var request = builder.Build(averages, lines);
|
||||
if (request.HasAny())
|
||||
{
|
||||
await client.SendRewards(request);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -40,76 +54,5 @@ namespace TestNetRewarder
|
|||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessChainState(ChainState chainState)
|
||||
{
|
||||
log.Log(chainState.EntireString());
|
||||
|
||||
var outgoingRewards = new List<RewardUsersCommand>();
|
||||
foreach (var reward in rewardRepo.Rewards)
|
||||
{
|
||||
ProcessReward(outgoingRewards, reward, chainState);
|
||||
}
|
||||
|
||||
var marketAverages = GetMarketAverages(chainState);
|
||||
var eventsOverview = GenerateEventsOverview(chainState);
|
||||
|
||||
log.Log($"Found {outgoingRewards.Count} rewards. " +
|
||||
$"Found {marketAverages.Length} market averages. " +
|
||||
$"Found {eventsOverview.Length} events.");
|
||||
|
||||
if (outgoingRewards.Any() || marketAverages.Any() || eventsOverview.Any())
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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,16 +1,15 @@
|
|||
using ArgsUniform;
|
||||
using Logging;
|
||||
using Nethereum.Model;
|
||||
using Utils;
|
||||
|
||||
namespace TestNetRewarder
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static Configuration Config { get; private set; } = null!;
|
||||
public static ILog Log { get; private set; } = null!;
|
||||
public static CancellationToken CancellationToken { get; private set; }
|
||||
public static BotClient BotClient { get; private set; } = null!;
|
||||
public static CancellationToken CancellationToken;
|
||||
private static Configuration Config = null!;
|
||||
private static ILog Log = null!;
|
||||
private static BotClient BotClient = null!;
|
||||
private static Processor processor = null!;
|
||||
private static DateTime lastCheck = DateTime.MinValue;
|
||||
|
||||
|
@ -32,7 +31,7 @@ namespace TestNetRewarder
|
|||
if (connector == null) throw new Exception("Invalid Geth information");
|
||||
|
||||
BotClient = new BotClient(Config, Log);
|
||||
processor = new Processor(Config, connector.CodexContracts, Log);
|
||||
processor = new Processor(Config, BotClient, connector.CodexContracts, Log);
|
||||
|
||||
EnsurePath(Config.DataPath);
|
||||
EnsurePath(Config.LogPath);
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
using DiscordRewards;
|
||||
using GethPlugin;
|
||||
|
||||
namespace TestNetRewarder
|
||||
{
|
||||
public class RequestBuilder : IRewardGiver
|
||||
{
|
||||
private readonly Dictionary<ulong, List<EthAddress>> rewards = new Dictionary<ulong, List<EthAddress>>();
|
||||
|
||||
public void Give(RewardConfig reward, EthAddress receiver)
|
||||
{
|
||||
if (rewards.ContainsKey(reward.RoleId))
|
||||
{
|
||||
rewards[reward.RoleId].Add(receiver);
|
||||
}
|
||||
else
|
||||
{
|
||||
rewards.Add(reward.RoleId, new List<EthAddress> { receiver });
|
||||
}
|
||||
}
|
||||
|
||||
public GiveRewardsCommand Build(MarketAverage[] marketAverages, string[] lines)
|
||||
{
|
||||
var result = new GiveRewardsCommand
|
||||
{
|
||||
Rewards = rewards.Select(p => new RewardUsersCommand
|
||||
{
|
||||
RewardId = p.Key,
|
||||
UserAddresses = p.Value.Select(v => v.Address).ToArray()
|
||||
}).ToArray(),
|
||||
Averages = marketAverages,
|
||||
EventsOverview = lines
|
||||
};
|
||||
|
||||
rewards.Clear();
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
using CodexContractsPlugin.ChainMonitor;
|
||||
using DiscordRewards;
|
||||
using GethPlugin;
|
||||
using NethereumWorkflow;
|
||||
using System.Numerics;
|
||||
|
||||
namespace TestNetRewarder
|
||||
{
|
||||
public interface IRewardGiver
|
||||
{
|
||||
void Give(RewardConfig reward, EthAddress receiver);
|
||||
}
|
||||
|
||||
public class RewardCheck : IChainStateChangeHandler
|
||||
{
|
||||
private readonly RewardConfig reward;
|
||||
private readonly IRewardGiver giver;
|
||||
|
||||
public RewardCheck(RewardConfig reward, IRewardGiver giver)
|
||||
{
|
||||
this.reward = reward;
|
||||
this.giver = giver;
|
||||
}
|
||||
|
||||
public void OnNewRequest(IChainStateRequest request)
|
||||
{
|
||||
if (MeetsRequirements(CheckType.ClientPostedContract, request))
|
||||
{
|
||||
GiveReward(reward, request.Client);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnRequestCancelled(IChainStateRequest request)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnRequestFinished(IChainStateRequest request)
|
||||
{
|
||||
if (MeetsRequirements(CheckType.HostFinishedSlot, request))
|
||||
{
|
||||
foreach (var host in request.Hosts.GetHosts())
|
||||
{
|
||||
GiveReward(reward, host);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnRequestFulfilled(IChainStateRequest request)
|
||||
{
|
||||
if (MeetsRequirements(CheckType.ClientStartedContract, request))
|
||||
{
|
||||
GiveReward(reward, request.Client);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnSlotFilled(IChainStateRequest request, BigInteger slotIndex)
|
||||
{
|
||||
if (MeetsRequirements(CheckType.HostFilledSlot, request))
|
||||
{
|
||||
var host = request.Hosts.GetHost((int)slotIndex);
|
||||
if (host != null)
|
||||
{
|
||||
GiveReward(reward, host);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnSlotFreed(IChainStateRequest request, BigInteger slotIndex)
|
||||
{
|
||||
}
|
||||
|
||||
private void GiveReward(RewardConfig reward, EthAddress receiver)
|
||||
{
|
||||
giver.Give(reward, receiver);
|
||||
}
|
||||
|
||||
private bool MeetsRequirements(CheckType type, IChainStateRequest request)
|
||||
{
|
||||
return
|
||||
reward.CheckConfig.Type == type &&
|
||||
MeetsDurationRequirement(request) &&
|
||||
MeetsSizeRequirement(request);
|
||||
}
|
||||
|
||||
private bool MeetsSizeRequirement(IChainStateRequest r)
|
||||
{
|
||||
var slotSize = r.Request.Ask.SlotSize.ToDecimal();
|
||||
decimal min = reward.CheckConfig.MinSlotSize.SizeInBytes;
|
||||
return slotSize >= min;
|
||||
}
|
||||
|
||||
private bool MeetsDurationRequirement(IChainStateRequest r)
|
||||
{
|
||||
var duration = TimeSpan.FromSeconds((double)r.Request.Ask.Duration);
|
||||
return duration >= reward.CheckConfig.MinDuration;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,102 +1,17 @@
|
|||
using CodexContractsPlugin.ChainMonitor;
|
||||
using DiscordRewards;
|
||||
using GethPlugin;
|
||||
using Nethereum.Model;
|
||||
using Newtonsoft.Json;
|
||||
using System.Numerics;
|
||||
|
||||
namespace TestNetRewarder
|
||||
{
|
||||
public class RewardChecker : IChainStateChangeHandler
|
||||
public class RewardChecker
|
||||
{
|
||||
private static readonly RewardRepo rewardRepo = new RewardRepo();
|
||||
|
||||
private async Task<bool> SendRewardsCommand(List<RewardUsersCommand> outgoingRewards, MarketAverage[] marketAverages, string[] eventsOverview)
|
||||
public RewardChecker(IRewardGiver giver)
|
||||
{
|
||||
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);
|
||||
var repo = new RewardRepo();
|
||||
var checks = repo.Rewards.Select(r => new RewardCheck(r, giver)).ToArray();
|
||||
Handler = new ChainChangeMux(checks);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
public IChainStateChangeHandler Handler { get; }
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue