diff --git a/examples/callisto_pool.json b/examples/callisto_pool.json index d2d596cd1..6a927c0f1 100644 --- a/examples/callisto_pool.json +++ b/examples/callisto_pool.json @@ -99,7 +99,6 @@ } }, "chainTypeOverride": "Callisto", - "dagDir": "", "daemons": [ { "host": "127.0.0.1", diff --git a/examples/ethereum_pool.json b/examples/ethereum_pool.json index 42137b0a8..420ebc8d4 100644 --- a/examples/ethereum_pool.json +++ b/examples/ethereum_pool.json @@ -100,7 +100,6 @@ } }, "chainTypeOverride": "Ethereum", - "dagDir": "", "daemons": [ { "host": "127.0.0.1", diff --git a/examples/ethereumclassic_pool.json b/examples/ethereumclassic_pool.json index 431034714..f2f6127cc 100644 --- a/examples/ethereumclassic_pool.json +++ b/examples/ethereumclassic_pool.json @@ -99,7 +99,6 @@ } }, "chainTypeOverride": "Classic", - "dagDir": "", "daemons": [ { "host": "127.0.0.1", diff --git a/examples/ubiq_pool.json b/examples/ubiq_pool.json index 81a001a35..9ca30f97a 100644 --- a/examples/ubiq_pool.json +++ b/examples/ubiq_pool.json @@ -99,7 +99,6 @@ } }, "chainTypeOverride": "Ubiq", - "dagDir": "", "daemons": [ { "host": "127.0.0.1", diff --git a/src/Miningcore.Tests/Benchmarks/BenchmarkRunner.cs b/src/Miningcore.Tests/Benchmarks/BenchmarkRunner.cs index cf6666f23..9c03a4f50 100644 --- a/src/Miningcore.Tests/Benchmarks/BenchmarkRunner.cs +++ b/src/Miningcore.Tests/Benchmarks/BenchmarkRunner.cs @@ -2,6 +2,7 @@ using BenchmarkDotNet.Configs; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Running; +using Miningcore.Tests.Benchmarks.Crypto; using Miningcore.Tests.Benchmarks.Stratum; using Xunit; using Xunit.Abstractions; @@ -18,7 +19,7 @@ public Benchmarks(ITestOutputHelper output) } [Fact(Skip = "** Uncomment me to run benchmarks **")] - public void Run_Benchmarks() + public void Run_Stratum_Benchmarks() { var logger = new AccumulationLogger(); @@ -31,4 +32,19 @@ public void Run_Benchmarks() // write benchmark summary output.WriteLine(logger.GetLog()); } + + [Fact(Skip = "** Uncomment me to run benchmarks **")] + public void Run_Crypto_Benchmarks() + { + var logger = new AccumulationLogger(); + + var config = ManualConfig.Create(DefaultConfig.Instance) + .AddLogger(logger) + .WithOptions(ConfigOptions.DisableOptimizationsValidator); + + BenchmarkRunner.Run(config); + + // write benchmark summary + output.WriteLine(logger.GetLog()); + } } diff --git a/src/Miningcore.Tests/Benchmarks/Crypto/EthashBenchmarks.cs b/src/Miningcore.Tests/Benchmarks/Crypto/EthashBenchmarks.cs new file mode 100644 index 000000000..3989e0029 --- /dev/null +++ b/src/Miningcore.Tests/Benchmarks/Crypto/EthashBenchmarks.cs @@ -0,0 +1,39 @@ +using Miningcore.Extensions; +using System.Globalization; +using System.Threading.Tasks; +using BenchmarkDotNet.Attributes; +using Miningcore.Crypto.Hashing.Ethash.Ethash; +using NLog; + +namespace Miningcore.Tests.Benchmarks.Crypto; + + +[MemoryDiagnoser] +public class EthashBenchmarks : TestBase +{ + private readonly byte[] testHash = "5fc898f16035bf5ac9c6d9077ae1e3d5fc1ecc3c9fd5bee8bb00e810fdacbaa0".HexToByteArray(); + private readonly ulong testNonce = ulong.Parse("50377003e5d830ca", NumberStyles.HexNumber, CultureInfo.InvariantCulture); + private const int testHeight = 60000; + + private ILogger logger; + + private readonly EthashLight ethash = new EthashLight(); + + [GlobalSetup] + public void Setup() + { + ModuleInitializer.Initialize(); + logger = new NullLogger(LogManager.LogFactory); + + ethash.Setup(3, 0); + } + + + [Benchmark] + public async Task Ethash_Compute() + { + var cache = await ethash.GetCacheAsync(logger, testHeight); + cache.Compute(logger, testHash, testNonce, out var mixDigest, out var result); + } +} + diff --git a/src/Miningcore.Tests/Crypto/EthashTests.cs b/src/Miningcore.Tests/Crypto/EthashTests.cs new file mode 100644 index 000000000..e65733076 --- /dev/null +++ b/src/Miningcore.Tests/Crypto/EthashTests.cs @@ -0,0 +1,81 @@ +using System.Globalization; +using System.Threading.Tasks; +using Miningcore.Crypto.Hashing.Ethash.Ethash; +using Miningcore.Extensions; +using NLog; +using Xunit; + +namespace Miningcore.Tests.Crypto; + + +public record TestBlock +{ + public double Difficulty { get; set; } + public byte[] Hash { get; set; } + public ulong Nonce { get; set; } + public byte[] MixDigest { get; set; } + public ulong Height { get; set; } +} + +public class EthashTests : TestBase +{ + private static readonly ILogger logger = new NullLogger(LogManager.LogFactory); + + [Fact] + public async Task Ethash_Hash() + { + var ethash = new EthashLight(); + ethash.Setup(3, 0); + + Assert.Equal("Ethash", ethash.AlgoName); + + var testBlocks = new TestBlock[] + { + new TestBlock { + Height = 22, + Hash = "372eca2454ead349c3df0ab5d00b0b706b23e49d469387db91811cee0358fc6d".HexToByteArray(), + Difficulty = 132416, + Nonce = ulong.Parse("495732e0ed7a801c", NumberStyles.HexNumber, CultureInfo.InvariantCulture), + MixDigest = "2f74cdeb198af0b9abe65d22d372e22fb2d474371774a9583c1cc427a07939f5".HexToByteArray(), + }, + new TestBlock { + Height = 30001, + Hash = "7e44356ee3441623bc72a683fd3708fdf75e971bbe294f33e539eedad4b92b34".HexToByteArray(), + Difficulty = 1532671, + Nonce = ulong.Parse("318df1c8adef7e5e", NumberStyles.HexNumber, CultureInfo.InvariantCulture), + MixDigest = "144b180aad09ae3c81fb07be92c8e6351b5646dda80e6844ae1b697e55ddde84".HexToByteArray(), + }, + new TestBlock { + Height = 60000, + Hash = "5fc898f16035bf5ac9c6d9077ae1e3d5fc1ecc3c9fd5bee8bb00e810fdacbaa0".HexToByteArray(), + Difficulty = 2467358, + Nonce = ulong.Parse("50377003e5d830ca", NumberStyles.HexNumber, CultureInfo.InvariantCulture), + MixDigest = "ab546a5b73c452ae86dadd36f0ed83a6745226717d3798832d1b20b489e82063".HexToByteArray(), + }, + }; + + var invalidBlock = new TestBlock + { + Height = 61439999, // 61440000 causes the c lib to crash + Hash = "foo".HexToByteArray(), + Difficulty = 0, + Nonce = ulong.Parse("cafebabec00000fe", NumberStyles.HexNumber, CultureInfo.InvariantCulture), + MixDigest = "bar".HexToByteArray(), + }; + + // test valid blocks + foreach(var testBlock in testBlocks) + { + var cache = await ethash.GetCacheAsync(logger, testBlock.Height); + var ok = cache.Compute(logger, testBlock.Hash, testBlock.Nonce, out var mixDigest, out var result); + Assert.True(ok); + Assert.Equal(testBlock.MixDigest, mixDigest); + } + + // test invalid block + var invalidCache = await ethash.GetCacheAsync(logger, invalidBlock.Height); + var invalidOk = invalidCache.Compute(logger, invalidBlock.Hash, invalidBlock.Nonce, out var invalidMixDigest, out var invalidResult); + Assert.True(invalidOk); + Assert.NotEqual(invalidBlock.MixDigest, invalidMixDigest); + } +} \ No newline at end of file diff --git a/src/Miningcore.Tests/Miningcore.Tests.csproj b/src/Miningcore.Tests/Miningcore.Tests.csproj index 96f0d3d14..ea719c312 100644 --- a/src/Miningcore.Tests/Miningcore.Tests.csproj +++ b/src/Miningcore.Tests/Miningcore.Tests.csproj @@ -77,4 +77,14 @@ + + + PreserveNewest + %(RecursiveDir)\%(Filename)%(Extension) + False + + + + + diff --git a/src/Miningcore/AutofacModule.cs b/src/Miningcore/AutofacModule.cs index ef2b595b9..4f195d1a3 100644 --- a/src/Miningcore/AutofacModule.cs +++ b/src/Miningcore/AutofacModule.cs @@ -1,29 +1,30 @@ using System.Reflection; using Autofac; +using Microsoft.AspNetCore.Mvc; +using Microsoft.IO; using Miningcore.Api; using Miningcore.Banning; using Miningcore.Blockchain.Bitcoin; using Miningcore.Blockchain.Conceal; using Miningcore.Blockchain.Cryptonote; using Miningcore.Blockchain.Equihash; +using Miningcore.Blockchain.Ergo; using Miningcore.Blockchain.Ethereum; using Miningcore.Configuration; using Miningcore.Crypto; using Miningcore.Crypto.Hashing.Equihash; +using Miningcore.Crypto.Hashing.Ethash; using Miningcore.Messaging; using Miningcore.Mining; +using Miningcore.Nicehash; using Miningcore.Notifications; using Miningcore.Payments; using Miningcore.Payments.PaymentSchemes; +using Miningcore.Pushover; using Miningcore.Time; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using Module = Autofac.Module; -using Microsoft.AspNetCore.Mvc; -using Microsoft.IO; -using Miningcore.Blockchain.Ergo; -using Miningcore.Nicehash; -using Miningcore.Pushover; namespace Miningcore; @@ -78,9 +79,15 @@ protected override void Load(ContainerBuilder builder) builder.RegisterAssemblyTypes(ThisAssembly) .Where(t => t.GetCustomAttributes().Any() && t.GetInterfaces().Any(i => i.IsAssignableFrom(typeof(IHashAlgorithm)))) - .Named(t=> t.GetCustomAttributes().First().Name) + .Named(t => t.GetCustomAttributes().First().Name) .PropertiesAutowired(); + builder.RegisterAssemblyTypes(ThisAssembly) + .Where(t => t.GetCustomAttributes().Any() && + t.GetInterfaces().Any(i => i.IsAssignableFrom(typeof(IEthashLight)))) + .Named(t => t.GetCustomAttributes().First().Name) + .PropertiesAutowired(); + builder.RegisterAssemblyTypes(ThisAssembly) .Where(t => t.IsAssignableTo()) .PropertiesAutowired() @@ -148,12 +155,12 @@ protected override void Load(ContainerBuilder builder) // Bitcoin and family builder.RegisterType(); - + ////////////////////// // Conceal builder.RegisterType(); - + ////////////////////// // Cryptonote diff --git a/src/Miningcore/Blockchain/Conceal/ConcealPool.cs b/src/Miningcore/Blockchain/Conceal/ConcealPool.cs index 99806e998..cbf5d6012 100644 --- a/src/Miningcore/Blockchain/Conceal/ConcealPool.cs +++ b/src/Miningcore/Blockchain/Conceal/ConcealPool.cs @@ -314,7 +314,7 @@ protected override async Task SetupJobManager(CancellationToken ct) disposables.Add(manager.Blocks .Select(_ => Observable.FromAsync(() => Guard(OnNewJobAsync, - ex=> logger.Debug(() => $"{nameof(OnNewJobAsync)}: {ex.Message}")))) + ex => logger.Debug(() => $"{nameof(OnNewJobAsync)}: {ex.Message}")))) .Concat() .Subscribe(_ => { }, ex => { @@ -338,7 +338,7 @@ private string GetMinerAlgo() { case CryptonightHashType.CryptonightCCX: return $"cn-ccx"; - + case CryptonightHashType.CryptonightGPU: return $"cn-gpu"; } diff --git a/src/Miningcore/Blockchain/Ethereum/Configuration/EthereumPoolConfigExtra.cs b/src/Miningcore/Blockchain/Ethereum/Configuration/EthereumPoolConfigExtra.cs index 1ba4775aa..e9fd2ee09 100644 --- a/src/Miningcore/Blockchain/Ethereum/Configuration/EthereumPoolConfigExtra.cs +++ b/src/Miningcore/Blockchain/Ethereum/Configuration/EthereumPoolConfigExtra.cs @@ -4,11 +4,6 @@ namespace Miningcore.Blockchain.Ethereum.Configuration; public class EthereumPoolConfigExtra { - /// - /// Base directory for generated DAGs - /// - public string DagDir { get; set; } - /// /// Useful to specify the real chain type when running geth /// diff --git a/src/Miningcore/Blockchain/Ethereum/EthereumConstants.cs b/src/Miningcore/Blockchain/Ethereum/EthereumConstants.cs index 10a9c31dc..835fb4c5b 100644 --- a/src/Miningcore/Blockchain/Ethereum/EthereumConstants.cs +++ b/src/Miningcore/Blockchain/Ethereum/EthereumConstants.cs @@ -6,8 +6,6 @@ namespace Miningcore.Blockchain.Ethereum; public class EthereumConstants { public const ulong EpochLength = 30000; - public const ulong CacheSizeForTesting = 1024; - public const ulong DagSizeForTesting = 1024 * 32; public static BigInteger BigMaxValue = BigInteger.Pow(2, 256); public static double Pow2x32 = Math.Pow(2, 32); public static BigInteger BigPow2x32 = new(Pow2x32); @@ -113,7 +111,7 @@ public enum GethChainType MainPow = 10001, EtherOne = 4949, Pink = 10100, - + Unknown = -1, } diff --git a/src/Miningcore/Blockchain/Ethereum/EthereumJob.cs b/src/Miningcore/Blockchain/Ethereum/EthereumJob.cs index a7a181e49..15b25d544 100644 --- a/src/Miningcore/Blockchain/Ethereum/EthereumJob.cs +++ b/src/Miningcore/Blockchain/Ethereum/EthereumJob.cs @@ -1,8 +1,6 @@ using System.Globalization; using System.Numerics; -using Miningcore.Crypto.Hashing.Etchash; using Miningcore.Crypto.Hashing.Ethash; -using Miningcore.Crypto.Hashing.Ubqhash; using Miningcore.Extensions; using Miningcore.Stratum; using NBitcoin; @@ -12,11 +10,12 @@ namespace Miningcore.Blockchain.Ethereum; public class EthereumJob { - public EthereumJob(string id, EthereumBlockTemplate blockTemplate, ILogger logger) + public EthereumJob(string id, EthereumBlockTemplate blockTemplate, ILogger logger, IEthashLight ethash) { Id = id; BlockTemplate = blockTemplate; this.logger = logger; + this.ethash = ethash; var target = blockTemplate.Target; if(target.StartsWith("0x")) @@ -31,6 +30,7 @@ public EthereumJob(string id, EthereumBlockTemplate blockTemplate, ILogger logge public EthereumBlockTemplate BlockTemplate { get; } private readonly uint256 blockTarget; private readonly ILogger logger; + private readonly IEthashLight ethash; public record SubmitResult(Share Share, string FullNonceHex = null, string HeaderHash = null, string MixHash = null); @@ -52,82 +52,9 @@ private void RegisterNonce(StratumConnection worker, string nonce) nonces.Add(nonceLower); } } - - public async Task ProcessShareEtcHashAsync(StratumConnection worker, - string workerName, string fullNonceHex, EtchashFull etchash, CancellationToken ct) - { - // dupe check - lock(workerNonces) - { - RegisterNonce(worker, fullNonceHex); - } - - var context = worker.ContextAs(); - - if(!ulong.TryParse(fullNonceHex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var fullNonce)) - throw new StratumException(StratumError.MinusOne, "bad nonce " + fullNonceHex); - - // get dag for block - var dag = await etchash.GetDagAsync(BlockTemplate.Height, logger, CancellationToken.None); - - // compute - if(!dag.Compute(logger, BlockTemplate.Header.HexToByteArray(), fullNonce, out var mixDigest, out var resultBytes)) - throw new StratumException(StratumError.MinusOne, "bad hash"); - - // test if share meets at least workers current difficulty - resultBytes.ReverseInPlace(); - var resultValue = new uint256(resultBytes); - var resultValueBig = resultBytes.AsSpan().ToBigInteger(); - var shareDiff = (double) BigInteger.Divide(EthereumConstants.BigMaxValue, resultValueBig) / EthereumConstants.Pow2x32; - var stratumDifficulty = context.Difficulty; - var ratio = shareDiff / stratumDifficulty; - var isBlockCandidate = resultValue <= blockTarget; - - if(!isBlockCandidate && ratio < 0.99) - { - // check if share matched the previous difficulty from before a vardiff retarget - if(context.VarDiff?.LastUpdate != null && context.PreviousDifficulty.HasValue) - { - ratio = shareDiff / context.PreviousDifficulty.Value; - - if(ratio < 0.99) - throw new StratumException(StratumError.LowDifficultyShare, $"low difficulty share ({shareDiff})"); - - // use previous difficulty - stratumDifficulty = context.PreviousDifficulty.Value; - } - - else - throw new StratumException(StratumError.LowDifficultyShare, $"low difficulty share ({shareDiff})"); - } - - var share = new Share - { - BlockHeight = (long) BlockTemplate.Height, - IpAddress = worker.RemoteEndpoint?.Address?.ToString(), - Miner = context.Miner, - Worker = workerName, - UserAgent = context.UserAgent, - IsBlockCandidate = isBlockCandidate, - Difficulty = stratumDifficulty * EthereumConstants.Pow2x32 - }; - - if(share.IsBlockCandidate) - { - fullNonceHex = "0x" + fullNonceHex; - var headerHash = BlockTemplate.Header; - var mixHash = mixDigest.ToHexString(true); - - share.TransactionConfirmationData = ""; - - return new SubmitResult(share, fullNonceHex, headerHash, mixHash); - } - - return new SubmitResult(share); - } public async Task ProcessShareAsync(StratumConnection worker, - string workerName, string fullNonceHex, EthashFull ethash, CancellationToken ct) + string workerName, string fullNonceHex, CancellationToken ct) { // dupe check lock(workerNonces) @@ -140,85 +67,13 @@ public async Task ProcessShareAsync(StratumConnection worker, if(!ulong.TryParse(fullNonceHex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var fullNonce)) throw new StratumException(StratumError.MinusOne, "bad nonce " + fullNonceHex); - // get dag for block - var dag = await ethash.GetDagAsync(BlockTemplate.Height, logger, CancellationToken.None); + // get light cache for block + var cache = await ethash.GetCacheAsync(logger, BlockTemplate.Height); // compute - if(!dag.Compute(logger, BlockTemplate.Header.HexToByteArray(), fullNonce, out var mixDigest, out var resultBytes)) + if(!cache.Compute(logger, BlockTemplate.Header.HexToByteArray(), fullNonce, out var mixDigest, out var resultBytes)) throw new StratumException(StratumError.MinusOne, "bad hash"); - // test if share meets at least workers current difficulty - resultBytes.ReverseInPlace(); - var resultValue = new uint256(resultBytes); - var resultValueBig = resultBytes.AsSpan().ToBigInteger(); - var shareDiff = (double) BigInteger.Divide(EthereumConstants.BigMaxValue, resultValueBig) / EthereumConstants.Pow2x32; - var stratumDifficulty = context.Difficulty; - var ratio = shareDiff / stratumDifficulty; - var isBlockCandidate = resultValue <= blockTarget; - - if(!isBlockCandidate && ratio < 0.99) - { - // check if share matched the previous difficulty from before a vardiff retarget - if(context.VarDiff?.LastUpdate != null && context.PreviousDifficulty.HasValue) - { - ratio = shareDiff / context.PreviousDifficulty.Value; - - if(ratio < 0.99) - throw new StratumException(StratumError.LowDifficultyShare, $"low difficulty share ({shareDiff})"); - - // use previous difficulty - stratumDifficulty = context.PreviousDifficulty.Value; - } - - else - throw new StratumException(StratumError.LowDifficultyShare, $"low difficulty share ({shareDiff})"); - } - - var share = new Share - { - BlockHeight = (long) BlockTemplate.Height, - IpAddress = worker.RemoteEndpoint?.Address?.ToString(), - Miner = context.Miner, - Worker = workerName, - UserAgent = context.UserAgent, - IsBlockCandidate = isBlockCandidate, - Difficulty = stratumDifficulty * EthereumConstants.Pow2x32 - }; - - if(share.IsBlockCandidate) - { - fullNonceHex = "0x" + fullNonceHex; - var headerHash = BlockTemplate.Header; - var mixHash = mixDigest.ToHexString(true); - - share.TransactionConfirmationData = ""; - - return new SubmitResult(share, fullNonceHex, headerHash, mixHash); - } - - return new SubmitResult(share); - } - - public async Task ProcessShareUbqHashAsync(StratumConnection worker, - string workerName, string fullNonceHex, UbqhashFull ubqhash, CancellationToken ct) - { - // dupe check - lock(workerNonces) - { - RegisterNonce(worker, fullNonceHex); - } - - var context = worker.ContextAs(); - - if(!ulong.TryParse(fullNonceHex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var fullNonce)) - throw new StratumException(StratumError.MinusOne, "bad nonce " + fullNonceHex); - - // get dag for block - var dag = await ubqhash.GetDagAsync(BlockTemplate.Height, logger, CancellationToken.None); - - // compute - if(!dag.Compute(logger, BlockTemplate.Header.HexToByteArray(), fullNonce, out var mixDigest, out var resultBytes)) - throw new StratumException(StratumError.MinusOne, "bad hash"); // test if share meets at least workers current difficulty resultBytes.ReverseInPlace(); diff --git a/src/Miningcore/Blockchain/Ethereum/EthereumJobManager.cs b/src/Miningcore/Blockchain/Ethereum/EthereumJobManager.cs index e1a82b769..0935b7311 100644 --- a/src/Miningcore/Blockchain/Ethereum/EthereumJobManager.cs +++ b/src/Miningcore/Blockchain/Ethereum/EthereumJobManager.cs @@ -1,5 +1,6 @@ using System.Globalization; using System.Numerics; +using System.Reactive; using System.Reactive.Linq; using System.Reactive.Threading.Tasks; using System.Text; @@ -8,25 +9,22 @@ using Miningcore.Blockchain.Ethereum.Configuration; using Miningcore.Blockchain.Ethereum.DaemonResponses; using Miningcore.Configuration; -using Miningcore.Crypto.Hashing.Etchash; -using Miningcore.Crypto.Hashing.Ethash; -using Miningcore.Crypto.Hashing.Ubqhash; using Miningcore.Extensions; using Miningcore.JsonRpc; using Miningcore.Messaging; +using Miningcore.Mining; using Miningcore.Notifications.Messages; +using Miningcore.Rpc; using Miningcore.Stratum; using Miningcore.Time; +using NBitcoin; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using NLog; +using static Miningcore.Util.ActionUtils; using Block = Miningcore.Blockchain.Ethereum.DaemonResponses.Block; using Contract = Miningcore.Contracts.Contract; using EC = Miningcore.Blockchain.Ethereum.EthCommands; -using static Miningcore.Util.ActionUtils; -using System.Reactive; -using Miningcore.Mining; -using Miningcore.Rpc; -using Newtonsoft.Json.Linq; namespace Miningcore.Blockchain.Ethereum; @@ -48,13 +46,11 @@ public EthereumJobManager( this.extraNonceProvider = extraNonceProvider; } + private EthereumCoinTemplate coin; private DaemonEndpointConfig[] daemonEndpoints; private RpcClient rpc; private EthereumNetworkType networkType; private GethChainType chainType; - private EtchashFull etchash; - private EthashFull ethash; - private UbqhashFull ubqhash; private readonly IMasterClock clock; private readonly IExtraNonceProvider extraNonceProvider; private const int MaxBlockBacklog = 6; @@ -106,7 +102,7 @@ protected bool UpdateJob(EthereumBlockTemplate blockTemplate, string via = null) var jobId = NextJobId("x8"); // update template - job = new EthereumJob(jobId, blockTemplate, logger); + job = new EthereumJob(jobId, blockTemplate, logger, coin.Ethash); lock(jobLock) { @@ -339,6 +335,7 @@ public object[] GetWorkParamsForStratum(EthereumWorkerContext context) public override void Configure(PoolConfig pc, ClusterConfig cc) { extraPoolConfig = pc.Extra.SafeExtensionDataAs(); + coin = pc.Template.As(); // extract standard daemon endpoints daemonEndpoints = pc.Daemons @@ -350,49 +347,12 @@ public override void Configure(PoolConfig pc, ClusterConfig cc) if(pc.EnableInternalStratum == true) { var coin = pc.Template.As(); - - // ensure dag location is configured - string dagDir = null; - - if(!string.IsNullOrEmpty(extraPoolConfig?.DagDir)) - { - dagDir = Environment.ExpandEnvironmentVariables(extraPoolConfig.DagDir); - } - else - { - // Default DAG folder - switch(coin.Symbol) - { - case "ETC": - dagDir = DagEtchash.GetDefaultDagDirectory(); - break; - case "UBIQ": - dagDir = DagUbqhash.GetDefaultDagDirectory(); - break; - default: - dagDir = Dag.GetDefaultDagDirectory(); - break; - } - } - // create it if necessary - Directory.CreateDirectory(dagDir); + logger.Info(() => $"Ethasher is: {coin.Ethasher}"); - // setup ethash - switch(coin.Symbol) - { - case "ETC": - var hardForkBlock = extraPoolConfig?.ChainTypeOverride == "Classic" ? EthereumClassicConstants.HardForkBlockMainnet : EthereumClassicConstants.HardForkBlockMordor; - logger.Debug(() => $"Hard fork block on `{extraPoolConfig?.ChainTypeOverride}`: {hardForkBlock}"); - etchash = new EtchashFull(3, dagDir, hardForkBlock); - break; - case "UBIQ": - ubqhash = new UbqhashFull(3, dagDir); - break; - default: - ethash = new EthashFull(3, dagDir); - break; - } + var hardForkBlock = extraPoolConfig?.ChainTypeOverride == "Classic" ? EthereumClassicConstants.HardForkBlockMainnet : EthereumClassicConstants.HardForkBlockMordor; + // TODO: improve this + coin.Ethash.Setup(3, hardForkBlock); } } @@ -466,86 +426,31 @@ private async Task SubmitShareAsync(StratumConnection worker, EthereumWorkerContext context, string workerName, EthereumJob job, string nonce, CancellationToken ct) { var coin = poolConfig.Template.As(); - - // validate & process - switch(coin.Symbol) - { - case "ETC": - var (shareEtchash, fullNonceHexEtchash, headerHashEtchash, mixHashEtchash) = await job.ProcessShareEtcHashAsync(worker, workerName, nonce, etchash, ct); - - // enrich share with common data - shareEtchash.PoolId = poolConfig.Id; - shareEtchash.NetworkDifficulty = BlockchainStats.NetworkDifficulty; - shareEtchash.Source = clusterConfig.ClusterName; - shareEtchash.Created = clock.Now; - - // if block candidate, submit & check if accepted by network - if(shareEtchash.IsBlockCandidate) - { - logger.Info(() => $"Submitting block {shareEtchash.BlockHeight}"); - - shareEtchash.IsBlockCandidate = await SubmitBlockAsync(shareEtchash, fullNonceHexEtchash, headerHashEtchash, mixHashEtchash); - - if(shareEtchash.IsBlockCandidate) - { - logger.Info(() => $"Daemon accepted block {shareEtchash.BlockHeight} submitted by {context.Miner}"); - OnBlockFound(); - } - } - - return shareEtchash; - case "UBIQ": - var (shareUbqhash, fullNonceHexUbqhash, headerHashUbqhash, mixHashUbqhash) = await job.ProcessShareUbqHashAsync(worker, workerName, nonce, ubqhash, ct); - - // enrich share with common data - shareUbqhash.PoolId = poolConfig.Id; - shareUbqhash.NetworkDifficulty = BlockchainStats.NetworkDifficulty; - shareUbqhash.Source = clusterConfig.ClusterName; - shareUbqhash.Created = clock.Now; - - // if block candidate, submit & check if accepted by network - if(shareUbqhash.IsBlockCandidate) - { - logger.Info(() => $"Submitting block {shareUbqhash.BlockHeight}"); + var (share, fullNonceHex, headerHash, mixHash) = await job.ProcessShareAsync(worker, workerName, nonce, ct); - shareUbqhash.IsBlockCandidate = await SubmitBlockAsync(shareUbqhash, fullNonceHexUbqhash, headerHashUbqhash, mixHashUbqhash); + // enrich share with common data + share.PoolId = poolConfig.Id; + share.NetworkDifficulty = BlockchainStats.NetworkDifficulty; + share.Source = clusterConfig.ClusterName; + share.Created = clock.Now; - if(shareUbqhash.IsBlockCandidate) - { - logger.Info(() => $"Daemon accepted block {shareUbqhash.BlockHeight} submitted by {context.Miner}"); - - OnBlockFound(); - } - } - - return shareUbqhash; - default: - var (share, fullNonceHex, headerHash, mixHash) = await job.ProcessShareAsync(worker, workerName, nonce, ethash, ct); - - // enrich share with common data - share.PoolId = poolConfig.Id; - share.NetworkDifficulty = BlockchainStats.NetworkDifficulty; - share.Source = clusterConfig.ClusterName; - share.Created = clock.Now; - - // if block candidate, submit & check if accepted by network - if(share.IsBlockCandidate) - { - logger.Info(() => $"Submitting block {share.BlockHeight}"); + // if block candidate, submit & check if accepted by network + if(share.IsBlockCandidate) + { + logger.Info(() => $"Submitting block {share.BlockHeight}"); - share.IsBlockCandidate = await SubmitBlockAsync(share, fullNonceHex, headerHash, mixHash); + share.IsBlockCandidate = await SubmitBlockAsync(share, fullNonceHex, headerHash, mixHash); - if(share.IsBlockCandidate) - { - logger.Info(() => $"Daemon accepted block {share.BlockHeight} submitted by {context.Miner}"); + if(share.IsBlockCandidate) + { + logger.Info(() => $"Daemon accepted block {share.BlockHeight} submitted by {context.Miner}"); - OnBlockFound(); - } - } - - return share; + OnBlockFound(); + } } + + return share; } public BlockchainStats BlockchainStats { get; } = new(); @@ -564,13 +469,19 @@ protected override void ConfigureDaemons() protected override async Task AreDaemonsHealthyAsync(CancellationToken ct) { var response = await rpc.ExecuteAsync(logger, EC.GetBlockByNumber, ct, new[] { (object) "latest", true }); - - return response.Error == null; + if(response.Error != null) + { + logger.Error(() => $"Daemon reports: {response.Error.Message}"); + return false; + } + return true; } protected override async Task AreDaemonsConnectedAsync(CancellationToken ct) { var response = await rpc.ExecuteAsync(logger, EC.GetPeerCount, ct); + if(response.Error != null) + logger.Error(() => $"Daemon reports: {response.Error.Message}"); return response.Error == null && response.Response.IntegralFromHex() > 0; } @@ -641,14 +552,14 @@ protected override async Task PostStartInitAsync(CancellationToken ct) // Periodically update network stats Observable.Interval(TimeSpan.FromMinutes(10)) .Select(via => Observable.FromAsync(() => - Guard(()=> UpdateNetworkStatsAsync(ct), - ex=> logger.Error(ex)))) + Guard(() => UpdateNetworkStatsAsync(ct), + ex => logger.Error(ex)))) .Concat() .Subscribe(); if(poolConfig.EnableInternalStratum == true) { - // make sure we have a current DAG + // make sure we have a current light cache using var timer = new PeriodicTimer(TimeSpan.FromSeconds(5)); do @@ -657,23 +568,11 @@ protected override async Task PostStartInitAsync(CancellationToken ct) if(blockTemplate != null) { - logger.Info(() => "Loading current DAG ..."); - - // setup dag file - switch(coin.Symbol) - { - case "ETC": - await etchash.GetDagAsync(blockTemplate.Height, logger, ct); - break; - case "UBIQ": - await ubqhash.GetDagAsync(blockTemplate.Height, logger, ct); - break; - default: - await ethash.GetDagAsync(blockTemplate.Height, logger, ct); - break; - } - - logger.Info(() => "Loaded current DAG"); + logger.Info(() => "Loading current light cache ..."); + + await coin.Ethash.GetCacheAsync(logger, blockTemplate.Height); + + logger.Info(() => "Loaded current light cache"); break; } @@ -698,7 +597,7 @@ protected virtual async Task SetupJobUpdates(CancellationToken ct) var endpointExtra = daemonEndpoints .Where(x => x.Extra.SafeExtensionDataAs() != null) - .Select(x=> Tuple.Create(x, x.Extra.SafeExtensionDataAs())) + .Select(x => Tuple.Create(x, x.Extra.SafeExtensionDataAs())) .FirstOrDefault(); if(endpointExtra?.Item2?.PortWs.HasValue == true) diff --git a/src/Miningcore/Configuration/ClusterConfig.cs b/src/Miningcore/Configuration/ClusterConfig.cs index 9392d47b4..d2ff62b1e 100644 --- a/src/Miningcore/Configuration/ClusterConfig.cs +++ b/src/Miningcore/Configuration/ClusterConfig.cs @@ -24,7 +24,7 @@ public enum CoinFamily [EnumMember(Value = "conceal")] Conceal, - + [EnumMember(Value = "cryptonote")] Cryptonote, @@ -438,13 +438,13 @@ public partial class ConcealCoinTemplate : CoinTemplate /// [JsonProperty(Order = -4, DefaultValueHandling = DefaultValueHandling.Include)] public int HashVariant { get; set; } - + /// /// Conceal network hashrate = `Difficulty / DifficultyTarget` /// See: parameter -> DIFFICULTY_TARGET in src/CryptoNoteConfig.h /// public ulong DifficultyTarget { get; set; } - + /// /// Smallest unit for Blockreward formatting /// @@ -581,6 +581,11 @@ public partial class EthereumCoinTemplate : CoinTemplate [DefaultValue(EthereumSubfamily.None)] [JsonConverter(typeof(StringEnumConverter), true)] public EthereumSubfamily Subfamily { get; set; } + + /// + /// Which hashing algorithm to use. (ethash, etchash or ubqhash) + /// + public string Ethasher { get; set; } = "ethash"; } public partial class ErgoCoinTemplate : CoinTemplate @@ -830,7 +835,7 @@ public partial class ClusterPaymentProcessingConfig /// /// Indentifier used in coinbase transactions to identify the pool /// - public string CoinbaseString { get; set; } + public string CoinbaseString { get; set; } } public partial class PersistenceConfig diff --git a/src/Miningcore/Configuration/ClusterConfigExtensions.cs b/src/Miningcore/Configuration/ClusterConfigExtensions.cs index d7c783306..b59074a03 100644 --- a/src/Miningcore/Configuration/ClusterConfigExtensions.cs +++ b/src/Miningcore/Configuration/ClusterConfigExtensions.cs @@ -4,6 +4,7 @@ using JetBrains.Annotations; using Miningcore.Crypto; using Miningcore.Crypto.Hashing.Algorithms; +using Miningcore.Crypto.Hashing.Ethash; using NBitcoin; using Newtonsoft.Json; @@ -152,11 +153,11 @@ public partial class ConcealCoinTemplate public override string GetAlgorithmName() { -// switch(Hash) -// { -// case CryptonightHashType.RandomX: -// return "RandomX"; -// } + // switch(Hash) + // { + // case CryptonightHashType.RandomX: + // return "RandomX"; + // } return Hash.ToString(); } @@ -170,11 +171,11 @@ public partial class CryptonoteCoinTemplate public override string GetAlgorithmName() { -// switch(Hash) -// { -// case CryptonightHashType.RandomX: -// return "RandomX"; -// } + // switch(Hash) + // { + // case CryptonightHashType.RandomX: + // return "RandomX"; + // } return Hash.ToString(); } @@ -186,9 +187,21 @@ public partial class EthereumCoinTemplate { #region Overrides of CoinTemplate + public EthereumCoinTemplate() + { + ethashLightValue = new Lazy(() => + EthashFactory.GetEthash(ComponentContext, Ethasher)); + } + + private readonly Lazy ethashLightValue; + + public IComponentContext ComponentContext { get; [UsedImplicitly] init; } + + public IEthashLight Ethash => ethashLightValue.Value; + public override string GetAlgorithmName() { - return "Ethhash"; + return Ethash.AlgoName; } #endregion diff --git a/src/Miningcore/Crypto/Hashing/Etchash/DagEtchash.cs b/src/Miningcore/Crypto/Hashing/Etchash/DagEtchash.cs deleted file mode 100644 index 447c372ce..000000000 --- a/src/Miningcore/Crypto/Hashing/Etchash/DagEtchash.cs +++ /dev/null @@ -1,143 +0,0 @@ -using System.Diagnostics; -using System.Text; -using Miningcore.Blockchain.Ethereum; -using Miningcore.Contracts; -using Miningcore.Extensions; -using Miningcore.Messaging; -using Miningcore.Native; -using Miningcore.Notifications.Messages; -using NLog; - -namespace Miningcore.Crypto.Hashing.Etchash; - -public class DagEtchash : IDisposable -{ - public DagEtchash(ulong epoch) - { - Epoch = epoch; - } - - public ulong Epoch { get; set; } - - private IntPtr handle = IntPtr.Zero; - private static readonly Semaphore sem = new(1, 1); - - internal static IMessageBus messageBus; - - public DateTime LastUsed { get; set; } - - public static unsafe string GetDefaultDagDirectory() - { - var chars = new byte[512]; - - fixed (byte* data = chars) - { - if(EtcHash.ethash_get_default_dirname(data, chars.Length)) - { - int length; - for(length = 0; length < chars.Length; length++) - { - if(data[length] == 0) - break; - } - - return Encoding.UTF8.GetString(data, length); - } - } - - return null; - } - - public void Dispose() - { - if(handle != IntPtr.Zero) - { - EtcHash.ethash_full_delete(handle); - handle = IntPtr.Zero; - } - } - - public async Task GenerateAsync(string dagDir, ulong dagEpochLength, ILogger logger, CancellationToken ct) - { - Contract.Requires(!string.IsNullOrEmpty(dagDir)); - - if(handle == IntPtr.Zero) - { - await Task.Run(() => - { - try - { - sem.WaitOne(); - - // re-check after obtaining lock - if(handle != IntPtr.Zero) - return; - - logger.Info(() => $"Generating DAG for epoch {Epoch}"); - - var started = DateTime.Now; - var block = Epoch * dagEpochLength; - - logger.Debug(() => $"Epoch length used: {dagEpochLength}"); - - // Generate a temporary cache - var light = EtcHash.ethash_light_new(block); - - try - { - // Generate the actual DAG - handle = EtcHash.ethash_full_new(dagDir, light, progress => - { - logger.Info(() => $"Generating DAG for epoch {Epoch}: {progress}%"); - - return !ct.IsCancellationRequested ? 0 : 1; - }); - - if(handle == IntPtr.Zero) - throw new OutOfMemoryException("ethash_full_new IO or memory error"); - - logger.Info(() => $"Done generating DAG for epoch {Epoch} after {DateTime.Now - started}"); - } - - finally - { - if(light != IntPtr.Zero) - EtcHash.ethash_light_delete(light); - } - } - - finally - { - sem.Release(); - } - }, ct); - } - } - - public unsafe bool Compute(ILogger logger, byte[] hash, ulong nonce, out byte[] mixDigest, out byte[] result) - { - Contract.RequiresNonNull(hash); - - var sw = Stopwatch.StartNew(); - - mixDigest = null; - result = null; - - var value = new EtcHash.ethash_return_value(); - - fixed (byte* input = hash) - { - EtcHash.ethash_full_compute(handle, input, nonce, ref value); - } - - if(value.success) - { - mixDigest = value.mix_hash.value; - result = value.result.value; - } - - messageBus?.SendTelemetry("Etchash", TelemetryCategory.Hash, sw.Elapsed, value.success); - - return value.success; - } -} diff --git a/src/Miningcore/Crypto/Hashing/Ethash/Abstractions.cs b/src/Miningcore/Crypto/Hashing/Ethash/Abstractions.cs new file mode 100644 index 000000000..10680ce07 --- /dev/null +++ b/src/Miningcore/Crypto/Hashing/Ethash/Abstractions.cs @@ -0,0 +1,15 @@ +using NLog; + +namespace Miningcore.Crypto.Hashing.Ethash; + +public interface IEthashLight : IDisposable +{ + void Setup(int numCaches, ulong hardForkBlock); + Task GetCacheAsync(ILogger logger, ulong block); + string AlgoName { get; } +} + +public interface IEthashCache : IDisposable +{ + bool Compute(ILogger logger, byte[] hash, ulong nonce, out byte[] mixDigest, out byte[] result); +} \ No newline at end of file diff --git a/src/Miningcore/Crypto/Hashing/Ethash/Dag.cs b/src/Miningcore/Crypto/Hashing/Ethash/Dag.cs deleted file mode 100644 index 5bb3936d2..000000000 --- a/src/Miningcore/Crypto/Hashing/Ethash/Dag.cs +++ /dev/null @@ -1,141 +0,0 @@ -using System.Diagnostics; -using System.Text; -using Miningcore.Blockchain.Ethereum; -using Miningcore.Contracts; -using Miningcore.Extensions; -using Miningcore.Messaging; -using Miningcore.Native; -using Miningcore.Notifications.Messages; -using NLog; - -namespace Miningcore.Crypto.Hashing.Ethash; - -public class Dag : IDisposable -{ - public Dag(ulong epoch) - { - Epoch = epoch; - } - - public ulong Epoch { get; set; } - - private IntPtr handle = IntPtr.Zero; - private static readonly Semaphore sem = new(1, 1); - - internal static IMessageBus messageBus; - - public DateTime LastUsed { get; set; } - - public static unsafe string GetDefaultDagDirectory() - { - var chars = new byte[512]; - - fixed (byte* data = chars) - { - if(EthHash.ethash_get_default_dirname(data, chars.Length)) - { - int length; - for(length = 0; length < chars.Length; length++) - { - if(data[length] == 0) - break; - } - - return Encoding.UTF8.GetString(data, length); - } - } - - return null; - } - - public void Dispose() - { - if(handle != IntPtr.Zero) - { - EthHash.ethash_full_delete(handle); - handle = IntPtr.Zero; - } - } - - public async Task GenerateAsync(string dagDir, ILogger logger, CancellationToken ct) - { - Contract.Requires(!string.IsNullOrEmpty(dagDir)); - - if(handle == IntPtr.Zero) - { - await Task.Run(() => - { - try - { - sem.WaitOne(); - - // re-check after obtaining lock - if(handle != IntPtr.Zero) - return; - - logger.Info(() => $"Generating DAG for epoch {Epoch}"); - - var started = DateTime.Now; - var block = Epoch * EthereumConstants.EpochLength; - - // Generate a temporary cache - var light = EthHash.ethash_light_new(block); - - try - { - // Generate the actual DAG - handle = EthHash.ethash_full_new(dagDir, light, progress => - { - logger.Info(() => $"Generating DAG for epoch {Epoch}: {progress}%"); - - return !ct.IsCancellationRequested ? 0 : 1; - }); - - if(handle == IntPtr.Zero) - throw new OutOfMemoryException("ethash_full_new IO or memory error"); - - logger.Info(() => $"Done generating DAG for epoch {Epoch} after {DateTime.Now - started}"); - } - - finally - { - if(light != IntPtr.Zero) - EthHash.ethash_light_delete(light); - } - } - - finally - { - sem.Release(); - } - }, ct); - } - } - - public unsafe bool Compute(ILogger logger, byte[] hash, ulong nonce, out byte[] mixDigest, out byte[] result) - { - Contract.RequiresNonNull(hash); - - var sw = Stopwatch.StartNew(); - - mixDigest = null; - result = null; - - var value = new EthHash.ethash_return_value(); - - fixed (byte* input = hash) - { - EthHash.ethash_full_compute(handle, input, nonce, ref value); - } - - if(value.success) - { - mixDigest = value.mix_hash.value; - result = value.result.value; - } - - messageBus?.SendTelemetry("Ethash", TelemetryCategory.Hash, sw.Elapsed, value.success); - - return value.success; - } -} diff --git a/src/Miningcore/Crypto/Hashing/Ethash/Etchash/Cache.cs b/src/Miningcore/Crypto/Hashing/Ethash/Etchash/Cache.cs new file mode 100644 index 000000000..585c751c4 --- /dev/null +++ b/src/Miningcore/Crypto/Hashing/Ethash/Etchash/Cache.cs @@ -0,0 +1,87 @@ +using System.Diagnostics; +using Miningcore.Contracts; +using Miningcore.Extensions; +using Miningcore.Messaging; +using Miningcore.Native; +using Miningcore.Notifications.Messages; +using NLog; + +namespace Miningcore.Crypto.Hashing.Ethash.Etchash; + +[Identifier("etchash")] +public class Cache : IEthashCache +{ + public Cache(ulong epoch) + { + Epoch = epoch; + LastUsed = DateTime.Now; + } + + private IntPtr handle = IntPtr.Zero; + private bool isGenerated = false; + private readonly object genLock = new(); + internal static IMessageBus messageBus; + private ulong hardForkBlock; + + public ulong Epoch { get; } + public DateTime LastUsed { get; set; } + + public void Dispose() + { + if(handle != IntPtr.Zero) + { + EtcHash.ethash_light_delete(handle); + handle = IntPtr.Zero; + } + } + + public async Task GenerateAsync(ILogger logger, ulong epochLength, ulong hardForkBlock) + { + await Task.Run(() => + { + lock(genLock) + { + if(!isGenerated) + { + this.hardForkBlock = hardForkBlock; + + var started = DateTime.Now; + logger.Debug(() => $"Generating cache for epoch {Epoch}"); + + var block = Epoch * epochLength; + handle = EtcHash.ethash_light_new(block, hardForkBlock); + + logger.Debug(() => $"Done generating cache for epoch {Epoch} after {DateTime.Now - started}"); + isGenerated = true; + } + } + }); + } + + public unsafe bool Compute(ILogger logger, byte[] hash, ulong nonce, out byte[] mixDigest, out byte[] result) + { + Contract.RequiresNonNull(hash); + + var sw = Stopwatch.StartNew(); + + mixDigest = null; + result = null; + + var value = new EtcHash.ethash_return_value(); + + fixed(byte* input = hash) + { + EtcHash.ethash_light_compute(handle, input, nonce, this.hardForkBlock, ref value); + } + + if(value.success) + { + mixDigest = value.mix_hash.value; + result = value.result.value; + } + + messageBus?.SendTelemetry("Etchash", TelemetryCategory.Hash, sw.Elapsed, value.success); + + return value.success; + } +} \ No newline at end of file diff --git a/src/Miningcore/Crypto/Hashing/Etchash/EtchashFull.cs b/src/Miningcore/Crypto/Hashing/Ethash/Etchash/EtchashLight.cs similarity index 50% rename from src/Miningcore/Crypto/Hashing/Etchash/EtchashFull.cs rename to src/Miningcore/Crypto/Hashing/Ethash/Etchash/EtchashLight.cs index a5edd7aa0..1f128f209 100644 --- a/src/Miningcore/Crypto/Hashing/Etchash/EtchashFull.cs +++ b/src/Miningcore/Crypto/Hashing/Ethash/Etchash/EtchashLight.cs @@ -1,26 +1,27 @@ +using System.Text; using Miningcore.Blockchain.Ethereum; using Miningcore.Contracts; +using Miningcore.Native; using NLog; -namespace Miningcore.Crypto.Hashing.Etchash; -public class EtchashFull : IDisposable +namespace Miningcore.Crypto.Hashing.Ethash.Etchash; + +[Identifier("etchash")] +public class EtchashLight : IEthashLight { - public EtchashFull(int numCaches, string dagDir, ulong hardForkBlock) + public void Setup(int numCaches, ulong hardForkBlock) { - Contract.Requires(!string.IsNullOrEmpty(dagDir)); - this.numCaches = numCaches; - this.dagDir = dagDir; this.hardForkBlock = hardForkBlock; } private int numCaches; // Maximum number of caches to keep before eviction (only init, don't modify) private readonly object cacheLock = new(); - private readonly Dictionary caches = new(); - private DagEtchash future; - private readonly string dagDir; - private readonly ulong hardForkBlock; + private readonly Dictionary caches = new(); + private Cache future; + private ulong hardForkBlock; + public string AlgoName { get; } = "Etchash"; public void Dispose() { @@ -28,12 +29,12 @@ public void Dispose() value.Dispose(); } - public async Task GetDagAsync(ulong block, ILogger logger, CancellationToken ct) + public async Task GetCacheAsync(ILogger logger, ulong block) { - var dagEpochLength = block >= hardForkBlock ? EthereumClassicConstants.EpochLength : EthereumConstants.EpochLength; - logger.Debug(() => $"Epoch length used: {dagEpochLength}"); - var epoch = block / dagEpochLength; - DagEtchash result; + var epochLength = block >= this.hardForkBlock ? EthereumClassicConstants.EpochLength : EthereumConstants.EpochLength; + logger.Debug(() => $"Epoch length used: {epochLength}"); + var epoch = block / epochLength; + Cache result; lock(cacheLock) { @@ -42,22 +43,22 @@ public async Task GetDagAsync(ulong block, ILogger logger, Cancellat if(!caches.TryGetValue(epoch, out result)) { - // No cached DAG, evict the oldest if the cache limit was reached + // No cached cache, 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.Info(() => $"Evicting DAG for epoch {epochToEvict} in favour of epoch {epoch}"); + logger.Info(() => $"Evicting cache 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 we have the new cache pre-generated, use that, otherwise create a new one if(future != null && future.Epoch == epoch) { - logger.Debug(() => $"Using pre-generated DAG for epoch {epoch}"); + logger.Debug(() => $"Using pre-generated cache for epoch {epoch}"); result = future; future = null; @@ -65,8 +66,8 @@ public async Task GetDagAsync(ulong block, ILogger logger, Cancellat else { - logger.Info(() => $"No pre-generated DAG available, creating new for epoch {epoch}"); - result = new DagEtchash(epoch); + logger.Info(() => $"No pre-generated cache available, creating new for epoch {epoch}"); + result = new Cache(epoch); } caches[epoch] = result; @@ -75,11 +76,11 @@ public async Task GetDagAsync(ulong block, ILogger logger, Cancellat // If we used up the future cache, or need a refresh, regenerate else if(future == null || future.Epoch <= epoch) { - logger.Info(() => $"Pre-generating DAG for epoch {epoch + 1}"); - future = new DagEtchash(epoch + 1); + logger.Info(() => $"Pre-generating cache for epoch {epoch + 1}"); + future = new Cache(epoch + 1); #pragma warning disable 4014 - future.GenerateAsync(dagDir, dagEpochLength, logger, ct); + future.GenerateAsync(logger, epochLength, this.hardForkBlock); #pragma warning restore 4014 } @@ -87,7 +88,7 @@ public async Task GetDagAsync(ulong block, ILogger logger, Cancellat } // get/generate current one - await result.GenerateAsync(dagDir, dagEpochLength, logger, ct); + await result.GenerateAsync(logger, epochLength, this.hardForkBlock); return result; } diff --git a/src/Miningcore/Crypto/Hashing/Ethash/Ethash/Cache.cs b/src/Miningcore/Crypto/Hashing/Ethash/Ethash/Cache.cs new file mode 100644 index 000000000..8846b166a --- /dev/null +++ b/src/Miningcore/Crypto/Hashing/Ethash/Ethash/Cache.cs @@ -0,0 +1,85 @@ +using System.Diagnostics; +using Miningcore.Blockchain.Ethereum; +using Miningcore.Contracts; +using Miningcore.Extensions; +using Miningcore.Messaging; +using Miningcore.Native; +using Miningcore.Notifications.Messages; +using NLog; + +namespace Miningcore.Crypto.Hashing.Ethash.Ethash; + +[Identifier("ethash")] +public class Cache : IEthashCache +{ + public Cache(ulong epoch) + { + Epoch = epoch; + LastUsed = DateTime.Now; + } + + private IntPtr handle = IntPtr.Zero; + private bool isGenerated = false; + private readonly object genLock = new(); + internal static IMessageBus messageBus; + public ulong Epoch { get; } + public DateTime LastUsed { get; set; } + + public void Dispose() + { + if(handle != IntPtr.Zero) + { + EthHash.ethash_light_delete(handle); + handle = IntPtr.Zero; + } + } + + public async Task GenerateAsync(ILogger logger) + { + await Task.Run(() => + { + lock(genLock) + { + if(!isGenerated) + { + + var started = DateTime.Now; + logger.Debug(() => $"Generating cache for epoch {Epoch}"); + + var block = Epoch * EthereumConstants.EpochLength; + handle = EthHash.ethash_light_new(block); + + logger.Debug(() => $"Done generating cache for epoch {Epoch} after {DateTime.Now - started}"); + isGenerated = true; + } + } + }); + } + + public unsafe bool Compute(ILogger logger, byte[] hash, ulong nonce, out byte[] mixDigest, out byte[] result) + { + Contract.RequiresNonNull(hash); + + var sw = Stopwatch.StartNew(); + + mixDigest = null; + result = null; + + var value = new EthHash.ethash_return_value(); + + fixed(byte* input = hash) + { + EthHash.ethash_light_compute(handle, input, nonce, ref value); + } + + if(value.success) + { + mixDigest = value.mix_hash.value; + result = value.result.value; + } + + messageBus?.SendTelemetry("Ethash", TelemetryCategory.Hash, sw.Elapsed, value.success); + + return value.success; + } +} \ No newline at end of file diff --git a/src/Miningcore/Crypto/Hashing/Ubqhash/UbqhashFull.cs b/src/Miningcore/Crypto/Hashing/Ethash/Ethash/EthashLight.cs similarity index 56% rename from src/Miningcore/Crypto/Hashing/Ubqhash/UbqhashFull.cs rename to src/Miningcore/Crypto/Hashing/Ethash/Ethash/EthashLight.cs index dbbb62bc3..6eecf094b 100644 --- a/src/Miningcore/Crypto/Hashing/Ubqhash/UbqhashFull.cs +++ b/src/Miningcore/Crypto/Hashing/Ethash/Ethash/EthashLight.cs @@ -1,24 +1,24 @@ +using System.Text; using Miningcore.Blockchain.Ethereum; using Miningcore.Contracts; +using Miningcore.Native; using NLog; -namespace Miningcore.Crypto.Hashing.Ubqhash; +namespace Miningcore.Crypto.Hashing.Ethash.Ethash; -public class UbqhashFull : IDisposable +[Identifier("ethash")] +public class EthashLight : IEthashLight { - public UbqhashFull(int numCaches, string dagDir) + public void Setup(int numCaches, ulong hardForkBlock) { - Contract.Requires(!string.IsNullOrEmpty(dagDir)); - this.numCaches = numCaches; - this.dagDir = dagDir; } private int numCaches; // Maximum number of caches to keep before eviction (only init, don't modify) private readonly object cacheLock = new(); - private readonly Dictionary caches = new(); - private DagUbqhash future; - private readonly string dagDir; + private readonly Dictionary caches = new(); + private Cache future; + public string AlgoName { get; } = "Ethash"; public void Dispose() { @@ -26,10 +26,10 @@ public void Dispose() value.Dispose(); } - public async Task GetDagAsync(ulong block, ILogger logger, CancellationToken ct) + public async Task GetCacheAsync(ILogger logger, ulong block) { var epoch = block / EthereumConstants.EpochLength; - DagUbqhash result; + Cache result; lock(cacheLock) { @@ -38,22 +38,22 @@ public async Task GetDagAsync(ulong block, ILogger logger, Cancellat if(!caches.TryGetValue(epoch, out result)) { - // No cached DAG, evict the oldest if the cache limit was reached + // No cached cache, 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.Info(() => $"Evicting DAG for epoch {epochToEvict} in favour of epoch {epoch}"); + logger.Info(() => $"Evicting cache 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 we have the new cache pre-generated, use that, otherwise create a new one if(future != null && future.Epoch == epoch) { - logger.Debug(() => $"Using pre-generated DAG for epoch {epoch}"); + logger.Debug(() => $"Using pre-generated cache for epoch {epoch}"); result = future; future = null; @@ -61,8 +61,8 @@ public async Task GetDagAsync(ulong block, ILogger logger, Cancellat else { - logger.Info(() => $"No pre-generated DAG available, creating new for epoch {epoch}"); - result = new DagUbqhash(epoch); + logger.Info(() => $"No pre-generated cache available, creating new for epoch {epoch}"); + result = new Cache(epoch); } caches[epoch] = result; @@ -71,11 +71,11 @@ public async Task GetDagAsync(ulong block, ILogger logger, Cancellat // If we used up the future cache, or need a refresh, regenerate else if(future == null || future.Epoch <= epoch) { - logger.Info(() => $"Pre-generating DAG for epoch {epoch + 1}"); - future = new DagUbqhash(epoch + 1); + logger.Info(() => $"Pre-generating cache for epoch {epoch + 1}"); + future = new Cache(epoch + 1); #pragma warning disable 4014 - future.GenerateAsync(dagDir, logger, ct); + future.GenerateAsync(logger); #pragma warning restore 4014 } @@ -83,7 +83,7 @@ public async Task GetDagAsync(ulong block, ILogger logger, Cancellat } // get/generate current one - await result.GenerateAsync(dagDir, logger, ct); + await result.GenerateAsync(logger); return result; } diff --git a/src/Miningcore/Crypto/Hashing/Ethash/EthashFactory.cs b/src/Miningcore/Crypto/Hashing/Ethash/EthashFactory.cs new file mode 100644 index 000000000..4d833075a --- /dev/null +++ b/src/Miningcore/Crypto/Hashing/Ethash/EthashFactory.cs @@ -0,0 +1,25 @@ +using System.Collections.Concurrent; +using Autofac; + +namespace Miningcore.Crypto.Hashing.Ethash; + +public static class EthashFactory +{ + private static readonly ConcurrentDictionary cacheFull = new(); + + public static IEthashLight GetEthash(IComponentContext ctx, string name) + { + if(name == "") + return null; + + // check cache + if(cacheFull.TryGetValue(name, out var result)) + return result; + + result = ctx.ResolveNamed(name); + + cacheFull.TryAdd(name, result); + + return result; + } +} \ No newline at end of file diff --git a/src/Miningcore/Crypto/Hashing/Ethash/Ubqhash/Cache.cs b/src/Miningcore/Crypto/Hashing/Ethash/Ubqhash/Cache.cs new file mode 100644 index 000000000..c204671c1 --- /dev/null +++ b/src/Miningcore/Crypto/Hashing/Ethash/Ubqhash/Cache.cs @@ -0,0 +1,85 @@ +using System.Diagnostics; +using Miningcore.Blockchain.Ethereum; +using Miningcore.Contracts; +using Miningcore.Extensions; +using Miningcore.Messaging; +using Miningcore.Native; +using Miningcore.Notifications.Messages; +using NLog; + +namespace Miningcore.Crypto.Hashing.Ethash.Ubqhash; + +[Identifier("ubqhash")] +public class Cache : IEthashCache +{ + public Cache(ulong epoch) + { + Epoch = epoch; + LastUsed = DateTime.Now; + } + + private IntPtr handle = IntPtr.Zero; + private bool isGenerated = false; + private readonly object genLock = new(); + internal static IMessageBus messageBus; + public ulong Epoch { get; } + public DateTime LastUsed { get; set; } + + public void Dispose() + { + if(handle != IntPtr.Zero) + { + UbqHash.ethash_light_delete(handle); + handle = IntPtr.Zero; + } + } + + public async Task GenerateAsync(ILogger logger) + { + await Task.Run(() => + { + lock(genLock) + { + if(!isGenerated) + { + + var started = DateTime.Now; + logger.Debug(() => $"Generating cache for epoch {Epoch}"); + + var block = Epoch * EthereumConstants.EpochLength; + handle = UbqHash.ethash_light_new(block); + + logger.Debug(() => $"Done generating cache for epoch {Epoch} after {DateTime.Now - started}"); + isGenerated = true; + } + } + }); + } + + public unsafe bool Compute(ILogger logger, byte[] hash, ulong nonce, out byte[] mixDigest, out byte[] result) + { + Contract.RequiresNonNull(hash); + + var sw = Stopwatch.StartNew(); + + mixDigest = null; + result = null; + + var value = new UbqHash.ethash_return_value(); + + fixed(byte* input = hash) + { + UbqHash.ethash_light_compute(handle, input, nonce, ref value); + } + + if(value.success) + { + mixDigest = value.mix_hash.value; + result = value.result.value; + } + + messageBus?.SendTelemetry("Ubqhash", TelemetryCategory.Hash, sw.Elapsed, value.success); + + return value.success; + } +} \ No newline at end of file diff --git a/src/Miningcore/Crypto/Hashing/Ethash/EthashFull.cs b/src/Miningcore/Crypto/Hashing/Ethash/Ubqhash/UbqhashLight.cs similarity index 57% rename from src/Miningcore/Crypto/Hashing/Ethash/EthashFull.cs rename to src/Miningcore/Crypto/Hashing/Ethash/Ubqhash/UbqhashLight.cs index a625010a5..3c5d47dfb 100644 --- a/src/Miningcore/Crypto/Hashing/Ethash/EthashFull.cs +++ b/src/Miningcore/Crypto/Hashing/Ethash/Ubqhash/UbqhashLight.cs @@ -1,24 +1,25 @@ +using System.Text; using Miningcore.Blockchain.Ethereum; using Miningcore.Contracts; +using Miningcore.Native; using NLog; -namespace Miningcore.Crypto.Hashing.Ethash; +namespace Miningcore.Crypto.Hashing.Ethash.Ubqhash; -public class EthashFull : IDisposable +[Identifier("ubqhash")] +public class UbqhashLight : IEthashLight { - public EthashFull(int numCaches, string dagDir) + public void Setup(int numCaches, ulong hardForkBlock) { - Contract.Requires(!string.IsNullOrEmpty(dagDir)); this.numCaches = numCaches; - this.dagDir = dagDir; } private int numCaches; // Maximum number of caches to keep before eviction (only init, don't modify) private readonly object cacheLock = new(); - private readonly Dictionary caches = new(); - private Dag future; - private readonly string dagDir; + private readonly Dictionary caches = new(); + private Cache future; + public string AlgoName { get; } = "Ubqhash"; public void Dispose() { @@ -26,10 +27,10 @@ public void Dispose() value.Dispose(); } - public async Task GetDagAsync(ulong block, ILogger logger, CancellationToken ct) + public async Task GetCacheAsync(ILogger logger, ulong block) { var epoch = block / EthereumConstants.EpochLength; - Dag result; + Cache result; lock(cacheLock) { @@ -38,22 +39,22 @@ public async Task GetDagAsync(ulong block, ILogger logger, CancellationToke if(!caches.TryGetValue(epoch, out result)) { - // No cached DAG, evict the oldest if the cache limit was reached + // No cached cache, 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.Info(() => $"Evicting DAG for epoch {epochToEvict} in favour of epoch {epoch}"); + logger.Info(() => $"Evicting cache 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 we have the new cache pre-generated, use that, otherwise create a new one if(future != null && future.Epoch == epoch) { - logger.Debug(() => $"Using pre-generated DAG for epoch {epoch}"); + logger.Debug(() => $"Using pre-generated cache for epoch {epoch}"); result = future; future = null; @@ -61,8 +62,8 @@ public async Task GetDagAsync(ulong block, ILogger logger, CancellationToke else { - logger.Info(() => $"No pre-generated DAG available, creating new for epoch {epoch}"); - result = new Dag(epoch); + logger.Info(() => $"No pre-generated cache available, creating new for epoch {epoch}"); + result = new Cache(epoch); } caches[epoch] = result; @@ -71,11 +72,11 @@ public async Task GetDagAsync(ulong block, ILogger logger, CancellationToke // If we used up the future cache, or need a refresh, regenerate else if(future == null || future.Epoch <= epoch) { - logger.Info(() => $"Pre-generating DAG for epoch {epoch + 1}"); - future = new Dag(epoch + 1); + logger.Info(() => $"Pre-generating cache for epoch {epoch + 1}"); + future = new Cache(epoch + 1); #pragma warning disable 4014 - future.GenerateAsync(dagDir, logger, ct); + future.GenerateAsync(logger); #pragma warning restore 4014 } @@ -83,7 +84,7 @@ public async Task GetDagAsync(ulong block, ILogger logger, CancellationToke } // get/generate current one - await result.GenerateAsync(dagDir, logger, ct); + await result.GenerateAsync(logger); return result; } diff --git a/src/Miningcore/Crypto/Hashing/Ubqhash/DagUbqhash.cs b/src/Miningcore/Crypto/Hashing/Ubqhash/DagUbqhash.cs deleted file mode 100644 index 9902f7cab..000000000 --- a/src/Miningcore/Crypto/Hashing/Ubqhash/DagUbqhash.cs +++ /dev/null @@ -1,141 +0,0 @@ -using System.Diagnostics; -using System.Text; -using Miningcore.Blockchain.Ethereum; -using Miningcore.Contracts; -using Miningcore.Extensions; -using Miningcore.Messaging; -using Miningcore.Native; -using Miningcore.Notifications.Messages; -using NLog; - -namespace Miningcore.Crypto.Hashing.Ubqhash; - -public class DagUbqhash : IDisposable -{ - public DagUbqhash(ulong epoch) - { - Epoch = epoch; - } - - public ulong Epoch { get; set; } - - private IntPtr handle = IntPtr.Zero; - private static readonly Semaphore sem = new(1, 1); - - internal static IMessageBus messageBus; - - public DateTime LastUsed { get; set; } - - public static unsafe string GetDefaultDagDirectory() - { - var chars = new byte[512]; - - fixed (byte* data = chars) - { - if(UbqHash.ethash_get_default_dirname(data, chars.Length)) - { - int length; - for(length = 0; length < chars.Length; length++) - { - if(data[length] == 0) - break; - } - - return Encoding.UTF8.GetString(data, length); - } - } - - return null; - } - - public void Dispose() - { - if(handle != IntPtr.Zero) - { - UbqHash.ethash_full_delete(handle); - handle = IntPtr.Zero; - } - } - - public async Task GenerateAsync(string dagDir, ILogger logger, CancellationToken ct) - { - Contract.Requires(!string.IsNullOrEmpty(dagDir)); - - if(handle == IntPtr.Zero) - { - await Task.Run(() => - { - try - { - sem.WaitOne(); - - // re-check after obtaining lock - if(handle != IntPtr.Zero) - return; - - logger.Info(() => $"Generating DAG for epoch {Epoch}"); - - var started = DateTime.Now; - var block = Epoch * EthereumConstants.EpochLength; - - // Generate a temporary cache - var light = UbqHash.ethash_light_new(block); - - try - { - // Generate the actual DAG - handle = UbqHash.ethash_full_new(dagDir, light, progress => - { - logger.Info(() => $"Generating DAG for epoch {Epoch}: {progress}%"); - - return !ct.IsCancellationRequested ? 0 : 1; - }); - - if(handle == IntPtr.Zero) - throw new OutOfMemoryException("ethash_full_new IO or memory error"); - - logger.Info(() => $"Done generating DAG for epoch {Epoch} after {DateTime.Now - started}"); - } - - finally - { - if(light != IntPtr.Zero) - UbqHash.ethash_light_delete(light); - } - } - - finally - { - sem.Release(); - } - }, ct); - } - } - - public unsafe bool Compute(ILogger logger, byte[] hash, ulong nonce, out byte[] mixDigest, out byte[] result) - { - Contract.RequiresNonNull(hash); - - var sw = Stopwatch.StartNew(); - - mixDigest = null; - result = null; - - var value = new UbqHash.ethash_return_value(); - - fixed (byte* input = hash) - { - UbqHash.ethash_full_compute(handle, input, nonce, ref value); - } - - if(value.success) - { - mixDigest = value.mix_hash.value; - result = value.result.value; - } - - messageBus?.SendTelemetry("Ubqhash", TelemetryCategory.Hash, sw.Elapsed, value.success); - - return value.success; - } -} diff --git a/src/Miningcore/Native/EtcHash.cs b/src/Miningcore/Native/EtcHash.cs index 3dd80fd5f..209e55eb6 100644 --- a/src/Miningcore/Native/EtcHash.cs +++ b/src/Miningcore/Native/EtcHash.cs @@ -29,7 +29,7 @@ public struct ethash_return_value /// The block number for which to create the handler /// Newly allocated ethash_light handler or NULL [DllImport("libetchash", EntryPoint = "ethash_light_new_export", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr ethash_light_new(ulong block_number); + public static extern IntPtr ethash_light_new(ulong block_number, ulong fork_block); /// /// Frees a previously allocated ethash_light handler @@ -46,7 +46,7 @@ public struct ethash_return_value /// The nonce to pack into the mix /// an object of ethash_return_value_t holding the return values [DllImport("libetchash", EntryPoint = "ethash_light_compute_export", CallingConvention = CallingConvention.Cdecl)] - public static extern void ethash_light_compute(IntPtr handle, byte* header_hash, ulong nonce, ref ethash_return_value result); + public static extern void ethash_light_compute(IntPtr handle, byte* header_hash, ulong nonce, ulong fork_block, ref ethash_return_value result); /// /// Allocate and initialize a new ethash_full handler @@ -64,7 +64,7 @@ public struct ethash_return_value /// /// [DllImport("libetchash", EntryPoint = "ethash_full_new_export", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr ethash_full_new(string dagDir, IntPtr light, ethash_callback_t callback); + public static extern IntPtr ethash_full_new(string dagDir, IntPtr light, ulong fork_block, ethash_callback_t callback); /// /// Frees a previously allocated ethash_full handler diff --git a/src/Miningcore/Program.cs b/src/Miningcore/Program.cs index 42bc7761d..b9925a9d9 100644 --- a/src/Miningcore/Program.cs +++ b/src/Miningcore/Program.cs @@ -27,7 +27,7 @@ using Miningcore.Configuration; using Miningcore.Crypto.Hashing.Algorithms; using Miningcore.Crypto.Hashing.Equihash; -using Miningcore.Crypto.Hashing.Ethash; +using Miningcore.Crypto.Hashing.Ethash.Ethash; using Miningcore.Extensions; using Miningcore.Messaging; using Miningcore.Mining; @@ -53,9 +53,9 @@ using NLog.Targets; using Prometheus; using WebSocketManager; +using static Miningcore.Util.ActionUtils; using ILogger = NLog.ILogger; using LogLevel = Microsoft.Extensions.Logging.LogLevel; -using static Miningcore.Util.ActionUtils; // ReSharper disable AssignNullToNotNullAttribute // ReSharper disable PossibleNullReferenceException @@ -183,12 +183,12 @@ public static async Task Main(string[] args) }); // NSwag - #if DEBUG +#if DEBUG services.AddOpenApiDocument(settings => { settings.DocumentProcessors.Insert(0, new NSwagDocumentProcessor()); }); - #endif +#endif services.AddResponseCompression(); services.AddCors(); @@ -218,9 +218,9 @@ public static async Task Main(string[] args) "/metrics" }, clusterConfig.Api?.MetricsIpWhitelist); - #if DEBUG +#if DEBUG app.UseOpenApi(); - #endif +#endif app.UseResponseCompression(); app.UseCors(corsPolicyBuilder => corsPolicyBuilder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()); @@ -369,20 +369,20 @@ protected override async Task ExecuteAsync(CancellationToken ct) .Where(config => config.Enabled) .Select(config => RunPool(config, coinTemplates, ct)); - await Guard(()=> Task.WhenAll(tasks), ex => + await Guard(() => Task.WhenAll(tasks), ex => { switch(ex) { case PoolStartupException pse: - { - var _logger = pse.PoolId != null ? LogUtil.GetPoolScopedLogger(GetType(), pse.PoolId) : logger; - _logger.Error(() => $"{pse.Message}"); + { + var _logger = pse.PoolId != null ? LogUtil.GetPoolScopedLogger(GetType(), pse.PoolId) : logger; + _logger.Error(() => $"{pse.Message}"); - logger.Error(() => "Cluster cannot start. Good Bye!"); + logger.Error(() => "Cluster cannot start. Good Bye!"); - hal.StopApplication(); - break; - } + hal.StopApplication(); + break; + } default: throw ex; @@ -533,7 +533,7 @@ private static CommandLineApplication ParseCommandLine(string[] args) versionOption = app.Option("-v|--version", "Version Information", CommandOptionType.NoValue); configFileOption = app.Option("-c|--config ", "Configuration File", CommandOptionType.SingleValue); - dumpConfigOption = app.Option("-dc|--dumpconfig", "Dump the configuration (useful for trouble-shooting typos in the config file)",CommandOptionType.NoValue); + dumpConfigOption = app.Option("-dc|--dumpconfig", "Dump the configuration (useful for trouble-shooting typos in the config file)", CommandOptionType.NoValue); shareRecoveryOption = app.Option("-rs", "Import lost shares using existing recovery file", CommandOptionType.SingleValue); generateSchemaOption = app.Option("-gcs|--generate-config-schema ", "Generate JSON schema from configuration options", CommandOptionType.SingleValue); app.HelpOption("-? | -h | --help"); @@ -560,7 +560,7 @@ private static ClusterConfig ReadConfig(string file) { using(var validatingReader = new JSchemaValidatingReader(jsonReader) { - Schema = LoadSchema() + Schema = LoadSchema() }) { return serializer.Deserialize(validatingReader); @@ -781,8 +781,14 @@ private static async Task PreFlightChecks(IServiceProvider services) EquihashSolver.messageBus = messageBus; EquihashSolver.MaxThreads = clusterConfig.EquihashMaxThreads ?? 1; - // Configure Ethhash - Dag.messageBus = messageBus; + // Configure Ethash + Miningcore.Crypto.Hashing.Ethash.Ethash.Cache.messageBus = messageBus; + + // Configure Etchash + Miningcore.Crypto.Hashing.Ethash.Etchash.Cache.messageBus = messageBus; + + // Configure Ubqhash + Miningcore.Crypto.Hashing.Ethash.Ubqhash.Cache.messageBus = messageBus; // Configure Verthash Verthash.messageBus = messageBus; @@ -823,7 +829,7 @@ private static async Task ConfigurePostgresCompatibilityOptions(IServiceProvider if(enableLegacyTimestampBehavior) { - logger.Info(()=> "Enabling Npgsql Legacy Timestamp Behavior"); + logger.Info(() => "Enabling Npgsql Legacy Timestamp Behavior"); AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); } @@ -886,7 +892,7 @@ private static void ConfigurePostgres(PostgresConfig pgConfig, ContainerBuilder connectionString.Append($"CommandTimeout={pgConfig.CommandTimeout ?? 300};"); - logger.Debug(()=> $"Using postgres connection string: {connectionString}"); + logger.Debug(() => $"Using postgres connection string: {connectionString}"); // register connection factory builder.RegisterInstance(new PgConnectionFactory(connectionString.ToString())) diff --git a/src/Miningcore/coins.json b/src/Miningcore/coins.json index bc624a715..5bb711f84 100644 --- a/src/Miningcore/coins.json +++ b/src/Miningcore/coins.json @@ -4280,7 +4280,8 @@ "uncle": "https://explorer.callisto.network/blocks/$height$" }, "explorerTxLink": "https://explorer.callisto.network/tx/{0}", - "explorerAccountLink": "https://explorer.callisto.network/address/{0}" + "explorerAccountLink": "https://explorer.callisto.network/address/{0}", + "ethasher": "ethash" }, "ethereum": { "name": "Ethereum", @@ -4297,7 +4298,8 @@ "uncle": "https://etherscan.io/uncle/$height$" }, "explorerTxLink": "https://etherscan.io/tx/{0}", - "explorerAccountLink": "https://etherscan.io/address/{0}" + "explorerAccountLink": "https://etherscan.io/address/{0}", + "ethasher": "ethash" }, "ethereumclassic": { "name": "Ethereum Classic", @@ -4314,7 +4316,8 @@ "uncle": "https://blockscout.com/etc/mainnet/block/$height$" }, "explorerTxLink": "https://blockscout.com/etc/mainnet/tx/{0}", - "explorerAccountLink": "https://blockscout.com/etc/mainnet/address/{0}" + "explorerAccountLink": "https://blockscout.com/etc/mainnet/address/{0}", + "ethasher": "etchash" }, "ethereum-pow": { "name": "EthereumPoW", @@ -4331,7 +4334,8 @@ "uncle": "https://www.oklink.com/en/ethw/uncle/$height$" }, "explorerTxLink": "https://www.oklink.com/en/ethw/tx/{0}", - "explorerAccountLink": "https://www.oklink.com/en/ethw/address/{0}" + "explorerAccountLink": "https://www.oklink.com/en/ethw/address/{0}", + "ethasher": "ethash" }, "etherone": { "name": "EtherOne", @@ -4348,7 +4352,8 @@ "uncle": "https://blockscout.etherone.one/uncles/$height$" }, "explorerTxLink": "https://blockscout.etherone.one/tx/{0}", - "explorerAccountLink": "https://blockscout.etherone.one/address/{0}" + "explorerAccountLink": "https://blockscout.etherone.one/address/{0}", + "ethasher": "ethash" }, "pinkchain": { "name": "PinkChain", @@ -4365,7 +4370,8 @@ "uncle": "https://pinkscan.org/uncle/$height$" }, "explorerTxLink": "https://pinkscan.org/tx/{0}", - "explorerAccountLink": "https://pinkscan.org/address/{0}" + "explorerAccountLink": "https://pinkscan.org/address/{0}", + "ethasher": "ethash" }, "ubiq": { "name": "Ubiq", @@ -4382,7 +4388,8 @@ "uncle": "https://ubiqscan.io/uncle/$height$" }, "explorerTxLink": "https://ubiqscan.io/tx/{0}", - "explorerAccountLink": "https://ubiqscan.io/address/{0}" + "explorerAccountLink": "https://ubiqscan.io/address/{0}", + "ethasher": "ubqhash" }, "ergo": { "name": "Ergo", diff --git a/src/Native/libetchash/ethash.h b/src/Native/libetchash/ethash.h index 875f5e891..513304da2 100644 --- a/src/Native/libetchash/ethash.h +++ b/src/Native/libetchash/ethash.h @@ -16,8 +16,8 @@ */ /** @file ethash.h -* @date 2015 -*/ + * @date 2015 + */ #pragma once #include @@ -29,12 +29,12 @@ #define ETHASH_REVISION 23 #define ETHASH_DATASET_BYTES_INIT 1073741824U // 2**30 #define ETHASH_DATASET_BYTES_GROWTH 8388608U // 2**23 -#define ETHASH_CACHE_BYTES_INIT 1073741824U // 2**24 -#define ETHASH_CACHE_BYTES_GROWTH 131072U // 2**17 +#define ETHASH_CACHE_BYTES_INIT 1073741824U // 2**24 +#define ETHASH_CACHE_BYTES_GROWTH 131072U // 2**17 #define ETHASH_EPOCH_LENGTH 30000U #define ETHASH_EPOCH_LENGTH_NEW 60000U -#define ETCHASH_FORK_BLOCK 11700000 // classic mainnet -//#define ETCHASH_FORK_BLOCK 2520000 // mordor +// #define ETCHASH_FORK_BLOCK 11700000 // classic mainnet +// #define ETCHASH_FORK_BLOCK 2520000 // mordor #define ETHASH_MIX_BYTES 128 #define ETHASH_HASH_BYTES 64 #define ETHASH_DATASET_PARENTS 256 @@ -44,108 +44,116 @@ #define ETHASH_DAG_MAGIC_NUM 0xFEE1DEADBADDCAFE #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif -/// Type of a seedhash/blockhash e.t.c. -typedef struct ethash_h256 { uint8_t b[32]; } ethash_h256_t; + /// Type of a seedhash/blockhash e.t.c. + typedef struct ethash_h256 + { + uint8_t b[32]; + } ethash_h256_t; // convenience macro to statically initialize an h256_t // usage: // ethash_h256_t a = ethash_h256_static_init(1, 2, 3, ... ) // have to provide all 32 values. If you don't provide all the rest // will simply be unitialized (not guranteed to be 0) -#define ethash_h256_static_init(...) \ - { {__VA_ARGS__} } - -struct ethash_light; -typedef struct ethash_light* ethash_light_t; -struct ethash_full; -typedef struct ethash_full* ethash_full_t; -typedef int(*ethash_callback_t)(unsigned); +#define ethash_h256_static_init(...) \ + { \ + { \ + __VA_ARGS__ \ + } \ + } + + struct ethash_light; + typedef struct ethash_light *ethash_light_t; + struct ethash_full; + typedef struct ethash_full *ethash_full_t; + typedef int (*ethash_callback_t)(unsigned); #pragma pack(push, 1) -typedef struct ethash_return_value { - ethash_h256_t result; - ethash_h256_t mix_hash; - bool success; -} ethash_return_value_t; + typedef struct ethash_return_value + { + ethash_h256_t result; + ethash_h256_t mix_hash; + bool success; + } ethash_return_value_t; #pragma pack(pop) -/** - * Allocate and initialize a new ethash_light handler - * - * @param block_number The block number for which to create the handler - * @return Newly allocated ethash_light handler or NULL in case of - * ERRNOMEM or invalid parameters used for @ref ethash_compute_cache_nodes() - */ -ethash_light_t ethash_light_new(uint64_t block_number); -/** - * Frees a previously allocated ethash_light handler - * @param light The light handler to free - */ -void ethash_light_delete(ethash_light_t light); -/** - * Calculate the light client data - * - * @param light The light client handler - * @param header_hash The header hash to pack into the mix - * @param nonce The nonce to pack into the mix - * @return an object of ethash_return_value_t holding the return values - */ -ethash_return_value_t ethash_light_compute( - ethash_light_t light, - ethash_h256_t const header_hash, - uint64_t nonce -); - -/** - * Allocate and initialize a new ethash_full handler - * - * @param light The light handler containing the cache. - * @param callback A callback function with signature of @ref ethash_callback_t - * It accepts an unsigned with which a progress of DAG calculation - * can be displayed. If all goes well the callback should return 0. - * If a non-zero value is returned then DAG generation will stop. - * Be advised. A progress value of 100 means that DAG creation is - * almost complete and that this function will soon return succesfully. - * It does not mean that the function has already had a succesfull return. - * @return Newly allocated ethash_full handler or NULL in case of - * ERRNOMEM or invalid parameters used for @ref ethash_compute_full_data() - */ -ethash_full_t ethash_full_new(ethash_light_t light, ethash_callback_t callback); - -/** - * Frees a previously allocated ethash_full handler - * @param full The light handler to free - */ -void ethash_full_delete(ethash_full_t full); -/** - * Calculate the full client data - * - * @param full The full client handler - * @param header_hash The header hash to pack into the mix - * @param nonce The nonce to pack into the mix - * @return An object of ethash_return_value to hold the return value - */ -ethash_return_value_t ethash_full_compute( - ethash_full_t full, - ethash_h256_t const header_hash, - uint64_t nonce -); -/** - * Get a pointer to the full DAG data - */ -void const* ethash_full_dag(ethash_full_t full); -/** - * Get the size of the DAG data - */ -uint64_t ethash_full_dag_size(ethash_full_t full); - -/** - * Calculate the seedhash for a given block number - */ -ethash_h256_t ethash_get_seedhash(uint64_t block_number); + /** + * Allocate and initialize a new ethash_light handler + * + * @param block_number The block number for which to create the handler + * @return Newly allocated ethash_light handler or NULL in case of + * ERRNOMEM or invalid parameters used for @ref ethash_compute_cache_nodes() + */ + ethash_light_t ethash_light_new(uint64_t block_number, uint64_t const fork_block); + /** + * Frees a previously allocated ethash_light handler + * @param light The light handler to free + */ + void ethash_light_delete(ethash_light_t light); + /** + * Calculate the light client data + * + * @param light The light client handler + * @param header_hash The header hash to pack into the mix + * @param nonce The nonce to pack into the mix + * @return an object of ethash_return_value_t holding the return values + */ + ethash_return_value_t ethash_light_compute( + ethash_light_t light, + ethash_h256_t const header_hash, + uint64_t nonce, + uint64_t const fork_block); + + /** + * Allocate and initialize a new ethash_full handler + * + * @param light The light handler containing the cache. + * @param callback A callback function with signature of @ref ethash_callback_t + * It accepts an unsigned with which a progress of DAG calculation + * can be displayed. If all goes well the callback should return 0. + * If a non-zero value is returned then DAG generation will stop. + * Be advised. A progress value of 100 means that DAG creation is + * almost complete and that this function will soon return succesfully. + * It does not mean that the function has already had a succesfull return. + * @return Newly allocated ethash_full handler or NULL in case of + * ERRNOMEM or invalid parameters used for @ref ethash_compute_full_data() + */ + ethash_full_t ethash_full_new(ethash_light_t light, uint64_t const fork_block, ethash_callback_t callback); + + /** + * Frees a previously allocated ethash_full handler + * @param full The light handler to free + */ + void ethash_full_delete(ethash_full_t full); + /** + * Calculate the full client data + * + * @param full The full client handler + * @param header_hash The header hash to pack into the mix + * @param nonce The nonce to pack into the mix + * @return An object of ethash_return_value to hold the return value + */ + ethash_return_value_t ethash_full_compute( + ethash_full_t full, + ethash_h256_t const header_hash, + uint64_t nonce); + /** + * Get a pointer to the full DAG data + */ + void const *ethash_full_dag(ethash_full_t full); + /** + * Get the size of the DAG data + */ + uint64_t ethash_full_dag_size(ethash_full_t full); + + /** + * Calculate the seedhash for a given block number + */ + ethash_h256_t ethash_get_seedhash(uint64_t block_number); #ifdef __cplusplus } diff --git a/src/Native/libetchash/exports.cpp b/src/Native/libetchash/exports.cpp index 506cb149c..bed4e80d6 100644 --- a/src/Native/libetchash/exports.cpp +++ b/src/Native/libetchash/exports.cpp @@ -19,7 +19,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "internal.h" #include "ethash.h" -extern "C" bool ethash_get_default_dirname(char* strbuf, size_t buffsize); +extern "C" bool ethash_get_default_dirname(char *strbuf, size_t buffsize); #ifdef _WIN32 #define MODULE_API __declspec(dllexport) @@ -27,19 +27,19 @@ extern "C" bool ethash_get_default_dirname(char* strbuf, size_t buffsize); #define MODULE_API #endif -extern "C" MODULE_API uint64_t ethash_get_datasize_export(uint64_t const block_number) +extern "C" MODULE_API uint64_t ethash_get_datasize_export(uint64_t const block_number, uint64_t const fork_block) { - return ethash_get_datasize(block_number); + return ethash_get_datasize(block_number, fork_block); } -extern "C" MODULE_API uint64_t ethash_get_cachesize_export(uint64_t const block_number) +extern "C" MODULE_API uint64_t ethash_get_cachesize_export(uint64_t const block_number, uint64_t const fork_block) { - return ethash_get_cachesize(block_number); + return ethash_get_cachesize(block_number, fork_block); } -extern "C" MODULE_API ethash_light_t ethash_light_new_export(uint64_t block_number) +extern "C" MODULE_API ethash_light_t ethash_light_new_export(uint64_t block_number, uint64_t const fork_block) { - return ethash_light_new(block_number); + return ethash_light_new(block_number, fork_block); } extern "C" MODULE_API void ethash_light_delete_export(ethash_light_t light) @@ -51,14 +51,15 @@ extern "C" MODULE_API void ethash_light_compute_export( ethash_light_t light, ethash_h256_t const *header_hash, uint64_t nonce, + uint64_t const fork_block, ethash_return_value_t *result) { - *result = ethash_light_compute(light, *header_hash, nonce); + *result = ethash_light_compute(light, *header_hash, nonce, fork_block); } -extern "C" MODULE_API ethash_full_t ethash_full_new_export(const char *dirname, ethash_light_t light, ethash_callback_t callback) +extern "C" MODULE_API ethash_full_t ethash_full_new_export(const char *dirname, ethash_light_t light, uint64_t const fork_block, ethash_callback_t callback) { - uint64_t full_size = ethash_get_datasize(light->block_number); + uint64_t full_size = ethash_get_datasize(light->block_number, fork_block); ethash_h256_t seedhash = ethash_get_seedhash(light->block_number); return ethash_full_new_internal(dirname, seedhash, full_size, light, callback); } @@ -77,7 +78,7 @@ extern "C" MODULE_API void ethash_full_compute_export( *result = ethash_full_compute(full, *header_hash, nonce); } -extern "C" MODULE_API void const* ethash_full_dag_export(ethash_full_t full) +extern "C" MODULE_API void const *ethash_full_dag_export(ethash_full_t full) { return ethash_full_dag(full); } diff --git a/src/Native/libetchash/internal.c b/src/Native/libetchash/internal.c index 3ab113d47..09cf71e2b 100644 --- a/src/Native/libetchash/internal.c +++ b/src/Native/libetchash/internal.c @@ -15,10 +15,10 @@ along with cpp-ethereum. If not, see . */ /** @file internal.c -* @author Tim Hughes -* @author Matthew Wampler-Doty -* @date 2015 -*/ + * @author Tim Hughes + * @author Matthew Wampler-Doty + * @date 2015 + */ #include #include @@ -45,42 +45,46 @@ #include #endif -uint64_t ethash_get_datasize(uint64_t const block_number) +uint64_t ethash_get_datasize(uint64_t const block_number, uint64_t const fork_block) { - return dag_sizes[etchash_calc_epoch(block_number)]; + return dag_sizes[etchash_calc_epoch(block_number, fork_block)]; } -uint64_t ethash_get_cachesize(uint64_t const block_number) +uint64_t ethash_get_cachesize(uint64_t const block_number, uint64_t const fork_block) { - return cache_sizes[etchash_calc_epoch(block_number)]; + return cache_sizes[etchash_calc_epoch(block_number, fork_block)]; } // Follows Sergio's "STRICT MEMORY HARD HASHING FUNCTIONS" (2014) // https://bitslog.files.wordpress.com/2013/12/memohash-v0-3.pdf // SeqMemoHash(s, R, N) bool static ethash_compute_cache_nodes( - node* const nodes, + node *const nodes, uint64_t cache_size, - ethash_h256_t const* seed -) + ethash_h256_t const *seed) { - if (cache_size % sizeof(node) != 0) { + if (cache_size % sizeof(node) != 0) + { return false; } - uint32_t const num_nodes = (uint32_t) (cache_size / sizeof(node)); + uint32_t const num_nodes = (uint32_t)(cache_size / sizeof(node)); - SHA3_512(nodes[0].bytes, (uint8_t*)seed, 32); + SHA3_512(nodes[0].bytes, (uint8_t *)seed, 32); - for (uint32_t i = 1; i != num_nodes; ++i) { + for (uint32_t i = 1; i != num_nodes; ++i) + { SHA3_512(nodes[i].bytes, nodes[i - 1].bytes, 64); } - for (uint32_t j = 0; j != ETHASH_CACHE_ROUNDS; j++) { - for (uint32_t i = 0; i != num_nodes; i++) { + for (uint32_t j = 0; j != ETHASH_CACHE_ROUNDS; j++) + { + for (uint32_t i = 0; i != num_nodes; i++) + { uint32_t const idx = nodes[i].words[0] % num_nodes; node data; data = nodes[(num_nodes - 1 + i) % num_nodes]; - for (uint32_t w = 0; w != NODE_WORDS; ++w) { + for (uint32_t w = 0; w != NODE_WORDS; ++w) + { data.words[w] ^= nodes[idx].words[w]; } SHA3_512(nodes[i].bytes, data.bytes, sizeof(data)); @@ -93,14 +97,13 @@ bool static ethash_compute_cache_nodes( } void ethash_calculate_dag_item( - node* const ret, + node *const ret, uint32_t node_index, - ethash_light_t const light -) + ethash_light_t const light) { - uint32_t num_parent_nodes = (uint32_t) (light->cache_size / sizeof(node)); - node const* cache_nodes = (node const *) light->cache; - node const* init = &cache_nodes[node_index % num_parent_nodes]; + uint32_t num_parent_nodes = (uint32_t)(light->cache_size / sizeof(node)); + node const *cache_nodes = (node const *)light->cache; + node const *init = &cache_nodes[node_index % num_parent_nodes]; memcpy(ret, init, sizeof(node)); ret->words[0] ^= node_index; SHA3_512(ret->bytes, ret->bytes, sizeof(node)); @@ -112,7 +115,8 @@ void ethash_calculate_dag_item( __m128i xmm3 = ret->xmm[3]; #endif - for (uint32_t i = 0; i != ETHASH_DATASET_PARENTS; ++i) { + for (uint32_t i = 0; i != ETHASH_DATASET_PARENTS; ++i) + { uint32_t parent_index = fnv_hash(node_index ^ i, ret->words[i % NODE_WORDS]) % num_parent_nodes; node const *parent = &cache_nodes[parent_index]; @@ -133,9 +137,10 @@ void ethash_calculate_dag_item( ret->xmm[2] = xmm2; ret->xmm[3] = xmm3; } - #else +#else { - for (unsigned w = 0; w != NODE_WORDS; ++w) { + for (unsigned w = 0; w != NODE_WORDS; ++w) + { ret->words[w] = fnv_hash(ret->words[w], parent->words[w]); } } @@ -145,25 +150,27 @@ void ethash_calculate_dag_item( } bool ethash_compute_full_data( - void* mem, + void *mem, uint64_t full_size, ethash_light_t const light, - ethash_callback_t callback -) + ethash_callback_t callback) { if (full_size % (sizeof(uint32_t) * MIX_WORDS) != 0 || - (full_size % sizeof(node)) != 0) { + (full_size % sizeof(node)) != 0) + { return false; } uint32_t const max_n = (uint32_t)(full_size / sizeof(node)); - node* full_nodes = mem; + node *full_nodes = mem; double const progress_change = 1.0f / max_n; double progress = 0.0f; // now compute full nodes - for (uint32_t n = 0; n != max_n; ++n) { + for (uint32_t n = 0; n != max_n; ++n) + { if (callback && n % (max_n / 100) == 0 && - callback((unsigned int)(ceil(progress * 100.0f))) != 0) { + callback((unsigned int)(ceil(progress * 100.0f))) != 0) + { return false; } @@ -174,15 +181,15 @@ bool ethash_compute_full_data( } static bool ethash_hash( - ethash_return_value_t* ret, - node const* full_nodes, + ethash_return_value_t *ret, + node const *full_nodes, ethash_light_t const light, uint64_t full_size, ethash_h256_t const header_hash, - uint64_t const nonce -) + uint64_t const nonce) { - if (full_size % MIX_WORDS != 0) { + if (full_size % MIX_WORDS != 0) + { return false; } @@ -196,22 +203,28 @@ static bool ethash_hash( SHA3_512(s_mix->bytes, s_mix->bytes, 40); fix_endian_arr32(s_mix[0].words, 16); - node* const mix = s_mix + 1; - for (uint32_t w = 0; w != MIX_WORDS; ++w) { + node *const mix = s_mix + 1; + for (uint32_t w = 0; w != MIX_WORDS; ++w) + { mix->words[w] = s_mix[0].words[w % NODE_WORDS]; } unsigned const page_size = sizeof(uint32_t) * MIX_WORDS; - unsigned const num_full_pages = (unsigned) (full_size / page_size); + unsigned const num_full_pages = (unsigned)(full_size / page_size); - for (unsigned i = 0; i != ETHASH_ACCESSES; ++i) { + for (unsigned i = 0; i != ETHASH_ACCESSES; ++i) + { uint32_t const index = fnv_hash(s_mix->words[0] ^ i, mix->words[i % MIX_WORDS]) % num_full_pages; - for (unsigned n = 0; n != MIX_NODES; ++n) { - node const* dag_node; - if (full_nodes) { + for (unsigned n = 0; n != MIX_NODES; ++n) + { + node const *dag_node; + if (full_nodes) + { dag_node = &full_nodes[MIX_NODES * index + n]; - } else { + } + else + { node tmp_node; ethash_calculate_dag_item(&tmp_node, index * MIX_NODES + n, light); dag_node = &tmp_node; @@ -229,19 +242,20 @@ static bool ethash_hash( mix[n].xmm[2] = _mm_xor_si128(xmm2, dag_node->xmm[2]); mix[n].xmm[3] = _mm_xor_si128(xmm3, dag_node->xmm[3]); } - #else +#else { - for (unsigned w = 0; w != NODE_WORDS; ++w) { + for (unsigned w = 0; w != NODE_WORDS; ++w) + { mix[n].words[w] = fnv_hash(mix[n].words[w], dag_node->words[w]); } } #endif } - } // compress mix - for (uint32_t w = 0; w != MIX_WORDS; w += 4) { + for (uint32_t w = 0; w != MIX_WORDS; w += 4) + { uint32_t reduction = mix->words[w + 0]; reduction = reduction * FNV_PRIME ^ mix->words[w + 1]; reduction = reduction * FNV_PRIME ^ mix->words[w + 2]; @@ -257,11 +271,10 @@ static bool ethash_hash( } void ethash_quick_hash( - ethash_h256_t* return_hash, - ethash_h256_t const* header_hash, + ethash_h256_t *return_hash, + ethash_h256_t const *header_hash, uint64_t nonce, - ethash_h256_t const* mix_hash -) + ethash_h256_t const *mix_hash) { uint8_t buf[64 + 32]; memcpy(buf, header_hash, 32); @@ -278,16 +291,15 @@ ethash_h256_t ethash_get_seedhash(uint64_t block_number) ethash_h256_reset(&ret); uint64_t const epochs = block_number / ETHASH_EPOCH_LENGTH; for (uint32_t i = 0; i < epochs; ++i) - SHA3_256(&ret, (uint8_t*)&ret, 32); + SHA3_256(&ret, (uint8_t *)&ret, 32); return ret; } bool ethash_quick_check_difficulty( - ethash_h256_t const* header_hash, + ethash_h256_t const *header_hash, uint64_t const nonce, - ethash_h256_t const* mix_hash, - ethash_h256_t const* boundary -) + ethash_h256_t const *mix_hash, + ethash_h256_t const *boundary) { ethash_h256_t return_hash; @@ -295,19 +307,22 @@ bool ethash_quick_check_difficulty( return ethash_check_difficulty(&return_hash, boundary); } -ethash_light_t ethash_light_new_internal(uint64_t cache_size, ethash_h256_t const* seed) +ethash_light_t ethash_light_new_internal(uint64_t cache_size, ethash_h256_t const *seed) { struct ethash_light *ret; ret = calloc(sizeof(*ret), 1); - if (!ret) { + if (!ret) + { return NULL; } ret->cache = malloc((size_t)cache_size); - if (!ret->cache) { + if (!ret->cache) + { goto fail_free_light; } - node* nodes = (node*)ret->cache; - if (!ethash_compute_cache_nodes(nodes, cache_size, seed)) { + node *nodes = (node *)ret->cache; + if (!ethash_compute_cache_nodes(nodes, cache_size, seed)) + { goto fail_free_cache_mem; } ret->cache_size = cache_size; @@ -320,39 +335,40 @@ ethash_light_t ethash_light_new_internal(uint64_t cache_size, ethash_h256_t cons return NULL; } -ethash_light_t ethash_light_new(uint64_t block_number) +ethash_light_t ethash_light_new(uint64_t block_number, uint64_t const fork_block) { ethash_h256_t seedhash = ethash_get_seedhash(block_number); ethash_light_t ret; - ret = ethash_light_new_internal(ethash_get_cachesize(block_number), &seedhash); + ret = ethash_light_new_internal(ethash_get_cachesize(block_number, fork_block), &seedhash); ret->block_number = block_number; return ret; } void ethash_light_delete(ethash_light_t light) { - if (light->cache) { + if (light->cache) + { free(light->cache); } free(light); } -uint64_t static etchash_calc_epoch(uint64_t const block_number) +uint64_t static etchash_calc_epoch(uint64_t const block_number, uint64_t const fork_block) { - uint64_t epochLen = block_number >= ETCHASH_FORK_BLOCK ? ETHASH_EPOCH_LENGTH_NEW : ETHASH_EPOCH_LENGTH; - return block_number / epochLen; + uint64_t epochLen = block_number >= fork_block ? ETHASH_EPOCH_LENGTH_NEW : ETHASH_EPOCH_LENGTH; + return block_number / epochLen; } ethash_return_value_t ethash_light_compute_internal( ethash_light_t light, uint64_t full_size, ethash_h256_t const header_hash, - uint64_t nonce -) + uint64_t nonce) { - ethash_return_value_t ret; + ethash_return_value_t ret; ret.success = true; - if (!ethash_hash(&ret, NULL, light, full_size, header_hash, nonce)) { + if (!ethash_hash(&ret, NULL, light, full_size, header_hash, nonce)) + { ret.success = false; } return ret; @@ -361,93 +377,102 @@ ethash_return_value_t ethash_light_compute_internal( ethash_return_value_t ethash_light_compute( ethash_light_t light, ethash_h256_t const header_hash, - uint64_t nonce -) + uint64_t nonce, + uint64_t const fork_block) { - uint64_t full_size = ethash_get_datasize(light->block_number); + uint64_t full_size = ethash_get_datasize(light->block_number, fork_block); return ethash_light_compute_internal(light, full_size, header_hash, nonce); } -static bool ethash_mmap(struct ethash_full* ret, FILE* f) +static bool ethash_mmap(struct ethash_full *ret, FILE *f) { int fd; - char* mmapped_data; + char *mmapped_data; errno = 0; ret->file = f; - if ((fd = ethash_fileno(ret->file)) == -1) { + if ((fd = ethash_fileno(ret->file)) == -1) + { return false; } - mmapped_data= mmap( + mmapped_data = mmap( NULL, (size_t)ret->file_size + ETHASH_DAG_MAGIC_NUM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, - 0 - ); - if (mmapped_data == MAP_FAILED) { + 0); + if (mmapped_data == MAP_FAILED) + { return false; } - ret->data = (node*)(mmapped_data + ETHASH_DAG_MAGIC_NUM_SIZE); + ret->data = (node *)(mmapped_data + ETHASH_DAG_MAGIC_NUM_SIZE); return true; } ethash_full_t ethash_full_new_internal( - char const* dirname, + char const *dirname, ethash_h256_t const seed_hash, uint64_t full_size, ethash_light_t const light, - ethash_callback_t callback -) + ethash_callback_t callback) { - struct ethash_full* ret; + struct ethash_full *ret; FILE *f = NULL; ret = calloc(sizeof(*ret), 1); - if (!ret) { + if (!ret) + { return NULL; } ret->file_size = (size_t)full_size; - switch (ethash_io_prepare(dirname, seed_hash, &f, (size_t)full_size, false)) { + switch (ethash_io_prepare(dirname, seed_hash, &f, (size_t)full_size, false)) + { case ETHASH_IO_FAIL: // ethash_io_prepare will do all ETHASH_CRITICAL() logging in fail case goto fail_free_full; case ETHASH_IO_MEMO_MATCH: - if (!ethash_mmap(ret, f)) { + if (!ethash_mmap(ret, f)) + { ETHASH_CRITICAL("mmap failure()"); goto fail_close_file; } return ret; case ETHASH_IO_MEMO_SIZE_MISMATCH: // if a DAG of same filename but unexpected size is found, silently force new file creation - if (ethash_io_prepare(dirname, seed_hash, &f, (size_t)full_size, true) != ETHASH_IO_MEMO_MISMATCH) { + if (ethash_io_prepare(dirname, seed_hash, &f, (size_t)full_size, true) != ETHASH_IO_MEMO_MISMATCH) + { ETHASH_CRITICAL("Could not recreate DAG file after finding existing DAG with unexpected size."); goto fail_free_full; } // fallthrough to the mismatch case here, DO NOT go through match case ETHASH_IO_MEMO_MISMATCH: - if (!ethash_mmap(ret, f)) { + if (!ethash_mmap(ret, f)) + { ETHASH_CRITICAL("mmap failure()"); goto fail_close_file; } break; } - if (!ethash_compute_full_data(ret->data, full_size, light, callback)) { + if (!ethash_compute_full_data(ret->data, full_size, light, callback)) + { ETHASH_CRITICAL("Failure at computing DAG data."); goto fail_free_full_data; } // after the DAG has been filled then we finalize it by writting the magic number at the beginning - if (fseek(f, 0, SEEK_SET) != 0) { + if (fseek(f, 0, SEEK_SET) != 0) + { ETHASH_CRITICAL("Could not seek to DAG file start to write magic number."); goto fail_free_full_data; } uint64_t const magic_num = ETHASH_DAG_MAGIC_NUM; - if (fwrite(&magic_num, ETHASH_DAG_MAGIC_NUM_SIZE, 1, f) != 1) { + if (fwrite(&magic_num, ETHASH_DAG_MAGIC_NUM_SIZE, 1, f) != 1) + { ETHASH_CRITICAL("Could not write magic number to DAG's beginning."); goto fail_free_full_data; } - if (fflush(f) != 0) {// make sure the magic number IS there + if (fflush(f) != 0) + { // make sure the magic number IS there ETHASH_CRITICAL("Could not flush memory mapped data to DAG file. Insufficient space?"); goto fail_free_full_data; } @@ -463,13 +488,14 @@ ethash_full_t ethash_full_new_internal( return NULL; } -ethash_full_t ethash_full_new(ethash_light_t light, ethash_callback_t callback) +ethash_full_t ethash_full_new(ethash_light_t light, uint64_t const fork_block, ethash_callback_t callback) { char strbuf[256]; - if (!ethash_get_default_dirname(strbuf, 256)) { + if (!ethash_get_default_dirname(strbuf, 256)) + { return NULL; } - uint64_t full_size = ethash_get_datasize(light->block_number); + uint64_t full_size = ethash_get_datasize(light->block_number, fork_block); ethash_h256_t seedhash = ethash_get_seedhash(light->block_number); return ethash_full_new_internal(strbuf, seedhash, full_size, light, callback); } @@ -478,7 +504,8 @@ void ethash_full_delete(ethash_full_t full) { // could check that munmap(..) == 0 but even if it did not can't really do anything here munmap(full->data, (size_t)full->file_size); - if (full->file) { + if (full->file) + { fclose(full->file); } free(full); @@ -487,24 +514,24 @@ void ethash_full_delete(ethash_full_t full) ethash_return_value_t ethash_full_compute( ethash_full_t full, ethash_h256_t const header_hash, - uint64_t nonce -) + uint64_t nonce) { ethash_return_value_t ret; ret.success = true; if (!ethash_hash( - &ret, - (node const*)full->data, - NULL, - full->file_size, - header_hash, - nonce)) { + &ret, + (node const *)full->data, + NULL, + full->file_size, + header_hash, + nonce)) + { ret.success = false; } return ret; } -void const* ethash_full_dag(ethash_full_t full) +void const *ethash_full_dag(ethash_full_t full) { return full->data; } diff --git a/src/Native/libetchash/internal.h b/src/Native/libetchash/internal.h index 7d630d272..ea59385d1 100644 --- a/src/Native/libetchash/internal.h +++ b/src/Native/libetchash/internal.h @@ -9,169 +9,168 @@ #endif #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif // compile time settings -#define NODE_WORDS (64/4) -#define MIX_WORDS (ETHASH_MIX_BYTES/4) +#define NODE_WORDS (64 / 4) +#define MIX_WORDS (ETHASH_MIX_BYTES / 4) #define MIX_NODES (MIX_WORDS / NODE_WORDS) #include -typedef union node { - uint8_t bytes[NODE_WORDS * 4]; - uint32_t words[NODE_WORDS]; - uint64_t double_words[NODE_WORDS / 2]; + typedef union node + { + uint8_t bytes[NODE_WORDS * 4]; + uint32_t words[NODE_WORDS]; + uint64_t double_words[NODE_WORDS / 2]; #if defined(_M_X64) && HAVE_SSE2 - __m128i xmm[NODE_WORDS/4]; + __m128i xmm[NODE_WORDS / 4]; #endif -} node; + } node; -static inline uint8_t ethash_h256_get(ethash_h256_t const* hash, unsigned int i) -{ - return hash->b[i]; -} + static inline uint8_t ethash_h256_get(ethash_h256_t const *hash, unsigned int i) + { + return hash->b[i]; + } -static inline void ethash_h256_set(ethash_h256_t* hash, unsigned int i, uint8_t v) -{ - hash->b[i] = v; -} + static inline void ethash_h256_set(ethash_h256_t *hash, unsigned int i, uint8_t v) + { + hash->b[i] = v; + } -static inline void ethash_h256_reset(ethash_h256_t* hash) -{ - memset(hash, 0, 32); -} + static inline void ethash_h256_reset(ethash_h256_t *hash) + { + memset(hash, 0, 32); + } -// Returns if hash is less than or equal to boundary (2^256/difficulty) -static inline bool ethash_check_difficulty( - ethash_h256_t const* hash, - ethash_h256_t const* boundary -) -{ - // Boundary is big endian - for (int i = 0; i < 32; i++) { - if (ethash_h256_get(hash, i) == ethash_h256_get(boundary, i)) { - continue; + // Returns if hash is less than or equal to boundary (2^256/difficulty) + static inline bool ethash_check_difficulty( + ethash_h256_t const *hash, + ethash_h256_t const *boundary) + { + // Boundary is big endian + for (int i = 0; i < 32; i++) + { + if (ethash_h256_get(hash, i) == ethash_h256_get(boundary, i)) + { + continue; + } + return ethash_h256_get(hash, i) < ethash_h256_get(boundary, i); } - return ethash_h256_get(hash, i) < ethash_h256_get(boundary, i); + return true; } - return true; -} -/** - * Difficulty quick check for POW preverification - * - * @param header_hash The hash of the header - * @param nonce The block's nonce - * @param mix_hash The mix digest hash - * @param boundary The boundary is defined as (2^256 / difficulty) - * @return true for succesful pre-verification and false otherwise - */ -bool ethash_quick_check_difficulty( - ethash_h256_t const* header_hash, - uint64_t const nonce, - ethash_h256_t const* mix_hash, - ethash_h256_t const* boundary -); - -struct ethash_light { - void* cache; - uint64_t cache_size; - uint64_t block_number; -}; - -/** - * Allocate and initialize a new ethash_light handler. Internal version - * - * @param cache_size The size of the cache in bytes - * @param seed Block seedhash to be used during the computation of the - * cache nodes - * @return Newly allocated ethash_light handler or NULL in case of - * ERRNOMEM or invalid parameters used for @ref ethash_compute_cache_nodes() - */ -ethash_light_t ethash_light_new_internal(uint64_t cache_size, ethash_h256_t const* seed); - -/** - * Calculate the light client data. Internal version. - * - * @param light The light client handler - * @param full_size The size of the full data in bytes. - * @param header_hash The header hash to pack into the mix - * @param nonce The nonce to pack into the mix - * @return The resulting hash. - */ -ethash_return_value_t ethash_light_compute_internal( - ethash_light_t light, - uint64_t full_size, - ethash_h256_t const header_hash, - uint64_t nonce -); - -struct ethash_full { - FILE* file; - uint64_t file_size; - node* data; -}; - -/** - * Allocate and initialize a new ethash_full handler. Internal version. - * - * @param dirname The directory in which to put the DAG file. - * @param seedhash The seed hash of the block. Used in the DAG file naming. - * @param full_size The size of the full data in bytes. - * @param cache A cache object to use that was allocated with @ref ethash_cache_new(). - * Iff this function succeeds the ethash_full_t will take memory - * memory ownership of the cache and free it at deletion. If - * not then the user still has to handle freeing of the cache himself. - * @param callback A callback function with signature of @ref ethash_callback_t - * It accepts an unsigned with which a progress of DAG calculation - * can be displayed. If all goes well the callback should return 0. - * If a non-zero value is returned then DAG generation will stop. - * @return Newly allocated ethash_full handler or NULL in case of - * ERRNOMEM or invalid parameters used for @ref ethash_compute_full_data() - */ -ethash_full_t ethash_full_new_internal( - char const* dirname, - ethash_h256_t const seed_hash, - uint64_t full_size, - ethash_light_t const light, - ethash_callback_t callback -); - -void ethash_calculate_dag_item( - node* const ret, - uint32_t node_index, - ethash_light_t const cache -); - -void ethash_quick_hash( - ethash_h256_t* return_hash, - ethash_h256_t const* header_hash, - const uint64_t nonce, - ethash_h256_t const* mix_hash -); - -uint64_t ethash_get_datasize(uint64_t const block_number); -uint64_t ethash_get_cachesize(uint64_t const block_number); -static uint64_t etchash_calc_epoch(uint64_t const block_number); - -/** - * Compute the memory data for a full node's memory - * - * @param mem A pointer to an ethash full's memory - * @param full_size The size of the full data in bytes - * @param cache A cache object to use in the calculation - * @param callback The callback function. Check @ref ethash_full_new() for details. - * @return true if all went fine and false for invalid parameters - */ -bool ethash_compute_full_data( - void* mem, - uint64_t full_size, - ethash_light_t const light, - ethash_callback_t callback -); + /** + * Difficulty quick check for POW preverification + * + * @param header_hash The hash of the header + * @param nonce The block's nonce + * @param mix_hash The mix digest hash + * @param boundary The boundary is defined as (2^256 / difficulty) + * @return true for succesful pre-verification and false otherwise + */ + bool ethash_quick_check_difficulty( + ethash_h256_t const *header_hash, + uint64_t const nonce, + ethash_h256_t const *mix_hash, + ethash_h256_t const *boundary); + + struct ethash_light + { + void *cache; + uint64_t cache_size; + uint64_t block_number; + }; + + /** + * Allocate and initialize a new ethash_light handler. Internal version + * + * @param cache_size The size of the cache in bytes + * @param seed Block seedhash to be used during the computation of the + * cache nodes + * @return Newly allocated ethash_light handler or NULL in case of + * ERRNOMEM or invalid parameters used for @ref ethash_compute_cache_nodes() + */ + ethash_light_t ethash_light_new_internal(uint64_t cache_size, ethash_h256_t const *seed); + + /** + * Calculate the light client data. Internal version. + * + * @param light The light client handler + * @param full_size The size of the full data in bytes. + * @param header_hash The header hash to pack into the mix + * @param nonce The nonce to pack into the mix + * @return The resulting hash. + */ + ethash_return_value_t ethash_light_compute_internal( + ethash_light_t light, + uint64_t full_size, + ethash_h256_t const header_hash, + uint64_t nonce); + + struct ethash_full + { + FILE *file; + uint64_t file_size; + node *data; + }; + + /** + * Allocate and initialize a new ethash_full handler. Internal version. + * + * @param dirname The directory in which to put the DAG file. + * @param seedhash The seed hash of the block. Used in the DAG file naming. + * @param full_size The size of the full data in bytes. + * @param cache A cache object to use that was allocated with @ref ethash_cache_new(). + * Iff this function succeeds the ethash_full_t will take memory + * memory ownership of the cache and free it at deletion. If + * not then the user still has to handle freeing of the cache himself. + * @param callback A callback function with signature of @ref ethash_callback_t + * It accepts an unsigned with which a progress of DAG calculation + * can be displayed. If all goes well the callback should return 0. + * If a non-zero value is returned then DAG generation will stop. + * @return Newly allocated ethash_full handler or NULL in case of + * ERRNOMEM or invalid parameters used for @ref ethash_compute_full_data() + */ + ethash_full_t ethash_full_new_internal( + char const *dirname, + ethash_h256_t const seed_hash, + uint64_t full_size, + ethash_light_t const light, + ethash_callback_t callback); + + void ethash_calculate_dag_item( + node *const ret, + uint32_t node_index, + ethash_light_t const cache); + + void ethash_quick_hash( + ethash_h256_t *return_hash, + ethash_h256_t const *header_hash, + const uint64_t nonce, + ethash_h256_t const *mix_hash); + + uint64_t ethash_get_datasize(uint64_t const block_number, uint64_t const fork_block); + uint64_t ethash_get_cachesize(uint64_t const block_number, uint64_t const fork_block); + static uint64_t etchash_calc_epoch(uint64_t const block_number, uint64_t const fork_block); + + /** + * Compute the memory data for a full node's memory + * + * @param mem A pointer to an ethash full's memory + * @param full_size The size of the full data in bytes + * @param cache A cache object to use in the calculation + * @param callback The callback function. Check @ref ethash_full_new() for details. + * @return true if all went fine and false for invalid parameters + */ + bool ethash_compute_full_data( + void *mem, + uint64_t full_size, + ethash_light_t const light, + ethash_callback_t callback); #ifdef __cplusplus }