Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provenance spec #2278

Merged
merged 4 commits into from
Sep 4, 2023
Merged

Provenance spec #2278

merged 4 commits into from
Sep 4, 2023

Conversation

casey
Copy link
Collaborator

@casey casey commented Jul 15, 2023

NB: THIS SPEC IS NOT FINAL, NOT IMPLEMENTED, AND IS SUBJECT TO CHANGE AT ANY
TIME. IT SHOULD NOT BE CONSIDERED FINAL UNTIL IT MAKE IT INTO A PUBLISHED,
NUMBERED RELEASE OF ord. IF YOU CREATE INSCRIPTIONS BASED ON THIS IN-PROGRESS
SPEC, IT WILL PROBABLY CHANGE AND YOU WILL PROBABLY GET REKT.

Add a draft provenance spec. Comments and suggestions are most welcome!

docs/src/inscriptions.md Outdated Show resolved Hide resolved
@casey casey force-pushed the collections-spec branch 2 times, most recently from bdd99da to b8a9f95 Compare July 15, 2023 23:50
@casey casey mentioned this pull request Jul 16, 2023
@timechainz
Copy link

Thank you @casey! I am so stoked for this feature! Could you help me understand why the OP_PUSH of the parent inscription id is necessary? If the indexer is validating that the parent "must be spent as one of the inputs of the inscribe transaction" couldn't the parent inscription ids be derived rather than explicitly provided? Seems like this could be omitted and reduce the on-chain footprint.

Is the idea that there could be parent inscriptions that are not desired to be listed as parent inscriptions (e.g. multi-inscription scenario)? In that case, couldn't the OP_PUSH simply reference the index of the parent in the commit tx rather than the whole inscription id?

@casey
Copy link
Collaborator Author

casey commented Jul 16, 2023

Thank you @casey! I am so stoked for this feature! Could you help me understand why the OP_PUSH of the parent inscription id is necessary? If the indexer is validating that the parent "must be spent as one of the inputs of the inscribe transaction" couldn't the parent inscription ids be derived rather than explicitly provided? Seems like this could be omitted and reduce the on-chain footprint.

This is two avoid ambiguity in case there is more than one inscription in the inputs, especially in the case of reinscription. Also in the case that there is an inscription in the inputs that you don't see, for example one created by a newer version of ord, which would become a parent.

Is the idea that there could be parent inscriptions that are not desired to be listed as parent inscriptions (e.g. multi-inscription scenario)? In that case, couldn't the OP_PUSH simply reference the index of the parent in the commit tx rather than the whole inscription id?

This is definitely possible, but similar to above, this makes creating child inscriptions sensitive to changes in the number of inscriptions in the inputs, which can happen due to, e.g., previously invalid inscriptions becoming recognized, old versions of ord, or reinscription.

I think the 32 bytes are a small price to pay to avoid ambiguity and not have to think about any of these cases.

@timechainz
Copy link

I think the 32 bytes are a small price to pay to avoid ambiguity and not have to think about any of these cases.

Makes sense. Thanks for walking me through it!

@cypherpork
Copy link

@casey, thank you for putting the PR together.

We created a collection last week based on the info we assembled from your various communications on the subject.

The way it looks – the collection follows the current spec, right? If so, it could be useful for examples of sorts.

