Author

Topic: How can taproot transactions manage to store so much data? (Read 187 times)

brand new
Activity: 0
Merit: 48
Taproot transactions are fascinating because they revolutionize how data is stored on the Bitcoin blockchain. They make use of Merkle trees and script improvements to achieve this feat. While the Merkle root isn't directly stored on-chain, it acts as a kind of digital seal, verifying the legitimacy of spending conditions. By bundling multiple spending conditions into a single commitment structure and revealing specific script paths when needed, Taproot ensures efficient verification without cluttering up the blockchain with unnecessary data. This smart approach keeps Bitcoin secure and scalable while allowing for a more streamlined representation of complex spending conditions.
sr. member
Activity: 364
Merit: 298
You push it into your own network, as each honest altcoin do.

This is the response I expected.  Your model operates effectively when there's an alternative chain, such as a sidechain, that retains the image data.  However, Ordinals doesn't operate on that premise.  The fundamental concept of Ordinals is that they're stored on the main chain—not a flaw but a deliberate design choice.  This distinction is clear.  No other chain can ensure the same long-term sustainability as Bitcoin's main chain.

I understand that it may seem illogical, but most people make financial decisions based on emotions.  By adopting your approach, you're compromising the core aspect of Ordinals that sets it apart from other (meaningless) tokens.
copper member
Activity: 821
Merit: 1992
Quote
How can you obtain the data solely from R?
You push it into your own network, as each honest altcoin do.

Quote
Essentially, this is akin to having only OP_RETURN data.
This is better, because then nobody knows, if your transaction has any commitment or not. You have just a regular signature (with any address type you want, including P2PK), and people can find out, that something is hidden behind it, only when you reveal that information. Which means, that you can also make private inscriptions. And all of that is also timestamped on-chain, which means, that it is covered with Proof of Work, as every other timestamped document.

Quote
What I meant to convey is that observers can confirm whether the image data requested matches with the OP_PUSHed bytes.
You can do the same with R-value commitments. The difference is that you don't have to push your data on-chain.
sr. member
Activity: 364
Merit: 298
You can just hide it in your signature. Here is how: https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2023-November/022176.html
Code:
Sign-to-contract looks like:

 * generate a secret random nonce r0
 * calculate the public version R0 = r0*G
 * calculate a derived nonce r = r0 + SHA256(R0, data), where "data"
   is what you want to commit to
 * generate your signature using public nonce R=r*G as usual

How can you obtain the data solely from R?  You're appending a hash to r0, but you're unable to reverse the hash to retrieve the data.  Essentially, this is akin to having only OP_RETURN data.

Note that Ordinals do not enforce "ownership" in any way. If you post two identical NFTs, then there is no mechanism anywhere, to prevent claiming, that you are the real owner.

I haven't delved into their protocol because, frankly, I find it uninteresting—it's just another valueless scheme.  What I meant to convey is that observers can confirm whether the image data requested matches with the OP_PUSHed bytes. 
copper member
Activity: 821
Merit: 1992
Quote
How can you store the entire image without forcing each node to process big transactions?
You can just hide it in your signature. Here is how: https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2023-November/022176.html
Code:
Sign-to-contract looks like:

 * generate a secret random nonce r0
 * calculate the public version R0 = r0*G
 * calculate a derived nonce r = r0 + SHA256(R0, data), where "data"
   is what you want to commit to
 * generate your signature using public nonce R=r*G as usual

Edit:
Quote
The main attraction is storing the image itself, unlike regular NFTs, where you typically only store a hash of the image and chain observers can verify the "ownership" of that image.
Note that Ordinals do not enforce "ownership" in any way. If you post two identical NFTs, then there is no mechanism anywhere, to prevent claiming, that you are the real owner. Those pushes are just huge OP_NOPs, and there are no consensus rules, which could bring "real NFTs" here. Because if those creators would really want to make NFTs, then they would use some sidechain to do that.
sr. member
Activity: 364
Merit: 298
For example: Ordinals could work with any address type, if the commitment would refer to R-value of the signature. But: if you reveal it on-chain, then every full node is forced to store it. If you have this transaction 0301e0480b374b32851a9462db29dc19fe830a7f7d7a88b81612b9d42099c0ae, then it could contain only "8639eaab74c917a6385fd2932e2085dca8401c28b6bd506146ad4e552e229013" as some R-value in the on-chain signature, and it would be enough to see that "this image is connected with this address". But instead, everything is pushed on-chain, so each node is forced to process this almost 4 MB transaction.

