public async Task StartAsync(CancellationToken cancel) { while (true) { try { if (cancel.IsCancellationRequested) { return; } switch (Phase) { case TumblerPhase.InputRegistration: { await Global.InitializeConfigAsync(); await Global.InitializeUtxoRefereeAsync(); RoundId++; Console.WriteLine($"New Round: {RoundId}"); Alices = new ConcurrentHashSet <Alice>(); BlindedOutputs = new ConcurrentHashSet <string>(); await Global.StateMachine.BroadcastPeerRegisteredAsync(); Bobs = new ConcurrentHashSet <Bob>(); await SetDenominationAsync(cancel); await SetFeesAsync(cancel); CalculateAnonymitySet(); await BroadcastPhaseChangeAsync(); InputRegistrationStopwatch.Restart(); using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancel, _ctsPhaseCancel.Token)) { while (Alices.Count < Global.Config.MinimumAnonymitySet) { var timeoutTask = Task.Delay(TimeSpan.FromSeconds((int)Global.Config.InputRegistrationPhaseTimeoutInSeconds), cts.Token); await timeoutTask.ContinueWith(t => { }); if (timeoutTask.Status == TaskStatus.RanToCompletion) { AnonymitySet = Math.Max((int)Global.Config.MinimumAnonymitySet, Alices.Count); } } } InputRegistrationStopwatch.Stop(); if (FallBackRound == false) { TimeSpentInInputRegistration = InputRegistrationStopwatch.Elapsed; } UpdatePhase(TumblerPhase.ConnectionConfirmation); break; } case TumblerPhase.ConnectionConfirmation: { RoundHash = NBitcoinHelpers.HashOutpoints(Alices.SelectMany(x => x.Inputs).Select(x => x.OutPoint)); await BroadcastPhaseChangeAsync(); using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancel, _ctsPhaseCancel.Token)) { await Task.Delay(TimeSpan.FromSeconds((int)Global.Config.ConnectionConfirmationPhaseTimeoutInSeconds), cts.Token).ContinueWith(t => { }); } if (Alices.All(x => x.State == AliceState.ConnectionConfirmed)) { UpdatePhase(TumblerPhase.OutputRegistration); } else { FallBackRound = true; UpdatePhase(TumblerPhase.InputRegistration); } break; } case TumblerPhase.OutputRegistration: { await BroadcastPhaseChangeAsync(); using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancel, _ctsPhaseCancel.Token)) { await Task.Delay(TimeSpan.FromSeconds((int)Global.Config.OutputRegistrationPhaseTimeoutInSeconds), cts.Token).ContinueWith(t => { }); } // Output registration never falls back // We don't know which Alice to ban // Therefore proceed to signing, and whichever Alice doesn't sign ban UpdatePhase(TumblerPhase.Signing); break; } case TumblerPhase.Signing: { BuildCoinJoin(); await BroadcastPhaseChangeAsync(); using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancel, _ctsPhaseCancel.Token)) { await Task.Delay(TimeSpan.FromSeconds((int)Global.Config.SigningPhaseTimeoutInSeconds), cts.Token).ContinueWith(t => { }); } if (!FullySignedCoinJoin) { FallBackRound = true; foreach (var alice in Alices) { if (alice.State != AliceState.SignedCoinJoin) { await Global.UtxoReferee.BanAliceAsync(alice); } } } UpdatePhase(TumblerPhase.InputRegistration); CoinJoin = null; break; } default: { throw new NotSupportedException("This should never happen"); } } } catch (Exception ex) { FallBackRound = true; UpdatePhase(TumblerPhase.InputRegistration); Console.WriteLine($"Fallback to InputRegistration, Reason: {nameof(TumblerStateMachine)} exception: {ex}"); } } }