Exemplo n.º 1
0
        public bool VerifyCollateral(BreezeConfiguration config, out Money missingFunds)
        {
            Network network = Network.ImpleumMain;

            if (config.TumblerNetwork == Network.TestNet || config.TumblerNetwork == Network.RegTest)
            {
                network = Network.ImpleumTest;
            }

            var stratisHelper = new RPCHelper(network);
            var stratisRpc    = stratisHelper.GetClient(config.RpcUser, config.RpcPassword, config.RpcUrl);

            RPCResponse listAddressGroupings = stratisRpc.SendCommand("listaddressgroupings");
            var         t = listAddressGroupings.Result;

            decimal collateralBalance = (from p in t
                                         from e in p
                                         where e.First.Value <String>() == config.TumblerEcdsaKeyAddress
                                         select e.ElementAt(1).Value <decimal>()).First();

            missingFunds = RegistrationParameters.MASTERNODE_COLLATERAL_THRESHOLD - new Money(collateralBalance, MoneyUnit.BTC);
            if (missingFunds <= 0)
            {
                missingFunds = new Money(0m, MoneyUnit.BTC);
                return(true);
            }
            return(false);
        }
Exemplo n.º 2
0
        public Transaction PerformBreezeRegistration(BreezeConfiguration config, DBUtils db)
        {
            var network = Network.StratisMain;

            if (config.IsTestNet)
            {
                // TODO: Change to StratisTest when support is added to NStratis
                network = Network.TestNet;
            }

            RPCHelper     stratisHelper   = null;
            RPCClient     stratisRpc      = null;
            BitcoinSecret privateKeyEcdsa = null;

            try {
                stratisHelper   = new RPCHelper(network);
                stratisRpc      = stratisHelper.GetClient(config.RpcUser, config.RpcPassword, config.RpcUrl);
                privateKeyEcdsa = stratisRpc.DumpPrivKey(BitcoinAddress.Create(config.TumblerEcdsaKeyAddress));
            }
            catch (Exception e) {
                Console.WriteLine("ERROR: Unable to retrieve private key to fund registration transaction");
                Console.WriteLine("Is the wallet unlocked?");
                Console.WriteLine(e);
                Environment.Exit(0);
            }

            // Retrieve tumbler's parameters so that the registration details can be constructed
            //var tumblerApi = new TumblerApiAccess(config.TumblerApiBaseUrl);
            //string json = tumblerApi.GetParameters().Result;
            //var tumblerParameters = JsonConvert.DeserializeObject<TumblerParameters>(json);
            var registrationToken = new RegistrationToken(255, config.Ipv4Address, config.Ipv6Address, config.OnionAddress, config.Port, config.TumblerRsaKeyPath);
            var msgBytes          = registrationToken.GetRegistrationTokenBytes(privateKeyEcdsa);

            // Create the registration transaction using the bytes generated above
            var rawTx = CreateBreezeRegistrationTx(network, msgBytes, config.TxOutputValueSetting);

            var txUtils = new TransactionUtils();

            try {
                // Replace fundrawtransaction with C# implementation. The legacy wallet
                // software does not support the RPC call.
                var fundedTx = txUtils.FundRawTx(stratisRpc, rawTx, config.TxFeeValueSetting, BitcoinAddress.Create(config.TumblerEcdsaKeyAddress));
                var signedTx = stratisRpc.SendCommand("signrawtransaction", fundedTx.ToHex());
                var txToSend = new Transaction(((JObject)signedTx.Result)["hex"].Value <string>());

                db.UpdateOrInsert <string>("RegistrationTransactions", DateTime.Now.ToString("yyyyMMddHHmmss"), txToSend.ToHex(), (o, n) => n);
                stratisRpc.SendRawTransaction(txToSend);

                return(txToSend);
            }
            catch (Exception e)
            {
                Console.WriteLine("ERROR: Unable to broadcast registration transaction");
                Console.WriteLine(e);
            }

            return(null);
        }
