2
0
mirror of synced 2025-01-11 17:14:25 +00:00

Debugging time segmenter

This commit is contained in:
benbierens 2024-02-19 14:56:49 +01:00
parent 3c210f96fc
commit da4101a042
No known key found for this signature in database
GPG Key ID: 877D2C2E09A22F3A
14 changed files with 161 additions and 137 deletions

View File

@ -52,7 +52,13 @@ namespace NethereumWorkflow
return closestBefore.BlockNumber; return closestBefore.BlockNumber;
} }
FetchBlocksAround(moment); var newBlocks = FetchBlocksAround(moment);
if (newBlocks == 0)
{
log.Log("Didn't find any new blocks.");
if (closestBefore != null) return closestBefore.BlockNumber;
throw new Exception("Failed to find highest before.");
}
return GetHighestBlockBefore(moment); return GetHighestBlockBefore(moment);
} }
@ -71,11 +77,17 @@ namespace NethereumWorkflow
return closestAfter.BlockNumber; return closestAfter.BlockNumber;
} }
FetchBlocksAround(moment); var newBlocks = FetchBlocksAround(moment);
if (newBlocks == 0)
{
log.Log("Didn't find any new blocks.");
if (closestAfter != null) return closestAfter.BlockNumber;
throw new Exception("Failed to find lowest before.");
}
return GetLowestBlockAfter(moment); return GetLowestBlockAfter(moment);
} }
private void FetchBlocksAround(DateTime moment) private int FetchBlocksAround(DateTime moment)
{ {
var timePerBlock = EstimateTimePerBlock(); var timePerBlock = EstimateTimePerBlock();
log.Debug("Fetching blocks around " + moment.ToString("o") + " timePerBlock: " + timePerBlock.TotalSeconds); log.Debug("Fetching blocks around " + moment.ToString("o") + " timePerBlock: " + timePerBlock.TotalSeconds);
@ -85,42 +97,55 @@ namespace NethereumWorkflow
var max = entries.Keys.Max(); var max = entries.Keys.Max();
var blockDifference = CalculateBlockDifference(moment, timePerBlock, max); var blockDifference = CalculateBlockDifference(moment, timePerBlock, max);
FetchUp(max, blockDifference); return
FetchDown(max, blockDifference); FetchUp(max, blockDifference) +
FetchDown(max, blockDifference);
} }
private void FetchDown(ulong max, ulong blockDifference) private int FetchDown(ulong max, ulong blockDifference)
{ {
var target = max - blockDifference - 1; var target = GetTarget(max, blockDifference);
var fetchDown = FetchRange; var fetchDown = FetchRange;
var newBlocks = 0;
while (fetchDown > 0) while (fetchDown > 0)
{ {
if (!entries.ContainsKey(target)) if (!entries.ContainsKey(target))
{ {
var newBlock = AddBlockNumber(target); var newBlock = AddBlockNumber("FD" + fetchDown, target);
if (newBlock == null) return; if (newBlock == null) return newBlocks;
newBlocks++;
fetchDown--; fetchDown--;
} }
target--; target--;
if (target <= 0) return; if (target <= 0) return newBlocks;
} }
return newBlocks;
} }
private void FetchUp(ulong max, ulong blockDifference) private int FetchUp(ulong max, ulong blockDifference)
{ {
var target = max - blockDifference; var target = GetTarget(max, blockDifference);
var fetchUp = FetchRange; var fetchUp = FetchRange;
var newBlocks = 0;
while (fetchUp > 0) while (fetchUp > 0)
{ {
if (!entries.ContainsKey(target)) if (!entries.ContainsKey(target))
{ {
var newBlock = AddBlockNumber(target); var newBlock = AddBlockNumber("FU" + fetchUp, target);
if (newBlock == null) return; if (newBlock == null) return newBlocks;
newBlocks++;
fetchUp--; fetchUp--;
} }
target++; target++;
if (target >= max) return; if (target >= max) return newBlocks;
} }
return newBlocks;
}
private ulong GetTarget(ulong max, ulong blockDifference)
{
if (max <= blockDifference) return 1;
return max - blockDifference;
} }
private ulong CalculateBlockDifference(DateTime moment, TimeSpan timePerBlock, ulong max) private ulong CalculateBlockDifference(DateTime moment, TimeSpan timePerBlock, ulong max)
@ -155,13 +180,14 @@ namespace NethereumWorkflow
} }
} }
private BlockTimeEntry? AddBlockNumber(decimal blockNumber) private BlockTimeEntry? AddBlockNumber(string a, decimal blockNumber)
{ {
return AddBlockNumber(Convert.ToUInt64(blockNumber)); return AddBlockNumber(a, Convert.ToUInt64(blockNumber));
} }
private BlockTimeEntry? AddBlockNumber(ulong blockNumber) private BlockTimeEntry? AddBlockNumber(string a, ulong blockNumber)
{ {
log.Log(a + " - Adding blockNumber: " + blockNumber);
if (entries.ContainsKey(blockNumber)) if (entries.ContainsKey(blockNumber))
{ {
return entries[blockNumber]; return entries[blockNumber];
@ -190,9 +216,10 @@ namespace NethereumWorkflow
{ {
var min = entries.Keys.Min(); var min = entries.Keys.Min();
var max = entries.Keys.Max(); var max = entries.Keys.Max();
log.Log("min/max: " + min + " / " + max);
var clippedMin = Math.Max(max - 100, min); var clippedMin = Math.Max(max - 100, min);
var minTime = entries[min].Utc; var minTime = entries[min].Utc;
var clippedMinBlock = AddBlockNumber(clippedMin); var clippedMinBlock = AddBlockNumber("EST", clippedMin);
if (clippedMinBlock != null) minTime = clippedMinBlock.Utc; if (clippedMinBlock != null) minTime = clippedMinBlock.Utc;
var maxTime = entries[max].Utc; var maxTime = entries[max].Utc;
@ -212,7 +239,7 @@ namespace NethereumWorkflow
if (!entries.Any()) if (!entries.Any())
{ {
AddCurrentBlock(); AddCurrentBlock();
AddBlockNumber(entries.Single().Key - 1); AddBlockNumber("INIT", entries.Single().Key - 1);
} }
} }
@ -225,7 +252,7 @@ namespace NethereumWorkflow
{ {
var number = Time.Wait(web3.Eth.Blocks.GetBlockNumber.SendRequestAsync()); var number = Time.Wait(web3.Eth.Blocks.GetBlockNumber.SendRequestAsync());
var blockNumber = number.ToDecimal(); var blockNumber = number.ToDecimal();
return AddBlockNumber(blockNumber); return AddBlockNumber("CUR", blockNumber);
} }
private DateTime? GetTimestampFromBlock(ulong blockNumber) private DateTime? GetTimestampFromBlock(ulong blockNumber)
@ -238,7 +265,7 @@ namespace NethereumWorkflow
} }
catch (Exception ex) catch (Exception ex)
{ {
int i = 0; log.Error(nameof(GetTimestampFromBlock) + " Exception: " + ex);
throw; throw;
} }
} }

View File

@ -3,7 +3,6 @@ using Nethereum.ABI.FunctionEncoding.Attributes;
using Nethereum.Contracts; using Nethereum.Contracts;
using Nethereum.RPC.Eth.DTOs; using Nethereum.RPC.Eth.DTOs;
using Nethereum.Web3; using Nethereum.Web3;
using System.Runtime.CompilerServices;
using Utils; using Utils;
namespace NethereumWorkflow namespace NethereumWorkflow
@ -90,14 +89,18 @@ namespace NethereumWorkflow
{ {
var blockTimeFinder = new BlockTimeFinder(web3, log); var blockTimeFinder = new BlockTimeFinder(web3, log);
var fromBlock = blockTimeFinder.GetLowestBlockNumberAfter(timeRange.From); var lowest = blockTimeFinder.GetLowestBlockNumberAfter(timeRange.From);
var toBlock = blockTimeFinder.GetHighestBlockNumberBefore(timeRange.To); var highest = blockTimeFinder.GetHighestBlockNumberBefore(timeRange.To);
var fromBlock = Math.Min(lowest, highest);
var toBlock = Math.Max(lowest, highest);
return GetEvents<TEvent>(address, fromBlock, toBlock); return GetEvents<TEvent>(address, fromBlock, toBlock);
} }
public List<EventLog<TEvent>> GetEvents<TEvent>(string address, ulong fromBlockNumber, ulong toBlockNumber) where TEvent : IEventDTO, new() public List<EventLog<TEvent>> GetEvents<TEvent>(string address, ulong fromBlockNumber, ulong toBlockNumber) where TEvent : IEventDTO, new()
{ {
log.Debug($"Getting events of type [{typeof(TEvent).Name}] in block range [{fromBlockNumber} - {toBlockNumber}]");
var eventHandler = web3.Eth.GetEvent<TEvent>(address); var eventHandler = web3.Eth.GetEvent<TEvent>(address);
var from = new BlockParameter(fromBlockNumber); var from = new BlockParameter(fromBlockNumber);
var to = new BlockParameter(toBlockNumber); var to = new BlockParameter(toBlockNumber);

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>

View File

@ -26,12 +26,10 @@ namespace BiblioTech
Program.AdminChecker.SetGuild(guild); Program.AdminChecker.SetGuild(guild);
Program.Log.Log($"Initializing for guild: '{guild.Name}'"); Program.Log.Log($"Initializing for guild: '{guild.Name}'");
var roleController = new RoleController(client);
var rewardsApi = new RewardsApi(roleController);
var adminChannels = guild.TextChannels.Where(Program.AdminChecker.IsAdminChannel).ToArray(); var adminChannels = guild.TextChannels.Where(Program.AdminChecker.IsAdminChannel).ToArray();
if (adminChannels == null || !adminChannels.Any()) throw new Exception("No admin message channel"); if (adminChannels == null || !adminChannels.Any()) throw new Exception("No admin message channel");
Program.AdminChecker.SetAdminChannel(adminChannels.First()); Program.AdminChecker.SetAdminChannel(adminChannels.First());
Program.RoleDriver = new RoleDriver(client);
var builders = commands.Select(c => var builders = commands.Select(c =>
{ {
@ -62,8 +60,6 @@ namespace BiblioTech
var json = JsonConvert.SerializeObject(exception.Errors, Formatting.Indented); var json = JsonConvert.SerializeObject(exception.Errors, Formatting.Indented);
Program.Log.Error(json); Program.Log.Error(json);
} }
rewardsApi.Start();
} }
private async Task SlashCommandHandler(SocketSlashCommand command) private async Task SlashCommandHandler(SocketSlashCommand command)

View File

@ -1,5 +1,6 @@
using ArgsUniform; using ArgsUniform;
using BiblioTech.Commands; using BiblioTech.Commands;
using BiblioTech.Rewards;
using Discord; using Discord;
using Discord.WebSocket; using Discord.WebSocket;
using Logging; using Logging;
@ -13,6 +14,7 @@ namespace BiblioTech
public static Configuration Config { get; private set; } = null!; public static Configuration Config { get; private set; } = null!;
public static UserRepo UserRepo { get; } = new UserRepo(); public static UserRepo UserRepo { get; } = new UserRepo();
public static AdminChecker AdminChecker { get; private set; } = null!; public static AdminChecker AdminChecker { get; private set; } = null!;
public static IDiscordRoleDriver RoleDriver { get; set; } = null!;
public static ILog Log { get; private set; } = null!; public static ILog Log { get; private set; } = null!;
public static Task Main(string[] args) public static Task Main(string[] args)
@ -29,10 +31,10 @@ namespace BiblioTech
EnsurePath(Config.UserDataPath); EnsurePath(Config.UserDataPath);
EnsurePath(Config.EndpointsPath); EnsurePath(Config.EndpointsPath);
return new Program().MainAsync(); return new Program().MainAsync(args);
} }
public async Task MainAsync() public async Task MainAsync(string[] args)
{ {
Log.Log("Starting Codex Discord Bot..."); Log.Log("Starting Codex Discord Bot...");
client = new DiscordSocketClient(); client = new DiscordSocketClient();
@ -52,10 +54,19 @@ namespace BiblioTech
await client.LoginAsync(TokenType.Bot, Config.ApplicationToken); await client.LoginAsync(TokenType.Bot, Config.ApplicationToken);
await client.StartAsync(); await client.StartAsync();
AdminChecker = new AdminChecker(); AdminChecker = new AdminChecker();
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel((context, options) =>
{
options.ListenAnyIP(Config.RewardApiPort);
});
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
Log.Log("Running..."); Log.Log("Running...");
await app.RunAsync();
await Task.Delay(-1); await Task.Delay(-1);
} }

View File

@ -0,0 +1,12 @@
{
"profiles": {
"BiblioTech": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:52960;http://localhost:52961"
}
}
}

View File

@ -0,0 +1,35 @@
using DiscordRewards;
using Microsoft.AspNetCore.Mvc;
namespace BiblioTech.Rewards
{
public interface IDiscordRoleDriver
{
Task GiveRewards(GiveRewardsCommand rewards);
}
[Route("api/[controller]")]
[ApiController]
public class RewardController : ControllerBase
{
[HttpGet]
public string Ping()
{
return "Pong";
}
[HttpPost]
public async Task<string> Give(GiveRewardsCommand cmd)
{
try
{
await Program.RoleDriver.GiveRewards(cmd);
}
catch (Exception ex)
{
Program.Log.Error("Exception: " + ex);
}
return "OK";
}
}
}

View File

@ -1,91 +0,0 @@
using DiscordRewards;
using Newtonsoft.Json;
using System.Net;
using TaskFactory = Utils.TaskFactory;
namespace BiblioTech.Rewards
{
public interface IDiscordRoleController
{
Task GiveRewards(GiveRewardsCommand rewards);
}
public class RewardsApi
{
private readonly HttpListener listener = new HttpListener();
private readonly TaskFactory taskFactory = new TaskFactory();
private readonly IDiscordRoleController roleController;
private CancellationTokenSource cts = new CancellationTokenSource();
public RewardsApi(IDiscordRoleController roleController)
{
this.roleController = roleController;
}
public void Start()
{
cts = new CancellationTokenSource();
var uri = $"http://*:{Program.Config.RewardApiPort}/";
listener.Prefixes.Add(uri);
listener.Start();
taskFactory.Run(ConnectionDispatcher, nameof(ConnectionDispatcher));
Program.Log.Log($"Reward API listening on '{uri}'");
}
public void Stop()
{
listener.Stop();
cts.Cancel();
taskFactory.WaitAll();
}
private void ConnectionDispatcher()
{
while (!cts.Token.IsCancellationRequested)
{
var wait = listener.GetContextAsync();
wait.Wait(cts.Token);
if (wait.IsCompletedSuccessfully)
{
taskFactory.Run(() =>
{
var context = wait.Result;
try
{
HandleConnection(context).Wait();
}
catch (Exception ex)
{
Program.Log.Error("Exception during HTTP handler: " + ex);
}
}, nameof(HandleConnection));
}
}
}
private async Task HandleConnection(HttpListenerContext context)
{
using var reader = new StreamReader(context.Request.InputStream);
var content = reader.ReadToEnd();
if (content == "Ping")
{
using var writer = new StreamWriter(context.Response.OutputStream);
writer.Write("Pong");
return;
}
if (!content.StartsWith("{")) return;
var rewards = JsonConvert.DeserializeObject<GiveRewardsCommand>(content);
if (rewards != null)
{
await ProcessRewards(rewards);
}
}
private async Task ProcessRewards(GiveRewardsCommand rewards)
{
await roleController.GiveRewards(rewards);
}
}
}