graph TB
a(R</br> </br> Author's Root) --> b(CR </br> </br> Collection Root) --> c(A1 </br> </br> First </br> </br> Collection Member </br> </br> Inscription 1)
b(CR </br> </br> Collection Root) --> d(A2 </br> </br> Second  </br> </br> Collection Member </br> </br> Inscription 2)
b(CR </br> </br> Collection Root) --> e(A3 </br> </br> Third  </br> </br> Collection Member </br> </br> Inscription 3)

Loading

The Author's Root's genesis transaction output (296122c...:0) becomes the second input (i1) for the genesis transaction of Collection Root.

The genesis transaction for the First Collection Member ("A1") uses the Collection Root's transaction output 179f1...:1 as the second input. You can find it in the second input (i1).

The Second Collection Member ("A2"), similar to the first collection member, uses the current transaction output of their Collection Root, which is now 5b44c01...:1 after the creation of the first collection member in its second input (i1).

A3 follows the same logic.

- Spend the parent P in one of the inputs of T.
- Include tag tag `3`, i.e. `OP_PUSH 3`, with the value of the serialized
inscription ID of P, `TXIDiINDEX`, serialized as the 32 byte binary `TXID`,
followed by the little-endian `INDEX`, with trailing zero bytes omitted.
Copy link

@cypherpork cypherpork Jul 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using little-endian in combination with the term trailing creates ambiguity, since little-endian is "backwards" and trailing for it could be thought of as left-to-right.
It is clear that for address 75f607b453ff61f63ed614c6476127f89687397488bbe6c49b151fd867f8ef5ci0 we should be recording tag3 as 5ceff867d81f159bc4e6bb8874398796f8276147c614d63ef661ff53b407f675.

But what if it is 75f607b453ff61f63ed614c6476127f89687397488bbe6c49b151fd867f8ef5ci31?

Should it be 5ceff867d81f159bc4e6bb8874398796f8276147c614d63ef661ff53b407f6751f or 5ceff867d81f159bc4e6bb8874398796f8276147c614d63ef661ff53b407f6750000001f?

It will help if there are examples here with non-zero indexes.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I included some examples with non-zero indices, check them out!

@casey casey force-pushed the collections-spec branch 2 times, most recently from 6d2228a to 9886db6 Compare July 19, 2023 22:43
@cypherpork
Copy link

cypherpork commented Jul 31, 2023

Using the results of ord index export as a driver of parsing code, I could parse genesis transactions from the beginning through Friday night to check the usage of any odd number tags other than tag 01.

People use tags 03 and 15.

Tag 03

I found several different styles of Tag 03:

  • Style 5, used to record rarity traits, utf8-encoded JSON-ish string.

    Example:

    Mainnet inscription ab25e70ffb4f561d863133f46f878565d0c526983ca70139ebfb8420eb90676ai0 has a tag 3 payload: 7b22747261697473223a5b7b2274726169745f74797065223a224261636b67726f756e64222c2276616c7565223a225468726f6e6520526f6f6d227d2c7b2274726169745f74797065223a2245796573222c2276616c7565223a22476c6f77227d2c7b2274726169745f74797065223a224d6f757468222c2276616c7565223a224d6568227d2c7b2274726169745f74797065223a2248616972222c2276616c7565223a224e656f6e2053747265616b73227d2c7b2274726169745f74797065223a22436c6f74686573222c2276616c7565223a224e696e6a6120546174746f6f227d5d7d, which decodes to rarity traits: {"traits":[{"trait_type":"Background","value":"Throne Room"},{"trait_type":"Eyes","value":"Glow"},{"trait_type":"Mouth","value":"Meh"},{"trait_type":"Hair","value":"Neon Streaks"},{"trait_type":"Clothes","value":"Ninja Tattoo"}]}

    The remainder of the styles are used to record provenance/collections.

  • Style 2, utf8-encoded big-endian parent inscription id with non-omitted zero index.

    Example:

    Mainnet inscription 1bcec9cc0e8a69f07e2303dbe826db436d4f3b2166d90afb0b060d2cd67f560ci0 has a tag 3 payload: 386431353335336636623366396539333264326430656331373964626262383438373231633361356262383834383165386136666665306231613639343163646930, which decodes to the parent ID: 8d15353f6b3f9e932d2d0ec179dbbb848721c3a5bb88481e8a6ffe0b1a6941cdi0

  • Style 3, binary-encoded little-endian parent transaction inscription id,

    • concatenated with 00000001 if the inscription has a grandparent,

    • concatenated with 00000000 if the inscription has only a parent (no grandparent).

      Example:

      Mainnet inscription 0baae63004061634cf74dd4a017e8695c582257c0e369dce12f38ed1e0afb0ffi0 has a tag 3 payload: dc3db76be046384a684a13dd03f3cdf43371975bc321db44140bea7ae5d084b800000001 which points to its parent b884d0e57aea0b1444db21c35b977133f4cdf303dd134a684a3846e06bb73ddc, which, in its turn, has a tag 3 payload: 6c8d5c24f5b943557793e17128b4cc312c9ad3e0f05f25e4be32ccc976f1c86d00000000 pointing to its parent 6dc8f176c9cc32bee4255ff0e0d39a2c31ccb42871e193775543b9f5245c8d6c

    Style 4, binary-encoded parent transaction inscription id in little-endian notation with omitted zero index.

    Example:

    Testnet inscription 1f96de0d3c75d32b7de72181141718aef2403884a3d48bc0585c04cfcb51178ai0 has a tag 3 payload: 1fc2fb3cfd8eadc76f7c4987413226795ea8a5d02c91b1e65175d7c403ef25f8 which points to its parent: f825ef03c4d77551e6b1912cd0a5a85e7926324187497c6fc7ad8efd3cfbc21fi0

Tag 15

Tag 15 contains a URL – probably of the creator/inscriber.

Example:

Mainnet inscription: 05fca8130c87110644be562d0e6409c9645ce8a3ef63741e56c511a7832028e4i0 has a tag 15 payload: 68747470733a2f2f7361747363726962652e78797a which decodes to https://satscribe.xyz

Below, I am referring to tag 15 as Style 6.

Data

Mainnet
Tag Number Style id Count First Seen Last Seen Cursed Not Cursed
03 2 9 Jun 21 11:03 Jun 22 03:19 0 9
03 5 220 Mar 30 01:32 Apr 02 02:42 0 220
03 3 1230 Mar 11 02:42 Jul 21 12:59 1230 0
15 6 1311 Jul 17 06:27 Jul 28 12:45 3 1308
Testnet
Tag Number Style id Count First Seen Last Seen Cursed Not Cursed
03 2 28 Jun 13 01:24 Jul 07 03:28 0 28
03 4 36 Jul 08 07:35 Jul 28 04:36 0 36
03 3 175 May 19 08:22 Jun 26 06:22 175 0
15 6 25 Jul 19 03:55 Jul 27 10:57 1 24
Signet
Tag Number Style id Count First Seen Last Seen Cursed Not Cursed
03 2 1 Mar 16 11:57 Mar 16 11:57 1 0
03 3 202 Mar 10 03:54 May 29 08:55 200 2

I am attaching non-aggregated results – separate files for each *net.
Parsing code is here: https://github.com/cypherpork/ordtagparser
non_01_tags_testnet.csv
non_01_tags_signet.csv
non_01_tags_mainnet.csv

@casey
Copy link
Collaborator Author

casey commented Jul 31, 2023

@cypherpork Nice! Super interesting stuff.

Comment on lines 132 to 137

- Create an inscribe transaction T as usual for C.
- Spend the parent P in one of the inputs of T.
- Include tag tag `3`, i.e. `OP_PUSH 3`, with the value of the serialized
inscription ID of P, `TXIDiINDEX`, serialized as the 32 byte binary `TXID`,
followed by the little-endian `INDEX`, with trailing zero bytes omitted.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we anticipate support for minting multiple children in one transaction in order to reduce the number of times the parent ordinal must be spent?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally yes, although that's a separate feature.

@tranchien2002
Copy link

gm sir @casey , i really need this feature in our product, when do you expect this feature will be merged into ord 👍

- Spend the parent P in one of the inputs of T.
- Include tag tag `3`, i.e. `OP_PUSH 3`, with the value of the serialized
inscription ID of P, `TXIDiINDEX`, serialized as the 32 byte binary `TXID`,
followed by the little-endian `INDEX`, with trailing zero bytes omitted.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the savings in bytes by emitting the trailing zeros is not worth the extra complexities of handling those cases

@casey casey changed the title Add draft provenance spec Provenance spec Aug 28, 2023
@casey
Copy link
Collaborator Author

casey commented Aug 28, 2023

I updated the spec to use a fixed-size 4 byte little endian encoding of the inscription ID index. It didn't seem worth it to use the more complex variable length encoding just to save four bytes. The implementation in #2353 was changed to match the spec. I also moved the spec for offsets into PR #2383, since it isn't part of the initial implementation, so it isn't ready to merge.

@casey casey marked this pull request as ready for review August 28, 2023 20:36
@casey
Copy link
Collaborator Author

casey commented Aug 29, 2023

Will it be worth supporting both cases - zeroes in and zeroes out? It looks like the majority of collection roots will have i0-s. Large collections may be happy to utilize a small cost-optimization like that.

I think I'd be inclined to support one or the other, just to make sure there's only one way to do things. I'm really not sure about the cost savings, like you say, there well be a lot of i0s, so I could be convinced either way.

@casey
Copy link
Collaborator Author

casey commented Aug 29, 2023

Okay, went back to variable length encoding for the index.

@sondotpin
Copy link
Contributor

- Create an inscribe transaction T as usual for C.
- Spend the parent P in one of the inputs of T.
- Include tag `3`, i.e. `OP_PUSH 3`, in C, with the value of the serialized
  binary inscription ID of P, serialized as the 32-byte `TXID`, followed by the
  four-byte little-endian `INDEX`.

Is this conflict if we use the P as the first input & not specific offset ?

@sondotpin
Copy link
Contributor

sondotpin commented Sep 3, 2023

4LKb8

We are having a demand to allow children born to the same parent to have certain permissions at the protocol level. Example: Whitelist all the child of P can inscribe another digital artifacts

The idea is that we can let the parent create intermediate inscriptions in creation the child, which can be limited to once or many times.

ord wallet create intermediary --time 1

create the intermediary insription_1 from parent P

then create the child C from insription_1

and C is child of P

@casey

@casey
Copy link
Collaborator Author

casey commented Sep 3, 2023

Is this conflict if we use the P as the first input & not specific offset ?

Yes, in that case the inscription would be made on P. See #2383 for a proposal on how to control the inscribed sat.

We are having a demand to allow children born to the same parent to have certain permissions at the protocol level.

That should be discussed separately, please feel free to open an issue.

@casey casey enabled auto-merge (squash) September 4, 2023 18:23
@casey casey merged commit e6ceb97 into ordinals:master Sep 4, 2023
6 checks passed
@sanjfomojis
Copy link

Okay, went back to variable length encoding for the index.

What was the reasoning in the end to change back to the variable length encoding in the index?

Fully understand that playing around with draft PR's is always dangerous, and so I'm not too worried about my own stuff, but I know we weren't the only ones who created some early parent child collections with trailing zeros so it would be a shame if some of them weren't indexed, especially the really early ones.

Happy for whatever is best for the protocol, but just wondering if there's any reason we can't have the trailing zeros as optional?

Happy to make a PR if there's no reason it can't be included.

@casey casey deleted the collections-spec branch September 16, 2023 15:22
@casey
Copy link
Collaborator Author

casey commented Sep 16, 2023

Okay, went back to variable length encoding for the index.

What was the reasoning in the end to change back to the variable length encoding in the index?

Fully understand that playing around with draft PR's is always dangerous, and so I'm not too worried about my own stuff, but I know we weren't the only ones who created some early parent child collections with trailing zeros so it would be a shame if some of them weren't indexed, especially the really early ones.

Happy for whatever is best for the protocol, but just wondering if there's any reason we can't have the trailing zeros as optional?

Happy to make a PR if there's no reason it can't be included.

I went back to the variable length encoding to save space. In this case, I think it would be fine if the fixed length encoding were also accepted, so if there are inscriptions out there that used the fixed-length encoding, I'd accept a PR to recognize them.

@sanjfomojis
Copy link

Awesome! Appreciate the quick response! Thanks for that.

@lifofifoX
Copy link
Collaborator

Is the plan to allow only 1 parent per child? Would the team be opposed to a change that allows multiple parents?

@casey
Copy link
Collaborator Author

casey commented Oct 2, 2023

Multiple parents for child seems fine to me.

@bruffstar
Copy link

What could a use case be for multiple parents @devords ?

@lifofifoX
Copy link
Collaborator

@bruffstar Breeding is probably the most obvious one, where the parents determine the output of the child inscription.

@bruffstar
Copy link

I see. My gut feeling is that the purest form of provenance is to allow for only a single parent. But I could be wrong.

@ayo-dan
Copy link

ayo-dan commented Oct 19, 2023

Would it be possible to have multiple parents that when used separately can lead to the same provenance effect?

For example, if you are minting a collection you can establish provenance by using Parent A in the first set of trxs and Parent B in the second set of trxs - but in the end both sets of trxs point to the same overarching collection.

This would be a very useful function for any service that enables the minting of collections.

@casey
Copy link
Collaborator Author

casey commented Oct 19, 2023

Nope, for an inscription to have a parent, it must be used in the same transaction in which it is inscribed. So parents cannot be added later. Otherwise, someone other than the inscriber could add a parent later.

@ayo-dan
Copy link

ayo-dan commented Oct 20, 2023

I appreciate the heads up Casey!

The issue I am trying to solve revolves around concurrency and not needing to premint/preinscribe a collection before selling it.

Minting/Inscribing services generally mint/inscribe the NFT/Inscription at the point when someone pays for it. When one of these services hosts a very popular collection many different wallets are all signing trxs in order to get their NFT/Inscription.

With the current configuration, these services would need to weave a single parent through many different trxs, signed by many different wallets. This becomes difficult when this parent needs to be the input of 1 trx, go through the mempool and into a block, and then become the input of another trx. This isn't impossible, but it forces the Minting/Inscribing service to create a long line of trxs that must be sequential - negating the benefits of the fee market model of BTC while still suffering its negative effects (trxs take longer to process if their fees aren't at what is currently considered standard for miners).

Thought I would bring this up as I am part of a team building a tool in the space and this is an issue we foresee! Happy to hear your thoughts.

@casey
Copy link
Collaborator Author

casey commented Oct 20, 2023

Yah, I definitely see the issue. Ideally minting services would batch inscribe, and use RBF to update pending batch inscriptions with new children continuously, and start a new batch when the last one was finally mined.

@ayo-dan
Copy link

ayo-dan commented Oct 20, 2023

Ok great! I appreciate your thoughts on the matter.

I will bring that up with the rest of my team.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet