Conversation
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
|
Good feature! (I appreciate the +1s, but they don't make me any more likely to merge something. In fact, they're kind of annoying, so they make me less likely to review and merge something 😅 I've marked them as spam. Please use the react button instead.) I like this a lot, and I think it's a great feature. Some minor thoughts:
My biggest piece of feedback is that I think it's confusing and duplicative for memos to be so close to parent-child but not use parent-child. They have so many similarities, namely that the transaction that inscribes a memo must also include the target as an input, and that the memo has a field which encodes the transaction ID of the target in the same way as the parent field encodes the transaction ID of the parent. Also, the current design permits the quite odd scenario of an inscription which is simultaneously a memo and a child. I think the same end result could be achieved in a simpler way if we added a new odd tag which was "flags" field. The first defined flag would be a single bit which, if set, would not include the inscription in lists of children for any of its its parents, for any purpose, we would also add the ability to query hidden children only, and also the ability to burn inscriptions immediately on creation. This would have the additional advantage of breaking the feature into three separate smaller PRs, each of which is independently useful, and which are easier to review and merge. The three PRs would be:
We could still call hidden child inscriptions "notes" or "memos", and the UX would be, I think for all practical purposes the same, just decomposed into three complementary features which are independently useful. |
|
@casey thanks for the feedback! I actually wanted to call the feature Notes, but noticed there is Those 3 separate PR ideas are great and should get us exactly what we need! I will give it a go, see if I can reproduce this setup locally. |
|
@bruffstar Oh yeah, the note field. I think that can be ignored. The note tag is there to attach data to an inscription that is guaranteed to be ignored by ord. It's not user-facing, so I think it's fine to recycle the name. Also, I just had the thought that we might not even need to add the ability to burn inscriptions on creation. We could just give them field 66, which guarantees that they are unbound, so they are never associated with any UTXO and thus don't clutter the wallet. This is much simpler than having to burn them on creation, since we don't have to create a new output and make sure they land in that new output, they just get a new tag. I would start with just the first PR, the |
Would the child still list its parents? That is, is the hiding only in the parent -> child direction, but not in the opposite direction? Maybe there's an extra bit to say whether the hiding happens in both directions |
|
I would like to push back on the idea of "hidden" children. Something like that really blurs the line between provenance concern and UI/UX concern. These things tend to then get muddy over time. IMO, a better implementation for something like this would be along the lines of #3025 or #4268. Entirely separate from parent/child. Additionally, it could be more open and let anyone send memos to any inscription. But then add the ability to specifically query ones that carry some provenance. |
|
I would really worry about spam if it was open to anyone. The idea at the start was only similar to parent child in that we include the target as an input, but feel this is now skewing away from what we want to achieve, which is appending contextual data over time to owned inscriptions. To avoid any confusion with parent/child, then it goes back to my original approach above which seperated concerns as "memos" (or notes, whatever we call them). |
|
I would not worry about spam at all. This is no different than inscribing into someone else's wallet. There's a real cost involved. Just that the UI should not be exposing it on inscription page. Underlying concept here is being able to attach inscriptions/data to existing inscriptions and be able to query them. Provenance layer is a subset of it. |
|
I would prefer Casey's hidden children approach for its simplicity, but I think reusing parent/child for this may cause confusion down the line. Keeping it as a separate tag feels cleaner, which is what this PR already does. But I disagree on open access. The use case isn't "attach data to someone else's inscription", it's an owner appending data to their own inscription over time. Settings, game state, provenance records, votes. The inscription reads its own notes and renders from them. If anyone can write, the target can't trust the data at its own endpoints without verifying provenance on every read. With owner only, it handles that at the protocol level. I think I came up with a pretty elegant and unobtrusive way of displaying these in the ord explorer as well if you check out the demos. Recursive endpoints follow same pattern as r/sat/ as well. It's also not comparable to inscribing into someone's wallet. Anything touching the wallet is exactly what we're trying to avoid here. Notes are burned, no UTXO, no wallet impact, that's the whole point. I'd propose keeping it as a dedicated tag, separate from parent/child, owner-only. Happy to adjust the tag number and naming per Casey's feedback. |
I think this value of this feature is specifically that it's gated to the inscription owner, offerings are open to anyone.
I'm definitely sympathetic to this line of thinking, but I can't really think of any concrete problems that hidden children would have, and it makes the feature much simpler. |
Concern is primarily complexity in muddling provenance and UI concerns. "Hidden" is a purely UI thing, and would not carry any meaning outside that context. It's implementation detail (for keeping it simple) leaking into the protocol. |
|
I don't actually think it's muddling them. Basically, people want to be able to create child inscriptions (can only be created by inscription holder, are inscriptions that can carry data in all the ways that inscriptions can carry data), but they want to do so to modify the original inscription through its recursive queries, and don't want the child to appear in the list of child inscrtipions. So a note is an inscription and does have provenance, because the holder created it, but shouldn't show up in the list of children. So the minimal implementation of this feature is a hidden flag. |
|
In that case, I think @bruffstar's original implementation of using a separate tag seems conceptually much cleaner/simpler. Because Something like a I'll end my bikeshedding with that! |
|
These are good questions:
They should appear as inscriptions, and there should be a back-link to the parent, but they should not appear in the parents list of children, or appear when searching for children of a given parent. They should appear in wallets, and they should be tradable, since they're inscriptions. |
I think if "notes" appear in wallets and are tradable, they are just children with a display filter and that's exactly the wallet pollution problem this feature is trying to solve. If I send 100 game moves as notes, I don't want 100 inscriptions sitting in my wallet. The owner shouldn't have to manage, ignore, or want to sell them. The idea would be fire and forget, the data lives on-chain, the inscription reads it, and the owner's wallet stays clean. In this PR they burn to OP_RETURN (and show burn charm, which I also like). Tag 66 idea would also work, unbound, but may be a bit more confusing. Either way, notes not being in the wallet is what makes this useful. |
|
What I mean by "they should be tradable" is that if they're in your wallet, you can see them and trade them. This can be avoided by burning them or tagging them with tag 66, which makes them untradable. This is orthogonal to whether or not they are child inscriptions or not. |
|
Agreed, the parent/child question and the burn question are separate. My concern with the hidden children route is that it layers multiple features to get one behavior. A hidden flag, plus tag 66 or burn, plus filtered children endpoints. Each piece isn't really useful on its own (except burn feature), a hidden child that isn't burned is just a child with a broken UI. They kind of only make sense combined. A dedicated tag gives you the "Note" identity, the indexing, and the endpoints in one concept. The recursive endpoints are also very clean... I wanted to use parent/child when I was trying this, but I landed on this setup. Notes use the same ownership mechanic as parent/child, spending the target as an input. But that's where the similarity should end. Same proof but with different purpose and understandable endpoints. |
Summary
Memos allow the owner of an inscription to attach append-only data to it. A memo is itself an inscription, linked to its target via tag
27, and burned on creation by sending it to anOP_RETURNoutput. Because the memo is burned, it does not remain in the creator's wallet. The content remains permanently readable on-chain.This is useful for applications that need to update state over time without reinscribing or creating child inscriptions - things like settings, game moves, provenance records, voting, and versioned app updates.
How it works
27contains the serialized binary inscription ID of the targetOP_RETURNoutput with 1 sat27is odd, so memos are backwards compatible with older versions ofordChanges
Tag & envelope parsing
Tag::Memo = 27with chunked value supportInscription::memo_target()decodes the tag toInscriptionIdIndexing
SEQUENCE_NUMBER_TO_MEMOSmultimap table (schema version 34 -> 35)Recursive endpoints
GET /r/memos/<ID>- first page of memo IDsGET /r/memos/<ID>/<PAGE>- paginated memo IDsGET /r/memos/<ID>/at/<INDEX>- memo at index (supports negative)GET /r/memos/<ID>/at/<INDEX>/content- memo content at index (proxied)Wallet CLI
ord wallet inscribe --memo <INSCRIPTION_ID> --file <FILE>--memocannot be combined with--parent(an inscription is either a memo or a child, not both)--delegatefor content-free memos that resolve through a delegateExplorer
/memos/<ID>on target inscriptions (e.g.all (2))/memos/<ID>browser page listing all memos for an inscriptionDocs
inscriptions/memos.mdcovering spec, CLI, delegate memos, and notesinscriptions/recursion.mdTests
memo_is_parsed_correctly- tag 27 parsed to correct inscription IDmemo_is_parsed_correctly_from_chunks- chunked tag 27 worksmemos_recursive_endpoint-/r/memos/<ID>returns correct memo list with paginationmemo_at_index-/r/memos/<ID>/at/<INDEX>with positive and negative indexingmemo_at_index_content-/r/memos/<ID>/at/<INDEX>/contentserves memo contentmemo_without_valid_target_is_ignored- memo with unspent target is not indexedmemo_content_readable_after_burn- burned memo content is still accessiblememo_proxy- memo content proxied correctly through recursive endpointNote
This PR was created with assistance from Claude Code. Please review thoroughly.