Contract versioning primitive

Contract versioning

How can we trust the code of an on-chain contract?

  1. We can fetch the code off-chain and compare it to a trusted version.
  2. We can ask another contract on-chain whether we can trust it,
    but we’ll still need to fetch contract code off-chain to trust that
    contract or any code signatures it gives us.

Solution

Define a new type and instruction that allows validating the code version of a contract on-chain:

  • contract_code
    • Must be comparable
    • Not storable or pushable because the code representation can change
  • CONTRACT_CODE : address -> contract_code
    • It could be typed, since contracts with different types can’t be equal: CONTRACT_CODE : contract A -> contract_code A
  • If no method is provided that exposes the contents of the code, it could be called contract_code_id
    • This would ensure that contract_code values remain consistent across protocol upgrades

Possible Implementations

  1. contract_code could be fetched as the raw bytes stored for the contract, on-demand
  • This requires fetching the code for both compared contracts, which could be expensive
  1. contract_code could be a hash of the code, computed at origination
  • This allows for more efficient fetching and caching than (1), but lacks uniqueness guarantees
  1. contract_code could be represented by a unique-ID assigned to each contract at origination
  • The contract’s storage and code could be stored separately, where the code of all contracts
    is deduplicated with unique-ID’s assigned to each one (internally). This would allow
    many copies of a contract to share one copy of code and reduce the origination fees for sufficiently
    large/many contracts.
5 Likes

I am technically not qualified enough but this sounds similar/or it goes in a similar direction?

Sorry but I am not sure I understand the problem you are solving. Contract scripts are extremely hard to change – the only time it is possible to modify a script is during a protocol activation – so checking at runtime that the script at a given address has not changed seems overkill. The contract that you are querying to know who to trust could simply store a whitelist of trusted contract addresses or a whitelist of trusted script hashes (of type bytes).

Moreover, the script is only part of what needs to be trusted because the storage can hold important information too, typically the address of the contract administrator or even peices of the contract logic inside a lambda.

1 Like

@Blindripper hash-consing is a way to deduplicate AST’s.
It could be used to calculate unique ID’s for contract code, but it doesn’t imply this feature.

@rafoo Right, you’d only need to check at runtime when adding a contract to a list of trusted addresses:
otherwise you’d store “code ID’s” (e.g. trusted script hashes).

If we trust the code, we can trust entrypoints that allow us to audit the storage.

Example

Suppose we have a DAO contract where:

  • A token represents your voting power
  • There’s a voting cycle on proposals
  • Proposals can fund projects, represented as contracts

Question

We want to propose a project with its own sub-DAO:

  • The sub-DAO has its own voting token and cycle
  • Proposals on the top-level DAO can be used to administrate the project

How do we trust that the sub-DAO contract respects users’
voting power on the top-level DAO and synchronizes with its voting process?

Existing solution: factories

A factory contract consists of a contract template and address registry with two entrypoints:

  1. Originate a contract using the template and add its address to the registry
  2. Query whether an address is in the registry

The top-level DAO stores trusted factory addresses

  • Users vote to add/remove factory addresses
  • Origination proposals consist of factory contract parameters
    Cons:
  • Both the template and the implementation of each factory must be trusted
  • The maximum-size contract cannot directly fit in a factory
  • The code of contracts too large to fit in a factory must be added piecewise,
    e.g. by storing it as lambdas/bytes or by originating multiple contracts
  • The contract must be originated by the factory to be trusted as an instance of it

Applying Contract Versioning

The top-level DAO stores trusted contract code ID’s (e.g. code hashes) and addresses

  • Users vote to add/remove contract code ID’s
  • Proposals to add an address include its code ID
    • The code ID is validated on submission
    • Once the code is validated, we know how the entrypoints will behave
      • This allows some storage can be validated on-chain
      • Trusted entrypoints make it easier for voters to audit the contract
1 Like

The latter does not let you check that a contract actually implements the given script.