diff --git a/examples/ethereum_pool.json b/examples/ethereum_pool.json index 2949a351c..730816cf5 100644 --- a/examples/ethereum_pool.json +++ b/examples/ethereum_pool.json @@ -109,14 +109,16 @@ } ], "paymentProcessing": { - "enabled": true, - "minimumPayment": 1, - "payoutScheme": "PPLNS", - "payoutSchemeConfig": { - "factor": 0.5 - }, - "keepUncles": false, - "keepTransactionFees": false + "enabled": true, + "minimumPayment": 1, + "payoutScheme": "PPLNS", + "payoutSchemeConfig": { + "factor": 0.5 + }, + "gas": 21000, + "maxFeePerGas": 50000000000, + "keepUncles": false, + "keepTransactionFees": false } } ] diff --git a/libs/runtimes/win-x64/libkawpow.dll b/libs/runtimes/win-x64/libkawpow.dll new file mode 100644 index 000000000..31dc85d31 Binary files /dev/null and b/libs/runtimes/win-x64/libkawpow.dll differ diff --git a/src/Miningcore/Api/Controllers/AdminApiController.cs b/src/Miningcore/Api/Controllers/AdminApiController.cs index aa80cf6c4..d0519081d 100644 --- a/src/Miningcore/Api/Controllers/AdminApiController.cs +++ b/src/Miningcore/Api/Controllers/AdminApiController.cs @@ -8,6 +8,7 @@ using System.Collections.Concurrent; using System.Net; using System.Threading.Tasks; +using NLog; namespace Miningcore.Api.Controllers { @@ -31,6 +32,8 @@ public AdminApiController(IComponentContext ctx) : base(ctx) private readonly Responses.AdminGcStats gcStats; + private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); + #region Actions [HttpGet("stats/gc")] @@ -87,10 +90,22 @@ public async Task GetMinerBalanceAsync(string poolId, string address) // map settings var mapped = mapper.Map(settings); + + // clamp limit + if(pool.PaymentProcessing != null) + mapped.PaymentThreshold = Math.Max(mapped.PaymentThreshold, pool.PaymentProcessing.MinimumPayment); + mapped.PoolId = pool.Id; mapped.Address = address; - var result = await cf.RunTx((con, tx) => minerRepo.UpdateSettings(con, tx, mapped)); + var result = await cf.RunTx(async (con, tx) => + { + await minerRepo.UpdateSettings(con, tx, mapped); + + return await minerRepo.GetSettings(con, tx, mapped.PoolId, mapped.Address); + }); + + logger.Info(()=> $"Updated settings for pool {pool.Id}, miner {address}"); return mapper.Map(result); } diff --git a/src/Miningcore/Api/Controllers/PoolApiController.cs b/src/Miningcore/Api/Controllers/PoolApiController.cs index 9a849f25a..80c2d1f0f 100644 --- a/src/Miningcore/Api/Controllers/PoolApiController.cs +++ b/src/Miningcore/Api/Controllers/PoolApiController.cs @@ -1,5 +1,4 @@ using Autofac; -using AutoMapper; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Infrastructure; using Miningcore.Api.Extensions; @@ -8,20 +7,19 @@ using Miningcore.Configuration; using Miningcore.Extensions; using Miningcore.Mining; -using Miningcore.Persistence; using Miningcore.Persistence.Model; using Miningcore.Persistence.Model.Projections; using Miningcore.Persistence.Repositories; using Miningcore.Time; using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.Data; using System.Globalization; using System.Linq; using System.Net; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.ActionConstraints; +using NLog; namespace Miningcore.Api.Controllers { @@ -33,6 +31,8 @@ public PoolApiController(IComponentContext ctx, IActionDescriptorCollectionProvi { statsRepo = ctx.Resolve(); blocksRepo = ctx.Resolve(); + minerRepo = ctx.Resolve(); + shareRepo = ctx.Resolve(); paymentsRepo = ctx.Resolve(); clock = ctx.Resolve(); pools = ctx.Resolve>(); @@ -42,10 +42,14 @@ public PoolApiController(IComponentContext ctx, IActionDescriptorCollectionProvi private readonly IStatsRepository statsRepo; private readonly IBlockRepository blocksRepo; private readonly IPaymentRepository paymentsRepo; + private readonly IMinerRepository minerRepo; + private readonly IShareRepository shareRepo; private readonly IMasterClock clock; private readonly IActionDescriptorCollectionProvider adcp; private readonly ConcurrentDictionary pools; + private static readonly NLog.ILogger logger = LogManager.GetCurrentClassLogger(); + #region Actions [HttpGet] @@ -537,6 +541,70 @@ public async Task> PageMinerEarningsByDayV2A return result; } + [HttpGet("{poolId}/miners/{address}/settings")] + public async Task GetMinerSettingsAsync(string poolId, string address) + { + var pool = GetPool(poolId); + + if(string.IsNullOrEmpty(address)) + throw new ApiException("Invalid or missing miner address", HttpStatusCode.NotFound); + + var result = await cf.Run(con=> minerRepo.GetSettings(con, null, pool.Id, address)); + + if(result == null) + throw new ApiException("No settings found", HttpStatusCode.NotFound); + + return mapper.Map(result); + } + + [HttpPost("{poolId}/miners/{address}/settings")] + public async Task SetMinerSettingsAsync(string poolId, string address, + [FromBody] Requests.UpdateMinerSettingsRequest request) + { + var pool = GetPool(poolId); + + if(string.IsNullOrEmpty(address)) + throw new ApiException("Invalid or missing miner address", HttpStatusCode.NotFound); + + if(request?.Settings == null) + throw new ApiException("Invalid or missing settings", HttpStatusCode.BadRequest); + + if(!IPAddress.TryParse(request.IpAddress, out var requestIp)) + throw new ApiException("Invalid IP address", HttpStatusCode.BadRequest); + + // fetch recent IPs + var ips = await cf.Run(con=> shareRepo.GetRecentyUsedIpAddresses(con, null, poolId, address)); + + // any known ips? + if(ips == null || ips.Length == 0) + throw new ApiException("Address not recently used for mining", HttpStatusCode.NotFound); + + // match? + if(!ips.Any(x=> IPAddress.TryParse(x, out var ipAddress) && ipAddress.IsEqual(requestIp))) + throw new ApiException("None of the recently used IP addresses matches the request", HttpStatusCode.Forbidden); + + // map settings + var mapped = mapper.Map(request.Settings); + + // clamp limit + if(pool.PaymentProcessing != null) + mapped.PaymentThreshold = Math.Max(mapped.PaymentThreshold, pool.PaymentProcessing.MinimumPayment); + + mapped.PoolId = pool.Id; + mapped.Address = address; + + // finally update the settings + return await cf.RunTx(async (con, tx) => + { + await minerRepo.UpdateSettings(con, tx, mapped); + + logger.Info(()=> $"Updated settings for pool {pool.Id}, miner {address}"); + + var result = await minerRepo.GetSettings(con, tx, mapped.PoolId, mapped.Address); + return mapper.Map(result); + }); + } + #endregion // Actions private async Task GetMinerPerformanceInternal( diff --git a/src/Miningcore/Api/Requests/UpdateMinerSettingsRequest.cs b/src/Miningcore/Api/Requests/UpdateMinerSettingsRequest.cs new file mode 100644 index 000000000..04ee3329b --- /dev/null +++ b/src/Miningcore/Api/Requests/UpdateMinerSettingsRequest.cs @@ -0,0 +1,10 @@ +using Miningcore.Api.Responses; + +namespace Miningcore.Api.Requests +{ + public class UpdateMinerSettingsRequest + { + public string IpAddress { get; set; } + public MinerSettings Settings { get; set; } + } +} diff --git a/src/Miningcore/AutofacModule.cs b/src/Miningcore/AutofacModule.cs index 9741d5cee..8ccbadb53 100644 --- a/src/Miningcore/AutofacModule.cs +++ b/src/Miningcore/AutofacModule.cs @@ -20,6 +20,7 @@ using Newtonsoft.Json.Serialization; using Module = Autofac.Module; using Microsoft.AspNetCore.Mvc; +using Miningcore.Blockchain.Ergo; using Miningcore.Nicehash; using Miningcore.Pushover; @@ -145,6 +146,7 @@ protected override void Load(ContainerBuilder builder) ////////////////////// // Ethereum + builder.RegisterType(); builder.RegisterType(); ////////////////////// @@ -152,6 +154,12 @@ protected override void Load(ContainerBuilder builder) builder.RegisterType(); + ////////////////////// + // Ergo + + builder.RegisterType(); + builder.RegisterType(); + base.Load(builder); } } diff --git a/src/Miningcore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/Miningcore/Blockchain/Bitcoin/BitcoinJobManager.cs index 315a781b1..d41b255e6 100644 --- a/src/Miningcore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/Miningcore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -8,7 +8,6 @@ using Miningcore.Configuration; using Miningcore.Contracts; using Miningcore.Crypto; -using Miningcore.DaemonInterface; using Miningcore.Extensions; using Miningcore.JsonRpc; using Miningcore.Messaging; @@ -48,26 +47,23 @@ protected override object[] GetBlockTemplateParams() return result; } - protected async Task> GetBlockTemplateAsync(CancellationToken ct) + protected async Task> GetBlockTemplateAsync(CancellationToken ct) { logger.LogInvoke(); - var result = await daemon.ExecuteCmdAnyAsync(logger, + var result = await rpcClient.ExecuteAsync(logger, BitcoinCommands.GetBlockTemplate, ct, extraPoolConfig?.GBTArgs ?? (object) GetBlockTemplateParams()); return result; } - protected DaemonResponse GetBlockTemplateFromJson(string json) + protected RpcResponse GetBlockTemplateFromJson(string json) { logger.LogInvoke(); var result = JsonConvert.DeserializeObject(json); - return new DaemonResponse - { - Response = result.ResultAs(), - }; + return new RpcResponse(result.ResultAs()); } private BitcoinJob CreateJob() @@ -194,7 +190,7 @@ public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfi base.Configure(poolConfig, clusterConfig); } - public override object[] GetSubscriberData(StratumConnection worker) + public virtual object[] GetSubscriberData(StratumConnection worker) { Contract.RequiresNonNull(worker, nameof(worker)); @@ -213,7 +209,7 @@ public override object[] GetSubscriberData(StratumConnection worker) return responseData; } - public override async ValueTask SubmitShareAsync(StratumConnection worker, object submission, + public virtual async ValueTask SubmitShareAsync(StratumConnection worker, object submission, double stratumDifficultyBase, CancellationToken ct) { Contract.RequiresNonNull(worker, nameof(worker)); @@ -247,19 +243,14 @@ public override async ValueTask SubmitShareAsync(StratumConnection worker if(job == null) throw new StratumException(StratumError.JobNotFound, "job not found"); - // extract worker/miner/payoutid - var split = workerValue.Split('.'); - var minerName = split[0]; - var workerName = split.Length > 1 ? split[1] : null; - // validate & process var (share, blockHex) = job.ProcessShare(worker, extraNonce2, nTime, nonce, versionBits); // enrich share with common data share.PoolId = poolConfig.Id; share.IpAddress = worker.RemoteEndpoint.Address.ToString(); - share.Miner = minerName; - share.Worker = workerName; + share.Miner = context.Miner; + share.Worker = context.Worker; share.UserAgent = context.UserAgent; share.Source = clusterConfig.ClusterName; share.Created = clock.Now; @@ -276,7 +267,7 @@ public override async ValueTask SubmitShareAsync(StratumConnection worker if(share.IsBlockCandidate) { - logger.Info(() => $"Daemon accepted block {share.BlockHeight} [{share.BlockHash}] submitted by {minerName}"); + logger.Info(() => $"Daemon accepted block {share.BlockHeight} [{share.BlockHash}] submitted by {context.Miner}"); OnBlockFound(); diff --git a/src/Miningcore/Blockchain/Bitcoin/BitcoinJobManagerBase.cs b/src/Miningcore/Blockchain/Bitcoin/BitcoinJobManagerBase.cs index 897e55a85..12aaa2d95 100644 --- a/src/Miningcore/Blockchain/Bitcoin/BitcoinJobManagerBase.cs +++ b/src/Miningcore/Blockchain/Bitcoin/BitcoinJobManagerBase.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; -using System.Net; using System.Reactive; using System.Reactive.Linq; using System.Threading; @@ -12,11 +11,10 @@ using Miningcore.Blockchain.Bitcoin.DaemonResponses; using Miningcore.Configuration; using Miningcore.Contracts; -using Miningcore.DaemonInterface; using Miningcore.Extensions; +using Miningcore.JsonRpc; using Miningcore.Messaging; using Miningcore.Notifications.Messages; -using Miningcore.Stratum; using Miningcore.Time; using Miningcore.Util; using NBitcoin; @@ -45,7 +43,7 @@ protected BitcoinJobManagerBase( } protected readonly IMasterClock clock; - protected DaemonClient daemon; + protected RpcClient rpcClient; protected readonly IExtraNonceProvider extraNonceProvider; protected const int ExtranonceBytes = 4; protected int maxActiveJobs = 4; @@ -99,7 +97,7 @@ protected virtual void SetupJobUpdates(CancellationToken ct) { logger.Info(() => $"Subscribing to ZMQ push-updates from {string.Join(", ", zmq.Values)}"); - var blockNotify = daemon.ZmqSubscribe(logger, ct, zmq) + var blockNotify = rpcClient.ZmqSubscribe(logger, ct, zmq) .Select(msg => { using(msg) @@ -207,20 +205,19 @@ protected virtual async Task ShowDaemonSyncProgressAsync(CancellationToken ct) return; } - var infos = await daemon.ExecuteCmdAllAsync(logger, BitcoinCommands.GetBlockchainInfo, ct); + var info = await rpcClient.ExecuteAsync(logger, BitcoinCommands.GetBlockchainInfo, ct); - if(infos.Length > 0) + if(info != null) { - var blockCount = infos - .Max(x => x.Response?.Blocks); + var blockCount = info.Response?.Blocks; if(blockCount.HasValue) { // get list of peers and their highest block height to compare to ours - var peerInfo = await daemon.ExecuteCmdAnyAsync(logger, BitcoinCommands.GetPeerInfo, ct); + var peerInfo = await rpcClient.ExecuteAsync(logger, BitcoinCommands.GetPeerInfo, ct); var peers = peerInfo.Response; - if(peers != null && peers.Length > 0) + if(peers is {Length: > 0}) { var totalBlocks = peers.Max(x => x.StartingHeight); var percent = totalBlocks > 0 ? (double) blockCount / totalBlocks * 100 : 0; @@ -236,10 +233,10 @@ private async Task UpdateNetworkStatsAsync(CancellationToken ct) try { - var results = await daemon.ExecuteBatchAnyAsync(logger, ct, - new DaemonCmd(BitcoinCommands.GetMiningInfo), - new DaemonCmd(BitcoinCommands.GetNetworkInfo), - new DaemonCmd(BitcoinCommands.GetNetworkHashPS) + var results = await rpcClient.ExecuteBatchAsync(logger, ct, + new RpcRequest(BitcoinCommands.GetMiningInfo), + new RpcRequest(BitcoinCommands.GetNetworkInfo), + new RpcRequest(BitcoinCommands.GetNetworkHashPS) ); if(results.Any(x => x.Error != null)) @@ -270,11 +267,11 @@ private async Task UpdateNetworkStatsAsync(CancellationToken ct) protected virtual async Task<(bool Accepted, string CoinbaseTx)> SubmitBlockAsync(Share share, string blockHex) { // execute command batch - var results = await daemon.ExecuteBatchAnyAsync(logger, CancellationToken.None, + var results = await rpcClient.ExecuteBatchAsync(logger, CancellationToken.None, hasSubmitBlockMethod - ? new DaemonCmd(BitcoinCommands.SubmitBlock, new[] { blockHex }) - : new DaemonCmd(BitcoinCommands.GetBlockTemplate, new { mode = "submit", data = blockHex }), - new DaemonCmd(BitcoinCommands.GetBlock, new[] { share.BlockHash })); + ? new RpcRequest(BitcoinCommands.SubmitBlock, new[] { blockHex }) + : new RpcRequest(BitcoinCommands.GetBlockTemplate, new { mode = "submit", data = blockHex }), + new RpcRequest(BitcoinCommands.GetBlock, new[] { share.BlockHash })); // did submission succeed? var submitResult = results[0]; @@ -305,36 +302,30 @@ private async Task UpdateNetworkStatsAsync(CancellationToken ct) protected virtual async Task AreDaemonsHealthyLegacyAsync(CancellationToken ct) { - var responses = await daemon.ExecuteCmdAllAsync(logger, BitcoinCommands.GetInfo, ct); - - if(responses.Where(x => x.Error?.InnerException?.GetType() == typeof(DaemonClientException)) - .Select(x => (DaemonClientException) x.Error.InnerException) - .Any(x => x.Code == HttpStatusCode.Unauthorized)) - logger.ThrowLogPoolStartupException("Daemon reports invalid credentials"); + var response = await rpcClient.ExecuteAsync(logger, BitcoinCommands.GetInfo, ct); - return responses.All(x => x.Error == null); + return response.Error == null; } protected virtual async Task AreDaemonsConnectedLegacyAsync(CancellationToken ct) { - var response = await daemon.ExecuteCmdAnyAsync(logger, BitcoinCommands.GetInfo, ct); + var response = await rpcClient.ExecuteAsync(logger, BitcoinCommands.GetInfo, ct); return response.Error == null && response.Response.Connections > 0; } protected virtual async Task ShowDaemonSyncProgressLegacyAsync(CancellationToken ct) { - var infos = await daemon.ExecuteCmdAllAsync(logger, BitcoinCommands.GetInfo, ct); + var info = await rpcClient.ExecuteAsync(logger, BitcoinCommands.GetInfo, ct); - if(infos.Length > 0) + if(info != null) { - var blockCount = infos - .Max(x => x.Response?.Blocks); + var blockCount = info.Response?.Blocks; if(blockCount.HasValue) { // get list of peers and their highest block height to compare to ours - var peerInfo = await daemon.ExecuteCmdAnyAsync(logger, BitcoinCommands.GetPeerInfo, ct); + var peerInfo = await rpcClient.ExecuteAsync(logger, BitcoinCommands.GetPeerInfo, ct); var peers = peerInfo.Response; if(peers != null && peers.Length > 0) @@ -353,8 +344,8 @@ private async Task UpdateNetworkStatsLegacyAsync(CancellationToken ct) try { - var results = await daemon.ExecuteBatchAnyAsync(logger, ct, - new DaemonCmd(BitcoinCommands.GetConnectionCount) + var results = await rpcClient.ExecuteBatchAsync(logger, ct, + new RpcRequest(BitcoinCommands.GetConnectionCount) ); if(results.Any(x => x.Error != null)) @@ -385,8 +376,7 @@ protected override void ConfigureDaemons() { var jsonSerializerSettings = ctx.Resolve(); - daemon = new DaemonClient(jsonSerializerSettings, messageBus, clusterConfig.ClusterName ?? poolConfig.PoolName, poolConfig.Id); - daemon.Configure(poolConfig.Daemons); + rpcClient = new RpcClient(poolConfig.Daemons.First(), jsonSerializerSettings, messageBus, poolConfig.Id); } protected override async Task AreDaemonsHealthyAsync(CancellationToken ct) @@ -394,14 +384,9 @@ protected override async Task AreDaemonsHealthyAsync(CancellationToken ct) if(hasLegacyDaemon) return await AreDaemonsHealthyLegacyAsync(ct); - var responses = await daemon.ExecuteCmdAllAsync(logger, BitcoinCommands.GetBlockchainInfo, ct); - - if(responses.Where(x => x.Error?.InnerException?.GetType() == typeof(DaemonClientException)) - .Select(x => (DaemonClientException) x.Error.InnerException) - .Any(x => x.Code == HttpStatusCode.Unauthorized)) - logger.ThrowLogPoolStartupException("Daemon reports invalid credentials"); + var response = await rpcClient.ExecuteAsync(logger, BitcoinCommands.GetBlockchainInfo, ct); - return responses.All(x => x.Error == null); + return response.Error == null; } protected override async Task AreDaemonsConnectedAsync(CancellationToken ct) @@ -409,7 +394,7 @@ protected override async Task AreDaemonsConnectedAsync(CancellationToken c if(hasLegacyDaemon) return await AreDaemonsConnectedLegacyAsync(ct); - var response = await daemon.ExecuteCmdAnyAsync(logger, BitcoinCommands.GetNetworkInfo, ct); + var response = await rpcClient.ExecuteAsync(logger, BitcoinCommands.GetNetworkInfo, ct); return response.Error == null && response.Response?.Connections > 0; } @@ -420,10 +405,10 @@ protected override async Task EnsureDaemonsSynchedAsync(CancellationToken ct) while(true) { - var responses = await daemon.ExecuteCmdAllAsync(logger, + var response = await rpcClient.ExecuteAsync(logger, BitcoinCommands.GetBlockTemplate, ct, GetBlockTemplateParams()); - var isSynched = responses.All(x => x.Error == null); + var isSynched = response.Error == null; if(isSynched) { @@ -446,25 +431,24 @@ protected override async Task EnsureDaemonsSynchedAsync(CancellationToken ct) protected override async Task PostStartInitAsync(CancellationToken ct) { - var commands = new[] + var requests = new[] { - new DaemonCmd(BitcoinCommands.ValidateAddress, new[] { poolConfig.Address }), - new DaemonCmd(BitcoinCommands.SubmitBlock), - new DaemonCmd(!hasLegacyDaemon ? BitcoinCommands.GetBlockchainInfo : BitcoinCommands.GetInfo), - new DaemonCmd(BitcoinCommands.GetDifficulty), - new DaemonCmd(BitcoinCommands.GetAddressInfo, new[] { poolConfig.Address }), + new RpcRequest(BitcoinCommands.ValidateAddress, new[] { poolConfig.Address }), + new RpcRequest(BitcoinCommands.SubmitBlock), + new RpcRequest(!hasLegacyDaemon ? BitcoinCommands.GetBlockchainInfo : BitcoinCommands.GetInfo), + new RpcRequest(BitcoinCommands.GetDifficulty), + new RpcRequest(BitcoinCommands.GetAddressInfo, new[] { poolConfig.Address }), }; - var results = await daemon.ExecuteBatchAnyAsync(logger, ct, commands); + var responses = await rpcClient.ExecuteBatchAsync(logger, ct, requests); - if(results.Any(x => x.Error != null)) + if(responses.Any(x => x.Error != null)) { - var resultList = results.ToList(); - // filter out optional RPCs - var errors = results - .Where(x => x.Error != null && commands[resultList.IndexOf(x)].Method != BitcoinCommands.SubmitBlock) - .Where(x => x.Error != null && commands[resultList.IndexOf(x)].Method != BitcoinCommands.GetAddressInfo) + var errors = responses + .Where((x, i) => x.Error != null && + requests[i].Method != BitcoinCommands.SubmitBlock && + requests[i].Method != BitcoinCommands.GetAddressInfo) .ToArray(); if(errors.Any()) @@ -472,12 +456,12 @@ protected override async Task PostStartInitAsync(CancellationToken ct) } // extract results - var validateAddressResponse = results[0].Error == null ? results[0].Response.ToObject() : null; - var submitBlockResponse = results[1]; - var blockchainInfoResponse = !hasLegacyDaemon ? results[2].Response.ToObject() : null; - var daemonInfoResponse = hasLegacyDaemon ? results[2].Response.ToObject() : null; - var difficultyResponse = results[3].Response.ToObject(); - var addressInfoResponse = results[4].Error == null ? results[4].Response.ToObject() : null; + var validateAddressResponse = responses[0].Error == null ? responses[0].Response.ToObject() : null; + var submitBlockResponse = responses[1]; + var blockchainInfoResponse = !hasLegacyDaemon ? responses[2].Response.ToObject() : null; + var daemonInfoResponse = hasLegacyDaemon ? responses[2].Response.ToObject() : null; + var difficultyResponse = responses[3].Response.ToObject(); + var addressInfoResponse = responses[4].Error == null ? responses[4].Response.ToObject() : null; // chain detection if(!hasLegacyDaemon) @@ -509,8 +493,8 @@ protected override async Task PostStartInitAsync(CancellationToken ct) if(clusterConfig.PaymentProcessing?.Enabled == true && poolConfig.PaymentProcessing?.Enabled == true) { // ensure pool owns wallet - //if (!validateAddressResponse.IsMine) - // logger.ThrowLogPoolStartupException($"Daemon does not own pool-address '{poolConfig.Address}'"); + if(validateAddressResponse is {IsMine: false} || addressInfoResponse is {IsMine: false}) + logger.Warn(()=> $"Daemon does not own pool-address '{poolConfig.Address}'"); ConfigureRewards(); } @@ -612,17 +596,11 @@ public virtual async Task ValidateAddressAsync(string address, Cancellatio if(string.IsNullOrEmpty(address)) return false; - var result = await daemon.ExecuteCmdAnyAsync(logger, ct, - BitcoinCommands.ValidateAddress, new[] { address }); + var result = await rpcClient.ExecuteAsync(logger, BitcoinCommands.ValidateAddress, ct, new[] { address }); return result.Response is {IsValid: true}; } - public abstract object[] GetSubscriberData(StratumConnection worker); - - public abstract ValueTask SubmitShareAsync(StratumConnection worker, object submission, - double stratumDifficultyBase, CancellationToken ct); - #endregion // API-Surface } } diff --git a/src/Miningcore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs b/src/Miningcore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs index e41bbaf42..01773d7b1 100644 --- a/src/Miningcore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs +++ b/src/Miningcore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs @@ -8,8 +8,8 @@ using Miningcore.Blockchain.Bitcoin.Configuration; using Miningcore.Blockchain.Bitcoin.DaemonResponses; using Miningcore.Configuration; -using Miningcore.DaemonInterface; using Miningcore.Extensions; +using Miningcore.JsonRpc; using Miningcore.Messaging; using Miningcore.Mining; using Miningcore.Payments; @@ -49,7 +49,7 @@ public BitcoinPayoutHandler( } protected readonly IComponentContext ctx; - protected DaemonClient daemon; + protected RpcClient rpcClient; protected BitcoinDaemonEndpointConfigExtra extraPoolConfig; protected BitcoinPoolPaymentProcessingConfigExtra extraPoolPaymentProcessingConfig; @@ -70,8 +70,7 @@ public virtual Task ConfigureAsync(ClusterConfig clusterConfig, PoolConfig poolC logger = LogUtil.GetPoolScopedLogger(typeof(BitcoinPayoutHandler), poolConfig); var jsonSerializerSettings = ctx.Resolve(); - daemon = new DaemonClient(jsonSerializerSettings, messageBus, clusterConfig.ClusterName ?? poolConfig.PoolName, poolConfig.Id); - daemon.Configure(poolConfig.Daemons); + rpcClient = new RpcClient(poolConfig.Daemons.First(), jsonSerializerSettings, messageBus, poolConfig.Id); return Task.FromResult(true); } @@ -101,11 +100,11 @@ public virtual async Task ClassifyBlocksAsync(IMiningPool pool, Block[] .ToArray(); // build command batch (block.TransactionConfirmationData is the hash of the blocks coinbase transaction) - var batch = page.Select(block => new DaemonCmd(BitcoinCommands.GetTransaction, + var batch = page.Select(block => new RpcRequest(BitcoinCommands.GetTransaction, new[] { block.TransactionConfirmationData })).ToArray(); // execute batch - var results = await daemon.ExecuteBatchAnyAsync(logger, ct, batch); + var results = await rpcClient.ExecuteBatchAsync(logger, ct, batch); for(var j = 0; j < results.Length; j++) { @@ -264,7 +263,7 @@ public virtual async Task PayoutAsync(IMiningPool pool, Balance[] balances, Canc // send command tryTransfer: - var result = await daemon.ExecuteCmdSingleAsync(logger, BitcoinCommands.SendMany, ct, args, new JsonSerializerSettings()); + var result = await rpcClient.ExecuteAsync(logger, BitcoinCommands.SendMany, ct, args); if(result.Error == null) { @@ -272,7 +271,7 @@ public virtual async Task PayoutAsync(IMiningPool pool, Balance[] balances, Canc { // lock wallet logger.Info(() => $"[{LogCategory}] Locking wallet"); - await daemon.ExecuteCmdSingleAsync(logger, BitcoinCommands.WalletLock, ct); + await rpcClient.ExecuteAsync(logger, BitcoinCommands.WalletLock, ct); } // check result @@ -296,7 +295,7 @@ public virtual async Task PayoutAsync(IMiningPool pool, Balance[] balances, Canc { logger.Info(() => $"[{LogCategory}] Unlocking wallet"); - var unlockResult = await daemon.ExecuteCmdSingleAsync(logger, BitcoinCommands.WalletPassphrase, ct, new[] + var unlockResult = await rpcClient.ExecuteAsync(logger, BitcoinCommands.WalletPassphrase, ct, new[] { (object) extraPoolPaymentProcessingConfig.WalletPassword, (object) 5 // unlock for N seconds diff --git a/src/Miningcore/Blockchain/CoinMetaData.cs b/src/Miningcore/Blockchain/CoinMetaData.cs index 0dcac4ded..f3265d2cb 100644 --- a/src/Miningcore/Blockchain/CoinMetaData.cs +++ b/src/Miningcore/Blockchain/CoinMetaData.cs @@ -24,6 +24,7 @@ public static class DevDonation { "ZEC", "t1YEgm6ovXFseeFxXgFY2zXxwsScD4BbfhT" }, { "BTG", "GRao6KHQ8a4GUjAZRVbeCLfRbSkJQQaeMg" }, { "XVG", "D5xPoHLM6HPkwWSqAweECTSQirJBmRjS8i" }, + { "ERG", "9foYU8JkoqWBSDA3ba8VHfduPXV2NaVNPPAFkdYoR9t9cPQGMv4 "}, { "XMR", "46S2AEwYmD9fnmZkxCpXf1T3U3DyEq3Ekb8Lg9kgUMGABn9Fp9q5nE2fBcXebrjrXfZHy5uC5HfLE6X4WLtSm35wUr9Mh46" }, { "RVN", "RF8wbxb3jeAcrH2z71NxccmyZmufk1D5m5" }, { "TUBE", "bxdAFKYA5sJYKM3zcn3SLaLRjsFF582VE1Uv5NChrVLm6o6UF4SdbZBZLrTBD6yEFZDzuTQGBCa8FLpX8charjxH2G3iMRX6R" }, diff --git a/src/Miningcore/Blockchain/Cryptonote/CryptonoteJobManager.cs b/src/Miningcore/Blockchain/Cryptonote/CryptonoteJobManager.cs index 6976671a8..e2f50a754 100644 --- a/src/Miningcore/Blockchain/Cryptonote/CryptonoteJobManager.cs +++ b/src/Miningcore/Blockchain/Cryptonote/CryptonoteJobManager.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; -using System.Net; using System.Reactive; using System.Reactive.Linq; using System.Security.Cryptography; @@ -16,7 +15,6 @@ using Miningcore.Blockchain.Cryptonote.DaemonResponses; using Miningcore.Blockchain.Cryptonote.StratumRequests; using Miningcore.Configuration; -using Miningcore.DaemonInterface; using Miningcore.Extensions; using Miningcore.JsonRpc; using Miningcore.Messaging; @@ -51,8 +49,8 @@ public CryptonoteJobManager( private byte[] instanceId; private DaemonEndpointConfig[] daemonEndpoints; - private DaemonClient daemon; - private DaemonClient walletDaemon; + private RpcClient rpcClient; + private RpcClient rpcClientWallet; private readonly IMasterClock clock; private CryptonoteNetworkType networkType; private CryptonotePoolConfigExtra extraPoolConfig; @@ -148,7 +146,7 @@ protected async Task UpdateJob(CancellationToken ct, string via = null, st return false; } - private async Task> GetBlockTemplateAsync(CancellationToken ct) + private async Task> GetBlockTemplateAsync(CancellationToken ct) { logger.LogInvoke(); @@ -158,35 +156,31 @@ private async Task> GetBlockTemplateAsy ReserveSize = CryptonoteConstants.ReserveSize }; - return await daemon.ExecuteCmdAnyAsync(logger, CryptonoteCommands.GetBlockTemplate, ct, request); + return await rpcClient.ExecuteAsync(logger, CryptonoteCommands.GetBlockTemplate, ct, request); } - private DaemonResponse GetBlockTemplateFromJson(string json) + private RpcResponse GetBlockTemplateFromJson(string json) { logger.LogInvoke(); var result = JsonConvert.DeserializeObject(json); - return new DaemonResponse - { - Response = result.ResultAs(), - }; + return new RpcResponse(result.ResultAs()); } private async Task ShowDaemonSyncProgressAsync(CancellationToken ct) { - var infos = await daemon.ExecuteCmdAllAsync(logger, CryptonoteCommands.GetInfo, ct); - var firstValidResponse = infos.FirstOrDefault(x => x.Error == null && x.Response != null)?.Response; + var response = await rpcClient.ExecuteAsync(logger, CryptonoteCommands.GetInfo, ct); + var info = response.Response; - if(firstValidResponse != null) + if(info != null) { - var lowestHeight = infos.Where(x => x.Error == null && x.Response != null) - .Min(x => x.Response.Height); + var lowestHeight = info.Height; - var totalBlocks = firstValidResponse.TargetHeight; + var totalBlocks = info.TargetHeight; var percent = (double) lowestHeight / totalBlocks * 100; - logger.Info(() => $"Daemons have downloaded {percent:0.00}% of blockchain from {firstValidResponse.OutgoingConnectionsCount} peers"); + logger.Info(() => $"Daemons have downloaded {percent:0.00}% of blockchain from {info.OutgoingConnectionsCount} peers"); } } @@ -196,14 +190,14 @@ private async Task UpdateNetworkStatsAsync(CancellationToken ct) try { - var infoResponse = await daemon.ExecuteCmdAnyAsync(logger, CryptonoteCommands.GetInfo, ct); + var response = await rpcClient.ExecuteAsync(logger, CryptonoteCommands.GetInfo, ct); - if(infoResponse.Error != null) - logger.Warn(() => $"Error(s) refreshing network stats: {infoResponse.Error.Message} (Code {infoResponse.Error.Code})"); + if(response.Error != null) + logger.Warn(() => $"Error(s) refreshing network stats: {response.Error.Message} (Code {response.Error.Code})"); - if(infoResponse.Response != null) + if(response.Response != null) { - var info = infoResponse.Response.ToObject(); + var info = response.Response.ToObject(); BlockchainStats.NetworkHashrate = info.Target > 0 ? (double) info.Difficulty / info.Target : 0; BlockchainStats.ConnectedPeers = info.OutgoingConnectionsCount + info.IncomingConnectionsCount; @@ -218,7 +212,7 @@ private async Task UpdateNetworkStatsAsync(CancellationToken ct) private async Task SubmitBlockAsync(Share share, string blobHex, string blobHash) { - var response = await daemon.ExecuteCmdAnyAsync(logger, CryptonoteCommands.SubmitBlock, CancellationToken.None, new[] { blobHex }); + var response = await rpcClient.ExecuteAsync(logger, CryptonoteCommands.SubmitBlock, CancellationToken.None, new[] { blobHex }); if(response.Error != null || response?.Response?.Status != "OK") { @@ -439,41 +433,29 @@ protected override void ConfigureDaemons() { var jsonSerializerSettings = ctx.Resolve(); - daemon = new DaemonClient(jsonSerializerSettings, messageBus, clusterConfig.ClusterName ?? poolConfig.PoolName, poolConfig.Id); - daemon.Configure(daemonEndpoints); + rpcClient = new RpcClient(daemonEndpoints.First(), jsonSerializerSettings, messageBus, poolConfig.Id); if(clusterConfig.PaymentProcessing?.Enabled == true && poolConfig.PaymentProcessing?.Enabled == true) { // also setup wallet daemon - walletDaemon = new DaemonClient(jsonSerializerSettings, messageBus, clusterConfig.ClusterName ?? poolConfig.PoolName, poolConfig.Id); - walletDaemon.Configure(walletDaemonEndpoints); + rpcClientWallet = new RpcClient(walletDaemonEndpoints.First(), jsonSerializerSettings, messageBus, poolConfig.Id); } } protected override async Task AreDaemonsHealthyAsync(CancellationToken ct) { // test daemons - var responses = await daemon.ExecuteCmdAllAsync(logger, CryptonoteCommands.GetInfo, ct); + var response = await rpcClient.ExecuteAsync(logger, CryptonoteCommands.GetInfo, ct); - if(responses.Where(x => x.Error?.InnerException?.GetType() == typeof(DaemonClientException)) - .Select(x => (DaemonClientException) x.Error.InnerException) - .Any(x => x.Code == HttpStatusCode.Unauthorized)) - logger.ThrowLogPoolStartupException("Daemon reports invalid credentials"); - - if(responses.Any(x => x.Error != null)) + if(response.Error != null) return false; if(clusterConfig.PaymentProcessing?.Enabled == true && poolConfig.PaymentProcessing?.Enabled == true) { // test wallet daemons - var responses2 = await walletDaemon.ExecuteCmdAllAsync(logger, CryptonoteWalletCommands.GetAddress, ct); - - if(responses2.Where(x => x.Error?.InnerException?.GetType() == typeof(DaemonClientException)) - .Select(x => (DaemonClientException) x.Error.InnerException) - .Any(x => x.Code == HttpStatusCode.Unauthorized)) - logger.ThrowLogPoolStartupException("Wallet-Daemon reports invalid credentials"); + var responses2 = await rpcClientWallet.ExecuteAsync(logger, CryptonoteWalletCommands.GetAddress, ct); - return responses2.All(x => x.Error == null); + return responses2.Error == null; } return true; @@ -481,7 +463,7 @@ protected override async Task AreDaemonsHealthyAsync(CancellationToken ct) protected override async Task AreDaemonsConnectedAsync(CancellationToken ct) { - var response = await daemon.ExecuteCmdAnyAsync(logger, CryptonoteCommands.GetInfo, ct); + var response = await rpcClient.ExecuteAsync(logger, CryptonoteCommands.GetInfo, ct); return response.Error == null && response.Response != null && (response.Response.OutgoingConnectionsCount + response.Response.IncomingConnectionsCount) > 0; @@ -499,10 +481,10 @@ protected override async Task EnsureDaemonsSynchedAsync(CancellationToken ct) ReserveSize = CryptonoteConstants.ReserveSize }; - var responses = await daemon.ExecuteCmdAllAsync(logger, + var response = await rpcClient.ExecuteAsync(logger, CryptonoteCommands.GetBlockTemplate, ct, request); - var isSynched = responses.All(x => x.Error == null || x.Error.Code != -9); + var isSynched = response.Error is not {Code: -9}; if(isSynched) { @@ -529,14 +511,14 @@ protected override async Task PostStartInitAsync(CancellationToken ct) // coin config var coin = poolConfig.Template.As(); - var infoResponse = await daemon.ExecuteCmdAnyAsync(logger, CryptonoteCommands.GetInfo, ct); + var infoResponse = await rpcClient.ExecuteAsync(logger, CryptonoteCommands.GetInfo, ct); if(infoResponse.Error != null) logger.ThrowLogPoolStartupException($"Init RPC failed: {infoResponse.Error.Message} (Code {infoResponse.Error.Code})"); if(clusterConfig.PaymentProcessing?.Enabled == true && poolConfig.PaymentProcessing?.Enabled == true) { - var addressResponse = await walletDaemon.ExecuteCmdAnyAsync(logger, ct, CryptonoteWalletCommands.GetAddress); + var addressResponse = await rpcClientWallet.ExecuteAsync(logger, CryptonoteWalletCommands.GetAddress, ct); // ensure pool owns wallet if(clusterConfig.PaymentProcessing?.Enabled == true && addressResponse.Response?.Address != poolConfig.Address) @@ -669,7 +651,7 @@ protected virtual void SetupJobUpdates(CancellationToken ct) { logger.Info(() => $"Subscribing to ZMQ push-updates from {string.Join(", ", zmq.Values)}"); - var blockNotify = daemon.ZmqSubscribe(logger, ct, zmq) + var blockNotify = rpcClient.ZmqSubscribe(logger, ct, zmq) .Where(msg => { bool result = false; diff --git a/src/Miningcore/Blockchain/Cryptonote/CryptonotePayoutHandler.cs b/src/Miningcore/Blockchain/Cryptonote/CryptonotePayoutHandler.cs index a3259059e..73b2fcbf3 100644 --- a/src/Miningcore/Blockchain/Cryptonote/CryptonotePayoutHandler.cs +++ b/src/Miningcore/Blockchain/Cryptonote/CryptonotePayoutHandler.cs @@ -10,8 +10,8 @@ using Miningcore.Blockchain.Cryptonote.DaemonRequests; using Miningcore.Blockchain.Cryptonote.DaemonResponses; using Miningcore.Configuration; -using Miningcore.DaemonInterface; using Miningcore.Extensions; +using Miningcore.JsonRpc; using Miningcore.Messaging; using Miningcore.Mining; using Miningcore.Native; @@ -52,15 +52,15 @@ public CryptonotePayoutHandler( } private readonly IComponentContext ctx; - private DaemonClient daemon; - private DaemonClient walletDaemon; + private RpcClient rpcClient; + private RpcClient rpcClientWallet; private CryptonoteNetworkType? networkType; private CryptonotePoolPaymentProcessingConfigExtra extraConfig; private bool walletSupportsTransferSplit; protected override string LogCategory => "Cryptonote Payout Handler"; - private async Task HandleTransferResponseAsync(DaemonResponse response, params Balance[] balances) + private async Task HandleTransferResponseAsync(RpcResponse response, params Balance[] balances) { var coin = poolConfig.Template.As(); @@ -85,7 +85,7 @@ private async Task HandleTransferResponseAsync(DaemonResponse HandleTransferResponseAsync(DaemonResponse response, params Balance[] balances) + private async Task HandleTransferResponseAsync(RpcResponse response, params Balance[] balances) { var coin = poolConfig.Template.As(); @@ -114,7 +114,7 @@ private async Task UpdateNetworkTypeAsync(CancellationToken ct) { if(!networkType.HasValue) { - var infoResponse = await daemon.ExecuteCmdAnyAsync(logger, CNC.GetInfo, ct, true); + var infoResponse = await rpcClient.ExecuteAsync(logger, CNC.GetInfo, ct, true); var info = infoResponse.Response.ToObject(); // chain detection @@ -144,7 +144,7 @@ private async Task UpdateNetworkTypeAsync(CancellationToken ct) private async Task EnsureBalance(decimal requiredAmount, CryptonoteCoinTemplate coin, CancellationToken ct) { - var response = await walletDaemon.ExecuteCmdSingleAsync(logger, CryptonoteWalletCommands.GetBalance, ct); + var response = await rpcClientWallet.ExecuteAsync(logger, CryptonoteWalletCommands.GetBalance, ct); if(response.Error != null) { @@ -198,7 +198,7 @@ private async Task PayoutBatch(Balance[] balances, CancellationToken ct) logger.Info(() => $"[{LogCategory}] Paying {FormatAmount(balances.Sum(x => x.Amount))} to {balances.Length} addresses:\n{string.Join("\n", balances.OrderByDescending(x => x.Amount).Select(x => $"{FormatAmount(x.Amount)} to {x.Address}"))}"); // send command - var transferResponse = await walletDaemon.ExecuteCmdSingleAsync(logger, CryptonoteWalletCommands.Transfer, ct, request); + var transferResponse = await rpcClientWallet.ExecuteAsync(logger, CryptonoteWalletCommands.Transfer, ct, request); // gracefully handle error -4 (transaction would be too large. try /transfer_split) if(transferResponse.Error?.Code == -4) @@ -208,7 +208,7 @@ private async Task PayoutBatch(Balance[] balances, CancellationToken ct) logger.Error(() => $"[{LogCategory}] Daemon command '{CryptonoteWalletCommands.Transfer}' returned error: {transferResponse.Error.Message} code {transferResponse.Error.Code}"); logger.Info(() => $"[{LogCategory}] Retrying transfer using {CryptonoteWalletCommands.TransferSplit}"); - var transferSplitResponse = await walletDaemon.ExecuteCmdSingleAsync(logger, CryptonoteWalletCommands.TransferSplit, ct, request); + var transferSplitResponse = await rpcClientWallet.ExecuteAsync(logger, CryptonoteWalletCommands.TransferSplit, ct, request); return await HandleTransferResponseAsync(transferSplitResponse, balances); } @@ -275,7 +275,7 @@ private async Task PayoutToPaymentId(Balance balance, CancellationToken ct logger.Info(() => $"[{LogCategory}] Paying {FormatAmount(balance.Amount)} to integrated address {balance.Address}"); // send command - var result = await walletDaemon.ExecuteCmdSingleAsync(logger, CryptonoteWalletCommands.Transfer, ct, request); + var result = await rpcClientWallet.ExecuteAsync(logger, CryptonoteWalletCommands.Transfer, ct, request); if(walletSupportsTransferSplit) { @@ -284,7 +284,7 @@ private async Task PayoutToPaymentId(Balance balance, CancellationToken ct { logger.Info(() => $"[{LogCategory}] Retrying transfer using {CryptonoteWalletCommands.TransferSplit}"); - result = await walletDaemon.ExecuteCmdSingleAsync(logger, CryptonoteWalletCommands.TransferSplit, ct, request); + result = await rpcClientWallet.ExecuteAsync(logger, CryptonoteWalletCommands.TransferSplit, ct, request); } } @@ -317,8 +317,7 @@ public async Task ConfigureAsync(ClusterConfig clusterConfig, PoolConfig poolCon }) .ToArray(); - daemon = new DaemonClient(jsonSerializerSettings, messageBus, clusterConfig.ClusterName ?? poolConfig.PoolName, poolConfig.Id); - daemon.Configure(daemonEndpoints); + rpcClient = new RpcClient(daemonEndpoints.First(), jsonSerializerSettings, messageBus, poolConfig.Id); // configure wallet daemon var walletDaemonEndpoints = poolConfig.Daemons @@ -332,14 +331,13 @@ public async Task ConfigureAsync(ClusterConfig clusterConfig, PoolConfig poolCon }) .ToArray(); - walletDaemon = new DaemonClient(jsonSerializerSettings, messageBus, clusterConfig.ClusterName ?? poolConfig.PoolName, poolConfig.Id); - walletDaemon.Configure(walletDaemonEndpoints); + rpcClientWallet = new RpcClient(walletDaemonEndpoints.First(), jsonSerializerSettings, messageBus, poolConfig.Id); // detect network await UpdateNetworkTypeAsync(ct); // detect transfer_split support - var response = await walletDaemon.ExecuteCmdSingleAsync(logger, CryptonoteWalletCommands.TransferSplit, ct); + var response = await rpcClientWallet.ExecuteAsync(logger, CryptonoteWalletCommands.TransferSplit, ct); walletSupportsTransferSplit = response.Error.Code != CryptonoteConstants.MoneroRpcMethodNotFound; } @@ -366,7 +364,7 @@ public async Task ClassifyBlocksAsync(IMiningPool pool, Block[] blocks, { var block = page[j]; - var rpcResult = await daemon.ExecuteCmdAnyAsync(logger, + var rpcResult = await rpcClient.ExecuteAsync(logger, CNC.GetBlockHeaderByHeight, ct, new GetBlockHeaderByHeightRequest { @@ -427,7 +425,8 @@ public Task CalculateBlockEffortAsync(IMiningPool pool, Block block, double accu return Task.FromResult(true); } - public override async Task UpdateBlockRewardBalancesAsync(IDbConnection con, IDbTransaction tx, IMiningPool pool, Block block, CancellationToken ct) + public override async Task UpdateBlockRewardBalancesAsync(IDbConnection con, IDbTransaction tx, + IMiningPool pool, Block block, CancellationToken ct) { var blockRewardRemaining = await base.UpdateBlockRewardBalancesAsync(con, tx, pool, block, ct); @@ -444,7 +443,7 @@ public async Task PayoutAsync(IMiningPool pool, Balance[] balances, Cancellation var coin = poolConfig.Template.As(); #if !DEBUG // ensure we have peers - var infoResponse = await daemon.ExecuteCmdAnyAsync(logger, CNC.GetInfo, ct); + var infoResponse = await rpcClient.ExecuteAsync(logger, CNC.GetInfo, ct); if (infoResponse.Error != null || infoResponse.Response == null || infoResponse.Response.IncomingConnectionsCount + infoResponse.Response.OutgoingConnectionsCount < 3) { @@ -467,7 +466,7 @@ public async Task PayoutAsync(IMiningPool pool, Balance[] balances, Cancellation if(addressPrefix != coin.AddressPrefix && addressIntegratedPrefix != coin.AddressPrefixIntegrated) { - logger.Warn(() => $"[{LogCategory}] Excluding payment to invalid address {x.Address}"); + logger.Warn(() => $"[{LogCategory}] Excluding payment to invalid address: {x.Address}"); return false; } @@ -477,7 +476,7 @@ public async Task PayoutAsync(IMiningPool pool, Balance[] balances, Cancellation if(addressPrefix != coin.AddressPrefixStagenet && addressIntegratedPrefix != coin.AddressPrefixIntegratedStagenet) { - logger.Warn(() => $"[{LogCategory}] Excluding payment to invalid address {x.Address}"); + logger.Warn(() => $"[{LogCategory}] Excluding payment to invalid address: {x.Address}"); return false; } @@ -487,7 +486,7 @@ public async Task PayoutAsync(IMiningPool pool, Balance[] balances, Cancellation if(addressPrefix != coin.AddressPrefixTestnet && addressIntegratedPrefix != coin.AddressPrefixIntegratedTestnet) { - logger.Warn(() => $"[{LogCategory}] Excluding payment to invalid address {x.Address}"); + logger.Warn(() => $"[{LogCategory}] Excluding payment to invalid address: {x.Address}"); return false; } @@ -561,7 +560,7 @@ public async Task PayoutAsync(IMiningPool pool, Balance[] balances, Cancellation } // save wallet - await walletDaemon.ExecuteCmdSingleAsync(logger, CryptonoteWalletCommands.Store, ct); + await rpcClientWallet.ExecuteAsync(logger, CryptonoteWalletCommands.Store, ct); } #endregion // IPayoutHandler diff --git a/src/Miningcore/Blockchain/Equihash/DaemonResponses/ZCashAsyncOperationStatus.cs b/src/Miningcore/Blockchain/Equihash/DaemonResponses/ZCashAsyncOperationStatus.cs index 428c8ca6f..a0d35dcef 100644 --- a/src/Miningcore/Blockchain/Equihash/DaemonResponses/ZCashAsyncOperationStatus.cs +++ b/src/Miningcore/Blockchain/Equihash/DaemonResponses/ZCashAsyncOperationStatus.cs @@ -14,6 +14,6 @@ public class ZCashAsyncOperationStatus public string Status { get; set; } public JToken Result { get; set; } - public JsonRpcException Error { get; set; } + public JsonRpcError Error { get; set; } } } diff --git a/src/Miningcore/Blockchain/Equihash/EquihashJobManager.cs b/src/Miningcore/Blockchain/Equihash/EquihashJobManager.cs index 17501457c..4ccf0025e 100644 --- a/src/Miningcore/Blockchain/Equihash/EquihashJobManager.cs +++ b/src/Miningcore/Blockchain/Equihash/EquihashJobManager.cs @@ -1,6 +1,5 @@ using System; using System.Linq; -using System.Reactive; using System.Threading; using System.Threading.Tasks; using Autofac; @@ -12,7 +11,6 @@ using Miningcore.Configuration; using Miningcore.Contracts; using Miningcore.Crypto.Hashing.Equihash; -using Miningcore.DaemonInterface; using Miningcore.Extensions; using Miningcore.JsonRpc; using Miningcore.Messaging; @@ -36,9 +34,10 @@ public EquihashJobManager( } private EquihashCoinTemplate coin; - public EquihashCoinTemplate.EquihashNetworkParams ChainConfig { get; private set; } private EquihashSolver solver; + public EquihashCoinTemplate.EquihashNetworkParams ChainConfig { get; private set; } + protected override void PostChainIdentifyConfigure() { ChainConfig = coin.GetNetwork(network.ChainName); @@ -47,33 +46,30 @@ protected override void PostChainIdentifyConfigure() base.PostChainIdentifyConfigure(); } - private async Task> GetBlockTemplateAsync(CancellationToken ct) + private async Task> GetBlockTemplateAsync(CancellationToken ct) { logger.LogInvoke(); - var subsidyResponse = await daemon.ExecuteCmdAnyAsync(logger, BitcoinCommands.GetBlockSubsidy, ct); + var subsidyResponse = await rpcClient.ExecuteAsync(logger, BitcoinCommands.GetBlockSubsidy, ct); - var result = await daemon.ExecuteCmdAnyAsync(logger, + var result = await rpcClient.ExecuteAsync(logger, BitcoinCommands.GetBlockTemplate, ct, extraPoolConfig?.GBTArgs ?? (object) GetBlockTemplateParams()); if(subsidyResponse.Error == null && result.Error == null && result.Response != null) result.Response.Subsidy = subsidyResponse.Response; else if(subsidyResponse.Error?.Code != (int) BitcoinRPCErrorCode.RPC_METHOD_NOT_FOUND) - result.Error = new JsonRpcException(-1, $"{BitcoinCommands.GetBlockSubsidy} failed", null); + result = new RpcResponse(null, new JsonRpcError(-1, $"{BitcoinCommands.GetBlockSubsidy} failed", null)); return result; } - private DaemonResponse GetBlockTemplateFromJson(string json) + private RpcResponse GetBlockTemplateFromJson(string json) { logger.LogInvoke(); var result = JsonConvert.DeserializeObject(json); - return new DaemonResponse - { - Response = result.ResultAs(), - }; + return new RpcResponse(result.ResultAs()); } protected override IDestination AddressToDestination(string address, BitcoinAddressType? addressType) @@ -210,13 +206,13 @@ public override async Task ValidateAddressAsync(string address, Cancellati return true; // handle z-addr - var result = await daemon.ExecuteCmdAnyAsync(logger, ct, - EquihashCommands.ZValidateAddress, new[] { address }); + var result = await rpcClient.ExecuteAsync(logger, + EquihashCommands.ZValidateAddress, ct, new[] { address }); return result.Response is {IsValid: true}; } - public override object[] GetSubscriberData(StratumConnection worker) + public object[] GetSubscriberData(StratumConnection worker) { Contract.RequiresNonNull(worker, nameof(worker)); @@ -234,7 +230,7 @@ public override object[] GetSubscriberData(StratumConnection worker) return responseData; } - public override async ValueTask SubmitShareAsync(StratumConnection worker, object submission, + public async ValueTask SubmitShareAsync(StratumConnection worker, object submission, double stratumDifficultyBase, CancellationToken ct) { Contract.RequiresNonNull(worker, nameof(worker)); @@ -270,11 +266,6 @@ public override async ValueTask SubmitShareAsync(StratumConnection worker if(job == null) throw new StratumException(StratumError.JobNotFound, "job not found"); - // extract worker/miner/payoutid - var split = workerValue.Split('.'); - var minerName = split[0]; - var workerName = split.Length > 1 ? split[1] : null; - // validate & process var (share, blockHex) = job.ProcessShare(worker, extraNonce2, nTime, solution); @@ -290,7 +281,7 @@ public override async ValueTask SubmitShareAsync(StratumConnection worker if(share.IsBlockCandidate) { - logger.Info(() => $"Daemon accepted block {share.BlockHeight} [{share.BlockHash}] submitted by {minerName}"); + logger.Info(() => $"Daemon accepted block {share.BlockHeight} [{share.BlockHash}] submitted by {context.Miner}"); OnBlockFound(); @@ -309,8 +300,8 @@ public override async ValueTask SubmitShareAsync(StratumConnection worker // enrich share with common data share.PoolId = poolConfig.Id; share.IpAddress = worker.RemoteEndpoint.Address.ToString(); - share.Miner = minerName; - share.Worker = workerName; + share.Miner = context.Miner; + share.Worker = context.Worker; share.UserAgent = context.UserAgent; share.Source = clusterConfig.ClusterName; share.NetworkDifficulty = job.Difficulty; diff --git a/src/Miningcore/Blockchain/Equihash/EquihashPayoutHandler.cs b/src/Miningcore/Blockchain/Equihash/EquihashPayoutHandler.cs index 3c914c4d2..2a48674bc 100644 --- a/src/Miningcore/Blockchain/Equihash/EquihashPayoutHandler.cs +++ b/src/Miningcore/Blockchain/Equihash/EquihashPayoutHandler.cs @@ -58,14 +58,14 @@ public override async Task ConfigureAsync(ClusterConfig clusterConfig, PoolConfi poolExtraConfig = poolConfig.Extra.SafeExtensionDataAs(); // detect network - var blockchainInfoResponse = await daemon.ExecuteCmdSingleAsync(logger, BitcoinCommands.GetBlockchainInfo, ct); + var blockchainInfoResponse = await rpcClient.ExecuteAsync(logger, BitcoinCommands.GetBlockchainInfo, ct); network = Network.GetNetwork(blockchainInfoResponse.Response.Chain.ToLower()); chainConfig = poolConfig.Template.As().GetNetwork(network.ChainName); // detect z_shieldcoinbase support - var response = await daemon.ExecuteCmdSingleAsync(logger, EquihashCommands.ZShieldCoinbase, ct); + var response = await rpcClient.ExecuteAsync(logger, EquihashCommands.ZShieldCoinbase, ct); supportsNativeShielding = response.Error.Code != (int) BitcoinRPCErrorCode.RPC_METHOD_NOT_FOUND; } @@ -79,15 +79,13 @@ public override async Task PayoutAsync(IMiningPool pool, Balance[] balances, Can else await ShieldCoinbaseEmulatedAsync(ct); - var didUnlockWallet = false; - // send in batches with no more than 50 recipients to avoid running into tx size limits var pageSize = 50; var pageCount = (int) Math.Ceiling(balances.Length / (double) pageSize); for(var i = 0; i < pageCount; i++) { - didUnlockWallet = false; + var didUnlockWallet = false; // get a page full of balances var page = balances @@ -107,13 +105,13 @@ public override async Task PayoutAsync(IMiningPool pool, Balance[] balances, Can var pageAmount = amounts.Sum(x => x.Amount); // check shielded balance - var balanceResult = await daemon.ExecuteCmdSingleAsync(logger, EquihashCommands.ZGetBalance, ct, new object[] + var balanceResponse = await rpcClient.ExecuteAsync(logger, EquihashCommands.ZGetBalance, ct, new object[] { poolExtraConfig.ZAddress, // default account ZMinConfirmations, // only spend funds covered by this many confirmations }); - if(balanceResult.Error != null || (decimal) (double) balanceResult.Response - TransferFee < pageAmount) + if(balanceResponse.Error != null || (decimal) (double) balanceResponse.Response - TransferFee < pageAmount) { logger.Info(() => $"[{LogCategory}] Insufficient shielded balance for payment of {FormatAmount(pageAmount)}"); return; @@ -131,11 +129,11 @@ public override async Task PayoutAsync(IMiningPool pool, Balance[] balances, Can // send command tryTransfer: - var result = await daemon.ExecuteCmdSingleAsync(logger, EquihashCommands.ZSendMany, ct, args); + var response = await rpcClient.ExecuteAsync(logger, EquihashCommands.ZSendMany, ct, args); - if(result.Error == null) + if(response.Error == null) { - var operationId = result.Response; + var operationId = response.Response; // check result if(string.IsNullOrEmpty(operationId)) @@ -148,7 +146,7 @@ public override async Task PayoutAsync(IMiningPool pool, Balance[] balances, Can while(continueWaiting) { - var operationResultResponse = await daemon.ExecuteCmdSingleAsync(logger, + var operationResultResponse = await rpcClient.ExecuteAsync(logger, EquihashCommands.ZGetOperationResult, ct, new object[] { new object[] { operationId } }); if(operationResultResponse.Error == null && @@ -185,26 +183,26 @@ public override async Task PayoutAsync(IMiningPool pool, Balance[] balances, Can } logger.Info(() => $"[{LogCategory}] Waiting for completion: {operationId}"); - await Task.Delay(TimeSpan.FromSeconds(10)); + await Task.Delay(TimeSpan.FromSeconds(10), ct); } } } else { - if(result.Error.Code == (int) BitcoinRPCErrorCode.RPC_WALLET_UNLOCK_NEEDED && !didUnlockWallet) + if(response.Error.Code == (int) BitcoinRPCErrorCode.RPC_WALLET_UNLOCK_NEEDED && !didUnlockWallet) { if(!string.IsNullOrEmpty(extraPoolPaymentProcessingConfig?.WalletPassword)) { logger.Info(() => $"[{LogCategory}] Unlocking wallet"); - var unlockResult = await daemon.ExecuteCmdSingleAsync(logger, BitcoinCommands.WalletPassphrase, ct, new[] + var unlockResponse = await rpcClient.ExecuteAsync(logger, BitcoinCommands.WalletPassphrase, ct, new[] { (object) extraPoolPaymentProcessingConfig.WalletPassword, (object) 5 // unlock for N seconds }); - if(unlockResult.Error == null) + if(unlockResponse.Error == null) { didUnlockWallet = true; goto tryTransfer; @@ -212,8 +210,8 @@ public override async Task PayoutAsync(IMiningPool pool, Balance[] balances, Can else { - logger.Error(() => $"[{LogCategory}] {BitcoinCommands.WalletPassphrase} returned error: {result.Error.Message} code {result.Error.Code}"); - NotifyPayoutFailure(poolConfig.Id, page, $"{BitcoinCommands.WalletPassphrase} returned error: {result.Error.Message} code {result.Error.Code}", null); + logger.Error(() => $"[{LogCategory}] {BitcoinCommands.WalletPassphrase} returned error: {response.Error.Message} code {response.Error.Code}"); + NotifyPayoutFailure(poolConfig.Id, page, $"{BitcoinCommands.WalletPassphrase} returned error: {response.Error.Message} code {response.Error.Code}", null); break; } } @@ -228,16 +226,17 @@ public override async Task PayoutAsync(IMiningPool pool, Balance[] balances, Can else { - logger.Error(() => $"[{LogCategory}] {EquihashCommands.ZSendMany} returned error: {result.Error.Message} code {result.Error.Code}"); + logger.Error(() => $"[{LogCategory}] {EquihashCommands.ZSendMany} returned error: {response.Error.Message} code {response.Error.Code}"); - NotifyPayoutFailure(poolConfig.Id, page, $"{EquihashCommands.ZSendMany} returned error: {result.Error.Message} code {result.Error.Code}", null); + NotifyPayoutFailure(poolConfig.Id, page, $"{EquihashCommands.ZSendMany} returned error: {response.Error.Message} code {response.Error.Code}", null); } } } // lock wallet logger.Info(() => $"[{LogCategory}] Locking wallet"); - await daemon.ExecuteCmdSingleAsync(logger, BitcoinCommands.WalletLock, ct); + + await rpcClient.ExecuteAsync(logger, BitcoinCommands.WalletLock, ct); } #endregion // IPayoutHandler @@ -257,19 +256,19 @@ private async Task ShieldCoinbaseAsync(CancellationToken ct) poolExtraConfig.ZAddress, // dest: pool's z-addr }; - var result = await daemon.ExecuteCmdSingleAsync(logger, EquihashCommands.ZShieldCoinbase, ct, args); + var response = await rpcClient.ExecuteAsync(logger, EquihashCommands.ZShieldCoinbase, ct, args); - if(result.Error != null) + if(response.Error != null) { - if(result.Error.Code == -6) + if(response.Error.Code == -6) logger.Info(() => $"[{LogCategory}] No funds to shield"); else - logger.Error(() => $"[{LogCategory}] {EquihashCommands.ZShieldCoinbase} returned error: {result.Error.Message} code {result.Error.Code}"); + logger.Error(() => $"[{LogCategory}] {EquihashCommands.ZShieldCoinbase} returned error: {response.Error.Message} code {response.Error.Code}"); return; } - var operationId = result.Response.OperationId; + var operationId = response.Response.OperationId; logger.Info(() => $"[{LogCategory}] {EquihashCommands.ZShieldCoinbase} operation id: {operationId}"); @@ -277,7 +276,7 @@ private async Task ShieldCoinbaseAsync(CancellationToken ct) while(continueWaiting) { - var operationResultResponse = await daemon.ExecuteCmdSingleAsync(logger, + var operationResultResponse = await rpcClient.ExecuteAsync(logger, EquihashCommands.ZGetOperationResult, ct, new object[] { new object[] { operationId } }); if(operationResultResponse.Error == null && @@ -309,7 +308,8 @@ private async Task ShieldCoinbaseAsync(CancellationToken ct) } logger.Info(() => $"[{LogCategory}] Waiting for shielding operation completion: {operationId}"); - await Task.Delay(TimeSpan.FromSeconds(10)); + + await Task.Delay(TimeSpan.FromSeconds(10), ct); } } @@ -318,15 +318,15 @@ private async Task ShieldCoinbaseEmulatedAsync(CancellationToken ct) logger.Info(() => $"[{LogCategory}] Shielding ZCash Coinbase funds (emulated)"); // get t-addr unspent balance for just the coinbase address (pool wallet) - var unspentResult = await daemon.ExecuteCmdSingleAsync(logger, BitcoinCommands.ListUnspent, ct); + var unspentResponse = await rpcClient.ExecuteAsync(logger, BitcoinCommands.ListUnspent, ct); - if(unspentResult.Error != null) + if(unspentResponse.Error != null) { - logger.Error(() => $"[{LogCategory}] {BitcoinCommands.ListUnspent} returned error: {unspentResult.Error.Message} code {unspentResult.Error.Code}"); + logger.Error(() => $"[{LogCategory}] {BitcoinCommands.ListUnspent} returned error: {unspentResponse.Error.Message} code {unspentResponse.Error.Code}"); return; } - var balance = unspentResult.Response + var balance = unspentResponse.Response .Where(x => x.Spendable && x.Address == poolConfig.Address) .Sum(x => x.Amount); @@ -358,15 +358,15 @@ private async Task ShieldCoinbaseEmulatedAsync(CancellationToken ct) }; // send command - var sendResult = await daemon.ExecuteCmdSingleAsync(logger, EquihashCommands.ZSendMany, ct, args); + var sendResponse = await rpcClient.ExecuteAsync(logger, EquihashCommands.ZSendMany, ct, args); - if(sendResult.Error != null) + if(sendResponse.Error != null) { - logger.Error(() => $"[{LogCategory}] {EquihashCommands.ZSendMany} returned error: {unspentResult.Error.Message} code {unspentResult.Error.Code}"); + logger.Error(() => $"[{LogCategory}] {EquihashCommands.ZSendMany} returned error: {unspentResponse.Error.Message} code {unspentResponse.Error.Code}"); return; } - var operationId = sendResult.Response; + var operationId = sendResponse.Response; logger.Info(() => $"[{LogCategory}] {EquihashCommands.ZSendMany} operation id: {operationId}"); @@ -374,7 +374,7 @@ private async Task ShieldCoinbaseEmulatedAsync(CancellationToken ct) while(continueWaiting) { - var operationResultResponse = await daemon.ExecuteCmdSingleAsync(logger, + var operationResultResponse = await rpcClient.ExecuteAsync(logger, EquihashCommands.ZGetOperationResult, ct, new object[] { new object[] { operationId } }); if(operationResultResponse.Error == null && @@ -407,7 +407,8 @@ private async Task ShieldCoinbaseEmulatedAsync(CancellationToken ct) } logger.Info(() => $"[{LogCategory}] Waiting for shielding transfer completion: {operationId}"); - await Task.Delay(TimeSpan.FromSeconds(10)); + + await Task.Delay(TimeSpan.FromSeconds(10), ct); } } } diff --git a/src/Miningcore/Blockchain/Ergo/Configuration/ErgoDaemonEndpointConfigExtra.cs b/src/Miningcore/Blockchain/Ergo/Configuration/ErgoDaemonEndpointConfigExtra.cs new file mode 100644 index 000000000..38857c775 --- /dev/null +++ b/src/Miningcore/Blockchain/Ergo/Configuration/ErgoDaemonEndpointConfigExtra.cs @@ -0,0 +1,10 @@ +namespace Miningcore.Blockchain.Ergo.Configuration +{ + public class ErgoDaemonEndpointConfigExtra + { + /// + /// The Ergo Node's API key in clear-text - not the hash + /// + public string ApiKey { get; set; } + } +} diff --git a/src/Miningcore/Blockchain/Ergo/Configuration/ErgoPaymentProcessingConfigExtra.cs b/src/Miningcore/Blockchain/Ergo/Configuration/ErgoPaymentProcessingConfigExtra.cs new file mode 100644 index 000000000..87c393707 --- /dev/null +++ b/src/Miningcore/Blockchain/Ergo/Configuration/ErgoPaymentProcessingConfigExtra.cs @@ -0,0 +1,15 @@ +namespace Miningcore.Blockchain.Ergo.Configuration +{ + public class ErgoPaymentProcessingConfigExtra + { + /// + /// Password for unlocking wallet + /// + public string WalletPassword { get; set; } + + /// + /// Minimum block confirmations + /// + public int? MinimumConfirmations { get; set; } + } +} diff --git a/src/Miningcore/Blockchain/Ergo/Configuration/ErgoPoolConfigExtra.cs b/src/Miningcore/Blockchain/Ergo/Configuration/ErgoPoolConfigExtra.cs new file mode 100644 index 000000000..8a40cf920 --- /dev/null +++ b/src/Miningcore/Blockchain/Ergo/Configuration/ErgoPoolConfigExtra.cs @@ -0,0 +1,20 @@ +using Miningcore.Configuration; + +namespace Miningcore.Blockchain.Ergo.Configuration +{ + public class ErgoPoolConfigExtra + { + /// + /// Maximum number of tracked jobs. + /// Default: 12 - you should increase this value if your blockrefreshinterval is higher than 300ms + /// + public int? MaxActiveJobs { get; set; } + + /// + /// Blocktemplate stream published via ZMQ + /// + public ZmqPubSubEndpointConfig BtStream { get; set; } + + public int? ExtraNonce1Size { get; set; } + } +} diff --git a/src/Miningcore/Blockchain/Ergo/ErgoClientExtensions.cs b/src/Miningcore/Blockchain/Ergo/ErgoClientExtensions.cs new file mode 100644 index 000000000..933323769 --- /dev/null +++ b/src/Miningcore/Blockchain/Ergo/ErgoClientExtensions.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Miningcore.Blockchain.Ergo +{ + public partial class ErgoClient + { + public Dictionary RequestHeaders { get; } = new(); + + private Task PrepareRequestAsync(HttpClient client, HttpRequestMessage request, StringBuilder url) + { + foreach(var pair in RequestHeaders) + request.Headers.Add(pair.Key, pair.Value); + + return Task.CompletedTask; + } + + private Task PrepareRequestAsync(HttpClient client, HttpRequestMessage request, string url) + { + return Task.CompletedTask; + } + + private static Task ProcessResponseAsync(HttpClient client, HttpResponseMessage response, CancellationToken ct) + { + return Task.CompletedTask; + } + } +} diff --git a/src/Miningcore/Blockchain/Ergo/ErgoClientFactory.cs b/src/Miningcore/Blockchain/Ergo/ErgoClientFactory.cs new file mode 100644 index 000000000..40a9149ac --- /dev/null +++ b/src/Miningcore/Blockchain/Ergo/ErgoClientFactory.cs @@ -0,0 +1,52 @@ +using System; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using Miningcore.Blockchain.Ergo.Configuration; +using Miningcore.Configuration; +using Miningcore.Extensions; +using Miningcore.Util; +using NLog; + +namespace Miningcore.Blockchain.Ergo +{ + public static class ErgoClientFactory + { + public static ErgoClient CreateClient(PoolConfig poolConfig, ClusterConfig clusterConfig, ILogger logger) + { + var epConfig = poolConfig.Daemons.First(); + var extra = epConfig.Extra.SafeExtensionDataAs(); + + if(logger != null && clusterConfig.PaymentProcessing?.Enabled == true && + poolConfig.PaymentProcessing?.Enabled == true && string.IsNullOrEmpty(extra?.ApiKey)) + logger.ThrowLogPoolStartupException("Ergo daemon apiKey not provided"); + + var baseUrl = new UriBuilder(epConfig.Ssl || epConfig.Http2 ? Uri.UriSchemeHttps : Uri.UriSchemeHttp, + epConfig.Host, epConfig.Port, epConfig.HttpPath); + + var result = new ErgoClient(baseUrl.ToString(), new HttpClient(new HttpClientHandler + { + AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip, + + ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true, + })); + + if(!string.IsNullOrEmpty(extra.ApiKey)) + result.RequestHeaders["api_key"] = extra.ApiKey; + + if(!string.IsNullOrEmpty(epConfig.User)) + { + var auth = $"{epConfig.User}:{epConfig.Password}"; + var base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(auth)); + + result.RequestHeaders["Authorization"] = new AuthenticationHeaderValue("Basic", base64).ToString(); + } +#if DEBUG + result.ReadResponseAsString = true; +#endif + return result; + } + } +} diff --git a/src/Miningcore/Blockchain/Ergo/ErgoConstants.cs b/src/Miningcore/Blockchain/Ergo/ErgoConstants.cs new file mode 100644 index 000000000..4d7535a11 --- /dev/null +++ b/src/Miningcore/Blockchain/Ergo/ErgoConstants.cs @@ -0,0 +1,20 @@ +using System; +using System.Linq; +using System.Text.RegularExpressions; + +// ReSharper disable InconsistentNaming + +namespace Miningcore.Blockchain.Ergo +{ + public static class ErgoConstants + { + public const uint ShareMultiplier = 256; + public const decimal SmallestUnit = 1000000000; + public static Regex RegexChain = new("ergo-([^-]+)-.+", RegexOptions.Compiled); + + public static byte[] M = Enumerable.Range(0, 1024) + .Select(x => BitConverter.GetBytes((ulong) x).Reverse()) + .SelectMany(byteArr => byteArr) + .ToArray(); + } +} diff --git a/src/Miningcore/Blockchain/Ergo/ErgoExtraNonceProvider.cs b/src/Miningcore/Blockchain/Ergo/ErgoExtraNonceProvider.cs new file mode 100644 index 000000000..eea4731f6 --- /dev/null +++ b/src/Miningcore/Blockchain/Ergo/ErgoExtraNonceProvider.cs @@ -0,0 +1,9 @@ +namespace Miningcore.Blockchain.Ergo +{ + public class ErgoExtraNonceProvider : ExtraNonceProviderBase + { + public ErgoExtraNonceProvider(string poolId, int size, byte? clusterInstanceId) : base(poolId, size, clusterInstanceId) + { + } + } +} diff --git a/src/Miningcore/Blockchain/Ergo/ErgoJob.cs b/src/Miningcore/Blockchain/Ergo/ErgoJob.cs new file mode 100644 index 000000000..8b6d1449d --- /dev/null +++ b/src/Miningcore/Blockchain/Ergo/ErgoJob.cs @@ -0,0 +1,241 @@ +using System; +using System.Collections.Concurrent; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using Miningcore.Contracts; +using Miningcore.Crypto; +using Miningcore.Crypto.Hashing.Algorithms; +using Miningcore.Extensions; +using Miningcore.Stratum; +using System.Numerics; +using NBitcoin; + +namespace Miningcore.Blockchain.Ergo +{ + public class ErgoJob + { + public WorkMessage BlockTemplate { get; private set; } + public double Difficulty { get; private set; } + public uint Height => BlockTemplate.Height; + public string JobId { get; protected set; } + + private object[] jobParams; + private BigInteger n; + private readonly ConcurrentDictionary submissions = new(StringComparer.OrdinalIgnoreCase); + private static readonly IHashAlgorithm hasher = new Blake2b(); + private int extraNonceSize; + + private static readonly uint nBase = (uint) Math.Pow(2, 26); + private const uint IncreaseStart = 600 * 1024; + private const uint IncreasePeriodForN = 50 * 1024; + private const uint NIncreasementHeightMax = 9216000; + + public static uint CalcN(uint height) + { + height = Math.Min(NIncreasementHeightMax, height); + + switch (height) + { + case < IncreaseStart: + return nBase; + case >= NIncreasementHeightMax: + return 2147387550; + } + + var step = nBase; + var iterationsNumber = (height - IncreaseStart) / IncreasePeriodForN + 1; + + for(var i = 0; i < iterationsNumber; i++) + step = step / 100 * 105; + + return step; + } + + protected bool RegisterSubmit(string nTime, string nonce) + { + var key = new StringBuilder() + .Append(nTime) + .Append(nonce) + .ToString(); + + return submissions.TryAdd(key, true); + } + + protected virtual byte[] SerializeCoinbase(string msg, string nonce) + { + using(var stream = new MemoryStream()) + { + stream.Write(msg.HexToByteArray()); + stream.Write(nonce.HexToByteArray()); + + return stream.ToArray(); + } + } + + private BigInteger[] GenIndexes(byte[] seed) + { + // hash seed + Span hash = stackalloc byte[32]; + hasher.Digest(seed, hash); + + // duplicate + Span extendedHash = stackalloc byte[64]; + hash.CopyTo(extendedHash); + hash.CopyTo(extendedHash.Slice(32, 32)); + + // map indexes + var result = new BigInteger[32]; + + for(var i = 0; i < 32; i++) + { + var x = BitConverter.ToUInt32(extendedHash.Slice(i, 4)).ToBigEndian(); + result[i] = x % n; + } + + return result; + } + + protected virtual Share ProcessShareInternal(StratumConnection worker, string nonce) + { + var context = worker.ContextAs(); + + // hash coinbase + var coinbase = SerializeCoinbase(BlockTemplate.Msg, nonce); + Span hash = stackalloc byte[32]; + hasher.Digest(coinbase, hash); + + // calculate i + var slice = hash.Slice(24, 8); + var tmp2 = new BigInteger(slice, true, true) % n; + var i = tmp2.ToByteArray(false, true).PadFront(0, 4); + + // calculate e + var h = new BigInteger(Height).ToByteArray(true, true).PadFront(0, 4); + var ihM = i.Concat(h).Concat(ErgoConstants.M).ToArray(); + hasher.Digest(ihM, hash); + var e = hash[1..].ToArray(); + + // calculate j + var eCoinbase = e.Concat(coinbase).ToArray(); + var jTmp = GenIndexes(eCoinbase); + var j = jTmp.Select(x => x.ToByteArray(true, true).PadFront(0, 4)).ToArray(); + + // calculate f + var f = j.Select(x => + { + var buf2 = x.Concat(h).Concat(ErgoConstants.M).ToArray(); + + // hash it + Span tmp = stackalloc byte[32]; + hasher.Digest(buf2, tmp); + + // extract 31 bytes at end + return new BigInteger(tmp[1..], true, true); + }).Aggregate((x, y) => x + y); + + // calculate fH + var fBytes = f.ToByteArray(true, true).PadFront(0, 32); + hasher.Digest(fBytes, hash); + var fh = new BigInteger(hash, true, true); + var fhTarget = new Target(fh); + + // diff check + var stratumDifficulty = context.Difficulty; + var ratio = fhTarget.Difficulty / stratumDifficulty; + + // check if the share meets the much harder block difficulty (block candidate) + var isBlockCandidate = fh < BlockTemplate.B; + + // test if share meets at least workers current difficulty + if(!isBlockCandidate && ratio < 0.99) + { + // check if share matched the previous difficulty from before a vardiff retarget + if(context.VarDiff?.LastUpdate != null && context.PreviousDifficulty.HasValue) + { + ratio = fhTarget.Difficulty / context.PreviousDifficulty.Value; + + if(ratio < 0.99) + throw new StratumException(StratumError.LowDifficultyShare, $"low difficulty share ({fhTarget.Difficulty})"); + + // use previous difficulty + stratumDifficulty = context.PreviousDifficulty.Value; + } + + else + throw new StratumException(StratumError.LowDifficultyShare, $"low difficulty share ({fhTarget.Difficulty})"); + } + + var result = new Share + { + BlockHeight = Height, + NetworkDifficulty = Difficulty, + Difficulty = stratumDifficulty / ErgoConstants.ShareMultiplier + }; + + if(isBlockCandidate) + { + result.IsBlockCandidate = true; + } + + return result; + } + + public object[] GetJobParams(bool isNew) + { + jobParams[^1] = isNew; + return jobParams; + } + + public virtual Share ProcessShare(StratumConnection worker, string extraNonce2, string nTime, string nonce) + { + Contract.RequiresNonNull(worker, nameof(worker)); + Contract.Requires(!string.IsNullOrEmpty(extraNonce2), $"{nameof(extraNonce2)} must not be empty"); + Contract.Requires(!string.IsNullOrEmpty(nTime), $"{nameof(nTime)} must not be empty"); + Contract.Requires(!string.IsNullOrEmpty(nonce), $"{nameof(nonce)} must not be empty"); + + var context = worker.ContextAs(); + + // validate nonce + if(nonce.Length != context.ExtraNonce1.Length + extraNonceSize * 2) + throw new StratumException(StratumError.Other, "incorrect size of nonce"); + + if(!nonce.StartsWith(context.ExtraNonce1)) + throw new StratumException(StratumError.Other, $"incorrect extraNonce2 in nonce (expected {context.ExtraNonce1}, got {nonce.Substring(0, Math.Min(nonce.Length, context.ExtraNonce1.Length))})"); + + // currently unused + if(nTime == "undefined") + nTime = string.Empty; + + // dupe check + if(!RegisterSubmit(nTime, nonce)) + throw new StratumException(StratumError.DuplicateShare, $"duplicate share"); + + return ProcessShareInternal(worker, nonce); + } + + public void Init(WorkMessage blockTemplate, int blockVersion, int extraNonceSize, string jobId) + { + this.extraNonceSize = extraNonceSize; + + BlockTemplate = blockTemplate; + JobId = jobId; + Difficulty = new Target(BlockTemplate.B).Difficulty; + n = CalcN(Height); + + jobParams = new object[] + { + JobId, + Height, + BlockTemplate.Msg, + string.Empty, + string.Empty, + blockVersion, + null, // to filled out by ErgoPool.SendJob + string.Empty, + false + }; + } + } +} diff --git a/src/Miningcore/Blockchain/Ergo/ErgoJobManager.cs b/src/Miningcore/Blockchain/Ergo/ErgoJobManager.cs new file mode 100644 index 000000000..e8b7c8ced --- /dev/null +++ b/src/Miningcore/Blockchain/Ergo/ErgoJobManager.cs @@ -0,0 +1,488 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; +using System.Threading; +using System.Threading.Tasks; +using Autofac; +using Miningcore.Blockchain.Ergo.Configuration; +using NLog; +using Miningcore.Configuration; +using Miningcore.Extensions; +using Miningcore.Messaging; +using Miningcore.Notifications.Messages; +using Miningcore.Stratum; +using Miningcore.Time; +using Miningcore.Util; +using Newtonsoft.Json; +using Contract = Miningcore.Contracts.Contract; +using static Miningcore.Util.ActionUtils; + +namespace Miningcore.Blockchain.Ergo +{ + public class ErgoJobManager : JobManagerBase + { + public ErgoJobManager( + IComponentContext ctx, + IMessageBus messageBus, + IMasterClock clock, + IExtraNonceProvider extraNonceProvider) : + base(ctx, messageBus) + { + Contract.RequiresNonNull(clock, nameof(clock)); + Contract.RequiresNonNull(extraNonceProvider, nameof(extraNonceProvider)); + + this.clock = clock; + this.extraNonceProvider = extraNonceProvider; + + extraNonceSize = 8 - extraNonceProvider.ByteSize; + } + + private ErgoCoinTemplate coin; + private ErgoClient ergoClient; + private string network; + private readonly List validJobs = new(); + private int maxActiveJobs = 4; + private readonly int extraNonceSize; + private readonly IExtraNonceProvider extraNonceProvider; + private readonly IMasterClock clock; + private ErgoPoolConfigExtra extraPoolConfig; + private int blockVersion; + + private void SetupJobUpdates() + { + var blockFound = blockFoundSubject.Synchronize(); + var pollTimerRestart = blockFoundSubject.Synchronize(); + + var triggers = new List> + { + blockFound.Select(x => (false, JobRefreshBy.BlockFound, (string) null)) + }; + + if(extraPoolConfig?.BtStream != null) + { + var btStream = BtStreamSubscribe(extraPoolConfig.BtStream); + + triggers.Add(btStream + .Select(json => (false, JobRefreshBy.BlockTemplateStream, json)) + .Publish() + .RefCount()); + } + + // periodically update block-template + var pollingInterval = poolConfig.BlockRefreshInterval > 0 ? poolConfig.BlockRefreshInterval : 1000; + + triggers.Add(Observable.Timer(TimeSpan.FromMilliseconds(pollingInterval)) + .TakeUntil(pollTimerRestart) + .Select(_ => (false, JobRefreshBy.Poll, (string) null)) + .Repeat()); + + // get initial blocktemplate + triggers.Add(Observable.Interval(TimeSpan.FromMilliseconds(1000)) + .Select(_ => (false, JobRefreshBy.Initial, (string) null)) + .TakeWhile(_ => !hasInitialBlockTemplate)); + + Jobs = Observable.Merge(triggers) + .Select(x => Observable.FromAsync(() => UpdateJob(x.Force, x.Via, x.Data))) + .Concat() + .Where(x => x.IsNew || x.Force) + .Do(x => + { + if(x.IsNew) + hasInitialBlockTemplate = true; + }) + .Select(x => GetJobParamsForStratum(x.IsNew)) + .Publish() + .RefCount(); + } + + private async Task<(bool IsNew, bool Force)> UpdateJob(bool forceUpdate, string via = null, string json = null) + { + logger.LogInvoke(); + + try + { + var blockTemplate = string.IsNullOrEmpty(json) ? + await GetBlockTemplateAsync() : + GetBlockTemplateFromJson(json); + + var job = currentJob; + + var isNew = job == null || + (blockTemplate != null && + (job.BlockTemplate?.Msg != blockTemplate.Msg || + blockTemplate.Height > job.BlockTemplate.Height)); + + if(isNew) + messageBus.NotifyChainHeight(poolConfig.Id, blockTemplate.Height, poolConfig.Template); + + if(isNew || forceUpdate) + { + job = new ErgoJob(); + + job.Init(blockTemplate, blockVersion, extraNonceSize, NextJobId()); + + lock(jobLock) + { + validJobs.Insert(0, job); + + // trim active jobs + while(validJobs.Count > maxActiveJobs) + validJobs.RemoveAt(validJobs.Count - 1); + } + + if(isNew) + { + if(via != null) + logger.Info(() => $"Detected new block {job.Height} [{via}]"); + else + logger.Info(() => $"Detected new block {job.Height}"); + + // update stats + BlockchainStats.LastNetworkBlockTime = clock.Now; + BlockchainStats.BlockHeight = job.Height; + BlockchainStats.NetworkDifficulty = job.Difficulty; + + var blockTimeAvg = 120; + BlockchainStats.NetworkHashrate = BlockchainStats.NetworkDifficulty / blockTimeAvg; + } + + else + { + if(via != null) + logger.Debug(() => $"Template update {job.Height} [{via}]"); + else + logger.Debug(() => $"Template update {job.Height}"); + } + + currentJob = job; + } + + return (isNew, forceUpdate); + } + + catch(ApiException ex) + { + logger.Error(() => $"Error during {nameof(UpdateJob)}: {ex.Result.Detail ?? ex.Result.Reason}"); + } + + catch(Exception ex) + { + logger.Error(ex, () => $"Error during {nameof(UpdateJob)}"); + } + + return (false, forceUpdate); + } + + private async Task GetBlockTemplateAsync() + { + logger.LogInvoke(); + + var work = await ergoClient.MiningRequestBlockCandidateAsync(CancellationToken.None); + + return work; + } + + private WorkMessage GetBlockTemplateFromJson(string json) + { + logger.LogInvoke(); + + return JsonConvert.DeserializeObject(json); + } + + private async Task ShowDaemonSyncProgressAsync() + { + var info = await Guard(() => ergoClient.GetNodeInfoAsync(), + ex => logger.Debug(ex)); + + if(info?.FullHeight.HasValue == true && info.HeadersHeight.HasValue) + { + var percent = (double) info.FullHeight.Value / info.HeadersHeight.Value * 100; + + logger.Info(() => $"Daemon has downloaded {percent:0.00}% of blockchain from {info.PeersCount} peers"); + } + + else + logger.Info(() => $"Daemon is downloading headers ..."); + } + + private void ConfigureRewards() + { + // Donation to MiningCore development + if(network == "mainnet" && + DevDonation.Addresses.TryGetValue(poolConfig.Template.Symbol, out var address)) + { + poolConfig.RewardRecipients = poolConfig.RewardRecipients.Concat(new[] + { + new RewardRecipient + { + Address = address, + Percentage = DevDonation.Percent, + Type = "dev" + } + }).ToArray(); + } + } + + private async Task SubmitBlockAsync(Share share, ErgoJob job, string nonce) + { + try + { + await ergoClient.MiningSubmitSolutionAsync(new PowSolutions + { + N = nonce, + }); + + return true; + } + + catch(ApiException ex) + { + logger.Warn(() => $"Block {share.BlockHeight} submission failed with: {ex.Result.Detail ?? ex.Result.Reason ?? ex.Message}"); + messageBus.SendMessage(new AdminNotification("Block submission failed", $"Pool {poolConfig.Id} {(!string.IsNullOrEmpty(share.Source) ? $"[{share.Source.ToUpper()}] " : string.Empty)}failed to submit block {share.BlockHeight}: {ex.Result.Detail ?? ex.Result.Reason ?? ex.Message}")); + } + + catch(Exception ex) + { + logger.Warn(() => $"Block {share.BlockHeight} submission failed with: {ex.Message}"); + messageBus.SendMessage(new AdminNotification("Block submission failed", $"Pool {poolConfig.Id} {(!string.IsNullOrEmpty(share.Source) ? $"[{share.Source.ToUpper()}] " : string.Empty)}failed to submit block {share.BlockHeight}: {ex.Message}")); + } + + return false; + } + + #region API-Surface + + public IObservable Jobs { get; private set; } + public BlockchainStats BlockchainStats { get; } = new(); + public string Network => network; + + public ErgoCoinTemplate Coin => coin; + + public object[] GetSubscriberData(StratumConnection worker) + { + Contract.RequiresNonNull(worker, nameof(worker)); + + var context = worker.ContextAs(); + + // assign unique ExtraNonce1 to worker (miner) + context.ExtraNonce1 = extraNonceProvider.Next(); + + // setup response data + var responseData = new object[] + { + context.ExtraNonce1, + extraNonceSize, + }; + + return responseData; + } + + public async ValueTask SubmitShareAsync(StratumConnection worker, object submission, double stratumDifficultyBase, CancellationToken ct) + { + Contract.RequiresNonNull(worker, nameof(worker)); + Contract.RequiresNonNull(submission, nameof(submission)); + + logger.LogInvoke(new[] { worker.ConnectionId }); + + if(submission is not object[] submitParams) + throw new StratumException(StratumError.Other, "invalid params"); + + var context = worker.ContextAs(); + + // extract params + var workerValue = (submitParams[0] as string)?.Trim(); + var jobId = submitParams[1] as string; + var extraNonce2 = submitParams[2] as string; + var nTime = submitParams[3] as string; + var nonce = submitParams[4] as string; + + if(string.IsNullOrEmpty(workerValue)) + throw new StratumException(StratumError.Other, "missing or invalid workername"); + + ErgoJob job; + + lock(jobLock) + { + job = validJobs.FirstOrDefault(x => x.JobId == jobId); + } + + if(job == null) + throw new StratumException(StratumError.JobNotFound, "job not found"); + + // validate & process + var share = job.ProcessShare(worker, extraNonce2, nTime, nonce); + + // enrich share with common data + share.PoolId = poolConfig.Id; + share.IpAddress = worker.RemoteEndpoint.Address.ToString(); + share.Miner = context.Miner; + share.Worker = context.Worker; + share.UserAgent = context.UserAgent; + share.Source = clusterConfig.ClusterName; + share.Created = clock.Now; + + // if block candidate, submit & check if accepted by network + if(share.IsBlockCandidate) + { + logger.Info(() => $"Submitting block {share.BlockHeight} [{share.BlockHash}]"); + + var acceptResponse = await SubmitBlockAsync(share, job, nonce); + + // is it still a block candidate? + share.IsBlockCandidate = acceptResponse; + + if(share.IsBlockCandidate) + { + logger.Info(() => $"Daemon accepted block {share.BlockHeight} [{share.BlockHash}] submitted by {context.Miner}"); + + OnBlockFound(); + + // persist the nonce to make block unlocking a bit more reliable + share.TransactionConfirmationData = nonce; + } + + else + { + // clear fields that no longer apply + share.TransactionConfirmationData = null; + } + } + + return share; + } + + public async Task ValidateAddress(string address, CancellationToken ct) + { + if(string.IsNullOrEmpty(address)) + return false; + + var validity = await Guard(() => ergoClient.CheckAddressValidityAsync(address, ct), + ex => logger.Debug(ex)); + + return validity?.IsValid == true; + } + + #endregion // API-Surface + + #region Overrides + + protected override async Task PostStartInitAsync(CancellationToken ct) + { + // validate pool address + if(string.IsNullOrEmpty(poolConfig.Address)) + logger.ThrowLogPoolStartupException($"Pool address is not configured"); + + var validity = await Guard(() => ergoClient.CheckAddressValidityAsync(poolConfig.Address, ct), + ex=> logger.ThrowLogPoolStartupException($"Error validating pool address: {ex}")); + + if(!validity.IsValid) + logger.ThrowLogPoolStartupException($"Daemon reports pool address {poolConfig.Address} as invalid: {validity.Error}"); + + var info = await Guard(() => ergoClient.GetNodeInfoAsync(ct), + ex=> logger.ThrowLogPoolStartupException($"Daemon reports: {ex.Message}")); + + blockVersion = info.Parameters.BlockVersion; + + // chain detection + var m = ErgoConstants.RegexChain.Match(info.Name); + if(!m.Success) + logger.ThrowLogPoolStartupException($"Unable to identify network type ({info.Name}"); + + network = m.Groups[1].Value.ToLower(); + + // Payment-processing setup + if(clusterConfig.PaymentProcessing?.Enabled == true && poolConfig.PaymentProcessing?.Enabled == true) + { + // check configured address belongs to wallet + var walletAddresses = await ergoClient.WalletAddressesAsync(ct); + + if(!walletAddresses.Contains(poolConfig.Address)) + logger.ThrowLogPoolStartupException($"Pool address {poolConfig.Address} is not controlled by wallet"); + + ConfigureRewards(); + } + + // update stats + BlockchainStats.NetworkType = network; + BlockchainStats.RewardType = "POW"; + + SetupJobUpdates(); + } + + public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfig) + { + coin = poolConfig.Template.As(); + + extraPoolConfig = poolConfig.Extra.SafeExtensionDataAs(); + + if(extraPoolConfig?.MaxActiveJobs.HasValue == true) + maxActiveJobs = extraPoolConfig.MaxActiveJobs.Value; + + base.Configure(poolConfig, clusterConfig); + } + + protected override void ConfigureDaemons() + { + ergoClient = ErgoClientFactory.CreateClient(poolConfig, clusterConfig, logger); + } + + protected override async Task AreDaemonsHealthyAsync(CancellationToken ct) + { + var info = await Guard(() => ergoClient.GetNodeInfoAsync(ct), + ex=> logger.ThrowLogPoolStartupException($"Daemon reports: {ex.Message}")); + + if(info?.IsMining != true) + logger.ThrowLogPoolStartupException($"Mining is disabled in Ergo Daemon"); + + return true; + } + + protected override async Task AreDaemonsConnectedAsync(CancellationToken ct) + { + var info = await Guard(() => ergoClient.GetNodeInfoAsync(ct), + ex=> logger.Debug(ex)); + + return info?.PeersCount > 0; + } + + protected override async Task EnsureDaemonsSynchedAsync(CancellationToken ct) + { + var syncPendingNotificationShown = false; + + while(true) + { + var info = await Guard(() => ergoClient.GetNodeInfoAsync(ct), + ex=> logger.Debug(ex)); + + var isSynched = info?.FullHeight.HasValue == true && info?.HeadersHeight.HasValue == true && + info.FullHeight.Value >= info.HeadersHeight.Value; + + if(isSynched) + { + logger.Info(() => "Daemon is synced with blockchain"); + break; + } + + if(!syncPendingNotificationShown) + { + logger.Info(() => "Daemon is still syncing with network. Manager will be started once synced"); + syncPendingNotificationShown = true; + } + + await ShowDaemonSyncProgressAsync(); + + // delay retry by 5s + await Task.Delay(5000, ct); + } + } + + private object[] GetJobParamsForStratum(bool isNew) + { + var job = currentJob; + return job?.GetJobParams(isNew); + } + + #endregion // Overrides + } +} diff --git a/src/Miningcore/Blockchain/Ergo/ErgoPayoutHandler.cs b/src/Miningcore/Blockchain/Ergo/ErgoPayoutHandler.cs new file mode 100644 index 000000000..1ba622169 --- /dev/null +++ b/src/Miningcore/Blockchain/Ergo/ErgoPayoutHandler.cs @@ -0,0 +1,399 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Autofac; +using AutoMapper; +using Miningcore.Blockchain.Ergo.Configuration; +using Miningcore.Configuration; +using Miningcore.Extensions; +using Miningcore.Messaging; +using Miningcore.Mining; +using Miningcore.Payments; +using Miningcore.Persistence; +using Miningcore.Persistence.Model; +using Miningcore.Persistence.Repositories; +using Miningcore.Time; +using Miningcore.Util; +using Block = Miningcore.Persistence.Model.Block; +using Contract = Miningcore.Contracts.Contract; +using static Miningcore.Util.ActionUtils; + +namespace Miningcore.Blockchain.Ergo +{ + [CoinFamily(CoinFamily.Ergo)] + public class ErgoPayoutHandler : PayoutHandlerBase, + IPayoutHandler + { + public ErgoPayoutHandler( + IComponentContext ctx, + IConnectionFactory cf, + IMapper mapper, + IShareRepository shareRepo, + IBlockRepository blockRepo, + IBalanceRepository balanceRepo, + IPaymentRepository paymentRepo, + IMasterClock clock, + IMessageBus messageBus) : + base(cf, mapper, shareRepo, blockRepo, balanceRepo, paymentRepo, clock, messageBus) + { + Contract.RequiresNonNull(ctx, nameof(ctx)); + Contract.RequiresNonNull(balanceRepo, nameof(balanceRepo)); + Contract.RequiresNonNull(paymentRepo, nameof(paymentRepo)); + + this.ctx = ctx; + } + + protected readonly IComponentContext ctx; + protected ErgoClient ergoClient; + private string network; + private ErgoPaymentProcessingConfigExtra extraPoolPaymentProcessingConfig; + + protected override string LogCategory => "Ergo Payout Handler"; + + private class PaymentException : Exception + { + public PaymentException(string msg) : base(msg) + { + } + } + + private void ReportAndRethrowApiError(string action, Exception ex, bool rethrow = true) + { + var error = ex.Message; + + if(ex is ApiException apiException) + error = apiException.Result.Detail ?? apiException.Result.Reason; + + logger.Warn(() => $"{action}: {error}"); + + if(rethrow) + throw ex; + } + + private async Task UnlockWallet(CancellationToken ct) + { + logger.Info(() => $"[{LogCategory}] Unlocking wallet"); + + var walletPassword = extraPoolPaymentProcessingConfig.WalletPassword ?? string.Empty; + + await Guard(() => ergoClient.WalletUnlockAsync(new Body4 {Pass = walletPassword}, ct), ex => + { + if (ex is ApiException apiException) + { + var error = apiException.Result.Detail; + + if (error != null && !error.ToLower().Contains("already unlocked")) + throw new PaymentException($"Failed to unlock wallet: {error}"); + } + + else + throw ex; + }); + + logger.Info(() => $"[{LogCategory}] Wallet unlocked"); + } + + private async Task LockWallet(CancellationToken ct) + { + logger.Info(() => $"[{LogCategory}] Locking wallet"); + + await Guard(() => ergoClient.WalletLockAsync(ct), + ex => ReportAndRethrowApiError("Failed to lock wallet", ex)); + + logger.Info(() => $"[{LogCategory}] Wallet locked"); + } + + #region IPayoutHandler + + public virtual async Task ConfigureAsync(ClusterConfig clusterConfig, PoolConfig poolConfig, CancellationToken ct) + { + Contract.RequiresNonNull(poolConfig, nameof(poolConfig)); + + logger = LogUtil.GetPoolScopedLogger(typeof(ErgoPayoutHandler), poolConfig); + + this.poolConfig = poolConfig; + this.clusterConfig = clusterConfig; + + extraPoolPaymentProcessingConfig = poolConfig.PaymentProcessing.Extra.SafeExtensionDataAs(); + + ergoClient = ErgoClientFactory.CreateClient(poolConfig, clusterConfig, null); + + // detect chain + var info = await ergoClient.GetNodeInfoAsync(ct); + network = ErgoConstants.RegexChain.Match(info.Name).Groups[1].Value.ToLower(); + } + + public virtual async Task ClassifyBlocksAsync(IMiningPool pool, Block[] blocks, CancellationToken ct) + { + Contract.RequiresNonNull(poolConfig, nameof(poolConfig)); + Contract.RequiresNonNull(blocks, nameof(blocks)); + + if(blocks.Length == 0) + return blocks; + + var coin = poolConfig.Template.As(); + var pageSize = 100; + var pageCount = (int) Math.Ceiling(blocks.Length / (double) pageSize); + var result = new List(); + var minConfirmations = extraPoolPaymentProcessingConfig?.MinimumConfirmations ?? (network == "mainnet" ? 720 : 72); + var minerRewardsPubKey = await ergoClient.MiningReadMinerRewardPubkeyAsync(ct); + var minerRewardsAddress = await ergoClient.MiningReadMinerRewardAddressAsync(ct); + + for(var i = 0; i < pageCount; i++) + { + // get a page full of blocks + var page = blocks + .Skip(i * pageSize) + .Take(pageSize) + .ToArray(); + + // fetch header ids for blocks in page + var headerBatch = page.Select(block => ergoClient.GetFullBlockAtAsync((int) block.BlockHeight, ct)).ToArray(); + + await Guard(()=> Task.WhenAll(headerBatch), + ex=> logger.Debug(ex)); + + for(var j = 0; j < page.Length; j++) + { + var block = page[j]; + var headerTask = headerBatch[j]; + + if(!headerTask.IsCompletedSuccessfully) + { + if(headerTask.IsFaulted) + logger.Warn(()=> $"Failed to fetch block {block.BlockHeight}: {headerTask.Exception?.InnerException?.Message ?? headerTask.Exception?.Message}"); + else + logger.Warn(()=> $"Failed to fetch block {block.BlockHeight}: {headerTask.Status.ToString().ToLower()}"); + + continue; + } + + var headerIds = headerTask.Result; + + // fetch blocks + var blockBatch = headerIds.Select(x=> ergoClient.GetFullBlockByIdAsync(x, ct)).ToArray(); + + await Guard(()=> Task.WhenAll(blockBatch), + ex=> logger.Debug(ex)); + + var blockHandled = false; + var pkMismatchCount = 0; + var nonceMismatchCount = 0; + var coinbaseNonWalletTxCount = 0; + + foreach (var blockTask in blockBatch) + { + if(blockHandled) + break; + + if(!blockTask.IsCompletedSuccessfully) + continue; + + var fullBlock = blockTask.Result; + + // only consider blocks with pow-solution pk matching ours + if(fullBlock.Header.PowSolutions.Pk != minerRewardsPubKey.RewardPubKey) + { + pkMismatchCount++; + continue; + } + + // only consider blocks with pow-solution nonce matching what we have on file + if(fullBlock.Header.PowSolutions.N != block.TransactionConfirmationData) + { + nonceMismatchCount++; + continue; + } + + var coinbaseWalletTxFound = false; + + // reset block reward + block.Reward = 0; + + foreach(var blockTx in fullBlock.BlockTransactions.Transactions) + { + var walletTx = await Guard(()=> ergoClient.WalletGetTransactionAsync(blockTx.Id, ct)); + var coinbaseOutput = walletTx?.Outputs?.FirstOrDefault(x => x.Address == minerRewardsAddress.RewardAddress); + + if(coinbaseOutput != null) + { + coinbaseWalletTxFound = true; + + block.ConfirmationProgress = Math.Min(1.0d, (double) walletTx.NumConfirmations / minConfirmations); + block.Reward += coinbaseOutput.Value / ErgoConstants.SmallestUnit; + block.Hash = fullBlock.Header.Id; + + if(walletTx.NumConfirmations >= minConfirmations) + { + // matured and spendable coinbase transaction + block.Status = BlockStatus.Confirmed; + block.ConfirmationProgress = 1; + } + } + } + + blockHandled = coinbaseWalletTxFound; + + if(blockHandled) + { + result.Add(block); + + if(block.Status == BlockStatus.Confirmed) + { + logger.Info(() => $"[{LogCategory}] Unlocked block {block.BlockHeight} worth {FormatAmount(block.Reward)}"); + + messageBus.NotifyBlockUnlocked(poolConfig.Id, block, coin); + } + + else + messageBus.NotifyBlockConfirmationProgress(poolConfig.Id, block, coin); + } + + else + coinbaseNonWalletTxCount++; + } + + if(!blockHandled) + { + string orphanReason = null; + + if(pkMismatchCount == blockBatch.Length) + orphanReason = "pk mismatch"; + else if(nonceMismatchCount == blockBatch.Length) + orphanReason = "nonce mismatch"; + else if(coinbaseNonWalletTxCount == blockBatch.Length) + orphanReason = "no related coinbase tx found in wallet"; + + if(!string.IsNullOrEmpty(orphanReason)) + { + block.Status = BlockStatus.Orphaned; + block.Reward = 0; + result.Add(block); + + logger.Info(() => $"[{LogCategory}] Block {block.BlockHeight} classified as orphaned due to {orphanReason}"); + + messageBus.NotifyBlockUnlocked(poolConfig.Id, block, coin); + } + } + } + } + + return result.ToArray(); + } + + public virtual Task CalculateBlockEffortAsync(IMiningPool pool, Block block, double accumulatedBlockShareDiff, CancellationToken ct) + { + block.Effort = accumulatedBlockShareDiff * ErgoConstants.ShareMultiplier / block.NetworkDifficulty; + + return Task.FromResult(true); + } + + public override double AdjustShareDifficulty(double difficulty) + { + return difficulty * ErgoConstants.ShareMultiplier; + } + + public virtual async Task PayoutAsync(IMiningPool pool, Balance[] balances, CancellationToken ct) + { + Contract.RequiresNonNull(balances, nameof(balances)); + + // build args + var amounts = balances + .Where(x => x.Amount > 0) + .ToDictionary(x => x.Address, x => Math.Round(x.Amount, 4)); + + if(amounts.Count == 0) + return; + + var balancesTotal = amounts.Sum(x => x.Value); + + try + { + logger.Info(() => $"[{LogCategory}] Paying {FormatAmount(balances.Sum(x => x.Amount))} to {balances.Length} addresses"); + + // get wallet status + var status = await ergoClient.GetWalletStatusAsync(ct); + + if(!status.IsInitialized) + throw new PaymentException($"Wallet is not initialized"); + + if(!status.IsUnlocked) + await UnlockWallet(ct); + + // get balance + var walletBalances = await ergoClient.WalletBalancesAsync(ct); + var walletTotal = walletBalances.Balance / ErgoConstants.SmallestUnit; + + logger.Info(() => $"[{LogCategory}] Current wallet balance is {FormatAmount(walletTotal)}"); + + // bail if balance does not satisfy payments + if(walletTotal < balancesTotal) + { + logger.Warn(() => $"[{LogCategory}] Wallet balance currently short of {FormatAmount(balancesTotal - walletTotal)}. Will try again."); + return; + } + + // validate addresses + logger.Info("Validating addresses ..."); + + foreach(var pair in amounts) + { + var validity = await Guard(() => ergoClient.CheckAddressValidityAsync(pair.Key, ct)); + + if(validity == null || !validity.IsValid) + logger.Warn(()=> $"Address {pair.Key} is not valid!"); + } + + // Create request batch + var requests = amounts.Select(x => new PaymentRequest + { + Address = x.Key, + Value = (long) (x.Value * ErgoConstants.SmallestUnit), + }).ToArray(); + + var txId = await Guard(()=> ergoClient.WalletPaymentTransactionGenerateAndSendAsync(requests, ct), ex => + { + if(ex is ApiException apiException) + { + var error = apiException.Result.Detail ?? apiException.Result.Reason; + + if(error.Contains("reason:")) + error = error.Substring(error.IndexOf("reason:")); + + throw new PaymentException($"Payment transaction failed: {error}"); + } + + else + throw ex; + }); + + if(string.IsNullOrEmpty(txId)) + throw new PaymentException("Payment transaction failed to return a transaction id"); + + // payment successful + logger.Info(() => $"[{LogCategory}] Payment transaction id: {txId}"); + + await PersistPaymentsAsync(balances, txId); + + NotifyPayoutSuccess(poolConfig.Id, balances, new[] {txId}, null); + } + + catch(PaymentException ex) + { + logger.Error(() => $"[{LogCategory}] {ex.Message}"); + + NotifyPayoutFailure(poolConfig.Id, balances, ex.Message, null); + } + + finally + { + await LockWallet(ct); + } + } + + #endregion // IPayoutHandler + } +} diff --git a/src/Miningcore/Blockchain/Ergo/ErgoPool.cs b/src/Miningcore/Blockchain/Ergo/ErgoPool.cs new file mode 100644 index 000000000..ee66db550 --- /dev/null +++ b/src/Miningcore/Blockchain/Ergo/ErgoPool.cs @@ -0,0 +1,396 @@ +using System; +using System.Reactive; +using System.Reactive.Linq; +using System.Reactive.Threading.Tasks; +using System.Threading; +using System.Threading.Tasks; +using System.Linq; +using System.Numerics; +using Autofac; +using AutoMapper; +using Miningcore.Blockchain.Bitcoin; +using Miningcore.Blockchain.Ergo.Configuration; +using Miningcore.Configuration; +using Miningcore.Extensions; +using Miningcore.JsonRpc; +using Miningcore.Messaging; +using Miningcore.Mining; +using Miningcore.Nicehash; +using Miningcore.Notifications.Messages; +using Miningcore.Persistence; +using Miningcore.Persistence.Repositories; +using Miningcore.Stratum; +using Miningcore.Time; +using Miningcore.Util; +using Newtonsoft.Json; +using static Miningcore.Util.ActionUtils; + +namespace Miningcore.Blockchain.Ergo +{ + [CoinFamily(CoinFamily.Ergo)] + public class ErgoPool : PoolBase + { + public ErgoPool(IComponentContext ctx, + JsonSerializerSettings serializerSettings, + IConnectionFactory cf, + IStatsRepository statsRepo, + IMapper mapper, + IMasterClock clock, + IMessageBus messageBus, + NicehashService nicehashService) : + base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus, nicehashService) + { + } + + protected object[] currentJobParams; + protected ErgoJobManager manager; + private ErgoPoolConfigExtra extraPoolConfig; + private ErgoCoinTemplate coin; + + protected virtual async Task OnSubscribeAsync(StratumConnection connection, Timestamped tsRequest) + { + var request = tsRequest.Value; + + if(request.Id == null) + throw new StratumException(StratumError.MinusOne, "missing request id"); + + var context = connection.ContextAs(); + var requestParams = request.ParamsAs(); + + var data = new object[] + { + new object[] + { + new object[] { BitcoinStratumMethods.SetDifficulty, connection.ConnectionId }, + new object[] { BitcoinStratumMethods.MiningNotify, connection.ConnectionId } + } + } + .Concat(manager.GetSubscriberData(connection)) + .ToArray(); + + await connection.RespondAsync(data, request.Id); + + // setup worker context + context.IsSubscribed = true; + context.UserAgent = requestParams?.Length > 0 ? requestParams[0].Trim() : null; + } + + protected virtual async Task OnAuthorizeAsync(StratumConnection connection, Timestamped tsRequest, CancellationToken ct) + { + var request = tsRequest.Value; + + if(request.Id == null) + throw new StratumException(StratumError.MinusOne, "missing request id"); + + var context = connection.ContextAs(); + var requestParams = request.ParamsAs(); + var workerValue = requestParams?.Length > 0 ? requestParams[0] : null; + var password = requestParams?.Length > 1 ? requestParams[1] : null; + var passParts = password?.Split(PasswordControlVarsSeparator); + + // extract worker/miner + var split = workerValue?.Split('.'); + var minerName = split?.FirstOrDefault()?.Trim(); + var workerName = split?.Skip(1).FirstOrDefault()?.Trim() ?? string.Empty; + + // assumes that minerName is an address + context.IsAuthorized = await manager.ValidateAddress(minerName, ct); + context.Miner = minerName; + context.Worker = workerName; + + if(context.IsAuthorized) + { + // respond + await connection.RespondAsync(context.IsAuthorized, request.Id); + + // log association + logger.Info(() => $"[{connection.ConnectionId}] Authorized worker {workerValue}"); + + // extract control vars from password + var staticDiff = GetStaticDiffFromPassparts(passParts); + + // Nicehash support + var nicehashDiff = await GetNicehashStaticMinDiff(connection, context.UserAgent, coin.Name, coin.GetAlgorithmName()); + + if(nicehashDiff.HasValue) + { + if(!staticDiff.HasValue || nicehashDiff > staticDiff) + { + logger.Info(() => $"[{connection.ConnectionId}] Nicehash detected. Using API supplied difficulty of {nicehashDiff.Value}"); + + staticDiff = nicehashDiff; + } + + else + logger.Info(() => $"[{connection.ConnectionId}] Nicehash detected. Using miner supplied difficulty of {staticDiff.Value}"); + } + + // Static diff + if(staticDiff.HasValue && + (context.VarDiff != null && staticDiff.Value >= context.VarDiff.Config.MinDiff || + context.VarDiff == null && staticDiff.Value > context.Difficulty)) + { + context.VarDiff = null; // disable vardiff + context.SetDifficulty(staticDiff.Value); + + logger.Info(() => $"[{connection.ConnectionId}] Setting static difficulty of {staticDiff.Value}"); + } + + // send intial update + await SendJob(connection, context, currentJobParams); + } + + else + { + await connection.RespondErrorAsync(StratumError.UnauthorizedWorker, "Authorization failed", request.Id, context.IsAuthorized); + + // issue short-time ban if unauthorized to prevent DDos on daemon (validateaddress RPC) + logger.Info(() => $"[{connection.ConnectionId}] Banning unauthorized worker {minerName} for {loginFailureBanTimeout.TotalSeconds} sec"); + + banManager.Ban(connection.RemoteEndpoint.Address, loginFailureBanTimeout); + + CloseConnection(connection); + } + } + + protected virtual async Task OnSubmitAsync(StratumConnection connection, Timestamped tsRequest, CancellationToken ct) + { + var request = tsRequest.Value; + var context = connection.ContextAs(); + + try + { + if(request.Id == null) + throw new StratumException(StratumError.MinusOne, "missing request id"); + + // check age of submission (aged submissions are usually caused by high server load) + var requestAge = clock.Now - tsRequest.Timestamp.UtcDateTime; + + if(requestAge > maxShareAge) + { + logger.Warn(() => $"[{connection.ConnectionId}] Dropping stale share submission request (server overloaded?)"); + return; + } + + // check worker state + context.LastActivity = clock.Now; + + // validate worker + if(!context.IsAuthorized) + throw new StratumException(StratumError.UnauthorizedWorker, "unauthorized worker"); + else if(!context.IsSubscribed) + throw new StratumException(StratumError.NotSubscribed, "not subscribed"); + + // submit + var requestParams = request.ParamsAs(); + var poolEndpoint = poolConfig.Ports[connection.LocalEndpoint.Port]; + + var share = await manager.SubmitShareAsync(connection, requestParams, poolEndpoint.Difficulty, ct); + + await connection.RespondAsync(true, request.Id); + + // publish + messageBus.SendMessage(new StratumShare(connection, share)); + + // telemetry + PublishTelemetry(TelemetryCategory.Share, clock.Now - tsRequest.Timestamp.UtcDateTime, true); + + logger.Info(() => $"[{connection.ConnectionId}] Share accepted: D={Math.Round(share.Difficulty * ErgoConstants.ShareMultiplier, 3)}"); + + // update pool stats + if(share.IsBlockCandidate) + poolStats.LastPoolBlockTime = clock.Now; + + // update client stats + context.Stats.ValidShares++; + await UpdateVarDiffAsync(connection); + } + + catch(StratumException ex) + { + // telemetry + PublishTelemetry(TelemetryCategory.Share, clock.Now - tsRequest.Timestamp.UtcDateTime, false); + + // update client stats + context.Stats.InvalidShares++; + logger.Info(() => $"[{connection.ConnectionId}] Share rejected: {ex.Message} [{context.UserAgent}]"); + + // banning + ConsiderBan(connection, context, poolConfig.Banning); + + throw; + } + } + + protected virtual Task OnNewJobAsync(object[] jobParams) + { + currentJobParams = jobParams; + + logger.Info(() => "Broadcasting job"); + + return Guard(()=> Task.WhenAll(ForEachConnection(async connection => + { + if(!connection.IsAlive) + return; + + var context = connection.ContextAs(); + + if(!context.IsSubscribed || !context.IsAuthorized || CloseIfDead(connection, context)) + return; + + // varDiff: if the client has a pending difficulty change, apply it now + if(context.ApplyPendingDifficulty()) + await SendJob(connection, context, currentJobParams); + })), ex=> logger.Debug(() => $"{nameof(OnNewJobAsync)}: {ex.Message}")); + } + + private async Task SendJob(StratumConnection connection, ErgoWorkerContext context, object[] jobParams) + { + // clone job params + var jobParamsActual = new object[jobParams.Length]; + + for(var i = 0; i < jobParamsActual.Length; i++) + jobParamsActual[i] = jobParams[i]; + + var target = new BigRational(BitcoinConstants.Diff1 * (BigInteger) (1 / context.Difficulty * 0x10000), 0x10000).GetWholePart(); + jobParamsActual[6] = target.ToString(); + + // send static diff of 1 since actual diff gets pre-multiplied to target + await connection.NotifyAsync(BitcoinStratumMethods.SetDifficulty, new object[] { 1 }); + + // send target + await connection.NotifyAsync(BitcoinStratumMethods.MiningNotify, jobParamsActual); + } + + public override double HashrateFromShares(double shares, double interval) + { + var multiplier = BitcoinConstants.Pow2x32 * ErgoConstants.ShareMultiplier; + var result = shares * multiplier / interval; + + // add flat pool side hashrate bonus to account for miner dataset generation + result *= 1.15; + + return result; + } + + public override double ShareMultiplier => ErgoConstants.ShareMultiplier; + + #region Overrides + + public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfig) + { + coin = poolConfig.Template.As(); + extraPoolConfig = poolConfig.Extra.SafeExtensionDataAs(); + + base.Configure(poolConfig, clusterConfig); + } + + protected override async Task SetupJobManager(CancellationToken ct) + { + var extraNonce1Size = extraPoolConfig?.ExtraNonce1Size ?? 2; + + manager = ctx.Resolve( + new TypedParameter(typeof(IExtraNonceProvider), new ErgoExtraNonceProvider(poolConfig.Id, extraNonce1Size, clusterConfig.InstanceId))); + + manager.Configure(poolConfig, clusterConfig); + + await manager.StartAsync(ct); + + if(poolConfig.EnableInternalStratum == true) + { + disposables.Add(manager.Jobs + .Select(job => Observable.FromAsync(() => + Guard(()=> OnNewJobAsync(job), + ex=> logger.Debug(() => $"{nameof(OnNewJobAsync)}: {ex.Message}")))) + .Concat() + .Subscribe(_ => { }, ex => + { + logger.Debug(ex, nameof(OnNewJobAsync)); + })); + + // start with initial blocktemplate + await manager.Jobs.Take(1).ToTask(ct); + } + + else + { + // keep updating NetworkStats + disposables.Add(manager.Jobs.Subscribe()); + } + } + + protected override async Task InitStatsAsync() + { + await base.InitStatsAsync(); + + blockchainStats = manager.BlockchainStats; + } + + protected override WorkerContextBase CreateWorkerContext() + { + return new ErgoWorkerContext(); + } + + protected override async Task OnRequestAsync(StratumConnection connection, + Timestamped tsRequest, CancellationToken ct) + { + var request = tsRequest.Value; + + try + { + switch(request.Method) + { + case BitcoinStratumMethods.Subscribe: + await OnSubscribeAsync(connection, tsRequest); + break; + + case BitcoinStratumMethods.Authorize: + await OnAuthorizeAsync(connection, tsRequest, ct); + break; + + case BitcoinStratumMethods.SubmitShare: + await OnSubmitAsync(connection, tsRequest, ct); + break; + + default: + logger.Debug(() => $"[{connection.ConnectionId}] Unsupported RPC request: {JsonConvert.SerializeObject(request, serializerSettings)}"); + + await connection.RespondErrorAsync(StratumError.Other, $"Unsupported request {request.Method}", request.Id); + break; + } + } + + catch(StratumException ex) + { + await connection.RespondErrorAsync(ex.Code, ex.Message, request.Id, false); + } + } + + protected override async Task GetNicehashStaticMinDiff(StratumConnection connection, string userAgent, string coinName, string algoName) + { + var result= await base.GetNicehashStaticMinDiff(connection, userAgent, coinName, algoName); + + // adjust value to fit with our target value calculation + if(result.HasValue) + result = result.Value / uint.MaxValue; + + return result; + } + + protected override async Task OnVarDiffUpdateAsync(StratumConnection connection, double newDiff) + { + var context = connection.ContextAs(); + + context.EnqueueNewDifficulty(newDiff); + + if(context.HasPendingDifficulty) + { + context.ApplyPendingDifficulty(); + + await SendJob(connection, context, currentJobParams); + } + } + + #endregion // Overrides + } +} diff --git a/src/Miningcore/Blockchain/Ergo/ErgoWorkerContext.cs b/src/Miningcore/Blockchain/Ergo/ErgoWorkerContext.cs new file mode 100644 index 000000000..179881c70 --- /dev/null +++ b/src/Miningcore/Blockchain/Ergo/ErgoWorkerContext.cs @@ -0,0 +1,23 @@ +using System.Numerics; +using Miningcore.Mining; + +namespace Miningcore.Blockchain.Ergo +{ + public class ErgoWorkerContext : WorkerContextBase + { + /// + /// Usually a wallet address + /// + public string Miner { get; set; } + + /// + /// Arbitrary worker identififer for miners using multiple rigs + /// + public string Worker { get; set; } + + /// + /// Unique value assigned per worker + /// + public string ExtraNonce1 { get; set; } + } +} diff --git a/src/Miningcore/Blockchain/Ergo/RPC/ErgoClient.cs b/src/Miningcore/Blockchain/Ergo/RPC/ErgoClient.cs new file mode 100644 index 000000000..464dc862c --- /dev/null +++ b/src/Miningcore/Blockchain/Ergo/RPC/ErgoClient.cs @@ -0,0 +1,10172 @@ +using System.Collections.Generic; +using System.Numerics; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +//---------------------- +// +// Generated using the NSwag toolchain v13.11.3.0 (NJsonSchema v10.4.4.0 (Newtonsoft.Json v12.0.0.0)) (http://NSwag.org) +// +//---------------------- + +#nullable enable + +#pragma warning disable 108 // Disable "CS0108 '{derivedDto}.ToJson()' hides inherited member '{dtoBase}.ToJson()'. Use the new keyword if hiding was intended." +#pragma warning disable 114 // Disable "CS0114 '{derivedDto}.RaisePropertyChanged(String)' hides inherited member 'dtoBase.RaisePropertyChanged(String)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword." +#pragma warning disable 472 // Disable "CS0472 The result of the expression is always 'false' since a value of type 'Int32' is never equal to 'null' of type 'Int32?' +#pragma warning disable 1573 // Disable "CS1573 Parameter '...' has no matching param tag in the XML comment for ... +#pragma warning disable 1591 // Disable "CS1591 Missing XML comment for publicly visible type or member ..." +#pragma warning disable 8073 // Disable "CS8073 The result of the expression is always 'false' since a value of type 'T' is never equal to 'null' of type 'T?'" + +namespace Miningcore.Blockchain.Ergo +{ + using System = global::System; + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.11.3.0 (NJsonSchema v10.4.4.0 (Newtonsoft.Json v12.0.0.0))")] + public partial class ErgoClient + { + private string _baseUrl = ""; + private System.Net.Http.HttpClient _httpClient; + private System.Lazy _settings; + + public ErgoClient(string baseUrl, System.Net.Http.HttpClient httpClient) + { + BaseUrl = baseUrl; + _httpClient = httpClient; + _settings = new System.Lazy(CreateSerializerSettings); + } + + private Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings() + { + var settings = new Newtonsoft.Json.JsonSerializerSettings(); + UpdateJsonSerializerSettings(settings); + return settings; + } + + public string BaseUrl + { + get { return _baseUrl; } + set { _baseUrl = value; } + } + + protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _settings.Value; } } + + partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings); + + + /// Get the Array of header ids + /// The number of items in list to return + /// The number of items in list to skip + /// Array of header ids + /// A server side error occurred. + public System.Threading.Tasks.Task> GetHeaderIdsAsync(int? limit, int? offset) + { + return GetHeaderIdsAsync(limit, offset, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get the Array of header ids + /// The number of items in list to return + /// The number of items in list to skip + /// Array of header ids + /// A server side error occurred. + public async System.Threading.Tasks.Task> GetHeaderIdsAsync(int? limit, int? offset, System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/blocks?"); + if (limit != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("limit") + "=").Append(System.Uri.EscapeDataString(ConvertToString(limit, System.Globalization.CultureInfo.InvariantCulture))).Append("&"); + } + if (offset != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("offset") + "=").Append(System.Uri.EscapeDataString(ConvertToString(offset, System.Globalization.CultureInfo.InvariantCulture))).Append("&"); + } + urlBuilder_.Length--; + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Send a mined block + /// Block is valid + /// A server side error occurred. + public System.Threading.Tasks.Task SendMinedBlockAsync(FullBlock body) + { + return SendMinedBlockAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Send a mined block + /// Block is valid + /// A server side error occurred. + public async System.Threading.Tasks.Task SendMinedBlockAsync(FullBlock body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/blocks"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + return; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get the header ids at a given height + /// Height of a wanted block + /// Array of header ids + /// A server side error occurred. + public System.Threading.Tasks.Task> GetFullBlockAtAsync(int blockHeight) + { + return GetFullBlockAtAsync(blockHeight, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get the header ids at a given height + /// Height of a wanted block + /// Array of header ids + /// A server side error occurred. + public async System.Threading.Tasks.Task> GetFullBlockAtAsync(int blockHeight, System.Threading.CancellationToken cancellationToken) + { + if (blockHeight == null) + throw new System.ArgumentNullException("blockHeight"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/blocks/at/{blockHeight}"); + urlBuilder_.Replace("{blockHeight}", System.Uri.EscapeDataString(ConvertToString(blockHeight, System.Globalization.CultureInfo.InvariantCulture))); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 404) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Blocks at this height doesn\'t exist", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get headers in a specified range + /// Min header height + /// Max header height (best header height by default) + /// Array of headers + /// A server side error occurred. + public System.Threading.Tasks.Task> GetChainSliceAsync(int? fromHeight, int? toHeight) + { + return GetChainSliceAsync(fromHeight, toHeight, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get headers in a specified range + /// Min header height + /// Max header height (best header height by default) + /// Array of headers + /// A server side error occurred. + public async System.Threading.Tasks.Task> GetChainSliceAsync(int? fromHeight, int? toHeight, System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/blocks/chainSlice?"); + if (fromHeight != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("fromHeight") + "=").Append(System.Uri.EscapeDataString(ConvertToString(fromHeight, System.Globalization.CultureInfo.InvariantCulture))).Append("&"); + } + if (toHeight != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("toHeight") + "=").Append(System.Uri.EscapeDataString(ConvertToString(toHeight, System.Globalization.CultureInfo.InvariantCulture))).Append("&"); + } + urlBuilder_.Length--; + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get the full block info by a given signature + /// ID of a wanted block + /// Block object + /// A server side error occurred. + public System.Threading.Tasks.Task GetFullBlockByIdAsync(string headerId) + { + return GetFullBlockByIdAsync(headerId, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get the full block info by a given signature + /// ID of a wanted block + /// Block object + /// A server side error occurred. + public async System.Threading.Tasks.Task GetFullBlockByIdAsync(string headerId, System.Threading.CancellationToken cancellationToken) + { + if (headerId == null) + throw new System.ArgumentNullException("headerId"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/blocks/{headerId}"); + urlBuilder_.Replace("{headerId}", System.Uri.EscapeDataString(ConvertToString(headerId, System.Globalization.CultureInfo.InvariantCulture))); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 404) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Block with this id doesn\'t exist", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get the block header info by a given signature + /// ID of a wanted block header + /// Block header object + /// A server side error occurred. + public System.Threading.Tasks.Task GetBlockHeaderByIdAsync(string headerId) + { + return GetBlockHeaderByIdAsync(headerId, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get the block header info by a given signature + /// ID of a wanted block header + /// Block header object + /// A server side error occurred. + public async System.Threading.Tasks.Task GetBlockHeaderByIdAsync(string headerId, System.Threading.CancellationToken cancellationToken) + { + if (headerId == null) + throw new System.ArgumentNullException("headerId"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/blocks/{headerId}/header"); + urlBuilder_.Replace("{headerId}", System.Uri.EscapeDataString(ConvertToString(headerId, System.Globalization.CultureInfo.InvariantCulture))); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 404) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Block with this id doesn\'t exist", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get the block transactions info by a given signature + /// ID of a wanted block transactions + /// Block transaction object + /// A server side error occurred. + public System.Threading.Tasks.Task GetBlockTransactionsByIdAsync(string headerId) + { + return GetBlockTransactionsByIdAsync(headerId, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get the block transactions info by a given signature + /// ID of a wanted block transactions + /// Block transaction object + /// A server side error occurred. + public async System.Threading.Tasks.Task GetBlockTransactionsByIdAsync(string headerId, System.Threading.CancellationToken cancellationToken) + { + if (headerId == null) + throw new System.ArgumentNullException("headerId"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/blocks/{headerId}/transactions"); + urlBuilder_.Replace("{headerId}", System.Uri.EscapeDataString(ConvertToString(headerId, System.Globalization.CultureInfo.InvariantCulture))); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 404) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Block with this id doesn\'t exist", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get Merkle proof for transaction + /// ID of a wanted block transactions + /// ID of a wanted transaction + /// Merkle proof object + /// A server side error occurred. + public System.Threading.Tasks.Task GetProofForTxAsync(string headerId, string txId) + { + return GetProofForTxAsync(headerId, txId, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get Merkle proof for transaction + /// ID of a wanted block transactions + /// ID of a wanted transaction + /// Merkle proof object + /// A server side error occurred. + public async System.Threading.Tasks.Task GetProofForTxAsync(string headerId, string txId, System.Threading.CancellationToken cancellationToken) + { + if (headerId == null) + throw new System.ArgumentNullException("headerId"); + + if (txId == null) + throw new System.ArgumentNullException("txId"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/blocks/{headerId}/proofFor/{txId}"); + urlBuilder_.Replace("{headerId}", System.Uri.EscapeDataString(ConvertToString(headerId, System.Globalization.CultureInfo.InvariantCulture))); + urlBuilder_.Replace("{txId}", System.Uri.EscapeDataString(ConvertToString(txId, System.Globalization.CultureInfo.InvariantCulture))); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get the last headers objects + /// count of a wanted block headers + /// Array of block headers + /// A server side error occurred. + public System.Threading.Tasks.Task> GetLastHeadersAsync(double count) + { + return GetLastHeadersAsync(count, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get the last headers objects + /// count of a wanted block headers + /// Array of block headers + /// A server side error occurred. + public async System.Threading.Tasks.Task> GetLastHeadersAsync(double count, System.Threading.CancellationToken cancellationToken) + { + if (count == null) + throw new System.ArgumentNullException("count"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/blocks/lastHeaders/{count}"); + urlBuilder_.Replace("{count}", System.Uri.EscapeDataString(ConvertToString(count, System.Globalization.CultureInfo.InvariantCulture))); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get the persistent modifier by its id + /// ID of a wanted modifier + /// Persistent modifier object + /// A server side error occurred. + public System.Threading.Tasks.Task GetModifierByIdAsync(string modifierId) + { + return GetModifierByIdAsync(modifierId, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get the persistent modifier by its id + /// ID of a wanted modifier + /// Persistent modifier object + /// A server side error occurred. + public async System.Threading.Tasks.Task GetModifierByIdAsync(string modifierId, System.Threading.CancellationToken cancellationToken) + { + if (modifierId == null) + throw new System.ArgumentNullException("modifierId"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/blocks/modifier/{modifierId}"); + urlBuilder_.Replace("{modifierId}", System.Uri.EscapeDataString(ConvertToString(modifierId, System.Globalization.CultureInfo.InvariantCulture))); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + return; + } + else + if (status_ == 404) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Modifier with this id doesn\'t exist", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Construct PoPow header according to given header id + /// ID of wanted header + /// PoPow header object + /// A server side error occurred. + public System.Threading.Tasks.Task GetPopowHeaderByIdAsync(string headerId) + { + return GetPopowHeaderByIdAsync(headerId, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Construct PoPow header according to given header id + /// ID of wanted header + /// PoPow header object + /// A server side error occurred. + public async System.Threading.Tasks.Task GetPopowHeaderByIdAsync(string headerId, System.Threading.CancellationToken cancellationToken) + { + if (headerId == null) + throw new System.ArgumentNullException("headerId"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/nipopow/popowHeaderById/{headerId}"); + urlBuilder_.Replace("{headerId}", System.Uri.EscapeDataString(ConvertToString(headerId, System.Globalization.CultureInfo.InvariantCulture))); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 404) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Header of extension of a corresponding block are not available", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Construct PoPow header for best header at given height + /// Height of a wanted header + /// PoPow header object + /// A server side error occurred. + public System.Threading.Tasks.Task GetPopowHeaderByHeightAsync(int height) + { + return GetPopowHeaderByHeightAsync(height, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Construct PoPow header for best header at given height + /// Height of a wanted header + /// PoPow header object + /// A server side error occurred. + public async System.Threading.Tasks.Task GetPopowHeaderByHeightAsync(int height, System.Threading.CancellationToken cancellationToken) + { + if (height == null) + throw new System.ArgumentNullException("height"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/nipopow/popowHeaderByHeight/{height}"); + urlBuilder_.Replace("{height}", System.Uri.EscapeDataString(ConvertToString(height, System.Globalization.CultureInfo.InvariantCulture))); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 404) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Header of extension of a corresponding block are not available", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Construct PoPoW proof for given min superchain length and suffix length + /// Minimal superchain length + /// Suffix length + /// Nipopow proof object + /// A server side error occurred. + public System.Threading.Tasks.Task GetPopowProofAsync(double minChainLength, double suffixLength) + { + return GetPopowProofAsync(minChainLength, suffixLength, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Construct PoPoW proof for given min superchain length and suffix length + /// Minimal superchain length + /// Suffix length + /// Nipopow proof object + /// A server side error occurred. + public async System.Threading.Tasks.Task GetPopowProofAsync(double minChainLength, double suffixLength, System.Threading.CancellationToken cancellationToken) + { + if (minChainLength == null) + throw new System.ArgumentNullException("minChainLength"); + + if (suffixLength == null) + throw new System.ArgumentNullException("suffixLength"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/nipopow/proof/{minChainLength}/{suffixLength}"); + urlBuilder_.Replace("{minChainLength}", System.Uri.EscapeDataString(ConvertToString(minChainLength, System.Globalization.CultureInfo.InvariantCulture))); + urlBuilder_.Replace("{suffixLength}", System.Uri.EscapeDataString(ConvertToString(suffixLength, System.Globalization.CultureInfo.InvariantCulture))); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Construct PoPoW proof for given min superchain length, suffix length and header ID + /// Minimal superchain length + /// Suffix length + /// ID of wanted header + /// Nipopow proof object + /// A server side error occurred. + public System.Threading.Tasks.Task GetPopowProofByHeaderIdAsync(double minChainLength, double suffixLength, string headerId) + { + return GetPopowProofByHeaderIdAsync(minChainLength, suffixLength, headerId, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Construct PoPoW proof for given min superchain length, suffix length and header ID + /// Minimal superchain length + /// Suffix length + /// ID of wanted header + /// Nipopow proof object + /// A server side error occurred. + public async System.Threading.Tasks.Task GetPopowProofByHeaderIdAsync(double minChainLength, double suffixLength, string headerId, System.Threading.CancellationToken cancellationToken) + { + if (minChainLength == null) + throw new System.ArgumentNullException("minChainLength"); + + if (suffixLength == null) + throw new System.ArgumentNullException("suffixLength"); + + if (headerId == null) + throw new System.ArgumentNullException("headerId"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/nipopow/proof/{minChainLength}/{suffixLength}/{headerId}"); + urlBuilder_.Replace("{minChainLength}", System.Uri.EscapeDataString(ConvertToString(minChainLength, System.Globalization.CultureInfo.InvariantCulture))); + urlBuilder_.Replace("{suffixLength}", System.Uri.EscapeDataString(ConvertToString(suffixLength, System.Globalization.CultureInfo.InvariantCulture))); + urlBuilder_.Replace("{headerId}", System.Uri.EscapeDataString(ConvertToString(headerId, System.Globalization.CultureInfo.InvariantCulture))); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get the information about the Node + /// Node info object + /// A server side error occurred. + public System.Threading.Tasks.Task GetNodeInfoAsync() + { + return GetNodeInfoAsync(System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get the information about the Node + /// Node info object + /// A server side error occurred. + public async System.Threading.Tasks.Task GetNodeInfoAsync(System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/info"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Submit an Ergo transaction to unconfirmed pool to send it over the network + /// JSON with ID of the new transaction + /// A server side error occurred. + public System.Threading.Tasks.Task SendTransactionAsync(ErgoTransaction body) + { + return SendTransactionAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Submit an Ergo transaction to unconfirmed pool to send it over the network + /// JSON with ID of the new transaction + /// A server side error occurred. + public async System.Threading.Tasks.Task SendTransactionAsync(ErgoTransaction body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/transactions"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Checks an Ergo transaction without sending it over the network. Checks that transaction is valid and its inputs are in the UTXO set. Returns transaction identifier if the transaction is passing the checks. + /// JSON with ID of the new transaction + /// A server side error occurred. + public System.Threading.Tasks.Task CheckTransactionAsync(ErgoTransaction body) + { + return CheckTransactionAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Checks an Ergo transaction without sending it over the network. Checks that transaction is valid and its inputs are in the UTXO set. Returns transaction identifier if the transaction is passing the checks. + /// JSON with ID of the new transaction + /// A server side error occurred. + public async System.Threading.Tasks.Task CheckTransactionAsync(ErgoTransaction body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/transactions/check"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get current pool of the unconfirmed transactions pool + /// The number of items in list to return + /// The number of items in list to skip + /// Array with Ergo transactions + /// A server side error occurred. + public System.Threading.Tasks.Task> GetUnconfirmedTransactionsAsync(int? limit, int? offset) + { + return GetUnconfirmedTransactionsAsync(limit, offset, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get current pool of the unconfirmed transactions pool + /// The number of items in list to return + /// The number of items in list to skip + /// Array with Ergo transactions + /// A server side error occurred. + public async System.Threading.Tasks.Task> GetUnconfirmedTransactionsAsync(int? limit, int? offset, System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/transactions/unconfirmed?"); + if (limit != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("limit") + "=").Append(System.Uri.EscapeDataString(ConvertToString(limit, System.Globalization.CultureInfo.InvariantCulture))).Append("&"); + } + if (offset != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("offset") + "=").Append(System.Uri.EscapeDataString(ConvertToString(offset, System.Globalization.CultureInfo.InvariantCulture))).Append("&"); + } + urlBuilder_.Length--; + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get histogram (waittime, (n_trans, sum(fee)) for transactions in mempool. It contains "bins"+1 bins, where i-th elements corresponds to transaction with wait time [i*maxtime/bins, (i+1)*maxtime/bins), and last bin corresponds to the transactions with wait time >= maxtime. + /// The number of bins in histogram + /// Maximal wait time in milliseconds + /// Array with fee histogram + /// A server side error occurred. + public System.Threading.Tasks.Task> GetFeeHistogramAsync(int? bins, long? maxtime) + { + return GetFeeHistogramAsync(bins, maxtime, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get histogram (waittime, (n_trans, sum(fee)) for transactions in mempool. It contains "bins"+1 bins, where i-th elements corresponds to transaction with wait time [i*maxtime/bins, (i+1)*maxtime/bins), and last bin corresponds to the transactions with wait time >= maxtime. + /// The number of bins in histogram + /// Maximal wait time in milliseconds + /// Array with fee histogram + /// A server side error occurred. + public async System.Threading.Tasks.Task> GetFeeHistogramAsync(int? bins, long? maxtime, System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/transactions/poolHistogram?"); + if (bins != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("bins") + "=").Append(System.Uri.EscapeDataString(ConvertToString(bins, System.Globalization.CultureInfo.InvariantCulture))).Append("&"); + } + if (maxtime != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("maxtime") + "=").Append(System.Uri.EscapeDataString(ConvertToString(maxtime, System.Globalization.CultureInfo.InvariantCulture))).Append("&"); + } + urlBuilder_.Length--; + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get recommended fee (in nanoErgs) for a transaction with specified size (in bytes) to be proceeded in specified time (in minutes) + /// Maximum transaction wait time in minutes + /// Transaction size + /// Recommended fee for the transaction (in nanoErgs) + /// A server side error occurred. + public System.Threading.Tasks.Task GetRecommendedFeeAsync(int waitTime, int txSize) + { + return GetRecommendedFeeAsync(waitTime, txSize, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get recommended fee (in nanoErgs) for a transaction with specified size (in bytes) to be proceeded in specified time (in minutes) + /// Maximum transaction wait time in minutes + /// Transaction size + /// Recommended fee for the transaction (in nanoErgs) + /// A server side error occurred. + public async System.Threading.Tasks.Task GetRecommendedFeeAsync(int waitTime, int txSize, System.Threading.CancellationToken cancellationToken) + { + if (waitTime == null) + throw new System.ArgumentNullException("waitTime"); + + if (txSize == null) + throw new System.ArgumentNullException("txSize"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/transactions/getFee?"); + urlBuilder_.Append(System.Uri.EscapeDataString("waitTime") + "=").Append(System.Uri.EscapeDataString(ConvertToString(waitTime, System.Globalization.CultureInfo.InvariantCulture))).Append("&"); + urlBuilder_.Append(System.Uri.EscapeDataString("txSize") + "=").Append(System.Uri.EscapeDataString(ConvertToString(txSize, System.Globalization.CultureInfo.InvariantCulture))).Append("&"); + urlBuilder_.Length--; + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get expected wait time for the transaction with specified fee and size + /// Transaction fee (in nanoErgs) + /// Transaction size + /// Expected wait time in milliseconds + /// A server side error occurred. + public System.Threading.Tasks.Task GetExpectedWaitTimeAsync(int fee, int txSize) + { + return GetExpectedWaitTimeAsync(fee, txSize, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get expected wait time for the transaction with specified fee and size + /// Transaction fee (in nanoErgs) + /// Transaction size + /// Expected wait time in milliseconds + /// A server side error occurred. + public async System.Threading.Tasks.Task GetExpectedWaitTimeAsync(int fee, int txSize, System.Threading.CancellationToken cancellationToken) + { + if (fee == null) + throw new System.ArgumentNullException("fee"); + + if (txSize == null) + throw new System.ArgumentNullException("txSize"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/transactions/waitTime?"); + urlBuilder_.Append(System.Uri.EscapeDataString("fee") + "=").Append(System.Uri.EscapeDataString(ConvertToString(fee, System.Globalization.CultureInfo.InvariantCulture))).Append("&"); + urlBuilder_.Append(System.Uri.EscapeDataString("txSize") + "=").Append(System.Uri.EscapeDataString(ConvertToString(txSize, System.Globalization.CultureInfo.InvariantCulture))).Append("&"); + urlBuilder_.Length--; + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get all known peers + /// Array of peer objects + /// A server side error occurred. + public System.Threading.Tasks.Task> GetAllPeersAsync() + { + return GetAllPeersAsync(System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get all known peers + /// Array of peer objects + /// A server side error occurred. + public async System.Threading.Tasks.Task> GetAllPeersAsync(System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/peers/all"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get current connected peers + /// Array of peer objects + /// A server side error occurred. + public System.Threading.Tasks.Task> GetConnectedPeersAsync() + { + return GetConnectedPeersAsync(System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get current connected peers + /// Array of peer objects + /// A server side error occurred. + public async System.Threading.Tasks.Task> GetConnectedPeersAsync(System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/peers/connected"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Add address to peers list + /// Attempt to connect to the peer + /// A server side error occurred. + public System.Threading.Tasks.Task ConnectToPeerAsync(string body) + { + return ConnectToPeerAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Add address to peers list + /// Attempt to connect to the peer + /// A server side error occurred. + public async System.Threading.Tasks.Task ConnectToPeerAsync(string body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/peers/connect"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + return; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get blacklisted peers + /// Array of the addresses + /// A server side error occurred. + public System.Threading.Tasks.Task> GetBlacklistedPeersAsync() + { + return GetBlacklistedPeersAsync(System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get blacklisted peers + /// Array of the addresses + /// A server side error occurred. + public async System.Threading.Tasks.Task> GetBlacklistedPeersAsync(System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/peers/blacklisted"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get last incomming message timestamp and current network time + /// Network status + /// A server side error occurred. + public System.Threading.Tasks.Task GetPeersStatusAsync() + { + return GetPeersStatusAsync(System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get last incomming message timestamp and current network time + /// Network status + /// A server side error occurred. + public async System.Threading.Tasks.Task GetPeersStatusAsync(System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/peers/status"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get random seed of 32 bytes + /// Base16-encoded 32 byte seed + /// A server side error occurred. + public System.Threading.Tasks.Task GetRandomSeedAsync() + { + return GetRandomSeedAsync(System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get random seed of 32 bytes + /// Base16-encoded 32 byte seed + /// A server side error occurred. + public async System.Threading.Tasks.Task GetRandomSeedAsync(System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/utils/seed"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Check address validity + /// address to check + /// Address validity with validation error + /// A server side error occurred. + public System.Threading.Tasks.Task CheckAddressValidityAsync(string address) + { + return CheckAddressValidityAsync(address, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Check address validity + /// address to check + /// Address validity with validation error + /// A server side error occurred. + public async System.Threading.Tasks.Task CheckAddressValidityAsync(string address, System.Threading.CancellationToken cancellationToken) + { + if (address == null) + throw new System.ArgumentNullException("address"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/utils/address/{address}"); + urlBuilder_.Replace("{address}", System.Uri.EscapeDataString(ConvertToString(address, System.Globalization.CultureInfo.InvariantCulture))); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Convert Pay-To-Public-Key Address to raw representation (hex-encoded serialized curve point) + /// address to extract public key from + /// hex-encoded public key (serialized secp256k1 element) + /// A server side error occurred. + public System.Threading.Tasks.Task AddressToRawAsync(string address) + { + return AddressToRawAsync(address, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Convert Pay-To-Public-Key Address to raw representation (hex-encoded serialized curve point) + /// address to extract public key from + /// hex-encoded public key (serialized secp256k1 element) + /// A server side error occurred. + public async System.Threading.Tasks.Task AddressToRawAsync(string address, System.Threading.CancellationToken cancellationToken) + { + if (address == null) + throw new System.ArgumentNullException("address"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/utils/addressToRaw/{address}"); + urlBuilder_.Replace("{address}", System.Uri.EscapeDataString(ConvertToString(address, System.Globalization.CultureInfo.InvariantCulture))); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Generate Pay-To-Public-Key address from hex-encoded raw pubkey (secp256k1 serialized point) + /// public key to get address from + /// Pay-to-public-key (P2PK) address + /// A server side error occurred. + public System.Threading.Tasks.Task RawToAddressAsync(string pubkeyHex) + { + return RawToAddressAsync(pubkeyHex, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Generate Pay-To-Public-Key address from hex-encoded raw pubkey (secp256k1 serialized point) + /// public key to get address from + /// Pay-to-public-key (P2PK) address + /// A server side error occurred. + public async System.Threading.Tasks.Task RawToAddressAsync(string pubkeyHex, System.Threading.CancellationToken cancellationToken) + { + if (pubkeyHex == null) + throw new System.ArgumentNullException("pubkeyHex"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/utils/rawToAddress/{pubkeyHex}"); + urlBuilder_.Replace("{pubkeyHex}", System.Uri.EscapeDataString(ConvertToString(pubkeyHex, System.Globalization.CultureInfo.InvariantCulture))); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Generate Ergo address from hex-encoded ErgoTree + /// ErgoTree to derive an address from + /// Ergo address + /// A server side error occurred. + public System.Threading.Tasks.Task ErgoTreeToAddressAsync(string ergoTreeHex) + { + return ErgoTreeToAddressAsync(ergoTreeHex, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Generate Ergo address from hex-encoded ErgoTree + /// ErgoTree to derive an address from + /// Ergo address + /// A server side error occurred. + public async System.Threading.Tasks.Task ErgoTreeToAddressAsync(string ergoTreeHex, System.Threading.CancellationToken cancellationToken) + { + if (ergoTreeHex == null) + throw new System.ArgumentNullException("ergoTreeHex"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/utils/ergoTreeToAddress/{ergoTreeHex}"); + urlBuilder_.Replace("{ergoTreeHex}", System.Uri.EscapeDataString(ConvertToString(ergoTreeHex, System.Globalization.CultureInfo.InvariantCulture))); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Generate random seed of specified length in bytes + /// seed length in bytes + /// Base16-encoded N byte seed + /// A server side error occurred. + public System.Threading.Tasks.Task GetRandomSeedWithLengthAsync(string length) + { + return GetRandomSeedWithLengthAsync(length, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Generate random seed of specified length in bytes + /// seed length in bytes + /// Base16-encoded N byte seed + /// A server side error occurred. + public async System.Threading.Tasks.Task GetRandomSeedWithLengthAsync(string length, System.Threading.CancellationToken cancellationToken) + { + if (length == null) + throw new System.ArgumentNullException("length"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/utils/seed/{length}"); + urlBuilder_.Replace("{length}", System.Uri.EscapeDataString(ConvertToString(length, System.Globalization.CultureInfo.InvariantCulture))); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Return Blake2b hash of specified message + /// Base16-encoded 32 byte hash + /// A server side error occurred. + public System.Threading.Tasks.Task HashBlake2bAsync(string body) + { + return HashBlake2bAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Return Blake2b hash of specified message + /// Base16-encoded 32 byte hash + /// A server side error occurred. + public async System.Threading.Tasks.Task HashBlake2bAsync(string body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/utils/hash/blake2b"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Initialize new wallet with randomly generated seed + /// New wallet with randomly generated seed created successfully + /// A server side error occurred. + public System.Threading.Tasks.Task WalletInitAsync(Body body) + { + return WalletInitAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Initialize new wallet with randomly generated seed + /// New wallet with randomly generated seed created successfully + /// A server side error occurred. + public async System.Threading.Tasks.Task WalletInitAsync(Body body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/wallet/init"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Create new wallet from existing mnemonic seed + /// Wallet restored successfully + /// A server side error occurred. + public System.Threading.Tasks.Task WalletRestoreAsync(Body2 body) + { + return WalletRestoreAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Create new wallet from existing mnemonic seed + /// Wallet restored successfully + /// A server side error occurred. + public async System.Threading.Tasks.Task WalletRestoreAsync(Body2 body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/wallet/restore"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + return; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Check whether mnemonic phrase is corresponding to the wallet seed + /// Whether passphrase match wallet + /// A server side error occurred. + public System.Threading.Tasks.Task CheckSeedAsync(Body3 body) + { + return CheckSeedAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Check whether mnemonic phrase is corresponding to the wallet seed + /// Whether passphrase match wallet + /// A server side error occurred. + public async System.Threading.Tasks.Task CheckSeedAsync(Body3 body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/wallet/check"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Unlock wallet + /// Wallet unlocked successfully + /// A server side error occurred. + public System.Threading.Tasks.Task WalletUnlockAsync(Body4 body) + { + return WalletUnlockAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Unlock wallet + /// Wallet unlocked successfully + /// A server side error occurred. + public async System.Threading.Tasks.Task WalletUnlockAsync(Body4 body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/wallet/unlock"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + return; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Lock wallet + /// Wallet locked successfully + /// A server side error occurred. + public System.Threading.Tasks.Task WalletLockAsync() + { + return WalletLockAsync(System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Lock wallet + /// Wallet locked successfully + /// A server side error occurred. + public async System.Threading.Tasks.Task WalletLockAsync(System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/wallet/lock"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + return; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Rescan wallet (all the available full blocks) + /// Wallet storage recreated + /// A server side error occurred. + public System.Threading.Tasks.Task WalletRescanAsync() + { + return WalletRescanAsync(System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Rescan wallet (all the available full blocks) + /// Wallet storage recreated + /// A server side error occurred. + public async System.Threading.Tasks.Task WalletRescanAsync(System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/wallet/rescan"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + return; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get wallet status + /// Wallet status + /// A server side error occurred. + public System.Threading.Tasks.Task GetWalletStatusAsync() + { + return GetWalletStatusAsync(System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get wallet status + /// Wallet status + /// A server side error occurred. + public async System.Threading.Tasks.Task GetWalletStatusAsync(System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/wallet/status"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Update address to be used to send change to + /// Change address updated successfully + /// A server side error occurred. + public System.Threading.Tasks.Task WalletUpdateChangeAddressAsync(Body5 body) + { + return WalletUpdateChangeAddressAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Update address to be used to send change to + /// Change address updated successfully + /// A server side error occurred. + public async System.Threading.Tasks.Task WalletUpdateChangeAddressAsync(Body5 body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/wallet/updateChangeAddress"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + return; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Derive new key according to a provided path + /// Resulted address + /// A server side error occurred. + public System.Threading.Tasks.Task WalletDeriveKeyAsync(Body6 body) + { + return WalletDeriveKeyAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Derive new key according to a provided path + /// Resulted address + /// A server side error occurred. + public async System.Threading.Tasks.Task WalletDeriveKeyAsync(Body6 body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/wallet/deriveKey"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Derive next key + /// Resulted secret path and address + /// A server side error occurred. + public System.Threading.Tasks.Task WalletDeriveNextKeyAsync() + { + return WalletDeriveNextKeyAsync(System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Derive next key + /// Resulted secret path and address + /// A server side error occurred. + public async System.Threading.Tasks.Task WalletDeriveNextKeyAsync(System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/wallet/deriveNextKey"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get total amount of confirmed Ergo tokens and assets + /// Get total amount of confirmed Ergo tokens and assets + /// A server side error occurred. + public System.Threading.Tasks.Task WalletBalancesAsync() + { + return WalletBalancesAsync(System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get total amount of confirmed Ergo tokens and assets + /// Get total amount of confirmed Ergo tokens and assets + /// A server side error occurred. + public async System.Threading.Tasks.Task WalletBalancesAsync(System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/wallet/balances"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get a list of all wallet-related transactions + /// Minimal tx inclusion height + /// Maximal tx inclusion height + /// Minimal confirmations number + /// Maximal confirmations number + /// A list of all wallet-related transactions + /// A server side error occurred. + public System.Threading.Tasks.Task> WalletTransactionsAsync(int? minInclusionHeight, int? maxInclusionHeight, int? minConfirmations, int? maxConfirmations) + { + return WalletTransactionsAsync(minInclusionHeight, maxInclusionHeight, minConfirmations, maxConfirmations, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get a list of all wallet-related transactions + /// Minimal tx inclusion height + /// Maximal tx inclusion height + /// Minimal confirmations number + /// Maximal confirmations number + /// A list of all wallet-related transactions + /// A server side error occurred. + public async System.Threading.Tasks.Task> WalletTransactionsAsync(int? minInclusionHeight, int? maxInclusionHeight, int? minConfirmations, int? maxConfirmations, System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/wallet/transactions?"); + if (minInclusionHeight != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("minInclusionHeight") + "=").Append(System.Uri.EscapeDataString(ConvertToString(minInclusionHeight, System.Globalization.CultureInfo.InvariantCulture))).Append("&"); + } + if (maxInclusionHeight != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("maxInclusionHeight") + "=").Append(System.Uri.EscapeDataString(ConvertToString(maxInclusionHeight, System.Globalization.CultureInfo.InvariantCulture))).Append("&"); + } + if (minConfirmations != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("minConfirmations") + "=").Append(System.Uri.EscapeDataString(ConvertToString(minConfirmations, System.Globalization.CultureInfo.InvariantCulture))).Append("&"); + } + if (maxConfirmations != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("maxConfirmations") + "=").Append(System.Uri.EscapeDataString(ConvertToString(maxConfirmations, System.Globalization.CultureInfo.InvariantCulture))).Append("&"); + } + urlBuilder_.Length--; + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get wallet-related transaction by id + /// Transaction id + /// Wallet-related transaction + /// A server side error occurred. + public System.Threading.Tasks.Task WalletGetTransactionAsync(string id) + { + return WalletGetTransactionAsync(id, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get wallet-related transaction by id + /// Transaction id + /// Wallet-related transaction + /// A server side error occurred. + public async System.Threading.Tasks.Task WalletGetTransactionAsync(string id, System.Threading.CancellationToken cancellationToken) + { + if (id == null) + throw new System.ArgumentNullException("id"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/wallet/transactionById?"); + urlBuilder_.Append(System.Uri.EscapeDataString("id") + "=").Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))).Append("&"); + urlBuilder_.Length--; + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 404) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Transaction with specified id not found in wallet", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get scan-related transactions by scan id + /// Scan id + /// Scan-related transactions + /// A server side error occurred. + public System.Threading.Tasks.Task> WalletTransactionsByScanIdAsync(string scanId) + { + return WalletTransactionsByScanIdAsync(scanId, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get scan-related transactions by scan id + /// Scan id + /// Scan-related transactions + /// A server side error occurred. + public async System.Threading.Tasks.Task> WalletTransactionsByScanIdAsync(string scanId, System.Threading.CancellationToken cancellationToken) + { + if (scanId == null) + throw new System.ArgumentNullException("scanId"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/wallet/transactionsByScanId?"); + urlBuilder_.Append(System.Uri.EscapeDataString("scanId") + "=").Append(System.Uri.EscapeDataString(ConvertToString(scanId, System.Globalization.CultureInfo.InvariantCulture))).Append("&"); + urlBuilder_.Length--; + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 404) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Transactions with related scan id not found in wallet", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get a list of all wallet-related boxes, both spent and unspent. Set minConfirmations to -1 to get mempool boxes included. + /// Minimal number of confirmations + /// Minimal box inclusion height + /// A list of all wallet-related boxes + /// A server side error occurred. + public System.Threading.Tasks.Task> WalletBoxesAsync(int? minConfirmations, int? minInclusionHeight) + { + return WalletBoxesAsync(minConfirmations, minInclusionHeight, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get a list of all wallet-related boxes, both spent and unspent. Set minConfirmations to -1 to get mempool boxes included. + /// Minimal number of confirmations + /// Minimal box inclusion height + /// A list of all wallet-related boxes + /// A server side error occurred. + public async System.Threading.Tasks.Task> WalletBoxesAsync(int? minConfirmations, int? minInclusionHeight, System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/wallet/boxes?"); + if (minConfirmations != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("minConfirmations") + "=").Append(System.Uri.EscapeDataString(ConvertToString(minConfirmations, System.Globalization.CultureInfo.InvariantCulture))).Append("&"); + } + if (minInclusionHeight != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("minInclusionHeight") + "=").Append(System.Uri.EscapeDataString(ConvertToString(minInclusionHeight, System.Globalization.CultureInfo.InvariantCulture))).Append("&"); + } + urlBuilder_.Length--; + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get a list of collected boxes. + /// This API method recieves balance and assets, according to which, it's collecting result + /// A list of all collected boxes + /// A server side error occurred. + public System.Threading.Tasks.Task> WalletBoxesCollectAsync(BoxesRequestHolder body) + { + return WalletBoxesCollectAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get a list of collected boxes. + /// This API method recieves balance and assets, according to which, it's collecting result + /// A list of all collected boxes + /// A server side error occurred. + public async System.Threading.Tasks.Task> WalletBoxesCollectAsync(BoxesRequestHolder body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/wallet/boxes/collect"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get a list of unspent boxes. Set minConfirmations to -1 to have mempool boxes considered. + /// Minimal number of confirmations + /// Minimal box inclusion height + /// A list of unspent boxes + /// A server side error occurred. + public System.Threading.Tasks.Task> WalletUnspentBoxesAsync(int? minConfirmations, int? minInclusionHeight) + { + return WalletUnspentBoxesAsync(minConfirmations, minInclusionHeight, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get a list of unspent boxes. Set minConfirmations to -1 to have mempool boxes considered. + /// Minimal number of confirmations + /// Minimal box inclusion height + /// A list of unspent boxes + /// A server side error occurred. + public async System.Threading.Tasks.Task> WalletUnspentBoxesAsync(int? minConfirmations, int? minInclusionHeight, System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/wallet/boxes/unspent?"); + if (minConfirmations != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("minConfirmations") + "=").Append(System.Uri.EscapeDataString(ConvertToString(minConfirmations, System.Globalization.CultureInfo.InvariantCulture))).Append("&"); + } + if (minInclusionHeight != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("minInclusionHeight") + "=").Append(System.Uri.EscapeDataString(ConvertToString(minInclusionHeight, System.Globalization.CultureInfo.InvariantCulture))).Append("&"); + } + urlBuilder_.Length--; + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get summary amount of confirmed plus unconfirmed Ergo tokens and assets + /// Get summary amount of confirmed plus unconfirmed Ergo tokens and assets + /// A server side error occurred. + public System.Threading.Tasks.Task WalletBalancesUnconfirmedAsync() + { + return WalletBalancesUnconfirmedAsync(System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get summary amount of confirmed plus unconfirmed Ergo tokens and assets + /// Get summary amount of confirmed plus unconfirmed Ergo tokens and assets + /// A server side error occurred. + public async System.Threading.Tasks.Task WalletBalancesUnconfirmedAsync(System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/wallet/balances/withUnconfirmed"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get wallet addresses + /// String with encoded wallet addresses + /// A server side error occurred. + public System.Threading.Tasks.Task> WalletAddressesAsync() + { + return WalletAddressesAsync(System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get wallet addresses + /// String with encoded wallet addresses + /// A server side error occurred. + public async System.Threading.Tasks.Task> WalletAddressesAsync(System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/wallet/addresses"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Generate arbitrary transaction from array of requests. + /// This API method receives a sequence of requests as an input. Each request will produce an output of the resulting transaction (with fee output created automatically). Currently supported types of requests are payment and asset issuance requests. An example for a transaction with requests of both kinds is provided below. Please note that for the payment request "assets" and "registers" fields are not needed. For asset issuance request, "registers" field is not needed. + /// You may specify boxes to spend by providing them in "inputsRaw". Please note you need to have strict equality between input and output total amounts of Ergs in this case. If you want wallet to pick up the boxes, leave "inputsRaw" empty. + /// Generated Ergo transaction + /// A server side error occurred. + public System.Threading.Tasks.Task WalletTransactionGenerateAsync(RequestsHolder body) + { + return WalletTransactionGenerateAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Generate arbitrary transaction from array of requests. + /// This API method receives a sequence of requests as an input. Each request will produce an output of the resulting transaction (with fee output created automatically). Currently supported types of requests are payment and asset issuance requests. An example for a transaction with requests of both kinds is provided below. Please note that for the payment request "assets" and "registers" fields are not needed. For asset issuance request, "registers" field is not needed. + /// You may specify boxes to spend by providing them in "inputsRaw". Please note you need to have strict equality between input and output total amounts of Ergs in this case. If you want wallet to pick up the boxes, leave "inputsRaw" empty. + /// Generated Ergo transaction + /// A server side error occurred. + public async System.Threading.Tasks.Task WalletTransactionGenerateAsync(RequestsHolder body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/wallet/transaction/generate"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 400) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Bad transaction request", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Generate unsigned transaction from array of requests. + /// The same as /wallet/transaction/generate but generates unsigned transaction. + /// Generated unsigned Ergo transaction + /// A server side error occurred. + public System.Threading.Tasks.Task WalletUnsignedTransactionGenerateAsync(RequestsHolder body) + { + return WalletUnsignedTransactionGenerateAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Generate unsigned transaction from array of requests. + /// The same as /wallet/transaction/generate but generates unsigned transaction. + /// Generated unsigned Ergo transaction + /// A server side error occurred. + public async System.Threading.Tasks.Task WalletUnsignedTransactionGenerateAsync(RequestsHolder body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/wallet/transaction/generateUnsigned"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 400) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Bad transaction request", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Sign arbitrary unsigned transaction with wallet secrets and also secrets provided. + /// With this API method an arbitrary unsigned transaction can be signed with secrets provided or stored in the wallet. Both DLOG and Diffie-Hellman tuple secrets are supported. + /// Please note that the unsigned transaction contains only identifiers of inputs and data inputs. If the node holds UTXO set, it is able to extract boxes needed. Otherwise, input (and data-input) boxes can be provided in "inputsRaw" and "dataInputsRaw" fields. + /// Generated Ergo transaction + /// A server side error occurred. + public System.Threading.Tasks.Task WalletTransactionSignAsync(TransactionSigningRequest body) + { + return WalletTransactionSignAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Sign arbitrary unsigned transaction with wallet secrets and also secrets provided. + /// With this API method an arbitrary unsigned transaction can be signed with secrets provided or stored in the wallet. Both DLOG and Diffie-Hellman tuple secrets are supported. + /// Please note that the unsigned transaction contains only identifiers of inputs and data inputs. If the node holds UTXO set, it is able to extract boxes needed. Otherwise, input (and data-input) boxes can be provided in "inputsRaw" and "dataInputsRaw" fields. + /// Generated Ergo transaction + /// A server side error occurred. + public async System.Threading.Tasks.Task WalletTransactionSignAsync(TransactionSigningRequest body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/wallet/transaction/sign"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 400) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Bad transaction request", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Generate and send arbitrary transaction + /// See description of /wallet/transaction/generate + /// Identifier of an Ergo transaction generated + /// A server side error occurred. + public System.Threading.Tasks.Task WalletTransactionGenerateAndSendAsync(RequestsHolder body) + { + return WalletTransactionGenerateAndSendAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Generate and send arbitrary transaction + /// See description of /wallet/transaction/generate + /// Identifier of an Ergo transaction generated + /// A server side error occurred. + public async System.Threading.Tasks.Task WalletTransactionGenerateAndSendAsync(RequestsHolder body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/wallet/transaction/send"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 400) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Bad transaction request", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Generate and send payment transaction (default fee of 0.001 Erg is used) + /// Identifier of an Ergo transaction generated + /// A server side error occurred. + public System.Threading.Tasks.Task WalletPaymentTransactionGenerateAndSendAsync(System.Collections.Generic.IEnumerable body) + { + return WalletPaymentTransactionGenerateAndSendAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Generate and send payment transaction (default fee of 0.001 Erg is used) + /// Identifier of an Ergo transaction generated + /// A server side error occurred. + public async System.Threading.Tasks.Task WalletPaymentTransactionGenerateAndSendAsync(System.Collections.Generic.IEnumerable body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/wallet/payment/send"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 400) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Bad payment request", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Request block candidate + /// External candidate + /// A server side error occurred. + public System.Threading.Tasks.Task MiningRequestBlockCandidateAsync() + { + return MiningRequestBlockCandidateAsync(System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Request block candidate + /// External candidate + /// A server side error occurred. + public async System.Threading.Tasks.Task MiningRequestBlockCandidateAsync(System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/mining/candidate"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Request block candidate + /// External candidate + /// A server side error occurred. + public System.Threading.Tasks.Task MiningRequestBlockCandidateWithMandatoryTransactionsAsync(System.Collections.Generic.IEnumerable body) + { + return MiningRequestBlockCandidateWithMandatoryTransactionsAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Request block candidate + /// External candidate + /// A server side error occurred. + public async System.Threading.Tasks.Task MiningRequestBlockCandidateWithMandatoryTransactionsAsync(System.Collections.Generic.IEnumerable body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/mining/candidateWithTxs"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Read miner reward address + /// Miner Reward Script (in P2S format) + /// A server side error occurred. + public System.Threading.Tasks.Task MiningReadMinerRewardAddressAsync() + { + return MiningReadMinerRewardAddressAsync(System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Read miner reward address + /// Miner Reward Script (in P2S format) + /// A server side error occurred. + public async System.Threading.Tasks.Task MiningReadMinerRewardAddressAsync(System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/mining/rewardAddress"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Read public key associated with miner rewards + /// Public key for miner rewards (as hex-encoded secp256k1 point) + /// A server side error occurred. + public System.Threading.Tasks.Task MiningReadMinerRewardPubkeyAsync() + { + return MiningReadMinerRewardPubkeyAsync(System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Read public key associated with miner rewards + /// Public key for miner rewards (as hex-encoded secp256k1 point) + /// A server side error occurred. + public async System.Threading.Tasks.Task MiningReadMinerRewardPubkeyAsync(System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/mining/rewardPublicKey"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Submit solution for current candidate + /// Solution is valid + /// A server side error occurred. + public System.Threading.Tasks.Task MiningSubmitSolutionAsync(PowSolutions body) + { + return MiningSubmitSolutionAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Submit solution for current candidate + /// Solution is valid + /// A server side error occurred. + public async System.Threading.Tasks.Task MiningSubmitSolutionAsync(PowSolutions body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/mining/solution"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + return; + } + else + if (status_ == 400) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Solution is invalid", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get box contents for a box by a unique identifier. + /// ID of a wanted box + /// Box object + /// A server side error occurred. + public System.Threading.Tasks.Task GetBoxByIdAsync(string boxId) + { + return GetBoxByIdAsync(boxId, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get box contents for a box by a unique identifier. + /// ID of a wanted box + /// Box object + /// A server side error occurred. + public async System.Threading.Tasks.Task GetBoxByIdAsync(string boxId, System.Threading.CancellationToken cancellationToken) + { + if (boxId == null) + throw new System.ArgumentNullException("boxId"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/utxo/byId/{boxId}"); + urlBuilder_.Replace("{boxId}", System.Uri.EscapeDataString(ConvertToString(boxId, System.Globalization.CultureInfo.InvariantCulture))); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 404) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Box with this id doesn\'t exist", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get serialized box from UTXO pool in Base16 encoding by an identifier. + /// ID of a wanted box + /// Json containing box identifier and hex-encoded box bytes + /// A server side error occurred. + public System.Threading.Tasks.Task GetBoxByIdBinaryAsync(string boxId) + { + return GetBoxByIdBinaryAsync(boxId, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get serialized box from UTXO pool in Base16 encoding by an identifier. + /// ID of a wanted box + /// Json containing box identifier and hex-encoded box bytes + /// A server side error occurred. + public async System.Threading.Tasks.Task GetBoxByIdBinaryAsync(string boxId, System.Threading.CancellationToken cancellationToken) + { + if (boxId == null) + throw new System.ArgumentNullException("boxId"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/utxo/byIdBinary/{boxId}"); + urlBuilder_.Replace("{boxId}", System.Uri.EscapeDataString(ConvertToString(boxId, System.Globalization.CultureInfo.InvariantCulture))); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 404) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Box with this id doesn\'t exist", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get box contents for a box by a unique identifier, from UTXO set and also the mempool. + /// ID of a box to obtain + /// Box object + /// A server side error occurred. + public System.Threading.Tasks.Task GetBoxWithPoolByIdAsync(string boxId) + { + return GetBoxWithPoolByIdAsync(boxId, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get box contents for a box by a unique identifier, from UTXO set and also the mempool. + /// ID of a box to obtain + /// Box object + /// A server side error occurred. + public async System.Threading.Tasks.Task GetBoxWithPoolByIdAsync(string boxId, System.Threading.CancellationToken cancellationToken) + { + if (boxId == null) + throw new System.ArgumentNullException("boxId"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/utxo/withPool/byId/{boxId}"); + urlBuilder_.Replace("{boxId}", System.Uri.EscapeDataString(ConvertToString(boxId, System.Globalization.CultureInfo.InvariantCulture))); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 404) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Box with this id doesn\'t exist", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get serialized box in Base16 encoding by an identifier, considering also the mempool. + /// ID of a wanted box + /// Json containing box identifier and hex-encoded box bytes + /// A server side error occurred. + public System.Threading.Tasks.Task GetBoxWithPoolByIdBinaryAsync(string boxId) + { + return GetBoxWithPoolByIdBinaryAsync(boxId, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get serialized box in Base16 encoding by an identifier, considering also the mempool. + /// ID of a wanted box + /// Json containing box identifier and hex-encoded box bytes + /// A server side error occurred. + public async System.Threading.Tasks.Task GetBoxWithPoolByIdBinaryAsync(string boxId, System.Threading.CancellationToken cancellationToken) + { + if (boxId == null) + throw new System.ArgumentNullException("boxId"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/utxo/withPool/byIdBinary/{boxId}"); + urlBuilder_.Replace("{boxId}", System.Uri.EscapeDataString(ConvertToString(boxId, System.Globalization.CultureInfo.InvariantCulture))); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 404) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Box with this id doesn\'t exist", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Get genesis boxes (boxes existed before the very first block) + /// A list of all the genesis boxes + /// A server side error occurred. + public System.Threading.Tasks.Task> GenesisBoxesAsync() + { + return GenesisBoxesAsync(System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Get genesis boxes (boxes existed before the very first block) + /// A list of all the genesis boxes + /// A server side error occurred. + public async System.Threading.Tasks.Task> GenesisBoxesAsync(System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/utxo/genesis"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 404) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Box with this id doesn\'t exist", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Create P2SAddress from Sigma source + /// Ergo address derived from source + /// A server side error occurred. + public System.Threading.Tasks.Task ScriptP2SAddressAsync(SourceHolder body) + { + return ScriptP2SAddressAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Create P2SAddress from Sigma source + /// Ergo address derived from source + /// A server side error occurred. + public async System.Threading.Tasks.Task ScriptP2SAddressAsync(SourceHolder body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/script/p2sAddress"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 400) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Bad source", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Create P2SHAddress from Sigma source + /// Ergo address derived from source + /// A server side error occurred. + public System.Threading.Tasks.Task ScriptP2SHAddressAsync(SourceHolder body) + { + return ScriptP2SHAddressAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Create P2SHAddress from Sigma source + /// Ergo address derived from source + /// A server side error occurred. + public async System.Threading.Tasks.Task ScriptP2SHAddressAsync(SourceHolder body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/script/p2shAddress"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 400) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Bad source", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Convert an address to hex-encoded serialized ErgoTree (script) + /// address to get a script from + /// Base16-encoded ErgoTree (script) + /// A server side error occurred. + public System.Threading.Tasks.Task AddressToTreeAsync(string address) + { + return AddressToTreeAsync(address, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Convert an address to hex-encoded serialized ErgoTree (script) + /// address to get a script from + /// Base16-encoded ErgoTree (script) + /// A server side error occurred. + public async System.Threading.Tasks.Task AddressToTreeAsync(string address, System.Threading.CancellationToken cancellationToken) + { + if (address == null) + throw new System.ArgumentNullException("address"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/script/addressToTree/{address}"); + urlBuilder_.Replace("{address}", System.Uri.EscapeDataString(ConvertToString(address, System.Globalization.CultureInfo.InvariantCulture))); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Convert an address to hex-encoded Sigma byte array constant which contains script bytes + /// address to get a script from + /// Base16-encoded Sigma byte array constant which contains script bytes + /// A server side error occurred. + public System.Threading.Tasks.Task AddressToBytesAsync(string address) + { + return AddressToBytesAsync(address, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Convert an address to hex-encoded Sigma byte array constant which contains script bytes + /// address to get a script from + /// Base16-encoded Sigma byte array constant which contains script bytes + /// A server side error occurred. + public async System.Threading.Tasks.Task AddressToBytesAsync(string address, System.Threading.CancellationToken cancellationToken) + { + if (address == null) + throw new System.ArgumentNullException("address"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/script/addressToBytes/{address}"); + urlBuilder_.Replace("{address}", System.Uri.EscapeDataString(ConvertToString(address, System.Globalization.CultureInfo.InvariantCulture))); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Execute script with context + /// Result of reduceToCrypto + /// A server side error occurred. + public System.Threading.Tasks.Task ExecuteWithContextAsync(ExecuteScript body) + { + return ExecuteWithContextAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Execute script with context + /// Result of reduceToCrypto + /// A server side error occurred. + public async System.Threading.Tasks.Task ExecuteWithContextAsync(ExecuteScript body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/script/executeWithContext"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 400) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Compiler error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Register a scan + /// Identifier of a scan generated + /// A server side error occurred. + public System.Threading.Tasks.Task RegisterScanAsync(ScanRequest body) + { + return RegisterScanAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Register a scan + /// Identifier of a scan generated + /// A server side error occurred. + public async System.Threading.Tasks.Task RegisterScanAsync(ScanRequest body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/scan/register"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 400) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Bad request", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Stop tracking and deregister scan + /// Identifier of a scan removed + /// A server side error occurred. + public System.Threading.Tasks.Task DeregisterScanAsync(ScanId body) + { + return DeregisterScanAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Stop tracking and deregister scan + /// Identifier of a scan removed + /// A server side error occurred. + public async System.Threading.Tasks.Task DeregisterScanAsync(ScanId body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/scan/deregister"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 400) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("No scan found", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// List all the registered scans + /// List of scans registered + /// A server side error occurred. + public System.Threading.Tasks.Task> ListAllScansAsync() + { + return ListAllScansAsync(System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// List all the registered scans + /// List of scans registered + /// A server side error occurred. + public async System.Threading.Tasks.Task> ListAllScansAsync(System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/scan/listAll"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// List boxes which are not spent. + /// identifier of a scan + /// Minimal number of confirmations + /// Minimal box inclusion height + /// List of unspent boxes + /// A server side error occurred. + public System.Threading.Tasks.Task> ListUnspentScansAsync(int scanId, int? minConfirmations, int? minInclusionHeight) + { + return ListUnspentScansAsync(scanId, minConfirmations, minInclusionHeight, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// List boxes which are not spent. + /// identifier of a scan + /// Minimal number of confirmations + /// Minimal box inclusion height + /// List of unspent boxes + /// A server side error occurred. + public async System.Threading.Tasks.Task> ListUnspentScansAsync(int scanId, int? minConfirmations, int? minInclusionHeight, System.Threading.CancellationToken cancellationToken) + { + if (scanId == null) + throw new System.ArgumentNullException("scanId"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/scan/unspentBoxes/{scanId}?"); + urlBuilder_.Replace("{scanId}", System.Uri.EscapeDataString(ConvertToString(scanId, System.Globalization.CultureInfo.InvariantCulture))); + if (minConfirmations != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("minConfirmations") + "=").Append(System.Uri.EscapeDataString(ConvertToString(minConfirmations, System.Globalization.CultureInfo.InvariantCulture))).Append("&"); + } + if (minInclusionHeight != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("minInclusionHeight") + "=").Append(System.Uri.EscapeDataString(ConvertToString(minInclusionHeight, System.Globalization.CultureInfo.InvariantCulture))).Append("&"); + } + urlBuilder_.Length--; + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Stop scan-related box tracking + /// The box is not tracked anymore + /// A server side error occurred. + public System.Threading.Tasks.Task ScanStopTrackingAsync(ScanIdBoxId body) + { + return ScanStopTrackingAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Stop scan-related box tracking + /// The box is not tracked anymore + /// A server side error occurred. + public async System.Threading.Tasks.Task ScanStopTrackingAsync(ScanIdBoxId body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/scan/stopTracking"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Generate signature commitments for inputs of an unsigned transaction + /// Transaction-related hints + /// A server side error occurred. + public System.Threading.Tasks.Task GenerateCommitmentsAsync(GenerateCommitmentsRequest body) + { + return GenerateCommitmentsAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Generate signature commitments for inputs of an unsigned transaction + /// Transaction-related hints + /// A server side error occurred. + public async System.Threading.Tasks.Task GenerateCommitmentsAsync(GenerateCommitmentsRequest body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/wallet/generateCommitments"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 400) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Extract hints from a transaction + /// Hints for the transaction + /// A server side error occurred. + public System.Threading.Tasks.Task ExtractHintsAsync(HintExtractionRequest body) + { + return ExtractHintsAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Extract hints from a transaction + /// Hints for the transaction + /// A server side error occurred. + public async System.Threading.Tasks.Task ExtractHintsAsync(HintExtractionRequest body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/wallet/extractHints"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 400) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Adds a box to scans, writes box to database if it is not there. You can use scan number 10 to add a box to the wallet. + /// It the box is added successfully, then its id is returned + /// A server side error occurred. + public System.Threading.Tasks.Task AddBoxAsync(ScanIdsBox body) + { + return AddBoxAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Adds a box to scans, writes box to database if it is not there. You can use scan number 10 to add a box to the wallet. + /// It the box is added successfully, then its id is returned + /// A server side error occurred. + public async System.Threading.Tasks.Task AddBoxAsync(ScanIdsBox body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/scan/addBox"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// Shuts down the node + /// The node will be shut down in 5 seconds + /// A server side error occurred. + public System.Threading.Tasks.Task NodeShutdownAsync() + { + return NodeShutdownAsync(System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Shuts down the node + /// The node will be shut down in 5 seconds + /// A server side error occurred. + public async System.Threading.Tasks.Task NodeShutdownAsync(System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/node/shutdown"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Content = new System.Net.Http.StringContent(string.Empty, System.Text.Encoding.UTF8, "application/json"); + request_.Method = new System.Net.Http.HttpMethod("POST"); + + await PrepareRequestAsync(client_, request_, urlBuilder_).ConfigureAwait(false); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + await PrepareRequestAsync(client_, request_, url_).ConfigureAwait(false); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + return; + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + protected struct ObjectResponseResult + { + public ObjectResponseResult(T responseObject, string responseText) + { + this.Object = responseObject; + this.Text = responseText; + } + + public T Object { get; } + + public string Text { get; } + } + + public bool ReadResponseAsString { get; set; } + + protected virtual async System.Threading.Tasks.Task> ReadObjectResponseAsync(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Threading.CancellationToken cancellationToken) + { + if (response == null || response.Content == null) + { + return new ObjectResponseResult(default(T)!, string.Empty); + } + + if (ReadResponseAsString) + { + var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + var typedBody = Newtonsoft.Json.JsonConvert.DeserializeObject(responseText, JsonSerializerSettings); + return new ObjectResponseResult(typedBody!, responseText); + } + catch (Newtonsoft.Json.JsonException exception) + { + var message = "Could not deserialize the response body string as " + typeof(T).FullName + "."; + throw new ApiException(message, (int)response.StatusCode, responseText, headers, exception); + } + } + else + { + try + { + using (var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) + using (var streamReader = new System.IO.StreamReader(responseStream)) + using (var jsonTextReader = new Newtonsoft.Json.JsonTextReader(streamReader)) + { + var serializer = Newtonsoft.Json.JsonSerializer.Create(JsonSerializerSettings); + var typedBody = serializer.Deserialize(jsonTextReader); + return new ObjectResponseResult(typedBody!, string.Empty); + } + } + catch (Newtonsoft.Json.JsonException exception) + { + var message = "Could not deserialize the response body stream as " + typeof(T).FullName + "."; + throw new ApiException(message, (int)response.StatusCode, string.Empty, headers, exception); + } + } + } + + private string ConvertToString(object? value, System.Globalization.CultureInfo cultureInfo) + { + if (value == null) + { + return ""; + } + + if (value is System.Enum) + { + var name = System.Enum.GetName(value.GetType(), value); + if (name != null) + { + var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name); + if (field != null) + { + var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute)) + as System.Runtime.Serialization.EnumMemberAttribute; + if (attribute != null) + { + return attribute.Value != null ? attribute.Value : name; + } + } + + var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo)); + return converted == null ? string.Empty : converted; + } + } + else if (value is bool) + { + return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant(); + } + else if (value is byte[]) + { + return System.Convert.ToBase64String((byte[]) value); + } + else if (value.GetType().IsArray) + { + var array = System.Linq.Enumerable.OfType((System.Array) value); + return string.Join(",", System.Linq.Enumerable.Select(array, o => ConvertToString(o, cultureInfo))); + } + + var result = System.Convert.ToString(value, cultureInfo); + return result == null ? "" : result; + } + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class ErgoTransactionInput + { + [Newtonsoft.Json.JsonProperty("boxId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string BoxId { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("spendingProof", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public SpendingProof SpendingProof { get; set; } = new SpendingProof(); + + [Newtonsoft.Json.JsonProperty("extension", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.IDictionary Extension { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class ErgoTransactionDataInput + { + [Newtonsoft.Json.JsonProperty("boxId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string BoxId { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("extension", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.IDictionary Extension { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class ErgoTransactionUnsignedInput + { + [Newtonsoft.Json.JsonProperty("boxId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string BoxId { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("extension", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.IDictionary Extension { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// Spending proof for transaction input + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class SpendingProof + { + [Newtonsoft.Json.JsonProperty("proofBytes", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string ProofBytes { get; set; }= default!; + + /// Variables to be put into context + [Newtonsoft.Json.JsonProperty("extension", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.IDictionary Extension { get; set; } = new System.Collections.Generic.Dictionary(); + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class SerializedBox + { + [Newtonsoft.Json.JsonProperty("boxId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string BoxId { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("bytes", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Bytes { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class ErgoTransactionOutput + { + [Newtonsoft.Json.JsonProperty("boxId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string BoxId { get; set; }= default!; + + /// Amount of Ergo token + [Newtonsoft.Json.JsonProperty("value", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0D, double.MaxValue)] + public long Value { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("ergoTree", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string ErgoTree { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("address", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Address { get; set; }= default!; + + /// Height the output was created at + [Newtonsoft.Json.JsonProperty("creationHeight", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int CreationHeight { get; set; }= default!; + + /// Assets list in the transaction + [Newtonsoft.Json.JsonProperty("assets", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection Assets { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("additionalRegisters", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public Registers AdditionalRegisters { get; set; } = new Registers(); + + [Newtonsoft.Json.JsonProperty("transactionId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string TransactionId { get; set; }= default!; + + /// Index in the transaction outputs + [Newtonsoft.Json.JsonProperty("index", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int Index { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class WalletBox + { + [Newtonsoft.Json.JsonProperty("box", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public ErgoTransactionOutput Box { get; set; } = new ErgoTransactionOutput(); + + /// Number of confirmations, if the box is included into the blockchain + [Newtonsoft.Json.JsonProperty("confirmationsNum", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0, int.MaxValue)] + public int? ConfirmationsNum { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("address", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Address { get; set; }= default!; + + /// Transaction which created the box + [Newtonsoft.Json.JsonProperty("creationTransaction", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string CreationTransaction { get; set; }= default!; + + /// Transaction which created the box + [Newtonsoft.Json.JsonProperty("spendingTransaction", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string? SpendingTransaction { get; set; }= default!; + + /// The height the box was spent at + [Newtonsoft.Json.JsonProperty("spendingHeight", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0, int.MaxValue)] + public int? SpendingHeight { get; set; }= default!; + + /// The height the transaction containing the box was included in a block at + [Newtonsoft.Json.JsonProperty("inclusionHeight", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0, int.MaxValue)] + public int InclusionHeight { get; set; }= default!; + + /// A flag signalling whether the box is created on main chain + [Newtonsoft.Json.JsonProperty("onchain", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public bool Onchain { get; set; }= default!; + + /// A flag signalling whether the box was spent + [Newtonsoft.Json.JsonProperty("spent", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public bool Spent { get; set; }= default!; + + /// An index of a box in the creating transaction + [Newtonsoft.Json.JsonProperty("creationOutIndex", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int CreationOutIndex { get; set; }= default!; + + /// Scan identifiers the box relates to + [Newtonsoft.Json.JsonProperty("scans", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection Scans { get; set; } = new System.Collections.ObjectModel.Collection(); + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// Unsigned Ergo transaction + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class UnsignedErgoTransaction + { + [Newtonsoft.Json.JsonProperty("id", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Id { get; set; }= default!; + + /// Unsigned inputs of the transaction + [Newtonsoft.Json.JsonProperty("inputs", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection Inputs { get; set; } = new System.Collections.ObjectModel.Collection(); + + /// Data inputs of the transaction + [Newtonsoft.Json.JsonProperty("dataInputs", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection DataInputs { get; set; } = new System.Collections.ObjectModel.Collection(); + + /// Outputs of the transaction + [Newtonsoft.Json.JsonProperty("outputs", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection Outputs { get; set; } = new System.Collections.ObjectModel.Collection(); + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// Ergo transaction + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class ErgoTransaction + { + [Newtonsoft.Json.JsonProperty("id", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Id { get; set; }= default!; + + /// Inputs of the transaction + [Newtonsoft.Json.JsonProperty("inputs", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection Inputs { get; set; } = new System.Collections.ObjectModel.Collection(); + + /// Data inputs of the transaction + [Newtonsoft.Json.JsonProperty("dataInputs", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection DataInputs { get; set; } = new System.Collections.ObjectModel.Collection(); + + /// Outputs of the transaction + [Newtonsoft.Json.JsonProperty("outputs", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection Outputs { get; set; } = new System.Collections.ObjectModel.Collection(); + + /// Size in bytes + [Newtonsoft.Json.JsonProperty("size", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int Size { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// Transaction augmented with some useful information + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class WalletTransaction + { + [Newtonsoft.Json.JsonProperty("id", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Id { get; set; }= default!; + + /// Transaction inputs + [Newtonsoft.Json.JsonProperty("inputs", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection Inputs { get; set; } = new System.Collections.ObjectModel.Collection(); + + /// Transaction data inputs + [Newtonsoft.Json.JsonProperty("dataInputs", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection DataInputs { get; set; } = new System.Collections.ObjectModel.Collection(); + + /// Transaction outputs + [Newtonsoft.Json.JsonProperty("outputs", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection Outputs { get; set; } = new System.Collections.ObjectModel.Collection(); + + /// Height of a block the transaction was included in + [Newtonsoft.Json.JsonProperty("inclusionHeight", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int InclusionHeight { get; set; }= default!; + + /// Number of transaction confirmations + [Newtonsoft.Json.JsonProperty("numConfirmations", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int NumConfirmations { get; set; }= default!; + + /// Scan identifiers the transaction relates to + [Newtonsoft.Json.JsonProperty("scans", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection Scans { get; set; } = new System.Collections.ObjectModel.Collection(); + + /// Size in bytes + [Newtonsoft.Json.JsonProperty("size", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int Size { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// Hex-encoded big-endian 256-bits secret exponent "w" along with generators "g", "h", and group elements "u", "v", such as g^w = u, h^w = v + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class DhtSecret + { + /// Hex-encoded big-endian 256-bits secret exponent + [Newtonsoft.Json.JsonProperty("secret", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Secret { get; set; }= default!; + + /// Hex-encoded "g" generator for the Diffie-Hellman tuple (secp256k1 curve point) + [Newtonsoft.Json.JsonProperty("g", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string G { get; set; }= default!; + + /// Hex-encoded "h" generator for the Diffie-Hellman tuple (secp256k1 curve point) + [Newtonsoft.Json.JsonProperty("h", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string H { get; set; }= default!; + + /// Hex-encoded "u" group element of the Diffie-Hellman tuple (secp256k1 curve point) + [Newtonsoft.Json.JsonProperty("u", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string U { get; set; }= default!; + + /// Hex-encoded "v" group element of the Diffie-Hellman tuple (secp256k1 curve point) + [Newtonsoft.Json.JsonProperty("v", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string V { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// A request to sign a transaction with secrets provided + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class TransactionSigningRequest + { + /// Unsigned transaction to sign + [Newtonsoft.Json.JsonProperty("tx", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public UnsignedErgoTransaction Tx { get; set; } = new UnsignedErgoTransaction(); + + /// Optional list of inputs to be used in serialized form + [Newtonsoft.Json.JsonProperty("inputsRaw", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection InputsRaw { get; set; }= default!; + + /// Optional list of inputs to be used in serialized form + [Newtonsoft.Json.JsonProperty("dataInputsRaw", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection DataInputsRaw { get; set; }= default!; + + /// Optional list of hints used for signing + [Newtonsoft.Json.JsonProperty("hints", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public TransactionHintsBag Hints { get; set; }= default!; + + /// Secrets used for signing + [Newtonsoft.Json.JsonProperty("secrets", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public Secrets Secrets { get; set; } = new Secrets(); + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// Holds encoded ErgoAddress + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class AddressHolder + { + [Newtonsoft.Json.JsonProperty("address", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Address { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// Holds request for wallet boxes + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class BoxesRequestHolder + { + /// Target assets + [Newtonsoft.Json.JsonProperty("targetAssets", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection> TargetAssets { get; set; } = new List>(); + + /// Target balance + [Newtonsoft.Json.JsonProperty("targetBalance", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public long TargetBalance { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// Holds many transaction requests and transaction fee + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class RequestsHolder + { + /// Sequence of transaction requests + [Newtonsoft.Json.JsonProperty("requests", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection Requests { get; set; } = new System.Collections.ObjectModel.Collection(); + + /// Transaction fee + [Newtonsoft.Json.JsonProperty("fee", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public long Fee { get; set; }= default!; + + /// List of inputs to be used in serialized form + [Newtonsoft.Json.JsonProperty("inputsRaw", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection InputsRaw { get; set; }= default!; + + /// List of data inputs to be used in serialized form + [Newtonsoft.Json.JsonProperty("dataInputsRaw", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection DataInputsRaw { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class SourceHolder + { + /// Sigma source to be compiled + [Newtonsoft.Json.JsonProperty("source", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Source { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class ErgoLikeTransaction + { + [Newtonsoft.Json.JsonProperty("id", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Id { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("inputs", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection Inputs { get; set; } = new System.Collections.ObjectModel.Collection(); + + [Newtonsoft.Json.JsonProperty("dataInputs", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection DataInputs { get; set; } = new System.Collections.ObjectModel.Collection(); + + [Newtonsoft.Json.JsonProperty("outputs", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection Outputs { get; set; } = new System.Collections.ObjectModel.Collection(); + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// Block header format used for sigma ErgoLikeContext + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class SigmaHeader + { + [Newtonsoft.Json.JsonProperty("id", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Id { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("timestamp", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public long Timestamp { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("version", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int Version { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("adProofsRoot", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string AdProofsRoot { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("adProofsId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string AdProofsId { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("stateRoot", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public AvlTreeData StateRoot { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("transactionsRoot", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string TransactionsRoot { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("transactionsId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string TransactionsId { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("nBits", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0D, double.MaxValue)] + public long NBits { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("extensionHash", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string ExtensionHash { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("extensionRoot", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string ExtensionRoot { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("extensionId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string ExtensionId { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("height", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0, int.MaxValue)] + public int Height { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("size", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0, int.MaxValue)] + public int Size { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("parentId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string ParentId { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("powSolutions", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public PowSolutions PowSolutions { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("votes", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Votes { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("minerPk", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string MinerPk { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("powOnetimePk", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string PowOnetimePk { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("powNonce", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string PowNonce { get; set; }= default!; + + /// sigma.BigInt + [Newtonsoft.Json.JsonProperty("powDistance", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public double PowDistance { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class PreHeader + { + [Newtonsoft.Json.JsonProperty("timestamp", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public long Timestamp { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("version", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int Version { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("nBits", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0D, double.MaxValue)] + public long NBits { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("height", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0, int.MaxValue)] + public int Height { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("parentId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string ParentId { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("votes", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Votes { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("minerPk", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string MinerPk { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class AvlTreeData + { + [Newtonsoft.Json.JsonProperty("digest", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Digest { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("treeFlags", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int TreeFlags { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("keyLength", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int KeyLength { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("valueLength", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int? ValueLength { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class ErgoLikeContext + { + /// state root before current block application + [Newtonsoft.Json.JsonProperty("lastBlockUtxoRoot", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public AvlTreeData LastBlockUtxoRoot { get; set; }= default!; + + /// fixed number of last block headers in descending order (first header is the newest one) + [Newtonsoft.Json.JsonProperty("headers", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection Headers { get; set; } = new System.Collections.ObjectModel.Collection(); + + /// fields of block header with the current `spendingTransaction`, that can be predicted by a miner before its formation + [Newtonsoft.Json.JsonProperty("preHeader", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public PreHeader PreHeader { get; set; } = new PreHeader(); + + /// boxes, that corresponds to id's of `spendingTransaction.dataInputs` + [Newtonsoft.Json.JsonProperty("dataBoxes", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection DataBoxes { get; set; } = new System.Collections.ObjectModel.Collection(); + + /// boxes, that corresponds to id's of `spendingTransaction.inputs` + [Newtonsoft.Json.JsonProperty("boxesToSpend", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection BoxesToSpend { get; set; } = new System.Collections.ObjectModel.Collection(); + + /// transaction that contains `self` box + [Newtonsoft.Json.JsonProperty("spendingTransaction", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public ErgoLikeTransaction SpendingTransaction { get; set; } = new ErgoLikeTransaction(); + + /// index of the box in `boxesToSpend` that contains the script we're evaluating + [Newtonsoft.Json.JsonProperty("selfIndex", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public long SelfIndex { get; set; }= default!; + + /// prover-defined key-value pairs, that may be used inside a script + [Newtonsoft.Json.JsonProperty("extension", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public object Extension { get; set; } = new object(); + + /// validation parameters passed to Interpreter.verify to detect soft-fork conditions + [Newtonsoft.Json.JsonProperty("validationSettings", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string ValidationSettings { get; set; }= default!; + + /// hard limit on accumulated execution cost, if exceeded lead to CostLimitException to be thrown + [Newtonsoft.Json.JsonProperty("costLimit", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public long CostLimit { get; set; }= default!; + + /// initial value of execution cost already accumulated before Interpreter.verify is called + [Newtonsoft.Json.JsonProperty("initCost", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public long InitCost { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class ExecuteScript + { + /// Sigma script to be executed + [Newtonsoft.Json.JsonProperty("script", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Script { get; set; }= default!; + + /// Environment for compiler + [Newtonsoft.Json.JsonProperty("namedConstants", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public object? NamedConstants { get; set; }= default!; + + /// Interpreter context + [Newtonsoft.Json.JsonProperty("context", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public ErgoLikeContext? Context { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// Algebraic data type of sigma proposition expressions + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class SigmaBoolean + { + /// Sigma opCode + [Newtonsoft.Json.JsonProperty("op", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int Op { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("h", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string H { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("g", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string G { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("u", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string U { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("v", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string V { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("condition", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public bool Condition { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class SigmaBooleanAndPredicate : SigmaBoolean + { + [Newtonsoft.Json.JsonProperty("args", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection Args { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class SigmaBooleanOrPredicate : SigmaBoolean + { + [Newtonsoft.Json.JsonProperty("args", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection Args { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class SigmaBooleanThresholdPredicate : SigmaBoolean + { + [Newtonsoft.Json.JsonProperty("args", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection Args { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// Result of executeWithContext request (reduceToCrypto) + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class CryptoResult + { + /// value of SigmaProp type which represents a statement verifiable via sigma protocol + [Newtonsoft.Json.JsonProperty("value", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public SigmaBoolean Value { get; set; }= default!; + + /// Estimated cost of contract execution + [Newtonsoft.Json.JsonProperty("cost", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public long Cost { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class ScanningPredicate + { + [Newtonsoft.Json.JsonProperty("predicate", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Predicate { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class ContainsPredicate : ScanningPredicate + { + [Newtonsoft.Json.JsonProperty("register", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Register { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("bytes", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Bytes { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class EqualsPredicate : ScanningPredicate + { + [Newtonsoft.Json.JsonProperty("register", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Register { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("bytes", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Bytes { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class ContainsAssetPredicate : ScanningPredicate + { + [Newtonsoft.Json.JsonProperty("assetId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string AssetId { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class AndPredicate : ScanningPredicate + { + [Newtonsoft.Json.JsonProperty("args", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection Args { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class OrPredicate : ScanningPredicate + { + [Newtonsoft.Json.JsonProperty("args", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection Args { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class ScanRequest + { + [Newtonsoft.Json.JsonProperty("scanName", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string ScanName { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("trackingRule", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public ScanningPredicate TrackingRule { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class Scan + { + [Newtonsoft.Json.JsonProperty("scanName", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string ScanName { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("scanId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int ScanId { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("trackingRule", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public ScanningPredicate TrackingRule { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class ScanId + { + [Newtonsoft.Json.JsonProperty("scanId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int ScanId1 { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class ScanIdBoxId + { + [Newtonsoft.Json.JsonProperty("scanId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int ScanId { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("boxId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string BoxId { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// Ergo box with associated scans (their respective identifiers) + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class ScanIdsBox + { + [Newtonsoft.Json.JsonProperty("scanIds", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection ScanIds { get; set; } = new System.Collections.ObjectModel.Collection(); + + [Newtonsoft.Json.JsonProperty("box", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public ErgoTransactionOutput Box { get; set; } = new ErgoTransactionOutput(); + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// Randomness and commitment for the first step of the Schnorr protocol + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class DlogCommitment + { + /// Hex-encoded big-endian 256-bits secret exponent + [Newtonsoft.Json.JsonProperty("r", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string R { get; set; }= default!; + + /// Hex-encoded "g" generator for the Diffie-Hellman tuple (secp256k1 curve point) + [Newtonsoft.Json.JsonProperty("a", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string A { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// request to extract prover hints from a transaction + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class HintExtractionRequest + { + /// Transaction to extract prover hints from + [Newtonsoft.Json.JsonProperty("tx", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public ErgoTransaction Tx { get; set; } = new ErgoTransaction(); + + /// Real signers of the transaction + [Newtonsoft.Json.JsonProperty("real", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection Real { get; set; } = new System.Collections.ObjectModel.Collection(); + + /// Simulated signers of the transaction + [Newtonsoft.Json.JsonProperty("simulated", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection Simulated { get; set; } = new System.Collections.ObjectModel.Collection(); + + /// Optional list of inputs to be used in serialized form + [Newtonsoft.Json.JsonProperty("inputsRaw", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection InputsRaw { get; set; }= default!; + + /// Optional list of inputs to be used in serialized form + [Newtonsoft.Json.JsonProperty("dataInputsRaw", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection DataInputsRaw { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// basic trait for prover commitments + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class Commitment + { + [Newtonsoft.Json.JsonProperty("hint", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public CommitmentHint Hint { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("pubkey", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public SigmaBoolean Pubkey { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("position", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Position { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("type", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public CommitmentType Type { get; set; }= default!; + + /// a group element of the commitment + [Newtonsoft.Json.JsonProperty("a", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string A { get; set; }= default!; + + /// b group element of the commitment (needed for DHT protocol only) + [Newtonsoft.Json.JsonProperty("b", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string B { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class SecretProven + { + [Newtonsoft.Json.JsonProperty("hint", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public SecretProvenHint Hint { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("challenge", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Challenge { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("pubkey", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public SigmaBoolean Pubkey { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("proof", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Proof { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("position", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Position { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// hints for inputs, key is input index, values is a set of hints for the input + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class InputHints : System.Collections.Generic.Dictionary> + { + + } + + /// prover hints extracted from a transaction + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class TransactionHintsBag + { + /// Hints which contain secrets, do not share them! + [Newtonsoft.Json.JsonProperty("secretHints", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection SecretHints { get; set; }= default!; + + /// Hints which contain public data only, share them freely! + [Newtonsoft.Json.JsonProperty("publicHints", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection PublicHints { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// request to generate commitments to sign a transaction + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class GenerateCommitmentsRequest + { + /// Unsigned transaction to sign + [Newtonsoft.Json.JsonProperty("tx", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public UnsignedErgoTransaction Tx { get; set; } = new UnsignedErgoTransaction(); + + /// Optionally, external secrets used for signing + [Newtonsoft.Json.JsonProperty("secrets", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public Secrets2 Secrets { get; set; }= default!; + + /// Optional list of inputs to be used in serialized form + [Newtonsoft.Json.JsonProperty("inputsRaw", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection InputsRaw { get; set; }= default!; + + /// Optional list of inputs to be used in serialized form + [Newtonsoft.Json.JsonProperty("dataInputsRaw", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection DataInputsRaw { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// Request for generation of payment transaction to a given address + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class PaymentRequest + { + [Newtonsoft.Json.JsonProperty("address", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Address { get; set; }= default!; + + /// Payment amount + [Newtonsoft.Json.JsonProperty("value", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public long Value { get; set; }= default!; + + /// Assets list in the transaction + [Newtonsoft.Json.JsonProperty("assets", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection Assets { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("registers", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public Registers Registers { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// Request for generation of asset issue transaction + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class AssetIssueRequest + { + /// Optional, first address in the wallet will be used if not defined + [Newtonsoft.Json.JsonProperty("address", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Address { get; set; }= default!; + + /// Optional, amount of ergs to be put into box with issued assets + [Newtonsoft.Json.JsonProperty("ergValue", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int ErgValue { get; set; }= default!; + + /// Supply amount + [Newtonsoft.Json.JsonProperty("amount", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public long Amount { get; set; }= default!; + + /// Assets name + [Newtonsoft.Json.JsonProperty("name", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Name { get; set; }= default!; + + /// Assets description + [Newtonsoft.Json.JsonProperty("description", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Description { get; set; }= default!; + + /// Number of decimal places + [Newtonsoft.Json.JsonProperty("decimals", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int Decimals { get; set; }= default!; + + /// Optional, possible values for registers R7...R9 + [Newtonsoft.Json.JsonProperty("registers", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public Registers Registers { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// Block with header and transactions + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class FullBlock + { + [Newtonsoft.Json.JsonProperty("header", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public BlockHeader Header { get; set; } = new BlockHeader(); + + [Newtonsoft.Json.JsonProperty("blockTransactions", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public BlockTransactions BlockTransactions { get; set; } = new BlockTransactions(); + + [Newtonsoft.Json.JsonProperty("adProofs", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public BlockADProofs AdProofs { get; set; } = new BlockADProofs(); + + [Newtonsoft.Json.JsonProperty("extension", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public Extension Extension { get; set; } = new Extension(); + + /// Size in bytes + [Newtonsoft.Json.JsonProperty("size", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int Size { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// An object containing all components of pow solution + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class PowSolutions + { + /// Base16-encoded public key + [Newtonsoft.Json.JsonProperty("pk", NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Pk { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("w", NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string W { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("n", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string N { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("d", NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int D { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class BlockHeaderWithoutPow + { + [Newtonsoft.Json.JsonProperty("id", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Id { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("timestamp", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public long Timestamp { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("version", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int Version { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("adProofsRoot", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string AdProofsRoot { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("stateRoot", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string StateRoot { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("transactionsRoot", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string TransactionsRoot { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("nBits", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0D, double.MaxValue)] + public long NBits { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("extensionHash", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string ExtensionHash { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("height", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0, int.MaxValue)] + public int Height { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("difficulty", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0, int.MaxValue)] + public int Difficulty { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("parentId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string ParentId { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("votes", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Votes { get; set; }= default!; + + /// Size in bytes + [Newtonsoft.Json.JsonProperty("size", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int Size { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("extensionId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string ExtensionId { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("transactionsId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string TransactionsId { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("adProofsId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string AdProofsId { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class PopowHeader + { + [Newtonsoft.Json.JsonProperty("header", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public BlockHeader Header { get; set; } = new BlockHeader(); + + /// Array of header interlinks + [Newtonsoft.Json.JsonProperty("interlinks", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection Interlinks { get; set; } = new System.Collections.ObjectModel.Collection(); + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class NipopowProof + { + /// security parameter (min μ-level superchain length) + [Newtonsoft.Json.JsonProperty("m", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public double M { get; set; }= default!; + + /// security parameter (min suffix length, >= 1) + [Newtonsoft.Json.JsonProperty("k", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public double K { get; set; }= default!; + + /// proof prefix headers + [Newtonsoft.Json.JsonProperty("prefix", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection Prefix { get; set; } = new System.Collections.ObjectModel.Collection(); + + [Newtonsoft.Json.JsonProperty("suffixHead", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public PopowHeader SuffixHead { get; set; } = new PopowHeader(); + + /// tail of the proof suffix headers + [Newtonsoft.Json.JsonProperty("suffixTail", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection SuffixTail { get; set; } = new System.Collections.ObjectModel.Collection(); + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class BlockHeader + { + [Newtonsoft.Json.JsonProperty("id", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Id { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("timestamp", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public long Timestamp { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("version", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int Version { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("adProofsRoot", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string AdProofsRoot { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("stateRoot", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string StateRoot { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("transactionsRoot", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string TransactionsRoot { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("nBits", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0D, double.MaxValue)] + public long NBits { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("extensionHash", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string ExtensionHash { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("powSolutions", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public PowSolutions PowSolutions { get; set; } = new PowSolutions(); + + [Newtonsoft.Json.JsonProperty("height", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0, int.MaxValue)] + public int Height { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("difficulty", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0, int.MaxValue)] + public string Difficulty { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("parentId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string ParentId { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("votes", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Votes { get; set; }= default!; + + /// Size in bytes + [Newtonsoft.Json.JsonProperty("size", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int Size { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("extensionId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string ExtensionId { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("transactionsId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string TransactionsId { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("adProofsId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string AdProofsId { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class BlockTransactions + { + [Newtonsoft.Json.JsonProperty("headerId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string HeaderId { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("transactions", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public Transactions Transactions { get; set; } = new Transactions(); + + /// Size in bytes + [Newtonsoft.Json.JsonProperty("size", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int Size { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class BlockADProofs + { + [Newtonsoft.Json.JsonProperty("headerId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string HeaderId { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("proofBytes", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string ProofBytes { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("digest", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Digest { get; set; }= default!; + + /// Size in bytes + [Newtonsoft.Json.JsonProperty("size", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int Size { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class Extension + { + [Newtonsoft.Json.JsonProperty("headerId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string HeaderId { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("digest", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Digest { get; set; }= default!; + + /// List of key-value records + [Newtonsoft.Json.JsonProperty("fields", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection? Fields { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class KeyValueItem : System.Collections.ObjectModel.Collection + { + + } + + /// Can be null if node is not mining or candidate block is not ready + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class CandidateBlock + { + [Newtonsoft.Json.JsonProperty("version", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int Version { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("extensionHash", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string ExtensionHash { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("timestamp", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public long Timestamp { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("stateRoot", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string StateRoot { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("nBits", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0D, double.MaxValue)] + public long NBits { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("adProofBytes", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string AdProofBytes { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("parentId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string ParentId { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("transactionsNumber", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int TransactionsNumber { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("transactions", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public Transactions Transactions { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("votes", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Votes { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// Merkle proof for a leaf, which is an array of bytes (e.g. a transaction identifier) + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class MerkleProof + { + /// Base16-encoded Merkle tree leaf bytes + [Newtonsoft.Json.JsonProperty("leaf", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Leaf { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("levels", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection> Levels { get; set; } = new List>(); + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// Proof that a block corresponding to given header without PoW contains some transactions + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class ProofOfUpcomingTransactions + { + /// Base16-encoded serialized header without Proof-of-Work + [Newtonsoft.Json.JsonProperty("msgPreimage", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string MsgPreimage { get; set; }= default!; + + /// Merkle proofs of transactions included into blocks (not necessarily all the block transactions) + [Newtonsoft.Json.JsonProperty("txProofs", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection TxProofs { get; set; } = new System.Collections.ObjectModel.Collection(); + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// Block candidate related data for external miner to perform work + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class WorkMessage + { + /// Base16-encoded block header bytes without PoW solution + [Newtonsoft.Json.JsonProperty("msg", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Msg { get; set; }= default!; + + /// Work target value + [Newtonsoft.Json.JsonProperty("b", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public BigInteger B { get; set; }= default!; + + /// Block height + [Newtonsoft.Json.JsonProperty("h", Required = Newtonsoft.Json.Required.Always)] + public uint Height { get; set; }= default!; + + /// Base16-encoded miner public key + [Newtonsoft.Json.JsonProperty("pk", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Pk { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("proof", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public ProofOfUpcomingTransactions Proof { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class Peer + { + [Newtonsoft.Json.JsonProperty("address", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Address { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("name", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string? Name { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("lastSeen", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int LastSeen { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("connectionType", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public PeerConnectionType? ConnectionType { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class PeersStatus + { + [Newtonsoft.Json.JsonProperty("lastIncomingMessage", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int LastIncomingMessage { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("currentNetworkTime", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int CurrentNetworkTime { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class NodeInfo + { + [Newtonsoft.Json.JsonProperty("name", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Name { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("appVersion", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string AppVersion { get; set; }= default!; + + /// Can be 'null' if state is empty (no full block is applied since node launch) + [Newtonsoft.Json.JsonProperty("fullHeight", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0, int.MaxValue)] + public int? FullHeight { get; set; }= default!; + + /// Can be 'null' if state is empty (no header applied since node launch) + [Newtonsoft.Json.JsonProperty("headersHeight", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0, int.MaxValue)] + public int? HeadersHeight { get; set; }= default!; + + /// Can be 'null' if no full block is applied since node launch + [Newtonsoft.Json.JsonProperty("bestFullHeaderId", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string? BestFullHeaderId { get; set; }= default!; + + /// Can be 'null' if no full block is applied since node launch + [Newtonsoft.Json.JsonProperty("previousFullHeaderId", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string? PreviousFullHeaderId { get; set; }= default!; + + /// Can be 'null' if no header applied since node launch + [Newtonsoft.Json.JsonProperty("bestHeaderId", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string? BestHeaderId { get; set; }= default!; + + /// Can be 'null' if state is empty (no full block is applied since node launch) + [Newtonsoft.Json.JsonProperty("stateRoot", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string? StateRoot { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("stateType", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public NodeInfoStateType StateType { get; set; }= default!; + + /// Can be 'null' if no full block is applied since node launch + [Newtonsoft.Json.JsonProperty("stateVersion", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string? StateVersion { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("isMining", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public bool IsMining { get; set; }= default!; + + /// Number of connected peers + [Newtonsoft.Json.JsonProperty("peersCount", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0, int.MaxValue)] + public int PeersCount { get; set; }= default!; + + /// Current unconfirmed transactions count + [Newtonsoft.Json.JsonProperty("unconfirmedCount", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0, 10000)] + public int UnconfirmedCount { get; set; }= default!; + + /// Difficulty on current bestFullHeaderId. Can be 'null' if no full block is applied since node launch + [Newtonsoft.Json.JsonProperty("difficulty", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0, int.MaxValue)] + public JToken Difficulty { get; set; }= default!; + + /// Current internal node time + [Newtonsoft.Json.JsonProperty("currentTime", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public long CurrentTime { get; set; }= default!; + + /// Time when the node was started + [Newtonsoft.Json.JsonProperty("launchTime", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public long LaunchTime { get; set; }= default!; + + /// Can be 'null' if no headers is applied since node launch + [Newtonsoft.Json.JsonProperty("headersScore", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string HeadersScore { get; set; }= default!; + + /// Can be 'null' if no full block is applied since node launch + [Newtonsoft.Json.JsonProperty("fullBlocksScore", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string FullBlocksScore { get; set; }= default!; + + /// Can be 'null' if genesis blocks is not produced yet + [Newtonsoft.Json.JsonProperty("genesisBlockId", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string? GenesisBlockId { get; set; }= default!; + + /// current parameters + [Newtonsoft.Json.JsonProperty("parameters", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required] + public Parameters Parameters { get; set; } = new Parameters(); + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class Parameters + { + /// Height when current parameters were considered(not actual height). Can be '0' if state is empty + [Newtonsoft.Json.JsonProperty("height", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0, int.MaxValue)] + public int Height { get; set; }= default!; + + /// Storage fee coefficient (per byte per storage period ~4 years) + [Newtonsoft.Json.JsonProperty("storageFeeFactor", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0, int.MaxValue)] + public int StorageFeeFactor { get; set; }= default!; + + /// Minimum value per byte of an output + [Newtonsoft.Json.JsonProperty("minValuePerByte", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0, int.MaxValue)] + public int MinValuePerByte { get; set; }= default!; + + /// Maximum block size (in bytes) + [Newtonsoft.Json.JsonProperty("maxBlockSize", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0, int.MaxValue)] + public int MaxBlockSize { get; set; }= default!; + + /// Maximum cumulative computational cost of input scripts in block transactions + [Newtonsoft.Json.JsonProperty("maxBlockCost", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0, int.MaxValue)] + public int MaxBlockCost { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("blockVersion", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int BlockVersion { get; set; }= default!; + + /// Validation cost of a single token + [Newtonsoft.Json.JsonProperty("tokenAccessCost", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0, int.MaxValue)] + public int TokenAccessCost { get; set; }= default!; + + /// Validation cost per one transaction input + [Newtonsoft.Json.JsonProperty("inputCost", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0, int.MaxValue)] + public int InputCost { get; set; }= default!; + + /// Validation cost per one data input + [Newtonsoft.Json.JsonProperty("dataInputCost", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0, int.MaxValue)] + public int DataInputCost { get; set; }= default!; + + /// Validation cost per one transaction output + [Newtonsoft.Json.JsonProperty("outputCost", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0, int.MaxValue)] + public int OutputCost { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// Ergo transaction objects + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class Transactions : System.Collections.ObjectModel.Collection + { + + } + + /// Fee histogram bin + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class FeeHistogramBin + { + [Newtonsoft.Json.JsonProperty("nTxns", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int NTxns { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("totalFee", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public long TotalFee { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// Fee histogram for transactions in mempool + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class FeeHistogram : System.Collections.ObjectModel.Collection + { + + } + + /// Token detail in the transaction + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class Asset + { + [Newtonsoft.Json.JsonProperty("tokenId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string TokenId { get; set; }= default!; + + /// Amount of the token + [Newtonsoft.Json.JsonProperty("amount", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public long Amount { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// Ergo box registers + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class Registers : System.Collections.Generic.Dictionary + { + + } + + /// Emission info for height + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class EmissionInfo + { + [Newtonsoft.Json.JsonProperty("minerReward", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public long MinerReward { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("totalCoinsIssued", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public long TotalCoinsIssued { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("totalRemainCoins", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public long TotalRemainCoins { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// Amount of Ergo tokens and assets + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class BalancesSnapshot + { + [Newtonsoft.Json.JsonProperty("height", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int Height { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("balance", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public long Balance { get; set; }= default!; + + // The node returns an object for this rather than an array, causing deserialize to crash + //[Newtonsoft.Json.JsonProperty("assets", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + //public System.Collections.Generic.ICollection Assets { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + /// Validity status of Ergo address + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class AddressValidity + { + [Newtonsoft.Json.JsonProperty("address", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Address { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("isValid", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public bool IsValid { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("error", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Error { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class ApiError + { + /// Error code + [Newtonsoft.Json.JsonProperty("error", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int Error { get; set; }= default!; + + /// String error code + [Newtonsoft.Json.JsonProperty("reason", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Reason { get; set; }= default!; + + /// Detailed error description + [Newtonsoft.Json.JsonProperty("detail", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string? Detail { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class Body + { + /// Password to encrypt wallet file with + [Newtonsoft.Json.JsonProperty("pass", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Pass { get; set; }= default!; + + /// Optional pass to password-protect mnemonic seed + [Newtonsoft.Json.JsonProperty("mnemonicPass", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string MnemonicPass { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class Body2 + { + /// Password to encrypt wallet file with + [Newtonsoft.Json.JsonProperty("pass", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Pass { get; set; }= default!; + + /// Mnemonic seed + [Newtonsoft.Json.JsonProperty("mnemonic", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Mnemonic { get; set; }= default!; + + /// Optional pass to password-protect mnemonic seed + [Newtonsoft.Json.JsonProperty("mnemonicPass", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string MnemonicPass { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class Body3 + { + /// Mnemonic seed (optional) + [Newtonsoft.Json.JsonProperty("mnemonic", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Mnemonic { get; set; }= default!; + + /// Optional pass to password-protect mnemonic seed + [Newtonsoft.Json.JsonProperty("mnemonicPass", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string MnemonicPass { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class Body4 + { + /// Password to decrypt wallet file with + [Newtonsoft.Json.JsonProperty("pass", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Pass { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class Body5 + { + /// Pay2PubKey address + [Newtonsoft.Json.JsonProperty("address", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Address { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class Body6 + { + /// Derivation path for a new secret to derive + [Newtonsoft.Json.JsonProperty("derivationPath", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string DerivationPath { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class Response + { + /// Mnemonic seed phrase + [Newtonsoft.Json.JsonProperty("mnemonic", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Mnemonic { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class Response2 + { + /// true if passphrase matches wallet, false otherwise + [Newtonsoft.Json.JsonProperty("matched", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public bool Matched { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class Response3 + { + /// true if wallet is initialized, false otherwise + [Newtonsoft.Json.JsonProperty("isInitialized", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public bool IsInitialized { get; set; }= default!; + + /// true if wallet is unlocked, false otherwise + [Newtonsoft.Json.JsonProperty("isUnlocked", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public bool IsUnlocked { get; set; }= default!; + + /// address to send change to. Empty when wallet is not initialized or locked. By default change address correponds to root key address, could be set via /wallet/updateChangeAddress method. + [Newtonsoft.Json.JsonProperty("changeAddress", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string ChangeAddress { get; set; }= default!; + + /// last scanned height for the wallet (and external scans) + [Newtonsoft.Json.JsonProperty("walletHeight", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int WalletHeight { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class Response4 + { + [Newtonsoft.Json.JsonProperty("address", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Address { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class Response5 + { + /// Derivation path of the resulted secret + [Newtonsoft.Json.JsonProperty("derivationPath", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string DerivationPath { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("address", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Address { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class Response6 + { + [Newtonsoft.Json.JsonProperty("rewardAddress", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string RewardAddress { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class Response7 + { + [Newtonsoft.Json.JsonProperty("rewardPubKey", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string RewardPubKey { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class Response8 + { + /// serialized Ergo tree + [Newtonsoft.Json.JsonProperty("tree", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Tree { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class Response9 + { + /// Base16-encoded bytes + [Newtonsoft.Json.JsonProperty("tree", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Tree { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class Secrets + { + /// Sequence of secret exponents (DLOG secrets) + [Newtonsoft.Json.JsonProperty("dlog", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection Dlog { get; set; }= default!; + + /// Sequence of secret Diffie-Hellman tuple exponents (DHT secrets) + [Newtonsoft.Json.JsonProperty("dht", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection Dht { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class Requests + { + [Newtonsoft.Json.JsonProperty("address", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Address { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("value", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(0D, double.MaxValue)] + public long Value { get; set; }= default!; + + [Newtonsoft.Json.JsonProperty("assets", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection Assets { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public enum CommitmentHint + { + [System.Runtime.Serialization.EnumMember(Value = @"cmtWithSecret")] + CmtWithSecret = 0, + + [System.Runtime.Serialization.EnumMember(Value = @"cmtReal")] + CmtReal = 1, + + [System.Runtime.Serialization.EnumMember(Value = @"cmtSimulated")] + CmtSimulated = 2, + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public enum CommitmentType + { + [System.Runtime.Serialization.EnumMember(Value = @"dlog")] + Dlog = 0, + + [System.Runtime.Serialization.EnumMember(Value = @"dht")] + Dht = 1, + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public enum SecretProvenHint + { + [System.Runtime.Serialization.EnumMember(Value = @"proofReal")] + ProofReal = 0, + + [System.Runtime.Serialization.EnumMember(Value = @"proofSimulated")] + ProofSimulated = 1, + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class Secrets2 + { + /// Sequence of secret exponents (DLOG secrets) + [Newtonsoft.Json.JsonProperty("dlog", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection Dlog { get; set; }= default!; + + /// Sequence of secret Diffie-Hellman tuple exponents (DHT secrets) + [Newtonsoft.Json.JsonProperty("dht", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection Dht { get; set; }= default!; + + private System.Collections.Generic.IDictionary _additionalProperties = new System.Collections.Generic.Dictionary(); + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties; } + set { _additionalProperties = value; } + } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public enum PeerConnectionType + { + [System.Runtime.Serialization.EnumMember(Value = @"Incoming")] + Incoming = 0, + + [System.Runtime.Serialization.EnumMember(Value = @"Outgoing")] + Outgoing = 1, + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public enum NodeInfoStateType + { + [System.Runtime.Serialization.EnumMember(Value = @"digest")] + Digest = 0, + + [System.Runtime.Serialization.EnumMember(Value = @"utxo")] + Utxo = 1, + + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.11.3.0 (NJsonSchema v10.4.4.0 (Newtonsoft.Json v12.0.0.0))")] + public partial class ApiException : System.Exception + { + public int StatusCode { get; private set; } + + public string? Response { get; private set; } + + public System.Collections.Generic.IReadOnlyDictionary> Headers { get; private set; } + + public ApiException(string message, int statusCode, string? response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Exception? innerException) + : base(message + "\n\nStatus: " + statusCode + "\nResponse: \n" + ((response == null) ? "(null)" : response.Substring(0, response.Length >= 512 ? 512 : response.Length)), innerException) + { + StatusCode = statusCode; + Response = response; + Headers = headers; + } + + public override string ToString() + { + return string.Format("HTTP Response: \n\n{0}\n\n{1}", Response, base.ToString()); + } + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.11.3.0 (NJsonSchema v10.4.4.0 (Newtonsoft.Json v12.0.0.0))")] + public partial class ApiException : ApiException + { + public TResult Result { get; private set; } + + public ApiException(string message, int statusCode, string? response, System.Collections.Generic.IReadOnlyDictionary> headers, TResult result, System.Exception? innerException) + : base(message, statusCode, response, headers, innerException) + { + Result = result; + } + } + +} + +#pragma warning restore 1591 +#pragma warning restore 1573 +#pragma warning restore 472 +#pragma warning restore 114 +#pragma warning restore 108 diff --git a/src/Miningcore/Blockchain/Ergo/RPC/ergo.nswag b/src/Miningcore/Blockchain/Ergo/RPC/ergo.nswag new file mode 100644 index 000000000..5da96ca99 --- /dev/null +++ b/src/Miningcore/Blockchain/Ergo/RPC/ergo.nswag @@ -0,0 +1,100 @@ +{ + "runtime": "Net50", + "defaultVariables": null, + "documentGenerator": { + "fromDocument": { + "url": "https://raw.githubusercontent.com/ergoplatform/ergo/master/src/main/resources/api/openapi.yaml", + "output": null, + "newLineBehavior": "Auto" + } + }, + "codeGenerators": { + "openApiToCSharpClient": { + "clientBaseClass": null, + "configurationClass": null, + "generateClientClasses": true, + "generateClientInterfaces": false, + "clientBaseInterface": null, + "injectHttpClient": true, + "disposeHttpClient": true, + "protectedMethods": [], + "generateExceptionClasses": true, + "exceptionClass": "ApiException", + "wrapDtoExceptions": true, + "useHttpClientCreationMethod": false, + "httpClientType": "System.Net.Http.HttpClient", + "useHttpRequestMessageCreationMethod": false, + "useBaseUrl": true, + "generateBaseUrlProperty": true, + "generateSyncMethods": false, + "generatePrepareRequestAndProcessResponseAsAsyncMethods": true, + "exposeJsonSerializerSettings": false, + "clientClassAccessModifier": "public", + "typeAccessModifier": "public", + "generateContractsOutput": false, + "contractsNamespace": null, + "contractsOutputFilePath": null, + "parameterDateTimeFormat": "s", + "parameterDateFormat": "yyyy-MM-dd", + "generateUpdateJsonSerializerSettingsMethod": true, + "useRequestAndResponseSerializationSettings": false, + "serializeTypeInformation": false, + "queryNullValue": "", + "className": "ErgoClient", + "operationGenerationMode": "MultipleClientsFromOperationId", + "additionalNamespaceUsages": [], + "additionalContractNamespaceUsages": [], + "generateOptionalParameters": false, + "generateJsonMethods": false, + "enforceFlagEnums": false, + "parameterArrayType": "System.Collections.Generic.IEnumerable", + "parameterDictionaryType": "System.Collections.Generic.IDictionary", + "responseArrayType": "System.Collections.Generic.ICollection", + "responseDictionaryType": "System.Collections.Generic.IDictionary", + "wrapResponses": false, + "wrapResponseMethods": [], + "generateResponseClasses": true, + "responseClass": "SwaggerResponse", + "namespace": "Miningcore.Blockchain.Ergo", + "requiredPropertiesMustBeDefined": false, + "dateType": "System.DateTimeOffset", + "jsonConverters": null, + "anyType": "object", + "dateTimeType": "System.DateTimeOffset", + "timeType": "System.TimeSpan", + "timeSpanType": "System.TimeSpan", + "arrayType": "System.Collections.Generic.ICollection", + "arrayInstanceType": "System.Collections.ObjectModel.Collection", + "dictionaryType": "System.Collections.Generic.IDictionary", + "dictionaryInstanceType": "System.Collections.Generic.Dictionary", + "arrayBaseType": "System.Collections.ObjectModel.Collection", + "dictionaryBaseType": "System.Collections.Generic.Dictionary", + "classStyle": "Poco", + "jsonLibrary": "NewtonsoftJson", + "generateDefaultValues": true, + "generateDataAnnotations": true, + "excludedTypeNames": [], + "excludedParameterNames": [], + "handleReferences": false, + "generateImmutableArrayProperties": false, + "generateImmutableDictionaryProperties": false, + "jsonSerializerSettingsTransformationMethod": null, + "inlineNamedArrays": false, + "inlineNamedDictionaries": false, + "inlineNamedTuples": true, + "inlineNamedAny": false, + "generateDtoTypes": true, + "generateOptionalPropertiesAsNullable": false, + "generateNullableReferenceTypes": true, + "templateDirectory": null, + "typeNameGeneratorType": null, + "propertyNameGeneratorType": null, + "enumNameGeneratorType": null, + "checksumCacheEnabled": false, + "serviceHost": null, + "serviceSchemes": null, + "output": "ErgoClient.cs", + "newLineBehavior": "Auto" + } + } +} \ No newline at end of file diff --git a/src/Miningcore/Blockchain/Ethereum/Configuration/EthereumPoolPaymentProcessingConfigExtra.cs b/src/Miningcore/Blockchain/Ethereum/Configuration/EthereumPoolPaymentProcessingConfigExtra.cs index 77065ccfa..d99e32725 100644 --- a/src/Miningcore/Blockchain/Ethereum/Configuration/EthereumPoolPaymentProcessingConfigExtra.cs +++ b/src/Miningcore/Blockchain/Ethereum/Configuration/EthereumPoolPaymentProcessingConfigExtra.cs @@ -11,5 +11,15 @@ public class EthereumPoolPaymentProcessingConfigExtra /// True to exempt uncle rewards from miner rewards /// public bool KeepUncles { get; set; } + + /// + /// Gas amount for payout tx (advanced users only) + /// + public ulong Gas { get; set; } + + /// + /// maximum amount you’re willing to pay + /// + public ulong MaxFeePerGas { get; set; } } } diff --git a/src/Miningcore/Blockchain/Ethereum/DaemonRequests/SendTransactionRequest.cs b/src/Miningcore/Blockchain/Ethereum/DaemonRequests/SendTransactionRequest.cs index fcaa1968b..22d3eb87c 100644 --- a/src/Miningcore/Blockchain/Ethereum/DaemonRequests/SendTransactionRequest.cs +++ b/src/Miningcore/Blockchain/Ethereum/DaemonRequests/SendTransactionRequest.cs @@ -41,5 +41,17 @@ public class SendTransactionRequest /// [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public string Data { get; set; } + + /// + /// Maximum fee per gas the sender is willing to pay to miners in wei. + /// + [JsonConverter(typeof(HexToIntegralTypeJsonConverter))] + public ulong MaxPriorityFeePerGas { get; set; } + + /// + /// The maximum total fee per gas the sender is willing to pay(includes the network / base fee and miner / priority fee) in wei + /// + [JsonConverter(typeof(HexToIntegralTypeJsonConverter))] + public ulong MaxFeePerGas { get; set; } } } diff --git a/src/Miningcore/Blockchain/Ethereum/DaemonResponses/GetBlockResponse.cs b/src/Miningcore/Blockchain/Ethereum/DaemonResponses/GetBlockResponse.cs index fd5ce61a9..49985ba85 100644 --- a/src/Miningcore/Blockchain/Ethereum/DaemonResponses/GetBlockResponse.cs +++ b/src/Miningcore/Blockchain/Ethereum/DaemonResponses/GetBlockResponse.cs @@ -177,5 +177,11 @@ public class Block /// Array of uncle hashes. /// public string[] Uncles { get; set; } + + /// + /// Base fee per gas. + /// + [JsonConverter(typeof(HexToIntegralTypeJsonConverter))] + public ulong BaseFeePerGas { get; set; } } } diff --git a/src/Miningcore/Blockchain/Ethereum/EthereumConstants.cs b/src/Miningcore/Blockchain/Ethereum/EthereumConstants.cs index 39bcc9e0b..07aed05e7 100644 --- a/src/Miningcore/Blockchain/Ethereum/EthereumConstants.cs +++ b/src/Miningcore/Blockchain/Ethereum/EthereumConstants.cs @@ -85,5 +85,6 @@ public static class EthCommands public const string SendTx = "eth_sendTransaction"; public const string UnlockAccount = "personal_unlockAccount"; public const string Subscribe = "eth_subscribe"; + public const string MaxPriorityFeePerGas = "eth_maxPriorityFeePerGas"; } } diff --git a/src/Miningcore/Blockchain/Ethereum/EthereumJobManager.cs b/src/Miningcore/Blockchain/Ethereum/EthereumJobManager.cs index d54e3f6b9..ae983373c 100644 --- a/src/Miningcore/Blockchain/Ethereum/EthereumJobManager.cs +++ b/src/Miningcore/Blockchain/Ethereum/EthereumJobManager.cs @@ -3,20 +3,15 @@ using System.Globalization; using System.IO; using System.Linq; -using System.Net; using System.Numerics; using System.Reactive.Linq; -using System.Reactive.Threading.Tasks; -using System.Text; using System.Threading; using System.Threading.Tasks; using Autofac; -using Miningcore.Blockchain.Bitcoin; using Miningcore.Blockchain.Ethereum.Configuration; using Miningcore.Blockchain.Ethereum.DaemonResponses; using Miningcore.Configuration; using Miningcore.Crypto.Hashing.Ethash; -using Miningcore.DaemonInterface; using Miningcore.Extensions; using Miningcore.JsonRpc; using Miningcore.Messaging; @@ -25,7 +20,6 @@ using Miningcore.Time; using Miningcore.Util; using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using NLog; using Block = Miningcore.Blockchain.Ethereum.DaemonResponses.Block; using Contract = Miningcore.Contracts.Contract; @@ -59,7 +53,7 @@ public EthereumJobManager( } private DaemonEndpointConfig[] daemonEndpoints; - private DaemonClient daemon; + private RpcClient rpcClient; private EthereumNetworkType networkType; private GethChainType chainType; private EthashFull ethash; @@ -150,23 +144,23 @@ private async Task GetBlockTemplateAsync(CancellationToke { logger.LogInvoke(); - var commands = new[] + var requests = new[] { - new DaemonCmd(EC.GetWork), - new DaemonCmd(EC.GetBlockByNumber, new[] { (object) "latest", true }) + new RpcRequest(EC.GetWork), + new RpcRequest(EC.GetBlockByNumber, new[] { (object) "latest", true }) }; - var results = await daemon.ExecuteBatchAnyAsync(logger, ct, commands); + var responses = await rpcClient.ExecuteBatchAsync(logger, ct, requests); - if(results.Any(x => x.Error != null)) + if(responses.Any(x => x.Error != null)) { - logger.Warn(() => $"Error(s) refreshing blocktemplate: {results.First(x => x.Error != null).Error.Message}"); + logger.Warn(() => $"Error(s) refreshing blocktemplate: {responses.First(x => x.Error != null).Error.Message}"); return null; } // extract results - var work = results[0].Response.ToObject(); - var block = results[1].Response.ToObject(); + var work = responses[0].Response.ToObject(); + var block = responses[1].Response.ToObject(); // append blockheight (Recent versions of geth return this as the 4th element in the getWork response, older geth does not) if(work.Length < 4) @@ -194,39 +188,33 @@ private async Task GetBlockTemplateAsync(CancellationToke private async Task ShowDaemonSyncProgressAsync(CancellationToken ct) { - var responses = await daemon.ExecuteCmdAllAsync(logger, EC.GetSyncState, ct); - var firstValidResponse = responses.FirstOrDefault(x => x.Error == null && x.Response != null)?.Response; + var syncStateResponse = await rpcClient.ExecuteAsync(logger, EC.GetSyncState, ct); - if(firstValidResponse != null) + if(syncStateResponse.Error == null) { // eth_syncing returns false if not synching - if(firstValidResponse is bool) + if(syncStateResponse.Response is false) return; - var syncStates = responses.Where(x => x.Error == null && x.Response != null && firstValidResponse is JObject) - .Select(x => ((JObject) x.Response).ToObject()) - .ToArray(); - - if(syncStates.Any()) + if(syncStateResponse.Response is SyncState syncState) { // get peer count - var response = await daemon.ExecuteCmdAllAsync(logger, EC.GetPeerCount, ct); - var validResponses = response.Where(x => x.Error == null && x.Response != null).ToArray(); - var peerCount = validResponses.Any() ? validResponses.Max(x => x.Response.IntegralFromHex()) : 0; + var getPeerCountResponse = await rpcClient.ExecuteAsync(logger, EC.GetPeerCount, ct); + var peerCount = getPeerCountResponse.Response.IntegralFromHex(); - if(syncStates.Any(x => x.WarpChunksAmount != 0)) + if(syncState.WarpChunksAmount != 0) { - var warpChunkAmount = syncStates.Min(x => x.WarpChunksAmount); - var warpChunkProcessed = syncStates.Max(x => x.WarpChunksProcessed); + var warpChunkAmount = syncState.WarpChunksAmount; + var warpChunkProcessed = syncState.WarpChunksProcessed; var percent = (double) warpChunkProcessed / warpChunkAmount * 100; logger.Info(() => $"Daemons have downloaded {percent:0.00}% of warp-chunks from {peerCount} peers"); } - else if(syncStates.Any(x => x.HighestBlock != 0)) + else if(syncState.HighestBlock != 0) { - var lowestHeight = syncStates.Min(x => x.CurrentBlock); - var totalBlocks = syncStates.Max(x => x.HighestBlock); + var lowestHeight = syncState.CurrentBlock; + var totalBlocks = syncState.HighestBlock; var percent = (double) lowestHeight / totalBlocks * 100; logger.Info(() => $"Daemons have downloaded {percent:0.00}% of blockchain from {peerCount} peers"); @@ -241,17 +229,17 @@ private async Task UpdateNetworkStatsAsync(CancellationToken ct) try { - var commands = new[] + var requests = new[] { - new DaemonCmd(EC.GetPeerCount), - new DaemonCmd(EC.GetBlockByNumber, new[] { (object) "latest", true }) + new RpcRequest(EC.GetPeerCount), + new RpcRequest(EC.GetBlockByNumber, new[] { (object) "latest", true }) }; - var results = await daemon.ExecuteBatchAnyAsync(logger, ct, commands); + var responses = await rpcClient.ExecuteBatchAsync(logger, ct, requests); - if(results.Any(x => x.Error != null)) + if(responses.Any(x => x.Error != null)) { - var errors = results.Where(x => x.Error != null) + var errors = responses.Where(x => x.Error != null) .ToArray(); if(errors.Any()) @@ -259,8 +247,8 @@ private async Task UpdateNetworkStatsAsync(CancellationToken ct) } // extract results - var peerCount = results[0].Response.ToObject().IntegralFromHex(); - var latestBlockInfo = results[1].Response.ToObject(); + var peerCount = responses[0].Response.ToObject().IntegralFromHex(); + var latestBlockInfo = responses[1].Response.ToObject(); var latestBlockHeight = latestBlockInfo.Height.Value; var latestBlockTimestamp = latestBlockInfo.Timestamp; @@ -268,8 +256,8 @@ private async Task UpdateNetworkStatsAsync(CancellationToken ct) var sampleSize = (ulong) 300; var sampleBlockNumber = latestBlockHeight - sampleSize; - var sampleBlockResults = await daemon.ExecuteCmdAllAsync(logger, EC.GetBlockByNumber, ct, new[] { (object) sampleBlockNumber.ToStringHexWithPrefix(), true }); - var sampleBlockTimestamp = sampleBlockResults.First(x => x.Error == null && x.Response?.Height != null).Response.Timestamp; + var sampleBlockResults = await rpcClient.ExecuteAsync(logger, EC.GetBlockByNumber, ct, new[] { (object) sampleBlockNumber.ToStringHexWithPrefix(), true }); + var sampleBlockTimestamp = sampleBlockResults.Response.Timestamp; var blockTime = (double) (latestBlockTimestamp - sampleBlockTimestamp) / sampleSize; var networkHashrate = (double) (latestBlockDifficulty / blockTime); @@ -287,7 +275,7 @@ private async Task UpdateNetworkStatsAsync(CancellationToken ct) private async Task SubmitBlockAsync(Share share, string fullNonceHex, string headerHash, string mixHash) { // submit work - var response = await daemon.ExecuteCmdAnyAsync(logger, EC.SubmitWork, CancellationToken.None, new[] + var response = await rpcClient.ExecuteAsync(logger, EC.SubmitWork, CancellationToken.None, new[] { fullNonceHex, headerHash, @@ -325,20 +313,6 @@ private object[] GetJobParamsForStratum(bool isNew) return new object[0]; } - private JsonRpcRequest DeserializeRequest(byte[] data) - { - using(var stream = new MemoryStream(data)) - { - using(var reader = new StreamReader(stream, Encoding.UTF8)) - { - using(var jreader = new JsonTextReader(reader)) - { - return serializer.Deserialize(jreader); - } - } - } - } - #region API-Surface public IObservable Jobs { get; private set; } @@ -442,25 +416,19 @@ protected override void ConfigureDaemons() { var jsonSerializerSettings = ctx.Resolve(); - daemon = new DaemonClient(jsonSerializerSettings, messageBus, clusterConfig.ClusterName ?? poolConfig.PoolName, poolConfig.Id); - daemon.Configure(daemonEndpoints); + rpcClient = new RpcClient(daemonEndpoints.First(), jsonSerializerSettings, messageBus, poolConfig.Id); } protected override async Task AreDaemonsHealthyAsync(CancellationToken ct) { - var responses = await daemon.ExecuteCmdAllAsync(logger, EC.GetBlockByNumber, ct, new[] { (object) "latest", true }); - - if(responses.Where(x => x.Error?.InnerException?.GetType() == typeof(DaemonClientException)) - .Select(x => (DaemonClientException) x.Error.InnerException) - .Any(x => x.Code == HttpStatusCode.Unauthorized)) - logger.ThrowLogPoolStartupException("Daemon reports invalid credentials"); + var response = await rpcClient.ExecuteAsync(logger, EC.GetBlockByNumber, ct, new[] { (object) "latest", true }); - return responses.All(x => x.Error == null); + return response.Error == null; } protected override async Task AreDaemonsConnectedAsync(CancellationToken ct) { - var response = await daemon.ExecuteCmdAnyAsync(logger, EC.GetPeerCount, ct); + var response = await rpcClient.ExecuteAsync(logger, EC.GetPeerCount, ct); return response.Error == null && response.Response.IntegralFromHex() > 0; } @@ -471,10 +439,9 @@ protected override async Task EnsureDaemonsSynchedAsync(CancellationToken ct) while(true) { - var responses = await daemon.ExecuteCmdAllAsync(logger, EC.GetSyncState, ct); + var responses = await rpcClient.ExecuteAsync(logger, EC.GetSyncState, ct); - var isSynched = responses.All(x => x.Error == null && - x.Response is false); + var isSynched = responses.Response is false; if(isSynched) { @@ -497,18 +464,18 @@ protected override async Task EnsureDaemonsSynchedAsync(CancellationToken ct) protected override async Task PostStartInitAsync(CancellationToken ct) { - var commands = new[] + var requests = new[] { - new DaemonCmd(EC.GetNetVersion), - new DaemonCmd(EC.GetAccounts), - new DaemonCmd(EC.GetCoinbase), + new RpcRequest(EC.GetNetVersion), + new RpcRequest(EC.GetAccounts), + new RpcRequest(EC.GetCoinbase), }; - var results = await daemon.ExecuteBatchAnyAsync(logger, ct, commands); + var responses = await rpcClient.ExecuteBatchAsync(logger, ct, requests); - if(results.Any(x => x.Error != null)) + if(responses.Any(x => x.Error != null)) { - var errors = results.Take(3).Where(x => x.Error != null) + var errors = responses.Take(3).Where(x => x.Error != null) .ToArray(); if(errors.Any()) @@ -516,9 +483,9 @@ protected override async Task PostStartInitAsync(CancellationToken ct) } // extract results - var netVersion = results[0].Response.ToObject(); - var accounts = results[1].Response.ToObject(); - var coinbase = results[2].Response.ToObject(); + var netVersion = responses[0].Response.ToObject(); + var accounts = responses[1].Response.ToObject(); + var coinbase = responses[2].Response.ToObject(); var gethChain = extraPoolConfig?.ChainTypeOverride ?? "Ethereum"; EthereumUtils.DetectNetworkAndChain(netVersion, gethChain, out networkType, out chainType); @@ -559,6 +526,8 @@ protected override async Task PostStartInitAsync(CancellationToken ct) } } + ConfigureRewards(); + SetupJobUpdates(ct); } diff --git a/src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs b/src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs index 7c3a807c3..8dca14dce 100644 --- a/src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs +++ b/src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs @@ -11,8 +11,8 @@ using Miningcore.Blockchain.Ethereum.DaemonRequests; using Miningcore.Blockchain.Ethereum.DaemonResponses; using Miningcore.Configuration; -using Miningcore.DaemonInterface; using Miningcore.Extensions; +using Miningcore.JsonRpc; using Miningcore.Messaging; using Miningcore.Mining; using Miningcore.Payments; @@ -52,7 +52,7 @@ public EthereumPayoutHandler( } private readonly IComponentContext ctx; - private DaemonClient daemon; + private RpcClient rpcClient; private EthereumNetworkType networkType; private GethChainType chainType; private const int BlockSearchOffset = 50; @@ -75,12 +75,7 @@ public async Task ConfigureAsync(ClusterConfig clusterConfig, PoolConfig poolCon // configure standard daemon var jsonSerializerSettings = ctx.Resolve(); - var daemonEndpoints = poolConfig.Daemons - .Where(x => string.IsNullOrEmpty(x.Category)) - .ToArray(); - - daemon = new DaemonClient(jsonSerializerSettings, messageBus, clusterConfig.ClusterName ?? poolConfig.PoolName, poolConfig.Id); - daemon.Configure(daemonEndpoints); + rpcClient = new RpcClient(poolConfig.Daemons.First(x => string.IsNullOrEmpty(x.Category)), jsonSerializerSettings, messageBus, poolConfig.Id); await DetectChainAsync(ct); } @@ -105,8 +100,8 @@ public async Task ClassifyBlocksAsync(IMiningPool pool, Block[] blocks, .ToArray(); // get latest block - var latestBlockResponses = await daemon.ExecuteCmdAllAsync(logger, EC.GetBlockByNumber, ct, new[] { (object) "latest", true }); - var latestBlockHeight = latestBlockResponses.First(x => x.Error == null && x.Response?.Height != null).Response.Height.Value; + var latestBlockResponse = await rpcClient.ExecuteAsync(logger, EC.GetBlockByNumber, ct, new[] { (object) "latest", true }); + var latestBlockHeight = latestBlockResponse.Response.Height.Value; // execute batch var blockInfos = await FetchBlocks(blockCache, ct, page.Select(block => (long) block.BlockHeight).ToArray()); @@ -128,9 +123,15 @@ public async Task ClassifyBlocksAsync(IMiningPool pool, Block[] blocks, // mature? if(latestBlockHeight - block.BlockHeight >= EthereumConstants.MinConfimations) { - var blockHashResponses = await daemon.ExecuteCmdAllAsync(logger, EC.GetBlockByNumber, ct, + var blockHashResponse = await rpcClient.ExecuteAsync(logger, EC.GetBlockByNumber, ct, new[] { (object) block.BlockHeight.ToStringHexWithPrefix(), true }); - var blockHash = blockHashResponses.First(x => x.Error == null && x.Response?.Hash != null).Response.Hash; + var blockHash = blockHashResponse.Response.Hash; + var baseGas = blockHashResponse.Response.BaseFeePerGas; + var gasUsed = blockHashResponse.Response.GasUsed; + + var burnedFee = (decimal) 0; + if(extraPoolConfig?.ChainTypeOverride == "Ethereum") + burnedFee = (baseGas * gasUsed / EthereumConstants.Wei); block.Hash = blockHash; block.Status = BlockStatus.Confirmed; @@ -143,7 +144,7 @@ public async Task ClassifyBlocksAsync(IMiningPool pool, Block[] blocks, block.Reward += blockInfo.Uncles.Length * (block.Reward / 32); // uncle rewards if(extraConfig?.KeepTransactionFees == false && blockInfo.Transactions?.Length > 0) - block.Reward += await GetTxRewardAsync(blockInfo, ct); // tx fees + block.Reward += await GetTxRewardAsync(blockInfo, ct) - burnedFee; logger.Info(() => $"[{LogCategory}] Unlocked block {block.BlockHeight} worth {FormatAmount(block.Reward)}"); @@ -170,13 +171,13 @@ public async Task ClassifyBlocksAsync(IMiningPool pool, Block[] blocks, if(blockInfo2.Uncles.Length > 0) { // fetch all uncles in a single RPC batch request - var uncleBatch = blockInfo2.Uncles.Select((x, index) => new DaemonCmd(EC.GetUncleByBlockNumberAndIndex, + var uncleBatch = blockInfo2.Uncles.Select((x, index) => new RpcRequest(EC.GetUncleByBlockNumberAndIndex, new[] { blockInfo2.Height.Value.ToStringHexWithPrefix(), index.ToStringHexWithPrefix() })) .ToArray(); logger.Info(() => $"[{LogCategory}] Fetching {blockInfo2.Uncles.Length} uncles for block {blockInfo2.Height}"); - var uncleResponses = await daemon.ExecuteBatchAnyAsync(logger, ct, uncleBatch); + var uncleResponses = await rpcClient.ExecuteBatchAsync(logger, ct, uncleBatch); logger.Info(() => $"[{LogCategory}] Fetched {uncleResponses.Count(x => x.Error == null && x.Response != null)} uncles for block {blockInfo2.Height}"); @@ -189,9 +190,9 @@ public async Task ClassifyBlocksAsync(IMiningPool pool, Block[] blocks, // mature? if(latestBlockHeight - uncle.Height.Value >= EthereumConstants.MinConfimations) { - var blockHashUncleResponses = await daemon.ExecuteCmdAllAsync(logger, EC.GetBlockByNumber, ct, + var blockHashUncleResponse = await rpcClient.ExecuteAsync(logger, EC.GetBlockByNumber, ct, new[] { (object) uncle.Height.Value.ToStringHexWithPrefix(), true }); - var blockHashUncle = blockHashUncleResponses.First(x => x.Error == null && x.Response?.Hash != null).Response.Hash; + var blockHashUncle = blockHashUncleResponse.Response.Hash; block.Hash = blockHashUncle; block.Status = BlockStatus.Confirmed; @@ -248,7 +249,7 @@ public override async Task UpdateBlockRewardBalancesAsync(IDbConnection public async Task PayoutAsync(IMiningPool pool, Balance[] balances, CancellationToken ct) { // ensure we have peers - var infoResponse = await daemon.ExecuteCmdSingleAsync(logger, EC.GetPeerCount, ct); + var infoResponse = await rpcClient.ExecuteAsync(logger, EC.GetPeerCount, ct); if(networkType == EthereumNetworkType.Mainnet && (infoResponse.Error != null || string.IsNullOrEmpty(infoResponse.Response) || @@ -288,14 +289,14 @@ public async Task PayoutAsync(IMiningPool pool, Balance[] balances, Cancellation if(cacheMisses.Any()) { - var blockBatch = cacheMisses.Select(height => new DaemonCmd(EC.GetBlockByNumber, + var blockBatch = cacheMisses.Select(height => new RpcRequest(EC.GetBlockByNumber, new[] { (object) height.ToStringHexWithPrefix(), true })).ToArray(); - var tmp = await daemon.ExecuteBatchAnyAsync(logger, ct, blockBatch); + var tmp = await rpcClient.ExecuteBatchAsync(logger, ct, blockBatch); var transformed = tmp .Where(x => x.Error == null && x.Response != null) @@ -334,10 +335,10 @@ internal static decimal GetBaseBlockReward(GethChainType chainType, ulong height private async Task GetTxRewardAsync(DaemonResponses.Block blockInfo, CancellationToken ct) { // fetch all tx receipts in a single RPC batch request - var batch = blockInfo.Transactions.Select(tx => new DaemonCmd(EC.GetTxReceipt, new[] { tx.Hash })) + var batch = blockInfo.Transactions.Select(tx => new RpcRequest(EC.GetTxReceipt, new[] { tx.Hash })) .ToArray(); - var results = await daemon.ExecuteBatchAnyAsync(logger, ct, batch); + var results = await rpcClient.ExecuteBatchAsync(logger, ct, batch); if(results.Any(x => x.Error != null)) throw new Exception($"Error fetching tx receipts: {string.Join(", ", results.Where(x => x.Error != null).Select(y => y.Error.Message))}"); @@ -366,10 +367,10 @@ private async Task DetectChainAsync(CancellationToken ct) { var commands = new[] { - new DaemonCmd(EC.GetNetVersion), + new RpcRequest(EC.GetNetVersion), }; - var results = await daemon.ExecuteBatchAnyAsync(logger, ct, commands); + var results = await rpcClient.ExecuteBatchAsync(logger, ct, commands); if(results.Any(x => x.Error != null)) { @@ -398,10 +399,18 @@ private async Task PayoutAsync(Balance balance, CancellationToken ct) { From = poolConfig.Address, To = balance.Address, - Value = writeHex(amount), + Value = amount.ToString("x").TrimStart('0'), }; - var response = await daemon.ExecuteCmdSingleAsync(logger, EC.SendTx, ct, new[] { request }); + if(extraPoolConfig?.ChainTypeOverride == "Ethereum") + { + var maxPriorityFeePerGas = await rpcClient.ExecuteAsync(logger, EC.MaxPriorityFeePerGas, ct); + request.Gas = extraConfig.Gas; + request.MaxPriorityFeePerGas = maxPriorityFeePerGas.Response.IntegralFromHex(); + request.MaxFeePerGas = extraConfig.MaxFeePerGas; + } + + var response = await rpcClient.ExecuteAsync(logger, EC.SendTx, ct, new[] { request }); if(response.Error != null) throw new Exception($"{EC.SendTx} returned error: {response.Error.Message} code {response.Error.Code}"); @@ -418,10 +427,5 @@ private async Task PayoutAsync(Balance balance, CancellationToken ct) // done return txHash; } - - private static string writeHex(BigInteger value) - { - return (value.ToString("x").TrimStart('0')); - } } } diff --git a/src/Miningcore/DaemonInterface/DaemonClientException.cs b/src/Miningcore/DaemonInterface/DaemonClientException.cs deleted file mode 100644 index 017e63548..000000000 --- a/src/Miningcore/DaemonInterface/DaemonClientException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Net; - -namespace Miningcore.DaemonInterface -{ - public class DaemonClientException : Exception - { - public DaemonClientException(string msg) : base(msg) - { - } - - public DaemonClientException(HttpStatusCode code, string msg) : base(msg) - { - Code = code; - } - - public HttpStatusCode Code { get; set; } - } -} diff --git a/src/Miningcore/DaemonInterface/DaemonCmd.cs b/src/Miningcore/DaemonInterface/DaemonCmd.cs deleted file mode 100644 index 55347629b..000000000 --- a/src/Miningcore/DaemonInterface/DaemonCmd.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Miningcore.DaemonInterface -{ - public class DaemonCmd - { - public DaemonCmd() - { - } - - public DaemonCmd(string method) - { - Method = method; - } - - public DaemonCmd(string method, object payload) - { - Method = method; - Payload = payload; - } - - public string Method { get; set; } - public object Payload { get; set; } - } -} diff --git a/src/Miningcore/DaemonInterface/DaemonResponse.cs b/src/Miningcore/DaemonInterface/DaemonResponse.cs deleted file mode 100644 index 7824e26c8..000000000 --- a/src/Miningcore/DaemonInterface/DaemonResponse.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Miningcore.Configuration; -using Miningcore.JsonRpc; - -namespace Miningcore.DaemonInterface -{ - public class DaemonResponse - { - public JsonRpcException Error { get; set; } - public T Response { get; set; } - public AuthenticatedNetworkEndpointConfig Instance { get; set; } - } -} diff --git a/src/Miningcore/Extensions/IpAddressExtensions.cs b/src/Miningcore/Extensions/IpAddressExtensions.cs new file mode 100644 index 000000000..f21f00cc1 --- /dev/null +++ b/src/Miningcore/Extensions/IpAddressExtensions.cs @@ -0,0 +1,24 @@ +using System.Net; + +namespace Miningcore.Extensions +{ + public static class IpAddressExtensions + { + public static bool IsEqual(this IPAddress address, IPAddress other) + { + if(address.Equals(other)) + return true; + + if(address.IsIPv4MappedToIPv6 && !other.IsIPv4MappedToIPv6 && address.MapToIPv4().Equals(other)) + return true; + + if(address.IsIPv4MappedToIPv6 && other.IsIPv4MappedToIPv6 && address.MapToIPv4().Equals(other.MapToIPv4())) + return true; + + if(!address.IsIPv4MappedToIPv6 && other.IsIPv4MappedToIPv6 && address.Equals(other.MapToIPv4())) + return true; + + return false; + } + } +} diff --git a/src/Miningcore/Extensions/ZmqExtensions.cs b/src/Miningcore/Extensions/ZmqExtensions.cs index 8f945f25f..3925637e9 100644 --- a/src/Miningcore/Extensions/ZmqExtensions.cs +++ b/src/Miningcore/Extensions/ZmqExtensions.cs @@ -15,24 +15,26 @@ namespace Miningcore.Extensions { public static class ZmqExtensions { - private static readonly ConcurrentDictionary knownKeys = - new(); + private record KeyData(byte[] PubKey, byte[] SecretKey); - private static readonly Lazy<(byte[] PubKey, byte[] SecretKey)> ownKey = new(() => + private static readonly ConcurrentDictionary knownKeys = new(); + + private static readonly Lazy ownKey = new(() => { if(!ZContext.Has("curve")) throw new NotSupportedException("ZMQ library does not support curve"); Z85.CurveKeypair(out var pubKey, out var secretKey); - return (pubKey, secretKey); + return new KeyData(pubKey, secretKey); }); const int PasswordIterations = 5000; - private static readonly byte[] NoSalt = new byte[32]; + + private static readonly byte[] noSalt = new byte[32]; private static byte[] DeriveKey(string password, int length = 32) { - using(var kbd = new Rfc2898DeriveBytes(Encoding.UTF8.GetBytes(password), NoSalt, PasswordIterations)) + using(var kbd = new Rfc2898DeriveBytes(Encoding.UTF8.GetBytes(password), noSalt, PasswordIterations)) { var block = kbd.GetBytes(length); return block; @@ -98,7 +100,7 @@ public static void SetupCurveTlsServer(this ZSocket socket, string keyPlain, ILo // Derive server's public-key from shared secret Z85.CurvePublic(out serverPubKey, keyBytes.ToZ85Encoded()); - knownKeys[keyPlain] = (serverPubKey, keyBytes); + knownKeys[keyPlain] = new KeyData(serverPubKey, keyBytes); } else @@ -135,7 +137,7 @@ public static void SetupCurveTlsClient(this ZSocket socket, string keyPlain, ILo // Derive server's public-key from shared secret Z85.CurvePublic(out serverPubKey, keyBytes.ToZ85Encoded()); - knownKeys[keyPlain] = (serverPubKey, keyBytes); + knownKeys[keyPlain] = new KeyData(serverPubKey, keyBytes); } else diff --git a/src/Miningcore/JsonRpc/JsonRpcException.cs b/src/Miningcore/JsonRpc/JsonRpcError.cs similarity index 87% rename from src/Miningcore/JsonRpc/JsonRpcException.cs rename to src/Miningcore/JsonRpc/JsonRpcError.cs index 658a25b2e..0101c31e3 100644 --- a/src/Miningcore/JsonRpc/JsonRpcException.cs +++ b/src/Miningcore/JsonRpc/JsonRpcError.cs @@ -27,9 +27,9 @@ namespace Miningcore.JsonRpc /// The remainder of the space is available for application defined errors. /// [JsonObject(MemberSerialization.OptIn)] - public class JsonRpcException + public record JsonRpcError { - public JsonRpcException(int code, string message, object data, Exception inner = null) + public JsonRpcError(int code, string message, object data, Exception inner = null) { Code = code; Message = message; @@ -38,15 +38,15 @@ public JsonRpcException(int code, string message, object data, Exception inner = } [JsonProperty(PropertyName = "code")] - public int Code { get; set; } + public int Code { get; } [JsonProperty(PropertyName = "message")] - public string Message { get; set; } + public string Message { get; } [JsonProperty(PropertyName = "data")] - public object Data { get; set; } + public object Data { get; } [JsonIgnore] - public Exception InnerException { get; set; } + public Exception InnerException { get; } } } diff --git a/src/Miningcore/JsonRpc/JsonRpcResponse.cs b/src/Miningcore/JsonRpc/JsonRpcResponse.cs index 7609a57b0..14c40e16d 100644 --- a/src/Miningcore/JsonRpc/JsonRpcResponse.cs +++ b/src/Miningcore/JsonRpc/JsonRpcResponse.cs @@ -14,7 +14,7 @@ public JsonRpcResponse(object result, object id = null) : base(result, id) { } - public JsonRpcResponse(JsonRpcException ex, object id = null, object result = null) : base(ex, id, result) + public JsonRpcResponse(JsonRpcError ex, object id = null, object result = null) : base(ex, id, result) { } } @@ -35,7 +35,7 @@ public JsonRpcResponse(T result, object id = null) Id = id; } - public JsonRpcResponse(JsonRpcException ex, object id, object result) + public JsonRpcResponse(JsonRpcError ex, object id, object result) { Error = ex; Id = id; @@ -51,7 +51,7 @@ public JsonRpcResponse(JsonRpcException ex, object id, object result) public object Result { get; set; } [JsonProperty(PropertyName = "error")] - public JsonRpcException Error { get; set; } + public JsonRpcError Error { get; set; } [JsonProperty(PropertyName = "id", NullValueHandling = NullValueHandling.Ignore)] public object Id { get; set; } diff --git a/src/Miningcore/DaemonInterface/DaemonClient.cs b/src/Miningcore/JsonRpc/RpcClient.cs similarity index 56% rename from src/Miningcore/DaemonInterface/DaemonClient.cs rename to src/Miningcore/JsonRpc/RpcClient.cs index 6e39947ff..5a136f2d3 100644 --- a/src/Miningcore/DaemonInterface/DaemonClient.cs +++ b/src/Miningcore/JsonRpc/RpcClient.cs @@ -14,7 +14,6 @@ using System.Threading.Tasks; using Miningcore.Configuration; using Miningcore.Extensions; -using Miningcore.JsonRpc; using Miningcore.Messaging; using Miningcore.Notifications.Messages; using Miningcore.Util; @@ -24,19 +23,20 @@ using ZeroMQ; using Contract = Miningcore.Contracts.Contract; -namespace Miningcore.DaemonInterface +namespace Miningcore.JsonRpc { /// - /// Provides JsonRpc based interface to a cluster of blockchain daemons for improved fault tolerance + /// JsonRpc interface to blockchain node /// - public class DaemonClient + public class RpcClient { - public DaemonClient(JsonSerializerSettings serializerSettings, IMessageBus messageBus, string server, string poolId) + public RpcClient(DaemonEndpointConfig endPoint, JsonSerializerSettings serializerSettings, IMessageBus messageBus, string poolId) { Contract.RequiresNonNull(serializerSettings, nameof(serializerSettings)); Contract.RequiresNonNull(messageBus, nameof(messageBus)); Contract.Requires(!string.IsNullOrEmpty(poolId), $"{nameof(poolId)} must not be empty"); + this.endPoint = endPoint; this.serializerSettings = serializerSettings; this.messageBus = messageBus; this.poolId = poolId; @@ -48,9 +48,10 @@ public DaemonClient(JsonSerializerSettings serializerSettings, IMessageBus messa } private readonly JsonSerializerSettings serializerSettings; - - protected DaemonEndpointConfig[] endPoints; + protected readonly DaemonEndpointConfig endPoint; private readonly JsonSerializer serializer; + private readonly IMessageBus messageBus; + private readonly string poolId; private static readonly HttpClient httpClient = new(new HttpClientHandler { @@ -59,165 +60,64 @@ public DaemonClient(JsonSerializerSettings serializerSettings, IMessageBus messa ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true, }); - // Telemetry - private readonly IMessageBus messageBus; - - private readonly string poolId; - - protected void PublishTelemetry(TelemetryCategory cat, TimeSpan elapsed, string info, bool? success = null, string error = null) - { - messageBus.SendMessage(new TelemetryEvent(poolId, cat, info, elapsed, success)); - } - #region API-Surface - public void Configure(DaemonEndpointConfig[] endPoints) - { - Contract.RequiresNonNull(endPoints, nameof(endPoints)); - Contract.Requires(endPoints.Length > 0, $"{nameof(endPoints)} must not be empty"); - - this.endPoints = endPoints; - } - - /// - /// Executes the request against all configured demons and returns their responses as an array - /// - public Task[]> ExecuteCmdAllAsync(ILogger logger, string method, CancellationToken ct) - { - return ExecuteCmdAllAsync(logger, method, ct); - } - - /// - /// Executes the request against all configured demons and returns their responses as an array - /// - public async Task[]> ExecuteCmdAllAsync(ILogger logger, string method, CancellationToken ct, - object payload = null, JsonSerializerSettings payloadJsonSerializerSettings = null) + public async Task> ExecuteAsync(ILogger logger, string method, CancellationToken ct, + object payload = null, bool throwOnError = false) where TResponse : class { Contract.Requires(!string.IsNullOrEmpty(method), $"{nameof(method)} must not be empty"); - logger.LogInvoke(new object[] { "\"" + method + "\"" }); - - var tasks = endPoints.Select(endPoint => BuildRequestTask(logger, endPoint, method, payload, CancellationToken.None, payloadJsonSerializerSettings)).ToArray(); + logger.LogInvoke(new object[] { method }); try { - await Task.WhenAll(tasks); + var response = await RequestAsync(logger, ct, endPoint, method, payload); + + if(response.Result is JToken token) + return new RpcResponse(token.ToObject(serializer), response.Error); + + return new RpcResponse((TResponse) response.Result, response.Error); } - catch(Exception) + catch(TaskCanceledException) { - // ignored + return new RpcResponse(null, new JsonRpcError(-500, "Cancelled", null)); } - var results = tasks.Select((x, i) => MapDaemonResponse(i, x)) - .ToArray(); - - return results; - } - - /// - /// Executes the request against all configured demons and returns the first successful response - /// - public Task> ExecuteCmdAnyAsync(ILogger logger, string method, CancellationToken ct, bool throwOnError = false) - { - return ExecuteCmdAnyAsync(logger, method, ct, null, null, throwOnError); - } - - /// - /// Executes the request against all configured demons and returns the first successful response - /// - public async Task> ExecuteCmdAnyAsync(ILogger logger, string method, CancellationToken ct, object payload = null, - JsonSerializerSettings payloadJsonSerializerSettings = null, bool throwOnError = false) - where TResponse : class - { - Contract.Requires(!string.IsNullOrEmpty(method), $"{nameof(method)} must not be empty"); - - logger.LogInvoke(new object[] { "\"" + method + "\"" }); - - var tasks = endPoints.Select(endPoint => BuildRequestTask(logger, endPoint, method, payload, CancellationToken.None, payloadJsonSerializerSettings)).ToArray(); - - var taskFirstCompleted = await Task.WhenAny(tasks); - var result = MapDaemonResponse(0, taskFirstCompleted, throwOnError); - return result; - } - - /// - /// Executes the request against all configured demons and returns the first successful response - /// - public async Task> ExecuteCmdAnyAsync(ILogger logger, CancellationToken ct, string method, object payload = null, - JsonSerializerSettings payloadJsonSerializerSettings = null, bool throwOnError = false) - where TResponse : class - { - Contract.Requires(!string.IsNullOrEmpty(method), $"{nameof(method)} must not be empty"); - - logger.LogInvoke(new object[] { "\"" + method + "\"" }); - - var tasks = endPoints.Select(endPoint => BuildRequestTask(logger, endPoint, method, payload, ct, payloadJsonSerializerSettings)).ToArray(); - - var taskFirstCompleted = await Task.WhenAny(tasks); - var result = MapDaemonResponse(0, taskFirstCompleted, throwOnError); - return result; - } - - /// - /// Executes the request against all configured demons and returns the first successful response - /// - public Task> ExecuteCmdSingleAsync(ILogger logger, string method, CancellationToken ct) - { - return ExecuteCmdAnyAsync(logger, method, ct); - } - - /// - /// Executes the request against all configured demons and returns the first successful response - /// - public async Task> ExecuteCmdSingleAsync(ILogger logger, string method, CancellationToken ct, object payload = null, - JsonSerializerSettings payloadJsonSerializerSettings = null) - where TResponse : class - { - Contract.Requires(!string.IsNullOrEmpty(method), $"{nameof(method)} must not be empty"); - - logger.LogInvoke(new object[] { "\"" + method + "\"" }); - - var task = BuildRequestTask(logger, endPoints.First(), method, payload, CancellationToken.None, payloadJsonSerializerSettings); - await Task.WhenAny(task); + catch(Exception ex) + { + if(throwOnError) + throw; - var result = MapDaemonResponse(0, task); - return result; + return new RpcResponse(null, new JsonRpcError(-500, ex.Message, null, ex)); + } } - /// - /// Executes the request against all configured demons and returns the first successful response - /// - public async Task> ExecuteCmdSingleAsync(ILogger logger, CancellationToken ct, string method, object payload = null, - JsonSerializerSettings payloadJsonSerializerSettings = null) - where TResponse : class + public Task> ExecuteAsync(ILogger logger, string method, CancellationToken ct, bool throwOnError = false) { - Contract.Requires(!string.IsNullOrEmpty(method), $"{nameof(method)} must not be empty"); - - logger.LogInvoke(new object[] { "\"" + method + "\"" }); - - var task = BuildRequestTask(logger, endPoints.First(), method, payload, ct, payloadJsonSerializerSettings); - await Task.WhenAny(task); - - var result = MapDaemonResponse(0, task); - return result; + return ExecuteAsync(logger, method, ct, null, throwOnError); } - /// - /// Executes the requests against all configured demons and returns the first successful response array - /// - public async Task[]> ExecuteBatchAnyAsync(ILogger logger, CancellationToken ct, params DaemonCmd[] batch) + public async Task[]> ExecuteBatchAsync(ILogger logger, CancellationToken ct, params RpcRequest[] batch) { Contract.RequiresNonNull(batch, nameof(batch)); - logger.LogInvoke(batch.Select(x => "\"" + x.Method + "\"").ToArray()); + logger.LogInvoke(string.Join(", ", batch.Select(x=> x.Method))); + + try + { + var response = await BatchRequestAsync(logger, ct, endPoint, batch); - var tasks = endPoints.Select(endPoint => BuildBatchRequestTask(logger, ct, endPoint, batch)).ToArray(); + return response + .Select(x => new RpcResponse(x.Result != null ? JToken.FromObject(x.Result) : null, x.Error)) + .ToArray(); + } - var taskFirstCompleted = await Task.WhenAny(tasks); - var result = MapDaemonBatchResponse(0, taskFirstCompleted); - return result; + catch(Exception ex) + { + return Enumerable.Repeat(new RpcResponse(null, new JsonRpcError(-500, ex.Message, null, ex)), batch.Length).ToArray(); + } } public IObservable WebsocketSubscribe(ILogger logger, CancellationToken ct, Dictionary ZmqSubscribe(ILogger logger, CancellationToken ct, logger.LogInvoke(); return Observable.Merge(portMap.Keys - .Select(endPoint => ZmqSubscribeEndpoint(logger, ct, endPoint, portMap[endPoint].Socket, portMap[endPoint].Topic))) + .Select(endPoint => ZmqSubscribeEndpoint(logger, ct, portMap[endPoint].Socket, portMap[endPoint].Topic))) .Publish() .RefCount(); } #endregion // API-Surface - private async Task BuildRequestTask(ILogger logger, DaemonEndpointConfig endPoint, string method, object payload, - CancellationToken ct, JsonSerializerSettings payloadJsonSerializerSettings = null) + private async Task RequestAsync(ILogger logger, CancellationToken ct, DaemonEndpointConfig endPoint, string method, object payload) { - var rpcRequestId = GetRequestId(); - - // telemetry var sw = Stopwatch.StartNew(); // build rpc request - var rpcRequest = new JsonRpcRequest(method, payload, rpcRequestId); + var rpcRequest = new JsonRpcRequest(method, payload, GetRequestId()); - // build request url - var protocol = (endPoint.Ssl || endPoint.Http2) ? "https" : "http"; + // build url + var protocol = endPoint.Ssl || endPoint.Http2 ? Uri.UriSchemeHttps : Uri.UriSchemeHttp; var requestUrl = $"{protocol}://{endPoint.Host}:{endPoint.Port}"; if(!string.IsNullOrEmpty(endPoint.HttpPath)) requestUrl += $"{(endPoint.HttpPath.StartsWith("/") ? string.Empty : "/")}{endPoint.HttpPath}"; - // build http request + // build request using(var request = new HttpRequestMessage(HttpMethod.Post, requestUrl)) { - request.Headers.ConnectionClose = false; // enable keep-alive - if(endPoint.Http2) request.Version = new Version(2, 0); + else + request.Headers.ConnectionClose = false; // enable keep-alive - // build request content - var json = JsonConvert.SerializeObject(rpcRequest, payloadJsonSerializerSettings ?? serializerSettings); + // build content + var json = JsonConvert.SerializeObject(rpcRequest, serializerSettings); request.Content = new StringContent(json, Encoding.UTF8, "application/json"); // build auth header @@ -296,9 +192,7 @@ private async Task BuildRequestTask(ILogger logger, DaemonEndpo { var result = serializer.Deserialize(jreader); - // telemetry - sw.Stop(); - PublishTelemetry(TelemetryCategory.RpcRequest, sw.Elapsed, method, response.IsSuccessStatusCode); + messageBus.SendTelemetry(poolId, TelemetryCategory.RpcRequest, method, sw.Elapsed, response.IsSuccessStatusCode); return result; } @@ -306,27 +200,26 @@ private async Task BuildRequestTask(ILogger logger, DaemonEndpo } } - private async Task[]> BuildBatchRequestTask(ILogger logger, CancellationToken ct, DaemonEndpointConfig endPoint, DaemonCmd[] batch) + private async Task[]> BatchRequestAsync(ILogger logger, CancellationToken ct, DaemonEndpointConfig endPoint, RpcRequest[] batch) { - // telemetry var sw = Stopwatch.StartNew(); // build rpc request var rpcRequests = batch.Select(x => new JsonRpcRequest(x.Method, x.Payload, GetRequestId())); - // build request url - var protocol = (endPoint.Ssl || endPoint.Http2) ? "https" : "http"; + // build url + var protocol = (endPoint.Ssl || endPoint.Http2) ? Uri.UriSchemeHttps : Uri.UriSchemeHttp; var requestUrl = $"{protocol}://{endPoint.Host}:{endPoint.Port}"; if(!string.IsNullOrEmpty(endPoint.HttpPath)) requestUrl += $"{(endPoint.HttpPath.StartsWith("/") ? string.Empty : "/")}{endPoint.HttpPath}"; - // build http request + // build request using(var request = new HttpRequestMessage(HttpMethod.Post, requestUrl)) { - request.Headers.ConnectionClose = false; // enable keep-alive - if(endPoint.Http2) request.Version = new Version(2, 0); + else + request.Headers.ConnectionClose = false; // enable keep-alive // build request content var json = JsonConvert.SerializeObject(rpcRequests, serializerSettings); @@ -352,9 +245,8 @@ private async Task[]> BuildBatchRequestTask(ILogger logg { var result = serializer.Deserialize[]>(jreader); - // telemetry - sw.Stop(); - PublishTelemetry(TelemetryCategory.RpcRequest, sw.Elapsed, string.Join(", ", batch.Select(x => x.Method)), true); + messageBus.SendTelemetry(poolId, TelemetryCategory.RpcRequest, string.Join(", ", batch.Select(x => x.Method)), + sw.Elapsed, response.IsSuccessStatusCode); return result; } @@ -368,68 +260,6 @@ protected string GetRequestId() return rpcRequestId; } - private DaemonResponse MapDaemonResponse(int i, Task x, bool throwOnError = false) - where TResponse : class - { - var resp = new DaemonResponse - { - Instance = endPoints[i] - }; - - if(x.IsFaulted) - { - Exception inner; - - if(x.Exception.InnerExceptions.Count == 1) - inner = x.Exception.InnerException; - else - inner = x.Exception; - - if(throwOnError) - throw inner; - - resp.Error = new JsonRpcException(-500, x.Exception.Message, null, inner); - } - - else if(x.IsCanceled) - { - resp.Error = new JsonRpcException(-500, "Cancelled", null); - } - - else - { - Debug.Assert(x.IsCompletedSuccessfully); - - if(x.Result?.Result is JToken token) - resp.Response = token?.ToObject(serializer); - else - resp.Response = (TResponse) x.Result?.Result; - - resp.Error = x.Result?.Error; - } - - return resp; - } - - private DaemonResponse[] MapDaemonBatchResponse(int i, Task[]> x) - { - if(x.IsFaulted) - return x.Result?.Select(y => new DaemonResponse - { - Instance = endPoints[i], - Error = new JsonRpcException(-500, x.Exception.Message, null) - }).ToArray(); - - Debug.Assert(x.IsCompletedSuccessfully); - - return x.Result?.Select(y => new DaemonResponse - { - Instance = endPoints[i], - Response = y.Result != null ? JToken.FromObject(y.Result) : null, - Error = y.Error - }).ToArray(); - } - private IObservable WebsocketSubscribeEndpoint(ILogger logger, CancellationToken ct, NetworkEndpointConfig endPoint, (int Port, string HttpPath, bool Ssl) conf, string method, object payload = null, JsonSerializerSettings payloadJsonSerializerSettings = null) @@ -518,7 +348,7 @@ private IObservable WebsocketSubscribeEndpoint(ILogger logger, Cancellat })); } - private static IObservable ZmqSubscribeEndpoint(ILogger logger, CancellationToken ct, DaemonEndpointConfig endPoint, string url, string topic) + private static IObservable ZmqSubscribeEndpoint(ILogger logger, CancellationToken ct, string url, string topic) { return Observable.Defer(() => Observable.Create(obs => { diff --git a/src/Miningcore/JsonRpc/RpcRequest.cs b/src/Miningcore/JsonRpc/RpcRequest.cs new file mode 100644 index 000000000..e1b15d13c --- /dev/null +++ b/src/Miningcore/JsonRpc/RpcRequest.cs @@ -0,0 +1,19 @@ +namespace Miningcore.JsonRpc +{ + public record RpcRequest + { + public RpcRequest(string method) + { + Method = method; + } + + public RpcRequest(string method, object payload) + { + Method = method; + Payload = payload; + } + + public string Method { get; } + public object Payload { get; } + } +} diff --git a/src/Miningcore/JsonRpc/RpcResponse.cs b/src/Miningcore/JsonRpc/RpcResponse.cs new file mode 100644 index 000000000..c5559bdaa --- /dev/null +++ b/src/Miningcore/JsonRpc/RpcResponse.cs @@ -0,0 +1,6 @@ +using Miningcore.Configuration; + +namespace Miningcore.JsonRpc +{ + public record RpcResponse(T Response, JsonRpcError Error = null); +} diff --git a/src/Miningcore/Mining/ShareRelay.cs b/src/Miningcore/Mining/ShareRelay.cs index 9576ac1dd..a4181cb5b 100644 --- a/src/Miningcore/Mining/ShareRelay.cs +++ b/src/Miningcore/Mining/ShareRelay.cs @@ -23,14 +23,11 @@ public class ShareRelay : IHostedService { public ShareRelay( ClusterConfig clusterConfig, - JsonSerializerSettings serializerSettings, IMessageBus messageBus) { - Contract.RequiresNonNull(serializerSettings, nameof(serializerSettings)); Contract.RequiresNonNull(messageBus, nameof(messageBus)); this.clusterConfig = clusterConfig; - this.serializerSettings = serializerSettings; this.messageBus = messageBus; } @@ -41,7 +38,6 @@ public ShareRelay( private readonly int QueueSizeWarningThreshold = 1024; private bool hasWarnedAboutBacklogSize; private ZSocket pubSocket; - private readonly JsonSerializerSettings serializerSettings; private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); @@ -66,7 +62,7 @@ private void InitializeQueue() try { - var flags = (int) WireFormat.ProtocolBuffers; + const int flags = (int) WireFormat.ProtocolBuffers; using(var msg = new ZMessage()) { @@ -111,9 +107,9 @@ private void CheckQueueBacklog() } } - public Task StartAsync(CancellationToken cancellationToken) + public Task StartAsync(CancellationToken ct) { - messageBus.Listen().Subscribe(x => queue.Add(x.Share)); + messageBus.Listen().Subscribe(x => queue.Add(x.Share, ct)); pubSocket = new ZSocket(ZSocketType.PUB); @@ -135,6 +131,7 @@ public Task StartAsync(CancellationToken cancellationToken) logger.ThrowLogPoolStartupException("ZeroMQ Curve is not supported in ShareRelay Connect-Mode"); pubSocket.Connect(clusterConfig.ShareRelay.PublishUrl); + logger.Info(() => $"Connected to {clusterConfig.ShareRelay.PublishUrl}"); } @@ -145,7 +142,7 @@ public Task StartAsync(CancellationToken cancellationToken) return Task.CompletedTask; } - public Task StopAsync(CancellationToken cancellationToken) + public Task StopAsync(CancellationToken ct) { pubSocket.Dispose(); diff --git a/src/Miningcore/Miningcore.csproj b/src/Miningcore/Miningcore.csproj index 7b863efdb..9d53307b0 100644 --- a/src/Miningcore/Miningcore.csproj +++ b/src/Miningcore/Miningcore.csproj @@ -22,17 +22,14 @@ - - - diff --git a/src/Miningcore/Native/LibKawpow.cs b/src/Miningcore/Native/LibKawpow.cs new file mode 100644 index 000000000..dba32c80b --- /dev/null +++ b/src/Miningcore/Native/LibKawpow.cs @@ -0,0 +1,36 @@ +using System; +using System.Runtime.InteropServices; + +// ReSharper disable FieldCanBeMadeReadOnly.Local +// ReSharper disable MemberCanBePrivate.Local +// ReSharper disable InconsistentNaming + +namespace Miningcore.Native +{ + public static unsafe class LibKawpow + { + [DllImport("libethash", EntryPoint = "ethash_create_epoch_context", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr CreateContext(int epoch_number); + + [DllImport("libethash", EntryPoint = "ethash_destroy_epoch_context", CallingConvention = CallingConvention.Cdecl)] + private static extern void DestroyContext(IntPtr context); + + [DllImport("libethash", EntryPoint = "hashext", CallingConvention = CallingConvention.Cdecl)] + private static extern Ethash_result hashext(IntPtr context, int block_number, ref Ethash_hash256 header_hash, ulong nonce, ref Ethash_hash256 mix_hash, ref Ethash_hash256 boundary1, ref Ethash_hash256 boundary2, out int retcode); + + [StructLayout(LayoutKind.Explicit)] + private struct Ethash_hash256 + { + [FieldOffset(0)] + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public byte[] bytes;//x32 + } + + [StructLayout(LayoutKind.Sequential)] + private struct Ethash_result + { + public Ethash_hash256 final_hash;//32 + public Ethash_hash256 mix_hash;//32 + } + } +} diff --git a/src/Miningcore/Notifications/NotificationService.cs b/src/Miningcore/Notifications/NotificationService.cs index 35269aa99..a7620ff88 100644 --- a/src/Miningcore/Notifications/NotificationService.cs +++ b/src/Miningcore/Notifications/NotificationService.cs @@ -83,13 +83,13 @@ private async Task OnPaymentNotificationAsync(PaymentNotification notification, var coin = poolConfigs[notification.PoolId].Template; // prepare tx links - string[] txLinks = null; + var txLinks = new string[0]; if(!string.IsNullOrEmpty(coin.ExplorerTxLink)) txLinks = notification.TxIds.Select(txHash => string.Format(coin.ExplorerTxLink, txHash)).ToArray(); const string subject = "Payout Success Notification"; - var message = $"Paid {FormatAmount(notification.Amount, notification.PoolId)} from pool {notification.PoolId} to {notification.RecpientsCount} recipients in Transaction(s) {txLinks}."; + var message = $"Paid {FormatAmount(notification.Amount, notification.PoolId)} from pool {notification.PoolId} to {notification.RecpientsCount} recipients in transaction(s) {(string.Join(", ", txLinks))}"; if(clusterConfig.Notifications?.Admin?.NotifyPaymentSuccess == true) { diff --git a/src/Miningcore/Payments/PaymentSchemes/PPLNSPaymentScheme.cs b/src/Miningcore/Payments/PaymentSchemes/PPLNSPaymentScheme.cs index 3f309c431..3499e0f50 100644 --- a/src/Miningcore/Payments/PaymentSchemes/PPLNSPaymentScheme.cs +++ b/src/Miningcore/Payments/PaymentSchemes/PPLNSPaymentScheme.cs @@ -92,7 +92,6 @@ public async Task UpdateBalancesAsync(IDbConnection con, IDbTransaction tx, IMin if(cutOffCount > 0) { await LogDiscardedSharesAsync(poolConfig, block, shareCutOffDate.Value); - #if !DEBUG logger.Info(() => $"Deleting {cutOffCount} discarded shares before {shareCutOffDate.Value:O}"); await shareRepo.DeleteSharesBeforeCreatedAsync(con, tx, poolConfig.Id, shareCutOffDate.Value); @@ -156,7 +155,7 @@ private async Task LogDiscardedSharesAsync(PoolConfig poolConfig, Block block, D #endregion // IPayoutScheme - private async Task CalculateRewardsAsync(IMiningPool pool, IPayoutHandler payoutHandler, decimal window, Block block, decimal blockReward, + private async Task CalculateRewardsAsync(IMiningPool pool, IPayoutHandler payoutHandler,decimal window, Block block, decimal blockReward, Dictionary shares, Dictionary rewards, CancellationToken ct) { var poolConfig = pool.Config; @@ -183,16 +182,16 @@ private async Task LogDiscardedSharesAsync(PoolConfig poolConfig, Block block, D { var share = page[i]; - // build address var address = share.Miner; + var shareDiffAdjusted = payoutHandler.AdjustShareDifficulty(share.Difficulty); // record attributed shares for diagnostic purposes if(!shares.ContainsKey(address)) - shares[address] = share.Difficulty; + shares[address] = shareDiffAdjusted; else - shares[address] += share.Difficulty; + shares[address] += shareDiffAdjusted; - var score = (decimal) (payoutHandler.AdjustShareDifficulty(share.Difficulty) / share.NetworkDifficulty); + var score = (decimal) (shareDiffAdjusted / share.NetworkDifficulty); // if accumulated score would cross threshold, cap it to the remaining value if(accumulatedScore + score >= window) diff --git a/src/Miningcore/Payments/PaymentSchemes/PROPPaymentScheme.cs b/src/Miningcore/Payments/PaymentSchemes/PROPPaymentScheme.cs index 89dd9796e..f3b73f72d 100644 --- a/src/Miningcore/Payments/PaymentSchemes/PROPPaymentScheme.cs +++ b/src/Miningcore/Payments/PaymentSchemes/PROPPaymentScheme.cs @@ -176,14 +176,15 @@ private async Task LogDiscardedSharesAsync(PoolConfig poolConfig, Block block, D { var share = page[i]; var address = share.Miner; + var shareDiffAdjusted = payoutHandler.AdjustShareDifficulty(share.Difficulty); // record attributed shares for diagnostic purposes if(!shares.ContainsKey(address)) - shares[address] = share.Difficulty; + shares[address] = shareDiffAdjusted; else - shares[address] += share.Difficulty; + shares[address] += shareDiffAdjusted; - var score = (decimal) (payoutHandler.AdjustShareDifficulty(share.Difficulty) / share.NetworkDifficulty); + var score = (decimal) (shareDiffAdjusted / share.NetworkDifficulty); if(!scores.ContainsKey(address)) scores[address] = score; diff --git a/src/Miningcore/Payments/PayoutManager.cs b/src/Miningcore/Payments/PayoutManager.cs index 43a011482..c9ddf2eeb 100644 --- a/src/Miningcore/Payments/PayoutManager.cs +++ b/src/Miningcore/Payments/PayoutManager.cs @@ -20,6 +20,7 @@ using Miningcore.Persistence.Model; using Miningcore.Persistence.Repositories; using NLog; +using Org.BouncyCastle.Utilities.Net; using Contract = Miningcore.Contracts.Contract; namespace Miningcore.Payments diff --git a/src/Miningcore/Persistence/Postgres/Repositories/MinerRepository.cs b/src/Miningcore/Persistence/Postgres/Repositories/MinerRepository.cs index 98a764020..9ea49466e 100644 --- a/src/Miningcore/Persistence/Postgres/Repositories/MinerRepository.cs +++ b/src/Miningcore/Persistence/Postgres/Repositories/MinerRepository.cs @@ -30,7 +30,7 @@ public async Task GetSettings(IDbConnection con, IDbTransaction t return mapper.Map(entity); } - public async Task UpdateSettings(IDbConnection con, IDbTransaction tx, MinerSettings settings) + public Task UpdateSettings(IDbConnection con, IDbTransaction tx, MinerSettings settings) { const string query = "INSERT INTO miner_settings(poolid, address, paymentthreshold, created, updated) " + "VALUES(@poolid, @address, @paymentthreshold, now(), now()) " + @@ -38,9 +38,7 @@ public async Task UpdateSettings(IDbConnection con, IDbTransactio "SET paymentthreshold = @paymentthreshold, updated = now() " + "WHERE miner_settings.poolid = @poolid AND miner_settings.address = @address"; - await con.ExecuteAsync(query, settings, tx); - - return await GetSettings(con, tx, settings.PoolId, settings.Address); + return con.ExecuteAsync(query, settings, tx); } } } diff --git a/src/Miningcore/Persistence/Postgres/Repositories/ShareRepository.cs b/src/Miningcore/Persistence/Postgres/Repositories/ShareRepository.cs index 7b5496839..b0b23a1c4 100644 --- a/src/Miningcore/Persistence/Postgres/Repositories/ShareRepository.cs +++ b/src/Miningcore/Persistence/Postgres/Repositories/ShareRepository.cs @@ -193,5 +193,17 @@ public async Task GetHashAccumulationBetweenCreatedAsync(ID return (await con.QueryAsync(query, new { poolId, start, end })) .ToArray(); } + + public async Task GetRecentyUsedIpAddresses(IDbConnection con, IDbTransaction tx, string poolId, string miner) + { + logger.LogInvoke(new object[] { poolId }); + + const string query = "SELECT DISTINCT s.ipaddress FROM (SELECT * FROM shares " + + "WHERE poolid = @poolId and miner = @miner ORDER BY CREATED DESC LIMIT 100) s"; + + return (await con.QueryAsync(query, new { poolId, miner }, tx)) + .ToArray(); + } } } + diff --git a/src/Miningcore/Persistence/Repositories/IMinerRepository.cs b/src/Miningcore/Persistence/Repositories/IMinerRepository.cs index 2a3ff1269..f80478420 100644 --- a/src/Miningcore/Persistence/Repositories/IMinerRepository.cs +++ b/src/Miningcore/Persistence/Repositories/IMinerRepository.cs @@ -7,6 +7,6 @@ namespace Miningcore.Persistence.Repositories public interface IMinerRepository { Task GetSettings(IDbConnection con, IDbTransaction tx, string poolId, string address); - Task UpdateSettings(IDbConnection con, IDbTransaction tx, MinerSettings settings); + Task UpdateSettings(IDbConnection con, IDbTransaction tx, MinerSettings settings); } } diff --git a/src/Miningcore/Persistence/Repositories/IShareRepository.cs b/src/Miningcore/Persistence/Repositories/IShareRepository.cs index 9c2bb53e9..970942a76 100644 --- a/src/Miningcore/Persistence/Repositories/IShareRepository.cs +++ b/src/Miningcore/Persistence/Repositories/IShareRepository.cs @@ -25,5 +25,7 @@ public interface IShareRepository Task GetAccumulatedShareDifficultyBetweenCreatedAsync(IDbConnection con, string poolId, DateTime start, DateTime end); Task GetAccumulatedShareDifficultyTotalAsync(IDbConnection con, string poolId); Task GetHashAccumulationBetweenCreatedAsync(IDbConnection con, string poolId, DateTime start, DateTime end); + + Task GetRecentyUsedIpAddresses(IDbConnection con, IDbTransaction tx, string poolId, string miner); } } diff --git a/src/Miningcore/Program.cs b/src/Miningcore/Program.cs index 7e86fd189..3cbdd0f54 100644 --- a/src/Miningcore/Program.cs +++ b/src/Miningcore/Program.cs @@ -28,10 +28,12 @@ using Miningcore.Api.Controllers; using Miningcore.Api.Middlewares; using Miningcore.Api.Responses; +using Miningcore.Blockchain.Ergo; using Miningcore.Configuration; using Miningcore.Crypto.Hashing.Algorithms; using Miningcore.Crypto.Hashing.Equihash; using Miningcore.Crypto.Hashing.Ethash; +using Miningcore.Extensions; using Miningcore.Messaging; using Miningcore.Mining; using Miningcore.Native; diff --git a/src/Miningcore/Stratum/StratumConnection.cs b/src/Miningcore/Stratum/StratumConnection.cs index 062aa5e83..3e92f0c30 100644 --- a/src/Miningcore/Stratum/StratumConnection.cs +++ b/src/Miningcore/Stratum/StratumConnection.cs @@ -188,7 +188,7 @@ public ValueTask RespondAsync(T payload, object id) public ValueTask RespondErrorAsync(StratumError code, string message, object id, object result = null, object data = null) { - return RespondAsync(new JsonRpcResponse(new JsonRpcException((int) code, message, null), id, result)); + return RespondAsync(new JsonRpcResponse(new JsonRpcError((int) code, message, null), id, result)); } public ValueTask RespondAsync(JsonRpcResponse response) @@ -231,7 +231,7 @@ private async ValueTask SendAsync(T payload) private async Task FillReceivePipeAsync(CancellationToken ct) { - while(true) + while(!ct.IsCancellationRequested) { logger.Debug(() => $"[{ConnectionId}] [NET] Waiting for data ..."); @@ -259,7 +259,7 @@ private async Task ProcessReceivePipeAsync(CancellationToken ct, TcpProxyProtocolConfig proxyProtocol, Func onRequestAsync) { - while(true) + while(!ct.IsCancellationRequested) { logger.Debug(() => $"[{ConnectionId}] [PIPE] Waiting for data ..."); @@ -299,15 +299,15 @@ private async Task ProcessReceivePipeAsync(CancellationToken ct, private async Task ProcessSendQueueAsync(CancellationToken ct) { - while(true) + while(!ct.IsCancellationRequested) { var msg = await sendQueue.ReceiveAsync(ct); - await SendMessage(msg); + await SendMessage(msg, ct); } } - private async Task SendMessage(object msg) + private async Task SendMessage(object msg, CancellationToken ct) { logger.Debug(() => $"[{ConnectionId}] Sending: {JsonConvert.SerializeObject(msg)}"); @@ -315,24 +315,14 @@ private async Task SendMessage(object msg) try { - await using(var stream = new MemoryStream(buffer, true)) + using(var ctsTimeout = CancellationTokenSource.CreateLinkedTokenSource(ct)) { - // serialize - await using(var writer = new StreamWriter(stream, StratumConstants.Encoding, MaxOutboundRequestLength, true)) - { - serializer.Serialize(writer, msg); - } + ctsTimeout.CancelAfter(sendTimeout); - stream.WriteByte((byte) '\n'); // terminator - - // send - using(var ctsTimeout = new CancellationTokenSource()) - { - ctsTimeout.CancelAfter(sendTimeout); + var cb = SerializeMessage(msg, buffer); - await networkStream.WriteAsync(buffer, 0, (int) stream.Position, ctsTimeout.Token); - await networkStream.FlushAsync(ctsTimeout.Token); - } + await networkStream.WriteAsync(buffer, 0, cb, ctsTimeout.Token); + await networkStream.FlushAsync(ctsTimeout.Token); } } @@ -342,6 +332,20 @@ private async Task SendMessage(object msg) } } + private static int SerializeMessage(object msg, byte[] buffer) + { + var stream = new MemoryStream(buffer, true); + + using (var writer = new StreamWriter(stream, StratumConstants.Encoding, MaxOutboundRequestLength, true)) + { + serializer.Serialize(writer, msg); + } + + stream.WriteByte((byte) '\n'); // terminator + + return (int) stream.Position; + } + private async Task ProcessRequestAsync( CancellationToken ct, Func onRequestAsync, diff --git a/src/Miningcore/Stratum/StratumServer.cs b/src/Miningcore/Stratum/StratumServer.cs index 3e1e4eb7d..bf46418bd 100644 --- a/src/Miningcore/Stratum/StratumServer.cs +++ b/src/Miningcore/Stratum/StratumServer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Net; @@ -71,10 +72,12 @@ static StratumServer() } } - protected readonly Dictionary connections = new(); - protected static readonly Dictionary certs = new(); + protected readonly ConcurrentDictionary connections = new(); + protected static readonly ConcurrentDictionary certs = new(); protected static readonly HashSet ignoredSocketErrors; - protected static readonly MethodBase streamWriterCtor = typeof(StreamWriter).GetConstructor(new[] { typeof(Stream), typeof(Encoding), typeof(int), typeof(bool) }); + + protected static readonly MethodBase streamWriterCtor = typeof(StreamWriter).GetConstructor( + new[] { typeof(Stream), typeof(Encoding), typeof(int), typeof(bool) }); protected readonly IComponentContext ctx; protected readonly IMessageBus messageBus; @@ -97,14 +100,16 @@ public Task RunAsync(CancellationToken ct, params StratumEndpoint[] endpoints) server.Bind(port.IPEndPoint); server.Listen(); - return Listen(server, port, GetTlsCert(port), ct); + return Task.Run(()=> Listen(server, port, ct), ct); }).ToArray(); return Task.WhenAll(tasks); } - private async Task Listen(Socket server, StratumEndpoint port, X509Certificate2 cert, CancellationToken ct) + private async Task Listen(Socket server, StratumEndpoint port, CancellationToken ct) { + var cert = GetTlsCert(port); + while(!ct.IsCancellationRequested) { try @@ -129,55 +134,39 @@ private async Task Listen(Socket server, StratumEndpoint port, X509Certificate2 private void AcceptConnection(Socket socket, StratumEndpoint port, X509Certificate2 cert, CancellationToken ct) { - Task.Run(() => + Task.Run(() => Guard(() => { - var client = (IPEndPoint) socket.RemoteEndPoint; + var remoteEndpoint = (IPEndPoint) socket.RemoteEndPoint; // dispose of banned clients as early as possible - if(client != null && banManager?.IsBanned(client.Address) == true) - { - logger.Debug(() => $"Disconnecting banned ip {client.Address}"); - socket.Close(); + if (DisconnectIfBanned(socket, remoteEndpoint)) return; - } - var connectionId = CorrelationIdGenerator.GetNextId(); - var connection = new StratumConnection(logger, clock, connectionId); - var remoteEndpoint = (IPEndPoint) socket.RemoteEndPoint; + // init connection + var connection = new StratumConnection(logger, clock, CorrelationIdGenerator.GetNextId()); - logger.Info(() => $"[{connectionId}] Accepting connection from {remoteEndpoint.Address}:{remoteEndpoint.Port} ..."); + logger.Info(() => $"[{connection.ConnectionId}] Accepting connection from {remoteEndpoint.Address}:{remoteEndpoint.Port} ..."); - RegisterConnection(connection, connectionId); + RegisterConnection(connection); OnConnect(connection, port.IPEndPoint); connection.DispatchAsync(socket, ct, port, remoteEndpoint, cert, OnRequestAsync, OnConnectionComplete, OnConnectionError); - }, ct); + }, ex=> logger.Error(ex)), ct); } - protected virtual void RegisterConnection(StratumConnection connection, string connectionId) + protected virtual void RegisterConnection(StratumConnection connection) { - lock(connections) - { - connections[connectionId] = connection; - } + var result = connections.TryAdd(connection.ConnectionId, connection); + Debug.Assert(result); - // ReSharper disable once InconsistentlySynchronizedField PublishTelemetry(TelemetryCategory.Connections, TimeSpan.Zero, true, connections.Count); } protected virtual void UnregisterConnection(StratumConnection connection) { - var subscriptionId = connection.ConnectionId; + var result = connections.TryRemove(connection.ConnectionId, out _); + Debug.Assert(result); - if(!string.IsNullOrEmpty(subscriptionId)) - { - lock(connections) - { - connections.Remove(subscriptionId); - } - } - - // ReSharper disable once InconsistentlySynchronizedField PublishTelemetry(TelemetryCategory.Connections, TimeSpan.Zero, true, connections.Count); } @@ -282,6 +271,7 @@ protected virtual void CloseConnection(StratumConnection connection) Contract.RequiresNonNull(connection, nameof(connection)); connection.Disconnect(); + UnregisterConnection(connection); } @@ -304,37 +294,25 @@ private X509Certificate2 GetTlsCert(StratumEndpoint port) return cert; } - protected void ForEachConnection(Action action) + private bool DisconnectIfBanned(Socket socket, IPEndPoint remoteEndpoint) { - StratumConnection[] tmp; + if(remoteEndpoint == null || banManager == null) + return false; - lock(connections) + if (banManager.IsBanned(remoteEndpoint.Address)) { - tmp = connections.Values.ToArray(); - } + logger.Debug(() => $"Disconnecting banned ip {remoteEndpoint.Address}"); + socket.Close(); - foreach(var client in tmp) - { - try - { - action(client); - } - - catch(Exception ex) - { - logger.Error(ex); - } + return true; } + + return false; } protected IEnumerable ForEachConnection(Func func) { - StratumConnection[] tmp; - - lock(connections) - { - tmp = connections.Values.ToArray(); - } + var tmp = connections.Values.ToArray(); return tmp.Select(func); } diff --git a/src/Miningcore/coins.json b/src/Miningcore/coins.json index dc6796dea..b583324e7 100644 --- a/src/Miningcore/coins.json +++ b/src/Miningcore/coins.json @@ -1493,5 +1493,13 @@ }, "explorerTxLink": "https://explorer.callisto.network/tx/{0}", "explorerAccountLink": "https://explorer.callisto.network/address/{0}" + }, + "ergo": { + "name": "Ergo", + "symbol": "ERG", + "family": "ergo", + "explorerBlockLink": "https://explorer.ergoplatform.com/en/blocks/$hash$", + "explorerTxLink": "https://explorer.ergoplatform.com/en/transactions/{0}", + "explorerAccountLink": "https://explorer.ergoplatform.com/en/addresses/{0}" } } diff --git a/src/Native/libkawpow/Makefile b/src/Native/libkawpow/Makefile new file mode 100644 index 000000000..75e475704 --- /dev/null +++ b/src/Native/libkawpow/Makefile @@ -0,0 +1,16 @@ +CFLAGS += -g -Wall -c -fPIC -O2 -Wno-pointer-sign -Wno-char-subscripts -Wno-unused-variable -Wno-unused-function -Wno-strict-aliasing -Wno-discarded-qualifiers -Wno-unused-const-variable +CXXFLAGS += -g -Wall -fPIC -fpermissive -O2 -Wno-char-subscripts -Wno-unused-variable -Wno-unused-function -Wno-strict-aliasing -Wno-sign-compare -std=c++11 +LDFLAGS += -shared +TARGET = libkawpow.so + +OBJECTS = ethash/ethash.o keccak/keccak.o keccak/keccakf800.o keccak/keccakf1600.o ethash/managed.o ethash/primes.o ethash/progpow.o + +all: $(TARGET) + +$(TARGET): $(OBJECTS) + $(CXX) $(LDFLAGS) -o $@ $^ $(LDLIBS) + +.PHONY: clean + +clean: + $(RM) $(TARGET) $(OBJECTS) diff --git a/src/Native/libkawpow/attributes.h b/src/Native/libkawpow/attributes.h new file mode 100644 index 000000000..83be231f0 --- /dev/null +++ b/src/Native/libkawpow/attributes.h @@ -0,0 +1,33 @@ +/* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. + * Copyright 2018-2019 Pawel Bylica. + * Licensed under the Apache License, Version 2.0. + */ + +#pragma once + +/** inline */ +#if _MSC_VER || __STDC_VERSION__ +#define INLINE inline +#else +#define INLINE +#endif + +/** [[always_inline]] */ +#if _MSC_VER +#define ALWAYS_INLINE __forceinline +#elif defined(__has_attribute) && __STDC_VERSION__ +#if __has_attribute(always_inline) +#define ALWAYS_INLINE __attribute__((always_inline)) +#endif +#endif +#if !defined(ALWAYS_INLINE) +#define ALWAYS_INLINE +#endif + +/** [[no_sanitize()]] */ +#if __clang__ +#define NO_SANITIZE(sanitizer) \ + __attribute__((no_sanitize(sanitizer))) +#else +#define NO_SANITIZE(sanitizer) +#endif diff --git a/src/Native/libkawpow/dllmain.cpp b/src/Native/libkawpow/dllmain.cpp new file mode 100644 index 000000000..69b58914b --- /dev/null +++ b/src/Native/libkawpow/dllmain.cpp @@ -0,0 +1,19 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "stdafx.h" + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + diff --git a/src/Native/libkawpow/ethash/CMakeLists.txt b/src/Native/libkawpow/ethash/CMakeLists.txt new file mode 100644 index 000000000..1eefc3f24 --- /dev/null +++ b/src/Native/libkawpow/ethash/CMakeLists.txt @@ -0,0 +1,37 @@ +# ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. +# Copyright 2018-2019 Pawel Bylica. +# Licensed under the Apache License, Version 2.0. + +include(GNUInstallDirs) + +add_library( + ethash SHARED + bit_manipulation.h + builtins.h + endianness.hpp + ${include_dir}/ethash/ethash.h + ${include_dir}/ethash/ethash.hpp + ethash-internal.hpp + ethash.cpp + ${include_dir}/ethash/hash_types.h + managed.cpp + kiss99.hpp + primes.h + primes.c + ${include_dir}/ethash/progpow.hpp + progpow.cpp +) +set_property(TARGET ethash PROPERTY POSITION_INDEPENDENT_CODE ON) + +target_link_libraries(ethash PRIVATE keccak) +target_include_directories(ethash PUBLIC $$) +if(CABLE_COMPILER_GNULIKE AND NOT SANITIZE MATCHES undefined) + target_compile_options(ethash PRIVATE $<$:-fno-rtti>) +endif() + +install( + TARGETS ethash + EXPORT ethashTargets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) diff --git a/src/Native/libkawpow/ethash/bit_manipulation.h b/src/Native/libkawpow/ethash/bit_manipulation.h new file mode 100644 index 000000000..b88bfdaab --- /dev/null +++ b/src/Native/libkawpow/ethash/bit_manipulation.h @@ -0,0 +1,81 @@ +/* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. + * Copyright 2018-2019 Pawel Bylica. + * Licensed under the Apache License, Version 2.0. + */ + +#pragma once + +#include "builtins.h" +#include "../support/attributes.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static inline uint32_t rotl32(uint32_t n, unsigned int c) +{ + const unsigned int mask = 31; + + c &= mask; + unsigned int neg_c = (unsigned int)(-(int)c); + return (n << c) | (n >> (neg_c & mask)); +} + +static inline uint32_t rotr32(uint32_t n, unsigned int c) +{ + const unsigned int mask = 31; + + c &= mask; + unsigned int neg_c = (unsigned int)(-(int)c); + return (n >> c) | (n << (neg_c & mask)); +} + +static inline uint32_t clz32(uint32_t x) +{ + return x ? (uint32_t)__builtin_clz(x) : 32; +} + +static inline uint32_t popcount32(uint32_t x) +{ + return (uint32_t)__builtin_popcount(x); +} + +static inline uint32_t mul_hi32(uint32_t x, uint32_t y) +{ + return (uint32_t)(((uint64_t)x * (uint64_t)y) >> 32); +} + + +/** FNV 32-bit prime. */ +static const uint32_t fnv_prime = 0x01000193; + +/** FNV 32-bit offset basis. */ +static const uint32_t fnv_offset_basis = 0x811c9dc5; + +/** + * The implementation of FNV-1 hash. + * + * See https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1_hash. + */ +NO_SANITIZE("unsigned-integer-overflow") +static inline uint32_t fnv1(uint32_t u, uint32_t v) noexcept +{ + return (u * fnv_prime) ^ v; +} + +/** + * The implementation of FNV-1a hash. + * + * See https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash. + */ +NO_SANITIZE("unsigned-integer-overflow") +static inline uint32_t fnv1a(uint32_t u, uint32_t v) noexcept +{ + return (u ^ v) * fnv_prime; +} + +#ifdef __cplusplus +} +#endif diff --git a/src/Native/libkawpow/ethash/builtins.h b/src/Native/libkawpow/ethash/builtins.h new file mode 100644 index 000000000..0c43188ad --- /dev/null +++ b/src/Native/libkawpow/ethash/builtins.h @@ -0,0 +1,43 @@ +/* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. + * Copyright 2018-2019 Pawel Bylica. + * Licensed under the Apache License, Version 2.0. + */ + +/** + * @file + * Implementation of GCC/clang builtins for MSVC compiler. + */ + +#pragma once + +#ifdef _MSC_VER +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Returns the number of leading 0-bits in `x`, starting at the most significant bit position. + * If `x` is 0, the result is undefined. + */ +static inline int __builtin_clz(unsigned int x) +{ + unsigned long most_significant_bit; + _BitScanReverse(&most_significant_bit, x); + return 31 - (int)most_significant_bit; +} + +/** + * Returns the number of 1-bits in `x`. + */ +static inline int __builtin_popcount(unsigned int x) +{ + return (int)__popcnt(x); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/Native/libkawpow/ethash/endianness.hpp b/src/Native/libkawpow/ethash/endianness.hpp new file mode 100644 index 000000000..3426f4689 --- /dev/null +++ b/src/Native/libkawpow/ethash/endianness.hpp @@ -0,0 +1,97 @@ +// ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. +// Copyright 2018-2019 Pawel Bylica. +// Licensed under the Apache License, Version 2.0. + +/// @file +/// This file contains helper functions to handle big-endian architectures. +/// The Ethash algorithm is naturally defined for little-endian architectures +/// so for those the helpers are just no-op empty functions. +/// For big-endian architectures we need 32-bit and 64-bit byte swapping in +/// some places. + +#pragma once + +#if _WIN32 + +#include + +#define bswap32 _byteswap_ulong +#define bswap64 _byteswap_uint64 + +// On Windows assume little endian. +#define __LITTLE_ENDIAN 1234 +#define __BIG_ENDIAN 4321 +#define __BYTE_ORDER __LITTLE_ENDIAN + +#elif __APPLE__ + +#include + +#define bswap32 __builtin_bswap32 +#define bswap64 __builtin_bswap64 + +#else + +#include + +#define bswap32 __builtin_bswap32 +#define bswap64 __builtin_bswap64 + +#endif + +namespace ethash +{ +#if __BYTE_ORDER == __LITTLE_ENDIAN + +struct le +{ + static uint32_t uint32(uint32_t x) noexcept { return x; } + static uint64_t uint64(uint64_t x) noexcept { return x; } + + static const hash1024& uint32s(const hash1024& h) noexcept { return h; } + static const hash512& uint32s(const hash512& h) noexcept { return h; } + static const hash256& uint32s(const hash256& h) noexcept { return h; } +}; + +struct be +{ + static uint64_t uint64(uint64_t x) noexcept { return bswap64(x); } +}; + + +#elif __BYTE_ORDER == __BIG_ENDIAN + +struct le +{ + static uint32_t uint32(uint32_t x) noexcept { return bswap32(x); } + static uint64_t uint64(uint64_t x) noexcept { return bswap64(x); } + + static hash1024 uint32s(hash1024 h) noexcept + { + for (auto& w : h.word32s) + w = uint32(w); + return h; + } + + static hash512 uint32s(hash512 h) noexcept + { + for (auto& w : h.word32s) + w = uint32(w); + return h; + } + + static hash256 uint32s(hash256 h) noexcept + { + for (auto& w : h.word32s) + w = uint32(w); + return h; + } +}; + +struct be +{ + static uint64_t uint64(uint64_t x) noexcept { return x; } +}; + +#endif +} // namespace ethash diff --git a/src/Native/libkawpow/ethash/ethash-internal.hpp b/src/Native/libkawpow/ethash/ethash-internal.hpp new file mode 100644 index 000000000..1d4ccd979 --- /dev/null +++ b/src/Native/libkawpow/ethash/ethash-internal.hpp @@ -0,0 +1,68 @@ +// ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. +// Copyright 2018-2019 Pawel Bylica. +// Licensed under the Apache License, Version 2.0. + +/// @file +/// Contains declarations of internal ethash functions to allow them to be +/// unit-tested. + +#pragma once + +#include "ethash.hpp" + +#include "endianness.hpp" + +#include +#include + +extern "C" struct ethash_epoch_context_full : ethash_epoch_context +{ + ethash_hash1024* full_dataset; + + constexpr ethash_epoch_context_full(int epoch, int light_num_items, + const ethash_hash512* light, const uint32_t* l1, int dataset_num_items, + ethash_hash1024* dataset) noexcept + : ethash_epoch_context{epoch, light_num_items, light, l1, dataset_num_items}, + full_dataset{dataset} + {} +}; + +namespace ethash +{ +inline bool is_less_or_equal(const hash256& a, const hash256& b) noexcept +{ + for (size_t i = 0; i < (sizeof(a) / sizeof(a.word64s[0])); ++i) + { + if (be::uint64(a.word64s[i]) > be::uint64(b.word64s[i])) + return false; + if (be::uint64(a.word64s[i]) < be::uint64(b.word64s[i])) + return true; + } + return true; +} + +inline bool is_equal(const hash256& a, const hash256& b) noexcept +{ + return std::memcmp(a.bytes, b.bytes, sizeof(a)) == 0; +} + +void build_light_cache(hash512 cache[], int num_items, const hash256& seed) noexcept; + +hash512 calculate_dataset_item_512(const epoch_context& context, int64_t index) noexcept; +hash1024 calculate_dataset_item_1024(const epoch_context& context, uint32_t index) noexcept; +hash2048 calculate_dataset_item_2048(const epoch_context& context, uint32_t index) noexcept; + +namespace generic +{ +using hash_fn_512 = hash512 (*)(const uint8_t* data, size_t size); +using build_light_cache_fn = void (*)(hash512 cache[], int num_items, const hash256& seed); + +void build_light_cache( + hash_fn_512 hash_fn, hash512 cache[], int num_items, const hash256& seed) noexcept; + +epoch_context_full* create_epoch_context( + build_light_cache_fn build_fn, int epoch_number, bool full) noexcept; + +} // namespace generic + +} // namespace ethash diff --git a/src/Native/libkawpow/ethash/ethash.cpp b/src/Native/libkawpow/ethash/ethash.cpp new file mode 100644 index 000000000..436ded06b --- /dev/null +++ b/src/Native/libkawpow/ethash/ethash.cpp @@ -0,0 +1,444 @@ +// ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. +// Copyright 2018-2019 Pawel Bylica. +// Licensed under the Apache License, Version 2.0. + +#include "hash_types.hpp" + +#include "../attributes.h" +#include "bit_manipulation.h" +#include "endianness.hpp" +#include "ethash-internal.hpp" +#include "primes.h" + +#include "keccak.hpp" +#include "progpow.hpp" + +#include +#include +#include +#include + +namespace ethash +{ +// Internal constants: +constexpr static int light_cache_init_size = 1 << 24; +constexpr static int light_cache_growth = 1 << 17; +constexpr static int light_cache_rounds = 3; +constexpr static int full_dataset_init_size = 1 << 30; +constexpr static int full_dataset_growth = 1 << 23; +constexpr static int full_dataset_item_parents = 512; + +// Verify constants: +static_assert(sizeof(hash512) == ETHASH_LIGHT_CACHE_ITEM_SIZE, ""); +static_assert(sizeof(hash1024) == ETHASH_FULL_DATASET_ITEM_SIZE, ""); +static_assert(light_cache_item_size == ETHASH_LIGHT_CACHE_ITEM_SIZE, ""); +static_assert(full_dataset_item_size == ETHASH_FULL_DATASET_ITEM_SIZE, ""); + + +namespace +{ +using ::fnv1; + +inline hash512 fnv1(const hash512& u, const hash512& v) noexcept +{ + hash512 r; + for (size_t i = 0; i < sizeof(r) / sizeof(r.word32s[0]); ++i) + r.word32s[i] = fnv1(u.word32s[i], v.word32s[i]); + return r; +} + +inline hash512 bitwise_xor(const hash512& x, const hash512& y) noexcept +{ + hash512 z; + for (size_t i = 0; i < sizeof(z) / sizeof(z.word64s[0]); ++i) + z.word64s[i] = x.word64s[i] ^ y.word64s[i]; + return z; +} +} // namespace + +int find_epoch_number(const hash256& seed) noexcept +{ + static constexpr int num_tries = 30000; // Divisible by 16. + + // Thread-local cache of the last search. + static thread_local int cached_epoch_number = 0; + static thread_local hash256 cached_seed = {}; + + // Load from memory once (memory will be clobbered by keccak256()). + const uint32_t seed_part = seed.word32s[0]; + const int e = cached_epoch_number; + hash256 s = cached_seed; + + if (s.word32s[0] == seed_part) + return e; + + // Try the next seed, will match for sequential epoch access. + s = keccak256(s); + if (s.word32s[0] == seed_part) + { + cached_seed = s; + cached_epoch_number = e + 1; + return e + 1; + } + + // Search for matching seed starting from epoch 0. + s = {}; + for (int i = 0; i < num_tries; ++i) + { + if (s.word32s[0] == seed_part) + { + cached_seed = s; + cached_epoch_number = i; + return i; + } + + s = keccak256(s); + } + + return -1; +} + +namespace generic +{ +void build_light_cache( + hash_fn_512 hash_fn, hash512 cache[], int num_items, const hash256& seed) noexcept +{ + hash512 item = hash_fn(seed.bytes, sizeof(seed)); + cache[0] = item; + for (int i = 1; i < num_items; ++i) + { + item = hash_fn(item.bytes, sizeof(item)); + cache[i] = item; + } + + for (int q = 0; q < light_cache_rounds; ++q) + { + for (int i = 0; i < num_items; ++i) + { + const uint32_t index_limit = static_cast(num_items); + + // Fist index: 4 first bytes of the item as little-endian integer. + const uint32_t t = le::uint32(cache[i].word32s[0]); + const uint32_t v = t % index_limit; + + // Second index. + const uint32_t w = static_cast(num_items + (i - 1)) % index_limit; + + const hash512 x = bitwise_xor(cache[v], cache[w]); + cache[i] = hash_fn(x.bytes, sizeof(x)); + } + } +} + +epoch_context_full* create_epoch_context( + build_light_cache_fn build_fn, int epoch_number, bool full) noexcept +{ + static_assert(sizeof(epoch_context_full) < sizeof(hash512), "epoch_context too big"); + static constexpr size_t context_alloc_size = sizeof(hash512); + + const int light_cache_num_items = calculate_light_cache_num_items(epoch_number); + const int full_dataset_num_items = calculate_full_dataset_num_items(epoch_number); + const size_t light_cache_size = get_light_cache_size(light_cache_num_items); + const size_t full_dataset_size = + full ? static_cast(full_dataset_num_items) * sizeof(hash1024) : + progpow::l1_cache_size; + + const size_t alloc_size = context_alloc_size + light_cache_size + full_dataset_size; + + char* const alloc_data = static_cast(std::calloc(1, alloc_size)); + if (!alloc_data) + return nullptr; // Signal out-of-memory by returning null pointer. + + hash512* const light_cache = reinterpret_cast(alloc_data + context_alloc_size); + const hash256 epoch_seed = calculate_epoch_seed(epoch_number); + build_fn(light_cache, light_cache_num_items, epoch_seed); + + uint32_t* const l1_cache = + reinterpret_cast(alloc_data + context_alloc_size + light_cache_size); + + hash1024* full_dataset = full ? reinterpret_cast(l1_cache) : nullptr; + + epoch_context_full* const context = new (alloc_data) epoch_context_full{ + epoch_number, + light_cache_num_items, + light_cache, + l1_cache, + full_dataset_num_items, + full_dataset, + }; + + auto* full_dataset_2048 = reinterpret_cast(l1_cache); + for (uint32_t i = 0; i < progpow::l1_cache_size / sizeof(full_dataset_2048[0]); ++i) + full_dataset_2048[i] = calculate_dataset_item_2048(*context, i); + return context; +} +} // namespace generic + +void build_light_cache(hash512 cache[], int num_items, const hash256& seed) noexcept +{ + return generic::build_light_cache(keccak512, cache, num_items, seed); +} + +struct item_state +{ + const hash512* const cache; + const int64_t num_cache_items; + const uint32_t seed; + + hash512 mix; + + ALWAYS_INLINE item_state(const epoch_context& context, int64_t index) noexcept + : cache{context.light_cache}, + num_cache_items{context.light_cache_num_items}, + seed{static_cast(index)} + { + mix = cache[index % num_cache_items]; + mix.word32s[0] ^= le::uint32(seed); + mix = le::uint32s(keccak512(mix)); + } + + ALWAYS_INLINE void update(uint32_t round) noexcept + { + static constexpr size_t num_words = sizeof(mix) / sizeof(uint32_t); + const uint32_t t = fnv1(seed ^ round, mix.word32s[round % num_words]); + const int64_t parent_index = t % num_cache_items; + mix = fnv1(mix, le::uint32s(cache[parent_index])); + } + + ALWAYS_INLINE hash512 final() noexcept { return keccak512(le::uint32s(mix)); } +}; + +hash512 calculate_dataset_item_512(const epoch_context& context, int64_t index) noexcept +{ + item_state item0{context, index}; + for (uint32_t j = 0; j < full_dataset_item_parents; ++j) + item0.update(j); + return item0.final(); +} + +/// Calculates a full dataset item +/// +/// This consist of two 512-bit items produced by calculate_dataset_item_partial(). +/// Here the computation is done interleaved for better performance. +hash1024 calculate_dataset_item_1024(const epoch_context& context, uint32_t index) noexcept +{ + item_state item0{context, int64_t(index) * 2}; + item_state item1{context, int64_t(index) * 2 + 1}; + + for (uint32_t j = 0; j < full_dataset_item_parents; ++j) + { + item0.update(j); + item1.update(j); + } + + return hash1024{{item0.final(), item1.final()}}; +} + +hash2048 calculate_dataset_item_2048(const epoch_context& context, uint32_t index) noexcept +{ + item_state item0{context, int64_t(index) * 4}; + item_state item1{context, int64_t(index) * 4 + 1}; + item_state item2{context, int64_t(index) * 4 + 2}; + item_state item3{context, int64_t(index) * 4 + 3}; + + for (uint32_t j = 0; j < full_dataset_item_parents; ++j) + { + item0.update(j); + item1.update(j); + item2.update(j); + item3.update(j); + } + + return hash2048{{item0.final(), item1.final(), item2.final(), item3.final()}}; +} + +namespace +{ +using lookup_fn = hash1024 (*)(const epoch_context&, uint32_t); + +inline hash512 hash_seed(const hash256& header_hash, uint64_t nonce) noexcept +{ + nonce = le::uint64(nonce); + uint8_t init_data[sizeof(header_hash) + sizeof(nonce)]; + std::memcpy(&init_data[0], &header_hash, sizeof(header_hash)); + std::memcpy(&init_data[sizeof(header_hash)], &nonce, sizeof(nonce)); + + return keccak512(init_data, sizeof(init_data)); +} + +inline hash256 hash_final(const hash512& seed, const hash256& mix_hash) +{ + uint8_t final_data[sizeof(seed) + sizeof(mix_hash)]; + std::memcpy(&final_data[0], seed.bytes, sizeof(seed)); + std::memcpy(&final_data[sizeof(seed)], mix_hash.bytes, sizeof(mix_hash)); + return keccak256(final_data, sizeof(final_data)); +} + +inline hash256 hash_kernel( + const epoch_context& context, const hash512& seed, lookup_fn lookup) noexcept +{ + static constexpr size_t num_words = sizeof(hash1024) / sizeof(uint32_t); + const uint32_t index_limit = static_cast(context.full_dataset_num_items); + const uint32_t seed_init = le::uint32(seed.word32s[0]); + + hash1024 mix{{le::uint32s(seed), le::uint32s(seed)}}; + + for (uint32_t i = 0; i < num_dataset_accesses; ++i) + { + const uint32_t p = fnv1(i ^ seed_init, mix.word32s[i % num_words]) % index_limit; + const hash1024 newdata = le::uint32s(lookup(context, p)); + + for (size_t j = 0; j < num_words; ++j) + mix.word32s[j] = fnv1(mix.word32s[j], newdata.word32s[j]); + } + + hash256 mix_hash; + for (size_t i = 0; i < num_words; i += 4) + { + const uint32_t h1 = fnv1(mix.word32s[i], mix.word32s[i + 1]); + const uint32_t h2 = fnv1(h1, mix.word32s[i + 2]); + const uint32_t h3 = fnv1(h2, mix.word32s[i + 3]); + mix_hash.word32s[i / 4] = h3; + } + + return le::uint32s(mix_hash); +} +} // namespace + +result hash(const epoch_context_full& context, const hash256& header_hash, uint64_t nonce) noexcept +{ + static const auto lazy_lookup = [](const epoch_context& ctx, uint32_t index) noexcept { + auto full_dataset = static_cast(ctx).full_dataset; + hash1024& item = full_dataset[index]; + if (item.word64s[0] == 0) + { + // TODO: Copy elision here makes it thread-safe? + item = calculate_dataset_item_1024(ctx, index); + } + + return item; + }; + + const hash512 seed = hash_seed(header_hash, nonce); + const hash256 mix_hash = hash_kernel(context, seed, lazy_lookup); + return {hash_final(seed, mix_hash), mix_hash}; +} + +search_result search_light(const epoch_context& context, const hash256& header_hash, + const hash256& boundary, uint64_t start_nonce, size_t iterations) noexcept +{ + const uint64_t end_nonce = start_nonce + iterations; + for (uint64_t nonce = start_nonce; nonce < end_nonce; ++nonce) + { + result r = hash(context, header_hash, nonce); + if (is_less_or_equal(r.final_hash, boundary)) + return {r, nonce}; + } + return {}; +} + +search_result search(const epoch_context_full& context, const hash256& header_hash, + const hash256& boundary, uint64_t start_nonce, size_t iterations) noexcept +{ + const uint64_t end_nonce = start_nonce + iterations; + for (uint64_t nonce = start_nonce; nonce < end_nonce; ++nonce) + { + result r = hash(context, header_hash, nonce); + if (is_less_or_equal(r.final_hash, boundary)) + return {r, nonce}; + } + return {}; +} +} // namespace ethash + +using namespace ethash; + +extern "C" { + +ethash_hash256 ethash_calculate_epoch_seed(int epoch_number) noexcept +{ + ethash_hash256 epoch_seed = {}; + for (int i = 0; i < epoch_number; ++i) + epoch_seed = ethash_keccak256_32(epoch_seed.bytes); + return epoch_seed; +} + +int ethash_calculate_light_cache_num_items(int epoch_number) noexcept +{ + static constexpr int item_size = sizeof(hash512); + static constexpr int num_items_init = light_cache_init_size / item_size; + static constexpr int num_items_growth = light_cache_growth / item_size; + static_assert( + light_cache_init_size % item_size == 0, "light_cache_init_size not multiple of item size"); + static_assert( + light_cache_growth % item_size == 0, "light_cache_growth not multiple of item size"); + + int num_items_upper_bound = num_items_init + epoch_number * num_items_growth; + int num_items = ethash_find_largest_prime(num_items_upper_bound); + return num_items; +} + +int ethash_calculate_full_dataset_num_items(int epoch_number) noexcept +{ + static constexpr int item_size = sizeof(hash1024); + static constexpr int num_items_init = full_dataset_init_size / item_size; + static constexpr int num_items_growth = full_dataset_growth / item_size; + static_assert(full_dataset_init_size % item_size == 0, + "full_dataset_init_size not multiple of item size"); + static_assert( + full_dataset_growth % item_size == 0, "full_dataset_growth not multiple of item size"); + + int num_items_upper_bound = num_items_init + epoch_number * num_items_growth; + int num_items = ethash_find_largest_prime(num_items_upper_bound); + return num_items; +} + +epoch_context* ethash_create_epoch_context(int epoch_number) noexcept +{ + return generic::create_epoch_context(build_light_cache, epoch_number, false); +} + +epoch_context_full* ethash_create_epoch_context_full(int epoch_number) noexcept +{ + return generic::create_epoch_context(build_light_cache, epoch_number, true); +} + +void ethash_destroy_epoch_context_full(epoch_context_full* context) noexcept +{ + ethash_destroy_epoch_context(context); +} + +void ethash_destroy_epoch_context(epoch_context* context) noexcept +{ + context->~epoch_context(); + std::free(context); +} + + +ethash_result ethash_hash( + const epoch_context* context, const hash256* header_hash, uint64_t nonce) noexcept +{ + const hash512 seed = hash_seed(*header_hash, nonce); + const hash256 mix_hash = hash_kernel(*context, seed, calculate_dataset_item_1024); + return {hash_final(seed, mix_hash), mix_hash}; +} + +bool ethash_verify_final_hash(const hash256* header_hash, const hash256* mix_hash, uint64_t nonce, + const hash256* boundary) noexcept +{ + const hash512 seed = hash_seed(*header_hash, nonce); + return is_less_or_equal(hash_final(seed, *mix_hash), *boundary); +} + +bool ethash_verify(const epoch_context* context, const hash256* header_hash, + const hash256* mix_hash, uint64_t nonce, const hash256* boundary) noexcept +{ + const hash512 seed = hash_seed(*header_hash, nonce); + if (!is_less_or_equal(hash_final(seed, *mix_hash), *boundary)) + return false; + + const hash256 expected_mix_hash = hash_kernel(*context, seed, calculate_dataset_item_1024); + return is_equal(expected_mix_hash, *mix_hash); +} + +} // extern "C" diff --git a/src/Native/libkawpow/ethash/ethash.h b/src/Native/libkawpow/ethash/ethash.h new file mode 100644 index 000000000..5d4230f6e --- /dev/null +++ b/src/Native/libkawpow/ethash/ethash.h @@ -0,0 +1,145 @@ +/* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. + * Copyright 2018-2019 Pawel Bylica. + * Licensed under the Apache License, Version 2.0. + */ + +#pragma once + +#include "hash_types.h" + +#include +#include + +#if defined(_MSC_VER) +// Microsoft +#define EXPORT __declspec(dllexport) +#define IMPORT __declspec(dllimport) +#elif defined(__GNUC__) +// GCC +#define EXPORT __attribute__((visibility("default"))) +#define IMPORT +#else +// do nothing and hope for the best? +#define EXPORT +#define IMPORT +#pragma warning Unknown dynamic link import / export semantics. +#endif + +#ifdef __cplusplus +#define NOEXCEPT noexcept +#else +#define NOEXCEPT +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The Ethash algorithm revision implemented as specified in the Ethash spec + * https://github.com/ethereum/wiki/wiki/Ethash. + */ +#define ETHASH_REVISION "23" + +#define ETHASH_EPOCH_LENGTH 7500 +#define ETHASH_LIGHT_CACHE_ITEM_SIZE 64 +#define ETHASH_FULL_DATASET_ITEM_SIZE 128 +#define ETHASH_NUM_DATASET_ACCESSES 64 + + +struct ethash_epoch_context +{ + const int epoch_number; + const int light_cache_num_items; + const union ethash_hash512* const light_cache; + const uint32_t* const l1_cache; + const int full_dataset_num_items; +}; + + +struct ethash_epoch_context_full; + + +struct ethash_result +{ + union ethash_hash256 final_hash; + union ethash_hash256 mix_hash; +}; + + +/** + * Calculates the number of items in the light cache for given epoch. + * + * This function will search for a prime number matching the criteria given + * by the Ethash so the execution time is not constant. It takes ~ 0.01 ms. + * + * @param epoch_number The epoch number. + * @return The number items in the light cache. + */ +int ethash_calculate_light_cache_num_items(int epoch_number) NOEXCEPT; + + +/** + * Calculates the number of items in the full dataset for given epoch. + * + * This function will search for a prime number matching the criteria given + * by the Ethash so the execution time is not constant. It takes ~ 0.05 ms. + * + * @param epoch_number The epoch number. + * @return The number items in the full dataset. + */ +int ethash_calculate_full_dataset_num_items(int epoch_number) NOEXCEPT; + +/** + * Calculates the epoch seed hash. + * @param epoch_number The epoch number. + * @return The epoch seed hash. + */ +union ethash_hash256 ethash_calculate_epoch_seed(int epoch_number) NOEXCEPT; + + +EXPORT struct ethash_epoch_context* ethash_create_epoch_context(int epoch_number) NOEXCEPT; + +/** + * Creates the epoch context with the full dataset initialized. + * + * The memory for the full dataset is only allocated and marked as "not-generated". + * The items of the full dataset are generated on the fly when hit for the first time. + * + * The memory allocated in the context MUST be freed with ethash_destroy_epoch_context_full(). + * + * @param epoch_number The epoch number. + * @return Pointer to the context or null in case of memory allocation failure. + */ +EXPORT struct ethash_epoch_context_full* ethash_create_epoch_context_full(int epoch_number) NOEXCEPT; + +EXPORT void ethash_destroy_epoch_context(struct ethash_epoch_context* context) NOEXCEPT; + +EXPORT void ethash_destroy_epoch_context_full(struct ethash_epoch_context_full* context) NOEXCEPT; + + +/** + * Get global shared epoch context. + */ +const struct ethash_epoch_context* ethash_get_global_epoch_context(int epoch_number) NOEXCEPT; + +/** + * Get global shared epoch context with full dataset initialized. + */ +const struct ethash_epoch_context_full* ethash_get_global_epoch_context_full( + int epoch_number) NOEXCEPT; + + +struct ethash_result ethash_hash(const struct ethash_epoch_context* context, + const union ethash_hash256* header_hash, uint64_t nonce) NOEXCEPT; + +bool ethash_verify(const struct ethash_epoch_context* context, + const union ethash_hash256* header_hash, const union ethash_hash256* mix_hash, uint64_t nonce, + const union ethash_hash256* boundary) NOEXCEPT; +bool ethash_verify_final_hash(const union ethash_hash256* header_hash, + const union ethash_hash256* mix_hash, uint64_t nonce, + const union ethash_hash256* boundary) NOEXCEPT; + +#ifdef __cplusplus +} +#endif diff --git a/src/Native/libkawpow/ethash/ethash.hpp b/src/Native/libkawpow/ethash/ethash.hpp new file mode 100644 index 000000000..835a3b731 --- /dev/null +++ b/src/Native/libkawpow/ethash/ethash.hpp @@ -0,0 +1,172 @@ +// ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. +// Copyright 2018-2019 Pawel Bylica. +// Licensed under the Apache License, Version 2.0. + +/// @file +/// +/// API design decisions: +/// +/// 1. Signed integer type is used whenever the size of the type is not +/// restricted by the Ethash specification. +/// See http://www.aristeia.com/Papers/C++ReportColumns/sep95.pdf. +/// See https://stackoverflow.com/questions/10168079/why-is-size-t-unsigned/. +/// See https://github.com/Microsoft/GSL/issues/171. + +#pragma once + +#include "ethash.h" +#include "hash_types.hpp" + +#include +#include +#include + +namespace ethash +{ +constexpr auto revision = ETHASH_REVISION; + +static constexpr int epoch_length = ETHASH_EPOCH_LENGTH; +static constexpr int light_cache_item_size = ETHASH_LIGHT_CACHE_ITEM_SIZE; +static constexpr int full_dataset_item_size = ETHASH_FULL_DATASET_ITEM_SIZE; +static constexpr int num_dataset_accesses = ETHASH_NUM_DATASET_ACCESSES; + +using epoch_context = ethash_epoch_context; +using epoch_context_full = ethash_epoch_context_full; + +using result = ethash_result; + +/// Constructs a 256-bit hash from an array of bytes. +/// +/// @param bytes A pointer to array of at least 32 bytes. +/// @return The constructed hash. +inline hash256 hash256_from_bytes(const uint8_t bytes[32]) noexcept +{ + hash256 h; + std::memcpy(&h, bytes, sizeof(h)); + return h; +} + +struct search_result +{ + bool solution_found = false; + uint64_t nonce = 0; + hash256 final_hash = {}; + hash256 mix_hash = {}; + + search_result() noexcept = default; + + search_result(result res, uint64_t n) noexcept + : solution_found(true), nonce(n), final_hash(res.final_hash), mix_hash(res.mix_hash) + {} +}; + + +/// Alias for ethash_calculate_light_cache_num_items(). +static constexpr auto calculate_light_cache_num_items = ethash_calculate_light_cache_num_items; + +/// Alias for ethash_calculate_full_dataset_num_items(). +static constexpr auto calculate_full_dataset_num_items = ethash_calculate_full_dataset_num_items; + +/// Alias for ethash_calculate_epoch_seed(). +static constexpr auto calculate_epoch_seed = ethash_calculate_epoch_seed; + + +/// Calculates the epoch number out of the block number. +inline constexpr int get_epoch_number(int block_number) noexcept +{ + return block_number / epoch_length; +} + +/** + * Coverts the number of items of a light cache to size in bytes. + * + * @param num_items The number of items in the light cache. + * @return The size of the light cache in bytes. + */ +inline constexpr size_t get_light_cache_size(int num_items) noexcept +{ + return static_cast(num_items) * light_cache_item_size; +} + +/** + * Coverts the number of items of a full dataset to size in bytes. + * + * @param num_items The number of items in the full dataset. + * @return The size of the full dataset in bytes. + */ +inline constexpr uint64_t get_full_dataset_size(int num_items) noexcept +{ + return static_cast(num_items) * full_dataset_item_size; +} + +/// Owned unique pointer to an epoch context. +using epoch_context_ptr = std::unique_ptr; + +using epoch_context_full_ptr = + std::unique_ptr; + +/// Creates Ethash epoch context. +/// +/// This is a wrapper for ethash_create_epoch_number C function that returns +/// the context as a smart pointer which handles the destruction of the context. +inline epoch_context_ptr create_epoch_context(int epoch_number) noexcept +{ + return {ethash_create_epoch_context(epoch_number), ethash_destroy_epoch_context}; +} + +inline epoch_context_full_ptr create_epoch_context_full(int epoch_number) noexcept +{ + return {ethash_create_epoch_context_full(epoch_number), ethash_destroy_epoch_context_full}; +} + + +inline result hash( + const epoch_context& context, const hash256& header_hash, uint64_t nonce) noexcept +{ + return ethash_hash(&context, &header_hash, nonce); +} + +result hash(const epoch_context_full& context, const hash256& header_hash, uint64_t nonce) noexcept; + +inline bool verify_final_hash(const hash256& header_hash, const hash256& mix_hash, uint64_t nonce, + const hash256& boundary) noexcept +{ + return ethash_verify_final_hash(&header_hash, &mix_hash, nonce, &boundary); +} + +inline bool verify(const epoch_context& context, const hash256& header_hash, const hash256& mix_hash, + uint64_t nonce, const hash256& boundary) noexcept +{ + return ethash_verify(&context, &header_hash, &mix_hash, nonce, &boundary); +} + +search_result search_light(const epoch_context& context, const hash256& header_hash, + const hash256& boundary, uint64_t start_nonce, size_t iterations) noexcept; + +search_result search(const epoch_context_full& context, const hash256& header_hash, + const hash256& boundary, uint64_t start_nonce, size_t iterations) noexcept; + + +/// Tries to find the epoch number matching the given seed hash. +/// +/// Mining pool protocols (many variants of stratum and "getwork") send out +/// seed hash instead of epoch number to workers. This function tries to recover +/// the epoch number from this seed hash. +/// +/// @param seed Ethash seed hash. +/// @return The epoch number or -1 if not found. +int find_epoch_number(const hash256& seed) noexcept; + + +/// Get global shared epoch context. +inline const epoch_context& get_global_epoch_context(int epoch_number) noexcept +{ + return *ethash_get_global_epoch_context(epoch_number); +} + +/// Get global shared epoch context with full dataset initialized. +inline const epoch_context_full& get_global_epoch_context_full(int epoch_number) noexcept +{ + return *ethash_get_global_epoch_context_full(epoch_number); +} +} // namespace ethash diff --git a/src/Native/libkawpow/ethash/hash_types.h b/src/Native/libkawpow/ethash/hash_types.h new file mode 100644 index 000000000..bd9343686 --- /dev/null +++ b/src/Native/libkawpow/ethash/hash_types.h @@ -0,0 +1,50 @@ +/* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. + * Copyright 2018-2019 Pawel Bylica. + * Licensed under the Apache License, Version 2.0. + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +union ethash_hash256 +{ + uint64_t word64s[4]; + uint32_t word32s[8]; + uint8_t bytes[32]; + char str[32]; +}; + +union ethash_hash512 +{ + uint64_t word64s[8]; + uint32_t word32s[16]; + uint8_t bytes[64]; + char str[64]; +}; + +union ethash_hash1024 +{ + union ethash_hash512 hash512s[2]; + uint64_t word64s[16]; + uint32_t word32s[32]; + uint8_t bytes[128]; + char str[128]; +}; + +union ethash_hash2048 +{ + union ethash_hash512 hash512s[4]; + uint64_t word64s[32]; + uint32_t word32s[64]; + uint8_t bytes[256]; + char str[256]; +}; + +#ifdef __cplusplus +} +#endif diff --git a/src/Native/libkawpow/ethash/hash_types.hpp b/src/Native/libkawpow/ethash/hash_types.hpp new file mode 100644 index 000000000..a31cff49f --- /dev/null +++ b/src/Native/libkawpow/ethash/hash_types.hpp @@ -0,0 +1,15 @@ +// ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. +// Copyright 2018-2019 Pawel Bylica. +// Licensed under the Apache License, Version 2.0. + +#pragma once + +#include "hash_types.h" + +namespace ethash +{ +using hash256 = ethash_hash256; +using hash512 = ethash_hash512; +using hash1024 = ethash_hash1024; +using hash2048 = ethash_hash2048; +} // namespace ethash diff --git a/src/Native/libkawpow/ethash/keccak.h b/src/Native/libkawpow/ethash/keccak.h new file mode 100644 index 000000000..79ef7c5cc --- /dev/null +++ b/src/Native/libkawpow/ethash/keccak.h @@ -0,0 +1,49 @@ +/* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. + * Copyright 2018-2019 Pawel Bylica. + * Licensed under the Apache License, Version 2.0. + */ + +#pragma once + +#include "hash_types.h" + +#include + +#ifdef __cplusplus +#define NOEXCEPT noexcept +#else +#define NOEXCEPT +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The Keccak-f[1600] function. + * + * The implementation of the Keccak-f function with 1600-bit width of the permutation (b). + * The size of the state is also 1600 bit what gives 25 64-bit words. + * + * @param state The state of 25 64-bit words on which the permutation is to be performed. + */ +void ethash_keccakf1600(uint64_t state[25]) NOEXCEPT; + +/** + * The Keccak-f[800] function. + * + * The implementation of the Keccak-f function with 800-bit width of the permutation (b). + * The size of the state is also 800 bit what gives 25 32-bit words. + * + * @param state The state of 25 32-bit words on which the permutation is to be performed. + */ +void ethash_keccakf800(uint32_t state[25]) NOEXCEPT; + +union ethash_hash256 ethash_keccak256(const uint8_t* data, size_t size) NOEXCEPT; +union ethash_hash256 ethash_keccak256_32(const uint8_t data[32]) NOEXCEPT; +union ethash_hash512 ethash_keccak512(const uint8_t* data, size_t size) NOEXCEPT; +union ethash_hash512 ethash_keccak512_64(const uint8_t data[64]) NOEXCEPT; + +#ifdef __cplusplus +} +#endif diff --git a/src/Native/libkawpow/ethash/keccak.hpp b/src/Native/libkawpow/ethash/keccak.hpp new file mode 100644 index 000000000..974067922 --- /dev/null +++ b/src/Native/libkawpow/ethash/keccak.hpp @@ -0,0 +1,35 @@ +// ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. +// Copyright 2018-2019 Pawel Bylica. +// Licensed under the Apache License, Version 2.0. + +#pragma once + +#include "keccak.h" +#include "hash_types.hpp" + +namespace ethash +{ +inline hash256 keccak256(const uint8_t* data, size_t size) noexcept +{ + return ethash_keccak256(data, size); +} + +inline hash256 keccak256(const hash256& input) noexcept +{ + return ethash_keccak256_32(input.bytes); +} + +inline hash512 keccak512(const uint8_t* data, size_t size) noexcept +{ + return ethash_keccak512(data, size); +} + +inline hash512 keccak512(const hash512& input) noexcept +{ + return ethash_keccak512_64(input.bytes); +} + +static constexpr auto keccak256_32 = ethash_keccak256_32; +static constexpr auto keccak512_64 = ethash_keccak512_64; + +} // namespace ethash diff --git a/src/Native/libkawpow/ethash/kiss99.hpp b/src/Native/libkawpow/ethash/kiss99.hpp new file mode 100644 index 000000000..8332a7ce9 --- /dev/null +++ b/src/Native/libkawpow/ethash/kiss99.hpp @@ -0,0 +1,64 @@ +/* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. + * Copyright 2018-2019 Pawel Bylica. + * Licensed under the Apache License, Version 2.0. + */ + +#pragma once + +#include "../support/attributes.h" +#include + +/** + * KISS PRNG by the spec from 1999. + * + * The implementation of KISS pseudo-random number generator + * by the specification published on 21 Jan 1999 in + * http://www.cse.yorku.ca/~oz/marsaglia-rng.html. + * The KISS is not versioned so here we are using `kiss99` prefix to indicate + * the version from 1999. + * + * The specification uses `unsigned long` type with the intention for 32-bit + * values. Because in GCC/clang for 64-bit architectures `unsigned long` is + * 64-bit size type, here the explicit `uint32_t` type is used. + * + * @defgroup kiss99 KISS99 + * @{ + */ + +/** + * The KISS generator. + */ +class kiss99 +{ + uint32_t z = 362436069; + uint32_t w = 521288629; + uint32_t jsr = 123456789; + uint32_t jcong = 380116160; + +public: + /** Creates KISS generator state with default values provided by the specification. */ + kiss99() noexcept = default; + + /** Creates KISS generator state with provided init values.*/ + kiss99(uint32_t _z, uint32_t _w, uint32_t _jsr, uint32_t _jcong) noexcept + : z{_z}, w{_w}, jsr{_jsr}, jcong{_jcong} + {} + + /** Generates next number from the KISS generator. */ + NO_SANITIZE("unsigned-integer-overflow") + uint32_t operator()() noexcept + { + z = 36969 * (z & 0xffff) + (z >> 16); + w = 18000 * (w & 0xffff) + (w >> 16); + + jcong = 69069 * jcong + 1234567; + + jsr ^= (jsr << 17); + jsr ^= (jsr >> 13); + jsr ^= (jsr << 5); + + return (((z << 16) + w) ^ jcong) + jsr; + } +}; + +/** @} */ diff --git a/src/Native/libkawpow/ethash/managed.cpp b/src/Native/libkawpow/ethash/managed.cpp new file mode 100644 index 000000000..47dd49b8e --- /dev/null +++ b/src/Native/libkawpow/ethash/managed.cpp @@ -0,0 +1,99 @@ +// ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. +// Copyright 2018-2019 Pawel Bylica. +// Licensed under the Apache License, Version 2.0. + +#include "ethash-internal.hpp" + +#include +#include + +#if !defined(__has_cpp_attribute) +#define __has_cpp_attribute(x) 0 +#endif + +#if __has_cpp_attribute(gnu::noinline) +#define ATTRIBUTE_NOINLINE [[gnu::noinline]] +#elif _MSC_VER +#define ATTRIBUTE_NOINLINE __declspec(noinline) +#else +#define ATTRIBUTE_NOINLINE +#endif + +using namespace ethash; + +namespace +{ +std::mutex shared_context_mutex; +std::shared_ptr shared_context; +thread_local std::shared_ptr thread_local_context; + +std::mutex shared_context_full_mutex; +std::shared_ptr shared_context_full; +thread_local std::shared_ptr thread_local_context_full; + +/// Update thread local epoch context. +/// +/// This function is on the slow path. It's separated to allow inlining the fast +/// path. +/// +/// @todo: Redesign to guarantee deallocation before new allocation. +ATTRIBUTE_NOINLINE +void update_local_context(int epoch_number) +{ + // Release the shared pointer of the obsoleted context. + thread_local_context.reset(); + + // Local context invalid, check the shared context. + std::lock_guard lock{shared_context_mutex}; + + if (!shared_context || shared_context->epoch_number != epoch_number) + { + // Release the shared pointer of the obsoleted context. + shared_context.reset(); + + // Build new context. + shared_context = create_epoch_context(epoch_number); + } + + thread_local_context = shared_context; +} + +ATTRIBUTE_NOINLINE +void update_local_context_full(int epoch_number) +{ + // Release the shared pointer of the obsoleted context. + thread_local_context_full.reset(); + + // Local context invalid, check the shared context. + std::lock_guard lock{shared_context_full_mutex}; + + if (!shared_context_full || shared_context_full->epoch_number != epoch_number) + { + // Release the shared pointer of the obsoleted context. + shared_context_full.reset(); + + // Build new context. + shared_context_full = create_epoch_context_full(epoch_number); + } + + thread_local_context_full = shared_context_full; +} +} // namespace + +const ethash_epoch_context* ethash_get_global_epoch_context(int epoch_number) noexcept +{ + // Check if local context matches epoch number. + if (!thread_local_context || thread_local_context->epoch_number != epoch_number) + update_local_context(epoch_number); + + return thread_local_context.get(); +} + +const ethash_epoch_context_full* ethash_get_global_epoch_context_full(int epoch_number) noexcept +{ + // Check if local context matches epoch number. + if (!thread_local_context_full || thread_local_context_full->epoch_number != epoch_number) + update_local_context_full(epoch_number); + + return thread_local_context_full.get(); +} diff --git a/src/Native/libkawpow/ethash/primes.c b/src/Native/libkawpow/ethash/primes.c new file mode 100644 index 000000000..e27a535e3 --- /dev/null +++ b/src/Native/libkawpow/ethash/primes.c @@ -0,0 +1,43 @@ +/* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. + * Copyright 2018-2019 Pawel Bylica. + * Licensed under the Apache License, Version 2.0. + */ + +#include "primes.h" + +/** Checks if the number is prime. Requires the number to be > 2 and odd. */ +static int is_odd_prime(int number) +{ + int d; + + /* Check factors up to sqrt(number). + To avoid computing sqrt, compare d*d <= number with 64-bit precision. */ + for (d = 3; (int64_t)d * (int64_t)d <= (int64_t)number; d += 2) + { + if (number % d == 0) + return 0; + } + + return 1; +} + +int ethash_find_largest_prime(int upper_bound) +{ + int n = upper_bound; + + if (n < 2) + return 0; + + if (n == 2) + return 2; + + /* If even number, skip it. */ + if (n % 2 == 0) + --n; + + /* Test descending odd numbers. */ + while (!is_odd_prime(n)) + n -= 2; + + return n; +} diff --git a/src/Native/libkawpow/ethash/primes.h b/src/Native/libkawpow/ethash/primes.h new file mode 100644 index 000000000..e37c532f2 --- /dev/null +++ b/src/Native/libkawpow/ethash/primes.h @@ -0,0 +1,25 @@ +/* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. + * Copyright 2018-2019 Pawel Bylica. + * Licensed under the Apache License, Version 2.0. + */ + +#pragma once + +#include "ethash.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Finds the largest prime number not greater than the provided upper bound. + * + * @param upper_bound The upper bound. SHOULD be greater than 1. + * @return The largest prime number `p` such `p <= upper_bound`. + * In case `upper_bound <= 1`, returns 0. + */ +int ethash_find_largest_prime(int upper_bound) NOEXCEPT; + +#ifdef __cplusplus +} +#endif diff --git a/src/Native/libkawpow/ethash/progpow.cpp b/src/Native/libkawpow/ethash/progpow.cpp new file mode 100644 index 000000000..70041bbe2 --- /dev/null +++ b/src/Native/libkawpow/ethash/progpow.cpp @@ -0,0 +1,658 @@ +// ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. +// Copyright 2018-2019 Pawel Bylica. +// Licensed under the Apache License, Version 2.0. + +#include "progpow.hpp" + +#include "bit_manipulation.h" +#include "endianness.hpp" +#include "ethash-internal.hpp" +#include "kiss99.hpp" +#include "keccak.hpp" + +#include + +namespace progpow +{ +namespace +{ +/// A variant of Keccak hash function for ProgPoW. +/// +/// This Keccak hash function uses 800-bit permutation (Keccak-f[800]) with 576 bitrate. +/// It take exactly 576 bits of input (split across 3 arguments) and adds no padding. +/// +/// @param header_hash The 256-bit header hash. +/// @param nonce The 64-bit nonce. +/// @param mix_hash Additional 256-bits of data. +/// @return The 256-bit output of the hash function. +void keccak_progpow_256(uint32_t* st) noexcept +{ + ethash_keccakf800(st); +} + +/// The same as keccak_progpow_256() but uses null mix +/// and returns top 64 bits of the output being a big-endian prefix of the 256-bit hash. + inline void keccak_progpow_64(uint32_t* st) noexcept +{ + keccak_progpow_256(st); +} + + +/// ProgPoW mix RNG state. +/// +/// Encapsulates the state of the random number generator used in computing ProgPoW mix. +/// This includes the state of the KISS99 RNG and the precomputed random permutation of the +/// sequence of mix item indexes. +class mix_rng_state +{ +public: + inline explicit mix_rng_state(uint32_t* seed) noexcept; + + uint32_t next_dst() noexcept { return dst_seq[(dst_counter++) % num_regs]; } + uint32_t next_src() noexcept { return src_seq[(src_counter++) % num_regs]; } + + kiss99 rng; + +private: + size_t dst_counter = 0; + std::array dst_seq; + size_t src_counter = 0; + std::array src_seq; +}; + +mix_rng_state::mix_rng_state(uint32_t* hash_seed) noexcept +{ + const auto seed_lo = static_cast(hash_seed[0]); + const auto seed_hi = static_cast(hash_seed[1]); + + const auto z = fnv1a(fnv_offset_basis, seed_lo); + const auto w = fnv1a(z, seed_hi); + const auto jsr = fnv1a(w, seed_lo); + const auto jcong = fnv1a(jsr, seed_hi); + + rng = kiss99{z, w, jsr, jcong}; + + // Create random permutations of mix destinations / sources. + // Uses Fisher-Yates shuffle. + for (uint32_t i = 0; i < num_regs; ++i) + { + dst_seq[i] = i; + src_seq[i] = i; + } + + for (uint32_t i = num_regs; i > 1; --i) + { + std::swap(dst_seq[i - 1], dst_seq[rng() % i]); + std::swap(src_seq[i - 1], src_seq[rng() % i]); + } +} + + +NO_SANITIZE("unsigned-integer-overflow") +inline uint32_t random_math(uint32_t a, uint32_t b, uint32_t selector) noexcept +{ + switch (selector % 11) + { + default: + case 0: + return a + b; + case 1: + return a * b; + case 2: + return mul_hi32(a, b); + case 3: + return std::min(a, b); + case 4: + return rotl32(a, b); + case 5: + return rotr32(a, b); + case 6: + return a & b; + case 7: + return a | b; + case 8: + return a ^ b; + case 9: + return clz32(a) + clz32(b); + case 10: + return popcount32(a) + popcount32(b); + } +} + +/// Merge data from `b` and `a`. +/// Assuming `a` has high entropy, only do ops that retain entropy even if `b` +/// has low entropy (i.e. do not do `a & b`). +NO_SANITIZE("unsigned-integer-overflow") +inline void random_merge(uint32_t& a, uint32_t b, uint32_t selector) noexcept +{ + const auto x = (selector >> 16) % 31 + 1; // Additional non-zero selector from higher bits. + switch (selector % 4) + { + case 0: + a = (a * 33) + b; + break; + case 1: + a = (a ^ b) * 33; + break; + case 2: + a = rotl32(a, x) ^ b; + break; + case 3: + a = rotr32(a, x) ^ b; + break; + } +} + +static const uint32_t ravencoin_kawpow[15] = { + 0x00000072, //R + 0x00000041, //A + 0x00000056, //V + 0x00000045, //E + 0x0000004E, //N + 0x00000043, //C + 0x0000004F, //O + 0x00000049, //I + 0x0000004E, //N + 0x0000004B, //K + 0x00000041, //A + 0x00000057, //W + 0x00000050, //P + 0x0000004F, //O + 0x00000057, //W +}; + +using lookup_fn = hash2048 (*)(const epoch_context&, uint32_t); + +using mix_array = std::array, num_lanes>; + +void round( + const epoch_context& context, uint32_t r, mix_array& mix, mix_rng_state state, lookup_fn lookup) +{ + const uint32_t num_items = static_cast(context.full_dataset_num_items / 2); + const uint32_t item_index = mix[r % num_lanes][0] % num_items; + const hash2048 item = lookup(context, item_index); + + constexpr size_t num_words_per_lane = sizeof(item) / (sizeof(uint32_t) * num_lanes); + constexpr int max_operations = + num_cache_accesses > num_math_operations ? num_cache_accesses : num_math_operations; + + // Process lanes. + for (int i = 0; i < max_operations; ++i) + { + if (i < num_cache_accesses) // Random access to cached memory. + { + const auto src = state.next_src(); + const auto dst = state.next_dst(); + const auto sel = state.rng(); + + for (size_t l = 0; l < num_lanes; ++l) + { + const size_t offset = mix[l][src] % l1_cache_num_items; + random_merge(mix[l][dst], le::uint32(context.l1_cache[offset]), sel); + } + } + if (i < num_math_operations) // Random math. + { + // Generate 2 unique source indexes. + const auto src_rnd = state.rng() % (num_regs * (num_regs - 1)); + const auto src1 = src_rnd % num_regs; // O <= src1 < num_regs + auto src2 = src_rnd / num_regs; // 0 <= src2 < num_regs - 1 + if (src2 >= src1) + ++src2; + + const auto sel1 = state.rng(); + const auto dst = state.next_dst(); + const auto sel2 = state.rng(); + + for (size_t l = 0; l < num_lanes; ++l) + { + const uint32_t data = random_math(mix[l][src1], mix[l][src2], sel1); + random_merge(mix[l][dst], data, sel2); + } + } + } + + // DAG access pattern. + uint32_t dsts[num_words_per_lane]; + uint32_t sels[num_words_per_lane]; + for (size_t i = 0; i < num_words_per_lane; ++i) + { + dsts[i] = i == 0 ? 0 : state.next_dst(); + sels[i] = state.rng(); + } + + // DAG access. + for (size_t l = 0; l < num_lanes; ++l) + { + const auto offset = ((l ^ r) % num_lanes) * num_words_per_lane; + for (size_t i = 0; i < num_words_per_lane; ++i) + { + const auto word = le::uint32(item.word32s[offset + i]); + random_merge(mix[l][dsts[i]], word, sels[i]); + } + } +} + +mix_array init_mix(uint32_t* hash_seed) +{ + const uint32_t z = fnv1a(fnv_offset_basis, static_cast(hash_seed[0])); + const uint32_t w = fnv1a(z, static_cast(hash_seed[1])); + + mix_array mix; + for (uint32_t l = 0; l < mix.size(); ++l) + { + const uint32_t jsr = fnv1a(w, l); + const uint32_t jcong = fnv1a(jsr, l); + kiss99 rng{z, w, jsr, jcong}; + + for (auto& row : mix[l]) + row = rng(); + } + return mix; +} + +hash256 hash_mix( + const epoch_context& context, int block_number, uint32_t * seed, lookup_fn lookup) noexcept +{ + auto mix = init_mix(seed); + auto number = uint64_t(block_number / period_length); + uint32_t new_state[2]; + new_state[0] = (uint32_t)number; + new_state[1] = (uint32_t)(number >> 32); + mix_rng_state state{new_state}; + + for (uint32_t i = 0; i < 64; ++i) + round(context, i, mix, state, lookup); + + // Reduce mix data to a single per-lane result. + uint32_t lane_hash[num_lanes]; + for (size_t l = 0; l < num_lanes; ++l) + { + lane_hash[l] = fnv_offset_basis; + for (uint32_t i = 0; i < num_regs; ++i) + lane_hash[l] = fnv1a(lane_hash[l], mix[l][i]); + } + + // Reduce all lanes to a single 256-bit result. + static constexpr size_t num_words = sizeof(hash256) / sizeof(uint32_t); + hash256 mix_hash; + for (uint32_t& w : mix_hash.word32s) + w = fnv_offset_basis; + for (size_t l = 0; l < num_lanes; ++l) + mix_hash.word32s[l % num_words] = fnv1a(mix_hash.word32s[l % num_words], lane_hash[l]); + return le::uint32s(mix_hash); +} +} // namespace + +result hashext(const epoch_context& context, int block_number, const hash256& header_hash, + uint64_t nonce, const hash256& mix_hash, const hash256& boundary1, const hash256& boundary2, int* retcode) noexcept +{ + uint32_t hash_seed[2]; // KISS99 initiator + + uint32_t state2[8]; + + { + uint32_t state[25] = {0x0}; // Keccak's state + + // Absorb phase for initial round of keccak + // 1st fill with header data (8 words) + for (int i = 0; i < 8; i++) + state[i] = header_hash.word32s[i]; + + // 2nd fill with nonce (2 words) + state[8] = (uint32_t)nonce; + state[9] = (uint32_t)(nonce >> 32); + + // 3rd apply ravencoin input constraints + for (int i = 10; i < 25; i++) + state[i] = ravencoin_kawpow[i-10]; + + keccak_progpow_64(state); + + for (int i = 0; i < 8; i++) + state2[i] = state[i]; + } + + hash_seed[0] = state2[0]; + hash_seed[1] = state2[1]; + //mix hash was here + + uint32_t state[25] = {0x0}; // Keccak's state + + // Absorb phase for last round of keccak (256 bits) + // 1st initial 8 words of state are kept as carry-over from initial keccak + for (int i = 0; i < 8; i++) + state[i] = state2[i]; + + // 2nd subsequent 8 words are carried from digest/mix + for (int i = 8; i < 16; i++) + state[i] = mix_hash.word32s[i-8]; + + // 3rd apply ravencoin input constraints + for (int i = 16; i < 25; i++) + state[i] = ravencoin_kawpow[i - 16]; + + // Run keccak loop + keccak_progpow_256(state); + + /* mod start */ + hash256 output; + for (int i = 0; i < 8; ++i) + output.word32s[i] = le::uint32(state[i]); + + + if (!is_less_or_equal(output, boundary1)) { + //if(boundary1 == boundary2) { + if(is_equal(boundary1, boundary2)) { + *retcode = 1; + return {output, mix_hash}; + } + else { + if (!is_less_or_equal(output, boundary2)) { + *retcode = 1; + return {output, mix_hash}; + } + } + } + + const hash256 computed_mix_hash = hash_mix(context, block_number, hash_seed, calculate_dataset_item_2048); + if(!is_equal(computed_mix_hash, mix_hash)) { + *retcode = 2; + return {output, mix_hash}; + } + /* mod end */ + *retcode = 0; + return {output, computed_mix_hash}; +} + +result hash(const epoch_context& context, int block_number, const hash256& header_hash, + uint64_t nonce) noexcept +{ + uint32_t hash_seed[2]; // KISS99 initiator + + uint32_t state2[8]; + + { + uint32_t state[25] = {0x0}; // Keccak's state + + // Absorb phase for initial round of keccak + // 1st fill with header data (8 words) + for (int i = 0; i < 8; i++) + state[i] = header_hash.word32s[i]; + + // 2nd fill with nonce (2 words) + state[8] = (uint32_t)nonce; + state[9] = (uint32_t)(nonce >> 32); + + // 3rd apply ravencoin input constraints + for (int i = 10; i < 25; i++) + state[i] = ravencoin_kawpow[i-10]; + + keccak_progpow_64(state); + + for (int i = 0; i < 8; i++) + state2[i] = state[i]; + } + + hash_seed[0] = state2[0]; + hash_seed[1] = state2[1]; + const hash256 mix_hash = hash_mix(context, block_number, hash_seed, calculate_dataset_item_2048); + + uint32_t state[25] = {0x0}; // Keccak's state + + // Absorb phase for last round of keccak (256 bits) + // 1st initial 8 words of state are kept as carry-over from initial keccak + for (int i = 0; i < 8; i++) + state[i] = state2[i]; + + // 2nd subsequent 8 words are carried from digest/mix + for (int i = 8; i < 16; i++) + state[i] = mix_hash.word32s[i-8]; + + // 3rd apply ravencoin input constraints + for (int i = 16; i < 25; i++) + state[i] = ravencoin_kawpow[i - 16]; + + // Run keccak loop + keccak_progpow_256(state); + + hash256 output; + for (int i = 0; i < 8; ++i) + output.word32s[i] = le::uint32(state[i]); + + return {output, mix_hash}; +} + +result hash(const epoch_context_full& context, int block_number, const hash256& header_hash, + uint64_t nonce) noexcept +{ + static const auto lazy_lookup = [](const epoch_context& ctx, uint32_t index) noexcept + { + auto* full_dataset_1024 = static_cast(ctx).full_dataset; + auto* full_dataset_2048 = reinterpret_cast(full_dataset_1024); + hash2048& item = full_dataset_2048[index]; + if (item.word64s[0] == 0) + { + // TODO: Copy elision here makes it thread-safe? + item = calculate_dataset_item_2048(ctx, index); + } + + return item; + }; + + uint32_t hash_seed[2]; // KISS99 initiator + + uint32_t state2[8]; + + { + uint32_t state[25] = {0x0}; // Keccak's state + + // Absorb phase for initial round of keccak + // 1st fill with header data (8 words) + for (int i = 0; i < 8; i++) + state[i] = header_hash.word32s[i]; + + // 2nd fill with nonce (2 words) + state[8] = (uint32_t)nonce; + state[9] = (uint32_t)(nonce >> 32); + + // 3rd apply ravencoin input constraints + for (int i = 10; i < 25; i++) + state[i] = ravencoin_kawpow[i-10]; + + keccak_progpow_64(state); + + for (int i = 0; i < 8; i++) + state2[i] = state[i]; + } + + hash_seed[0] = state2[0]; + hash_seed[1] = state2[1]; + + const hash256 mix_hash = hash_mix(context, block_number, hash_seed, lazy_lookup); + + uint32_t state[25] = {0x0}; // Keccak's state + + + // Absorb phase for last round of keccak (256 bits) + // 1st initial 8 words of state are kept as carry-over from initial keccak + for (int i = 0; i < 8; i++) + state[i] = state2[i]; + + // 2nd subsequent 8 words are carried from digest/mix + for (int i = 8; i < 16; i++) + state[i] = mix_hash.word32s[i-8]; + + // 3rd apply ravencoin input constraints + for (int i = 16; i < 25; i++) + state[i] = ravencoin_kawpow[i - 16]; + + + + // Run keccak loop + keccak_progpow_256(state); + + + hash256 output; + for (int i = 0; i < 8; ++i) + output.word32s[i] = le::uint32(state[i]); + + + + return {output, mix_hash}; +} + +bool verify(const epoch_context& context, int block_number, const hash256& header_hash, + const hash256& mix_hash, uint64_t nonce, const hash256& boundary) noexcept +{ + uint32_t hash_seed[2]; // KISS99 initiator + uint32_t state2[8]; + + { + // Absorb phase for initial round of keccak + // 1st fill with header data (8 words) + uint32_t state[25] = {0x0}; // Keccak's state + for (int i = 0; i < 8; i++) + state[i] = header_hash.word32s[i]; + + // 2nd fill with nonce (2 words) + state[8] = (uint32_t)nonce; + state[9] = (uint32_t)(nonce >> 32); + + // 3rd apply ravencoin input constraints + for (int i = 10; i < 25; i++) + state[i] = ravencoin_kawpow[i-10]; + + keccak_progpow_64(state); + + for (int i = 0; i < 8; i++) + state2[i] = state[i]; + } + + hash_seed[0] = state2[0]; + hash_seed[1] = state2[1]; + + uint32_t state[25] = {0x0}; // Keccak's state + + // Absorb phase for last round of keccak (256 bits) + // 1st initial 8 words of state are kept as carry-over from initial keccak + for (int i = 0; i < 8; i++) + state[i] = state2[i]; + + + // 2nd subsequent 8 words are carried from digest/mix + for (int i = 8; i < 16; i++) + state[i] = mix_hash.word32s[i-8]; + + // 3rd apply ravencoin input constraints + for (int i = 16; i < 25; i++) + state[i] = ravencoin_kawpow[i - 16]; + + // Run keccak loop + keccak_progpow_256(state); + + hash256 output; + for (int i = 0; i < 8; ++i) + output.word32s[i] = le::uint32(state[i]); + + + if (!is_less_or_equal(output, boundary)) { + return false; + } + + const hash256 expected_mix_hash = + hash_mix(context, block_number, hash_seed, calculate_dataset_item_2048); + + return is_equal(expected_mix_hash, mix_hash); +} + +//bool light_verify(const char* str_header_hash, +// const char* str_mix_hash, const char* str_nonce, const char* str_boundary, char* str_final) noexcept +//{ +// +// hash256 header_hash = to_hash256(str_header_hash); +// hash256 mix_hash = to_hash256(str_mix_hash); +// hash256 boundary = to_hash256(str_boundary); +// +// uint64_t nonce = std::stoull(str_nonce, nullptr, 16); +// +// uint32_t state2[8]; +// +// { +// // Absorb phase for initial round of keccak +// // 1st fill with header data (8 words) +// uint32_t state[25]; // Keccak's state +// for (int i = 0; i < 8; i++) +// state[i] = header_hash.word32s[i]; +// // 2nd fill with nonce (2 words) +// state[8] = (uint32_t)nonce; +// state[9] = (uint32_t)(nonce >> 32); +// // 3rd all remaining elements to zero +// for (int i = 10; i < 25; i++) +// state[i] = 0; +// +// keccak_progpow_64(state); +// +// for (int i = 0; i < 8; i++) +// state2[i] = state[i]; +// } +// +// uint32_t state[25]; // Keccak's state +// for (int i = 0; i < 8; i++) +// state[i] = state2[i]; +// +// // Absorb phase for last round of keccak (256 bits) +// // 1st initial 8 words of state are kept as carry-over from initial keccak +// // 2nd subsequent 8 words are carried from digest/mix +// for (int i = 8; i < 16; i++) +// state[i] = mix_hash.word32s[i-8]; +// +// // 3rd all other elements to zero +// for (int i = 16; i < 25; i++) +// state[i] = 0; +// +// // Run keccak loop +// keccak_progpow_256(state); +// +// hash256 output; +// for (int i = 0; i < 8; ++i) +// output.word32s[i] = le::uint32(state[i]); +// if (!is_less_or_equal(output, boundary)) +// return false; +// +// if (!is_less_or_equal(output, boundary)) +// return false; +// +// memcpy(str_final,&to_hex(output)[0],64); +// return true; +//} + +search_result search_light(const epoch_context& context, int block_number, + const hash256& header_hash, const hash256& boundary, uint64_t start_nonce, + size_t iterations) noexcept +{ + const uint64_t end_nonce = start_nonce + iterations; + for (uint64_t nonce = start_nonce; nonce < end_nonce; ++nonce) + { + result r = hash(context, block_number, header_hash, nonce); + if (is_less_or_equal(r.final_hash, boundary)) + return {r, nonce}; + } + return {}; +} + +search_result search(const epoch_context_full& context, int block_number, + const hash256& header_hash, const hash256& boundary, uint64_t start_nonce, + size_t iterations) noexcept +{ + const uint64_t end_nonce = start_nonce + iterations; + for (uint64_t nonce = start_nonce; nonce < end_nonce; ++nonce) + { + result r = hash(context, block_number, header_hash, nonce); + if (is_less_or_equal(r.final_hash, boundary)) + return {r, nonce}; + } + return {}; +} + +} // namespace progpow diff --git a/src/Native/libkawpow/ethash/progpow.hpp b/src/Native/libkawpow/ethash/progpow.hpp new file mode 100644 index 000000000..f1de8222e --- /dev/null +++ b/src/Native/libkawpow/ethash/progpow.hpp @@ -0,0 +1,68 @@ +// ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. +// Copyright 2018-2019 Pawel Bylica. +// Licensed under the Apache License, Version 2.0. + +/// @file +/// +/// ProgPoW API +/// +/// This file provides the public API for ProgPoW as the Ethash API extension. + +#include "ethash.hpp" + +#if defined(_MSC_VER) +// Microsoft +#define EXPORT __declspec(dllexport) +#define IMPORT __declspec(dllimport) +#elif defined(__GNUC__) +// GCC +#define EXPORT __attribute__((visibility("default"))) +#define IMPORT +#else +// do nothing and hope for the best? +#define EXPORT +#define IMPORT +#pragma warning Unknown dynamic link import / export semantics. +#endif + +namespace progpow +{ +using namespace ethash; // Include ethash namespace. + + +/// The ProgPoW algorithm revision implemented as specified in the spec +/// https://github.com/ifdefelse/ProgPOW#change-history. +constexpr auto revision = "0.9.4"; + +constexpr int period_length = 3; +constexpr uint32_t num_regs = 32; +constexpr size_t num_lanes = 16; +constexpr int num_cache_accesses = 11; +constexpr int num_math_operations = 18; +constexpr size_t l1_cache_size = 16 * 1024; +constexpr size_t l1_cache_num_items = l1_cache_size / sizeof(uint32_t); + +extern "C" EXPORT result hashext(const epoch_context& context, int block_number, const hash256& header_hash, + uint64_t nonce, const hash256& mix_hash, const hash256& boundary1, const hash256& boundary2, int* retcode) noexcept; + +result hash(const epoch_context& context, int block_number, const hash256& header_hash, + uint64_t nonce) noexcept; + +result hash(const epoch_context_full& context, int block_number, const hash256& header_hash, + uint64_t nonce) noexcept; + +extern "C" EXPORT bool verify(const epoch_context& context, int block_number, const hash256& header_hash, + const hash256& mix_hash, uint64_t nonce, const hash256& boundary) noexcept; + +//bool light_verify(const char* str_header_hash, +// const char* str_mix_hash, const char* str_nonce, const char* str_boundary, char* str_final) noexcept; + +search_result search_light(const epoch_context& context, int block_number, + const hash256& header_hash, const hash256& boundary, uint64_t start_nonce, + size_t iterations) noexcept; + +search_result search(const epoch_context_full& context, int block_number, + const hash256& header_hash, const hash256& boundary, uint64_t start_nonce, + size_t iterations) noexcept; + +} // namespace progpow diff --git a/src/Native/libkawpow/ethash/version.h b/src/Native/libkawpow/ethash/version.h new file mode 100644 index 000000000..f08900fc0 --- /dev/null +++ b/src/Native/libkawpow/ethash/version.h @@ -0,0 +1,18 @@ +/* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. + * Copyright 2019 Pawel Bylica. + * Licensed under the Apache License, Version 2.0. + */ + +#pragma once + +/** The ethash library version. */ +#define ETHASH_VERSION "0.5.1-alpha.1" + +#ifdef __cplusplus +namespace ethash +{ +/// The ethash library version. +constexpr auto version = ETHASH_VERSION; + +} // namespace ethash +#endif diff --git a/src/Native/libkawpow/exports.cpp b/src/Native/libkawpow/exports.cpp new file mode 100644 index 000000000..2004b4f48 --- /dev/null +++ b/src/Native/libkawpow/exports.cpp @@ -0,0 +1,16 @@ +/* +Copyright 2017 Coin Foundry (coinfoundry.org) +Authors: Oliver Weichhold (oliver@weichhold.com) +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ diff --git a/src/Native/libkawpow/keccak/CMakeLists.txt b/src/Native/libkawpow/keccak/CMakeLists.txt new file mode 100644 index 000000000..60f960f35 --- /dev/null +++ b/src/Native/libkawpow/keccak/CMakeLists.txt @@ -0,0 +1,26 @@ +# ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. +# Copyright 2019 Pawel Bylica. +# Licensed under the Apache License, Version 2.0. + +include(CMakePackageConfigHelpers) +include(GNUInstallDirs) + +add_library( + keccak + ${include_dir}/ethash/keccak.h + ${include_dir}/ethash/keccak.hpp + keccak.c + keccakf800.c + keccakf1600.c +) +set_property(TARGET keccak PROPERTY POSITION_INDEPENDENT_CODE ON) + +target_include_directories(keccak PUBLIC $$) + +install( + TARGETS keccak + EXPORT ethashTargets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) diff --git a/src/Native/libkawpow/keccak/keccak.c b/src/Native/libkawpow/keccak/keccak.c new file mode 100644 index 000000000..3dfafdf2f --- /dev/null +++ b/src/Native/libkawpow/keccak/keccak.c @@ -0,0 +1,123 @@ +/* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. + * Copyright 2018-2019 Pawel Bylica. + * Licensed under the Apache License, Version 2.0. + */ + +#include "../ethash/keccak.h" + +#include "../support/attributes.h" +#include + +#if _WIN32 +/* On Windows assume little endian. */ +#define __LITTLE_ENDIAN 1234 +#define __BIG_ENDIAN 4321 +#define __BYTE_ORDER __LITTLE_ENDIAN +#elif __APPLE__ +#include +#else +#include +#endif + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define to_le64(X) X +#else +#define to_le64(X) __builtin_bswap64(X) +#endif + + +/** Loads 64-bit integer from given memory location as little-endian number. */ +static INLINE ALWAYS_INLINE uint64_t load_le(const uint8_t* data) +{ + /* memcpy is the best way of expressing the intention. Every compiler will + optimize is to single load instruction if the target architecture + supports unaligned memory access (GCC and clang even in O0). + This is great trick because we are violating C/C++ memory alignment + restrictions with no performance penalty. */ + uint64_t word; + memcpy(&word, data, sizeof(word)); + return to_le64(word); +} + +static INLINE ALWAYS_INLINE void keccak( + uint64_t* out, size_t bits, const uint8_t* data, size_t size) +{ + static const size_t word_size = sizeof(uint64_t); + const size_t hash_size = bits / 8; + const size_t block_size = (1600 - bits * 2) / 8; + + size_t i; + uint64_t* state_iter; + uint64_t last_word = 0; + uint8_t* last_word_iter = (uint8_t*)&last_word; + + uint64_t state[25] = {0}; + + while (size >= block_size) + { + for (i = 0; i < (block_size / word_size); ++i) + { + state[i] ^= load_le(data); + data += word_size; + } + + ethash_keccakf1600(state); + + size -= block_size; + } + + state_iter = state; + + while (size >= word_size) + { + *state_iter ^= load_le(data); + ++state_iter; + data += word_size; + size -= word_size; + } + + while (size > 0) + { + *last_word_iter = *data; + ++last_word_iter; + ++data; + --size; + } + *last_word_iter = 0x01; + *state_iter ^= to_le64(last_word); + + state[(block_size / word_size) - 1] ^= 0x8000000000000000; + + ethash_keccakf1600(state); + + for (i = 0; i < (hash_size / word_size); ++i) + out[i] = to_le64(state[i]); +} + +union ethash_hash256 ethash_keccak256(const uint8_t* data, size_t size) +{ + union ethash_hash256 hash; + keccak(hash.word64s, 256, data, size); + return hash; +} + +union ethash_hash256 ethash_keccak256_32(const uint8_t data[32]) +{ + union ethash_hash256 hash; + keccak(hash.word64s, 256, data, 32); + return hash; +} + +union ethash_hash512 ethash_keccak512(const uint8_t* data, size_t size) +{ + union ethash_hash512 hash; + keccak(hash.word64s, 512, data, size); + return hash; +} + +union ethash_hash512 ethash_keccak512_64(const uint8_t data[64]) +{ + union ethash_hash512 hash; + keccak(hash.word64s, 512, data, 64); + return hash; +} diff --git a/src/Native/libkawpow/keccak/keccakf1600.c b/src/Native/libkawpow/keccak/keccakf1600.c new file mode 100644 index 000000000..e12b268d1 --- /dev/null +++ b/src/Native/libkawpow/keccak/keccakf1600.c @@ -0,0 +1,255 @@ +/* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. + * Copyright 2018-2019 Pawel Bylica. + * Licensed under the Apache License, Version 2.0. + */ + +#include + +static uint64_t rol(uint64_t x, unsigned s) +{ + return (x << s) | (x >> (64 - s)); +} + +static const uint64_t round_constants[24] = { + 0x0000000000000001, + 0x0000000000008082, + 0x800000000000808a, + 0x8000000080008000, + 0x000000000000808b, + 0x0000000080000001, + 0x8000000080008081, + 0x8000000000008009, + 0x000000000000008a, + 0x0000000000000088, + 0x0000000080008009, + 0x000000008000000a, + 0x000000008000808b, + 0x800000000000008b, + 0x8000000000008089, + 0x8000000000008003, + 0x8000000000008002, + 0x8000000000000080, + 0x000000000000800a, + 0x800000008000000a, + 0x8000000080008081, + 0x8000000000008080, + 0x0000000080000001, + 0x8000000080008008, +}; + +void ethash_keccakf1600(uint64_t state[25]) +{ + /* The implementation based on the "simple" implementation by Ronny Van Keer. */ + + int round; + + uint64_t Aba, Abe, Abi, Abo, Abu; + uint64_t Aga, Age, Agi, Ago, Agu; + uint64_t Aka, Ake, Aki, Ako, Aku; + uint64_t Ama, Ame, Ami, Amo, Amu; + uint64_t Asa, Ase, Asi, Aso, Asu; + + uint64_t Eba, Ebe, Ebi, Ebo, Ebu; + uint64_t Ega, Ege, Egi, Ego, Egu; + uint64_t Eka, Eke, Eki, Eko, Eku; + uint64_t Ema, Eme, Emi, Emo, Emu; + uint64_t Esa, Ese, Esi, Eso, Esu; + + uint64_t Ba, Be, Bi, Bo, Bu; + + uint64_t Da, De, Di, Do, Du; + + Aba = state[0]; + Abe = state[1]; + Abi = state[2]; + Abo = state[3]; + Abu = state[4]; + Aga = state[5]; + Age = state[6]; + Agi = state[7]; + Ago = state[8]; + Agu = state[9]; + Aka = state[10]; + Ake = state[11]; + Aki = state[12]; + Ako = state[13]; + Aku = state[14]; + Ama = state[15]; + Ame = state[16]; + Ami = state[17]; + Amo = state[18]; + Amu = state[19]; + Asa = state[20]; + Ase = state[21]; + Asi = state[22]; + Aso = state[23]; + Asu = state[24]; + + for (round = 0; round < 24; round += 2) + { + /* Round (round + 0): Axx -> Exx */ + + Ba = Aba ^ Aga ^ Aka ^ Ama ^ Asa; + Be = Abe ^ Age ^ Ake ^ Ame ^ Ase; + Bi = Abi ^ Agi ^ Aki ^ Ami ^ Asi; + Bo = Abo ^ Ago ^ Ako ^ Amo ^ Aso; + Bu = Abu ^ Agu ^ Aku ^ Amu ^ Asu; + + Da = Bu ^ rol(Be, 1); + De = Ba ^ rol(Bi, 1); + Di = Be ^ rol(Bo, 1); + Do = Bi ^ rol(Bu, 1); + Du = Bo ^ rol(Ba, 1); + + Ba = Aba ^ Da; + Be = rol(Age ^ De, 44); + Bi = rol(Aki ^ Di, 43); + Bo = rol(Amo ^ Do, 21); + Bu = rol(Asu ^ Du, 14); + Eba = Ba ^ (~Be & Bi) ^ round_constants[round]; + Ebe = Be ^ (~Bi & Bo); + Ebi = Bi ^ (~Bo & Bu); + Ebo = Bo ^ (~Bu & Ba); + Ebu = Bu ^ (~Ba & Be); + + Ba = rol(Abo ^ Do, 28); + Be = rol(Agu ^ Du, 20); + Bi = rol(Aka ^ Da, 3); + Bo = rol(Ame ^ De, 45); + Bu = rol(Asi ^ Di, 61); + Ega = Ba ^ (~Be & Bi); + Ege = Be ^ (~Bi & Bo); + Egi = Bi ^ (~Bo & Bu); + Ego = Bo ^ (~Bu & Ba); + Egu = Bu ^ (~Ba & Be); + + Ba = rol(Abe ^ De, 1); + Be = rol(Agi ^ Di, 6); + Bi = rol(Ako ^ Do, 25); + Bo = rol(Amu ^ Du, 8); + Bu = rol(Asa ^ Da, 18); + Eka = Ba ^ (~Be & Bi); + Eke = Be ^ (~Bi & Bo); + Eki = Bi ^ (~Bo & Bu); + Eko = Bo ^ (~Bu & Ba); + Eku = Bu ^ (~Ba & Be); + + Ba = rol(Abu ^ Du, 27); + Be = rol(Aga ^ Da, 36); + Bi = rol(Ake ^ De, 10); + Bo = rol(Ami ^ Di, 15); + Bu = rol(Aso ^ Do, 56); + Ema = Ba ^ (~Be & Bi); + Eme = Be ^ (~Bi & Bo); + Emi = Bi ^ (~Bo & Bu); + Emo = Bo ^ (~Bu & Ba); + Emu = Bu ^ (~Ba & Be); + + Ba = rol(Abi ^ Di, 62); + Be = rol(Ago ^ Do, 55); + Bi = rol(Aku ^ Du, 39); + Bo = rol(Ama ^ Da, 41); + Bu = rol(Ase ^ De, 2); + Esa = Ba ^ (~Be & Bi); + Ese = Be ^ (~Bi & Bo); + Esi = Bi ^ (~Bo & Bu); + Eso = Bo ^ (~Bu & Ba); + Esu = Bu ^ (~Ba & Be); + + + /* Round (round + 1): Exx -> Axx */ + + Ba = Eba ^ Ega ^ Eka ^ Ema ^ Esa; + Be = Ebe ^ Ege ^ Eke ^ Eme ^ Ese; + Bi = Ebi ^ Egi ^ Eki ^ Emi ^ Esi; + Bo = Ebo ^ Ego ^ Eko ^ Emo ^ Eso; + Bu = Ebu ^ Egu ^ Eku ^ Emu ^ Esu; + + Da = Bu ^ rol(Be, 1); + De = Ba ^ rol(Bi, 1); + Di = Be ^ rol(Bo, 1); + Do = Bi ^ rol(Bu, 1); + Du = Bo ^ rol(Ba, 1); + + Ba = Eba ^ Da; + Be = rol(Ege ^ De, 44); + Bi = rol(Eki ^ Di, 43); + Bo = rol(Emo ^ Do, 21); + Bu = rol(Esu ^ Du, 14); + Aba = Ba ^ (~Be & Bi) ^ round_constants[round + 1]; + Abe = Be ^ (~Bi & Bo); + Abi = Bi ^ (~Bo & Bu); + Abo = Bo ^ (~Bu & Ba); + Abu = Bu ^ (~Ba & Be); + + Ba = rol(Ebo ^ Do, 28); + Be = rol(Egu ^ Du, 20); + Bi = rol(Eka ^ Da, 3); + Bo = rol(Eme ^ De, 45); + Bu = rol(Esi ^ Di, 61); + Aga = Ba ^ (~Be & Bi); + Age = Be ^ (~Bi & Bo); + Agi = Bi ^ (~Bo & Bu); + Ago = Bo ^ (~Bu & Ba); + Agu = Bu ^ (~Ba & Be); + + Ba = rol(Ebe ^ De, 1); + Be = rol(Egi ^ Di, 6); + Bi = rol(Eko ^ Do, 25); + Bo = rol(Emu ^ Du, 8); + Bu = rol(Esa ^ Da, 18); + Aka = Ba ^ (~Be & Bi); + Ake = Be ^ (~Bi & Bo); + Aki = Bi ^ (~Bo & Bu); + Ako = Bo ^ (~Bu & Ba); + Aku = Bu ^ (~Ba & Be); + + Ba = rol(Ebu ^ Du, 27); + Be = rol(Ega ^ Da, 36); + Bi = rol(Eke ^ De, 10); + Bo = rol(Emi ^ Di, 15); + Bu = rol(Eso ^ Do, 56); + Ama = Ba ^ (~Be & Bi); + Ame = Be ^ (~Bi & Bo); + Ami = Bi ^ (~Bo & Bu); + Amo = Bo ^ (~Bu & Ba); + Amu = Bu ^ (~Ba & Be); + + Ba = rol(Ebi ^ Di, 62); + Be = rol(Ego ^ Do, 55); + Bi = rol(Eku ^ Du, 39); + Bo = rol(Ema ^ Da, 41); + Bu = rol(Ese ^ De, 2); + Asa = Ba ^ (~Be & Bi); + Ase = Be ^ (~Bi & Bo); + Asi = Bi ^ (~Bo & Bu); + Aso = Bo ^ (~Bu & Ba); + Asu = Bu ^ (~Ba & Be); + } + + state[0] = Aba; + state[1] = Abe; + state[2] = Abi; + state[3] = Abo; + state[4] = Abu; + state[5] = Aga; + state[6] = Age; + state[7] = Agi; + state[8] = Ago; + state[9] = Agu; + state[10] = Aka; + state[11] = Ake; + state[12] = Aki; + state[13] = Ako; + state[14] = Aku; + state[15] = Ama; + state[16] = Ame; + state[17] = Ami; + state[18] = Amo; + state[19] = Amu; + state[20] = Asa; + state[21] = Ase; + state[22] = Asi; + state[23] = Aso; + state[24] = Asu; +} diff --git a/src/Native/libkawpow/keccak/keccakf800.c b/src/Native/libkawpow/keccak/keccakf800.c new file mode 100644 index 000000000..5b9a18025 --- /dev/null +++ b/src/Native/libkawpow/keccak/keccakf800.c @@ -0,0 +1,253 @@ +/* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. + * Copyright 2018-2019 Pawel Bylica. + * Licensed under the Apache License, Version 2.0. + */ + +#include + +static uint32_t rol(uint32_t x, unsigned s) +{ + return (x << s) | (x >> (32 - s)); +} + +static const uint32_t round_constants[22] = { + 0x00000001, + 0x00008082, + 0x0000808A, + 0x80008000, + 0x0000808B, + 0x80000001, + 0x80008081, + 0x00008009, + 0x0000008A, + 0x00000088, + 0x80008009, + 0x8000000A, + 0x8000808B, + 0x0000008B, + 0x00008089, + 0x00008003, + 0x00008002, + 0x00000080, + 0x0000800A, + 0x8000000A, + 0x80008081, + 0x00008080, +}; + +void ethash_keccakf800(uint32_t state[25]) +{ + /* The implementation directly translated from ethash_keccakf1600. */ + + int round; + + uint32_t Aba, Abe, Abi, Abo, Abu; + uint32_t Aga, Age, Agi, Ago, Agu; + uint32_t Aka, Ake, Aki, Ako, Aku; + uint32_t Ama, Ame, Ami, Amo, Amu; + uint32_t Asa, Ase, Asi, Aso, Asu; + + uint32_t Eba, Ebe, Ebi, Ebo, Ebu; + uint32_t Ega, Ege, Egi, Ego, Egu; + uint32_t Eka, Eke, Eki, Eko, Eku; + uint32_t Ema, Eme, Emi, Emo, Emu; + uint32_t Esa, Ese, Esi, Eso, Esu; + + uint32_t Ba, Be, Bi, Bo, Bu; + + uint32_t Da, De, Di, Do, Du; + + Aba = state[0]; + Abe = state[1]; + Abi = state[2]; + Abo = state[3]; + Abu = state[4]; + Aga = state[5]; + Age = state[6]; + Agi = state[7]; + Ago = state[8]; + Agu = state[9]; + Aka = state[10]; + Ake = state[11]; + Aki = state[12]; + Ako = state[13]; + Aku = state[14]; + Ama = state[15]; + Ame = state[16]; + Ami = state[17]; + Amo = state[18]; + Amu = state[19]; + Asa = state[20]; + Ase = state[21]; + Asi = state[22]; + Aso = state[23]; + Asu = state[24]; + + for (round = 0; round < 22; round += 2) + { + /* Round (round + 0): Axx -> Exx */ + + Ba = Aba ^ Aga ^ Aka ^ Ama ^ Asa; + Be = Abe ^ Age ^ Ake ^ Ame ^ Ase; + Bi = Abi ^ Agi ^ Aki ^ Ami ^ Asi; + Bo = Abo ^ Ago ^ Ako ^ Amo ^ Aso; + Bu = Abu ^ Agu ^ Aku ^ Amu ^ Asu; + + Da = Bu ^ rol(Be, 1); + De = Ba ^ rol(Bi, 1); + Di = Be ^ rol(Bo, 1); + Do = Bi ^ rol(Bu, 1); + Du = Bo ^ rol(Ba, 1); + + Ba = Aba ^ Da; + Be = rol(Age ^ De, 12); + Bi = rol(Aki ^ Di, 11); + Bo = rol(Amo ^ Do, 21); + Bu = rol(Asu ^ Du, 14); + Eba = Ba ^ (~Be & Bi) ^ round_constants[round]; + Ebe = Be ^ (~Bi & Bo); + Ebi = Bi ^ (~Bo & Bu); + Ebo = Bo ^ (~Bu & Ba); + Ebu = Bu ^ (~Ba & Be); + + Ba = rol(Abo ^ Do, 28); + Be = rol(Agu ^ Du, 20); + Bi = rol(Aka ^ Da, 3); + Bo = rol(Ame ^ De, 13); + Bu = rol(Asi ^ Di, 29); + Ega = Ba ^ (~Be & Bi); + Ege = Be ^ (~Bi & Bo); + Egi = Bi ^ (~Bo & Bu); + Ego = Bo ^ (~Bu & Ba); + Egu = Bu ^ (~Ba & Be); + + Ba = rol(Abe ^ De, 1); + Be = rol(Agi ^ Di, 6); + Bi = rol(Ako ^ Do, 25); + Bo = rol(Amu ^ Du, 8); + Bu = rol(Asa ^ Da, 18); + Eka = Ba ^ (~Be & Bi); + Eke = Be ^ (~Bi & Bo); + Eki = Bi ^ (~Bo & Bu); + Eko = Bo ^ (~Bu & Ba); + Eku = Bu ^ (~Ba & Be); + + Ba = rol(Abu ^ Du, 27); + Be = rol(Aga ^ Da, 4); + Bi = rol(Ake ^ De, 10); + Bo = rol(Ami ^ Di, 15); + Bu = rol(Aso ^ Do, 24); + Ema = Ba ^ (~Be & Bi); + Eme = Be ^ (~Bi & Bo); + Emi = Bi ^ (~Bo & Bu); + Emo = Bo ^ (~Bu & Ba); + Emu = Bu ^ (~Ba & Be); + + Ba = rol(Abi ^ Di, 30); + Be = rol(Ago ^ Do, 23); + Bi = rol(Aku ^ Du, 7); + Bo = rol(Ama ^ Da, 9); + Bu = rol(Ase ^ De, 2); + Esa = Ba ^ (~Be & Bi); + Ese = Be ^ (~Bi & Bo); + Esi = Bi ^ (~Bo & Bu); + Eso = Bo ^ (~Bu & Ba); + Esu = Bu ^ (~Ba & Be); + + + /* Round (round + 1): Exx -> Axx */ + + Ba = Eba ^ Ega ^ Eka ^ Ema ^ Esa; + Be = Ebe ^ Ege ^ Eke ^ Eme ^ Ese; + Bi = Ebi ^ Egi ^ Eki ^ Emi ^ Esi; + Bo = Ebo ^ Ego ^ Eko ^ Emo ^ Eso; + Bu = Ebu ^ Egu ^ Eku ^ Emu ^ Esu; + + Da = Bu ^ rol(Be, 1); + De = Ba ^ rol(Bi, 1); + Di = Be ^ rol(Bo, 1); + Do = Bi ^ rol(Bu, 1); + Du = Bo ^ rol(Ba, 1); + + Ba = Eba ^ Da; + Be = rol(Ege ^ De, 12); + Bi = rol(Eki ^ Di, 11); + Bo = rol(Emo ^ Do, 21); + Bu = rol(Esu ^ Du, 14); + Aba = Ba ^ (~Be & Bi) ^ round_constants[round + 1]; + Abe = Be ^ (~Bi & Bo); + Abi = Bi ^ (~Bo & Bu); + Abo = Bo ^ (~Bu & Ba); + Abu = Bu ^ (~Ba & Be); + + Ba = rol(Ebo ^ Do, 28); + Be = rol(Egu ^ Du, 20); + Bi = rol(Eka ^ Da, 3); + Bo = rol(Eme ^ De, 13); + Bu = rol(Esi ^ Di, 29); + Aga = Ba ^ (~Be & Bi); + Age = Be ^ (~Bi & Bo); + Agi = Bi ^ (~Bo & Bu); + Ago = Bo ^ (~Bu & Ba); + Agu = Bu ^ (~Ba & Be); + + Ba = rol(Ebe ^ De, 1); + Be = rol(Egi ^ Di, 6); + Bi = rol(Eko ^ Do, 25); + Bo = rol(Emu ^ Du, 8); + Bu = rol(Esa ^ Da, 18); + Aka = Ba ^ (~Be & Bi); + Ake = Be ^ (~Bi & Bo); + Aki = Bi ^ (~Bo & Bu); + Ako = Bo ^ (~Bu & Ba); + Aku = Bu ^ (~Ba & Be); + + Ba = rol(Ebu ^ Du, 27); + Be = rol(Ega ^ Da, 4); + Bi = rol(Eke ^ De, 10); + Bo = rol(Emi ^ Di, 15); + Bu = rol(Eso ^ Do, 24); + Ama = Ba ^ (~Be & Bi); + Ame = Be ^ (~Bi & Bo); + Ami = Bi ^ (~Bo & Bu); + Amo = Bo ^ (~Bu & Ba); + Amu = Bu ^ (~Ba & Be); + + Ba = rol(Ebi ^ Di, 30); + Be = rol(Ego ^ Do, 23); + Bi = rol(Eku ^ Du, 7); + Bo = rol(Ema ^ Da, 9); + Bu = rol(Ese ^ De, 2); + Asa = Ba ^ (~Be & Bi); + Ase = Be ^ (~Bi & Bo); + Asi = Bi ^ (~Bo & Bu); + Aso = Bo ^ (~Bu & Ba); + Asu = Bu ^ (~Ba & Be); + } + + state[0] = Aba; + state[1] = Abe; + state[2] = Abi; + state[3] = Abo; + state[4] = Abu; + state[5] = Aga; + state[6] = Age; + state[7] = Agi; + state[8] = Ago; + state[9] = Agu; + state[10] = Aka; + state[11] = Ake; + state[12] = Aki; + state[13] = Ako; + state[14] = Aku; + state[15] = Ama; + state[16] = Ame; + state[17] = Ami; + state[18] = Amo; + state[19] = Amu; + state[20] = Asa; + state[21] = Ase; + state[22] = Asi; + state[23] = Aso; + state[24] = Asu; +} diff --git a/src/Native/libkawpow/libkawpow.sln b/src/Native/libkawpow/libkawpow.sln new file mode 100644 index 000000000..1d1e6f955 --- /dev/null +++ b/src/Native/libkawpow/libkawpow.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31229.75 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libethhash", "liblibkawpow.vcxproj", "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug|x64.ActiveCfg = Debug|x64 + {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug|x64.Build.0 = Debug|x64 + {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug|x86.ActiveCfg = Debug|Win32 + {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug|x86.Build.0 = Debug|Win32 + {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Release|x64.ActiveCfg = Release|x64 + {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Release|x64.Build.0 = Release|x64 + {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Release|x86.ActiveCfg = Release|Win32 + {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {DDE0FE54-030A-4DFD-98A1-952779FB461F} + EndGlobalSection +EndGlobal diff --git a/src/Native/libkawpow/liblibkawpow.vcxproj b/src/Native/libkawpow/liblibkawpow.vcxproj new file mode 100644 index 000000000..9d05a07b3 --- /dev/null +++ b/src/Native/libkawpow/liblibkawpow.vcxproj @@ -0,0 +1,206 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942} + Win32Proj + netmultihashnative + 10.0 + libkawpow + + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\..\..\..\..\boost_1_62_0;$(ProjectDir)windows\include\libsodium;$(IncludePath);$(ProjectDir) + $(SolutionDir)\..\..\..\..\boost_1_62_0\lib32-msvc-14.0;$(LibraryPath) + + + true + $(SolutionDir)\..\..\..\..\boost_1_62_0;$(ProjectDir)windows\include\libsodium;$(IncludePath);$(ProjectDir) + $(SolutionDir)\..\..\..\..\boost_1_62_0\lib64-msvc-14.0;$(ProjectDir)windows\lib\x64;$(LibraryPath) + + + false + $(SolutionDir)\..\..\..\..\boost_1_62_0;$(ProjectDir)windows\include\libsodium;$(IncludePath);$(ProjectDir) + $(SolutionDir)\..\..\..\..\boost_1_62_0\lib32-msvc-14.0;$(LibraryPath) + + + false + $(SolutionDir)\..\..\..\..\boost_1_62_0;$(ProjectDir)windows\include\libsodium;$(ProjectDir)windows\include\libsodium;$(IncludePath);$(ProjectDir) + $(SolutionDir)\..\..\..\..\boost_1_62_0\lib64-msvc-14.0;$(LibraryPath) + + + + + + Level3 + Disabled + SODIUM_STATIC;_CRT_SECURE_NO_WARNINGS + true + MultiThreadedDebug + stdcpp14 + + + Windows + true + Ws2_32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + SODIUM_STATIC;_CRT_SECURE_NO_WARNINGS + true + MultiThreadedDebug + stdcpp14 + + + Windows + true + Ws2_32.lib;%(AdditionalDependencies) + + + + + + + Level3 + MaxSpeed + true + true + SODIUM_STATIC;_CRT_SECURE_NO_WARNINGS + true + MultiThreaded + stdcpp14 + + + Windows + true + true + true + Ws2_32.lib;%(AdditionalDependencies) + + + + + + + Level3 + MaxSpeed + true + true + SODIUM_STATIC;_CRT_SECURE_NO_WARNINGS + true + MultiThreaded + stdcpp14 + + + Windows + true + true + true + Ws2_32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Native/libkawpow/stdafx.cpp b/src/Native/libkawpow/stdafx.cpp new file mode 100644 index 000000000..bd27597c6 --- /dev/null +++ b/src/Native/libkawpow/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// $safeprojectname$.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/src/Native/libkawpow/stdafx.h b/src/Native/libkawpow/stdafx.h new file mode 100644 index 000000000..f3a07375c --- /dev/null +++ b/src/Native/libkawpow/stdafx.h @@ -0,0 +1,16 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include + + + +// TODO: reference additional headers your program requires here diff --git a/src/Native/libkawpow/support/attributes.h b/src/Native/libkawpow/support/attributes.h new file mode 100644 index 000000000..83be231f0 --- /dev/null +++ b/src/Native/libkawpow/support/attributes.h @@ -0,0 +1,33 @@ +/* ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. + * Copyright 2018-2019 Pawel Bylica. + * Licensed under the Apache License, Version 2.0. + */ + +#pragma once + +/** inline */ +#if _MSC_VER || __STDC_VERSION__ +#define INLINE inline +#else +#define INLINE +#endif + +/** [[always_inline]] */ +#if _MSC_VER +#define ALWAYS_INLINE __forceinline +#elif defined(__has_attribute) && __STDC_VERSION__ +#if __has_attribute(always_inline) +#define ALWAYS_INLINE __attribute__((always_inline)) +#endif +#endif +#if !defined(ALWAYS_INLINE) +#define ALWAYS_INLINE +#endif + +/** [[no_sanitize()]] */ +#if __clang__ +#define NO_SANITIZE(sanitizer) \ + __attribute__((no_sanitize(sanitizer))) +#else +#define NO_SANITIZE(sanitizer) +#endif diff --git a/src/Native/libkawpow/targetver.h b/src/Native/libkawpow/targetver.h new file mode 100644 index 000000000..87c0086de --- /dev/null +++ b/src/Native/libkawpow/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/src/Native/libmultihash/blake2s.c b/src/Native/libmultihash/blake2s.c deleted file mode 100644 index 466540a0e..000000000 --- a/src/Native/libmultihash/blake2s.c +++ /dev/null @@ -1,15 +0,0 @@ -#include "blake2s.h" -#include -#include -#include -#include - -#include "sha3/sph_blake2s.h" - -void blake2s_hash(const char* input, char* output, int inlen) -{ - blake2s_state ctx_blake2s; - blake2s_init(&ctx_blake2s, BLAKE2S_OUTBYTES); - blake2s_update(&ctx_blake2s, input, inlen); - blake2s_final(&ctx_blake2s, output, BLAKE2S_OUTBYTES); -} diff --git a/src/Native/libmultihash/blake2s.h b/src/Native/libmultihash/blake2s.h deleted file mode 100644 index 4e13893c1..000000000 --- a/src/Native/libmultihash/blake2s.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef BLAKE2S_H -#define BLAKE2S_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -void blake2s_hash(const char* input, char* output, int inlen); - -#ifdef __cplusplus -} -#endif - -#endif