Aztec
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Write
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.

      Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Explore these features while you wait
      Complete general settings
      Bookmark and like published notes
      Write a few more notes
      Complete general settings
      Write a few more notes
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Help
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Write
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.

    Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Explore these features while you wait
    Complete general settings
    Bookmark and like published notes
    Write a few more notes
    Complete general settings
    Write a few more notes
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # Contract upgradeability ## Purpose A contract should be able to change its own implementation to fix bugs, add functionality, etc. The change of the implementation shouldn't change either the address or the storage of the contract, allowing to swap just the code without breaking state or composability. ## Proposal We'll describe here a proposal using shared mutable to be able to update contract class ids, which encode vks and bytecode. ### Triggering updates The contract instance deployer can store the updated contract class ids in shared mutables: ```rust #[storage] struct Storage<Context> { updated_class_ids: Map<AztecAddress, SharedMutable<ContractClassId, UPDATE_DELAY, Context>, Context>, } ``` The updates can be triggered by calling the following function the deployer, which will allow msg_sender to update its own class id: ```rust #[public] fn update(new_contract_class_id: ContractClassId) { let address = context.msg_sender(); // Is it possible for this to fail? assert( context.nullifier_exists(address.to_field(), context.this_address()), "msg.sender is not deployed", ); assert( context.nullifier_exists(new_contract_class_id.to_field(), REGISTERER_CONTRACT_ADDRESS), "New contract class is not registered", ); storage.updated_class_ids.at(address).schedule_value_change(new_contract_class_id); let event = ContractInstanceUpdated { DEPLOYER_CONTRACT_INSTANCE_UPDATED_MAGIC_VALUE, address, new_contract_class_id, }; let payload = event.serialize(); context.emit_unencrypted_log(payload); } ``` The update will come into effect after UPDATE_DELAY blocks. ### How to read this shared mutable from circuits Both the AVM and the private kernels will need to read the shared mutable state to find out what the current value of the updated_contract_class_id is. Shared mutable writes a hash (``) in public storage of ScheduledValueChange and ScheduledDelayChange. We can read that hash from the public data tree and provide the preimage of the hash to reconstruct ScheduledValueChange, which holds what the current value should be: ```rust pub(crate) struct ScheduledValueChange<T> { pre: T, post: T, // Block at which `post` value is used instead of `pre` block_of_change: u32, } ``` ### Changes to Bytecode validation In public, we can query the current value of the SharedMutable hash directly from the public data tree, since we are executing at the tip of the chain. - If the leaf doesn't exist, then bytecode validation works as it did before updates - If the leaf exists, we then verify the ScheduledValueChange hint, and given the current block and change.block_of_change we decide what the current value (`pre` or `post`) is for updated_class_id. - If updated_class_id is zero, then bytecode validation works as it did before updates - If updated_class_id is nonzero, we perform membership of the bytecode against that updated class id instead of the original class id. In any case, we need to still verify membership the initialization nullifier for the contract address, that is unchanged. ### Changes to VK checks In private, we can't query the current value of the SharedMutable hash. Instead we perform a historical read of the appropiate slot for the SharedMutable hash. Set the appropiate value for max_block_number for the TX. All txs that interact with an updatable contract need to set a max_block_number. Initially, all contracts are updatable except protocol contracts. The appropiate value for max_block_number follows the same logic as on noir's shared mutable implementation. It could be simplified down a bit if we don't allow delay changes in updates. After setting the max_block_number, using the historical value for updated_class_id works the same as in public. If we have an updated_class_id for the called address, we verify the VK against that one. If we don't have it we verify the VK normally against the address. ### Changes to the archiver The archiver now needs to listen to unencrypted logs about updates performed to contracts. The events need to be stored in the store as objects since reorgs need to delete those update objects, and blocks mined need to add them. When necessary, the current class id of the contract can be computed by the archiver given the current block number and the updates that have been applied to the contract. ### Changes to the simulator Before executing any contract, PXE's simulator must verify that the bytecode being executed matches the latest implementation. Latest here means the current class ID given the block the PXE is synced to. This is important because the current block might contain new note types that only the latest implementation can understand. ### Backwards compatibility Any contract update must be compatible with previous versions in storage layout and note shape. This is because the newer implementation might be requested to process notes or to access storage structs from past implementations. ### Changes to Aztec.js interface The PXE stores the contract instances and classes in a local database. When a contract is updated, in order to interact with it we need to pass the new artifact to the PXE, since the protocol doesn't publish artifacts. My initial proposal consists on not changing anything in the Aztec.js interface, and instead leverage the current flows that we have. This would be the example flow to update a contract. ```rust #[aztec] contract Updatable { ... #[private] fn update_to(new_class_id: ContractClassId) { ContractInstanceDeployer::at(DEPLOYER_CONTRACT_ADDRESS).update(new_class_id).enqueue( &mut context, ); } ... ``` ```typescript const contract = await UpdatableContract.deploy(wallet, ...args) .send() .deployed(); const updatedContractClassId = (await getContractClassFromArtifact(UpdatedContractArtifact)).id; await contract.methods.update_to(updatedContractClassId).send().wait(); ``` Now, when the update has happened, calling `at` with the new contract artifact will automatically update the contract instance in the PXE if it's outdated: ```typescript // 'at' will call PXE updateContract if outdated const updatedContract = await UpdatedContract.at(address, wallet); ``` I think this flow would be ideal for dapps since a new version of the dapp would be published with the new artifact. Whenever a user interacts with the new version of the dapp it'll call `at` and the PXE would automatically get updated. If you try to call `at` with a different contract that is not the current version, it'll fail ```typescript // throws when trying to update to RandomContract // since the current one is UpdatedContract await RandomContract.at(address, wallet); ``` However we'll probably need feedback from real apps to see if they need some specific methods in aztec.js for contract updates. ## Possible improvements ### Contract immutability We can make some contracts be immutable by injecting a boolean in the address preimage. We currently compute it like this: ```rust pub fn compute(public_keys: PublicKeys, partial_address: PartialAddress) -> AztecAddress { let public_keys_hash = public_keys.hash(); let pre_address = poseidon2_hash_with_separator( [public_keys_hash.to_field(), partial_address.to_field()], GENERATOR_INDEX__CONTRACT_ADDRESS_V1, ); ``` We could add this boolean to the pre_address. The contract instance deployer can check that boolean, requiring some hints (the public keys and partial address) to be passed to verify that the derivation matches the original address. The private kernels, when calling an immutable contract can skip setting the max_block_number property to the tx if that boolean is false. ### Upgrades as an opcode The triggering of contract upgrades can be promoted to an AVM opcode, instead of being a public method of the contract instance deployer. I see some problems with this approach: - It would be a complex gadget, that needs to emulate the behavior of SharedMutable - We'd only see gas improvements when upgrading contracts, which is not that common of an operation to require an opcode IMO - Contract updated logs would be emitted by the contract that is being updated, instead of the deployer protocol contract. This might be painful? --- # Comments *Because hackmd comments are horrible* ## Palla - About this comment: ``` // Is it possible for this to fail? assert( context.nullifier_exists(address.to_field(), context.this_address()), "msg.sender is not deployed", ); ``` Yeah, I believe that could happen if an undeployed contract enqueues a call to upgrade from private-land. - Given how easy it is to upgrade a contract just by issuing a call, we should add in aztec-nr a check that `call` does not go to any of the precompiles, and add a separate `system_call` method for those. - [Alvaro] True, we probably can do that purely in aztec noir though, since the precompiles have identifiable contract addresses - AVM has a `GETCONTRACTINSTANCE` opcode that can be used to retrieve the class id. We need to review its usages (if any) and make sure we're querying the deployer in those cases. - How are we going to decide on the value for `UPDATE_DELAY`? Should we allow each contract to set its own delay somehow? - [Mike] This seems reasonable to me. We already have the chaos of contracts being able to specify their own delays for their own sharedMutable state variables, so we might as well embrace it? Hmm... perhaps we want to enforce a minimum delay, though? Otherwise some very popular, and highly-composable contract could ruin the ecosystem, if it sets a delay of 1 block: then every tx in the network would be forced to adhere to 1 block ttl. - [Mike] I'd be happy with it being quite a long minimum delay, with the messaging: "If you want to use contract upgrades to batch bugs very quickly, you cannot. Use a pause mechanism instead". Hmm... but then if a pause mechanism with a short delay is implemented by a popular contract, we're in the same boat of forcing every tx to have a short delay. Hmmm. We've already made our bed here. Although, perhaps aztec.nr should enforce a minimum delay for contracts' sharedMutable state variables too. Dear reader, you might be able to tell that I'm re-remembering all of the details of sharedMutable from last year, as I type this, and am adding very little value. - [Alvaro] Shared mutables have configurable delays. We can expose an API in the deployer contract to change the delay, and assert it's within reasonable bounds. ## Nico - >Given how easy it is to upgrade a contract just by issuing a call, we should add in aztec-nr a check that `call` does not go to any of the precompiles, and add a separate `system_call` method for those. Agreed, make it an opcode or w/e but not a regular call. - >How are we going to decide on the value for UPDATE_DELAY? Should we allow each contract to set its own delay somehow? SharedMutable already supports changing the delay, which is non-trivial but not terribly complicated. I think it'd be nice to have this. Yes, you can artificially add a delay by timelocking the call that performs the upgrade, but that's not very visible - I'd rather have that at the base layer for something as critical as this. We can have a default value (e.g. 5 days?) and let people change it. We can also add a minimum to prevent timing issues, and a maximum to prevent people accidentally nuking the mechanism for themselves.

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password
    or
    Sign in via Facebook Sign in via X(Twitter) Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    By signing in, you agree to our terms of service.

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully