# 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);
```

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.