Skip to content

Instantly share code, notes, and snippets.

@Ayms
Last active March 19, 2023 19:24
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Ayms/01dbfebf219965054b4a3beed1bfeba7 to your computer and use it in GitHub Desktop.
Save Ayms/01dbfebf219965054b4a3beed1bfeba7 to your computer and use it in GitHub Desktop.
A Bitcoin NFT System

A Bitcoin NFT system

Note: this proposal needs to be slighltly modified since the storage limit for OP_RETURN is 80B and does not work for all cases, but the principles will remain the same and other storage solutions exist, see https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2023-February/021398.html

Then for now some transactions mentionned in that proposal are not standard (ie not relayed by the nodes) but valid (ie a miner can include them into a block)

So, after discussion with the bictoin community, see Allow several OP_RETURN in one tx and no limited size

In case this change, which looks to be admitted by the majority, is trivial and not even a soft fork, is not implemented then please see the different workarounds at the end of this proposal

Given that 80B limitation, reminder:

A message signature is 65B (header+r+s, not to be misunderstood with a transaction signature as explained here)

A hash is 32B

An address is 20B (hash_160 of the public key)

A Chainpoint proof is n*32 B where n is ~ the depth of the Merkle tree

The intent here is not to be too technical, then you can consider using the very excellent tools from us bitcoin-transactions and the browserified version bitcoin tools

Introduction

NFT is another barbarous name invented by the crypto folks meaning quasi nothing, by NFT in this proposal we will refer to whatever can be sold or referenced from the real world, web and metaverse using a blockchain system

The easy way solution is usually to use Ethereum, create some NFT tokens via ERC-721/1155 contracts and/or use some marketplaces such as Opensea which always lead to a centralized and insecure system that can collapse at any moment, and which is still complicate since nobody understand what is behind, fortunately (or not) MetaMask will do the job for you without you understanding what you are doing, like wrapped ETH

This is explained here: Bitcoin, Ethereum, smart contracts, lightning, sidechains, Defi, DApps, NFTs, DEX, DAO explained - Centralization vs Decentralization

Especially: tokens and sidechains

And: Opensea

Purpose

The purpose of this proposal is to propose a simple NFT system based on the Bitcoin blockchain, assuming that the main purpose of a NFT is to be sold/bought, but not only, it can be something that you keep for yourself proving your ownership on the blockchain, or something that you offer to someone else, the advantages compared to using Ethereum or any blockchain/sidechain on both networks (or others) will be explained below

Referencing a NFT

A NFT can be a physical good, an electronic item (such as image), something virtual or not, in fact anything

If we take a document or an image, a simple reference to the NFT is its hash (SHA-256), but it is not really relevant since it's easy for a thief to slightly modify the document or image which will lead to a different hash

Now in what follows we will allocate a hash to reference any NFT

The final hash reference to a NFT

Depending on the conditions the hash can be known (public marketplace for example) or unknown (only the buyer and the seller know it or the owner)

Thererfore the reference to the NFT will be the SHA-256 of its hash, which then hides the original hash, this is of course useless if the hash is known but let's keep the same convention, called |reference| in what follows

Minting a NFT on Bitcoin

"Minting a NFT" just means to record your ownership of the NFT in the blockchain

To mint a NFT that you own on Bitcoin, you can do a transaction whose output will be:

From address A (the seller):
 OP_RETURN |reference|
 DUST (A or B) or refund to an address of yourself (A or B)

Since the transaction is signed by A, we know that A is the owner of the reference, but note that from this point the public key of A is known, therefore if the purpose is to store a long term NFT then use another address B of yourself (because in some time it will be easier to crack a bitcoin address for which we know the public key rather than one that holds some bitcoins but has not been spent, so knowing only the hash of this address)

An OP_RETURN output is just something where you can store some data, limited to 512B

A lot of variations are possible in this proposal, for example you can decide to allocate one A address per NFT, it just depends on the use cases

For all the transactions mentioned in this proposal, opt-in for Replace By Fee must not be used Replace By Fee

It would be possible not to use OP_RETURN and for example store the |reference| in scriptSig, then construct an address (scriptPubKey) that will drop it while executing, but this would be a non standard transaction, so you shoud rely on some miners to mine it, without stealing your NFT, since it's easy for a miner to change the transaction and mint the NFT for himself

Minting several NFTs in one transaction

You can do the very same using the Chainpoint protocol, which is to build a Merkle tree from |reference1| |reference2|....|reference N| (for which we recall will be the SHA-256 of the original NFTs hash), and replace the root of this tree in |reference| above

How Chainpoint work is well described here: Chainpoint Proof, basically while making a Chainpoint tree you get a proof to rebuild it from any |reference| (that you must know), which is just the other hashes that you need to know to reach the root

In that case only the owner of the multiple NFTs knows the proofs and/or the |references|, unless he decides to release them, which he should do as described in the next paragraph (because the hashes and proofs must be known to check the validity of NFT transfers)

In what follows we will make reference to a Chainpoint |reference|

Avoiding double minting

Double minting of NFTs is quite trivial, as far as we know there are zero protections against this in existing systems (Ethereum and others), the principle is simple: you mint several time the same NFT and possibly sell it several time or get robbed (the thief does transactions using the |reference| that he saw), because it's easy for a malicious person to do the very same transaction that you did and pretend the ownership of the NFT, or it's easy for you to mint it to different marketplaces or mint it several time, and then sell it several time

To illustrate this, read Solving the double minting problem

Unfortunately the above article just refers to images and the solution does not work at all at the end, because associating the picture to the camera or whatever that took it does not work and is easy to fake

Coming back to our bitcoin case, it seems simple to elect you as the real owner of the NFT since you made the transaction first, then based on the timestamp you are definitely the owner, but no

The thief could intercept your transaction, decide not to broadcast it further, replace it by a transaction minting your NFT for himself, with higher network fees for example and more direct access to miners, then this transaction will be the first one and the thief would have stolen your NFT

You can't do anything against this without using a third party to record that you had the knowledge of the |reference| first

The same problematic exists for example for js code loading, see Bitcoin-transactions wallet/tools, here our github account is used to check the code

Then the very same is proposed here, preferably using the Wayback Machine to store |reference| (that you are the only one to know at this stage) signed by A before minting the NFT, then for sure you are the owner of the NFT because you are the first one to have referenced |reference| and signed it with your address (note again that as soon as you sign something the public key of A is known)

You can use other systems, github, even Twitter

What if all of those systems disappear and I can't prove anything any longer? Well, then probably internet has disappeared too

Anyway like the WebRTC peer introduction mechanism you can do this by any means you like, your own website, even a letter, you just need to prove that A had the knowledge of the double hash first

The third party proof could be stored in the minting operations but this would complicate the multiple minting operation, since a NFT is usually public the owner must disclose the third party proof, if he does not then you can suspect the seller not being the real owner of the NFT

Now the double minting case is supposed to be marginal, except if you are a thief yourself

Transferring a NFT for free

For a gift, loyalty program, discount coupons or whatever other reason, A can decide to transfer a NFT for free to C, the transaction will be:

From address A (the seller):
 OP_RETURN |some code||reference|
 DUST (C) and refund to an address of yourself (A or B)

|some code| is just used to make the distinction between a minting operation

Transferring several NFTs for free

To reduce the cost of transferring several NFTs for free, the transaction would be:

From address A (the seller):
 OP_RETURN |some code||reference1||reference2|...|referenceN|
 DUST (C1) 
 DUST (C2)
 ...
 DUST (CN) 
 and refund to an address of yourself (A or B)

Where C1 to CN are the addresses of the beneficiaries of the free NFTs in order according to the |referenceX| string

A sends the original hash to C1...CN

As explained in the next paragraph please note that minting "low value" NFT is not mandatory if A did reference the NFTs at a third party with its signature, this reduces the costs of exchanging such NFTs

To reduce the costs again, something more simple can be used, the transaction would be:

From address A (the seller):
 OP_RETURN |some code|hash_of(|reference1||address1||reference2||address2|...|referenceN||addressN|)
 DUST (A or B) or refund to an address of yourself (A or B)

This allows to allocate in one small transaction many NFTs, but while this proposal insists on decentralization, this solution must rely on a centralized system on A side (TODO: is this one really useful?)

C Buying a NFT from A

The transaction will be:

From address C (the buyer):
 OP_RETURN |price||some code||reference|- signed by A
 |price| to A and refund to C or other C address

