Skip to content

Instantly share code, notes, and snippets.

@setavenger
Last active January 16, 2026 06:31
Show Gist options
  • Select an option

  • Save setavenger/a0cd7e71b47ded9fca9c99085130cf2a to your computer and use it in GitHub Desktop.

Select an option

Save setavenger/a0cd7e71b47ded9fca9c99085130cf2a to your computer and use it in GitHub Desktop.
Silent Payments notifications via Nostr - Designing notification messages via Nostr (and other channels) to eliminate scanning efforts for individual transactions.

Silent Payments notifications via Nostr

This write-up has the goal to discuss the approach of sending notifications for incoming Silent Payments transactions via Nostr. The design is an extension of the discussions around "Stealth addresses using nostr" which had a Nostr-only approach.

The design allows for notifications being sent outside of Nostr so for a clearer structure there is a Silent Payments and a Nostr part. The Silent Payments outlines why the schema is suggested as it is and the Nostr part discusses the using nostr as the communication layer for the notifications.

Silent Payments

Silent Payments offer a novel way for receiving payments without interactivity. But the way SP is designed it requires us to look for the needle(s) in the haystack(s).

The sender (Alice) knows which needles she placed in the haystacks. The receiver (Bob) needs to check everything to know what belongs to him!

The BIP already proposed out-of-band communication to somewhat alleviate this burden for the receiver. If the sender lets the receiver know that she made a payment to Bob in tx1 then Bob does not necessarily have to check tx2,3,...,n in a given block. If Bob thinks Carol or an unknown person sent him a payment without a notification he can always fall back to full scanning.

So how can we structure such a notification?

The minimum requirement for notifications to make sense is the txid. Then Bob can theoretically find all the relevant information to prove to himself that the notification is legitimate.

But ideally Alice sends more information. In order to fully compute the outputs "from scratch", Bob needs the previous scriptpubkeys. Those are not within the block data and in most cases are not trivial to retrieve. So Alice should also provide the tweak for the tx. This adds no burden to Alice as she had to compute tweak anyways to compute the output for Bob.

A confirmed blockhash/-height would be very useful for Bob to find the txid faster. This should only be optional though. Providing Bob with the confirming block is going to add a new burden on Alice. Alice then has to monitor the transaction and can only send the notification once she knows the confirming block.

Note: One could also add the relevant outputs to this as well. I'm not certain where I stand on this. The notification and the transaction should be verified. In that process the outputs would be most likely touched anyways. If Merkle proofs were used the confirming height/hash would be required.

Schema

The final content will look something like this:

{
    "txid": "5a45ff552ec2193faa2a964f7bbf99574786045f38248ea4a5ca1ff1166a1736",
    "tweak": "03464a0fdc066dc95f09ef85794ac86982de71875e513c758188b3f01c09e546fb",
    "blockhash": "94e561958b0270a6a0496fa8313712787dcacf91b3d546493aea0e7efce0fc45" // optional
}

*Note that the blockhash is optional and can be omitted by Alice. In that case Bob needs to check what the status of the transaction is.

Receiving the notification

This write-up tries to be as unopinionated as possible regarding wallet implementations. Ideally any source from full-node to light client + indexer should work with the design outlined.

One way how this could look: Upon receiving the notification Bob checks for the txid against his chain backend. Bob pulls the transaction and does the receiver-scan computations for the transaction. He already has the tweak so he needs no further external information. He computes the ECDH secret and finds out which outputs in the transaction belong to him. Then Bob's wallet can proceed as it would with any other found output.

Nostr

Using Nostr as a means of communication for the notification messages would be an option. Which Nostr messaging logic and which keys are used is quite important. The wrong approach could leak metadata and degrade privacy of Bob and possibly Alice. Many of the intricacies have been discussed in the post and comments of "Stealth addresses using nostr" on Delving Bitcoin. I have outlined the worst case below:

A bad design would be using NIP-04 direct messages. Using Alice's npub and Bob's scan key. NIP-04 leaks which npubs have had direct communication. The whole world would know that Alice has had interaction with Bob's scan key.

NIP-17 would conceal the sender but still show which npub has received some form of information. These notes could be anything from privately stored information to a message or notification. As there the sender is not linked these notes can be anything.

Which key does the receiver use?

Now that Alice's key does not matter anymore we will look at the keys for Bob. Several Options exist:

  1. Use the scan key
  2. Use a dedicated new key for the address
  3. Bob's existing npub that he uses for normal nostr activity, or any key with additional non-SP activity

(1) Using the scan key would be simple as it does not require additional handling of an npub for outside activity which is not related to the SP address. An immediate downside is that any Nostr activity for that npub would be a strong "payment received" signal for outside observers.

(2) A key which was created with the sole purpose of using it for receiving notification messages would have the same issues as (1).

(3) The additional activity would help conceal the strong "payment received" signal. The only issue would be if Bob wants to separate his Nostr identity and his SP address. But in that case it's probably better to fall back to full scanning for now.

It should be noted that one could design wallets in a way that Alice could copy-paste the above JSON to send via any messenger. Bob then imports the info into his wallet and the process would continue in the same way.

Relays

To briefly touch upon the relay part of Nostr. As there is no global state in Nostr Alice and Bob must have at least one relay in common. Otherwise Bob will never see the notification sent out by Alice. Therefore Bob should also advertise a list of recommended relays to avoid notifications ending up in the "void".

Final Design

Summarising the ideas outlined in this write-up.

Advertising

Bob advertises his Silent Payment address as a BIP321 URI string as he now already does. He attaches the optional tags for his npub and the relay list.

bitocin:?sp1qsilentpayment=&npub=npubbobsprofile&relays=wss://relay1.example.com,ws://relay2.example.com

The Content

Using the JSON defined in the Schema section above as the base.

{
    "txid": "5a45ff552ec2193faa2a964f7bbf99574786045f38248ea4a5ca1ff1166a1736",
    "tweak": "03464a0fdc066dc95f09ef85794ac86982de71875e513c758188b3f01c09e546fb",
    "blockhash": "94e561958b0270a6a0496fa8313712787dcacf91b3d546493aea0e7efce0fc45" // optional
}

Communication

Ideally wallet implementations would allow for an import of the above JSON data so notifications can be sent via any messaging layer. With the Nostr route Alice would send a NIP-17 Nostr DM to Bobs npub via at least one of the specified relays listed in the URI.

Checking the notification

How any wallet implementation verifies and uses the notification is not scope of this write-up. Several different designs for Silent Payments wallets exist. They handle the scanning and finding of UTXOs differently. Different data sources are used as each wallet makes its own privacy and performance trade-offs. Therefore every wallet should handle notifications in its own way to keep the trade-offs it wants.

Additional thoughts

The notification data could be attached in messaging clients and embedded similarly as it's currently done with Cashu tokens. Based on this a wallet could do automatic labelling of UTXOs in the wallet. This is also relatively safe. Only Alice knows which txid belongs to Bob. Faking this notification is basically impossible without Alice or Bob leaking info to third parties to begin with.

Closing remarks:

The design is influenced by "Stealth addresses using nostr". The desired UX (reduce scanning time) improvements of Stealth addresses are basically identical to the above design. But this design brings it back to Silent Payments where we have a global state - the blockhain - to which one can always fall back to. This was my main concern reading Stealth addresses.

@ismaelsadeeq
Copy link

ismaelsadeeq commented Jan 3, 2026

Running relays is not trivial and requires significant bandwidth. What are we trying to optimize for? compute at the expense of bandwidth?
How do we prevent potential spam to Alice, An adversary Bob who knows Alice’s npub and silent payment address, which are both public, could continuously run a targeted attack to Alice by lying that she has incoming payment without any consequences to him.
This would also cause Alice to waste the compute resources she is trying to optimize and avoid in the first place.

@RubenSomsen
Copy link

RubenSomsen commented Jan 6, 2026

I agree with the general sentiment of splitting up the relay from the rest of the protocol. It'd be nice if the design is reusable no matter what relay is chosen. I won't be commenting on any Nostr specific parts.

If Bob thinks Carol or an unknown person sent him a payment without a notification he can always fall back to full scanning.

More importantly, if the relay proves unreliable for whatever reason (spam as pointed out by @ismaelsadeeq, or missing notifications), money is not lost. The existence of the fallback is exactly what makes it possible to go with less reliable strategies like this. And the longer you wait with scanning, the higher your cut-through benefit.

Some observations on the data being transmitted:

There is a peculiar security problem with Bob supplying the tweak data - Bob's client could lazily provide incorrect tweak data that does result in a claimable payment but wasn't derived from the inputs. This means the payment will only be claimable as long as the communicated tweak isn't forgotten by Alice. Spending the output is one (inefficient) way to defend against this, but you'd probably prefer the tweak data to come from elsewhere. The problem could theoretically also occur with third party tweak data providers, but is far less likely since they are providing general data that isn't specific to you.

