Beispiel #1
0
 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));
Beispiel #2
0
        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;
            }
        }
Beispiel #3
0
        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));
Beispiel #4
0
        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;
            }
        }
Beispiel #5
0
        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);
        }
Beispiel #6
0
        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]}\".";
Beispiel #7
0
        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;
        }