Super User Account

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 the Account.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.

1 Like

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: