Skip to content

LBTC NBXplorer Doesn’t Detect On-Chain Events for Blinded Outputs #522

@alejandroacho

Description

@alejandroacho

We are building a Liquid in regtest workflow using:

  • NBXplorer to fire OnChainEvent notifications for addresses derived from an xpub.
  • Elements node (via elements-cli) to blind, and sign transactions for those same addresses.

But we are facing some issues in order to be able to track blinded transactions with NBX.

Transactions with unblinded outputs works just fine, even if, for example, main output is blinded and the change one isn't. Transactions with plain P2WPKH outputs are detected by NBXplorer, which fires OnChainEvent notifications as expected. It doesn't seems to matter if we broadcast the transaction through CLI or through NBX, it fires the event.

But blinded outputs do not work. Once we blind all the outputs in Elements and broadcast, NBXplorer no longer recognizes the address and do not emits any events. We are generating a valid transaction that can be sent to the network through CLI but it won't trigger the event on NBX. If we try to broadcast the tx through NBX, the tx is sent to the network but NBX fails with the following error, and it does not trigger the event neither:

2025-05-27 14:32:06.450 | fail: Microsoft.AspNetCore.Server.Kestrel: Connection id "0HNCT38KKOVPU", Request id "0HNCT38KKOVPU:00000005": An unhandled exception was thrown by the application.
2025-05-27 14:32:06.450 | System.NullReferenceException: Object reference not set to an instance of an object.
2025-05-27 14:32:06.450 |    at NBitcoin.ExtPubKey.Derive(KeyPath derivation)
2025-05-27 14:32:06.450 |    at NBXplorer.DerivationStrategy.DirectDerivationStrategy.GetChild(KeyPath keyPath) in /source/NBXplorer.Client/DerivationStrategy/DirectDerivationStrategy.cs:line 55
2025-05-27 14:32:06.450 |    at NBXplorer.NBXplorerNetworkProvider.LiquidNBXplorerNetwork.GenerateBlindingKey(DerivationStrategyBase derivationStrategy, KeyPath keyPath, Script script, Network network) in /source/NBXplorer.Client/NBXplorerNetworkProvider.Liquid.cs:line 83
2025-05-27 14:32:06.450 |    at NBXplorer.Extensions.<>c__DisplayClass8_0.<UnblindTransaction>b__0(KeyPathInformation kv) in /source/NBXplorer/Extensions.cs:line 110
2025-05-27 14:32:06.450 |    at System.Linq.Enumerable.SelectArrayIterator`2.MoveNext()
2025-05-27 14:32:06.450 |    at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.ToList()
2025-05-27 14:32:06.450 |    at NBXplorer.Extensions.UnblindTransaction(RPCClient rpc, DerivationStrategyBase ts, ElementsTransaction tx, IEnumerable`1 keyInfos) in /source/NBXplorer/Extensions.cs:line 109
2025-05-27 14:32:06.450 |    at NBXplorer.Backend.Repository.ElementMatchContext.Unblind(RPCClient rpc, DerivationStrategyBase ts, ElementsTransaction tx, KeyPathInformation[] keyInfos) in /source/NBXplorer/Backend/Repository.cs:line 565
2025-05-27 14:32:06.450 |    at NBXplorer.Backend.Repository.ElementMatchContext.Unblind(DbConnection connection, RPCClient rpc, Dictionary`2 txs, MultiValueDictionary`2 allKeyInfos) in /source/NBXplorer/Backend/Repository.cs:line 548
2025-05-27 14:32:06.450 |    at NBXplorer.Backend.Repository.SaveMatches(DbConnectionHelper connection, MatchQuery matchQuery, IList`1 records, CancellationToken cancellationToken) in /source/NBXplorer/Backend/Repository.cs:line 689
2025-05-27 14:32:06.450 |    at NBXplorer.Backend.Repository.GetMatches(DbConnectionHelper connection, IList`1 txs, SlimChainedBlock slimBlock, DateTimeOffset now, Boolean useCache, CancellationToken cancellationToken) in /source/NBXplorer/Backend/Repository.cs:line 617
2025-05-27 14:32:06.450 |    at NBXplorer.Backend.Indexer.SaveMatches(DbConnectionHelper conn, List`1 transactions, SlimChainedBlock slimChainedBlock, Boolean fireEvents, Nullable`1 seenAt) in /source/NBXplorer/Backend/Indexer.cs:line 467
2025-05-27 14:32:06.450 |    at NBXplorer.Backend.Indexer.SaveMatches(Transaction transaction) in /source/NBXplorer/Backend/Indexer.cs:line 625
2025-05-27 14:32:06.450 |    at NBXplorer.Backend.Indexer.SaveMatches(Transaction transaction) in /source/NBXplorer/Backend/Indexer.cs:line 625
2025-05-27 14:32:06.450 |    at NBXplorer.Controllers.MainController.Broadcast(TrackedSourceContext trackedSourceContext, Boolean testMempoolAccept) in /source/NBXplorer/Controllers/MainController.cs:line 879
2025-05-27 14:32:06.450 |    at lambda_method251(Closure, Object)
2025-05-27 14:32:06.450 |    at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
2025-05-27 14:32:06.450 |    at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
2025-05-27 14:32:06.450 |    at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
2025-05-27 14:32:06.450 |    at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
2025-05-27 14:32:06.450 |    at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
2025-05-27 14:32:06.450 |    at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
2025-05-27 14:32:06.450 |    at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|26_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
2025-05-27 14:32:06.450 |    at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ExceptionContextSealed context)
2025-05-27 14:32:06.450 |    at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
2025-05-27 14:32:06.450 |    at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
2025-05-27 14:32:06.450 |    at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
2025-05-27 14:32:06.450 |    at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
2025-05-27 14:32:06.450 |    at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
2025-05-27 14:32:06.450 |    at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
2025-05-27 14:32:06.450 |    at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)
2025-05-27 14:32:06.464 | fail: NBXplorer.Indexer.LBTC: Unhandled exception in the indexer, retrying in 20 seconds
2025-05-27 14:32:06.464 | System.NullReferenceException: Object reference not set to an instance of an object.
2025-05-27 14:32:06.464 |    at NBitcoin.ExtPubKey.Derive(KeyPath derivation)
2025-05-27 14:32:06.464 |    at NBXplorer.DerivationStrategy.DirectDerivationStrategy.GetChild(KeyPath keyPath) in /source/NBXplorer.Client/DerivationStrategy/DirectDerivationStrategy.cs:line 55
2025-05-27 14:32:06.464 |    at NBXplorer.NBXplorerNetworkProvider.LiquidNBXplorerNetwork.GenerateBlindingKey(DerivationStrategyBase derivationStrategy, KeyPath keyPath, Script script, Network network) in /source/NBXplorer.Client/NBXplorerNetworkProvider.Liquid.cs:line 83
2025-05-27 14:32:06.464 |    at NBXplorer.Extensions.<>c__DisplayClass8_0.<UnblindTransaction>b__0(KeyPathInformation kv) in /source/NBXplorer/Extensions.cs:line 110
2025-05-27 14:32:06.464 |    at System.Linq.Enumerable.SelectArrayIterator`2.MoveNext()
2025-05-27 14:32:06.464 |    at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.ToList()
2025-05-27 14:32:06.464 |    at NBXplorer.Extensions.UnblindTransaction(RPCClient rpc, DerivationStrategyBase ts, ElementsTransaction tx, IEnumerable`1 keyInfos) in /source/NBXplorer/Extensions.cs:line 109
2025-05-27 14:32:06.464 |    at NBXplorer.Backend.Repository.ElementMatchContext.Unblind(RPCClient rpc, DerivationStrategyBase ts, ElementsTransaction tx, KeyPathInformation[] keyInfos) in /source/NBXplorer/Backend/Repository.cs:line 565
2025-05-27 14:32:06.464 |    at NBXplorer.Backend.Repository.ElementMatchContext.Unblind(DbConnection connection, RPCClient rpc, Dictionary`2 txs, MultiValueDictionary`2 allKeyInfos) in /source/NBXplorer/Backend/Repository.cs:line 548
2025-05-27 14:32:06.464 |    at NBXplorer.Backend.Repository.SaveMatches(DbConnectionHelper connection, MatchQuery matchQuery, IList`1 records, CancellationToken cancellationToken) in /source/NBXplorer/Backend/Repository.cs:line 689
2025-05-27 14:32:06.464 |    at NBXplorer.Backend.Repository.GetMatches(DbConnectionHelper connection, IList`1 txs, SlimChainedBlock slimBlock, DateTimeOffset now, Boolean useCache, CancellationToken cancellationToken) in /source/NBXplorer/Backend/Repository.cs:line 617
2025-05-27 14:32:06.464 |    at NBXplorer.Backend.Indexer.SaveMatches(DbConnectionHelper conn, List`1 transactions, SlimChainedBlock slimChainedBlock, Boolean fireEvents, Nullable`1 seenAt) in /source/NBXplorer/Backend/Indexer.cs:line 467
2025-05-27 14:32:06.464 |    at NBXplorer.Backend.Indexer.IndexerLoopCore(CancellationToken token) in /source/NBXplorer/Backend/Indexer.cs:line 220
2025-05-27 14:32:06.464 |    at NBXplorer.Backend.Indexer.IndexerLoopCore(CancellationToken token) in /source/NBXplorer/Backend/Indexer.cs:line 220
2025-05-27 14:32:06.464 |    at NBXplorer.Backend.Indexer.IndexerLoopCore(CancellationToken token) in /source/NBXplorer/Backend/Indexer.cs:line 148
2025-05-27 14:32:06.464 |    at NBXplorer.Backend.Indexer.IndexerLoop() in /source/NBXplorer/Backend/Indexer.cs:line 95
2025-05-27 14:32:26.468 | info: NBXplorer.Events: LBTC: Node state changed: Ready => NotStarted
2025-05-27 14:32:26.478 | info: NBXplorer.Indexer.LBTC: TCP Connection succeed, handshaking...
2025-05-27 14:32:26.480 | info: NBXplorer.Indexer.LBTC: Handshaked
2025-05-27 14:32:26.480 | info: NBXplorer.Indexer.LBTC: Testing RPC connection to http://elements:18884/wallet/main
2025-05-27 14:32:26.482 | info: NBXplorer.Indexer.LBTC: RPC connection successful
2025-05-27 14:32:26.488 | info: NBXplorer.Indexer.LBTC: Full node version detected: 230204
2025-05-27 14:32:26.490 | info: NBXplorer.Indexer.LBTC: Has txindex support
2025-05-27 14:32:26.492 | info: NBXplorer.Indexer.LBTC: NBXplorer is correctly whitelisted by the node
2025-05-27 14:32:26.526 | info: NBXplorer.Events: LBTC: Node state changed: NotStarted => NBXplorerSynching
2025-05-27 14:32:26.533 | info: NBXplorer.Indexer.LBTC: Node disconnected (Creating new connection)
2025-05-27 14:32:26.534 | info: NBXplorer.Indexer.LBTC: Node disconnected ()
2025-05-27 14:32:26.541 | info: NBXplorer.Events: LBTC: Node state changed: NBXplorerSynching => NotStarted
2025-05-27 14:32:26.548 | fail: NBXplorer.Indexer.LBTC: Unhandled exception in the indexer, retrying in 25 seconds
2025-05-27 14:32:26.548 | System.OperationCanceledException: The peer has been disconnected
2025-05-27 14:32:26.548 |    at NBXplorer.Backend.Indexer.AskNextHeaders(Node node, CancellationToken token) in /source/NBXplorer/Backend/Indexer.cs:line 382
2025-05-27 14:32:26.548 |    at NBXplorer.Backend.Indexer.ConnectNode(CancellationToken token) in /source/NBXplorer/Backend/Indexer.cs:line 363
2025-05-27 14:32:26.548 |    at NBXplorer.Backend.Indexer.IndexerLoopCore(CancellationToken token) in /source/NBXplorer/Backend/Indexer.cs:line 146
2025-05-27 14:32:26.548 |    at NBXplorer.Backend.Indexer.IndexerLoop() in /source/NBXplorer/Backend/Indexer.cs:line 95
2025-05-27 14:32:51.560 | info: NBXplorer.Indexer.LBTC: TCP Connection succeed, handshaking...
2025-05-27 14:32:51.564 | info: NBXplorer.Indexer.LBTC: Handshaked
2025-05-27 14:32:51.564 | info: NBXplorer.Indexer.LBTC: Testing RPC connection to http://elements:18884/wallet/main
2025-05-27 14:32:51.568 | info: NBXplorer.Indexer.LBTC: RPC connection successful
2025-05-27 14:32:51.578 | info: NBXplorer.Indexer.LBTC: Full node version detected: 230204
2025-05-27 14:32:51.579 | info: NBXplorer.Indexer.LBTC: Has txindex support
2025-05-27 14:32:51.581 | info: NBXplorer.Indexer.LBTC: NBXplorer is correctly whitelisted by the node
2025-05-27 14:32:51.590 | info: NBXplorer.Events: LBTC: Node state changed: NotStarted => NBXplorerSynching
2025-05-27 14:32:51.624 | info: NBXplorer.Events: LBTC: Node state changed: NBXplorerSynching => Ready

At this point we trie two things. The first one was to change the way we generate the change address. We tried a different approach (currently we are using an address created through CLI):

  • Create the address with NBX which results in an address that the Elements node doesn't recognize as its own, so it can be signed nor blinded. We tried also to import the descriptor given from NBX with the new address but we can not import descriptors without private keys in our wallet.

We tried also giving NBXplorer a full descriptor (fingerprint + path + tpub, with or without wpkh()), via its HTTP API and via config. But NBXplorer rejects everything except the bare tpub (no path), returning a 404 with no useful error message:

We tried several schemas but only plain tpub (eg: tpubDDmKqdJPsmbr8WjATSrRfyVijKd7rT34D6UxyBKDLSJKYLiPDfqqnakAv1MZ93S3yNsYMfhWRUKh1D1yHya5u3FxYE3pjD22YwuHvXbzeSu) is accepted:

Image


Docker config and version:

  nbxplorer:
    image: nicolasdorier/nbxplorer:2.5.17
    container_name: 40swap_nbxplorer
    ports:
      - 32838:32838
    depends_on:
      postgres:
        condition: service_healthy
      bitcoind:
        condition: service_started
    restart: always
    environment:
      NBXPLORER_NETWORK: regtest
      NBXPLORER_BIND: 0.0.0.0:32838
      NBXPLORER_NOAUTH: 1
      NBXPLORER_CHAINS: "btc,lbtc"
      NBXPLORER_BTCRPCURL: http://bitcoind:18443/
      NBXPLORER_BTCNODEENDPOINT: bitcoind:18333
      NBXPLORER_POSTGRES: Host=postgres;Port=5432;Database=nbxplorer;Username=40swap;Password=40swap
      NBXPLORER_BTCRPCUSER: "40swap"
      NBXPLORER_BTCRPCPASSWORD: "pass"
      NBXPLORER_MAXGAPSIZE: 400
      NBXPLORER_MINGAPSIZE: 300
      NBXPLORER_LBTCRPCURL: http://elements:18884/wallet/main
      NBXPLORER_LBTCNODEENDPOINT: elements:18886
      NBXPLORER_LBTCRPCUSER: "40swap"
      NBXPLORER_LBTCRPCPASSWORD: "pass"
    volumes:
      - "nbxplorer-data:/datadir"
      - "bitcoind-data:/root/.bitcoin"
    links:
      - bitcoind
      - elements

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions