guidanoli
  • NEW!
    NEW!  Connect Ideas Across Notes
    Save time and share insights. With Paragraph Citation, you can quote others’ work with source info built in. If someone cites your note, you’ll see a card showing where it’s used—bringing notes closer together.
    Got it
      • Create new note
      • Create a note from template
        • 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
          • Only me
          • Signed-in users
          • Everyone
          Only me Signed-in users Everyone
        • Write
          • Only me
          • Signed-in users
          • Everyone
          Only me 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
      • Save as template
      • 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 Create Help
    Create Create new note Create a note from template
    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
    Only me
    • Only me
    • Signed-in users
    • Everyone
    Only me Signed-in users Everyone
    Write
    Only me
    • Only me
    • Signed-in users
    • Everyone
    Only me 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
    # Recoverable app funds In the case of a critical failure in the application, it should be possible for a permissioned party to foreclose the application and allow users to withdraw their funds as per the last finalized state. This feature would require the addition of a **guardian** role that would be capable of foreclosing the application. In practice, this role would be assigned to a multi-sig contract to ensure state-of-the-art security. It would not add any extra trust assumption on the security of the application. It could, however, impose a threat to liveness if the guardian is malicious. Users assume the guardian would only foreclose the application if a real threat is detected. Also, if the guardian does not act promptly, the application could be compromised. This role would not be as powerful as the application owner, because it would not be able to change the application consensus. Instead, the guardian can only foreclose the application, which reverts all inputs that come afterwards. Initially, to make things simple, let us assume that this guardian role is non-transferable and appoi construction (just like the owner) and cannot be transfered or revoked. Application devs that do not wish to appoint a guardian can pass `address(0)`. The guardian address should be retrievable through a new view function: ```solidity // Returns the address of the guardian, // which has the power to foreclose the application. function getGuardian() external view returns (address); ``` ```mermaid graph TD Owner[/"fa:fa-user Owner"\] Guardian[/"fa:fa-users Guardian"\] App["App"] Owner -->|change consensus| App Guardian -->|foreclose| App style App fill:#008DA5,stroke:#00F6FF,stroke-width:3px,color:#fff style Owner fill:#00F6FF,stroke:#008DA5,stroke-width:2px,color:#000 style Guardian fill:#00F6FF,stroke:#008DA5,stroke-width:2px,color:#000 ``` The `foreclose` entrypoint should be pretty simple to call because the guardian can be a multi-sig, and having complicated arguments like Merkle proofs can slow down the process of foreclosing an application (which can be quite urgent if an attack is detected and the multi-sig members have little time to compute a proof and come to a consensus). ```solidity // Forecloses the application, allowing users // to withdraw their funds by providing Merkle // proofs of their in-app accounts. // [!] Can only be called by the guardian. function foreclose() external; ``` An application is therefore in one of two possible states: alive or foreclosed. I'd like to avoid the term "dead" because (1) it is morbid and (2) and because the application still operates in some sense because outputs can still be validated/executed and user funds can still be withdrawn. Once foreclosed, it can never go back to the "alive" state again. Below is the state machine that illustrates this behavior. ```mermaid stateDiagram-v2 [*] --> Active Active --> Foreclosed : foreclose style Active fill:#008DA5,stroke:#00F6FF,stroke-width:3px,color:#fff style Foreclosed fill:#008DA5,stroke:#00F6FF,stroke-width:3px,color:#fff ``` This status will be accessible through a new view function, which initially returns `false` and, once foreclosed, returns `true`. Once foreclosed, an application will behave differently to protect user funds: ```solidity // Check whether the application has been foreclosed. // An application that has been foreclosed will remain so. function isForeclosed() external view returns (bool); ``` - Inputs (which includes deposits) cannot be sent to the app anymore. This will be possible by adding a check on the `InputBox` contract and is assumed that any custom `InputBox` contract follows this protocol: On the `addInput` function, if `app.isForeclosed()` returns `true`, then a custom error `ApplicationForeclosed()` is raised. - Epochs regarding the app cannot be settled anymore. This will implemented similarly on the `Authority`, `Quorum`, and `DaveConsensus` contracts and is assumed that any consensus contract follows this protocol: On the `submitClaim` function (in the case of `Authority` and `Quorum`) or on the `settle` function (in the case of `DaveConsensus`), if `app.isForeclosed()` returns `true`, then a custom error `ApplicationForeclosed()` is raised. There should also be a `Foreclosure` event for off-chain components listening to events instead of polling functions. ```solidity // The application has been foreclosed. event Foreclosure(); ``` > [!Note] > An important observation to make is that requiring an application to be active in order to add inputs indirectly fixes the problem of applications having inputs prior to their deployment, which is considered an anti-feature of the v2 contracts. > This would allow the node to sync inputs starting from the application contract deployment block, and not from the input box deployment block. Besides preventing users from losing their funds, it is important to allow them to withdraw their funds. Now, there are two types of funds locked in the application contract: Funds that were deposited through inputs that have been processed and finalized, and those deposited through inputs that either haven't been processed or finalized. Let's call these two types of funds finalized and unfinalized, respectively. Before diving into how users will be able to recover these types of funds, let's expand on the explanation a bit. Finalized funds go beyond deposits. Direct inputs are also important. Once an application processes an input, these funds could go from one user to another. Think of a simple wallet application: After depositing 100 CTSI, one can transfer 50 CTSI to someone else. It is crucial to use the latest finalized state to avoid the double-spending problem. Meanwhile, non-finalized funds are simply deposits that haven't been processed yet. We ignore non-deposit inputs because the application back-end or the consensus mechanism might have bugs. We must therefore allow users to revert any unprocessed deposit. Now that we have drawn the distinction between finalized and non-finalized funds, let us go into how to recover them. We start with finalized funds, since they usually represent most of an application's TVL. In order to recover finalized funds, the application contract needs a way to validate that a given user is owed a certain amount of a given asset. This information needs to come from the machine. Several designs have been proposed, but one stood out for its resistence to bugs in the consensus mechanism: The machine maintains an **accounts drive** located in predefined memory region. Once the application is foreclosed, users can withdraw their funds by providing a Merkle proof of their account(s) to the application contract. The **accounts drive** contains user accounts. Each account occupies $2^a$ data blocks (or $2^{a+5}$ bytes), and there can be up to $2^b$ accounts. The whole drive therefore occupies $2^{a+b+5}$ bytes. The drive starts at memory address $c\cdot 2^{a+b+5}$. The parameters $a$, $b$, and $c$ must be accessible through the following view functions in the application contract. ```solidity // Parameter 'a': the log (base 2) of the number of leaves // in the machine state tree that are reserved for // each account in the accounts drive. function getLog2LeavesPerAccount() external view returns (uint8); // Parameter 'b': the log (base 2) of the maximum number // of accounts that can be stored in the accounts drive. function getLog2MaxNumOfAccounts() external view returns (uint8); // Parameter 'c': the factor that, when multiplied with the // accounts drive size 2 ^ {a + b + 5}, yields the start memory // address of the accounts drive. function getAccountsDriveStartIndex() external view returns (uint64); ``` ![Accounts Drive](https://hackmd.io/_uploads/rJQ6y3Jd-e.png) The application developer must provide the value of parameters $a$, $b$, and $c$ when deploying the application contract. The encoding of accounts is **application-specific**. Therefore, there needs to be a smart contract that knows how to decode accounts and issue withdrawal outputs. The application developers must also provide the address of such contract when deploying the application contract. When a withdrawal is requested, the application contract will [`STATICCALL`](https://www.evm.codes/?fork=osaka#fa) to this custom contract, so that it can decode the account and construct the withdrawal output to be executed by the application contract. The interface of this contract is the following. ```solidity interface IWithdrawalOutputBuilder { // Build an output that, when executed by the application // contract, transfers the funds of an account to its owner. // The encoding of the account is application-specific. // This function will be called via the `STATICCALL` opcode, // so any state changes such as contract creations, // log emissions, storage writes, self-destructions and // Ether transfers will be reverted and abort the execution // of the withdrawal output. These state-changing constraints // are already checked by the Solidity compiler when implementing // this function as either view or pure. function buildWithdrawalOutput(bytes calldata account) external view returns (bytes memory output); } ``` In order to allow users to withdraw their funds after an application is foreclosed, we need to add the following definitions to the application contract interface. ```solidity // Proof of an account in the accounts drive. // The array of siblings is bottom-up, // from the account root up to the machine state root. struct AccountValidityProof { uint64 accountIndex; bytes32[] accountRootSiblings; } // The funds of an account were withdrawn. event Withdrawal( uint64 accountIndex, bytes account, bytes output ); // Raised whenever trying to withdraw the // funds of an account before the app is foreclosed. error NotForeclosed(); // Raised whenever an account root siblings array // with the wrong length is provided. error InvalidAccountRootSiblingsArrayLength(); // Raised whenever an account validity proof // produces a machine Merkle root different from // the last finalized machine Merkle root. error InvalidMachineMerkleRoot(bytes32 machineMerkleRoot); // Get the number of withdrawals. // Useful for fast-syncing Withdrawal events. function getNumberOfWithdrawals() external view returns (uint256); // Check whether an account had its funds withdrawn // given its index in the accounts drive. function wereAccountFundsWithdrawn(uint256 accountIndex) external view returns (bool); // Validate the existence of an account in the // accounts drive given a proof. function validateAccount( bytes calldata account, AccountValidityProof calldata proof ) external view; // Withdraw the funds of an account, given a proof // of its existence in the accounts drive. function withdraw( bytes calldata account, AccountValidityProof calldata proof ) external; // Get withdraw output builder that gets static-called // whenever the funds of an account are to be withdrawn. function getWithdrawOutputBuilder() external view returns (IWithdrawOutputBuilder); ``` The `withdraw` function will validate the proof by computing the machine Merkle root from the account Merkle root, index, and siblings. It will then check against the consensus contract whether this was the last finalized machine Merkle root through a new function: ```solidity // Get the last finalized machine Merkle root. function getLastFinalizedMachineMerkleRoot() external view returns (bytes32); ``` This function will be added to the `IOutputsMerkleRootValidator` interface, and implemented by Authority, Quorum, and PRT. The implementation for PRT should be pretty straightforward, given that the `arbitrationResult` already returns the post-epoch machine Merkle root. Meanwhile, Authority and Quorum don't work with machine Merkle roots in the SDK v2 contracts. Instead, they only work with outputs Merkle roots. One way to circumvent this limitation is to make the `submitClaim` function receive the Merkle proof of the outputs Merkle root in the machine (like the PRT `settle` function). This will be a **breaking change** on the **`IConsensus`** interface which the node must adapt itself. Since the PRT implementation already expects such a proof, we hope this enables some code reuse on the node side. ```solidity // @notice Submit a claim to the consensus. // @param appContract The application contract address // @param lastProcessedBlockNumber The number of the last processed block // @param outputsMerkleRoot The outputs Merkle root // @param proof The bottom-up Merkle proof of the outputs Merkle root // in the machine Merkle root // @dev MUST fire a `ClaimSubmitted` event. // @dev MAY fire a `ClaimAccepted` event, if the acceptance criteria is met. function submitClaim( address appContract, uint256 lastProcessedBlockNumber, bytes32 outputsMerkleRoot, bytes32[] calldata proof // <---- NEW! ) external; ``` Internally, the consensus contract will update the latest finalized machine Merkle root whenever a more recent epoch is finalized. By default, this function will return `bytes32(0)`, which avoids any accounts from being proven. On the **`IApplicationFactory`** interface, some **breaking changes** will be necessary as well to accomodate the addition of new constructor parameters on the `Application` contract. All parameters related to withdrawals will be put in a structure for simpler code structure and argument forwarding: ```solidity // @notice Withdrawal configuration parameters. // @param guardian The address of the account with guardian priviledges // @param log2LeavesPerAccount The base-2 log of leaves per account // @param log2MaxNumOfAccounts The base-2 log of max. num. of accounts // @param accountsDriveStartIndex The offset of the accounts drive // @param withdrawOutputBuilder The address of the withdraw output builder struct WithdrawalConfig { address guardian; uint8 log2LeavesPerAccount; uint8 log2MaxNumOfAccounts; uint64 accountsDriveStartIndex; IWithdrawOutputBuilder withdrawOutputBuilder; } ``` This struct will be received as parameter on every app deployment entrypoint. If the application developer wants to disable withdrawals, they can set `withdrawOutputBuilder` to `address(0)`. They can also avoid the application from even being foreclosed by setting `guardian` to `address(0)`. These address parameters are not validated (all values are valid), while the account drive parameters do have one simple restriction: > The accounts drive must be fully contained within the machine memory range In terms of the parameters $a$, $b$, and $c$, this is equivalent to: $$ (c + 1) \cdot 2^{a+b+5} \le 2^{64} $$ The easiest way to guarantee this is to make $a=b=c=0$. So, indeed, for applications that do not support withdrawals, it is OK to set all fields in this struct to 0. The accounts drive parameters will be meaningless, but valid, and the application cannot ever be foreclosed and withdrawals are effectively off.

    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 Google 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