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 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.
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.
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.
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.
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.
Now that Alice's key does not matter anymore we will look at the keys for Bob. Several Options exist:
- Use the scan key
- Use a dedicated new key for the address
- 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.
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".
Summarising the ideas outlined in this write-up.
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
Using the JSON defined in the Schema section above as the base.
{
"txid": "5a45ff552ec2193faa2a964f7bbf99574786045f38248ea4a5ca1ff1166a1736",
"tweak": "03464a0fdc066dc95f09ef85794ac86982de71875e513c758188b3f01c09e546fb",
"blockhash": "94e561958b0270a6a0496fa8313712787dcacf91b3d546493aea0e7efce0fc45" // optional
}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.
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.
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.
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.
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.
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.