Skip to content
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 for entitlements and safe reference downcasting #54

Merged
merged 34 commits into from
May 23, 2023
Merged
Changes from 5 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
15865cd
beginning of FLIP
dsainati1 Dec 14, 2022
7d033d4
add more details
dsainati1 Dec 15, 2022
73b0757
add section on interface inheritance
dsainati1 Dec 16, 2022
be521b9
fix formatting
dsainati1 Dec 16, 2022
74551c7
fix formatting
dsainati1 Dec 16, 2022
8a5cfa1
typo
dsainati1 Dec 16, 2022
fd62422
Apply suggestions from code review
dsainati1 Jan 3, 2023
ba6a3d0
Merge branch 'sainati/auth-fields' of github.com:onflow/flips into sa…
dsainati1 Jan 3, 2023
040d35f
respond to review
dsainati1 Jan 3, 2023
85edfaa
add an alternatives considered section
dsainati1 Jan 3, 2023
c92d088
clarify auth types are subtypes of non-auth references
dsainati1 Jan 4, 2023
8fe43e9
add section about potentially removing restricted types
dsainati1 Jan 4, 2023
590fbe3
Apply suggestions from code review
dsainati1 Jan 5, 2023
f7f7079
add section about rollout
dsainati1 Jan 10, 2023
1192f36
update rollout section to remove reference to versioned references
dsainati1 Jan 20, 2023
bbff7fc
update FLIP to incorporate new entitlements idea, currently generated…
dsainati1 Feb 2, 2023
f842b76
add section about standalone entitlements
dsainati1 Feb 2, 2023
30ed8e2
update FLIP to refer to separate entitlement language objects
dsainati1 Feb 17, 2023
8dba175
update FLIP to improve subtyping model for interfaces
dsainati1 Feb 21, 2023
7e3ac2b
add info about conjuctive and disjunctive entitlement sets
dsainati1 Mar 9, 2023
e7844ee
Merge branch 'main' of github.com:onflow/flips into sainati/auth-fields
dsainati1 Mar 9, 2023
b959350
add sections about entitlement mappings
dsainati1 Mar 14, 2023
5ad5053
fix minor mistake
dsainati1 Mar 14, 2023
2951197
fix typo
dsainati1 Mar 15, 2023
0e4e857
fix formula typo
dsainati1 Mar 17, 2023
9554833
remove section about restrictions on which entitlements may appear on…
dsainati1 Mar 31, 2023
1990033
Merge branch 'main' of github.com:onflow/flips into sainati/auth-fields
dsainati1 Apr 25, 2023
a362d9c
rewrite attachments section to use new proposed idea
dsainati1 Apr 25, 2023
c5d0eae
remove dynamic behavior for entitlements
dsainati1 Apr 25, 2023
cf4b8d6
respond to review
dsainati1 May 2, 2023
3ae4318
respond to review
dsainati1 May 10, 2023
b156ed4
Apply suggestions from code review
dsainati1 May 18, 2023
f6fba22
add more detail to example
dsainati1 May 19, 2023
7d6ab3c
Merge branch 'sainati/auth-fields' of github.com:onflow/flips into sa…
dsainati1 May 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
351 changes: 351 additions & 0 deletions cadence/2022-12-14-auth-remodel.md
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

file name should be 20221214-...

Original file line number Diff line number Diff line change
@@ -0,0 +1,351 @@
---
status: draft
flip: NNN (do not set)
dsainati1 marked this conversation as resolved.
Show resolved Hide resolved
authors: Daniel Sainati ([email protected])
sponsor: Daniel Sainati ([email protected])
updated: 2022-12-16
---

# `auth` model changes

## Objective

This FLIP proposes two major changes to the language:
1) The addition of a new access modifier on contracts, structs and resources: `auth`
2) A change to the behavior of reference types to add granular authority restrictions to specific interfaces, and allow safe downcasting

## Motivation

