damus
damus copied to clipboard
Add description and metadata to pay_invoice command
Summary
[!WARNING] Draft, do not merge yet
This PR adds zap-request information to the pay_invoice NWC command, in order for the NWC wallet to get the recipient information, which will help us get real people's names on the wallet transaction view.
For an understanding on why this is necessary to fix most of the "Unknown" entries on the wallet transactions view, please reference this diagram:
sequenceDiagram
participant NWC Provider
participant Client
participant Recipient LNURL server
participant Relays listed on zap request
Client->>Recipient LNURL server: Verify support and recipient info
Recipient LNURL server->>Client: Support and recipient info
Client->>Recipient LNURL server: NIP-57 `kind:9734` zap request
Recipient LNURL server->>Client: Lightning Invoice (description hash only)
Client->>NWC Provider: NIP-47 `pay_invoice` command (Invoice with description hash only)
NWC Provider->>Recipient LNURL server: Pay invoice
Recipient LNURL server->>Relays listed on zap request: NIP-57 `kind:9735` zap receipt (w/ embedded zap request)
Relays listed on zap request->>Client: NIP-57 `kind:9735` zap receipt (w/ embedded zap request)
Please see https://github.com/nostr-protocol/nips/issues/1843 for more details.
Checklist
- [x] I have read (or I am familiar with) the Contribution Guidelines
- [x] I have tested the changes in this PR
- [x] I have opened or referred to an existing github issue related to this change.
- [x] My PR is either small, or I have split it into smaller logical commits that are easier to review
- [x] I have added the signoff line to all my commits. See Signing off your work
- [x] I have added appropriate changelog entries for the changes in this PR. See Adding changelog entries
- [ ] I do not need to add a changelog entry. Reason: [Please provide a reason]
- [x] I have added appropriate
Closes:orFixes:tags in the commit messages wherever applicable, or made sure those are not needed. See Submitting patches
Test report
Please provide a test report for the changes in this PR. You can use the template below, but feel free to modify it as needed.
Device: iPhone SE simulator
iOS: 18.2
Damus: bdba6af44118c9af1e4bc81509b4dd35778edf71
Setup: Coinos NWC wallet attached
Steps:
- Send a zap to someone using the NWC wallet
- Check transactions list
- See if the profile of the zap recipient shows up.
Results:
- [ ] PASS
- [ ] Partial PASS
- Details: [Please provide details of the partial pass]
- [x] FAIL: It seems to successfully include the info, but does not seem like Coinos is storing it? Or perhaps it is malformed on our end? I will check with the Coinos dev team
Other notes
[Please provide any other information that you think is relevant to this PR.]
On Wed, Apr 23, 2025 at 07:47:20PM -0700, Daniel D’Aquino wrote:
Changelog-Added: Zap receiver information now included for outgoing zaps Closes: https://github.com/damus-io/damus/issues/2927 Signed-off-by: Daniel D’Aquino @.***>
Closes: https://github.com/damus-io/damus/pull/2992
LGTM!
Tested with a test AlbyHub wallet, and unfortunately I am not getting the desired effect (I still get "Unknown" for outgoing zap recipients). I haven't figured out whether Coinos and AlbyHub do not support this yet, or if I have made a mistake on my end. I am talking to the respective developers of those wallets to help narrow down the cause.
@jb55 please let me know if this looks good, or if you have any suggestions. Thank you!
On Wed, Apr 30, 2025 at 04:26:47PM -0700, Daniel D’Aquino wrote:
Makes it easier to work with other Swift types
Signed-off-by: Daniel D’Aquino @.***>
modifying this file only makes it harder to backport this to nostrdb until we have the update branch merged.
I'm not entirely sure we need all this code duplication just to verify a note during finalization. Can't we just verify after finalization? We just need to provide the
note = ndb_builder_finalize() id = ndb_calculate_id(note) ndb_note_verify(note->pubkey, id, note->sig) }
+/// Finalizes the ndb note builder, assuming the signature and pubkey have been manually set, and verifies if the signature is valid for that computed ID. +/// +/// This is useful when manually decoding an ndb note from another user, and verification of both id and signature is needed +int ndb_builder_finalize_verify(struct ndb_builder *builder, struct ndb_note **note) +{
- secp256k1_context *ctx;
- int strings_len = builder->strings.p - builder->strings.start;
- unsigned char *note_end = builder->note_cur.p + strings_len;
- int total_size = note_end - builder->note_cur.start;
- // move the strings buffer next to the end of our ndb_note
- memmove(builder->note_cur.p, builder->strings.start, strings_len);
- // set the strings location
- builder->note->strings = builder->note_cur.p - builder->note_cur.start;
- // record the total size
- //builder->note->size = total_size;
- *note = builder->note;
- // use the remaining memory for building our id buffer
- unsigned char *end = builder->mem.end;
- unsigned char start = (unsigned char)(*note) + total_size;
- if (!ndb_calculate_id(builder->note, start, end - start))
return 0;- // verify! If it's an invalid note we don't need to
- // bother finalizing it
- ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
- if (!ndb_note_verify(ctx, builder->note->pubkey, builder->note->id, builder->note->sig)) {
ndb_debug("note verification failed\n");return 0;- }
- // make sure we're aligned as a whole
- total_size = (total_size + 7) & ~7;
- assert((total_size % 8) == 0);
- return total_size; +}
struct ndb_note * ndb_builder_note(struct ndb_builder *builder) { return builder->note; diff --git a/nostrdb/nostrdb.h b/nostrdb/nostrdb.h index 483d3edc1..65d34c8f2 100644 --- a/nostrdb/nostrdb.h +++ b/nostrdb/nostrdb.h @@ -366,6 +366,7 @@ int ndb_ws_event_from_json(const char *json, int len, struct ndb_tce *tce, unsig int ndb_note_from_json(const char *json, int len, struct ndb_note **, unsigned char *buf, int buflen); int ndb_builder_init(struct ndb_builder *builder, unsigned char *buf, int bufsize); int ndb_builder_finalize(struct ndb_builder *builder, struct ndb_note **note, struct ndb_keypair *privkey); +int ndb_builder_finalize_verify(struct ndb_builder *builder, struct ndb_note **note); int ndb_builder_set_content(struct ndb_builder *builder, const char *content, int len); void ndb_builder_set_created_at(struct ndb_builder *builder, uint64_t created_at); void ndb_builder_set_sig(struct ndb_builder *builder, unsigned char *sig);
note = ndb_builder_finalize() id = ndb_calculate_id(note) ndb_note_verify(note->pubkey, id, note->sig)
@jb55, I initially wanted to do something like this, however I cannot do ndb_calculate_id(note); ndb_calculate_id has two other parameters that need to be filled in:
int ndb_calculate_id(struct ndb_note *note, unsigned char *buf, int buflen);
There is one usage of this function, which is ndb_builder_finalize. That function performs a lot of pointer arithmetic to arrive at these values. I can try to replicate that arithmetic in Swift, but it's very verbose and awkward to code that in Swift. Here is an (unfinished/untested) draft of that:
let builderPointer: UnsafeMutablePointer<ndb_builder> = UnsafeMutablePointer(mutating: &builder)
let stringsLen = builderPointer.pointee.strings.start.distance(to: builderPointer.pointee.strings.p)
let noteCur = builderPointer.pointee.note_cur
let noteEnd = noteCur.p.advanced(by: stringsLen)
var totalSize = noteCur.start.distance(to: noteEnd)
let memEnd = builderPointer.pointee.mem.end
let noteRawPtr = UnsafeMutableRawPointer(builderPointer.pointee.note)
let idBufferStart = noteRawPtr!.advanced(by: totalSize)
let memEndAddress: UInt = UInt(bitPattern: memEnd)
let idBufferStartAddress: UInt = UInt(bitPattern: idBufferStart)
let availableLength = Int(memEndAddress - idBufferStartAddress)
ndb_calculate_id(builderPointer.pointee.note, idBufferStart, Int32(availableLength))
(etc)
It's quite ugly, which is why I thought it would be better to do this part in C.
I can also try to access a lower level function directly to calculate the ID, such as:
void sha256(struct sha256 *sha, const void *p, size_t size)
But it's has sort of a similar problem, I am not sure what the expected format is for the buffer in *p. I imagine it needs to be in a specific format and I can't simply place a pointer to the finalized ndb_note?
Can you please advise on the best way to do this?
On Wed, May 07, 2025 at 07:11:31PM -0700, Daniel D’Aquino wrote:
danieldaquino left a comment (damus-io/damus#2992)
note = ndb_builder_finalize() id = ndb_calculate_id(note) ndb_note_verify(note->pubkey, id, note->sig)
@jb55, I initially wanted to do something like this, however I cannot do
ndb_calculate_id(note);ndb_calculate_idhas two other parameters that need to be filled in:int ndb_calculate_id(struct ndb_note *note, unsigned char *buf, int buflen);
This is just a scratch buffer so that the library doesn't have to allocate any memory to calculate the id. You can just create a 1mb array and then pass that:
let MAX_NOTE_SIZE = 2 << 18 // (512kb ?, 2 << 19 for 1 MiB)
let buflen = MAX_NOTE_SIZE
let buf = malloc(buflen)
ndb_calculate_id(note, buf, buflen)
free(buf)
There is one usage of this function, which is
ndb_builder_finalize. That function performs a lot of pointer arithmetic to arrive at these values. I can try to replicate that arithmetic in Swift, but it's very verbose and awkward to code that in Swift. Here is an (unfinished/untested) draft of that:let builderPointer: UnsafeMutablePointer<ndb_builder> = UnsafeMutablePointer(mutating: &builder) let stringsLen = builderPointer.pointee.strings.start.distance(to: builderPointer.pointee.strings.p) let noteCur = builderPointer.pointee.note_cur let noteEnd = noteCur.p.advanced(by: stringsLen) var totalSize = noteCur.start.distance(to: noteEnd) let memEnd = builderPointer.pointee.mem.end let noteRawPtr = UnsafeMutableRawPointer(builderPointer.pointee.note) let idBufferStart = noteRawPtr!.advanced(by: totalSize) let memEndAddress: UInt = UInt(bitPattern: memEnd) let idBufferStartAddress: UInt = UInt(bitPattern: idBufferStart) let availableLength = Int(memEndAddress - idBufferStartAddress) ndb_calculate_id(builderPointer.pointee.note, idBufferStart, Int32(availableLength)) (etc)It's quite ugly, which is why I thought it would be better to do this part in C.
I can also try to access a lower level function directly to calculate the ID, such as:
void sha256(struct sha256 *sha, const void *p, size_t size)But it's has sort of a similar problem, I am not sure what the expected format is for the buffer in
*p. I imagine it needs to be in a specific format and I can't simply place a pointer to the finalizedndb_note?Can you please advise on the best way to do this?
-- Reply to this email directly or view it on GitHub: https://github.com/damus-io/damus/pull/2992#issuecomment-2861254167 You are receiving this because you were mentioned.
Message ID: @.***>
This is just a scratch buffer so that the library doesn't have to allocate any memory to calculate the id. You can just create a 1mb array and then pass that
Thank you @jb55! I will implement that and update the PR.
@jb55, updated the PR, retested unit tests (which covers the new decoding code), and wallet transaction loading with AlbyHub. Please let me know if you have any other suggestions or concerns for these changes. Thank you!
Thank you!