Exemplo n.º 3
0
        public static void Main(string[] args)
        {
            var serviceProvider = new ServiceCollection()
                                  .AddLogging()
                                  .AddSingleton <ITumblerService, TumblerService>()
                                  .BuildServiceProvider();

            serviceProvider
            .GetService <ILoggerFactory>()
            .AddConsole(LogLevel.Debug);

            // TODO: It is messy having both a BreezeServer logger and an NTumbleBit logger
            var logger = serviceProvider.GetService <ILoggerFactory>()
                         .CreateLogger <Program>();

            logger.LogInformation("{Time} Reading Breeze server configuration", DateTime.Now);

            // Check OS-specific default config path for the config file. Create default file if it does not exist
            var configDir  = BreezeConfiguration.GetDefaultDataDir("BreezeServer");
            var configPath = Path.Combine(configDir, "breeze.conf");

            logger.LogInformation("{Time} Configuration file path {Path}", DateTime.Now, configPath);

            var config = new BreezeConfiguration(configPath);

            var dbPath = Path.Combine(configDir, "db");

            logger.LogInformation("{Time} Database path {Path}", DateTime.Now, dbPath);

            var db = new DBUtils(dbPath);

            logger.LogInformation("{Time} Checking node registration on the blockchain", DateTime.Now);

            var registration = new BreezeRegistration();

            if (!registration.CheckBreezeRegistration(config, db))
            {
                logger.LogInformation("{Time} Creating or updating node registration", DateTime.Now);
                var regTx = registration.PerformBreezeRegistration(config, db);
                if (regTx != null)
                {
                    logger.LogInformation("{Time} Submitted transaction {TxId} via RPC for broadcast", DateTime.Now, regTx.GetHash().ToString());
                }
                else
                {
                    logger.LogInformation("{Time} Unable to broadcast transaction via RPC", DateTime.Now);
                    Environment.Exit(0);
                }
            }
            else
            {
                logger.LogInformation("{Time} Node registration has already been performed", DateTime.Now);
            }

            logger.LogInformation("{Time} Starting Tumblebit server", DateTime.Now);

            db.UpdateOrInsert <string>("TumblerStartupLog", DateTime.Now.ToString("yyyyMMddHHmmss"), "Tumbler starting", (o, n) => n);

            var tumbler = serviceProvider.GetService <ITumblerService>();

            tumbler.StartTumbler(config.IsTestNet);
        }
Exemplo n.º 4
0
        public bool CheckBreezeRegistration(BreezeConfiguration config, DBUtils db)
        {
            var network = Network.StratisMain;

            if (config.IsTestNet)
            {
                // TODO: Change to StratisTest when it is added to NStratis
                network = Network.TestNet;
            }

            // In order to determine if the registration sequence has been performed
            // before, and to see if a previous performance is still valid, interrogate
            // the database to see if any transactions have been recorded.

            var transactions = db.GetDictionary <string, string>("RegistrationTransactions");

            // If no transactions exist, the registration definitely needs to be done
            if (transactions == null || transactions.Count == 0)
            {
                return(false);
            }

            string highestKey = null;

            foreach (var txn in transactions)
            {
                // Find most recent transaction. Assume that the rowKeys are ordered
                // lexicographically.
                if (highestKey == null)
                {
                    highestKey = txn.Key;
                }

                if (String.Compare(txn.Key, highestKey) == 1)
                {
                    highestKey = txn.Key;
                }
            }

            var mostRecentTxn = new Transaction(transactions[highestKey]);

            // Decode transaction and check if the decoded bitstream matches the
            // current configuration

            // TODO: Check if transaction is actually confirmed on the blockchain?
            var registrationToken = new RegistrationToken();

            registrationToken.ParseTransaction(mostRecentTxn, network);

            if (!config.Ipv4Address.Equals(registrationToken.Ipv4Addr))
            {
                return(false);
            }

            if (!config.Ipv6Address.Equals(registrationToken.Ipv6Addr))
            {
                return(false);
            }

            if (config.OnionAddress != registrationToken.OnionAddress)
            {
                return(false);
            }

            if (config.Port != registrationToken.Port)
            {
                return(false);
            }

            return(true);
        }