I think there might be a misunderstanding here, or perhaps I'm not following you correctly.  Ordinals require storing arbitrary data on-chain; that's essentially their purpose.  The main attraction is storing the image itself, unlike regular NFTs, where you typically only store a hash of the image and chain observers can verify the "ownership" of that image.  How can you store the entire image without forcing each node to process big transactions? 
copper member
Activity: 821
Merit: 1992
Quote
But I thought tapscripts only stored the merkle root on chain.
There are two things: sending to Taproot, and spending from Taproot. If you send your coins to Taproot, then you put only someone's public key. But: if you spend coins from Taproot, then you can spend by key, or by TapScript. If you spend by key, you provide a Schnorr signature, matching that public key from the address. But if you spend by TapScript, then you reveal some public key, and the whole TapScript leaf, along with the SPV proof, that it is a part of the tree.

Quote
Each one of the leafs gets collapsed down to a single hash (the merkle root) and that's the only thing that gets committed to the chain. Therefore, if you have a any data longer than a hash, it will vanish into the merkle root.
This is true if some leaf is unused. But if it is used, it has to be revealed. If you have a tree, you can have N leaves, but the problem is, that one of them has to be revealed, if you spend by TapScript. And that leaf can be as complex, as you want, taking for example almost 4 MB, because it is limited only by block size in consensus rules (there are more restrictions, like transaction size, but still, you can fill the whole block with something like 40 transactions, if you assume 100 kB per transaction).

Quote
Is that why you have to spend it in order to get the ordinal on chain?
Yes, you reveal data in your input.

Quote
Does the committed ordinal data only exist in the proof that you provide?
It can exist anywhere, but people who created Ordinals, decided to put in on-chain. For example: Ordinals could work with any address type, if the commitment would refer to R-value of the signature. But: if you reveal it on-chain, then every full node is forced to store it. If you have this transaction 0301e0480b374b32851a9462db29dc19fe830a7f7d7a88b81612b9d42099c0ae, then it could contain only "8639eaab74c917a6385fd2932e2085dca8401c28b6bd506146ad4e552e229013" as some R-value in the on-chain signature, and it would be enough to see that "this image is connected with this address". But instead, everything is pushed on-chain, so each node is forced to process this almost 4 MB transaction.
newbie
Activity: 19
Merit: 25
Quote
They do.  They wrap arbitrary data in a tapscript and inject it in the witness part of the transaction. 
 

But I thought tapscripts only stored the merkle root on chain. Each one of the leafs gets collapsed down to a single hash (the merkle root) and that's the only thing that gets committed to the chain. Therefore, if you have a any data longer than a hash, it will vanish into the merkle root. 

My above explanation could be wrong so please correct me if it's wrong. 

Is that why you have to spend it in order to get the ordinal on chain? Does the committed ordinal data only exist in the proof that you provide?
sr. member
Activity: 364
Merit: 298
So when someone makes an ordinal they are not using tapscript right?

They do.  They wrap arbitrary data in a tapscript and inject it in the witness part of the transaction. 

Does this mean that I could do an ordinal in a non-taproot address bitcoin transaction? So I could mint an ordinal with a P2PKH address?

An ordinal specifically?  No, because the ordinal protocol relies on taproot, AFAIK.  However, arbitrary data?  Certainly.  You could achieve that by treating multiple P2PKH as 20-byte blocks.  I think you can achieve it with OP_RETURN too, but there are standardness limits. 
newbie
Activity: 19
Merit: 25
Quote
So, to sum up, there are many ways to make the whole system secure, without pushing everything on-chain. But users just decided to put their data anyway, even if there is no need to do so.
 

Thank you very much for your answer. I think I'm starting to wrap my head around this better... 

So when someone makes an ordinal they are not using tapscript right? They are simply writing Opcodes into the transaction just like you would with a non-taproot transaction?  If I did a typical P2PKH transaction I would put
Code:
"scriptPubKey": "OP_DUP OP_HASH160 HASH_OF_PUBKEY_IN_HEX OP_EQUALVERIFY OP_CHECKSIG"
as my locking script, but If I wanted to do an ordinal I would make my locking script
Code:
"scriptPubKey": "OP_DUP OP_HASH160 HASH_OF_PUBKEY_IN_HEX OP_EQUALVERIFY OP_CHECKSIG OP_FALSE OP_IF OP_PUSH "ord" OP_PUSH 1 OP_PUSH "text/plain;charset=utf-8" OP_PUSH 0 OP_PUSH "Hello, world!" OP_ENDIF"
? Does this mean that I could do an ordinal in a non-taproot address bitcoin transaction? So I could mint an ordinal with a P2PKH address?
copper member
Activity: 821
Merit: 1992
Look at this example: https://mempool.space/tx/0301e0480b374b32851a9462db29dc19fe830a7f7d7a88b81612b9d42099c0ae
Code:
OP_PUSHBYTES_32 aa7c645e1ee2a6b9fcd113e98f057cbc70eec92cb0d18d7f138c178f625bbc76
OP_CHECKSIG
OP_0
OP_IF
OP_PUSHBYTES_3 "ord"
OP_PUSHBYTES_1 01
OP_PUSHBYTES_10 "image/jpeg"
OP_0
OP_PUSHDATA2 <520-byte block>
OP_PUSHDATA2 <520-byte block>
OP_PUSHDATA2 <520-byte block>
...
OP_PUSHDATA2 <520-byte block>
OP_PUSHDATA2 <520-byte block>
OP_PUSHDATA2 <520-byte block>
OP_ENDIF
See? Everything is splitted just into 520-byte blocks.

Quote
But I thought that the taproot upgrade simply enables people to tweak the public key to embed a merkle root on chain by tweaking the public key to create a modified address.
Tweaking the key is one thing. But how do you know, if the TapScript is valid or not? You have to see that. And Ordinals just abuse the fact, that the whole TapScript is always revealed. Which means, that even if there is a way to make things smaller, without sacrificing security, then still, people can choose the least optimal way, just because they can.

For example: if you have a single public key, then you can use this script: " OP_CHECKSIG". It will be simple. But: anyone can abuse the Script, and instead write: "OP_2 OP_2 OP_ADD OP_4 OP_EQUALVERIFY OP_CHECKSIG". Then, this part "2+2=4" is useless, but it is still there, and it has to be revealed, if you want to spend by TapScript.

Quote
Even then, the merkle root isn't actually stored on chain. You just have to know it to spend coins from that address right?
Yes, but you don't have to pick the best way. Which means, that if you want to make a condition: "Alice or Bob can spend those coins", then you can create two branches: " OP_CHECKSIG" and " OP_CHECKSIG". And then, you can reveal one of them. But: you can abuse the TapScript, and make a single branch with both conditions: "OP_IF OP_CHECKSIG OP_ELSE OP_CHECKSIG OP_ENDIF". Then, everything will be always revealed, if you will spend by TapScript.

Quote
I know my understanding is wrong because if all you needed to know to spend coins from the script path was the merkle root you wouldn't need to know the hash of any of the leaves. So can you guys fill in the gaps in my understanding?
You have to prove, that the revealed TapScript is committed to your address. Which means, that you provide some kind of SPV proof, similar to proving, that a given transaction is part of the block. And then, you reveal the whole TapScript in that leaf. Then, all comes into what users picked: they could for example always spend by public keys, and reveal their TapScripts outside Bitcoin. Or they could put their Ordinals into a separate leaf, which could begin with OP_RETURN, so it would be never pushed on-chain, but could be still committed to their address.

So, to sum up, there are many ways to make the whole system secure, without pushing everything on-chain. But users just decided to put their data anyway, even if there is no need to do so. And also, for that reason, I think about making pubkey-only system, which could be resistant to that kind of attack. Or at least about making a lightweight node, which would only process signatures, without storing data, so it would be similar to OP_CHECKSIGFROMSTACK.
legendary
Activity: 1568
Merit: 6660
bitcoincleanup.com / bitmixlist.org
As you can see in the script opcode, the push instructions are never evaluated. If they were, then all of these gigantic ordinals transactions would fail because they would overflow the stack. The manner in which the Ordinals transactions are spent is by using the conventional Schnorr signature directly, not any of the leaf evaluations. And also P2TR outputs do not count any sigops so its effectively limited only by block size, i.e. it's possible to fill an entire block with a large enough Ordinal.
newbie
Activity: 19
Merit: 25
I don't quite understand how ordinals are able to store so much data on chain. I know about the ordinals envelope. 

    OP_FALSE
    OP_IF
       OP_PUSH "ord"
       OP_PUSH 1
       OP_PUSH "text/plain;charset=utf-8"
       OP_PUSH 0
       OP_PUSH "Hello, world!"
     OP_ENDIF 

According to the ordinals handbook, in this example, the text "Hello, world!" is stored on chain. But I thought that the taproot upgrade simply enables people to tweak the public key to embed a merkle root on chain by tweaking the public key to create a modified address. Even then, the merkle root isn't actually stored on chain. You just have to know it to spend coins from that address right? 

I know my understanding is wrong because if all you needed to know to spend coins from the script path was the merkle root you wouldn't need to know the hash of any of the leaves. So can you guys fill in the gaps in my understanding? 

I know that bitcoin transactions have some code for scripting in the actual hexadecimal transaction. For example a simple locking script in a bitcoin transaction output looks like this "scriptPubKey": "OP_DUP OP_HASH160 HASH_OF_PUBKEY_IN_HEX OP_EQUALVERIFY OP_CHECKSIG" so to spend that transaction whoever has the private key of the address in the UTXO provides an unlocking script consisting of the signature and public key. But this is just a little bit of data that bitcoin transactions store, how does taproot allow for such larger amounts of data to be stored?
Jump to: