-
Notifications
You must be signed in to change notification settings - Fork 215
LBTC NBXplorer Doesn’t Detect On-Chain Events for Blinded Outputs #522
Description
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:
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
