274 lines
9.4 KiB
C#
Raw Normal View History

using BlockchainUtils;
using CodexContractsPlugin.Marketplace;
2023-12-20 10:55:29 +01:00
using GethPlugin;
2023-09-19 13:39:24 +02:00
using Logging;
2023-12-20 11:34:23 +01:00
using Nethereum.ABI;
using Nethereum.Contracts;
2025-08-13 15:23:31 +02:00
using Nethereum.Hex.HexConvertors.Extensions;
2023-12-20 11:34:23 +01:00
using Nethereum.Util;
2024-04-09 13:24:30 +02:00
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
2023-12-20 09:48:22 +01:00
using Utils;
2023-09-19 13:39:24 +02:00
namespace CodexContractsPlugin
2023-09-19 10:24:43 +02:00
{
public interface ICodexContracts
{
2023-09-20 10:13:29 +02:00
CodexContractsDeployment Deployment { get; }
2023-09-19 13:39:24 +02:00
bool IsDeployed();
2023-12-15 11:02:06 +01:00
string MintTestTokens(IHasEthAddress owner, TestToken testTokens);
string MintTestTokens(EthAddress ethAddress, TestToken testTokens);
2023-10-30 13:30:14 +01:00
TestToken GetTestTokenBalance(IHasEthAddress owner);
TestToken GetTestTokenBalance(EthAddress ethAddress);
string TransferTestTokens(EthAddress to, TestToken amount);
2023-12-20 09:48:22 +01:00
ICodexContractsEvents GetEvents(TimeRange timeRange);
ICodexContractsEvents GetEvents(BlockInterval blockInterval);
EthAddress? GetSlotHost(byte[] requestId, decimal slotIndex);
RequestState GetRequestState(byte[] requestId);
Request GetRequest(byte[] requestId);
ulong GetPeriodNumber(DateTime utc);
2024-11-25 15:45:09 +01:00
void WaitUntilNextPeriod();
2025-08-13 15:23:31 +02:00
ProofState GetProofState(byte[] requestId, decimal slotIndex, ulong period);
byte[] GetSlotId(byte[] requestId, decimal slotIndex);
2025-07-03 11:03:13 +02:00
ICodexContracts WithDifferentGeth(IGethNode node);
}
2025-08-13 15:23:31 +02:00
public enum ProofState
{
2025-08-13 15:23:31 +02:00
NotRequired,
NotMissed,
MissedNotMarked,
MissedAndMarked,
2023-09-19 10:24:43 +02:00
}
2024-04-09 13:24:30 +02:00
[JsonConverter(typeof(StringEnumConverter))]
public enum RequestState
{
New,
Started,
Cancelled,
Finished,
Failed
}
2023-09-19 10:24:43 +02:00
public class CodexContractsAccess : ICodexContracts
{
2023-09-19 13:39:24 +02:00
private readonly ILog log;
2023-10-30 13:30:14 +01:00
private readonly IGethNode gethNode;
2023-09-19 13:39:24 +02:00
2023-10-30 13:30:14 +01:00
public CodexContractsAccess(ILog log, IGethNode gethNode, CodexContractsDeployment deployment)
2023-09-19 10:24:43 +02:00
{
2023-09-19 13:39:24 +02:00
this.log = log;
2023-10-30 13:30:14 +01:00
this.gethNode = gethNode;
Deployment = deployment;
2023-09-19 10:24:43 +02:00
}
2023-09-20 10:13:29 +02:00
public CodexContractsDeployment Deployment { get; }
2023-09-19 13:39:24 +02:00
public bool IsDeployed()
{
2023-12-20 09:48:22 +01:00
return !string.IsNullOrEmpty(StartInteraction().GetTokenName(Deployment.TokenAddress));
}
2023-12-15 11:02:06 +01:00
public string MintTestTokens(IHasEthAddress owner, TestToken testTokens)
2023-09-19 16:22:07 +02:00
{
2023-12-15 11:02:06 +01:00
return MintTestTokens(owner.EthAddress, testTokens);
2023-09-19 16:22:07 +02:00
}
2023-12-15 11:02:06 +01:00
public string MintTestTokens(EthAddress ethAddress, TestToken testTokens)
2023-09-19 13:39:24 +02:00
{
return StartInteraction().MintTestTokens(ethAddress, testTokens.TstWei, Deployment.TokenAddress);
2023-09-19 13:39:24 +02:00
}
2023-10-30 13:30:14 +01:00
public TestToken GetTestTokenBalance(IHasEthAddress owner)
2023-09-19 16:22:07 +02:00
{
2023-10-30 13:30:14 +01:00
return GetTestTokenBalance(owner.EthAddress);
2023-09-19 16:22:07 +02:00
}
2023-10-30 13:30:14 +01:00
public TestToken GetTestTokenBalance(EthAddress ethAddress)
2023-09-19 13:39:24 +02:00
{
2023-12-20 09:48:22 +01:00
var balance = StartInteraction().GetBalance(Deployment.TokenAddress, ethAddress.Address);
return balance.TstWei();
2023-09-19 13:39:24 +02:00
}
2023-12-20 09:48:22 +01:00
public string TransferTestTokens(EthAddress to, TestToken amount)
2025-07-03 11:03:13 +02:00
{
return StartInteraction().TransferTestTokens(Deployment.TokenAddress, to.Address, amount.TstWei);
2025-07-03 11:03:13 +02:00
}
public ICodexContractsEvents GetEvents(TimeRange timeRange)
2023-12-20 09:48:22 +01:00
{
return GetEvents(gethNode.ConvertTimeRangeToBlockRange(timeRange));
2023-12-20 09:48:22 +01:00
}
public ICodexContractsEvents GetEvents(BlockInterval blockInterval)
{
return new CodexContractsEvents(log, gethNode, Deployment, blockInterval);
}
public EthAddress? GetSlotHost(byte[] requestId, decimal slotIndex)
{
var slotId = GetSlotId(requestId, slotIndex);
2023-12-20 11:34:23 +01:00
var func = new GetHostFunction
{
SlotId = slotId
2023-12-20 11:34:23 +01:00
};
2024-01-26 17:29:57 -05:00
var address = gethNode.Call<GetHostFunction, string>(Deployment.MarketplaceAddress, func);
if (string.IsNullOrEmpty(address)) return null;
return new EthAddress(address);
2023-12-20 11:34:23 +01:00
}
public RequestState GetRequestState(byte[] requestId)
{
if (requestId == null) throw new ArgumentNullException(nameof(requestId));
if (requestId.Length != 32) throw new InvalidDataException(nameof(requestId) + $"{nameof(requestId)} length should be 32 bytes, but was: {requestId.Length}" + requestId.Length);
var func = new RequestStateFunction
{
RequestId = requestId
};
return gethNode.Call<RequestStateFunction, RequestState>(Deployment.MarketplaceAddress, func);
}
public Request GetRequest(byte[] requestId)
{
if (requestId == null) throw new ArgumentNullException(nameof(requestId));
if (requestId.Length != 32) throw new InvalidDataException(nameof(requestId) + $"{nameof(requestId)} length should be 32 bytes, but was: {requestId.Length}" + requestId.Length);
var func = new GetRequestFunction
{
RequestId = requestId
};
var request = gethNode.Call<GetRequestFunction, GetRequestOutputDTO>(Deployment.MarketplaceAddress, func);
return request.ReturnValue1;
}
public ulong GetPeriodNumber(DateTime utc)
{
DateTimeOffset utco = DateTime.SpecifyKind(utc, DateTimeKind.Utc);
var now = utco.ToUnixTimeSeconds();
var periodSeconds = (int)Deployment.Config.Proofs.Period;
var result = now / periodSeconds;
return Convert.ToUInt64(result);
}
2024-11-25 15:45:09 +01:00
public void WaitUntilNextPeriod()
2024-11-22 16:09:18 +01:00
{
2024-11-25 15:45:09 +01:00
var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
var periodSeconds = (int)Deployment.Config.Proofs.Period;
var secondsLeft = now % periodSeconds;
Thread.Sleep(TimeSpan.FromSeconds(secondsLeft + 1));
2024-11-22 16:09:18 +01:00
}
2025-08-13 15:23:31 +02:00
public ProofState GetProofState(byte[] requestId, decimal slotIndex, ulong period)
{
var slotId = GetSlotId(requestId, slotIndex);
2025-08-13 15:23:31 +02:00
var required = IsProofRequired(slotId);
if (!required) return ProofState.NotRequired;
2025-08-13 15:23:31 +02:00
return IsProofMissing(slotId, period);
}
2025-07-03 11:03:13 +02:00
public ICodexContracts WithDifferentGeth(IGethNode node)
{
return new CodexContractsAccess(log, node, Deployment);
}
2025-08-13 15:23:31 +02:00
public byte[] GetSlotId(byte[] requestId, decimal slotIndex)
{
var encoder = new ABIEncode();
var encoded = encoder.GetABIEncoded(
new ABIValue("bytes32", requestId),
new ABIValue("uint256", slotIndex.ToBig())
);
return Sha3Keccack.Current.CalculateHash(encoded);
}
2025-08-13 15:23:31 +02:00
private bool IsProofRequired(byte[] slotId)
{
var func = new IsProofRequiredFunction
{
Id = slotId
};
2025-08-13 15:23:31 +02:00
var result = gethNode.Call<IsProofRequiredFunction, IsProofRequiredOutputDTO>(Deployment.MarketplaceAddress, func);
return result.ReturnValue1;
}
2025-08-13 15:23:31 +02:00
private ProofState IsProofMissing(byte[] slotId, ulong period)
{
// In case of a missed proof, one of two things can be true:
// 1 - The proof was missed but no validator marked it as missing:
// We can see this by calling "canMarkProofAsMissing" and it returns true/doesn't throw.
// 2 - The proof was missed and it was marked as missing by a validator:
// We can see this by a successful call to "MarkProofAsMissing" on-chain.
if (CallCanMarkProofAsMissing(slotId, period))
{
return ProofState.MissedNotMarked;
}
if (WasMarkProofAsMissingCalled(slotId, period))
{
return ProofState.MissedAndMarked;
}
return ProofState.NotMissed;
}
private bool CallCanMarkProofAsMissing(byte[] slotId, ulong period)
{
try
{
2025-08-13 13:54:33 +02:00
var func = new CanMarkProofAsMissingFunction
{
SlotId = slotId,
Period = period
};
2025-08-13 13:54:33 +02:00
2025-08-13 15:23:31 +02:00
gethNode.Call<CanMarkProofAsMissingFunction>(Deployment.MarketplaceAddress, func);
2025-08-13 13:54:33 +02:00
2025-08-13 15:23:31 +02:00
return true;
}
catch (AggregateException exc)
{
if (exc.InnerExceptions.Count == 1)
{
if (exc.InnerExceptions[0].GetType() == typeof(SmartContractCustomErrorRevertException))
{
return false;
}
}
throw;
}
2025-08-13 15:23:31 +02:00
}
private bool WasMarkProofAsMissingCalled(byte[] slotId, ulong period)
{
var now = DateTime.UtcNow;
var currentPeriod = new TimeRange(now - Deployment.Config.PeriodDuration, now);
var interval = gethNode.ConvertTimeRangeToBlockRange(currentPeriod);
var slot = slotId.ToHex().ToLowerInvariant();
var found = false;
gethNode.IterateFunctionCalls<MarkProofAsMissingFunction>(interval, (b, fn) =>
{
if (fn.Period == period && fn.SlotId.ToHex().ToLowerInvariant() == slot)
{
found = true;
}
});
return found;
}
2023-12-20 09:48:22 +01:00
private ContractInteractions StartInteraction()
{
return new ContractInteractions(log, gethNode);
}
2023-09-19 10:24:43 +02:00
}
}