public static double?IdleUpdate(WorkerContextBase context, VarDiffConfig options, IMasterClock clock) { var ctx = context.VarDiff; var difficulty = context.Difficulty; // abort if a regular update is just happening if (!Monitor.TryEnter(ctx)) { return(null); } try { var now = clock.Now; var ts = now.ToUnixSeconds(); double timeDelta; if (ctx.LastTs.HasValue) { timeDelta = ts - ctx.LastTs.Value; } else { timeDelta = ts - ctx.Created.ToUnixSeconds(); } timeDelta += SafetyMargin; // we only get involved if there was never an update or the last update happened longer than retargetTime ago if (timeDelta < options.RetargetTime) { return(null); } // update the last time ctx.LastTs = ts; var minDiff = options.MinDiff; var maxDiff = options.MaxDiff ?? Math.Max(minDiff, double.MaxValue); // for regtest // Always calculate the time until now even there is no share submitted. var timeTotal = (ctx.TimeBuffer?.Sum() ?? 0) + (timeDelta - SafetyMargin); var avg = timeTotal / ((ctx.TimeBuffer?.Size ?? 0) + 1); // Possible New Diff var newDiff = difficulty * options.TargetTime / avg; if (TryApplyNewDiff(ref newDiff, difficulty, minDiff, maxDiff, ts, ctx, options, clock)) { return(newDiff); } } finally { Monitor.Exit(ctx); } return(null); }
protected override async Task <double?> GetNicehashStaticMinDiff(WorkerContextBase context, string coinName, string algoName) { var result = await base.GetNicehashStaticMinDiff(context, coinName, algoName); // adjust value to fit with our target value calculation if (result.HasValue) { result = result.Value / uint.MaxValue; } return(result); }
public void VarDiff_Should_Honor_MinDiff_When_Adjusting_Down() { var vdm = new VarDiffManager(config); var ctx = new WorkerContextBase { Difficulty = 1500, VarDiff = new VarDiffContext() }; var shares = new List <long> { 2000000000, 3000000000, 4000000000 }; var newDiff = vdm.Update(ctx, shares, string.Empty, logger); Assert.NotNull(newDiff); Assert.True(newDiff.Value.EqualsDigitPrecision3(1000)); }
public void VarDiff_Should_Honor_MaxDelta_When_Adjusting_Up() { var vdm = new VarDiffManager(config, clock); var ctx = new WorkerContextBase { Difficulty = 7500, VarDiff = new VarDiffContext() }; var shares = new List <long> { 2, 3, 4 }; var newDiff = vdm.Update(ctx, shares, string.Empty, logger); Assert.NotNull(newDiff); Assert.True(newDiff.Value.EqualsDigitPrecision3(8500)); }
public double?Update(WorkerContextBase ctx, IList <long> shares, string connectionId, ILogger logger) { Contract.RequiresNonNull(ctx, nameof(ctx)); logger.Debug(() => $"Update"); lock (ctx) { var difficulty = ctx.Difficulty; var minDiff = options.MinDiff; var maxDiff = options.MaxDiff ?? double.MaxValue; double?newDiff = null; if ((shares.Count > 0 && ctx.VarDiff.LastShareTs.HasValue) || shares.Count > 1) { // make timestamps relative to each other var tsRelative = new List <long>(); // first value is relative to last value of previous buffer if (ctx.VarDiff.LastShareTs.HasValue) { tsRelative.Add(Math.Max(0, shares[0] - ctx.VarDiff.LastShareTs.Value)); } for (var i = 1; i < shares.Count; i++) { tsRelative.Add(Math.Max(0, shares[i] - shares[i - 1])); } // take average var avg = tsRelative.Average() / 1000d; // re-target if outside bounds if (avg < tMin || avg > tMax) { var change = options.TargetTime / avg; newDiff = difficulty * change; // prevent huge jumps var delta = newDiff.Value - ctx.Difficulty; if (Math.Abs(delta) > maxJump) { newDiff = difficulty + (delta > 0 ? maxJump : -maxJump); } // round to next 100 if big enough if (newDiff > 1000) { newDiff = Math.Round(newDiff.Value / 100d, 0) * 100; } } // store ctx.VarDiff.LastShareTs = shares.Last(); ctx.VarDiff.SilenceCount = 0; } else { ctx.VarDiff.SilenceCount++; // radical measures if there were no shares submitted at all within the buffer window newDiff = difficulty / Math.Pow(2, ctx.VarDiff.SilenceCount); } if (newDiff.HasValue) { // clamp to min/max if (newDiff < minDiff) { newDiff = minDiff; } else if (newDiff > maxDiff) { newDiff = maxDiff; } // check if different if (!newDiff.Value.EqualsDigitPrecision3(ctx.Difficulty)) { ctx.VarDiff.LastUpdate = clock.UtcNow; } else { newDiff = null; } } return(newDiff); } }
private const double SafetyMargin = 1; // ensure we don't miss a cycle due a sub-second fraction delta; public static double?Update(WorkerContextBase context, VarDiffConfig options, IMasterClock clock) { var ctx = context.VarDiff; var difficulty = context.Difficulty; var now = clock.Now; var ts = now.ToUnixSeconds(); try { Monitor.Enter(ctx); if (ctx.LastTs.HasValue) { var minDiff = options.MinDiff; var maxDiff = options.MaxDiff ?? Math.Max(minDiff, double.MaxValue); // for regtest var timeDelta = ts - ctx.LastTs.Value; // make sure buffer exists as this point ctx.TimeBuffer ??= new CircularBuffer <double>(BufferSize); // Always calculate the time until now even there is no share submitted. var timeTotal = ctx.TimeBuffer.Sum() + timeDelta; var avg = timeTotal / (ctx.TimeBuffer.Size + 1); // Once there is a share submitted, store the time into the buffer and update the last time. ctx.TimeBuffer.PushBack(timeDelta); ctx.LastTs = ts; // Check if we need to change the difficulty var variance = options.TargetTime * (options.VariancePercent / 100.0); var tMin = options.TargetTime - variance; var tMax = options.TargetTime + variance; if (ts - ctx.LastRetarget < options.RetargetTime || avg >= tMin && avg <= tMax) { return(null); } // Possible New Diff var newDiff = difficulty * options.TargetTime / avg; if (TryApplyNewDiff(ref newDiff, difficulty, minDiff, maxDiff, ts, ctx, options, clock)) { return(newDiff); } } else { // init ctx.LastRetarget = ts; ctx.LastTs = ts; } } finally { Monitor.Exit(ctx); } return(null); }
public void SetContext <T>(T value) where T : WorkerContextBase { context = value; }