I was assuming ‘auth &Account’ would be old AuthAccount, ‘&Account’ would be old PublicAccount.
Yeah if someone actually possesses the concrete value (as opposed to a reference to it) they would have “owned access”, as you put it, allowing them to access anything and everything on the composite that isn’t private. It’s a little weird to think about this in the case of a struct
because I guess more than one person could have “owned access” to that struct at a time though.
How do we feel about the proposed idea in Super User Account - #15 by bastian?
In the example I’m using an interface for ContractAdmin
, but as discussed in https://github.com/onflow/flips/pull/54, this would now be an entitlement
declaration.
If we feel like this is a good start I could write up a full definition for Account
and open a FLIP.
I think it is great @bastian
While trying to continue with writing entitlements for the account type, as started in Super User Account - #15 by bastian, I realized that the problem mentioned above becomes increasingly prevalent when trying to encourage
- Composition, i.e. breaking functionality into smaller pieces, e.g. separate key from contract management
- Fine-grained permissions, e.g. having separate entitlements for contract addition and contract update
Basically, for each nested function with an entitlement, the outer container needs a separate member to expose it.
For example, to keep contract management separate, by having the functionality in a separate nested type instead of directly on the container type (Account
), and to have separate entitlements for contract additions and updates:
entitlement ContractDeployment {
fun add(
name: String,
code: [UInt8],
... contractInitializerArguments
): DeployedContract
}
entitlement ContractsDeployment {
let contractsDeployment: auth(ContractDeployment) &Account.Contracts
}
entitlement ContractUpdate {
fun update__experimental(
name: String,
code: [UInt8]
): DeployedContract
}
entitlement ContractsUpdate {
let contractsUpdate: auth(ContractUpdate) &Account.Contracts
}
// ... other entitlement interfaces granting access to keys, capabilities, etc.
struct Account {
let address: Address
// ... other fields, functions, and types
// expose "read-only" contract operations (e.g. Account.Contracts.get)
let contracts: &Account.Contracts
// expose contract deployment operation
access(ContractsDeployment) let contractsDeployment: auth(ContractDeployment) &Account.Contracts
// expose contract update operation
access(ContractsUpdate) let contractsUpdate: auth(ContractUpdate) &Account.Contracts
struct Contracts {
fun get(name: String): DeployedContract?
access(ContractDeployment) fun add(
name: String,
code: [UInt8],
... contractInitializerArguments
): DeployedContract
access(ContractUpdate) fun update__experimental(
name: String,
code: [UInt8]
): DeployedContract
// ... other fields and functions
}
}
This would allow for example a type like auth(ContractsDeployment) &Account
, which only authorizes access to the contract deployment operation, but not the contract update operation.
Alternatives:
- Flatten the types and functions, have all operations on
Account
. This seems counter to the composition principle, so I’d like to avoid that - Less fine grained entitlements
- Polymorphic access: It would be nice to express that having access to a type translates to access to a nested object (e.g. authorizations for the
Account
type translate to authorizations on theAccount.Contracts
type) - ?
My vote is on this, Maybe something like:
struct Account {
let address: Address
// ... other fields, functions, and types
let contracts: auth(self) &Account.Contracts
struct Contracts {
fun get(name: String): DeployedContract?
access(ContractDeployment) fun add(
name: String,
code: [UInt8],
... contractInitializerArguments
): DeployedContract
access(ContractUpdate) fun update__experimental(
name: String,
code: [UInt8]
): DeployedContract
// ... other fields and functions
}
}
so auth(self) can inherit the entitlements.
Now that the entitlements FLIP has been approved, I have opened a FLIP for refactoring the AuthAccount
and PublicAccount
types into a single Account
type: