Skip to content

Commit 39d9a44

Browse files
committed
Suggestions from jstarry, buffalojoec and t-nelson
1 parent c10fb27 commit 39d9a44

File tree

1 file changed

+132
-90
lines changed

1 file changed

+132
-90
lines changed

proposals/0167-loader-v4.md

Lines changed: 132 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,7 @@ This was a workaround to circumvent the finality of the `is_executable` flag,
2222
which will be ignored by the program runtime from SIMD-0162 onwards.
2323
Consequentially, this setup of the program account being a proxy account,
2424
containing the address of the actual program data account, is no longer
25-
necessary and should be removed. Likewise the distinction of executable program
26-
accounts and non-executable buffer accounts is also no longer necessary and
27-
should be removed as well.
25+
necessary and should be removed.
2826

2927
In loader-v3 every instruction which modified the program data had to re-verify
3028
the ELF in the end. Instead we are now aiming for a more modular workflow which
@@ -36,6 +34,8 @@ transactions.
3634
Another issue with loader-v3 is that the executable file stored in the
3735
programdata account is misaligned relative to the beginning of the account.
3836
This currently requires a copy in the ELF loader to re-align the program.
37+
To avoid any other alignment issues all states of the accounts owned by a
38+
loader should have the same layout and only be differentiated by a status enum.
3939

4040
Additionally, there currently is no complete specification of the loaders
4141
program management instructions. This proposal would thus fill that gap once
@@ -71,7 +71,7 @@ a complete implementation, specification and documentation.
7171

7272
## New Terminology
7373

74-
None.
74+
The _current slot_ is as in the Clock sysvar.
7575

7676
## Detailed Design
7777

@@ -87,16 +87,17 @@ instruction `UpgradeableLoaderInstruction::Migrate` (see SIMD-0315).
8787
Accounts of programs owned by loader-v4 must have the following layout:
8888

8989
- Header (which is 48 bytes long):
90+
- `u64` status enum:
91+
- Enum variant `0u64`: `Invalid`, account was zero-filled externally
92+
- Enum variant `1u64`: `NeverBeenDeployed`, used as write buffer
93+
- Enum variant `2u64`: `Retracted`, program is in maintenance
94+
- Enum variant `3u64`: `Deployed`, program is ready to be executed
95+
- Enum variant `4u64`: `Finalized`, same as `Deployed`, but can not be
96+
modified anymore
9097
- `u64` Slot in which the program was last deployed, retracted or
9198
initialized.
9299
- `[u8; 32]` Authority address which can send program management
93-
instructions. Or if the status is finalized, then the address of the next
94-
version of the program.
95-
- `u64` status enum:
96-
- Enum variant `0u64`: Retracted, program is in maintenance
97-
- Enum variant `1u64`: Deployed, program is ready to be executed
98-
- Enum variant `2u64`: Finalized, same as `Deployed`, but can not be
99-
modified anymore
100+
instructions.
100101
- Body:
101102
- `[u8]` The programs executable file
102103

@@ -111,7 +112,9 @@ otherwise throw `AccountDataTooSmall`
111112
otherwise throw `MissingRequiredSignature`
112113
- the authority stored in the program account is the one provided,
113114
otherwise throw `IncorrectAuthority`
114-
- the status stored in the program account is not finalized,
115+
- the status stored in the program account is not `Invalid`,
116+
otherwise throw `InvalidArgument`
117+
- the status stored in the program account is not `Finalized`,
115118
otherwise throw `Immutable`
116119

117120
### Execution / Invocation
@@ -120,7 +123,7 @@ Invoking programs owned by loader-v4 checks in the following order that:
120123

121124
- the owner of the program account is loader-v4
122125
- the program account is at least as long enough for the header
123-
- the status stored in the program account is not retracted
126+
- the status stored in the program account is `Deployed` or `Finalized`
124127
- the program account was not deployed within the current slot (delay
125128
visibility)
126129
- the executable file stored in the program account passes executable
@@ -130,8 +133,6 @@ failing any of the above checks must throw `UnsupportedProgramId`.
130133

131134
### Program Management Instructions
132135

133-
All program management instructions must cost 2000 CUs.
134-
135136
#### Write
136137

137138
- Instruction accounts:
@@ -142,10 +143,12 @@ All program management instructions must cost 2000 CUs.
142143
- `u32` Byte offset at which to write the given bytes
143144
- `[u8]` Chunk of the programs executable file
144145
- Behavior:
146+
- Charge 32 + chunk_length_in_bytes CUs
145147
- Check there are at least two instruction accounts,
146148
otherwise throw `NotEnoughAccountKeys`
147149
- Verify the program account
148-
- Check the status stored in the program account is retracted,
150+
- Check the status stored in the program account is `NeverBeenDeployed` or
151+
`Retracted`,
149152
otherwise throw `InvalidArgument`
150153
- Check that the end offset (sum of offset and length of the chunk) does
151154
not exceed the maximum (program account length minus the header size),
@@ -158,26 +161,28 @@ All program management instructions must cost 2000 CUs.
158161
- Instruction accounts:
159162
- `[writable]` The program account to copy to.
160163
- `[signer]` The authority of the program.
161-
- `[]` The program(data) account to copy from.
164+
- `[]` The account to copy from.
162165
- Instruction data:
163166
- Enum variant `1u32`
164167
- `u32` Byte offset at which to write
165168
- `u32` Byte offset at which to read
166169
- `u32` Length of the chunk to copy in bytes
167170
- Behavior:
171+
- Charge 32 + chunk_length_in_bytes CUs
168172
- Check there are at least three instruction accounts,
169173
otherwise throw `NotEnoughAccountKeys`
170174
- Check that program account and source account do not alias,
171175
otherwise throw `AccountBorrowFailed`
172176
- Verify the program account
173-
- Check the status stored in the program account is retracted,
177+
- Check the status stored in the program account is `NeverBeenDeployed` or
178+
`Retracted`,
174179
otherwise throw `InvalidArgument`
175-
- Check that the source account is owned by loader v1, v2, v3 or v4,
180+
- Check that the source account is owned by loader v2, v3 or v4,
176181
otherwise throw `InvalidArgument`
177182
- and look-up the source header size:
178-
- loader-v1: 0 bytes
179183
- loader-v2: 0 bytes
180-
- loader-v3: 45 bytes
184+
- loader-v3 buffer: 37 bytes
185+
- loader-v3 programdata: 45 bytes
181186
- loader-v4: 48 bytes
182187
- Check that the source end offset (sum of source offset and length) does
183188
not exceed the maximum (source account length minus the source header size),
@@ -193,11 +198,11 @@ All program management instructions must cost 2000 CUs.
193198
- Instruction accounts:
194199
- `[writable]` The program account to change the size of.
195200
- `[signer]` The authority of the program.
196-
- `[writable]` Optional, the recipient account.
197201
- Instruction data:
198202
- Enum variant `2u32`
199203
- `u32` The new size after the operation.
200204
- Behavior:
205+
- Charge 32 + new_size_in_bytes CUs
201206
- Check there are at least two instruction accounts,
202207
otherwise throw `NotEnoughAccountKeys`
203208
- If this is an initialization (program account length is too short to
@@ -209,145 +214,182 @@ All program management instructions must cost 2000 CUs.
209214
otherwise throw `MissingRequiredSignature`
210215
- If this is not an initialization:
211216
- Verify the program account
212-
- Check that the status stored in the program account is retracted,
217+
- Check that the status stored in the program account is
218+
`NeverBeenDeployed` or `Retracted`,
213219
otherwise throw `InvalidArgument`
214220
- Check that there are enough funds in the program account for rent
215-
exemption, otherwise throw `InsufficientFunds`
216-
- If there are more than enough funds:
217-
- Check there are at least three instruction accounts,
221+
exemption of the new length, otherwise throw `InsufficientFunds`
222+
- Set the length of the program account to the requested new size plus
223+
the header size
224+
- In case that this is an initialization, also initialize the header:
225+
- Set the slot to zero, **not** the current slot
226+
- Set the authority address (from the instruction account at index 1)
227+
- Set the status to `NeverBeenDeployed`
228+
229+
#### WithdrawLamports
230+
231+
- Instruction accounts:
232+
- `[writable]` The program account to withdraw from.
233+
- `[signer]` The authority of the program.
234+
- `[writable]` The recipient account.
235+
- Instruction data:
236+
- Enum variant `3u32`
237+
- `u64` The amount in lamports.
238+
- Behavior:
239+
- Charge 32 CUs
240+
- Check there are at least two instruction accounts,
218241
otherwise throw `NotEnoughAccountKeys`
219-
- Check that the recipient account (instruction account at index 2) is
220-
writable, otherwise throw `InvalidArgument`
221-
- If a recipient account was provided that is not the program account:
222-
- Transfer the surplus from the program account to the recipient account
223-
- otherwise, if the requested new size is zero throw `InvalidArgument`
224-
- If the requested new size is zero:
242+
- Check that program account and recipient account do not alias,
243+
otherwise throw `AccountBorrowFailed`
244+
- Verify the program account
245+
- Check that there would be enough funds in the program account remaining for
246+
rent exemption after the withdrawl, otherwise throw `InsufficientFunds`
247+
- If all lamports are being withdrawn:
248+
- Check that the status stored in the program account is
249+
`NeverBeenDeployed` or `Retracted`,
250+
otherwise throw `InvalidArgument`
225251
- Set the length of the program account to 0 (removing the header too)
226-
- If the requested new size is greater than zero:
227-
- Set the length of the program account to the requested new size plus
228-
the header size
229-
- In case that this is an initialization, also initialize the header:
230-
- Set the `is_executable` flag to `true`
231-
- Set the slot to zero, **not** the current slot
232-
- Set the authority address (from the instruction account at index 1)
233-
- Set the status to retracted
252+
- Transfer the amount from the program account to the recipient account
234253

235254
#### Deploy
236255

237256
- Instruction accounts:
238257
- `[writable]` The program account to deploy.
239258
- `[signer]` The authority of the program.
240259
- Instruction data:
241-
- Enum variant `3u32`
260+
- Enum variant `4u32`
242261
- Behavior:
262+
- Charge 32 CUs
243263
- Check there are at least two instruction accounts,
244264
otherwise throw `NotEnoughAccountKeys`
245265
- Verify the program account
246266
- Check that the slot stored in the program account is not the current
247267
(deployment cooldown), otherwise throw `InvalidArgument`
248268
- Note: The cooldown enforces that each pair of an address and a slot can
249269
uniquely identify a deployment of a program, which simplifies caching logic.
250-
- Check that the status stored in the program account is retracted
270+
- Check that the status stored in the program account is `NeverBeenDeployed`
271+
or `Retracted`
251272
otherwise throw `InvalidArgument`
273+
- Charge program_length_in_bytes CUs
252274
- Check that the executable file stored in the program account passes
253275
executable verification
254276
- Change the slot in the program account to the current slot
255-
- Change the status stored in the program account to deployed
277+
- Change the status stored in the program account to `Deployed`
278+
- Set the `is_executable` flag to `true`
256279

257280
#### Retract
258281

259282
- Instruction accounts:
260283
- `[writable]` The program account to retract.
261284
- `[signer]` The authority of the program.
262285
- Instruction data:
263-
- Enum variant `4u32`
286+
- Enum variant `5u32`
264287
- Behavior:
288+
- Charge 32 CUs
265289
- Check there are at least two instruction accounts,
266290
otherwise throw `NotEnoughAccountKeys`
267291
- Verify the program account
268292
- Check that the slot stored in the program account is not the current
269293
(deployment cooldown), otherwise throw `InvalidArgument`
270-
- Check that the status stored in the program account is deployed,
294+
- Check that the status stored in the program account is `Deployed`,
271295
otherwise throw `InvalidArgument`
272296
- Note: The slot is **not** set to the current slot to allow a
273297
retract-modify-redeploy-sequence within the same slot or even within the
274298
same transaction.
275-
- Change the status stored in the program account to retracted
299+
- Change the status stored in the program account to `Retracted`
300+
- Set the `is_executable` flag to `false`
276301

277-
#### TransferAuthority
302+
#### SetAuthority
278303

279304
- Instruction accounts:
280305
- `[writable]` The program account to change the authority of.
281306
- `[signer]` The current authority of the program.
282-
- `[signer]` The new authority of the program.
307+
- `[optional(signer)]` The new authority of the program.
283308
- Instruction data:
284-
- Enum variant `5u32`
309+
- Enum variant `6u32`
285310
- Behavior:
311+
- Charge 32 CUs
286312
- Check there are at least three instruction accounts,
287313
otherwise throw `NotEnoughAccountKeys`
288314
- Verify the program account
289315
- Check that the new authority (instruction account at index 2)
290-
signed as well, otherwise throw `MissingRequiredSignature`
316+
is either the system program or has signed,
317+
otherwise throw `MissingRequiredSignature`
291318
- Check that the authority stored in the program account is different
292319
from the one provided, otherwise throw `InvalidArgument`
293320
- Copy the new authority address into the program account
321+
- If the the new authority is the system program:
322+
- Check that the status stored in the program account is `Deployed`,
323+
otherwise throw `InvalidArgument`
324+
- Change the status stored in the program account to `Finalized`
325+
326+
### Workflows
327+
328+
#### Inital deployment
329+
330+
- Assign account to loader-v4
331+
- SetProgramLength to ELF size
332+
- [Transaction boundary]
333+
- Write chunks repeatedly
334+
- [Transaction boundary]
335+
- Deploy
336+
337+
#### Redeployment
338+
339+
- Assign buffer account to loader-v4
340+
- SetProgramLength of buffer to ELF size
341+
- [Transaction boundary]
342+
- Write chunks repeatedly
343+
- [Transaction boundary]
344+
- Retract program
345+
- SetProgramLength of program to ELF size
346+
- Copy from buffer to program
347+
- Deploy program
348+
- SetProgramLength of buffer to 0
349+
- WithdrawLamports of buffer to program
350+
351+
#### Close: Temporary
352+
353+
- Retract
354+
355+
#### Close: Recycle
356+
357+
- Retract
358+
- SetProgramLength to 0
359+
- WithdrawLamports all
360+
361+
#### Close: Permanent
362+
363+
- Retract
364+
- SetProgramLength to 0
365+
- WithdrawLamports leaving enough for rent expemtion of the header
366+
- SetAuthority to the system program
367+
368+
#### Transfer authority
369+
370+
- SetAuthority
294371

295372
#### Finalize
296373

297-
- Instruction accounts:
298-
- `[writable]` The program account to change the authority of.
299-
- `[signer]` The current authority of the program.
300-
- `[]` Optional, the reserved address for the next version of the program.
301-
- Instruction data:
302-
- Enum variant `6u32`
303-
- Behavior:
304-
- Check there are at least three instruction accounts,
305-
otherwise throw `NotEnoughAccountKeys`
306-
- Verify the program account
307-
- Check that the status stored in the program account is deployed,
308-
otherwise throw `InvalidArgument`
309-
- for the program account of the next version
310-
(instruction account at index 2) check that:
311-
- the owner of the program account is loader-v4,
312-
otherwise throw `InvalidAccountOwner`
313-
- the program account is at least as long enough for the header,
314-
otherwise throw `AccountDataTooSmall`
315-
- the authority stored in the program account is the one provided,
316-
otherwise throw `IncorrectAuthority`
317-
- the status stored in the program account is not finalized,
318-
otherwise throw `Immutable`
319-
- Copy the address of the next version into the next version field stored in
320-
the previous versions program account
321-
- Change the status stored in the program account to finalized
374+
- SetAuthority to the system program
322375

323376
## Impact
324377

325378
- This proposal covers all the use cases loader-v3 had but in a cleaner way and
326379
comes with a specification.
327-
- loader-v3 had a separate account type for buffers and extra commands for
328-
these buffer accounts, in loader-v4 program accounts can act as buffers, there
329-
is no more distinction.
330-
- loader-v3 deployments always needed a buffer, in loader-v4 it is optional,
331-
one can upload a redeployment into the program account directly.
380+
- loader-v3 had a separate account layout for buffers and extra commands for
381+
these buffer accounts, in loader-v4 they are only differentiated by status.
332382
- loader-v3 had two accounts per program, loader-v4 goes back to having only
333383
one, thus needs less funds to reach rent exemption.
334-
- loader-v3 closing of programs did finalize them, in loader-v4 all the funds
335-
can be retrieved and the program address repurposed.
384+
- loader-v3 closing of programs did always finalize them, in loader-v4 there
385+
is an option to retrieve all the funds the program address can be repurposed.
336386
- loader-v3 programs could only grow, loader-v4 can shrink programs and also
337387
retrieve the surplus of funds no longer required for rent exception.
338388
- loader-v3 programs were always "live" after the first deployment, with
339389
loader-v4 one can temporarily put a program into maintenance mode without a
340390
redeployment.
341-
- loader-v3 always required the entire program to be uploaded for a
342-
redeployment, loader-v4 supports partial uploads for patching chunks of the
343-
program.
344391
- loader-v3 ELFs were misaligned, loader-v4 properly aligns the executable
345392
file relative to the beginning of the account.
346-
- loader-v4 allows finalized programs to mark which other program supersedes
347-
them which can then be offered as an option in frontends. This provides a
348-
more secure alternative to redeployment / upgrading of programs at the same
349-
address. The keypair for the next version linked during finalization should be
350-
generated beforehand.
351393
- An option to migrate programs from loader-v3 to loader-v4 without changing
352394
their program address will be available via a new loader-v3 instruction. (see
353395
SIMD-0315)

0 commit comments

Comments
 (0)