One approach would be to have the message that Bob communicates be absolutely minimal - Bob just provides the taproot output it wants you to scan. You then use the already established light client methods (see if it matches a filter, then download block + tweak data) to actually check whether this output is a payment to you.

The other route would be to maximize DoS resistance. Wait for the tx to confirm, provide the merkle path to the tx. If all inputs are taproot, then the scriptpubkeys can also be provided and checked against the signature (they're part of the sighash). For non-taproot the full transaction (matching the txid) could be provided, but this would potentially be a lot more data. Afaict this is the minimal amount of data that proves (SPV) correctness.

Alternatively, if the txid, vout, amount, and the (encrypted) shared secret (i.e. the private key part of everything except the spend key (+label)) was provided, then the output could be spent blindly provided the data is accurate. I'm not convinced this is practical though, since you'd need a wallet that deals with the ambiguity of having possibly fake coins. Observing a successful spend would be the evidence that the data was correct.

@setavenger
Copy link
Author

@ismaelsadeeq Thanks for reading through it. I had this reply ready for a while and hoped to come up with something for the last point. @RubenSomsen has mentioned some of the points in here already, but for sake of completeness I'm leaving this as is.


Running relays is not trivial and requires significant bandwidth. What are we trying to optimize for compute at the expense of bandwidth?

For Nostr it's not required that everyone runs their own personal relay.
The assumption in this design is that Alice and Bob use relays hosted by third parties.
If any of the two run their own relays this should be viewed as outside of design here.
Any relay can be used just as anyone can use a public/private electrum server or fullnode.

But for the sake of it let's assume for a moment that Bob is running his own relay.
If he were to only receive honest notifications, the compute would be strictly lower as he does not check transactions of no interest.
This also assumes he does not fallback to full scanning.
For the bandwidth we compare the notification message against pulling the tweaks + outputs.
The overall bandwidth difference depends on the amount of transactions which are received.
I don't have an exact byte number yet for the notification message, as this also depends on the encryption used.
But generally I can't imagine the notification message being larger than a client downloading tweaks and outputs for every block.

How do we prevent potential spam to Alice, Bob, or a third party?

Several rough ideas:

  • Relays implement spam filters. Something like one source sending too many events to it in short time span.
  • NIP-13 PoW, but it would be best to keep this in Bitcoin where the strongest PoW already exists
  • Based on the above point, one could change the logic that Alice needs to provide the txid + merkle proof.

Generally speaking:

  • If a Bob is risky or high profile target then the npub might already get spammed anyways
  • Nostr ecosystem already deals with this general issue - all Nostr-based chat apps, clients and services deal with it as well
  • Fallback is always possible! If the DoS attack vector is too large a fallback to the most secure layer Bitcoin with fullnode is always possible.
  • This design leans more towards the optimistic case.
  • For the pessimistic case you get spam protection through Bitcoin's PoW

I had the below section ready but could not make it work properly. The attempt to sufficiently fix a DoS attack would probably lead back to Bitcoin's PoW. Adding this nonetheless because maybe somebody reads this and can come up with something based on it.

By knowing Alice’s npub and silent payment address, which are both public , could continuously run a targetted attack to Alice by lying that she has incoming payment without any consequences to him. This would also cause Alice to waste the compute resources she is trying to optimize and avoid in the first place.

I'm going to flip Alice and Bob because I use Bob as the receiver in write-up.
This is very related to the point above. The combination of Merkle proof + ECDH secret + DLEQ can help us here.

  1. Merkle proof: The transaction actually exists
  2. ECDH secret + DLEQ: The sender of the notification actually owned the inputs
  • With this flow it would create a rather large cost for the sender of a notification to make use do significant work.
  • The DLEQ does not proof that the transaction contains an output that belongs to us.
  • It does however proof that some computation was made in the tx which belongs to us.
  • Only the owner of those UTXOs can make the proof.
  • It does require the client to somehow have easy access to the public keys of the Tx.
  • Could this be mitigated by comparing to the tweak of the TX?

@BullishNode
Copy link

I like this idea and interested to collaborate. I've also been thinking about this from a directory level, see: https://github.com/BullishNode/nostr-silent-payments-directory

@setavenger
Copy link
Author

Thanks everybody for commenting! I have moved the conversation to Delving Bitcoin to get bigger audience to chime in on the idea.

@BullishNode I briefly read through your directory idea. It seems to have a lot of overlap with what I arrived at as well. I'd be happy to talk more about how this could be merged or generally brought into the wild.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment