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