Sets up tests for blocktimefinder.

This commit is contained in:
Ben 2024-03-21 12:18:04 +01:00
parent db37143db6
commit 4fdb310ca4
No known key found for this signature in database
GPG Key ID: 541B9D8C9F1426A1
6 changed files with 208 additions and 40 deletions

View File

@ -1,22 +1,19 @@
namespace NethereumWorkflow namespace NethereumWorkflow
{ {
public partial class BlockTimeFinder public class BlockTimeEntry
{ {
public class BlockTimeEntry public BlockTimeEntry(ulong blockNumber, DateTime utc)
{ {
public BlockTimeEntry(ulong blockNumber, DateTime utc) BlockNumber = blockNumber;
{ Utc = utc;
BlockNumber = blockNumber; }
Utc = utc;
}
public ulong BlockNumber { get; } public ulong BlockNumber { get; }
public DateTime Utc { get; } public DateTime Utc { get; }
public override string ToString() public override string ToString()
{ {
return $"[{BlockNumber}] @ {Utc.ToString("o")}"; return $"[{BlockNumber}] @ {Utc.ToString("o")}";
}
} }
} }
} }

View File

@ -1,36 +1,33 @@
using Logging; using Logging;
using Nethereum.RPC.Eth.DTOs;
using Nethereum.Web3;
using Utils;
namespace NethereumWorkflow namespace NethereumWorkflow
{ {
public partial class BlockTimeFinder public class BlockTimeFinder
{ {
private const ulong FetchRange = 6; private const ulong FetchRange = 6;
private const int MaxEntries = 1024; private const int MaxEntries = 1024;
private static readonly Dictionary<ulong, BlockTimeEntry> entries = new Dictionary<ulong, BlockTimeEntry>(); private static readonly Dictionary<ulong, BlockTimeEntry> entries = new Dictionary<ulong, BlockTimeEntry>();
private readonly Web3 web3; private readonly IWeb3Blocks web3;
private readonly ILog log; private readonly ILog log;
public BlockTimeFinder(Web3 web3, ILog log) public BlockTimeFinder(IWeb3Blocks web3, ILog log)
{ {
this.web3 = web3; this.web3 = web3;
this.log = log; this.log = log;
} }
public ulong GetHighestBlockNumberBefore(DateTime moment) public ulong? GetHighestBlockNumberBefore(DateTime moment)
{ {
log.Log("Looking for highest block before " + moment.ToString("o")); log.Debug("Looking for highest block before " + moment.ToString("o"));
AssertMomentIsInPast(moment); AssertMomentIsInPast(moment);
Initialize(); Initialize();
return GetHighestBlockBefore(moment); return GetHighestBlockBefore(moment);
} }
public ulong GetLowestBlockNumberAfter(DateTime moment) public ulong? GetLowestBlockNumberAfter(DateTime moment)
{ {
log.Log("Looking for lowest block after " + moment.ToString("o")); log.Debug("Looking for lowest block after " + moment.ToString("o"));
AssertMomentIsInPast(moment); AssertMomentIsInPast(moment);
Initialize(); Initialize();
@ -48,7 +45,7 @@ namespace NethereumWorkflow
closestAfter.Utc > moment && closestAfter.Utc > moment &&
closestBefore.BlockNumber + 1 == closestAfter.BlockNumber) closestBefore.BlockNumber + 1 == closestAfter.BlockNumber)
{ {
log.Log("Found highest-Before: " + closestBefore); log.Debug("Found highest-Before: " + closestBefore);
return closestBefore.BlockNumber; return closestBefore.BlockNumber;
} }
@ -67,7 +64,7 @@ namespace NethereumWorkflow
closestAfter.Utc > moment && closestAfter.Utc > moment &&
closestBefore.BlockNumber + 1 == closestAfter.BlockNumber) closestBefore.BlockNumber + 1 == closestAfter.BlockNumber)
{ {
log.Log("Found lowest-after: " + closestAfter); log.Debug("Found lowest-after: " + closestAfter);
return closestAfter.BlockNumber; return closestAfter.BlockNumber;
} }
@ -223,24 +220,13 @@ namespace NethereumWorkflow
private BlockTimeEntry? AddCurrentBlock() private BlockTimeEntry? AddCurrentBlock()
{ {
var number = Time.Wait(web3.Eth.Blocks.GetBlockNumber.SendRequestAsync()); var blockNumber = web3.GetCurrentBlockNumber();
var blockNumber = number.ToDecimal();
return AddBlockNumber(blockNumber); return AddBlockNumber(blockNumber);
} }
private DateTime? GetTimestampFromBlock(ulong blockNumber) private DateTime? GetTimestampFromBlock(ulong blockNumber)
{ {
try return web3.GetTimestampForBlock(blockNumber);
{
var block = Time.Wait(web3.Eth.Blocks.GetBlockWithTransactionsByNumber.SendRequestAsync(new BlockParameter(blockNumber)));
if (block == null) return null;
return DateTimeOffset.FromUnixTimeSeconds(Convert.ToInt64(block.Timestamp.ToDecimal())).UtcDateTime;
}
catch (Exception ex)
{
int i = 0;
throw;
}
} }
private BlockTimeEntry? FindClosestBeforeEntry(DateTime moment) private BlockTimeEntry? FindClosestBeforeEntry(DateTime moment)

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
@ -88,7 +87,8 @@ namespace NethereumWorkflow
public List<EventLog<TEvent>> GetEvents<TEvent>(string address, TimeRange timeRange) where TEvent : IEventDTO, new() public List<EventLog<TEvent>> GetEvents<TEvent>(string address, TimeRange timeRange) where TEvent : IEventDTO, new()
{ {
var blockTimeFinder = new BlockTimeFinder(web3, log); var wrapper = new Web3Wrapper(web3, log);
var blockTimeFinder = new BlockTimeFinder(wrapper, log);
var fromBlock = blockTimeFinder.GetLowestBlockNumberAfter(timeRange.From); var fromBlock = blockTimeFinder.GetLowestBlockNumberAfter(timeRange.From);
var toBlock = blockTimeFinder.GetHighestBlockNumberBefore(timeRange.To); var toBlock = blockTimeFinder.GetHighestBlockNumberBefore(timeRange.To);

View File

@ -0,0 +1,46 @@
using Logging;
using Nethereum.RPC.Eth.DTOs;
using Nethereum.Web3;
using Utils;
namespace NethereumWorkflow
{
public interface IWeb3Blocks
{
decimal GetCurrentBlockNumber();
DateTime? GetTimestampForBlock(decimal blockNumber);
}
public class Web3Wrapper : IWeb3Blocks
{
private readonly Web3 web3;
private readonly ILog log;
public Web3Wrapper(Web3 web3, ILog log)
{
this.web3 = web3;
this.log = log;
}
public decimal GetCurrentBlockNumber()
{
var number = Time.Wait(web3.Eth.Blocks.GetBlockNumber.SendRequestAsync());
return number.ToDecimal();
}
public DateTime? GetTimestampForBlock(decimal blockNumber)
{
try
{
var block = Time.Wait(web3.Eth.Blocks.GetBlockWithTransactionsByNumber.SendRequestAsync(new BlockParameter(Convert.ToUInt64(blockNumber))));
if (block == null) return null;
return DateTimeOffset.FromUnixTimeSeconds(Convert.ToInt64(block.Timestamp.ToDecimal())).UtcDateTime;
}
catch (Exception ex)
{
log.Error("Exception while getting timestamp for block: " + ex);
return null;
}
}
}
}

View File

@ -7,12 +7,14 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="nunit" Version="3.13.3" /> <PackageReference Include="nunit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.4.2" /> <PackageReference Include="NUnit3TestAdapter" Version="4.4.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Framework\NethereumWorkflow\NethereumWorkflow.csproj" />
<ProjectReference Include="..\..\Framework\Utils\Utils.csproj" /> <ProjectReference Include="..\..\Framework\Utils\Utils.csproj" />
</ItemGroup> </ItemGroup>

View File

@ -0,0 +1,137 @@
using Logging;
using Moq;
using NethereumWorkflow;
using NUnit.Framework;
namespace FrameworkTests.NethereumWorkflow
{
[TestFixture]
public class BlockTimeFinderTests
{
private Mock<IWeb3Blocks> web3 = new Mock<IWeb3Blocks>();
private Mock<ILog> log = new Mock<ILog>();
private Dictionary<decimal, Block> blocks = new Dictionary<decimal, Block>();
private DateTime start = DateTime.Now;
private BlockTimeFinder finder = null!;
private void SetupContinuousBlockchain()
{
start = DateTime.UtcNow.AddDays(-1).AddSeconds(-30);
blocks = new Dictionary<decimal, Block>();
for (var i = 0; i < 30; i++)
{
decimal d = 100 + i;
blocks.Add(d, new Block(d, start + TimeSpan.FromSeconds(i * 2)));
}
}
[SetUp]
public void SetUp()
{
SetupContinuousBlockchain();
web3 = new Mock<IWeb3Blocks>();
web3.Setup(w => w.GetCurrentBlockNumber()).Returns(blocks.Keys.Max());
web3.Setup(w => w.GetTimestampForBlock(It.IsAny<decimal>())).Returns<decimal>(d =>
{
if (blocks.ContainsKey(d)) return blocks[d].Time;
return null;
});
finder = new BlockTimeFinder(web3.Object, log.Object);
}
[Test]
public void FindsMiddleOfChain()
{
var b1 = blocks[115];
var b2 = blocks[116];
var momentBetween = b1.JustAfter;
var b1Number = finder.GetHighestBlockNumberBefore(momentBetween);
var b2Number = finder.GetLowestBlockNumberAfter(momentBetween);
Assert.That(b1Number, Is.EqualTo(b1.Number));
Assert.That(b2Number, Is.EqualTo(b2.Number));
}
[Test]
public void FindsFrontOfChain_Lowest()
{
var first = blocks.First().Value;
var firstNumber = finder.GetLowestBlockNumberAfter(first.JustBefore);
Assert.That(firstNumber, Is.EqualTo(first.Number));
}
[Test]
public void FindsFrontOfChain_Highest()
{
var first = blocks.First().Value;
var firstNumber = finder.GetHighestBlockNumberBefore(first.JustAfter);
Assert.That(firstNumber, Is.EqualTo(first.Number));
}
[Test]
public void FindsTailOfChain_Lowest()
{
var last = blocks.Last().Value;
var lastNumber = finder.GetLowestBlockNumberAfter(last.JustBefore);
Assert.That(lastNumber, Is.EqualTo(last.Number));
}
[Test]
public void FindsTailOfChain_Highest()
{
var last = blocks.Last().Value;
var lastNumber = finder.GetHighestBlockNumberBefore(last.JustAfter);
Assert.That(lastNumber, Is.EqualTo(last.Number));
}
[Test]
public void FailsToFindBlockBeforeFrontOfChain()
{
var first = blocks.First().Value;
var notFound = finder.GetHighestBlockNumberBefore(first.Time);
Assert.That(notFound, Is.Null);
}
[Test]
public void FailsToFindBlockAfterTailOfChain()
{
var last = blocks.Last().Value;
var notFound = finder.GetLowestBlockNumberAfter(last.Time);
Assert.That(notFound, Is.Null);
}
}
public class Block
{
public Block(decimal number, DateTime time)
{
Number = number;
Time = time;
}
public decimal Number { get; }
public DateTime Time { get; }
public DateTime JustBefore { get { return Time.AddSeconds(-1); } }
public DateTime JustAfter { get { return Time.AddSeconds(1); } }
}
}