Currently in Cadence, when users are in possession of a reference value, unless that reference is declared
with an `auth` type, users are unable to downcast that reference to a more specific type. This restriction
exists for security reasons; it should not be possible for someone with a `&{Balance}` reference to downcast that
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe make a general point, and then mention (and link to) the Fungible Token standard contract as a concrete example, before referring to specific types defined in that contract – readers may not (immediately) know what those types (Balance, Vault) refer to.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems likely to me that anybody reading or contributing to a language FLIP for Cadence or Flow is aware of the basic Balance, Vault, Provider and Receiver types.

to a `&Vault`. Cadence's current access control model relies on exposing limited interfaces to grant others capabilities
to resources they own.

However, this can often result in restrictions that are unnecessary and onerous to users. In the above example, a user
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Fungible Token contract is a great running example 👍

Maybe mention that:

  • Balance provides access to the read-only field balance
  • Receiver provides access to a mutating function deposit, but that mutation is "safe" in the sense that a call by any party is OK
  • Deposit provides access to a mutating function withdraw, but that mutation is "unsafe", it should not be possible for any party to call the function, only certain authorized parties

The Balance and Receiver types are already mentioned, but maybe help the reader and explain why their use is sade, so they do not have to look up the details in the code, and also mention the Deposit type explicitly and why access should be restricted.

with a `&{Balance}` reference would not be able to cast that reference to a `&{Receiver}` reference, despite the fact
that there would be no safety concerns in doing so; it should be possible to call `deposit` regardless of what kind of
reference a user possesses.

The proposed change is designed to overcome this limitation by allowing developers to directly mark which fields and functions
are dangerous and should be access-limited (using the new `auth` keyword), and which should be generally available to anyone.
dsainati1 marked this conversation as resolved.
Show resolved Hide resolved

## User Benefit

This feature would make interacting with references significantly easier, as users would no longer be restricted from
accessing methods like `deposit` that are safe for anyone to use, just because they do not possess a reference of the specific type,
or because they are trying to write a generic method that works, say, on any `NFT` type.

It would also increase safety, as we would be able to do additional checking on the values and types to which users create capabilities.
dsainati1 marked this conversation as resolved.
Show resolved Hide resolved
For example, it is currently possible for a user to (presumably accidentally) create a public Capability directly to a `Vault` object,
rather than a Capability to a `&{Balance, Receiver}` restricted type as is intended. With these changes, such a Capability still would
not be subject to having its funds `withdraw`n, as this `Vault` Capability would not be `auth`. In order to have access to an `auth`
method like `withdraw`, the user would need to explicitly create the Capability with an `auth` reference, and we could enforce statically
(or warn) that `auth` reference capabilies should not be stored publicly.

## Design Proposal

### `auth` fields

The first portion of this FLIP proposes to add a new access control modifier to field and function declarations in composite types:
`access(auth)`, which allows access to either the immediate owner of the resource (i.e. anybody who has the actual resource value),
or someone with an `auth` reference to the type on which the member is defined.

Like `access(contract)` and `access(account)`, this new modifier sits exactly between `pub` and `priv` (or equivalently `access(self)`)
in permissiveness; it allows less access than `pub`, but strictly more than `priv`, as an `auth` field or function can be used anywhere
in the implementation of the composite. To see why, consider that `self` is necessarily of the same type as the composite, meaning
that the access rules defined above allow any `auth` members to be accessed on it.

As such, the following would be prohibited statically:

```cadence
pub resource R {
access(auth) fun foo() { ... }
}
let r: &R = // ...
r.foo()
```

while all of these would be permitted:

```cadence
pub resource R {
access(auth) fun foo() { ... }
pub fun bar() {
self.foo()
}
}
let r: @R = // ...
r.foo()
let ref: auth &R = &r
ref.foo()
```
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll be honest. I would love to remove or at least deprecate pub and priv. If this code examples (for example) used access(self) and access(all) it'd be easier to parse (visually, as a human) and more consistent.

pub resource R {
    access(E) fun foo() { ... }
    access(all) fun bar() {
        self.foo()
    }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree 100%. I'll update the examples in the FLIP, but IMO a proposal to remove and replace pub and priv deserves its own FLIP.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes it simpler, and I like that.


Note also that the interface implementation rules allow a composite to implement an `access(auth)` interface member with a `pub`
composite member, as this is less restrictive, but not the other way around, as this would allow the interface type to gain more access
to the composite than should be possible. So this would be acceptable:

```cadence
pub resource interface I {
access(auth) fun foo()
}

pub resource R: I {
pub fun foo() {}
}
```

since upcasting a value of type `&R` to type `&{I}` would not allow the reference to call any more functions. There is no similar concern
with downcasting a `&{I}` to a `&R` and gaining the ability to call `foo`, because `foo` is declared as `pub` on `R`, and we treat the
access control declarations on the concrete composite as the "source of truth" for the value's access control at runtime.

while this is not:

```cadence
pub resource interface I {
pub fun foo()
}

pub resource R: I {
access(auth) fun foo() {}
}
```

since if this were to typecheck, anybody with a `&R` reference could upcast it to `&{I}` and thus gain the ability to call `foo`.

### Safely Downcastable References

The second, more complex part of this proposal, is a change to the behavior of references to allow the `auth` modifier not to
refer to the entire referenced value, but to the specific set of interfaces to which the referenced value has `auth` permissions.
To express this, the `auth` keyword can now be used with similar syntax to that of restricted types: `auth{T1, T2, ...}`, where the `T`s
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will the old syntax (i.e: without curly braces) continue to be supported, say for restricted types?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally the old syntax would become deprecated eventually, as currently the only reasonable way to interpret it is as a reference that is auth over the entirety of the referenced type, which in the new model would be a very dangerous behavior to have as a default. What would be the use case for keeping the old auth syntax that does not require a restriction set?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sweet, then we can reuse the restricted types syntax when we add algebraic effects 🥲

in the curly braces denote the interface types to which that reference has `auth` acccess. This permits these references
dsainati1 marked this conversation as resolved.
Show resolved Hide resolved
to access `auth` members on the interfaces to which they have `auth` access. So, for example, given three interface definitions and
a composite definition:

```cadecnce
dsainati1 marked this conversation as resolved.
Show resolved Hide resolved
pub resource interface A {
access(auth) fun foo()
}
pub resource interface B {
access(auth) fun bar()
}
pub resource interface C {
pub fun baz()
}
pub resource R: A, B, C {
// ...
}
```

a value of type `auth{A} &R` would be able to access `foo`, because the reference has `auth` access to `A`, but not `bar`, because
it does not have `auth` access to `B`. However, `baz` would be accessible on this reference, since it has `pub` access.

The interfaces over which a reference can be authorized must be a supertypes of the underlying referenced type: it is nonsensical,
for example, to express a type like `auth{A} &{B}`, since `A` and `B` are disjoint interfaces that do not share a heirarchy, and thus
the `auth` access to `A` granted by this reference would have no effect. As such, given a list of `I`s in a reference `auth{I1, I2} &T`,
we require that `I` appear in the conformance heirarchy of `T` if it is a composite type, or that `I` exists in the restriction set of `T`
if it is a restricted type.

The other part of this change is to remove the limitations on resource downcasting that used to exist. Prior to this change,
non-`auth` references could not be downcast at all, since the sole purpose of the `auth` keyword was to indicate that references could be
downcast. With the proposed change, all reference types can be downcast or upcast the same way any other type would be. So, for example
`&{A} as! &R` would be valid, as would `&AnyResource as? &{B, C}` or `&{A} as? &{B}`, using the hierarchy defined above.

However, the `auth`-ness (and the set of interfaces for which the reference is `auth`) would not change on downcasting, nor would that set
be expandable via casting. The subtyping rules for `auth` references is that `auth {U1, U2, ... } &X <: auth {T1, T2, ... } &X` whenever `{U1, U2, ... }`
is a superset of `{T1, T2, ... }`, or equivalently `∀T ∈ {T1, T2, ... }, ∃U ∈ {U1, U2, ... }, T = U`.
dsainati1 marked this conversation as resolved.
Show resolved Hide resolved

As such, `auth{A, B} &R` would be statically upcastable to `auth{A} &R`, since this decreases the permissions on the
reference, it would require a runtime cast to go from `auth{A} &R` to `auth{A, B} &R`, as this cast would only succeed if the underlying
runtime value was `auth` for both `A` and `B`. So in the code below:
dsainati1 marked this conversation as resolved.
Show resolved Hide resolved

```cadence
fun foo(ref: &R): Bool {
let authRef = ref as? auth{A, B} &R
return authRef != nil
}
let r <- create R()
let ref1 = &r as auth{A} &R
let ref2 = &r as auth{A, B, C} &R
```

`foo` would return `true` when called with `ref2`, because the runtime type of `ref` in the failable cast is a subtype of `auth{A, B} &R`, since
`{A, B, C}` is a superset of `{A, B}`, but would return `false` when called with `ref1`, since `{A}` is not a superset of `{A, B}`.

Because only interface types can appear in the curly braces after the `auth` keyword, the `auth` keyword without a restriction set will denote
full (unrestricted) `auth` access to the reference type. E.g. `auth &R` would be a reference with full `auth` access to `R`.

### Drawbacks

This dramatically increases the complexity of references and capabilities by requiring users to think not only about which types they
are creating the references with, but also which `auth` accesses they are providing on each reference they create. It also increases
the implementation complexity of the type system significantly by adding an additional dimension to the reference type's subtype heirarchy.

### Best Practices

This would encourage users to use the new `auth` access modifier to restrict access on their contracts and resources; and use different
`auth` restriction sets to control access.

### Tutorials and Examples

With this new design, the `Vault` heirarchy might be written like so:

```cadence
pub resource interface Provider {
access(auth) fun withdraw(amount: UFix64): @Vault {
// ...
}
}

pub resource interface Receiver {
pub fun deposit(from: @Vault) {
// ...
}
}

pub resource interface Balance {
pub var balance: UFix64
}

pub resource Vault: Provider, Receiver, Balance {
access(auth) fun withdraw(amount: UFix64): @Vault {
// ...
}
pub fun deposit(from: @Vault) {
// ...
}
pub var balance: UFix64
}
```

Then, someone with a `&{Balance}` reference would be able to cast this to a `&{Receiver}` and call `deposit` on it.
They would also be able to cast this to a `&Vault` refrence, but because this reference is non-`auth` for `Provider`,
dsainati1 marked this conversation as resolved.
Show resolved Hide resolved
they would be unable to call the `withdraw` function. However, if a user possessed a `auth{Provider} &{Balance}` reference,
they would be able to cast this to `auth{Provider} &{Provider}` and call `withdraw`.
Copy link
Member

@SupunS SupunS Jan 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The auth{T} &{U} syntax is a bit confusing to me.

My first concern is, in a concrete type (struct/resource) scenario, the reference-taking syntax is let ref = &r as auth &T, but for a restricted type, the user has to specify the type twice let ref = &r as auth{T} &{T}. Maybe make the T in auth{T} optional if both the auth-type and the restricted type are the same? Anyway they can't downcast it further I guess.

The second concern is it is easy to confuse the type of the resulting reference. For eg: auth{Provider} &{Balance} made me wonder whether I can (or why I can't) call withdraw on the resulting reference.
I don't really have any suggestions for an alternative syntax, but if we can find one/improve this to iron out any potential confusion, that would be great.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree the syntax is a little bit confusing; at first glance it seems like you have to specify the type twice (once in the auth{T} and once in the &{T}, but they are really doing quite different things. I think it is going to be quite rare for users to want to declare a reference value this way, however. In fact, with these changes there is little reason to ever actually create a reference value with anything other than its underlying concrete type: let ref = &r as auth{Provider} &Vault makes a lot more sense than let ref = &r as auth{Provider} &{Provider, Receiver} given that reference types can now be downcast. The "security" aspect of restricted types is effectively being moved to the auth part of the type, while the actual reference type itself is now only being used for interface conformance checking.

I suspect that if we implemented this change, we would only really see restricted types being used as supertypes; i.e. if I want to write a function that operates over all Providers I would write it as fun(_ p: {Provider}).

Copy link
Member

@turbolent turbolent Jan 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I initially had feedback similar to Supun's, but ended up not posting it, so it's great it came up again:

Restrictions (Us in T{Us}) are not really restrictions anymore, as downcasting is now allowed.

We still might want to express something like T{Us} (where Us is a set of interfaces) for the case where T is AnyStruct/AnyResource. Maybe we should just make the syntax {Us} and call it an "interface set"?

The continued existence of restricted types (T{Us}) maybe cause confusion for developers, so maybe we should just get rid of them along the way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I really like this idea; the interface set type makes a lot of sense and is more intuitive than restricted types anyways.

Copy link
Member

@SupunS SupunS Jan 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With restricted types gone, {Us} sounds more like an 'intersection type' 😄

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, {Us} has been always pretty much like TypeScript's intersection types. Swift has protocol composition, a similar feature (even uses the same U1 & U2 syntax as TS), though there such a type is not first class (e.g a variable cannot have such a type)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for changing restricted types to intersection types. Allowing interfaces in type position (even if they need to be qualified somehow) would obviate the need for a separate Interface proxy type, making Cadence issue #2233 trivial to implement

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe replacing restricted types with standalone intersection types should be its own FLIP?


### Compatibility

This would not be backwards compatible with existing code, and thus would need to be part of the Stable Cadence release.
Most contracts would need to be audited and rewritten to use the new `auth` access modifier, as they would immediately become
vulnerable to having `pub` fields accessed from now-downcastable references.

### User Impact
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vishalchangrani for visibility/GTM etc


There is a huge user impact to rolling out this change; every single contract would become invalid, and would need to be manually audited
by the authors in order to be used. This is because all code previously written using the old model of access control (wherein giving someone
a `&{Balance}` would prevent them from calling `pub` functions on `Provider` like `withdraw`), would become invalidated, allowing everyone
to call `pub` members like `withdraw` unless those methods were updated to be `auth`.

## Prior Art

* This section needs filling out; would love to know if there are any languages out there doing something similar to this.

## Questions and Discussion Topics

* The upcoming proposed changes to permit interfaces to conform to other interfaces will necessarily change the subtyping rules
for `auth` refrences, as it would no longer be sufficient to compare the sets for the two `auth` references just based on membership.
dsainati1 marked this conversation as resolved.
Show resolved Hide resolved
The membership check would still exist, but it would function as a width subtyping rule, in addition to a depth rule based on
interface chains. I.e. it would be the case that for two auth references, `auth{U} &X <: auth{T1} &X`, if `U <: T`.

We can combine this depth rule with the already existing width rule to yield a combined subtyping rule: `auth{U1, U2, ... } &X <: auth{T1, T2, ... } &X`,
whenever `∀T ∈ {T1, T2, ... }, ∃U ∈ {U1, U2, ... }, U <: T`.

So, for example, given an interface heirarchy:

```cadence
pub resource interface A {}
pub resource interface B {}
pub resource interface C: A {}
pub resource interface D: B {}
pub resource interface E: C, D {}
pub resource R: E {}
```

we would have `auth{E} &R <: auth{C} &R <: auth{A} &R <: &R` and `auth{E} &R <: auth{D} &R <: auth{B} &R <: &R`.
It would also be the case that `auth{D, C} &R <: auth{A} &R` as well, because `C <: A`, and that `auth{E} &R <: auth{B, C} &R`,
beacuse `E <: B` and `E <: C`.
dsainati1 marked this conversation as resolved.
Show resolved Hide resolved

* It is unclear how this should interact with the new attachments feature. Prior to this proposal a functional mental model
for attachments was to treat them as implicit type-disambiguated `pub` fields on the resource or struct to which they were attached.
The existing behavior preventing downcasting of non-`auth` references would prevent someone from accessing a `Vault`-attachment (which
would have access to functions like `withdraw` via the `base` reference) if they only possessed a `&{Balance}`, since only `Balance`-attachments
would be visible with such a reference.

One simple approach would be to allow attachments to be accessed only on values that have `auth` access to the attachment's `base` type.
I.e. given an `attachment A for I`, `v[A]` would be permissable when `v` has type `auth{I} &R` but not when `v` is just a regular `&R`,
dsainati1 marked this conversation as resolved.
Show resolved Hide resolved
or even an `&{I}`. Functionally this would mean that attachments would become `auth` fields in the mental model described above.

This approach is simple but very restrictive. In the motivating `KittyHat` use case for example, only users with an `auth`-reference
to the `Kitty` resource would be able to use the `Hat`. This would either make the `KittyHat` almost unusable without giving out
an unreasonable amount of authority to users, or require the author of the `Kitty` to write a specific interface describing the set of
features they would want an attachment to be able to use. This latter case would completely defeat the point of attachments in the first place,
since they are designed to be usable with no prior planning from the base value's author.

An alternative would be to implicitly parameterize the attachment's `auth` access over the `auth` access of its base value. In this model,
attachments would remain `pub` accessible, but would themselves permit the declaration of `auth` members. The `base` value,
which currently is simply a `&R` reference for an attachment `attachment A for R` in any of `A`'s member functions, would now have it's `auth`-ness
dsainati1 marked this conversation as resolved.
Show resolved Hide resolved
depend on the `auth`-ness of the member function in question. In a member declaration `access(auth) fun foo()` in `A`, the `base` variable would have
type `auth &R`, while in a member declaration in `A` `pub fun bar()`, `base` would just be an `&R`. This would effectively mean that the `auth`-access
members of the `base` would only be available to the attachment author in `auth`-access members on the attachment. Similarly, in an `auth`-access
member, the `self` reference of the attachment `A` would be `auth &A`, while in a `pub`-access member it would just be `&A`.

This would then be combined with a change to the attachment access rules: rather than `v[A]` always returning an `&A?` value, the type of the returned
attachment reference would depend on the type of `v`. If `v` is not a reference, or a reference with `auth` access to `A`'s `base` type, then `v[A]` would return
an `(auth &A)?` typed, while if the reference did not have access to `A`'s `base`, then the access would be a regular `&A?` type. This would prevent
the attachment from accessing `auth` members on its `base` unless the specific instance of that base to which it is attached has the proper `auth` access.

So, for example, given the following declaration:
```cadence
attachment CurrencyConverter for Provider {
pub fun convert(_ amount: UFix64): UFix64 {
// ...
}

pub fun convertVault(_ vault: @Vault): @Vault {
vault.balance = self.convert(vault.balance)
return <-vault
}

access(auth) fun withdraw(_ amount: UFix64): @Vault {
let convertedAmount = self.convert(amount)
// this is permitted because this function has `auth` access
// on the attachment, and thus `base` has type `auth{Provider} &{Provider}`
return <-base.withdraw(amount: amount)
}

pub fun deposit (from: @Vault) {
// cast is permissable under the new reference casting rules
let baseReceiverOptional = base as? &{Receiver}
if let baseReceiver = baseReceiverOptional {
let convertedVault <- self.convertVault(<-from)
// this is ok because `deposit` has `pub` access
baseReceiver.deposit(from: <-convertedVault)
}
}

pub fun maliciousStealingFunctionA(_ amount: UFix64): @Vault {
// This fails statically, as `self` here is just an `&A`
// because `maliciousStealingFunctionA`'s access is `pub`,
// and therefore `self` does not have access to `withdraw`
return <-self.withdraw(amount)
}

pub fun maliciousStealingFunctionB(_ amount: UFix64): @Vault {
// This fails statically, as `base` here is just an `&{Provider}`
// because `maliciousStealingFunctionB`'s access is `pub`,
// and therefore `base` does not have access to `withdraw`
return <-base.withdraw(amount: amount)
}
}

let vault <- attach CurrencyConverter() to <-create Vault(balance: /*...*/)
let authVaultReference = &vault as auth{Provider} &Vault
let converterRef = authVaultReference[CurrencyConverter]! // has type auth &CurrencyConverter, can call `withdraw`

let otherVaultReference = &vault as auth{Balance} &Vault
let otheConverterRef = otherVaultReference[CurrencyConverter]! // has type &CurrencyConverter, cannot call `withdraw`
```

* One potential change this unlocks would be to restrict the creation of Capabilities based on the `auth`-ness of the reference
they contain. We could restrict the `public` domain to be only for non-`auth` Capabilities, while the `private` domain would be only
for `auth`-references, which would prevent accidentally allowing anybody to get access to your `withdraw` function, for example.