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