cadence icon indicating copy to clipboard operation
cadence copied to clipboard

ReadOnly(immutable) Auth Reference

Open bluesign opened this issue 3 years ago • 3 comments

Issue To Be Solved

I think it would be very nice to have ability to have readonly auth reference.

There is a very nice pattern used to query data like here: https://github.com/dapperlabs/nfl-smart-contracts/blob/6eb1b95db276e7a5efa702be601e8f541dbf2ffd/contracts/AllDay.cdc#L126

But this requires writing a lot of extra code.

Instead maybe we can have a readonly auth reference to Any. Which can return a immutable reference, which panics when someone tries to mutate.

Suggested Solution

I am thinking something like:

var t = &thing as readonly &T

bluesign avatar Dec 10 '21 13:12 bluesign

This is an interesting problem. One of the reasons why we haven't introduced the notion of "constness" into Cadence is because it's inherently single-dimensional. It's constant, or it's mutable. And most people think of this as "It's safe, or it's unsafe."

In the context of a blockchain, I think that this dimension is both too restrictive, and also not fine-grained enough. I can give two examples:

  1. Think about a token Vault. Giving out public const access to my Vault is too restrictive, because I also want to give out access to deposits, which are mutating. But giving out read-write access to my Vault is also wrong, because I don't want to give public access to withdrawals. Const/mutable is the wrong axis for access control in this case.
  2. Imagine a possible future version of CryptoKitties on Flow. I might want to give you access to use my Kitty for siring, which is a mutating action. But if I do, I probably don't want you to rename my Kitty, which is a different mutating action. But I want anyone to read the name and genes and breeding count of my Kitty. So, I have three different sets of access control I want to give, const/mutable isn't fine-grained enough.

Our answer in designing Cadence was restricting access to specific interfaces. If I give &Kitty{Siring}, you can use my Kitty to get your own cat pregnant. If I give you &Kitty{KittyInfo}, you can read the genes and other metadata, but you can't do anything else with it. And, of course, I can give you &Kitty{KittyInfo, Siring} and you can do both (but not rename her!).

What's missing from this, and what I think you're picking up on, @bluesign, is that we don't have a way to let someone get a more specific, but still safe interface from a generic reference. For example, you might give me a reference to an NFT, and it'd probably be okay for me to cast it to &{KittyInfo} if the underlying NFT is actually a Kitty. Of course, Cadence can't know that &{KittyInfo} is safe, while &{Siring} is not. So it doesn't allow any down-casting without an auth reference.

One possible answer is to allow the author of a Resource class to be able to tag certain interfaces as being "safe". Again, this is somewhat implicit in other languages that allow you to cast a const reference of a base type into a const reference of a derived type. It's keeping the reference holder's access in the same "category". But, as I pointed out before, the categories for Resources are more complex than just const/mutable.

If the author of the hypothetical Kitty contract could say "KittyInfo" is safe, then you wouldn't need an auth reference, and you could get the info you need by downcasting.

Would this address the point you are getting at, or is there something else you're thinking of here?

dete avatar Dec 14 '21 01:12 dete

Hey @dete, I love restrictions on composites, but it seems it is well suited to filter methods than fields. With composite fields (especially nested ones), you don't have any control over.

This is leading to habit of setting nested members ( usually every member ) private on the resource, and using getter methods which can be restricted with interfaces.

Then we have something like this: ( code duplication )

    pub struct SeriesData {
        pub let id: UInt64
        pub let name: String
        pub let active: Bool

        init (id: UInt64) {
            let series = &AllDay.seriesByID[id] as! &AllDay.Series
            self.id = series.id
            self.name = series.name
            self.active = series.active
        }
    }

    pub resource Series {
        pub let id: UInt64
        pub let name: String
        pub var active: Bool
   }

Which is super useful to convert properties to readonly structs. ( and safer )

But if we could get readonly reference to Series, It would be just:

&AllDay.seriesByID[id] as readonly &AllDay.Series

Here you can say to put:

    pub let id: UInt64
    pub let name: String
    pub var active: Bool

in an interface, but it is fine until you have a composite field and things can get nested.

I think less code is better in general, also I believe having access to all resources ( even if I am not the owner ) in a read only way, can be useful in general.

Maybe even in the future we can even have functions to locate an NFT ( let's say by id ) wherever in the blockchain it is.

bluesign avatar Dec 14 '21 15:12 bluesign

After long time, I did some thinking and came to the conclusion that single dimension is not enough @dete I agree with you on that totally.

But then I think also there is good middle ground here:

What if, when we cast to readonly, we can down-cast freely.

var kitty = borrow<&Kitty{Siring}>(from:/somewhere)
var kittyReadonly = &kitty as readonly &Kitty

and now I can access Siring with kitty, but all other properties etc with kittyReadonly.

Main problem I am lately seeing in the contracts is access(contract) (aka private) abuse. This data is on the chain, but normally you cannot access most of it.

Of course there are some obstacles like Capabilities and what will happen if they are copied and stored etc. But I think it can be somehow fixed with some creative thinking.

bluesign avatar Aug 03 '22 15:08 bluesign

I believe this could now be achieved using entitlements, and two proposed FLIPs combined: https://github.com/onflow/flips/pull/89 and https://github.com/onflow/flips/pull/86

SupunS avatar May 31 '23 15:05 SupunS

thanks @SupunS, I am closing in favor of better system.

bluesign avatar May 31 '23 15:05 bluesign