diff --git a/src/MiningCore.Tests/Crypto/EthashTests.cs b/src/MiningCore.Tests/Crypto/EthashTests.cs deleted file mode 100644 index 056a3f6dd..000000000 --- a/src/MiningCore.Tests/Crypto/EthashTests.cs +++ /dev/null @@ -1,121 +0,0 @@ -using System; -using System.Linq; -using System.Numerics; -using System.Text; -using System.Threading.Tasks; -using MiningCore.Crypto.Hashing.Algorithms; -using MiningCore.Crypto.Hashing.Ethash; -using MiningCore.Extensions; -using NLog; -using Xunit; - -namespace MiningCore.Tests.Crypto -{ - public class EthashTests : TestBase - { - private ILogger logger = LogManager.GetCurrentClassLogger(); - - [Fact] - public async Task Ethhash_Verify_Valid_Blocks() - { - var validBlocks = new[] { - new Block - { - // from proof of concept nine testnet, epoch 0 - Height = 22, - HashNoNonce ="372eca2454ead349c3df0ab5d00b0b706b23e49d469387db91811cee0358fc6d".HexToByteArray(), - Difficulty = new BigInteger(132416), - Nonce = 0x495732e0ed7a801c, - MixDigest = "2f74cdeb198af0b9abe65d22d372e22fb2d474371774a9583c1cc427a07939f5".HexToByteArray(), - }, - // from proof of concept nine testnet, epoch 1 - new Block - { - Height = 30001, - HashNoNonce = "7e44356ee3441623bc72a683fd3708fdf75e971bbe294f33e539eedad4b92b34".HexToByteArray(), - Difficulty = new BigInteger(1532671), - Nonce = 0x318df1c8adef7e5e, - MixDigest = "144b180aad09ae3c81fb07be92c8e6351b5646dda80e6844ae1b697e55ddde84".HexToByteArray(), - }, - // from proof of concept nine testnet, epoch 2 - new Block - { - Height = 60000, - HashNoNonce = "5fc898f16035bf5ac9c6d9077ae1e3d5fc1ecc3c9fd5bee8bb00e810fdacbaa0".HexToByteArray(), - Difficulty = new BigInteger(2467358), - Nonce = 0x50377003e5d830ca, - MixDigest = "ab546a5b73c452ae86dadd36f0ed83a6745226717d3798832d1b20b489e82063".HexToByteArray(), - }, - }; - - using (var ethash = new EthashLight(3)) - { - Assert.True(await ethash.VerifyBlockAsync(validBlocks[0], logger)); - Assert.True(await ethash.VerifyBlockAsync(validBlocks[1], logger)); - Assert.True(await ethash.VerifyBlockAsync(validBlocks[2], logger)); - } - } - - [Fact] - public async Task Ethhash_Verify_Invalid_Blocks() - { - var hasher = new Sha3_256(); - - var invalidBlocks = new[] { - // totally nonsense block - new Block - { - Height = 61440000, - HashNoNonce = hasher.Digest(Encoding.UTF8.GetBytes("foo")), - Difficulty = new BigInteger(0), - Nonce = 0xcafebabec00000fe, - MixDigest = hasher.Digest(Encoding.UTF8.GetBytes("bar")), - }, - new Block - { - // from proof of concept nine testnet, epoch 0 - altered Nonce - Height = 22, - HashNoNonce ="372eca2454ead349c3df0ab5d00b0b706b23e49d469387db91811cee0358fc6d".HexToByteArray(), - Difficulty = new BigInteger(132416), - Nonce = 0x495732e0ed7a801d, - MixDigest = "2f74cdeb198af0b9abe65d22d372e22fb2d474371774a9583c1cc427a07939f5".HexToByteArray(), - }, - new Block - { - // from proof of concept nine testnet, epoch 0 - altered HashNoNonce - Height = 22, - HashNoNonce ="472eca2454ead349c3df0ab5d00b0b706b23e49d469387db91811cee0358fc6d".HexToByteArray(), - Difficulty = new BigInteger(132416), - Nonce = 0x495732e0ed7a801c, - MixDigest = "2f74cdeb198af0b9abe65d22d372e22fb2d474371774a9583c1cc427a07939f5".HexToByteArray(), - }, - new Block - { - // from proof of concept nine testnet, epoch 0 - altered MixDigest - Height = 22, - HashNoNonce ="372eca2454ead349c3df0ab5d00b0b706b23e49d469387db91811cee0358fc6d".HexToByteArray(), - Difficulty = new BigInteger(132416), - Nonce = 0x495732e0ed7a801c, - MixDigest = "3f74cdeb198af0b9abe65d22d372e22fb2d474371774a9583c1cc427a07939f5".HexToByteArray(), - }, - }; - - using (var ethash = new EthashLight(3)) - { - Assert.False(await ethash.VerifyBlockAsync(invalidBlocks[0], logger)); - Assert.False(await ethash.VerifyBlockAsync(invalidBlocks[1], logger)); - Assert.False(await ethash.VerifyBlockAsync(invalidBlocks[2], logger)); - Assert.False(await ethash.VerifyBlockAsync(invalidBlocks[3], logger)); - } - } - - [Fact] - public async Task EthHash_VerifyAsync_Should_Throw_On_Null_Argument() - { - using (var ethash = new EthashLight(3)) - { - await Assert.ThrowsAsync(async () => await ethash.VerifyBlockAsync(null, logger)); - } - } - } -} diff --git a/src/MiningCore.Tests/Crypto/HashingTests.cs b/src/MiningCore.Tests/Crypto/HashingTests.cs index c5735274d..c520cfc49 100644 --- a/src/MiningCore.Tests/Crypto/HashingTests.cs +++ b/src/MiningCore.Tests/Crypto/HashingTests.cs @@ -325,7 +325,7 @@ public void DummyHasher_Should_Always_Throw() [Fact] public void EquihashVerifier_Should_Verify_Success() { - var hasher = EquihashSolver.Instance.Value; + var hasher = new EquihashSolver_ZCash(); var header = "0400000008e9694cc2120ec1b5733cc12687b609058eec4f7046a521ad1d1e3049b400003e7420ed6f40659de0305ef9b7ec037f4380ed9848bc1c015691c90aa16ff3930000000000000000000000000000000000000000000000000000000000000000c9310d5874e0001f000000000000000000000000000000010b000000000000000000000000000040" .HexToByteArray(); var solution = "00b43863a213bfe79f00337f5a729f09710abcc07035ef8ac34372abddecf2f82715f7223f075af96f0604fc124d6151fc8fb516d24a137faec123a89aa9a433f8a25a6bcfc554c28be556f6c878f96539186fab191505f278df48bf1ad2240e5bb39f372a143de1dd1b672312e00d52a3dd83f471b0239a7e8b30d4b9153027df87c8cd0b64de76749539fea376b4f39d08cf3d5e821495e52fdfa6f8085e59fc670656121c9d7c01388c8b4b4585aa7b9ac3f7ae796f9eb1fadba1730a1860eed797feabb18832b5e8f003c0adaf0788d1016e7a8969144018ecc86140aa4553962aa739a4850b509b505e158c5f9e2d5376374652e9e6d81b19fa0351be229af136efbce681463cc53d7880c1eeca3411154474ff8a7b2bac034a2026646776a517bf63921c31fbbd6be7c3ff42aab28230bfe81d33800b892b262f3579b7a41925a59f5cc1d4f523577c19ff9f92023146fa26486595bd89a1ba459eb0b5cec0578c3a071dbec73eca054c723ab30ce8e69de32e779cd2f1030e39878ac6ea3cdca743b43aedefe1a9b4f2da861038e2759defef0b8cad11d4179f2f08881b53ccc203e558c0571e049d998a257b3279016aad0d7999b609f6331a0d0f88e286a70432ca7f50a5bb8fafbbe9230b4ccb1fa57361c163d6b9f84579d61f41585a022d07dc8e55a8de4d8f87641dae777819458a2bf1bb02c438480ff11621ca8442ec2946875cce247c8877051359e9c822670d37bb00fa806e60e8e890ce62540fda2d5b1c790ca1e005030ac6d8e63db577bb98be111ee146828f9c48ee6257d7627b93ea3dd11aac3412e63dfc7ca132a73c4f51e7650f3f8ecf57bfc18716990b492d50e0a3e5fbf6136e771b91f7283ec3326209265b9531d157f8a07a4117fc8fb29ba1363afc6f9f0608251ea595256727a5bbe28f42a42edfbfa9017680e32980d4ad381612612b2bc7ad91e82eca693ea4fc27049a99636b50a576f1e55c72202d582b150ef194c1419f53177ecf315ea6b0e2f1aa8cd8f59b165aa0d89561c537fb6141f5813b7a4968fe16afc703326113f68508d88ff8d0aee1e88a84c0ae56c72f27511290ced48e93e8c95419d14aed1a5b2e9b2c9c1070c593e5eb50bb9a80e14e9f9fe501f56b1b3140159e8213b75d48d14af472a604484cd8e7e7abb6820245ed3ab29f9947463a033c586194be45eadec8392c8614d83a1e9ca0fe5655fa14f7a9c1d1f8f2185a06193ff4a3c3e9a96b02310033ceaa25894e7c56a6147e691597098054e285d39656d3d459ec5d13243c062b6eb44e19a13bdfc0b3c96bd3d1aeb75bb6b080322aea23555993cb529243958bb1a0e5d5027e6c78155437242d1d13c1d6e442a0e3783147a08bbfc0c2529fb705ad27713df40486fd58f001977f25dfd3c202451c07010a3880bca63959ca61f10ed3871f1152166fce2b52135718a8ceb239a0664a31c62defaad70be4b920dce70549c10d9138fbbad7f291c5b73fa21c3889929b143bc1576b72f70667ac11052b686891085290d871db528b5cfdc10a6d563925227609f10d1768a0e02dc7471ad424f94f737d4e7eb0fb167f1434fc4ae2d49e152f06f0845b6db0a44f0d6f5e7410420e6bd1f430b1af956005bf72b51405a04d9a5d9906ceca52c22c855785c3c3ac4c3e9bf532d31bab321e1db66f6a9f7dc9c017f2b7d8dfeb933cf5bbae71311ae318f6d187ebc5c843be342b08a9a0ff7c4b9c4b0f4fa74b13296afe84b6481440d58332e07b3d051ed55219d28e77af6612134da4431b797c63ef55bc53831e2f421db620fee51ba0967e4ed7009ef90af2204259bbfbb54537fd35c2132fa8e7f9c84bf9938d248862c6ca1cca9f48b0b33aa1589185c4eabc1c32".HexToByteArray(); var result = hasher.Verify(header, solution); @@ -336,7 +336,7 @@ public void EquihashVerifier_Should_Verify_Success() [Fact] public void EquihashVerifier_Should_Not_Verify_Invalid_Solution() { - var hasher = EquihashSolver.Instance.Value; + var hasher = new EquihashSolver_ZCash(); var header = "0400000008e9694cc2120ec1b5733cc12687b609058eec4f7046a521ad1d1e3049b400003e7420ed6f40659de0305ef9b7ec037f4380ed9848bc1c015691c90aa16ff3930000000000000000000000000000000000000000000000000000000000000000c9310d5874e0001f000000000000000000000000000000010b000000000000000000000000000040".HexToByteArray(); var solution = "90b43863a213bfe79f00337f5a729f09710abcc07035ef8ac34372abddecf2f82715f7223f075af96f0604fc124d6151fc8fb516d24a137faec123a89aa9a433f8a25a6bcfc554c28be556f6c878f96539186fab191505f278df48bf1ad2240e5bb39f372a143de1dd1b672312e00d52a3dd83f471b0239a7e8b30d4b9153027df87c8cd0b64de76749539fea376b4f39d08cf3d5e821495e52fdfa6f8085e59fc670656121c9d7c01388c8b4b4585aa7b9ac3f7ae796f9eb1fadba1730a1860eed797feabb18832b5e8f003c0adaf0788d1016e7a8969144018ecc86140aa4553962aa739a4850b509b505e158c5f9e2d5376374652e9e6d81b19fa0351be229af136efbce681463cc53d7880c1eeca3411154474ff8a7b2bac034a2026646776a517bf63921c31fbbd6be7c3ff42aab28230bfe81d33800b892b262f3579b7a41925a59f5cc1d4f523577c19ff9f92023146fa26486595bd89a1ba459eb0b5cec0578c3a071dbec73eca054c723ab30ce8e69de32e779cd2f1030e39878ac6ea3cdca743b43aedefe1a9b4f2da861038e2759defef0b8cad11d4179f2f08881b53ccc203e558c0571e049d998a257b3279016aad0d7999b609f6331a0d0f88e286a70432ca7f50a5bb8fafbbe9230b4ccb1fa57361c163d6b9f84579d61f41585a022d07dc8e55a8de4d8f87641dae777819458a2bf1bb02c438480ff11621ca8442ec2946875cce247c8877051359e9c822670d37bb00fa806e60e8e890ce62540fda2d5b1c790ca1e005030ac6d8e63db577bb98be111ee146828f9c48ee6257d7627b93ea3dd11aac3412e63dfc7ca132a73c4f51e7650f3f8ecf57bfc18716990b492d50e0a3e5fbf6136e771b91f7283ec3326209265b9531d157f8a07a4117fc8fb29ba1363afc6f9f0608251ea595256727a5bbe28f42a42edfbfa9017680e32980d4ad381612612b2bc7ad91e82eca693ea4fc27049a99636b50a576f1e55c72202d582b150ef194c1419f53177ecf315ea6b0e2f1aa8cd8f59b165aa0d89561c537fb6141f5813b7a4968fe16afc703326113f68508d88ff8d0aee1e88a84c0ae56c72f27511290ced48e93e8c95419d14aed1a5b2e9b2c9c1070c593e5eb50bb9a80e14e9f9fe501f56b1b3140159e8213b75d48d14af472a604484cd8e7e7abb6820245ed3ab29f9947463a033c586194be45eadec8392c8614d83a1e9ca0fe5655fa14f7a9c1d1f8f2185a06193ff4a3c3e9a96b02310033ceaa25894e7c56a6147e691597098054e285d39656d3d459ec5d13243c062b6eb44e19a13bdfc0b3c96bd3d1aeb75bb6b080322aea23555993cb529243958bb1a0e5d5027e6c78155437242d1d13c1d6e442a0e3783147a08bbfc0c2529fb705ad27713df40486fd58f001977f25dfd3c202451c07010a3880bca63959ca61f10ed3871f1152166fce2b52135718a8ceb239a0664a31c62defaad70be4b920dce70549c10d9138fbbad7f291c5b73fa21c3889929b143bc1576b72f70667ac11052b686891085290d871db528b5cfdc10a6d563925227609f10d1768a0e02dc7471ad424f94f737d4e7eb0fb167f1434fc4ae2d49e152f06f0845b6db0a44f0d6f5e7410420e6bd1f430b1af956005bf72b51405a04d9a5d9906ceca52c22c855785c3c3ac4c3e9bf532d31bab321e1db66f6a9f7dc9c017f2b7d8dfeb933cf5bbae71311ae318f6d187ebc5c843be342b08a9a0ff7c4b9c4b0f4fa74b13296afe84b6481440d58332e07b3d051ed55219d28e77af6612134da4431b797c63ef55bc53831e2f421db620fee51ba0967e4ed7009ef90af2204259bbfbb54537fd35c2132fa8e7f9c84bf9938d248862c6ca1cca9f48b0b33aa1589185c4eabc1c32".HexToByteArray(); var result = hasher.Verify(header, solution); @@ -347,7 +347,7 @@ public void EquihashVerifier_Should_Not_Verify_Invalid_Solution() [Fact] public void EquihashVerifier_Should_Not_Verify_Fake_Solution() { - var hasher = EquihashSolver.Instance.Value; + var hasher = new EquihashSolver_ZCash(); var header = "0400000008e9694cc2120ec1b5733cc12687b609058eec4f7046a521ad1d1e3049b400003e7420ed6f40659de0305ef9b7ec037f4380ed9848bc1c015691c90aa16ff3930000000000000000000000000000000000000000000000000000000000000000c9310d5874e0001f000000000000000000000000000000010b000000000000000000000000000040".HexToByteArray(); var solution = Enumerable.Repeat((byte) 0, 1344).ToArray(); var result = hasher.Verify(header, solution); @@ -358,14 +358,14 @@ public void EquihashVerifier_Should_Not_Verify_Fake_Solution() [Fact] public void EquihashVerifier_Should_Throw_On_Null_Input() { - var hasher = EquihashSolver.Instance.Value; + var hasher = new EquihashSolver_ZCash(); Assert.Throws(() => hasher.Verify(null, null)); } [Fact] public void EquihashVerifier_Should_Throw_On_Wrong_Argument_Length() { - var hasher = EquihashSolver.Instance.Value; + var hasher = new EquihashSolver_ZCash(); Assert.Throws(() => hasher.Verify(new byte[3], null)); Assert.Throws(() => hasher.Verify(new byte[140], new byte[3])); } diff --git a/src/MiningCore/Banning/IntegratedBanManager.cs b/src/MiningCore/Banning/IntegratedBanManager.cs index 162f27746..e3e6676d1 100644 --- a/src/MiningCore/Banning/IntegratedBanManager.cs +++ b/src/MiningCore/Banning/IntegratedBanManager.cs @@ -36,8 +36,6 @@ public class IntegratedBanManager : IBanManager public bool IsBanned(IPAddress address) { - Contract.RequiresNonNull(address, nameof(address)); - var result = cache.Get(address.ToString()); return result != null; } diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs index d0f4a6ed0..fc0637201 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs @@ -63,6 +63,25 @@ public class BitcoinJob protected string[] merkleBranchesHex; protected MerkleTree mt; + protected Network NBitcoinNetworkType + { + get + { + switch (networkType) + { + case BitcoinNetworkType.Main: + return Network.Main; + case BitcoinNetworkType.Test: + return Network.TestNet; + case BitcoinNetworkType.RegTest: + return Network.RegTest; + + default: + throw new NotSupportedException("unsupported network type"); + } + } + } + /////////////////////////////////////////// // GetJobParams related properties @@ -75,7 +94,7 @@ public class BitcoinJob protected static byte[] scriptSigFinalBytes = new Script(Op.GetPushOp(Encoding.UTF8.GetBytes("/MiningCore/"))).ToBytes(); protected static byte[] sha256Empty = Enumerable.Repeat((byte) 0, 32).ToArray(); - protected static uint txVersion = 1u; // transaction version (currently 1) - see https://en.bitcoin.it/wiki/Transaction + protected uint txVersion = 1u; // transaction version (currently 1) - see https://en.bitcoin.it/wiki/Transaction protected static uint txInputCount = 1u; protected static uint txInPrevOutIndex = (uint) (Math.Pow(2, 32) - 1); @@ -135,7 +154,7 @@ protected virtual void BuildCoinbase() bs.ReadWriteAsVarInt(ref sigScriptLength); bs.ReadWrite(ref sigScriptInitialBytes); - // done + // done coinbaseInitial = stream.ToArray(); coinbaseInitialHex = coinbaseInitial.ToHexString(); } @@ -166,45 +185,19 @@ protected virtual void BuildCoinbase() protected virtual byte[] SerializeOutputTransaction(Transaction tx) { - var withDefaultWitnessCommitment = !string.IsNullOrEmpty(BlockTemplate.DefaultWitnessCommitment); - - var outputCount = (uint) tx.Outputs.Count; - if (withDefaultWitnessCommitment) - outputCount++; - using(var stream = new MemoryStream()) { var bs = new BitcoinStream(stream, true); - // write output count - bs.ReadWriteAsVarInt(ref outputCount); - - long amount; - byte[] raw; - uint rawLength; - - // serialize witness (segwit) - if (withDefaultWitnessCommitment) - { - amount = 0; - raw = BlockTemplate.DefaultWitnessCommitment.HexToByteArray(); - rawLength = (uint)raw.Length; - - bs.ReadWrite(ref amount); - bs.ReadWriteAsVarInt(ref rawLength); - bs.ReadWrite(ref raw); - } + // serialize outputs + var vout = tx.Outputs; + bs.ReadWrite(ref vout); - // serialize outputs - foreach (var output in tx.Outputs) + // serialize witness (segwit) + if (!string.IsNullOrEmpty(BlockTemplate.DefaultWitnessCommitment)) { - amount = output.Value.Satoshi; - var outScript = output.ScriptPubKey; - raw = outScript.ToBytes(true); - rawLength = (uint) raw.Length; - - bs.ReadWrite(ref amount); - bs.ReadWriteAsVarInt(ref rawLength); + var witScript = new WitScript(BlockTemplate.DefaultWitnessCommitment); + var raw = witScript.ToBytes(); bs.ReadWrite(ref raw); } @@ -239,7 +232,7 @@ protected virtual Transaction CreateOutputTransaction() { rewardToPool = new Money(BlockTemplate.CoinbaseValue * blockRewardMultiplier, MoneyUnit.Satoshi); - var tx = new Transaction(); + var tx = Transaction.Create(NBitcoinNetworkType); tx.Outputs.Insert(0, new TxOut(rewardToPool, poolAddressDestination) { @@ -290,7 +283,7 @@ protected virtual byte[] SerializeHeader(byte[] coinbaseHash, uint nTime, uint n protected virtual (Share Share, string BlockHex) ProcessShareInternal(StratumClient worker, string extraNonce2, uint nTime, uint nonce) { - var context = worker.GetContextAs(); + var context = worker.ContextAs(); var extraNonce1 = context.ExtraNonce1; // build coinbase @@ -488,7 +481,7 @@ public virtual (Share Share, string BlockHex) ProcessShare(StratumClient worker, Contract.Requires(!string.IsNullOrEmpty(nTime), $"{nameof(nTime)} must not be empty"); Contract.Requires(!string.IsNullOrEmpty(nonce), $"{nameof(nonce)} must not be empty"); - var context = worker.GetContextAs(); + var context = worker.ContextAs(); // validate nTime if (nTime.Length != 8) diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index 87194320b..1d6ae7682 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -39,7 +39,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.DaemonInterface; using MiningCore.Extensions; using MiningCore.JsonRpc; +using MiningCore.Messaging; using MiningCore.Notifications; +using MiningCore.Notifications.Messages; using MiningCore.Stratum; using MiningCore.Time; using MiningCore.Util; @@ -56,22 +58,20 @@ public class BitcoinJobManager : JobManagerBase { public BitcoinJobManager( IComponentContext ctx, - NotificationService notificationService, IMasterClock clock, + IMessageBus messageBus, IExtraNonceProvider extraNonceProvider) : - base(ctx) + base(ctx, messageBus) { Contract.RequiresNonNull(ctx, nameof(ctx)); - Contract.RequiresNonNull(notificationService, nameof(notificationService)); Contract.RequiresNonNull(clock, nameof(clock)); + Contract.RequiresNonNull(messageBus, nameof(messageBus)); Contract.RequiresNonNull(extraNonceProvider, nameof(extraNonceProvider)); - this.notificationService = notificationService; this.clock = clock; this.extraNonceProvider = extraNonceProvider; } - protected readonly NotificationService notificationService; protected readonly IMasterClock clock; protected DaemonClient daemon; protected readonly IExtraNonceProvider extraNonceProvider; @@ -326,7 +326,7 @@ private async Task UpdateNetworkStatsAsync() if (!string.IsNullOrEmpty(submitError)) { logger.Warn(() => $"[{LogCat}] Block {share.BlockHeight} submission failed with: {submitError}"); - notificationService.NotifyAdmin("Block submission failed", $"Pool {poolConfig.Id} {(!string.IsNullOrEmpty(share.Source) ? $"[{share.Source.ToUpper()}] " : string.Empty)}failed to submit block {share.BlockHeight}: {submitError}"); + messageBus.SendMessage(new AdminNotification("Block submission failed", $"Pool {poolConfig.Id} {(!string.IsNullOrEmpty(share.Source) ? $"[{share.Source.ToUpper()}] " : string.Empty)}failed to submit block {share.BlockHeight}: {submitError}")); return (false, null); } @@ -338,7 +338,7 @@ private async Task UpdateNetworkStatsAsync() if (!accepted) { logger.Warn(() => $"[{LogCat}] Block {share.BlockHeight} submission failed for pool {poolConfig.Id} because block was not found after submission"); - notificationService.NotifyAdmin($"[{share.PoolId.ToUpper()}]-[{share.Source}] Block submission failed", $"[{share.PoolId.ToUpper()}]-[{share.Source}] Block {share.BlockHeight} submission failed for pool {poolConfig.Id} because block was not found after submission"); + messageBus.SendMessage(new AdminNotification($"[{share.PoolId.ToUpper()}]-[{share.Source}] Block submission failed", $"[{share.PoolId.ToUpper()}]-[{share.Source}] Block {share.BlockHeight} submission failed for pool {poolConfig.Id} because block was not found after submission")); } return (accepted, block?.Transactions.FirstOrDefault()); @@ -451,7 +451,7 @@ public virtual object[] GetSubscriberData(StratumClient worker) { Contract.RequiresNonNull(worker, nameof(worker)); - var context = worker.GetContextAs(); + var context = worker.ContextAs(); // assign unique ExtraNonce1 to worker (miner) context.ExtraNonce1 = extraNonceProvider.Next(); @@ -501,7 +501,7 @@ public virtual async Task SubmitShareAsync(StratumClient worker, object s if (!(submission is object[] submitParams)) throw new StratumException(StratumError.Other, "invalid params"); - var context = worker.GetContextAs(); + var context = worker.ContextAs(); // extract params var workerValue = (submitParams[0] as string)?.Trim(); @@ -593,11 +593,15 @@ public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfi base.Configure(poolConfig, clusterConfig); } - protected override void ConfigureDaemons() + protected virtual void PostChainIdentifyConfigure() + { + } + + protected override void ConfigureDaemons() { var jsonSerializerSettings = ctx.Resolve(); - daemon = new DaemonClient(jsonSerializerSettings); + daemon = new DaemonClient(jsonSerializerSettings, messageBus, clusterConfig.ClusterName ?? poolConfig.PoolName, poolConfig.Id); daemon.Configure(poolConfig.Daemons); } @@ -685,12 +689,28 @@ protected override async Task PostStartInitAsync(CancellationToken ct) var daemonInfoResponse = hasLegacyDaemon ? results[2].Response.ToObject() : null; var difficultyResponse = results[3].Response.ToObject(); - // ensure pool owns wallet - if (!validateAddressResponse.IsValid) + // chain detection + if (!hasLegacyDaemon) + { + if (blockchainInfoResponse.Chain.ToLower() == "test") + networkType = BitcoinNetworkType.Test; + else if (blockchainInfoResponse.Chain.ToLower() == "regtest") + networkType = BitcoinNetworkType.RegTest; + else + networkType = BitcoinNetworkType.Main; + } + + else + networkType = daemonInfoResponse.Testnet ? BitcoinNetworkType.Test : BitcoinNetworkType.Main; + + PostChainIdentifyConfigure(); + + // ensure pool owns wallet + if (!validateAddressResponse.IsValid) logger.ThrowLogPoolStartupException($"Daemon reports pool-address '{poolConfig.Address}' as invalid", LogCat); - if (clusterConfig.PaymentProcessing?.Enabled == true && !validateAddressResponse.IsMine) - logger.ThrowLogPoolStartupException($"Daemon does not own pool-address '{poolConfig.Address}'", LogCat); + //if (clusterConfig.PaymentProcessing?.Enabled == true && !validateAddressResponse.IsMine) + // logger.ThrowLogPoolStartupException($"Daemon does not own pool-address '{poolConfig.Address}'", LogCat); isPoS = difficultyResponse.Values().Any(x => x.Path == "proof-of-stake"); @@ -707,20 +727,6 @@ protected override async Task PostStartInitAsync(CancellationToken ct) else poolAddressDestination = new PubKey(validateAddressResponse.PubKey); - // chain detection - if (!hasLegacyDaemon) - { - if (blockchainInfoResponse.Chain.ToLower() == "test") - networkType = BitcoinNetworkType.Test; - else if (blockchainInfoResponse.Chain.ToLower() == "regtest") - networkType = BitcoinNetworkType.RegTest; - else - networkType = BitcoinNetworkType.Main; - } - - else - networkType = daemonInfoResponse.Testnet ? BitcoinNetworkType.Test : BitcoinNetworkType.Main; - if(clusterConfig.PaymentProcessing?.Enabled == true && poolConfig.PaymentProcessing?.Enabled == true) ConfigureRewards(); @@ -751,7 +757,7 @@ protected override async Task PostStartInitAsync(CancellationToken ct) SetupJobUpdates(); } - protected virtual IDestination AddressToDestination(string address) + protected virtual IDestination AddressToDestination(string address) { return BitcoinUtils.AddressToDestination(address); } diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs index fe6640946..83c80db8d 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs @@ -30,6 +30,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Configuration; using MiningCore.DaemonInterface; using MiningCore.Extensions; +using MiningCore.Messaging; using MiningCore.Notifications; using MiningCore.Payments; using MiningCore.Persistence; @@ -62,8 +63,8 @@ public BitcoinPayoutHandler( IBalanceRepository balanceRepo, IPaymentRepository paymentRepo, IMasterClock clock, - NotificationService notificationService) : - base(cf, mapper, shareRepo, blockRepo, balanceRepo, paymentRepo, clock, notificationService) + IMessageBus messageBus) : + base(cf, mapper, shareRepo, blockRepo, balanceRepo, paymentRepo, clock, messageBus) { Contract.RequiresNonNull(ctx, nameof(ctx)); Contract.RequiresNonNull(balanceRepo, nameof(balanceRepo)); @@ -96,7 +97,7 @@ public virtual Task ConfigureAsync(ClusterConfig clusterConfig, PoolConfig poolC logger = LogUtil.GetPoolScopedLogger(typeof(BitcoinPayoutHandler), poolConfig); var jsonSerializerSettings = ctx.Resolve(); - daemon = new DaemonClient(jsonSerializerSettings); + daemon = new DaemonClient(jsonSerializerSettings, messageBus, clusterConfig.ClusterName ?? poolConfig.PoolName, poolConfig.Id); daemon.Configure(poolConfig.Daemons); return Task.FromResult(true); diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinPool.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinPool.cs index e9ee59d95..ad6b00e80 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinPool.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinPool.cs @@ -45,9 +45,8 @@ public BitcoinPool(IComponentContext ctx, IStatsRepository statsRepo, IMapper mapper, IMasterClock clock, - IMessageBus messageBus, - NotificationService notificationService) : - base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus, notificationService) + IMessageBus messageBus) : + base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus) { } } diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs index 5f409cbc6..e1ac83fba 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinPoolBase.cs @@ -32,7 +32,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.JsonRpc; using MiningCore.Messaging; using MiningCore.Mining; -using MiningCore.Notifications; +using MiningCore.Notifications.Messages; using MiningCore.Persistence; using MiningCore.Persistence.Repositories; using MiningCore.Stratum; @@ -52,9 +52,8 @@ public BitcoinPoolBase(IComponentContext ctx, IStatsRepository statsRepo, IMapper mapper, IMasterClock clock, - IMessageBus messageBus, - NotificationService notificationService) : - base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus, notificationService) + IMessageBus messageBus) : + base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus) { } @@ -71,7 +70,7 @@ protected virtual void OnSubscribe(StratumClient client, Timestamped(); + var context = client.ContextAs(); var requestParams = request.ParamsAs(); var data = new object[] @@ -106,7 +105,7 @@ protected virtual async Task OnAuthorizeAsync(StratumClient client, Timestamped< return; } - var context = client.GetContextAs(); + var context = client.ContextAs(); var requestParams = request.ParamsAs(); var workerValue = requestParams?.Length > 0 ? requestParams[0] : null; var password = requestParams?.Length > 1 ? requestParams[1] : null; @@ -160,7 +159,7 @@ protected virtual async Task OnAuthorizeAsync(StratumClient client, Timestamped< protected virtual async Task OnSubmitAsync(StratumClient client, Timestamped tsRequest) { var request = tsRequest.Value; - var context = client.GetContextAs(); + var context = client.ContextAs(); try { @@ -191,10 +190,14 @@ protected virtual async Task OnSubmitAsync(StratumClient client, Timestamped $"[{LogCat}] [{client.ConnectionId}] Share accepted: D={Math.Round(share.Difficulty, 3)}"); // update pool stats @@ -210,6 +213,9 @@ protected virtual async Task OnSubmitAsync(StratumClient client, Timestamped $"[{LogCat}] [{client.ConnectionId}] Share rejected: {ex.Code}"); @@ -222,7 +228,7 @@ protected virtual async Task OnSubmitAsync(StratumClient client, Timestamped tsRequest) { var request = tsRequest.Value; - var context = client.GetContextAs(); + var context = client.ContextAs(); // acknowledge client.Respond(true, request.Id); @@ -279,7 +285,7 @@ protected virtual void OnNewJob(object jobParams) ForEachClient(client => { - var context = client.GetContextAs(); + var context = client.ContextAs(); if (context.IsSubscribed && context.IsAuthorized) { @@ -375,7 +381,7 @@ protected override async Task OnRequestAsync(StratumClient client, case BitcoinStratumMethods.MiningMultiVersion: // ignored break; - + default: logger.Debug(() => $"[{LogCat}] [{client.ConnectionId}] Unsupported RPC request: {JsonConvert.SerializeObject(request, serializerSettings)}"); @@ -398,12 +404,15 @@ public override double HashrateFromShares(double shares, double interval) if ((poolConfig.Coin.Type == CoinType.XVG && poolConfig.Coin.Algorithm.ToLower() == "x17")) result *= 2.55; - return result; + if (poolConfig?.Coin?.Algorithm?.ToLower() == "scrypt") + result *= 1.5; + + return result; } protected override void OnVarDiffUpdate(StratumClient client, double newDiff) { - var context = client.GetContextAs(); + var context = client.ContextAs(); context.EnqueueNewDifficulty(newDiff); // apply immediately and notify client diff --git a/src/MiningCore/Blockchain/BitcoinGold/BitcoinGoldJob.cs b/src/MiningCore/Blockchain/BitcoinGold/BitcoinGoldJob.cs index 268c3934c..b2a44306f 100644 --- a/src/MiningCore/Blockchain/BitcoinGold/BitcoinGoldJob.cs +++ b/src/MiningCore/Blockchain/BitcoinGold/BitcoinGoldJob.cs @@ -21,9 +21,11 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; using System.Numerics; using MiningCore.Blockchain.Bitcoin; +using MiningCore.Blockchain.Bitcoin.DaemonResponses; using MiningCore.Blockchain.ZCash; using MiningCore.Blockchain.ZCash.DaemonResponses; using MiningCore.Configuration; @@ -40,13 +42,18 @@ namespace MiningCore.Blockchain.BitcoinGold { public class BitcoinGoldJob : ZCashJob { - #region Overrides of ZCashJob + public BitcoinGoldJob() + { + txVersion = 1u; + } - protected override Transaction CreateOutputTransaction() + #region Overrides of ZCashJob + + protected override Transaction CreateOutputTransaction() { rewardToPool = new Money(BlockTemplate.CoinbaseValue * blockRewardMultiplier, MoneyUnit.Satoshi); - var tx = new Transaction(); + var tx = Transaction.Create(NBitcoinNetworkType); // pool reward (t-addr) tx.AddOutput(rewardToPool, poolAddressDestination); @@ -97,20 +104,21 @@ public override void Init(ZCashBlockTemplate blockTemplate, string jobId, this.poolAddressDestination = poolAddressDestination; this.networkType = networkType; - if (ZCashConstants.CoinbaseTxConfig.TryGetValue(poolConfig.Coin.Type, out var coinbaseTx)) - coinbaseTx.TryGetValue(networkType, out coinbaseTxConfig); + if (ZCashConstants.Chains.TryGetValue(poolConfig.Coin.Type, out var coinbaseTx)) + coinbaseTx.TryGetValue(networkType, out chainConfig); BlockTemplate = blockTemplate; JobId = jobId; - Difficulty = (double)new BigRational(coinbaseTxConfig.Diff1b, BlockTemplate.Target.HexToByteArray().ToBigInteger()); + Difficulty = (double)new BigRational(chainConfig.Diff1b, BlockTemplate.Target.HexToByteArray().ReverseArray().ToBigInteger()); this.isPoS = isPoS; this.shareMultiplier = shareMultiplier; this.headerHasher = headerHasher; this.blockHasher = blockHasher; + this.equihash = chainConfig.Solver(); - if (!string.IsNullOrEmpty(BlockTemplate.Target)) + if (!string.IsNullOrEmpty(BlockTemplate.Target)) blockTargetValue = new uint256(BlockTemplate.Target); else { diff --git a/src/MiningCore/Blockchain/BitcoinGold/BitcoinGoldJobManager.cs b/src/MiningCore/Blockchain/BitcoinGold/BitcoinGoldJobManager.cs index 7f291a05e..fda94cbad 100644 --- a/src/MiningCore/Blockchain/BitcoinGold/BitcoinGoldJobManager.cs +++ b/src/MiningCore/Blockchain/BitcoinGold/BitcoinGoldJobManager.cs @@ -4,6 +4,7 @@ using MiningCore.Blockchain.ZCash; using MiningCore.Blockchain.ZCash.DaemonResponses; using MiningCore.DaemonInterface; +using MiningCore.Messaging; using MiningCore.Notifications; using MiningCore.Time; @@ -12,9 +13,9 @@ namespace MiningCore.Blockchain.BitcoinGold public class BitcoinGoldJobManager : ZCashJobManager { public BitcoinGoldJobManager(IComponentContext ctx, - NotificationService notificationService, IMasterClock clock, - IExtraNonceProvider extraNonceProvider) : base(ctx, notificationService, clock, extraNonceProvider) + IMessageBus messageBus, + IExtraNonceProvider extraNonceProvider) : base(ctx, clock, messageBus, extraNonceProvider) { getBlockTemplateParams = new object[] { diff --git a/src/MiningCore/Blockchain/BitcoinGold/BitcoinGoldPool.cs b/src/MiningCore/Blockchain/BitcoinGold/BitcoinGoldPool.cs index 5c6627b8d..f865f5da1 100644 --- a/src/MiningCore/Blockchain/BitcoinGold/BitcoinGoldPool.cs +++ b/src/MiningCore/Blockchain/BitcoinGold/BitcoinGoldPool.cs @@ -25,7 +25,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Blockchain.ZCash.DaemonResponses; using MiningCore.Configuration; using MiningCore.Messaging; -using MiningCore.Notifications; using MiningCore.Persistence; using MiningCore.Persistence.Repositories; using MiningCore.Time; @@ -42,9 +41,8 @@ public BitcoinGoldPool(IComponentContext ctx, IStatsRepository statsRepo, IMapper mapper, IMasterClock clock, - IMessageBus messageBus, - NotificationService notificationService) : - base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus, notificationService) + IMessageBus messageBus) : + base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus) { } diff --git a/src/MiningCore/Blockchain/CoinMetaData.cs b/src/MiningCore/Blockchain/CoinMetaData.cs index 7fed93d36..1d569dc0d 100644 --- a/src/MiningCore/Blockchain/CoinMetaData.cs +++ b/src/MiningCore/Blockchain/CoinMetaData.cs @@ -49,7 +49,7 @@ public static class CoinMetaData { CoinType.MONA, new Dictionary { { string.Empty, $"https://bchain.info/MONA/block/{BlockHeightPH}" }}}, { CoinType.GLT, new Dictionary { { string.Empty, $"https://bchain.info/GLT/block/{BlockHeightPH}" }}}, { CoinType.VTC, new Dictionary { { string.Empty, $"https://bchain.info/VTC/block/{BlockHeightPH}" }}}, - { CoinType.BTG, new Dictionary { { string.Empty, $"https://btg-bitcore2.trezor.io/block/{BlockHashPH}" } }}, + { CoinType.BTG, new Dictionary { { string.Empty, $"https://explorer.bitcoingold.org/insight/block/{BlockHashPH}" } }}, { CoinType.ELLA, new Dictionary { { string.Empty, $"https://explorer.ellaism.org/block/{BlockHeightPH}" }}}, { CoinType.EXP, new Dictionary { { string.Empty, $"http://www.gander.tech/blocks/{BlockHeightPH}" }}}, { CoinType.AEON, new Dictionary { { string.Empty, $"https://chainradar.com/aeon/block/{BlockHeightPH}" }}}, @@ -87,7 +87,7 @@ public static class CoinMetaData { CoinType.STAK, "https://straks.info/transaction/{0}" }, { CoinType.GLT, "https://bchain.info/GLT/tx/{0}" }, { CoinType.VTC, "https://bchain.info/VTC/tx/{0}" }, - { CoinType.BTG, "https://btgexp.com/tx/{0}" }, + { CoinType.BTG, "https://explorer.bitcoingold.org/insight/tx/{0}" }, { CoinType.ELLA, "https://explorer.ellaism.org/tx/{0}" }, { CoinType.EXP, "http://www.gander.tech/tx/{0}" }, { CoinType.AEON, "https://chainradar.com/aeon/transaction/{0}" }, @@ -124,7 +124,7 @@ public static class CoinMetaData { CoinType.STAK, "https://straks.info/address/{0}" }, { CoinType.GLT, "https://bchain.info/GLT/addr/{0}" }, { CoinType.VTC, "https://bchain.info/VTC/addr/{0}" }, - { CoinType.BTG, "https://btgexp.com/address/{0}" }, + { CoinType.BTG, "https://explorer.bitcoingold.org/insight/address/{0}" }, { CoinType.ELLA, "https://explorer.ellaism.org/addr/{0}" }, { CoinType.EXP, "http://www.gander.tech/address/{0}" }, { CoinType.MOON, "https://chainz.cryptoid.info/moon/address.dws?{0}.htm" }, diff --git a/src/MiningCore/Blockchain/Dash/DashJob.cs b/src/MiningCore/Blockchain/Dash/DashJob.cs index c8143901a..1c4b5b97a 100644 --- a/src/MiningCore/Blockchain/Dash/DashJob.cs +++ b/src/MiningCore/Blockchain/Dash/DashJob.cs @@ -34,8 +34,10 @@ protected override Transaction CreateOutputTransaction() var blockReward = new Money(BlockTemplate.CoinbaseValue * blockRewardMultiplier, MoneyUnit.Satoshi); rewardToPool = new Money(BlockTemplate.CoinbaseValue, MoneyUnit.Satoshi); - var tx = new Transaction(); - rewardToPool = CreateDashOutputs(tx, blockReward); + var tx = Transaction.Create(NBitcoinNetworkType); + + // outputs + rewardToPool = CreateDashOutputs(tx, blockReward); // Finally distribute remaining funds to pool tx.Outputs.Insert(0, new TxOut(rewardToPool, poolAddressDestination) diff --git a/src/MiningCore/Blockchain/Dash/DashPayoutHandler.cs b/src/MiningCore/Blockchain/Dash/DashPayoutHandler.cs index d88ce285a..acde0e1a4 100644 --- a/src/MiningCore/Blockchain/Dash/DashPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Dash/DashPayoutHandler.cs @@ -25,6 +25,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using AutoMapper; using MiningCore.Blockchain.Bitcoin; using MiningCore.Configuration; +using MiningCore.Messaging; using MiningCore.Notifications; using MiningCore.Persistence; using MiningCore.Persistence.Model; @@ -48,8 +49,8 @@ public DashPayoutHandler( IBalanceRepository balanceRepo, IPaymentRepository paymentRepo, IMasterClock clock, - NotificationService notificationService) : - base(ctx, cf, mapper, shareRepo, blockRepo, balanceRepo, paymentRepo, clock, notificationService) + IMessageBus messageBus) : + base(ctx, cf, mapper, shareRepo, blockRepo, balanceRepo, paymentRepo, clock, messageBus) { } diff --git a/src/MiningCore/Blockchain/Dash/DashPool.cs b/src/MiningCore/Blockchain/Dash/DashPool.cs index 875fb851f..3c8354ac0 100644 --- a/src/MiningCore/Blockchain/Dash/DashPool.cs +++ b/src/MiningCore/Blockchain/Dash/DashPool.cs @@ -23,7 +23,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Blockchain.Bitcoin; using MiningCore.Configuration; using MiningCore.Messaging; -using MiningCore.Notifications; using MiningCore.Persistence; using MiningCore.Persistence.Repositories; using MiningCore.Time; @@ -40,9 +39,8 @@ public DashPool(IComponentContext ctx, IStatsRepository statsRepo, IMapper mapper, IMasterClock clock, - IMessageBus messageBus, - NotificationService notificationService) : - base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus, notificationService) + IMessageBus messageBus) : + base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus) { } } diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumBlockTemplate.cs b/src/MiningCore/Blockchain/Ethereum/EthereumBlockTemplate.cs index 6dacd3998..3bdd34208 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumBlockTemplate.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumBlockTemplate.cs @@ -28,11 +28,6 @@ public class EthereumBlockTemplate /// public string Target { get; set; } - /// - /// hash of the parent block. - /// - public string ParentHash { get; set; } - /// /// integer of the difficulty for this block /// diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumConstants.cs b/src/MiningCore/Blockchain/Ethereum/EthereumConstants.cs index 858cfa6d5..eeb7686e9 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumConstants.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumConstants.cs @@ -20,7 +20,7 @@ public class EthereumConstants public const string BlockTypeUncle = "uncle"; #if !DEBUG - public const int MinPayoutPeerCount = 3; + public const int MinPayoutPeerCount = 1; #else public const int MinPayoutPeerCount = 1; #endif diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumJob.cs b/src/MiningCore/Blockchain/Ethereum/EthereumJob.cs index 6e3de9d8d..01df0d947 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumJob.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumJob.cs @@ -61,7 +61,7 @@ private void RegisterNonce(StratumClient worker, string nonce) } // assemble full-nonce - var context = worker.GetContextAs(); + var context = worker.ContextAs(); var fullNonceHex = context.ExtraNonce1 + nonce; if (!ulong.TryParse(fullNonceHex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var fullNonce)) @@ -74,9 +74,8 @@ private void RegisterNonce(StratumClient worker, string nonce) if (!dag.Compute(logger, BlockTemplate.Header.HexToByteArray(), fullNonce, out var mixDigest, out var resultBytes)) throw new StratumException(StratumError.MinusOne, "bad hash"); - resultBytes.ReverseArray(); - - // test if share meets at least workers current difficulty + // test if share meets at least workers current difficulty + resultBytes.ReverseArray(); var resultValue = new uint256(resultBytes); var resultValueBig = resultBytes.ToBigInteger(); var shareDiff = (double) BigInteger.Divide(EthereumConstants.BigMaxValue, resultValueBig) / EthereumConstants.Pow2x32; @@ -112,7 +111,7 @@ private void RegisterNonce(StratumClient worker, string nonce) UserAgent = context.UserAgent, IsBlockCandidate = isBlockCandidate, Difficulty = stratumDifficulty * EthereumConstants.Pow2x32, - BlockHash = mixDigest.ToHexString(true) // OW: is this correct? + BlockHash = mixDigest.ToHexString(true) }; if (share.IsBlockCandidate) diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs index c86bda8e5..5d0e9238b 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs @@ -20,9 +20,11 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Net; +using System.Numerics; using System.Reactive.Linq; using System.Text; using System.Threading; @@ -37,7 +39,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.DaemonInterface; using MiningCore.Extensions; using MiningCore.JsonRpc; +using MiningCore.Messaging; using MiningCore.Notifications; +using MiningCore.Notifications.Messages; using MiningCore.Stratum; using MiningCore.Time; using MiningCore.Util; @@ -54,17 +58,16 @@ public class EthereumJobManager : JobManagerBase { public EthereumJobManager( IComponentContext ctx, - NotificationService notificationService, IMasterClock clock, + IMessageBus messageBus, JsonSerializerSettings serializerSettings) : - base(ctx) + base(ctx, messageBus) { Contract.RequiresNonNull(ctx, nameof(ctx)); - Contract.RequiresNonNull(notificationService, nameof(notificationService)); Contract.RequiresNonNull(clock, nameof(clock)); + Contract.RequiresNonNull(messageBus, nameof(messageBus)); this.clock = clock; - this.notificationService = notificationService; serializer = new JsonSerializer { @@ -77,7 +80,6 @@ public EthereumJobManager( private EthereumNetworkType networkType; private ParityChainType chainType; private EthashFull ethash; - private readonly NotificationService notificationService; private readonly IMasterClock clock; private readonly EthereumExtraNonceProvider extraNonceProvider = new EthereumExtraNonceProvider(); @@ -159,35 +161,28 @@ private async Task GetBlockTemplateAsync() { logger.LogInvoke(LogCat); - var commands = new[] + var response = await daemon.ExecuteCmdAnyAsync(EC.GetWork); + + if (response.Error != null) { - new DaemonCmd(EC.GetBlockByNumber, new[] { (object) "pending", true }), - new DaemonCmd(EC.GetWork), - }; - - var results = await daemon.ExecuteBatchAnyAsync(commands); + logger.Warn(() => $"[{LogCat}] Error(s) refreshing blocktemplate: {response.Error})"); + return null; + } - if (results.Any(x => x.Error != null)) + if (response.Response == null) { - var errors = results.Where(x => x.Error != null) - .ToArray(); - - if (errors.Any()) - { - logger.Warn(() => $"[{LogCat}] Error(s) refreshing blocktemplate: {string.Join(", ", errors.Select(y => y.Error.Message))})"); - return null; - } + logger.Warn(() => $"[{LogCat}] Error(s) refreshing blocktemplate: {EC.GetWork} returned null response"); + return null; } // extract results - var block = results[0].Response.ToObject(); - var work = results[1].Response.ToObject(); - var result = AssembleBlockTemplate(block, work); + var work = response.Response.ToObject(); + var result = AssembleBlockTemplate(work); return result; } - private EthereumBlockTemplate AssembleBlockTemplate(Block block, string[] work) + private EthereumBlockTemplate AssembleBlockTemplate(string[] work) { // only parity returns the 4th element (block height) if (work.Length < 4) @@ -196,23 +191,18 @@ private EthereumBlockTemplate AssembleBlockTemplate(Block block, string[] work) return null; } - // make sure block matches work + // extract values var height = work[3].IntegralFromHex(); - - if (height != block.Height) - { - logger.Debug(() => $"[{LogCat}] Discarding block template update as getWork result is not related to pending block"); - return null; - } - - var result = new EthereumBlockTemplate + var targetString = work[2]; + var target = BigInteger.Parse(targetString.Substring(2), NumberStyles.HexNumber); + + var result = new EthereumBlockTemplate { Header = work[0], Seed = work[1], - Target = work[2], - Difficulty = block.Difficulty.IntegralFromHex(), - Height = block.Height.Value, - ParentHash = block.ParentHash, + Target = targetString, + Difficulty = (ulong) BigInteger.Divide(EthereumConstants.BigMaxValue, target), + Height = height, }; return result; @@ -310,8 +300,8 @@ private async Task SubmitBlockAsync(Share share, string fullNonceHex, stri { var error = response.Error?.Message ?? response?.Response?.ToString(); - logger.Warn(() => $"[{LogCat}] Block {share.BlockHeight} submission failed with: {error}"); - notificationService.NotifyAdmin("Block submission failed", $"Pool {poolConfig.Id} {(!string.IsNullOrEmpty(share.Source) ? $"[{share.Source.ToUpper()}] " : string.Empty)}failed to submit block {share.BlockHeight}: {error}"); + logger.Warn(() => $"[{LogCat}] Block {share.BlockHeight} submission failed with: {error}"); + messageBus.SendMessage(new AdminNotification("Block submission failed", $"Pool {poolConfig.Id} {(!string.IsNullOrEmpty(share.Source) ? $"[{share.Source.ToUpper()}] " : string.Empty)}failed to submit block {share.BlockHeight}: {error}")); return false; } @@ -397,7 +387,7 @@ public bool ValidateAddress(string address) public void PrepareWorker(StratumClient client) { - var context = client.GetContextAs(); + var context = client.ContextAs(); context.ExtraNonce1 = extraNonceProvider.Next(); } @@ -408,7 +398,7 @@ public async Task SubmitShareAsync(StratumClient worker, Contract.RequiresNonNull(request, nameof(request)); logger.LogInvoke(LogCat, new[] { worker.ConnectionId }); - var context = worker.GetContextAs(); + var context = worker.ContextAs(); // var miner = request[0]; var jobId = request[1]; @@ -459,7 +449,7 @@ protected override void ConfigureDaemons() { var jsonSerializerSettings = ctx.Resolve(); - daemon = new DaemonClient(jsonSerializerSettings); + daemon = new DaemonClient(jsonSerializerSettings, messageBus, clusterConfig.ClusterName ?? poolConfig.PoolName, poolConfig.Id); daemon.Configure(daemonEndpoints); } @@ -635,24 +625,6 @@ protected virtual void SetupJobUpdates() logger.Info(() => $"[{LogCat}] Subscribing to WebSocket push-updates from {string.Join(", ", wsDaemons.Keys.Select(x=> x.Host).Distinct())}"); - // stream pending blocks - var pendingBlockObs = daemon.WebsocketSubscribe(wsDaemons, EC.ParitySubscribe, new[] { (object) EC.GetBlockByNumber, new[] { "pending", (object)true } }) - .Select(data => - { - try - { - var psp = DeserializeRequest(data).ParamsAs>(); - return psp?.Result; - } - - catch (Exception ex) - { - logger.Info(() => $"[{LogCat}] Error deserializing pending block: {ex.Message}"); - } - - return null; - }); - // stream work updates var getWorkObs = daemon.WebsocketSubscribe(wsDaemons, EC.ParitySubscribe, new[] { (object) EC.GetWork }) .Select(data => @@ -671,10 +643,8 @@ protected virtual void SetupJobUpdates() return null; }); - Jobs = Observable.CombineLatest( - pendingBlockObs.Where(x => x != null), - getWorkObs.Where(x => x != null), - AssembleBlockTemplate) + Jobs = getWorkObs.Where(x => x != null) + .Select(AssembleBlockTemplate) .Select(UpdateJob) .Do(isNew => { diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumPayoutHandler.cs b/src/MiningCore/Blockchain/Ethereum/EthereumPayoutHandler.cs index 6ad206bd8..c52e39cc2 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumPayoutHandler.cs @@ -32,6 +32,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Configuration; using MiningCore.DaemonInterface; using MiningCore.Extensions; +using MiningCore.Messaging; using MiningCore.Notifications; using MiningCore.Payments; using MiningCore.Persistence; @@ -59,8 +60,8 @@ public EthereumPayoutHandler( IBalanceRepository balanceRepo, IPaymentRepository paymentRepo, IMasterClock clock, - NotificationService notificationService) : - base(cf, mapper, shareRepo, blockRepo, balanceRepo, paymentRepo, clock, notificationService) + IMessageBus messageBus) : + base(cf, mapper, shareRepo, blockRepo, balanceRepo, paymentRepo, clock, messageBus) { Contract.RequiresNonNull(ctx, nameof(ctx)); Contract.RequiresNonNull(balanceRepo, nameof(balanceRepo)); @@ -95,7 +96,7 @@ public async Task ConfigureAsync(ClusterConfig clusterConfig, PoolConfig poolCon .Where(x => string.IsNullOrEmpty(x.Category)) .ToArray(); - daemon = new DaemonClient(jsonSerializerSettings); + daemon = new DaemonClient(jsonSerializerSettings, messageBus, clusterConfig.ClusterName ?? poolConfig.PoolName, poolConfig.Id); daemon.Configure(daemonEndpoints); await DetectChainAsync(); diff --git a/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs b/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs index a385bb4b2..25083dbcd 100644 --- a/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs +++ b/src/MiningCore/Blockchain/Ethereum/EthereumPool.cs @@ -33,7 +33,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.JsonRpc; using MiningCore.Messaging; using MiningCore.Mining; -using MiningCore.Notifications; +using MiningCore.Notifications.Messages; using MiningCore.Persistence; using MiningCore.Persistence.Repositories; using MiningCore.Stratum; @@ -52,9 +52,8 @@ public EthereumPool(IComponentContext ctx, IStatsRepository statsRepo, IMapper mapper, IMasterClock clock, - IMessageBus messageBus, - NotificationService notificationService) : - base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus, notificationService) + IMessageBus messageBus) : + base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus) { } @@ -64,7 +63,7 @@ public EthereumPool(IComponentContext ctx, private void OnSubscribe(StratumClient client, Timestamped tsRequest) { var request = tsRequest.Value; - var context = client.GetContextAs(); + var context = client.ContextAs(); if (request.Id == null) { @@ -104,7 +103,7 @@ private void OnSubscribe(StratumClient client, Timestamped tsReq private void OnAuthorize(StratumClient client, Timestamped tsRequest) { var request = tsRequest.Value; - var context = client.GetContextAs(); + var context = client.ContextAs(); if (request.Id == null) { @@ -149,7 +148,7 @@ private void OnAuthorize(StratumClient client, Timestamped tsReq private async Task OnSubmitAsync(StratumClient client, Timestamped tsRequest) { var request = tsRequest.Value; - var context = client.GetContextAs(); + var context = client.ContextAs(); try { @@ -186,13 +185,16 @@ private async Task OnSubmitAsync(StratumClient client, Timestamped $"[{LogCat}] [{client.ConnectionId}] Share accepted: D={Math.Round(share.Difficulty / EthereumConstants.Pow2x32, 3)}"); + EnsureInitialWorkSent(client); // update pool stats if (share.IsBlockCandidate) @@ -207,6 +209,9 @@ private async Task OnSubmitAsync(StratumClient client, Timestamped $"[{LogCat}] [{client.ConnectionId}] Share rejected: {ex.Code}"); @@ -218,7 +223,7 @@ private async Task OnSubmitAsync(StratumClient client, Timestamped(); + var context = client.ContextAs(); lock (context) { @@ -241,7 +246,7 @@ private void OnNewJob(object jobParams) ForEachClient(client => { - var context = client.GetContextAs(); + var context = client.ContextAs(); if (context.IsSubscribed && context.IsAuthorized && context.IsInitialWorkSent) { @@ -338,7 +343,7 @@ protected override void OnVarDiffUpdate(StratumClient client, double newDiff) base.OnVarDiffUpdate(client, newDiff); // apply immediately and notify client - var context = client.GetContextAs(); + var context = client.ContextAs(); if (context.HasPendingDifficulty) { diff --git a/src/MiningCore/Blockchain/Flo/FloJobManager.cs b/src/MiningCore/Blockchain/Flo/FloJobManager.cs index 7e7634705..709e09bd8 100644 --- a/src/MiningCore/Blockchain/Flo/FloJobManager.cs +++ b/src/MiningCore/Blockchain/Flo/FloJobManager.cs @@ -22,12 +22,11 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System.Threading.Tasks; using Autofac; using MiningCore.Blockchain.Bitcoin; -using MiningCore.Blockchain.Bitcoin.Configuration; using MiningCore.Blockchain.Bitcoin.DaemonResponses; using MiningCore.Blockchain.Flo.Configuration; using MiningCore.Configuration; using MiningCore.Extensions; -using MiningCore.Notifications; +using MiningCore.Messaging; using MiningCore.Time; using NLog; @@ -37,10 +36,10 @@ public class FloJobManager : BitcoinJobManager { public FloJobManager( IComponentContext ctx, - NotificationService notificationService, IMasterClock clock, + IMessageBus messageBus, IExtraNonceProvider extraNonceProvider) : - base(ctx, notificationService, clock, extraNonceProvider) + base(ctx, clock, messageBus, extraNonceProvider) { } diff --git a/src/MiningCore/Blockchain/Flo/FloPayoutHandler.cs b/src/MiningCore/Blockchain/Flo/FloPayoutHandler.cs index 3ae6f4e72..06c22858b 100644 --- a/src/MiningCore/Blockchain/Flo/FloPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Flo/FloPayoutHandler.cs @@ -30,6 +30,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Configuration; using MiningCore.DaemonInterface; using MiningCore.Extensions; +using MiningCore.Messaging; using MiningCore.Notifications; using MiningCore.Persistence; using MiningCore.Persistence.Model; @@ -54,8 +55,8 @@ public FloPayoutHandler( IBalanceRepository balanceRepo, IPaymentRepository paymentRepo, IMasterClock clock, - NotificationService notificationService) : - base(ctx, cf, mapper, shareRepo, blockRepo, balanceRepo, paymentRepo, clock, notificationService) + IMessageBus messageBus) : + base(ctx, cf, mapper, shareRepo, blockRepo, balanceRepo, paymentRepo, clock, messageBus) { } @@ -77,7 +78,7 @@ public override Task ConfigureAsync(ClusterConfig clusterConfig, PoolConfig pool logger = LogUtil.GetPoolScopedLogger(typeof(BitcoinPayoutHandler), poolConfig); var jsonSerializerSettings = ctx.Resolve(); - daemon = new DaemonClient(jsonSerializerSettings); + daemon = new DaemonClient(jsonSerializerSettings, messageBus, clusterConfig.ClusterName ?? poolConfig.PoolName, poolConfig.Id); daemon.Configure(poolConfig.Daemons); return Task.FromResult(true); diff --git a/src/MiningCore/Blockchain/Flo/FloPool.cs b/src/MiningCore/Blockchain/Flo/FloPool.cs index fe72d0662..dc1622565 100644 --- a/src/MiningCore/Blockchain/Flo/FloPool.cs +++ b/src/MiningCore/Blockchain/Flo/FloPool.cs @@ -26,12 +26,10 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Configuration; using MiningCore.Extensions; using MiningCore.Messaging; -using MiningCore.Notifications; using MiningCore.Persistence; using MiningCore.Persistence.Repositories; using MiningCore.Time; using MiningCore.Util; -using MiningCore.Mining; using Newtonsoft.Json; namespace MiningCore.Blockchain.Flo @@ -45,9 +43,8 @@ public FloPool(IComponentContext ctx, IStatsRepository statsRepo, IMapper mapper, IMasterClock clock, - IMessageBus messageBus, - NotificationService notificationService) : - base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus, notificationService) + IMessageBus messageBus) : + base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus) { } @@ -61,7 +58,7 @@ public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfi if (string.IsNullOrEmpty(extraConfig?.FloData)) logger.ThrowLogPoolStartupException("Pool coinbase FloData is not configured", LogCat); - + } protected override BitcoinJobManager CreateJobManager() diff --git a/src/MiningCore/Blockchain/JobManagerBase.cs b/src/MiningCore/Blockchain/JobManagerBase.cs index 07edce4d7..a5477cd89 100644 --- a/src/MiningCore/Blockchain/JobManagerBase.cs +++ b/src/MiningCore/Blockchain/JobManagerBase.cs @@ -32,6 +32,8 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using Autofac; using MiningCore.Configuration; using MiningCore.Extensions; +using MiningCore.Messaging; +using MiningCore.Notifications.Messages; using MiningCore.Util; using NetMQ; using NetMQ.Sockets; @@ -42,14 +44,17 @@ namespace MiningCore.Blockchain { public abstract class JobManagerBase { - protected JobManagerBase(IComponentContext ctx) + protected JobManagerBase(IComponentContext ctx, IMessageBus messageBus) { Contract.RequiresNonNull(ctx, nameof(ctx)); + Contract.RequiresNonNull(messageBus, nameof(messageBus)); this.ctx = ctx; + this.messageBus = messageBus; } protected readonly IComponentContext ctx; + protected readonly IMessageBus messageBus; protected ClusterConfig clusterConfig; protected TJob currentJob; @@ -153,7 +158,12 @@ protected IObservable BtStreamSubscribe(ZmqPubSubEndpointConfig config) // convert var json = Encoding.UTF8.GetString(data); + // publish obs.OnNext(json); + + // telemetry + messageBus.SendMessage(new TelemetryEvent(clusterConfig.ClusterName ?? poolConfig.PoolName, poolConfig.Id, + TelemetryCategory.BtStream, DateTime.UtcNow - DateTimeOffset.FromUnixTimeSeconds(timestamp))); } } } diff --git a/src/MiningCore/Blockchain/Monero/MoneroJob.cs b/src/MiningCore/Blockchain/Monero/MoneroJob.cs index f18fb3b0e..9c59a31b3 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroJob.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroJob.cs @@ -148,7 +148,7 @@ public void PrepareWorkerJob(MoneroWorkerJob workerJob, out string blob, out str Contract.Requires(!string.IsNullOrEmpty(workerHash), $"{nameof(workerHash)} must not be empty"); Contract.Requires(workerExtraNonce != 0, $"{nameof(workerExtraNonce)} must not be empty"); - var context = worker.GetContextAs(); + var context = worker.ContextAs(); // validate nonce if (!MoneroConstants.RegexValidNonce.IsMatch(nonce)) diff --git a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs index 606a11b70..ebbb8f65a 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroJobManager.cs @@ -29,7 +29,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System.Reactive.Disposables; using System.Reactive.Linq; using System.Security.Cryptography; -using System.Text; using System.Threading; using System.Threading.Tasks; using Autofac; @@ -43,8 +42,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.DaemonInterface; using MiningCore.Extensions; using MiningCore.JsonRpc; +using MiningCore.Messaging; using MiningCore.Native; -using MiningCore.Notifications; +using MiningCore.Notifications.Messages; using MiningCore.Stratum; using MiningCore.Time; using MiningCore.Util; @@ -62,15 +62,14 @@ public class MoneroJobManager : JobManagerBase { public MoneroJobManager( IComponentContext ctx, - NotificationService notificationService, - IMasterClock clock) : - base(ctx) + IMasterClock clock, + IMessageBus messageBus) : + base(ctx, messageBus) { Contract.RequiresNonNull(ctx, nameof(ctx)); - Contract.RequiresNonNull(notificationService, nameof(notificationService)); Contract.RequiresNonNull(clock, nameof(clock)); + Contract.RequiresNonNull(messageBus, nameof(messageBus)); - this.notificationService = notificationService; this.clock = clock; using(var rng = RandomNumberGenerator.Create()) @@ -84,7 +83,6 @@ public MoneroJobManager( private DaemonEndpointConfig[] daemonEndpoints; private DaemonClient daemon; private DaemonClient walletDaemon; - private readonly NotificationService notificationService; private readonly IMasterClock clock; private MoneroNetworkType networkType; private MoneroPoolConfigExtra extraPoolConfig; @@ -215,7 +213,7 @@ private async Task SubmitBlockAsync(Share share, string blobHex, string bl var error = response.Error?.Message ?? response.Response?.Status; logger.Warn(() => $"[{LogCat}] Block {share.BlockHeight} [{blobHash.Substring(0, 6)}] submission failed with: {error}"); - notificationService.NotifyAdmin("Block submission failed", $"Pool {poolConfig.Id} {(!string.IsNullOrEmpty(share.Source) ? $"[{share.Source.ToUpper()}] " : string.Empty)}failed to submit block {share.BlockHeight}: {error}"); + messageBus.SendMessage(new AdminNotification("Block submission failed", $"Pool {poolConfig.Id} {(!string.IsNullOrEmpty(share.Source) ? $"[{share.Source.ToUpper()}] " : string.Empty)}failed to submit block {share.BlockHeight}: {error}")); return false; } @@ -319,7 +317,7 @@ public async Task SubmitShareAsync(StratumClient worker, Contract.RequiresNonNull(request, nameof(request)); logger.LogInvoke(LogCat, new[] { worker.ConnectionId }); - var context = worker.GetContextAs(); + var context = worker.ContextAs(); var job = currentJob; if (workerJob.Height != job?.BlockTemplate.Height) @@ -374,13 +372,13 @@ protected override void ConfigureDaemons() { var jsonSerializerSettings = ctx.Resolve(); - daemon = new DaemonClient(jsonSerializerSettings); + daemon = new DaemonClient(jsonSerializerSettings, messageBus, clusterConfig.ClusterName ?? poolConfig.PoolName, poolConfig.Id); daemon.Configure(daemonEndpoints); if (clusterConfig.PaymentProcessing?.Enabled == true && poolConfig.PaymentProcessing?.Enabled == true) { // also setup wallet daemon - walletDaemon = new DaemonClient(jsonSerializerSettings); + walletDaemon = new DaemonClient(jsonSerializerSettings, messageBus, clusterConfig.ClusterName ?? poolConfig.PoolName, poolConfig.Id); walletDaemon.Configure(walletDaemonEndpoints); } } diff --git a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs index 0b1881312..6b144f94a 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs @@ -31,6 +31,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Configuration; using MiningCore.DaemonInterface; using MiningCore.Extensions; +using MiningCore.Messaging; using MiningCore.Native; using MiningCore.Notifications; using MiningCore.Payments; @@ -59,8 +60,8 @@ public MoneroPayoutHandler( IBalanceRepository balanceRepo, IPaymentRepository paymentRepo, IMasterClock clock, - NotificationService notificationService) : - base(cf, mapper, shareRepo, blockRepo, balanceRepo, paymentRepo, clock, notificationService) + IMessageBus messageBus) : + base(cf, mapper, shareRepo, blockRepo, balanceRepo, paymentRepo, clock, messageBus) { Contract.RequiresNonNull(ctx, nameof(ctx)); Contract.RequiresNonNull(balanceRepo, nameof(balanceRepo)); @@ -210,8 +211,7 @@ private async Task PayoutToPaymentId(Balance balance) { ExtractAddressAndPaymentId(balance.Address, out var address, out var paymentId); - if (string.IsNullOrEmpty(paymentId)) - throw new InvalidOperationException("invalid paymentid"); + var isIntegratedAddress = string.IsNullOrEmpty(paymentId); // build request var request = new TransferRequest @@ -228,7 +228,13 @@ private async Task PayoutToPaymentId(Balance balance) GetTxKey = true }; - logger.Info(() => $"[{LogCategory}] Paying out {FormatAmount(balance.Amount)} with paymentId {paymentId}"); + if (!isIntegratedAddress) + request.PaymentId = paymentId; + + if(!isIntegratedAddress) + logger.Info(() => $"[{LogCategory}] Paying out {FormatAmount(balance.Amount)} to address {balance.Address} with paymentId {paymentId}"); + else + logger.Info(() => $"[{LogCategory}] Paying out {FormatAmount(balance.Amount)} to integrated address {balance.Address}"); // send command var result = await walletDaemon.ExecuteCmdSingleAsync(MWC.Transfer, request); @@ -273,7 +279,7 @@ public async Task ConfigureAsync(ClusterConfig clusterConfig, PoolConfig poolCon }) .ToArray(); - daemon = new DaemonClient(jsonSerializerSettings); + daemon = new DaemonClient(jsonSerializerSettings, messageBus, clusterConfig.ClusterName ?? poolConfig.PoolName, poolConfig.Id); daemon.Configure(daemonEndpoints); // configure wallet daemon @@ -288,7 +294,7 @@ public async Task ConfigureAsync(ClusterConfig clusterConfig, PoolConfig poolCon }) .ToArray(); - walletDaemon = new DaemonClient(jsonSerializerSettings); + walletDaemon = new DaemonClient(jsonSerializerSettings, messageBus, clusterConfig.ClusterName ?? poolConfig.PoolName, poolConfig.Id); walletDaemon.Configure(walletDaemonEndpoints); // detect network @@ -499,8 +505,8 @@ public async Task PayoutAsync(Balance[] balances) } } #endif - // balances with paymentIds - var minimumPaymentToPaymentId = extraConfig?.MinimumPaymentToPaymentId ?? poolConfig.PaymentProcessing.MinimumPayment; + // balances with paymentIds + var minimumPaymentToPaymentId = extraConfig?.MinimumPaymentToPaymentId ?? poolConfig.PaymentProcessing.MinimumPayment; var paymentIdBalances = balances.Except(simpleBalances) .Where(x => x.Amount >= minimumPaymentToPaymentId) diff --git a/src/MiningCore/Blockchain/Monero/MoneroPool.cs b/src/MiningCore/Blockchain/Monero/MoneroPool.cs index 7cb78e6db..4358d3902 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPool.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPool.cs @@ -35,6 +35,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Messaging; using MiningCore.Mining; using MiningCore.Notifications; +using MiningCore.Notifications.Messages; using MiningCore.Persistence; using MiningCore.Persistence.Repositories; using MiningCore.Stratum; @@ -52,9 +53,8 @@ public MoneroPool(IComponentContext ctx, IStatsRepository statsRepo, IMapper mapper, IMasterClock clock, - IMessageBus messageBus, - NotificationService notificationService) : - base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus, notificationService) + IMessageBus messageBus) : + base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus) { } @@ -65,7 +65,7 @@ public MoneroPool(IComponentContext ctx, private void OnLogin(StratumClient client, Timestamped tsRequest) { var request = tsRequest.Value; - var context = client.GetContextAs(); + var context = client.ContextAs(); if (request.Id == null) { @@ -141,7 +141,7 @@ private void OnLogin(StratumClient client, Timestamped tsRequest private void OnGetJob(StratumClient client, Timestamped tsRequest) { var request = tsRequest.Value; - var context = client.GetContextAs(); + var context = client.ContextAs(); if (request.Id == null) { @@ -165,7 +165,7 @@ private void OnGetJob(StratumClient client, Timestamped tsReques private MoneroJobParams CreateWorkerJob(StratumClient client) { - var context = client.GetContextAs(); + var context = client.ContextAs(); var job = new MoneroWorkerJob(NextJobId(), context.Difficulty); manager.PrepareWorkerJob(job, out var blob, out var target); @@ -193,7 +193,7 @@ private MoneroJobParams CreateWorkerJob(StratumClient client) private async Task OnSubmitAsync(StratumClient client, Timestamped tsRequest) { var request = tsRequest.Value; - var context = client.GetContextAs(); + var context = client.ContextAs(); try { @@ -245,11 +245,15 @@ private async Task OnSubmitAsync(StratumClient client, Timestamped $"[{LogCat}] [{client.ConnectionId}] Share accepted: D={Math.Round(share.Difficulty, 3)}"); + // telemetry + PublishTelemetry(TelemetryCategory.Share, clock.Now - tsRequest.Timestamp.UtcDateTime, true); + + logger.Info(() => $"[{LogCat}] [{client.ConnectionId}] Share accepted: D={Math.Round(share.Difficulty, 3)}"); // update pool stats if (share.IsBlockCandidate) @@ -264,6 +268,9 @@ private async Task OnSubmitAsync(StratumClient client, Timestamped $"[{LogCat}] [{client.ConnectionId}] Share rejected: {ex.Message}"); @@ -284,7 +291,7 @@ private void OnNewJob() ForEachClient(client => { - var context = client.GetContextAs(); + var context = client.ContextAs(); if (context.IsSubscribed && context.IsAuthorized) { @@ -340,7 +347,7 @@ protected override async Task OnRequestAsync(StratumClient client, Timestamped tsRequest) { var request = tsRequest.Value; - var context = client.GetContextAs(); + var context = client.ContextAs(); switch (request.Method) { @@ -380,7 +387,7 @@ protected override void OnVarDiffUpdate(StratumClient client, double newDiff) base.OnVarDiffUpdate(client, newDiff); // apply immediately and notify client - var context = client.GetContextAs(); + var context = client.ContextAs(); if (context.HasPendingDifficulty) { diff --git a/src/MiningCore/Blockchain/Straks/StraksJob.cs b/src/MiningCore/Blockchain/Straks/StraksJob.cs index 2a149a8a6..e1c5cf4b5 100644 --- a/src/MiningCore/Blockchain/Straks/StraksJob.cs +++ b/src/MiningCore/Blockchain/Straks/StraksJob.cs @@ -35,7 +35,7 @@ protected override Transaction CreateOutputTransaction() var blockReward = new Money(BlockTemplate.CoinbaseValue, MoneyUnit.Satoshi); rewardToPool = new Money(BlockTemplate.CoinbaseValue, MoneyUnit.Satoshi); - var tx = new Transaction(); + var tx = Transaction.Create(NBitcoinNetworkType); rewardToPool = CreateStraksOutputs(tx, blockReward); // Finally distribute remaining funds to pool diff --git a/src/MiningCore/Blockchain/Straks/StraksPool.cs b/src/MiningCore/Blockchain/Straks/StraksPool.cs index b3481e193..e6b823c2c 100644 --- a/src/MiningCore/Blockchain/Straks/StraksPool.cs +++ b/src/MiningCore/Blockchain/Straks/StraksPool.cs @@ -41,9 +41,8 @@ public StraksPool(IComponentContext ctx, IStatsRepository statsRepo, IMapper mapper, IMasterClock clock, - IMessageBus messageBus, - NotificationService notificationService) : - base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus, notificationService) + IMessageBus messageBus) : + base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus) { } } diff --git a/src/MiningCore/Blockchain/ZCash/DaemonResponses/GetBlockTemplateResponse.cs b/src/MiningCore/Blockchain/ZCash/DaemonResponses/GetBlockTemplateResponse.cs index d45ddfae6..8341b1b49 100644 --- a/src/MiningCore/Blockchain/ZCash/DaemonResponses/GetBlockTemplateResponse.cs +++ b/src/MiningCore/Blockchain/ZCash/DaemonResponses/GetBlockTemplateResponse.cs @@ -48,5 +48,8 @@ public class ZCashBlockTemplate : Bitcoin.DaemonResponses.BlockTemplate public string[] Mutable { get; set; } public ZCashBlockSubsidy Subsidy { get; set; } - } + + [JsonProperty("finalsaplingroothash")] + public string FinalSaplingRootHash { get; set; } + } } diff --git a/src/MiningCore/Blockchain/ZCash/ZCashConstants.cs b/src/MiningCore/Blockchain/ZCash/ZCashConstants.cs index 531263f01..4ab04973a 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashConstants.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashConstants.cs @@ -18,20 +18,30 @@ portions of the Software. SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +using System; using System.Collections.Generic; using System.Globalization; using MiningCore.Blockchain.Bitcoin; using MiningCore.Configuration; +using MiningCore.Crypto.Hashing.Equihash; +using NBitcoin; using NBitcoin.BouncyCastle.Math; +using NBitcoin.Zcash; namespace MiningCore.Blockchain.ZCash { - public class ZCashCoinbaseTxConfig + public class ZCashChainConfig { public BigInteger Diff1 { get; set; } public System.Numerics.BigInteger Diff1b { get; set; } + public Func CreateCoinbaseTx { get; set; } + public bool UsesZCashAddressFormat { get; set; } = true; - public bool PayFoundersReward { get; set; } + public int SolutionSize { get; set; } = 1344; + public int SolutionPreambleSize { get; set; } = 3; + public Func Solver { get; set; } = () => ZCashConstants.EquihashSolver_200_9; + + public bool PayFoundersReward { get; set; } public decimal PercentFoundersReward { get; set; } public string[] FoundersRewardAddresses { get; set; } public ulong FoundersRewardSubsidySlowStartInterval { get; set; } @@ -43,26 +53,50 @@ public class ZCashCoinbaseTxConfig public ulong TreasuryRewardStartBlockHeight { get; set; } public string[] TreasuryRewardAddresses { get; set; } public double TreasuryRewardAddressChangeInterval { get; set; } + + // ZCash Overwinter & Sapling + public uint? OverwinterActivationHeight { get; set; } + public uint? SaplingActivationHeight { get; set; } } public class ZCashConstants { public const int TargetPaddingLength = 32; - private static readonly Dictionary ZCashCoinbaseTxConfig = new Dictionary + private static readonly Network ZCashNetworkMain; + private static readonly Network ZCashNetworkTest; + private static readonly Network ZCashNetworkReg; + + internal static readonly EquihashSolverBase EquihashSolver_200_9 = new EquihashSolver_ZCash(); + internal static readonly EquihashSolverBase EquihashSolver_144_5 = new EquihashSolver_Btg(); + + static ZCashConstants() + { + ZcashNetworks.Instance.EnsureRegistered(); + + ZCashNetworkMain = Network.GetNetwork("zcash-main"); + ZCashNetworkTest = Network.GetNetwork("zcash-test"); + ZCashNetworkReg = Network.GetNetwork("zcash-reg"); + } + + private static readonly Dictionary ZCashCoinbaseTxConfig = new Dictionary { { - BitcoinNetworkType.Main, new ZCashCoinbaseTxConfig + BitcoinNetworkType.Main, new ZCashChainConfig { Diff1 = new BigInteger("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16), Diff1b = System.Numerics.BigInteger.Parse("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", NumberStyles.HexNumber), + CreateCoinbaseTx = ()=> Transaction.Create(ZCashNetworkMain), PayFoundersReward = true, PercentFoundersReward = 20, FoundersRewardSubsidyHalvingInterval = 840000, FoundersRewardSubsidySlowStartInterval = 20000, - FoundersRewardAddresses = new[] + OverwinterActivationHeight = 347500, + SaplingActivationHeight = 419200, + + FoundersRewardAddresses = new[] { "t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd", "t3cL9AucCajm3HXDhb5jBnJK2vapVoXsop3", "t3fqvkzrrNaMcamkQMwAyHRjfDdM2xQvDTR", "t3TgZ9ZT2CTSK44AnUPi6qeNaHa2eC7pUyF", "t3SpkcPQPfuRYHsP5vz3Pv86PgKo5m9KVmx", "t3Xt4oQMRPagwbpQqkgAViQgtST4VoSWR6S", @@ -84,17 +118,21 @@ public class ZCashConstants } }, { - BitcoinNetworkType.Test, new ZCashCoinbaseTxConfig + BitcoinNetworkType.Test, new ZCashChainConfig { Diff1 = new BigInteger("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16), Diff1b = System.Numerics.BigInteger.Parse("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", NumberStyles.HexNumber), + CreateCoinbaseTx = ()=> Transaction.Create(ZCashNetworkTest), PayFoundersReward = true, PercentFoundersReward = 20, FoundersRewardSubsidyHalvingInterval = 840000, FoundersRewardSubsidySlowStartInterval = 20000, - FoundersRewardAddresses = new[] + OverwinterActivationHeight = 207500, + SaplingActivationHeight = 280000, + + FoundersRewardAddresses = new[] { "t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi", "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543", "t2NGQjYMQhFndDHguvUw4wZdNdsssA6K7x2", "t2ENg7hHVqqs9JwU5cgjvSbxnT2a9USNfhy", "t2BkYdVCHzvTJJUTx4yZB8qeegD8QsPx8bo", "t2J8q1xH1EuigJ52MfExyyjYtN3VgvshKDf", "t2Crq9mydTm37kZokC68HzT6yez3t2FBnFj", "t2EaMPUiQ1kthqcP5UEkF42CAFKJqXCkXC9", @@ -112,10 +150,11 @@ public class ZCashConstants } }, { - BitcoinNetworkType.RegTest, new ZCashCoinbaseTxConfig + BitcoinNetworkType.RegTest, new ZCashChainConfig { Diff1 = new BigInteger("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16), Diff1b = System.Numerics.BigInteger.Parse("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", NumberStyles.HexNumber), + CreateCoinbaseTx = ()=> Transaction.Create(ZCashNetworkReg), PayFoundersReward = true, PercentFoundersReward = 20, @@ -130,13 +169,14 @@ public class ZCashConstants }, }; - private static readonly Dictionary ZCLCoinbaseTxConfig = new Dictionary + private static readonly Dictionary ZCLCoinbaseTxConfig = new Dictionary { { - BitcoinNetworkType.Main, new ZCashCoinbaseTxConfig + BitcoinNetworkType.Main, new ZCashChainConfig { Diff1 = new BigInteger("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16), Diff1b = System.Numerics.BigInteger.Parse("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", NumberStyles.HexNumber), + CreateCoinbaseTx = ()=> Transaction.Create(Network.Main), PayFoundersReward = false, PercentFoundersReward = 0, @@ -150,10 +190,11 @@ public class ZCashConstants } }, { - BitcoinNetworkType.Test, new ZCashCoinbaseTxConfig + BitcoinNetworkType.Test, new ZCashChainConfig { Diff1 = new BigInteger("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16), Diff1b = System.Numerics.BigInteger.Parse("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", NumberStyles.HexNumber), + CreateCoinbaseTx = ()=> Transaction.Create(Network.TestNet), PayFoundersReward = false, PercentFoundersReward = 0, @@ -167,10 +208,11 @@ public class ZCashConstants } }, { - BitcoinNetworkType.RegTest, new ZCashCoinbaseTxConfig + BitcoinNetworkType.RegTest, new ZCashChainConfig { Diff1 = new BigInteger("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16), Diff1b = System.Numerics.BigInteger.Parse("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", NumberStyles.HexNumber), + CreateCoinbaseTx = ()=> Transaction.Create(Network.RegTest), PayFoundersReward = false, PercentFoundersReward = 0, @@ -185,13 +227,84 @@ public class ZCashConstants }, }; - private static readonly Dictionary ZencashCoinbaseTxConfig = new Dictionary + private static readonly Dictionary BTGCoinbaseTxConfig = new Dictionary + { + { + BitcoinNetworkType.Main, new ZCashChainConfig + { + Diff1 = new BigInteger("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16), + Diff1b = System.Numerics.BigInteger.Parse("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", NumberStyles.HexNumber), + CreateCoinbaseTx = ()=> Transaction.Create(Network.Main), + UsesZCashAddressFormat = false, + Solver = ()=> EquihashSolver_144_5, + SolutionSize = 100, + SolutionPreambleSize = 1, + + PayFoundersReward = false, + PercentFoundersReward = 0, + FoundersRewardSubsidyHalvingInterval = 0, + FoundersRewardSubsidySlowStartInterval = 0, + + FoundersRewardAddresses = new[] + { + "" + } + } + }, + { + BitcoinNetworkType.Test, new ZCashChainConfig + { + Diff1 = new BigInteger("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16), + Diff1b = System.Numerics.BigInteger.Parse("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", NumberStyles.HexNumber), + CreateCoinbaseTx = ()=> Transaction.Create(Network.TestNet), + UsesZCashAddressFormat = false, + Solver = ()=> EquihashSolver_144_5, + SolutionSize = 100, + SolutionPreambleSize = 1, + + PayFoundersReward = false, + PercentFoundersReward = 0, + FoundersRewardSubsidyHalvingInterval = 0, + FoundersRewardSubsidySlowStartInterval = 0, + + FoundersRewardAddresses = new[] + { + "" + } + } + }, + { + BitcoinNetworkType.RegTest, new ZCashChainConfig + { + Diff1 = new BigInteger("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16), + Diff1b = System.Numerics.BigInteger.Parse("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", NumberStyles.HexNumber), + CreateCoinbaseTx = ()=> Transaction.Create(Network.RegTest), + UsesZCashAddressFormat = false, + Solver = ()=> EquihashSolver_144_5, + SolutionSize = 100, + SolutionPreambleSize = 1, + + PayFoundersReward = false, + PercentFoundersReward = 0, + FoundersRewardSubsidyHalvingInterval = 0, + FoundersRewardSubsidySlowStartInterval = 0, + + FoundersRewardAddresses = new[] + { + "" + } + } + }, + }; + + private static readonly Dictionary ZencashCoinbaseTxConfig = new Dictionary { { - BitcoinNetworkType.Main, new ZCashCoinbaseTxConfig + BitcoinNetworkType.Main, new ZCashChainConfig { Diff1 = new BigInteger("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16), Diff1b = System.Numerics.BigInteger.Parse("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", NumberStyles.HexNumber), + CreateCoinbaseTx = ()=> Transaction.Create(Network.Main), PayFoundersReward = true, PercentFoundersReward = 8.5m, @@ -230,10 +343,11 @@ public class ZCashConstants } }, { - BitcoinNetworkType.Test, new ZCashCoinbaseTxConfig + BitcoinNetworkType.Test, new ZCashChainConfig { Diff1 = new BigInteger("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16), Diff1b = System.Numerics.BigInteger.Parse("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", NumberStyles.HexNumber), + CreateCoinbaseTx = ()=> Transaction.Create(Network.TestNet), PayFoundersReward = true, PercentFoundersReward = 8.5m, @@ -271,10 +385,11 @@ public class ZCashConstants } }, { - BitcoinNetworkType.RegTest, new ZCashCoinbaseTxConfig + BitcoinNetworkType.RegTest, new ZCashChainConfig { Diff1 = new BigInteger("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16), Diff1b = System.Numerics.BigInteger.Parse("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", NumberStyles.HexNumber), + CreateCoinbaseTx = ()=> Transaction.Create(Network.RegTest), PayFoundersReward = true, PercentFoundersReward = 8.5m, @@ -298,13 +413,14 @@ public class ZCashConstants }, }; - private static readonly Dictionary BTCPCoinbaseTxConfig = new Dictionary + private static readonly Dictionary BTCPCoinbaseTxConfig = new Dictionary { { - BitcoinNetworkType.Main, new ZCashCoinbaseTxConfig + BitcoinNetworkType.Main, new ZCashChainConfig { Diff1 = new BigInteger("007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16), Diff1b = System.Numerics.BigInteger.Parse("007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", NumberStyles.HexNumber), + CreateCoinbaseTx = ()=> Transaction.Create(Network.Main), PayFoundersReward = false, PercentFoundersReward = 0, @@ -318,10 +434,11 @@ public class ZCashConstants } }, { - BitcoinNetworkType.Test, new ZCashCoinbaseTxConfig + BitcoinNetworkType.Test, new ZCashChainConfig { Diff1 = new BigInteger("007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16), Diff1b = System.Numerics.BigInteger.Parse("007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", NumberStyles.HexNumber), + CreateCoinbaseTx = ()=> Transaction.Create(Network.TestNet), PayFoundersReward = false, PercentFoundersReward = 0, @@ -335,10 +452,11 @@ public class ZCashConstants } }, { - BitcoinNetworkType.RegTest, new ZCashCoinbaseTxConfig + BitcoinNetworkType.RegTest, new ZCashChainConfig { Diff1 = new BigInteger("007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16), Diff1b = System.Numerics.BigInteger.Parse("007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", NumberStyles.HexNumber), + CreateCoinbaseTx = ()=> Transaction.Create(Network.RegTest), PayFoundersReward = false, PercentFoundersReward = 0, @@ -353,13 +471,13 @@ public class ZCashConstants }, }; - public static Dictionary> CoinbaseTxConfig = - new Dictionary> + public static Dictionary> Chains = + new Dictionary> { { CoinType.ZEC, ZCashCoinbaseTxConfig }, { CoinType.ZCL, ZCLCoinbaseTxConfig }, { CoinType.ZEN, ZencashCoinbaseTxConfig }, - { CoinType.BTG, ZCLCoinbaseTxConfig }, + { CoinType.BTG, BTGCoinbaseTxConfig }, { CoinType.BTCP, BTCPCoinbaseTxConfig }, }; } diff --git a/src/MiningCore/Blockchain/ZCash/ZCashJob.cs b/src/MiningCore/Blockchain/ZCash/ZCashJob.cs index bb55c9008..f543e5970 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashJob.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashJob.cs @@ -23,6 +23,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System.Globalization; using System.IO; using System.Linq; +using System.Reflection; using MiningCore.Blockchain.Bitcoin; using MiningCore.Blockchain.ZCash.DaemonResponses; using MiningCore.Configuration; @@ -36,57 +37,77 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Util; using NBitcoin; using NBitcoin.DataEncoders; +using NBitcoin.Zcash; namespace MiningCore.Blockchain.ZCash { public class ZCashJob : BitcoinJob { - protected ZCashCoinbaseTxConfig coinbaseTxConfig; + protected ZCashChainConfig chainConfig; protected decimal blockReward; protected decimal rewardFees; protected uint coinbaseIndex = 4294967295u; protected uint coinbaseSequence = 4294967295u; + protected uint txVersionGroupId; + protected uint txExpiryHeight = 20; + protected uint txNJoinSplits = 0; protected readonly IHashAlgorithm sha256D = new Sha256D(); protected byte[] coinbaseInitialHash; protected byte[] merkleRoot; protected byte[] merkleRootReversed; protected string merkleRootReversedHex; - protected EquihashSolver equihash = EquihashSolver.Instance.Value; + protected EquihashSolverBase equihash; - #region Overrides of BitcoinJob + // ZCash Sapling & Overwinter support + protected bool isOverwinterActive = false; + protected bool isSaplingActive = false; - protected override Transaction CreateOutputTransaction() + // temporary reflection hack to force overwinter + protected static FieldInfo overwinterField = typeof(ZcashTransaction).GetField("fOverwintered", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly); + protected static FieldInfo versionGroupField = typeof(ZcashTransaction).GetField("nVersionGroupId", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly); + + #region Overrides of BitcoinJob + + protected override Transaction CreateOutputTransaction() { - var tx = new Transaction(); + var tx = chainConfig.CreateCoinbaseTx(); + + if (isOverwinterActive) + overwinterField.SetValue(tx, true); - if (coinbaseTxConfig.PayFoundersReward && - (coinbaseTxConfig.LastFoundersRewardBlockHeight >= BlockTemplate.Height || - coinbaseTxConfig.TreasuryRewardStartBlockHeight > 0)) + // set versions + tx.Version = txVersion; + versionGroupField.SetValue(tx, txVersionGroupId); + + // calculate outputs + if (chainConfig.PayFoundersReward && + (chainConfig.LastFoundersRewardBlockHeight >= BlockTemplate.Height || + chainConfig.TreasuryRewardStartBlockHeight > 0)) { // founders or treasury reward? - if (coinbaseTxConfig.TreasuryRewardStartBlockHeight > 0 && - BlockTemplate.Height >= coinbaseTxConfig.TreasuryRewardStartBlockHeight) + if (chainConfig.TreasuryRewardStartBlockHeight > 0 && + BlockTemplate.Height >= chainConfig.TreasuryRewardStartBlockHeight) { // pool reward (t-addr) - rewardToPool = new Money(Math.Round(blockReward * (1m - (coinbaseTxConfig.PercentTreasuryReward) / 100m)) + rewardFees, MoneyUnit.Satoshi); + rewardToPool = new Money(Math.Round(blockReward * (1m - (chainConfig.PercentTreasuryReward) / 100m)) + rewardFees, MoneyUnit.Satoshi); tx.AddOutput(rewardToPool, poolAddressDestination); // treasury reward (t-addr) var destination = FoundersAddressToScriptDestination(GetTreasuryRewardAddress()); - var amount = new Money(Math.Round(blockReward * (coinbaseTxConfig.PercentTreasuryReward / 100m)), MoneyUnit.Satoshi); + var amount = new Money(Math.Round(blockReward * (chainConfig.PercentTreasuryReward / 100m)), MoneyUnit.Satoshi); tx.AddOutput(amount, destination); } else { // pool reward (t-addr) - rewardToPool = new Money(Math.Round(blockReward * (1m - (coinbaseTxConfig.PercentFoundersReward) / 100m)) + rewardFees, MoneyUnit.Satoshi); + rewardToPool = new Money(Math.Round(blockReward * (1m - (chainConfig.PercentFoundersReward) / 100m)) + rewardFees, MoneyUnit.Satoshi); tx.AddOutput(rewardToPool, poolAddressDestination); // founders reward (t-addr) var destination = FoundersAddressToScriptDestination(GetFoundersRewardAddress()); - var amount = new Money(Math.Round(blockReward * (coinbaseTxConfig.PercentFoundersReward / 100m)), MoneyUnit.Satoshi); + var amount = new Money(Math.Round(blockReward * (chainConfig.PercentFoundersReward / 100m)), MoneyUnit.Satoshi); tx.AddOutput(amount, destination); } } @@ -104,31 +125,14 @@ protected override Transaction CreateOutputTransaction() protected override void BuildCoinbase() { - var script = TxIn.CreateCoinbase((int) BlockTemplate.Height).ScriptSig; - // output transaction txOut = CreateOutputTransaction(); + txOut.AddInput(TxIn.CreateCoinbase((int) BlockTemplate.Height)); using(var stream = new MemoryStream()) { - var bs = new BitcoinStream(stream, true); - - // version - bs.ReadWrite(ref txVersion); - - // serialize (simulated) input transaction - bs.ReadWriteAsVarInt(ref txInputCount); - bs.ReadWrite(ref sha256Empty); - bs.ReadWrite(ref coinbaseIndex); - bs.ReadWrite(ref script); - bs.ReadWrite(ref coinbaseSequence); - - // serialize output transaction - var txOutBytes = SerializeOutputTransaction(txOut); - bs.ReadWrite(ref txOutBytes); - - // misc - bs.ReadWrite(ref txLockTime); + var bs = new ZcashStream(stream, true); + bs.ReadWrite(ref txOut); // done coinbaseInitial = stream.ToArray(); @@ -137,7 +141,7 @@ protected override void BuildCoinbase() } } - public override void Init(ZCashBlockTemplate blockTemplate, string jobId, + public override void Init(ZCashBlockTemplate blockTemplate, string jobId, PoolConfig poolConfig, ClusterConfig clusterConfig, IMasterClock clock, IDestination poolAddressDestination, BitcoinNetworkType networkType, bool isPoS, double shareMultiplier, decimal blockrewardMultiplier, @@ -159,18 +163,43 @@ public override void Init(ZCashBlockTemplate blockTemplate, string jobId, this.poolAddressDestination = poolAddressDestination; this.networkType = networkType; - if (ZCashConstants.CoinbaseTxConfig.TryGetValue(poolConfig.Coin.Type, out var coinbaseTx)) - coinbaseTx.TryGetValue(networkType, out coinbaseTxConfig); + if (ZCashConstants.Chains.TryGetValue(poolConfig.Coin.Type, out var chain)) + chain.TryGetValue(networkType, out chainConfig); BlockTemplate = blockTemplate; JobId = jobId; - Difficulty = (double) new BigRational(coinbaseTxConfig.Diff1b, BlockTemplate.Target.HexToByteArray().ReverseArray().ToBigInteger()); + Difficulty = (double) new BigRational(chainConfig.Diff1b, BlockTemplate.Target.HexToByteArray().ReverseArray().ToBigInteger()); + txExpiryHeight = blockTemplate.Height + 100; + // ZCash Sapling & Overwinter support + isSaplingActive = chainConfig.SaplingActivationHeight.HasValue && + chainConfig.SaplingActivationHeight.Value > 0 && + blockTemplate.Height >= chainConfig.SaplingActivationHeight.Value; + + isOverwinterActive = isSaplingActive || + chainConfig.OverwinterActivationHeight.HasValue && + chainConfig.OverwinterActivationHeight.Value > 0 && + blockTemplate.Height >= chainConfig.OverwinterActivationHeight.Value; + + if (isSaplingActive) + { + txVersion = 4; + txVersionGroupId = 0x892F2085; + } + + else if(isOverwinterActive) + { + txVersion = 3; + txVersionGroupId = 0x03C48270; + } + + // Misc this.isPoS = isPoS; this.shareMultiplier = shareMultiplier; this.headerHasher = headerHasher; this.blockHasher = blockHasher; + this.equihash = chainConfig.Solver(); if (!string.IsNullOrEmpty(BlockTemplate.Target)) blockTargetValue = new uint256(BlockTemplate.Target); @@ -187,7 +216,7 @@ public override void Init(ZCashBlockTemplate blockTemplate, string jobId, blockReward = blockTemplate.Subsidy.Miner * BitcoinConstants.SatoshisPerBitcoin; - if (coinbaseTxConfig?.PayFoundersReward == true) + if (chainConfig?.PayFoundersReward == true) { var founders = blockTemplate.Subsidy.Founders ?? blockTemplate.Subsidy.Community; @@ -210,13 +239,18 @@ public override void Init(ZCashBlockTemplate blockTemplate, string jobId, merkleRootReversed = merkleRoot.ReverseArray(); merkleRootReversedHex = merkleRootReversed.ToHexString(); + // misc + var hashReserved = isSaplingActive && !string.IsNullOrEmpty(blockTemplate.FinalSaplingRootHash) ? + blockTemplate.FinalSaplingRootHash.HexToByteArray().ReverseArray().ToHexString() : + sha256Empty.ToHexString(); + jobParams = new object[] { JobId, BlockTemplate.Version.ReverseByteOrder().ToStringHex8(), previousBlockHashReversedHex, merkleRootReversedHex, - sha256Empty.ToHexString(), // hashReserved + hashReserved, BlockTemplate.CurTime.ReverseByteOrder().ToStringHex8(), BlockTemplate.Bits.HexToByteArray().ReverseArray().ToHexString(), false @@ -232,7 +266,7 @@ public override (Share Share, string BlockHex) ProcessShare(StratumClient worker Contract.Requires(!string.IsNullOrEmpty(nTime), $"{nameof(nTime)} must not be empty"); Contract.Requires(!string.IsNullOrEmpty(solution), $"{nameof(solution)} must not be empty"); - var context = worker.GetContextAs(); + var context = worker.ContextAs(); // validate nTime if (nTime.Length != 8) @@ -249,7 +283,7 @@ public override (Share Share, string BlockHex) ProcessShare(StratumClient worker throw new StratumException(StratumError.Other, "incorrect size of extraNonce2"); // validate solution - if (solution.Length != 2694) + if (solution.Length != (chainConfig.SolutionSize + chainConfig.SolutionPreambleSize) * 2) throw new StratumException(StratumError.Other, "incorrect size of solution"); // dupe check @@ -271,6 +305,9 @@ protected virtual byte[] SerializeHeader(uint nTime, string nonce) Nonce = nonce }; + if (isSaplingActive && !string.IsNullOrEmpty(BlockTemplate.FinalSaplingRootHash)) + blockHeader.HashReserved = BlockTemplate.FinalSaplingRootHash.HexToByteArray().ReverseArray(); + return blockHeader.ToBytes(); } @@ -296,14 +333,14 @@ protected byte[] SerializeBlock(byte[] header, byte[] coinbase, byte[] solution) protected virtual (Share Share, string BlockHex) ProcessShareInternal(StratumClient worker, string nonce, uint nTime, string solution) { - var context = worker.GetContextAs(); + var context = worker.ContextAs(); var solutionBytes = solution.HexToByteArray(); // serialize block-header var headerBytes = SerializeHeader(nTime, nonce); // 144 bytes (doesn't contain soln) // verify solution - if (!equihash.Verify(headerBytes, solutionBytes.Skip(3).ToArray())) // skip preamble (3 bytes) + if (!equihash.Verify(headerBytes, solutionBytes.Skip(chainConfig.SolutionPreambleSize).ToArray())) // skip preamble (3 bytes) throw new StratumException(StratumError.Other, "invalid solution"); // hash block-header @@ -313,7 +350,7 @@ protected virtual (Share Share, string BlockHex) ProcessShareInternal(StratumCli var headerValue = new uint256(headerHash); // calc share-diff - var shareDiff = (double) new BigRational(coinbaseTxConfig.Diff1b, headerHash.ToBigInteger()) * shareMultiplier; + var shareDiff = (double) new BigRational(chainConfig.Diff1b, headerHash.ToBigInteger()) * shareMultiplier; var stratumDifficulty = context.Difficulty; var ratio = shareDiff / stratumDifficulty; @@ -376,21 +413,21 @@ protected bool RegisterSubmit(string nonce, string solution) public string GetFoundersRewardAddress() { - var maxHeight = coinbaseTxConfig.LastFoundersRewardBlockHeight; + var maxHeight = chainConfig.LastFoundersRewardBlockHeight; - var addressChangeInterval = (maxHeight + (ulong) coinbaseTxConfig.FoundersRewardAddresses.Length) / (ulong) coinbaseTxConfig.FoundersRewardAddresses.Length; + var addressChangeInterval = (maxHeight + (ulong) chainConfig.FoundersRewardAddresses.Length) / (ulong) chainConfig.FoundersRewardAddresses.Length; var index = BlockTemplate.Height / addressChangeInterval; - var address = coinbaseTxConfig.FoundersRewardAddresses[index]; + var address = chainConfig.FoundersRewardAddresses[index]; return address; } protected string GetTreasuryRewardAddress() { - var index = (int) Math.Floor((BlockTemplate.Height - coinbaseTxConfig.TreasuryRewardStartBlockHeight) / - coinbaseTxConfig.TreasuryRewardAddressChangeInterval % coinbaseTxConfig.TreasuryRewardAddresses.Length); + var index = (int) Math.Floor((BlockTemplate.Height - chainConfig.TreasuryRewardStartBlockHeight) / + chainConfig.TreasuryRewardAddressChangeInterval % chainConfig.TreasuryRewardAddresses.Length); - var address = coinbaseTxConfig.TreasuryRewardAddresses[index]; + var address = chainConfig.TreasuryRewardAddresses[index]; return address; } diff --git a/src/MiningCore/Blockchain/ZCash/ZCashJobManager.cs b/src/MiningCore/Blockchain/ZCash/ZCashJobManager.cs index 16c30b8d8..c866503c9 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashJobManager.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashJobManager.cs @@ -12,11 +12,14 @@ using MiningCore.Contracts; using MiningCore.DaemonInterface; using MiningCore.Extensions; +using MiningCore.JsonRpc; +using MiningCore.Messaging; using MiningCore.Notifications; using MiningCore.Stratum; using MiningCore.Time; using NBitcoin; using NBitcoin.DataEncoders; +using NBitcoin.OpenAsset; namespace MiningCore.Blockchain.ZCash { @@ -25,9 +28,9 @@ public class ZCashJobManager : BitcoinJobManager { public ZCashJobManager( IComponentContext ctx, - NotificationService notificationService, IMasterClock clock, - IExtraNonceProvider extraNonceProvider) : base(ctx, notificationService, clock, extraNonceProvider) + IMessageBus messageBus, + IExtraNonceProvider extraNonceProvider) : base(ctx, clock, messageBus, extraNonceProvider) { getBlockTemplateParams = new object[] { @@ -38,6 +41,7 @@ public ZCashJobManager( }; } + protected ZCashChainConfig chainConfig; private ZCashPoolConfigExtra zcashExtraPoolConfig; #region Overrides of JobManagerBase @@ -46,12 +50,24 @@ public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfi { zcashExtraPoolConfig = poolConfig.Extra.SafeExtensionDataAs(); - base.Configure(poolConfig, clusterConfig); + base.Configure(poolConfig, clusterConfig); } #endregion - public override async Task ValidateAddressAsync(string address) + #region Overrides of BitcoinJobManager + + protected override void PostChainIdentifyConfigure() + { + if (ZCashConstants.Chains.TryGetValue(poolConfig.Coin.Type, out var coinbaseTx)) + coinbaseTx.TryGetValue(networkType, out chainConfig); + + base.PostChainIdentifyConfigure(); + } + + #endregion + + public override async Task ValidateAddressAsync(string address) { Contract.Requires(!string.IsNullOrEmpty(address), $"{nameof(address)} must not be empty"); @@ -77,6 +93,8 @@ protected override async Task> GetBlockTempla if (subsidyResponse.Error == null && result.Error == null && result.Response != null) result.Response.Subsidy = subsidyResponse.Response; + else + result.Error = new JsonRpcException(-1, $"{BitcoinCommands.GetBlockSubsidy} failed", null); return result; } @@ -85,7 +103,7 @@ public override object[] GetSubscriberData(StratumClient worker) { Contract.RequiresNonNull(worker, nameof(worker)); - var context = worker.GetContextAs(); + var context = worker.ContextAs(); // assign unique ExtraNonce1 to worker (miner) context.ExtraNonce1 = extraNonceProvider.Next(); @@ -101,6 +119,9 @@ public override object[] GetSubscriberData(StratumClient worker) protected override IDestination AddressToDestination(string address) { + if (!chainConfig.UsesZCashAddressFormat) + return base.AddressToDestination(address); + var decoded = Encoders.Base58.DecodeData(address); var hash = decoded.Skip(2).Take(20).ToArray(); var result = new KeyId(hash); @@ -118,7 +139,7 @@ public override async Task SubmitShareAsync(StratumClient worker, object if (!(submission is object[] submitParams)) throw new StratumException(StratumError.Other, "invalid params"); - var context = worker.GetContextAs(); + var context = worker.ContextAs(); // extract params var workerValue = (submitParams[0] as string)?.Trim(); diff --git a/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs b/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs index c1c7661c1..9f2a38b04 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs @@ -30,14 +30,12 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Blockchain.ZCash.DaemonResponses; using MiningCore.Configuration; using MiningCore.Extensions; -using MiningCore.Notifications; +using MiningCore.Messaging; using MiningCore.Persistence; using MiningCore.Persistence.Model; using MiningCore.Persistence.Repositories; using MiningCore.Time; -using MiningCore.Util; using Newtonsoft.Json.Linq; -using Block = MiningCore.Persistence.Model.Block; using Contract = MiningCore.Contracts.Contract; namespace MiningCore.Blockchain.ZCash @@ -54,15 +52,15 @@ public ZCashPayoutHandler( IBalanceRepository balanceRepo, IPaymentRepository paymentRepo, IMasterClock clock, - NotificationService notificationService) : - base(ctx, cf, mapper, shareRepo, blockRepo, balanceRepo, paymentRepo, clock, notificationService) + IMessageBus messageBus) : + base(ctx, cf, mapper, shareRepo, blockRepo, balanceRepo, paymentRepo, clock, messageBus) { } protected ZCashPoolConfigExtra poolExtraConfig; protected bool supportsNativeShielding; protected BitcoinNetworkType networkType; - protected ZCashCoinbaseTxConfig coinbaseTxConfig; + protected ZCashChainConfig chainConfig; protected override string LogCategory => "ZCash Payout Handler"; protected const decimal TransferFee = 0.0001m; protected const int ZMinConfirmations = 8; @@ -86,8 +84,8 @@ public override async Task ConfigureAsync(ClusterConfig clusterConfig, PoolConfi networkType = BitcoinNetworkType.Main; // lookup config - if (ZCashConstants.CoinbaseTxConfig.TryGetValue(poolConfig.Coin.Type, out var coinbaseTx)) - coinbaseTx.TryGetValue(networkType, out coinbaseTxConfig); + if (ZCashConstants.Chains.TryGetValue(poolConfig.Coin.Type, out var coinbaseTx)) + coinbaseTx.TryGetValue(networkType, out chainConfig); // detect z_shieldcoinbase support var response = await daemon.ExecuteCmdSingleAsync(ZCashCommands.ZShieldCoinbase); diff --git a/src/MiningCore/Blockchain/ZCash/ZCashPool.cs b/src/MiningCore/Blockchain/ZCash/ZCashPool.cs index 65466320c..747b65f72 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashPool.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashPool.cs @@ -24,7 +24,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Configuration; using MiningCore.Extensions; using MiningCore.Messaging; -using MiningCore.Notifications; using MiningCore.Persistence; using MiningCore.Persistence.Repositories; using MiningCore.Time; @@ -42,9 +41,8 @@ public ZCashPool(IComponentContext ctx, IStatsRepository statsRepo, IMapper mapper, IMasterClock clock, - IMessageBus messageBus, - NotificationService notificationService) : - base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus, notificationService) + IMessageBus messageBus) : + base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus) { } diff --git a/src/MiningCore/Blockchain/ZCash/ZCashPoolBase.cs b/src/MiningCore/Blockchain/ZCash/ZCashPoolBase.cs index 4f8b56c5d..7c467f272 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashPoolBase.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashPoolBase.cs @@ -33,7 +33,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Extensions; using MiningCore.JsonRpc; using MiningCore.Messaging; -using MiningCore.Notifications; using MiningCore.Persistence; using MiningCore.Persistence.Repositories; using MiningCore.Stratum; @@ -53,13 +52,12 @@ public ZCashPoolBase(IComponentContext ctx, IStatsRepository statsRepo, IMapper mapper, IMasterClock clock, - IMessageBus messageBus, - NotificationService notificationService) : - base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus, notificationService) + IMessageBus messageBus) : + base(ctx, serializerSettings, cf, statsRepo, mapper, clock, messageBus) { } - private ZCashCoinbaseTxConfig coinbaseTxConfig; + private ZCashChainConfig chainConfig; private double hashrateDivisor; protected override BitcoinJobManager CreateJobManager() @@ -76,11 +74,11 @@ protected override async Task SetupJobManager(CancellationToken ct) { await base.SetupJobManager(ct); - if (ZCashConstants.CoinbaseTxConfig.TryGetValue(poolConfig.Coin.Type, out var coinbaseTx)) - coinbaseTx.TryGetValue(manager.NetworkType, out coinbaseTxConfig); + if (ZCashConstants.Chains.TryGetValue(poolConfig.Coin.Type, out var coinbaseTx)) + coinbaseTx.TryGetValue(manager.NetworkType, out chainConfig); - hashrateDivisor = (double)new BigRational(coinbaseTxConfig.Diff1b, - ZCashConstants.CoinbaseTxConfig[CoinType.ZEC][manager.NetworkType].Diff1b); + hashrateDivisor = (double)new BigRational(chainConfig.Diff1b, + ZCashConstants.Chains[CoinType.ZEC][manager.NetworkType].Diff1b); } #endregion @@ -88,7 +86,7 @@ protected override async Task SetupJobManager(CancellationToken ct) protected override void OnSubscribe(StratumClient client, Timestamped tsRequest) { var request = tsRequest.Value; - var context = client.GetContextAs(); + var context = client.ContextAs(); if (request.Id == null) { @@ -116,7 +114,7 @@ protected override async Task OnAuthorizeAsync(StratumClient client, Timestamped { await base.OnAuthorizeAsync(client, tsRequest); - var context = client.GetContextAs(); + var context = client.ContextAs(); if (context.IsAuthorized) { @@ -129,7 +127,7 @@ protected override async Task OnAuthorizeAsync(StratumClient client, Timestamped private void OnSuggestTarget(StratumClient client, Timestamped tsRequest) { var request = tsRequest.Value; - var context = client.GetContextAs(); + var context = client.ContextAs(); if (request.Id == null) { @@ -144,7 +142,7 @@ private void OnSuggestTarget(StratumClient client, Timestamped t { if (System.Numerics.BigInteger.TryParse(target, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var targetBig)) { - var newDiff = (double) new BigRational(coinbaseTxConfig.Diff1b, targetBig); + var newDiff = (double) new BigRational(chainConfig.Diff1b, targetBig); var poolEndpoint = poolConfig.Ports[client.PoolEndpoint.Port]; if (newDiff >= poolEndpoint.Difficulty) @@ -210,7 +208,7 @@ protected override void OnNewJob(object jobParams) ForEachClient(client => { - var context = client.GetContextAs(); + var context = client.ContextAs(); if (context.IsSubscribed && context.IsAuthorized) { @@ -246,7 +244,7 @@ public override double HashrateFromShares(double shares, double interval) protected override void OnVarDiffUpdate(StratumClient client, double newDiff) { - var context = client.GetContextAs(); + var context = client.ContextAs(); context.EnqueueNewDifficulty(newDiff); @@ -263,7 +261,7 @@ protected override void OnVarDiffUpdate(StratumClient client, double newDiff) private string EncodeTarget(double difficulty) { var diff = BigInteger.ValueOf((long) (difficulty * 255d)); - var quotient = coinbaseTxConfig.Diff1.Divide(diff).Multiply(BigInteger.ValueOf(255)); + var quotient = chainConfig.Diff1.Divide(diff).Multiply(BigInteger.ValueOf(255)); var bytes = quotient.ToByteArray(); var padded = ArrayPool.Shared.Rent(ZCashConstants.TargetPaddingLength); diff --git a/src/MiningCore/Configuration/ClusterConfigValidation.cs b/src/MiningCore/Configuration/ClusterConfigValidation.cs index c8db2c091..1014d9834 100644 --- a/src/MiningCore/Configuration/ClusterConfigValidation.cs +++ b/src/MiningCore/Configuration/ClusterConfigValidation.cs @@ -176,8 +176,8 @@ public PoolConfigValidator() }) .WithMessage("Pool: Invalid stratum port number {port}"); - RuleFor(j => j.Ports.Values) - .SetCollectionValidator(x => new PoolEndpointValidator()) + RuleForEach(j => j.Ports.Values) + .SetValidator(x => new PoolEndpointValidator()) .When(x => x.Ports != null); RuleFor(j => j.Address) @@ -190,8 +190,8 @@ public PoolConfigValidator() .NotEmpty() .WithMessage("Pool: Daemons missing or empty"); - RuleFor(j => j.Daemons) - .SetCollectionValidator(new AuthenticatedNetworkEndpointConfigValidator()); + RuleForEach(j => j.Daemons) + .SetValidator(new AuthenticatedNetworkEndpointConfigValidator()); } } @@ -248,8 +248,8 @@ public ClusterConfigValidator() }) .WithMessage("Stratum port {port} assigned multiple times"); - RuleFor(j => j.Pools) - .SetCollectionValidator(new PoolConfigValidator()); + RuleForEach(j => j.Pools) + .SetValidator(new PoolConfigValidator()); } } diff --git a/src/MiningCore/Crypto/Hashing/Equihash/EquihashSolver.cs b/src/MiningCore/Crypto/Hashing/Equihash/EquihashSolver.cs index e4b809caf..274dd7808 100644 --- a/src/MiningCore/Crypto/Hashing/Equihash/EquihashSolver.cs +++ b/src/MiningCore/Crypto/Hashing/Equihash/EquihashSolver.cs @@ -27,20 +27,8 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. namespace MiningCore.Crypto.Hashing.Equihash { - public unsafe class EquihashSolver + public abstract class EquihashSolverBase { - private EquihashSolver(int maxConcurrency) - { - // we need to limit concurrency here due to the enormous memory - // requirements of the equihash algorithm (up 1GB per thread) - sem = new Semaphore(maxConcurrency, maxConcurrency); - } - - private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); - - public static Lazy Instance { get; } = new Lazy(() => - new EquihashSolver(maxThreads)); - private static int maxThreads = 1; public static int MaxThreads @@ -48,14 +36,15 @@ public static int MaxThreads get => maxThreads; set { - if(Instance.IsValueCreated) - throw new InvalidOperationException("Too late: singleton value already created"); + if(sem.IsValueCreated) + throw new InvalidOperationException("Too late: semaphore already created"); maxThreads = value; } } - private readonly Semaphore sem; + protected static readonly Lazy sem = new Lazy(() => + new Semaphore(maxThreads, maxThreads)); /// /// Verify an Equihash solution @@ -63,22 +52,29 @@ public static int MaxThreads /// header including nonce (140 bytes) /// equihash solution (excluding 3 bytes with size, so 1344 bytes length) - Do not include byte size preamble "fd4005" /// - public bool Verify(byte[] header, byte[] solution) + public abstract bool Verify(byte[] header, byte[] solution); + } + + public unsafe class EquihashSolver_ZCash : EquihashSolverBase + { + private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); + + public override bool Verify(byte[] header, byte[] solution) { Contract.RequiresNonNull(header, nameof(header)); Contract.Requires(header.Length == 140, $"{nameof(header)} must be exactly 140 bytes"); Contract.RequiresNonNull(solution, nameof(solution)); Contract.Requires(solution.Length == 1344, $"{nameof(solution)} must be exactly 1344 bytes"); - logger.LogInvoke(); + logger.LogInvoke(); try { - sem.WaitOne(); + sem.Value.WaitOne(); - fixed(byte *h = header) + fixed (byte* h = header) { - fixed(byte *s = solution) + fixed (byte* s = solution) { return LibMultihash.equihash_verify(h, header.Length, s, solution.Length); } @@ -87,7 +83,40 @@ public bool Verify(byte[] header, byte[] solution) finally { - sem.Release(); + sem.Value.Release(); + } + } + } + + public unsafe class EquihashSolver_Btg : EquihashSolverBase + { + private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); + + public override bool Verify(byte[] header, byte[] solution) + { + Contract.RequiresNonNull(header, nameof(header)); + Contract.Requires(header.Length == 140, $"{nameof(header)} must be exactly 140 bytes"); + Contract.RequiresNonNull(solution, nameof(solution)); + Contract.Requires(solution.Length == 100, $"{nameof(solution)} must be exactly 100 bytes"); + + logger.LogInvoke(); + + try + { + sem.Value.WaitOne(); + + fixed (byte* h = header) + { + fixed (byte* s = solution) + { + return LibMultihash.equihash_verify_btg(h, header.Length, s, solution.Length); + } + } + } + + finally + { + sem.Value.Release(); } } } diff --git a/src/MiningCore/Crypto/Hashing/Ethash/Block.cs b/src/MiningCore/Crypto/Hashing/Ethash/Block.cs deleted file mode 100644 index 1b6ffee1c..000000000 --- a/src/MiningCore/Crypto/Hashing/Ethash/Block.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Numerics; -using System.Text; - -namespace MiningCore.Crypto.Hashing.Ethash -{ - public class Block - { - public ulong Height { get; set; } - public BigInteger Difficulty { get; set; } - public byte[] HashNoNonce { get; set; } - public ulong Nonce { get; set; } - public byte[] MixDigest { get; set; } - } -} diff --git a/src/MiningCore/Crypto/Hashing/Ethash/EthashFull.cs b/src/MiningCore/Crypto/Hashing/Ethash/EthashFull.cs index cb188a036..2d5a509ae 100644 --- a/src/MiningCore/Crypto/Hashing/Ethash/EthashFull.cs +++ b/src/MiningCore/Crypto/Hashing/Ethash/EthashFull.cs @@ -73,7 +73,7 @@ public async Task GetDagAsync(ulong block, ILogger logger) } // If we used up the future cache, or need a refresh, regenerate - if (future == null || future.Epoch <= epoch) + else if (future == null || future.Epoch <= epoch) { logger.Info(() => $"Pre-generating DAG for epoch {epoch + 1}"); future = new Dag(epoch + 1); diff --git a/src/MiningCore/Crypto/Hashing/Ethash/EthashLight.cs b/src/MiningCore/Crypto/Hashing/Ethash/EthashLight.cs deleted file mode 100644 index 1fe7b1cef..000000000 --- a/src/MiningCore/Crypto/Hashing/Ethash/EthashLight.cs +++ /dev/null @@ -1,125 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using System.Threading.Tasks; -using MiningCore.Blockchain.Ethereum; -using MiningCore.Contracts; -using MiningCore.Extensions; -using NLog; - -namespace MiningCore.Crypto.Hashing.Ethash -{ - public class EthashLight : IDisposable - { - public EthashLight(int numCaches) - { - this.numCaches = numCaches; - } - - private int numCaches; // Maximum number of caches to keep before eviction (only init, don't modify) - private readonly object cacheLock = new object(); - private readonly Dictionary caches = new Dictionary(); - private Cache future; - - public void Dispose() - { - foreach(var value in caches.Values) - value.Dispose(); - } - - public async Task VerifyBlockAsync(Block block, ILogger logger) - { - Contract.RequiresNonNull(block, nameof(block)); - - if (block.Height > EthereumConstants.EpochLength * 2048) - { - logger.Debug(() => $"Block height {block.Height} exceeds limit of {EthereumConstants.EpochLength * 2048}"); - return false; - } - - if (block.Difficulty.CompareTo(BigInteger.Zero) == 0) - { - logger.Debug(() => $"Invalid block diff"); - return false; - } - - // look up cache - var cache = await GetCacheAsync(block.Height, logger); - - // Recompute the hash using the cache - if (!cache.Compute(logger, block.HashNoNonce, block.Nonce, out var mixDigest, out var resultBytes)) - return false; - - // avoid mixdigest malleability as it's not included in a block's "hashNononce" - if (!block.MixDigest.SequenceEqual(mixDigest)) - return false; - - // The actual check. - var target = BigInteger.Divide(EthereumConstants.BigMaxValue, block.Difficulty); - var resultValue = new BigInteger(resultBytes.ReverseArray()); - var result = resultValue.CompareTo(target) <= 0; - return result; - } - - private async Task GetCacheAsync(ulong block, ILogger logger) - { - var epoch = block / EthereumConstants.EpochLength; - Cache result; - - lock(cacheLock) - { - if (numCaches == 0) - numCaches = 3; - - if (!caches.TryGetValue(epoch, out result)) - { - // No cached DAG, evict the oldest if the cache limit was reached - while(caches.Count >= numCaches) - { - var toEvict = caches.Values.OrderBy(x => x.LastUsed).First(); - var key = caches.First(pair => pair.Value == toEvict).Key; - var epochToEvict = toEvict.Epoch; - - logger.Debug(() => $"Evicting DAG for epoch {epochToEvict} in favour of epoch {epoch}"); - toEvict.Dispose(); - caches.Remove(key); - } - - // If we have the new DAG pre-generated, use that, otherwise create a new one - if (future != null && future.Epoch == epoch) - { - logger.Debug(() => $"Using pre-generated DAG for epoch {epoch}"); - - result = future; - future = null; - } - - else - { - logger.Debug(() => $"No pre-generated DAG available, creating new for epoch {epoch}"); - result = new Cache(epoch); - } - - caches[epoch] = result; - - // If we just used up the future cache, or need a refresh, regenerate - if (future == null || future.Epoch <= epoch) - { - logger.Debug(() => $"Pre-generating DAG for epoch {epoch + 1}"); - future = new Cache(epoch + 1); - -#pragma warning disable 4014 - future.GenerateAsync(logger); -#pragma warning restore 4014 - } - } - - result.LastUsed = DateTime.Now; - } - - await result.GenerateAsync(logger); - return result; - } - } -} diff --git a/src/MiningCore/DaemonInterface/DaemonClient.cs b/src/MiningCore/DaemonInterface/DaemonClient.cs index 182815fab..2915c7ff2 100644 --- a/src/MiningCore/DaemonInterface/DaemonClient.cs +++ b/src/MiningCore/DaemonInterface/DaemonClient.cs @@ -39,6 +39,8 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Configuration; using MiningCore.Extensions; using MiningCore.JsonRpc; +using MiningCore.Messaging; +using MiningCore.Notifications.Messages; using MiningCore.Stratum; using MiningCore.Util; using NetMQ; @@ -55,11 +57,16 @@ namespace MiningCore.DaemonInterface /// public class DaemonClient { - public DaemonClient(JsonSerializerSettings serializerSettings) + public DaemonClient(JsonSerializerSettings serializerSettings, IMessageBus messageBus, string server, string poolId) { Contract.RequiresNonNull(serializerSettings, nameof(serializerSettings)); + Contract.RequiresNonNull(messageBus, nameof(messageBus)); + Contract.Requires(!string.IsNullOrEmpty(poolId), $"{nameof(poolId)} must not be empty"); this.serializerSettings = serializerSettings; + this.messageBus = messageBus; + this.server = server; + this.poolId = poolId; serializer = new JsonSerializer { @@ -74,6 +81,16 @@ public DaemonClient(JsonSerializerSettings serializerSettings) private Dictionary httpClients; private readonly JsonSerializer serializer; + // Telemetry + private readonly IMessageBus messageBus; + private readonly string server; + private readonly string poolId; + + protected void PublishTelemetry(TelemetryCategory cat, TimeSpan elapsed, string info, bool? success = null, string error = null) + { + messageBus.SendMessage(new TelemetryEvent(server, poolId, cat, info, elapsed, success)); + } + #region API-Surface public void Configure(DaemonEndpointConfig[] endPoints, string digestAuthRealm = null) @@ -255,6 +272,10 @@ private async Task BuildRequestTask(DaemonEndpointConfig endPoi { var rpcRequestId = GetRequestId(); + // telemetry + var sw = new Stopwatch(); + sw.Start(); + // build rpc request var rpcRequest = new JsonRpcRequest(method, payload, rpcRequestId); @@ -293,6 +314,11 @@ private async Task BuildRequestTask(DaemonEndpointConfig endPoi using (var jreader = new JsonTextReader(reader)) { var result = serializer.Deserialize(jreader); + + // telemetry + sw.Stop(); + PublishTelemetry(TelemetryCategory.RpcRequest, sw.Elapsed, method, response.IsSuccessStatusCode); + return result; } } @@ -303,6 +329,10 @@ private async Task BuildRequestTask(DaemonEndpointConfig endPoi private async Task[]> BuildBatchRequestTask(DaemonEndpointConfig endPoint, DaemonCmd[] batch) { + // telemetry + var sw = new Stopwatch(); + sw.Start(); + // build rpc request var rpcRequests = batch.Select(x => new JsonRpcRequest(x.Method, x.Payload, GetRequestId())); @@ -336,7 +366,13 @@ private async Task[]> BuildBatchRequestTask(DaemonEndpoi { // check success if (!response.IsSuccessStatusCode) + { + // telemetry + sw.Stop(); + PublishTelemetry(TelemetryCategory.RpcRequest, sw.Elapsed, string.Join(", ", batch.Select(x=> x.Method)), false, response.StatusCode.ToString()); + throw new DaemonClientException(response.StatusCode, response.ReasonPhrase); + } // deserialize response using(var stream = await response.Content.ReadAsStreamAsync()) @@ -346,6 +382,11 @@ private async Task[]> BuildBatchRequestTask(DaemonEndpoi using(var jreader = new JsonTextReader(reader)) { var result = serializer.Deserialize[]>(jreader); + + // telemetry + sw.Stop(); + PublishTelemetry(TelemetryCategory.RpcRequest, sw.Elapsed, string.Join(", ", batch.Select(x => x.Method)), true); + return result; } } diff --git a/src/MiningCore/Mining/Abstractions.cs b/src/MiningCore/Mining/Abstractions.cs index 21549e352..a0ecc73c0 100644 --- a/src/MiningCore/Mining/Abstractions.cs +++ b/src/MiningCore/Mining/Abstractions.cs @@ -47,5 +47,6 @@ public interface IMiningPool void Configure(PoolConfig poolConfig, ClusterConfig clusterConfig); double HashrateFromShares(double shares, double interval); Task StartAsync(CancellationToken ctsToken); + void Stop(); } } diff --git a/src/MiningCore/Mining/PoolBase.cs b/src/MiningCore/Mining/PoolBase.cs index 044b50e12..90e48ce6f 100644 --- a/src/MiningCore/Mining/PoolBase.cs +++ b/src/MiningCore/Mining/PoolBase.cs @@ -37,6 +37,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Extensions; using MiningCore.Messaging; using MiningCore.Notifications; +using MiningCore.Notifications.Messages; using MiningCore.Persistence; using MiningCore.Persistence.Repositories; using MiningCore.Stratum; @@ -58,8 +59,7 @@ protected PoolBase(IComponentContext ctx, IStatsRepository statsRepo, IMapper mapper, IMasterClock clock, - IMessageBus messageBus, - NotificationService notificationService) : base(ctx, clock) + IMessageBus messageBus) : base(ctx, clock) { Contract.RequiresNonNull(ctx, nameof(ctx)); Contract.RequiresNonNull(serializerSettings, nameof(serializerSettings)); @@ -68,19 +68,16 @@ protected PoolBase(IComponentContext ctx, Contract.RequiresNonNull(mapper, nameof(mapper)); Contract.RequiresNonNull(clock, nameof(clock)); Contract.RequiresNonNull(messageBus, nameof(messageBus)); - Contract.RequiresNonNull(notificationService, nameof(notificationService)); this.serializerSettings = serializerSettings; this.cf = cf; this.statsRepo = statsRepo; this.mapper = mapper; this.messageBus = messageBus; - this.notificationService = notificationService; } protected PoolStats poolStats = new PoolStats(); protected readonly JsonSerializerSettings serializerSettings; - protected readonly NotificationService notificationService; protected readonly IConnectionFactory cf; protected readonly IStatsRepository statsRepo; protected readonly IMapper mapper; @@ -158,11 +155,16 @@ private void EnsureNoZombieClient(StratumClient client) }); } + protected void PublishTelemetry(TelemetryCategory cat, TimeSpan elapsed, bool? success = null) + { + messageBus.SendMessage(new TelemetryEvent(clusterConfig.ClusterName ?? poolConfig.PoolName, poolConfig.Id, cat, elapsed, success)); + } + #region VarDiff protected void UpdateVarDiff(StratumClient client, bool isIdleUpdate = false) { - var context = client.GetContextAs(); + var context = client.ContextAs(); if (context.VarDiff != null) { @@ -220,7 +222,7 @@ private void StartVarDiffIdleUpdate(StratumClient client, PoolEndpoint poolEndpo protected virtual void OnVarDiffUpdate(StratumClient client, double newDiff) { - var context = client.GetContextAs(); + var context = client.ContextAs(); context.EnqueueNewDifficulty(newDiff); } @@ -383,6 +385,11 @@ public virtual async Task StartAsync(CancellationToken ct) } } - #endregion // API-Surface + public void Stop() + { + StopListeners(); + } + + #endregion // API-Surface } } diff --git a/src/MiningCore/Mining/ShareReceiver.cs b/src/MiningCore/Mining/ShareReceiver.cs index 8249fb832..7134a0738 100644 --- a/src/MiningCore/Mining/ShareReceiver.cs +++ b/src/MiningCore/Mining/ShareReceiver.cs @@ -21,7 +21,7 @@ namespace MiningCore.Mining { /// - /// Received external shares from relay and re-publishes for consumption + /// Receives external shares from relays and re-publishes for consumption /// public class ShareReceiver { diff --git a/src/MiningCore/Mining/ShareRecorder.cs b/src/MiningCore/Mining/ShareRecorder.cs index ec833616c..1e7319dc1 100644 --- a/src/MiningCore/Mining/ShareRecorder.cs +++ b/src/MiningCore/Mining/ShareRecorder.cs @@ -57,8 +57,7 @@ public ShareRecorder(IConnectionFactory cf, IMapper mapper, JsonSerializerSettings jsonSerializerSettings, IShareRepository shareRepo, IBlockRepository blockRepo, IMasterClock clock, - IMessageBus messageBus, - NotificationService notificationService) + IMessageBus messageBus) { Contract.RequiresNonNull(cf, nameof(cf)); Contract.RequiresNonNull(mapper, nameof(mapper)); @@ -67,14 +66,12 @@ public ShareRecorder(IConnectionFactory cf, IMapper mapper, Contract.RequiresNonNull(jsonSerializerSettings, nameof(jsonSerializerSettings)); Contract.RequiresNonNull(clock, nameof(clock)); Contract.RequiresNonNull(messageBus, nameof(messageBus)); - Contract.RequiresNonNull(notificationService, nameof(notificationService)); this.cf = cf; this.mapper = mapper; this.jsonSerializerSettings = jsonSerializerSettings; this.clock = clock; this.messageBus = messageBus; - this.notificationService = notificationService; this.shareRepo = shareRepo; this.blockRepo = blockRepo; @@ -89,13 +86,11 @@ public ShareRecorder(IConnectionFactory cf, IMapper mapper, private readonly JsonSerializerSettings jsonSerializerSettings; private readonly IMasterClock clock; private readonly IMessageBus messageBus; - private readonly NotificationService notificationService; private ClusterConfig clusterConfig; private readonly IMapper mapper; private readonly BlockingCollection queue = new BlockingCollection(); private readonly int QueueSizeWarningThreshold = 1024; - private readonly TimeSpan relayReceiveTimeout = TimeSpan.FromSeconds(60); private Policy faultPolicy; private bool hasLoggedPolicyFallbackFailure; private bool hasWarnedAboutBacklogSize; @@ -109,7 +104,7 @@ private void PersistSharesFaulTolerant(IList shares) { var context = new Dictionary { { PolicyContextKeyShares, shares } }; - faultPolicy.Execute(() => { PersistShares(shares); }, context); + faultPolicy.Execute(ctx => PersistShares((IList)ctx[PolicyContextKeyShares]), context); } private void PersistShares(IList shares) @@ -292,9 +287,8 @@ private void NotifyAdminOnPolicyFallback() { notifiedAdminOnPolicyFallback = true; - notificationService.NotifyAdmin( - "Share Recorder Policy Fallback", - $"The Share Recorder's Policy Fallback has been engaged. Check share recovery file {recoveryFilename}."); + messageBus.SendMessage(new AdminNotification("Share Recorder Policy Fallback", + $"The Share Recorder's Policy Fallback has been engaged. Check share recovery file {recoveryFilename}.")); } } diff --git a/src/MiningCore/MiningCore.csproj b/src/MiningCore/MiningCore.csproj index 6234f7724..a56d996d4 100644 --- a/src/MiningCore/MiningCore.csproj +++ b/src/MiningCore/MiningCore.csproj @@ -43,27 +43,29 @@ - - - - - - - - + + + + + + + + + - - - + + + + - - - + + + - + diff --git a/src/MiningCore/Native/LibMultihash.cs b/src/MiningCore/Native/LibMultihash.cs index 81e0d949c..143951849 100644 --- a/src/MiningCore/Native/LibMultihash.cs +++ b/src/MiningCore/Native/LibMultihash.cs @@ -112,6 +112,9 @@ public static unsafe class LibMultihash [DllImport("libmultihash", EntryPoint = "equihash_verify_export", CallingConvention = CallingConvention.Cdecl)] public static extern bool equihash_verify(byte* header, int headerLength, byte* solution, int solutionLength); + [DllImport("libmultihash", EntryPoint = "equihash_verify_btg_export", CallingConvention = CallingConvention.Cdecl)] + public static extern bool equihash_verify_btg(byte* header, int headerLength, byte* solution, int solutionLength); + [DllImport("libmultihash", EntryPoint = "sha3_256_export", CallingConvention = CallingConvention.Cdecl)] public static extern int sha3_256(byte* input, byte* output, uint inputLength); diff --git a/src/MiningCore/Notifications/Messages/AdminNotification.cs b/src/MiningCore/Notifications/Messages/AdminNotification.cs new file mode 100644 index 000000000..dbe0097eb --- /dev/null +++ b/src/MiningCore/Notifications/Messages/AdminNotification.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MiningCore.Notifications.Messages +{ + public class AdminNotification + { + public AdminNotification(string subject, string message) + { + Subject = subject; + Message = message; + } + + public string Subject { get; } + public string Message { get; } + } +} diff --git a/src/MiningCore/Notifications/Messages/PaymentNotification.cs b/src/MiningCore/Notifications/Messages/PaymentNotification.cs new file mode 100644 index 000000000..559a7ee26 --- /dev/null +++ b/src/MiningCore/Notifications/Messages/PaymentNotification.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MiningCore.Notifications.Messages +{ + public class PaymentNotification + { + public PaymentNotification(string poolId, string error, decimal amount, int recpientsCount, string txInfo, decimal? txFee) + { + PoolId = poolId; + Error = error; + Amount = amount; + RecpientsCount = recpientsCount; + TxInfo = txInfo; + TxFee = txFee; + } + + public PaymentNotification(string poolId, string error, decimal amount) : this(poolId, error, amount, 0, null, null) + { + } + + public decimal? TxFee { get; } + public string TxInfo { get; } + public int RecpientsCount { get; } + public decimal Amount { get; } + public string Error { get; } + public string PoolId { get; } + } +} diff --git a/src/MiningCore/Notifications/Messages/TelemetryEvent.cs b/src/MiningCore/Notifications/Messages/TelemetryEvent.cs new file mode 100644 index 000000000..e68716627 --- /dev/null +++ b/src/MiningCore/Notifications/Messages/TelemetryEvent.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MiningCore.Notifications.Messages +{ + public enum TelemetryCategory + { + Share = 1, // Share processed + BtStream, // Blocktemplate over BTStream + RpcRequest // JsonRPC Request to Daemon + } + + public class TelemetryEvent + { + public TelemetryEvent(string server, string poolId, TelemetryCategory category, TimeSpan elapsed, bool? success = null, string error = null) + { + Server = server; + PoolId = poolId; + Category = category; + Elapsed = elapsed; + Success = success; + Error = error; + } + + public TelemetryEvent(string server, string poolId, TelemetryCategory category, string info, TimeSpan elapsed, bool? success = null, string error = null) : + this(server, poolId, category, elapsed, success, error) + { + Info = info; + } + + public string Server { get; set; } + public string PoolId { get; set; } + public TelemetryCategory Category { get; set; } + public string Info { get; } + public TimeSpan Elapsed { get; set; } + public bool? Success { get; set; } + public string Error { get; } + } +} diff --git a/src/MiningCore/Notifications/NotificationService.cs b/src/MiningCore/Notifications/NotificationService.cs index 2d577c5df..cb99583db 100644 --- a/src/MiningCore/Notifications/NotificationService.cs +++ b/src/MiningCore/Notifications/NotificationService.cs @@ -30,7 +30,7 @@ public NotificationService( IMessageBus messageBus) { Contract.RequiresNonNull(clusterConfig, nameof(clusterConfig)); - Contract.RequiresNonNull(messageBus, nameof(messageBus)); + Contract.RequiresNonNull(messageBus, nameof(messageBus)); this.clusterConfig = clusterConfig; this.serializerSettings = serializerSettings; @@ -50,6 +50,17 @@ public NotificationService( .Concat() .Subscribe(); + messageBus.Listen() + .Subscribe(x => + { + queue?.Add(new QueuedNotification + { + Category = NotificationCategory.Admin, + Subject = x.Subject, + Msg = x.Message + }); + }); + messageBus.Listen() .Subscribe(x => { @@ -61,6 +72,32 @@ public NotificationService( Msg = $"Pool {x.PoolId} found block candidate {x.BlockHeight}" }); }); + + messageBus.Listen() + .Subscribe(x => + { + if (string.IsNullOrEmpty(x.Error)) + { + queue?.Add(new QueuedNotification + { + Category = NotificationCategory.PaymentSuccess, + PoolId = x.PoolId, + Subject = "Payout Success Notification", + Msg = $"Paid {FormatAmount(x.Amount, x.PoolId)} from pool {x.PoolId} to {x.RecpientsCount} recipients in Transaction(s) {x.TxInfo}." + }); + } + + else + { + queue?.Add(new QueuedNotification + { + Category = NotificationCategory.PaymentFailure, + PoolId = x.PoolId, + Subject = "Payout Failure Notification", + Msg = $"Failed to pay out {x.Amount} {poolConfigs[x.PoolId].Coin.Type} from pool {x.PoolId}: {x.Error}" + }); + } + }); } } @@ -95,42 +132,6 @@ struct QueuedNotification public string Msg; } - #region API-Surface - - public void NotifyPaymentSuccess(string poolId, decimal amount, int recpientsCount, string txInfo, decimal? txFee) - { - queue?.Add(new QueuedNotification - { - Category = NotificationCategory.PaymentSuccess, - PoolId = poolId, - Subject = "Payout Success Notification", - Msg = $"Paid {FormatAmount(amount, poolId)} from pool {poolId} to {recpientsCount} recipients in Transaction(s) {txInfo}." - }); - } - - public void NotifyPaymentFailure(string poolId, decimal amount, string message) - { - queue?.Add(new QueuedNotification - { - Category = NotificationCategory.PaymentFailure, - PoolId = poolId, - Subject = "Payout Failure Notification", - Msg = $"Failed to pay out {amount} {poolConfigs[poolId].Coin.Type} from pool {poolId}: {message}" - }); - } - - public void NotifyAdmin(string subject, string message) - { - queue?.Add(new QueuedNotification - { - Category = NotificationCategory.Admin, - Subject = subject, - Msg = message - }); - } - - #endregion // API-Surface - public string FormatAmount(decimal amount, string poolId) { return $"{amount:0.#####} {poolConfigs[poolId].Coin.Type}"; diff --git a/src/MiningCore/Payments/PayoutHandlerBase.cs b/src/MiningCore/Payments/PayoutHandlerBase.cs index 7aa791519..e38deb656 100644 --- a/src/MiningCore/Payments/PayoutHandlerBase.cs +++ b/src/MiningCore/Payments/PayoutHandlerBase.cs @@ -25,7 +25,8 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Blockchain; using MiningCore.Configuration; using MiningCore.Extensions; -using MiningCore.Notifications; +using MiningCore.Messaging; +using MiningCore.Notifications.Messages; using MiningCore.Persistence; using MiningCore.Persistence.Model; using MiningCore.Persistence.Repositories; @@ -39,13 +40,15 @@ namespace MiningCore.Payments { public abstract class PayoutHandlerBase { - protected PayoutHandlerBase(IConnectionFactory cf, IMapper mapper, + protected PayoutHandlerBase( + IConnectionFactory cf, + IMapper mapper, IShareRepository shareRepo, IBlockRepository blockRepo, IBalanceRepository balanceRepo, IPaymentRepository paymentRepo, IMasterClock clock, - NotificationService notificationService) + IMessageBus messageBus) { Contract.RequiresNonNull(cf, nameof(cf)); Contract.RequiresNonNull(mapper, nameof(mapper)); @@ -54,7 +57,7 @@ protected PayoutHandlerBase(IConnectionFactory cf, IMapper mapper, Contract.RequiresNonNull(balanceRepo, nameof(balanceRepo)); Contract.RequiresNonNull(paymentRepo, nameof(paymentRepo)); Contract.RequiresNonNull(clock, nameof(clock)); - Contract.RequiresNonNull(notificationService, nameof(notificationService)); + Contract.RequiresNonNull(messageBus, nameof(messageBus)); this.cf = cf; this.mapper = mapper; @@ -63,7 +66,7 @@ protected PayoutHandlerBase(IConnectionFactory cf, IMapper mapper, this.blockRepo = blockRepo; this.balanceRepo = balanceRepo; this.paymentRepo = paymentRepo; - this.notificationService = notificationService; + this.messageBus = messageBus; BuildFaultHandlingPolicy(); } @@ -75,7 +78,7 @@ protected PayoutHandlerBase(IConnectionFactory cf, IMapper mapper, protected readonly IPaymentRepository paymentRepo; protected readonly IShareRepository shareRepo; protected readonly IMasterClock clock; - protected readonly NotificationService notificationService; + protected readonly IMessageBus messageBus; protected ClusterConfig clusterConfig; private Policy faultPolicy; @@ -160,13 +163,13 @@ protected virtual void NotifyPayoutSuccess(string poolId, Balance[] balances, st if (CoinMetaData.TxInfoLinks.TryGetValue(poolConfig.Coin.Type, out var baseUrl)) txInfo = string.Join(", ", txHashes.Select(txHash => $"{txHash}")); - notificationService.NotifyPaymentSuccess(poolId, balances.Sum(x => x.Amount), balances.Length, txInfo, txFee); + messageBus.SendMessage(new PaymentNotification(poolId, null, balances.Sum(x => x.Amount), balances.Length, txInfo, txFee)); } } protected virtual void NotifyPayoutFailure(string poolId, Balance[] balances, string error, Exception ex) { - notificationService.NotifyPaymentFailure(poolId, balances.Sum(x => x.Amount), error ?? ex?.Message); + messageBus.SendMessage(new PaymentNotification(poolId, error ?? ex?.Message, balances.Sum(x => x.Amount))); } } } diff --git a/src/MiningCore/Payments/PayoutManager.cs b/src/MiningCore/Payments/PayoutManager.cs index 5d4e91d14..72a55d948 100644 --- a/src/MiningCore/Payments/PayoutManager.cs +++ b/src/MiningCore/Payments/PayoutManager.cs @@ -27,7 +27,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using Autofac.Features.Metadata; using MiningCore.Configuration; using MiningCore.Extensions; +using MiningCore.Messaging; using MiningCore.Notifications; +using MiningCore.Notifications.Messages; using MiningCore.Persistence; using MiningCore.Persistence.Model; using MiningCore.Persistence.Repositories; @@ -46,21 +48,21 @@ public PayoutManager(IComponentContext ctx, IBlockRepository blockRepo, IShareRepository shareRepo, IBalanceRepository balanceRepo, - NotificationService notificationService) + IMessageBus messageBus) { Contract.RequiresNonNull(ctx, nameof(ctx)); Contract.RequiresNonNull(cf, nameof(cf)); Contract.RequiresNonNull(blockRepo, nameof(blockRepo)); Contract.RequiresNonNull(shareRepo, nameof(shareRepo)); Contract.RequiresNonNull(balanceRepo, nameof(balanceRepo)); - Contract.RequiresNonNull(notificationService, nameof(notificationService)); + Contract.RequiresNonNull(messageBus, nameof(messageBus)); this.ctx = ctx; this.cf = cf; this.blockRepo = blockRepo; this.shareRepo = shareRepo; this.balanceRepo = balanceRepo; - this.notificationService = notificationService; + this.messageBus = messageBus; } private readonly IBalanceRepository balanceRepo; @@ -68,7 +70,7 @@ public PayoutManager(IComponentContext ctx, private readonly IConnectionFactory cf; private readonly IComponentContext ctx; private readonly IShareRepository shareRepo; - private readonly NotificationService notificationService; + private readonly IMessageBus messageBus; private readonly AutoResetEvent stopEvent = new AutoResetEvent(false); private ClusterConfig clusterConfig; private Thread thread; @@ -180,7 +182,7 @@ private async Task PayoutPoolBalancesAsync(PoolConfig pool, IPayoutHandler handl private Task NotifyPayoutFailureAsync(Balance[] balances, PoolConfig pool, Exception ex) { - notificationService.NotifyPaymentFailure(pool.Id, balances.Sum(x => x.Amount), ex.Message); + messageBus.SendMessage(new PaymentNotification(pool.Id, ex.Message, balances.Sum(x => x.Amount))); return Task.FromResult(true); } diff --git a/src/MiningCore/Program.cs b/src/MiningCore/Program.cs index d1862b460..4d1879f88 100644 --- a/src/MiningCore/Program.cs +++ b/src/MiningCore/Program.cs @@ -45,6 +45,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Extensions; using MiningCore.Mining; using MiningCore.Native; +using MiningCore.Notifications; using MiningCore.Payments; using MiningCore.Persistence.Dummy; using MiningCore.Persistence.Postgres; @@ -75,6 +76,8 @@ public class Program private static StatsRecorder statsRecorder; private static ClusterConfig clusterConfig; private static ApiServer apiServer; + private static NotificationService notificationService; + private static readonly Dictionary pools = new Dictionary(); public static AdminGcStats gcStats = new AdminGcStats(); @@ -157,8 +160,8 @@ public static void Main(string[] args) } Shutdown(); - Process.GetCurrentProcess().CloseMainWindow(); - Process.GetCurrentProcess().Close(); + + Process.GetCurrentProcess().Kill(); } private static void LogRuntimeInfo() @@ -495,7 +498,7 @@ private static void ConfigureMisc() { // Configure Equihash if (clusterConfig.EquihashMaxThreads.HasValue) - EquihashSolver.MaxThreads = clusterConfig.EquihashMaxThreads.Value; + EquihashSolverBase.MaxThreads = clusterConfig.EquihashMaxThreads.Value; } private static void ConfigurePersistence(ContainerBuilder builder) @@ -557,6 +560,8 @@ private static void ConfigureDummyPersistence(ContainerBuilder builder) private static async Task Start() { + notificationService = container.Resolve(); + if (clusterConfig.ShareRelay == null) { // start share recorder @@ -613,6 +618,7 @@ await Task.WhenAll(clusterConfig.Pools.Where(x => x.Enabled).Select(async poolCo // create and configure var pool = poolImpl.Value; pool.Configure(poolConfig, clusterConfig); + pools[poolConfig.Id] = pool; // pre-start attachments shareReceiver?.AttachPool(pool); @@ -673,6 +679,9 @@ private static void Shutdown() logger.Info(() => "Shutdown ..."); Console.WriteLine("Shutdown..."); + foreach (var pool in pools.Values) + pool.Stop(); + shareRelay?.Stop(); shareRecorder?.Stop(); statsRecorder?.Stop(); diff --git a/src/MiningCore/Stratum/StratumClient.cs b/src/MiningCore/Stratum/StratumClient.cs index e27c1f226..61885f3b0 100644 --- a/src/MiningCore/Stratum/StratumClient.cs +++ b/src/MiningCore/Stratum/StratumClient.cs @@ -115,7 +115,7 @@ public void SetContext(T value) where T : WorkerContextBase context = value; } - public T GetContextAs() where T: WorkerContextBase + public T ContextAs() where T: WorkerContextBase { return (T) context; } diff --git a/src/MiningCore/Stratum/StratumServer.cs b/src/MiningCore/Stratum/StratumServer.cs index fde585500..55c4f3f79 100644 --- a/src/MiningCore/Stratum/StratumServer.cs +++ b/src/MiningCore/Stratum/StratumServer.cs @@ -61,6 +61,7 @@ protected StratumServer(IComponentContext ctx, IMasterClock clock) protected IBanManager banManager; protected bool disableConnectionLogging = false; protected ILogger logger; + private Async loopStop; protected abstract string LogCat { get; } @@ -68,17 +69,24 @@ public void StartListeners(string id, params (IPEndPoint IPEndPoint, TcpProxyPro { Contract.RequiresNonNull(stratumPorts, nameof(stratumPorts)); - // every port gets serviced by a dedicated loop thread - foreach(var endpoint in stratumPorts) + // all ports get serviced by a single loop thread, actual work is dispatched to taskpool threads + var thread = new Thread(_ => { - var thread = new Thread(_ => + var loop = new Loop(); + + // loop thread must be terminated from within + loopStop = loop.CreateAsync((handle) => { - var loop = new Loop(); + loop.Stop(); + + handle.Dispose(); + }); - try + try + { + foreach (var endpoint in stratumPorts) { - var listener = loop - .CreateTcp() + var tcp = loop.CreateTcp() .NoDelay(true) .SimultaneousAccepts(false) .Listen(endpoint.IPEndPoint, (con, ex) => @@ -91,50 +99,33 @@ public void StartListeners(string id, params (IPEndPoint IPEndPoint, TcpProxyPro lock (ports) { - ports[endpoint.IPEndPoint.Port] = listener; + ports[endpoint.IPEndPoint.Port] = tcp; } - } - catch (Exception ex) - { - logger.Error(ex, $"[{LogCat}] {ex}"); - throw; + logger.Info(() => $"[{LogCat}] Stratum ports {endpoint.IPEndPoint.Address}:{endpoint.IPEndPoint.Port} online"); } - logger.Info(() => $"[{LogCat}] Stratum port {endpoint.IPEndPoint.Address}:{endpoint.IPEndPoint.Port} online"); + // Go + loop.RunDefault(); - try - { - loop.RunDefault(); - } + // Done + loop.Dispose(); - catch(Exception ex) - { - logger.Error(ex, $"[{LogCat}] {ex}"); - } - }) { Name = $"UvLoopThread {id}:{endpoint.IPEndPoint.Port}" }; + logger.Info(() => $"[{LogCat}] Stratum stopped"); + } - thread.Start(); - } + catch(Exception ex) + { + logger.Error(ex, $"[{LogCat}] {ex}"); + } + }) { Name = $"UvLoopThread [{id.ToUpper()}]" }; + + thread.Start(); } public void StopListeners() { - lock(ports) - { - var portValues = ports.Values.ToArray(); - - for(int i = 0; i < portValues.Length; i++) - { - var listener = portValues[i]; - - listener.Shutdown((tcp, ex) => - { - if (tcp?.IsValid == true) - tcp.Dispose(); - }); - } - } + loopStop.Send(); } private void OnClientConnected(Tcp con, (IPEndPoint IPEndPoint, TcpProxyProtocolConfig ProxyProtocol) endpointConfig, Loop loop) @@ -161,7 +152,7 @@ private void OnClientConnected(Tcp con, (IPEndPoint IPEndPoint, TcpProxyProtocol var client = new StratumClient(); client.Init(loop, con, ctx, clock, endpointConfig, connectionId, - data => OnReceive(client, data), + data => Task.Run(()=> OnReceive(client, data)), () => OnReceiveComplete(client), ex => OnReceiveError(client, ex)); @@ -180,63 +171,59 @@ private void OnClientConnected(Tcp con, (IPEndPoint IPEndPoint, TcpProxyProtocol } } - protected virtual void OnReceive(StratumClient client, PooledArraySegment data) + protected async void OnReceive(StratumClient client, PooledArraySegment data) { - // get off of LibUV event-loop-thread immediately - Task.Run(async () => + using (data) { - using (data) - { - JsonRpcRequest request = null; + JsonRpcRequest request = null; - try + try + { + // boot pre-connected clients + if (banManager?.IsBanned(client.RemoteEndpoint.Address) == true) { - // boot pre-connected clients - if (banManager?.IsBanned(client.RemoteEndpoint.Address) == true) - { - logger.Info(() => $"[{LogCat}] [{client.ConnectionId}] Disconnecting banned client @ {client.RemoteEndpoint.Address}"); - DisconnectClient(client); - return; - } - - // de-serialize - logger.Trace(() => $"[{LogCat}] [{client.ConnectionId}] Received request data: {StratumConstants.Encoding.GetString(data.Array, 0, data.Size)}"); - request = client.DeserializeRequest(data); + logger.Info(() => $"[{LogCat}] [{client.ConnectionId}] Disconnecting banned client @ {client.RemoteEndpoint.Address}"); + DisconnectClient(client); + return; + } - // dispatch - if (request != null) - { - logger.Debug(() => $"[{LogCat}] [{client.ConnectionId}] Dispatching request '{request.Method}' [{request.Id}]"); - await OnRequestAsync(client, new Timestamped(request, clock.Now)); - } + // de-serialize + logger.Trace(() => $"[{LogCat}] [{client.ConnectionId}] Received request data: {StratumConstants.Encoding.GetString(data.Array, 0, data.Size)}"); + request = client.DeserializeRequest(data); - else - logger.Trace(() => $"[{LogCat}] [{client.ConnectionId}] Unable to deserialize request"); + // dispatch + if (request != null) + { + logger.Debug(() => $"[{LogCat}] [{client.ConnectionId}] Dispatching request '{request.Method}' [{request.Id}]"); + await OnRequestAsync(client, new Timestamped(request, clock.Now)); } - catch (JsonReaderException jsonEx) - { - // junk received (no valid json) - logger.Error(() => $"[{LogCat}] [{client.ConnectionId}] Connection json error state: {jsonEx.Message}"); + else + logger.Trace(() => $"[{LogCat}] [{client.ConnectionId}] Unable to deserialize request"); + } - if (clusterConfig.Banning?.BanOnJunkReceive.HasValue == false || clusterConfig.Banning?.BanOnJunkReceive == true) - { - logger.Info(() => $"[{LogCat}] [{client.ConnectionId}] Banning client for sending junk"); - banManager?.Ban(client.RemoteEndpoint.Address, TimeSpan.FromMinutes(30)); - } - } + catch (JsonReaderException jsonEx) + { + // junk received (no valid json) + logger.Error(() => $"[{LogCat}] [{client.ConnectionId}] Connection json error state: {jsonEx.Message}"); - catch (Exception ex) + if (clusterConfig.Banning?.BanOnJunkReceive.HasValue == false || clusterConfig.Banning?.BanOnJunkReceive == true) { - var innerEx = ex.InnerException != null ? ": " + ex : ""; - - if (request != null) - logger.Error(ex, () => $"[{LogCat}] [{client.ConnectionId}] Error processing request {request.Method} [{request.Id}]{innerEx}"); - else - logger.Error(ex, () => $"[{LogCat}] [{client.ConnectionId}] Error processing request{innerEx}"); + logger.Info(() => $"[{LogCat}] [{client.ConnectionId}] Banning client for sending junk"); + banManager?.Ban(client.RemoteEndpoint.Address, TimeSpan.FromMinutes(30)); } } - }); + + catch (Exception ex) + { + var innerEx = ex.InnerException != null ? ": " + ex : ""; + + if (request != null) + logger.Error(ex, () => $"[{LogCat}] [{client.ConnectionId}] Error processing request {request.Method} [{request.Id}]{innerEx}"); + else + logger.Error(ex, () => $"[{LogCat}] [{client.ConnectionId}] Error processing request{innerEx}"); + } + } } protected virtual void OnReceiveError(StratumClient client, Exception ex) diff --git a/src/MiningCore/runtimes/win-x64/native/libmultihash.dll b/src/MiningCore/runtimes/win-x64/native/libmultihash.dll index 83d1dc302..5b2d0c6ff 100644 Binary files a/src/MiningCore/runtimes/win-x64/native/libmultihash.dll and b/src/MiningCore/runtimes/win-x64/native/libmultihash.dll differ diff --git a/src/MiningCore/runtimes/win-x86/native/libmultihash.dll b/src/MiningCore/runtimes/win-x86/native/libmultihash.dll index 903c200ed..ae6201d9e 100644 Binary files a/src/MiningCore/runtimes/win-x86/native/libmultihash.dll and b/src/MiningCore/runtimes/win-x86/native/libmultihash.dll differ diff --git a/src/Native/libmultihash/equi/crypto/equihash.cpp b/src/Native/libmultihash/equi/crypto/equihash.cpp index 2db961502..ce8283ce2 100644 --- a/src/Native/libmultihash/equi/crypto/equihash.cpp +++ b/src/Native/libmultihash/equi/crypto/equihash.cpp @@ -34,12 +34,14 @@ EhSolverCancelledException solver_cancelled; template -int Equihash::InitialiseState(eh_HashState& base_state) +int Equihash::InitialiseState(eh_HashState& base_state, const char *_personalization) { uint32_t le_N = htole32(N); uint32_t le_K = htole32(K); unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES] = {}; - memcpy(personalization, "ZcashPoW", 8); + + //memcpy(personalization, "ZcashPoW", 8); + memcpy(personalization, _personalization, 8); memcpy(personalization+8, &le_N, 4); memcpy(personalization+12, &le_K, 4); return crypto_generichash_blake2b_init_salt_personal(&base_state, @@ -776,7 +778,7 @@ bool Equihash::IsValidSolution(const eh_HashState& base_state, std::vector< } // Explicit instantiations for Equihash<96,3> -template int Equihash<96,3>::InitialiseState(eh_HashState& base_state); +template int Equihash<96,3>::InitialiseState(eh_HashState& base_state, const char *personalization); #ifdef ENABLE_MINING template bool Equihash<96,3>::BasicSolve(const eh_HashState& base_state, const std::function)> validBlock, @@ -788,7 +790,7 @@ template bool Equihash<96,3>::OptimisedSolve(const eh_HashState& base_state, template bool Equihash<96,3>::IsValidSolution(const eh_HashState& base_state, std::vector soln); // Explicit instantiations for Equihash<200,9> -template int Equihash<200,9>::InitialiseState(eh_HashState& base_state); +template int Equihash<200,9>::InitialiseState(eh_HashState& base_state, const char *personalization); #ifdef ENABLE_MINING template bool Equihash<200,9>::BasicSolve(const eh_HashState& base_state, const std::function)> validBlock, @@ -800,7 +802,7 @@ template bool Equihash<200,9>::OptimisedSolve(const eh_HashState& base_state, template bool Equihash<200,9>::IsValidSolution(const eh_HashState& base_state, std::vector soln); // Explicit instantiations for Equihash<96,5> -template int Equihash<96,5>::InitialiseState(eh_HashState& base_state); +template int Equihash<96,5>::InitialiseState(eh_HashState& base_state, const char *personalization); #ifdef ENABLE_MINING template bool Equihash<96,5>::BasicSolve(const eh_HashState& base_state, const std::function)> validBlock, @@ -812,7 +814,7 @@ template bool Equihash<96,5>::OptimisedSolve(const eh_HashState& base_state, template bool Equihash<96,5>::IsValidSolution(const eh_HashState& base_state, std::vector soln); // Explicit instantiations for Equihash<48,5> -template int Equihash<48,5>::InitialiseState(eh_HashState& base_state); +template int Equihash<48,5>::InitialiseState(eh_HashState& base_state, const char *personalization); #ifdef ENABLE_MINING template bool Equihash<48,5>::BasicSolve(const eh_HashState& base_state, const std::function)> validBlock, @@ -822,3 +824,15 @@ template bool Equihash<48,5>::OptimisedSolve(const eh_HashState& base_state, const std::function cancelled); #endif template bool Equihash<48,5>::IsValidSolution(const eh_HashState& base_state, std::vector soln); + +// Explicit instantiations for Equihash<144,5> +template int Equihash<144, 5>::InitialiseState(eh_HashState& base_state, const char *personalization); +#ifdef ENABLE_MINING +template bool Equihash<144, 5>::BasicSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled); +template bool Equihash<144, 5>::OptimisedSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled); +#endif +template bool Equihash<144, 5>::IsValidSolution(const eh_HashState& base_state, std::vector soln); diff --git a/src/Native/libmultihash/equi/crypto/equihash.h b/src/Native/libmultihash/equi/crypto/equihash.h index a48cf454b..737156d93 100644 --- a/src/Native/libmultihash/equi/crypto/equihash.h +++ b/src/Native/libmultihash/equi/crypto/equihash.h @@ -185,7 +185,7 @@ class Equihash Equihash() { } - int InitialiseState(eh_HashState& base_state); + int InitialiseState(eh_HashState& base_state, const char *personalization); #ifdef ENABLE_MINING bool BasicSolve(const eh_HashState& base_state, const std::function)> validBlock, @@ -203,16 +203,19 @@ static Equihash<96,3> Eh96_3; static Equihash<200,9> Eh200_9; static Equihash<96,5> Eh96_5; static Equihash<48,5> Eh48_5; +static Equihash<144, 5> Eh144_5; // BTG -#define EhInitialiseState(n, k, base_state) \ +#define EhInitialiseState(n, k, base_state, personalization) \ if (n == 96 && k == 3) { \ - Eh96_3.InitialiseState(base_state); \ + Eh96_3.InitialiseState(base_state, personalization); \ } else if (n == 200 && k == 9) { \ - Eh200_9.InitialiseState(base_state); \ + Eh200_9.InitialiseState(base_state, personalization); \ } else if (n == 96 && k == 5) { \ - Eh96_5.InitialiseState(base_state); \ + Eh96_5.InitialiseState(base_state, personalization); \ } else if (n == 48 && k == 5) { \ - Eh48_5.InitialiseState(base_state); \ + Eh48_5.InitialiseState(base_state, personalization); \ + } else if (n == 144 && k == 5) { \ + Eh144_5.InitialiseState(base_state, personalization); \ } else { \ throw std::invalid_argument("Unsupported Equihash parameters"); \ } diff --git a/src/Native/libmultihash/equi/equihashverify.cc b/src/Native/libmultihash/equi/equihashverify.cc index 0d9423d83..a4df92b93 100644 --- a/src/Native/libmultihash/equi/equihashverify.cc +++ b/src/Native/libmultihash/equi/equihashverify.cc @@ -2,13 +2,21 @@ #include "crypto/equihash.h" #include "equihashverify.h" -bool verifyEH(const char *hdr, const std::vector &soln){ +static const char *default_personalization = "ZcashPoW"; + +bool verifyEH_200_9(const char *hdr, const std::vector &soln, const char *personalization){ unsigned int n = 200; unsigned int k = 9; + if (soln.size() != 1344) + return false; + + if (personalization == NULL) + personalization = default_personalization; + // Hash state crypto_generichash_blake2b_state state; - EhInitialiseState(n, k, state); + EhInitialiseState(n, k, state, personalization); crypto_generichash_blake2b_update(&state, (const unsigned char*)hdr, 140); @@ -16,3 +24,24 @@ bool verifyEH(const char *hdr, const std::vector &soln){ return isValid; } + +bool verifyEH_144_5(const char *hdr, const std::vector &soln, const char *personalization) { + unsigned int n = 144; + unsigned int k = 5; + + if (soln.size() != 100) + return false; + + if (personalization == NULL) + personalization = default_personalization; + + // Hash state + crypto_generichash_blake2b_state state; + EhInitialiseState(n, k, state, personalization); + + crypto_generichash_blake2b_update(&state, (const unsigned char*)hdr, 140); + + bool isValid = Eh144_5.IsValidSolution(state, soln); + + return isValid; +} diff --git a/src/Native/libmultihash/equi/equihashverify.h b/src/Native/libmultihash/equi/equihashverify.h index ee5b4b664..cdb1c8c8b 100644 --- a/src/Native/libmultihash/equi/equihashverify.h +++ b/src/Native/libmultihash/equi/equihashverify.h @@ -7,7 +7,8 @@ extern "C" { #endif -bool verifyEH(const char*, const std::vector&); +bool verifyEH_200_9(const char*, const std::vector&, const char *personalization); +bool verifyEH_144_5(const char*, const std::vector&, const char *personalization); #ifdef __cplusplus } diff --git a/src/Native/libmultihash/exports.cpp b/src/Native/libmultihash/exports.cpp index 7d57ec87b..3afceaca5 100644 --- a/src/Native/libmultihash/exports.cpp +++ b/src/Native/libmultihash/exports.cpp @@ -201,13 +201,24 @@ extern "C" MODULE_API void x16s_export(const char* input, char* output, uint32_t extern "C" MODULE_API bool equihash_verify_export(const char* header, int header_length, const char* solution, int solution_length) { - if (header_length != 140 || solution_length != 1344) { + if (header_length != 140) { return false; } std::vector vecSolution(solution, solution + solution_length); - return verifyEH(header, vecSolution); + return verifyEH_200_9(header, vecSolution, NULL); +} + +extern "C" MODULE_API bool equihash_verify_btg_export(const char* header, int header_length, const char* solution, int solution_length) +{ + if (header_length != 140) { + return false; + } + + std::vector vecSolution(solution, solution + solution_length); + + return verifyEH_144_5(header, vecSolution, "BgoldPoW"); } extern "C" MODULE_API void sha3_256_export(const char* input, char* output, uint32_t input_len)