|
| 1 | +--- |
| 2 | +fip: "<to be assigned>" <!--keep the qoutes around the fip number, i.e: `fip: "0001"`--> |
| 3 | +title: Add support for upgradable actors |
| 4 | +author: Fridrik Asmundsson (@fridrik01), Steven Allen (@stebalien) |
| 5 | +discussions-to: https://github.com/filecoin-project/FIPs/discussions/396 |
| 6 | +status: Draft |
| 7 | +type: Technical Core |
| 8 | +created: 2023-11-27 |
| 9 | +--- |
| 10 | + |
| 11 | +## Simple Summary |
| 12 | + |
| 13 | +This FIP introduces support for upgradable actors, enabling deployed actors to update their code while retaining their address, state, and balance. |
| 14 | + |
| 15 | +## Abstract |
| 16 | + |
| 17 | +This FIP proposes the integration of upgradable actors into the Filecoin network through the introduction of a new `upgrade_actor` syscall and an optional `upgrade` WebAssembly (WASM) entrypoint. |
| 18 | + |
| 19 | +Upgradable actors provide a framework for seamlessly replacing deployed actor code, significantly enhancing the user experience when updating deployed actor code. |
| 20 | + |
| 21 | +## Change Motivation |
| 22 | + |
| 23 | +Currently, all actors on the Filecoin Network are immutable once deployed. To modify the underlying actor code, such as fixing a security bug, the following steps are required: |
| 24 | +1. Deploy a new actor with the corrected code. |
| 25 | +2. Migrate all state from the previous actor to the new one. |
| 26 | +3. Update all other actors interacting with the old actor to use the new actor. |
| 27 | + |
| 28 | +By adding support for upgradable actors, deployed actors can easily upgrade their code and no longer need to go through the series of steps mentioned above. |
| 29 | + |
| 30 | +This also simplifies the upgrade process for builtin actors. Currently, any changes made to the builtin actors require a network upgrade, a new Lotus version, and collaboration with the network to push the new version to production. This is a time consuming and manual process. However, with upgradable actors, we can instead deploy a new code to the builtin actors allowing the changes to be pushed to production almost instantly and does not require a new network upgrade or new Lotus version. |
| 31 | + |
| 32 | +## Specification |
| 33 | + |
| 34 | +Introducing support for actor upgrades involves the following changes to the FVM: |
| 35 | + |
| 36 | +1. Adding a new `upgrade` WASM entrypoint, which actors must implement in order to be a valid upgrade target. |
| 37 | +2. Adding a new `upgrade_actor` syscall, enabling actors to upgrade themselves. |
| 38 | + |
| 39 | +These changes are discussed in detail in the following sections. |
| 40 | + |
| 41 | +### New upgrade WASM Entrypoint |
| 42 | + |
| 43 | +We introduce a new optional `upgrade` WASM entrypoint. Deployed actors must implement this entrypoint to be a valid upgrade target. It is defined as follows: |
| 44 | + |
| 45 | +```rust |
| 46 | +pub fn upgrade(params_id: u32, upgrade_info_id: u32) -> u32 |
| 47 | +``` |
| 48 | + |
| 49 | +Parameters: |
| 50 | +- `params_id`: An IPLD block handle provided by the caller and sent to the upgrade receiver, or `0` for none. |
| 51 | +- `upgrade_info_id`: An IPLD block handle for an `UpgradeInfo` struct provided by the FVM runtime (defined below). |
| 52 | + |
| 53 | +The single `u32` return value is an IPLD block handle, or `0` for none. |
| 54 | + |
| 55 | +The `UpgradeInfo` struct is defined as follows: |
| 56 | + |
| 57 | +```rust |
| 58 | +#[derive(Clone, Debug, Copy, PartialEq, Eq, Serialize_tuple, Deserialize_tuple)] |
| 59 | +pub struct UpgradeInfo { |
| 60 | + // the old code cid we are upgrading from |
| 61 | + pub old_code_cid: Cid, |
| 62 | +} |
| 63 | +``` |
| 64 | + |
| 65 | +When a target actor's `upgrade` WASM entrypoint is called, it can make necessary state tree changes from the calling if needed to its actor code. The `UpgradeInfo` struct provided by the FVM runtime can be used to check what code CID its upgrading from. A successful return from the `upgrade` entrypoint instructs the FVM that it should proceed with the upgrade. The target actor can reject the upgrade by calling `sdk::vm::exit()`` before returning from the upgrade entrypoint. |
| 66 | + |
| 67 | +### New upgrade_actor syscall |
| 68 | + |
| 69 | +We introduce a new `upgrade_actor` syscall which calls the `upgrade` wasm entrypoint of the calling actor and then atomically replaces the code CID of the calling actor with the provided code CID. It is defined as follows: |
| 70 | + |
| 71 | +```rust |
| 72 | +pub fn upgrade_actor( |
| 73 | + new_code_cid_off: *const u8, |
| 74 | + params: u32, |
| 75 | +) -> Result<Send>; |
| 76 | +``` |
| 77 | + |
| 78 | +Parameters: |
| 79 | +- `new_code_cid_off`: The code CID the calling actor should be replaced with. |
| 80 | +- `params`: The IPLD block handle passed, or `0` for none. |
| 81 | + |
| 82 | +On successful upgrade, this syscall will not return. Instead, the current invocation will "complete" and the return value will be the block returned by the new code's `upgrade` endpoint. If the new code rejects the upgrade (calls `sdk::vm::exit()`) or performs an illegal operation, this syscall will return the exit code plus the error returned by the upgrade endpoint. |
| 83 | + |
| 84 | +This syscall will: |
| 85 | +1. Validate that the pointers passed to the syscall are in-bounds. |
| 86 | +2. Validate that `new_code_cid_off` is a valid code CID. |
| 87 | +3. Validate that the calling actor is not currently executing in "read-only" mode. If so, the syscall fails with a "ReadOnly" (13) syscall error. |
| 88 | +4. Checks whether the calling actor is already on the call stack where it has previously been called on its `invoke` entrypoint (note that we allow calling `upgrade` recursively). If so, the syscall fails with a "Forbidden" (11) syscall error. |
| 89 | +5. Checks that we have space for storing the return block. If not, the syscall fails with a "LimitExceeded" (3) syscall error. |
| 90 | +6. Start a new Call Manager transaction: |
| 91 | + 1. Validate that the calling actor has not been deleted. If so, the syscall fails with a "IllegalOperation" (2) syscall error. |
| 92 | + 2. Update the actor in the state tree with the new `new_code_cid` keeping the same `state`, `sequence` and `balance`. |
| 93 | + 3. Invoke the target actor's `upgrade` entrypoint. |
| 94 | + 4. If the target actor does not implement the `upgrade` entrypoint, then return the syscall fails with a `ExitCode::SYS_INVALID_RECEIVER` exit code. |
| 95 | + 5. If the target actor aborts the `upgrade` entrypoint by calling `sdk::vm::exit()`, the syscall fails with the provided exit code. |
| 96 | +7. Apply transaction, committing changes. |
| 97 | +8. Abort the calling actor and return the IPLD block from the `upgrade` entrypoint. |
| 98 | + |
| 99 | +## Design Rationale |
| 100 | + |
| 101 | +### Additional metadata syscall |
| 102 | + |
| 103 | +We considered adding a new `get_old_code_cid` syscall to get the calling actors code CID. That has the benefit of keeping the `upgrade` entrypoint signature consistent with the `invoke` signature. We however rejected that as we felt the benefit didn't outweight the overhead of adding a new syscall. Furthermore, it did not provide the flexibility of passing in a IPLD handle for a `UpgradeInfo` struct where we can easily add more fields if required. |
| 104 | + |
| 105 | +## Backwards Compatibility |
| 106 | + |
| 107 | +Fully backwards compatibility is expected. |
| 108 | + |
| 109 | +## Test Cases |
| 110 | + |
| 111 | +Detailed test cases are provided with the implementation. |
| 112 | + |
| 113 | +## Security Considerations |
| 114 | + |
| 115 | +Upgradable actors pose potential security risks, as users can replace deployed actors' code. However, measures are in place to minimize these risks: |
| 116 | + |
| 117 | +- Upgradable actors are opt-in by default, ensuring no impact on currently deployed actors. |
| 118 | +- Actors can only upgrade themselves, preventing one actor from upgrading another actor to a new version. |
| 119 | +- We reject re-entrant `upgrade_actor` syscalls, i.e., if some actor `A` is already on the call stack, no "deeper" instance of `A` should be able to call the upgrade syscall. |
| 120 | + |
| 121 | +Detailed tests cover these security considerations and edge cases. |
| 122 | + |
| 123 | +## Incentive Considerations |
| 124 | + |
| 125 | +This FIP does not materially impact incentives in any way. |
| 126 | + |
| 127 | +## Product Considerations |
| 128 | + |
| 129 | +This FIP makes it possible to upgrade deployed actors, for example in cases where a bug or security concern was identified in the deployed code, allowing a simple safe way to addres such issues which significantly improves the user experience from how it is today. |
| 130 | + |
| 131 | +## Implementation |
| 132 | + |
| 133 | +https://github.com/filecoin-project/ref-fvm/pull/1866 |
| 134 | + |
| 135 | +## TODO |
| 136 | + |
| 137 | +N/A |
| 138 | + |
| 139 | +## Copyright |
| 140 | + |
| 141 | +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). |
0 commit comments