|
| 1 | +--- |
| 2 | +simd: '0167' |
| 3 | +title: Loader-v4 |
| 4 | +authors: |
| 5 | + - Alexander Meißner |
| 6 | +category: Standard |
| 7 | +type: Core |
| 8 | +status: Draft |
| 9 | +created: 2024-08-15 |
| 10 | +feature: TBD |
| 11 | +--- |
| 12 | + |
| 13 | +## Summary |
| 14 | + |
| 15 | +A new upgradeable loader which only requires a single account per program. |
| 16 | + |
| 17 | +## Motivation |
| 18 | + |
| 19 | +Loader-v3, which is currently the only deployable loader, requires two accounts |
| 20 | +per program. This was a workaround to circumvent the finality of the |
| 21 | +`is_executable` flag, which is removed in SIMD-0162. Consequentially, this |
| 22 | +setup of the program account being a proxy account, containing the address of |
| 23 | +the actual programdata account, is no longer necessary and should be removed. |
| 24 | + |
| 25 | +Additionally, there currently is no complete specification of the loaders |
| 26 | +program management instructions. This proposal would thus fill that gap once |
| 27 | +loader-v4 goes into production. |
| 28 | + |
| 29 | +See impact for further motivation. |
| 30 | + |
| 31 | +## Alternatives Considered |
| 32 | + |
| 33 | +A delay-visibility-free redeployment could be achieved by keeping the swap |
| 34 | +program around until the end of the slot. This would however mean that two |
| 35 | +accounts per program must be loaded until the dapp developer reclaims the |
| 36 | +second one. That would defy the purpose of this proposal which is to get rid |
| 37 | +of the proxy account. |
| 38 | + |
| 39 | +## New Terminology |
| 40 | + |
| 41 | +None. |
| 42 | + |
| 43 | +## Detailed Design |
| 44 | + |
| 45 | +The loader-v4 program management and execution must be enabled with the |
| 46 | +associated feature gate, which simultaneously disables new deployments on |
| 47 | +loader-v3. |
| 48 | + |
| 49 | +Loader-v4 has the on-chain address |
| 50 | +`LoaderV411111111111111111111111111111111111` and comes with following program |
| 51 | +management instructions: |
| 52 | + |
| 53 | +- Write |
| 54 | + - Instruction accounts: |
| 55 | + - `[writable]` The program account to write to. |
| 56 | + - `[signer]` The authority of the program. |
| 57 | + - Instruction data: |
| 58 | + - Enum variant `0u32` |
| 59 | + - `u32` Offset at which to write the given bytes |
| 60 | + - `[u8]` Chunk of the programs executable file |
| 61 | + - Behavior: |
| 62 | + - Check there are at least two instruction accounts, |
| 63 | + otherwise throw `MissingAccount` |
| 64 | + - Verify the program account |
| 65 | + - Check the status stored in the program account is retracted, |
| 66 | + otherwise thorw `InvalidArgument` |
| 67 | + - Check that the end offset (sum of offset and length of the chunk) does |
| 68 | + not exceed the maximum (program account length minus the header size), |
| 69 | + otherwise throw `AccountDataTooSmall` |
| 70 | + - Copy the chunk into the program account at the offset shifted by the |
| 71 | + header size |
| 72 | +- Truncate |
| 73 | + - Instruction accounts: |
| 74 | + - `[(signer), writable]` The program account to change the size of. |
| 75 | + - `[signer]` The authority of the program. |
| 76 | + - `[writable]` Optional, the recipient account. |
| 77 | + - Instruction data: |
| 78 | + - Enum variant `1u32` |
| 79 | + - `u32` The new size after the operation. |
| 80 | + - Behavior: |
| 81 | + - Check there are at least two instruction accounts, |
| 82 | + otherwise throw `MissingAccount` |
| 83 | + - If this is an initialization (program account length is too short to |
| 84 | + contain the header and the requested new size is greater 0): |
| 85 | + - the owner of the program account is loader-v4, |
| 86 | + otherwise throw `InvalidAccountOwner` |
| 87 | + - the program account is writable, otherwise throw `InvalidArgument` |
| 88 | + - the program account (instruction account at index 0) signed, |
| 89 | + otherwise throw `MissingRequiredSignature` |
| 90 | + - the provided authority (instruction account at index 1) signed, |
| 91 | + otherwise throw `MissingRequiredSignature` |
| 92 | + - If this is not an initialization: |
| 93 | + - Verify the program account |
| 94 | + - Check that the status stored in the program account is retracted, |
| 95 | + otherwise throw `InvalidArgument` |
| 96 | + - Check that there are enough funds in the program account for rent |
| 97 | + exemption, otherwise thorw `InsufficientFunds` |
| 98 | + - If there are more than enough funds: |
| 99 | + - Check there are at least three instruction accounts, |
| 100 | + otherwise throw `MissingAccount` |
| 101 | + - Check that the recipient account (instruction account at index 2) is |
| 102 | + writable, otherwise throw `InvalidArgument` |
| 103 | + - Transfer the surplus from the program account to the recipient account |
| 104 | + - If the requested new size is zero: |
| 105 | + - Delete the entire program account, including the header |
| 106 | + - If the requested new size is greater than zero: |
| 107 | + - Set the length of the program account to the requested new size plus |
| 108 | + the header size |
| 109 | + - In case that this is an initialization, also initialize the header: |
| 110 | + - Set the slot to zero, **not** the current slot |
| 111 | + - Set the authority address (from the instruction account at index 1) |
| 112 | + - Set the status to retracted |
| 113 | +- Deploy |
| 114 | + - Instruction accounts: |
| 115 | + - `[writable]` The program account to deploy. |
| 116 | + - `[signer]` The authority of the program. |
| 117 | + - `[writable]` Optional, an undeployed source program account to take data and lamports from. |
| 118 | + - Instruction data: |
| 119 | + - Enum variant `2u32` |
| 120 | + - Behavior: |
| 121 | + - Check there are at least two instruction accounts, |
| 122 | + otherwise throw `MissingAccount` |
| 123 | + - Verify the program account |
| 124 | + - Check that the deployment cooldown expired, |
| 125 | + otherwise throw `InvalidArgument` |
| 126 | + - Check that the status stored in the program account is retracted |
| 127 | + otherwise throw `InvalidArgument` |
| 128 | + - In case a source program was provided (instruction account at index 2): |
| 129 | + - Verify the source program account |
| 130 | + - Check that the status stored in the source program account is retracted, |
| 131 | + otherwise throw `InvalidArgument` |
| 132 | + - Check that the executable file stored in the source program account |
| 133 | + passes executable verification |
| 134 | + - Copy the entire source program account into the program account |
| 135 | + - Set the length of the source program account to zero |
| 136 | + - Transfer all funds of the source program account to the program |
| 137 | + account |
| 138 | + - In case no source program was provided: |
| 139 | + - Check that the executable file stored in the program account passes |
| 140 | + executable verification |
| 141 | + - Change the slot in the program account to the current slot |
| 142 | + - Change the status stored in the program account to deployed |
| 143 | +- Retract |
| 144 | + - Instruction accounts: |
| 145 | + - `[writable]` The program account to retract. |
| 146 | + - `[signer]` The authority of the program. |
| 147 | + - Instruction data: |
| 148 | + - Enum variant `3u32` |
| 149 | + - Behavior: |
| 150 | + - Check there are at least two instruction accounts, |
| 151 | + otherwise throw `MissingAccount` |
| 152 | + - Verify the program account |
| 153 | + - Check that the deployment cooldown expired, |
| 154 | + otherwise throw `InvalidArgument` |
| 155 | + - Check that the status stored in the program account is deployed, |
| 156 | + otherwise throw `InvalidArgument` |
| 157 | + - Note: The slot is **not** set to the current slot to allow a |
| 158 | + retract-modify-redeploy-sequence within the same slot |
| 159 | + - Change the status stored in the program account to retracted |
| 160 | +- TransferAuthority |
| 161 | + - Instruction accounts: |
| 162 | + - `[writable]` The program account to change the authority of. |
| 163 | + - `[signer]` The current authority of the program. |
| 164 | + - `[signer]` Optional, the new authority of the program. |
| 165 | + - Instruction data: |
| 166 | + - Enum variant `4u32` |
| 167 | + - Behavior: |
| 168 | + - Check there are at least two instruction accounts, |
| 169 | + otherwise throw `MissingAccount` |
| 170 | + - Verify the program account |
| 171 | + - In case a new authority was provided (instruction account at index 2): |
| 172 | + - Check that it signed as well, |
| 173 | + otherwise throw `MissingRequiredSignature` |
| 174 | + - Check that the authority stored in the program account is different |
| 175 | + from the one provided, otherwise throw `InvalidArgument` |
| 176 | + - Copy the new authority address into the program account |
| 177 | + - In case no new authority was provided: |
| 178 | + - Check that the status stored in the program account is deployed, |
| 179 | + otherwise throw `InvalidArgument` |
| 180 | + - Change the status stored in the program account to finalized |
| 181 | + |
| 182 | +Accounts of programs owned by loader-v4 must have the following layout: |
| 183 | + |
| 184 | +- Header (which is 48 bytes long): |
| 185 | + - `u64` Slot in which the program was last deployed, retracted or |
| 186 | + initialized. |
| 187 | + - `[u8; 32]` Authority address which can send program management |
| 188 | + instructions. |
| 189 | + - `u64` status enum: |
| 190 | + - Enum variant `0u64`: Retracted, program is in maintenance |
| 191 | + - Enum variant `1u64`: Deployed, program is ready to be executed |
| 192 | + - Enum variant `2u64`: Finalized, same as `Deployed`, but can not be |
| 193 | + modified anymore |
| 194 | +- Body: |
| 195 | + - `[u8]` The programs executable file |
| 196 | + |
| 197 | +Verification the program account checks in the following order that: |
| 198 | + |
| 199 | +- the owner of the program account is loader-v4, |
| 200 | +otherwise throw `InvalidAccountOwner` |
| 201 | +- the program account is at least as long enough for the header, |
| 202 | +otherwise throw `AccountDataTooSmall` |
| 203 | +- the program account is writable, otherwise throw `InvalidArgument` |
| 204 | +- the provided authority (instruction account at index 1) signed, |
| 205 | +otherwise throw `MissingRequiredSignature` |
| 206 | +- the authority stored in the program account is the one provided, |
| 207 | +otherwise throw `IncorrectAuthority` |
| 208 | +- the status stored in the program account is not finalized, |
| 209 | +otherwise thorw `Immutable` |
| 210 | + |
| 211 | +Invoking programs owned by loader-v4 checks in the following order that: |
| 212 | + |
| 213 | +- the owner of the program account is loader-v4, |
| 214 | +otherwise throw `UnsupportedProgramId` |
| 215 | +- the program account is at least as long enough for the header, |
| 216 | +otherwise throw `AccountDataTooSmall` |
| 217 | +- the status stored in the program account is not retracted, |
| 218 | +otherwise thorw `UnsupportedProgramId` |
| 219 | +- the program account was not deployed recently (delay-visibility), |
| 220 | +otherwise thorw `UnsupportedProgramId` |
| 221 | +- the executable file stored in the program account passes executable |
| 222 | +verification, otherwise thorw `UnsupportedProgramId` |
| 223 | + |
| 224 | +Otherwise the execution semantics stay the same as in loader-v2 and v3. |
| 225 | +Delay-visibility also stays the same as in loader-v3: |
| 226 | + |
| 227 | +- Re/deployed programs act as closed until the end of the slot, |
| 228 | +only then becoming available for execution |
| 229 | +- The feature set that the executable file is verified against is not |
| 230 | +necessarily the current one, but the one of the epoch of the next slot |
| 231 | + |
| 232 | +## Impact |
| 233 | + |
| 234 | +This proposal: |
| 235 | + |
| 236 | +- covers all the use cases loader-v3 had but in a cleaner way and comes with |
| 237 | +a specification. |
| 238 | +- makes deployment slightly cheaper for dapp developers as they would no longer |
| 239 | +have to burn funds for the rent exception of the proxy account. |
| 240 | +- provides an alternative redeployment path which does not require a big |
| 241 | +deposit of funds for rent exception during the upload. |
| 242 | +- enables dapp developers to withdrawl the surplus of funds required for rent |
| 243 | +exception when shortening the length of program accounts or closing them. |
| 244 | +- shortens the workflow of temporarily closing a program to a single |
| 245 | +instruction, instead of having to build and redeploy an empty program. |
| 246 | +- allows transaction account loading to be simplifed once all loader-v3 |
| 247 | +programs are migrated. That is because every program would load exactly one |
| 248 | +account and these accounts would no longer need to be inspected for redirection |
| 249 | +links. |
| 250 | + |
| 251 | +## Security Considerations |
| 252 | + |
| 253 | +None. |
| 254 | + |
| 255 | +## Backwards Compatibility |
| 256 | + |
| 257 | +This proposal does not break any existing programs. However, dapp developers |
| 258 | +might want to profit from the new program mangement instructions without |
| 259 | +influencing their users work flows. To do so they would need a way to turn the |
| 260 | +program accounts of loader-v3 to program accounts of loader-v4, changing the |
| 261 | +account owner but keeping the program address. Such a loader migration would |
| 262 | +be possible because programdata accounts of both loaders are 48 bytes long. |
| 263 | +Either a new instruction in loader-v3 or an automatic mechanism in the program |
| 264 | +runtime (triggered by feature activation) could then perform the following |
| 265 | +steps per program: |
| 266 | + |
| 267 | +- loader-v3 clears the program proxy account (setting its size to zero) |
| 268 | +- loader-v3 transfers all funds from the programdata to the proxy account |
| 269 | +- loader-v3 gifts the program proxy account to loader-v4 |
| 270 | +- loader-v4 initializes it via `Truncate` |
| 271 | +- loader-v4 copies the data from the programdata account via `Write` |
| 272 | +- loader-v4 deploys it via `Deploy` |
| 273 | +- loader-v3 closes the programdata account (setting its size to zero) |
0 commit comments