-
Notifications
You must be signed in to change notification settings - Fork 23
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
FLIP 95: proposal for entitlements migration #95
Conversation
One question came up in some Hybrid Custody discussions. What happens to dictionaries which are key'd by type when entitlements launch? For some types, that will not matter, but HC has a dictionary key'd on |
Hmm, this is an interesting question. Naively I'd say we would just migrate the type of the key of the dictionary according to this scheme, but the important point to note there is that because entitlements have no runtime counterpart (i.e they only exist statically), this would mean that the keys in the dictionary would only ever have the same entitlements as the key type. This may mean some of the keys inside the dictionary become less powerful than they were before. I don't really see any way around this though; it is not really possible to create a dictionary like this, where the keys within have more entitlements than the general key type. |
Can we drill into this more? It's actually pretty important to have a feature like this because it's what allows HybridCustody to share explicit capabilities, and it's definitely in the realm of possibility that you would want to share two capabilities to the same resource for different purposes. Not only that, but keying on the type is what allows the consumer to actually know what permissions they expect to get. For instance, I might ask to borrow Taking this away seems like a pretty big change, why can't entitlements have a runtime counterpart? And does that mean |
Ofc capability can hash with entitlements, no? |
Sure. What I mean when I say that there isn't a runtime component to entitlements is this: if I have a program like so:
This will successfully return an This is not the case with entitlements:
This would error at runtime, as upcasting (Side note: this is somewhat of an oversimplification: of course you can save a Capability value with entitlements to storage, and then reload it with those same entitlements. This requires some form of runtime typing, but it is very limited to just support this particular use case. For this reason it might be more accurate to say that entitlements do have runtime types, but they are restricted such that they always exactly match the value's static type). What this all means is that if I have a dictionary that is typed as That said, I agree it is still valuable to be able to have multiple capabilities on the same resource that have different permission sets. Would it be possible to distinct dictionaries for each capability "type" that can be granted? I want to be clear that the behavior of Capabilities with regard to their borrow types are the same, you can only borrow a Capability with a type if that Capability is actually accessible with that type. |
Ok but if my dictionary was {Capability: V} there is no problem right? |
cc @turbolent I believe we were planning to remove the untyped |
If you do this you would break Hybrid Custody entirely, do not remove |
I'm not sure I follow on this question, but I don't think that's possible, no. But here is the contract which manages this: Basically, you add a capability to the CapabilityProxy.Proxy resource and it gets stored in one of these two dictionaries:
The type is the type of the Capability itself (
What would the borrow type be in this case? Does this mean we just need to rework the key to be the borrowed type instead? |
I believe that for generic Capabilities (i.e. those without type arguments like in this example), this should actually work. There is no static entitlement present to conform the runtime type to, so in this case the runtime type would just stay the same as whatever it used to be. |
But under the hood it is a typed capability, it's just that the thing storing it can store any capability which is why it isn't typed on the dictionary itself. At the end of the day, we will need some way to go from Am I missing something here? |
|
So this behavior is also changing, then? The following always panics:
calling
A lot of things are built on this assumption, I didn't realize entitlements were set to redefine not just access control but how we ask for and evaluate types as well, I think we need a call about this, it sounds bigger than I thought. |
Sorry, I think I may have miscommunicated this, the behavior we are going to have is this:
This "change" (it's not really a change since this didn't exist previously) is only for looking at which entitlements are present on the type. If the borrow type is upcast, |
Up-casted types are totally fine to stay their ambiguous types (I think), I guess my confusion is how we ensure migration can happen correctly such that we still accurately keep these types we key on since they will be a big part of hybrid custody. I want to be as clear as possible here since there seems to be confusion, let's say I do the following:
When I try to obtain that capability again, would I be able to reconstruct
|
Because you’re using the generic Capability type these examples should both be fine I believe. If the dictionary type used a non-generic Capability then this would not work when the capabilities are entitled. |
I think gist of this thread is we need to guarantee with contract update logic. If you were storing as our old |
Thanks for the details and clarification @dsainati1!
I think the last piece here, then, is how we can ensure that the type stored as a key in a dictionary like this one is the same as the type you can actually get to. It sounds like this is fine, but I just want to confirm that things will work or that a migration guaranteeing this kind of outcome is possible. So if I have the following: let caps: {Type: Capability} = {}
// The interface NFTStorefrontV2.Remover doesn't exist, it's just an example
let cap = acct.getCapability<&NFTStorefrontV2.Storefront{NFTStorefrontV2.Remover}>(...)
caps[cap.getType()] = cap and we migrate to entitlements, would something like this be able to read the dictionary back? Assume I make an equivalent entitlement called "remover" let type = Type<Capability<auth(remover) &NFTStorefrontV2.Storefront>>()
let cap = caps[type]! as! Capability<auth(remover) &NFTStorefrontV2.Storefront>
assert(cap.check(), ...) |
Ah I see what you are saying. Yeah those would be two separate runtime types. The one stored there pre-Stable Cadence would have a @turbolent would it be feasible to also migrate existing reference and capability runtime types as part of this migration to handle this case? |
This flip does not require a review and approval by wider community, as it makes no impact on the Cadence developers, it only impacts the migration process of entitlements during Flow network upgrade to Cadence 1.0. The Flip was created for informing the community and documenting the migration proposal in public. |
This adds a "FLIP" proposing a migration scheme for entitlements to enable the rollout of this feature with the release of Stable Cadence. Note that this is not strictly speaking a protocol or language change, just a proposal for how we will handle the rollout of the feature.