cardano-foundation / CIPs

Cardano Improvement Proposals (CIPs)
https://cips.cardano.org/
Creative Commons Attribution 4.0 International
489 stars 320 forks source link

CIP-0027: Covering existing policy locked assets #168

Open ullamonster opened 2 years ago

ullamonster commented 2 years ago

For assets which have been minted and their policy locked, this method could be used to set their royalties in a decentralized manner.

Requirements:

  1. New royalty token minted from same policy hash
  2. Plutus script for locking (basic always fails script will work)

Process:

  1. Creator mints a token from their same policy wallet/hash which contains the following metadata:
    • Policy Hash
    • This Tokens Policy Script (for calculating this token policy id)
    • Target Token Policy Script (for validating rightful ownership of hash and resultant policy id)
    • Royalties for Target
  2. Creator locks the royalty token into the "global royalty smartcontract" with Datum == target Policy ID
  3. The token cannot be unlocked from the smartcontract - royalty cannot be changed

Notes:

By permentantly locking the token the royalty is immutable. Secondary markets can easily query this smartcontract address and search for a Datum matching the Policy ID they are looking to enforce (or which misses the proper royalty metadata).

For the plutus script, an alternative might be to code it for not just locking permenantly, but to mint the royalty token and lock it, with validation that the minter is doing so from their policy wallet, and the metadata etc is embedded in a consistent way, ensuring consistency and enforcing standardization.

Any creator, any policy ID, can be then set using this smartcontract. I believe it would be also possible in the plutus script to ensure no duplicates are created for a target policy id. So royalty is set once, whether a new, existing unlocked, or existing locked policy.

Theoretically this could also be a single solution, making it possible/easy for creators to not be required to embed royalties into a preplanned policy, especially if they need to wait for some reason but could go ahead and begin minting.

huths0lo commented 2 years ago

How would you mint with the old policy hash, once the policy is locked?

ullamonster commented 2 years ago

Here is a write up I just did, with a testnet test: https://github.com/MadeWithLovelace/MintedWithLovelace/blob/sandbox/dapp-smartcontracts/global-registry/README.md

huths0lo commented 2 years ago

Here is a write up I just did, with a testnet test: https://github.com/MadeWithLovelace/MintedWithLovelace/blob/sandbox/dapp-smartcontracts/global-registry/README.md

I read the document, and it doesnt make any sense to me. 777 is a top level tag. You wouldnt bury it under a 721 tag. And you also dont give it a name at all. Its an unnamed token.

The token you minted as a test doesnt have any of the metadata you are proposing, so its hard to tell what the purpose of sharing the cardanoscan link was for. But, my takeaway is that you want to make a new token under a new policy, and write in the old policy ID inside its metadata.

That wont work for a multitude of reasons. First off, whats stopping me from using the Space Budz policy ID within my metadata, and forever laying claim to royalties. The second, and most significant problem is that people bought assets with no knowledge that the creator was going to add them later? Third, what would stop me from adding 100% royalties?

All of these reasons are why it was decided to not create a scenario where royalties could be appended at a future date.

ullamonster commented 2 years ago

The 777 being burried = the name of this token as a reference to it's purpose for humans, maybe code can find that useful, but it'snot meant to be a top level tag in this implementation, rather a reference that this token is setting royalty over something.

The something is in the metadata below that name tag, and will have a policy id matching what you as the secondary market are looking for (you hashed that policy id to find the matching datum, then you look at metadata and find it, and find what it's script data contains...you then generate a policy id from that script data which must match the target policy id. You do this policy id generation for the "self" to see if the 777 token's actual policy ID at the UTXO matches what you generated in your validation of the metadata...and both the "self" and the target policy's script data embedded each has a keyHash which MUST MATCH.

This is why you cannot fake a 777 token.

Try it with the one I shared in the above readme. You cannot mint a token with a policy id which is derived from my hash without my signing key. So you can try, you can manually put the matching metadata, but the token itself will have it's actual policy id which won't match up if I generate a policy ID using the data in the metadata of the 777 token under "self". Therefore I can validate ownership and only honor royalties set over a target policy by a validated/proven 777 token's metadata. (that is the whole purpose of embedding BOTH policy id's respective script data (hash must match, each has own slot height; generating each's policy id by the person (secondary market) validating proof of ownership when comparing agains the actual 777 tokens policy id at the utxo)

I think if you carefully follow along the test that I performed and view the data on the testnet it may help clarify how this works and why it works.

ullamonster commented 2 years ago

Perhaps naming the token "777" is confusing? Again, it's an "easily recognizable" indicator as to the purpose of this token being "royalty control" over a target. Also because the implementation is different from the original CIP-0027 it seems like naming it is important rather than no name...and we implement a smartcontract for this since it needs to be "findable" given a token with a different policy ID (but shared policy hash).

Although it may not need a name standard, it could theoretically be called anything since a secondary market searches the smartcontract address for a matching datum and only cares about this token's policy ID when validating ownership and comparing hashes, policy script outcomes, etc.

huths0lo commented 2 years ago

"you then generate a policy id from that script data"

I dont get at all what your saying. How do you make a new policy for an old script that is expired?

You can find any script you want. Just go on pool pm, and click on the lock at the top right next to asset ID. As far as "You must do this from xxx wallet". You dont make policy keys from wallets. And a project creator wont necessarily have access to the wallet they used when minting. Most wouldnt, because its done on a minters engine, using a wallet created for the purpose of the drop.

ullamonster commented 2 years ago

You can generate the same policy id of any script you want using this command: cardano-cli transaction policyid --script-file nativetokens/MyNFT.script

So, let's step through it and set some pretend ids and hashes to keep things simple.

You are the owner of a policy wallet with pubkeyhash MyPolicyHash. You minted a collection to policy MyFirstPolicyID and it's now locked. You did this using MyPolicyHash. Now you want to set a royalty over it because you didn't follow CIP-0027 and you want it to be done onchain in a decentralized way. You mint a new single NFT to a new policy id, using MyPolicyHash (same policy hash used to make your other id). This new policy ID is MySecondPolidyID. Again, made from the same policy signing wallet. In this mint you have the metadata structure I proposed, which contains the locking height for both MyFirstPolicyID and MySecondPolicyID as well as MyPolicyHash. Note that MyPolicyHash MUST be the same for this new minted token and your "target" policy id. This token has the policy id MySecondPolicyID. Now anyone can look at that token's metadata, and the token's actual policy id at a utxo, and calculate using the command I shared above, and using the provided policy script data, the correct/expected policy id for both MyFirstPolicyID and MySecondPolicyID...and they can easily verify that the utxo token indeed has the derived MySecondPolicyID, meaning ONLY the owner of MyPolicyHash could possibly have minted it.

So we are not only validating that the given script data results in the correct policy ids, we are validating that the token minted sitting in the global royalty registry smartcontract has the policy id matching what you derive when applying the above command to the script data under "self" in the token's own metadata...and as such, if you can derive the policy id of your target id where you want to set your royalty over, using the same keyHash already proven as owned by you and correct slot height for that target policy, then you have a match.

myHash -> myOldLockedID -> myNewRoyaltyID (found in THIS token containing the metadata) = All must derive from the same keyHash.

This validation means the rate set in the metadata was set by the owner of MyPolicyHash, which was already used along with the slot height in the metadata for the target policy (MyFirstPolicyID) to successfully derive that target policy id.

This can be done after a policy locks. You can try it right now with the testnet data I provided in the readme. You can try to mint one that overrides mine, but your minted token will not/can not have a policy id derived from my keyHash. I hope that makes sense, I'm trying my best to explain it clearly, I'm a coder first not an explainer :)

But it works and I proved it in my readme and all of it is on the testnet for review. It sounds like you didn't understand what I was explaining, however if you believe it does not work I welcome proof as I cannot find any evidence of it not working and my tests resulted in proof that it works.

ullamonster commented 2 years ago

Quick note on wallet ownership: This is a challenge, however if a service was used, that service could mint this token on behalf of the user so long as the service did not destroy the wallet. I'm not sure why they would've but I agree that in that case this would not work and there would need to be some other method for those who had this occur before any onchain standard was in place.

However even in the case of custodial minting, this solution will work if custodians are cooperative. This is a better/improved outcome to the necessity of having to mint such a royalty token within the same policy, which even a cooperative custodian could not do after it was locked and if they did not adhere to the standards for having done so.

huths0lo commented 2 years ago

I'm still lost on your logic. The policy scripts are public. You can derive the hash of any policy id. And there is no such thing as a wallet signed policy. Policies and wallets are mutually exclusive.

ullamonster commented 2 years ago

I'm still lost on your logic. The policy scripts are public. You can derive the hash of any policy id. And there is no such thing as a wallet signed policy. Policies and wallets are mutually exclusive.

Hmm, not sure how to clarify better. If I have a token with a policy ID, and I present to you the slot height and my policy keyhash, you can validate that the policy ID I presented HAD TO BE minted by my keyhash/slot height combo. So if I also then present the slot height of any other token I minted using that policy keyhash, you can validate that indeed you get the same policy id when running my keyhash/thattoken's slot height through the cardano-cli transaction policyid command. So if I can present a token that displays it's own slot height AND the slot height of the token I aim to set royalty over, along with my 1 keyhash which was used to mint BOTH token policies, you can confirm that since I am controlling the token with the policyid which is derived from the keyhash, then the target policy which you can confirm HAD to be derived from the SAME keyhash, is indeed also mine.

So the royalty token has a big sign saying "here's the data to confirm that I was minted by the same policy keyhash as the target, therefore the royalty I'm setting is set by the owner of the policy keyhash signing key, as demonstrated in my own policy id derived from this data I'm holding up on a "sign" (metadata is the "bilboard" advertising this and giving others a way to validate my rights to the policy when combined with the royalty control token's own policy id seen at the utxo of the smartcontract)

I don't know how to explain it clearer sorry. But I have performed the steps in creating a proof of this concept on testnet. Given the royalty control token I minted, you can see that the metadata shows the params of how that control token was minted, the policy ID is a derivation of my policy keyhash and locking slot...and using that SAME policy keyhash you can then also prove that the target policy I'm setting royalty over, definitively was minted using that SAME policy keyhash which you can prove I own because the control token's policy id is a result of the keyhash and slot height for it. What am I missing here? I almost feel like I'm being trolled tbh, but don't want to sound mean if there is truly a miscommunication. :)

ullamonster commented 2 years ago

Further comments and response left at cardano forums starting here: https://forum.cardano.org/t/cip-royalties/68599/23?u=artistulla

huths0lo commented 2 years ago

I don't know how to explain it clearer sorry

You're going to have to find a way. Because I'm not following how you're deriving a hash that someone else couldnt also derive from publically available information. I get that you're saying "See I minted this royalty token with a policy". But that doesnt prove the connection to the previous policy that is locked.

And again, you still havent answered the question as to why the community would want to allow this anyways; since there is nothing preventing someone from doing a rugpull.

ullamonster commented 2 years ago

Can you demonstrate, as in walk me through your logic/thinking on how to do a rugpull utilizing this please (keeping in mind that markets have to change their coding to implement this new approach anyway and would certainly be responsible for having some standards, like setting a max limit...or not and warning their users...again up to the market, and what's to say someone sets 100% out the gate...if markets are reasonably responsible in how they pull the data and honor things, this is an issue that would have no motivating value. So a "rug puller" would need the entire marketplace to be in on it. )

To answer your question: " I get that you're saying "See I minted this royalty token with a policy". But that doesnt prove the connection to the previous policy that is locked."

Answer: Consider the following facts for a moment and I think it will make sense.

  1. The Royalty Control token metadata contains the ONE policy keyhash used for minting BOTH this Royalty Token and the target policy we claim we own.
  2. The Royalty Control token metadata contains the slot locking height for BOTH.
  3. The Royalty Control token Datum contains our target policy, hashed.
  4. The Royalty Control token has a policy ID we can see at the UTXO.

So we have 2 policy ids: 1 is this token's own 1 is "claimed"

How do we prove we own the claimed, given the 4 facts above?

It's just simple algebra.

Let's call the Royalty Control token's policy ID A. We'll call the 1 policy keyhash which both apparently share, B. And finally the policy id of the target we seek to claim that we own and set royalty over, C.

If B, our keyhash, = A ...and B = C, Then A = C

If I can prove to you in a way you can compute using the cli and command "transaction policyid" given each A and C's policy scripts...and both contain B as their keyhash...and you are able to successfully produce A (royalty's policyid) and C (target's policyid) out of B, then B made A and C...which means the owner of A is the owner of B and the owner of C.

ullamonster commented 2 years ago

Two quick thoughts on "what if a creator goes rogue and suddenly ups the %":

By giving artists the ability to do this, when they may not have known better before and it's now locked, is of greater weight and value when abuse could easily be mitigated by a marketplace. If, for competitive advantage, a marketplace had a complex/centralized way for an artist to setup royalties over a locked/unroyatied policy, they would do it and they would mitigate the risks of abuse or attempts at taking 100% (rug pull) etc. So why not implement a solution that is decentralized and requires arguably less effort on the part of markets to adopt and mitigate?

Another thought is this: Say a creator sets a reasonable % and then goes rogue. It could be a standard that if you submit another Royalty Control token to the smartcontract, the only section you may modify is the "self" slot height of course (and policy id over the royalty control of course), the "controlslot" since it's for when this new one was made, and the "addr" and that's it. If a creator changes the %, the marketplace ignores that entirely. They can see which is the latest vs first by taking the "controlslot" value and comparing them both against each other and against the actual mint tx times of each Royalty Token (so the control slot value cannot be faked).

Again, what is gained in this is inclusion of anyone past, present, future who was ignorant or had someone helping them who didn't know how or if about setting royalties etc...allows flexibility in their rollout for new projects...and very crucially allows for modification of the payout addresses. All while maintaining decentralized/onchain...and all while utilizing the fails plutus script!! :)

huths0lo commented 2 years ago

I'm going to respond, and then abandon this conversation, because you obviously want to see this through, and godspeed to you for that.

But here are the problems. The risk reward is 100% not congruent with ensuring to the community that a creator cannot do a rugpull. And if someone wants to create an nft, 5 minutes of research would go a very long ways. So if they chose to run off and mint a bunch of nft's without doing 5 minutes of research, we should not be introducing a system that can ruin every other project, who did it the right way. You could spend your efforts educating, and it would be far more adventageous.

Secondarily, I dont see any way to prove anything about the original policy. Everything you are talking about is readily available publicly. The only way you can prove that a key is yours, is by minting something with it. You cant do that once its locked. And giving the hash proves nothing. Because without your key being public, we cant hash it to see if we derive at the same hash.

Now your next problem; how do you convince the marketplaces to adopt your plan, when they've already agreed to the 777 standard? Any standard is exactly that; something agreed upon by the majority. Unless you're planning to have a conversation with all the major markets, and getting them to agree to this new design, its dead on arrival. And I think you already know what their answer will be. Sidestepping it by just tossing up a new proposal isnt creating a new standard. A standard only comes forward when people agree to use it. I can tell you that I would never support what you're proposing.

ullamonster commented 2 years ago

Perhaps a good compromise on these points of view is to integrate the two. It's easy enough to simply add additional metadata to a 777 token and markets can still utilize the same cip-0027 format token either as an initial royalty or when making payment modifications with proof of ownership. So the policy scripts of the 777 and the target can still be embedded without compromising the structure or use of the 777, and while expanding it's capablity.

So to remain consistent and satisfy the parameters being employed by CIP-0027, it may be a reasonable approach to merge these two into a single implementation. The caveat would be the 777 token, if found with additional metadata particularly the "self" section and if found locked at the royalty registry smartcontract, would be an indicator that this particular 777 also contains additional metadata possibly pertaining to a different (but provably owned) policy ID. Of course within the metadata it may be discovered that the target is it's own policy ID, it can be without consequence. So it is a matter of expanding the capabilities, options, reach and future proofing the implementation and design of the 777 token, as outlined at https://github.com/MadeWithLovelace/MintedWithLovelace/tree/sandbox/dapp-smartcontracts/global-registry (not updated with this new merge approach yet, but I'll update it soon).

And whether or not others find this useful is of course yet to be seen. I'm honestly just trying to make some improvements on what you've initiated, not really interested in combatting which it feels is the general tone, a lot of "that won't work, prove it, you haven't answered xyz" and not any "let's talk about that, you bring up some good points about helping artists and improving the system in reasonable ways." I'm still open to constructive discussion on this and would love to find a ground where this can take place in the interest of progress and comradery rather than what feels and looks like defensiveness and combativeness.

I'll leave this discussion open for anyone else who may be interested in exploring from a solution/iimprovement perspective :)

huths0lo commented 2 years ago

I'm not the ultimate authority on the matter. The CIP was written from a community first initiative. It was always intended to have future discussions on ways to enhance it. But above all else, the design has to protect the community. Introducing a significant flaw (aka the potential to do a rugpull) is going to be an uphill battle. I've mentioned this previously; the original text of the CIP is vastly different than its final state. That was entirely because of the compromises made between all of the major parties involved. I've seen the proposal attacked by a handful of fringe parties. All of the people that have attacked it, have a distinct agenda; and the design of the policy doesnt work well for the agenda. Their agendas completely ignore the decisions that were put in to the design, to protect the community. A few people have said theyre going to improve the design, which I'm confident the community would be receptive too. However, every proposal I've seen is just to try to get their proposers agenda across.

I honestly am not really sure what problem you're trying to fix. The standard is pretty well known within the community now; and is becoming more so every day. If your concern is that the next spacebudz will launch, and they'll have made some major mistep; that is highly unlikely. Claymates came to know of the policy, and implemented it on their latest drops. If your concern is that it takes too long for policy ids to be verified, then your concern is with the centralization of how policies get verified, and you should be looking at the proposal that is being spearheaded by the Cardano Foundation, and the Cardano Guild of NFT creators to remedy that.

Its impossible to do something that makes everyone happy. Anyone whos gone to Thanksgiving dinner knows this. On that same token, you cant have a free for all. I am 100% open to improving the design. I'm not open to fixing fringe uses cases at the cost of defeating entire purpose of the design. As you said, you can put much more metadata in to an NFT's minting than whats in the proposal. Thats the reason it was decided to pick a new tag entirely. You can mint 721, and any other tags metadatas you like. If you want to strike out and create an entirely new tag, thats also perfectly acceptable. I have no issue with you putting up a new proposal for a 777 tag of your own design. But I'm saying this as my sole opinion; I dont see it being highly successful unless it meets all of the safety features of the current design, and also improves upon it.