onflow / cadence

Cadence, the resource-oriented smart contract programming language 🏃‍♂️
https://developers.flow.com/cadence
Apache License 2.0
531 stars 139 forks source link

Getting/Borrowing a capability as a sibling interface-type of the orignally issued type #3533

Open SupunS opened 4 weeks ago

SupunS commented 4 weeks ago

Issue to be solved

Came up in the thread https://discord.com/channels/613813861610684416/1273464365701398568/1273464651971039336.

Suppose a resource R implements both I1 and I2.

resource R: I1, I2 {}

And a capability to the resource is being issued with I1. i.e: Capability<&{I1}>

Currently this capability is no allowed to be get/borrowed as Capability<&{I2}>. However, once the capability is borrowed, it is possible to upcast/downcast to any of the interface-types that R implements.

The question is, does it make sense to borrow it with I2 as well? (note that none of these grant extra permissions/entitlements)

Suggested Solution

No response

bjartek commented 4 weeks ago

It appears that this work for caps created in 1.0 but not migrated caps? See here https://discord.com/channels/613813861610684416/1273709822218473513

SupunS commented 4 weeks ago

It appears that this work for caps created in 1.0 but not migrated caps? See here https://discord.com/channels/613813861610684416/1273709822218473513

There the problem is, borrowing as a super-type/sub-type works when issued with a single interface. But the same fails when issued with multiple interfaces. e.g:

resource interface I1: I2 {}

resource interface I2 {}

resource interface I3 {}

 resource R: I1, I2, I3 {}

// Case-1: Issue as `{I2}` only
// Below borrowing will pass
var a = self.account.capabilities.storage.issue<&{I2}>(/storage/r)
self.account.capabilities.publish(a, at: /public/a)

// Case-2: Issue as `{I2, I3}`.
// Below borrowing will fail
var a = self.account.capabilities.storage.issue<&{I2, I3}>(/storage/r)
self.account.capabilities.publish(a, at: /public/a)

// Borrow as `{I1}`
self.account.capabilities.borrow<&{I1}>(/public/a)!
bjartek commented 4 weeks ago

Nice find

SupunS commented 3 weeks ago

Opened a separate issue https://github.com/onflow/cadence/issues/3537 for the second one, as those two more likely need separate handling

SupunS commented 3 weeks ago

Took a while for me to realize, https://github.com/onflow/cadence/issues/3537 is also actually the same sibling problem. e.g: The type hierarchy is as follows:

    AnyResource
        / \
       /   \
     I2     I3
    /  \   /
   /    \ /
  I1  {I2,I3}
   \    /
    \  /
      R

{I2,I3} is not a direct subtype or a supertype of I1, but rather a sibling. That's why the borrowing fails, just like the sibling case (original issue in the description).Though both are super-types of R.

A more realistic example is added in https://github.com/onflow/cadence/issues/3537#issuecomment-2299424819

SupunS commented 3 weeks ago

We can support this from the technical point of view. But then it would make the capability issue type mostly useless, and only the entitlements would matter.

j1010001 commented 2 weeks ago

This is not causing staging/update to fail, if someone runs into the problem they can solve it by upgrading their contract post-Crescendo. To reduce risk we will not fix this before Crescendo but after. We will update Docs to explain the problem and how to work around it.