Line data Source code
1 : #include "sysvar/fd_sysvar_rent.h"
2 : #include "program/fd_bpf_loader_program.h"
3 : #include "program/fd_builtin_programs.h"
4 : #include "fd_runtime_stack.h"
5 : #include "fd_pubkey_utils.h"
6 : #include "fd_system_ids.h"
7 : #include "fd_hashes.h"
8 : #include <assert.h>
9 :
10 : static fd_pubkey_t
11 0 : get_program_data_address( fd_pubkey_t const * program_addr ) {
12 0 : uchar const * seed = program_addr->uc;
13 0 : ulong seed_sz = 32UL;
14 0 : fd_pubkey_t out;
15 0 : uint custom_err;
16 0 : uchar out_bump_seed;
17 0 : fd_pubkey_find_program_address( &fd_solana_bpf_loader_upgradeable_program_id, 1UL, &seed, &seed_sz, &out, &out_bump_seed, &custom_err );
18 0 : return out;
19 0 : }
20 :
21 : fd_tmp_account_t *
22 : tmp_account_new( fd_tmp_account_t * acc,
23 0 : ulong acc_sz ) {
24 0 : acc->data_sz = acc_sz;
25 0 : fd_memset( acc->data, 0, acc_sz );
26 0 : return acc;
27 0 : }
28 :
29 : fd_tmp_account_t *
30 : tmp_account_read( fd_tmp_account_t * acc,
31 : fd_accdb_user_t * accdb,
32 : fd_funk_txn_xid_t const * xid,
33 0 : fd_pubkey_t const * addr ) {
34 0 : fd_accdb_ro_t ro[1];
35 0 : if( FD_LIKELY( !fd_accdb_open_ro( accdb, ro, xid, addr ) ) ) return NULL;
36 0 : tmp_account_new( acc, fd_accdb_ref_data_sz( ro ) );
37 0 : acc->meta = *ro->meta;
38 0 : acc->addr = *addr;
39 0 : fd_memcpy( acc->data, fd_accdb_ref_data_const( ro ), fd_accdb_ref_data_sz( ro ) );
40 0 : acc->data_sz = fd_accdb_ref_data_sz( ro );
41 0 : return acc;
42 0 : }
43 :
44 : void
45 : tmp_account_store( fd_tmp_account_t * acc,
46 : fd_accdb_user_t * accdb,
47 : fd_funk_txn_xid_t const * xid,
48 : fd_bank_t * bank,
49 0 : fd_capture_ctx_t * capture_ctx ) {
50 0 : if( FD_UNLIKELY( fd_pubkey_eq( &acc->addr, &fd_solana_system_program_id ) ) ) {
51 0 : FD_LOG_ERR(( "Attempted to write to the system program account" ));
52 0 : }
53 :
54 : /* FIXME usage of "txn_account" */
55 0 : fd_txn_account_t rec[1];
56 0 : fd_funk_rec_prepare_t prepare = {0};
57 0 : int ok = !!fd_txn_account_init_from_funk_mutable(
58 0 : rec,
59 0 : &acc->addr,
60 0 : accdb,
61 0 : xid,
62 0 : 1,
63 0 : acc->data_sz,
64 0 : &prepare );
65 0 : if( FD_UNLIKELY( !ok ) ) {
66 0 : FD_LOG_CRIT(( "fd_txn_account_init_from_funk_mutable failed" ));
67 0 : }
68 :
69 0 : fd_lthash_value_t prev_hash[1];
70 0 : fd_hashes_account_lthash( &acc->addr, fd_txn_account_get_meta( rec ), fd_txn_account_get_data( rec ), prev_hash );
71 :
72 0 : fd_txn_account_set_executable( rec, acc->meta.executable );
73 0 : fd_txn_account_set_owner ( rec, fd_type_pun_const( acc->meta.owner ) );
74 0 : fd_txn_account_set_lamports ( rec, acc->meta.lamports );
75 0 : fd_txn_account_set_data ( rec, acc->data, acc->data_sz );
76 :
77 0 : fd_hashes_update_lthash( rec->pubkey, rec->meta, prev_hash, bank, capture_ctx );
78 0 : fd_txn_account_mutable_fini( rec, accdb, &prepare );
79 0 : }
80 :
81 : /* https://github.com/anza-xyz/agave/blob/v3.0.2/runtime/src/bank/builtins/core_bpf_migration/target_core_bpf.rs#L12 */
82 :
83 : struct target_core_bpf {
84 : fd_pubkey_t program_address;
85 : fd_tmp_account_t * program_data_account;
86 : fd_pubkey_t upgrade_authority_address;
87 : uint has_upgrade_authority_address : 1;
88 : };
89 :
90 : typedef struct target_core_bpf target_core_bpf_t;
91 :
92 : /* https://github.com/anza-xyz/agave/blob/v3.0.2/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L13 */
93 :
94 : struct target_builtin {
95 : fd_tmp_account_t * program_account;
96 : fd_pubkey_t program_data_address;
97 : };
98 :
99 : typedef struct target_builtin target_builtin_t;
100 :
101 : /* https://github.com/anza-xyz/agave/blob/v3.0.2/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L22 */
102 :
103 : target_builtin_t *
104 : target_builtin_new_checked( target_builtin_t * target_builtin,
105 : fd_pubkey_t const * program_address,
106 : int migration_target,
107 : fd_accdb_user_t * accdb,
108 : fd_funk_txn_xid_t const * xid,
109 0 : fd_runtime_stack_t * runtime_stack ) {
110 :
111 : /* https://github.com/anza-xyz/agave/blob/v3.0.2/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L27-L49 */
112 :
113 0 : fd_tmp_account_t * program_account = &runtime_stack->bpf_migration.program_account;
114 0 : switch( migration_target ) {
115 0 : case FD_CORE_BPF_MIGRATION_TARGET_BUILTIN:
116 0 : if( FD_UNLIKELY( !tmp_account_read( program_account, accdb, xid, program_address ) ) ) {
117 : /* CoreBpfMigrationError::AccountNotFound(*program_address) */
118 0 : return NULL;
119 0 : }
120 0 : if( FD_UNLIKELY( 0!=memcmp( program_account->meta.owner, &fd_solana_native_loader_id, 32 ) ) ) {
121 : /* CoreBpfMigrationError::IncorrectOwner(*program_address) */
122 0 : return NULL;
123 0 : }
124 0 : break;
125 0 : case FD_CORE_BPF_MIGRATION_TARGET_STATELESS: {
126 : /* Program account should not exist */
127 0 : fd_accdb_ro_t ro[1];
128 0 : int progdata_exists = !!fd_accdb_open_ro( accdb, ro, xid, program_address );
129 0 : if( progdata_exists ) {
130 : /* CoreBpfMigrationError::AccountAlreadyExists(*program_address) */
131 0 : fd_accdb_close_ro( accdb, ro );
132 0 : return NULL;
133 0 : }
134 0 : break;
135 0 : }
136 0 : default:
137 0 : FD_LOG_ERR(( "invalid migration_target %d", migration_target ));
138 0 : }
139 :
140 : /* https://github.com/anza-xyz/agave/blob/v3.0.2/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L51 */
141 :
142 0 : fd_pubkey_t program_data_address = get_program_data_address( program_address );
143 :
144 : /* https://github.com/anza-xyz/agave/blob/v3.0.2/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L53-L61 */
145 :
146 0 : do {
147 : /* Program data account should not exist */
148 0 : fd_accdb_ro_t ro[1];
149 0 : int progdata_exists = !!fd_accdb_open_ro( accdb, ro, xid, &program_data_address );
150 0 : if( progdata_exists ) {
151 : /* CoreBpfMigrationError::AccountAlreadyExists(*program_address) */
152 0 : fd_accdb_close_ro( accdb, ro );
153 0 : return NULL;
154 0 : }
155 0 : } while(0);
156 :
157 : /* https://github.com/anza-xyz/agave/blob/v3.0.2/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L63-L67 */
158 :
159 0 : *target_builtin = (target_builtin_t) {
160 0 : .program_account = program_account,
161 0 : .program_data_address = program_data_address
162 0 : };
163 0 : return target_builtin;
164 0 : }
165 :
166 : /* https://github.com/anza-xyz/agave/blob/v3.0.2/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L22-L49 */
167 :
168 : static fd_tmp_account_t *
169 : source_buffer_new_checked( fd_tmp_account_t * acc,
170 : fd_accdb_user_t * accdb,
171 : fd_funk_txn_xid_t const * xid,
172 0 : fd_pubkey_t const * pubkey ) {
173 :
174 0 : if( FD_UNLIKELY( !tmp_account_read( acc, accdb, xid, pubkey ) ) ) {
175 : /* CoreBpfMigrationError::AccountNotFound(*buffer_address) */
176 0 : return NULL;
177 0 : }
178 :
179 0 : if( FD_UNLIKELY( 0!=memcmp( acc->meta.owner, &fd_solana_bpf_loader_upgradeable_program_id, 32 ) ) ) {
180 : /* CoreBpfMigrationError::IncorrectOwner(*buffer_address) */
181 0 : return NULL;
182 0 : }
183 :
184 0 : ulong const buffer_metadata_sz = 37UL;
185 0 : if( acc->data_sz < buffer_metadata_sz ) {
186 : /* CoreBpfMigrationError::InvalidBufferAccount(*buffer_address) */
187 0 : return NULL;
188 0 : }
189 :
190 0 : fd_bpf_upgradeable_loader_state_t state[1];
191 0 : if( FD_UNLIKELY( !fd_bincode_decode_static(
192 0 : bpf_upgradeable_loader_state, state,
193 0 : acc->data, acc->data_sz,
194 0 : NULL ) ) ) {
195 0 : return NULL;
196 0 : }
197 :
198 0 : return acc;
199 0 : }
200 :
201 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L82-L95 */
202 :
203 : static fd_tmp_account_t *
204 : new_target_program_account( fd_tmp_account_t * acc,
205 : target_builtin_t const * target,
206 0 : fd_rent_t const * rent ) {
207 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L86-L88 */
208 0 : fd_bpf_upgradeable_loader_state_t state = {
209 0 : .discriminant = fd_bpf_upgradeable_loader_state_enum_program,
210 0 : .inner = {
211 0 : .program = {
212 0 : .programdata_address = target->program_data_address,
213 0 : }
214 0 : }
215 0 : };
216 :
217 0 : tmp_account_new( acc, fd_bpf_upgradeable_loader_state_size( &state ) );
218 0 : acc->meta.lamports = fd_rent_exempt_minimum_balance( rent, SIZE_OF_PROGRAM );
219 0 : acc->meta.executable = 1;
220 0 : memcpy( acc->meta.owner, fd_solana_bpf_loader_upgradeable_program_id.uc, sizeof(fd_pubkey_t) );
221 :
222 0 : return acc;
223 0 : }
224 :
225 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L108-L153 */
226 : static fd_tmp_account_t *
227 : new_target_program_data_account( fd_tmp_account_t * acc,
228 : fd_tmp_account_t const * source,
229 : fd_pubkey_t const * upgrade_authority_address,
230 : fd_rent_t const * rent,
231 0 : ulong slot ) {
232 0 : ulong const buffer_metadata_sz = BUFFER_METADATA_SIZE;
233 :
234 0 : if( FD_UNLIKELY( source->data_sz < buffer_metadata_sz ) )
235 0 : return NULL; /* CoreBpfMigrationError::InvalidBufferAccount */
236 :
237 0 : fd_bpf_upgradeable_loader_state_t state;
238 0 : if( !fd_bincode_decode_static(
239 0 : bpf_upgradeable_loader_state,
240 0 : &state,
241 0 : source->data,
242 0 : buffer_metadata_sz,
243 0 : NULL ) )
244 0 : return NULL;
245 :
246 0 : if( FD_UNLIKELY( state.discriminant!=fd_bpf_upgradeable_loader_state_enum_buffer ) )
247 0 : return NULL; /* CoreBpfMigrationError::InvalidBufferAccount */
248 :
249 0 : if( FD_UNLIKELY( state.inner.buffer.has_authority_address != (!!upgrade_authority_address) ) )
250 0 : return NULL; /* CoreBpfMigrationError::InvalidBufferAccount */
251 :
252 0 : if( FD_UNLIKELY( upgrade_authority_address &&
253 0 : !fd_pubkey_eq( upgrade_authority_address, &state.inner.buffer.authority_address ) ) )
254 0 : return NULL; /* CoreBpfMigrationError::UpgradeAuthorityMismatch */
255 :
256 0 : void const * elf = (uchar const *)source->data + buffer_metadata_sz;
257 0 : ulong elf_sz = /* */source->data_sz - buffer_metadata_sz;
258 :
259 0 : ulong space = PROGRAMDATA_METADATA_SIZE + elf_sz;
260 0 : ulong lamports = fd_rent_exempt_minimum_balance( rent, space );
261 0 : fd_pubkey_t owner = fd_solana_bpf_loader_upgradeable_program_id;
262 :
263 0 : fd_bpf_upgradeable_loader_state_t programdata_meta = {
264 0 : .discriminant = fd_bpf_upgradeable_loader_state_enum_program_data,
265 0 : .inner = {
266 0 : .program_data = {
267 0 : .slot = slot,
268 0 : .has_upgrade_authority_address = !!upgrade_authority_address,
269 0 : .upgrade_authority_address = upgrade_authority_address ? *upgrade_authority_address : (fd_pubkey_t){{0}}
270 0 : }
271 0 : }
272 0 : };
273 :
274 0 : tmp_account_new( acc, space );
275 0 : acc->meta.lamports = lamports;
276 0 : memcpy( acc->meta.owner, owner.uc, sizeof(fd_pubkey_t) );
277 0 : fd_bincode_encode_ctx_t ctx = { .data=acc->data, .dataend=(uchar *)acc->data+PROGRAMDATA_METADATA_SIZE };
278 0 : if( FD_UNLIKELY( fd_bpf_upgradeable_loader_state_encode( &programdata_meta, &ctx )!=FD_BINCODE_SUCCESS ) ) {
279 0 : FD_LOG_ERR(( "fd_bpf_upgradeable_loader_state_encode failed" ));
280 0 : }
281 0 : fd_memcpy( (uchar *)acc->data+PROGRAMDATA_METADATA_SIZE, elf, elf_sz );
282 :
283 0 : return acc;
284 0 : }
285 :
286 : void
287 : migrate_builtin_to_core_bpf1( fd_core_bpf_migration_config_t const * config,
288 : fd_accdb_user_t * accdb,
289 : fd_funk_txn_xid_t const * xid,
290 : fd_bank_t * bank,
291 : fd_runtime_stack_t * runtime_stack,
292 : fd_pubkey_t const * builtin_program_id,
293 0 : fd_capture_ctx_t * capture_ctx ) {
294 0 : target_builtin_t target[1];
295 0 : if( FD_UNLIKELY( !target_builtin_new_checked(
296 0 : target,
297 0 : builtin_program_id,
298 0 : config->migration_target,
299 0 : accdb,
300 0 : xid,
301 0 : runtime_stack ) ) )
302 0 : return;
303 :
304 0 : fd_tmp_account_t * source = &runtime_stack->bpf_migration.source;
305 0 : if( FD_UNLIKELY( !source_buffer_new_checked(
306 0 : source,
307 0 : accdb,
308 0 : xid,
309 0 : config->source_buffer_address ) ) )
310 0 : return;
311 :
312 0 : fd_rent_t const * rent = fd_bank_rent_query( bank );
313 0 : ulong const slot = fd_bank_slot_get ( bank );
314 :
315 0 : fd_tmp_account_t * new_target_program = &runtime_stack->bpf_migration.new_target_program;
316 0 : if( FD_UNLIKELY( !new_target_program_account(
317 0 : new_target_program,
318 0 : target,
319 0 : rent ) ) )
320 0 : return;
321 :
322 0 : fd_tmp_account_t * new_target_program_data = &runtime_stack->bpf_migration.new_target_program_data;
323 0 : if( FD_UNLIKELY( !new_target_program_data_account(
324 0 : new_target_program_data,
325 0 : source,
326 0 : config->upgrade_authority_address,
327 0 : rent,
328 0 : slot ) ) )
329 0 : return;
330 :
331 0 : ulong old_data_sz;
332 0 : if( FD_UNLIKELY( __builtin_uaddl_overflow(
333 0 : target->program_account->data_sz,
334 0 : source->data_sz,
335 0 : &old_data_sz ) ) ) {
336 0 : return;
337 0 : }
338 0 : ulong new_data_sz;
339 0 : if( FD_UNLIKELY( __builtin_uaddl_overflow(
340 0 : new_target_program ->data_sz,
341 0 : new_target_program_data->data_sz,
342 0 : &new_data_sz ) ) ) {
343 0 : return;
344 0 : }
345 :
346 0 : assert( new_target_program_data->data_sz>=PROGRAMDATA_METADATA_SIZE );
347 : /* FIXME call fd_directly_invoke_loader_v3_deploy */
348 :
349 0 : ulong lamports_to_burn;
350 0 : if( FD_UNLIKELY( __builtin_uaddl_overflow(
351 0 : target->program_account->meta.lamports,
352 0 : source->meta.lamports,
353 0 : &lamports_to_burn ) ) ) {
354 0 : return;
355 0 : }
356 0 : ulong lamports_to_fund;
357 0 : if( FD_UNLIKELY( __builtin_uaddl_overflow(
358 0 : new_target_program ->meta.lamports,
359 0 : new_target_program_data->meta.lamports,
360 0 : &lamports_to_fund ) ) ) {
361 0 : return;
362 0 : }
363 :
364 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L286-L297 */
365 0 : ulong capitalization = fd_bank_capitalization_get( bank );
366 0 : int cap_ok = 1;
367 0 : if( lamports_to_burn>lamports_to_fund ) {
368 0 : cap_ok = __builtin_usubl_overflow(
369 0 : capitalization,
370 0 : (lamports_to_burn-lamports_to_fund),
371 0 : &capitalization );
372 0 : } else if( lamports_to_burn<lamports_to_fund ) {
373 0 : cap_ok = __builtin_uaddl_overflow(
374 0 : capitalization,
375 0 : (lamports_to_fund-lamports_to_burn),
376 0 : &capitalization );
377 0 : }
378 0 : if( FD_UNLIKELY( !cap_ok ) ) {
379 0 : FD_LOG_ERR(( "Capitalization overflow while migrating builtin program to core BPF" ));
380 0 : }
381 0 : fd_bank_capitalization_set( bank, capitalization );
382 :
383 : /* Write back accounts */
384 0 : tmp_account_store( new_target_program, accdb, xid, bank, capture_ctx );
385 0 : tmp_account_store( new_target_program_data, accdb, xid, bank, capture_ctx );
386 0 : fd_tmp_account_t * empty = &runtime_stack->bpf_migration.empty;
387 0 : tmp_account_new( empty, 0UL );
388 0 : empty->addr = source->addr;
389 0 : tmp_account_store( empty, accdb, xid, bank, capture_ctx );
390 :
391 : /* FIXME "remove the built-in program from the bank's list of builtins" */
392 : /* FIXME "update account data size delta" */
393 0 : }
394 :
395 : /* Mimics migrate_builtin_to_core_bpf().
396 : https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L235-L318 */
397 : void
398 : fd_migrate_builtin_to_core_bpf( fd_bank_t * bank,
399 : fd_accdb_user_t * accdb,
400 : fd_funk_txn_xid_t const * xid,
401 : fd_runtime_stack_t * runtime_stack,
402 : fd_core_bpf_migration_config_t const * config,
403 0 : fd_capture_ctx_t * capture_ctx ) {
404 0 : migrate_builtin_to_core_bpf1( config, accdb, xid, bank, runtime_stack, config->builtin_program_id, capture_ctx );
405 0 : }
|