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

Upgradeable Contracts #1578

Open
samliok opened this issue Sep 19, 2024 · 3 comments
Open

Upgradeable Contracts #1578

samliok opened this issue Sep 19, 2024 · 3 comments
Assignees

Comments

@samliok
Copy link
Contributor

samliok commented Sep 19, 2024

🧵Discussion regarding upgradable contracts

@samliok samliok self-assigned this Sep 19, 2024
@samliok
Copy link
Contributor Author

samliok commented Sep 19, 2024

Decoupling Code and State

Currently, a contract's state space is prefixed by its address.

flowchart TD
    State(Global State)
    ContractA("Contract A Address")
    ContractB("Contract B Address")
    ContractC("Contract C Address")
    StateA("Contract A State Space")
    StateB("Contract B State Space")
    StateC("Contract C State Space")

    State --> ContractA
    State --> ContractB
    State --> ContractC
    ContractA --> StateA
    ContractB --> StateB
    ContractC --> StateC

    style State stroke:#f9f,stroke-width:4px
    style ContractA stroke:#bbf,stroke-width:2px
    style ContractB stroke:#bbf,stroke-width:2px
    style ContractC stroke:#bbf,stroke-width:2px
    style StateA stroke:#bfb,stroke-width:2px
    style StateB stroke:#bfb,stroke-width:2px
    style StateC stroke:#bfb,stroke-width:2px
Loading

The state space of a contract should be decoupled with its code. This way, contracts would have much greater flexibility in interacting with state, and offer a very clean and explicit interface for managing state access and ownership. StateSpace would become a property of contracts, with their own read/write/own permissions. Initially every contract would be instantiated with it's own state space, but would be able to move access and control as it pleases.

flowchart TD
    State(State)
    Space1("State Space 0x1a2b")
    Space2("State Space 0x3c4d")
    Space3("State Space 0x5e6f")
    State --> Space1
    State --> Space2
    State --> Space3
    style State stroke:#f9f,stroke-width:4px
    style Space1 stroke:#bbf,stroke-width:2px
    style Space2 stroke:#bbf,stroke-width:2px
    style Space3 stroke:#bbf,stroke-width:2px
  
Loading
classDiagram
    %% Defining the classes
    class ContractA {
        StateSpace: 0x1a2b
    }
    class ContractB {
          StateSpace: 0x3c4d
    }
    class ContractC {
       StateSpace: 0x5e6f
    }
	class ContractD {
		StateSpace: nil
	}
	

	class StateSpace {
		owner: Address
		writers: map[Address]bool
		statePrefix: []byte
	}
Loading

This design gives contracts much more flexibility regarding state access as contracts can share state and move ownership. A StateSpace can have multiple writers, but only one owner. This allows multiple contracts to explicitly program how their StateSpace is accessed, offering new functionality to contracts. For example, contracts can award write access to other contracts after a voting process, or to the highest bidder of an auction. Another idea would be to "lend" write access to a StateSpace for any contract willing to pay a monthly fee.

A lightweight implementation of this would expose one additional host function

	extern fn set_state_access(access: AccessControl)
	
	enum AccessControl {
		// gives `contract` write access to this contracts StateSpace
		// calling contract must have ownership of its StateSpace
		Write(Contract)
		// moves ownership to `contract`, 
		// calling contract must have ownership, 
		Own(Contract)
		// removes all access `contract` has to calling contract
		Remove(Contract)
		// delegates access control to `contract`
		Delegate(Contract)
	}

Upgrading contracts are quite easy.

	Contract A
	pub fn upgrade(new: Address) {
		// validate caller & other logic...
		
		// change owner
		set_state_access(AccessControl::own(address))		
	}

	Contract B
	pub fn init(contractA: Address) {
		// Allow contractA to move ownership to ContractB
		set_state_access(AccessControl::delegate(contractA))
	}

In this example, ContractB would be initialized letting ContractA to set it's StateSpace. Contract A would then move ownership to ContractB once granted access. If ContractA would still like write access, it can give itself write permissions before moving ownership to ContractB

@richardpringle
Copy link
Contributor

@samliok, just to clarify, own is a superset of write perms? The owner can change ownership, otherwise, the writer can do everything else?

What about account balance? Is that something that both an owner and a writer can change or would that just be an owner. It might be helpful to add a small permissions table here

@samliok
Copy link
Contributor Author

samliok commented Sep 27, 2024

Yep. Own is a superset of Write. Good point about account balances, I can see justifications for having it either way. Maybe this could be an additional toggle when moving perms, such as separating write into WriteWithBalance and Write.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants