diff --git a/src/Miningcore/AutofacModule.cs b/src/Miningcore/AutofacModule.cs index db6c9032a5..423ee33447 100644 --- a/src/Miningcore/AutofacModule.cs +++ b/src/Miningcore/AutofacModule.cs @@ -11,6 +11,7 @@ using Miningcore.Configuration; using Miningcore.Crypto; using Miningcore.Crypto.Hashing.Equihash; +using Miningcore.Crypto.Hashing.Ethash; using Miningcore.Messaging; using Miningcore.Mining; using Miningcore.Notifications; @@ -87,6 +88,12 @@ protected override void Load(ContainerBuilder builder) .PropertiesAutowired() .AsSelf(); + 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() @@ -163,7 +170,6 @@ protected override void Load(ContainerBuilder builder) ////////////////////// // Ethereum - builder.RegisterType(); builder.RegisterType(); ////////////////////// diff --git a/src/Miningcore/Blockchain/Ethereum/EthereumConstants.cs b/src/Miningcore/Blockchain/Ethereum/EthereumConstants.cs index 92fd6993c8..925cfe51e7 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); @@ -109,6 +107,11 @@ public class PomConstants public const decimal BaseRewardInitial = 7.0m; } +public class TeapartyConstants +{ + public const decimal BaseRewardInitial = 0.1m; +} + public class CanxiumConstants { public const decimal BaseRewardInitial = 0.1875m; @@ -129,6 +132,7 @@ public enum EthereumNetworkType Altcoin = 2330, MaxxChain = 10201, Pom = 801921, + Teaparty = 1773, Canxium = 3003, Unknown = -1, @@ -149,6 +153,7 @@ public enum GethChainType Altcoin = 2330, MaxxChain = 10201, Pom = 801921, + Teaparty = 1773, Canxium = 3003, Unknown = -1, diff --git a/src/Miningcore/Blockchain/Ethereum/EthereumJob.cs b/src/Miningcore/Blockchain/Ethereum/EthereumJob.cs index a7a181e499..7aa97575f2 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); @@ -53,154 +53,8 @@ private void RegisterNonce(StratumConnection worker, string nonce) } } - 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) - { - // 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 ethash.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 ProcessShareUbqHashAsync(StratumConnection worker, - string workerName, string fullNonceHex, UbqhashFull ubqhash, CancellationToken ct) + string workerName, string fullNonceHex, CancellationToken ct) { // dupe check lock(workerNonces) @@ -213,11 +67,11 @@ public async Task ProcessShareUbqHashAsync(StratumConnection worke 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); + // get dag/light cache for block + var cache = await ethash.GetCacheAsync(logger, BlockTemplate.Height, ct); // 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 diff --git a/src/Miningcore/Blockchain/Ethereum/EthereumJobManager.cs b/src/Miningcore/Blockchain/Ethereum/EthereumJobManager.cs index ac35801c52..031af56ac0 100644 --- a/src/Miningcore/Blockchain/Ethereum/EthereumJobManager.cs +++ b/src/Miningcore/Blockchain/Ethereum/EthereumJobManager.cs @@ -1,6 +1,5 @@ using System.Globalization; using System.Numerics; -using System.Reactive; using System.Reactive.Linq; using System.Reactive.Threading.Tasks; using System.Text; @@ -9,24 +8,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 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; @@ -47,14 +44,12 @@ public EthereumJobManager( this.clock = clock; 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 +101,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 +334,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 @@ -349,50 +345,21 @@ public override void Configure(PoolConfig pc, ClusterConfig cc) if(pc.EnableInternalStratum == true) { - var coin = pc.Template.As(); - - // ensure dag location is configured + // Automatic switch between Full DAG and Light Cache + // If dagDir provided: Full DAG + // If darDir empty: Light Cache 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); - - // 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; - } + + logger.Info(() => $"Ethasher is: {coin.Ethasher}"); + + var hardForkBlock = extraPoolConfig?.ChainTypeOverride == "Classic" ? EthereumClassicConstants.HardForkBlockMainnet : EthereumClassicConstants.HardForkBlockMordor; + // TODO: improve this + coin.Ethash.Setup(3, hardForkBlock, dagDir); } } @@ -465,87 +432,30 @@ public async Task SubmitShareV2Async(StratumConnection worker, string[] r 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) + var (share, fullNonceHex, headerHash, mixHash) = await job.ProcessShareAsync(worker, workerName, nonce, ct); + + 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) { - 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}"); - - shareUbqhash.IsBlockCandidate = await SubmitBlockAsync(shareUbqhash, fullNonceHexUbqhash, headerHashUbqhash, mixHashUbqhash); - - 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}"); - - share.IsBlockCandidate = await SubmitBlockAsync(share, fullNonceHex, headerHash, mixHash); - - if(share.IsBlockCandidate) - { - logger.Info(() => $"Daemon accepted block {share.BlockHeight} submitted by {context.Miner}"); - - OnBlockFound(); - } - } - - return share; + logger.Info(() => $"Submitting block {share.BlockHeight}"); + + share.IsBlockCandidate = await SubmitBlockAsync(share, fullNonceHex, headerHash, mixHash); + + if(share.IsBlockCandidate) + { + logger.Info(() => $"Daemon accepted block {share.BlockHeight} submitted by {context.Miner}"); + + OnBlockFound(); + } } + + return share; } public BlockchainStats BlockchainStats { get; } = new(); @@ -565,12 +475,21 @@ 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 +560,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 DAG/light cache using var timer = new PeriodicTimer(TimeSpan.FromSeconds(5)); do @@ -657,23 +576,18 @@ 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"); + if(!string.IsNullOrEmpty(extraPoolConfig?.DagDir)) + logger.Info(() => "Loading current DAG ..."); + else + logger.Info(() => "Loading current light cache ..."); + + await coin.Ethash.GetCacheAsync(logger, blockTemplate.Height, ct); + + if(!string.IsNullOrEmpty(extraPoolConfig?.DagDir)) + logger.Info(() => "Loaded current DAG"); + else + logger.Info(() => "Loaded current light cache"); + break; } @@ -698,7 +612,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 494c49b2fc..0d356c3470 100644 --- a/src/Miningcore/Configuration/ClusterConfig.cs +++ b/src/Miningcore/Configuration/ClusterConfig.cs @@ -612,6 +612,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 diff --git a/src/Miningcore/Configuration/ClusterConfigExtensions.cs b/src/Miningcore/Configuration/ClusterConfigExtensions.cs index 45df5c45bb..7ed450ae76 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; @@ -204,9 +205,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 447c372cef..0000000000 --- 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/Etchash/EtchashFull.cs b/src/Miningcore/Crypto/Hashing/Etchash/EtchashFull.cs deleted file mode 100644 index a5edd7aa0b..0000000000 --- a/src/Miningcore/Crypto/Hashing/Etchash/EtchashFull.cs +++ /dev/null @@ -1,94 +0,0 @@ -using Miningcore.Blockchain.Ethereum; -using Miningcore.Contracts; -using NLog; - -namespace Miningcore.Crypto.Hashing.Etchash; - -public class EtchashFull : IDisposable -{ - public EtchashFull(int numCaches, string dagDir, 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; - - public void Dispose() - { - foreach(var value in caches.Values) - value.Dispose(); - } - - public async Task GetDagAsync(ulong block, ILogger logger, CancellationToken ct) - { - var dagEpochLength = block >= hardForkBlock ? EthereumClassicConstants.EpochLength : EthereumConstants.EpochLength; - logger.Debug(() => $"Epoch length used: {dagEpochLength}"); - var epoch = block / dagEpochLength; - DagEtchash result; - - lock(cacheLock) - { - if(numCaches == 0) - numCaches = 3; - - if(!caches.TryGetValue(epoch, out result)) - { - // No cached DAG, evict the oldest if the cache limit was reached - while(caches.Count >= numCaches) - { - var toEvict = caches.Values.OrderBy(x => x.LastUsed).First(); - var key = caches.First(pair => pair.Value == toEvict).Key; - var epochToEvict = toEvict.Epoch; - - logger.Info(() => $"Evicting DAG for epoch {epochToEvict} in favour of epoch {epoch}"); - toEvict.Dispose(); - caches.Remove(key); - } - - // If we have the new DAG pre-generated, use that, otherwise create a new one - if(future != null && future.Epoch == epoch) - { - logger.Debug(() => $"Using pre-generated DAG for epoch {epoch}"); - - result = future; - future = null; - } - - else - { - logger.Info(() => $"No pre-generated DAG available, creating new for epoch {epoch}"); - result = new DagEtchash(epoch); - } - - caches[epoch] = result; - } - - // 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); - -#pragma warning disable 4014 - future.GenerateAsync(dagDir, dagEpochLength, logger, ct); -#pragma warning restore 4014 - } - - result.LastUsed = DateTime.Now; - } - - // get/generate current one - await result.GenerateAsync(dagDir, dagEpochLength, logger, ct); - - return result; - } -} diff --git a/src/Miningcore/Crypto/Hashing/Ethash/Abstractions.cs b/src/Miningcore/Crypto/Hashing/Ethash/Abstractions.cs new file mode 100644 index 0000000000..653231efe9 --- /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 totalCache, ulong hardForkBlock, string dagDir = null); + Task GetCacheAsync(ILogger logger, ulong block, CancellationToken ct); + string AlgoName { get; } +} + +public interface IEthashCache : IDisposable +{ + bool Compute(ILogger logger, byte[] hash, ulong nonce, out byte[] mixDigest, out byte[] result); +} diff --git a/src/Miningcore/Crypto/Hashing/Ethash/Dag.cs b/src/Miningcore/Crypto/Hashing/Ethash/Dag.cs deleted file mode 100644 index 5bb3936d28..0000000000 --- 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 0000000000..f94689913b --- /dev/null +++ b/src/Miningcore/Crypto/Hashing/Ethash/Etchash/Cache.cs @@ -0,0 +1,160 @@ +using System.Diagnostics; +using System.Text; +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, string dagDir = null) + { + Epoch = epoch; + this.dagDir = dagDir; + 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; } + private string dagDir; + 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) + { + // Full DAG + if(!string.IsNullOrEmpty(dagDir)) + { + EtcHash.ethash_full_delete(handle); + } + // Light Cache + else + { + EtcHash.ethash_light_delete(handle); + } + + handle = IntPtr.Zero; + } + } + + public async Task GenerateAsync(ILogger logger, ulong epochLength, ulong hardForkBlock, CancellationToken ct) + { + if(handle == IntPtr.Zero) + { + await Task.Run(() => + { + lock(genLock) + { + if(!isGenerated) + { + // re-check after obtaining lock + if(handle != IntPtr.Zero) + return; + + this.hardForkBlock = hardForkBlock; + var started = DateTime.Now; + var block = Epoch * epochLength; + // Full DAG + if(!string.IsNullOrEmpty(dagDir)) + { + logger.Debug(() => $"Generating DAG for epoch {Epoch}"); + logger.Debug(() => $"Epoch length used: {epochLength}"); + // Generate a temporary cache + var light = EtcHash.ethash_light_new(block, hardForkBlock); + // Generate the actual DAG + handle = EtcHash.ethash_full_new(dagDir, light, hardForkBlock, 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"); + if(light != IntPtr.Zero) + EthHash.ethash_light_delete(light); + logger.Info(() => $"Done generating DAG for epoch {Epoch} after {DateTime.Now - started}"); + } + // Light Cache + else + { + logger.Debug(() => $"Generating cache for epoch {Epoch}"); + handle = EtcHash.ethash_light_new(block, hardForkBlock); + logger.Debug(() => $"Done generating cache for epoch {Epoch} after {DateTime.Now - started}"); + } + isGenerated = true; + } + } + }, 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(); + + // Full DAG + if(!string.IsNullOrEmpty(dagDir)) + { + fixed (byte* input = hash) + { + EtcHash.ethash_full_compute(handle, input, nonce, ref value); + } + } + // Light Cache + else + { + 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; + } +} diff --git a/src/Miningcore/Crypto/Hashing/Ethash/Etchash/EtchashLight.cs b/src/Miningcore/Crypto/Hashing/Ethash/Etchash/EtchashLight.cs new file mode 100644 index 0000000000..6825a94365 --- /dev/null +++ b/src/Miningcore/Crypto/Hashing/Ethash/Etchash/EtchashLight.cs @@ -0,0 +1,96 @@ +using System.Text; +using Miningcore.Blockchain.Ethereum; +using Miningcore.Contracts; +using Miningcore.Native; +using NLog; + +namespace Miningcore.Crypto.Hashing.Ethash.Etchash; + +[Identifier("etchash")] +public class EtchashLight : IEthashLight +{ + public void Setup(int totalCache, ulong hardForkBlock, string dagDir = null) + { + this.numCaches = totalCache; + this.hardForkBlock = hardForkBlock; + 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 Cache future; + private string dagDir; + private ulong hardForkBlock; + public string AlgoName { get; } = "Etchash"; + + public void Dispose() + { + foreach(var value in caches.Values) + value.Dispose(); + } + + public async Task GetCacheAsync(ILogger logger, ulong block, CancellationToken ct) + { + var epochLength = block >= this.hardForkBlock ? EthereumClassicConstants.EpochLength : EthereumConstants.EpochLength; + logger.Debug(() => $"Epoch length used: {epochLength}"); + var epoch = block / epochLength; + Cache result; + + lock(cacheLock) + { + if(numCaches == 0) + numCaches = 3; + + if(!caches.TryGetValue(epoch, out result)) + { + // 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 cache for epoch {epochToEvict} in favour of epoch {epoch}"); + toEvict.Dispose(); + caches.Remove(key); + } + + // 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 cache for epoch {epoch}"); + + result = future; + future = null; + } + + else + { + logger.Info(() => $"No pre-generated cache available, creating new for epoch {epoch}"); + result = new Cache(epoch, dagDir); + } + + caches[epoch] = result; + } + + // If we used up the future cache, or need a refresh, regenerate + else if(future == null || future.Epoch <= epoch) + { + logger.Info(() => $"Pre-generating cache for epoch {epoch + 1}"); + future = new Cache(epoch + 1, dagDir); + +#pragma warning disable 4014 + future.GenerateAsync(logger, epochLength, this.hardForkBlock, ct); +#pragma warning restore 4014 + } + + result.LastUsed = DateTime.Now; + } + + // get/generate current one + await result.GenerateAsync(logger, epochLength, this.hardForkBlock, ct); + + 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 0000000000..9941db230d --- /dev/null +++ b/src/Miningcore/Crypto/Hashing/Ethash/Ethash/Cache.cs @@ -0,0 +1,169 @@ +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.Ethash; + +[Identifier("ethash")] +public class Cache : IEthashCache +{ + public Cache(ulong epoch, string dagDir = null) + { + Epoch = epoch; + this.dagDir = dagDir; + 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; } + private string dagDir; + 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) + { + // Full DAG + if(!string.IsNullOrEmpty(dagDir)) + { + EthHash.ethash_full_delete(handle); + } + // Light Cache + else + { + EthHash.ethash_light_delete(handle); + } + + handle = IntPtr.Zero; + } + } + + public async Task GenerateAsync(ILogger logger, CancellationToken ct) + { + if(handle == IntPtr.Zero) + { + await Task.Run(() => + { + lock(genLock) + { + if(!isGenerated) + { + // re-check after obtaining lock + if(handle != IntPtr.Zero) + return; + + var started = DateTime.Now; + var block = Epoch * EthereumConstants.EpochLength; + + // Full DAG + if(!string.IsNullOrEmpty(dagDir)) + { + logger.Debug(() => $"Generating DAG for epoch {Epoch}"); + logger.Debug(() => $"Epoch length used: {EthereumConstants.EpochLength}"); + + // Generate a temporary cache + var light = EthHash.ethash_light_new(block); + + // 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"); + + if(light != IntPtr.Zero) + EthHash.ethash_light_delete(light); + + logger.Info(() => $"Done generating DAG for epoch {Epoch} after {DateTime.Now - started}"); + } + // Light Cache + else + { + logger.Debug(() => $"Generating cache for epoch {Epoch}"); + + handle = EthHash.ethash_light_new(block); + + logger.Debug(() => $"Done generating cache for epoch {Epoch} after {DateTime.Now - started}"); + } + + isGenerated = true; + } + } + }, 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(); + + // Full DAG + if(!string.IsNullOrEmpty(dagDir)) + { + fixed (byte* input = hash) + { + EthHash.ethash_full_compute(handle, input, nonce, ref value); + } + } + // Light Cache + else + { + 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; + } +} diff --git a/src/Miningcore/Crypto/Hashing/Ubqhash/UbqhashFull.cs b/src/Miningcore/Crypto/Hashing/Ethash/Ethash/EthashLight.cs similarity index 54% rename from src/Miningcore/Crypto/Hashing/Ubqhash/UbqhashFull.cs rename to src/Miningcore/Crypto/Hashing/Ethash/Ethash/EthashLight.cs index dbbb62bc36..0247985983 100644 --- a/src/Miningcore/Crypto/Hashing/Ubqhash/UbqhashFull.cs +++ b/src/Miningcore/Crypto/Hashing/Ethash/Ethash/EthashLight.cs @@ -1,24 +1,26 @@ +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 totalCache, ulong hardForkBlock, string dagDir = null) { - Contract.Requires(!string.IsNullOrEmpty(dagDir)); - - this.numCaches = numCaches; + this.numCaches = totalCache; 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; + private string dagDir; + public string AlgoName { get; } = "Ethash"; public void Dispose() { @@ -26,10 +28,10 @@ public void Dispose() value.Dispose(); } - public async Task GetDagAsync(ulong block, ILogger logger, CancellationToken ct) + public async Task GetCacheAsync(ILogger logger, ulong block, CancellationToken ct) { var epoch = block / EthereumConstants.EpochLength; - DagUbqhash result; + Cache result; lock(cacheLock) { @@ -38,22 +40,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 +63,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, dagDir); } caches[epoch] = result; @@ -71,11 +73,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, dagDir); #pragma warning disable 4014 - future.GenerateAsync(dagDir, logger, ct); + future.GenerateAsync(logger, ct); #pragma warning restore 4014 } @@ -83,7 +85,7 @@ public async Task GetDagAsync(ulong block, ILogger logger, Cancellat } // get/generate current one - await result.GenerateAsync(dagDir, logger, ct); + await result.GenerateAsync(logger, ct); 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 0000000000..6bd92a6e0d --- /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; + } +} 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 0000000000..2e8a331650 --- /dev/null +++ b/src/Miningcore/Crypto/Hashing/Ethash/Ubqhash/Cache.cs @@ -0,0 +1,166 @@ +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.Ubqhash; + +[Identifier("ubqhash")] +public class Cache : IEthashCache +{ + public Cache(ulong epoch, string dagDir = null) + { + Epoch = epoch; + this.dagDir = dagDir; + 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; } + private string dagDir; + 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) + { + // Full DAG + if(!string.IsNullOrEmpty(dagDir)) + { + UbqHash.ethash_full_delete(handle); + } + // Light Cache + else + { + UbqHash.ethash_light_delete(handle); + } + + handle = IntPtr.Zero; + } + } + + public async Task GenerateAsync(ILogger logger, CancellationToken ct) + { + if(handle == IntPtr.Zero) + { + await Task.Run(() => + { + lock(genLock) + { + if(!isGenerated) + { + // re-check after obtaining lock + if(handle != IntPtr.Zero) + return; + + var started = DateTime.Now; + var block = Epoch * EthereumConstants.EpochLength; + + // Full DAG + if(!string.IsNullOrEmpty(dagDir)) + { + logger.Debug(() => $"Generating DAG for epoch {Epoch}"); + logger.Debug(() => $"Epoch length used: {EthereumConstants.EpochLength}"); + + // Generate a temporary cache + var light = UbqHash.ethash_light_new(block); + + // 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}"); + } + // Light Cache + else + { + logger.Debug(() => $"Generating cache for epoch {Epoch}"); + + handle = UbqHash.ethash_light_new(block); + + logger.Debug(() => $"Done generating cache for epoch {Epoch} after {DateTime.Now - started}"); + } + + isGenerated = true; + } + } + }, 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(); + + // Full DAG + if(!string.IsNullOrEmpty(dagDir)) + { + fixed (byte* input = hash) + { + UbqHash.ethash_full_compute(handle, input, nonce, ref value); + } + } + // Light Cache + else + { + 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; + } +} diff --git a/src/Miningcore/Crypto/Hashing/Ethash/EthashFull.cs b/src/Miningcore/Crypto/Hashing/Ethash/Ubqhash/UbqhashLight.cs similarity index 54% rename from src/Miningcore/Crypto/Hashing/Ethash/EthashFull.cs rename to src/Miningcore/Crypto/Hashing/Ethash/Ubqhash/UbqhashLight.cs index a625010a57..fbf79953ec 100644 --- a/src/Miningcore/Crypto/Hashing/Ethash/EthashFull.cs +++ b/src/Miningcore/Crypto/Hashing/Ethash/Ubqhash/UbqhashLight.cs @@ -1,24 +1,26 @@ +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 totalCache, ulong hardForkBlock, string dagDir = null) { - Contract.Requires(!string.IsNullOrEmpty(dagDir)); - - this.numCaches = numCaches; + this.numCaches = totalCache; 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; + private string dagDir; + public string AlgoName { get; } = "Ubqhash"; public void Dispose() { @@ -26,10 +28,10 @@ public void Dispose() value.Dispose(); } - public async Task GetDagAsync(ulong block, ILogger logger, CancellationToken ct) + public async Task GetCacheAsync(ILogger logger, ulong block, CancellationToken ct) { var epoch = block / EthereumConstants.EpochLength; - Dag result; + Cache result; lock(cacheLock) { @@ -38,22 +40,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 +63,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, dagDir); } caches[epoch] = result; @@ -71,11 +73,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, dagDir); #pragma warning disable 4014 - future.GenerateAsync(dagDir, logger, ct); + future.GenerateAsync(logger, ct); #pragma warning restore 4014 } @@ -83,7 +85,7 @@ public async Task GetDagAsync(ulong block, ILogger logger, CancellationToke } // get/generate current one - await result.GenerateAsync(dagDir, logger, ct); + await result.GenerateAsync(logger, ct); 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 9902f7cabe..0000000000 --- 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 3dd80fd5fa..209e55eb61 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 42bc7761da..e13011bc74 100644 --- a/src/Miningcore/Program.cs +++ b/src/Miningcore/Program.cs @@ -27,7 +27,9 @@ using Miningcore.Configuration; using Miningcore.Crypto.Hashing.Algorithms; using Miningcore.Crypto.Hashing.Equihash; -using Miningcore.Crypto.Hashing.Ethash; +using Miningcore.Crypto.Hashing.Ethash.Etchash; +using Miningcore.Crypto.Hashing.Ethash.Ethash; +using Miningcore.Crypto.Hashing.Ethash.Ubqhash; using Miningcore.Extensions; using Miningcore.Messaging; using Miningcore.Mining; @@ -782,7 +784,13 @@ private static async Task PreFlightChecks(IServiceProvider services) EquihashSolver.MaxThreads = clusterConfig.EquihashMaxThreads ?? 1; // Configure Ethhash - Dag.messageBus = messageBus; + 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; diff --git a/src/Miningcore/coins.json b/src/Miningcore/coins.json index 3d5dac9dd3..6ceb362814 100644 --- a/src/Miningcore/coins.json +++ b/src/Miningcore/coins.json @@ -4623,6 +4623,7 @@ "canonicalName": "Callisto-Network", "symbol": "CLO", "family": "ethereum", + "ethasher": "ethash", "website": "", "market": "", "twitter": "", @@ -4640,6 +4641,7 @@ "canonicalName": "Ethereum", "symbol": "ETH", "family": "ethereum", + "ethasher": "ethash", "website": "https://ethereum.org/", "market": "", "twitter": "https://twitter.com/ethdotorg", @@ -4657,6 +4659,7 @@ "canonicalName": "Ethereum Classic", "symbol": "ETC", "family": "ethereum", + "ethasher": "etchash", "website": "https://ethereumclassic.org/", "market": "", "twitter": "https://twitter.com/eth_classic", @@ -4674,6 +4677,7 @@ "canonicalName": "EthereumPoW", "symbol": "ETHW", "family": "ethereum", + "ethasher": "ethash", "website": "https://ethereumpow.org/", "market": "", "twitter": "https://twitter.com/EthereumPoW", @@ -4691,6 +4695,7 @@ "canonicalName": "EtherOne", "symbol": "ETHONE", "family": "ethereum", + "ethasher": "ethash", "website": "https://etherone.one/", "market": "", "twitter": "https://twitter.com/EtherOneChain", @@ -4708,6 +4713,7 @@ "canonicalName": "PinkChain", "symbol": "PINK", "family": "ethereum", + "ethasher": "ethash", "website": "https://pinkchain.net/", "market": "", "twitter": "https://twitter.com/PinkchainNet", @@ -4725,6 +4731,7 @@ "canonicalName": "Ubiq", "symbol": "UBIQ", "family": "ethereum", + "ethasher": "ubqhash", "website": "https://ubiqsmart.com/", "market": "", "twitter": "https://twitter.com/ubiqsmart", @@ -4742,6 +4749,7 @@ "canonicalName": "JIB-Chain", "symbol": "JBC", "family": "ethereum", + "ethasher": "ethash", "website": "https://jibchain.net/", "market": "", "twitter": "", @@ -4759,6 +4767,7 @@ "canonicalName": "AltcoinChain", "symbol": "ALT", "family": "ethereum", + "ethasher": "ethash", "website": "https://www.altcoinchain.org/", "market": "", "twitter": "https://twitter.com/chainAltcoin", @@ -4776,6 +4785,7 @@ "canonicalName": "Proof-of-Memmes", "symbol": "POM", "family": "ethereum", + "ethasher": "ethash", "website": "https://www.proofofmemes.org/", "market": "https://www.coingecko.com/en/coins/proof-of-memes-pomchain", "twitter": "https://twitter.com/Proof_Of_Memes", @@ -4793,6 +4803,7 @@ "canonicalName": "MaxxChain", "symbol": "PWR", "family": "ethereum", + "ethasher": "ethash", "website": "https://www.maxxchain.org/", "market": "", "twitter": "https://twitter.com/Maxx_Chain", @@ -4810,6 +4821,7 @@ "canonicalName": "Canxium", "symbol": "CAU", "family": "ethereum", + "ethasher": "ethash", "website": "https://canxium.org/", "market": "https://coinpaprika.com/coin/cau-canxium", "twitter": "https://twitter.com/canxiumchain", diff --git a/src/Native/libetchash/ethash.h b/src/Native/libetchash/ethash.h index 875f5e8914..ae8c7cbf5b 100644 --- a/src/Native/libetchash/ethash.h +++ b/src/Native/libetchash/ethash.h @@ -33,7 +33,7 @@ #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 11700000 // classic mainnet //#define ETCHASH_FORK_BLOCK 2520000 // mordor #define ETHASH_MIX_BYTES 128 #define ETHASH_HASH_BYTES 64 @@ -76,10 +76,11 @@ typedef struct ethash_return_value { * Allocate and initialize a new ethash_light handler * * @param block_number The block number for which to create the handler + * @param fork_block The block number when ETH forked ETC (Useful for mordor) * @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); +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 @@ -91,18 +92,21 @@ void ethash_light_delete(ethash_light_t light); * @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 + * @param fork_block The block number when ETH forked ETC (Useful for mordor) * @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 nonce, + uint64_t const fork_block ); /** * Allocate and initialize a new ethash_full handler * * @param light The light handler containing the cache. + * @param fork_block The block number when ETH forked ETC (Useful for mordor) * @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. @@ -113,7 +117,7 @@ ethash_return_value_t ethash_light_compute( * @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); +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 diff --git a/src/Native/libetchash/exports.cpp b/src/Native/libetchash/exports.cpp index 506cb149c9..cfe217fa8f 100644 --- a/src/Native/libetchash/exports.cpp +++ b/src/Native/libetchash/exports.cpp @@ -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); } diff --git a/src/Native/libetchash/internal.c b/src/Native/libetchash/internal.c index 3ab113d478..c2ee634a37 100644 --- a/src/Native/libetchash/internal.c +++ b/src/Native/libetchash/internal.c @@ -45,14 +45,14 @@ #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) @@ -320,11 +320,11 @@ 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; } @@ -337,9 +337,9 @@ void ethash_light_delete(ethash_light_t light) 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; + uint64_t epochLen = block_number >= fork_block ? ETHASH_EPOCH_LENGTH_NEW : ETHASH_EPOCH_LENGTH; return block_number / epochLen; } @@ -361,10 +361,11 @@ 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); } @@ -463,13 +464,13 @@ 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)) { 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); } diff --git a/src/Native/libetchash/internal.h b/src/Native/libetchash/internal.h index 7d630d272d..c161c1e3b6 100644 --- a/src/Native/libetchash/internal.h +++ b/src/Native/libetchash/internal.h @@ -153,9 +153,9 @@ void ethash_quick_hash( 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); +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 @@ -175,4 +175,4 @@ bool ethash_compute_full_data( #ifdef __cplusplus } -#endif +#endif \ No newline at end of file