public async Task <IActionResult> LedgerConnection(
            string command,
            // getinfo
            string cryptoCode = null,
            // getxpub
            [ModelBinder(typeof(ModelBinders.DerivationSchemeModelBinder))]
            DerivationStrategyBase derivationScheme = null,
            int account = 0,
            // sendtoaddress
            string destination = null, string amount = null, string feeRate = null, string substractFees = null
            )
        {
            if (!HttpContext.WebSockets.IsWebSocketRequest)
            {
                return(NotFound());
            }
            var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();

            using (var normalOperationTimeout = new CancellationTokenSource())
                using (var signTimeout = new CancellationTokenSource())
                {
                    normalOperationTimeout.CancelAfter(TimeSpan.FromMinutes(30));
                    var    hw     = new HardwareWalletService(webSocket);
                    object result = null;
                    try
                    {
                        BTCPayNetwork network = null;
                        if (cryptoCode != null)
                        {
                            network = _NetworkProvider.GetNetwork(cryptoCode);
                            if (network == null)
                            {
                                throw new FormatException("Invalid value for crypto code");
                            }
                        }

                        BitcoinAddress destinationAddress = null;
                        if (destination != null)
                        {
                            try
                            {
                                destinationAddress = BitcoinAddress.Create(destination, network.NBitcoinNetwork);
                            }
                            catch { }
                            if (destinationAddress == null)
                            {
                                throw new FormatException("Invalid value for destination");
                            }
                        }

                        FeeRate feeRateValue = null;
                        if (feeRate != null)
                        {
                            try
                            {
                                feeRateValue = new FeeRate(Money.Satoshis(int.Parse(feeRate, CultureInfo.InvariantCulture)), 1);
                            }
                            catch { }
                            if (feeRateValue == null || feeRateValue.FeePerK <= Money.Zero)
                            {
                                throw new FormatException("Invalid value for fee rate");
                            }
                        }

                        Money amountBTC = null;
                        if (amount != null)
                        {
                            try
                            {
                                amountBTC = Money.Parse(amount);
                            }
                            catch { }
                            if (amountBTC == null || amountBTC <= Money.Zero)
                            {
                                throw new FormatException("Invalid value for amount");
                            }
                        }

                        bool subsctractFeesValue = false;
                        if (substractFees != null)
                        {
                            try
                            {
                                subsctractFeesValue = bool.Parse(substractFees);
                            }
                            catch { throw new FormatException("Invalid value for subtract fees"); }
                        }
                        if (command == "test")
                        {
                            result = await hw.Test(normalOperationTimeout.Token);
                        }
                        if (command == "getxpub")
                        {
                            var getxpubResult = await hw.GetExtPubKey(network, account, normalOperationTimeout.Token);

                            result = getxpubResult;
                        }
                        if (command == "getinfo")
                        {
                            var strategy = GetDirectDerivationStrategy(derivationScheme);
                            if (strategy == null || await hw.GetKeyPath(network, strategy, normalOperationTimeout.Token) == null)
                            {
                                throw new Exception($"This store is not configured to use this ledger");
                            }

                            var feeProvider     = _feeRateProvider.CreateFeeProvider(network);
                            var recommendedFees = feeProvider.GetFeeRateAsync();
                            var balance         = _walletProvider.GetWallet(network).GetBalance(derivationScheme);
                            result = new GetInfoResult()
                            {
                                Balance = (double)(await balance).ToDecimal(MoneyUnit.BTC), RecommendedSatoshiPerByte = (int)(await recommendedFees).GetFee(1).Satoshi
                            };
                        }

                        if (command == "sendtoaddress")
                        {
                            if (!_dashboard.IsFullySynched(network.CryptoCode, out var summary))
                            {
                                throw new Exception($"{network.CryptoCode}: not started or fully synched");
                            }
                            var strategy = GetDirectDerivationStrategy(derivationScheme);
                            var wallet   = _walletProvider.GetWallet(network);
                            var change   = wallet.GetChangeAddressAsync(derivationScheme);

                            var unspentCoins = await wallet.GetUnspentCoins(derivationScheme);

                            var changeAddress = await change;
                            var send          = new[] { (
示例#2
0
        public async Task <IActionResult> LedgerConnection(
            [ModelBinder(typeof(WalletIdModelBinder))]
            WalletId walletId,
            string command,
            // getinfo
            // getxpub
            int account = 0,
            // sendtoaddress
            bool noChange      = false,
            string destination = null, string amount = null, string feeRate = null, bool substractFees = false, bool disableRBF = false
            )
        {
            if (!HttpContext.WebSockets.IsWebSocketRequest)
            {
                return(NotFound());
            }

            var cryptoCode         = walletId.CryptoCode;
            var storeData          = (await Repository.FindStore(walletId.StoreId, GetUserId()));
            var derivationSettings = GetPaymentMethod(walletId, storeData);

            var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();

            using (var normalOperationTimeout = new CancellationTokenSource())
                using (var signTimeout = new CancellationTokenSource())
                {
                    normalOperationTimeout.CancelAfter(TimeSpan.FromMinutes(30));
                    var    hw     = new HardwareWalletService(webSocket);
                    var    model  = new WalletSendLedgerModel();
                    object result = null;
                    try
                    {
                        BTCPayNetwork network = null;
                        if (cryptoCode != null)
                        {
                            network = NetworkProvider.GetNetwork(cryptoCode);
                            if (network == null)
                            {
                                throw new FormatException("Invalid value for crypto code");
                            }
                        }

                        if (destination != null)
                        {
                            try
                            {
                                BitcoinAddress.Create(destination.Trim(), network.NBitcoinNetwork);
                                model.Destination = destination.Trim();
                            }
                            catch { }
                        }


                        if (feeRate != null)
                        {
                            try
                            {
                                model.FeeSatoshiPerByte = int.Parse(feeRate, CultureInfo.InvariantCulture);
                            }
                            catch { }
                            if (model.FeeSatoshiPerByte <= 0)
                            {
                                throw new FormatException("Invalid value for fee rate");
                            }
                        }

                        if (amount != null)
                        {
                            try
                            {
                                model.Amount = Money.Parse(amount).ToDecimal(MoneyUnit.BTC);
                            }
                            catch { }
                            if (model.Amount <= 0m)
                            {
                                throw new FormatException("Invalid value for amount");
                            }
                        }

                        model.SubstractFees = substractFees;
                        model.NoChange      = noChange;
                        model.DisableRBF    = disableRBF;
                        if (command == "test")
                        {
                            result = await hw.Test(normalOperationTimeout.Token);
                        }
                        if (command == "sendtoaddress")
                        {
                            if (!_dashboard.IsFullySynched(network.CryptoCode, out var summary))
                            {
                                throw new Exception($"{network.CryptoCode}: not started or fully synched");
                            }



                            var strategy = GetDirectDerivationStrategy(derivationSettings.AccountDerivation);
                            // Some deployment have the wallet root key path saved in the store blob
                            // If it does, we only have to make 1 call to the hw to check if it can sign the given strategy,
                            if (derivationSettings.AccountKeyPath == null || !await hw.CanSign(network, strategy, derivationSettings.AccountKeyPath, normalOperationTimeout.Token))
                            {
                                // If the saved wallet key path is not present or incorrect, let's scan the wallet to see if it can sign strategy
                                var foundKeyPath = await hw.FindKeyPath(network, strategy, normalOperationTimeout.Token);

                                if (foundKeyPath == null)
                                {
                                    throw new HardwareWalletException($"This store is not configured to use this ledger");
                                }
                                derivationSettings.AccountKeyPath = foundKeyPath;
                                storeData.SetSupportedPaymentMethod(derivationSettings);
                                await Repository.UpdateStore(storeData);
                            }


                            var psbt = await CreatePSBT(network, derivationSettings, model, normalOperationTimeout.Token);

                            signTimeout.CancelAfter(TimeSpan.FromMinutes(5));
                            psbt.PSBT = await hw.SignTransactionAsync(psbt.PSBT, psbt.ChangeAddress?.ScriptPubKey, signTimeout.Token);

                            if (!psbt.PSBT.TryFinalize(out var errors))
                            {
                                throw new Exception($"Error while finalizing the transaction ({new PSBTException(errors).ToString()})");
                            }
                            var transaction = psbt.PSBT.ExtractTransaction();
                            try
                            {
                                var broadcastResult = await ExplorerClientProvider.GetExplorerClient(network).BroadcastAsync(transaction);

                                if (!broadcastResult.Success)
                                {
                                    throw new Exception($"RPC Error while broadcasting: {broadcastResult.RPCCode} {broadcastResult.RPCCodeMessage} {broadcastResult.RPCMessage}");
                                }
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("Error while broadcasting: " + ex.Message);
                            }
                            var wallet = _walletProvider.GetWallet(network);
                            wallet.InvalidateCache(derivationSettings.AccountDerivation);
                            result = new SendToAddressResult()
                            {
                                TransactionId = transaction.GetHash().ToString()
                            };
                        }
                    }
                    catch (OperationCanceledException)
                    { result = new LedgerTestResult()
                      {
                          Success = false, Error = "Timeout"
                      }; }
                    catch (Exception ex)
                    { result = new LedgerTestResult()
                      {
                          Success = false, Error = ex.Message
                      }; }
                    finally { hw.Dispose(); }
                    try
                    {
                        if (result != null)
                        {
                            UTF8Encoding UTF8NOBOM = new UTF8Encoding(false);
                            var          bytes     = UTF8NOBOM.GetBytes(JsonConvert.SerializeObject(result, _mvcJsonOptions.Value.SerializerSettings));
                            await webSocket.SendAsync(new ArraySegment <byte>(bytes), WebSocketMessageType.Text, true, new CancellationTokenSource(2000).Token);
                        }
                    }
                    catch { }
                    finally
                    {
                        await webSocket.CloseSocket();
                    }
                }
            return(new EmptyResult());
        }
示例#3
0
        public async Task <IActionResult> LedgerConnection(
            string storeId,
            string command,
            // getinfo
            string cryptoCode = null,
            // sendtoaddress
            string destination = null, string amount = null, string feeRate = null, string substractFees = null
            )
        {
            if (!HttpContext.WebSockets.IsWebSocketRequest)
            {
                return(NotFound());
            }
            var store = await _Repo.FindStore(storeId, GetUserId());

            if (store == null)
            {
                return(NotFound());
            }

            var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();

            var    hw     = new HardwareWalletService(webSocket);
            object result = null;

            try
            {
                BTCPayNetwork network = null;
                if (cryptoCode != null)
                {
                    network = _NetworkProvider.GetNetwork(cryptoCode);
                    if (network == null)
                    {
                        throw new FormatException("Invalid value for crypto code");
                    }
                }

                BitcoinAddress destinationAddress = null;
                if (destination != null)
                {
                    try
                    {
                        destinationAddress = BitcoinAddress.Create(destination);
                    }
                    catch { }
                    if (destinationAddress == null)
                    {
                        throw new FormatException("Invalid value for destination");
                    }
                }

                FeeRate feeRateValue = null;
                if (feeRate != null)
                {
                    try
                    {
                        feeRateValue = new FeeRate(Money.Satoshis(int.Parse(feeRate)), 1);
                    }
                    catch { }
                    if (feeRateValue == null || feeRateValue.FeePerK <= Money.Zero)
                    {
                        throw new FormatException("Invalid value for fee rate");
                    }
                }

                Money amountBTC = null;
                if (amount != null)
                {
                    try
                    {
                        amountBTC = Money.Parse(amount);
                    }
                    catch { }
                    if (amountBTC == null || amountBTC <= Money.Zero)
                    {
                        throw new FormatException("Invalid value for amount");
                    }
                }

                bool subsctractFeesValue = false;
                if (substractFees != null)
                {
                    try
                    {
                        subsctractFeesValue = bool.Parse(substractFees);
                    }
                    catch { throw new FormatException("Invalid value for substract fees"); }
                }
                if (command == "test")
                {
                    result = await hw.Test();
                }
                if (command == "getxpub")
                {
                    result = await hw.GetExtPubKey(network);
                }
                if (command == "getinfo")
                {
                    var strategy     = GetDirectDerivationStrategy(store, network);
                    var strategyBase = GetDerivationStrategy(store, network);
                    if (!await hw.SupportDerivation(network, strategy))
                    {
                        throw new Exception($"This store is not configured to use this ledger");
                    }

                    var feeProvider     = _FeeRateProvider.CreateFeeProvider(network);
                    var recommendedFees = feeProvider.GetFeeRateAsync();
                    var balance         = _WalletProvider.GetWallet(network).GetBalance(strategyBase);
                    result = new GetInfoResult()
                    {
                        Balance = (double)(await balance).ToDecimal(MoneyUnit.BTC), RecommendedSatoshiPerByte = (int)(await recommendedFees).GetFee(1).Satoshi
                    };
                }

                if (command == "sendtoaddress")
                {
                    var strategy     = GetDirectDerivationStrategy(store, network);
                    var strategyBase = GetDerivationStrategy(store, network);
                    var wallet       = _WalletProvider.GetWallet(network);
                    var change       = wallet.GetChangeAddressAsync(strategyBase);
                    var unspentCoins = await wallet.GetUnspentCoins(strategyBase);

                    var changeAddress = await change;
                    unspentCoins.Item2.TryAdd(changeAddress.Item1.ScriptPubKey, changeAddress.Item2);
                    var transaction = await hw.SendToAddress(strategy, unspentCoins.Item1, network,
                                                             new[] { (destinationAddress as IDestination, amountBTC, subsctractFeesValue) },
        public async Task <IActionResult> AddDerivationSchemeLedger(
            string storeId,
            string cryptoCode,
            string command,
            int account = 0)
        {
            if (!HttpContext.WebSockets.IsWebSocketRequest)
            {
                return(NotFound());
            }

            var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();

            var    hw      = new HardwareWalletService(webSocket);
            object result  = null;
            var    network = _NetworkProvider.GetNetwork(cryptoCode);

            using (var normalOperationTimeout = new CancellationTokenSource())
            {
                normalOperationTimeout.CancelAfter(TimeSpan.FromMinutes(30));
                try
                {
                    if (command == "test")
                    {
                        result = await hw.Test(normalOperationTimeout.Token);
                    }
                    if (command == "getxpub")
                    {
                        var getxpubResult = await hw.GetExtPubKey(network, account, normalOperationTimeout.Token);

                        result = getxpubResult;
                    }
                }
                catch (OperationCanceledException)
                { result = new LedgerTestResult()
                  {
                      Success = false, Error = "Timeout"
                  }; }
                catch (Exception ex)
                { result = new LedgerTestResult()
                  {
                      Success = false, Error = ex.Message
                  }; }
                finally { hw.Dispose(); }
                try
                {
                    if (result != null)
                    {
                        UTF8Encoding UTF8NOBOM = new UTF8Encoding(false);
                        var          bytes     = UTF8NOBOM.GetBytes(JsonConvert.SerializeObject(result, MvcJsonOptions.Value.SerializerSettings));
                        await webSocket.SendAsync(new ArraySegment <byte>(bytes), WebSocketMessageType.Text, true, new CancellationTokenSource(2000).Token);
                    }
                }
                catch { }
                finally
                {
                    await webSocket.CloseSocket();
                }
            }
            return(new EmptyResult());
        }
示例#5
0
        public async Task <IActionResult> LedgerConnection(
            [ModelBinder(typeof(WalletIdModelBinder))]
            WalletId walletId,
            string command,
            // getinfo
            // getxpub
            int account = 0,
            // sendtoaddress
            string destination = null, string amount = null, string feeRate = null, string substractFees = null
            )
        {
            if (!HttpContext.WebSockets.IsWebSocketRequest)
            {
                return(NotFound());
            }

            var cryptoCode       = walletId.CryptoCode;
            var storeData        = (await Repository.FindStore(walletId.StoreId, GetUserId()));
            var derivationScheme = GetPaymentMethod(walletId, storeData).DerivationStrategyBase;

            var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();

            using (var normalOperationTimeout = new CancellationTokenSource())
                using (var signTimeout = new CancellationTokenSource())
                {
                    normalOperationTimeout.CancelAfter(TimeSpan.FromMinutes(30));
                    var    hw     = new HardwareWalletService(webSocket);
                    object result = null;
                    try
                    {
                        BTCPayNetwork network = null;
                        if (cryptoCode != null)
                        {
                            network = NetworkProvider.GetNetwork(cryptoCode);
                            if (network == null)
                            {
                                throw new FormatException("Invalid value for crypto code");
                            }
                        }

                        BitcoinAddress destinationAddress = null;
                        if (destination != null)
                        {
                            try
                            {
                                destinationAddress = BitcoinAddress.Create(destination, network.NBitcoinNetwork);
                            }
                            catch { }
                            if (destinationAddress == null)
                            {
                                throw new FormatException("Invalid value for destination");
                            }
                        }

                        FeeRate feeRateValue = null;
                        if (feeRate != null)
                        {
                            try
                            {
                                feeRateValue = new FeeRate(Money.Satoshis(int.Parse(feeRate, CultureInfo.InvariantCulture)), 1);
                            }
                            catch { }
                            if (feeRateValue == null || feeRateValue.FeePerK <= Money.Zero)
                            {
                                throw new FormatException("Invalid value for fee rate");
                            }
                        }

                        Money amountBTC = null;
                        if (amount != null)
                        {
                            try
                            {
                                amountBTC = Money.Parse(amount);
                            }
                            catch { }
                            if (amountBTC == null || amountBTC <= Money.Zero)
                            {
                                throw new FormatException("Invalid value for amount");
                            }
                        }

                        bool subsctractFeesValue = false;
                        if (substractFees != null)
                        {
                            try
                            {
                                subsctractFeesValue = bool.Parse(substractFees);
                            }
                            catch { throw new FormatException("Invalid value for subtract fees"); }
                        }
                        if (command == "test")
                        {
                            result = await hw.Test(normalOperationTimeout.Token);
                        }
                        if (command == "sendtoaddress")
                        {
                            if (!_dashboard.IsFullySynched(network.CryptoCode, out var summary))
                            {
                                throw new Exception($"{network.CryptoCode}: not started or fully synched");
                            }
                            var strategy = GetDirectDerivationStrategy(derivationScheme);
                            var wallet   = _walletProvider.GetWallet(network);
                            var change   = wallet.GetChangeAddressAsync(derivationScheme);

                            var unspentCoins = await wallet.GetUnspentCoins(derivationScheme);

                            var changeAddress = await change;
                            var send          = new[] { (
        public async Task <IActionResult> LedgerConnection(
            [ModelBinder(typeof(WalletIdModelBinder))]
            WalletId walletId,
            string command,
            // getinfo
            // getxpub
            int account = 0,
            // sendtoaddress
            bool noChange      = false,
            string destination = null, string amount = null, string feeRate = null, string substractFees = null
            )
        {
            if (!HttpContext.WebSockets.IsWebSocketRequest)
            {
                return(NotFound());
            }

            var cryptoCode       = walletId.CryptoCode;
            var storeData        = (await Repository.FindStore(walletId.StoreId, GetUserId()));
            var derivationScheme = GetPaymentMethod(walletId, storeData).DerivationStrategyBase;

            var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();

            using (var normalOperationTimeout = new CancellationTokenSource())
                using (var signTimeout = new CancellationTokenSource())
                {
                    normalOperationTimeout.CancelAfter(TimeSpan.FromMinutes(30));
                    var    hw     = new HardwareWalletService(webSocket);
                    object result = null;
                    try
                    {
                        BTCPayNetwork network = null;
                        if (cryptoCode != null)
                        {
                            network = NetworkProvider.GetNetwork(cryptoCode);
                            if (network == null)
                            {
                                throw new FormatException("Invalid value for crypto code");
                            }
                        }

                        BitcoinAddress destinationAddress = null;
                        if (destination != null)
                        {
                            try
                            {
                                destinationAddress = BitcoinAddress.Create(destination.Trim(), network.NBitcoinNetwork);
                            }
                            catch { }
                            if (destinationAddress == null)
                            {
                                throw new FormatException("Invalid value for destination");
                            }
                        }

                        FeeRate feeRateValue = null;
                        if (feeRate != null)
                        {
                            try
                            {
                                feeRateValue = new FeeRate(Money.Satoshis(int.Parse(feeRate, CultureInfo.InvariantCulture)), 1);
                            }
                            catch { }
                            if (feeRateValue == null || feeRateValue.FeePerK <= Money.Zero)
                            {
                                throw new FormatException("Invalid value for fee rate");
                            }
                        }

                        Money amountBTC = null;
                        if (amount != null)
                        {
                            try
                            {
                                amountBTC = Money.Parse(amount);
                            }
                            catch { }
                            if (amountBTC == null || amountBTC <= Money.Zero)
                            {
                                throw new FormatException("Invalid value for amount");
                            }
                        }

                        bool subsctractFeesValue = false;
                        if (substractFees != null)
                        {
                            try
                            {
                                subsctractFeesValue = bool.Parse(substractFees);
                            }
                            catch { throw new FormatException("Invalid value for subtract fees"); }
                        }
                        if (command == "test")
                        {
                            result = await hw.Test(normalOperationTimeout.Token);
                        }
                        if (command == "sendtoaddress")
                        {
                            if (!_dashboard.IsFullySynched(network.CryptoCode, out var summary))
                            {
                                throw new Exception($"{network.CryptoCode}: not started or fully synched");
                            }
                            var         strategy       = GetDirectDerivationStrategy(derivationScheme);
                            var         wallet         = _walletProvider.GetWallet(network);
                            var         change         = wallet.GetChangeAddressAsync(derivationScheme);
                            var         keypaths       = new Dictionary <Script, KeyPath>();
                            List <Coin> availableCoins = new List <Coin>();
                            foreach (var c in await wallet.GetUnspentCoins(derivationScheme))
                            {
                                keypaths.TryAdd(c.Coin.ScriptPubKey, c.KeyPath);
                                availableCoins.Add(c.Coin);
                            }

                            var changeAddress = await change;

                            var storeBlob    = storeData.GetStoreBlob();
                            var paymentId    = new Payments.PaymentMethodId(cryptoCode, Payments.PaymentTypes.BTCLike);
                            var foundKeyPath = storeBlob.GetWalletKeyPathRoot(paymentId);
                            // Some deployment have the wallet root key path saved in the store blob
                            // If it does, we only have to make 1 call to the hw to check if it can sign the given strategy,
                            if (foundKeyPath == null || !await hw.CanSign(network, strategy, foundKeyPath, normalOperationTimeout.Token))
                            {
                                // If the saved wallet key path is not present or incorrect, let's scan the wallet to see if it can sign strategy
                                foundKeyPath = await hw.FindKeyPath(network, strategy, normalOperationTimeout.Token);

                                if (foundKeyPath == null)
                                {
                                    throw new HardwareWalletException($"This store is not configured to use this ledger");
                                }
                                storeBlob.SetWalletKeyPathRoot(paymentId, foundKeyPath);
                                storeData.SetStoreBlob(storeBlob);
                                await Repository.UpdateStore(storeData);
                            }
retry:
                            var send = new[] { (