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