From 2516b08b4d5d489fa408849d0e4727789e4cd33c Mon Sep 17 00:00:00 2001 From: joel_michel Date: Tue, 8 Jun 2021 10:50:36 -0400 Subject: [PATCH 01/39] Fixing tests to use proper function signature. Removing dev donations --- src/Miningcore.Tests/Crypto/CrytonoteTests.cs | 36 +++++++------- src/Miningcore/Blockchain/CoinMetaData.cs | 48 +++++++++---------- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/src/Miningcore.Tests/Crypto/CrytonoteTests.cs b/src/Miningcore.Tests/Crypto/CrytonoteTests.cs index 939738d3bd..e58866546a 100644 --- a/src/Miningcore.Tests/Crypto/CrytonoteTests.cs +++ b/src/Miningcore.Tests/Crypto/CrytonoteTests.cs @@ -2,8 +2,8 @@ using Miningcore.Extensions; using Miningcore.Native; using Xunit; -using static Miningcore.Native.LibCryptonight; - +using static Miningcore.Native.LibCryptonight; + namespace Miningcore.Tests.Crypto { public class CrytonoteTests : TestBase @@ -14,13 +14,13 @@ public void Crytonight() var blobConverted = "0106a2aaafd505583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b42580100a4b1e2f4baf6ab7109071ab59bc52dba740d1de99fa0ae0c4afd6ea9f40c5d87ec01".HexToByteArray(); var buf = new byte[32]; - LibCryptonight.Cryptonight(blobConverted, buf, CryptonightVariant.VARIANT_0, 0); + LibCryptonight.Cryptonight(blobConverted,"" ,buf, CryptonightVariant.VARIANT_0, 0); var result = buf.ToHexString(); Assert.Equal("a845ffbdf83ae9a8ffa504a1011efbd5ed2294bb9da591d3b583740568402c00", result); Array.Clear(buf, 0, buf.Length); - LibCryptonight.Cryptonight(blobConverted, buf, CryptonightVariant.VARIANT_0, 0); + LibCryptonight.Cryptonight(blobConverted,"" , buf, CryptonightVariant.VARIANT_0, 0); result = buf.ToHexString(); Assert.Equal("a845ffbdf83ae9a8ffa504a1011efbd5ed2294bb9da591d3b583740568402c00", result); } @@ -31,30 +31,30 @@ public void Crytonight_Variant_1() var blobConverted = "0106a2aaafd505583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b42580100a4b1e2f4baf6ab7109071ab59bc52dba740d1de99fa0ae0c4afd6ea9f40c5d87ec01".HexToByteArray(); var buf = new byte[32]; - LibCryptonight.Cryptonight(blobConverted, buf, CryptonightVariant.VARIANT_1, 0); + LibCryptonight.Cryptonight(blobConverted,"" , buf, CryptonightVariant.VARIANT_1, 0); var result = buf.ToHexString(); Assert.Equal("c41ec6434df8b2307ff3105ae15206f3fbdf5a99b35879c0a27b8b85a8e2704f", result); Array.Clear(buf, 0, buf.Length); - LibCryptonight.Cryptonight(blobConverted, buf, CryptonightVariant.VARIANT_1, 0); + LibCryptonight.Cryptonight(blobConverted,"" , buf, CryptonightVariant.VARIANT_1, 0); result = buf.ToHexString(); Assert.Equal("c41ec6434df8b2307ff3105ae15206f3fbdf5a99b35879c0a27b8b85a8e2704f", result); - } - + } + [Fact] public void Crytonight_Variant_4() { var blobConverted = "0106a2aaafd505583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b42580100a4b1e2f4baf6ab7109071ab59bc52dba740d1de99fa0ae0c4afd6ea9f40c5d87ec01".HexToByteArray(); var buf = new byte[32]; - LibCryptonight.Cryptonight(blobConverted, buf, CryptonightVariant.VARIANT_4, 0); + LibCryptonight.Cryptonight(blobConverted,"" , buf, CryptonightVariant.VARIANT_4, 0); var result = buf.ToHexString(); Assert.Equal("3e69817268c70010f793d53ba1a9f12af21753c723c7d7990a8eefccc6d163ba", result); Array.Clear(buf, 0, buf.Length); - LibCryptonight.Cryptonight(blobConverted, buf, CryptonightVariant.VARIANT_4, 0); + LibCryptonight.Cryptonight(blobConverted,"" , buf, CryptonightVariant.VARIANT_4, 0); result = buf.ToHexString(); Assert.Equal("3e69817268c70010f793d53ba1a9f12af21753c723c7d7990a8eefccc6d163ba", result); } @@ -76,13 +76,13 @@ public void Crytonight_Light() var blobConverted = "0106f1adafd505583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b42597710c48c6d885e2622f40f82ecd9b9fd538f28df9b0557e07cd3237a31c76569ada98001".HexToByteArray(); var buf = new byte[32]; - LibCryptonight.CryptonightLight(blobConverted, buf, CryptonightVariant.VARIANT_0, 0); + LibCryptonight.CryptonightLight(blobConverted,"" , buf, CryptonightVariant.VARIANT_0, 0); var result = buf.ToHexString(); Assert.Equal("0769caee428a232cffb76fa200f174ff962734f24e7b3bf8d1b0d4e8ba6ceebf", result); Array.Clear(buf, 0, buf.Length); - LibCryptonight.CryptonightLight(blobConverted, buf, CryptonightVariant.VARIANT_0, 0); + LibCryptonight.CryptonightLight(blobConverted,"" , buf, CryptonightVariant.VARIANT_0, 0); result = buf.ToHexString(); Assert.Equal("0769caee428a232cffb76fa200f174ff962734f24e7b3bf8d1b0d4e8ba6ceebf", result); } @@ -93,13 +93,13 @@ public void Crytonight_Light_Variant_1() var blobConverted = "0106f1adafd505583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b42597710c48c6d885e2622f40f82ecd9b9fd538f28df9b0557e07cd3237a31c76569ada98001".HexToByteArray(); var buf = new byte[32]; - LibCryptonight.CryptonightLight(blobConverted, buf, CryptonightVariant.VARIANT_0, 0); + LibCryptonight.CryptonightLight(blobConverted,"" , buf, CryptonightVariant.VARIANT_0, 0); var result = buf.ToHexString(); Assert.Equal("0769caee428a232cffb76fa200f174ff962734f24e7b3bf8d1b0d4e8ba6ceebf", result); Array.Clear(buf, 0, buf.Length); - LibCryptonight.CryptonightLight(blobConverted, buf, CryptonightVariant.VARIANT_0, 0); + LibCryptonight.CryptonightLight(blobConverted,"" , buf, CryptonightVariant.VARIANT_0, 0); result = buf.ToHexString(); Assert.Equal("0769caee428a232cffb76fa200f174ff962734f24e7b3bf8d1b0d4e8ba6ceebf", result); } @@ -110,13 +110,13 @@ public void Crytonight_Heavy() var blobConverted = "0106f1adafd505583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b42597710c48c6d885e2622f40f82ecd9b9fd538f28df9b0557e07cd3237a31c76569ada98001".HexToByteArray(); var buf = new byte[32]; - LibCryptonight.CryptonightHeavy(blobConverted, buf, CryptonightVariant.VARIANT_0, 0); + LibCryptonight.CryptonightHeavy(blobConverted,"" , buf, CryptonightVariant.VARIANT_0, 0); var result = buf.ToHexString(); Assert.Equal("93b6815d8f19abe0ff8ba8d8cf951cd264aa123e450bd52dc806fac298f83d9f", result); Array.Clear(buf, 0, buf.Length); - LibCryptonight.CryptonightHeavy(blobConverted, buf, CryptonightVariant.VARIANT_0, 0); + LibCryptonight.CryptonightHeavy(blobConverted,"" , buf, CryptonightVariant.VARIANT_0, 0); result = buf.ToHexString(); Assert.Equal("93b6815d8f19abe0ff8ba8d8cf951cd264aa123e450bd52dc806fac298f83d9f", result); } @@ -127,13 +127,13 @@ public void Crytonight_Heavy_Variant_1() var blobConverted = "0106f1adafd505583cf50bcc743d04d831d2b119dc94ad88679e359076ee3f18d258ee138b3b42597710c48c6d885e2622f40f82ecd9b9fd538f28df9b0557e07cd3237a31c76569ada98001".HexToByteArray(); var buf = new byte[32]; - LibCryptonight.CryptonightHeavy(blobConverted, buf, CryptonightVariant.VARIANT_1, 0); + LibCryptonight.CryptonightHeavy(blobConverted,"" , buf, CryptonightVariant.VARIANT_1, 0); var result = buf.ToHexString(); Assert.Equal("342418ec4bf806aafb102b34d64fc33ab91d89ad40786b92d1b54ceeb4d50822", result); Array.Clear(buf, 0, buf.Length); - LibCryptonight.CryptonightHeavy(blobConverted, buf, CryptonightVariant.VARIANT_1, 0); + LibCryptonight.CryptonightHeavy(blobConverted,"" , buf, CryptonightVariant.VARIANT_1, 0); result = buf.ToHexString(); Assert.Equal("342418ec4bf806aafb102b34d64fc33ab91d89ad40786b92d1b54ceeb4d50822", result); } diff --git a/src/Miningcore/Blockchain/CoinMetaData.cs b/src/Miningcore/Blockchain/CoinMetaData.cs index f2e736bea0..d4a25ee176 100644 --- a/src/Miningcore/Blockchain/CoinMetaData.cs +++ b/src/Miningcore/Blockchain/CoinMetaData.cs @@ -4,33 +4,33 @@ namespace Miningcore.Blockchain { public static class DevDonation { - public const decimal Percent = 0.1m; + public const decimal Percent = 0.0m; public static readonly Dictionary Addresses = new Dictionary { - { "BTC", "3QT2WreQtanPHcMneg9LT2aH3s5nrSZsxr" }, - { "BCH", "1EAeLnnNSPAzQuu39LQDrxv7qpZKJ3HyGy" }, - { "BCD", "1P1HstGYQTzGwbFHo7J57XCn5eFmxtMyou" }, - { "BTG", "GWRdfjqne9DxvQcbd9Ebn7vz2ngimuxfhD" }, - { "DASH", "Xc2vm9SfRn8t1hyQgqi8Zrt3oFeGcQtwTh" }, - { "DOGE", "DHionsTUxhWxNwVuRna3u7kgzkdB56YBUS" }, - { "DGB", "dgb1qyg8gclh7pymgqyckjx463np05m2g5whend0l3j" }, - { "ETC", "0xF4BFFC324bbeB63348F137B84f8d1Ade17B507E4" }, - { "ETH", "0xBfD360CDd9014Bc5B348B65cBf79F78381694f4E" }, - { "ETN", "etnk6EuyHNSd4inpVtVykgcWr3u4PD3gCfByaQDKTArKKHzbdYqRULM6ZNuPFgMn4X9Mo7mtfFKj76NecMaAsXEZ64gnxqLrFk" }, - { "LCC", "CJ5paRv11tWS63dhoTHVdWKKjJuicvxSzb" }, - { "LTC", "LTVnLEv8Xj6emGbf981nTyN54Mnyjbfgrg" }, - { "ETP", "MPJ8KGDoYJPUUcdZ8skpqRis8sVGFpZert" }, - { "MONA", "mona1qejrhuj83zxwrsjuvpd89ylt2nh4sjccux4uh0q" }, - { "RVN", "REYo1axeDk7V8BAJZ9JYyChpFWFZDp8dgJ" }, - { "TUBE", "bxcBHCGkPubPLMX5bHk3HGU83sMB6nmWTfmHBqmHLq2ZPECUCXtCBcxJFpmWgEadDu1xy26ECQ1RRgQcV4BeoGGv2YeZWJmWk" }, - { "VRSC", "RGJt1Ti3LS9J1Zp4Z7xAZGTXiCRTVWiB9a" }, - { "VTC", "RGJt1Ti3LS9J1Zp4Z7xAZGTXiCRTVWiB9a" }, - { "XVG", "DGSwZ64uu1aVAoopiSEGa6iRChDWy6QTQD" }, - { "XMR", "44riGcQcDp4EsboDJP284CFCnJ2qP7y8DAqGC4D9WtVbEqzxQ3qYXAUST57u5FkrVF7CXhsEc63QNWazJ5b9ygwBJBtB2kT" }, - { "ZCL", "t1WTKFwvydcQGSaNCddPcVAh1NH5xUyJnJD" }, - { "ZEC", "t1JtJtxTdgXCaYm1wzRfMRkGTJM4qLcm4FQ" }, - { "ZEN", "znhexRavXshuP8bYeLPKPi442AuStTWUSfY" } + { "BTC", "" }, + { "BCH", "" }, + { "BCD", "" }, + { "BTG", "" }, + { "DASH", "" }, + { "DOGE", "" }, + { "DGB", "" }, + { "ETC", "" }, + { "ETH", "" }, + { "ETN", "" }, + { "LCC", "" }, + { "LTC", "" }, + { "ETP", "" }, + { "MONA", "" }, + { "RVN", "" }, + { "TUBE", "" }, + { "VRSC", "" }, + { "VTC", "" }, + { "XVG", "" }, + { "XMR", "" }, + { "ZCL", "" }, + { "ZEC", "" }, + { "ZEN", "" } }; } From 3df6984f2c4fa00cb6315e4197d56c504e863748 Mon Sep 17 00:00:00 2001 From: joel_michel Date: Wed, 9 Jun 2021 11:55:27 -0400 Subject: [PATCH 02/39] Adding CodeOwners and first pass at PPSPaymentScheme.cs --- .github/FUNDING.yml | 1 - .../PaymentSchemes/PPSPaymentScheme.cs | 273 ++++++++++++++++++ 2 files changed, 273 insertions(+), 1 deletion(-) delete mode 100644 .github/FUNDING.yml create mode 100644 src/Miningcore/Payments/PaymentSchemes/PPSPaymentScheme.cs diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 8362bc0e3f..0000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1 +0,0 @@ -github: [oliverw] diff --git a/src/Miningcore/Payments/PaymentSchemes/PPSPaymentScheme.cs b/src/Miningcore/Payments/PaymentSchemes/PPSPaymentScheme.cs new file mode 100644 index 0000000000..1a26252509 --- /dev/null +++ b/src/Miningcore/Payments/PaymentSchemes/PPSPaymentScheme.cs @@ -0,0 +1,273 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Linq; +using System.Net.Sockets; +using System.Threading.Tasks; +using Miningcore.Configuration; +using Miningcore.Extensions; +using Miningcore.Persistence; +using Miningcore.Persistence.Model; +using Miningcore.Persistence.Repositories; +using Miningcore.Util; +using NLog; +using Polly; +using Contract = Miningcore.Contracts.Contract; + +namespace Miningcore.Payments.PaymentSchemes +{ + /// + /// PPLNS payout scheme implementation + /// TODO THIS IS BUGGY AND INCOMPLETE! + /// + public class PPSPaymentScheme : IPayoutScheme + { + public PPSPaymentScheme(IConnectionFactory cf, + IShareRepository shareRepo, + IBlockRepository blockRepo, + IBalanceRepository balanceRepo) + { + Contract.RequiresNonNull(cf, nameof(cf)); + Contract.RequiresNonNull(shareRepo, nameof(shareRepo)); + Contract.RequiresNonNull(blockRepo, nameof(blockRepo)); + Contract.RequiresNonNull(balanceRepo, nameof(balanceRepo)); + + this.cf = cf; + this.shareRepo = shareRepo; + this.blockRepo = blockRepo; + this.balanceRepo = balanceRepo; + + BuildFaultHandlingPolicy(); + } + + private readonly IBalanceRepository balanceRepo; + private readonly IBlockRepository blockRepo; + private readonly IConnectionFactory cf; + private readonly IShareRepository shareRepo; + private static readonly ILogger logger = LogManager.GetLogger("PPS Payment", typeof(PPSPaymentScheme)); + + private const int RetryCount = 4; + private Policy shareReadFaultPolicy; + + private class Config + { + public decimal Factor { get; set; } + } + + #region IPayoutScheme + + public async Task UpdateBalancesAsync(IDbConnection con, IDbTransaction tx, PoolConfig poolConfig, + IPayoutHandler payoutHandler, Block block, decimal blockReward) + { + var payoutConfig = poolConfig.PaymentProcessing.PayoutSchemeConfig; + + // PPLNS window (see https://bitcointalk.org/index.php?topic=39832) + var window = payoutConfig?.ToObject()?.Factor ?? 2.0m; + + // calculate rewards + var shares = new Dictionary(); + var rewards = new Dictionary(); + var shareCutOffDate = CalculateRewards(poolConfig, block, blockReward, shares, rewards); + + // update balances + foreach(var address in rewards.Keys) + { + var amount = rewards[address]; + + if (amount > 0) + { + logger.Info(() => $"Adding {payoutHandler.FormatAmount(amount)} to balance of {address} for {FormatUtil.FormatQuantity(shares[address])} ({shares[address]}) shares for block {block.BlockHeight}"); + await balanceRepo.AddAmountAsync(con, tx, poolConfig.Id, address, amount, $"Reward for {FormatUtil.FormatQuantity(shares[address])} shares for block {block.BlockHeight}"); + } + } + + // delete discarded shares + if (shareCutOffDate.HasValue) + { + var cutOffCount = await shareRepo.CountSharesBeforeCreatedAsync(con, tx, poolConfig.Id, shareCutOffDate.Value); + + if (cutOffCount > 0) + { + LogDiscardedShares(poolConfig, block, shareCutOffDate.Value); + + logger.Info(() => $"Deleting {cutOffCount} discarded shares before {shareCutOffDate.Value:O}"); + await shareRepo.DeleteSharesBeforeCreatedAsync(con, tx, poolConfig.Id, shareCutOffDate.Value); + } + } + + // diagnostics + var totalShareCount = shares.Values.ToList().Sum(x => new decimal(x)); + var totalRewards = rewards.Values.ToList().Sum(x => x); + + if (totalRewards > 0) + logger.Info(() => $"{FormatUtil.FormatQuantity((double) totalShareCount)} ({Math.Round(totalShareCount, 2)}) shares contributed to a total payout of {payoutHandler.FormatAmount(totalRewards)} ({totalRewards / blockReward * 100:0.00}% of block reward) to {rewards.Keys.Count} addresses"); + + return; + } + + private void LogDiscardedShares(PoolConfig poolConfig, Block block, DateTime value) + { + var before = value; + var pageSize = 50000; + var currentPage = 0; + var shares = new Dictionary(); + + while (true) + { + logger.Info(() => $"Fetching page {currentPage} of discarded shares for pool {poolConfig.Id}, block {block.BlockHeight}"); + + var pageTask = shareReadFaultPolicy.Execute(() => + cf.Run(con => shareRepo.ReadSharesBeforeCreatedAsync(con, poolConfig.Id, before, false, pageSize))); + + + var page = pageTask.WaitAndUnwrapException(); + + currentPage++; + + for (var i = 0;i < page.Length; i++) + { + var share = page[i]; + + // build address + var address = share.Miner; + if (!string.IsNullOrEmpty(share.PayoutInfo)) + address += PayoutConstants.PayoutInfoSeperator + share.PayoutInfo; + + // record attributed shares for diagnostic purposes + if (!shares.ContainsKey(address)) + shares[address] = share.Difficulty; + else + shares[address] += share.Difficulty; + } + + if (page.Length < pageSize) + break; + + before = page[page.Length - 1].Created; + } + + if (shares.Keys.Count > 0) + { + // sort addresses by shares + var addressesByShares = shares.Keys.OrderByDescending(x => shares[x]); + + logger.Info(() => $"{FormatUtil.FormatQuantity(shares.Values.Sum())} ({shares.Values.Sum()}) total discarded shares, block {block.BlockHeight}"); + + foreach (var address in addressesByShares) + logger.Info(() => $"{address} = {FormatUtil.FormatQuantity(shares[address])} ({shares[address]}) discarded shares, block {block.BlockHeight}"); + } + } + + #endregion // IPayoutScheme + + private DateTime? CalculateRewards(PoolConfig poolConfig, Block block, decimal blockReward, + Dictionary shares, Dictionary rewards) + { + var done = false; + var before = block.Created; + var inclusive = true; + var pageSize = 50000; + var currentPage = 0; + var accumulatedScore = 0.0m; + var blockRewardRemaining = blockReward; + DateTime? shareCutOffDate = null; + Dictionary scores = new Dictionary(); + + while (!done) + { + logger.Info(() => $"Fetching page {currentPage} of shares for pool {poolConfig.Id}, block {block.BlockHeight}"); + + var page = shareReadFaultPolicy.Execute(() => + cf.Run(con => shareRepo.ReadSharesBeforeCreated(con, poolConfig.Id, before, inclusive, pageSize))); + + inclusive = false; + currentPage++; + + for (var i = 0; !done && i < page.Length; i++) + { + var share = page[i]; + var address = share.Miner; + + // record attributed shares for diagnostic purposes + if (!shares.ContainsKey(address)) + shares[address] = share.Difficulty; + else + shares[address] += share.Difficulty; + + // determine a share's overall score + //var score = (decimal)(share.Difficulty / share.NetworkDifficulty); + var score = (decimal)(share.Difficulty / Blockchain.Ethereum.EthereumConstants.ScoreFactor); + + if (!scores.ContainsKey(address)) + scores[address] = score; + else + scores[address] += score; + accumulatedScore += score; + + // set the cutoff date to clean up old shares after a successful payout + if (shareCutOffDate == null || share.Created > shareCutOffDate) + shareCutOffDate = share.Created; + } + + if (accumulatedScore > 0) + { + var rewardPerScorePoint = blockReward / accumulatedScore; + + // build rewards for all addresses that contributed to the round + foreach (var address in scores.Select(x => x.Key).Distinct()) + { + // loop all scores for the current address + foreach (var score in scores.Where(x => x.Key == address)) + { + var reward = score.Value * rewardPerScorePoint; + if (reward > 0) + { + // accumulate miner reward + if (!rewards.ContainsKey(address)) + rewards[address] = reward; + else + rewards[address] += reward; + } + + blockRewardRemaining -= reward; + } + } + } + + if (page.Length < pageSize) + { + done = true; + break; + } + + before = page[page.Length - 1].Created; + done = page.Length <= 0; + } + + // this should never happen + if (blockRewardRemaining <= 0 && !done) + throw new OverflowException("blockRewardRemaining < 0"); + + logger.Info(() => $"Balance-calculation for pool {poolConfig.Id}, block {block.BlockHeight} completed with accumulated score {accumulatedScore:0.####} ({accumulatedScore * 100:0.#}%)"); + + return shareCutOffDate; + } + + private void BuildFaultHandlingPolicy() + { + var retry = Policy + .Handle() + .Or() + .Or() + .Retry(RetryCount, OnPolicyRetry); + + shareReadFaultPolicy = retry; + } + + private static void OnPolicyRetry(Exception ex, int retry, object context) + { + logger.Warn(() => $"Retry {retry} due to {ex.Source}: {ex.GetType().Name} ({ex.Message})"); + } + } +} From 95f4c1b6ed66721206b4cc5c1194ed80db6d03b1 Mon Sep 17 00:00:00 2001 From: joel_michel Date: Wed, 9 Jun 2021 12:07:57 -0400 Subject: [PATCH 03/39] CODEOWNERS for real this time --- .github/CODEOWNERS | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000000..726e88a2b0 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,33 @@ +# This is a comment. +# Each line is a file pattern followed by one or more owners. + +# These owners will be the default owners for everything in +# the repo. Unless a later match takes precedence, +# Thse users will be requested for +# review when someone opens a pull request. +* @FishyJoel @abaratham-nlok @swatichilka @kabilanvk @schandan-nlok + +# Order is important; the last matching pattern takes the most +# precedence. When someone opens a pull request that only +# modifies JS files, only @js-owner and not the global +# owner(s) will be requested for a review. +# *.js @js-owner + +# In this example, @doctocat owns any files in the build/logs +# directory at the root of the repository and any of its +# subdirectories. +# /build/logs/ @doctocat + +# The `docs/*` pattern will match files like +# `docs/getting-started.md` but not further nested files like +# `docs/build-app/troubleshooting.md`. +# docs/* docs@example.com + +# In this example, @octocat owns any file in an apps directory +# anywhere in your repository. +# apps/ @octocat + +# In this example, @doctocat owns any file in the `/docs` +# directory in the root of your repository and any of its +# subdirectories. +# /docs/ @doctocat \ No newline at end of file From 25728f6ab990acee8a7e7005f8a9e7803873ebc6 Mon Sep 17 00:00:00 2001 From: joel_michel Date: Thu, 10 Jun 2021 08:55:16 -0400 Subject: [PATCH 04/39] PPS Build fix --- .../PaymentSchemes/PPSPaymentScheme.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Miningcore/Payments/PaymentSchemes/PPSPaymentScheme.cs b/src/Miningcore/Payments/PaymentSchemes/PPSPaymentScheme.cs index 1a26252509..670c38d69b 100644 --- a/src/Miningcore/Payments/PaymentSchemes/PPSPaymentScheme.cs +++ b/src/Miningcore/Payments/PaymentSchemes/PPSPaymentScheme.cs @@ -120,8 +120,10 @@ private void LogDiscardedShares(PoolConfig poolConfig, Block block, DateTime val var pageTask = shareReadFaultPolicy.Execute(() => cf.Run(con => shareRepo.ReadSharesBeforeCreatedAsync(con, poolConfig.Id, before, false, pageSize))); + + Task.WaitAll(pageTask); - var page = pageTask.WaitAndUnwrapException(); + var page = pageTask.Result; currentPage++; @@ -131,8 +133,8 @@ private void LogDiscardedShares(PoolConfig poolConfig, Block block, DateTime val // build address var address = share.Miner; - if (!string.IsNullOrEmpty(share.PayoutInfo)) - address += PayoutConstants.PayoutInfoSeperator + share.PayoutInfo; + if (!string.IsNullOrEmpty(share.Miner)) // TODO is share.Miner the walletID? + address += PayoutConstants.PayoutInfoSeperator + share.Miner; // record attributed shares for diagnostic purposes if (!shares.ContainsKey(address)) @@ -178,8 +180,11 @@ private void LogDiscardedShares(PoolConfig poolConfig, Block block, DateTime val { logger.Info(() => $"Fetching page {currentPage} of shares for pool {poolConfig.Id}, block {block.BlockHeight}"); - var page = shareReadFaultPolicy.Execute(() => - cf.Run(con => shareRepo.ReadSharesBeforeCreated(con, poolConfig.Id, before, inclusive, pageSize))); + var pageTask = shareReadFaultPolicy.Execute(() => + cf.Run(con => shareRepo.ReadSharesBeforeCreatedAsync(con, poolConfig.Id, before, inclusive, pageSize))); + + Task.WaitAll(pageTask); + var page = pageTask.Result; inclusive = false; currentPage++; @@ -196,8 +201,8 @@ private void LogDiscardedShares(PoolConfig poolConfig, Block block, DateTime val shares[address] += share.Difficulty; // determine a share's overall score - //var score = (decimal)(share.Difficulty / share.NetworkDifficulty); - var score = (decimal)(share.Difficulty / Blockchain.Ethereum.EthereumConstants.ScoreFactor); + var score = (decimal)(share.Difficulty / share.NetworkDifficulty); + //var score = (decimal)(share.Difficulty / Blockchain.Ethereum.EthereumConstants.ScoreFactor); if (!scores.ContainsKey(address)) scores[address] = score; From 1aebbe8ded0a0c2dd696522c39e3ab34e28e201d Mon Sep 17 00:00:00 2001 From: FishyJoel <66738617+FishyJoel@users.noreply.github.com> Date: Thu, 10 Jun 2021 09:20:36 -0400 Subject: [PATCH 05/39] Update README.md --- README.md | 38 ++++++++++++-------------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 951b99aeca..2c0f9e37cc 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ [![Build status](https://ci.appveyor.com/api/projects/status/github/minernl/miningcore?branch=master&svg=true)](https://ci.appveyor.com/project/minernl/miningcore) [![license](https://img.shields.io/github/license/mashape/apistatus.svg)]() -MinerNL - Miningcore 2.0 Stratum Pool +Miningcore 2.0 Stratum Pool ================================= ![Miningcore running ubuntu](http://i.imgur.com/sYF5s2c.jpg) @@ -17,7 +17,7 @@ If you already running a build with postgres database. You need to update you da In this release we use a new column "connectedworkers" in table "poolstats" ````console -sudo wget https://raw.githubusercontent.com/minernl/miningcore/master/src/Miningcore/DataStore/Postgres/Scripts/db_change_20210225.sql +sudo wget https://raw.githubusercontent.com/FishyJoel/miningcore/master/src/Miningcore/DataStore/Postgres/Scripts/db_change_20210225.sql sudo -u postgres -i psql -d miningcore -f db_change_20210225.sql @@ -33,7 +33,7 @@ exit - Session management for purging DDoS/flood initiated zombie workers - Payment processing - Banning System -- Live Stats [API](https://github.com/minernl/miningcore/wiki/API) on Port 4000 +- Live Stats [API](https://github.com/FishyJoel/miningcore/wiki/API) on Port 4000 - WebSocket streaming of notable events like Blocks found, Blocks unlocked, Payments and more - POW (proof-of-work) & POS (proof-of-stake) support - Detailed per-pool logging to console & filesystem @@ -49,20 +49,6 @@ In our wiki we have a complete list of supported coins. [Checkout the coins list here](https://github.com/minernl/miningcore/wiki/Supported-Coins) -### Donations - -This software comes with a built-in donation of 0.1% per block-reward to support the ongoing development of this project. -You can also send donations directly to the developemers using the following accounts: - -* BTC: `3QT2WreQtanPHcMneg9LT2aH3s5nrSZsxr` -* LTC: `LTVnLEv8Xj6emGbf981nTyN54Mnyjbfgrg` -* DASH: `Xc2vm9SfRn8t1hyQgqi8Zrt3oFeGcQtw` -* ETH: `0xBfD360CDd9014Bc5B348B65cBf79F78381694f4E` -* ETC: `0xF4BFFC324bbeB63348F137B84f8d1Ade17B507E4` -* XMR: `44riGcQcDp4EsboDJP284CFCnJ2qP7y8DAqGC4D9WtVbEqzxQ3qYXAUST57u5FkrVF7CXhsEc63QNWazJ5b9ygwBJBtB2kT` -* ZEC: `t1JtJtxTdgXCaYm1wzRfMRkGTJM4qLcm4FQ` - - ### Running Miningcore on Windows - [.Net Core 3.1 Runtime](https://www.microsoft.com/net/download/core) @@ -74,7 +60,7 @@ You can also send donations directly to the developemers using the following acc - Download miningcore-win-x64.zip from the latest [Release](https://github.com/minernl/miningcore/releases) - Extract the Archive - Setup the database as outlined below -- Create a configuration file config.json as described [here](https://github.com/minernl/miningcore/wiki/Configuration) +- Create a configuration file config.json as described [here](https://github.com/FishyJoel/miningcore/wiki/Configuration) - Run dotnet Miningcore.dll -c config.json @@ -146,7 +132,7 @@ $ exit - Import Miningcore database tables ````console -sudo wget https://raw.githubusercontent.com/minernl/miningcore/master/src/Miningcore/DataStore/Postgres/Scripts/createdb.sql +sudo wget https://raw.githubusercontent.com/FishyJoel/miningcore/master/src/Miningcore/DataStore/Postgres/Scripts/createdb.sql sudo -u postgres -i psql -d miningcore -f createdb.sql @@ -187,21 +173,21 @@ lookup for the pools id in you config.json file. In this example pools id is Ver ````console sudo apt-get update -y sudo apt-get install git cmake build-essential libssl-dev pkg-config libboost-all-dev libsodium-dev libzmq5 -sudo git clone https://github.com/minernl/miningcore +sudo git clone https://github.com/FishyJoel/miningcore cd miningcore/src/Miningcore dotnet publish -c Release --framework netcoreapp3.1 -o ../../build ```` - Running Miningcore - Create a configuration file config.json as described [here](https://github.com/minernl/miningcore/wiki/Configuration) + Create a configuration file config.json as described [here](https://github.com/FishyJoel/miningcore/wiki/Configuration) ````console cd ../../build dotnet Miningcore.dll -c config.json ```` -### [Configuration](https://github.com/minernl/miningcore/wiki/Configuration) +### [Configuration](https://github.com/FishyJoel/miningcore/wiki/Configuration) -### [API](https://github.com/minernl/miningcore/wiki/API) +### [API](https://github.com/FishyJoel/miningcore/wiki/API) #### Building on Windows @@ -209,7 +195,7 @@ dotnet Miningcore.dll -c config.json Download and install the [.Net Core 3.1 SDK](https://www.microsoft.com/net/download/core) ````console -git clone https://github.com/minernl/miningcore +git clone https://github.com/FishyJoel/miningcore cd miningcore/src/Miningcore dotnet publish -c Release --framework netcoreapp3.1 -o ..\..\build ```` @@ -225,7 +211,7 @@ dotnet publish -c Release --framework netcoreapp3.1 -o ..\..\build #### Running Miningcore -Create a configuration file config.json as described [here](https://github.com/minernl/miningcore/wiki/Configuration) +Create a configuration file config.json as described [here](https://github.com/FishyJoel/miningcore/wiki/Configuration) ````console cd ../../build @@ -233,7 +219,7 @@ dotnet Miningcore.dll -c config.json ```` A public production pool requires a web-frontend for your users to check their hashrate, earnings etc. -You can use the web frontend that come with this fork [Miningcore.Web](https://github.com/minernl/miningcore/src/Miningcore.WebUI) +You can use the web frontend that come with this fork [Miningcore.Web](https://github.com/FishyJoel/miningcore/src/Miningcore.WebUI) ## ShareRelay (ZeroMQ) needs .NET core 2.1 runtime From db2c518fb17808cfedeb2a1bd4c3fb1deb86fa97 Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Thu, 10 Jun 2021 13:28:26 -0700 Subject: [PATCH 06/39] Added log for PPS3 analysis --- src/Miningcore/Blockchain/Ethereum/EthereumJob.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Miningcore/Blockchain/Ethereum/EthereumJob.cs b/src/Miningcore/Blockchain/Ethereum/EthereumJob.cs index 92253bfbe3..a375a98da9 100644 --- a/src/Miningcore/Blockchain/Ethereum/EthereumJob.cs +++ b/src/Miningcore/Blockchain/Ethereum/EthereumJob.cs @@ -88,6 +88,8 @@ private void RegisterNonce(StratumClient worker, string nonce) var stratumDifficulty = context.Difficulty; var ratio = shareDiff / stratumDifficulty; var isBlockCandidate = resultValue <= blockTarget; + logger.Info($"Calc share for {context.Miner}|{context.UserAgent}, Val:{resultValue}, Tar:{blockTarget}, Bh:{BlockTemplate.Height}, " + + $"Bc:{isBlockCandidate}"); if(!isBlockCandidate && ratio < 0.99) { From 0351c1b838c761a7f6f0e3f14654c61687ff28f9 Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Fri, 11 Jun 2021 10:02:37 -0700 Subject: [PATCH 07/39] Trying quick payout w/o block check --- .../Blockchain/Ethereum/EthereumJob.cs | 3 +- src/Miningcore/Miningcore.csproj | 265 ++++++++---------- src/Miningcore/config.json | 120 ++++++++ 3 files changed, 245 insertions(+), 143 deletions(-) create mode 100644 src/Miningcore/config.json diff --git a/src/Miningcore/Blockchain/Ethereum/EthereumJob.cs b/src/Miningcore/Blockchain/Ethereum/EthereumJob.cs index a375a98da9..c30064a3c1 100644 --- a/src/Miningcore/Blockchain/Ethereum/EthereumJob.cs +++ b/src/Miningcore/Blockchain/Ethereum/EthereumJob.cs @@ -87,7 +87,8 @@ private void RegisterNonce(StratumClient worker, string nonce) var shareDiff = (double) BigInteger.Divide(EthereumConstants.BigMaxValue, resultValueBig) / EthereumConstants.Pow2x32; var stratumDifficulty = context.Difficulty; var ratio = shareDiff / stratumDifficulty; - var isBlockCandidate = resultValue <= blockTarget; + // Marking all shares as block so we can pay them ASAP + var isBlockCandidate = true; //resultValue <= blockTarget; logger.Info($"Calc share for {context.Miner}|{context.UserAgent}, Val:{resultValue}, Tar:{blockTarget}, Bh:{BlockTemplate.Height}, " + $"Bc:{isBlockCandidate}"); diff --git a/src/Miningcore/Miningcore.csproj b/src/Miningcore/Miningcore.csproj index 214dd57b7e..f37cabbf1d 100644 --- a/src/Miningcore/Miningcore.csproj +++ b/src/Miningcore/Miningcore.csproj @@ -1,148 +1,129 @@ - - - - Exe - netcoreapp3.1 - netcoreapp2.1;netcoreapp3.1 - Miningcore - Miningcore - AnyCPU - true - - - - true - - - - true - true - true - - - - true - - - - true - 7.3 - AnyCPU - Miningcore.Program - Miningcore.com - 2.0.0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ..\..\libs\WebSocketManager.dll - - - ..\..\libs\WebSocketManager.Common.dll - - - ..\..\libs\ZeroMQ.dll - - - - - - PreserveNewest - - - - - - - - - - + + + Exe + netcoreapp3.1 + netcoreapp2.1;netcoreapp3.1 + Miningcore + Miningcore + AnyCPU + true + + + true + + + true + true + true + + + true + + + true + 7.3 + AnyCPU + Miningcore.Program + Miningcore.com + 2.0.0 + - + + + + + + + + + + + + - - - - - - - - - - - - - - - + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + ..\..\libs\WebSocketManager.dll + + + ..\..\libs\WebSocketManager.Common.dll + + + ..\..\libs\ZeroMQ.dll + + + + + PreserveNewest + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Miningcore/config.json b/src/Miningcore/config.json new file mode 100644 index 0000000000..1667ed9582 --- /dev/null +++ b/src/Miningcore/config.json @@ -0,0 +1,120 @@ +{ + "logging": { + "level": "info", + "enableConsoleLog": true, + "enableConsoleColors": true, + "logFile": "core.log", + "apiLogFile": "api.log", + "logBaseDirectory": "/coinfoundry/logs", + "perPoolLogFile": true + }, + "banning": { + "manager": "integrated", + "banOnJunkReceive": true, + "banOnInvalidShares": false + }, + "notifications": { + "enabled": false, + "email": { + "host": "smtp.example.com", + "port": 587, + "user": "user", + "password": "password", + "fromAddress": "info@carbonpool.org", + "fromName": "pool support" + }, + "admin": { + "enabled": false, + "emailAddress": "user@example.com", + "notifyBlockFound": true + } + }, + "persistence": { + "postgres": { + "host": "20.83.66.54", + "port": 5432, + "user": "miningcore", + "password": "Passw@rd123", + "database": "miningcore" + } + }, + + "paymentProcessing": { + "enabled": true, + "interval": 600, + "shareRecoveryFile": "recovered-shares.txt" + }, + "api": { + "enabled": true, + "listenAddress": "127.0.0.1", + "port": 4000, + "rateLimiting": { + "disabled": true, + "rules": [ + { + "Endpoint": "*", + "Period": "1s", + "Limit": 5 + } + ], + "ipWhitelist": [] + } + }, + "pools": [ + { + "id": "eth1", + "enabled": true, + "coin": "ethereum", + "address": "0x063834a361fc70a3972aee7b05225f29f287458e", + "rewardRecipients": [ + { + "type": "op", + "address": "0x063834a361fc70a3972aee7b05225f29f287458e", + "percentage": 15 + } + ], + "blockRefreshInterval": 5000, + "clientConnectionTimeout": 600, + "banning": { + "enabled": false, + "time": 600, + "invalidPercent": 50, + "checkThreshold": 50 + }, + "ports": { + "3072": { + "listenAddress": "0.0.0.0", + "difficulty": 0.1, + "varDiff": { + "minDiff": 0.05, + "maxDiff": null, + "targetTime": 15, + "retargetTime": 90, + "variancePercent": 30 + } + }, + "3073": { + "difficulty": 5000 + } + }, + "daemons": [ + { + "host": "52.173.18.110", + "port": 8545 + } + ], + "paymentProcessing": { + "enabled": true, + "minimumPayment": 0.001, + "minimumPaymentToPaymentId": 5.0, + "payoutScheme": "PPLNS", + "payoutSchemeConfig": { + "factor": 2.0 + }, + "coinbasePassword": "Crbn!234", + "keepUncles": false, + "keepTransactionFees": false + } + } + ] +} From 9ae8e10413b1729f42bfea198b1fba28a6bbfd87 Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Fri, 11 Jun 2021 10:10:15 -0700 Subject: [PATCH 08/39] Removing the config --- src/Miningcore/config.json | 120 ------------------------------------- 1 file changed, 120 deletions(-) delete mode 100644 src/Miningcore/config.json diff --git a/src/Miningcore/config.json b/src/Miningcore/config.json deleted file mode 100644 index 1667ed9582..0000000000 --- a/src/Miningcore/config.json +++ /dev/null @@ -1,120 +0,0 @@ -{ - "logging": { - "level": "info", - "enableConsoleLog": true, - "enableConsoleColors": true, - "logFile": "core.log", - "apiLogFile": "api.log", - "logBaseDirectory": "/coinfoundry/logs", - "perPoolLogFile": true - }, - "banning": { - "manager": "integrated", - "banOnJunkReceive": true, - "banOnInvalidShares": false - }, - "notifications": { - "enabled": false, - "email": { - "host": "smtp.example.com", - "port": 587, - "user": "user", - "password": "password", - "fromAddress": "info@carbonpool.org", - "fromName": "pool support" - }, - "admin": { - "enabled": false, - "emailAddress": "user@example.com", - "notifyBlockFound": true - } - }, - "persistence": { - "postgres": { - "host": "20.83.66.54", - "port": 5432, - "user": "miningcore", - "password": "Passw@rd123", - "database": "miningcore" - } - }, - - "paymentProcessing": { - "enabled": true, - "interval": 600, - "shareRecoveryFile": "recovered-shares.txt" - }, - "api": { - "enabled": true, - "listenAddress": "127.0.0.1", - "port": 4000, - "rateLimiting": { - "disabled": true, - "rules": [ - { - "Endpoint": "*", - "Period": "1s", - "Limit": 5 - } - ], - "ipWhitelist": [] - } - }, - "pools": [ - { - "id": "eth1", - "enabled": true, - "coin": "ethereum", - "address": "0x063834a361fc70a3972aee7b05225f29f287458e", - "rewardRecipients": [ - { - "type": "op", - "address": "0x063834a361fc70a3972aee7b05225f29f287458e", - "percentage": 15 - } - ], - "blockRefreshInterval": 5000, - "clientConnectionTimeout": 600, - "banning": { - "enabled": false, - "time": 600, - "invalidPercent": 50, - "checkThreshold": 50 - }, - "ports": { - "3072": { - "listenAddress": "0.0.0.0", - "difficulty": 0.1, - "varDiff": { - "minDiff": 0.05, - "maxDiff": null, - "targetTime": 15, - "retargetTime": 90, - "variancePercent": 30 - } - }, - "3073": { - "difficulty": 5000 - } - }, - "daemons": [ - { - "host": "52.173.18.110", - "port": 8545 - } - ], - "paymentProcessing": { - "enabled": true, - "minimumPayment": 0.001, - "minimumPaymentToPaymentId": 5.0, - "payoutScheme": "PPLNS", - "payoutSchemeConfig": { - "factor": 2.0 - }, - "coinbasePassword": "Crbn!234", - "keepUncles": false, - "keepTransactionFees": false - } - } - ] -} From 129b2efd77f5d5032191bee7e8d3585c205d2566 Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Fri, 11 Jun 2021 15:25:33 -0700 Subject: [PATCH 09/39] Added PPS3 payment method --- src/Miningcore/AutofacModule.cs | 4 + .../Blockchain/Ethereum/EthereumJob.cs | 3 +- src/Miningcore/Configuration/ClusterConfig.cs | 1 + .../PaymentSchemes/PPS3PaymentScheme.cs | 266 ++++++++++++++++++ src/Miningcore/Payments/PayoutManager.cs | 85 +++--- .../Persistence/Model/PaymentSchedule.cs | 31 ++ .../Postgres/Entities/PaymentSchedule.cs | 31 ++ .../Repositories/PaymentRepository.cs | 210 ++++++++------ .../Repositories/IPaymentRepository.cs | 2 + 9 files changed, 498 insertions(+), 135 deletions(-) create mode 100644 src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs create mode 100644 src/Miningcore/Persistence/Model/PaymentSchedule.cs create mode 100644 src/Miningcore/Persistence/Postgres/Entities/PaymentSchedule.cs diff --git a/src/Miningcore/AutofacModule.cs b/src/Miningcore/AutofacModule.cs index 274a6a8ddc..d09c1adba3 100644 --- a/src/Miningcore/AutofacModule.cs +++ b/src/Miningcore/AutofacModule.cs @@ -130,6 +130,10 @@ protected override void Load(ContainerBuilder builder) .Keyed(PayoutScheme.SOLO) .SingleInstance(); + builder.RegisterType() + .Keyed(PayoutScheme.PPS3) + .SingleInstance(); + ////////////////////// // Bitcoin and family diff --git a/src/Miningcore/Blockchain/Ethereum/EthereumJob.cs b/src/Miningcore/Blockchain/Ethereum/EthereumJob.cs index c30064a3c1..a375a98da9 100644 --- a/src/Miningcore/Blockchain/Ethereum/EthereumJob.cs +++ b/src/Miningcore/Blockchain/Ethereum/EthereumJob.cs @@ -87,8 +87,7 @@ private void RegisterNonce(StratumClient worker, string nonce) var shareDiff = (double) BigInteger.Divide(EthereumConstants.BigMaxValue, resultValueBig) / EthereumConstants.Pow2x32; var stratumDifficulty = context.Difficulty; var ratio = shareDiff / stratumDifficulty; - // Marking all shares as block so we can pay them ASAP - var isBlockCandidate = true; //resultValue <= blockTarget; + var isBlockCandidate = resultValue <= blockTarget; logger.Info($"Calc share for {context.Miner}|{context.UserAgent}, Val:{resultValue}, Tar:{blockTarget}, Bh:{BlockTemplate.Height}, " + $"Bc:{isBlockCandidate}"); diff --git a/src/Miningcore/Configuration/ClusterConfig.cs b/src/Miningcore/Configuration/ClusterConfig.cs index 7fa4acfb3f..94d25783d9 100644 --- a/src/Miningcore/Configuration/ClusterConfig.cs +++ b/src/Miningcore/Configuration/ClusterConfig.cs @@ -352,6 +352,7 @@ public enum PayoutScheme SOLO = 3, PPS = 4, PPBS = 5, + PPS3 = 6 } public partial class ClusterLoggingConfig diff --git a/src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs b/src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs new file mode 100644 index 0000000000..234569b462 --- /dev/null +++ b/src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs @@ -0,0 +1,266 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Linq; +using System.Net.Sockets; +using System.Threading.Tasks; +using Miningcore.Configuration; +using Miningcore.Extensions; +using Miningcore.Persistence; +using Miningcore.Persistence.Model; +using Miningcore.Persistence.Repositories; +using Miningcore.Util; +using NLog; +using Polly; +using Contract = Miningcore.Contracts.Contract; + +namespace Miningcore.Payments.PaymentSchemes +{ + /// + /// PPLNS payout scheme implementation + /// TODO THIS IS BUGGY AND INCOMPLETE! + /// + public class PPS3PaymentScheme : IPayoutScheme + { + public PPS3PaymentScheme(IConnectionFactory cf, + IShareRepository shareRepo, + IBlockRepository blockRepo, + IBalanceRepository balanceRepo) + { + Contract.RequiresNonNull(cf, nameof(cf)); + Contract.RequiresNonNull(shareRepo, nameof(shareRepo)); + Contract.RequiresNonNull(blockRepo, nameof(blockRepo)); + Contract.RequiresNonNull(balanceRepo, nameof(balanceRepo)); + + this.cf = cf; + this.shareRepo = shareRepo; + this.blockRepo = blockRepo; + this.balanceRepo = balanceRepo; + + BuildFaultHandlingPolicy(); + } + + private readonly IBalanceRepository balanceRepo; + private readonly IBlockRepository blockRepo; + private readonly IConnectionFactory cf; + private readonly IShareRepository shareRepo; + private static readonly ILogger logger = LogManager.GetLogger("PPS3 Payment", typeof(PPSPaymentScheme)); + + private const int RetryCount = 4; + private IAsyncPolicy shareReadFaultPolicy; + + private class Config + { + public decimal Factor { get; set; } + } + + #region IPayoutScheme + + public async Task UpdateBalancesAsync(IDbConnection con, IDbTransaction tx, PoolConfig poolConfig, + IPayoutHandler payoutHandler, Block block, decimal blockReward) + { + var payoutConfig = poolConfig.PaymentProcessing.PayoutSchemeConfig; + + // PPLNS window (see https://bitcointalk.org/index.php?topic=39832) + var window = payoutConfig?.ToObject()?.Factor ?? 2.0m; + + // calculate rewards + var shares = new Dictionary(); + var rewards = new Dictionary(); + var paidUntil = DateTime.Now; + var shareCutOffDate = await CalculateRewards(poolConfig, block, blockReward, shares, rewards, paidUntil); + + // update balances + foreach(var address in rewards.Keys) + { + //TODO: Paying flat 3 cents untll we finalize block calc + var amount = 0.03m;//rewards[address]; + + if(amount <= 0) continue; + logger.Info(() => $"Adding {payoutHandler.FormatAmount(amount)} to balance of {address} for {FormatUtil.FormatQuantity(shares[address])} ({shares[address]}) shares for block {block?.BlockHeight}"); + await balanceRepo.AddAmountAsync(con, tx, poolConfig.Id, address, amount, $"Reward for {FormatUtil.FormatQuantity(shares[address])} shares for block {block?.BlockHeight}"); + } + + // delete discarded shares + if(shareCutOffDate.HasValue) + { + var cutOffCount = await shareRepo.CountSharesBeforeCreatedAsync(con, tx, poolConfig.Id, shareCutOffDate.Value); + + if(cutOffCount > 0) + { + await LogDiscardedShares(poolConfig, block, shareCutOffDate.Value); + logger.Info(() => $"Deleting {cutOffCount} discarded shares before {shareCutOffDate.Value:O}"); + await shareRepo.DeleteSharesBeforeCreatedAsync(con, tx, poolConfig.Id, shareCutOffDate.Value); + } + } + + // diagnostics + var totalShareCount = shares.Values.ToList().Sum(x => new decimal(x)); + var totalRewards = rewards.Values.ToList().Sum(x => x); + + if(totalRewards > 0) + logger.Info(() => $"{FormatUtil.FormatQuantity((double) totalShareCount)} ({Math.Round(totalShareCount, 2)}) shares contributed to a total payout of {payoutHandler.FormatAmount(totalRewards)} ({totalRewards / blockReward * 100:0.00}% of block reward) to {rewards.Keys.Count} addresses"); + + return; + } + + #endregion // IPayoutScheme + + private async Task LogDiscardedShares(PoolConfig poolConfig, Block block, DateTime value) + { + var before = value; + var pageSize = 50000; + var currentPage = 0; + var shares = new Dictionary(); + + while(true) + { + logger.Info(() => $"Fetching page {currentPage} of discarded shares for pool {poolConfig.Id}, block {block?.BlockHeight}"); + + var page = await shareReadFaultPolicy.ExecuteAsync(() => + cf.Run(con => shareRepo.ReadSharesBeforeCreatedAsync(con, poolConfig.Id, before, false, pageSize))); + + currentPage++; + + foreach(var share in page) + { + // build address + var address = share.Miner; + if(!string.IsNullOrEmpty(share.Miner)) // TODO is share.Miner the walletID? + address += PayoutConstants.PayoutInfoSeperator + share.Miner; + + // record attributed shares for diagnostic purposes + if(!shares.ContainsKey(address)) + shares[address] = share.Difficulty; + else + shares[address] += share.Difficulty; + } + + if(page.Length < pageSize) + break; + + before = page[page.Length - 1].Created; + } + + if(shares.Keys.Count > 0) + { + // sort addresses by shares + var addressesByShares = shares.Keys.OrderByDescending(x => shares[x]); + + logger.Info(() => $"{FormatUtil.FormatQuantity(shares.Values.Sum())} ({shares.Values.Sum()}) total discarded shares, block {block?.BlockHeight}"); + + foreach(var address in addressesByShares) + logger.Info(() => $"{address} = {FormatUtil.FormatQuantity(shares[address])} ({shares[address]}) discarded shares, block {block?.BlockHeight}"); + } + } + + private async Task CalculateRewards(PoolConfig poolConfig, Block block, decimal blockReward, + IDictionary shares, IDictionary rewards, DateTime paidUntil) + { + var done = false; + var before = paidUntil; + var inclusive = true; + var pageSize = 50000; + var currentPage = 0; + var accumulatedScore = 0.0m; + var blockRewardRemaining = blockReward; + DateTime? shareCutOffDate = null; + Dictionary scores = new Dictionary(); + + while(!done) + { + logger.Info(() => $"Fetching page {currentPage} of shares for pool {poolConfig.Id}, block {block?.BlockHeight}"); + + var page = await shareReadFaultPolicy.ExecuteAsync(() => + cf.Run(con => shareRepo.ReadSharesBeforeCreatedAsync(con, poolConfig.Id, before, inclusive, pageSize))); + + inclusive = false; + currentPage++; + + foreach(var share in page) + { + var address = share.Miner; + + // record attributed shares for diagnostic purposes + if(!shares.ContainsKey(address)) + shares[address] = share.Difficulty; + else + shares[address] += share.Difficulty; + + // determine a share's overall score + var score = (decimal) (share.Difficulty / share.NetworkDifficulty); + //var score = (decimal)(share.Difficulty / Blockchain.Ethereum.EthereumConstants.ScoreFactor); + + if(!scores.ContainsKey(address)) + scores[address] = score; + else + scores[address] += score; + accumulatedScore += score; + + // set the cutoff date to clean up old shares after a successful payout + if(shareCutOffDate == null || share.Created > shareCutOffDate) + shareCutOffDate = share.Created; + } + + if(accumulatedScore > 0) + { + var rewardPerScorePoint = blockReward / accumulatedScore; + + // build rewards for all addresses that contributed to the round + foreach(var address in scores.Select(x => x.Key).Distinct()) + { + // loop all scores for the current address + foreach(var score in scores.Where(x => x.Key == address)) + { + var reward = score.Value * rewardPerScorePoint; + if(reward > 0) + { + // accumulate miner reward + if(!rewards.ContainsKey(address)) + rewards[address] = reward; + else + rewards[address] += reward; + } + //TODO: Disabling until we finalize block calculation + //blockRewardRemaining -= reward; + } + } + } + + if(page.Length < pageSize) + { + done = true; + break; + } + + before = page[page.Length - 1].Created; + done = page.Length <= 0; + } + + // this should never happen + if(blockRewardRemaining <= 0 && !done) + throw new OverflowException("blockRewardRemaining < 0"); + + logger.Info(() => $"Balance-calculation for pool {poolConfig.Id}, block {block?.BlockHeight} completed with accumulated score {accumulatedScore:0.####} ({accumulatedScore * 100:0.#}%)"); + + return shareCutOffDate; + } + + private void BuildFaultHandlingPolicy() + { + var retry = Policy + .Handle() + .Or() + .Or() + .RetryAsync(RetryCount, OnPolicyRetry); + + shareReadFaultPolicy = retry; + } + + private static void OnPolicyRetry(Exception ex, int retry, object context) + { + logger.Warn(() => $"Retry {retry} due to {ex.Source}: {ex.GetType().Name} ({ex.Message})"); + } + } +} diff --git a/src/Miningcore/Payments/PayoutManager.cs b/src/Miningcore/Payments/PayoutManager.cs index 9abdcdec70..7448868b7d 100644 --- a/src/Miningcore/Payments/PayoutManager.cs +++ b/src/Miningcore/Payments/PayoutManager.cs @@ -72,53 +72,53 @@ public void Start() { //try //{ - //await ProcessPoolPaymentsAsync(); - foreach(var pool in clusterConfig.Pools.Where(x => x.Enabled && x.PaymentProcessing.Enabled)) + //await ProcessPoolPaymentsAsync(); + foreach(var pool in clusterConfig.Pools.Where(x => x.Enabled && x.PaymentProcessing.Enabled)) + { + logger.Info(() => $"Processing payments for pool [{pool.Id}]"); + + try { - logger.Info(() => $"Processing payments for pool [{pool.Id}]"); + var family = HandleFamilyOverride(pool.Template.Family, pool); - try - { - var family = HandleFamilyOverride(pool.Template.Family, pool); + // resolve payout handler + var handlerImpl = ctx.Resolve>>>() + .First(x => x.Value.Metadata.SupportedFamilies.Contains(family)).Value; - // resolve payout handler - var handlerImpl = ctx.Resolve>>>() - .First(x => x.Value.Metadata.SupportedFamilies.Contains(family)).Value; + var handler = handlerImpl.Value; + await handler.ConfigureAsync(clusterConfig, pool); - var handler = handlerImpl.Value; - await handler.ConfigureAsync(clusterConfig, pool); + // resolve payout scheme + var scheme = ctx.ResolveKeyed(pool.PaymentProcessing.PayoutScheme); - // resolve payout scheme - var scheme = ctx.ResolveKeyed(pool.PaymentProcessing.PayoutScheme); + await UpdatePoolBalancesAsync(pool, handler, scheme); + await PayoutPoolBalancesAsync(pool, handler); + } - await UpdatePoolBalancesAsync(pool, handler, scheme); - await PayoutPoolBalancesAsync(pool, handler); - } + catch(InvalidOperationException ex) + { + logger.Error(ex.InnerException ?? ex, () => $"[{pool.Id}] Payment processing failed"); + } - catch(InvalidOperationException ex) + catch(AggregateException ex) + { + switch(ex.InnerException) { - logger.Error(ex.InnerException ?? ex, () => $"[{pool.Id}] Payment processing failed"); - } + case HttpRequestException httpEx: + logger.Error(() => $"[{pool.Id}] Payment processing failed: {httpEx.Message}"); + break; - catch(AggregateException ex) - { - switch(ex.InnerException) - { - case HttpRequestException httpEx: - logger.Error(() => $"[{pool.Id}] Payment processing failed: {httpEx.Message}"); - break; - - default: - logger.Error(ex.InnerException, () => $"[{pool.Id}] Payment processing failed"); - break; - } + default: + logger.Error(ex.InnerException, () => $"[{pool.Id}] Payment processing failed"); + break; } + } - catch(Exception ex) - { - logger.Error(ex, () => $"[{pool.Id}] Payment processing failed"); - } + catch(Exception ex) + { + logger.Error(ex, () => $"[{pool.Id}] Payment processing failed"); } + } //} //catch(Exception ex) @@ -131,7 +131,7 @@ public void Start() }); } - + private static CoinFamily HandleFamilyOverride(CoinFamily family, PoolConfig pool) { switch(family) @@ -197,9 +197,14 @@ await cf.RunTx(async (con, tx) => }); } } - else - logger.Info(() => $"No updated blocks for pool {pool.Id}"); + { + logger.Info(() => $"No updated blocks for pool {pool.Id} but still payment processed"); + await cf.RunTx(async (con, tx) => + { + await scheme.UpdateBalancesAsync(con, tx, pool, handler, null, 0); + }); + } } private async Task PayoutPoolBalancesAsync(PoolConfig pool, IPayoutHandler handler) @@ -267,7 +272,7 @@ internal void Configure(ClusterConfig clusterConfig) interval = TimeSpan.FromSeconds(clusterConfig.PaymentProcessing.Interval > 0 ? clusterConfig.PaymentProcessing.Interval : 600); } - + public void Stop() { @@ -278,6 +283,6 @@ public void Stop() logger.Info(() => "Payment Service Stopped"); } - + } } diff --git a/src/Miningcore/Persistence/Model/PaymentSchedule.cs b/src/Miningcore/Persistence/Model/PaymentSchedule.cs new file mode 100644 index 0000000000..0e8e9c38c8 --- /dev/null +++ b/src/Miningcore/Persistence/Model/PaymentSchedule.cs @@ -0,0 +1,31 @@ +/* +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. +*/ + +using System; + +namespace Miningcore.Persistence.Model +{ + public class PaymentSchedule + { + public string PoolId { get; set; } + public string Miner { get; set; } + public DateTime PaidUntil { get; set; } + } +} diff --git a/src/Miningcore/Persistence/Postgres/Entities/PaymentSchedule.cs b/src/Miningcore/Persistence/Postgres/Entities/PaymentSchedule.cs new file mode 100644 index 0000000000..c867ba4a15 --- /dev/null +++ b/src/Miningcore/Persistence/Postgres/Entities/PaymentSchedule.cs @@ -0,0 +1,31 @@ +/* +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. +*/ + +using System; + +namespace Miningcore.Persistence.Postgres.Entities +{ + public class PaymentSchedule + { + public string PoolId { get; set; } + public string Miner { get; set; } + public DateTime PaidUntil { get; set; } + } +} diff --git a/src/Miningcore/Persistence/Postgres/Repositories/PaymentRepository.cs b/src/Miningcore/Persistence/Postgres/Repositories/PaymentRepository.cs index 43b9b8fe1f..ef82dc3947 100644 --- a/src/Miningcore/Persistence/Postgres/Repositories/PaymentRepository.cs +++ b/src/Miningcore/Persistence/Postgres/Repositories/PaymentRepository.cs @@ -1,98 +1,122 @@ -/* -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. -*/ - -using System.Data; -using System.Linq; +/* +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. +*/ + +using System.Data; +using System.Linq; using System.Threading.Tasks; -using AutoMapper; -using Dapper; -using Miningcore.Extensions; -using Miningcore.Persistence.Model; -using Miningcore.Persistence.Model.Projections; -using Miningcore.Persistence.Repositories; -using NLog; - -namespace Miningcore.Persistence.Postgres.Repositories -{ - public class PaymentRepository : IPaymentRepository - { - public PaymentRepository(IMapper mapper) - { - this.mapper = mapper; +using AutoMapper; +using Dapper; +using Miningcore.Extensions; +using Miningcore.Persistence.Model; +using Miningcore.Persistence.Model.Projections; +using Miningcore.Persistence.Repositories; +using NLog; + +namespace Miningcore.Persistence.Postgres.Repositories +{ + public class PaymentRepository : IPaymentRepository + { + public PaymentRepository(IMapper mapper) + { + this.mapper = mapper; + } + + private readonly IMapper mapper; + private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); + + public async Task InsertAsync(IDbConnection con, IDbTransaction tx, Payment payment) + { + logger.LogInvoke(); + + var mapped = mapper.Map(payment); + + const string query = "INSERT INTO payments(poolid, coin, address, amount, transactionconfirmationdata, created) " + + "VALUES(@poolid, @coin, @address, @amount, @transactionconfirmationdata, @created)"; + + await con.ExecuteAsync(query, mapped, tx); + } + + public async Task PagePaymentsAsync(IDbConnection con, string poolId, string address, int page, int pageSize) + { + logger.LogInvoke(new[] { poolId }); + + var query = "SELECT * FROM payments WHERE poolid = @poolid "; + + if(!string.IsNullOrEmpty(address)) + query += " AND address = @address "; + + query += "ORDER BY created DESC OFFSET @offset FETCH NEXT (@pageSize) ROWS ONLY"; + + return (await con.QueryAsync(query, new { poolId, address, offset = page * pageSize, pageSize })) + .Select(mapper.Map) + .ToArray(); + } + + public async Task PageBalanceChangesAsync(IDbConnection con, string poolId, string address, int page, int pageSize) + { + logger.LogInvoke(new[] { poolId }); + + const string query = "SELECT * FROM balance_changes WHERE poolid = @poolid " + + "AND address = @address " + + "ORDER BY created DESC OFFSET @offset FETCH NEXT (@pageSize) ROWS ONLY"; + + return (await con.QueryAsync(query, new { poolId, address, offset = page * pageSize, pageSize })) + .Select(mapper.Map) + .ToArray(); + } + + public async Task PageMinerPaymentsByDayAsync(IDbConnection con, string poolId, string address, int page, int pageSize) + { + logger.LogInvoke(new[] { poolId }); + + const string query = "SELECT SUM(amount) AS amount, date_trunc('day', created) AS date FROM payments WHERE poolid = @poolid " + + "AND address = @address " + + "GROUP BY date " + + "ORDER BY date DESC OFFSET @offset FETCH NEXT (@pageSize) ROWS ONLY"; + + return (await con.QueryAsync(query, new { poolId, address, offset = page * pageSize, pageSize })) + .ToArray(); } - private readonly IMapper mapper; - private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); - - public async Task InsertAsync(IDbConnection con, IDbTransaction tx, Payment payment) - { + public async Task GetPaymentScheduleAsync(IDbConnection con, string poolId, string miner) + { logger.LogInvoke(); - var mapped = mapper.Map(payment); - - const string query = "INSERT INTO payments(poolid, coin, address, amount, transactionconfirmationdata, created) " + - "VALUES(@poolid, @coin, @address, @amount, @transactionconfirmationdata, @created)"; - - await con.ExecuteAsync(query, mapped, tx); - } - - public async Task PagePaymentsAsync(IDbConnection con, string poolId, string address, int page, int pageSize) - { - logger.LogInvoke(new[] { poolId }); - - var query = "SELECT * FROM payments WHERE poolid = @poolid "; - - if(!string.IsNullOrEmpty(address)) - query += " AND address = @address "; - - query += "ORDER BY created DESC OFFSET @offset FETCH NEXT (@pageSize) ROWS ONLY"; - - return (await con.QueryAsync(query, new { poolId, address, offset = page * pageSize, pageSize })) - .Select(mapper.Map) - .ToArray(); - } - - public async Task PageBalanceChangesAsync(IDbConnection con, string poolId, string address, int page, int pageSize) - { - logger.LogInvoke(new[] { poolId }); - - const string query = "SELECT * FROM balance_changes WHERE poolid = @poolid " + - "AND address = @address " + - "ORDER BY created DESC OFFSET @offset FETCH NEXT (@pageSize) ROWS ONLY"; - - return (await con.QueryAsync(query, new { poolId, address, offset = page * pageSize, pageSize })) - .Select(mapper.Map) - .ToArray(); - } - - public async Task PageMinerPaymentsByDayAsync(IDbConnection con, string poolId, string address, int page, int pageSize) - { - logger.LogInvoke(new[] { poolId }); - - const string query = "SELECT SUM(amount) AS amount, date_trunc('day', created) AS date FROM payments WHERE poolid = @poolid " + - "AND address = @address " + - "GROUP BY date " + - "ORDER BY date DESC OFFSET @offset FETCH NEXT (@pageSize) ROWS ONLY"; - - return (await con.QueryAsync(query, new { poolId, address, offset = page * pageSize, pageSize })) - .ToArray(); - } - } -} + const string query = "SELECT poolid,miner,paiduntil FROM payment_schedule WHERE poolid = @poolId AND miner = @miner"; + + return await con.QuerySingleOrDefaultAsync(query, new { poolId, miner }); + } + + public async Task UpdatePaymentScheduleAsync(IDbConnection con, IDbTransaction tx, PaymentSchedule paymentSchedule) + { + logger.LogInvoke(); + + var mapped = mapper.Map(paymentSchedule); + + const string query = @"INSERT INTO payment_schedule(poolid, miner, paiduntil) + VALUES(@poolid, @miner, @paiduntil) + ON CONFLICT (poolid, miner) + DO UPDATE + SET paiduntil = @paiduntil;"; + + await con.ExecuteAsync(query, mapped, tx); + } + } +} diff --git a/src/Miningcore/Persistence/Repositories/IPaymentRepository.cs b/src/Miningcore/Persistence/Repositories/IPaymentRepository.cs index 40e7f2adf3..0435a9869f 100644 --- a/src/Miningcore/Persistence/Repositories/IPaymentRepository.cs +++ b/src/Miningcore/Persistence/Repositories/IPaymentRepository.cs @@ -32,5 +32,7 @@ public interface IPaymentRepository Task PagePaymentsAsync(IDbConnection con, string poolId, string address, int page, int pageSize); Task PageBalanceChangesAsync(IDbConnection con, string poolId, string address, int page, int pageSize); Task PageMinerPaymentsByDayAsync(IDbConnection con, string poolId, string address, int page, int pageSize); + Task GetPaymentScheduleAsync(IDbConnection con, string poolId, string miner); + Task UpdatePaymentScheduleAsync(IDbConnection con, IDbTransaction tx, PaymentSchedule paymentSchedule); } } From e721d6d6b6c0c39f317ab64fedfa73a595141a29 Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Fri, 11 Jun 2021 18:35:10 -0700 Subject: [PATCH 10/39] Adjusted PPS3 calc to get more rewards --- .../PaymentSchemes/PPS3PaymentScheme.cs | 67 ++++++++++--------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs b/src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs index 234569b462..15996d70bc 100644 --- a/src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs +++ b/src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs @@ -53,6 +53,7 @@ public PPS3PaymentScheme(IConnectionFactory cf, private class Config { public decimal Factor { get; set; } + public decimal FixedReward { get; set; } } #region IPayoutScheme @@ -60,22 +61,22 @@ private class Config public async Task UpdateBalancesAsync(IDbConnection con, IDbTransaction tx, PoolConfig poolConfig, IPayoutHandler payoutHandler, Block block, decimal blockReward) { - var payoutConfig = poolConfig.PaymentProcessing.PayoutSchemeConfig; + var payoutConfig = poolConfig.PaymentProcessing.PayoutSchemeConfig?.ToObject(); // PPLNS window (see https://bitcointalk.org/index.php?topic=39832) - var window = payoutConfig?.ToObject()?.Factor ?? 2.0m; + var window = payoutConfig?.Factor ?? 2.0m; + var fixedReward = payoutConfig?.FixedReward ?? 0.03m; // calculate rewards var shares = new Dictionary(); var rewards = new Dictionary(); var paidUntil = DateTime.Now; - var shareCutOffDate = await CalculateRewards(poolConfig, block, blockReward, shares, rewards, paidUntil); + var shareCutOffDate = await CalculateRewards(poolConfig, block, blockReward, shares, rewards, paidUntil, fixedReward); // update balances foreach(var address in rewards.Keys) { - //TODO: Paying flat 3 cents untll we finalize block calc - var amount = 0.03m;//rewards[address]; + var amount = rewards[address]; if(amount <= 0) continue; logger.Info(() => $"Adding {payoutHandler.FormatAmount(amount)} to balance of {address} for {FormatUtil.FormatQuantity(shares[address])} ({shares[address]}) shares for block {block?.BlockHeight}"); @@ -156,7 +157,7 @@ private async Task LogDiscardedShares(PoolConfig poolConfig, Block block, DateTi } private async Task CalculateRewards(PoolConfig poolConfig, Block block, decimal blockReward, - IDictionary shares, IDictionary rewards, DateTime paidUntil) + IDictionary shares, IDictionary rewards, DateTime paidUntil, decimal fixedReward) { var done = false; var before = paidUntil; @@ -166,7 +167,7 @@ private async Task LogDiscardedShares(PoolConfig poolConfig, Block block, DateTi var accumulatedScore = 0.0m; var blockRewardRemaining = blockReward; DateTime? shareCutOffDate = null; - Dictionary scores = new Dictionary(); + var scores = new Dictionary(); while(!done) { @@ -198,35 +199,39 @@ private async Task LogDiscardedShares(PoolConfig poolConfig, Block block, DateTi scores[address] += score; accumulatedScore += score; + //TODO: Paying flat 3 cents until block calc is finalized + rewards[address] = rewards.ContainsKey(address) ? rewards[address] + fixedReward : fixedReward; + // set the cutoff date to clean up old shares after a successful payout if(shareCutOffDate == null || share.Created > shareCutOffDate) shareCutOffDate = share.Created; } - if(accumulatedScore > 0) - { - var rewardPerScorePoint = blockReward / accumulatedScore; - - // build rewards for all addresses that contributed to the round - foreach(var address in scores.Select(x => x.Key).Distinct()) - { - // loop all scores for the current address - foreach(var score in scores.Where(x => x.Key == address)) - { - var reward = score.Value * rewardPerScorePoint; - if(reward > 0) - { - // accumulate miner reward - if(!rewards.ContainsKey(address)) - rewards[address] = reward; - else - rewards[address] += reward; - } - //TODO: Disabling until we finalize block calculation - //blockRewardRemaining -= reward; - } - } - } + // TODO: Disabling score based reward until block calc is finalized + //if(accumulatedScore > 0) + //{ + // var rewardPerScorePoint = blockReward / accumulatedScore; + + // // build rewards for all addresses that contributed to the round + // foreach(var address in scores.Select(x => x.Key).Distinct()) + // { + // // loop all scores for the current address + // foreach(var score in scores.Where(x => x.Key == address)) + // { + // var reward = score.Value * rewardPerScorePoint; + // if(reward > 0) + // { + // // accumulate miner reward + // if(!rewards.ContainsKey(address)) + // rewards[address] = reward; + // else + // rewards[address] += reward; + // } + + // blockRewardRemaining -= reward; + // } + // } + //} if(page.Length < pageSize) { From 75c5d7d8a9256edb02d8ced0640e655ff9ef0926 Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Fri, 11 Jun 2021 18:55:11 -0700 Subject: [PATCH 11/39] Added log for total rewards --- src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs b/src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs index 15996d70bc..521cfcdb8f 100644 --- a/src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs +++ b/src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs @@ -165,6 +165,7 @@ private async Task LogDiscardedShares(PoolConfig poolConfig, Block block, DateTi var pageSize = 50000; var currentPage = 0; var accumulatedScore = 0.0m; + var accumulatedRewards = 0.0m; var blockRewardRemaining = blockReward; DateTime? shareCutOffDate = null; var scores = new Dictionary(); @@ -201,6 +202,7 @@ private async Task LogDiscardedShares(PoolConfig poolConfig, Block block, DateTi //TODO: Paying flat 3 cents until block calc is finalized rewards[address] = rewards.ContainsKey(address) ? rewards[address] + fixedReward : fixedReward; + accumulatedRewards += rewards[address]; // set the cutoff date to clean up old shares after a successful payout if(shareCutOffDate == null || share.Created > shareCutOffDate) @@ -247,7 +249,8 @@ private async Task LogDiscardedShares(PoolConfig poolConfig, Block block, DateTi if(blockRewardRemaining <= 0 && !done) throw new OverflowException("blockRewardRemaining < 0"); - logger.Info(() => $"Balance-calculation for pool {poolConfig.Id}, block {block?.BlockHeight} completed with accumulated score {accumulatedScore:0.####} ({accumulatedScore * 100:0.#}%)"); + //logger.Info(() => $"Balance-calculation for pool {poolConfig.Id}, block {block?.BlockHeight} completed with accumulated score {accumulatedScore:0.####} ({accumulatedScore * 100:0.#}%)"); + logger.Info(() => $"Balance-calculation for pool {poolConfig.Id} completed with accumulated rewards of {accumulatedRewards:0.####}"); return shareCutOffDate; } From 43a748c7a910cf481c03dddbfbb6871cb4978f43 Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Fri, 11 Jun 2021 22:59:11 -0700 Subject: [PATCH 12/39] Added logs for analysis --- src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs b/src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs index 521cfcdb8f..cf97b5a68b 100644 --- a/src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs +++ b/src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs @@ -203,11 +203,13 @@ private async Task LogDiscardedShares(PoolConfig poolConfig, Block block, DateTi //TODO: Paying flat 3 cents until block calc is finalized rewards[address] = rewards.ContainsKey(address) ? rewards[address] + fixedReward : fixedReward; accumulatedRewards += rewards[address]; + logger.Info(() => $"{rewards[address]} rewards for {address} with total {accumulatedRewards:0.####}"); // set the cutoff date to clean up old shares after a successful payout if(shareCutOffDate == null || share.Created > shareCutOffDate) shareCutOffDate = share.Created; } + logger.Info(() => $"{accumulatedRewards:0.####} balance accumulated from {page.Length} shares for pool {poolConfig.Id} and page {currentPage}"); // TODO: Disabling score based reward until block calc is finalized //if(accumulatedScore > 0) @@ -250,7 +252,7 @@ private async Task LogDiscardedShares(PoolConfig poolConfig, Block block, DateTi throw new OverflowException("blockRewardRemaining < 0"); //logger.Info(() => $"Balance-calculation for pool {poolConfig.Id}, block {block?.BlockHeight} completed with accumulated score {accumulatedScore:0.####} ({accumulatedScore * 100:0.#}%)"); - logger.Info(() => $"Balance-calculation for pool {poolConfig.Id} completed with accumulated rewards of {accumulatedRewards:0.####}"); + logger.Info(() => $"Balance-calculation for pool {poolConfig.Id} completed with accumulated rewards {accumulatedRewards:0.####}"); return shareCutOffDate; } From d90eff54d517d9da6541d6df0e19550469be60da Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Sat, 12 Jun 2021 09:34:56 -0700 Subject: [PATCH 13/39] Removed debug logs --- .../Payments/PaymentSchemes/PPS3PaymentScheme.cs | 10 ++++------ src/Miningcore/Payments/PayoutManager.cs | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs b/src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs index cf97b5a68b..bffea6049c 100644 --- a/src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs +++ b/src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs @@ -178,8 +178,7 @@ private async Task LogDiscardedShares(PoolConfig poolConfig, Block block, DateTi cf.Run(con => shareRepo.ReadSharesBeforeCreatedAsync(con, poolConfig.Id, before, inclusive, pageSize))); inclusive = false; - currentPage++; - + foreach(var share in page) { var address = share.Miner; @@ -203,14 +202,12 @@ private async Task LogDiscardedShares(PoolConfig poolConfig, Block block, DateTi //TODO: Paying flat 3 cents until block calc is finalized rewards[address] = rewards.ContainsKey(address) ? rewards[address] + fixedReward : fixedReward; accumulatedRewards += rewards[address]; - logger.Info(() => $"{rewards[address]} rewards for {address} with total {accumulatedRewards:0.####}"); // set the cutoff date to clean up old shares after a successful payout if(shareCutOffDate == null || share.Created > shareCutOffDate) shareCutOffDate = share.Created; } - logger.Info(() => $"{accumulatedRewards:0.####} balance accumulated from {page.Length} shares for pool {poolConfig.Id} and page {currentPage}"); - + // TODO: Disabling score based reward until block calc is finalized //if(accumulatedScore > 0) //{ @@ -237,6 +234,7 @@ private async Task LogDiscardedShares(PoolConfig poolConfig, Block block, DateTi // } //} + currentPage++; if(page.Length < pageSize) { done = true; @@ -252,7 +250,7 @@ private async Task LogDiscardedShares(PoolConfig poolConfig, Block block, DateTi throw new OverflowException("blockRewardRemaining < 0"); //logger.Info(() => $"Balance-calculation for pool {poolConfig.Id}, block {block?.BlockHeight} completed with accumulated score {accumulatedScore:0.####} ({accumulatedScore * 100:0.#}%)"); - logger.Info(() => $"Balance-calculation for pool {poolConfig.Id} completed with accumulated rewards {accumulatedRewards:0.####}"); + logger.Info(() => $"Balance-calculation for pool {poolConfig.Id} completed with accumulated rewards of ${accumulatedRewards:0.####}"); return shareCutOffDate; } diff --git a/src/Miningcore/Payments/PayoutManager.cs b/src/Miningcore/Payments/PayoutManager.cs index 7448868b7d..aee6980972 100644 --- a/src/Miningcore/Payments/PayoutManager.cs +++ b/src/Miningcore/Payments/PayoutManager.cs @@ -202,7 +202,7 @@ await cf.RunTx(async (con, tx) => logger.Info(() => $"No updated blocks for pool {pool.Id} but still payment processed"); await cf.RunTx(async (con, tx) => { - await scheme.UpdateBalancesAsync(con, tx, pool, handler, null, 0); + await scheme.UpdateBalancesAsync(con, tx, pool, handler, null, 1); }); } } From 7bbad2d87df306aed1ccfd69ebd3acb23b88a99f Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Sat, 12 Jun 2021 12:09:47 -0700 Subject: [PATCH 14/39] Logged account unlock errors --- src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs b/src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs index c3e928d7e5..3daa61bb67 100644 --- a/src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs +++ b/src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs @@ -462,6 +462,10 @@ private async Task PayoutAsync(Balance balance) null }); + if(unlockResponse.Error != null || unlockResponse.Response == null || (bool) unlockResponse.Response == false) + { + logger.Warn(() => $"[{LogCategory}] Account Unlock failed. Code={unlockResponse.Error.Code}, Data={unlockResponse.Error.Data}, Msg={unlockResponse.Error.Message}"); + } //if(unlockResponse.Error != null || unlockResponse.Response == null || (bool) unlockResponse.Response == false) // throw new Exception("Unable to unlock coinbase account for sending transaction"); } @@ -469,7 +473,7 @@ private async Task PayoutAsync(Balance balance) { throw new Exception("Unable to unlock coinbase account for sending transaction"); } - + // send transaction logger.Info(() => $"[{LogCategory}] Sending {FormatAmount(balance.Amount)} {balance.Amount} to {balance.Address}"); From b43f9d1bda4fdfa68f1e8a01fac4cf4433d6e4b9 Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Sat, 12 Jun 2021 17:35:51 -0700 Subject: [PATCH 15/39] Corrected the log for accumulated rewards --- src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs b/src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs index bffea6049c..ad2d01c194 100644 --- a/src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs +++ b/src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs @@ -250,7 +250,7 @@ private async Task LogDiscardedShares(PoolConfig poolConfig, Block block, DateTi throw new OverflowException("blockRewardRemaining < 0"); //logger.Info(() => $"Balance-calculation for pool {poolConfig.Id}, block {block?.BlockHeight} completed with accumulated score {accumulatedScore:0.####} ({accumulatedScore * 100:0.#}%)"); - logger.Info(() => $"Balance-calculation for pool {poolConfig.Id} completed with accumulated rewards of ${accumulatedRewards:0.####}"); + logger.Info(() => $"Balance-calculation for pool {poolConfig.Id} completed with accumulated rewards of ${accumulatedRewards:0.######}"); return shareCutOffDate; } From 52125f947112f0f22bb4b2e08b82b04165bb4bd5 Mon Sep 17 00:00:00 2001 From: joel_michel Date: Sun, 13 Jun 2021 21:29:06 -0400 Subject: [PATCH 16/39] Fix for no block under PPLNS --- src/Miningcore/Payments/PaymentSchemes/PPLNSPaymentScheme.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Miningcore/Payments/PaymentSchemes/PPLNSPaymentScheme.cs b/src/Miningcore/Payments/PaymentSchemes/PPLNSPaymentScheme.cs index de65cc2479..15bd2183d5 100644 --- a/src/Miningcore/Payments/PaymentSchemes/PPLNSPaymentScheme.cs +++ b/src/Miningcore/Payments/PaymentSchemes/PPLNSPaymentScheme.cs @@ -177,6 +177,11 @@ private async Task LogDiscardedSharesAsync(PoolConfig poolConfig, Block block, D private async Task CalculateRewardsAsync(PoolConfig poolConfig, decimal window, Block block, decimal blockReward, Dictionary shares, Dictionary rewards) { + if(null == block) + { + logger.Debug(() => $"No Block found so nothing to do under PPLNS."); + return null; + } var done = false; var before = block.Created; var inclusive = true; From caf8dcbc5ca3d0f52d67c8f2bfd2e057a35fe1a5 Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Mon, 14 Jun 2021 11:30:09 -0700 Subject: [PATCH 17/39] Updated to .net 5 --- docker-compose.yml | 10 ++++++++ src/Miningcore.Tests/Miningcore.Tests.csproj | 2 +- src/Miningcore.sln | 1 + src/Miningcore/Dockerfile | 24 +++++++++++++++++++ src/Miningcore/Miningcore.csproj | 5 ++-- src/Miningcore/Properties/launchSettings.json | 3 +++ 6 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 docker-compose.yml create mode 100644 src/Miningcore/Dockerfile diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000..a522a2f7c2 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,10 @@ +version: '3.4' + +services: + miningcore: + image: miningcore + ports: + - "3072:3072" + build: + context: . + dockerfile: src/Miningcore/Dockerfile diff --git a/src/Miningcore.Tests/Miningcore.Tests.csproj b/src/Miningcore.Tests/Miningcore.Tests.csproj index e110c4437a..bff17a711b 100644 --- a/src/Miningcore.Tests/Miningcore.Tests.csproj +++ b/src/Miningcore.Tests/Miningcore.Tests.csproj @@ -10,7 +10,7 @@ - netcoreapp3.1 + net5.0 false AnyCPU Miningcore.Tests diff --git a/src/Miningcore.sln b/src/Miningcore.sln index e34a73fd76..29a6e13ecb 100644 --- a/src/Miningcore.sln +++ b/src/Miningcore.sln @@ -10,6 +10,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{71672AAD-51F8-49EC-9EFD-E504D65A765A}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig + ..\docker-compose.yml = ..\docker-compose.yml EndProjectSection EndProject Global diff --git a/src/Miningcore/Dockerfile b/src/Miningcore/Dockerfile new file mode 100644 index 0000000000..c595e19aac --- /dev/null +++ b/src/Miningcore/Dockerfile @@ -0,0 +1,24 @@ +#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. + +FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build +WORKDIR /src +COPY ["src/Miningcore/Miningcore.csproj", "src/Miningcore/"] +RUN dotnet restore "src/Miningcore/Miningcore.csproj" +RUN apt-get update -y \ + && apt-get install -y --no-install-recommends cmake build-essential libssl-dev pkg-config libboost-all-dev libsodium-dev libzmq5 +COPY . . +WORKDIR "/src/src/Miningcore" +RUN dotnet build "Miningcore.csproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "Miningcore.csproj" -c Release -o /app/publish +COPY ["src/Miningcore/config.json", "/app/publish/"] + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +#ENTRYPOINT ["dotnet", "Miningcore.dll", "-c", "config.json"] +ENTRYPOINT ["sh"] diff --git a/src/Miningcore/Miningcore.csproj b/src/Miningcore/Miningcore.csproj index f37cabbf1d..00f3349ace 100644 --- a/src/Miningcore/Miningcore.csproj +++ b/src/Miningcore/Miningcore.csproj @@ -1,8 +1,7 @@ Exe - netcoreapp3.1 - netcoreapp2.1;netcoreapp3.1 + net5.0 Miningcore Miningcore AnyCPU @@ -26,6 +25,7 @@ Miningcore.Program Miningcore.com 2.0.0 + Linux @@ -63,6 +63,7 @@ + diff --git a/src/Miningcore/Properties/launchSettings.json b/src/Miningcore/Properties/launchSettings.json index 206d2adf0b..7845fab596 100644 --- a/src/Miningcore/Properties/launchSettings.json +++ b/src/Miningcore/Properties/launchSettings.json @@ -4,6 +4,9 @@ "commandName": "Project", "commandLineArgs": "-c ..\\..\\..\\config.json", "nativeDebugging": true + }, + "Docker": { + "commandName": "Docker" } } } \ No newline at end of file From d18c3712e97165a18c52b0027f8f6ae962e89c44 Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Tue, 15 Jun 2021 11:45:20 -0700 Subject: [PATCH 18/39] docker image support --- docker-compose.yml | 2 ++ src/Miningcore/Dockerfile | 12 +++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index a522a2f7c2..c2b36a259b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,6 +5,8 @@ services: image: miningcore ports: - "3072:3072" + - "3073:3073" + - "4000:4000" build: context: . dockerfile: src/Miningcore/Dockerfile diff --git a/src/Miningcore/Dockerfile b/src/Miningcore/Dockerfile index c595e19aac..a73d68c413 100644 --- a/src/Miningcore/Dockerfile +++ b/src/Miningcore/Dockerfile @@ -7,18 +7,20 @@ FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build WORKDIR /src COPY ["src/Miningcore/Miningcore.csproj", "src/Miningcore/"] RUN dotnet restore "src/Miningcore/Miningcore.csproj" -RUN apt-get update -y \ - && apt-get install -y --no-install-recommends cmake build-essential libssl-dev pkg-config libboost-all-dev libsodium-dev libzmq5 +RUN apt-get update -y && apt-get install -y procps sudo +#RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo +RUN apt-get update -y && \ + apt-get install -y --no-install-recommends cmake build-essential libssl-dev libsodium-dev pkg-config libboost-all-dev libzmq5 COPY . . WORKDIR "/src/src/Miningcore" RUN dotnet build "Miningcore.csproj" -c Release -o /app/build FROM build AS publish -RUN dotnet publish "Miningcore.csproj" -c Release -o /app/publish +RUN dotnet publish "Miningcore.csproj" -c Release --framework net5.0 -o /app/publish COPY ["src/Miningcore/config.json", "/app/publish/"] FROM base AS final WORKDIR /app +RUN apt-get update -y && apt-get install -y libsodium-dev COPY --from=publish /app/publish . -#ENTRYPOINT ["dotnet", "Miningcore.dll", "-c", "config.json"] -ENTRYPOINT ["sh"] +ENTRYPOINT ["dotnet", "Miningcore.dll", "-c", "config.json"] From cc1d52b5b6686625f2a874e4d73e5a83345ccead Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Tue, 15 Jun 2021 12:00:36 -0700 Subject: [PATCH 19/39] Set up CI with Azure Pipelines [skip ci] --- azure-pipelines.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 azure-pipelines.yml diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000000..7a7540f5e2 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,30 @@ +# Docker +# Build a Docker image +# https://docs.microsoft.com/azure/devops/pipelines/languages/docker + +trigger: +- develop + +resources: +- repo: self + +variables: + tag: '$(Build.BuildId)' + +stages: +- stage: Build + displayName: Build image + jobs: + - job: Build + displayName: Build + pool: + name: ado-test-vmss + steps: + - task: Docker@2 + displayName: Build an image + inputs: + command: build + dockerfile: '**/Dockerfile' + tags: | + latest + $(tag) From 97d979a1a37a748886910e872c902a9e68d62cfb Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Tue, 15 Jun 2021 12:26:58 -0700 Subject: [PATCH 20/39] Added docker installer --- azure-pipelines.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 7a7540f5e2..4a27b42ed0 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -20,6 +20,9 @@ stages: pool: name: ado-test-vmss steps: + - task: DockerInstaller@0 + inputs: + dockerVersion: '20.10.7' - task: Docker@2 displayName: Build an image inputs: From 768d7cffa513b3af0faf4ceaebf752136b158edb Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Tue, 15 Jun 2021 12:34:42 -0700 Subject: [PATCH 21/39] Adjusting dockerfile path --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4a27b42ed0..f1d68f5604 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -27,7 +27,7 @@ stages: displayName: Build an image inputs: command: build - dockerfile: '**/Dockerfile' + dockerfile: 'src/Miningcore/Dockerfile' tags: | latest $(tag) From a56b7c87f4a88ddcf8d7c8dda7f689da207e6ab7 Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Tue, 15 Jun 2021 12:40:47 -0700 Subject: [PATCH 22/39] Added docker start --- azure-pipelines.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4a27b42ed0..269a72992e 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -23,6 +23,10 @@ stages: - task: DockerInstaller@0 inputs: dockerVersion: '20.10.7' + - task: Bash@3 + inputs: + targetType: 'inline' + script: 'sudo systemctl start docker' - task: Docker@2 displayName: Build an image inputs: From 9cf2ce82a2d89f15bc0098cad5eb0af4839b8a58 Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Tue, 15 Jun 2021 14:03:00 -0700 Subject: [PATCH 23/39] Updated agent info --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 3e566a2075..c330140b22 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -18,7 +18,7 @@ stages: - job: Build displayName: Build pool: - name: ado-test-vmss + name: ubuntu-latest steps: - task: DockerInstaller@0 inputs: From c679237a2f40f9d0008fd7d07cc807f1ee9d21fa Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Tue, 15 Jun 2021 14:10:23 -0700 Subject: [PATCH 24/39] Fixed vm image name --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c330140b22..678645fbf3 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -18,7 +18,7 @@ stages: - job: Build displayName: Build pool: - name: ubuntu-latest + vmImage: ubuntu-latest steps: - task: DockerInstaller@0 inputs: From ddc616b134cf8f68bac07434d785e53822802d33 Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Tue, 15 Jun 2021 14:18:13 -0700 Subject: [PATCH 25/39] Updated build context path --- azure-pipelines.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 678645fbf3..8f9854c840 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -24,6 +24,7 @@ stages: inputs: dockerVersion: '20.10.7' - task: Bash@3 + enabled: false inputs: targetType: 'inline' script: 'sudo systemctl start docker' @@ -32,6 +33,7 @@ stages: inputs: command: build dockerfile: 'src/Miningcore/Dockerfile' + buildContext: $(Build.Repository.LocalPath) tags: | latest $(tag) From e730e4e170f23913f554c644e0cc40ec7119df1f Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Tue, 15 Jun 2021 14:47:39 -0700 Subject: [PATCH 26/39] Adding a base config without credentials --- src/Miningcore/config.json | 121 +++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 src/Miningcore/config.json diff --git a/src/Miningcore/config.json b/src/Miningcore/config.json new file mode 100644 index 0000000000..dcba65d7d1 --- /dev/null +++ b/src/Miningcore/config.json @@ -0,0 +1,121 @@ +{ + "logging": { + "level": "info", + "enableConsoleLog": true, + "enableConsoleColors": true, + "logFile": "core.log", + "apiLogFile": "api.log", + "logBaseDirectory": "/coinfoundry/logs", + "perPoolLogFile": true + }, + "banning": { + "manager": "integrated", + "banOnJunkReceive": true, + "banOnInvalidShares": false + }, + "notifications": { + "enabled": false, + "email": { + "host": "smtp.example.com", + "port": 587, + "user": "user", + "password": "password", + "fromAddress": "info@carbonpool.org", + "fromName": "pool support" + }, + "admin": { + "enabled": false, + "emailAddress": "user@example.com", + "notifyBlockFound": true + } + }, + "persistence": { + "postgres": { + "host": "db_host", + "port": 5432, + "user": "db_user", + "password": "db_password", + "database": "db_name" + } + }, + + "paymentProcessing": { + "enabled": true, + "interval": 600, + "shareRecoveryFile": "recovered-shares.txt" + }, + "api": { + "enabled": true, + "listenAddress": "127.0.0.1", + "port": 4000, + "rateLimiting": { + "disabled": true, + "rules": [ + { + "Endpoint": "*", + "Period": "1s", + "Limit": 5 + } + ], + "ipWhitelist": [] + } + }, + "pools": [ + { + "id": "eth1", + "enabled": true, + "coin": "ethereum", + "address": "wallet_address", + "rewardRecipients": [ + { + "type": "op", + "address": "wallet_address", + "percentage": 15 + } + ], + "blockRefreshInterval": 5000, + "clientConnectionTimeout": 600, + "banning": { + "enabled": false, + "time": 600, + "invalidPercent": 50, + "checkThreshold": 50 + }, + "ports": { + "3072": { + "listenAddress": "0.0.0.0", + "difficulty": 0.1, + "varDiff": { + "minDiff": 0.05, + "maxDiff": null, + "targetTime": 15, + "retargetTime": 90, + "variancePercent": 30 + } + }, + "3073": { + "difficulty": 5000 + } + }, + "daemons": [ + { + "host": "127.0.0.1", + "port": 0 + } + ], + "paymentProcessing": { + "enabled": true, + "minimumPayment": 0.001, + "minimumPaymentToPaymentId": 5.0, + "payoutScheme": "PPS3", + "payoutSchemeConfig": { + "factor": 2.0, + "fixedReward": 0.000013 + }, + "coinbasePassword": "wallet_password", + "keepUncles": false, + "keepTransactionFees": false + } + } + ] +} From edc8be8f846b437a7590f3af6b7f29591cacecc2 Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Tue, 15 Jun 2021 15:14:21 -0700 Subject: [PATCH 27/39] Added repo config --- azure-pipelines.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 8f9854c840..5692a5d9a2 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -31,9 +31,11 @@ stages: - task: Docker@2 displayName: Build an image inputs: - command: build - dockerfile: 'src/Miningcore/Dockerfile' - buildContext: $(Build.Repository.LocalPath) + containerRegistry: 'sandbox-carborepo' + repository: 'miningcore' + command: 'buildAndPush' + Dockerfile: '**/Dockerfile' + buildContext: '$(Build.Repository.LocalPath)' tags: | latest $(tag) From 83086a53b5e34c00efb1155c0f1a294d1753748a Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Tue, 15 Jun 2021 16:37:50 -0700 Subject: [PATCH 28/39] Updated registry info --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 5692a5d9a2..abae4f9062 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -31,7 +31,7 @@ stages: - task: Docker@2 displayName: Build an image inputs: - containerRegistry: 'sandbox-carborepo' + containerRegistry: 'sandbox-poolrepo' repository: 'miningcore' command: 'buildAndPush' Dockerfile: '**/Dockerfile' From 0bbf2a461a7caa79e8a25db5e0891d06b4223a65 Mon Sep 17 00:00:00 2001 From: Tuong Huynh Date: Tue, 15 Jun 2021 23:10:05 -0700 Subject: [PATCH 29/39] Add option to load comfig.json from azure app configuration service. --- src/Miningcore/Miningcore.csproj | 1 + .../PoolCore/AzureAppConfiguration.cs | 26 ++++++++++ src/Miningcore/PoolCore/PoolConfig.cs | 52 ++++++++++++++++++- src/Miningcore/PoolCore/PoolCore.cs | 14 +++-- src/Miningcore/Program.cs | 14 +++-- src/Miningcore/config.secrets | 3 ++ 6 files changed, 102 insertions(+), 8 deletions(-) create mode 100644 src/Miningcore/PoolCore/AzureAppConfiguration.cs create mode 100644 src/Miningcore/config.secrets diff --git a/src/Miningcore/Miningcore.csproj b/src/Miningcore/Miningcore.csproj index f37cabbf1d..715ed70262 100644 --- a/src/Miningcore/Miningcore.csproj +++ b/src/Miningcore/Miningcore.csproj @@ -61,6 +61,7 @@ + diff --git a/src/Miningcore/PoolCore/AzureAppConfiguration.cs b/src/Miningcore/PoolCore/AzureAppConfiguration.cs new file mode 100644 index 0000000000..1050ffd0fd --- /dev/null +++ b/src/Miningcore/PoolCore/AzureAppConfiguration.cs @@ -0,0 +1,26 @@ +using System; +using Microsoft.Extensions.Configuration; + +namespace Miningcore.PoolCore +{ + public class AzureAppConfiguration + { + public static readonly string ConfigJson = "config.json"; + public static readonly string PersistencePostgresUser = "persistence.postgres.user"; + public static readonly string PersistencePostgresPassword = "persistence.postgres.password"; + public static readonly string CoinbasePassword = "paymentProcessing.coinbasePassword"; + public static readonly string ConnectionString = "ConnectionString"; + + public static IConfigurationRoot GetAppConfig(string prefix) { + + var builder = new ConfigurationBuilder(); + builder.AddAzureAppConfiguration(options => { + options.Connect(Environment.GetEnvironmentVariable(ConnectionString)) + .TrimKeyPrefix(prefix); + }); + + return builder.Build(); + } + + } +} \ No newline at end of file diff --git a/src/Miningcore/PoolCore/PoolConfig.cs b/src/Miningcore/PoolCore/PoolConfig.cs index 917e3d33cc..9c861416f8 100644 --- a/src/Miningcore/PoolCore/PoolConfig.cs +++ b/src/Miningcore/PoolCore/PoolConfig.cs @@ -35,12 +35,60 @@ public static ClusterConfig GetConfigContent(string configFile) } - private static ClusterConfig ReadConfig(string configFile) + public static ClusterConfig GetConfigContentFromAppConfig(string prefix) + { + // Read config.json file + clusterConfig = ReadConfigFromAppConfiguration(prefix); + ValidateConfig(); + + return clusterConfig; + + } + + private static ClusterConfig ReadConfigFromAppConfiguration(string prefix) { try + { + + var config = AzureAppConfiguration.GetAppConfig(prefix); + + var serializer = JsonSerializer.Create(new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver() + }); + + JsonTextReader reader = new JsonTextReader(new StringReader(config[AzureAppConfiguration.ConfigJson])); + + clusterConfig = serializer.Deserialize(reader); + // Update dynamic pass and others config here + + clusterConfig.Persistence.Postgres.User = config[AzureAppConfiguration.PersistencePostgresUser]; + clusterConfig.Persistence.Postgres.Password = config[AzureAppConfiguration.PersistencePostgresPassword]; + foreach(var poolConfig in clusterConfig.Pools) + { + poolConfig.PaymentProcessing.Extra["coinbasePassword"] = config["pools." + poolConfig.Id + "." + AzureAppConfiguration.CoinbasePassword]; + } + + return clusterConfig; + + } + catch(JsonSerializationException ex) + { + HumanizeJsonParseException(ex); + throw; + } + + catch(JsonException ex) { - Console.WriteLine($"Using configuration file {configFile}\n"); + Console.WriteLine($"Error: {ex.Message}"); + throw; + } + } + private static ClusterConfig ReadConfig(string configFile) + { + try + { var serializer = JsonSerializer.Create(new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() diff --git a/src/Miningcore/PoolCore/PoolCore.cs b/src/Miningcore/PoolCore/PoolCore.cs index cc494fd5c4..baa96f0383 100644 --- a/src/Miningcore/PoolCore/PoolCore.cs +++ b/src/Miningcore/PoolCore/PoolCore.cs @@ -81,7 +81,7 @@ internal class Pool internal static ClusterConfig clusterConfig; internal static IContainer container; - internal static void StartMiningCorePool(string configFile) + internal static void StartMiningCorePool(string configFile, string appConfigPrefix) { try { @@ -106,8 +106,16 @@ internal static void StartMiningCorePool(string configFile) if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && RuntimeInformation.ProcessArchitecture == Architecture.X86) throw new PoolStartupAbortException("Miningcore requires 64-Bit Windows"); - // Read config.json file - clusterConfig = PoolConfig.GetConfigContent(configFile); + if (configFile != null) + { + // Read config.json file + clusterConfig = PoolConfig.GetConfigContent(configFile); + } + else + { + // Read config.json from azure app configuration + clusterConfig = PoolConfig.GetConfigContentFromAppConfig(appConfigPrefix); + } // Initialize Logging FileLogger.ConfigureLogging(); diff --git a/src/Miningcore/Program.cs b/src/Miningcore/Program.cs index ea038f8285..9913d3a3fa 100644 --- a/src/Miningcore/Program.cs +++ b/src/Miningcore/Program.cs @@ -31,6 +31,7 @@ public static void Main(string[] args) var versionOption = MiningCore.Option("-v|--version", "Version Information", CommandOptionType.NoValue); var configFileOption = MiningCore.Option("-c|--config ", "Configuration File", CommandOptionType.SingleValue); + var appConfigPrefixOption = MiningCore.Option("-ac|--appconfig ", "Azure App Configuration Prefix", CommandOptionType.SingleValue); dumpConfigOption = MiningCore.Option("-dc|--dumpconfig", "Dump the configuration (useful for trouble-shooting typos in the config file)", CommandOptionType.NoValue); shareRecoveryOption = MiningCore.Option("-rs", "Import lost shares using existing recovery file", CommandOptionType.SingleValue); MiningCore.HelpOption("-? | -h | --help"); @@ -39,6 +40,7 @@ public static void Main(string[] args) Console.WriteLine("-----------------------------------------------------------------------------------------------------------------------"); string configFile = "config_template.json"; + string appConfigPrefix = "/"; if(versionOption.HasValue()) { @@ -51,6 +53,11 @@ public static void Main(string[] args) configFile = configFileOption.Value(); } + if(appConfigPrefixOption.HasValue()) + { + appConfigPrefix = appConfigPrefixOption.Value(); + } + // Dump Config to JSON output if(dumpConfigOption.HasValue()) { @@ -66,12 +73,13 @@ public static void Main(string[] args) if(!configFileOption.HasValue()) { - MiningCore.ShowHelp(); + Console.WriteLine("Start Miningcore and load config.json from azure app config."); + PoolCore.Pool.StartMiningCorePool(null, appConfigPrefix); } else { - // Start Miningcore PoolCore - PoolCore.Pool.StartMiningCorePool(configFile); + Console.WriteLine("Start Miningcore and load config.json from local file."); + PoolCore.Pool.StartMiningCorePool(configFile, null); } }); MiningCore.Execute(args); diff --git a/src/Miningcore/config.secrets b/src/Miningcore/config.secrets new file mode 100644 index 0000000000..d79423ddda --- /dev/null +++ b/src/Miningcore/config.secrets @@ -0,0 +1,3 @@ +persistence.postgres.user=persistence-postgres-user +persistence.postgres.password=persistence-postgres-password +pools.ethe1.paymentProcessing.coinbasePassword=pools-ethe1-paymentProcessing-coinbasePassword From 7614cf7658749e2c4cd12bf57b17a9372d9744bb Mon Sep 17 00:00:00 2001 From: Shwetha Chandan Date: Wed, 16 Jun 2021 13:49:26 +0530 Subject: [PATCH 30/39] log network difficulty --- src/Miningcore/Blockchain/Ethereum/EthereumPool.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Miningcore/Blockchain/Ethereum/EthereumPool.cs b/src/Miningcore/Blockchain/Ethereum/EthereumPool.cs index a88eb1fedf..0b034b8707 100644 --- a/src/Miningcore/Blockchain/Ethereum/EthereumPool.cs +++ b/src/Miningcore/Blockchain/Ethereum/EthereumPool.cs @@ -189,7 +189,7 @@ private async Task OnSubmitAsync(StratumClient client, Timestamped $"[{client.ConnectionId}] Share accepted: D={Math.Round(share.Difficulty / EthereumConstants.Pow2x32, 3)}"); + logger.Info(() => $"[{client.ConnectionId}] Share accepted: D={Math.Round(share.Difficulty / EthereumConstants.Pow2x32, 3)} ND={Math.Round(share.NetworkDifficulty / EthereumConstants.Pow2x32, 3)}"); await EnsureInitialWorkSent(client); // update pool stats From b933b8a23d6757a42b3268243876250fb351d8b9 Mon Sep 17 00:00:00 2001 From: Tuong Huynh Date: Thu, 17 Jun 2021 11:04:01 -0700 Subject: [PATCH 31/39] Read key vault secrets when referred from azure app configuration --- src/Miningcore/Miningcore.csproj | 1 + src/Miningcore/PoolCore/AzureAppConfiguration.cs | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Miningcore/Miningcore.csproj b/src/Miningcore/Miningcore.csproj index 715ed70262..02b2957270 100644 --- a/src/Miningcore/Miningcore.csproj +++ b/src/Miningcore/Miningcore.csproj @@ -48,6 +48,7 @@ + diff --git a/src/Miningcore/PoolCore/AzureAppConfiguration.cs b/src/Miningcore/PoolCore/AzureAppConfiguration.cs index 1050ffd0fd..e26cce2ae3 100644 --- a/src/Miningcore/PoolCore/AzureAppConfiguration.cs +++ b/src/Miningcore/PoolCore/AzureAppConfiguration.cs @@ -1,5 +1,6 @@ using System; using Microsoft.Extensions.Configuration; +using Azure.Identity; namespace Miningcore.PoolCore { @@ -16,7 +17,11 @@ public static IConfigurationRoot GetAppConfig(string prefix) { var builder = new ConfigurationBuilder(); builder.AddAzureAppConfiguration(options => { options.Connect(Environment.GetEnvironmentVariable(ConnectionString)) - .TrimKeyPrefix(prefix); + .ConfigureKeyVault(kv => + { + kv.SetCredential(new DefaultAzureCredential()); + }) + .TrimKeyPrefix(prefix); }); return builder.Build(); From e80f40534ae404c09d8e1c3a868ea8aa72e7e9fd Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Thu, 17 Jun 2021 17:01:29 -0700 Subject: [PATCH 32/39] Using env config to override default config --- src/Miningcore/Dockerfile | 6 +- src/Miningcore/Miningcore.csproj | 3 + src/Miningcore/PoolCore/PoolConfig.cs | 79 +++++++++++++++++--- src/Miningcore/PoolCore/PoolCore.cs | 103 +++++++++++--------------- src/Miningcore/PoolCore/PoolLogo.cs | 32 ++++---- src/Miningcore/Program.cs | 99 +++++++++++++------------ src/Miningcore/config.json | 4 +- 7 files changed, 189 insertions(+), 137 deletions(-) diff --git a/src/Miningcore/Dockerfile b/src/Miningcore/Dockerfile index a73d68c413..2dec293b60 100644 --- a/src/Miningcore/Dockerfile +++ b/src/Miningcore/Dockerfile @@ -21,6 +21,8 @@ COPY ["src/Miningcore/config.json", "/app/publish/"] FROM base AS final WORKDIR /app -RUN apt-get update -y && apt-get install -y libsodium-dev +RUN apt-get update -y && apt-get install -y procps libsodium-dev +RUN mkdir logs COPY --from=publish /app/publish . -ENTRYPOINT ["dotnet", "Miningcore.dll", "-c", "config.json"] +EXPOSE 3072 3073 4000 +ENTRYPOINT ["dotnet", "Miningcore.dll"] diff --git a/src/Miningcore/Miningcore.csproj b/src/Miningcore/Miningcore.csproj index 00f3349ace..8422a89267 100644 --- a/src/Miningcore/Miningcore.csproj +++ b/src/Miningcore/Miningcore.csproj @@ -96,6 +96,9 @@ PreserveNewest + + PreserveNewest + diff --git a/src/Miningcore/PoolCore/PoolConfig.cs b/src/Miningcore/PoolCore/PoolConfig.cs index 917e3d33cc..fad9ac396b 100644 --- a/src/Miningcore/PoolCore/PoolConfig.cs +++ b/src/Miningcore/PoolCore/PoolConfig.cs @@ -6,13 +6,13 @@ Copyright 2021 MinerNL (Miningcore.com) using System; using System.IO; using System.Linq; -using System.Reactive.Linq; using System.Text; using System.Text.RegularExpressions; using FluentValidation; using Miningcore.Configuration; using Miningcore.Mining; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using Newtonsoft.Json.Serialization; using JsonSerializer = Newtonsoft.Json.JsonSerializer; @@ -21,9 +21,9 @@ namespace Miningcore.PoolCore { public class PoolConfig { - + private const string BaseConfigFile = "config.json"; private static ClusterConfig clusterConfig; - private static readonly Regex regexJsonTypeConversionError = new Regex("\"([^\"]+)\"[^\']+\'([^\']+)\'.+\\s(\\d+),.+\\s(\\d+)", RegexOptions.Compiled); + private static readonly Regex RegexJsonTypeConversionError = new Regex("\"([^\"]+)\"[^\']+\'([^\']+)\'.+\\s(\\d+),.+\\s(\\d+)", RegexOptions.Compiled); public static ClusterConfig GetConfigContent(string configFile) { @@ -32,7 +32,72 @@ public static ClusterConfig GetConfigContent(string configFile) ValidateConfig(); return clusterConfig; + } + + public static ClusterConfig GetConfigContentFromJson(string config) + { + try + { + var baseConfig = JsonConvert.DeserializeObject(File.ReadAllText(BaseConfigFile)); + var toBeMerged = JObject.Parse(config); + baseConfig.Merge(toBeMerged, new JsonMergeSettings { MergeArrayHandling = MergeArrayHandling.Merge }); + clusterConfig = baseConfig.ToObject(); + } + catch(JsonSerializationException ex) + { + Console.WriteLine($"Error: {ex.Message}"); + throw; + } + catch(JsonException ex) + { + Console.WriteLine($"Error: {ex.Message}"); + throw; + } + catch(IOException ex) + { + Console.WriteLine($"Error: {ex.Message}"); + throw; + } + + ValidateConfig(); + + return clusterConfig; + } + + private static ClusterConfig ReadConfigJson(string config) + { + try + { + Console.WriteLine($"Using configuration '{config}'\n"); + + var serializer = JsonSerializer.Create(new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver() + }); + using(var reader = new StringReader(config)) + { + using(var jsonReader = new JsonTextReader(reader)) + { + return serializer.Deserialize(jsonReader); + } + } + } + catch(JsonSerializationException ex) + { + HumanizeJsonParseException(ex); + throw; + } + catch(JsonException ex) + { + Console.WriteLine($"Error: {ex.Message}"); + throw; + } + catch(IOException ex) + { + Console.WriteLine($"Error: {ex.Message}"); + throw; + } } private static ClusterConfig ReadConfig(string configFile) @@ -54,19 +119,16 @@ private static ClusterConfig ReadConfig(string configFile) } } } - catch(JsonSerializationException ex) { HumanizeJsonParseException(ex); throw; } - catch(JsonException ex) { Console.WriteLine($"Error: {ex.Message}"); throw; } - catch(IOException ex) { Console.WriteLine($"Error: {ex.Message}"); @@ -82,12 +144,10 @@ private static void ValidateConfig() if(!config.EnableInternalStratum.HasValue) config.EnableInternalStratum = clusterConfig.ShareRelays == null || clusterConfig.ShareRelays.Length == 0; } - try { clusterConfig.Validate(); } - catch(ValidationException ex) { Console.WriteLine($"Configuration is not valid:\n\n{string.Join("\n", ex.Errors.Select(x => "=> " + x.ErrorMessage))}"); @@ -97,12 +157,11 @@ private static void ValidateConfig() { Console.WriteLine($"Pool Configuration file is valid"); } - } private static void HumanizeJsonParseException(JsonSerializationException ex) { - var m = regexJsonTypeConversionError.Match(ex.Message); + var m = RegexJsonTypeConversionError.Match(ex.Message); if(m.Success) { diff --git a/src/Miningcore/PoolCore/PoolCore.cs b/src/Miningcore/PoolCore/PoolCore.cs index cc494fd5c4..9b3681c97a 100644 --- a/src/Miningcore/PoolCore/PoolCore.cs +++ b/src/Miningcore/PoolCore/PoolCore.cs @@ -14,22 +14,11 @@ Copyright 2021 MinerNL (Miningcore.com) using System.Reactive.Threading.Tasks; using System.Reflection; using System.Runtime.InteropServices; -using System.Text; -using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Autofac; using Autofac.Features.Metadata; using AutoMapper; -using FluentValidation; -using McMaster.Extensions.CommandLineUtils; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.AspNetCore.Mvc; -using Miningcore.Api; -using Miningcore.Api.Controllers; using Miningcore.Api.Responses; using Miningcore.Configuration; using Miningcore.Crypto.Hashing.Equihash; @@ -38,34 +27,19 @@ Copyright 2021 MinerNL (Miningcore.com) using Miningcore.Mining; using Miningcore.Notifications; using Miningcore.Payments; -using Miningcore.PoolCore; using Miningcore.Util; using NBitcoin.Zcash; using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; using NLog; -using NLog.Conditions; -using NLog.Config; -using NLog.Layouts; -using NLog.Targets; -using JsonSerializer = Newtonsoft.Json.JsonSerializer; -using Microsoft.Extensions.Logging; -using LogLevel = NLog.LogLevel; using ILogger = NLog.ILogger; -using NLog.Extensions.Logging; -using Prometheus; -using WebSocketManager; -using Miningcore.Api.Middlewares; using System.Collections.Concurrent; -using Microsoft.AspNetCore.Http; -using AspNetCoreRateLimit; namespace Miningcore.PoolCore { internal class Pool { - private static readonly CancellationTokenSource cts = new CancellationTokenSource(); - private static readonly ILogger logger = LogManager.GetLogger("PoolCore"); + private static readonly CancellationTokenSource Cts = new CancellationTokenSource(); + private static readonly ILogger Logger = LogManager.GetLogger("PoolCore"); private static ShareRecorder shareRecorder; private static ShareRelay shareRelay; private static ShareReceiver shareReceiver; @@ -74,7 +48,7 @@ internal class Pool private static NotificationService notificationService; private static MetricsPublisher metricsPublisher; private static BtStreamReceiver btStreamReceiver; - private static readonly ConcurrentDictionary pools = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary Pools = new ConcurrentDictionary(); private static AdminGcStats gcStats = new AdminGcStats(); private static readonly IPAddress IPv4LoopBackOnIPv6 = IPAddress.Parse("::ffff:127.0.0.1"); @@ -82,17 +56,28 @@ internal class Pool internal static IContainer container; internal static void StartMiningCorePool(string configFile) + { + StartMiningCorePoolInternal(PoolConfig.GetConfigContent(configFile)); + } + + internal static void StartMiningCorePoolWithJson(string config) + { + StartMiningCorePoolInternal(PoolConfig.GetConfigContentFromJson(config)); + } + + private static void StartMiningCorePoolInternal(ClusterConfig config) { try { + var assembly = Assembly.GetEntryAssembly(); // Display Software Version Info - var basePath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); - Console.WriteLine($"{Assembly.GetEntryAssembly().GetName().Name} - MinerNL build v{Assembly.GetEntryAssembly().GetName().Version}"); + var basePath = Path.GetDirectoryName(assembly?.Location); + Console.WriteLine($"{assembly?.GetName().Name} - MinerNL build v{assembly?.GetName().Version}"); Console.WriteLine($"Run location: {basePath}"); Console.WriteLine(" "); // log unhandled program exception errors - AppDomain currentDomain = AppDomain.CurrentDomain; + var currentDomain = AppDomain.CurrentDomain; currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MC_UnhandledException); currentDomain.ProcessExit += OnProcessExit; Console.CancelKeyPress += new ConsoleCancelEventHandler(OnCancelKeyPress); @@ -100,21 +85,21 @@ internal static void StartMiningCorePool(string configFile) // ValidateRuntimeEnvironment(); // root check if(!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Environment.UserName == "root") - logger.Warn(() => "Running as root is discouraged!"); + Logger.Warn(() => "Running as root is discouraged!"); // require 64-bit Windows OS if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && RuntimeInformation.ProcessArchitecture == Architecture.X86) throw new PoolStartupAbortException("Miningcore requires 64-Bit Windows"); // Read config.json file - clusterConfig = PoolConfig.GetConfigContent(configFile); + clusterConfig = config; // Initialize Logging FileLogger.ConfigureLogging(); // LogRuntimeInfo(); //----------------------------------------------------------------------------- - logger.Info(() => $"{RuntimeInformation.FrameworkDescription.Trim()} on {RuntimeInformation.OSDescription.Trim()} [{RuntimeInformation.ProcessArchitecture}]"); + Logger.Info(() => $"{RuntimeInformation.FrameworkDescription.Trim()} on {RuntimeInformation.OSDescription.Trim()} [{RuntimeInformation.ProcessArchitecture}]"); // Bootstrap(); //----------------------------------------------------------------------------- @@ -124,7 +109,7 @@ internal static void StartMiningCorePool(string configFile) var builder = new ContainerBuilder(); builder.RegisterAssemblyModules(typeof(AutofacModule).GetTypeInfo().Assembly); builder.RegisterInstance(clusterConfig); - builder.RegisterInstance(pools); + builder.RegisterInstance(Pools); builder.RegisterInstance(gcStats); // AutoMapper @@ -141,8 +126,8 @@ internal static void StartMiningCorePool(string configFile) MonitorGarbageCollection(); // Start Miningcore Pool Services - if(!cts.IsCancellationRequested) - StartMiningcorePoolServices().Wait(cts.Token); + if(!Cts.IsCancellationRequested) + StartMiningcorePoolServices().Wait(Cts.Token); } @@ -188,9 +173,9 @@ internal static void StartMiningCorePool(string configFile) { // Shutdown(); Console.WriteLine("Miningcore is shuting down... bye!"); - logger?.Info(() => "Miningcore is shuting down... bye!"); + Logger?.Info(() => "Miningcore is shuting down... bye!"); - foreach(var poolToStop in pools.Values) + foreach(var poolToStop in Pools.Values) { Console.WriteLine($"Stopping pool {poolToStop}"); poolToStop.Stop(); @@ -205,22 +190,21 @@ internal static void StartMiningCorePool(string configFile) } } - - + // ************************************************************************** // MININGCORE POOL SERVICES // ************************************************************************** private static async Task StartMiningcorePoolServices() { var coinTemplates = PoolCoinTemplates.LoadCoinTemplates(); - logger.Info($"{coinTemplates.Keys.Count} coins loaded from {string.Join(", ", clusterConfig.CoinTemplates)}"); + Logger.Info($"{coinTemplates.Keys.Count} coins loaded from {string.Join(", ", clusterConfig.CoinTemplates)}"); // Populate pool configs with corresponding template foreach(var poolConfig in clusterConfig.Pools.Where(x => x.Enabled)) { // Foreach coin definition if(!coinTemplates.TryGetValue(poolConfig.Coin, out var template)) - logger.ThrowLogPoolStartupException($"Pool {poolConfig.Id} references undefined coin '{poolConfig.Coin}'"); + Logger.ThrowLogPoolStartupException($"Pool {poolConfig.Id} references undefined coin '{poolConfig.Coin}'"); poolConfig.Template = template; } @@ -257,7 +241,7 @@ private static async Task StartMiningcorePoolServices() } else { - logger.Warn("API is disabled"); + Logger.Warn("API is disabled"); } // start payment processor @@ -269,7 +253,7 @@ private static async Task StartMiningcorePoolServices() } else { - logger.Warn("Payment processing is Disabled"); + Logger.Warn("Payment processing is Disabled"); } if(clusterConfig.ShareRelay == null) @@ -281,7 +265,7 @@ private static async Task StartMiningcorePoolServices() } else { - logger.Info("Share Relay is Active!"); + Logger.Info("Share Relay is Active!"); } // start stratum pools @@ -294,21 +278,20 @@ await Task.WhenAll(clusterConfig.Pools.Where(x => x.Enabled).Select(async poolCo // create and configure var stratumPool = poolImpl.Value; stratumPool.Configure(poolConfig, clusterConfig); - pools[poolConfig.Id] = stratumPool; + Pools[poolConfig.Id] = stratumPool; // pre-start attachments shareReceiver?.AttachPool(stratumPool); statsRecorder?.AttachPool(stratumPool); //apiServer?.AttachPool(stratumPool); - await stratumPool.StartAsync(cts.Token); + await stratumPool.StartAsync(Cts.Token); })); // keep running - await Observable.Never().ToTask(cts.Token); + await Observable.Never().ToTask(Cts.Token); } - - + private static void MonitorGarbageCollection() { var thread = new Thread(() => @@ -320,7 +303,7 @@ private static void MonitorGarbageCollection() var s = GC.WaitForFullGCApproach(); if(s == GCNotificationStatus.Succeeded) { - logger.Info(() => "Garbage Collection bin Full soon"); + Logger.Info(() => "Garbage Collection bin Full soon"); sw.Start(); } @@ -328,7 +311,7 @@ private static void MonitorGarbageCollection() if(s == GCNotificationStatus.Succeeded) { - logger.Info(() => "Garbage Collection bin Full!!"); + Logger.Info(() => "Garbage Collection bin Full!!"); sw.Stop(); @@ -353,9 +336,9 @@ internal static Task RecoverSharesAsync(string recoveryFilename) // log unhandled program exception errors private static void MC_UnhandledException(object sender, UnhandledExceptionEventArgs args ) { - if(logger != null) + if(Logger != null) { - logger.Error(args.ExceptionObject); + Logger.Error(args.ExceptionObject); LogManager.Flush(TimeSpan.Zero); } Exception e = (Exception) args.ExceptionObject; @@ -366,12 +349,12 @@ private static void MC_UnhandledException(object sender, UnhandledExceptionEvent protected static void OnCancelKeyPress(object sender, ConsoleCancelEventArgs args ) { - logger?.Info(() => $"Miningcore is stopping because exit key [{args.SpecialKey}] recieved. Exiting."); + Logger?.Info(() => $"Miningcore is stopping because exit key [{args.SpecialKey}] recieved. Exiting."); Console.WriteLine($"Miningcore is stopping because exit key [{args.SpecialKey}] recieved. Exiting."); try { - cts?.Cancel(); + Cts?.Cancel(); } catch { @@ -382,12 +365,12 @@ protected static void OnCancelKeyPress(object sender, ConsoleCancelEventArgs arg private static void OnProcessExit(object sender, EventArgs e) { - logger?.Info(() => "Miningcore received process stop request."); + Logger?.Info(() => "Miningcore received process stop request."); Console.WriteLine("Miningcore received process stop request."); try { - cts?.Cancel(); + Cts?.Cancel(); } catch { diff --git a/src/Miningcore/PoolCore/PoolLogo.cs b/src/Miningcore/PoolCore/PoolLogo.cs index f3d67ef363..9307282464 100644 --- a/src/Miningcore/PoolCore/PoolLogo.cs +++ b/src/Miningcore/PoolCore/PoolLogo.cs @@ -4,8 +4,6 @@ Copyright 2021 MinerNL (Miningcore.com) */ using System; -using System.Collections.Generic; -using System.Text; namespace Miningcore.PoolCore { @@ -15,7 +13,7 @@ class PoolLogo public static void Logo() { - Console.WriteLine($@" + Console.WriteLine(@" ███╗ ███╗██╗███╗ ██╗██╗███╗ ██╗ ██████╗ ██████╗ ██████╗ ██████╗ ███████╗ ████╗ ████║██║████╗ ██║██║████╗ ██║██╔════╝ ██╔════╝██╔═══██╗██╔══██╗██╔════╝ ██╔████╔██║██║██╔██╗ ██║██║██╔██╗ ██║██║ ███╗██║ ██║ ██║██████╔╝█████╗ @@ -23,22 +21,22 @@ public static void Logo() ██║ ╚═╝ ██║██║██║ ╚████║██║██║ ╚████║╚██████╔╝╚██████╗╚██████╔╝██║ ██║███████╗ "); Console.ForegroundColor = ConsoleColor.DarkYellow; - Console.WriteLine($" MININGCORE - making mining easy"); - Console.WriteLine($" https://github.com/minernl/miningcore\n"); + Console.WriteLine(" MININGCORE - making mining easy"); + Console.WriteLine(" https://github.com/minernl/miningcore\n"); Console.WriteLine(); Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine($" Part off all donation goes to the core developers"); - Console.WriteLine($" If you want to donate to them yourself:\n"); - Console.WriteLine($" BTC - 3QT2WreQtanPHcMneg9LT2aH3s5nrSZsxr"); - Console.WriteLine($" LTC - LTVnLEv8Xj6emGbf981nTyN54Mnyjbfgrg"); - Console.WriteLine($" DASH - Xc2vm9SfRn8t1hyQgqi8Zrt3oFeGcQtwTh"); - Console.WriteLine($" ETH - 0xBfD360CDd9014Bc5B348B65cBf79F78381694f4E"); - Console.WriteLine($" ETC - 0xF4BFFC324bbeB63348F137B84f8d1Ade17B507E4"); - Console.WriteLine($" UMA - 0x10c42769a8a07421C168c19612A434A72D460d08"); - Console.WriteLine($" XLM - GDQP2KPQGKIHYJGXNUIYOMHARUARCA7DJT5FO2FFOOKY3B2WSQHG4W37:::ucl:::864367071"); - Console.WriteLine($" XMR - 44riGcQcDp4EsboDJP284CFCnJ2qP7y8DAqGC4D9WtVbEqzxQ3qYXAUST57u5FkrVF7CXhsEc63QNWazJ5b9ygwBJBtB2kT"); - Console.WriteLine($" XPR - rw2ciyaNshpHe7bCHo4bRWq6pqqynnWKQg:::ucl:::2242232925"); - Console.WriteLine($" ZEC - t1JtJtxTdgXCaYm1wzRfMRkGTJM4qLcm4FQ"); + Console.WriteLine(" Part off all donation goes to the core developers"); + Console.WriteLine(" If you want to donate to them yourself:\n"); + Console.WriteLine(" BTC - 3QT2WreQtanPHcMneg9LT2aH3s5nrSZsxr"); + Console.WriteLine(" LTC - LTVnLEv8Xj6emGbf981nTyN54Mnyjbfgrg"); + Console.WriteLine(" DASH - Xc2vm9SfRn8t1hyQgqi8Zrt3oFeGcQtwTh"); + Console.WriteLine(" ETH - 0xBfD360CDd9014Bc5B348B65cBf79F78381694f4E"); + Console.WriteLine(" ETC - 0xF4BFFC324bbeB63348F137B84f8d1Ade17B507E4"); + Console.WriteLine(" UMA - 0x10c42769a8a07421C168c19612A434A72D460d08"); + Console.WriteLine(" XLM - GDQP2KPQGKIHYJGXNUIYOMHARUARCA7DJT5FO2FFOOKY3B2WSQHG4W37:::ucl:::864367071"); + Console.WriteLine(" XMR - 44riGcQcDp4EsboDJP284CFCnJ2qP7y8DAqGC4D9WtVbEqzxQ3qYXAUST57u5FkrVF7CXhsEc63QNWazJ5b9ygwBJBtB2kT"); + Console.WriteLine(" XPR - rw2ciyaNshpHe7bCHo4bRWq6pqqynnWKQg:::ucl:::2242232925"); + Console.WriteLine(" ZEC - t1JtJtxTdgXCaYm1wzRfMRkGTJM4qLcm4FQ"); Console.WriteLine(); Console.ResetColor(); } diff --git a/src/Miningcore/Program.cs b/src/Miningcore/Program.cs index ea038f8285..e5b96bd3af 100644 --- a/src/Miningcore/Program.cs +++ b/src/Miningcore/Program.cs @@ -2,15 +2,13 @@ using Miningcore.Configuration; using Miningcore.PoolCore; using System; -using System.Collections.Generic; -using System.IO; using System.Reflection; -using System.Text; namespace Miningcore { public class Program { + private const string EnvironmentConfig = "cfg"; private static CommandOption dumpConfigOption; private static CommandOption shareRecoveryOption; private static ClusterConfig clusterConfig; @@ -19,62 +17,71 @@ public static void Main(string[] args) { PoolLogo.Logo(); - var MiningCore = new CommandLineApplication(throwOnUnexpectedArg: false) + var miningCore = new CommandLineApplication(throwOnUnexpectedArg: false) { Name = "dotnet Miningcore.dll", FullName = "MiningCore 2.0 - Stratum Mining Pool Engine", Description = "Stratum mining pool engine for Bitcoin and Altcoins", - ShortVersionGetter = () => $"- MinerNL build v{Assembly.GetEntryAssembly().GetName().Version.ToString(2)}", - LongVersionGetter = () => $"- MinerNL build v{Assembly.GetEntryAssembly().GetName().Version}", + ShortVersionGetter = () => $"- MinerNL build v{Assembly.GetEntryAssembly()?.GetName().Version.ToString(2)}", + LongVersionGetter = () => $"- MinerNL build v{Assembly.GetEntryAssembly()?.GetName().Version}", ExtendedHelpText = "--------------------------------------------------------------------------------------------------------------" }; - var versionOption = MiningCore.Option("-v|--version", "Version Information", CommandOptionType.NoValue); - var configFileOption = MiningCore.Option("-c|--config ", "Configuration File", CommandOptionType.SingleValue); - dumpConfigOption = MiningCore.Option("-dc|--dumpconfig", "Dump the configuration (useful for trouble-shooting typos in the config file)", CommandOptionType.NoValue); - shareRecoveryOption = MiningCore.Option("-rs", "Import lost shares using existing recovery file", CommandOptionType.SingleValue); - MiningCore.HelpOption("-? | -h | --help"); - MiningCore.OnExecute( () => - { - Console.WriteLine("-----------------------------------------------------------------------------------------------------------------------"); + var versionOption = miningCore.Option("-v|--version", "Version Information", CommandOptionType.NoValue); + var configFileOption = miningCore.Option("-c|--config ", "Configuration File", CommandOptionType.SingleValue); + dumpConfigOption = miningCore.Option("-dc|--dumpconfig", "Dump the configuration (useful for trouble-shooting typos in the config file)", CommandOptionType.NoValue); + shareRecoveryOption = miningCore.Option("-rs", "Import lost shares using existing recovery file", CommandOptionType.SingleValue); + miningCore.HelpOption("-? | -h | --help"); + miningCore.OnExecute(() => + { + Console.WriteLine("-----------------------------------------------------------------------------------------------------------------------"); - string configFile = "config_template.json"; + var configFile = "config_template.json"; - if(versionOption.HasValue()) - { - MiningCore.ShowVersion(); - } + if(versionOption.HasValue()) + { + miningCore.ShowVersion(); + } - // overwrite default config_template.json with -c | --config file - if(configFileOption.HasValue()) - { - configFile = configFileOption.Value(); - } + // overwrite default config_template.json with -c | --config file + if(configFileOption.HasValue()) + { + configFile = configFileOption.Value(); + } - // Dump Config to JSON output - if(dumpConfigOption.HasValue()) - { - clusterConfig = PoolCore.PoolConfig.GetConfigContent(configFile); - PoolCore.PoolConfig.DumpParsedConfig(clusterConfig); - } + // Dump Config to JSON output + if(dumpConfigOption.HasValue()) + { + clusterConfig = PoolCore.PoolConfig.GetConfigContent(configFile); + PoolCore.PoolConfig.DumpParsedConfig(clusterConfig); + } - // Shares recovery from file to database - if(shareRecoveryOption.HasValue()) - { - PoolCore.Pool.RecoverSharesAsync(shareRecoveryOption.Value()).Wait(); - } + // Shares recovery from file to database + if(shareRecoveryOption.HasValue()) + { + Pool.RecoverSharesAsync(shareRecoveryOption.Value()).Wait(); + } - if(!configFileOption.HasValue()) - { - MiningCore.ShowHelp(); - } - else - { - // Start Miningcore PoolCore - PoolCore.Pool.StartMiningCorePool(configFile); - } - }); - MiningCore.Execute(args); + if(configFileOption.HasValue()) + { + // Start Miningcore PoolCore + Pool.StartMiningCorePool(configFile); + } + else + { + var envConfig = Environment.GetEnvironmentVariable(EnvironmentConfig); + if(!string.IsNullOrEmpty(envConfig)) + { + // Start Miningcore PoolCore + Pool.StartMiningCorePoolWithJson(envConfig); + } + else + { + miningCore.ShowHelp(); + } + } + }); + miningCore.Execute(args); } } } diff --git a/src/Miningcore/config.json b/src/Miningcore/config.json index dcba65d7d1..130bf6a140 100644 --- a/src/Miningcore/config.json +++ b/src/Miningcore/config.json @@ -5,7 +5,7 @@ "enableConsoleColors": true, "logFile": "core.log", "apiLogFile": "api.log", - "logBaseDirectory": "/coinfoundry/logs", + "logBaseDirectory": "logs/", "perPoolLogFile": true }, "banning": { @@ -110,7 +110,7 @@ "payoutScheme": "PPS3", "payoutSchemeConfig": { "factor": 2.0, - "fixedReward": 0.000013 + "fixedReward": "0.000013" }, "coinbasePassword": "wallet_password", "keepUncles": false, From d3637b733700697b4ac68bdc61a3bdaea758ec20 Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Thu, 17 Jun 2021 17:06:38 -0700 Subject: [PATCH 33/39] Cleaned up tasks --- azure-pipelines.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index abae4f9062..b2704f3a09 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -21,6 +21,7 @@ stages: vmImage: ubuntu-latest steps: - task: DockerInstaller@0 + enabled: false inputs: dockerVersion: '20.10.7' - task: Bash@3 From 7e30435844f0146da87db686d96a9102e568a2ff Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Thu, 17 Jun 2021 17:40:56 -0700 Subject: [PATCH 34/39] Switching to beta repo --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index b2704f3a09..e4e7871665 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -32,7 +32,7 @@ stages: - task: Docker@2 displayName: Build an image inputs: - containerRegistry: 'sandbox-poolrepo' + containerRegistry: 'beta-repo' repository: 'miningcore' command: 'buildAndPush' Dockerfile: '**/Dockerfile' From fad672209627fbb4e7a9fb01b3a5873fe56dd8b6 Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Fri, 18 Jun 2021 07:29:12 -0700 Subject: [PATCH 35/39] Added logs for clarity --- src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs | 2 +- src/Miningcore/Payments/PayoutManager.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs b/src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs index ad2d01c194..5ab722fe76 100644 --- a/src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs +++ b/src/Miningcore/Payments/PaymentSchemes/PPS3PaymentScheme.cs @@ -250,7 +250,7 @@ private async Task LogDiscardedShares(PoolConfig poolConfig, Block block, DateTi throw new OverflowException("blockRewardRemaining < 0"); //logger.Info(() => $"Balance-calculation for pool {poolConfig.Id}, block {block?.BlockHeight} completed with accumulated score {accumulatedScore:0.####} ({accumulatedScore * 100:0.#}%)"); - logger.Info(() => $"Balance-calculation for pool {poolConfig.Id} completed with accumulated rewards of ${accumulatedRewards:0.######}"); + logger.Info(() => $"Balance-calculation for pool {poolConfig.Id} completed with accumulated rewards of ${accumulatedRewards:0.#######}"); return shareCutOffDate; } diff --git a/src/Miningcore/Payments/PayoutManager.cs b/src/Miningcore/Payments/PayoutManager.cs index aee6980972..9d568b622b 100644 --- a/src/Miningcore/Payments/PayoutManager.cs +++ b/src/Miningcore/Payments/PayoutManager.cs @@ -227,7 +227,7 @@ private async Task PayoutPoolBalancesAsync(PoolConfig pool, IPayoutHandler handl } else - logger.Info(() => $"No balances over configured minimum payout for pool {pool.Id}"); + logger.Info(() => $"No balances over configured minimum payout {pool.PaymentProcessing.MinimumPayment:0.#######} for pool {pool.Id}"); } private Task NotifyPayoutFailureAsync(Balance[] balances, PoolConfig pool, Exception ex) From 04f051ffc44f296040e0020b1a5f65e0f65011df Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Fri, 18 Jun 2021 10:47:44 -0700 Subject: [PATCH 36/39] Added logs for payment analysis --- .../Ethereum/EthereumPayoutHandler.cs | 21 ++++++++++--------- src/Miningcore/config.json | 4 ++-- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs b/src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs index 3daa61bb67..1b0ca0f6cd 100644 --- a/src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs +++ b/src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs @@ -130,7 +130,7 @@ public async Task ClassifyBlocksAsync(Block[] blocks) messageBus.NotifyBlockConfirmationProgress(poolConfig.Id, block, coin); - + if(string.Equals(blockInfo.Miner, poolConfig.Address, StringComparison.OrdinalIgnoreCase)) { // additional check @@ -162,11 +162,11 @@ public async Task ClassifyBlocksAsync(Block[] blocks) logger.Debug(() => $"** (WALLET_MATCH) Is the Block mined by us? {match}"); logger.Debug(() => $"** Possible Uncle or Orphan block found"); } - + } // mature? - if(match && (latestBlockHeight - block.BlockHeight >= EthereumConstants.MinConfimations) ) + if(match && (latestBlockHeight - block.BlockHeight >= EthereumConstants.MinConfimations)) { block.Status = BlockStatus.Confirmed; block.ConfirmationProgress = 1; @@ -340,7 +340,7 @@ internal static decimal GetBaseBlockReward(ParityChainType chainType, ulong heig switch(chainType) { case ParityChainType.Mainnet: - if (height >= EthereumConstants.ConstantinopleHardForkHeight) + if(height >= EthereumConstants.ConstantinopleHardForkHeight) return EthereumConstants.ConstantinopleReward; if(height >= EthereumConstants.ByzantiumHardForkHeight) return EthereumConstants.ByzantiumBlockReward; @@ -473,20 +473,21 @@ private async Task PayoutAsync(Balance balance) { throw new Exception("Unable to unlock coinbase account for sending transaction"); } - + + var amount = (BigInteger) Math.Floor(balance.Amount * EthereumConstants.Wei); // send transaction - logger.Info(() => $"[{LogCategory}] Sending {FormatAmount(balance.Amount)} {balance.Amount} to {balance.Address}"); + logger.Info(() => $"[{LogCategory}] Sending {FormatAmount(balance.Amount)} {amount} to {balance.Address}"); var request = new SendTransactionRequest { From = poolConfig.Address, To = balance.Address, - Value = (BigInteger) Math.Floor(balance.Amount * EthereumConstants.Wei), + Value = amount, }; -// ToDo test difference -// NL: Value = (BigInteger) Math.Floor(balance.Amount * EthereumConstants.Wei), -// AX: Value = writeHex(amount), + // ToDo test difference + // NL: Value = (BigInteger) Math.Floor(balance.Amount * EthereumConstants.Wei), + // AX: Value = writeHex(amount), var response = await daemon.ExecuteCmdSingleAsync(logger, EthCommands.SendTx, new[] { request }); if(response.Error != null) diff --git a/src/Miningcore/config.json b/src/Miningcore/config.json index 130bf6a140..f194e50515 100644 --- a/src/Miningcore/config.json +++ b/src/Miningcore/config.json @@ -105,12 +105,12 @@ ], "paymentProcessing": { "enabled": true, - "minimumPayment": 0.001, + "minimumPayment": "0.000044", "minimumPaymentToPaymentId": 5.0, "payoutScheme": "PPS3", "payoutSchemeConfig": { "factor": 2.0, - "fixedReward": "0.000013" + "fixedReward": "0.0000022" }, "coinbasePassword": "wallet_password", "keepUncles": false, From bb794f8e8c7226ed213d2157e8ac3eb2fa6b9f95 Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Fri, 18 Jun 2021 11:21:12 -0700 Subject: [PATCH 37/39] Hard coded gas price to 20Gwei for transaction --- src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs b/src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs index 1b0ca0f6cd..91cd62be84 100644 --- a/src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs +++ b/src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs @@ -483,6 +483,7 @@ private async Task PayoutAsync(Balance balance) From = poolConfig.Address, To = balance.Address, Value = amount, + GasPrice = 20000000000 }; // ToDo test difference From fa7033ceed42d7da73082e1c4a58c3dc19ed4fcf Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Fri, 18 Jun 2021 11:57:13 -0700 Subject: [PATCH 38/39] Set gas to 20k --- src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs b/src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs index 91cd62be84..7a9df72aa2 100644 --- a/src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs +++ b/src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs @@ -483,6 +483,7 @@ private async Task PayoutAsync(Balance balance) From = poolConfig.Address, To = balance.Address, Value = amount, + Gas = 21000, GasPrice = 20000000000 }; From 257fdfd3cbb5c38114286b1a66bc42c557d8ed45 Mon Sep 17 00:00:00 2001 From: Kabilan VK Date: Fri, 18 Jun 2021 12:22:10 -0700 Subject: [PATCH 39/39] Made gas configurable --- .../EthereumPoolPaymentProcessingConfigExtra.cs | 5 +++++ src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs | 3 +-- src/Miningcore/config.json | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Miningcore/Blockchain/Ethereum/Configuration/EthereumPoolPaymentProcessingConfigExtra.cs b/src/Miningcore/Blockchain/Ethereum/Configuration/EthereumPoolPaymentProcessingConfigExtra.cs index 8b375e7857..e370f3eff2 100644 --- a/src/Miningcore/Blockchain/Ethereum/Configuration/EthereumPoolPaymentProcessingConfigExtra.cs +++ b/src/Miningcore/Blockchain/Ethereum/Configuration/EthereumPoolPaymentProcessingConfigExtra.cs @@ -36,5 +36,10 @@ public class EthereumPoolPaymentProcessingConfigExtra /// True to exempt uncle rewards from miner rewards /// public bool KeepUncles { get; set; } + + /// + /// Gas provided for the transaction execution + /// + public ulong Gas { get; set; } } } diff --git a/src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs b/src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs index 7a9df72aa2..6023546df5 100644 --- a/src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs +++ b/src/Miningcore/Blockchain/Ethereum/EthereumPayoutHandler.cs @@ -483,8 +483,7 @@ private async Task PayoutAsync(Balance balance) From = poolConfig.Address, To = balance.Address, Value = amount, - Gas = 21000, - GasPrice = 20000000000 + Gas = extraConfig.Gas }; // ToDo test difference diff --git a/src/Miningcore/config.json b/src/Miningcore/config.json index f194e50515..6849859089 100644 --- a/src/Miningcore/config.json +++ b/src/Miningcore/config.json @@ -114,7 +114,8 @@ }, "coinbasePassword": "wallet_password", "keepUncles": false, - "keepTransactionFees": false + "keepTransactionFees": false, + "Gas": 21000 } } ]