diff --git a/Framework/ArgsUniform/Assigner.cs b/Framework/ArgsUniform/Assigner.cs index 4db7a08c..483f02e7 100644 --- a/Framework/ArgsUniform/Assigner.cs +++ b/Framework/ArgsUniform/Assigner.cs @@ -95,7 +95,10 @@ namespace ArgsUniform } else { - if (uniformProperty.PropertyType == typeof(string) || uniformProperty.PropertyType == typeof(int)) + if ( + uniformProperty.PropertyType == typeof(string) || + uniformProperty.PropertyType == typeof(int) || + uniformProperty.PropertyType == typeof(decimal)) { uniformProperty.SetValue(result, Convert.ChangeType(value, uniformProperty.PropertyType)); return true; diff --git a/Framework/Core/PluginTools.cs b/Framework/Core/PluginTools.cs index e6eb18a6..94b2dd00 100644 --- a/Framework/Core/PluginTools.cs +++ b/Framework/Core/PluginTools.cs @@ -7,7 +7,6 @@ namespace Core { public interface IPluginTools : IWorkflowTool, ILogTool, IHttpFactory, IFileTool { - IWebCallTimeSet WebCallTimeSet { get; } IK8sTimeSet K8STimeSet { get; } /// diff --git a/Framework/KubernetesWorkflow/K8sController.cs b/Framework/KubernetesWorkflow/K8sController.cs index ffcac5df..e07116a5 100644 --- a/Framework/KubernetesWorkflow/K8sController.cs +++ b/Framework/KubernetesWorkflow/K8sController.cs @@ -724,9 +724,11 @@ namespace KubernetesWorkflow private V1Pod GetPodForDeployment(RunningDeployment deployment) { return Time.Retry(() => GetPodForDeplomentInternal(deployment), - // We will wait up to 1 minute, k8s might be moving pods around. - maxTimeout: TimeSpan.FromMinutes(1), - retryTime: TimeSpan.FromSeconds(10), + // K8s might be moving pods around. If it's scaling the cluster + // to handle the increased load, it might take a while before the new + // VMs are up and ready. So we use a generous timeout. + maxTimeout: TimeSpan.FromMinutes(15.0), + retryTime: TimeSpan.FromSeconds(30.0), description: "Find pod by label for deployment."); } diff --git a/Framework/KubernetesWorkflow/K8sNameUtils.cs b/Framework/KubernetesWorkflow/K8sNameUtils.cs index 2c9b1e52..5be69f7f 100644 --- a/Framework/KubernetesWorkflow/K8sNameUtils.cs +++ b/Framework/KubernetesWorkflow/K8sNameUtils.cs @@ -22,7 +22,9 @@ .Replace("\\", "-") .Replace("[", "-") .Replace("]", "-") - .Replace(",", "-"); + .Replace(",", "-") + .Replace("(", "-") + .Replace(")", "-"); if (result.Length > maxLength) result = result.Substring(0, maxLength); result = result.Trim('-'); diff --git a/Framework/KubernetesWorkflow/StartupWorkflow.cs b/Framework/KubernetesWorkflow/StartupWorkflow.cs index 8b9d5a19..11247fc7 100644 --- a/Framework/KubernetesWorkflow/StartupWorkflow.cs +++ b/Framework/KubernetesWorkflow/StartupWorkflow.cs @@ -61,8 +61,7 @@ namespace KubernetesWorkflow var startResult = controller.BringOnline(recipes, location); var containers = CreateContainers(startResult, recipes, startupConfig); - var info = GetPodInfo(startResult.Deployment); - var rc = new RunningPod(Guid.NewGuid().ToString(), info, startupConfig, startResult, containers); + var rc = new RunningPod(Guid.NewGuid().ToString(), startupConfig, startResult, containers); cluster.Configuration.Hooks.OnContainersStarted(rc); if (startResult.ExternalService != null) @@ -73,7 +72,7 @@ namespace KubernetesWorkflow }); } - public void WaitUntilOnline(RunningPod rc) + public PodInfo WaitUntilOnline(RunningPod rc) { K8s(controller => { @@ -82,6 +81,8 @@ namespace KubernetesWorkflow controller.WaitUntilOnline(c); } }); + + return GetPodInfo(rc.StartResult.Deployment); } public PodInfo GetPodInfo(RunningDeployment deployment) diff --git a/Framework/KubernetesWorkflow/Types/FutureContainers.cs b/Framework/KubernetesWorkflow/Types/FutureContainers.cs index 296be534..805b8e2c 100644 --- a/Framework/KubernetesWorkflow/Types/FutureContainers.cs +++ b/Framework/KubernetesWorkflow/Types/FutureContainers.cs @@ -13,7 +13,8 @@ public RunningPod WaitForOnline() { - workflow.WaitUntilOnline(runningPod); + var podInfo = workflow.WaitUntilOnline(runningPod); + runningPod.Initialize(podInfo); return runningPod; } } diff --git a/Framework/KubernetesWorkflow/Types/RunningContainer.cs b/Framework/KubernetesWorkflow/Types/RunningContainer.cs index 81faf154..fc7d64b2 100644 --- a/Framework/KubernetesWorkflow/Types/RunningContainer.cs +++ b/Framework/KubernetesWorkflow/Types/RunningContainer.cs @@ -27,7 +27,7 @@ namespace KubernetesWorkflow.Types public Address GetAddress(string portTag) { var addresses = Addresses.Where(a => a.PortTag == portTag).ToArray(); - if (!addresses.Any()) throw new Exception("No addresses found for portTag: " + portTag); + if (addresses.Length == 0) throw new Exception("No addresses found for portTag: " + portTag); var select = SelectAddress(addresses); return select.Address; diff --git a/Framework/KubernetesWorkflow/Types/RunningPod.cs b/Framework/KubernetesWorkflow/Types/RunningPod.cs index bbe7f080..d1034885 100644 --- a/Framework/KubernetesWorkflow/Types/RunningPod.cs +++ b/Framework/KubernetesWorkflow/Types/RunningPod.cs @@ -4,10 +4,10 @@ namespace KubernetesWorkflow.Types { public class RunningPod { - public RunningPod(string id, PodInfo podInfo, StartupConfig startupConfig, StartResult startResult, RunningContainer[] containers) + public RunningPod(string id, StartupConfig startupConfig, StartResult startResult, RunningContainer[] containers) { Id = id; - PodInfo = podInfo; + PodInfo = null!; StartupConfig = startupConfig; StartResult = startResult; Containers = containers; @@ -16,7 +16,7 @@ namespace KubernetesWorkflow.Types } public string Id { get; } - public PodInfo PodInfo { get; } + public PodInfo PodInfo { get; private set; } public StartupConfig StartupConfig { get; } public StartResult StartResult { get; } public RunningContainer[] Containers { get; } @@ -30,6 +30,11 @@ namespace KubernetesWorkflow.Types [JsonIgnore] public bool IsStopped { get; internal set; } + public void Initialize(PodInfo podInfo) + { + PodInfo = podInfo; + } + public string Describe() { return string.Join(",", Containers.Select(c => c.Name)); diff --git a/Framework/NethereumWorkflow/NethereumInteraction.cs b/Framework/NethereumWorkflow/NethereumInteraction.cs index 47b7ee20..b8ada938 100644 --- a/Framework/NethereumWorkflow/NethereumInteraction.cs +++ b/Framework/NethereumWorkflow/NethereumInteraction.cs @@ -1,4 +1,5 @@ -using BlockchainUtils; +using System.Numerics; +using BlockchainUtils; using Logging; using Nethereum.ABI.FunctionEncoding.Attributes; using Nethereum.Contracts; @@ -22,25 +23,26 @@ namespace NethereumWorkflow this.blockCache = blockCache; } - public string SendEth(string toAddress, decimal ethAmount) + + public string SendEth(string toAddress, BigInteger ethAmount) { log.Debug(); - var receipt = Time.Wait(web3.Eth.GetEtherTransferService().TransferEtherAndWaitForReceiptAsync(toAddress, ethAmount)); + var receipt = Time.Wait(web3.Eth.GetEtherTransferService().TransferEtherAndWaitForReceiptAsync(toAddress, ((decimal)ethAmount))); if (!receipt.Succeeded()) throw new Exception("Unable to send Eth"); return receipt.TransactionHash; } - public decimal GetEthBalance() + public BigInteger GetEthBalance() { log.Debug(); return GetEthBalance(web3.TransactionManager.Account.Address); } - public decimal GetEthBalance(string address) + public BigInteger GetEthBalance(string address) { log.Debug(); var balance = Time.Wait(web3.Eth.GetBalance.SendRequestAsync(address)); - return Web3.Convert.FromWei(balance.Value); + return balance.Value; } public TResult Call(string contractAddress, TFunction function) where TFunction : FunctionMessage, new() @@ -57,6 +59,13 @@ namespace NethereumWorkflow return Time.Wait(handler.QueryAsync(contractAddress, function, new BlockParameter(blockNumber))); } + public void Call(string contractAddress, TFunction function) where TFunction : FunctionMessage, new() + { + log.Debug(typeof(TFunction).ToString()); + var handler = web3.Eth.GetContractQueryHandler(); + Time.Wait(handler.QueryRawAsync(contractAddress, function)); + } + public void Call(string contractAddress, TFunction function, ulong blockNumber) where TFunction : FunctionMessage, new() { log.Debug(typeof(TFunction).ToString()); @@ -110,15 +119,29 @@ namespace NethereumWorkflow public List> GetEvents(string address, ulong fromBlockNumber, ulong toBlockNumber) where TEvent : IEventDTO, new() { - var eventHandler = web3.Eth.GetEvent(address); + var logs = new List(); + var p = web3.Processing.Logs.CreateProcessor( + action: logs.Add, + minimumBlockConfirmations: 1, + criteria: l => l.IsLogForEvent() + ); + var from = new BlockParameter(fromBlockNumber); var to = new BlockParameter(toBlockNumber); - var blockFilter = Time.Wait(eventHandler.CreateFilterBlockRangeAsync(from, to)); - return Time.Wait(eventHandler.GetAllChangesAsync(blockFilter)); + var ct = new CancellationTokenSource().Token; + Time.Wait(p.ExecuteAsync(toBlockNumber: to.BlockNumber, cancellationToken: ct, startAtBlockNumberIfNotProcessed: from.BlockNumber)); + + return logs + .Where(l => l.IsLogForEvent()) + .Select(l => l.DecodeEvent()) + .ToList(); } public BlockInterval ConvertTimeRangeToBlockRange(TimeRange timeRange) { + if (timeRange.To - timeRange.From < TimeSpan.FromSeconds(1.0)) + throw new Exception(nameof(ConvertTimeRangeToBlockRange) + ": Time range too small."); + var wrapper = new Web3Wrapper(web3, log); var blockTimeFinder = new BlockTimeFinder(blockCache, wrapper, log); diff --git a/Framework/Utils/EthTokenExtensions.cs b/Framework/Utils/EthTokenExtensions.cs index a85d121e..6fe6f346 100644 --- a/Framework/Utils/EthTokenExtensions.cs +++ b/Framework/Utils/EthTokenExtensions.cs @@ -1,15 +1,17 @@ -namespace Utils +using System.Numerics; + +namespace Utils { public class Ether : IComparable { - public Ether(decimal wei) + public Ether(BigInteger wei) { Wei = wei; Eth = wei / TokensIntExtensions.WeiPerEth; } - public decimal Wei { get; } - public decimal Eth { get; } + public BigInteger Wei { get; } + public BigInteger Eth { get; } public int CompareTo(Ether? other) { @@ -75,7 +77,7 @@ public static class TokensIntExtensions { - public const decimal WeiPerEth = 1000000000000000000; + public static readonly BigInteger WeiPerEth = new BigInteger(1000000000000000000); public static Ether Eth(this int i) { @@ -89,10 +91,22 @@ public static Ether Eth(this decimal i) { - return new Ether(i * WeiPerEth); + var a = new BigInteger(i); + return new Ether(a * WeiPerEth); } public static Ether Wei(this decimal i) + { + var a = new BigInteger(i); + return new Ether(a); + } + + public static Ether Eth(this BigInteger i) + { + return new Ether(i * WeiPerEth); + } + + public static Ether Wei(this BigInteger i) { return new Ether(i); } diff --git a/Framework/WebUtils/HttpFactory.cs b/Framework/WebUtils/HttpFactory.cs index 8120527c..ac931078 100644 --- a/Framework/WebUtils/HttpFactory.cs +++ b/Framework/WebUtils/HttpFactory.cs @@ -7,12 +7,14 @@ namespace WebUtils IHttp CreateHttp(string id, Action onClientCreated); IHttp CreateHttp(string id, Action onClientCreated, IWebCallTimeSet timeSet); IHttp CreateHttp(string id); + + IWebCallTimeSet WebCallTimeSet { get; } } public class HttpFactory : IHttpFactory { private readonly ILog log; - private readonly IWebCallTimeSet defaultTimeSet; + private readonly IWebCallTimeSet timeSet; private readonly Action factoryOnClientCreated; public HttpFactory(ILog log) @@ -33,13 +35,15 @@ namespace WebUtils public HttpFactory(ILog log, IWebCallTimeSet defaultTimeSet, Action onClientCreated) { this.log = log; - this.defaultTimeSet = defaultTimeSet; - this.factoryOnClientCreated = onClientCreated; + timeSet = defaultTimeSet; + factoryOnClientCreated = onClientCreated; } + public IWebCallTimeSet WebCallTimeSet => timeSet; + public IHttp CreateHttp(string id, Action onClientCreated) { - return CreateHttp(id, onClientCreated, defaultTimeSet); + return CreateHttp(id, onClientCreated, timeSet); } public IHttp CreateHttp(string id, Action onClientCreated, IWebCallTimeSet ts) @@ -53,7 +57,7 @@ namespace WebUtils public IHttp CreateHttp(string id) { - return new Http(id, log, defaultTimeSet, factoryOnClientCreated); + return new Http(id, log, timeSet, factoryOnClientCreated); } private static void DoNothing(HttpClient client) diff --git a/Framework/WebUtils/WebCallTimeSet.cs b/Framework/WebUtils/WebCallTimeSet.cs index 347a77b8..7189dde5 100644 --- a/Framework/WebUtils/WebCallTimeSet.cs +++ b/Framework/WebUtils/WebCallTimeSet.cs @@ -23,12 +23,12 @@ { public TimeSpan HttpCallTimeout() { - return TimeSpan.FromMinutes(2); + return TimeSpan.FromMinutes(5); } public TimeSpan HttpRetryTimeout() { - return TimeSpan.FromMinutes(5); + return TimeSpan.FromMinutes(20); } public TimeSpan HttpCallRetryDelay() diff --git a/ProjectPlugins/CodexClient/CodexAccess.cs b/ProjectPlugins/CodexClient/CodexAccess.cs index 12622bbc..3dc1a213 100644 --- a/ProjectPlugins/CodexClient/CodexAccess.cs +++ b/ProjectPlugins/CodexClient/CodexAccess.cs @@ -1,4 +1,5 @@ -using CodexOpenApi; +using System.Threading; +using CodexOpenApi; using Logging; using Newtonsoft.Json; using Utils; @@ -119,7 +120,7 @@ namespace CodexClient public Stream DownloadFile(string contentId) { - var fileResponse = OnCodex(api => api.DownloadNetworkStreamAsync(contentId)); + var fileResponse = OnCodexNoRetry(api => api.DownloadNetworkStreamAsync(contentId)); if (fileResponse.StatusCode != 200) throw new Exception("Download failed with StatusCode: " + fileResponse.StatusCode); return fileResponse.Stream; } @@ -212,15 +213,22 @@ namespace CodexClient processControl.DeleteDataDirFolder(); } - private T OnCodex(Func> action) + private T OnCodexNoRetry(Func> action) { - var result = httpFactory.CreateHttp(GetHttpId(), h => CheckContainerCrashed()).OnClient(client => CallCodex(client, action)); + var timeSet = httpFactory.WebCallTimeSet; + var noRetry = new Retry(nameof(OnCodexNoRetry), + maxTimeout: TimeSpan.FromSeconds(1.0), + sleepAfterFail: TimeSpan.FromSeconds(2.0), + onFail: f => { }, + failFast: true); + + var result = httpFactory.CreateHttp(GetHttpId(), h => CheckContainerCrashed()).OnClient(client => CallCodex(client, action), noRetry); return result; } - private T OnCodex(Func> action, Retry retry) + private T OnCodex(Func> action) { - var result = httpFactory.CreateHttp(GetHttpId(), h => CheckContainerCrashed()).OnClient(client => CallCodex(client, action), retry); + var result = httpFactory.CreateHttp(GetHttpId(), h => CheckContainerCrashed()).OnClient(client => CallCodex(client, action)); return result; } diff --git a/ProjectPlugins/CodexClient/CodexNode.cs b/ProjectPlugins/CodexClient/CodexNode.cs index 4a444117..40293da4 100644 --- a/ProjectPlugins/CodexClient/CodexNode.cs +++ b/ProjectPlugins/CodexClient/CodexNode.cs @@ -17,6 +17,7 @@ namespace CodexClient ContentId UploadFile(TrackedFile file); ContentId UploadFile(TrackedFile file, string contentType, string contentDisposition); TrackedFile? DownloadContent(ContentId contentId, string fileLabel = ""); + TrackedFile? DownloadContent(ContentId contentId, TimeSpan timeout, string fileLabel = ""); LocalDataset DownloadStreamless(ContentId cid); /// /// TODO: This will monitor the quota-used of the node until 'size' bytes are added. That's a very bad way @@ -77,7 +78,16 @@ namespace CodexClient public void Initialize() { - InitializePeerNodeId(); + // This is the moment we first connect to a codex node. Sometimes, Kubernetes takes a while to spin up the + // container. So we'll adding a custom, generous retry here. + var kubeSpinupRetry = new Retry("CodexNode_Initialize", + maxTimeout: TimeSpan.FromMinutes(10.0), + sleepAfterFail: TimeSpan.FromSeconds(10.0), + onFail: f => { }, + failFast: false); + + kubeSpinupRetry.Run(InitializePeerNodeId); + InitializeLogReplacements(); hooks.OnNodeStarted(this, peerId, nodeId); @@ -185,13 +195,18 @@ namespace CodexClient } public TrackedFile? DownloadContent(ContentId contentId, string fileLabel = "") + { + return DownloadContent(contentId, TimeSpan.FromMinutes(10.0), fileLabel); + } + + public TrackedFile? DownloadContent(ContentId contentId, TimeSpan timeout, string fileLabel = "") { var file = fileManager.CreateEmptyFile(fileLabel); hooks.OnFileDownloading(contentId); Log($"Downloading '{contentId}'..."); var logMessage = $"Downloaded '{contentId}' to '{file.Filename}'"; - var measurement = Stopwatch.Measure(log, logMessage, () => DownloadToFile(contentId.Id, file)); + var measurement = Stopwatch.Measure(log, logMessage, () => DownloadToFile(contentId.Id, file, timeout)); var size = file.GetFilesize(); transferSpeeds.AddDownloadSample(size, measurement); @@ -299,7 +314,7 @@ namespace CodexClient private void InitializePeerNodeId() { - var debugInfo = Time.Retry(codexAccess.GetDebugInfo, "ensure online"); + var debugInfo = codexAccess.GetDebugInfo(); if (!debugInfo.Version.IsValid()) { throw new Exception($"Invalid version information received from Codex node {GetName()}: {debugInfo.Version}"); @@ -339,13 +354,14 @@ namespace CodexClient .ToArray(); } - private void DownloadToFile(string contentId, TrackedFile file) + private void DownloadToFile(string contentId, TrackedFile file, TimeSpan timeout) { using var fileStream = File.OpenWrite(file.Filename); - var timeout = TimeSpan.FromMinutes(2.0); // todo: make this user-controllable. try { // Type of stream generated by openAPI client does not support timeouts. + // So we use a task and cancellation token to track our timeout manually. + var start = DateTime.UtcNow; var cts = new CancellationTokenSource(); var downloadTask = Task.Run(() => @@ -373,7 +389,7 @@ namespace CodexClient public void WaitUntilQuotaUsedIncreased(CodexSpace startSpace, ByteSize expectedIncreaseOfQuotaUsed) { - WaitUntilQuotaUsedIncreased(startSpace, expectedIncreaseOfQuotaUsed, TimeSpan.FromMinutes(2)); + WaitUntilQuotaUsedIncreased(startSpace, expectedIncreaseOfQuotaUsed, TimeSpan.FromMinutes(30)); } public void WaitUntilQuotaUsedIncreased( diff --git a/ProjectPlugins/CodexClient/CodexTypes.cs b/ProjectPlugins/CodexClient/CodexTypes.cs index f9a0d9d2..12e7c92c 100644 --- a/ProjectPlugins/CodexClient/CodexTypes.cs +++ b/ProjectPlugins/CodexClient/CodexTypes.cs @@ -66,7 +66,7 @@ namespace CodexClient public class Manifest { public string RootHash { get; set; } = string.Empty; - public ByteSize OriginalBytes { get; set; } = ByteSize.Zero; + public ByteSize DatasetSize { get; set; } = ByteSize.Zero; public ByteSize BlockSize { get; set; } = ByteSize.Zero; public bool Protected { get; set; } } diff --git a/ProjectPlugins/CodexClient/Mapper.cs b/ProjectPlugins/CodexClient/Mapper.cs index 35f872ef..69d586f8 100644 --- a/ProjectPlugins/CodexClient/Mapper.cs +++ b/ProjectPlugins/CodexClient/Mapper.cs @@ -208,7 +208,7 @@ namespace CodexClient return new Manifest { BlockSize = new ByteSize(Convert.ToInt64(manifest.BlockSize)), - OriginalBytes = new ByteSize(Convert.ToInt64(manifest.DatasetSize)), + DatasetSize = new ByteSize(Convert.ToInt64(manifest.DatasetSize)), RootHash = manifest.TreeCid, Protected = manifest.Protected }; diff --git a/ProjectPlugins/CodexClient/StoragePurchaseContract.cs b/ProjectPlugins/CodexClient/StoragePurchaseContract.cs index 7b37899a..b958da98 100644 --- a/ProjectPlugins/CodexClient/StoragePurchaseContract.cs +++ b/ProjectPlugins/CodexClient/StoragePurchaseContract.cs @@ -12,6 +12,7 @@ namespace CodexClient ContentId ContentId { get; } StoragePurchase? GetStatus(); void WaitForStorageContractSubmitted(); + void WaitForStorageContractExpired(); void WaitForStorageContractStarted(); void WaitForStorageContractFinished(); void WaitForContractFailed(IMarketplaceConfigInput config); @@ -81,6 +82,12 @@ namespace CodexClient AssertDuration(PendingToSubmitted, timeout, nameof(PendingToSubmitted)); } + public void WaitForStorageContractExpired() + { + var timeout = Purchase.Expiry + gracePeriod + gracePeriod; + WaitForStorageContractState(timeout, StoragePurchaseState.Cancelled); + } + public void WaitForStorageContractStarted() { var timeout = Purchase.Expiry + gracePeriod; @@ -124,7 +131,7 @@ namespace CodexClient ); } - WaitForStorageContractState(timeout, StoragePurchaseState.Failed); + WaitForStorageContractState(timeout, StoragePurchaseState.Errored); } private TimeSpan TimeNeededToFailEnoughProofsToFreeASlot(IMarketplaceConfigInput config) @@ -157,7 +164,7 @@ namespace CodexClient hooks.OnStorageContractUpdated(purchaseStatus); } - if (lastState == StoragePurchaseState.Errored) + if (desiredState != StoragePurchaseState.Errored && lastState == StoragePurchaseState.Errored) { FrameworkAssert.Fail("Contract errored: " + statusJson); } diff --git a/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainEvents.cs b/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainEvents.cs index 6bbfa3cc..983b6177 100644 --- a/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainEvents.cs +++ b/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainEvents.cs @@ -7,7 +7,7 @@ namespace CodexContractsPlugin.ChainMonitor { private ChainEvents( BlockInterval blockInterval, - Request[] requests, + StorageRequestedEventDTO[] requests, RequestFulfilledEventDTO[] fulfilled, RequestCancelledEventDTO[] cancelled, RequestFailedEventDTO[] failed, @@ -30,7 +30,7 @@ namespace CodexContractsPlugin.ChainMonitor } public BlockInterval BlockInterval { get; } - public Request[] Requests { get; } + public StorageRequestedEventDTO[] Requests { get; } public RequestFulfilledEventDTO[] Fulfilled { get; } public RequestCancelledEventDTO[] Cancelled { get; } public RequestFailedEventDTO[] Failed { get; } @@ -54,7 +54,7 @@ namespace CodexContractsPlugin.ChainMonitor { return new ChainEvents( events.BlockInterval, - events.GetStorageRequests(), + events.GetStorageRequestedEvents(), events.GetRequestFulfilledEvents(), events.GetRequestCancelledEvents(), events.GetRequestFailedEvents(), diff --git a/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainState.cs b/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainState.cs index 1f260c7d..1cca3f4e 100644 --- a/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainState.cs +++ b/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainState.cs @@ -1,6 +1,7 @@ using BlockchainUtils; using CodexContractsPlugin.Marketplace; using Logging; +using Nethereum.Hex.HexConvertors.Extensions; using System.Numerics; using Utils; @@ -13,7 +14,7 @@ namespace CodexContractsPlugin.ChainMonitor void OnRequestFulfilled(RequestEvent requestEvent); void OnRequestCancelled(RequestEvent requestEvent); void OnRequestFailed(RequestEvent requestEvent); - void OnSlotFilled(RequestEvent requestEvent, EthAddress host, BigInteger slotIndex); + void OnSlotFilled(RequestEvent requestEvent, EthAddress host, BigInteger slotIndex, bool isRepair); void OnSlotFreed(RequestEvent requestEvent, BigInteger slotIndex); void OnSlotReservationsFull(RequestEvent requestEvent, BigInteger slotIndex); void OnProofSubmitted(BlockTimeEntry block, string id); @@ -115,15 +116,22 @@ namespace CodexContractsPlugin.ChainMonitor ApplyTimeImplicitEvents(blockNumber, eventsUtc); } - private void ApplyEvent(Request request) + private void ApplyEvent(StorageRequestedEventDTO @event) { - if (requests.Any(r => Equal(r.Request.RequestId, request.RequestId))) - throw new Exception("Received NewRequest event for id that already exists."); + if (requests.Any(r => Equal(r.RequestId, @event.RequestId))) + { + var r = FindRequest(@event); + if (r == null) throw new Exception("ChainState is inconsistent. Received already-known requestId that's not known."); + if (@event.Block.BlockNumber != @event.Block.BlockNumber) throw new Exception("Same request found in different blocks."); + log.Log("Received the same request-creation event multiple times."); + return; + } - var newRequest = new ChainStateRequest(log, request, RequestState.New); + var request = contracts.GetRequest(@event.RequestId); + var newRequest = new ChainStateRequest(log, @event.RequestId, @event.Block, request, RequestState.New); requests.Add(newRequest); - handler.OnNewRequest(new RequestEvent(request.Block, newRequest)); + handler.OnNewRequest(new RequestEvent(@event.Block, newRequest)); } private void ApplyEvent(RequestFulfilledEventDTO @event) @@ -154,16 +162,18 @@ namespace CodexContractsPlugin.ChainMonitor { var r = FindRequest(@event); if (r == null) return; - r.Hosts.Add(@event.Host, (int)@event.SlotIndex); + var slotIndex = (int)@event.SlotIndex; + var isRepair = !r.Hosts.IsFilled(slotIndex) && r.Hosts.WasPreviouslyFilled(slotIndex); + r.Hosts.HostFillsSlot(@event.Host, slotIndex); r.Log($"[{@event.Block.BlockNumber}] SlotFilled (host:'{@event.Host}', slotIndex:{@event.SlotIndex})"); - handler.OnSlotFilled(new RequestEvent(@event.Block, r), @event.Host, @event.SlotIndex); + handler.OnSlotFilled(new RequestEvent(@event.Block, r), @event.Host, @event.SlotIndex, isRepair); } private void ApplyEvent(SlotFreedEventDTO @event) { var r = FindRequest(@event); if (r == null) return; - r.Hosts.RemoveHost((int)@event.SlotIndex); + r.Hosts.SlotFreed((int)@event.SlotIndex); r.Log($"[{@event.Block.BlockNumber}] SlotFreed (slotIndex:{@event.SlotIndex})"); handler.OnSlotFreed(new RequestEvent(@event.Block, r), @event.SlotIndex); } @@ -196,22 +206,22 @@ namespace CodexContractsPlugin.ChainMonitor } } - private ChainStateRequest? FindRequest(IHasRequestId request) + private ChainStateRequest? FindRequest(IHasBlockAndRequestId hasBoth) { - var r = requests.SingleOrDefault(r => Equal(r.Request.RequestId, request.RequestId)); + var r = requests.SingleOrDefault(r => Equal(r.RequestId, hasBoth.RequestId)); if (r != null) return r; try { - var req = contracts.GetRequest(request.RequestId); - var state = contracts.GetRequestState(req); - var newRequest = new ChainStateRequest(log, req, state); + var req = contracts.GetRequest(hasBoth.RequestId); + var state = contracts.GetRequestState(hasBoth.RequestId); + var newRequest = new ChainStateRequest(log, hasBoth.RequestId, hasBoth.Block, req, state); requests.Add(newRequest); return newRequest; } catch (Exception ex) { - var msg = "Failed to get request from chain: " + ex; + var msg = $"Failed to get request with id '{hasBoth.RequestId.ToHex()}' from chain: {ex}"; log.Error(msg); handler.OnError(msg); return null; diff --git a/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainStateChangeHandlerMux.cs b/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainStateChangeHandlerMux.cs index a9a46a25..b5b4ade4 100644 --- a/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainStateChangeHandlerMux.cs +++ b/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainStateChangeHandlerMux.cs @@ -38,9 +38,9 @@ namespace CodexContractsPlugin.ChainMonitor foreach (var handler in Handlers) handler.OnRequestFulfilled(requestEvent); } - public void OnSlotFilled(RequestEvent requestEvent, EthAddress host, BigInteger slotIndex) + public void OnSlotFilled(RequestEvent requestEvent, EthAddress host, BigInteger slotIndex, bool isRepair) { - foreach (var handler in Handlers) handler.OnSlotFilled(requestEvent, host, slotIndex); + foreach (var handler in Handlers) handler.OnSlotFilled(requestEvent, host, slotIndex, isRepair); } public void OnSlotFreed(RequestEvent requestEvent, BigInteger slotIndex) diff --git a/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainStateRequest.cs b/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainStateRequest.cs index 408d2746..8d570775 100644 --- a/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainStateRequest.cs +++ b/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainStateRequest.cs @@ -1,11 +1,15 @@ -using CodexContractsPlugin.Marketplace; +using BlockchainUtils; +using CodexContractsPlugin.Marketplace; using Logging; +using Nethereum.Hex.HexConvertors.Extensions; using Utils; namespace CodexContractsPlugin.ChainMonitor { public interface IChainStateRequest { + byte[] RequestId { get; } + public BlockTimeEntry Block { get; } Request Request { get; } RequestState State { get; } DateTime ExpiryUtc { get; } @@ -18,21 +22,27 @@ namespace CodexContractsPlugin.ChainMonitor { private readonly ILog log; - public ChainStateRequest(ILog log, Request request, RequestState state) + public ChainStateRequest(ILog log, byte[] requestId, BlockTimeEntry block, Request request, RequestState state) { + if (requestId == null || requestId.Length != 32) throw new ArgumentException(nameof(requestId)); + this.log = log; + RequestId = requestId; + Block = block; Request = request; State = state; - ExpiryUtc = request.Block.Utc + TimeSpan.FromSeconds((double)request.Expiry); - FinishedUtc = request.Block.Utc + TimeSpan.FromSeconds((double)request.Ask.Duration); + ExpiryUtc = Block.Utc + TimeSpan.FromSeconds((double)request.Expiry); + FinishedUtc = Block.Utc + TimeSpan.FromSeconds((double)request.Ask.Duration); - Log($"[{request.Block.BlockNumber}] Created as {State}."); + Log($"[{Block.BlockNumber}] Created as {State}."); Client = new EthAddress(request.Client); Hosts = new RequestHosts(); } + public byte[] RequestId { get; } + public BlockTimeEntry Block { get; } public Request Request { get; } public RequestState State { get; private set; } public DateTime ExpiryUtc { get; } @@ -48,20 +58,32 @@ namespace CodexContractsPlugin.ChainMonitor public void Log(string msg) { - log.Log($"Request '{Request.Id}': {msg}"); + log.Log($"Request '{RequestId.ToHex()}': {msg}"); } } public class RequestHosts { private readonly Dictionary hosts = new Dictionary(); + private readonly List filled = new List(); - public void Add(EthAddress host, int index) + public void HostFillsSlot(EthAddress host, int index) { hosts.Add(index, host); + filled.Add(index); + } + + public bool IsFilled(int index) + { + return hosts.ContainsKey(index); + } + + public bool WasPreviouslyFilled(int index) + { + return filled.Contains(index); } - public void RemoveHost(int index) + public void SlotFreed(int index) { hosts.Remove(index); } diff --git a/ProjectPlugins/CodexContractsPlugin/ChainMonitor/DoNothingChainEventHandler.cs b/ProjectPlugins/CodexContractsPlugin/ChainMonitor/DoNothingChainEventHandler.cs index 43407998..db21d52d 100644 --- a/ProjectPlugins/CodexContractsPlugin/ChainMonitor/DoNothingChainEventHandler.cs +++ b/ProjectPlugins/CodexContractsPlugin/ChainMonitor/DoNothingChainEventHandler.cs @@ -26,7 +26,7 @@ namespace CodexContractsPlugin.ChainMonitor { } - public void OnSlotFilled(RequestEvent requestEvent, EthAddress host, BigInteger slotIndex) + public void OnSlotFilled(RequestEvent requestEvent, EthAddress host, BigInteger slotIndex, bool isRepair) { } @@ -46,4 +46,48 @@ namespace CodexContractsPlugin.ChainMonitor { } } + + public class DoNothingThrowingChainEventHandler : IChainStateChangeHandler + { + public void OnNewRequest(RequestEvent requestEvent) + { + } + + public void OnRequestCancelled(RequestEvent requestEvent) + { + } + + public void OnRequestFailed(RequestEvent requestEvent) + { + } + + public void OnRequestFinished(RequestEvent requestEvent) + { + } + + public void OnRequestFulfilled(RequestEvent requestEvent) + { + } + + public void OnSlotFilled(RequestEvent requestEvent, EthAddress host, BigInteger slotIndex, bool isRepair) + { + } + + public void OnSlotFreed(RequestEvent requestEvent, BigInteger slotIndex) + { + } + + public void OnSlotReservationsFull(RequestEvent requestEvent, BigInteger slotIndex) + { + } + + public void OnError(string msg) + { + throw new Exception(msg); + } + + public void OnProofSubmitted(BlockTimeEntry block, string id) + { + } + } } diff --git a/ProjectPlugins/CodexContractsPlugin/ChainMonitor/PeriodMonitor.cs b/ProjectPlugins/CodexContractsPlugin/ChainMonitor/PeriodMonitor.cs index f140dd97..2fbd9a97 100644 --- a/ProjectPlugins/CodexContractsPlugin/ChainMonitor/PeriodMonitor.cs +++ b/ProjectPlugins/CodexContractsPlugin/ChainMonitor/PeriodMonitor.cs @@ -1,4 +1,5 @@ using Logging; +using Nethereum.Hex.HexConvertors.Extensions; using Utils; namespace CodexContractsPlugin.ChainMonitor @@ -46,7 +47,7 @@ namespace CodexContractsPlugin.ChainMonitor { for (ulong slotIndex = 0; slotIndex < request.Request.Ask.Slots; slotIndex++) { - var state = contracts.GetProofState(request.Request, slotIndex, lastBlockInPeriod, periodNumber); + var state = contracts.GetProofState(request.RequestId, slotIndex, lastBlockInPeriod, periodNumber); total++; if (state.Required) @@ -116,7 +117,7 @@ namespace CodexContractsPlugin.ChainMonitor var missed = "None"; if (MissedProofs.Length > 0) { - missed = string.Join("+", MissedProofs.Select(p => $"{p.FormatHost()} missed {p.Request.Request.Id} slot {p.SlotIndex}")); + missed = string.Join("+", MissedProofs.Select(p => $"{p.FormatHost()} missed {p.Request.RequestId.ToHex()} slot {p.SlotIndex}")); } return $"Period:{PeriodNumber}=[Slots:{TotalNumSlots},ProofsRequired:{TotalProofsRequired},ProofsMissed:{missed}]"; } diff --git a/ProjectPlugins/CodexContractsPlugin/CodexContractsAccess.cs b/ProjectPlugins/CodexContractsPlugin/CodexContractsAccess.cs index ef8d2003..dcc411cf 100644 --- a/ProjectPlugins/CodexContractsPlugin/CodexContractsAccess.cs +++ b/ProjectPlugins/CodexContractsPlugin/CodexContractsAccess.cs @@ -20,15 +20,18 @@ namespace CodexContractsPlugin string MintTestTokens(EthAddress ethAddress, TestToken testTokens); TestToken GetTestTokenBalance(IHasEthAddress owner); TestToken GetTestTokenBalance(EthAddress ethAddress); + void TransferTestTokens(EthAddress to, TestToken amount); ICodexContractsEvents GetEvents(TimeRange timeRange); ICodexContractsEvents GetEvents(BlockInterval blockInterval); - EthAddress? GetSlotHost(Request storageRequest, decimal slotIndex); - RequestState GetRequestState(Request request); + EthAddress? GetSlotHost(byte[] requestId, decimal slotIndex); + RequestState GetRequestState(byte[] requestId); Request GetRequest(byte[] requestId); ulong GetPeriodNumber(DateTime utc); void WaitUntilNextPeriod(); - ProofState GetProofState(Request storageRequest, decimal slotIndex, ulong blockNumber, ulong period); + ProofState GetProofState(byte[] requestId, decimal slotIndex, ulong blockNumber, ulong period); + + ICodexContracts WithDifferentGeth(IGethNode node); } public class ProofState @@ -93,6 +96,11 @@ namespace CodexContractsPlugin return balance.TstWei(); } + public void TransferTestTokens(EthAddress to, TestToken amount) + { + StartInteraction().TransferTestTokens(Deployment.TokenAddress, to.Address, amount.TstWei); + } + public ICodexContractsEvents GetEvents(TimeRange timeRange) { return GetEvents(gethNode.ConvertTimeRangeToBlockRange(timeRange)); @@ -103,9 +111,9 @@ namespace CodexContractsPlugin return new CodexContractsEvents(log, gethNode, Deployment, blockInterval); } - public EthAddress? GetSlotHost(Request storageRequest, decimal slotIndex) + public EthAddress? GetSlotHost(byte[] requestId, decimal slotIndex) { - var slotId = GetSlotId(storageRequest, slotIndex); + var slotId = GetSlotId(requestId, slotIndex); var func = new GetHostFunction { SlotId = slotId @@ -115,17 +123,22 @@ namespace CodexContractsPlugin return new EthAddress(address); } - public RequestState GetRequestState(Request request) + 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 = request.RequestId + RequestId = requestId }; return gethNode.Call(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 @@ -152,9 +165,9 @@ namespace CodexContractsPlugin Thread.Sleep(TimeSpan.FromSeconds(secondsLeft + 1)); } - public ProofState GetProofState(Request storageRequest, decimal slotIndex, ulong blockNumber, ulong period) + public ProofState GetProofState(byte[] requestId, decimal slotIndex, ulong blockNumber, ulong period) { - var slotId = GetSlotId(storageRequest, slotIndex); + var slotId = GetSlotId(requestId, slotIndex); var required = IsProofRequired(slotId, blockNumber); if (!required) return new ProofState(false, false); @@ -163,11 +176,16 @@ namespace CodexContractsPlugin return new ProofState(required, missing); } - private byte[] GetSlotId(Request request, decimal slotIndex) + public ICodexContracts WithDifferentGeth(IGethNode node) + { + return new CodexContractsAccess(log, node, Deployment); + } + + private byte[] GetSlotId(byte[] requestId, decimal slotIndex) { var encoder = new ABIEncode(); var encoded = encoder.GetABIEncoded( - new ABIValue("bytes32", request.RequestId), + new ABIValue("bytes32", requestId), new ABIValue("uint256", slotIndex.ToBig()) ); @@ -193,7 +211,7 @@ namespace CodexContractsPlugin SlotId = slotId, Period = period }; - gethNode.Call(Deployment.MarketplaceAddress, funcB, blockNumber); + gethNode.Call(Deployment.MarketplaceAddress, funcB, blockNumber); } catch (AggregateException exc) { diff --git a/ProjectPlugins/CodexContractsPlugin/CodexContractsContainerRecipe.cs b/ProjectPlugins/CodexContractsPlugin/CodexContractsContainerRecipe.cs index eadbf4e9..09c5b829 100644 --- a/ProjectPlugins/CodexContractsPlugin/CodexContractsContainerRecipe.cs +++ b/ProjectPlugins/CodexContractsPlugin/CodexContractsContainerRecipe.cs @@ -7,7 +7,7 @@ namespace CodexContractsPlugin { public class CodexContractsContainerRecipe : ContainerRecipeFactory { - public const string MarketplaceAddressFilename = "/hardhat/deployments/codexdisttestnetwork/Marketplace.json"; + public const string DeployedAddressesFilename = "/hardhat/ignition/deployments/chain-789988/deployed_addresses.json"; public const string MarketplaceArtifactFilename = "/hardhat/artifacts/contracts/Marketplace.sol/Marketplace.json"; private readonly DebugInfoVersion versionInfo; diff --git a/ProjectPlugins/CodexContractsPlugin/CodexContractsEvents.cs b/ProjectPlugins/CodexContractsPlugin/CodexContractsEvents.cs index 6adf4ae7..13850767 100644 --- a/ProjectPlugins/CodexContractsPlugin/CodexContractsEvents.cs +++ b/ProjectPlugins/CodexContractsPlugin/CodexContractsEvents.cs @@ -11,7 +11,7 @@ namespace CodexContractsPlugin public interface ICodexContractsEvents { BlockInterval BlockInterval { get; } - Request[] GetStorageRequests(); + StorageRequestedEventDTO[] GetStorageRequestedEvents(); RequestFulfilledEventDTO[] GetRequestFulfilledEvents(); RequestCancelledEventDTO[] GetRequestCancelledEvents(); RequestFailedEventDTO[] GetRequestFailedEvents(); @@ -38,18 +38,10 @@ namespace CodexContractsPlugin public BlockInterval BlockInterval { get; } - public Request[] GetStorageRequests() + public StorageRequestedEventDTO[] GetStorageRequestedEvents() { var events = gethNode.GetEvents(deployment.MarketplaceAddress, BlockInterval); - var i = new ContractInteractions(log, gethNode); - return events.Select(e => - { - var requestEvent = i.GetRequest(deployment.MarketplaceAddress, e.Event.RequestId); - var result = requestEvent.ReturnValue1; - result.Block = GetBlock(e.Log.BlockNumber.ToUlong()); - result.RequestId = e.Event.RequestId; - return result; - }).ToArray(); + return events.Select(SetBlockOnEvent).ToArray(); } public RequestFulfilledEventDTO[] GetRequestFulfilledEvents() diff --git a/ProjectPlugins/CodexContractsPlugin/CodexContractsStarter.cs b/ProjectPlugins/CodexContractsPlugin/CodexContractsStarter.cs index c7e900be..63032158 100644 --- a/ProjectPlugins/CodexContractsPlugin/CodexContractsStarter.cs +++ b/ProjectPlugins/CodexContractsPlugin/CodexContractsStarter.cs @@ -35,12 +35,15 @@ namespace CodexContractsPlugin var container = containers.Containers[0]; Log("Container started."); + var watcher = workflow.CreateCrashWatcher(container); + watcher.Start(); try { var result = DeployContract(container, workflow, gethNode); workflow.Stop(containers, waitTillStopped: false); + watcher.Stop(); Log("Container stopped."); return result; } @@ -71,11 +74,16 @@ namespace CodexContractsPlugin var extractor = new ContractsContainerInfoExtractor(tools.GetLog(), workflow, container); var marketplaceAddress = extractor.ExtractMarketplaceAddress(); + if (string.IsNullOrEmpty(marketplaceAddress)) throw new Exception("Marketplace address not received."); var (abi, bytecode) = extractor.ExtractMarketplaceAbiAndByteCode(); + if (string.IsNullOrEmpty(abi)) throw new Exception("ABI not received."); + if (string.IsNullOrEmpty(bytecode)) throw new Exception("bytecode not received."); EnsureCompatbility(abi, bytecode); var interaction = new ContractInteractions(tools.GetLog(), gethNode); var tokenAddress = interaction.GetTokenAddress(marketplaceAddress); + if (string.IsNullOrEmpty(tokenAddress)) throw new Exception("Token address not received."); + Log("TokenAddress: " + tokenAddress); Log("Extract completed. Checking sync..."); diff --git a/ProjectPlugins/CodexContractsPlugin/ContractInteractions.cs b/ProjectPlugins/CodexContractsPlugin/ContractInteractions.cs index 289db637..4735b133 100644 --- a/ProjectPlugins/CodexContractsPlugin/ContractInteractions.cs +++ b/ProjectPlugins/CodexContractsPlugin/ContractInteractions.cs @@ -2,8 +2,6 @@ using CodexContractsPlugin.Marketplace; using GethPlugin; using Logging; -using Nethereum.ABI.FunctionEncoding.Attributes; -using Nethereum.Contracts; using Nethereum.Hex.HexConvertors.Extensions; using System.Numerics; using Utils; @@ -33,9 +31,9 @@ namespace CodexContractsPlugin try { log.Debug(tokenAddress); - var function = new GetTokenNameFunction(); + var function = new NameFunction(); - return gethNode.Call(tokenAddress, function); + return gethNode.Call(tokenAddress, function); } catch (Exception ex) { @@ -53,12 +51,24 @@ namespace CodexContractsPlugin public decimal GetBalance(string tokenAddress, string account) { log.Debug($"({tokenAddress}) {account}"); - var function = new GetTokenBalanceFunction + var function = new BalanceOfFunction { - Owner = account + Account = account }; - return gethNode.Call(tokenAddress, function).ToDecimal(); + return gethNode.Call(tokenAddress, function).ToDecimal(); + } + + public void TransferTestTokens(string tokenAddress, string toAccount, BigInteger amount) + { + log.Debug($"({tokenAddress}) {toAccount} {amount}"); + var function = new TransferFunction + { + To = toAccount, + Value = amount + }; + + gethNode.SendTransaction(tokenAddress, function); } public GetRequestOutputDTO GetRequest(string marketplaceAddress, byte[] requestId) @@ -90,7 +100,7 @@ namespace CodexContractsPlugin log.Debug($"({tokenAddress}) {amount} --> {account}"); if (string.IsNullOrEmpty(account)) throw new ArgumentException("Invalid arguments for MintTestTokens"); - var function = new MintTokensFunction + var function = new MintFunction { Holder = account, Amount = amount @@ -110,26 +120,4 @@ namespace CodexContractsPlugin return gethNode.IsContractAvailable(marketplaceAbi, marketplaceAddress); } } - - [Function("name", "string")] - public class GetTokenNameFunction : FunctionMessage - { - } - - [Function("mint")] - public class MintTokensFunction : FunctionMessage - { - [Parameter("address", "holder", 1)] - public string Holder { get; set; } = string.Empty; - - [Parameter("uint256", "amount", 2)] - public BigInteger Amount { get; set; } - } - - [Function("balanceOf", "uint256")] - public class GetTokenBalanceFunction : FunctionMessage - { - [Parameter("address", "owner", 1)] - public string Owner { get; set; } = string.Empty; - } } diff --git a/ProjectPlugins/CodexContractsPlugin/ContractsContainerInfoExtractor.cs b/ProjectPlugins/CodexContractsPlugin/ContractsContainerInfoExtractor.cs index 0cf31528..7e2b94cb 100644 --- a/ProjectPlugins/CodexContractsPlugin/ContractsContainerInfoExtractor.cs +++ b/ProjectPlugins/CodexContractsPlugin/ContractsContainerInfoExtractor.cs @@ -27,7 +27,7 @@ namespace CodexContractsPlugin var marketplaceAddress = Retry(FetchMarketplaceAddress); if (string.IsNullOrEmpty(marketplaceAddress)) throw new InvalidOperationException("Unable to fetch marketplace account from codex-contracts node. Test infra failure."); - log.Debug("Got MarketplaceAddress: " + marketplaceAddress); + log.Log("MarketplaceAddress: " + marketplaceAddress); return marketplaceAddress; } @@ -43,9 +43,10 @@ namespace CodexContractsPlugin private string FetchMarketplaceAddress() { - var json = workflow.ExecuteCommand(container, "cat", CodexContractsContainerRecipe.MarketplaceAddressFilename); - var marketplace = JsonConvert.DeserializeObject(json); - return marketplace!.address; + var json = workflow.ExecuteCommand(container, "cat", CodexContractsContainerRecipe.DeployedAddressesFilename); + json = json.Replace("#", "_"); + var addresses = JsonConvert.DeserializeObject(json); + return addresses!.Marketplace_Marketplace; } private (string, string) FetchMarketplaceAbiAndByteCode() @@ -67,8 +68,10 @@ namespace CodexContractsPlugin } } - public class MarketplaceJson + public class DeployedAddressesJson { - public string address { get; set; } = string.Empty; + public string Token_TestToken { get; set; } = string.Empty; + public string Verifier_Groth16Verifier { get; set; } = string.Empty; + public string Marketplace_Marketplace { get; set; } = string.Empty; } } diff --git a/ProjectPlugins/CodexContractsPlugin/Marketplace/Customizations.cs b/ProjectPlugins/CodexContractsPlugin/Marketplace/Customizations.cs index 1ac64325..7af43cfb 100644 --- a/ProjectPlugins/CodexContractsPlugin/Marketplace/Customizations.cs +++ b/ProjectPlugins/CodexContractsPlugin/Marketplace/Customizations.cs @@ -17,48 +17,45 @@ namespace CodexContractsPlugin.Marketplace byte[] RequestId { get; set; } } + public interface IHasBlockAndRequestId : IHasBlock, IHasRequestId + { + } + public interface IHasSlotIndex { ulong SlotIndex { get; set; } } - public partial class Request : RequestBase, IHasBlock, IHasRequestId + public partial class Request { - [JsonIgnore] - public BlockTimeEntry Block { get; set; } - public byte[] RequestId { get; set; } - public EthAddress ClientAddress { get { return new EthAddress(Client); } } - - [JsonIgnore] - public string Id - { - get - { - return BitConverter.ToString(RequestId).Replace("-", "").ToLowerInvariant(); - } - } } - public partial class RequestFulfilledEventDTO : IHasBlock, IHasRequestId + public partial class StorageRequestedEventDTO : IHasBlockAndRequestId { [JsonIgnore] public BlockTimeEntry Block { get; set; } } - public partial class RequestCancelledEventDTO : IHasBlock, IHasRequestId + public partial class RequestFulfilledEventDTO : IHasBlockAndRequestId { [JsonIgnore] public BlockTimeEntry Block { get; set; } } - public partial class RequestFailedEventDTO : IHasBlock, IHasRequestId + public partial class RequestCancelledEventDTO : IHasBlockAndRequestId { [JsonIgnore] public BlockTimeEntry Block { get; set; } } - public partial class SlotFilledEventDTO : IHasBlock, IHasRequestId, IHasSlotIndex + public partial class RequestFailedEventDTO : IHasBlockAndRequestId + { + [JsonIgnore] + public BlockTimeEntry Block { get; set; } + } + + public partial class SlotFilledEventDTO : IHasBlockAndRequestId, IHasSlotIndex { [JsonIgnore] public BlockTimeEntry Block { get; set; } @@ -70,13 +67,13 @@ namespace CodexContractsPlugin.Marketplace } } - public partial class SlotFreedEventDTO : IHasBlock, IHasRequestId, IHasSlotIndex + public partial class SlotFreedEventDTO : IHasBlockAndRequestId, IHasSlotIndex { [JsonIgnore] public BlockTimeEntry Block { get; set; } } - public partial class SlotReservationsFullEventDTO : IHasBlock, IHasRequestId, IHasSlotIndex + public partial class SlotReservationsFullEventDTO : IHasBlockAndRequestId, IHasSlotIndex { [JsonIgnore] public BlockTimeEntry Block { get; set; } @@ -88,7 +85,7 @@ namespace CodexContractsPlugin.Marketplace public BlockTimeEntry Block { get; set; } } - public partial class ReserveSlotFunction : IHasBlock, IHasRequestId, IHasSlotIndex + public partial class ReserveSlotFunction : IHasBlockAndRequestId, IHasSlotIndex { [JsonIgnore] public BlockTimeEntry Block { get; set; } @@ -96,8 +93,23 @@ namespace CodexContractsPlugin.Marketplace public partial class MarketplaceConfig : IMarketplaceConfigInput { - public int MaxNumberOfSlashes => this.Collateral.MaxNumberOfSlashes; - public TimeSpan PeriodDuration => TimeSpan.FromSeconds(this.Proofs.Period); + public int MaxNumberOfSlashes + { + get + { + if (Collateral == null) return -1; + return Collateral.MaxNumberOfSlashes; + } + } + + public TimeSpan PeriodDuration + { + get + { + if (Proofs == null) return TimeSpan.MinValue; + return TimeSpan.FromSeconds(this.Proofs.Period); + } + } } } #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. diff --git a/ProjectPlugins/CodexContractsPlugin/Marketplace/Marketplace.cs b/ProjectPlugins/CodexContractsPlugin/Marketplace/Marketplace.cs index 2f2c8ede..d83821ed 100644 --- a/ProjectPlugins/CodexContractsPlugin/Marketplace/Marketplace.cs +++ b/ProjectPlugins/CodexContractsPlugin/Marketplace/Marketplace.cs @@ -15,7 +15,7 @@ namespace CodexContractsPlugin.Marketplace public class MarketplaceDeploymentBase : ContractDeploymentMessage { - public static string BYTECODE = "0x60c060405234801561001057600080fd5b50604051614faf380380614faf83398101604081905261002f9161053b565b602083015180516040850151516001805460ff191660ff90921691909117905582906001600160401b03811660000361007b5760405163015536c760e51b815260040160405180910390fd5b6001600160401b031660805261010043116100a9576040516338f5f66160e11b815260040160405180910390fd5b8151600280546020850151604086015160608701516001600160401b039586166001600160801b0319909416939093176801000000000000000095909216949094021761ffff60801b1916600160801b60ff9485160260ff60881b191617600160881b9390911692909202919091178155608083015183919060039061012f90826106d9565b5050600480546001600160a01b0319166001600160a01b0393841617905550831660a05250825151606460ff909116111561017d576040516302bd816360e41b815260040160405180910390fd5b606483600001516040015160ff1611156101aa576040516354e5e0ab60e11b815260040160405180910390fd5b825160408101516020909101516064916101c391610797565b60ff1611156101e5576040516317ff9d0f60e21b815260040160405180910390fd5b82518051600b805460208085015160408087015160609788015160ff90811663010000000263ff0000001992821662010000029290921663ffff0000199482166101000261ffff1990971698821698909817959095179290921695909517178355808801518051600c80549383015196830151978301518516600160881b0260ff60881b1998909516600160801b029790971661ffff60801b196001600160401b0397881668010000000000000000026001600160801b031990951697909216969096179290921791909116939093171783556080820151869391929190600d906102d090826106d9565b50505060408201515160038201805460ff191660ff909216919091179055606090910151600490910180546001600160401b0319166001600160401b03909216919091179055506107c8915050565b634e487b7160e01b600052604160045260246000fd5b60405160a081016001600160401b03811182821017156103575761035761031f565b60405290565b604051608081016001600160401b03811182821017156103575761035761031f565b604051601f8201601f191681016001600160401b03811182821017156103a7576103a761031f565b604052919050565b805160ff811681146103c057600080fd5b919050565b80516001600160401b03811681146103c057600080fd5b600060a082840312156103ee57600080fd5b6103f6610335565b9050610401826103c5565b815261040f602083016103c5565b6020820152610420604083016103af565b6040820152610431606083016103af565b606082015260808201516001600160401b0381111561044f57600080fd5b8201601f8101841361046057600080fd5b80516001600160401b038111156104795761047961031f565b61048c601f8201601f191660200161037f565b8181528560208385010111156104a157600080fd5b60005b828110156104c0576020818501810151838301820152016104a4565b5060006020838301015280608085015250505092915050565b6000602082840312156104eb57600080fd5b604051602081016001600160401b038111828210171561050d5761050d61031f565b60405290508061051c836103af565b905292915050565b80516001600160a01b03811681146103c057600080fd5b60008060006060848603121561055057600080fd5b83516001600160401b0381111561056657600080fd5b840180860360e081121561057957600080fd5b61058161035d565b608082121561058f57600080fd5b61059761035d565b91506105a2836103af565b82526105b0602084016103af565b60208301526105c1604084016103af565b60408301526105d2606084016103af565b60608301529081526080820151906001600160401b038211156105f457600080fd5b610600888385016103dc565b60208201526106128860a085016104d9565b604082015261062360c084016103c5565b6060820152945061063991505060208501610524565b915061064760408501610524565b90509250925092565b600181811c9082168061066457607f821691505b60208210810361068457634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156106d457806000526020600020601f840160051c810160208510156106b15750805b601f840160051c820191505b818110156106d157600081556001016106bd565b50505b505050565b81516001600160401b038111156106f2576106f261031f565b610706816107008454610650565b8461068a565b6020601f82116001811461073a57600083156107225750848201515b600019600385901b1c1916600184901b1784556106d1565b600084815260208120601f198516915b8281101561076a578785015182556020948501946001909201910161074a565b50848210156107885786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b60ff81811683821602908116908181146107c157634e487b7160e01b600052601160045260246000fd5b5092915050565b60805160a05161478a610825600039600081816104dd01528181610f76015281816120390152818161266801528181612718015281816128a7015281816129570152612d790152600081816135b801526138bd015261478a6000f3fe608060405234801561001057600080fd5b50600436106101cf5760003560e01c80636e2b54ee11610104578063c0cc4add116100a2578063e8aa0a0711610071578063e8aa0a071461047f578063f752196b14610492578063fb1e61ca146104bb578063fc0c546a146104db57600080fd5b8063c0cc4add14610433578063c5d4335114610446578063d02bbe3314610459578063d1bb36b61461046c57600080fd5b8063a29c29a4116100de578063a29c29a4146103bd578063a3a0807e146103d0578063b396dc79146103f3578063be5cdc481461041357600080fd5b80636e2b54ee1461038f5780639777b72c146103a257806399b6da0c146103aa57600080fd5b8063329b5a0b1161017157806351a766421161014b57806351a766421461030e5780635da73835146103215780636b00c8cf146103365780636c70bee91461037a57600080fd5b8063329b5a0b146102a3578063458d2bf1146102d65780634641dce6146102e957600080fd5b806312827602116101ad57806312827602146102395780631d873c1b1461024c578063237d84821461025f57806326d6f8341461027257600080fd5b806302fa8e65146101d457806305b90773146102045780630aefaabe14610224575b600080fd5b6101e76101e2366004613958565b610501565b6040516001600160401b0390911681526020015b60405180910390f35b610217610212366004613958565b6105df565b6040516101fb9190613987565b6102376102323660046139b6565b610702565b005b610237610247366004613a1d565b610895565b61023761025a366004613a60565b610966565b61023761026d366004613a1d565b610e18565b610295610280366004613958565b60009081526012602052604090206003015490565b6040519081526020016101fb565b6101e76102b1366004613958565b600090815260116020526040902060020154600160c01b90046001600160401b031690565b6102956102e4366004613958565b611066565b6102fc6102f7366004613958565b61107f565b60405160ff90911681526020016101fb565b61029561031c366004613958565b611092565b6103296110f1565b6040516101fb9190613aa0565b610362610344366004613958565b6000908152601260205260409020600401546001600160a01b031690565b6040516001600160a01b0390911681526020016101fb565b610382611118565b6040516101fb9190613b7e565b61023761039d366004613958565b61128f565b61032961129c565b6102376103b8366004613c06565b6112bb565b6102376103cb366004613958565b611801565b6103e36103de366004613958565b611853565b60405190151581526020016101fb565b610406610401366004613958565b61188f565b6040516101fb9190613d35565b610426610421366004613958565b611b71565b6040516101fb9190613d70565b6103e3610441366004613958565b611c3f565b610237610454366004613d84565b611c52565b6103e3610467366004613a1d565b6120d3565b61023761047a366004613a1d565b612175565b61023761048d366004613da9565b6121ba565b6101e76104a0366004613958565b6000908152600660205260409020546001600160401b031690565b6104ce6104c9366004613958565b612333565b6040516101fb9190613dd7565b7f0000000000000000000000000000000000000000000000000000000000000000610362565b60008061050d836105df565b9050600081600481111561052357610523613971565b14806105405750600181600481111561053e5761053e613971565b145b1561056c575050600090815260116020526040902060020154600160801b90046001600160401b031690565b600281600481111561058057610580613971565b036105ac575050600090815260116020526040902060020154600160c01b90046001600160401b031690565b6000838152601160205260409020600201546105d890600160801b90046001600160401b031642612549565b9392505050565b60008181526010602052604081205482906001600160a01b031661061657604051635eeb253d60e11b815260040160405180910390fd5b600083815260116020526040812090815460ff16600481111561063b5761063b613971565b14801561067a5750600084815260116020526040902060020154600160c01b90046001600160401b03166001600160401b0316426001600160401b0316115b156106895760029250506106fc565b6001815460ff1660048111156106a1576106a1613971565b14806106c257506000815460ff1660048111156106c0576106c0613971565b145b80156106e6575060028101546001600160401b03600160801b909104811642909116115b156106f55760039250506106fc565b5460ff1691505b50919050565b826000808281526012602052604090205460ff16600681111561072757610727613971565b0361074557604051638b41ec7f60e01b815260040160405180910390fd5b600084815260126020526040902060048101546001600160a01b03163314610799576040517f57a6f4e900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006107a486611b71565b905060048160068111156107ba576107ba613971565b036107f1576040517fc2cbf77700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600281600681111561080557610805613971565b0361081f5761081a8260010154878787612559565b61088d565b600581600681111561083357610833613971565b036108485761081a82600101548787876127a2565b600381600681111561085c5761085c613971565b0361086b5761081a33876129eb565b600181600681111561087f5761087f613971565b0361088d5761088d86612a0d565b505050505050565b61089f82826120d3565b6108d5576040517f424a04ab00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006108e18383612c5d565b60008181526020819052604090209091506108fc9033612ca2565b50600154600082815260208190526040902060ff9091169061091d90612cb7565b03610961576040516001600160401b038316815283907fc8e6c955744189a19222ec226b72ac1435d88d5745252dac56e6f679f64c037a9060200160405180910390a25b505050565b60008381526010602052604090205483906001600160a01b031661099d57604051635eeb253d60e11b815260040160405180910390fd5b600084815260106020526040902060048101546001600160401b03908116908516106109f5576040517f3b920b8800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610a018686612c5d565b6000818152602081905260409020909150610a1c9033612cc1565b610a52576040517fd651ce1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000818152601260209081526040808320600181018a90556002810180546fffffffffffffffff00000000000000001916600160401b6001600160401b038c1602179055898452601190925282209091610aab84611b71565b6006811115610abc57610abc613971565b14158015610ae457506006610ad084611b71565b6006811115610ae157610ae1613971565b14155b15610b1b576040517fff556acf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60048201805473ffffffffffffffffffffffffffffffffffffffff1916331790556002820180546001600160401b03421667ffffffffffffffff19909116179055610b8c83600090815260056020526040902080546001600160401b03421667ffffffffffffffff19909116179055565b610b9683876121ba565b60028101805460019190600090610bb79084906001600160401b0316613e00565b92506101000a8154816001600160401b0302191690836001600160401b03160217905550610bfc888360020160009054906101000a90046001600160401b0316612ce3565b816001016000828254610c0f9190613e1f565b90915550506040805160e081018252600186015481526002860154602082015260038601549181019190915260048501546001600160401b038082166060840152600160401b820481166080840152600160801b8204811660a0840152600160c01b9091041660c08201526000908190610c8890612d12565b90506006610c9586611b71565b6006811115610ca657610ca6613971565b03610cd957600b54606490610cbe9060ff1683613e32565b610cc89190613e5f565b610cd29082613e1f565b9150610cdd565b8091505b610ce73383612d31565b8160136000016000828254610cfc9190613e73565b9091555050600384018190556004840154610d20906001600160a01b031686612e05565b835460ff191660011784556040516001600160401b038a1681528a907f8f301470a994578b52323d625dfbf827ca5208c81747d3459be7b8867baec3ec9060200160405180910390a2600486015460028401546001600160401b039081169116148015610da257506000835460ff166004811115610da057610da0613971565b145b15610e0c57825460ff191660011783556002830180546001600160401b034216600160401b026fffffffffffffffff0000000000000000199091161790556040518a907f85e1543bf2f84fe80c6badbce3648c8539ad1df4d2b3d822938ca0538be727e690600090a25b50505050505050505050565b816001610e2482611b71565b6006811115610e3557610e35613971565b14610e535760405163ae9dcffd60e01b815260040160405180910390fd5b610e5d8383612e27565b6000838152601260209081526040808320600180820154855260108452828520600b54845160e08101865292820154835260028201549583019590955260038101549382019390935260048301546001600160401b038082166060840152600160401b820481166080840152600160801b8204811660a0840152600160c01b9091041660c0820152909391926064916201000090910460ff1690610f0090612d12565b610f0a9190613e32565b610f149190613e5f565b600b54909150600090606490610f34906301000000900460ff1684613e32565b610f3e9190613e5f565b90508060136001016000828254610f559190613e73565b909155505060405163a9059cbb60e01b8152336004820152602481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a9059cbb906044016020604051808303816000875af1158015610fc7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610feb9190613e86565b61100857604051637c2ccffd60e11b815260040160405180910390fd5b8184600301600082825461101c9190613e1f565b9091555050600b5460008881526006602052604090205461010090910460ff16906001600160401b03166001600160401b03161061105d5761105d87612a0d565b50505050505050565b600061107982611074612eaa565b612eb5565b92915050565b60006110798261108d612eaa565b612ec9565b60008181526012602090815260408083206001810154845260109092528220600c54610100906110cc90600160801b900460ff1682613ea8565b60018301546110df9161ffff1690613e32565b6110e99190613e5f565b949350505050565b336000908152600a602052604090206060906111139061111090612f5b565b90565b905090565b6111206138e2565b604080516101008082018352600b805460ff8082166080808701918252948304821660a080880191909152620100008404831660c08801526301000000909304821660e0870152855285519182018652600c80546001600160401b038082168552600160401b820416602085810191909152600160801b82048416988501989098527101000000000000000000000000000000000090049091166060830152600d80549596939593870194929391928401916111db90613ec2565b80601f016020809104026020016040519081016040528092919081815260200182805461120790613ec2565b80156112545780601f1061122957610100808354040283529160200191611254565b820191906000526020600020905b81548152906001019060200180831161123757829003601f168201915b5050509190925250505081526040805160208181018352600385015460ff1682528301526004909201546001600160401b0316910152919050565b6112998133611c52565b50565b3360009081526009602052604090206060906111139061111090612f5b565b60006112ce6112c983614053565b612f68565b9050336112de602084018461415c565b6001600160a01b031614611305576040516334c69e3160e11b815260040160405180910390fd5b6000818152601060205260409020546001600160a01b031615611354576040517ffc7d069000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61136661014083016101208401614179565b6001600160401b031615806113ad575061138660e0830160c08401614179565b6001600160401b03166113a161014084016101208501614179565b6001600160401b031610155b156113e4576040517fdf63f61a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6113f460a0830160808401614179565b6001600160401b0316600003611436576040517f535ed2be00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61144660a0830160808401614179565b6001600160401b0316611460610100840160e08501614179565b6001600160401b031611156114a1576040517fb9551ab100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6114b160e0830160c08401614179565b6001600160401b03166000036114f3576040517f090a5ecd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6020820135600003611531576040517f6aba7aae00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606082013560000361156f576040517ffb7df0c700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408201356000036115ad576040517f47ba51c700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6115bb610100830183614196565b6115c590806141b6565b9050600003611600576040517f86f8cf9b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600f546001600160401b031661161c60e0840160c08501614179565b6001600160401b0316111561165d576040517f1267b3f200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600081815260106020526040902082906116778282614356565b5061168a905060e0830160c08401614179565b6116949042613e00565b600082815260116020526040902060020180546001600160401b0392909216600160801b0267ffffffffffffffff60801b199092169190911790556116e161014083016101208401614179565b6116eb9042613e00565b600082815260116020908152604090912060020180546001600160401b0393909316600160c01b0277ffffffffffffffffffffffffffffffffffffffffffffffff9093169290921790915561174c906117469084018461415c565b82612f98565b600061175f61175a84614053565b612fba565b600083815260116020526040812060010182905560138054929350839290919061178a908490613e73565b9091555061179a90503382612d31565b6000828152601160209081526040918290206002015491517f1bf9c457accf8703dbf7cdf1b58c2f74ddf2e525f98155c70b3d318d74609bd8926117f492869290880191600160c01b90046001600160401b03169061450a565b60405180910390a1505050565b806000808281526012602052604090205460ff16600681111561182657611826613971565b0361184457604051638b41ec7f60e01b815260040160405180910390fd5b61184f823333610702565b5050565b600080600061186984611864612eaa565b612ff6565b90925090508180156110e95750600254600160801b900460ff9081169116109392505050565b611912604051806040016040528061394b6040805160a080820183526000808352835160e081018552818152602080820183905281860183905260608083018490526080830184905293820183905260c0820183905280850191909152845180860186529283528201529091820190815260006020820181905260409091015290565b816000808281526012602052604090205460ff16600681111561193757611937613971565b0361195557604051638b41ec7f60e01b815260040160405180910390fd5b60008381526012602052604090206119e6604051806040016040528061394b6040805160a080820183526000808352835160e081018552818152602080820183905281860183905260608083018490526080830184905293820183905260c0820183905280850191909152845180860186529283528201529091820190815260006020820181905260409091015290565b600180830154600090815260106020908152604091829020825160a0808201855282546001600160a01b03168252845160e08101865295830154865260028301548685015260038301548686015260048301546001600160401b038082166060890152600160401b820481166080890152600160801b8204811692880192909252600160c01b90041660c0860152918201939093528151808301835260058401805492949385019282908290611a9b90613ec2565b80601f0160208091040260200160405190810160405280929190818152602001828054611ac790613ec2565b8015611b145780601f10611ae957610100808354040283529160200191611b14565b820191906000526020600020905b815481529060010190602001808311611af757829003601f168201915b50505091835250506001919091015460209182015290825260078301546001600160401b0390811683830152600890930154604090920191909152918352600290930154600160401b900490921691810191909152915050919050565b600081815260126020526040812060018101548203611b935750600092915050565b6000611ba282600101546105df565b90506004825460ff166006811115611bbc57611bbc613971565b03611bcb575060049392505050565b6002816004811115611bdf57611bdf613971565b03611bee575060059392505050565b6003816004811115611c0257611c02613971565b03611c11575060029392505050565b6004816004811115611c2557611c25613971565b03611c34575060039392505050565b505460ff1692915050565b600061107982611c4d612eaa565b6130ae565b60008281526010602052604090205482906001600160a01b0316611c8957604051635eeb253d60e11b815260040160405180910390fd5b6000838152601060209081526040808320601190925290912081546001600160a01b03163314611ccc576040516334c69e3160e11b815260040160405180910390fd5b6000611cd7866105df565b90506002816004811115611ced57611ced613971565b14158015611d0d57506004816004811115611d0a57611d0a613971565b14155b8015611d2b57506003816004811115611d2857611d28613971565b14155b15611d62576040517fc00b5b5700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160010154600003611da0576040517fbd8bdd9400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002816004811115611db457611db4613971565b03611e5257815460ff1916600217825560405186907ff903f4774c7bd27355f9d7fcbc382b079b164a697a44ac5d95267a4c3cb3bb2290600090a2600086815260116020526040902060020154611e1c908790600160c01b90046001600160401b0316612ce3565b6002830154611e3491906001600160401b0316613e32565b826001016000828254611e479190613e73565b90915550611fdf9050565b6004816004811115611e6657611e66613971565b03611fd3576040805160a0808201835285546001600160a01b03168252825160e08101845260018701548152600287015460208281019190915260038801548286015260048801546001600160401b038082166060850152600160401b820481166080850152600160801b8204811694840194909452600160c01b900490921660c08201529082015281518083018352600586018054611fc994889390850192909182908290611f1590613ec2565b80601f0160208091040260200160405190810160405280929190818152602001828054611f4190613ec2565b8015611f8e5780601f10611f6357610100808354040283529160200191611f8e565b820191906000526020600020905b815481529060010190602001808311611f7157829003601f168201915b50505091835250506001919091015460209182015290825260078301546001600160401b031690820152600890910154604090910152612fba565b6001830155611fdf565b815460ff191660031782555b8254611ff4906001600160a01b0316876130e8565b60018201546014805482919060009061200e908490613e73565b909155505060405163a9059cbb60e01b81526001600160a01b038781166004830152602482018390527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303816000875af1158015612082573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120a69190613e86565b6120c357604051637c2ccffd60e11b815260040160405180910390fd5b5050600060019091015550505050565b600033816120e18585612c5d565b905060006120ee82611b71565b9050600081600681111561210457612104613971565b14806121215750600681600681111561211f5761211f613971565b145b801561214a5750600154600083815260208190526040902060ff9091169061214890612cb7565b105b801561216b575060008281526020819052604090206121699084612cc1565b155b9695505050505050565b81600161218182611b71565b600681111561219257612192613971565b146121b05760405163ae9dcffd60e01b815260040160405180910390fd5b610961838361310a565b6000828152601260209081526040808320600101548084526010909252909120546001600160a01b031661220157604051635eeb253d60e11b815260040160405180910390fd5b600083815260126020526040902060048101546001600160a01b03163314612255576040517fce351b9400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001810154600090815260106020526040808220815160038082526080820190935290929181602001602082028036833701905050905061229d61229887611066565b6132a7565b816000815181106122b0576122b06145b7565b602090810291909101015260068201546122c9906132b8565b816001815181106122dc576122dc6145b7565b6020026020010181815250508260020160089054906101000a90046001600160401b03166001600160401b03168160028151811061231c5761231c6145b7565b60200260200101818152505061088d8686836132c4565b6123a86040805160a080820183526000808352835160e081018552818152602080820183905281860183905260608083018490526080830184905293820183905260c0820183905280850191909152845180860186529283528201529091820190815260006020820181905260409091015290565b60008281526010602052604090205482906001600160a01b03166123df57604051635eeb253d60e11b815260040160405180910390fd5b600083815260106020908152604091829020825160a0808201855282546001600160a01b03168252845160e0810186526001840154815260028401548186015260038401548187015260048401546001600160401b038082166060840152600160401b820481166080840152600160801b8204811693830193909352600160c01b900490911660c0820152928101929092528251808401845260058201805493949293928501928290829061249390613ec2565b80601f01602080910402602001604051908101604052809291908181526020018280546124bf90613ec2565b801561250c5780601f106124e15761010080835404028352916020019161250c565b820191906000526020600020905b8154815290600101906020018083116124ef57829003601f168201915b50505091835250506001919091015460209182015290825260078301546001600160401b0316908201526008909101546040909101529392505050565b60008282188284100282186105d8565b60008481526010602052604090205484906001600160a01b031661259057604051635eeb253d60e11b815260040160405180910390fd5b600085815260116020908152604080832060108352818420815460ff191660031782558885526012909352922081546125d2906001600160a01b0316896130e8565b60048101546125ea906001600160a01b0316886129eb565b6002810154600090612606908a906001600160401b0316612ce3565b60038301549091506126188183613e73565b6014805460009061262a908490613e73565b90915550508254600490849060ff1916600183021790555060405163a9059cbb60e01b81526001600160a01b038981166004830152602482018490527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303816000875af11580156126b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126d59190613e86565b6126f257604051637c2ccffd60e11b815260040160405180910390fd5b60405163a9059cbb60e01b81526001600160a01b038881166004830152602482018390527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303816000875af1158015612761573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127859190613e86565b610e0c57604051637c2ccffd60e11b815260040160405180910390fd5b60008481526010602052604090205484906001600160a01b03166127d957604051635eeb253d60e11b815260040160405180910390fd5b600084815260126020526040902060048101546127ff906001600160a01b0316866129eb565b60028101546000906128459088906001600160401b0316612840826000908152601160205260409020600201546001600160401b03600160c01b9091041690565b613462565b60038301549091506128578183613e73565b60148054600090612869908490613e73565b90915550508254600490849060ff1916600183021790555060405163a9059cbb60e01b81526001600160a01b038781166004830152602482018490527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303816000875af11580156128f0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129149190613e86565b61293157604051637c2ccffd60e11b815260040160405180910390fd5b60405163a9059cbb60e01b81526001600160a01b038681166004830152602482018390527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303816000875af11580156129a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129c49190613e86565b6129e157604051637c2ccffd60e11b815260040160405180910390fd5b5050505050505050565b6001600160a01b0382166000908152600a602052604090206109619082613541565b60008181526012602090815260408083206001810154808552601190935292206002830154612a469083906001600160401b0316612ce3565b816001016000828254612a599190613e73565b90915550506004830154612a76906001600160a01b0316856129eb565b6000848152602081905260409020612a8d9061354d565b825460ff191660061783556002808401805467ffffffffffffffff1916905560006003850181905560048501805473ffffffffffffffffffffffffffffffffffffffff19169055908201805460019290612af19084906001600160401b03166145cd565b82546101009290920a6001600160401b038181021990931691831602179091556002850154604051600160401b90910490911681528391507f33ba8f7627565d89f7ada2a6b81ea532b7aa9b11e91a78312d6e1fca0bfcd1dc9060200160405180910390a26000848152600660205260409020805467ffffffffffffffff19169055600082815260106020526040812060028301546004820154919291612ba4916001600160401b0390811691166145cd565b60048301546001600160401b039182169250600160c01b90041681118015612be157506001835460ff166004811115612bdf57612bdf613971565b145b1561088d57825460ff19166004178355612bfc6001426145cd565b6002840180546001600160401b0392909216600160801b0267ffffffffffffffff60801b1990921691909117905560405184907f4769361a442504ecaf038f35e119bcccdd5e42096b24c09e3c17fd17c6684c0290600090a2505050505050565b60008282604051602001612c849291909182526001600160401b0316602082015260400190565b60405160208183030381529060405280519060200120905092915050565b60006105d8836001600160a01b038416613556565b6000611079825490565b6001600160a01b038116600090815260018301602052604081205415156105d8565b6000828152601160205260408120600201546105d89084908490600160801b90046001600160401b0316613462565b600081608001516001600160401b031682604001516110799190613e32565b6040517f23b872dd0000000000000000000000000000000000000000000000000000000081526001600160a01b038381166004830152306024830181905260448301849052917f0000000000000000000000000000000000000000000000000000000000000000909116906323b872dd906064016020604051808303816000875af1158015612dc4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612de89190613e86565b61096157604051637c2ccffd60e11b815260040160405180910390fd5b6001600160a01b0382166000908152600a6020526040902061096190826135a5565b612e31828261310a565b60008281526008602090815260408083206001600160401b038086168552908352818420805460ff1916600190811790915586855260069093529083208054929390929091612e8291859116613e00565b92506101000a8154816001600160401b0302191690836001600160401b031602179055505050565b6000611113426135b1565b60006105d8612ec48484612ec9565b6135dd565b600080612ed8610100436145ec565b60025490915060009061010090612f079071010000000000000000000000000000000000900460ff1686614600565b612f119190614622565b6001600160401b031690506000612f2a610100876145ec565b9050600061010082612f3c8587613e73565b612f469190613e73565b612f5091906145ec565b979650505050505050565b606060006105d883613637565b600081604051602001612f7b9190613dd7565b604051602081830303815290604052805190602001209050919050565b6001600160a01b038216600090815260096020526040902061096190826135a5565b6000612fc98260200151613693565b602083015160a0810151606090910151612fe39190614600565b6001600160401b03166110799190613e32565b600080600061300485611b71565b60008681526005602052604081205491925090613029906001600160401b03166135b1565b9050600182600681111561303f5761303f613971565b141580613053575061305185826136b2565b155b15613066576000809350935050506130a7565b6130708686612ec9565b9250600061307d846135dd565b9050600061308a88611092565b90508015806130a0575061309e81836145ec565b155b9550505050505b9250929050565b60008060006130bd8585612ff6565b90925090508180156130df575060025460ff600160801b909104811690821610155b95945050505050565b6001600160a01b03821660009081526009602052604090206109619082613541565b6000613115826136c8565b6001600160401b03169050428110613159576040517f6b4b1a4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60025461317690600160401b90046001600160401b031682613e73565b42106131ae576040517fde55698e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526007602090815260408083206001600160401b038616845290915290205460ff161561320a576040517efab7d900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61321483836130ae565b61324a576040517fd3ffa66b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526008602090815260408083206001600160401b038616845290915290205460ff1615610961576040517f98e7e55100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600060ff198216816110e9826136db565b6000806105d8836136db565b6000838152600760205260408120906132db612eaa565b6001600160401b0316815260208101919091526040016000205460ff161561332f576040517f3edef7db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600480546040517f94c8919d0000000000000000000000000000000000000000000000000000000081526001600160a01b03909116916394c8919d91613379918691869101614650565b602060405180830381865afa158015613396573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133ba9190613e86565b6133f0576040517ffcd03a4700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000838152600760205260408120600191613409612eaa565b6001600160401b031681526020808201929092526040908101600020805460ff19169315159390931790925590518481527f3b989d183b84b02259d7c14b34a9c9eb0fccb4c355a920d25e581e25aef4993d91016117f4565b60008381526010602052604081206001600160401b03808416908516106134b5576040517f56607cb000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160e081018252600183015481526002830154602082015260038301549181019190915260048201546001600160401b038082166060840152600160401b820481166080840152600160801b8204811660a0840152600160c01b9091041660c082015261352490613693565b61352e85856145cd565b6001600160401b03166130df9190613e32565b60006105d8838361374d565b61129981613847565b600081815260018301602052604081205461359d57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155611079565b506000611079565b60006105d88383613556565b60006110797f0000000000000000000000000000000000000000000000000000000000000000836146fa565b60008060ff83166135ef600143613e1f565b6135f99190613e1f565b409050600081900361360d5761360d614728565b60408051602081018390520160405160208183030381529060405280519060200120915050919050565b60608160000180548060200260200160405190810160405280929190818152602001828054801561368757602002820191906000526020600020905b815481526020019060010190808311613673575b50505050509050919050565b600081608001516001600160401b031682602001516110799190613e32565b60006001600160401b03808416908316106105d8565b60006110796136d6836138a9565b6138b6565b7fff00000000000000000000000000000000000000000000000000000000000000811660015b60208110156106fc57600891821c9161371b908290613e32565b83901b7fff00000000000000000000000000000000000000000000000000000000000000169190911790600101613701565b60008181526001830160205260408120548015613836576000613771600183613e1f565b855490915060009061378590600190613e1f565b90508082146137ea5760008660000182815481106137a5576137a56145b7565b90600052602060002001549050808760000184815481106137c8576137c86145b7565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806137fb576137fb61473e565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050611079565b6000915050611079565b5092915050565b6000613851825490565b905060005b818110156138a157826001016000846000018381548110613879576138796145b7565b9060005260206000200154815260200190815260200160002060009055806001019050613856565b505060009055565b6000611079826001613e00565b60006110797f000000000000000000000000000000000000000000000000000000000000000083614600565b60408051610100810182526000608080830182815260a080850184905260c0850184905260e08501849052908452845190810185528281526020808201849052818601849052606080830185905292820192909252818401528351908101845290815290918201905b8152600060209091015290565b60006020828403121561396a57600080fd5b5035919050565b634e487b7160e01b600052602160045260246000fd5b602081016005831061399b5761399b613971565b91905290565b6001600160a01b038116811461129957600080fd5b6000806000606084860312156139cb57600080fd5b8335925060208401356139dd816139a1565b915060408401356139ed816139a1565b809150509250925092565b6001600160401b038116811461129957600080fd5b8035613a18816139f8565b919050565b60008060408385031215613a3057600080fd5b823591506020830135613a42816139f8565b809150509250929050565b600061010082840312156106fc57600080fd5b60008060006101408486031215613a7657600080fd5b833592506020840135613a88816139f8565b9150613a978560408601613a4d565b90509250925092565b602080825282518282018190526000918401906040840190835b81811015613ad8578351835260209384019390920191600101613aba565b509095945050505050565b6000815180845260005b81811015613b0957602081850181015186830182015201613aed565b506000602082860101526020601f19601f83011685010191505092915050565b6001600160401b0381511682526001600160401b03602082015116602083015260ff604082015116604083015260ff60608201511660608301526000608082015160a060808501526110e960a0850182613ae3565b602081526000825160ff815116602084015260ff602082015116604084015260ff604082015116606084015260ff606082015116608084015250602083015160e060a0840152613bd2610100840182613b29565b90506040840151613be960c08501825160ff169052565b5060608401516001600160401b03811660e0850152509392505050565b600060208284031215613c1857600080fd5b81356001600160401b03811115613c2e57600080fd5b820161016081850312156105d857600080fd5b6000815160408452613c566040850182613ae3565b602093840151949093019390935250919050565b6001600160a01b038151168252600060208201518051602085015260208101516040850152604081015160608501526001600160401b0360608201511660808501526001600160401b0360808201511660a08501526001600160401b0360a08201511660c08501526001600160401b0360c08201511660e0850152506040820151610160610100850152613d02610160850182613c41565b90506060830151613d1f6101208601826001600160401b03169052565b5060808301516101408501528091505092915050565b602081526000825160406020840152613d516060840182613c6a565b90506001600160401b0360208501511660408401528091505092915050565b602081016007831061399b5761399b613971565b60008060408385031215613d9757600080fd5b823591506020830135613a42816139a1565b6000806101208385031215613dbd57600080fd5b82359150613dce8460208501613a4d565b90509250929050565b6020815260006105d86020830184613c6a565b634e487b7160e01b600052601160045260246000fd5b6001600160401b03818116838216019081111561107957611079613dea565b8181038181111561107957611079613dea565b808202811582820484141761107957611079613dea565b634e487b7160e01b600052601260045260246000fd5b600082613e6e57613e6e613e49565b500490565b8082018082111561107957611079613dea565b600060208284031215613e9857600080fd5b815180151581146105d857600080fd5b61ffff828116828216039081111561107957611079613dea565b600181811c90821680613ed657607f821691505b6020821081036106fc57634e487b7160e01b600052602260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b0381118282101715613f2e57613f2e613ef6565b60405290565b60405160a081016001600160401b0381118282101715613f2e57613f2e613ef6565b60405160e081016001600160401b0381118282101715613f2e57613f2e613ef6565b604051601f8201601f191681016001600160401b0381118282101715613fa057613fa0613ef6565b604052919050565b600060408284031215613fba57600080fd5b613fc2613f0c565b905081356001600160401b03811115613fda57600080fd5b8201601f81018413613feb57600080fd5b80356001600160401b0381111561400457614004613ef6565b614017601f8201601f1916602001613f78565b81815285602083850101111561402c57600080fd5b81602084016020830137600060209282018301528352928301359282019290925292915050565b600081360361016081121561406757600080fd5b61406f613f34565b833561407a816139a1565b815260e0601f198301121561408e57600080fd5b614096613f56565b602085810135825260408087013591830191909152606086013590820152915060808401356140c4816139f8565b606083015260a08401356140d7816139f8565b608083015260c08401356140ea816139f8565b60a083015260e08401356140fd816139f8565b60c08301526020810191909152610100830135906001600160401b0382111561412557600080fd5b61413136838601613fa8565b60408201526141436101208501613a0d565b6060820152610140939093013560808401525090919050565b60006020828403121561416e57600080fd5b81356105d8816139a1565b60006020828403121561418b57600080fd5b81356105d8816139f8565b60008235603e198336030181126141ac57600080fd5b9190910192915050565b6000808335601e198436030181126141cd57600080fd5b8301803591506001600160401b038211156141e757600080fd5b6020019150368190038213156130a757600080fd5b60008135611079816139f8565b601f82111561096157806000526020600020601f840160051c810160208510156142305750805b601f840160051c820191505b81811015614250576000815560010161423c565b5050505050565b8135601e1983360301811261426b57600080fd5b820180356001600160401b038111801561428457600080fd5b81360360208401131561429657600080fd5b600090506142ae826142a88654613ec2565b86614209565b80601f8311600181146142e3578284156142cb5750848201602001355b600019600386901b1c1916600185901b178655614342565b600086815260209020601f19851690845b82811015614316576020858901810135835594850194600190920191016142f4565b50858210156143365760001960f88760031b161c19602085890101351681555b505060018460011b0186555b505050505060209190910135600190910155565b8135614361816139a1565b815473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03919091161781556020820135600182015560408201356002820155606082013560038201556004810160808301356143b9816139f8565b815467ffffffffffffffff19166001600160401b0382161782555060a08301356143e2816139f8565b81546fffffffffffffffff0000000000000000191660409190911b6fffffffffffffffff00000000000000001617815561445661442160c085016141fc565b825467ffffffffffffffff60801b191660809190911b77ffffffffffffffff0000000000000000000000000000000016178255565b6144ae61446560e085016141fc565b825477ffffffffffffffffffffffffffffffffffffffffffffffff1660c09190911b7fffffffffffffffff00000000000000000000000000000000000000000000000016178255565b506144c96144c0610100840184614196565b60058301614257565b6144fa6144d961012084016141fc565b600783016001600160401b0382166001600160401b03198254161781555050565b6101409190910135600890910155565b8381528235602080830191909152830135604080830191909152830135606080830191909152610120820190840135614542816139f8565b6001600160401b0381166080840152506080840135614560816139f8565b6001600160401b03811660a08401525060a084013561457e816139f8565b6001600160401b03811660c08401525061459a60c08501613a0d565b6001600160401b0390811660e084015283166101008301526110e9565b634e487b7160e01b600052603260045260246000fd5b6001600160401b03828116828216039081111561107957611079613dea565b6000826145fb576145fb613e49565b500690565b6001600160401b03818116838216029081169081811461384057613840613dea565b60006001600160401b0383168061463b5761463b613e49565b806001600160401b0384160691505092915050565b82358152602080840135908201526000610120820161467f604084016040870180358252602090810135910152565b614699608084016080870180358252602090810135910152565b6146b360c0840160c0870180358252602090810135910152565b610120610100840152835190819052602084019061014084019060005b818110156146ee5783518352602093840193909201916001016146d0565b50909695505050505050565b60006001600160401b0383168061471357614713613e49565b806001600160401b0384160491505092915050565b634e487b7160e01b600052600160045260246000fd5b634e487b7160e01b600052603160045260246000fdfea26469706673582212208753932439ab3be782432684ba36d249b5e5029b7c1b55364b935c22d15237d664736f6c634300081c0033"; + public static string BYTECODE = "0x60c060405234801561001057600080fd5b50604051614fab380380614fab83398101604081905261002f9161053b565b602083015180516040850151516001805460ff191660ff90921691909117905582906001600160401b03811660000361007b5760405163015536c760e51b815260040160405180910390fd5b6001600160401b031660805261010043116100a9576040516338f5f66160e11b815260040160405180910390fd5b8151600280546020850151604086015160608701516001600160401b039586166001600160801b0319909416939093176801000000000000000095909216949094021761ffff60801b1916600160801b60ff9485160260ff60881b191617600160881b9390911692909202919091178155608083015183919060039061012f90826106d9565b5050600480546001600160a01b0319166001600160a01b0393841617905550831660a05250825151606460ff909116111561017d576040516302bd816360e41b815260040160405180910390fd5b606483600001516040015160ff1611156101aa576040516354e5e0ab60e11b815260040160405180910390fd5b825160408101516020909101516064916101c391610797565b60ff1611156101e5576040516317ff9d0f60e21b815260040160405180910390fd5b82518051600b805460208085015160408087015160609788015160ff90811663010000000263ff0000001992821662010000029290921663ffff0000199482166101000261ffff1990971698821698909817959095179290921695909517178355808801518051600c80549383015196830151978301518516600160881b0260ff60881b1998909516600160801b029790971661ffff60801b196001600160401b0397881668010000000000000000026001600160801b031990951697909216969096179290921791909116939093171783556080820151869391929190600d906102d090826106d9565b50505060408201515160038201805460ff191660ff909216919091179055606090910151600490910180546001600160401b0319166001600160401b03909216919091179055506107c8915050565b634e487b7160e01b600052604160045260246000fd5b60405160a081016001600160401b03811182821017156103575761035761031f565b60405290565b604051608081016001600160401b03811182821017156103575761035761031f565b604051601f8201601f191681016001600160401b03811182821017156103a7576103a761031f565b604052919050565b805160ff811681146103c057600080fd5b919050565b80516001600160401b03811681146103c057600080fd5b600060a082840312156103ee57600080fd5b6103f6610335565b9050610401826103c5565b815261040f602083016103c5565b6020820152610420604083016103af565b6040820152610431606083016103af565b606082015260808201516001600160401b0381111561044f57600080fd5b8201601f8101841361046057600080fd5b80516001600160401b038111156104795761047961031f565b61048c601f8201601f191660200161037f565b8181528560208385010111156104a157600080fd5b60005b828110156104c0576020818501810151838301820152016104a4565b5060006020838301015280608085015250505092915050565b6000602082840312156104eb57600080fd5b604051602081016001600160401b038111828210171561050d5761050d61031f565b60405290508061051c836103af565b905292915050565b80516001600160a01b03811681146103c057600080fd5b60008060006060848603121561055057600080fd5b83516001600160401b0381111561056657600080fd5b840180860360e081121561057957600080fd5b61058161035d565b608082121561058f57600080fd5b61059761035d565b91506105a2836103af565b82526105b0602084016103af565b60208301526105c1604084016103af565b60408301526105d2606084016103af565b60608301529081526080820151906001600160401b038211156105f457600080fd5b610600888385016103dc565b60208201526106128860a085016104d9565b604082015261062360c084016103c5565b6060820152945061063991505060208501610524565b915061064760408501610524565b90509250925092565b600181811c9082168061066457607f821691505b60208210810361068457634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156106d457806000526020600020601f840160051c810160208510156106b15750805b601f840160051c820191505b818110156106d157600081556001016106bd565b50505b505050565b81516001600160401b038111156106f2576106f261031f565b610706816107008454610650565b8461068a565b6020601f82116001811461073a57600083156107225750848201515b600019600385901b1c1916600184901b1784556106d1565b600084815260208120601f198516915b8281101561076a578785015182556020948501946001909201910161074a565b50848210156107885786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b60ff81811683821602908116908181146107c157634e487b7160e01b600052601160045260246000fd5b5092915050565b60805160a051614786610825600039600081816104dd01528181610f76015281816120350152818161266401528181612714015281816128a3015281816129530152612d750152600081816135b401526138b901526147866000f3fe608060405234801561001057600080fd5b50600436106101cf5760003560e01c80636e2b54ee11610104578063c0cc4add116100a2578063e8aa0a0711610071578063e8aa0a071461047f578063f752196b14610492578063fb1e61ca146104bb578063fc0c546a146104db57600080fd5b8063c0cc4add14610433578063c5d4335114610446578063d02bbe3314610459578063d1bb36b61461046c57600080fd5b8063a29c29a4116100de578063a29c29a4146103bd578063a3a0807e146103d0578063b396dc79146103f3578063be5cdc481461041357600080fd5b80636e2b54ee1461038f5780639777b72c146103a257806399b6da0c146103aa57600080fd5b8063329b5a0b1161017157806351a766421161014b57806351a766421461030e5780635da73835146103215780636b00c8cf146103365780636c70bee91461037a57600080fd5b8063329b5a0b146102a3578063458d2bf1146102d65780634641dce6146102e957600080fd5b806312827602116101ad57806312827602146102395780631d873c1b1461024c578063237d84821461025f57806326d6f8341461027257600080fd5b806302fa8e65146101d457806305b90773146102045780630aefaabe14610224575b600080fd5b6101e76101e2366004613954565b610501565b6040516001600160401b0390911681526020015b60405180910390f35b610217610212366004613954565b6105df565b6040516101fb9190613983565b6102376102323660046139b2565b610702565b005b610237610247366004613a19565b610895565b61023761025a366004613a5c565b610966565b61023761026d366004613a19565b610e18565b610295610280366004613954565b60009081526012602052604090206003015490565b6040519081526020016101fb565b6101e76102b1366004613954565b600090815260116020526040902060020154600160c01b90046001600160401b031690565b6102956102e4366004613954565b611066565b6102fc6102f7366004613954565b61107f565b60405160ff90911681526020016101fb565b61029561031c366004613954565b611092565b6103296110f1565b6040516101fb9190613a9c565b610362610344366004613954565b6000908152601260205260409020600401546001600160a01b031690565b6040516001600160a01b0390911681526020016101fb565b610382611118565b6040516101fb9190613b7a565b61023761039d366004613954565b61128f565b61032961129c565b6102376103b8366004613c02565b6112bb565b6102376103cb366004613954565b611801565b6103e36103de366004613954565b611853565b60405190151581526020016101fb565b610406610401366004613954565b61188f565b6040516101fb9190613d31565b610426610421366004613954565b611b6d565b6040516101fb9190613d6c565b6103e3610441366004613954565b611c3b565b610237610454366004613d80565b611c4e565b6103e3610467366004613a19565b6120cf565b61023761047a366004613a19565b612171565b61023761048d366004613da5565b6121b6565b6101e76104a0366004613954565b6000908152600660205260409020546001600160401b031690565b6104ce6104c9366004613954565b61232f565b6040516101fb9190613dd3565b7f0000000000000000000000000000000000000000000000000000000000000000610362565b60008061050d836105df565b905060008160048111156105235761052361396d565b14806105405750600181600481111561053e5761053e61396d565b145b1561056c575050600090815260116020526040902060020154600160801b90046001600160401b031690565b60028160048111156105805761058061396d565b036105ac575050600090815260116020526040902060020154600160c01b90046001600160401b031690565b6000838152601160205260409020600201546105d890600160801b90046001600160401b031642612545565b9392505050565b60008181526010602052604081205482906001600160a01b031661061657604051635eeb253d60e11b815260040160405180910390fd5b600083815260116020526040812090815460ff16600481111561063b5761063b61396d565b14801561067a5750600084815260116020526040902060020154600160c01b90046001600160401b03166001600160401b0316426001600160401b0316115b156106895760029250506106fc565b6001815460ff1660048111156106a1576106a161396d565b14806106c257506000815460ff1660048111156106c0576106c061396d565b145b80156106e6575060028101546001600160401b03600160801b909104811642909116115b156106f55760039250506106fc565b5460ff1691505b50919050565b826000808281526012602052604090205460ff1660068111156107275761072761396d565b0361074557604051638b41ec7f60e01b815260040160405180910390fd5b600084815260126020526040902060048101546001600160a01b03163314610799576040517f57a6f4e900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006107a486611b6d565b905060048160068111156107ba576107ba61396d565b036107f1576040517fc2cbf77700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028160068111156108055761080561396d565b0361081f5761081a8260010154878787612555565b61088d565b60058160068111156108335761083361396d565b036108485761081a826001015487878761279e565b600381600681111561085c5761085c61396d565b0361086b5761081a33876129e7565b600181600681111561087f5761087f61396d565b0361088d5761088d86612a09565b505050505050565b61089f82826120cf565b6108d5576040517f424a04ab00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006108e18383612c59565b60008181526020819052604090209091506108fc9033612c9e565b50600154600082815260208190526040902060ff9091169061091d90612cb3565b03610961576040516001600160401b038316815283907fc8e6c955744189a19222ec226b72ac1435d88d5745252dac56e6f679f64c037a9060200160405180910390a25b505050565b60008381526010602052604090205483906001600160a01b031661099d57604051635eeb253d60e11b815260040160405180910390fd5b600084815260106020526040902060048101546001600160401b03908116908516106109f5576040517f3b920b8800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610a018686612c59565b6000818152602081905260409020909150610a1c9033612cbd565b610a52576040517fd651ce1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000818152601260209081526040808320600181018a90556002810180546fffffffffffffffff00000000000000001916600160401b6001600160401b038c1602179055898452601190925282209091610aab84611b6d565b6006811115610abc57610abc61396d565b14158015610ae457506006610ad084611b6d565b6006811115610ae157610ae161396d565b14155b15610b1b576040517fff556acf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60048201805473ffffffffffffffffffffffffffffffffffffffff1916331790556002820180546001600160401b03421667ffffffffffffffff19909116179055610b8c83600090815260056020526040902080546001600160401b03421667ffffffffffffffff19909116179055565b610b9683876121b6565b60028101805460019190600090610bb79084906001600160401b0316613dfc565b92506101000a8154816001600160401b0302191690836001600160401b03160217905550610bfc888360020160009054906101000a90046001600160401b0316612cdf565b816001016000828254610c0f9190613e1b565b90915550506040805160e081018252600186015481526002860154602082015260038601549181019190915260048501546001600160401b038082166060840152600160401b820481166080840152600160801b8204811660a0840152600160c01b9091041660c08201526000908190610c8890612d0e565b90506006610c9586611b6d565b6006811115610ca657610ca661396d565b03610cd957600b54606490610cbe9060ff1683613e2e565b610cc89190613e5b565b610cd29082613e1b565b9150610cdd565b8091505b610ce73383612d2d565b8160136000016000828254610cfc9190613e6f565b9091555050600384018190556004840154610d20906001600160a01b031686612e01565b835460ff191660011784556040516001600160401b038a1681528a907f8f301470a994578b52323d625dfbf827ca5208c81747d3459be7b8867baec3ec9060200160405180910390a2600486015460028401546001600160401b039081169116148015610da257506000835460ff166004811115610da057610da061396d565b145b15610e0c57825460ff191660011783556002830180546001600160401b034216600160401b026fffffffffffffffff0000000000000000199091161790556040518a907f85e1543bf2f84fe80c6badbce3648c8539ad1df4d2b3d822938ca0538be727e690600090a25b50505050505050505050565b816001610e2482611b6d565b6006811115610e3557610e3561396d565b14610e535760405163ae9dcffd60e01b815260040160405180910390fd5b610e5d8383612e23565b6000838152601260209081526040808320600180820154855260108452828520600b54845160e08101865292820154835260028201549583019590955260038101549382019390935260048301546001600160401b038082166060840152600160401b820481166080840152600160801b8204811660a0840152600160c01b9091041660c0820152909391926064916201000090910460ff1690610f0090612d0e565b610f0a9190613e2e565b610f149190613e5b565b600b54909150600090606490610f34906301000000900460ff1684613e2e565b610f3e9190613e5b565b90508060136001016000828254610f559190613e6f565b909155505060405163a9059cbb60e01b8152336004820152602481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a9059cbb906044016020604051808303816000875af1158015610fc7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610feb9190613e82565b61100857604051637c2ccffd60e11b815260040160405180910390fd5b8184600301600082825461101c9190613e1b565b9091555050600b5460008881526006602052604090205461010090910460ff16906001600160401b03166001600160401b03161061105d5761105d87612a09565b50505050505050565b600061107982611074612ea6565b612eb1565b92915050565b60006110798261108d612ea6565b612ec5565b60008181526012602090815260408083206001810154845260109092528220600c54610100906110cc90600160801b900460ff1682613ea4565b60018301546110df9161ffff1690613e2e565b6110e99190613e5b565b949350505050565b336000908152600a602052604090206060906111139061111090612f57565b90565b905090565b6111206138de565b604080516101008082018352600b805460ff8082166080808701918252948304821660a080880191909152620100008404831660c08801526301000000909304821660e0870152855285519182018652600c80546001600160401b038082168552600160401b820416602085810191909152600160801b82048416988501989098527101000000000000000000000000000000000090049091166060830152600d80549596939593870194929391928401916111db90613ebe565b80601f016020809104026020016040519081016040528092919081815260200182805461120790613ebe565b80156112545780601f1061122957610100808354040283529160200191611254565b820191906000526020600020905b81548152906001019060200180831161123757829003601f168201915b5050509190925250505081526040805160208181018352600385015460ff1682528301526004909201546001600160401b0316910152919050565b6112998133611c4e565b50565b3360009081526009602052604090206060906111139061111090612f57565b60006112ce6112c98361404f565b612f64565b9050336112de6020840184614158565b6001600160a01b031614611305576040516334c69e3160e11b815260040160405180910390fd5b6000818152601060205260409020546001600160a01b031615611354576040517ffc7d069000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61136661014083016101208401614175565b6001600160401b031615806113ad575061138660e0830160c08401614175565b6001600160401b03166113a161014084016101208501614175565b6001600160401b031610155b156113e4576040517fdf63f61a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6113f460a0830160808401614175565b6001600160401b0316600003611436576040517f535ed2be00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61144660a0830160808401614175565b6001600160401b0316611460610100840160e08501614175565b6001600160401b031611156114a1576040517fb9551ab100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6114b160e0830160c08401614175565b6001600160401b03166000036114f3576040517f090a5ecd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6020820135600003611531576040517f6aba7aae00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606082013560000361156f576040517ffb7df0c700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408201356000036115ad576040517f47ba51c700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6115bb610100830183614192565b6115c590806141b2565b9050600003611600576040517f86f8cf9b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600f546001600160401b031661161c60e0840160c08501614175565b6001600160401b0316111561165d576040517f1267b3f200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600081815260106020526040902082906116778282614352565b5061168a905060e0830160c08401614175565b6116949042613dfc565b600082815260116020526040902060020180546001600160401b0392909216600160801b0267ffffffffffffffff60801b199092169190911790556116e161014083016101208401614175565b6116eb9042613dfc565b600082815260116020908152604090912060020180546001600160401b0393909316600160c01b0277ffffffffffffffffffffffffffffffffffffffffffffffff9093169290921790915561174c9061174690840184614158565b82612f94565b600061175f61175a8461404f565b612fb6565b600083815260116020526040812060010182905560138054929350839290919061178a908490613e6f565b9091555061179a90503382612d2d565b6000828152601160209081526040918290206002015491517f1bf9c457accf8703dbf7cdf1b58c2f74ddf2e525f98155c70b3d318d74609bd8926117f492869290880191600160c01b90046001600160401b031690614506565b60405180910390a1505050565b806000808281526012602052604090205460ff1660068111156118265761182661396d565b0361184457604051638b41ec7f60e01b815260040160405180910390fd5b61184f823333610702565b5050565b600080600061186984611864612ea6565b612ff2565b90925090508180156110e95750600254600160801b900460ff9081169116109392505050565b61191260405180604001604052806139476040805160a080820183526000808352835160e081018552818152602080820183905281860183905260608083018490526080830184905293820183905260c0820183905280850191909152845180860186529283528201529091820190815260006020820181905260409091015290565b60008281526012602052604081205460ff1660068111156119355761193561396d565b0361195357604051638b41ec7f60e01b815260040160405180910390fd5b60008281526012602052604090206119e460405180604001604052806139476040805160a080820183526000808352835160e081018552818152602080820183905281860183905260608083018490526080830184905293820183905260c0820183905280850191909152845180860186529283528201529091820190815260006020820181905260409091015290565b600180830154600090815260106020908152604091829020825160a0808201855282546001600160a01b03168252845160e08101865295830154865260028301548685015260038301548686015260048301546001600160401b038082166060890152600160401b820481166080890152600160801b8204811692880192909252600160c01b90041660c0860152918201939093528151808301835260058401805492949385019282908290611a9990613ebe565b80601f0160208091040260200160405190810160405280929190818152602001828054611ac590613ebe565b8015611b125780601f10611ae757610100808354040283529160200191611b12565b820191906000526020600020905b815481529060010190602001808311611af557829003601f168201915b50505091835250506001919091015460209182015290825260078301546001600160401b0390811683830152600890930154604090920191909152918352600290930154600160401b90049092169181019190915292915050565b600081815260126020526040812060018101548203611b8f5750600092915050565b6000611b9e82600101546105df565b90506004825460ff166006811115611bb857611bb861396d565b03611bc7575060049392505050565b6002816004811115611bdb57611bdb61396d565b03611bea575060059392505050565b6003816004811115611bfe57611bfe61396d565b03611c0d575060029392505050565b6004816004811115611c2157611c2161396d565b03611c30575060039392505050565b505460ff1692915050565b600061107982611c49612ea6565b6130aa565b60008281526010602052604090205482906001600160a01b0316611c8557604051635eeb253d60e11b815260040160405180910390fd5b6000838152601060209081526040808320601190925290912081546001600160a01b03163314611cc8576040516334c69e3160e11b815260040160405180910390fd5b6000611cd3866105df565b90506002816004811115611ce957611ce961396d565b14158015611d0957506004816004811115611d0657611d0661396d565b14155b8015611d2757506003816004811115611d2457611d2461396d565b14155b15611d5e576040517fc00b5b5700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160010154600003611d9c576040517fbd8bdd9400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002816004811115611db057611db061396d565b03611e4e57815460ff1916600217825560405186907ff903f4774c7bd27355f9d7fcbc382b079b164a697a44ac5d95267a4c3cb3bb2290600090a2600086815260116020526040902060020154611e18908790600160c01b90046001600160401b0316612cdf565b6002830154611e3091906001600160401b0316613e2e565b826001016000828254611e439190613e6f565b90915550611fdb9050565b6004816004811115611e6257611e6261396d565b03611fcf576040805160a0808201835285546001600160a01b03168252825160e08101845260018701548152600287015460208281019190915260038801548286015260048801546001600160401b038082166060850152600160401b820481166080850152600160801b8204811694840194909452600160c01b900490921660c08201529082015281518083018352600586018054611fc594889390850192909182908290611f1190613ebe565b80601f0160208091040260200160405190810160405280929190818152602001828054611f3d90613ebe565b8015611f8a5780601f10611f5f57610100808354040283529160200191611f8a565b820191906000526020600020905b815481529060010190602001808311611f6d57829003601f168201915b50505091835250506001919091015460209182015290825260078301546001600160401b031690820152600890910154604090910152612fb6565b6001830155611fdb565b815460ff191660031782555b8254611ff0906001600160a01b0316876130e4565b60018201546014805482919060009061200a908490613e6f565b909155505060405163a9059cbb60e01b81526001600160a01b038781166004830152602482018390527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303816000875af115801561207e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120a29190613e82565b6120bf57604051637c2ccffd60e11b815260040160405180910390fd5b5050600060019091015550505050565b600033816120dd8585612c59565b905060006120ea82611b6d565b905060008160068111156121005761210061396d565b148061211d5750600681600681111561211b5761211b61396d565b145b80156121465750600154600083815260208190526040902060ff9091169061214490612cb3565b105b8015612167575060008281526020819052604090206121659084612cbd565b155b9695505050505050565b81600161217d82611b6d565b600681111561218e5761218e61396d565b146121ac5760405163ae9dcffd60e01b815260040160405180910390fd5b6109618383613106565b6000828152601260209081526040808320600101548084526010909252909120546001600160a01b03166121fd57604051635eeb253d60e11b815260040160405180910390fd5b600083815260126020526040902060048101546001600160a01b03163314612251576040517fce351b9400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001810154600090815260106020526040808220815160038082526080820190935290929181602001602082028036833701905050905061229961229487611066565b6132a3565b816000815181106122ac576122ac6145b3565b602090810291909101015260068201546122c5906132b4565b816001815181106122d8576122d86145b3565b6020026020010181815250508260020160089054906101000a90046001600160401b03166001600160401b031681600281518110612318576123186145b3565b60200260200101818152505061088d8686836132c0565b6123a46040805160a080820183526000808352835160e081018552818152602080820183905281860183905260608083018490526080830184905293820183905260c0820183905280850191909152845180860186529283528201529091820190815260006020820181905260409091015290565b60008281526010602052604090205482906001600160a01b03166123db57604051635eeb253d60e11b815260040160405180910390fd5b600083815260106020908152604091829020825160a0808201855282546001600160a01b03168252845160e0810186526001840154815260028401548186015260038401548187015260048401546001600160401b038082166060840152600160401b820481166080840152600160801b8204811693830193909352600160c01b900490911660c0820152928101929092528251808401845260058201805493949293928501928290829061248f90613ebe565b80601f01602080910402602001604051908101604052809291908181526020018280546124bb90613ebe565b80156125085780601f106124dd57610100808354040283529160200191612508565b820191906000526020600020905b8154815290600101906020018083116124eb57829003601f168201915b50505091835250506001919091015460209182015290825260078301546001600160401b0316908201526008909101546040909101529392505050565b60008282188284100282186105d8565b60008481526010602052604090205484906001600160a01b031661258c57604051635eeb253d60e11b815260040160405180910390fd5b600085815260116020908152604080832060108352818420815460ff191660031782558885526012909352922081546125ce906001600160a01b0316896130e4565b60048101546125e6906001600160a01b0316886129e7565b6002810154600090612602908a906001600160401b0316612cdf565b60038301549091506126148183613e6f565b60148054600090612626908490613e6f565b90915550508254600490849060ff1916600183021790555060405163a9059cbb60e01b81526001600160a01b038981166004830152602482018490527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303816000875af11580156126ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126d19190613e82565b6126ee57604051637c2ccffd60e11b815260040160405180910390fd5b60405163a9059cbb60e01b81526001600160a01b038881166004830152602482018390527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303816000875af115801561275d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127819190613e82565b610e0c57604051637c2ccffd60e11b815260040160405180910390fd5b60008481526010602052604090205484906001600160a01b03166127d557604051635eeb253d60e11b815260040160405180910390fd5b600084815260126020526040902060048101546127fb906001600160a01b0316866129e7565b60028101546000906128419088906001600160401b031661283c826000908152601160205260409020600201546001600160401b03600160c01b9091041690565b61345e565b60038301549091506128538183613e6f565b60148054600090612865908490613e6f565b90915550508254600490849060ff1916600183021790555060405163a9059cbb60e01b81526001600160a01b038781166004830152602482018490527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303816000875af11580156128ec573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129109190613e82565b61292d57604051637c2ccffd60e11b815260040160405180910390fd5b60405163a9059cbb60e01b81526001600160a01b038681166004830152602482018390527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303816000875af115801561299c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129c09190613e82565b6129dd57604051637c2ccffd60e11b815260040160405180910390fd5b5050505050505050565b6001600160a01b0382166000908152600a60205260409020610961908261353d565b60008181526012602090815260408083206001810154808552601190935292206002830154612a429083906001600160401b0316612cdf565b816001016000828254612a559190613e6f565b90915550506004830154612a72906001600160a01b0316856129e7565b6000848152602081905260409020612a8990613549565b825460ff191660061783556002808401805467ffffffffffffffff1916905560006003850181905560048501805473ffffffffffffffffffffffffffffffffffffffff19169055908201805460019290612aed9084906001600160401b03166145c9565b82546101009290920a6001600160401b038181021990931691831602179091556002850154604051600160401b90910490911681528391507f33ba8f7627565d89f7ada2a6b81ea532b7aa9b11e91a78312d6e1fca0bfcd1dc9060200160405180910390a26000848152600660205260409020805467ffffffffffffffff19169055600082815260106020526040812060028301546004820154919291612ba0916001600160401b0390811691166145c9565b60048301546001600160401b039182169250600160c01b90041681118015612bdd57506001835460ff166004811115612bdb57612bdb61396d565b145b1561088d57825460ff19166004178355612bf86001426145c9565b6002840180546001600160401b0392909216600160801b0267ffffffffffffffff60801b1990921691909117905560405184907f4769361a442504ecaf038f35e119bcccdd5e42096b24c09e3c17fd17c6684c0290600090a2505050505050565b60008282604051602001612c809291909182526001600160401b0316602082015260400190565b60405160208183030381529060405280519060200120905092915050565b60006105d8836001600160a01b038416613552565b6000611079825490565b6001600160a01b038116600090815260018301602052604081205415156105d8565b6000828152601160205260408120600201546105d89084908490600160801b90046001600160401b031661345e565b600081608001516001600160401b031682604001516110799190613e2e565b6040517f23b872dd0000000000000000000000000000000000000000000000000000000081526001600160a01b038381166004830152306024830181905260448301849052917f0000000000000000000000000000000000000000000000000000000000000000909116906323b872dd906064016020604051808303816000875af1158015612dc0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612de49190613e82565b61096157604051637c2ccffd60e11b815260040160405180910390fd5b6001600160a01b0382166000908152600a6020526040902061096190826135a1565b612e2d8282613106565b60008281526008602090815260408083206001600160401b038086168552908352818420805460ff1916600190811790915586855260069093529083208054929390929091612e7e91859116613dfc565b92506101000a8154816001600160401b0302191690836001600160401b031602179055505050565b6000611113426135ad565b60006105d8612ec08484612ec5565b6135d9565b600080612ed4610100436145e8565b60025490915060009061010090612f039071010000000000000000000000000000000000900460ff16866145fc565b612f0d919061461e565b6001600160401b031690506000612f26610100876145e8565b9050600061010082612f388587613e6f565b612f429190613e6f565b612f4c91906145e8565b979650505050505050565b606060006105d883613633565b600081604051602001612f779190613dd3565b604051602081830303815290604052805190602001209050919050565b6001600160a01b038216600090815260096020526040902061096190826135a1565b6000612fc5826020015161368f565b602083015160a0810151606090910151612fdf91906145fc565b6001600160401b03166110799190613e2e565b600080600061300085611b6d565b60008681526005602052604081205491925090613025906001600160401b03166135ad565b9050600182600681111561303b5761303b61396d565b14158061304f575061304d85826136ae565b155b15613062576000809350935050506130a3565b61306c8686612ec5565b92506000613079846135d9565b9050600061308688611092565b905080158061309c575061309a81836145e8565b155b9550505050505b9250929050565b60008060006130b98585612ff2565b90925090508180156130db575060025460ff600160801b909104811690821610155b95945050505050565b6001600160a01b0382166000908152600960205260409020610961908261353d565b6000613111826136c4565b6001600160401b03169050428110613155576040517f6b4b1a4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60025461317290600160401b90046001600160401b031682613e6f565b42106131aa576040517fde55698e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526007602090815260408083206001600160401b038616845290915290205460ff1615613206576040517efab7d900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61321083836130aa565b613246576040517fd3ffa66b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526008602090815260408083206001600160401b038616845290915290205460ff1615610961576040517f98e7e55100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600060ff198216816110e9826136d7565b6000806105d8836136d7565b6000838152600760205260408120906132d7612ea6565b6001600160401b0316815260208101919091526040016000205460ff161561332b576040517f3edef7db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600480546040517f94c8919d0000000000000000000000000000000000000000000000000000000081526001600160a01b03909116916394c8919d9161337591869186910161464c565b602060405180830381865afa158015613392573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133b69190613e82565b6133ec576040517ffcd03a4700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000838152600760205260408120600191613405612ea6565b6001600160401b031681526020808201929092526040908101600020805460ff19169315159390931790925590518481527f3b989d183b84b02259d7c14b34a9c9eb0fccb4c355a920d25e581e25aef4993d91016117f4565b60008381526010602052604081206001600160401b03808416908516106134b1576040517f56607cb000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160e081018252600183015481526002830154602082015260038301549181019190915260048201546001600160401b038082166060840152600160401b820481166080840152600160801b8204811660a0840152600160c01b9091041660c08201526135209061368f565b61352a85856145c9565b6001600160401b03166130db9190613e2e565b60006105d88383613749565b61129981613843565b600081815260018301602052604081205461359957508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155611079565b506000611079565b60006105d88383613552565b60006110797f0000000000000000000000000000000000000000000000000000000000000000836146f6565b60008060ff83166135eb600143613e1b565b6135f59190613e1b565b409050600081900361360957613609614724565b60408051602081018390520160405160208183030381529060405280519060200120915050919050565b60608160000180548060200260200160405190810160405280929190818152602001828054801561368357602002820191906000526020600020905b81548152602001906001019080831161366f575b50505050509050919050565b600081608001516001600160401b031682602001516110799190613e2e565b60006001600160401b03808416908316106105d8565b60006110796136d2836138a5565b6138b2565b7fff00000000000000000000000000000000000000000000000000000000000000811660015b60208110156106fc57600891821c91613717908290613e2e565b83901b7fff000000000000000000000000000000000000000000000000000000000000001691909117906001016136fd565b6000818152600183016020526040812054801561383257600061376d600183613e1b565b855490915060009061378190600190613e1b565b90508082146137e65760008660000182815481106137a1576137a16145b3565b90600052602060002001549050808760000184815481106137c4576137c46145b3565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806137f7576137f761473a565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050611079565b6000915050611079565b5092915050565b600061384d825490565b905060005b8181101561389d57826001016000846000018381548110613875576138756145b3565b9060005260206000200154815260200190815260200160002060009055806001019050613852565b505060009055565b6000611079826001613dfc565b60006110797f0000000000000000000000000000000000000000000000000000000000000000836145fc565b60408051610100810182526000608080830182815260a080850184905260c0850184905260e08501849052908452845190810185528281526020808201849052818601849052606080830185905292820192909252818401528351908101845290815290918201905b8152600060209091015290565b60006020828403121561396657600080fd5b5035919050565b634e487b7160e01b600052602160045260246000fd5b60208101600583106139975761399761396d565b91905290565b6001600160a01b038116811461129957600080fd5b6000806000606084860312156139c757600080fd5b8335925060208401356139d98161399d565b915060408401356139e98161399d565b809150509250925092565b6001600160401b038116811461129957600080fd5b8035613a14816139f4565b919050565b60008060408385031215613a2c57600080fd5b823591506020830135613a3e816139f4565b809150509250929050565b600061010082840312156106fc57600080fd5b60008060006101408486031215613a7257600080fd5b833592506020840135613a84816139f4565b9150613a938560408601613a49565b90509250925092565b602080825282518282018190526000918401906040840190835b81811015613ad4578351835260209384019390920191600101613ab6565b509095945050505050565b6000815180845260005b81811015613b0557602081850181015186830182015201613ae9565b506000602082860101526020601f19601f83011685010191505092915050565b6001600160401b0381511682526001600160401b03602082015116602083015260ff604082015116604083015260ff60608201511660608301526000608082015160a060808501526110e960a0850182613adf565b602081526000825160ff815116602084015260ff602082015116604084015260ff604082015116606084015260ff606082015116608084015250602083015160e060a0840152613bce610100840182613b25565b90506040840151613be560c08501825160ff169052565b5060608401516001600160401b03811660e0850152509392505050565b600060208284031215613c1457600080fd5b81356001600160401b03811115613c2a57600080fd5b820161016081850312156105d857600080fd5b6000815160408452613c526040850182613adf565b602093840151949093019390935250919050565b6001600160a01b038151168252600060208201518051602085015260208101516040850152604081015160608501526001600160401b0360608201511660808501526001600160401b0360808201511660a08501526001600160401b0360a08201511660c08501526001600160401b0360c08201511660e0850152506040820151610160610100850152613cfe610160850182613c3d565b90506060830151613d1b6101208601826001600160401b03169052565b5060808301516101408501528091505092915050565b602081526000825160406020840152613d4d6060840182613c66565b90506001600160401b0360208501511660408401528091505092915050565b60208101600783106139975761399761396d565b60008060408385031215613d9357600080fd5b823591506020830135613a3e8161399d565b6000806101208385031215613db957600080fd5b82359150613dca8460208501613a49565b90509250929050565b6020815260006105d86020830184613c66565b634e487b7160e01b600052601160045260246000fd5b6001600160401b03818116838216019081111561107957611079613de6565b8181038181111561107957611079613de6565b808202811582820484141761107957611079613de6565b634e487b7160e01b600052601260045260246000fd5b600082613e6a57613e6a613e45565b500490565b8082018082111561107957611079613de6565b600060208284031215613e9457600080fd5b815180151581146105d857600080fd5b61ffff828116828216039081111561107957611079613de6565b600181811c90821680613ed257607f821691505b6020821081036106fc57634e487b7160e01b600052602260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b0381118282101715613f2a57613f2a613ef2565b60405290565b60405160a081016001600160401b0381118282101715613f2a57613f2a613ef2565b60405160e081016001600160401b0381118282101715613f2a57613f2a613ef2565b604051601f8201601f191681016001600160401b0381118282101715613f9c57613f9c613ef2565b604052919050565b600060408284031215613fb657600080fd5b613fbe613f08565b905081356001600160401b03811115613fd657600080fd5b8201601f81018413613fe757600080fd5b80356001600160401b0381111561400057614000613ef2565b614013601f8201601f1916602001613f74565b81815285602083850101111561402857600080fd5b81602084016020830137600060209282018301528352928301359282019290925292915050565b600081360361016081121561406357600080fd5b61406b613f30565b83356140768161399d565b815260e0601f198301121561408a57600080fd5b614092613f52565b602085810135825260408087013591830191909152606086013590820152915060808401356140c0816139f4565b606083015260a08401356140d3816139f4565b608083015260c08401356140e6816139f4565b60a083015260e08401356140f9816139f4565b60c08301526020810191909152610100830135906001600160401b0382111561412157600080fd5b61412d36838601613fa4565b604082015261413f6101208501613a09565b6060820152610140939093013560808401525090919050565b60006020828403121561416a57600080fd5b81356105d88161399d565b60006020828403121561418757600080fd5b81356105d8816139f4565b60008235603e198336030181126141a857600080fd5b9190910192915050565b6000808335601e198436030181126141c957600080fd5b8301803591506001600160401b038211156141e357600080fd5b6020019150368190038213156130a357600080fd5b60008135611079816139f4565b601f82111561096157806000526020600020601f840160051c8101602085101561422c5750805b601f840160051c820191505b8181101561424c5760008155600101614238565b5050505050565b8135601e1983360301811261426757600080fd5b820180356001600160401b038111801561428057600080fd5b81360360208401131561429257600080fd5b600090506142aa826142a48654613ebe565b86614205565b80601f8311600181146142df578284156142c75750848201602001355b600019600386901b1c1916600185901b17865561433e565b600086815260209020601f19851690845b82811015614312576020858901810135835594850194600190920191016142f0565b50858210156143325760001960f88760031b161c19602085890101351681555b505060018460011b0186555b505050505060209190910135600190910155565b813561435d8161399d565b815473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03919091161781556020820135600182015560408201356002820155606082013560038201556004810160808301356143b5816139f4565b815467ffffffffffffffff19166001600160401b0382161782555060a08301356143de816139f4565b81546fffffffffffffffff0000000000000000191660409190911b6fffffffffffffffff00000000000000001617815561445261441d60c085016141f8565b825467ffffffffffffffff60801b191660809190911b77ffffffffffffffff0000000000000000000000000000000016178255565b6144aa61446160e085016141f8565b825477ffffffffffffffffffffffffffffffffffffffffffffffff1660c09190911b7fffffffffffffffff00000000000000000000000000000000000000000000000016178255565b506144c56144bc610100840184614192565b60058301614253565b6144f66144d561012084016141f8565b600783016001600160401b0382166001600160401b03198254161781555050565b6101409190910135600890910155565b838152823560208083019190915283013560408083019190915283013560608083019190915261012082019084013561453e816139f4565b6001600160401b038116608084015250608084013561455c816139f4565b6001600160401b03811660a08401525060a084013561457a816139f4565b6001600160401b03811660c08401525061459660c08501613a09565b6001600160401b0390811660e084015283166101008301526110e9565b634e487b7160e01b600052603260045260246000fd5b6001600160401b03828116828216039081111561107957611079613de6565b6000826145f7576145f7613e45565b500690565b6001600160401b03818116838216029081169081811461383c5761383c613de6565b60006001600160401b0383168061463757614637613e45565b806001600160401b0384160691505092915050565b82358152602080840135908201526000610120820161467b604084016040870180358252602090810135910152565b614695608084016080870180358252602090810135910152565b6146af60c0840160c0870180358252602090810135910152565b610120610100840152835190819052602084019061014084019060005b818110156146ea5783518352602093840193909201916001016146cc565b50909695505050505050565b60006001600160401b0383168061470f5761470f613e45565b806001600160401b0384160491505092915050565b634e487b7160e01b600052600160045260246000fd5b634e487b7160e01b600052603160045260246000fdfea2646970667358221220232fe024bec06a9b520ba91c64fc996624bcc0b3f5a0229b9ff9046572714c3864736f6c634300081c0033"; public MarketplaceDeploymentBase() : base(BYTECODE) { } public MarketplaceDeploymentBase(string byteCode) : base(byteCode) { } [Parameter("tuple", "config", 1)] diff --git a/ProjectPlugins/CodexContractsPlugin/TestTokenContract.cs b/ProjectPlugins/CodexContractsPlugin/TestTokenContract.cs new file mode 100644 index 00000000..81ba4491 --- /dev/null +++ b/ProjectPlugins/CodexContractsPlugin/TestTokenContract.cs @@ -0,0 +1,207 @@ +using System.Numerics; +using Nethereum.ABI.FunctionEncoding.Attributes; +using Nethereum.Contracts; + +// Generated code, do not modify. +// See Marketplace/README for how to update. + +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. +namespace CodexContractsPlugin +{ + public partial class TestTokenDeployment : TestTokenDeploymentBase + { + public TestTokenDeployment() : base(BYTECODE) { } + public TestTokenDeployment(string byteCode) : base(byteCode) { } + } + + public class TestTokenDeploymentBase : ContractDeploymentMessage + { + public static string BYTECODE = "0x608060405234801561001057600080fd5b50604051806040016040528060098152602001682a32b9ba2a37b5b2b760b91b815250604051806040016040528060038152602001621514d560ea1b815250816003908161005e9190610112565b50600461006b8282610112565b5050506101d0565b634e487b7160e01b600052604160045260246000fd5b600181811c9082168061009d57607f821691505b6020821081036100bd57634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111561010d57806000526020600020601f840160051c810160208510156100ea5750805b601f840160051c820191505b8181101561010a57600081556001016100f6565b50505b505050565b81516001600160401b0381111561012b5761012b610073565b61013f816101398454610089565b846100c3565b6020601f821160018114610173576000831561015b5750848201515b600019600385901b1c1916600184901b17845561010a565b600084815260208120601f198516915b828110156101a35787850151825560209485019460019092019101610183565b50848210156101c15786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b610823806101df6000396000f3fe608060405234801561001057600080fd5b50600436106100be5760003560e01c806340c10f191161007657806395d89b411161005b57806395d89b4114610176578063a9059cbb1461017e578063dd62ed3e1461019157600080fd5b806340c10f191461013857806370a082311461014d57600080fd5b806318160ddd116100a757806318160ddd1461010457806323b872dd14610116578063313ce5671461012957600080fd5b806306fdde03146100c3578063095ea7b3146100e1575b600080fd5b6100cb6101ca565b6040516100d8919061066c565b60405180910390f35b6100f46100ef3660046106d6565b61025c565b60405190151581526020016100d8565b6002545b6040519081526020016100d8565b6100f4610124366004610700565b610276565b604051601281526020016100d8565b61014b6101463660046106d6565b61029a565b005b61010861015b36600461073d565b6001600160a01b031660009081526020819052604090205490565b6100cb6102a8565b6100f461018c3660046106d6565b6102b7565b61010861019f36600461075f565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6060600380546101d990610792565b80601f016020809104026020016040519081016040528092919081815260200182805461020590610792565b80156102525780601f1061022757610100808354040283529160200191610252565b820191906000526020600020905b81548152906001019060200180831161023557829003601f168201915b5050505050905090565b60003361026a8185856102c5565b60019150505b92915050565b6000336102848582856102d7565b61028f858585610374565b506001949350505050565b6102a482826103ec565b5050565b6060600480546101d990610792565b60003361026a818585610374565b6102d28383836001610422565b505050565b6001600160a01b0383811660009081526001602090815260408083209386168352929052205460001981101561036e578181101561035f576040517ffb8f41b20000000000000000000000000000000000000000000000000000000081526001600160a01b038416600482015260248101829052604481018390526064015b60405180910390fd5b61036e84848484036000610422565b50505050565b6001600160a01b0383166103b7576040517f96c6fd1e00000000000000000000000000000000000000000000000000000000815260006004820152602401610356565b6001600160a01b0382166103e15760405163ec442f0560e01b815260006004820152602401610356565b6102d2838383610529565b6001600160a01b0382166104165760405163ec442f0560e01b815260006004820152602401610356565b6102a460008383610529565b6001600160a01b038416610465576040517fe602df0500000000000000000000000000000000000000000000000000000000815260006004820152602401610356565b6001600160a01b0383166104a8576040517f94280d6200000000000000000000000000000000000000000000000000000000815260006004820152602401610356565b6001600160a01b038085166000908152600160209081526040808320938716835292905220829055801561036e57826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161051b91815260200190565b60405180910390a350505050565b6001600160a01b03831661055457806002600082825461054991906107cc565b909155506105df9050565b6001600160a01b038316600090815260208190526040902054818110156105c0576040517fe450d38c0000000000000000000000000000000000000000000000000000000081526001600160a01b03851660048201526024810182905260448101839052606401610356565b6001600160a01b03841660009081526020819052604090209082900390555b6001600160a01b0382166105fb5760028054829003905561061a565b6001600160a01b03821660009081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161065f91815260200190565b60405180910390a3505050565b602081526000825180602084015260005b8181101561069a576020818601810151604086840101520161067d565b506000604082850101526040601f19601f83011684010191505092915050565b80356001600160a01b03811681146106d157600080fd5b919050565b600080604083850312156106e957600080fd5b6106f2836106ba565b946020939093013593505050565b60008060006060848603121561071557600080fd5b61071e846106ba565b925061072c602085016106ba565b929592945050506040919091013590565b60006020828403121561074f57600080fd5b610758826106ba565b9392505050565b6000806040838503121561077257600080fd5b61077b836106ba565b9150610789602084016106ba565b90509250929050565b600181811c908216806107a657607f821691505b6020821081036107c657634e487b7160e01b600052602260045260246000fd5b50919050565b8082018082111561027057634e487b7160e01b600052601160045260246000fdfea26469706673582212209301276ee274d22ba81d0ea93fe7e3b411212420214338b4bbbd546fd54253a564736f6c634300081c0033"; + public TestTokenDeploymentBase() : base(BYTECODE) { } + public TestTokenDeploymentBase(string byteCode) : base(byteCode) { } + + } + + public partial class AllowanceFunction : AllowanceFunctionBase { } + + [Function("allowance", "uint256")] + public class AllowanceFunctionBase : FunctionMessage + { + [Parameter("address", "owner", 1)] + public virtual string Owner { get; set; } + [Parameter("address", "spender", 2)] + public virtual string Spender { get; set; } + } + + public partial class ApproveFunction : ApproveFunctionBase { } + + [Function("approve", "bool")] + public class ApproveFunctionBase : FunctionMessage + { + [Parameter("address", "spender", 1)] + public virtual string Spender { get; set; } + [Parameter("uint256", "value", 2)] + public virtual BigInteger Value { get; set; } + } + + public partial class BalanceOfFunction : BalanceOfFunctionBase { } + + [Function("balanceOf", "uint256")] + public class BalanceOfFunctionBase : FunctionMessage + { + [Parameter("address", "account", 1)] + public virtual string Account { get; set; } + } + + public partial class DecimalsFunction : DecimalsFunctionBase { } + + [Function("decimals", "uint8")] + public class DecimalsFunctionBase : FunctionMessage + { + + } + + public partial class MintFunction : MintFunctionBase { } + + [Function("mint")] + public class MintFunctionBase : FunctionMessage + { + [Parameter("address", "holder", 1)] + public virtual string Holder { get; set; } + [Parameter("uint256", "amount", 2)] + public virtual BigInteger Amount { get; set; } + } + + public partial class NameFunction : NameFunctionBase { } + + [Function("name", "string")] + public class NameFunctionBase : FunctionMessage + { + + } + + public partial class SymbolFunction : SymbolFunctionBase { } + + [Function("symbol", "string")] + public class SymbolFunctionBase : FunctionMessage + { + + } + + public partial class TotalSupplyFunction : TotalSupplyFunctionBase { } + + [Function("totalSupply", "uint256")] + public class TotalSupplyFunctionBase : FunctionMessage + { + + } + + public partial class TransferFunction : TransferFunctionBase { } + + [Function("transfer", "bool")] + public class TransferFunctionBase : FunctionMessage + { + [Parameter("address", "to", 1)] + public virtual string To { get; set; } + [Parameter("uint256", "value", 2)] + public virtual BigInteger Value { get; set; } + } + + public partial class TransferFromFunction : TransferFromFunctionBase { } + + [Function("transferFrom", "bool")] + public class TransferFromFunctionBase : FunctionMessage + { + [Parameter("address", "from", 1)] + public virtual string From { get; set; } + [Parameter("address", "to", 2)] + public virtual string To { get; set; } + [Parameter("uint256", "value", 3)] + public virtual BigInteger Value { get; set; } + } + + public partial class ApprovalEventDTO : ApprovalEventDTOBase { } + + [Event("Approval")] + public class ApprovalEventDTOBase : IEventDTO + { + [Parameter("address", "owner", 1, true)] + public virtual string Owner { get; set; } + [Parameter("address", "spender", 2, true)] + public virtual string Spender { get; set; } + [Parameter("uint256", "value", 3, false)] + public virtual BigInteger Value { get; set; } + } + + public partial class TransferEventDTO : TransferEventDTOBase { } + + [Event("Transfer")] + public class TransferEventDTOBase : IEventDTO + { + [Parameter("address", "from", 1, true)] + public virtual string From { get; set; } + [Parameter("address", "to", 2, true)] + public virtual string To { get; set; } + [Parameter("uint256", "value", 3, false)] + public virtual BigInteger Value { get; set; } + } + + public partial class AllowanceOutputDTO : AllowanceOutputDTOBase { } + + [FunctionOutput] + public class AllowanceOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + + + public partial class BalanceOfOutputDTO : BalanceOfOutputDTOBase { } + + [FunctionOutput] + public class BalanceOfOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } + + public partial class DecimalsOutputDTO : DecimalsOutputDTOBase { } + + [FunctionOutput] + public class DecimalsOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint8", "", 1)] + public virtual byte ReturnValue1 { get; set; } + } + + + + public partial class NameOutputDTO : NameOutputDTOBase { } + + [FunctionOutput] + public class NameOutputDTOBase : IFunctionOutputDTO + { + [Parameter("string", "", 1)] + public virtual string ReturnValue1 { get; set; } + } + + public partial class SymbolOutputDTO : SymbolOutputDTOBase { } + + [FunctionOutput] + public class SymbolOutputDTOBase : IFunctionOutputDTO + { + [Parameter("string", "", 1)] + public virtual string ReturnValue1 { get; set; } + } + + public partial class TotalSupplyOutputDTO : TotalSupplyOutputDTOBase { } + + [FunctionOutput] + public class TotalSupplyOutputDTOBase : IFunctionOutputDTO + { + [Parameter("uint256", "", 1)] + public virtual BigInteger ReturnValue1 { get; set; } + } +} +#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. diff --git a/ProjectPlugins/GethPlugin/GethContainerInfoExtractor.cs b/ProjectPlugins/GethPlugin/GethContainerInfoExtractor.cs index 4fc76763..f16b7b79 100644 --- a/ProjectPlugins/GethPlugin/GethContainerInfoExtractor.cs +++ b/ProjectPlugins/GethPlugin/GethContainerInfoExtractor.cs @@ -60,7 +60,16 @@ namespace GethPlugin private static string Retry(Func fetch) { - return Time.Retry(fetch, nameof(GethContainerInfoExtractor)); + // This class is the first moment where we interact with our new geth container. + // K8s might be moving pods and/or setting up new VMs. + // So we apply a generous retry timeout. + var retry = new Retry(nameof(GethContainerInfoExtractor), + maxTimeout: TimeSpan.FromMinutes(15.0), + sleepAfterFail: TimeSpan.FromSeconds(20.0), + onFail: f => { }, + failFast: false); + + return retry.Run(fetch); } } diff --git a/ProjectPlugins/GethPlugin/GethNode.cs b/ProjectPlugins/GethPlugin/GethNode.cs index cfd0af37..6f307eef 100644 --- a/ProjectPlugins/GethPlugin/GethNode.cs +++ b/ProjectPlugins/GethPlugin/GethNode.cs @@ -21,6 +21,7 @@ namespace GethPlugin string SendEth(EthAddress account, Ether eth); TResult Call(string contractAddress, TFunction function) where TFunction : FunctionMessage, new(); TResult Call(string contractAddress, TFunction function, ulong blockNumber) where TFunction : FunctionMessage, new(); + void Call(string contractAddress, TFunction function) where TFunction : FunctionMessage, new(); void Call(string contractAddress, TFunction function, ulong blockNumber) where TFunction : FunctionMessage, new(); string SendTransaction(string contractAddress, TFunction function) where TFunction : FunctionMessage, new(); Transaction GetTransaction(string transactionHash); @@ -32,6 +33,7 @@ namespace GethPlugin BlockInterval ConvertTimeRangeToBlockRange(TimeRange timeRange); BlockTimeEntry GetBlockForNumber(ulong number); void IterateFunctionCalls(BlockInterval blockInterval, Action onCall) where TFunc : FunctionMessage, new(); + IGethNode WithDifferentAccount(EthAccount account); } public class DeploymentGethNode : BaseGethNode, IGethNode @@ -68,6 +70,21 @@ namespace GethPlugin var creator = new NethereumInteractionCreator(log, blockCache, address.Host, address.Port, account.PrivateKey); return creator.CreateWorkflow(); } + + public IGethNode WithDifferentAccount(EthAccount account) + { + return new DeploymentGethNode(log, blockCache, + new GethDeployment( + StartResult.Pod, + StartResult.DiscoveryPort, + StartResult.HttpPort, + StartResult.WsPort, + new GethAccount( + account.EthAddress.Address, + account.PrivateKey + ), + account.PrivateKey)); + } } public class CustomGethNode : BaseGethNode, IGethNode @@ -95,6 +112,11 @@ namespace GethPlugin throw new NotImplementedException(); } + public IGethNode WithDifferentAccount(EthAccount account) + { + return new CustomGethNode(log, blockCache, gethHost, gethPort, account.PrivateKey); + } + protected override NethereumInteraction StartInteraction() { var creator = new NethereumInteractionCreator(log, blockCache, gethHost, gethPort, privateKey); @@ -116,7 +138,7 @@ namespace GethPlugin public Ether GetEthBalance(EthAddress address) { - return StartInteraction().GetEthBalance(address.Address).Eth(); + return StartInteraction().GetEthBalance(address.Address).Wei(); } public string SendEth(IHasEthAddress owner, Ether eth) @@ -139,6 +161,11 @@ namespace GethPlugin return StartInteraction().Call(contractAddress, function, blockNumber); } + public void Call(string contractAddress, TFunction function) where TFunction : FunctionMessage, new() + { + StartInteraction().Call(contractAddress, function); + } + public void Call(string contractAddress, TFunction function, ulong blockNumber) where TFunction : FunctionMessage, new() { StartInteraction().Call(contractAddress, function, blockNumber); diff --git a/Tests/CodexReleaseTests/DataTests/DataExpiryTest.cs b/Tests/CodexReleaseTests/DataTests/DataExpiryTest.cs index 4b9139e6..a4e3500d 100644 --- a/Tests/CodexReleaseTests/DataTests/DataExpiryTest.cs +++ b/Tests/CodexReleaseTests/DataTests/DataExpiryTest.cs @@ -1,4 +1,5 @@ using CodexContractsPlugin; +using CodexPlugin; using CodexTests; using NUnit.Framework; using Utils; @@ -8,17 +9,23 @@ namespace CodexReleaseTests.DataTests [TestFixture] public class DataExpiryTest : CodexDistTest { + private readonly TimeSpan blockTtl = TimeSpan.FromMinutes(1.0); + private readonly TimeSpan blockInterval = TimeSpan.FromSeconds(10.0); + private readonly int blockCount = 100000; + + private ICodexSetup WithFastBlockExpiry(ICodexSetup setup) + { + return setup + .WithBlockTTL(blockTtl) + .WithBlockMaintenanceInterval(blockInterval) + .WithBlockMaintenanceNumber(blockCount); + } + [Test] public void DeletesExpiredData() { - var fileSize = 100.MB(); - var blockTtl = TimeSpan.FromMinutes(1.0); - var interval = TimeSpan.FromSeconds(10.0); - var node = StartCodex(s => s - .WithBlockTTL(blockTtl) - .WithBlockMaintenanceInterval(interval) - .WithBlockMaintenanceNumber(100000) - ); + var fileSize = 3.MB(); + var node = StartCodex(s => WithFastBlockExpiry(s)); var startSpace = node.Space(); Assert.That(startSpace.QuotaUsedBytes, Is.EqualTo(0)); @@ -46,18 +53,13 @@ namespace CodexReleaseTests.DataTests [Test] public void DeletesExpiredDataUsedByStorageRequests() { - var fileSize = 100.MB(); - var blockTtl = TimeSpan.FromMinutes(1.0); - var interval = TimeSpan.FromSeconds(10.0); + var fileSize = 3.MB(); var bootstrapNode = StartCodex(); var geth = StartGethNode(s => s.IsMiner()); var contracts = Ci.StartCodexContracts(geth, bootstrapNode.Version); - var node = StartCodex(s => s + var node = StartCodex(s => WithFastBlockExpiry(s) .EnableMarketplace(geth, contracts, m => m.WithInitial(100.Eth(), 100.Tst())) - .WithBlockTTL(blockTtl) - .WithBlockMaintenanceInterval(interval) - .WithBlockMaintenanceNumber(100000) ); var startSpace = node.Space(); @@ -92,5 +94,55 @@ namespace CodexReleaseTests.DataTests Assert.That(cleanupSpace.QuotaUsedBytes, Is.EqualTo(startSpace.QuotaUsedBytes)); Assert.That(cleanupSpace.FreeBytes, Is.EqualTo(startSpace.FreeBytes)); } + + [Test] + [Ignore("Issue not fixed. Ticket: https://github.com/codex-storage/nim-codex/issues/1291")] + public void StorageRequestsKeepManifests() + { + var bootstrapNode = StartCodex(s => s.WithName("Bootstrap")); + var geth = StartGethNode(s => s.IsMiner()); + var contracts = Ci.StartCodexContracts(geth, bootstrapNode.Version); + var client = StartCodex(s => WithFastBlockExpiry(s) + .WithName("client") + .WithBootstrapNode(bootstrapNode) + .EnableMarketplace(geth, contracts, m => m.WithInitial(100.Eth(), 100.Tst())) + ); + + var hosts = StartCodex(3, s => WithFastBlockExpiry(s) + .WithName("host") + .WithBootstrapNode(bootstrapNode) + .EnableMarketplace(geth, contracts, m => m.AsStorageNode().WithInitial(100.Eth(), 100.Tst())) + ); + foreach (var host in hosts) host.Marketplace.MakeStorageAvailable(new CodexClient.CreateStorageAvailability( + totalSpace: 2.GB(), + maxDuration: TimeSpan.FromDays(2.0), + minPricePerBytePerSecond: 1.TstWei(), + totalCollateral: 10.Tst())); + + var uploadCid = client.UploadFile(GenerateTestFile(5.MB())); + var request = client.Marketplace.RequestStorage(new CodexClient.StoragePurchaseRequest(uploadCid) + { + CollateralPerByte = 1.TstWei(), + Duration = TimeSpan.FromDays(1.0), + Expiry = TimeSpan.FromHours(1.0), + MinRequiredNumberOfNodes = 3, + NodeFailureTolerance = 1, + PricePerBytePerSecond = 10.TstWei(), + ProofProbability = 99999 + }); + request.WaitForStorageContractSubmitted(); + request.WaitForStorageContractStarted(); + var storeCid = request.ContentId; + + var clientManifest = client.DownloadManifestOnly(storeCid); + Assert.That(clientManifest.Manifest.Protected, Is.True); + + client.Stop(waitTillStopped: true); + Thread.Sleep(blockTtl * 2.0); + + var checker = StartCodex(s => s.WithName("checker").WithBootstrapNode(bootstrapNode)); + var manifest = checker.DownloadManifestOnly(storeCid); + Assert.That(manifest.Manifest.Protected, Is.True); + } } } diff --git a/Tests/CodexReleaseTests/DataTests/DecodeTest.cs b/Tests/CodexReleaseTests/DataTests/DecodeTest.cs index 1aaf742f..0720ff10 100644 --- a/Tests/CodexReleaseTests/DataTests/DecodeTest.cs +++ b/Tests/CodexReleaseTests/DataTests/DecodeTest.cs @@ -1,6 +1,4 @@ -using System.Security.Cryptography; -using CodexReleaseTests.Utils; -using Nethereum.JsonRpc.Client; +using CodexReleaseTests.Utils; using NUnit.Framework; using Utils; diff --git a/Tests/CodexReleaseTests/DataTests/LocalFilesTest.cs b/Tests/CodexReleaseTests/DataTests/LocalFilesTest.cs index 4069aa49..8a69c834 100644 --- a/Tests/CodexReleaseTests/DataTests/LocalFilesTest.cs +++ b/Tests/CodexReleaseTests/DataTests/LocalFilesTest.cs @@ -33,9 +33,9 @@ namespace CodexReleaseTests.DataTests var local2 = localFiles.Content.Single(f => f.Cid == cid2); Assert.That(local1.Manifest.Protected, Is.False); - Assert.That(local1.Manifest.OriginalBytes, Is.EqualTo(size1)); + Assert.That(local1.Manifest.DatasetSize, Is.EqualTo(size1)); Assert.That(local2.Manifest.Protected, Is.False); - Assert.That(local2.Manifest.OriginalBytes, Is.EqualTo(size2)); + Assert.That(local2.Manifest.DatasetSize, Is.EqualTo(size2)); } } } diff --git a/Tests/CodexReleaseTests/DataTests/ManifestOnlyDownloadTest.cs b/Tests/CodexReleaseTests/DataTests/ManifestOnlyDownloadTest.cs index c55b3f8f..f1fcc84c 100644 --- a/Tests/CodexReleaseTests/DataTests/ManifestOnlyDownloadTest.cs +++ b/Tests/CodexReleaseTests/DataTests/ManifestOnlyDownloadTest.cs @@ -27,7 +27,7 @@ namespace CodexReleaseTests.DataTests Assert.That(spaceDiff, Is.LessThan(64.KB().SizeInBytes)); Assert.That(localDataset.Cid, Is.EqualTo(cid)); - Assert.That(localDataset.Manifest.OriginalBytes.SizeInBytes, Is.EqualTo(file.GetFilesize().SizeInBytes)); + Assert.That(localDataset.Manifest.DatasetSize.SizeInBytes, Is.EqualTo(file.GetFilesize().SizeInBytes)); } } } diff --git a/Tests/CodexReleaseTests/DataTests/StreamlessDownloadTest.cs b/Tests/CodexReleaseTests/DataTests/StreamlessDownloadTest.cs index d53d16dc..93b89f19 100644 --- a/Tests/CodexReleaseTests/DataTests/StreamlessDownloadTest.cs +++ b/Tests/CodexReleaseTests/DataTests/StreamlessDownloadTest.cs @@ -22,7 +22,7 @@ namespace CodexReleaseTests.DataTests var localDataset = downloader.DownloadStreamlessWait(cid, size); Assert.That(localDataset.Cid, Is.EqualTo(cid)); - Assert.That(localDataset.Manifest.OriginalBytes.SizeInBytes, Is.EqualTo(file.GetFilesize().SizeInBytes)); + Assert.That(localDataset.Manifest.DatasetSize.SizeInBytes, Is.EqualTo(file.GetFilesize().SizeInBytes)); // Stop the uploader node and verify that the downloader has the data. uploader.Stop(waitTillStopped: true); diff --git a/Tests/CodexReleaseTests/DataTests/SwarmTest.cs b/Tests/CodexReleaseTests/DataTests/SwarmTest.cs index 427085a5..f4099ec0 100644 --- a/Tests/CodexReleaseTests/DataTests/SwarmTest.cs +++ b/Tests/CodexReleaseTests/DataTests/SwarmTest.cs @@ -7,15 +7,22 @@ using Utils; namespace CodexReleaseTests.DataTests { - [TestFixture] + [TestFixture(2, 10)] + [TestFixture(5, 20)] + [TestFixture(10, 20)] public class SwarmTests : AutoBootstrapDistTest { + private readonly int numberOfNodes; + private readonly int filesizeMb; + + public SwarmTests(int numberOfNodes, int filesizeMb) + { + this.numberOfNodes = numberOfNodes; + this.filesizeMb = filesizeMb; + } + [Test] - [Combinatorial] - public void SmallSwarm( - [Values(2)] int numberOfNodes, - [Values(10)] int filesizeMb - ) + public void Swarm() { var filesize = filesizeMb.MB(); var nodes = StartCodex(numberOfNodes); @@ -28,11 +35,7 @@ namespace CodexReleaseTests.DataTests } [Test] - [Combinatorial] - public void StreamlessSmallSwarm( - [Values(2)] int numberOfNodes, - [Values(10)] int filesizeMb - ) + public void StreamlessSwarm() { var filesize = filesizeMb.MB(); var nodes = StartCodex(numberOfNodes); @@ -86,7 +89,7 @@ namespace CodexReleaseTests.DataTests var file = remaining.PickOneRandom(); try { - var dl = node.DownloadContent(file.Cid); + var dl = node.DownloadContent(file.Cid, TimeSpan.FromMinutes(30)); lock (file.Lock) { file.Downloaded.Add(dl); diff --git a/Tests/CodexReleaseTests/DataTests/UnknownCidTest.cs b/Tests/CodexReleaseTests/DataTests/UnknownCidTest.cs index 2d64e863..40b7f9f1 100644 --- a/Tests/CodexReleaseTests/DataTests/UnknownCidTest.cs +++ b/Tests/CodexReleaseTests/DataTests/UnknownCidTest.cs @@ -19,7 +19,7 @@ namespace CodexReleaseTests.DataTests try { - node.DownloadContent(unknownCid); + node.DownloadContent(unknownCid, TimeSpan.FromMinutes(2.0)); } catch (Exception ex) { diff --git a/Tests/CodexReleaseTests/MarketTests/ChainMonitor.cs b/Tests/CodexReleaseTests/MarketTests/ChainMonitor.cs deleted file mode 100644 index 72b9da02..00000000 --- a/Tests/CodexReleaseTests/MarketTests/ChainMonitor.cs +++ /dev/null @@ -1,49 +0,0 @@ -using CodexContractsPlugin; -using CodexContractsPlugin.ChainMonitor; -using Logging; - -namespace CodexReleaseTests.MarketTests -{ - public class ChainMonitor - { - private readonly ChainState chainMonitor; - private readonly TimeSpan interval; - private CancellationTokenSource cts = new CancellationTokenSource(); - private Task worker = null!; - - public ChainMonitor(ILog log, ICodexContracts contracts, DateTime startUtc, TimeSpan interval) - { - chainMonitor = new ChainState(log, contracts, new DoNothingChainEventHandler(), startUtc, true); - this.interval = interval; - } - - public void Start() - { - cts = new CancellationTokenSource(); - worker = Task.Run(Worker); - } - - public void Stop() - { - cts.Cancel(); - worker.Wait(); - - worker = null!; - cts = null!; - } - - public PeriodMonitorResult GetPeriodReports() - { - return chainMonitor.PeriodMonitor.GetAndClearReports(); - } - - private void Worker() - { - while (!cts.IsCancellationRequested) - { - Thread.Sleep(interval); - chainMonitor.Update(); - } - } - } -} diff --git a/Tests/CodexReleaseTests/MarketTests/FailTest.cs b/Tests/CodexReleaseTests/MarketTests/FailTest.cs index cbd39600..9c5308be 100644 --- a/Tests/CodexReleaseTests/MarketTests/FailTest.cs +++ b/Tests/CodexReleaseTests/MarketTests/FailTest.cs @@ -21,7 +21,7 @@ namespace CodexReleaseTests.MarketTests [Test] [Combinatorial] public void Fail( - [Values([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])] int rerun + [Rerun] int rerun ) { var hosts = StartHosts(); @@ -68,7 +68,7 @@ namespace CodexReleaseTests.MarketTests private IStoragePurchaseContract CreateStorageRequest(ICodexNode client) { - var cid = client.UploadFile(GenerateTestFile(5.MB())); + var cid = client.UploadFile(GenerateTestFile(3.MB())); return client.Marketplace.RequestStorage(new StoragePurchaseRequest(cid) { Duration = HostAvailabilityMaxDuration / 2, diff --git a/Tests/CodexReleaseTests/MarketTests/FinishTest.cs b/Tests/CodexReleaseTests/MarketTests/FinishTest.cs index 87df023c..5b316075 100644 --- a/Tests/CodexReleaseTests/MarketTests/FinishTest.cs +++ b/Tests/CodexReleaseTests/MarketTests/FinishTest.cs @@ -6,13 +6,13 @@ using Utils; namespace CodexReleaseTests.MarketTests { [TestFixture(5, 3, 1)] - [TestFixture(10, 20, 10)] + [TestFixture(10, 8, 4)] public class FinishTest : MarketplaceAutoBootstrapDistTest { public FinishTest(int hosts, int slots, int tolerance) { this.hosts = hosts; - purchaseParams = new PurchaseParams(slots, tolerance, uploadFilesize: 10.MB()); + purchaseParams = new PurchaseParams(slots, tolerance, uploadFilesize: 3.MB()); } private readonly TestToken pricePerBytePerSecond = 10.TstWei(); @@ -22,12 +22,12 @@ namespace CodexReleaseTests.MarketTests protected override int NumberOfHosts => hosts; protected override int NumberOfClients => 1; protected override ByteSize HostAvailabilitySize => purchaseParams.SlotSize.Multiply(5.1); - protected override TimeSpan HostAvailabilityMaxDuration => Get8TimesConfiguredPeriodDuration() * 12; + protected override TimeSpan HostAvailabilityMaxDuration => GetContractDuration() * 2; [Test] [Combinatorial] public void Finish( - [Values([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])] int rerun + [Rerun] int rerun ) { var hosts = StartHosts(); diff --git a/Tests/CodexReleaseTests/MarketTests/JustTokensTest.cs b/Tests/CodexReleaseTests/MarketTests/JustTokensTest.cs new file mode 100644 index 00000000..093b5637 --- /dev/null +++ b/Tests/CodexReleaseTests/MarketTests/JustTokensTest.cs @@ -0,0 +1,51 @@ +using BlockchainUtils; +using CodexContractsPlugin; +using CodexPlugin; +using DistTestCore; +using GethPlugin; +using NUnit.Framework; +using Utils; + +namespace CodexReleaseTests.MarketTests +{ + [TestFixture] + public class TestTokenTransferTest : DistTest + { + private readonly EthAccount user1 = EthAccountGenerator.GenerateNew(); + private readonly EthAccount user2 = EthAccountGenerator.GenerateNew(); + + [Test] + public void CanTransferTokens() + { + var node = Ci.StartCodexNode(); + var blockCache = new BlockCache(); + var geth = Ci.StartGethNode(blockCache, s => s.IsMiner()); + var contracts = Ci.StartCodexContracts(geth, node.Version); + + geth.SendEth(user1.EthAddress, 1.Eth()); + geth.SendEth(user2.EthAddress, 1.Eth()); + + contracts.MintTestTokens(user1.EthAddress, 10.Tst()); + Balances(contracts, 10.Tst(), 0.Tst()); + + var geth1 = geth.WithDifferentAccount(user1); + var geth2 = geth.WithDifferentAccount(user2); + var contracts1 = contracts.WithDifferentGeth(geth1); + var contracts2 = contracts.WithDifferentGeth(geth2); + + contracts1.TransferTestTokens(user2.EthAddress, 5.Tst()); + Balances(contracts, 5.Tst(), 5.Tst()); + + contracts2.TransferTestTokens(user1.EthAddress, 2.Tst()); + Balances(contracts, 7.Tst(), 3.Tst()); + } + + private void Balances(ICodexContracts contracts, TestToken one, TestToken two) + { + var balance1 = contracts.GetTestTokenBalance(user1.EthAddress); + var balance2 = contracts.GetTestTokenBalance(user2.EthAddress); + Assert.That(balance1, Is.EqualTo(one)); + Assert.That(balance2, Is.EqualTo(two)); + } + } +} diff --git a/Tests/CodexReleaseTests/MarketTests/MaxCapacityTest.cs b/Tests/CodexReleaseTests/MarketTests/MaxCapacityTest.cs new file mode 100644 index 00000000..cd7306d6 --- /dev/null +++ b/Tests/CodexReleaseTests/MarketTests/MaxCapacityTest.cs @@ -0,0 +1,72 @@ +using CodexClient; +using CodexReleaseTests.Utils; +using NUnit.Framework; +using Utils; + +namespace CodexReleaseTests.MarketTests +{ + public class MaxCapacityTest : MarketplaceAutoBootstrapDistTest + { + private readonly TestToken pricePerBytePerSecond = 10.TstWei(); + private readonly PurchaseParams purchaseParams = new PurchaseParams( + nodes: 10, + tolerance: 5, + uploadFilesize: 10.MB() + ); + + protected override int NumberOfHosts => purchaseParams.Nodes / 2; + protected override int NumberOfClients => 1; + protected override ByteSize HostAvailabilitySize => purchaseParams.SlotSize.Multiply(2.1); + protected override TimeSpan HostAvailabilityMaxDuration => GetContractDuration() * 2; + + [Test] + [Combinatorial] + public void TwoSlotsEach( + [Rerun] int rerun + ) + { + var hosts = StartHosts(); + var client = StartClients().Single(); + AssertHostAvailabilitiesAreEmpty(hosts); + + var request = CreateStorageRequest(client); + + request.WaitForStorageContractSubmitted(); + AssertContractIsOnChain(request); + + WaitForContractStarted(request); + AssertContractSlotsAreFilledByHosts(request, hosts); + } + + private IStoragePurchaseContract CreateStorageRequest(ICodexNode client) + { + var cid = client.UploadFile(GenerateTestFile(purchaseParams.UploadFilesize)); + var config = GetContracts().Deployment.Config; + return client.Marketplace.RequestStorage(new StoragePurchaseRequest(cid) + { + Duration = GetContractDuration(), + Expiry = GetContractExpiry(), + MinRequiredNumberOfNodes = (uint)purchaseParams.Nodes, + NodeFailureTolerance = (uint)purchaseParams.Tolerance, + PricePerBytePerSecond = pricePerBytePerSecond, + ProofProbability = 20, + CollateralPerByte = 100.TstWei() + }); + } + + private TimeSpan GetContractExpiry() + { + return GetContractDuration() / 2; + } + + private TimeSpan GetContractDuration() + { + return Get8TimesConfiguredPeriodDuration(); + } + + private TimeSpan Get8TimesConfiguredPeriodDuration() + { + return GetPeriodDuration() * 8.0; + } + } +} diff --git a/Tests/CodexReleaseTests/MarketTests/RepairTest.cs b/Tests/CodexReleaseTests/MarketTests/RepairTest.cs index c515f639..04929659 100644 --- a/Tests/CodexReleaseTests/MarketTests/RepairTest.cs +++ b/Tests/CodexReleaseTests/MarketTests/RepairTest.cs @@ -29,13 +29,11 @@ namespace CodexReleaseTests.MarketTests #endregion - [Ignore("Test is ready. Waiting for repair implementation. " + - "Slots are never freed because proofs are never marked as missing. Issue: https://github.com/codex-storage/nim-codex/issues/1153")] [Test] [Combinatorial] public void RollingRepairSingleFailure( - [Values([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])] int rerun, - [Values(10)] int numFailures) + [Rerun] int rerun, + [Values(5)] int numFailures) { var hosts = StartHosts().ToList(); var client = StartClients().Single(); @@ -98,7 +96,7 @@ namespace CodexReleaseTests.MarketTests private void WaitForNewSlotFilledEvent(IStoragePurchaseContract contract, ulong slotIndex) { Log(nameof(WaitForNewSlotFilledEvent)); - var start = DateTime.UtcNow; + var start = DateTime.UtcNow - TimeSpan.FromSeconds(10.0); var timeout = contract.Purchase.Expiry; while (DateTime.UtcNow < start + timeout) @@ -122,6 +120,7 @@ namespace CodexReleaseTests.MarketTests if (matches.Length == 1) { Log($"Found the correct new slotFilled event: {matches[0].ToString()}"); + return; } Thread.Sleep(TimeSpan.FromSeconds(15)); diff --git a/Tests/CodexReleaseTests/MarketTests/SequentialContracts.cs b/Tests/CodexReleaseTests/MarketTests/SequentialContracts.cs index 383093cf..4f06d81e 100644 --- a/Tests/CodexReleaseTests/MarketTests/SequentialContracts.cs +++ b/Tests/CodexReleaseTests/MarketTests/SequentialContracts.cs @@ -21,13 +21,13 @@ namespace CodexReleaseTests.MarketTests protected override int NumberOfHosts => hosts; protected override int NumberOfClients => 6; protected override ByteSize HostAvailabilitySize => purchaseParams.SlotSize.Multiply(100.0); - protected override TimeSpan HostAvailabilityMaxDuration => Get8TimesConfiguredPeriodDuration() * 12; + protected override TimeSpan HostAvailabilityMaxDuration => GetContractDuration() * 2; private readonly TestToken pricePerBytePerSecond = 10.TstWei(); [Test] [Combinatorial] public void Sequential( - [Values(10)] int numGenerations) + [Values(5)] int numGenerations) { var hosts = StartHosts(); var clients = StartClients(); @@ -93,7 +93,7 @@ namespace CodexReleaseTests.MarketTests MinRequiredNumberOfNodes = (uint)purchaseParams.Nodes, NodeFailureTolerance = (uint)purchaseParams.Tolerance, PricePerBytePerSecond = pricePerBytePerSecond, - ProofProbability = 10000, + ProofProbability = 100000, CollateralPerByte = 1.TstWei() }); } diff --git a/Tests/CodexReleaseTests/MarketTests/StartTest.cs b/Tests/CodexReleaseTests/MarketTests/StartTest.cs index c64aef2a..d3272347 100644 --- a/Tests/CodexReleaseTests/MarketTests/StartTest.cs +++ b/Tests/CodexReleaseTests/MarketTests/StartTest.cs @@ -11,7 +11,7 @@ namespace CodexReleaseTests.MarketTests private readonly PurchaseParams purchaseParams = new PurchaseParams( nodes: 3, tolerance: 1, - uploadFilesize: 10.MB() + uploadFilesize: 3.MB() ); private readonly TestToken pricePerBytePerSecond = 10.TstWei(); @@ -23,7 +23,7 @@ namespace CodexReleaseTests.MarketTests [Test] [Combinatorial] public void Start( - [Values([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])] int rerun + [Rerun] int rerun ) { var hosts = StartHosts(); diff --git a/Tests/CodexReleaseTests/RerunAttribute.cs b/Tests/CodexReleaseTests/RerunAttribute.cs new file mode 100644 index 00000000..d30b010d --- /dev/null +++ b/Tests/CodexReleaseTests/RerunAttribute.cs @@ -0,0 +1,16 @@ +using NUnit.Framework; + +namespace CodexReleaseTests +{ + public class RerunAttribute : ValuesAttribute + { + private const int NumberOfReRuns = 8; + + public RerunAttribute() + { + var list = new List(); + for (var i = 0; i < NumberOfReRuns; i++) list.Add(i); + data = list.ToArray(); + } + } +} diff --git a/Tests/CodexReleaseTests/Utils/ChainMonitor.cs b/Tests/CodexReleaseTests/Utils/ChainMonitor.cs index 8b4bd816..cc24ad18 100644 --- a/Tests/CodexReleaseTests/Utils/ChainMonitor.cs +++ b/Tests/CodexReleaseTests/Utils/ChainMonitor.cs @@ -41,7 +41,7 @@ namespace CodexReleaseTests.Utils private void Worker(Action onFailure) { - var state = new ChainState(log, contracts, new DoNothingChainEventHandler(), startUtc, doProofPeriodMonitoring: true); + var state = new ChainState(log, contracts, new DoNothingThrowingChainEventHandler(), startUtc, doProofPeriodMonitoring: true); Thread.Sleep(updateInterval); log.Log("Chain monitoring started"); diff --git a/Tests/CodexReleaseTests/Utils/MarketplaceAutoBootstrapDistTest.cs b/Tests/CodexReleaseTests/Utils/MarketplaceAutoBootstrapDistTest.cs index 5c429fee..42bc03fa 100644 --- a/Tests/CodexReleaseTests/Utils/MarketplaceAutoBootstrapDistTest.cs +++ b/Tests/CodexReleaseTests/Utils/MarketplaceAutoBootstrapDistTest.cs @@ -176,8 +176,7 @@ namespace CodexReleaseTests.Utils var result = new ChainMonitor(log, contracts, startUtc); result.Start(() => { - log.Error("Failure in chain monitor. No chain updates after this point."); - //Assert.Fail("Failure in chain monitor."); + Assert.Fail("Failure in chain monitor."); }); return result; } @@ -262,10 +261,15 @@ namespace CodexReleaseTests.Utils var fills = events.GetSlotFilledEvents(); return fills.Select(f => { - var host = possibleHosts.Single(h => h.EthAddress.Address == f.Host.Address); + // We can encounter a fill event that's from an old host. + // We must disregard those. + var host = possibleHosts.SingleOrDefault(h => h.EthAddress.Address == f.Host.Address); + if (host == null) return null; return new SlotFill(f, host); - - }).ToArray(); + }) + .Where(f => f != null) + .Cast() + .ToArray(); } protected void AssertClientHasPaidForContract(TestToken pricePerBytePerSecond, ICodexNode client, IStoragePurchaseContract contract, ICodexNodeGroup hosts) @@ -363,7 +367,7 @@ namespace CodexReleaseTests.Utils return Time.Retry(() => { var events = GetContracts().GetEvents(GetTestRunTimeRange()); - var submitEvent = events.GetStorageRequests().SingleOrDefault(e => e.RequestId.ToHex(false) == contract.PurchaseId); + var submitEvent = events.GetStorageRequestedEvents().SingleOrDefault(e => e.RequestId.ToHex() == contract.PurchaseId); if (submitEvent == null) { // We're too early. @@ -402,12 +406,21 @@ namespace CodexReleaseTests.Utils protected void AssertContractIsOnChain(IStoragePurchaseContract contract) { + // Check the creation event. AssertOnChainEvents(events => { - var onChainRequests = events.GetStorageRequests(); - if (onChainRequests.Any(r => r.Id == contract.PurchaseId)) return; + var onChainRequests = events.GetStorageRequestedEvents(); + if (onChainRequests.Any(r => r.RequestId.ToHex() == contract.PurchaseId)) return; throw new Exception($"OnChain request {contract.PurchaseId} not found..."); }, nameof(AssertContractIsOnChain)); + + // Check that the getRequest call returns it. + var rid = contract.PurchaseId.HexToByteArray(); + var r = GetContracts().GetRequest(rid); + if (r == null) throw new Exception($"Failed to get Request from {nameof(GetRequestFunction)}"); + Assert.That(r.Ask.Duration, Is.EqualTo(contract.Purchase.Duration.TotalSeconds)); + Assert.That(r.Ask.Slots, Is.EqualTo(contract.Purchase.MinRequiredNumberOfNodes)); + Assert.That(((int)r.Ask.ProofProbability), Is.EqualTo(contract.Purchase.ProofProbability)); } protected void AssertOnChainEvents(Action onEvents, string description) @@ -444,7 +457,7 @@ namespace CodexReleaseTests.Utils float downtime = numBlocksInDowntimeSegment; float window = 256.0f; var chanceOfDowntime = downtime / window; - return 1.0f + chanceOfDowntime + chanceOfDowntime; + return 1.0f + (5.0f * chanceOfDowntime); } public class SlotFill { diff --git a/Tests/CodexReleaseTests/Utils/PurchaseParams.cs b/Tests/CodexReleaseTests/Utils/PurchaseParams.cs index 483d6f36..eef539aa 100644 --- a/Tests/CodexReleaseTests/Utils/PurchaseParams.cs +++ b/Tests/CodexReleaseTests/Utils/PurchaseParams.cs @@ -33,7 +33,7 @@ namespace CodexReleaseTests.Utils var numSlotBlocks = 1 + ((numBlocks - 1) / Nodes); // round-up div. // Next power of two: - var numSlotBlocksPow2 = NextPowerOf2(numSlotBlocks); + var numSlotBlocksPow2 = IsOrNextPowerOf2(numSlotBlocks); return new ByteSize(blockSize.SizeInBytes * numSlotBlocksPow2); } @@ -51,8 +51,9 @@ namespace CodexReleaseTests.Utils return new ByteSize(blockSize.SizeInBytes * totalBlocks); } - private int NextPowerOf2(int n) + private int IsOrNextPowerOf2(int n) { + if (IsPowerOfTwo(n)) return n; n = n - 1; var lg = Convert.ToInt32(Math.Round(Math.Log2(Convert.ToDouble(n)))); return 1 << (lg + 1); @@ -63,5 +64,10 @@ namespace CodexReleaseTests.Utils var x = size.SizeInBytes; return (x != 0) && ((x & (x - 1)) == 0); } + + private static bool IsPowerOfTwo(int x) + { + return (x != 0) && ((x & (x - 1)) == 0); + } } } diff --git a/Tests/DistTestCore/DistTest.cs b/Tests/DistTestCore/DistTest.cs index 4c6cb0af..9e693cb7 100644 --- a/Tests/DistTestCore/DistTest.cs +++ b/Tests/DistTestCore/DistTest.cs @@ -179,16 +179,34 @@ namespace DistTestCore private IWebCallTimeSet GetWebCallTimeSet() { + if (IsRunningInCluster()) + { + Log(" > Detected we're running in the cluster. Using long webCall timeset."); + return new LongWebCallTimeSet(); + } + if (ShouldUseLongTimeouts()) return new LongWebCallTimeSet(); return new DefaultWebCallTimeSet(); } private IK8sTimeSet GetK8sTimeSet() { + if (IsRunningInCluster()) + { + Log(" > Detected we're running in the cluster. Using long kubernetes timeset."); + return new LongK8sTimeSet(); + } + if (ShouldUseLongTimeouts()) return new LongK8sTimeSet(); return new DefaultK8sTimeSet(); } + private bool IsRunningInCluster() + { + var testType = Environment.GetEnvironmentVariable("TEST_TYPE"); + return testType == "release-tests"; + } + private bool ShouldWaitForCleanup() { return CurrentTestMethodHasAttribute(); @@ -256,7 +274,7 @@ namespace DistTestCore private string GetCurrentTestName() { - return $"[{TestContext.CurrentContext.Test.Name}]"; + return $"[{NameUtils.GetRawFixtureName()}:{NameUtils.GetTestMethodName()}]"; } public DistTestResult GetTestResult() diff --git a/Tests/DistTestCore/NameUtils.cs b/Tests/DistTestCore/NameUtils.cs index 44919d51..c742ceec 100644 --- a/Tests/DistTestCore/NameUtils.cs +++ b/Tests/DistTestCore/NameUtils.cs @@ -28,10 +28,11 @@ namespace DistTestCore public static string GetRawFixtureName() { var test = TestContext.CurrentContext.Test; - if (test.ClassName!.Contains("AdhocContext")) return "none"; - var className = test.ClassName!.Substring(test.ClassName.LastIndexOf('.') + 1); - className += FormatArguments(test); - return className.Replace('.', '-'); + var fullName = test.FullName; + if (fullName.Contains("AdhocContext")) return "none"; + var name = fullName.Substring(0, fullName.LastIndexOf('.')); + name += FormatArguments(test); + return name.Replace('.', '-').Replace(',', '-'); } public static string GetCategoryName() diff --git a/Tests/ExperimentalTests/BasicTests/MarketplaceTests.cs b/Tests/ExperimentalTests/BasicTests/MarketplaceTests.cs index 803f6d72..e8a08304 100644 --- a/Tests/ExperimentalTests/BasicTests/MarketplaceTests.cs +++ b/Tests/ExperimentalTests/BasicTests/MarketplaceTests.cs @@ -120,7 +120,7 @@ namespace ExperimentalTests.BasicTests //Thread.Sleep(GethContainerRecipe.BlockInterval * blocks); AssertBalance(contracts, client, Is.LessThan(clientInitialBalance), "Buyer was not charged for storage."); - Assert.That(contracts.GetRequestState(request), Is.EqualTo(RequestState.Finished)); + Assert.That(contracts.GetRequestState(request.RequestId), Is.EqualTo(RequestState.Finished)); } private TrackedFile CreateFile(ByteSize fileSize) @@ -148,24 +148,25 @@ namespace ExperimentalTests.BasicTests }, purchase.Expiry + TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(5), "Checking SlotFilled events"); } - private void AssertStorageRequest(Request request, StoragePurchaseRequest purchase, ICodexContracts contracts, ICodexNode buyer) + private void AssertStorageRequest(StorageRequestedEventDTO request, StoragePurchaseRequest purchase, ICodexContracts contracts, ICodexNode buyer) { - Assert.That(contracts.GetRequestState(request), Is.EqualTo(RequestState.Started)); - Assert.That(request.ClientAddress, Is.EqualTo(buyer.EthAddress)); + Assert.That(contracts.GetRequestState(request.RequestId), Is.EqualTo(RequestState.Started)); + var r = contracts.GetRequest(request.RequestId); + Assert.That(r.ClientAddress, Is.EqualTo(buyer.EthAddress)); Assert.That(request.Ask.Slots, Is.EqualTo(purchase.MinRequiredNumberOfNodes)); } - private Request GetOnChainStorageRequest(ICodexContracts contracts, IGethNode geth) + private StorageRequestedEventDTO GetOnChainStorageRequest(ICodexContracts contracts, IGethNode geth) { var events = contracts.GetEvents(GetTestRunTimeRange()); - var requests = events.GetStorageRequests(); + var requests = events.GetStorageRequestedEvents(); Assert.That(requests.Length, Is.EqualTo(1)); return requests.Single(); } - private void AssertContractSlot(ICodexContracts contracts, Request request, int contractSlotIndex) + private void AssertContractSlot(ICodexContracts contracts, StorageRequestedEventDTO request, int contractSlotIndex) { - var slotHost = contracts.GetSlotHost(request, contractSlotIndex); + var slotHost = contracts.GetSlotHost(request.RequestId, contractSlotIndex); Assert.That(slotHost?.Address, Is.Not.Null); } } diff --git a/Tools/BiblioTech/AdminChecker.cs b/Tools/BiblioTech/AdminChecker.cs index 96f89691..5c7ff103 100644 --- a/Tools/BiblioTech/AdminChecker.cs +++ b/Tools/BiblioTech/AdminChecker.cs @@ -35,6 +35,7 @@ namespace BiblioTech public async Task SendInAdminChannel(string[] lines) { + if (adminChannel == null) return; var chunker = new LineChunker(lines); var chunks = chunker.GetChunks(); if (!chunks.Any()) return; diff --git a/Tools/BiblioTech/BaseCommand.cs b/Tools/BiblioTech/BaseCommand.cs index eb411dd2..83e30f26 100644 --- a/Tools/BiblioTech/BaseCommand.cs +++ b/Tools/BiblioTech/BaseCommand.cs @@ -20,7 +20,9 @@ namespace BiblioTech Program.Log.Log($"Responding to '{Name}'"); var context = new CommandContext(command, command.Data.Options); await command.RespondAsync(StartingMessage, ephemeral: IsEphemeral(context)); - await Invoke(context); + + // Fire and forget invocation handler. Return SlashCommandHandler immediately. + _ = Invoke(context); } catch (Exception ex) { diff --git a/Tools/BiblioTech/BaseGethCommand.cs b/Tools/BiblioTech/BaseGethCommand.cs index 49069953..104784cc 100644 --- a/Tools/BiblioTech/BaseGethCommand.cs +++ b/Tools/BiblioTech/BaseGethCommand.cs @@ -8,9 +8,13 @@ namespace BiblioTech { protected override async Task Invoke(CommandContext context) { - var gethConnector = GethConnector.GethConnector.Initialize(Program.Log); + var gethConnector = GetGeth(); + if (gethConnector == null) + { + await context.Followup("Blockchain operations are (temporarily) unavailable."); + return; + } - if (gethConnector == null) return; var gethNode = gethConnector.GethNode; var contracts = gethConnector.CodexContracts; @@ -23,6 +27,19 @@ namespace BiblioTech await Execute(context, gethNode, contracts); } + private GethConnector.GethConnector? GetGeth() + { + try + { + return GethConnector.GethConnector.Initialize(Program.Log); + } + catch (Exception ex) + { + Program.Log.Error("Failed to initialize geth connector: " + ex); + return null; + } + } + protected abstract Task Execute(CommandContext context, IGethNode gethNode, ICodexContracts contracts); } } diff --git a/Tools/BiblioTech/CodexChecking/CheckRepo.cs b/Tools/BiblioTech/CodexChecking/CheckRepo.cs index a8b2860b..a02e44b7 100644 --- a/Tools/BiblioTech/CodexChecking/CheckRepo.cs +++ b/Tools/BiblioTech/CodexChecking/CheckRepo.cs @@ -1,16 +1,20 @@ -using Newtonsoft.Json; +using Logging; +using Newtonsoft.Json; +using Utils; namespace BiblioTech.CodexChecking { public class CheckRepo { private const string modelFilename = "model.json"; + private readonly ILog log; private readonly Configuration config; private readonly object _lock = new object(); private CheckRepoModel? model = null; - public CheckRepo(Configuration config) + public CheckRepo(ILog log, Configuration config) { + this.log = log; this.config = config; } @@ -18,20 +22,32 @@ namespace BiblioTech.CodexChecking { lock (_lock) { - if (model == null) LoadModel(); - - var existing = model.Reports.SingleOrDefault(r => r.UserId == userId); - if (existing == null) + var sw = System.Diagnostics.Stopwatch.StartNew(); + try { - var newEntry = new CheckReport + if (model == null) LoadModel(); + + var existing = model.Reports.SingleOrDefault(r => r.UserId == userId); + if (existing == null) { - UserId = userId, - }; - model.Reports.Add(newEntry); - SaveChanges(); - return newEntry; + var newEntry = new CheckReport + { + UserId = userId, + }; + model.Reports.Add(newEntry); + SaveChanges(); + return newEntry; + } + return existing; + } + finally + { + var elapsed = sw.Elapsed; + if (elapsed > TimeSpan.FromMilliseconds(500)) + { + log.Log($"Warning {nameof(GetOrCreate)} took {Time.FormatDuration(elapsed)}"); + } } - return existing; } } diff --git a/Tools/BiblioTech/CodexChecking/CodexTwoWayChecker.cs b/Tools/BiblioTech/CodexChecking/CodexTwoWayChecker.cs index ac83ccd9..6fb2f28e 100644 --- a/Tools/BiblioTech/CodexChecking/CodexTwoWayChecker.cs +++ b/Tools/BiblioTech/CodexChecking/CodexTwoWayChecker.cs @@ -15,6 +15,7 @@ namespace BiblioTech.CodexChecking Task CouldNotDownloadCid(); Task GiveCidToUser(string cid); Task GiveDataFileToUser(string fileContent); + Task CodexUnavailable(); Task ToAdminChannel(string msg); } @@ -44,6 +45,12 @@ namespace BiblioTech.CodexChecking } var cid = UploadData(check.UniqueData); + if (cid == null) + { + await handler.CodexUnavailable(); + return; + } + await handler.GiveCidToUser(cid); } @@ -96,7 +103,12 @@ namespace BiblioTech.CodexChecking if (IsManifestLengthCompatible(handler, check, manifest)) { - if (IsContentCorrect(handler, check, receivedCid)) + var correct = IsContentCorrect(handler, check, receivedCid); + if (!correct.HasValue) { + await handler.CodexUnavailable(); + return; + } + if (correct.Value) { await CheckNowCompleted(handler, check, userId, "UploadCheck"); return; @@ -120,7 +132,7 @@ namespace BiblioTech.CodexChecking check.CompletedUtc < expiry; } - private string UploadData(string uniqueData) + private string? UploadData(string uniqueData) { var filePath = Path.Combine(config.ChecksDataPath, Guid.NewGuid().ToString()); @@ -163,7 +175,7 @@ namespace BiblioTech.CodexChecking private bool IsManifestLengthCompatible(ICheckResponseHandler handler, TransferCheck check, Manifest manifest) { var dataLength = check.UniqueData.Length; - var manifestLength = manifest.OriginalBytes.SizeInBytes; + var manifestLength = manifest.DatasetSize.SizeInBytes; Log($"Checking manifest length: dataLength={dataLength},manifestLength={manifestLength}"); @@ -172,7 +184,7 @@ namespace BiblioTech.CodexChecking manifestLength < (dataLength + 1); } - private bool IsContentCorrect(ICheckResponseHandler handler, TransferCheck check, string receivedCid) + private bool? IsContentCorrect(ICheckResponseHandler handler, TransferCheck check, string receivedCid) { try { @@ -190,6 +202,8 @@ namespace BiblioTech.CodexChecking } }); + if (content == null) return null; + Log($"Checking content: content={content},check={check.UniqueData}"); return content == check.UniqueData; } diff --git a/Tools/BiblioTech/CodexChecking/CodexWrapper.cs b/Tools/BiblioTech/CodexChecking/CodexWrapper.cs index 3c295d7f..f1f35822 100644 --- a/Tools/BiblioTech/CodexChecking/CodexWrapper.cs +++ b/Tools/BiblioTech/CodexChecking/CodexWrapper.cs @@ -21,32 +21,72 @@ namespace BiblioTech.CodexChecking var httpFactory = CreateHttpFactory(); factory = new CodexNodeFactory(log, httpFactory, dataDir: config.DataPath); + + Task.Run(CheckCodexNode); } - public void OnCodex(Action action) + public T? OnCodex(Func func) where T : class { lock (codexLock) { - action(Get()); + if (currentCodexNode == null) return null; + return func(currentCodexNode); } } - public T OnCodex(Func func) + private void CheckCodexNode() { - lock (codexLock) + Thread.Sleep(TimeSpan.FromSeconds(10.0)); + + while (true) { - return func(Get()); + lock (codexLock) + { + var newNode = GetNewCodexNode(); + if (newNode != null && currentCodexNode == null) ShowConnectionRestored(); + if (newNode == null && currentCodexNode != null) ShowConnectionLost(); + currentCodexNode = newNode; + } + + Thread.Sleep(TimeSpan.FromMinutes(15.0)); } } - private ICodexNode Get() + private ICodexNode? GetNewCodexNode() { - if (currentCodexNode == null) + try { - currentCodexNode = CreateCodex(); - } + if (currentCodexNode != null) + { + try + { + // Current instance is responsive? Keep it. + var info = currentCodexNode.GetDebugInfo(); + if (info != null && info.Version != null && + !string.IsNullOrEmpty(info.Version.Revision)) return currentCodexNode; + } + catch + { + } + } - return currentCodexNode; + return CreateCodex(); + } + catch (Exception ex) + { + log.Error("Exception when trying to check codex node: " + ex.Message); + return null; + } + } + + private void ShowConnectionLost() + { + Program.AdminChecker.SendInAdminChannel("Codex node connection lost."); + } + + private void ShowConnectionRestored() + { + Program.AdminChecker.SendInAdminChannel("Codex node connection restored."); } private ICodexNode CreateCodex() @@ -70,16 +110,34 @@ namespace BiblioTech.CodexChecking { if (string.IsNullOrEmpty(config.CodexEndpointAuth) || !config.CodexEndpointAuth.Contains(":")) { - return new HttpFactory(log); + return new HttpFactory(log, new SnappyTimeSet()); } var tokens = config.CodexEndpointAuth.Split(':'); if (tokens.Length != 2) throw new Exception("Expected ':' in CodexEndpointAuth parameter."); - return new HttpFactory(log, onClientCreated: client => + return new HttpFactory(log, new SnappyTimeSet(), onClientCreated: client => { client.SetBasicAuthentication(tokens[0], tokens[1]); }); } + + public class SnappyTimeSet : IWebCallTimeSet + { + public TimeSpan HttpCallRetryDelay() + { + return TimeSpan.FromSeconds(1.0); + } + + public TimeSpan HttpCallTimeout() + { + return TimeSpan.FromSeconds(3.0); + } + + public TimeSpan HttpRetryTimeout() + { + return TimeSpan.FromSeconds(12.0); + } + } } } diff --git a/Tools/BiblioTech/CommandHandler.cs b/Tools/BiblioTech/CommandHandler.cs index 10e1e24b..52f0388d 100644 --- a/Tools/BiblioTech/CommandHandler.cs +++ b/Tools/BiblioTech/CommandHandler.cs @@ -6,7 +6,6 @@ using BiblioTech.Rewards; using Logging; using BiblioTech.CodexChecking; using Nethereum.Model; -using static Org.BouncyCastle.Math.EC.ECCurve; namespace BiblioTech { diff --git a/Tools/BiblioTech/Commands/CheckDownloadCommand.cs b/Tools/BiblioTech/Commands/CheckDownloadCommand.cs index 0e1aa563..d2119833 100644 --- a/Tools/BiblioTech/Commands/CheckDownloadCommand.cs +++ b/Tools/BiblioTech/Commands/CheckDownloadCommand.cs @@ -18,7 +18,7 @@ namespace BiblioTech.Commands } public override string Name => "checkdownload"; - public override string StartingMessage => RandomBusyMessage.Get(); + public override string StartingMessage => "Connecting to the testnet... Please be patient... " + RandomBusyMessage.Get(); public override string Description => "Checks the download connectivity of your Codex node."; public override CommandOption[] Options => [contentOption]; diff --git a/Tools/BiblioTech/Commands/CheckResponseHandler.cs b/Tools/BiblioTech/Commands/CheckResponseHandler.cs index f7b1c4f0..c10ef23e 100644 --- a/Tools/BiblioTech/Commands/CheckResponseHandler.cs +++ b/Tools/BiblioTech/Commands/CheckResponseHandler.cs @@ -26,6 +26,11 @@ namespace BiblioTech.Commands await context.Followup("Could not download the CID."); } + public async Task CodexUnavailable() + { + await context.Followup("Couldn't perform check: Our Codex node appears unavailable. Try again later?"); + } + public async Task GiveCidToUser(string cid) { await context.Followup( diff --git a/Tools/BiblioTech/Commands/CheckUploadCommand.cs b/Tools/BiblioTech/Commands/CheckUploadCommand.cs index b1589b5c..51d6d394 100644 --- a/Tools/BiblioTech/Commands/CheckUploadCommand.cs +++ b/Tools/BiblioTech/Commands/CheckUploadCommand.cs @@ -18,7 +18,7 @@ namespace BiblioTech.Commands } public override string Name => "checkupload"; - public override string StartingMessage => RandomBusyMessage.Get(); + public override string StartingMessage => "Connecting to the testnet... Please be patient... " + RandomBusyMessage.Get(); public override string Description => "Checks the upload connectivity of your Codex node."; public override CommandOption[] Options => [cidOption]; diff --git a/Tools/BiblioTech/Commands/MintCommand.cs b/Tools/BiblioTech/Commands/MintCommand.cs index 8a6cd33c..e734805e 100644 --- a/Tools/BiblioTech/Commands/MintCommand.cs +++ b/Tools/BiblioTech/Commands/MintCommand.cs @@ -93,12 +93,12 @@ namespace BiblioTech.Commands private bool ShouldSendEth(IGethNode gethNode, EthAddress addr) { var eth = gethNode.GetEthBalance(addr); - return eth.Eth < Program.Config.SendEth; + return ((decimal)eth.Eth) < Program.Config.SendEth; } private string FormatTransactionLink(string transaction) { - var url = $"https://explorer.testnet.codex.storage/tx/{transaction}"; + var url = Program.Config.TransactionLinkFormat.Replace("", transaction); return $"- [View on block explorer](<{url}>){Environment.NewLine}Transaction ID - `{transaction}`"; } } diff --git a/Tools/BiblioTech/Configuration.cs b/Tools/BiblioTech/Configuration.cs index a8187379..09f0f044 100644 --- a/Tools/BiblioTech/Configuration.cs +++ b/Tools/BiblioTech/Configuration.cs @@ -30,7 +30,7 @@ namespace BiblioTech public int RewardApiPort { get; set; } = 31080; [Uniform("send-eth", "se", "SENDETH", true, "Amount of Eth send by the mint command.")] - public int SendEth { get; set; } = 10; + public decimal SendEth { get; set; } = 10.0m; [Uniform("mint-tt", "mt", "MINTTT", true, "Amount of TSTWEI minted by the mint command.")] public BigInteger MintTT { get; set; } = 1073741824; @@ -44,6 +44,9 @@ namespace BiblioTech [Uniform("codex-endpoint-auth", "cea", "CODEXENDPOINTAUTH", false, "Codex endpoint basic auth. Colon separated username and password. (default: empty, no auth used.)")] public string CodexEndpointAuth { get; set; } = ""; + [Uniform("transaction-link-format", "tlf", "TRANSACTIONLINKFORMAT", false, "Format of links to transactions on the blockchain. Use '' to inject the transaction ID into this string. (default 'https://explorer.testnet.codex.storage/tx/')")] + public string TransactionLinkFormat { get; set; } = "https://explorer.testnet.codex.storage/tx/"; + #region Role Rewards /// diff --git a/Tools/BiblioTech/Program.cs b/Tools/BiblioTech/Program.cs index 0ce84bde..28662388 100644 --- a/Tools/BiblioTech/Program.cs +++ b/Tools/BiblioTech/Program.cs @@ -4,10 +4,7 @@ using BiblioTech.Commands; using BiblioTech.Rewards; using Discord; using Discord.WebSocket; -using DiscordRewards; using Logging; -using Nethereum.Model; -using Newtonsoft.Json; namespace BiblioTech { @@ -88,7 +85,7 @@ namespace BiblioTech client = new DiscordSocketClient(); client.Log += ClientLog; - var checkRepo = new CheckRepo(Config); + var checkRepo = new CheckRepo(Log, Config); var codexWrapper = new CodexWrapper(Log, Config); var checker = new CodexTwoWayChecker(Log, Config, checkRepo, codexWrapper); var notifyCommand = new NotifyCommand(); diff --git a/Tools/BiblioTech/docker/Dockerfile b/Tools/BiblioTech/docker/Dockerfile index 98887e17..f66c4b06 100644 --- a/Tools/BiblioTech/docker/Dockerfile +++ b/Tools/BiblioTech/docker/Dockerfile @@ -9,9 +9,9 @@ FROM ${IMAGE} AS builder ARG APP_HOME WORKDIR ${APP_HOME} -COPY ./Tools/BiblioTech ./Tools/BiblioTech -COPY ./Framework ./Framework -COPY ./ProjectPlugins ./ProjectPlugins +COPY Tools/BiblioTech Tools/BiblioTech +COPY Framework Framework +COPY ProjectPlugins ProjectPlugins RUN dotnet restore Tools/BiblioTech RUN dotnet publish Tools/BiblioTech -c Release -o out @@ -23,4 +23,7 @@ ENV APP_HOME=${APP_HOME} WORKDIR ${APP_HOME} COPY --from=builder ${APP_HOME}/out . -CMD dotnet ${APP_HOME}/BiblioTech.dll +COPY --chmod=0755 Tools/BiblioTech/docker/docker-entrypoint.sh / + +ENTRYPOINT ["/docker-entrypoint.sh"] +CMD ["bash", "-c", "dotnet ${APP_HOME}/BiblioTech.dll"] diff --git a/Tools/BiblioTech/docker/docker-entrypoint.sh b/Tools/BiblioTech/docker/docker-entrypoint.sh new file mode 100644 index 00000000..f2434a03 --- /dev/null +++ b/Tools/BiblioTech/docker/docker-entrypoint.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# Marketplace address from URL +if [[ -n "${MARKETPLACE_ADDRESS_FROM_URL}" ]]; then + WAIT=${MARKETPLACE_ADDRESS_FROM_URL_WAIT:-300} + SECONDS=0 + SLEEP=1 + # Run and retry if fail + while (( SECONDS < WAIT )); do + MARKETPLACE_ADDRESS=($(curl -s -f -m 5 "${MARKETPLACE_ADDRESS_FROM_URL}")) + # Check if exit code is 0 and returned value is not empty + if [[ $? -eq 0 && -n "${MARKETPLACE_ADDRESS}" ]]; then + export CODEXCONTRACTS_MARKETPLACEADDRESS="${MARKETPLACE_ADDRESS}" + break + else + # Sleep and check again + echo "Can't get Marketplace address from ${MARKETPLACE_ADDRESS_FROM_URL} - Retry in $SLEEP seconds / $((WAIT - SECONDS))" + sleep $SLEEP + fi + done +fi + +# Show +echo -e "\nRun parameters:" +vars=$(env | grep "CODEX" | grep -v -e "[0-9]_SERVICE_" -e "[0-9]_NODEPORT_") +echo -e "${vars//CODEX/ - CODEX}" +echo -e " - $@\n" + +# Run +exec "$@" diff --git a/Tools/MarketInsights/ContributionBuilder.cs b/Tools/MarketInsights/ContributionBuilder.cs index 50bf48ad..387fd62b 100644 --- a/Tools/MarketInsights/ContributionBuilder.cs +++ b/Tools/MarketInsights/ContributionBuilder.cs @@ -1,6 +1,5 @@ using BlockchainUtils; using CodexContractsPlugin.ChainMonitor; -using GethPlugin; using Logging; using System.Numerics; using Utils; @@ -47,7 +46,7 @@ namespace MarketInsights AddRequestToAverage(segment.Started, requestEvent); } - public void OnSlotFilled(RequestEvent requestEvent, EthAddress host, BigInteger slotIndex) + public void OnSlotFilled(RequestEvent requestEvent, EthAddress host, BigInteger slotIndex, bool isRepair) { } diff --git a/Tools/TestNetRewarder/EmojiMaps.cs b/Tools/TestNetRewarder/EmojiMaps.cs index 38684c55..dcde4f97 100644 --- a/Tools/TestNetRewarder/EmojiMaps.cs +++ b/Tools/TestNetRewarder/EmojiMaps.cs @@ -78,6 +78,7 @@ public string NewRequest => "🌱"; public string Started => "🌳"; public string SlotFilled => "🟢"; + public string SlotRepaired => "♻"; public string SlotFreed => "⭕"; public string SlotReservationsFull => "☑️"; public string Finished => "✅"; diff --git a/Tools/TestNetRewarder/EventsFormatter.cs b/Tools/TestNetRewarder/EventsFormatter.cs index 7e363432..0f5ee9fa 100644 --- a/Tools/TestNetRewarder/EventsFormatter.cs +++ b/Tools/TestNetRewarder/EventsFormatter.cs @@ -1,8 +1,8 @@ using BlockchainUtils; -using CodexContractsPlugin; using CodexContractsPlugin.ChainMonitor; +using CodexContractsPlugin.Marketplace; using DiscordRewards; -using GethPlugin; +using Nethereum.Hex.HexConvertors.Extensions; using System.Globalization; using System.Numerics; using Utils; @@ -16,10 +16,12 @@ namespace TestNetRewarder private readonly List errors = new List(); private readonly EmojiMaps emojiMaps = new EmojiMaps(); private readonly Configuration config; + private readonly string periodDuration; - public EventsFormatter(Configuration config) + public EventsFormatter(Configuration config, MarketplaceConfig marketplaceConfig) { this.config = config; + periodDuration = Time.FormatDuration(marketplaceConfig.PeriodDuration); } public ChainEventMessage[] GetInitializationEvents(Configuration config) @@ -49,7 +51,7 @@ namespace TestNetRewarder public void OnNewRequest(RequestEvent requestEvent) { var request = requestEvent.Request; - AddRequestBlock(requestEvent, $"{emojiMaps.NewRequest} New Request", + AddRequestBlock(requestEvent, emojiMaps.NewRequest, "New Request", $"Client: {request.Client}", $"Content: {BytesToHexString(request.Request.Content.Cid)}", $"Duration: {BigIntToDuration(request.Request.Ask.Duration)}", @@ -58,33 +60,34 @@ namespace TestNetRewarder $"PricePerBytePerSecond: {BitIntToTestTokens(request.Request.Ask.PricePerBytePerSecond)}", $"Number of Slots: {request.Request.Ask.Slots}", $"Slot Tolerance: {request.Request.Ask.MaxSlotLoss}", - $"Slot Size: {BigIntToByteSize(request.Request.Ask.SlotSize)}" + $"Slot Size: {BigIntToByteSize(request.Request.Ask.SlotSize)}", + $"Proof Probability: 1 / {request.Request.Ask.ProofProbability} every {periodDuration}" ); } public void OnRequestCancelled(RequestEvent requestEvent) { - AddRequestBlock(requestEvent, $"{emojiMaps.Cancelled} Cancelled"); + AddRequestBlock(requestEvent, emojiMaps.Cancelled, "Cancelled"); } public void OnRequestFailed(RequestEvent requestEvent) { - AddRequestBlock(requestEvent, $"{emojiMaps.Failed} Failed"); + AddRequestBlock(requestEvent, emojiMaps.Failed, "Failed"); } public void OnRequestFinished(RequestEvent requestEvent) { - AddRequestBlock(requestEvent, $"{emojiMaps.Finished} Finished"); + AddRequestBlock(requestEvent, emojiMaps.Finished, "Finished"); } public void OnRequestFulfilled(RequestEvent requestEvent) { - AddRequestBlock(requestEvent, $"{emojiMaps.Started} Started"); + AddRequestBlock(requestEvent, emojiMaps.Started, "Started"); } - public void OnSlotFilled(RequestEvent requestEvent, EthAddress host, BigInteger slotIndex) + public void OnSlotFilled(RequestEvent requestEvent, EthAddress host, BigInteger slotIndex, bool isRepair) { - AddRequestBlock(requestEvent, $"{emojiMaps.SlotFilled} Slot Filled", + AddRequestBlock(requestEvent, GetSlotFilledIcon(isRepair), GetSlotFilledTitle(isRepair), $"Host: {host}", $"Slot Index: {slotIndex}" ); @@ -92,14 +95,14 @@ namespace TestNetRewarder public void OnSlotFreed(RequestEvent requestEvent, BigInteger slotIndex) { - AddRequestBlock(requestEvent, $"{emojiMaps.SlotFreed} Slot Freed", + AddRequestBlock(requestEvent, emojiMaps.SlotFreed, "Slot Freed", $"Slot Index: {slotIndex}" ); } public void OnSlotReservationsFull(RequestEvent requestEvent, BigInteger slotIndex) { - AddRequestBlock(requestEvent, $"{emojiMaps.SlotReservationsFull} Slot Reservations Full", + AddRequestBlock(requestEvent, emojiMaps.SlotReservationsFull, "Slot Reservations Full", $"Slot Index: {slotIndex}" ); } @@ -133,6 +136,18 @@ namespace TestNetRewarder AddBlock(0, $"{emojiMaps.ProofReport} **Proof system report**", lines.ToArray()); } + private string GetSlotFilledIcon(bool isRepair) + { + if (isRepair) return emojiMaps.SlotRepaired; + return emojiMaps.SlotFilled; + } + + private string GetSlotFilledTitle(bool isRepair) + { + if (isRepair) return $"Slot Repaired"; + return $"Slot Filled"; + } + private void AddMissedProofDetails(List lines, PeriodReport[] reports) { var reportsWithMissedProofs = reports.Where(r => r.MissedProofs.Length > 0).ToArray(); @@ -168,10 +183,10 @@ namespace TestNetRewarder lines.Add($"[{missedProof.FormatHost()}] missed proof for {FormatRequestId(missedProof.Request)} (slotIndex: {missedProof.SlotIndex})"); } - private void AddRequestBlock(RequestEvent requestEvent, string eventName, params string[] content) + private void AddRequestBlock(RequestEvent requestEvent, string icon, string eventName, params string[] content) { var blockNumber = $"[{requestEvent.Block.BlockNumber} {FormatDateTime(requestEvent.Block.Utc)}]"; - var title = $"{blockNumber} **{eventName}** {FormatRequestId(requestEvent)}"; + var title = $"{blockNumber} {icon} **{eventName}** {FormatRequestId(requestEvent)}"; AddBlock(requestEvent.Block.BlockNumber, title, content); } @@ -223,14 +238,15 @@ namespace TestNetRewarder private string FormatRequestId(IChainStateRequest request) { - return FormatRequestId(request.Request.Id); + return FormatRequestId(request.RequestId); } - private string FormatRequestId(string id) + private string FormatRequestId(byte[] id) { + var str = id.ToHex(); return - $"({emojiMaps.StringToEmojis(id, 3)})" + - $"`{id}`"; + $"({emojiMaps.StringToEmojis(str, 3)})" + + $"`{str}`"; } private string BytesToHexString(byte[] bytes) diff --git a/Tools/TestNetRewarder/Processor.cs b/Tools/TestNetRewarder/Processor.cs index 649ebf47..036f8795 100644 --- a/Tools/TestNetRewarder/Processor.cs +++ b/Tools/TestNetRewarder/Processor.cs @@ -25,7 +25,7 @@ namespace TestNetRewarder if (config.ProofReportHours < 1) throw new Exception("ProofReportHours must be one or greater"); builder = new RequestBuilder(); - eventsFormatter = new EventsFormatter(config); + eventsFormatter = new EventsFormatter(config, contracts.Deployment.Config); chainState = new ChainState(log, contracts, eventsFormatter, config.HistoryStartUtc, doProofPeriodMonitoring: config.ShowProofPeriodReports > 0); diff --git a/Tools/TestNetRewarder/docker/Dockerfile b/Tools/TestNetRewarder/docker/Dockerfile index 4a241610..57edc587 100644 --- a/Tools/TestNetRewarder/docker/Dockerfile +++ b/Tools/TestNetRewarder/docker/Dockerfile @@ -9,9 +9,9 @@ FROM ${IMAGE} AS builder ARG APP_HOME WORKDIR ${APP_HOME} -COPY ./Tools/TestNetRewarder ./Tools/TestNetRewarder -COPY ./Framework ./Framework -COPY ./ProjectPlugins ./ProjectPlugins +COPY Tools/TestNetRewarder Tools/TestNetRewarder +COPY Framework Framework +COPY ProjectPlugins ProjectPlugins RUN dotnet restore Tools/TestNetRewarder RUN dotnet publish Tools/TestNetRewarder -c Release -o out @@ -23,4 +23,7 @@ ENV APP_HOME=${APP_HOME} WORKDIR ${APP_HOME} COPY --from=builder ${APP_HOME}/out . -CMD dotnet ${APP_HOME}/TestNetRewarder.dll +COPY --chmod=0755 Tools/TestNetRewarder/docker/docker-entrypoint.sh / + +ENTRYPOINT ["/docker-entrypoint.sh"] +CMD ["bash", "-c", "dotnet ${APP_HOME}/TestNetRewarder.dll"] diff --git a/Tools/TestNetRewarder/docker/docker-entrypoint.sh b/Tools/TestNetRewarder/docker/docker-entrypoint.sh new file mode 100644 index 00000000..f2434a03 --- /dev/null +++ b/Tools/TestNetRewarder/docker/docker-entrypoint.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# Marketplace address from URL +if [[ -n "${MARKETPLACE_ADDRESS_FROM_URL}" ]]; then + WAIT=${MARKETPLACE_ADDRESS_FROM_URL_WAIT:-300} + SECONDS=0 + SLEEP=1 + # Run and retry if fail + while (( SECONDS < WAIT )); do + MARKETPLACE_ADDRESS=($(curl -s -f -m 5 "${MARKETPLACE_ADDRESS_FROM_URL}")) + # Check if exit code is 0 and returned value is not empty + if [[ $? -eq 0 && -n "${MARKETPLACE_ADDRESS}" ]]; then + export CODEXCONTRACTS_MARKETPLACEADDRESS="${MARKETPLACE_ADDRESS}" + break + else + # Sleep and check again + echo "Can't get Marketplace address from ${MARKETPLACE_ADDRESS_FROM_URL} - Retry in $SLEEP seconds / $((WAIT - SECONDS))" + sleep $SLEEP + fi + done +fi + +# Show +echo -e "\nRun parameters:" +vars=$(env | grep "CODEX" | grep -v -e "[0-9]_SERVICE_" -e "[0-9]_NODEPORT_") +echo -e "${vars//CODEX/ - CODEX}" +echo -e " - $@\n" + +# Run +exec "$@" diff --git a/Tools/TraceContract/ChainRequestTracker.cs b/Tools/TraceContract/ChainRequestTracker.cs index 2e313ae8..7e2638ad 100644 --- a/Tools/TraceContract/ChainRequestTracker.cs +++ b/Tools/TraceContract/ChainRequestTracker.cs @@ -1,6 +1,7 @@ using System.Numerics; using BlockchainUtils; using CodexContractsPlugin.ChainMonitor; +using Nethereum.Hex.HexConvertors.Extensions; using Utils; namespace TraceContract @@ -70,11 +71,11 @@ namespace TraceContract } } - public void OnSlotFilled(RequestEvent requestEvent, EthAddress host, BigInteger slotIndex) + public void OnSlotFilled(RequestEvent requestEvent, EthAddress host, BigInteger slotIndex, bool isRepair) { if (IsMyRequest(requestEvent)) { - output.LogSlotFilled(requestEvent, host, slotIndex); + output.LogSlotFilled(requestEvent, host, slotIndex, isRepair); } } @@ -96,7 +97,7 @@ namespace TraceContract private bool IsMyRequest(RequestEvent requestEvent) { - return requestId == requestEvent.Request.Request.Id.ToLowerInvariant(); + return requestId == requestEvent.Request.RequestId.ToHex().ToLowerInvariant(); } } diff --git a/Tools/TraceContract/ChainTracer.cs b/Tools/TraceContract/ChainTracer.cs index fc69dd14..a4227ab4 100644 --- a/Tools/TraceContract/ChainTracer.cs +++ b/Tools/TraceContract/ChainTracer.cs @@ -3,6 +3,7 @@ using CodexContractsPlugin.ChainMonitor; using CodexContractsPlugin.Marketplace; using Logging; using Nethereum.Hex.HexConvertors.Extensions; +using Nethereum.Model; using Utils; namespace TraceContract @@ -28,10 +29,12 @@ namespace TraceContract var request = GetRequest(); if (request == null) throw new Exception("Failed to find the purchase in the last week of transactions."); - log.Log($"Request started at {request.Block.Utc}"); - var contractEnd = RunToContractEnd(request); + var creationEvent = FindRequestCreationEvent(); - var requestTimeline = new TimeRange(request.Block.Utc.AddMinutes(-1.0), contractEnd.AddMinutes(1.0)); + log.Log($"Request started at {creationEvent.Block.Utc}"); + var contractEnd = RunToContractEnd(creationEvent); + + var requestTimeline = new TimeRange(creationEvent.Block.Utc.AddMinutes(-1.0), contractEnd.AddMinutes(1.0)); log.Log($"Request timeline: {requestTimeline.From} -> {requestTimeline.To}"); // For this timeline, we log all the calls to reserve-slot. @@ -52,7 +55,7 @@ namespace TraceContract return requestTimeline; } - private DateTime RunToContractEnd(Request request) + private DateTime RunToContractEnd(StorageRequestedEventDTO request) { var utc = request.Block.Utc.AddMinutes(-1.0); var tracker = new ChainRequestTracker(output, input.PurchaseId); @@ -78,35 +81,35 @@ namespace TraceContract return tracker.FinishUtc; } - 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 Request? GetRequest() + { + return contracts.GetRequest(input.RequestId); + } + + public StorageRequestedEventDTO FindRequestCreationEvent() + { + var range = new TimeRange(DateTime.UtcNow - TimeSpan.FromHours(3.0), DateTime.UtcNow); + var limit = DateTime.UtcNow - TimeSpan.FromDays(30); + + while (range.From > limit) + { + var events = contracts.GetEvents(range); + foreach (var r in events.GetStorageRequestedEvents()) + { + if (r.RequestId.ToHex() == input.RequestId.ToHex()) return r; + } + + range = new TimeRange(range.From - TimeSpan.FromHours(3.0), range.From); + } + + throw new Exception("Unable to find storage request creation event on-chain after (limit) " + Time.FormatTimestamp(limit)); + } + private static TimeRange LastHour() { return new TimeRange(DateTime.UtcNow.AddHours(-1.0), DateTime.UtcNow); diff --git a/Tools/TraceContract/Config.cs b/Tools/TraceContract/Config.cs index 3b49a77a..efb82645 100644 --- a/Tools/TraceContract/Config.cs +++ b/Tools/TraceContract/Config.cs @@ -4,7 +4,7 @@ { public string RpcEndpoint { get; } = "https://rpc.testnet.codex.storage"; public int GethPort { get; } = 443; - public string MarketplaceAddress { get; } = "0x7c7a749DE7156305E55775e7Ab3931abd6f7300E"; + public string MarketplaceAddress { get; } = "0x5378a4EA5dA2a548ce22630A3AE74b052000C62D"; public string TokenAddress { get; } = "0x34a22f3911De437307c6f4485931779670f78764"; public string Abi { get; } = @"[{""inputs"":[{""components"":[{""components"":[{""internalType"":""uint8"",""name"":""repairRewardPercentage"",""type"":""uint8""},{""internalType"":""uint8"",""name"":""maxNumberOfSlashes"",""type"":""uint8""},{""internalType"":""uint16"",""name"":""slashCriterion"",""type"":""uint16""},{""internalType"":""uint8"",""name"":""slashPercentage"",""type"":""uint8""}],""internalType"":""struct CollateralConfig"",""name"":""collateral"",""type"":""tuple""},{""components"":[{""internalType"":""uint256"",""name"":""period"",""type"":""uint256""},{""internalType"":""uint256"",""name"":""timeout"",""type"":""uint256""},{""internalType"":""uint8"",""name"":""downtime"",""type"":""uint8""},{""internalType"":""string"",""name"":""zkeyHash"",""type"":""string""}],""internalType"":""struct ProofConfig"",""name"":""proofs"",""type"":""tuple""}],""internalType"":""struct MarketplaceConfig"",""name"":""configuration"",""type"":""tuple""},{""internalType"":""contract IERC20"",""name"":""token_"",""type"":""address""},{""internalType"":""contract IGroth16Verifier"",""name"":""verifier"",""type"":""address""}],""stateMutability"":""nonpayable"",""type"":""constructor""},{""anonymous"":false,""inputs"":[{""indexed"":false,""internalType"":""SlotId"",""name"":""id"",""type"":""bytes32""}],""name"":""ProofSubmitted"",""type"":""event""},{""anonymous"":false,""inputs"":[{""indexed"":true,""internalType"":""RequestId"",""name"":""requestId"",""type"":""bytes32""}],""name"":""RequestCancelled"",""type"":""event""},{""anonymous"":false,""inputs"":[{""indexed"":true,""internalType"":""RequestId"",""name"":""requestId"",""type"":""bytes32""}],""name"":""RequestFailed"",""type"":""event""},{""anonymous"":false,""inputs"":[{""indexed"":true,""internalType"":""RequestId"",""name"":""requestId"",""type"":""bytes32""}],""name"":""RequestFulfilled"",""type"":""event""},{""anonymous"":false,""inputs"":[{""indexed"":true,""internalType"":""RequestId"",""name"":""requestId"",""type"":""bytes32""},{""indexed"":false,""internalType"":""uint256"",""name"":""slotIndex"",""type"":""uint256""}],""name"":""SlotFilled"",""type"":""event""},{""anonymous"":false,""inputs"":[{""indexed"":true,""internalType"":""RequestId"",""name"":""requestId"",""type"":""bytes32""},{""indexed"":false,""internalType"":""uint256"",""name"":""slotIndex"",""type"":""uint256""}],""name"":""SlotFreed"",""type"":""event""},{""anonymous"":false,""inputs"":[{""indexed"":false,""internalType"":""RequestId"",""name"":""requestId"",""type"":""bytes32""},{""components"":[{""internalType"":""uint64"",""name"":""slots"",""type"":""uint64""},{""internalType"":""uint256"",""name"":""slotSize"",""type"":""uint256""},{""internalType"":""uint256"",""name"":""duration"",""type"":""uint256""},{""internalType"":""uint256"",""name"":""proofProbability"",""type"":""uint256""},{""internalType"":""uint256"",""name"":""reward"",""type"":""uint256""},{""internalType"":""uint256"",""name"":""collateral"",""type"":""uint256""},{""internalType"":""uint64"",""name"":""maxSlotLoss"",""type"":""uint64""}],""indexed"":false,""internalType"":""struct Ask"",""name"":""ask"",""type"":""tuple""},{""indexed"":false,""internalType"":""uint256"",""name"":""expiry"",""type"":""uint256""}],""name"":""StorageRequested"",""type"":""event""},{""inputs"":[],""name"":""config"",""outputs"":[{""components"":[{""components"":[{""internalType"":""uint8"",""name"":""repairRewardPercentage"",""type"":""uint8""},{""internalType"":""uint8"",""name"":""maxNumberOfSlashes"",""type"":""uint8""},{""internalType"":""uint16"",""name"":""slashCriterion"",""type"":""uint16""},{""internalType"":""uint8"",""name"":""slashPercentage"",""type"":""uint8""}],""internalType"":""struct CollateralConfig"",""name"":""collateral"",""type"":""tuple""},{""components"":[{""internalType"":""uint256"",""name"":""period"",""type"":""uint256""},{""internalType"":""uint256"",""name"":""timeout"",""type"":""uint256""},{""internalType"":""uint8"",""name"":""downtime"",""type"":""uint8""},{""internalType"":""string"",""name"":""zkeyHash"",""type"":""string""}],""internalType"":""struct ProofConfig"",""name"":""proofs"",""type"":""tuple""}],""internalType"":""struct MarketplaceConfig"",""name"":"""",""type"":""tuple""}],""stateMutability"":""view"",""type"":""function""},{""inputs"":[{""internalType"":""RequestId"",""name"":""requestId"",""type"":""bytes32""},{""internalType"":""uint256"",""name"":""slotIndex"",""type"":""uint256""},{""components"":[{""components"":[{""internalType"":""uint256"",""name"":""x"",""type"":""uint256""},{""internalType"":""uint256"",""name"":""y"",""type"":""uint256""}],""internalType"":""struct G1Point"",""name"":""a"",""type"":""tuple""},{""components"":[{""components"":[{""internalType"":""uint256"",""name"":""real"",""type"":""uint256""},{""internalType"":""uint256"",""name"":""imag"",""type"":""uint256""}],""internalType"":""struct Fp2Element"",""name"":""x"",""type"":""tuple""},{""components"":[{""internalType"":""uint256"",""name"":""real"",""type"":""uint256""},{""internalType"":""uint256"",""name"":""imag"",""type"":""uint256""}],""internalType"":""struct Fp2Element"",""name"":""y"",""type"":""tuple""}],""internalType"":""struct G2Point"",""name"":""b"",""type"":""tuple""},{""components"":[{""internalType"":""uint256"",""name"":""x"",""type"":""uint256""},{""internalType"":""uint256"",""name"":""y"",""type"":""uint256""}],""internalType"":""struct G1Point"",""name"":""c"",""type"":""tuple""}],""internalType"":""struct Groth16Proof"",""name"":""proof"",""type"":""tuple""}],""name"":""fillSlot"",""outputs"":[],""stateMutability"":""nonpayable"",""type"":""function""},{""inputs"":[{""internalType"":""SlotId"",""name"":""slotId"",""type"":""bytes32""}],""name"":""freeSlot"",""outputs"":[],""stateMutability"":""nonpayable"",""type"":""function""},{""inputs"":[{""internalType"":""SlotId"",""name"":""slotId"",""type"":""bytes32""}],""name"":""getActiveSlot"",""outputs"":[{""components"":[{""components"":[{""internalType"":""address"",""name"":""client"",""type"":""address""},{""components"":[{""internalType"":""uint64"",""name"":""slots"",""type"":""uint64""},{""internalType"":""uint256"",""name"":""slotSize"",""type"":""uint256""},{""internalType"":""uint256"",""name"":""duration"",""type"":""uint256""},{""internalType"":""uint256"",""name"":""proofProbability"",""type"":""uint256""},{""internalType"":""uint256"",""name"":""reward"",""type"":""uint256""},{""internalType"":""uint256"",""name"":""collateral"",""type"":""uint256""},{""internalType"":""uint64"",""name"":""maxSlotLoss"",""type"":""uint64""}],""internalType"":""struct Ask"",""name"":""ask"",""type"":""tuple""},{""components"":[{""internalType"":""string"",""name"":""cid"",""type"":""string""},{""internalType"":""bytes32"",""name"":""merkleRoot"",""type"":""bytes32""}],""internalType"":""struct Content"",""name"":""content"",""type"":""tuple""},{""internalType"":""uint256"",""name"":""expiry"",""type"":""uint256""},{""internalType"":""bytes32"",""name"":""nonce"",""type"":""bytes32""}],""internalType"":""struct Request"",""name"":""request"",""type"":""tuple""},{""internalType"":""uint256"",""name"":""slotIndex"",""type"":""uint256""}],""internalType"":""struct Marketplace.ActiveSlot"",""name"":"""",""type"":""tuple""}],""stateMutability"":""view"",""type"":""function""},{""inputs"":[{""internalType"":""SlotId"",""name"":""id"",""type"":""bytes32""}],""name"":""getChallenge"",""outputs"":[{""internalType"":""bytes32"",""name"":"""",""type"":""bytes32""}],""stateMutability"":""view"",""type"":""function""},{""inputs"":[{""internalType"":""SlotId"",""name"":""slotId"",""type"":""bytes32""}],""name"":""getHost"",""outputs"":[{""internalType"":""address"",""name"":"""",""type"":""address""}],""stateMutability"":""view"",""type"":""function""},{""inputs"":[{""internalType"":""SlotId"",""name"":""id"",""type"":""bytes32""}],""name"":""getPointer"",""outputs"":[{""internalType"":""uint8"",""name"":"""",""type"":""uint8""}],""stateMutability"":""view"",""type"":""function""},{""inputs"":[{""internalType"":""RequestId"",""name"":""requestId"",""type"":""bytes32""}],""name"":""getRequest"",""outputs"":[{""components"":[{""internalType"":""address"",""name"":""client"",""type"":""address""},{""components"":[{""internalType"":""uint64"",""name"":""slots"",""type"":""uint64""},{""internalType"":""uint256"",""name"":""slotSize"",""type"":""uint256""},{""internalType"":""uint256"",""name"":""duration"",""type"":""uint256""},{""internalType"":""uint256"",""name"":""proofProbability"",""type"":""uint256""},{""internalType"":""uint256"",""name"":""reward"",""type"":""uint256""},{""internalType"":""uint256"",""name"":""collateral"",""type"":""uint256""},{""internalType"":""uint64"",""name"":""maxSlotLoss"",""type"":""uint64""}],""internalType"":""struct Ask"",""name"":""ask"",""type"":""tuple""},{""components"":[{""internalType"":""string"",""name"":""cid"",""type"":""string""},{""internalType"":""bytes32"",""name"":""merkleRoot"",""type"":""bytes32""}],""internalType"":""struct Content"",""name"":""content"",""type"":""tuple""},{""internalType"":""uint256"",""name"":""expiry"",""type"":""uint256""},{""internalType"":""bytes32"",""name"":""nonce"",""type"":""bytes32""}],""internalType"":""struct Request"",""name"":"""",""type"":""tuple""}],""stateMutability"":""view"",""type"":""function""},{""inputs"":[{""internalType"":""SlotId"",""name"":""id"",""type"":""bytes32""}],""name"":""isProofRequired"",""outputs"":[{""internalType"":""bool"",""name"":"""",""type"":""bool""}],""stateMutability"":""view"",""type"":""function""},{""inputs"":[{""internalType"":""SlotId"",""name"":""slotId"",""type"":""bytes32""},{""internalType"":""Periods.Period"",""name"":""period"",""type"":""uint256""}],""name"":""markProofAsMissing"",""outputs"":[],""stateMutability"":""nonpayable"",""type"":""function""},{""inputs"":[{""internalType"":""SlotId"",""name"":""slotId"",""type"":""bytes32""}],""name"":""missingProofs"",""outputs"":[{""internalType"":""uint256"",""name"":"""",""type"":""uint256""}],""stateMutability"":""view"",""type"":""function""},{""inputs"":[],""name"":""myRequests"",""outputs"":[{""internalType"":""RequestId[]"",""name"":"""",""type"":""bytes32[]""}],""stateMutability"":""view"",""type"":""function""},{""inputs"":[],""name"":""mySlots"",""outputs"":[{""internalType"":""SlotId[]"",""name"":"""",""type"":""bytes32[]""}],""stateMutability"":""view"",""type"":""function""},{""inputs"":[{""internalType"":""RequestId"",""name"":""requestId"",""type"":""bytes32""}],""name"":""requestEnd"",""outputs"":[{""internalType"":""uint256"",""name"":"""",""type"":""uint256""}],""stateMutability"":""view"",""type"":""function""},{""inputs"":[{""internalType"":""RequestId"",""name"":""requestId"",""type"":""bytes32""}],""name"":""requestExpiry"",""outputs"":[{""internalType"":""uint256"",""name"":"""",""type"":""uint256""}],""stateMutability"":""view"",""type"":""function""},{""inputs"":[{""internalType"":""RequestId"",""name"":""requestId"",""type"":""bytes32""}],""name"":""requestState"",""outputs"":[{""internalType"":""enum RequestState"",""name"":"""",""type"":""uint8""}],""stateMutability"":""view"",""type"":""function""},{""inputs"":[{""components"":[{""internalType"":""address"",""name"":""client"",""type"":""address""},{""components"":[{""internalType"":""uint64"",""name"":""slots"",""type"":""uint64""},{""internalType"":""uint256"",""name"":""slotSize"",""type"":""uint256""},{""internalType"":""uint256"",""name"":""duration"",""type"":""uint256""},{""internalType"":""uint256"",""name"":""proofProbability"",""type"":""uint256""},{""internalType"":""uint256"",""name"":""reward"",""type"":""uint256""},{""internalType"":""uint256"",""name"":""collateral"",""type"":""uint256""},{""internalType"":""uint64"",""name"":""maxSlotLoss"",""type"":""uint64""}],""internalType"":""struct Ask"",""name"":""ask"",""type"":""tuple""},{""components"":[{""internalType"":""string"",""name"":""cid"",""type"":""string""},{""internalType"":""bytes32"",""name"":""merkleRoot"",""type"":""bytes32""}],""internalType"":""struct Content"",""name"":""content"",""type"":""tuple""},{""internalType"":""uint256"",""name"":""expiry"",""type"":""uint256""},{""internalType"":""bytes32"",""name"":""nonce"",""type"":""bytes32""}],""internalType"":""struct Request"",""name"":""request"",""type"":""tuple""}],""name"":""requestStorage"",""outputs"":[],""stateMutability"":""nonpayable"",""type"":""function""},{""inputs"":[{""internalType"":""SlotId"",""name"":""slotId"",""type"":""bytes32""}],""name"":""slotState"",""outputs"":[{""internalType"":""enum SlotState"",""name"":"""",""type"":""uint8""}],""stateMutability"":""view"",""type"":""function""},{""inputs"":[{""internalType"":""SlotId"",""name"":""id"",""type"":""bytes32""},{""components"":[{""components"":[{""internalType"":""uint256"",""name"":""x"",""type"":""uint256""},{""internalType"":""uint256"",""name"":""y"",""type"":""uint256""}],""internalType"":""struct G1Point"",""name"":""a"",""type"":""tuple""},{""components"":[{""components"":[{""internalType"":""uint256"",""name"":""real"",""type"":""uint256""},{""internalType"":""uint256"",""name"":""imag"",""type"":""uint256""}],""internalType"":""struct Fp2Element"",""name"":""x"",""type"":""tuple""},{""components"":[{""internalType"":""uint256"",""name"":""real"",""type"":""uint256""},{""internalType"":""uint256"",""name"":""imag"",""type"":""uint256""}],""internalType"":""struct Fp2Element"",""name"":""y"",""type"":""tuple""}],""internalType"":""struct G2Point"",""name"":""b"",""type"":""tuple""},{""components"":[{""internalType"":""uint256"",""name"":""x"",""type"":""uint256""},{""internalType"":""uint256"",""name"":""y"",""type"":""uint256""}],""internalType"":""struct G1Point"",""name"":""c"",""type"":""tuple""}],""internalType"":""struct Groth16Proof"",""name"":""proof"",""type"":""tuple""}],""name"":""submitProof"",""outputs"":[],""stateMutability"":""nonpayable"",""type"":""function""},{""inputs"":[],""name"":""token"",""outputs"":[{""internalType"":""contract IERC20"",""name"":"""",""type"":""address""}],""stateMutability"":""view"",""type"":""function""},{""inputs"":[{""internalType"":""SlotId"",""name"":""id"",""type"":""bytes32""}],""name"":""willProofBeRequired"",""outputs"":[{""internalType"":""bool"",""name"":"""",""type"":""bool""}],""stateMutability"":""view"",""type"":""function""},{""inputs"":[{""internalType"":""RequestId"",""name"":""requestId"",""type"":""bytes32""}],""name"":""withdrawFunds"",""outputs"":[],""stateMutability"":""nonpayable"",""type"":""function""}]"; diff --git a/Tools/TraceContract/ElasticSearchLogDownloader.cs b/Tools/TraceContract/ElasticSearchLogDownloader.cs index a55d85ca..20e264f1 100644 --- a/Tools/TraceContract/ElasticSearchLogDownloader.cs +++ b/Tools/TraceContract/ElasticSearchLogDownloader.cs @@ -40,7 +40,7 @@ namespace TraceContract var queryTemplate = CreateQueryTemplate(podName, startUtc, endUtc); targetFile.Write($"Downloading '{podName}' to '{targetFile.Filename}'."); - var reconstructor = new LogReconstructor(targetFile, endpoint, queryTemplate); + var reconstructor = new LogReconstructor(log, targetFile, endpoint, queryTemplate); reconstructor.DownloadFullLog(); log.Log("Log download finished."); @@ -91,6 +91,7 @@ namespace TraceContract public class LogReconstructor { private readonly List queue = new List(); + private readonly ILog log; private readonly LogFile targetFile; private readonly IEndpoint endpoint; private readonly string queryTemplate; @@ -98,9 +99,11 @@ namespace TraceContract private string searchAfter = ""; private int lastHits = 1; private ulong? lastLogLine; + private uint linesWritten = 0; - public LogReconstructor(LogFile targetFile, IEndpoint endpoint, string queryTemplate) + public LogReconstructor(ILog log, LogFile targetFile, IEndpoint endpoint, string queryTemplate) { + this.log = log; this.targetFile = targetFile; this.endpoint = endpoint; this.queryTemplate = queryTemplate; @@ -113,6 +116,8 @@ namespace TraceContract QueryElasticSearch(); ProcessQueue(); } + + log.Log($"{linesWritten} lines written."); } private void QueryElasticSearch() @@ -124,6 +129,8 @@ namespace TraceContract var response = endpoint.HttpPostString("/_search", query); lastHits = response.hits.hits.Length; + log.Log($"pageSize: {sizeOfPage} after: {searchAfter} -> {lastHits} hits"); + if (lastHits > 0) { UpdateSearchAfter(response); @@ -176,7 +183,7 @@ namespace TraceContract private void ProcessQueue() { - if (lastLogLine == null) + if (lastLogLine == null && queue.Any()) { lastLogLine = queue.Min(q => q.Number) - 1; } @@ -208,6 +215,7 @@ namespace TraceContract private void WriteEntryToFile(LogQueueEntry currentEntry) { targetFile.Write(currentEntry.Message); + linesWritten++; } private void DeleteOldEntries(ulong wantedNumber) diff --git a/Tools/TraceContract/Input.cs b/Tools/TraceContract/Input.cs index a0c63a03..b3d31e04 100644 --- a/Tools/TraceContract/Input.cs +++ b/Tools/TraceContract/Input.cs @@ -1,4 +1,6 @@ -namespace TraceContract +using Nethereum.Hex.HexConvertors.Extensions; + +namespace TraceContract { public class Input { @@ -17,5 +19,15 @@ //"066df09a3a2e2587cfd577a0e96186c915b113d02b331b06e56f808494cff2b4"; } } + + public byte[] RequestId + { + get + { + var r = PurchaseId.HexToByteArray(); + if (r == null || r.Length != 32) throw new ArgumentException(nameof(PurchaseId)); + return r; + } + } } } diff --git a/Tools/TraceContract/Output.cs b/Tools/TraceContract/Output.cs index be89d3f6..0a919967 100644 --- a/Tools/TraceContract/Output.cs +++ b/Tools/TraceContract/Output.cs @@ -1,7 +1,9 @@ using System.Numerics; +using BlockchainUtils; using CodexContractsPlugin.ChainMonitor; using CodexContractsPlugin.Marketplace; using Logging; +using Newtonsoft.Json; using Utils; namespace TraceContract @@ -10,13 +12,13 @@ namespace TraceContract { private class Entry { - public Entry(DateTime utc, string msg) + public Entry(BlockTimeEntry blk, string msg) { - Utc = utc; + Blk = blk; Msg = msg; } - public DateTime Utc { get; } + public BlockTimeEntry Blk { get; } public string Msg { get; } } @@ -48,52 +50,48 @@ namespace TraceContract public void LogRequestCreated(RequestEvent requestEvent) { - Add(requestEvent.Block.Utc, $"Storage request created: '{requestEvent.Request.Request.Id}'"); + var msg = $"Storage request created: '{requestEvent.Request.RequestId}' = {Environment.NewLine}${JsonConvert.SerializeObject(requestEvent.Request.Request, Formatting.Indented)}{Environment.NewLine}"; + Add(requestEvent.Block, msg); } public void LogRequestCancelled(RequestEvent requestEvent) { - Add(requestEvent.Block.Utc, "Expired"); + Add(requestEvent.Block, "Expired"); } public void LogRequestFailed(RequestEvent requestEvent) { - Add(requestEvent.Block.Utc, "Failed"); + Add(requestEvent.Block, "Failed"); } public void LogRequestFinished(RequestEvent requestEvent) { - Add(requestEvent.Block.Utc, "Finished"); + Add(requestEvent.Block, "Finished"); } public void LogRequestStarted(RequestEvent requestEvent) { - Add(requestEvent.Block.Utc, "Started"); + Add(requestEvent.Block, "Started"); } - public void LogSlotFilled(RequestEvent requestEvent, EthAddress host, BigInteger slotIndex) + public void LogSlotFilled(RequestEvent requestEvent, EthAddress host, BigInteger slotIndex, bool isRepair) { - Add(requestEvent.Block.Utc, $"Slot filled. Index: {slotIndex} Host: '{host}'"); + Add(requestEvent.Block, $"Slot filled. Index: {slotIndex} Host: '{host}' isRepair: {isRepair}"); } public void LogSlotFreed(RequestEvent requestEvent, BigInteger slotIndex) { - Add(requestEvent.Block.Utc, $"Slot freed. Index: {slotIndex}"); + Add(requestEvent.Block, $"Slot freed. Index: {slotIndex}"); } public void LogSlotReservationsFull(RequestEvent requestEvent, BigInteger slotIndex) { - Add(requestEvent.Block.Utc, $"Slot reservations full. Index: {slotIndex}"); - } - - public void LogReserveSlotCalls(ReserveSlotFunction[] reserveSlotFunctions) - { - foreach (var call in reserveSlotFunctions) LogReserveSlotCall(call); + Add(requestEvent.Block, $"Slot reservations full. Index: {slotIndex}"); } public void WriteContractEvents() { - var sorted = entries.OrderBy(e => e.Utc).ToArray(); + var sorted = entries.OrderBy(e => e.Blk.Utc).ToArray(); foreach (var e in sorted) Write(e); } @@ -111,17 +109,17 @@ namespace TraceContract private void Write(Entry e) { - log.Log($"[{Time.FormatTimestamp(e.Utc)}] {e.Msg}"); + log.Log($"Block: {e.Blk.BlockNumber} [{Time.FormatTimestamp(e.Blk.Utc)}] {e.Msg}"); } public void LogReserveSlotCall(ReserveSlotFunction call) { - Add(call.Block.Utc, $"Reserve-slot called. Index: {call.SlotIndex} Host: '{call.FromAddress}'"); + Add(call.Block, $"Reserve-slot called. Block: {call.Block.BlockNumber} Index: {call.SlotIndex} Host: '{call.FromAddress}'"); } - private void Add(DateTime utc, string msg) + private void Add(BlockTimeEntry blk, string msg) { - entries.Add(new Entry(utc, msg)); + entries.Add(new Entry(blk, msg)); } } }