public async Task SsAsync() { var rpc = new MockRpcClient(); var roundConfig = new CoordinatorRoundConfig(); var utxoReferee = new UtxoReferee(Network.Main, "./", rpc, roundConfig); var confirmationTarget = 12; var round = new CoordinatorRound(rpc, utxoReferee, roundConfig, adjustedConfirmationTarget: confirmationTarget, confirmationTarget, roundConfig.ConfirmationTargetReductionRate, TimeSpan.FromSeconds(roundConfig.InputRegistrationTimeout));
public CcjRound(RPCClient rpc, UtxoReferee utxoReferee, CcjRoundConfig config) { try { Interlocked.Increment(ref RoundCount); RoundId = Interlocked.Read(ref RoundCount); RpcClient = Guard.NotNull(nameof(rpc), rpc); UtxoReferee = Guard.NotNull(nameof(utxoReferee), utxoReferee); Guard.NotNull(nameof(config), config); ConfirmationTarget = (int)config.ConfirmationTarget; CoordinatorFeePercent = (decimal)config.CoordinatorFeePercent; AnonymitySet = (int)config.AnonymitySet; InputRegistrationTimeout = TimeSpan.FromSeconds((long)config.InputRegistrationTimeout); ConnectionConfirmationTimeout = TimeSpan.FromSeconds((long)config.ConnectionConfirmationTimeout); OutputRegistrationTimeout = TimeSpan.FromSeconds((long)config.OutputRegistrationTimeout); SigningTimeout = TimeSpan.FromSeconds((long)config.SigningTimeout); PhaseLock = new object(); Phase = CcjRoundPhase.InputRegistration; StatusLock = new object(); Status = CcjRoundStatus.NotStarted; RegisteredUnblindedSignatures = new List <UnblindedSignature>(); RegisteredUnblindedSignaturesLock = new object(); MixingLevels = new MixingLevels(config.Denomination, new Signer(new Key())); for (int i = 0; i < config.MaximumMixingLevelCount - 1; i++) { MixingLevels.AddNewLevel(); } _unsignedCoinJoinHex = null; UnsignedCoinJoin = null; SignedCoinJoin = null; Alices = new List <Alice>(); Bobs = new List <Bob>(); Logger.LogInfo <CcjRound>($"New round ({RoundId}) is created.\n\t" + $"BaseDenomination: {MixingLevels.GetBaseDenomination().ToString(false, true)} BTC.\n\t" + $"{nameof(ConfirmationTarget)}: {ConfirmationTarget}.\n\t" + $"{nameof(CoordinatorFeePercent)}: {CoordinatorFeePercent}%.\n\t" + $"{nameof(AnonymitySet)}: {AnonymitySet}."); } catch (Exception ex) { Logger.LogError <CcjRound>($"Round ({RoundId}): Could not create."); Logger.LogError <CcjRound>(ex); throw; } }
public async Task TryOptimizeFeesTestAsync() { var rpc = new MockRpcClient(); rpc.Network = Network.Main; rpc.OnEstimateSmartFeeAsync = (confTarget, estMode) => Task.FromResult(new EstimateSmartFeeResponse { Blocks = 1, FeeRate = new FeeRate(10m) }); var roundConfig = new CoordinatorRoundConfig(); var utxoReferee = new UtxoReferee(Network.Main, "./", rpc, roundConfig); var confirmationTarget = 12; var round = new CoordinatorRound(rpc, utxoReferee, roundConfig, adjustedConfirmationTarget: confirmationTarget, confirmationTarget, roundConfig.ConfirmationTargetReductionRate, TimeSpan.FromSeconds(roundConfig.InputRegistrationTimeout));
public CcjRound(RPCClient rpc, UtxoReferee utxoReferee, CcjRoundConfig config) { try { Interlocked.Increment(ref RoundCount); RoundId = Interlocked.Read(ref RoundCount); RpcClient = Guard.NotNull(nameof(rpc), rpc); UtxoReferee = Guard.NotNull(nameof(utxoReferee), utxoReferee); Guard.NotNull(nameof(config), config); Denomination = config.Denomination; ConfirmationTarget = (int)config.ConfirmationTarget; CoordinatorFeePercent = (decimal)config.CoordinatorFeePercent; AnonymitySet = (int)config.AnonymitySet; InputRegistrationTimeout = TimeSpan.FromSeconds((long)config.InputRegistrationTimeout); ConnectionConfirmationTimeout = TimeSpan.FromSeconds((long)config.ConnectionConfirmationTimeout); OutputRegistrationTimeout = TimeSpan.FromSeconds((long)config.OutputRegistrationTimeout); SigningTimeout = TimeSpan.FromSeconds((long)config.SigningTimeout); PhaseLock = new object(); Phase = CcjRoundPhase.InputRegistration; StatusLock = new object(); Status = CcjRoundStatus.NotStarted; RoundHash = null; _unsignedCoinJoinHex = null; UnsignedCoinJoin = null; SignedCoinJoin = null; Alices = new List <Alice>(); Bobs = new List <Bob>(); Logger.LogInfo <CcjRound>($"New round ({RoundId}) is created.\n\t" + $"{nameof(Denomination)}: {Denomination.ToString(false, true)} BTC.\n\t" + $"{nameof(ConfirmationTarget)}: {ConfirmationTarget}.\n\t" + $"{nameof(CoordinatorFeePercent)}: {CoordinatorFeePercent}.\n\t" + $"{nameof(AnonymitySet)}: {AnonymitySet}."); } catch (Exception ex) { Logger.LogError <CcjRound>($"Round ({RoundId}) creation failed."); Logger.LogError <CcjRound>(ex); throw; } }
public static async Task InitializeUtxoRefereeAsync() { UtxoRefereeJobCancel?.Cancel(); if (UtxoRefereeJob != null) { await UtxoRefereeJob; UtxoRefereeJobCancel?.Dispose(); } UtxoRefereePath = Path.Combine(DataDir, "BannedUtxos.json"); if (File.Exists(UtxoRefereePath)) { UtxoReferee = await UtxoReferee.CreateFromFileAsync(UtxoRefereePath); } else { UtxoReferee = new UtxoReferee(); } UtxoRefereeJobCancel = new CancellationTokenSource(); UtxoRefereeJob = UtxoReferee.StartAsync(UtxoRefereeJobCancel.Token); }
private volatile bool _disposedValue = false; // To detect redundant calls public Coordinator(Network network, BlockNotifier blockNotifier, string folderPath, IRPCClient rpc, CoordinatorRoundConfig roundConfig) { Network = Guard.NotNull(nameof(network), network); BlockNotifier = Guard.NotNull(nameof(blockNotifier), blockNotifier); FolderPath = Guard.NotNullOrEmptyOrWhitespace(nameof(folderPath), folderPath, trim: true); RpcClient = Guard.NotNull(nameof(rpc), rpc); RoundConfig = Guard.NotNull(nameof(roundConfig), roundConfig); Rounds = ImmutableList <CoordinatorRound> .Empty; LastSuccessfulCoinJoinTime = DateTimeOffset.UtcNow; Directory.CreateDirectory(FolderPath); UtxoReferee = new UtxoReferee(Network, FolderPath, RpcClient, RoundConfig); if (File.Exists(CoinJoinsFilePath)) { try { var getTxTasks = new List <(Task <Transaction> txTask, string line)>(); var batch = RpcClient.PrepareBatch(); var toRemove = new List <string>(); string[] allLines = File.ReadAllLines(CoinJoinsFilePath); foreach (string line in allLines) { try { getTxTasks.Add((batch.GetRawTransactionAsync(uint256.Parse(line)), line)); } catch (Exception ex) { toRemove.Add(line); var logEntry = ex is RPCException rpce && rpce.RPCCode == RPCErrorCode.RPC_INVALID_ADDRESS_OR_KEY ? $"CoinJoins file contains invalid transaction ID {line}" : $"CoinJoins file got corrupted. Deleting offending line \"{line[..20]}\".";
public void UtxoRefereeSerialization() { var record = UtxoReferee.BannedRecordFromLine("2018-11-23 15-23-14:1:44:2716e680f47d74c1bc6f031da22331564dd4c6641d7216576aad1b846c85d492:True:195"); Assert.Equal(new DateTimeOffset(2018, 11, 23, 15, 23, 14, TimeSpan.Zero), record.timeOfBan); Assert.Equal(1, record.severity); Assert.Equal(44u, record.utxo.N); Assert.Equal(new uint256("2716e680f47d74c1bc6f031da22331564dd4c6641d7216576aad1b846c85d492"), record.utxo.Hash); Assert.True(record.isNoted); Assert.Equal(195, record.bannedForRound); DateTimeOffset dateTime = DateTimeOffset.UtcNow; DateTimeOffset now = new DateTimeOffset(dateTime.Ticks - (dateTime.Ticks % TimeSpan.TicksPerSecond), TimeSpan.Zero); string record2Line = UtxoReferee.BannedRecordToLine(record.utxo, 3, now, false, 99); var record2 = UtxoReferee.BannedRecordFromLine(record2Line); Assert.Equal(now, record2.timeOfBan); Assert.Equal(3, record2.severity); Assert.Equal(44u, record2.utxo.N); Assert.Equal(new uint256("2716e680f47d74c1bc6f031da22331564dd4c6641d7216576aad1b846c85d492"), record2.utxo.Hash); Assert.False(record2.isNoted); Assert.Equal(99, record2.bannedForRound); }
private volatile bool _disposedValue = false; // To detect redundant calls public Coordinator(Network network, BlockNotifier blockNotifier, string folderPath, IRPCClient rpc, CoordinatorRoundConfig roundConfig) { Network = Guard.NotNull(nameof(network), network); BlockNotifier = Guard.NotNull(nameof(blockNotifier), blockNotifier); FolderPath = Guard.NotNullOrEmptyOrWhitespace(nameof(folderPath), folderPath, trim: true); RpcClient = Guard.NotNull(nameof(rpc), rpc); RoundConfig = Guard.NotNull(nameof(roundConfig), roundConfig); Rounds = new List <CoordinatorRound>(); RoundsListLock = new AsyncLock(); CoinJoins = new List <uint256>(); UnconfirmedCoinJoins = new List <uint256>(); CoinJoinsLock = new AsyncLock(); LastSuccessfulCoinJoinTime = DateTimeOffset.UtcNow; Directory.CreateDirectory(FolderPath); UtxoReferee = new UtxoReferee(Network, FolderPath, RpcClient, RoundConfig); if (File.Exists(CoinJoinsFilePath)) { try { var getTxTasks = new List <(Task <Transaction> txTask, string line)>(); var batch = RpcClient.PrepareBatch(); var toRemove = new List <string>(); string[] allLines = File.ReadAllLines(CoinJoinsFilePath); foreach (string line in allLines) { try { getTxTasks.Add((batch.GetRawTransactionAsync(uint256.Parse(line)), line)); } catch (Exception ex) { toRemove.Add(line); var logEntry = ex is RPCException rpce && rpce.RPCCode == RPCErrorCode.RPC_INVALID_ADDRESS_OR_KEY ? $"CoinJoins file contains invalid transaction ID {line}" : $"CoinJoins file got corrupted. Deleting offending line \"{line.Substring(0, 20)}\"."; Logger.LogWarning($"{logEntry}. {ex.GetType()}: {ex.Message}"); } } batch.SendBatchAsync().GetAwaiter().GetResult(); foreach (var(txTask, line) in getTxTasks) { try { var tx = txTask.GetAwaiter().GetResult(); CoinJoins.Add(tx.GetHash()); } catch (Exception ex) { toRemove.Add(line); var logEntry = ex is RPCException rpce && rpce.RPCCode == RPCErrorCode.RPC_INVALID_ADDRESS_OR_KEY ? $"CoinJoins file contains invalid transaction ID {line}" : $"CoinJoins file got corrupted. Deleting offending line \"{line.Substring(0, 20)}\"."; Logger.LogWarning($"{logEntry}. {ex.GetType()}: {ex.Message}"); } } if (toRemove.Count != 0) // a little performance boost, it'll be empty almost always { var newAllLines = allLines.Where(x => !toRemove.Contains(x)); File.WriteAllLines(CoinJoinsFilePath, newAllLines); } } catch (Exception ex) { Logger.LogWarning($"CoinJoins file got corrupted. Deleting {CoinJoinsFilePath}. {ex.GetType()}: {ex.Message}"); File.Delete(CoinJoinsFilePath); } uint256[] mempoolHashes = RpcClient.GetRawMempoolAsync().GetAwaiter().GetResult(); UnconfirmedCoinJoins.AddRange(CoinJoins.Intersect(mempoolHashes)); } try { string roundCountFilePath = Path.Combine(folderPath, "RoundCount.txt"); if (File.Exists(roundCountFilePath)) { string roundCount = File.ReadAllText(roundCountFilePath); CoordinatorRound.RoundCount = long.Parse(roundCount); } else { // First time initializes (so the first constructor will increment it and we'll start from 1.) CoordinatorRound.RoundCount = 0; } } catch (Exception ex) { CoordinatorRound.RoundCount = 0; Logger.LogInfo($"{nameof(CoordinatorRound.RoundCount)} file was corrupt. Resetting to 0."); Logger.LogDebug(ex); } BlockNotifier.OnBlock += BlockNotifier_OnBlockAsync; }