Exemplo n.º 5
0
        public bool CheckBreezeRegistration(BreezeConfiguration config, string regStorePath, string configurationHash, string onionAddress, RsaKey tumblerKey)
        {
            // In order to determine if the registration sequence has been performed
            // before, and to see if a previous performance is still valid, interrogate
            // the database to see if any transactions have been recorded.

            RegistrationStore regStore = new RegistrationStore(regStorePath);

            List <RegistrationRecord> transactions = regStore.GetByServerId(config.TumblerEcdsaKeyAddress);

            // If no transactions exist, the registration definitely needs to be done
            if (transactions == null || transactions.Count == 0)
            {
                return(false);
            }

            RegistrationRecord mostRecent = null;

            foreach (RegistrationRecord record in transactions)
            {
                // Find most recent transaction
                if (mostRecent == null)
                {
                    mostRecent = record;
                }

                if (record.RecordTimestamp > mostRecent.RecordTimestamp)
                {
                    mostRecent = record;
                }
            }

            // Check if the stored record matches the current configuration

            RegistrationToken registrationToken;

            try
            {
                registrationToken = mostRecent.Record;
            }
            catch (NullReferenceException e)
            {
                Console.WriteLine(e);
                return(false);
            }

            // IPv4
            if (config.Ipv4Address == null && registrationToken.Ipv4Addr != null)
            {
                return(false);
            }

            if (config.Ipv4Address != null && registrationToken.Ipv4Addr == null)
            {
                return(false);
            }

            if (config.Ipv4Address != null && registrationToken.Ipv4Addr != null)
            {
                if (!config.Ipv4Address.Equals(registrationToken.Ipv4Addr))
                {
                    return(false);
                }
            }

            // IPv6
            if (config.Ipv6Address == null && registrationToken.Ipv6Addr != null)
            {
                return(false);
            }

            if (config.Ipv6Address != null && registrationToken.Ipv6Addr == null)
            {
                return(false);
            }

            if (config.Ipv6Address != null && registrationToken.Ipv6Addr != null)
            {
                if (!config.Ipv6Address.Equals(registrationToken.Ipv6Addr))
                {
                    return(false);
                }
            }

            // Onion
            if (onionAddress != registrationToken.OnionAddress)
            {
                return(false);
            }

            if (config.Port != registrationToken.Port)
            {
                return(false);
            }

            // This verifies that the tumbler parameters are unchanged
            if (configurationHash != registrationToken.ConfigurationHash)
            {
                return(false);
            }

            // TODO: Check if transaction is actually confirmed on the blockchain?

            return(true);
        }
Exemplo n.º 6
0
        public Transaction PerformBreezeRegistration(BreezeConfiguration config, string regStorePath, string configurationHash, string onionAddress, RsaKey tumblerKey)
        {
            Network network = Network.ImpleumMain;

            if (config.TumblerNetwork == Network.TestNet || config.TumblerNetwork == Network.RegTest)
            {
                network = Network.ImpleumTest;
            }

            RPCHelper     stratisHelper   = null;
            RPCClient     stratisRpc      = null;
            BitcoinSecret privateKeyEcdsa = null;

            try {
                stratisHelper   = new RPCHelper(network);
                stratisRpc      = stratisHelper.GetClient(config.RpcUser, config.RpcPassword, config.RpcUrl);
                privateKeyEcdsa = stratisRpc.DumpPrivKey(BitcoinAddress.Create(config.TumblerEcdsaKeyAddress));
            }
            catch (Exception e) {
                Console.WriteLine("ERROR: Unable to retrieve private key to fund registration transaction");
                Console.WriteLine("Is the Stratis wallet unlocked & RPC enabled?");
                Console.WriteLine(e);
                Environment.Exit(0);
            }

            RegistrationToken registrationToken = new RegistrationToken(PROTOCOL_VERSION_TO_USE, config.TumblerEcdsaKeyAddress, config.Ipv4Address, config.Ipv6Address, onionAddress, configurationHash, config.Port, privateKeyEcdsa.PubKey);

            byte[] msgBytes = registrationToken.GetRegistrationTokenBytes(tumblerKey, privateKeyEcdsa);

            // Create the registration transaction using the bytes generated above
            Transaction rawTx = CreateBreezeRegistrationTx(network, msgBytes, config.TxOutputValueSetting);

            TransactionUtils  txUtils  = new TransactionUtils();
            RegistrationStore regStore = new RegistrationStore(regStorePath);

            try {
                // Replace fundrawtransaction with C# implementation. The legacy wallet
                // software does not support the RPC call.
                Transaction fundedTx = txUtils.FundRawTx(stratisRpc, rawTx, config.TxFeeValueSetting, BitcoinAddress.Create(config.TumblerEcdsaKeyAddress));
                RPCResponse signedTx = stratisRpc.SendCommand("signrawtransaction", fundedTx.ToHex());
                Transaction txToSend = new Transaction(((JObject)signedTx.Result)["hex"].Value <string>());

                RegistrationRecord regRecord = new RegistrationRecord(DateTime.Now,
                                                                      Guid.NewGuid(),
                                                                      txToSend.GetHash().ToString(),
                                                                      txToSend.ToHex(),
                                                                      registrationToken,
                                                                      null);

                regStore.Add(regRecord);

                stratisRpc.SendRawTransaction(txToSend);

                return(txToSend);
            }
            catch (Exception e)
            {
                Console.WriteLine("ERROR: Unable to broadcast registration transaction");
                Console.WriteLine(e);
            }

            return(null);
        }
Exemplo n.º 7
0
        public static void Main(string[] args)
        {
            var serviceProvider = new ServiceCollection()
                                  .AddLogging()
                                  .AddSingleton <ITumblerService, TumblerService>()
                                  .BuildServiceProvider();

            serviceProvider
            .GetService <ILoggerFactory>()
            .AddConsole(LogLevel.Debug);

            // TODO: It is messy having both a BreezeServer logger and an NTumbleBit logger
            var logger = serviceProvider.GetService <ILoggerFactory>()
                         .CreateLogger <Program>();

            logger.LogInformation("{Time} Reading Breeze server configuration", DateTime.Now);

            // Check OS-specific default config path for the config file. Create default file if it does not exist
            string configDir  = BreezeConfiguration.GetDefaultDataDir("BreezeServer");
            string configPath = Path.Combine(configDir, "breeze.conf");

            logger.LogInformation("{Time} Configuration file path {Path}", DateTime.Now, configPath);

            BreezeConfiguration config = new BreezeConfiguration(configPath);

            logger.LogInformation("{Time} Pre-initialising server to obtain parameters for configuration", DateTime.Now);

            var preTumblerConfig = serviceProvider.GetService <ITumblerService>();

            preTumblerConfig.StartTumbler(config.IsTestNet, true);

            string configurationHash = preTumblerConfig.runtime.ClassicTumblerParameters.GetHash().ToString();
            string onionAddress      = preTumblerConfig.runtime.TorUri.Host.Substring(0, 16);

            NTumbleBit.RsaKey tumblerKey = preTumblerConfig.runtime.TumblerKey;

            // Mustn't be occupying hidden service URL when the TumblerService is reinitialised
            preTumblerConfig.runtime.TorConnection.Dispose();

            // No longer need this instance of the class
            preTumblerConfig = null;

            string regStorePath = Path.Combine(configDir, "registrationHistory.json");

            logger.LogInformation("{Time} Registration history path {Path}", DateTime.Now, regStorePath);
            logger.LogInformation("{Time} Checking node registration", DateTime.Now);

            BreezeRegistration registration = new BreezeRegistration();

            if (!registration.CheckBreezeRegistration(config, regStorePath, configurationHash, onionAddress, tumblerKey))
            {
                logger.LogInformation("{Time} Creating or updating node registration", DateTime.Now);
                var regTx = registration.PerformBreezeRegistration(config, regStorePath, configurationHash, onionAddress, tumblerKey);
                if (regTx != null)
                {
                    logger.LogInformation("{Time} Submitted transaction {TxId} via RPC for broadcast", DateTime.Now, regTx.GetHash().ToString());
                }
                else
                {
                    logger.LogInformation("{Time} Unable to broadcast transaction via RPC", DateTime.Now);
                    Environment.Exit(0);
                }
            }
            else
            {
                logger.LogInformation("{Time} Node registration has already been performed", DateTime.Now);
            }

            logger.LogInformation("{Time} Starting Tumblebit server", DateTime.Now);

            var tumbler = serviceProvider.GetService <ITumblerService>();

            tumbler.StartTumbler(config.IsTestNet, false);
        }