View File

@ -4,13 +4,13 @@ using DiscordRewards;
namespace BiblioTech.Rewards namespace BiblioTech.Rewards
{ {
public class RoleController : IDiscordRoleController public class RoleDriver : IDiscordRoleDriver
{ {
private readonly DiscordSocketClient client; private readonly DiscordSocketClient client;
private readonly SocketTextChannel? rewardsChannel; private readonly SocketTextChannel? rewardsChannel;
private readonly RewardRepo repo = new RewardRepo(); private readonly RewardRepo repo = new RewardRepo();
public RoleController(DiscordSocketClient client) public RoleDriver(DiscordSocketClient client)
{ {
this.client = client; this.client = client;
@ -107,7 +107,13 @@ namespace BiblioTech.Rewards
private SocketGuild GetGuild() private SocketGuild GetGuild()
{ {
return client.Guilds.Single(g => g.Name == Program.Config.ServerName); var guild = client.Guilds.SingleOrDefault(g => g.Name == Program.Config.ServerName);
if (guild == null)
{
throw new Exception($"Unable to find guild by name: '{Program.Config.ServerName}'. " +
$"Known guilds: [{string.Join(",", client.Guilds.Select(g => g.Name))}]");
}
return guild;
} }
} }

View File

@ -1,4 +1,5 @@
using DiscordRewards; using CodexContractsPlugin.Marketplace;
using DiscordRewards;
using Logging; using Logging;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -17,13 +18,30 @@ namespace TestNetRewarder
public async Task<bool> IsOnline() public async Task<bool> IsOnline()
{ {
return await HttpPost("Ping") == "Ping"; var result = await HttpGet();
log.Log("Is DiscordBot online: " + result);
return result == "Pong";
} }
public async Task SendRewards(GiveRewardsCommand command) public async Task<bool> SendRewards(GiveRewardsCommand command)
{ {
if (command == null || command.Rewards == null || !command.Rewards.Any()) return; if (command == null || command.Rewards == null || !command.Rewards.Any()) return false;
await HttpPost(JsonConvert.SerializeObject(command)); return await HttpPost(JsonConvert.SerializeObject(command)) == "OK";
}
private async Task<string> HttpGet()
{
try
{
var client = new HttpClient();
var response = await client.GetAsync(GetUrl());
return await response.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
log.Error(ex.ToString());
return string.Empty;
}
} }
private async Task<string> HttpPost(string content) private async Task<string> HttpPost(string content)
@ -43,7 +61,7 @@ namespace TestNetRewarder
private string GetUrl() private string GetUrl()
{ {
return $"{configuration.DiscordHost}:{configuration.DiscordPort}"; return $"{configuration.DiscordHost}:{configuration.DiscordPort}/api/reward";
} }
} }
} }

View File

@ -44,11 +44,14 @@ namespace TestNetRewarder
if (outgoingRewards.Any()) if (outgoingRewards.Any())
{ {
await SendRewardsCommand(outgoingRewards); if (!await SendRewardsCommand(outgoingRewards))
{
log.Error("Failed to send reward command.");
}
} }
} }
private async Task SendRewardsCommand(List<RewardUsersCommand> outgoingRewards) private async Task<bool> SendRewardsCommand(List<RewardUsersCommand> outgoingRewards)
{ {
var cmd = new GiveRewardsCommand var cmd = new GiveRewardsCommand
{ {
@ -56,7 +59,7 @@ namespace TestNetRewarder
}; };
log.Debug("Sending rewards: " + JsonConvert.SerializeObject(cmd)); log.Debug("Sending rewards: " + JsonConvert.SerializeObject(cmd));
await Program.BotClient.SendRewards(cmd); return await Program.BotClient.SendRewards(cmd);
} }
private void ProcessReward(List<RewardUsersCommand> outgoingRewards, RewardConfig reward, ChainState chainState) private void ProcessReward(List<RewardUsersCommand> outgoingRewards, RewardConfig reward, ChainState chainState)

View File

@ -59,6 +59,7 @@ namespace TestNetRewarder
var blockNumber = gc.GethNode.GetSyncedBlockNumber(); var blockNumber = gc.GethNode.GetSyncedBlockNumber();
if (blockNumber == null || blockNumber < 1) throw new Exception("Geth connection failed."); if (blockNumber == null || blockNumber < 1) throw new Exception("Geth connection failed.");
Log.Log("Geth OK. Block number: " + blockNumber);
} }
private static async Task EnsureBotOnline() private static async Task EnsureBotOnline()

View File

@ -33,6 +33,7 @@ namespace TestNetRewarder
// Wait for the entire time segment to be in the past. // Wait for the entire time segment to be in the past.
var delay = (end - now).Add(TimeSpan.FromSeconds(3)); var delay = (end - now).Add(TimeSpan.FromSeconds(3));
waited = true; waited = true;
log.Log($"Waiting till time segment is in the past... {Time.FormatDuration(delay)}");
await Task.Delay(delay, Program.CancellationToken); await Task.Delay(delay, Program.CancellationToken);
} }

View File

@ -0,0 +1,2 @@
docker build -f docker/Dockerfile -t thatbenbierens/codex-rewardbot:initial ../..
docker push thatbenbierens/codex-rewardbot:initial