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
{
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 DateTime Utc { get; }
public ulong BlockNumber { get; }
public DateTime Utc { get; }
public override string ToString()
{
return $"[{BlockNumber}] @ {Utc.ToString("o")}";
}
public override string ToString()
{
return $"[{BlockNumber}] @ {Utc.ToString("o")}";
}
}
}

View File

@ -1,36 +1,33 @@
using Logging;
using Nethereum.RPC.Eth.DTOs;
using Nethereum.Web3;
using Utils;
namespace NethereumWorkflow
{
public partial class BlockTimeFinder
public class BlockTimeFinder
{
private const ulong FetchRange = 6;
private const int MaxEntries = 1024;
private static readonly Dictionary<ulong, BlockTimeEntry> entries = new Dictionary<ulong, BlockTimeEntry>();
private readonly Web3 web3;
private readonly IWeb3Blocks web3;
private readonly ILog log;
public BlockTimeFinder(Web3 web3, ILog log)
public BlockTimeFinder(IWeb3Blocks web3, ILog log)
{
this.web3 = web3;
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);
Initialize();
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);
Initialize();
@ -48,7 +45,7 @@ namespace NethereumWorkflow
closestAfter.Utc > moment &&
closestBefore.BlockNumber + 1 == closestAfter.BlockNumber)
{
log.Log("Found highest-Before: " + closestBefore);
log.Debug("Found highest-Before: " + closestBefore);
return closestBefore.BlockNumber;
}
@ -67,7 +64,7 @@ namespace NethereumWorkflow
closestAfter.Utc > moment &&
closestBefore.BlockNumber + 1 == closestAfter.BlockNumber)
{
log.Log("Found lowest-after: " + closestAfter);
log.Debug("Found lowest-after: " + closestAfter);
return closestAfter.BlockNumber;
}
@ -223,24 +220,13 @@ namespace NethereumWorkflow
private BlockTimeEntry? AddCurrentBlock()
{
var number = Time.Wait(web3.Eth.Blocks.GetBlockNumber.SendRequestAsync());
var blockNumber = number.ToDecimal();
var blockNumber = web3.GetCurrentBlockNumber();
return AddBlockNumber(blockNumber);
}
private DateTime? GetTimestampFromBlock(ulong blockNumber)
{
try
{
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;
}
return web3.GetTimestampForBlock(blockNumber);
}
private BlockTimeEntry? FindClosestBeforeEntry(DateTime moment)

View File

@ -3,7 +3,6 @@ using Nethereum.ABI.FunctionEncoding.Attributes;
using Nethereum.Contracts;
using Nethereum.RPC.Eth.DTOs;
using Nethereum.Web3;
using System.Runtime.CompilerServices;
using Utils;
namespace NethereumWorkflow
@ -88,7 +87,8 @@ namespace NethereumWorkflow
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 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>
<ItemGroup>
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="nunit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.4.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Framework\NethereumWorkflow\NethereumWorkflow.csproj" />
<ProjectReference Include="..\..\Framework\Utils\Utils.csproj" />
</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); } }
}
}