Esempio n. 1
0
        public async Task <IActionResult> LedgerConnection(
            [ModelBinder(typeof(WalletIdModelBinder))]
            WalletId walletId,
            string command,
            // getinfo
            // getxpub
            int account = 0,
            // sendtoaddress
            string hintChange = null
            )
        {
            if (!HttpContext.WebSockets.IsWebSocketRequest)
            {
                return(NotFound());
            }
            var storeData = CurrentStore;
            var network   = NetworkProvider.GetNetwork <BTCPayNetwork>(walletId.CryptoCode);

            if (network == null)
            {
                throw new FormatException("Invalid value for crypto code");
            }
            PSBT psbt = GetAmbientPSBT(network.NBitcoinNetwork, true);
            var  derivationSettings = GetDerivationSchemeSettings(walletId);

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

            using (var normalOperationTimeout = new CancellationTokenSource())
                using (var signTimeout = new CancellationTokenSource())
                {
                    normalOperationTimeout.CancelAfter(TimeSpan.FromMinutes(30));
                    var    hw     = new LedgerHardwareWalletService(webSocket);
                    object result = null;
                    try
                    {
                        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 accountKey = derivationSettings.GetSigningAccountKeySettings();
                            // Some deployment does not have the AccountKeyPath set, let's fix this...
                            if (accountKey.AccountKeyPath == null)
                            {
                                // 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.FindKeyPathFromDerivation(network,
                                                                                      derivationSettings.AccountDerivation,
                                                                                      normalOperationTimeout.Token);

                                accountKey.AccountKeyPath = foundKeyPath ?? throw new HardwareWalletException($"This store is not configured to use this ledger");
                                storeData.SetSupportedPaymentMethod(derivationSettings);
                                await Repository.UpdateStore(storeData);
                            }
                            // If it has already the AccountKeyPath, we did not looked up for it, so we need to check if we are on the right ledger
                            else
                            {
                                // Checking if ledger is right with the RootFingerprint is faster as it does not need to make a query to the parent xpub,
                                // but some deployment does not have it, so let's use AccountKeyPath instead
                                if (accountKey.RootFingerprint == null)
                                {
                                    var actualPubKey = await hw.GetExtPubKey(network, accountKey.AccountKeyPath, normalOperationTimeout.Token);

                                    if (!derivationSettings.AccountDerivation.GetExtPubKeys().Any(p => p.GetPublicKey() == actualPubKey.GetPublicKey()))
                                    {
                                        throw new HardwareWalletException($"This store is not configured to use this ledger");
                                    }
                                }
                                // We have the root fingerprint, we can check the root from it
                                else
                                {
                                    var actualPubKey = await hw.GetPubKey(network, new KeyPath(), normalOperationTimeout.Token);

                                    if (actualPubKey.GetHDFingerPrint() != accountKey.RootFingerprint.Value)
                                    {
                                        throw new HardwareWalletException($"This store is not configured to use this ledger");
                                    }
                                }
                            }

                            // Some deployment does not have the RootFingerprint set, let's fix this...
                            if (accountKey.RootFingerprint == null)
                            {
                                accountKey.RootFingerprint = (await hw.GetPubKey(network, new KeyPath(), normalOperationTimeout.Token)).GetHDFingerPrint();
                                storeData.SetSupportedPaymentMethod(derivationSettings);
                                await Repository.UpdateStore(storeData);
                            }

                            derivationSettings.RebaseKeyPaths(psbt);
                            var changeAddress = string.IsNullOrEmpty(hintChange) ? null : BitcoinAddress.Create(hintChange, network.NBitcoinNetwork);
                            signTimeout.CancelAfter(TimeSpan.FromMinutes(5));
                            psbt = await hw.SignTransactionAsync(psbt, accountKey.GetRootedKeyPath(), accountKey.AccountKey, changeAddress?.ScriptPubKey, signTimeout.Token);

                            SetAmbientPSBT(null);
                            result = new SendToAddressResult()
                            {
                                PSBT = psbt.ToBase64()
                            };
                        }
                    }
                    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, _serializerSettings));
                            await webSocket.SendAsync(new ArraySegment <byte>(bytes), WebSocketMessageType.Text, true, new CancellationTokenSource(2000).Token);
                        }
                    }
                    catch { }
                    finally
                    {
                        await webSocket.CloseSocket();
                    }
                }
            return(new EmptyResult());
        }
        public async Task <IActionResult> AddDerivationSchemeLedger(
            string storeId,
            string cryptoCode,
            string command,
            string keyPath = "")
        {
            if (!HttpContext.WebSockets.IsWebSocketRequest)
            {
                return(NotFound());
            }

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

            var    hw      = new LedgerHardwareWalletService(webSocket);
            object result  = null;
            var    network = _NetworkProvider.GetNetwork <BTCPayNetwork>(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 k = KeyPath.Parse(keyPath);
                        if (k.Indexes.Length == 0)
                        {
                            throw new FormatException("Invalid key path");
                        }

                        var getxpubResult = new GetXPubs();
                        getxpubResult.ExtPubKey = await hw.GetExtPubKey(network, k, normalOperationTimeout.Token);

                        var segwit     = network.NBitcoinNetwork.Consensus.SupportSegwit;
                        var derivation = new DerivationStrategyFactory(network.NBitcoinNetwork).CreateDirectDerivationStrategy(getxpubResult.ExtPubKey, new DerivationStrategyOptions()
                        {
                            ScriptPubKeyType = segwit ? ScriptPubKeyType.SegwitP2SH : ScriptPubKeyType.Legacy
                        });
                        getxpubResult.DerivationScheme = derivation;
                        getxpubResult.RootFingerprint  = (await hw.GetExtPubKey(network, new KeyPath(), normalOperationTimeout.Token)).ExtPubKey.PubKey.GetHDFingerPrint();
                        getxpubResult.Source           = hw.Device;
                        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, network.NBXplorerNetwork.JsonSerializerSettings));
                        await webSocket.SendAsync(new ArraySegment <byte>(bytes), WebSocketMessageType.Text, true, new CancellationTokenSource(2000).Token);
                    }
                }
                catch { }
                finally
                {
                    await webSocket.CloseSocket();
                }
            }
            return(new EmptyResult());
        }