Once A and C have agreed on a price then A should send to C the |price||some code||reference| signed by him, which proves that the deal is accepted

If C tries to cheat and does broadcast a transaction that does not match the agreed price (since C cannot fake the signature of A), then C is still not the owner of the NFT and has lost its bitcoin, if a thief intercepts the transaction and does another one for himself, that's the same, since he is not willing to pay for the price

If anyone else sese the transaction and really wants that NFT, then he just has to do a transaction with the correct price, C has definitely lost his bitcoin and the NFT, so it's better not trying to cheat

If A receives the correct amount, then he will send to C the original hash of |reference|, the same principle applies for all following transactions

We can not really enforce this, A could not send the hash, but for what purpose? This represents the same trust model than when you are buying some goods (the hash is the ticket), now in normal conditions the original hash is not really mandatory, A could still pretend to be the owner of the NFT but then he must proves that he owns address C, which is not the case, in any case C could restart the process from the begining with a new initial hash, a new double one associated to the previous one and advertise it via a third party

The purpose could be that A still pretends to be the real owner of the NFT, again this does not work very well since A would have to prove that he owns address C, which is not the case

Note that we know from minting/signing the public key of A, therefore anybody can verify the signature in the OP_RETURN

Note again that the signature of A is enough to know its public key and check the signature, therefore minting is not mandatory (depending on the use cases again)

Note also that in Ethereum style, it can be the other way around, where A would initiate the final transaction to the NFT contract that will transfer to C and pay A

C selling a NFT to D

That's the same:

From address D (the buyer):
 OP_RETURN |price||some code||reference|- signed by C
 |price| to C and refund to D or other D address

C sends the original hash to D

C Buying a NFT from A from a Chainpoint |reference|

A sends to C the |price||some code||reference||Chainpoint proof| signed by him, C then do the following transaction:

From address C (the buyer):
 OP_RETURN |price||some code||reference||Chainpoint proof|- signed by A
 |price| to A and refund to C or other C address

Group/organization C of buyers

C is then a multisig address funded by the group, and as before:

From address C (the buyers):
	OP_RETURN |price||some code||reference|- signed by A
	|price| to A and refund to C or other C address

Same principle would apply for Chainpoint |reference|

But, in all of this, what is the purpose of the double hash for |reference|?

Just to hide (if applicable) that the hash is only known by the buyer and the seller if the transactions are correct, then if anything unexpected happens they can prove that they are the only one to know the original hash of the NFT

We can take here the example of Kering (luxury group): Kering NFT solution, this is a variation of what is proposed here, when you buy a luxury product you get a certificate, then you go to their website, use the certificate information + your name & co, then you get a second certificate and the associated hash, the hash is then stored in the bitcoin blockchain in an OP_RETURN using the Chainpoint protocol via Woleet service

So here you are the only one to know the hash (with Kering), the problem of this solution is that it is very centralized and you can't correlate the hash to the product. That's where the double hash method could be used, you print on the luxury product the double hash (QR code or whatever) and give the original hash to the buyer, then if the product is counterfeit (with the same double hash) or stolen you can detect it since the malicious seller does not know the original hash

That's the same with LVMH/Aura solution, based finally on Ethereum, the hash (QR code) can easily be counterfeit and we give below our opinion about sidechains/blockchains on top of others

Transferring a secret NFT

NFTs are usually public and as explained above should be advertised to a third party, then if it's for example electronic art everybody has access to it

We are wondering since years how to sell a secret NFT, it's of course difficult to sell something that you can't disclose

So let's take some electronic art or a sensible study (this is our use case, see Deanonymizing quietly all of Tor), you can encrypt it and store it in a reliable place proving a timestamp (wayback machine for example) with your address, contact and the double hash signed by you, and a proof that what you are selling exists (image, plausible summary, etc)

Then you can mint it or not, agree on a price with the buyer using some anonymous means if necessary, then transfer it the very exact way as explained above, where the buyer will make the final transaction

If the transaction is correct you send the initial hash to the buyer + the encryption method used, and the initial hash is the encryption key

Then you are the only one to have the original NFT (with the seller)

This is just an example and different things can be imagined

Of course, like all NFTs in this proposal you could transact directly with the buyer but then the whole purpose of NFTs woud be supposed to be of no use, while its purpose is to store uniquely a reference for the owner to be able to prove that he is the owner, and for the buyer to prove that the NFT exists, proofs available to anybody in the world, so if you sell several time the same thing via a NFT system or not, the real owner might see it one day and the world will see that you are a crook (even if anonymous), then you should take care of the consequences (like hackers not succeeding to wash/mix what they have stolen and returning it to avoid going to jail one day), same principle applies if the seller does not send the encryption info to the buyer

In addition here the NFT systems proves that something exist and is public, even if encrypted there is also a proof of what it may contain

Costs

Currently the network fees are 6-12 satoshis per byte, and the dust limit can be set at 546 satoshis

It's not possible to give a precise number here since the transactions depend on the number of inputs but an estimation would be less than 1 USD per transaction (probably 0.5 USD) against 70 to 100 USD on gas fees in Ethereum

This is far less than Ethereum NFT tokens see Moxie experience

Is this system centralized?

No unlike whatever exists about sidechains (except Lightning), Ethereum NFTs or Dstuff, it is not, because you do not rely on any centralized platform to use it (the third party is the one you choose and it can't interact with the transactions), or Oracles that would be involved in your transactions creation/confirmation/transfer

Is this system anonymous?

Not totally as currently designed, like Lightning again, since buyer/seller must "know" each others, but they can do it via anonymous means like Signal, Protonmail, Telegram or other stuff, or Tor, the advise is to create a new good old gmail address for this purpose and use PGP keys

In any case others around do not know what is going on

Why not a super sidechain instead?

You can read this conclusion from a previous article: Conclusion

We have different examples, but let's take a few, Binance Smart Chain, supposedly compatible with Ethereum is a mess (or FTT/FTX token, see Collapse of FTX and others: the Fxcking Token Theory), and Stacks for Bitcoin supposedly allowing "smart contracts" and NFT on bitcoin is the same, both centralized, insecure, making it difficult to swap the tokens between one chain to another

Let's hope this proposal shows how powerfull (and less costly) is bitcoin, if we elaborate on Stacks it's another blockchain on top of Bitcoin, they register the blocks inside OP_RETURN in Bitcoin via a "proof of transfer". Why do they do this? No idea but surely because their blockchain is neither secured neither decentralized, this is just flooding the Bitcoin network with useless transactions duplicating proofs on two blockchains

There are many "proof-of-whatever" that are just fake stuff (like Filecoin proof of replication protocol/research#4), useless, quite dubious, as stated in our conclusion above the intermediate states shoud be burned and only the final/relevant result stored in Bitcoin, this is very exactly what is doing Lightning and what we are doing here

In any case, whatever sidechain/blockchain is proposed can only lead to something funny, because even the worldwide networks bitcoin and ethereum are still not decentralized today, despite of their age, because something like a few thousands nodes is not what we can call a decentralized system, then proposing something decentralized on top of it or not does not look serious, the only real decentralized network remains Bittorrent

This proposal seems to contradict one of our article How stablecoins really work (Tether example), how to crack them and why you should take care if you use them, but no in fact since everything is signed here

Local Bitcoin

We will not talk here about the craziness of the Bitcoin forks period, which we have described here: "Bitcoin Tartuffe - User guide: How to create your bitcoin fork in 5mn, fool everybody and become rich

But we can envision a local Bitcoin, which would be limited to an area and is just a fork of the Bitcoin code (and not the Bitcoin network), restarting from the beginning

Then it's possible to adapt the rules according to the local area, first extending the OP_RETURN size limit and for example reducing the costs by eliminating the dust limit, network fees by mining at lowest difficulty, unlimited supply and no halving

This seems to contradict the good practices of Bitcoin but not really, since the area is limited to people that are supposed to cooperate together, then the risk of attack is limited also

You might say that we are criticizing in Bitcoin, Ethereum, smart contracts, lightning, sidechains, Defi, DApps, NFTs, DEX, DAO explained - Centralization vs Decentralization the fact that most of ERC-20/NFT tokens are of zero value, because coming from nowhere, but we are proposing the same

Not exactly, the local Bitcoin could be seen as a barter money, with a stable value, mined by everybody in the area, then unlike ERC-20 tokens you cannot issue 1 Billion of them all of a sudden

It remains decentralized since everybody is participating and get rewarded according to the probability distribution

To a certain extent, since the local area management might decide to shut down the system

An example is a cruise boat (who might have limited connectivity to interact with an outside blockchain and is not willing to burn its batteries with mining) where each element of the boat (cabins, kitchen, etc) would mine the local Bitcoin, then you can buy/exchange NFTs on the boat, services, use the token for the casino, restaurants, shops, the boat metaverse, etc

The local Bitcoin can be crawled from the outside and transactions can be sent from the outside, so, for example, luxury vendors and others can propose NFTs on board (and deliver them on board or when the boat is at some harbour), and NFTs can be proposed inside the boat shops/metaverse

Indeed since the local Bitcoin is not a fork of Bitcoin network, you don't have to implement replay protection (SIGHASH), then usual Bitcoin transactions can be built and sent to the local Bitcoin, of course you must not make mistakes between both chains

How can we bridge this with the Bitcoin network or any blockchain/token ? See A Universal Coin Swap system based on Bitcoin

This can apply to planes and many other local areas

License and funding

Note that in this proposal the DUST output are not lost and can be spent by their owners if they own several of them of course

While this work is public, it is not in the public area and under any open source license, then to use it, please contact: aymeric peersm com, PGP Key fingerprint : 65EF AE3D 973F D36A AB25 1225 2A1D 7B34 2627 AC39

The goal here is to reference who did fund this proposal

https://web.archive.org/web/20221231185645/https://gist.github.com/Ayms/01dbfebf219965054b4a3beed1bfeba7

Workaround 1 to the 80B OP_RETURN limitation

There are many ways to store things in bitcoin transactions, the taproot witness OP_FALSE OP_IF trick, where data is stored after OP_IF without any size limit since this part is ignored and therefore the script size limit of 520 B does not apply

You can put data in a 1 of m multisig transaction where only 1 is a valid key and the rest data

You can put data in scriptSig/witness like OP_PUSH data OP_DROP or others but will end up most likely with a non standard transaction (ie not relayed by nodes but still valid for miners)

The drawback of those solutions is that you need to do two transactions to unveil the data, and in our proposals we must keep in mind that things should be easy for the buyer, so two transactions do not work very well, given that you need the first one to be confirmed to perform the second one

Then you have the worse storage method with only one transaction if OP_RETURN can't do it for a standard transaction: store in addresses, this has been done in the past, you just put the data in output addresses, nobody can check this that's why it remains standard transactions, so it is stored but you will never be able to spend those outputs, therefore doing this you are burning bitcoins

Anyway, if bitcoin does not extend the OP_RETURN size limit in what follows:

OP_RETURN something

Will become:

OP_RETURN 80B |data|
p2pkh (20B) or p2pk(64B) |data| DUST
...
p2pkh (20B) or p2pk(64B) |data| DUST

Where p2pk is using uncompressed keys here on purpose so it's less expensive, leading to a 0x4+64B storage (note: compressed keys for p2pk are 0x3/0x2+32B), while p2pkh leads to only 20B (hash_160), p2pk transactions are for scriptPubKey: |public_key|OP_CHECKSIG

Depending on the use cases you can choose between both p2pk/p2pkh but for sure you will burn the DUST (because there is no private key associated to |data|), to understand this better please read https://gist.github.com/Ayms/04b3084a14ee202e707b3faec57ed26e#example-1-standard-p2pkh-pay-to-public-key-hash

This remains not expensive but not good practice at all, and a regression with the uncompressed deprecated public key concept, so let's hope that the bitcoin community will integrate the OP_RETURN change

Workaround 2 to the 80B OP_RETURN limitation

This one is less deviant than the previous workaround, the idea is to use the annex field of a Taproot transaction, which is not implemented today, please see https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2023-February/021486.html

@felipelalli
Copy link

Urgent action is necessary to establish a standard for Bitcoin NFT system. Can the Lightning Network potentially provide benefits for this standard?

@Ayms
Copy link
Author

Ayms commented Jan 27, 2023

Yes, I think, it's complementary with Lightning if this is your question

As explained in https://lists.linuxfoundation.org/pipermail/lightning-dev/2023-January/003832.html, let's see what people say

Probably you have seen also the continuation A Universal Coin saw system based on bitcoin

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment