Setting up contract tracer

This commit is contained in:
ThatBen 2025-05-20 10:19:07 +02:00
parent 0e66e8e94a
commit a85caae203
No known key found for this signature in database
GPG Key ID: 62C543548433D43E
9 changed files with 248 additions and 10 deletions

View File

@ -4,8 +4,6 @@ using GethPlugin;
using Logging;
using Nethereum.Contracts;
using Nethereum.Hex.HexTypes;
using Nethereum.RPC.Eth.DTOs;
using Nethereum.Web3;
using Utils;
namespace CodexContractsPlugin
@ -105,7 +103,11 @@ namespace CodexContractsPlugin
public ReserveSlotFunction[] GetReserveSlotCalls()
{
var result = new List<ReserveSlotFunction>();
gethNode.IterateFunctionCalls<ReserveSlotFunction>(BlockInterval, result.Add);
gethNode.IterateFunctionCalls<ReserveSlotFunction>(BlockInterval, (b, fn) =>
{
fn.Block = b;
result.Add(fn);
});
return result.ToArray();
}

View File

@ -15,6 +15,11 @@ namespace CodexContractsPlugin.Marketplace
byte[] RequestId { get; set; }
}
public interface IHasSlotIndex
{
ulong SlotIndex { get; set; }
}
public partial class Request : RequestBase, IHasBlock, IHasRequestId
{
[JsonIgnore]
@ -51,20 +56,20 @@ namespace CodexContractsPlugin.Marketplace
public BlockTimeEntry Block { get; set; }
}
public partial class SlotFilledEventDTO : IHasBlock, IHasRequestId
public partial class SlotFilledEventDTO : IHasBlock, IHasRequestId, IHasSlotIndex
{
[JsonIgnore]
public BlockTimeEntry Block { get; set; }
public EthAddress Host { get; set; }
}
public partial class SlotFreedEventDTO : IHasBlock, IHasRequestId
public partial class SlotFreedEventDTO : IHasBlock, IHasRequestId, IHasSlotIndex
{
[JsonIgnore]
public BlockTimeEntry Block { get; set; }
}
public partial class SlotReservationsFullEventDTO : IHasBlock, IHasRequestId
public partial class SlotReservationsFullEventDTO : IHasBlock, IHasRequestId, IHasSlotIndex
{
[JsonIgnore]
public BlockTimeEntry Block { get; set; }
@ -75,5 +80,11 @@ namespace CodexContractsPlugin.Marketplace
[JsonIgnore]
public BlockTimeEntry Block { get; set; }
}
public partial class ReserveSlotFunction : IHasBlock, IHasRequestId, IHasSlotIndex
{
[JsonIgnore]
public BlockTimeEntry Block { get; set; }
}
}
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.

View File

@ -31,7 +31,7 @@ namespace GethPlugin
List<EventLog<TEvent>> GetEvents<TEvent>(string address, TimeRange timeRange) where TEvent : IEventDTO, new();
BlockInterval ConvertTimeRangeToBlockRange(TimeRange timeRange);
BlockTimeEntry GetBlockForNumber(ulong number);
void IterateFunctionCalls<TFunc>(BlockInterval blockInterval, Action<TFunc> onCall) where TFunc : FunctionMessage, new();
void IterateFunctionCalls<TFunc>(BlockInterval blockInterval, Action<BlockTimeEntry, TFunc> onCall) where TFunc : FunctionMessage, new();
}
public class DeploymentGethNode : BaseGethNode, IGethNode
@ -189,7 +189,7 @@ namespace GethPlugin
return StartInteraction().GetBlockWithTransactions(number);
}
public void IterateFunctionCalls<TFunc>(BlockInterval blockRange, Action<TFunc> onCall) where TFunc : FunctionMessage, new()
public void IterateFunctionCalls<TFunc>(BlockInterval blockRange, Action<BlockTimeEntry, TFunc> onCall) where TFunc : FunctionMessage, new()
{
var i = StartInteraction();
for (var blkI = blockRange.From; blkI <= blockRange.To; blkI++)
@ -201,7 +201,11 @@ namespace GethPlugin
if (t.IsTransactionForFunctionMessage<TFunc>())
{
var func = t.DecodeTransactionToFunctionMessage<TFunc>();
if (func != null) onCall(func);
if (func != null)
{
var b = GetBlockForNumber(blkI);
onCall(b, func);
}
}
}
}

View File

@ -271,7 +271,7 @@ namespace CodexReleaseTests.MarketTests
Log($"Request '{requestId}' failed to start. There were {calls.Length} hosts who called reserve-slot for it:");
foreach (var c in calls)
{
Log($" - Host: {c.FromAddress} RequestId: {c.RequestId.ToHex()} SlotIndex: {c.SlotIndex}");
Log($" - {c.Block.Utc} Host: {c.FromAddress} RequestId: {c.RequestId.ToHex()} SlotIndex: {c.SlotIndex}");
}
}
}

11
TraceContract/Config.cs Normal file

File diff suppressed because one or more lines are too long

18
TraceContract/Output.cs Normal file
View File

@ -0,0 +1,18 @@
using CodexContractsPlugin;
using CodexContractsPlugin.Marketplace;
namespace TraceContract
{
public class Output
{
public void LogRequest(Request request)
{
throw new NotImplementedException();
}
public void LogEventOrCall<T>(T[] calls) where T : IHasRequestId, IHasBlock
{
throw new NotImplementedException();
}
}
}

169
TraceContract/Program.cs Normal file
View File

@ -0,0 +1,169 @@
using BlockchainUtils;
using CodexContractsPlugin;
using CodexContractsPlugin.Marketplace;
using Core;
using GethPlugin;
using Logging;
using Nethereum.Hex.HexConvertors.Extensions;
using Utils;
namespace TraceContract
{
public class Input
{
public string PurchaseId { get; } = "a7fe97dc32216aba0cbe74b87beb3f919aa116090dd5e0d48085a1a6b0080e82";
}
public class Program
{
public static void Main(string[] args)
{
var p = new Program();
p.Run();
}
private readonly ILog log = new ConsoleLog();
private readonly Input input = new();
private readonly Output output = new();
private readonly Config config = new();
private void Run()
{
try
{
TracePurchase();
}
catch (Exception exc)
{
log.Error(exc.ToString());
}
}
private void TracePurchase()
{
Log("Setting up...");
var contracts = ConnectCodexContracts();
var chainTracer = new ChainTracer(log, contracts, input, output);
chainTracer.TraceChainTimeline();
Log("Done");
}
private ICodexContracts ConnectCodexContracts()
{
ProjectPlugin.Load<GethPlugin.GethPlugin>();
ProjectPlugin.Load<CodexContractsPlugin.CodexContractsPlugin>();
var entryPoint = new EntryPoint(log, new KubernetesWorkflow.Configuration(null, TimeSpan.FromMinutes(1.0), TimeSpan.FromSeconds(10.0), "_Unused!_"), Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData));
entryPoint.Announce();
var ci = entryPoint.CreateInterface();
var account = EthAccountGenerator.GenerateNew();
var blockCache = new BlockCache();
var geth = new CustomGethNode(log, blockCache, config.RpcEndpoint, config.GethPort, account.PrivateKey);
var deployment = new CodexContractsDeployment(
config: new CodexContractsPlugin.Marketplace.MarketplaceConfig(),
marketplaceAddress: config.MarketplaceAddress,
abi: config.Abi,
tokenAddress: config.TokenAddress
);
return ci.WrapCodexContractsDeployment(geth, deployment);
}
private void Log(string msg)
{
log.Log(msg);
}
}
public class ChainTracer
{
private readonly ILog log;
private readonly ICodexContracts contracts;
private readonly Input input;
private readonly Output output;
public ChainTracer(ILog log, ICodexContracts contracts, Input input, Output output)
{
this.log = log;
this.contracts = contracts;
this.input = input;
this.output = output;
}
public void TraceChainTimeline()
{
var request = GetRequest();
if (request == null) throw new Exception("Failed to find the purchase in the last week of transactions.");
output.LogRequest(request);
var requestTimeRange = new TimeRange(request.Block.Utc.AddMinutes(-1.0), DateTime.UtcNow);
var events = contracts.GetEvents(requestTimeRange);
// Log calls to reserve slot for request
var calls = Filter(events.GetReserveSlotCalls());
output.LogEventOrCall(calls);
// log all events.
var fulls = events.GetSlotReservationsFullEvents();
output.LogEventOrCall(Filter(fulls));
}
private T[] Filter<T>(T[] calls) where T : IHasRequestId
{
return calls.Where(c => IsThisRequest(c.RequestId)).ToArray();
}
private Request? GetRequest()
{
var request = FindRequest(LastHour());
if (request == null) request = FindRequest(LastDay());
if (request == null) request = FindRequest(LastWeek());
return request;
}
private Request? FindRequest(TimeRange timeRange)
{
var events = contracts.GetEvents(timeRange);
var requests = events.GetStorageRequests();
foreach (var r in requests)
{
if (IsThisRequest(r.RequestId))
{
return r;
}
}
return null;
}
private bool IsThisRequest(byte[] requestId)
{
return requestId.ToHex().ToLowerInvariant() == input.PurchaseId.ToLowerInvariant();
}
private TimeRange LastHour()
{
return new TimeRange(DateTime.UtcNow.AddHours(-1.0), DateTime.UtcNow);
}
private TimeRange LastDay()
{
return new TimeRange(DateTime.UtcNow.AddDays(-1.0), DateTime.UtcNow);
}
private TimeRange LastWeek()
{
return new TimeRange(DateTime.UtcNow.AddDays(-7.0), DateTime.UtcNow);
}
private void Log(string msg)
{
log.Log(msg);
}
}
}

View File

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Framework\Logging\Logging.csproj" />
<ProjectReference Include="..\ProjectPlugins\CodexContractsPlugin\CodexContractsPlugin.csproj" />
<ProjectReference Include="..\ProjectPlugins\GethPlugin\GethPlugin.csproj" />
</ItemGroup>
</Project>

View File

@ -86,6 +86,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodexClient", "ProjectPlugi
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebUtils", "Framework\WebUtils\WebUtils.csproj", "{372C9E5D-5453-4D45-9948-E9324E21AD65}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TraceContract", "TraceContract\TraceContract.csproj", "{6F4C72D7-4B6E-45FD-93C6-2099CBE5B2AC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -232,6 +234,10 @@ Global
{372C9E5D-5453-4D45-9948-E9324E21AD65}.Debug|Any CPU.Build.0 = Debug|Any CPU
{372C9E5D-5453-4D45-9948-E9324E21AD65}.Release|Any CPU.ActiveCfg = Release|Any CPU
{372C9E5D-5453-4D45-9948-E9324E21AD65}.Release|Any CPU.Build.0 = Release|Any CPU
{6F4C72D7-4B6E-45FD-93C6-2099CBE5B2AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6F4C72D7-4B6E-45FD-93C6-2099CBE5B2AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6F4C72D7-4B6E-45FD-93C6-2099CBE5B2AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6F4C72D7-4B6E-45FD-93C6-2099CBE5B2AC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -272,6 +278,7 @@ Global
{4648B5AA-A0A7-44BA-89BC-2FD57370943C} = {81AE04BC-CBFA-4E6F-B039-8208E9AFAAE7}
{9AF12703-29AF-416D-9781-204223D6D0E5} = {8F1F1C2A-E313-4E0C-BE40-58FB0BA91124}
{372C9E5D-5453-4D45-9948-E9324E21AD65} = {81AE04BC-CBFA-4E6F-B039-8208E9AFAAE7}
{6F4C72D7-4B6E-45FD-93C6-2099CBE5B2AC} = {7591C5B3-D86E-4AE4-8ED2-B272D17FE7E3}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {237BF0AA-9EC4-4659-AD9A-65DEB974250C}