public async Task <StoresController> CreateStoreAsync() { ExtKey = new ExtKey().GetWif(parent.Network); var store = parent.PayTester.GetController <StoresController>(UserId); await store.CreateStore(new CreateStoreViewModel() { Name = "Test Store" }); StoreId = store.CreatedStoreId; DerivationScheme = new DerivationStrategyFactory(parent.Network).Parse(ExtKey.Neuter().ToString() + "-[legacy]"); await store.UpdateStore(StoreId, new StoreViewModel() { SpeedPolicy = SpeedPolicy.MediumSpeed }); await store.AddDerivationScheme(StoreId, new DerivationSchemeViewModel() { CryptoCurrency = "BTC", DerivationSchemeFormat = "BTCPay", DerivationScheme = DerivationScheme.ToString(), }, "Save"); return(store); }
public async Task <StoresController> CreateStoreAsync(string cryptoCode = null) { cryptoCode = cryptoCode ?? CryptoCode; SupportedNetwork = parent.NetworkProvider.GetNetwork(cryptoCode); ExtKey = new ExtKey().GetWif(SupportedNetwork.NBitcoinNetwork); var store = parent.PayTester.GetController <StoresController>(UserId); await store.CreateStore(new CreateStoreViewModel() { Name = "Test Store" }); StoreId = store.CreatedStoreId; DerivationScheme = new DerivationStrategyFactory(SupportedNetwork.NBitcoinNetwork).Parse(ExtKey.Neuter().ToString() + "-[legacy]"); var vm = (StoreViewModel)((ViewResult)await store.UpdateStore(StoreId)).Model; vm.SpeedPolicy = SpeedPolicy.MediumSpeed; await store.UpdateStore(StoreId, vm); await store.AddDerivationScheme(StoreId, new DerivationSchemeViewModel() { CryptoCurrency = cryptoCode, DerivationSchemeFormat = "BTCPay", DerivationScheme = DerivationScheme.ToString(), Confirmation = true }, "Save"); return(store); }
private DerivationStrategyBase FindMatch(HashSet <string> hintLabels, DerivationStrategyBase result) { var facto = new DerivationStrategyFactory(Network); var firstKeyPath = new KeyPath("0/0"); if (HintScriptPubKey == null) { return(result); } if (HintScriptPubKey == result.GetDerivation(firstKeyPath).ScriptPubKey) { return(result); } if (result is MultisigDerivationStrategy) { hintLabels.Add("keeporder"); } var resultNoLabels = result.ToString(); resultNoLabels = string.Join('-', resultNoLabels.Split('-').Where(p => !IsLabel(p))); foreach (var labels in ItemCombinations(hintLabels.ToList())) { var hinted = facto.Parse(resultNoLabels + '-' + string.Join('-', labels.Select(l => $"[{l}]").ToArray())); if (HintScriptPubKey == hinted.GetDerivation(firstKeyPath).ScriptPubKey) { return(hinted); } } throw new FormatException("Could not find any match"); }
public NBXplorerNetwork(INetworkSet networkSet, NetworkType networkType, DerivationStrategyFactory derivationStrategyFactory = null) { NBitcoinNetwork = networkSet.GetNetwork(networkType); CryptoCode = networkSet.CryptoCode; DefaultSettings = NBXplorerDefaultSettings.GetDefaultSettings(networkType); DerivationStrategyFactory = derivationStrategyFactory; }
public async Task RegisterDerivationSchemeAsync(string crytoCode) { var store = parent.PayTester.GetController <StoresController>(UserId); var networkProvider = parent.PayTester.GetService <BTCPayNetworkProvider>(); var derivation = new DerivationStrategyFactory(networkProvider.GetNetwork(crytoCode).NBitcoinNetwork).Parse(ExtKey.Neuter().ToString() + "-[legacy]"); await store.AddDerivationScheme(StoreId, new DerivationSchemeViewModel() { CryptoCurrency = crytoCode, DerivationSchemeFormat = crytoCode, DerivationScheme = derivation.ToString(), Confirmation = true }, "Save"); }
public InvoiceWatcher(ExplorerClient explorerClient, InvoiceRepository invoiceRepository, BTCPayWallet wallet, InvoiceNotificationManager notificationManager) { LongPollingMode = explorerClient.Network == Network.RegTest; PollInterval = explorerClient.Network == Network.RegTest ? TimeSpan.FromSeconds(10.0) : TimeSpan.FromMinutes(1.0); _Wallet = wallet ?? throw new ArgumentNullException(nameof(wallet)); _ExplorerClient = explorerClient ?? throw new ArgumentNullException(nameof(explorerClient)); _DerivationFactory = new DerivationStrategyFactory(_ExplorerClient.Network); _InvoiceRepository = invoiceRepository ?? throw new ArgumentNullException(nameof(invoiceRepository)); _NotificationManager = notificationManager ?? throw new ArgumentNullException(nameof(notificationManager)); }
public async Task<WalletId> RegisterDerivationSchemeAsync(string cryptoCode, bool segwit = false) { SupportedNetwork = parent.NetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode); var store = parent.PayTester.GetController<StoresController>(UserId, StoreId); ExtKey = new ExtKey().GetWif(SupportedNetwork.NBitcoinNetwork); DerivationScheme = new DerivationStrategyFactory(SupportedNetwork.NBitcoinNetwork).Parse(ExtKey.Neuter().ToString() + (segwit ? "" : "-[legacy]")); await store.AddDerivationScheme(StoreId, new DerivationSchemeViewModel() { DerivationScheme = DerivationScheme.ToString(), Confirmation = true }, cryptoCode); return new WalletId(StoreId, cryptoCode); }
public InvoiceWatcher(ExplorerClient explorerClient, BTCPayNetworkProvider networkProvider, InvoiceRepository invoiceRepository, EventAggregator eventAggregator, BTCPayWallet wallet, InvoiceWatcherAccessor accessor) { LongPollingMode = explorerClient.Network == Network.RegTest; PollInterval = explorerClient.Network == Network.RegTest ? TimeSpan.FromSeconds(10.0) : TimeSpan.FromMinutes(1.0); _Wallet = wallet ?? throw new ArgumentNullException(nameof(wallet)); _ExplorerClient = explorerClient ?? throw new ArgumentNullException(nameof(explorerClient)); _DerivationFactory = new DerivationStrategyFactory(_ExplorerClient.Network); _InvoiceRepository = invoiceRepository ?? throw new ArgumentNullException(nameof(invoiceRepository)); _EventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator)); _NetworkProvider = networkProvider; accessor.Instance = this; }
public async Task RegisterDerivationSchemeAsync(string cryptoCode) { SupportedNetwork = parent.NetworkProvider.GetNetwork(cryptoCode); var store = parent.PayTester.GetController <StoresController>(UserId, StoreId); ExtKey = new ExtKey().GetWif(SupportedNetwork.NBitcoinNetwork); DerivationScheme = new DerivationStrategyFactory(SupportedNetwork.NBitcoinNetwork).Parse(ExtKey.Neuter().ToString() + "-[legacy]"); var vm = (StoreViewModel)((ViewResult)store.UpdateStore()).Model; vm.SpeedPolicy = SpeedPolicy.MediumSpeed; await store.UpdateStore(vm); await store.AddDerivationScheme(StoreId, new DerivationSchemeViewModel() { DerivationScheme = DerivationScheme.ToString(), Confirmation = true }, cryptoCode); }
public async Task <GetXPubResult> GetExtPubKey(BTCPayNetwork network) { if (network == null) { throw new ArgumentNullException(nameof(network)); } var pubkey = await GetExtPubKey(_Ledger, network, new KeyPath("49'").Derive(network.CoinType).Derive(0, true), false); var derivation = new DerivationStrategyFactory(network.NBitcoinNetwork).CreateDirectDerivationStrategy(pubkey, new DerivationStrategyOptions() { P2SH = true, Legacy = false }); return(new GetXPubResult() { ExtPubKey = derivation.ToString() }); }
public async Task <GetXPubResult> GetExtPubKey(BTCPayNetwork network, KeyPath keyPath, CancellationToken cancellation) { if (network == null) { throw new ArgumentNullException(nameof(network)); } var segwit = network.NBitcoinNetwork.Consensus.SupportSegwit; var pubkey = await GetExtPubKey(Ledger, network, keyPath, false, cancellation); var derivation = new DerivationStrategyFactory(network.NBitcoinNetwork).CreateDirectDerivationStrategy(pubkey, new DerivationStrategyOptions() { P2SH = segwit, Legacy = !segwit }); return(new GetXPubResult() { ExtPubKey = derivation.ToString(), KeyPath = keyPath }); }
public async Task <GetXPubResult> GetExtPubKey(BTCPayNetwork network, int account) { if (network == null) { throw new ArgumentNullException(nameof(network)); } var segwit = network.NBitcoinNetwork.Consensus.SupportSegwit; var path = network.GetRootKeyPath().Derive(account, true); var pubkey = await GetExtPubKey(_Ledger, network, path, false); var derivation = new DerivationStrategyFactory(network.NBitcoinNetwork).CreateDirectDerivationStrategy(pubkey, new DerivationStrategyOptions() { P2SH = segwit, Legacy = !segwit }); return(new GetXPubResult() { ExtPubKey = derivation.ToString(), KeyPath = path }); }
public Task BindModelAsync(ModelBindingContext bindingContext) { if (!typeof(DerivationStrategyBase).GetTypeInfo().IsAssignableFrom(bindingContext.ModelType)) { return(Task.CompletedTask); } ValueProviderResult val = bindingContext.ValueProvider.GetValue( bindingContext.ModelName); if (val == null) { return(Task.CompletedTask); } string key = val.FirstValue as string; if (key == null) { return(Task.CompletedTask); } var networkProvider = (BTCPayNetworkProvider)bindingContext.HttpContext.RequestServices.GetService(typeof(BTCPayNetworkProvider)); var cryptoCode = bindingContext.ValueProvider.GetValue("cryptoCode").FirstValue; var network = networkProvider.GetNetwork <BTCPayNetwork>(cryptoCode ?? "BTC"); try { var data = new DerivationStrategyFactory(network.NBitcoinNetwork).Parse(key); if (!bindingContext.ModelType.IsInstanceOfType(data)) { throw new FormatException("Invalid destination type"); } bindingContext.Result = ModelBindingResult.Success(data); } catch { throw new FormatException("Invalid derivation scheme"); } return(Task.CompletedTask); }
public DerivationStrategyBase Parse(string str) { if (str == null) { throw new ArgumentNullException(nameof(str)); } str = str.Trim(); HashSet <string> hintedLabels = new HashSet <string>(); var hintDestination = HintScriptPubKey?.GetDestination(); if (hintDestination != null) { if (hintDestination is KeyId) { hintedLabels.Add("legacy"); } if (hintDestination is ScriptId) { hintedLabels.Add("p2sh"); } } if (!Network.Consensus.SupportSegwit) { hintedLabels.Add("legacy"); } try { var result = new DerivationStrategyFactory(Network).Parse(str); return(FindMatch(hintedLabels, result)); } catch (Exception ex) { Console.WriteLine(""); } var parts = str.Split('-'); bool hasLabel = false; for (int i = 0; i < parts.Length; i++) { if (IsLabel(parts[i])) { if (!hasLabel) { hintedLabels.Clear(); if (!Network.Consensus.SupportSegwit) { hintedLabels.Add("legacy"); } } hasLabel = true; hintedLabels.Add(parts[i].Substring(1, parts[i].Length - 2).ToLowerInvariant()); continue; } try { var data = Network.GetBase58CheckEncoder().DecodeData(parts[i]); if (data.Length < 4) { continue; } var prefix = Utils.ToUInt32(data, false); var standardPrefix = Utils.ToBytes(0x0488b21eU, false); for (int ii = 0; ii < 4; ii++) { data[ii] = standardPrefix[ii]; } var derivationScheme = new BitcoinExtPubKey(Network.GetBase58CheckEncoder().EncodeData(data), Network.Main).ToNetwork(Network).ToString(); if (ElectrumMappingDerivationType.TryGetValue(prefix, out var type)) { switch (type) { case DerivationType.Legacy: hintedLabels.Add("legacy"); break; case DerivationType.SegwitP2SH: hintedLabels.Add("p2sh"); break; } } parts[i] = derivationScheme; } catch { continue; } } if (hintDestination != null) { if (hintDestination is WitKeyId) { hintedLabels.Remove("legacy"); hintedLabels.Remove("p2sh"); } } str = string.Join('-', parts.Where(p => !IsLabel(p))); foreach (var label in hintedLabels) { str = $"{str}-[{label}]"; } return(FindMatch(hintedLabels, new DerivationStrategyFactory(Network).Parse(str))); }
public DerivationStrategyJsonConverter(DerivationStrategyFactory factory) { Factory = factory; }
// We will: // 1. Create a multi sig wallet of Alice and Bob // 2. Fund it with 1 BTC // 3. Send 0.4 BTC to a random address from it public static async Task Main(string[] args) { // Start bitcoind and NBXplorer in regtest: // * Run "bitcoind -regtest" // * Run ".\build.ps1", then ".\run.ps1 -regtest" in NBXplorer var network = Network.RegTest; var client = CreateNBXClient(network); // Now let's simulate alice and bob in a 2-2 multisig var alice = new Party(new Mnemonic(Wordlist.English), "Alice", new KeyPath("1'/2'/3'")); var bob = new Party(new Mnemonic(Wordlist.English), "Bob", new KeyPath("5'/2'/3'")); Console.WriteLine($"Alice should secretly save '{alice.Mnemonic}', and remember her password 'Alice'"); Console.WriteLine("---"); Console.WriteLine($"Alice should secretly save '{bob.Mnemonic}', and remember her password 'Bob'"); Console.WriteLine("---"); Console.WriteLine($"Alice should share '{alice.AccountExtPubKey.GetWif(network)}' with Bob"); Console.WriteLine("---"); Console.WriteLine($"Bob should share '{bob.AccountExtPubKey.GetWif(network)}' with Alice"); var factory = new DerivationStrategyFactory(network); var derivationStrategy = factory.CreateMultiSigDerivationStrategy(new[] { alice.AccountExtPubKey.GetWif(network), bob.AccountExtPubKey.GetWif(network) }, 2, new DerivationStrategyOptions() { ScriptPubKeyType = ScriptPubKeyType.SegwitP2SH }); Console.WriteLine("---"); Console.WriteLine($"The derivation strategy '{derivationStrategy}' represents all the data you need to know to track the multisig wallet"); // NBXplorer will start tracking this wallet. await client.TrackAsync(derivationStrategy); // This allow you to get events out of NBXPlorer var evts = client.CreateLongPollingNotificationSession(); // Now let's fund the wallet var address1 = (await client.GetUnusedAsync(derivationStrategy, DerivationFeature.Deposit)).Address; var rpc = new RPCClient(network); // If that fail, your bitcoin node need some bitcoins // bitcoin-cli -regtest getnewaddress // bitcoin-cli -regtest generatetoaddress 101 <address> await rpc.SendToAddressAsync(address1, Money.Coins(1.0m)); await WaitTransaction(evts, derivationStrategy); Console.WriteLine("---"); Console.WriteLine("Sent some money to the multi sig wallet"); Console.WriteLine("---"); // You can list transactions var txs = await client.GetTransactionsAsync(derivationStrategy); Console.WriteLine($"Number of unconf transactions: {txs.UnconfirmedTransactions.Transactions.Count}"); Console.WriteLine("---"); var balance = await client.GetBalanceAsync(derivationStrategy); Console.WriteLine($"Balance: {balance.Unconfirmed}"); Console.WriteLine("---"); var randomDestination = new Key().PubKey.GetAddress(ScriptPubKeyType.Segwit, network); var psbt = (await client.CreatePSBTAsync(derivationStrategy, new CreatePSBTRequest() { Destinations = { new CreatePSBTDestination() { Destination = randomDestination, Amount = Money.Coins(0.4m), SubstractFees = true // We will pay fee by sending to destination a bit less than 0.4 BTC } }, FeePreference = new FeePreference() { // 10 sat/byte. You can remove this in prod, as it will use bitcoin's core estimation. ExplicitFeeRate = new FeeRate(10.0m) } })).PSBT; var signedByAlice = Sign(alice, derivationStrategy, psbt); Console.WriteLine("---"); var signedByBob = Sign(bob, derivationStrategy, psbt); // OK both have signed var fullySignedPSBT = signedByAlice.Combine(signedByBob); fullySignedPSBT.Finalize(); var fullySignedTx = fullySignedPSBT.ExtractTransaction(); await client.BroadcastAsync(fullySignedTx); // Let's wait NBX receives the tx await WaitTransaction(evts, derivationStrategy); balance = await client.GetBalanceAsync(derivationStrategy); Console.WriteLine($"New balance: {balance.Unconfirmed}"); }
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()); }
public async Task <IActionResult> UpdateStore(string storeId, StoreViewModel model, string command) { if (!ModelState.IsValid) { return(View(model)); } var store = await _Repo.FindStore(storeId, GetUserId()); if (store == null) { return(NotFound()); } if (command == "Save") { bool needUpdate = false; if (store.SpeedPolicy != model.SpeedPolicy) { needUpdate = true; store.SpeedPolicy = model.SpeedPolicy; } if (store.StoreName != model.StoreName) { needUpdate = true; store.StoreName = model.StoreName; } if (store.StoreWebsite != model.StoreWebsite) { needUpdate = true; store.StoreWebsite = model.StoreWebsite; } if (store.DerivationStrategy != model.DerivationScheme) { needUpdate = true; try { var strategy = ParseDerivationStrategy(model.DerivationScheme); await _Wallet.TrackAsync(strategy); await _CallbackController.RegisterCallbackUriAsync(strategy, Request); store.DerivationStrategy = model.DerivationScheme; } catch { ModelState.AddModelError(nameof(model.DerivationScheme), "Invalid Derivation Scheme"); return(View(model)); } } if (needUpdate) { await _Repo.UpdateStore(store); StatusMessage = "Store successfully updated"; } return(RedirectToAction(nameof(UpdateStore), new { storeId = storeId })); } else { var facto = new DerivationStrategyFactory(_Network); var scheme = facto.Parse(model.DerivationScheme); var line = scheme.GetLineFor(DerivationFeature.Deposit); for (int i = 0; i < 10; i++) { var address = line.Derive((uint)i); model.AddressSamples.Add((line.Path.Derive((uint)i).ToString(), address.ScriptPubKey.GetDestinationAddress(_Network).ToString())); } return(View(model)); } }
public DerivationStrategyBase Parse(string str) { if (str == null) { throw new ArgumentNullException(nameof(str)); } str = str.Trim(); HashSet <string> hintedLabels = new HashSet <string>(); var hintDestination = HintScriptPubKey?.GetDestination(); if (hintDestination != null) { if (hintDestination is KeyId) { hintedLabels.Add("legacy"); } if (hintDestination is ScriptId) { hintedLabels.Add("p2sh"); } } else { if (Network.Consensus.SupportSegwit) { hintedLabels.Add("p2sh"); } else { hintedLabels.Add("legacy"); } } try { var result = new DerivationStrategyFactory(Network).Parse(str); return(FindMatch(hintedLabels, result)); } catch { } Dictionary <uint, string[]> electrumMapping = new Dictionary <uint, string[]>(); //Source https://github.com/spesmilo/electrum/blob/9edffd17542de5773e7284a8c8a2673c766bb3c3/lib/bitcoin.py var standard = 0x0488b21eU; electrumMapping.Add(standard, new[] { "legacy" }); var p2wpkh_p2sh = 0x049d7cb2U; electrumMapping.Add(p2wpkh_p2sh, new string[] { "p2sh" }); var p2wpkh = 0x4b24746U; electrumMapping.Add(p2wpkh, Array.Empty <string>()); var parts = str.Split('-'); for (int i = 0; i < parts.Length; i++) { if (IsLabel(parts[i])) { hintedLabels.Add(parts[i].Substring(1, parts[i].Length - 2).ToLowerInvariant()); continue; } try { var data = Network.GetBase58CheckEncoder().DecodeData(parts[i]); if (data.Length < 4) { continue; } var prefix = NU.ToUInt32(data, false); var standardPrefix = NU.ToBytes(Network.NetworkType == NetworkType.Mainnet ? 0x0488b21eU : 0x043587cf, false); for (int ii = 0; ii < 4; ii++) { data[ii] = standardPrefix[ii]; } var derivationScheme = new BitcoinExtPubKey(Network.GetBase58CheckEncoder().EncodeData(data), Network).ToString(); electrumMapping.TryGetValue(prefix, out string[] labels); if (labels != null) { foreach (var label in labels) { hintedLabels.Add(label.ToLowerInvariant()); } } parts[i] = derivationScheme; } catch { continue; } } if (hintDestination != null) { if (hintDestination is WitKeyId) { hintedLabels.Remove("legacy"); hintedLabels.Remove("p2sh"); } } str = string.Join('-', parts.Where(p => !IsLabel(p))); foreach (var label in hintedLabels) { str = $"{str}-[{label}]"; } return(FindMatch(hintedLabels, new DerivationStrategyFactory(Network).Parse(str))); }
public async Task <IActionResult> ConnectWebSocket( string cryptoCode, bool includeTransaction = true, CancellationToken cancellation = default) { if (!HttpContext.WebSockets.IsWebSocketRequest) { return(NotFound()); } GetNetwork(cryptoCode); // Internally check if cryptoCode is correct string listenAllDerivationSchemes = null; var listenedBlocks = new ConcurrentDictionary <string, string>(); var listenedDerivations = new ConcurrentDictionary <(Network, DerivationStrategyBase), DerivationStrategyBase>(); WebsocketMessageListener server = new WebsocketMessageListener(await HttpContext.WebSockets.AcceptWebSocketAsync(), _SerializerSettings); CompositeDisposable subscriptions = new CompositeDisposable(); subscriptions.Add(_EventAggregator.Subscribe <Events.NewBlockEvent>(async o => { if (listenedBlocks.ContainsKey(o.CryptoCode)) { var chain = ChainProvider.GetChain(o.CryptoCode); if (chain == null) { return; } var block = chain.GetBlock(o.BlockId); if (block != null) { await server.Send(new Models.NewBlockEvent() { CryptoCode = o.CryptoCode, Hash = block.HashBlock, Height = block.Height, PreviousBlockHash = block?.Previous.HashBlock }); } } })); subscriptions.Add(_EventAggregator.Subscribe <Events.NewTransactionMatchEvent>(async o => { var network = Waiters.GetWaiter(o.CryptoCode); if (network == null) { return; } if ( listenAllDerivationSchemes == "*" || listenAllDerivationSchemes == o.CryptoCode || listenedDerivations.ContainsKey((network.Network.NBitcoinNetwork, o.Match.DerivationStrategy))) { var chain = ChainProvider.GetChain(o.CryptoCode); if (chain == null) { return; } var blockHeader = o.BlockId == null ? null : chain.GetBlock(o.BlockId); await server.Send(new Models.NewTransactionEvent() { CryptoCode = o.CryptoCode, DerivationStrategy = o.Match.DerivationStrategy, BlockId = blockHeader?.HashBlock, TransactionData = ToTransactionResult(includeTransaction, chain, new[] { o.SavedTransaction }), Inputs = o.Match.Inputs, Outputs = o.Match.Outputs }); } })); try { while (server.Socket.State == WebSocketState.Open) { object message = await server.NextMessageAsync(cancellation); switch (message) { case Models.NewBlockEventRequest r: r.CryptoCode = r.CryptoCode ?? cryptoCode; listenedBlocks.TryAdd(r.CryptoCode, r.CryptoCode); break; case Models.NewTransactionEventRequest r: if (r.DerivationSchemes != null) { r.CryptoCode = r.CryptoCode ?? cryptoCode; var network = Waiters.GetWaiter(r.CryptoCode)?.Network; if (network == null) { break; } foreach (var derivation in r.DerivationSchemes) { var parsed = new DerivationStrategyFactory(network.NBitcoinNetwork).Parse(derivation); listenedDerivations.TryAdd((network.NBitcoinNetwork, parsed), parsed); } } else { listenAllDerivationSchemes = r.CryptoCode; } break; default: break; } } } catch when(server.Socket.State != WebSocketState.Open) { } finally { subscriptions.Dispose(); await server.DisposeAsync(cancellation); } return(new EmptyResult()); }
public async Task <IActionResult> ConnectWebSocket( string cryptoCode, bool includeTransaction = true, CancellationToken cancellation = default) { if (!HttpContext.WebSockets.IsWebSocketRequest) { return(NotFound()); } GetNetwork(cryptoCode, false); // Internally check if cryptoCode is correct string listenAllDerivationSchemes = null; string listenAllTrackedSource = null; var listenedBlocks = new ConcurrentDictionary <string, string>(); var listenedDerivations = new ConcurrentDictionary <(Network, DerivationStrategyBase), DerivationStrategyBase>(); var listenedTrackedSource = new ConcurrentDictionary <(Network, TrackedSource), TrackedSource>(); WebsocketMessageListener server = new WebsocketMessageListener(await HttpContext.WebSockets.AcceptWebSocketAsync(), _SerializerSettings); CompositeDisposable subscriptions = new CompositeDisposable(); subscriptions.Add(_EventAggregator.Subscribe <Models.NewBlockEvent>(async o => { if (listenedBlocks.ContainsKey(o.CryptoCode)) { await server.Send(o); } })); subscriptions.Add(_EventAggregator.Subscribe <Models.NewTransactionEvent>(async o => { var network = Waiters.GetWaiter(o.CryptoCode); if (network == null) { return; } bool forward = false; var derivationScheme = (o.TrackedSource as DerivationSchemeTrackedSource)?.DerivationStrategy; if (derivationScheme != null) { forward |= listenAllDerivationSchemes == "*" || listenAllDerivationSchemes == o.CryptoCode || listenedDerivations.ContainsKey((network.Network.NBitcoinNetwork, derivationScheme)); } forward |= listenAllTrackedSource == "*" || listenAllTrackedSource == o.CryptoCode || listenedTrackedSource.ContainsKey((network.Network.NBitcoinNetwork, o.TrackedSource)); if (forward) { var derivation = (o.TrackedSource as DerivationSchemeTrackedSource)?.DerivationStrategy; await server.Send(o); } })); try { while (server.Socket.State == WebSocketState.Open) { object message = await server.NextMessageAsync(cancellation); switch (message) { case Models.NewBlockEventRequest r: r.CryptoCode = r.CryptoCode ?? cryptoCode; listenedBlocks.TryAdd(r.CryptoCode, r.CryptoCode); break; case Models.NewTransactionEventRequest r: var network = Waiters.GetWaiter(r.CryptoCode)?.Network; if (r.DerivationSchemes != null) { r.CryptoCode = r.CryptoCode ?? cryptoCode; if (network != null) { foreach (var derivation in r.DerivationSchemes) { var parsed = new DerivationStrategyFactory(network.NBitcoinNetwork).Parse(derivation); listenedDerivations.TryAdd((network.NBitcoinNetwork, parsed), parsed); } } } else if ( // Back compat: If no derivation scheme precised and ListenAllDerivationSchemes not set, we listen all (r.TrackedSources == null && r.ListenAllDerivationSchemes == null) || (r.ListenAllDerivationSchemes != null && r.ListenAllDerivationSchemes.Value)) { listenAllDerivationSchemes = r.CryptoCode; } if (r.ListenAllTrackedSource != null && r.ListenAllTrackedSource.Value) { listenAllTrackedSource = r.CryptoCode; } else if (r.TrackedSources != null) { r.CryptoCode = r.CryptoCode ?? cryptoCode; if (network != null) { foreach (var trackedSource in r.TrackedSources) { if (TrackedSource.TryParse(trackedSource, out var parsed, network.NBitcoinNetwork)) { listenedTrackedSource.TryAdd((network.NBitcoinNetwork, parsed), parsed); } } } } break; default: break; } } } catch when(server.Socket.State != WebSocketState.Open) { } finally { subscriptions.Dispose(); await server.DisposeAsync(cancellation); } return(new EmptyResult()); }
public LiquidNBXplorerNetwork(INetworkSet networkSet, NetworkType networkType, DerivationStrategyFactory derivationStrategyFactory = null) : base(networkSet, networkType, derivationStrategyFactory) { }