Exemplo n.º 8
0
        public static void Main(string[] args)
        {
            var comparer          = new CommandlineArgumentComparer();
            var isRegTest         = args.Contains("regtest", comparer);
            var isTestNet         = args.Contains("testnet", comparer);
            var forceRegistration = args.Contains("forceRegistration", comparer);

            var useTor = !args.Contains("noTor", comparer);

            TumblerProtocolType?tumblerProtocol = null;

            try
            {
                string tumblerProtocolString = args.Where(a => a.StartsWith("-tumblerProtocol=")).Select(a => a.Substring("-tumblerProtocol=".Length).Replace("\"", "")).FirstOrDefault();
                if (!isRegTest && (tumblerProtocolString != null || !useTor))
                {
                    Console.WriteLine("Options -TumblerProtocol and -NoTor can only be used in combination with -RegTest switch.");
                    return;
                }

                if (tumblerProtocolString != null)
                {
                    tumblerProtocol = Enum.Parse <TumblerProtocolType>(tumblerProtocolString, true);
                }

                if (useTor && tumblerProtocol.HasValue && tumblerProtocol.Value == TumblerProtocolType.Http)
                {
                    Console.WriteLine("TumblerProtocol can only be changed to Http when Tor is disabled. Please use -NoTor switch to disable Tor.");
                    return;
                }
            }
            catch
            {
                Console.WriteLine($"Incorrect tumbling prococol specified; the valid values are {TumblerProtocolType.Tcp} and {TumblerProtocolType.Http}");
                return;
            }

            var serviceProvider = new ServiceCollection()
                                  .AddLogging()
                                  .AddSingleton <ITumblerService, TumblerService>()
                                  .BuildServiceProvider();

            serviceProvider
            .GetService <ILoggerFactory>()
            .AddConsole(LogLevel.Debug);

            // TODO: It is messy having both a BreezeServer logger and an NTumbleBit logger
            var logger = serviceProvider.GetService <ILoggerFactory>()
                         .CreateLogger <Program>();

            logger.LogInformation("{Time} Reading Breeze server configuration", DateTime.Now);

            // Check OS-specific default config path for the config file. Create default file if it does not exist
            string configDir = BreezeConfiguration.GetDefaultDataDir("BreezeServer");

            if (isRegTest)
            {
                configDir = Path.Combine(configDir, "ImpleumRegTest");
            }
            else if (isTestNet)
            {
                configDir = Path.Combine(configDir, "ImpleumTest");
            }
            else
            {
                configDir = Path.Combine(configDir, "ImpleumMain");
            }

            string configPath = Path.Combine(configDir, "breeze.conf");

            logger.LogInformation("{Time} Configuration file path {Path}", DateTime.Now, configPath);

            BreezeConfiguration config = new BreezeConfiguration(configPath);

            if (!useTor)
            {
                config.UseTor = false;
            }

            logger.LogInformation("{Time} Pre-initialising server to obtain parameters for configuration", DateTime.Now);

            var preTumblerConfig = serviceProvider.GetService <ITumblerService>();

            preTumblerConfig.StartTumbler(config, true, torMandatory: !isRegTest, tumblerProtocol: tumblerProtocol);

            string configurationHash = preTumblerConfig.runtime.ClassicTumblerParameters.GetHash().ToString();
            string onionAddress      = preTumblerConfig.runtime.TorUri.Host.Substring(0, 16);

            NTumbleBit.RsaKey tumblerKey = preTumblerConfig.runtime.TumblerKey;

            // No longer need this instance of the class
            if (config.UseTor)
            {
                preTumblerConfig.runtime.TorConnection.Dispose();
            }
            preTumblerConfig = null;

            string regStorePath = Path.Combine(configDir, "registrationHistory.json");

            logger.LogInformation("{Time} Registration history path {Path}", DateTime.Now, regStorePath);
            logger.LogInformation("{Time} Checking node registration", DateTime.Now);

            BreezeRegistration registration = new BreezeRegistration();

            if (forceRegistration || !registration.CheckBreezeRegistration(config, regStorePath, configurationHash, onionAddress, tumblerKey))
            {
                logger.LogInformation("{Time} Creating or updating node registration", DateTime.Now);
                var regTx = registration.PerformBreezeRegistration(config, regStorePath, configurationHash, onionAddress, tumblerKey);
                if (regTx != null)
                {
                    logger.LogInformation("{Time} Submitted transaction {TxId} via RPC for broadcast", DateTime.Now, regTx.GetHash().ToString());
                }
                else
                {
                    logger.LogInformation("{Time} Unable to broadcast transaction via RPC", DateTime.Now);
                    Environment.Exit(0);
                }
            }
            else
            {
                logger.LogInformation("{Time} Node registration has already been performed", DateTime.Now);
            }

            // Perform collateral balance check and report the result
            Money collateralShortfall;

            if (registration.VerifyCollateral(config, out collateralShortfall))
            {
                logger.LogInformation($"{{Time}} The collateral address {config.TumblerEcdsaKeyAddress} has sufficient funds.", DateTime.Now);
            }
            else
            {
                logger.LogWarning($"{{Time}} The collateral address {config.TumblerEcdsaKeyAddress} doesn't have enough funds. Collateral requirement is {RegistrationParameters.MASTERNODE_COLLATERAL_THRESHOLD} but only {collateralShortfall} is available at the collateral address. This is expected if you have only just run the masternode for the first time. Please send funds to the collateral address no later than {RegistrationParameters.WINDOW_PERIOD_BLOCK_COUNT} blocks after the registration transaction.", DateTime.Now);
            }

            logger.LogInformation("{Time} Starting Tumblebit server", DateTime.Now);

            // The TimeStamp and BlockSignature flags could be set to true when the Stratis network is instantiated.
            // We need to set it to false here to ensure compatibility with the Bitcoin protocol.
            Transaction.TimeStamp = false;
            Block.BlockSignature  = false;

            var tumbler = serviceProvider.GetService <ITumblerService>();

            tumbler.StartTumbler(config, false, torMandatory: !isRegTest, tumblerProtocol: tumblerProtocol);
        }