protected override void StartCore(CancellationToken cancellationToken) { IsTumbling = true; new Thread(() => { Logs.Client.LogInformation("State machines started"); uint256 lastBlock = uint256.Zero; int lastCycle = 0; while (true) { try { lastBlock = Runtime.Services.BlockExplorerService.WaitBlock(lastBlock, cancellationToken); var height = Runtime.Services.BlockExplorerService.GetCurrentHeight(); Logs.Client.LogInformation("New Block: " + height); var cycle = Runtime.TumblerParameters.CycleGenerator.GetRegisteringCycle(height); if (lastCycle != cycle.Start) { // Only start a new cycle if there are sufficient wallet funds bool firstCycle = ManagedCycles.All(c => c.IsPostRegistration(height)); if (Runtime.HasEnoughFundsForCycle(firstCycle)) { lastCycle = cycle.Start; ManagedCycles.Add(cycle); Logs.Client.LogInformation("New Cycle: {0}", cycle.Start); PaymentStateMachine.State state = GetPaymentStateMachineState(cycle); if (state == null) { var stateMachine = new PaymentStateMachine(Runtime, null); stateMachine.NeedSave = true; Save(stateMachine, cycle.Start); } } else { Logs.Client.LogInformation("There is no enough funds to sustain another tumbling cycle."); } } var progressInfo = new ProgressInfo(height, Runtime.DataDir); var cycles = Runtime.TumblerParameters.CycleGenerator.GetCycles(height); var machineStates = cycles .SelectMany(c => Runtime.Repository.List <PaymentStateMachine.State>(GetPartitionKey(c.Start))) .Where(m => m.TumblerParametersHash == _ParametersHash) .ToArray(); NBitcoin.Utils.Shuffle(machineStates); bool hadInvalidPhase = false; if (Runtime.Network != Network.RegTest) { //Waiting for the block to propagate to server so invalid-phase happens less often //This also make the server less overwhelmed by sudden request peak. var waitRandom = TimeSpan.FromSeconds(RandomUtils.GetUInt32() % 120 + 10); Logs.Client.LogDebug("Waiting {0} seconds before updating machine states...", (int)waitRandom.TotalSeconds); cancellationToken.WaitHandle.WaitOne(waitRandom); cancellationToken.ThrowIfCancellationRequested(); } else { // Need to ensure that the rest of the processing only happens after the server // has definitely recognised that a block has been received. Invalid phase // errors will result otherwise. Logs.Client.LogDebug("Waiting 2 seconds before updating machine states..."); cancellationToken.WaitHandle.WaitOne(2); cancellationToken.ThrowIfCancellationRequested(); } foreach (var state in machineStates) { var machine = new PaymentStateMachine(Runtime, state); if (machine.Status == PaymentStateMachineStatus.Wasted) { Logs.Client.LogDebug($"Skipping cycle {machine.StartCycle}, because it is wasted"); continue; } var statusBefore = machine.GetInternalState(); try { var cycleProgressInfo = machine.Update(); InvalidPhaseCount = 0; if (cycleProgressInfo != null) { progressInfo.CycleProgressInfoList.Add(cycleProgressInfo); } } catch (PrematureRequestException) { Logs.Client.LogInformation("Skipping update, need to wait for tor circuit renewal for cycle {0} ({1})", machine.StartCycle, machine.Status); break; } catch (Exception ex) when(IsInvalidPhase(ex)) { if (!hadInvalidPhase) { hadInvalidPhase = true; InvalidPhaseCount++; if (InvalidPhaseCount > 2) { Logs.Client.LogError(new EventId(), ex, "Invalid-Phase happened repeatedly, check that your node currently " + "at height {0} is currently sync to the network; cycle {1} ({2})", height, machine.StartCycle, machine.Status); } } } catch (Exception ex) { Logs.Client.LogError(new EventId(), ex, "Unhandled StateMachine Error for cycle {0} ({1})", machine.StartCycle, machine.Status); } Save(machine); } Logs.Client.LogDebug("Number of running cycles reported to the progress API: {0}", progressInfo.CycleProgressInfoList.Count); int numberOfCompletedCycles = ManagedCycles.Count(c => c.IsComplete(height)); bool allCyclesAreComplete = !ManagedCycles.Any() || (numberOfCompletedCycles == ManagedCycles.Count); if (allCyclesAreComplete) { Logs.Client.LogInformation("Tumbling finished; all {0} tumbling cycles have been completed", ManagedCycles.Count); Stop(); break; } else { Logs.Client.LogDebug("Tumbling running; there are {0} of {1} cycles completed", numberOfCompletedCycles, ManagedCycles.Count); } progressInfo.Save(); } catch (OperationCanceledException) when(cancellationToken.IsCancellationRequested) { Logs.Client.LogInformation("Tumbling cancelled; all {0} tumbling cycles have been aborted", ManagedCycles.Count); Stop(); break; } catch (Exception ex) { Logs.Client.LogError(new EventId(), ex, "StateMachineExecutor Error: " + ex.ToString()); cancellationToken.WaitHandle.WaitOne(5000); } } IsTumbling = false; }).Start(); }
protected override void StartCore(CancellationToken cancellationToken) { new Thread(() => { Logs.Client.LogInformation("State machines started"); uint256 lastBlock = uint256.Zero; int lastCycle = 0; while (true) { Exception unhandled = null; try { lastBlock = Runtime.Services.BlockExplorerService.WaitBlock(lastBlock, cancellationToken); var height = Runtime.Services.BlockExplorerService.GetCurrentHeight(); Logs.Client.LogInformation("New Block: " + height); var cycle = Runtime.TumblerParameters.CycleGenerator.GetRegistratingCycle(height); if (lastCycle != cycle.Start) { lastCycle = cycle.Start; Logs.Client.LogInformation("New Cycle: " + cycle.Start); var state = Runtime.Repository.Get <PaymentStateMachine.State>(GetPartitionKey(cycle.Start), ""); if (state == null) { var stateMachine = new PaymentStateMachine(Runtime, null); Save(stateMachine, cycle.Start); } } var cycles = Runtime.TumblerParameters.CycleGenerator.GetCycles(height); foreach (var state in cycles.SelectMany(c => Runtime.Repository.List <PaymentStateMachine.State>(GetPartitionKey(c.Start)))) { var machine = new PaymentStateMachine(Runtime, state); try { machine.Update(); machine.InvalidPhaseCount = 0; } catch (PrematureRequestException) { Logs.Client.LogInformation("Skipping update, need to wait for tor circuit renewal"); break; } catch (Exception ex) { var invalidPhase = ex.Message.IndexOf("invalid-phase", StringComparison.OrdinalIgnoreCase) >= 0; if (invalidPhase) { machine.InvalidPhaseCount++; } else { machine.InvalidPhaseCount = 0; } if (!invalidPhase || machine.InvalidPhaseCount > 2) { Logs.Client.LogError("StateMachine Error: " + ex.ToString()); } } Save(machine, machine.StartCycle); } } catch (OperationCanceledException ex) { if (cancellationToken.IsCancellationRequested) { Stopped(); break; } else { unhandled = ex; } } catch (Exception ex) { unhandled = ex; } if (unhandled != null) { Logs.Client.LogError("StateMachineExecutor Error: " + unhandled.ToString()); cancellationToken.WaitHandle.WaitOne(5000); } } }).Start(); }
protected override void StartCore(CancellationToken cancellationToken) { new Thread(() => { Logs.Client.LogInformation("State machines started"); uint256 lastBlock = uint256.Zero; int lastCycle = 0; while (true) { try { lastBlock = Runtime.Services.BlockExplorerService.WaitBlock(lastBlock, cancellationToken); var height = Runtime.Services.BlockExplorerService.GetCurrentHeight(); Logs.Client.LogInformation("New Block: " + height); var cycle = Runtime.TumblerParameters.CycleGenerator.GetRegisteringCycle(height); if (lastCycle != cycle.Start) { lastCycle = cycle.Start; Logs.Client.LogInformation("New Cycle: " + cycle.Start); PaymentStateMachine.State state = GetPaymentStateMachineState(cycle); if (state == null) { var stateMachine = new PaymentStateMachine(Runtime, null); stateMachine.NeedSave = true; Save(stateMachine, cycle.Start); } } var progressInfo = new ProgressInfo(height); var cycles = Runtime.TumblerParameters.CycleGenerator.GetCycles(height); var machineStates = cycles .SelectMany(c => Runtime.Repository.List <PaymentStateMachine.State>(GetPartitionKey(c.Start))) .Where(m => m.TumblerParametersHash == _ParametersHash) .ToArray(); NBitcoin.Utils.Shuffle(machineStates); bool hadInvalidPhase = false; if (Runtime.Network != Network.RegTest) { //Waiting for the block to propagate to server so invalid-phase happens less often //This also make the server less overwhelmed by sudden request peak var waitRandom = TimeSpan.FromSeconds(RandomUtils.GetUInt32() % 120 + 10); Logs.Client.LogDebug("Waiting " + (int)waitRandom.TotalSeconds + " seconds before updating machine states..."); cancellationToken.WaitHandle.WaitOne(waitRandom); cancellationToken.ThrowIfCancellationRequested(); } else { // Need to ensure that the rest of the processing only happens after the server // has definitely recognised that a block has been received. Invalid phase // errors will result otherwise Logs.Client.LogDebug("Waiting 2 seconds before updating machine states..."); cancellationToken.WaitHandle.WaitOne(2); cancellationToken.ThrowIfCancellationRequested(); } foreach (var state in machineStates) { var machine = new PaymentStateMachine(Runtime, state); if (machine.Status == PaymentStateMachineStatus.Wasted) { Logs.Client.LogDebug($"Skipping cycle {machine.StartCycle}, because if is wasted"); continue; } var statusBefore = machine.GetInternalState(); try { var cycleProgressInfo = machine.Update(); InvalidPhaseCount = 0; if (cycleProgressInfo != null) { progressInfo.CycleProgressInfoList.Add(cycleProgressInfo); } } catch (PrematureRequestException) { Logs.Client.LogInformation("Skipping update, need to wait for tor circuit renewal"); break; } catch (Exception ex) when(IsInvalidPhase(ex)) { if (!hadInvalidPhase) { hadInvalidPhase = true; InvalidPhaseCount++; if (InvalidPhaseCount > 2) { Logs.Client.LogError(new EventId(), ex, $"Invalid-Phase happened repeatedly, check that your node currently at height {height} is currently sync to the network"); } } } catch (Exception ex) { Logs.Client.LogError(new EventId(), ex, "Unhandled StateMachine Error"); } Save(machine); } progressInfo.Save(); } catch (OperationCanceledException) when(cancellationToken.IsCancellationRequested) { Stopped(); break; } catch (Exception ex) { Logs.Client.LogError(new EventId(), ex, "StateMachineExecutor Error: " + ex.ToString()); cancellationToken.WaitHandle.WaitOne(5000); } } }).Start(); }
protected override void StartCore(CancellationToken cancellationToken) { new Thread(() => { Logs.Client.LogInformation("State machines started"); uint256 lastBlock = uint256.Zero; int lastCycle = 0; while (true) { Exception unhandled = null; try { lastBlock = Runtime.Services.BlockExplorerService.WaitBlock(lastBlock, cancellationToken); var height = Runtime.Services.BlockExplorerService.GetCurrentHeight(); Logs.Client.LogInformation("New Block: " + height); var cycle = Runtime.TumblerParameters.CycleGenerator.GetRegistratingCycle(height); if (lastCycle != cycle.Start) { lastCycle = cycle.Start; Logs.Client.LogInformation("New Cycle: " + cycle.Start); PaymentStateMachine.State state = GetPaymentStateMachineState(cycle); if (state == null) { var stateMachine = new PaymentStateMachine(Runtime, null); Save(stateMachine, cycle.Start); } } var cycles = Runtime.TumblerParameters.CycleGenerator.GetCycles(height); var machineStates = cycles .SelectMany(c => Runtime.Repository.List <PaymentStateMachine.State>(GetPartitionKey(c.Start))) .Where(m => m.TumblerParametersHash == _ParametersHash) .ToArray(); NBitcoin.Utils.Shuffle(machineStates); bool hadInvalidPhase = false; //Waiting for the block to propagate to server so invalid-phase happens less often cancellationToken.WaitHandle.WaitOne(10000); cancellationToken.ThrowIfCancellationRequested(); foreach (var state in machineStates) { bool noSave = false; var machine = new PaymentStateMachine(Runtime, state); var statusBefore = machine.GetInternalState(); try { machine.Update(); InvalidPhaseCount = 0; } catch (PrematureRequestException) { Logs.Client.LogInformation("Skipping update, need to wait for tor circuit renewal"); break; } catch (Exception ex) { var invalidPhase = ex.Message.IndexOf("invalid-phase", StringComparison.OrdinalIgnoreCase) >= 0; if (invalidPhase) { if (!hadInvalidPhase) { hadInvalidPhase = true; InvalidPhaseCount++; if (InvalidPhaseCount > 2) { Logs.Client.LogError(new EventId(), ex, $"Invalid-Phase happened repeatedly, check that your node currently at height {height} is currently sync to the network"); } } noSave = true; } else { Logs.Client.LogError(new EventId(), ex, "Unhandled StateMachine Error"); } } if (!noSave) { Save(machine, machine.StartCycle); } } } catch (OperationCanceledException ex) { if (cancellationToken.IsCancellationRequested) { Stopped(); break; } else { unhandled = ex; } } catch (Exception ex) { unhandled = ex; } if (unhandled != null) { Logs.Client.LogError("StateMachineExecutor Error: " + unhandled.ToString()); cancellationToken.WaitHandle.WaitOne(5000); } } }).Start(); }