Line data Source code
1 : #include "context/fd_exec_slot_ctx.h"
2 : #include "sysvar/fd_sysvar_rent.h"
3 : #include "program/fd_bpf_loader_program.h"
4 : #include "program/fd_builtin_programs.h"
5 : #include "fd_pubkey_utils.h"
6 :
7 : /* Mimics bank.new_target_program_account(). Assumes out_rec is a
8 : modifiable record.
9 :
10 : From the calling context, out_rec points to a native program record
11 : (e.g. Config, ALUT native programs). There should be enough space in
12 : out_rec->data to hold at least 36 bytes (the size of a BPF
13 : upgradeable program account) when calling this function. The native
14 : program account's owner is set to the BPF loader upgradeable program
15 : ID, and lamports are increased / deducted to contain the rent exempt
16 : minimum balance.
17 :
18 : https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L79-L95 */
19 : static int
20 : fd_new_target_program_account( fd_exec_slot_ctx_t * slot_ctx,
21 : fd_pubkey_t const * target_program_data_address,
22 0 : fd_txn_account_t * out_rec ) {
23 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L86-L88 */
24 0 : fd_bpf_upgradeable_loader_state_t state = {
25 0 : .discriminant = fd_bpf_upgradeable_loader_state_enum_program,
26 0 : .inner = {
27 0 : .program = {
28 0 : .programdata_address = *target_program_data_address,
29 0 : }
30 0 : }
31 0 : };
32 :
33 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L89-L90 */
34 0 : fd_rent_t const * rent = fd_bank_rent_query( slot_ctx->bank );
35 0 : if( FD_UNLIKELY( rent==NULL ) ) {
36 0 : return -1;
37 0 : }
38 :
39 0 : fd_txn_account_set_lamports( out_rec, fd_rent_exempt_minimum_balance( rent, SIZE_OF_PROGRAM ) );
40 0 : fd_bincode_encode_ctx_t ctx = {
41 0 : .data = fd_txn_account_get_data_mut( out_rec ),
42 0 : .dataend = fd_txn_account_get_data_mut( out_rec ) + SIZE_OF_PROGRAM,
43 0 : };
44 :
45 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L91-L9 */
46 0 : int err = fd_bpf_upgradeable_loader_state_encode( &state, &ctx );
47 0 : if( FD_UNLIKELY( err ) ) {
48 0 : return err;
49 0 : }
50 0 : fd_txn_account_set_owner( out_rec, &fd_solana_bpf_loader_upgradeable_program_id );
51 :
52 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L93-L94 */
53 0 : fd_txn_account_set_executable( out_rec, 1 );
54 0 : return FD_RUNTIME_EXECUTE_SUCCESS;
55 0 : }
56 :
57 : /* Mimics bank.new_target_program_data_account(). Assumes
58 : new_target_program_data_account is a modifiable record.
59 : config_upgrade_authority_address may be NULL.
60 :
61 : This function uses an existing buffer account buffer_acc_rec to set
62 : the program data account data for a core program BPF migration. Sets
63 : the lamports and data fields of new_target_program_data_account
64 : based on the ELF data length, and sets the owner to the BPF loader
65 : upgradeable program ID.
66 :
67 : https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L97-L153 */
68 : static int
69 : fd_new_target_program_data_account( fd_exec_slot_ctx_t * slot_ctx,
70 : fd_pubkey_t * config_upgrade_authority_address,
71 : fd_txn_account_t * buffer_acc_rec,
72 : fd_txn_account_t * new_target_program_data_account,
73 0 : fd_spad_t * runtime_spad ) {
74 :
75 0 : FD_SPAD_FRAME_BEGIN( runtime_spad ) {
76 :
77 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L113-L116 */
78 0 : int err;
79 0 : fd_bpf_upgradeable_loader_state_t * state = fd_bincode_decode_spad(
80 0 : bpf_upgradeable_loader_state, runtime_spad,
81 0 : fd_txn_account_get_data( buffer_acc_rec ),
82 0 : fd_txn_account_get_data_len( buffer_acc_rec ),
83 0 : &err );
84 0 : if( FD_UNLIKELY( err ) ) return err;
85 :
86 0 : if( FD_UNLIKELY( !fd_bpf_upgradeable_loader_state_is_buffer( state ) ) ) {
87 0 : return -1;
88 0 : }
89 :
90 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L118-L125 */
91 0 : if( config_upgrade_authority_address!=NULL ) {
92 0 : if( FD_UNLIKELY( !state->inner.buffer.has_authority_address ||
93 0 : !fd_pubkey_eq( config_upgrade_authority_address, &state->inner.buffer.authority_address ) ) ) {
94 0 : return -1;
95 0 : }
96 0 : }
97 :
98 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L127-L132 */
99 0 : fd_rent_t const * rent = fd_bank_rent_query( slot_ctx->bank );
100 0 : if( FD_UNLIKELY( rent==NULL ) ) {
101 0 : return -1;
102 0 : }
103 :
104 0 : const uchar * elf = fd_txn_account_get_data( buffer_acc_rec ) + BUFFER_METADATA_SIZE;
105 0 : ulong space = PROGRAMDATA_METADATA_SIZE - BUFFER_METADATA_SIZE + fd_txn_account_get_data_len( buffer_acc_rec );
106 0 : ulong lamports = fd_rent_exempt_minimum_balance( rent, space );
107 :
108 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L134-L137 */
109 0 : fd_bpf_upgradeable_loader_state_t programdata_metadata = {
110 0 : .discriminant = fd_bpf_upgradeable_loader_state_enum_program_data,
111 0 : .inner = {
112 0 : .program_data = {
113 0 : .slot = fd_bank_slot_get( slot_ctx->bank ),
114 0 : .has_upgrade_authority_address = !!config_upgrade_authority_address,
115 0 : .upgrade_authority_address = config_upgrade_authority_address ? *config_upgrade_authority_address : (fd_pubkey_t){{0}}
116 0 : }
117 0 : }
118 0 : };
119 :
120 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L139-L144 */
121 0 : fd_txn_account_set_lamports( new_target_program_data_account, lamports );
122 0 : fd_bincode_encode_ctx_t encode_ctx = {
123 0 : .data = fd_txn_account_get_data_mut( new_target_program_data_account ),
124 0 : .dataend = fd_txn_account_get_data_mut( new_target_program_data_account ) + PROGRAMDATA_METADATA_SIZE,
125 0 : };
126 0 : err = fd_bpf_upgradeable_loader_state_encode( &programdata_metadata, &encode_ctx );
127 0 : if( FD_UNLIKELY( err ) ) {
128 0 : return err;
129 0 : }
130 0 : fd_txn_account_set_owner( new_target_program_data_account, &fd_solana_bpf_loader_upgradeable_program_id );
131 :
132 : /* Copy the ELF data over
133 : https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L145 */
134 0 : fd_memcpy( fd_txn_account_get_data_mut( new_target_program_data_account ) + PROGRAMDATA_METADATA_SIZE, elf, fd_txn_account_get_data_len( buffer_acc_rec ) - BUFFER_METADATA_SIZE );
135 :
136 0 : return FD_RUNTIME_EXECUTE_SUCCESS;
137 :
138 0 : } FD_SPAD_FRAME_END;
139 0 : }
140 :
141 : /* Initializes a source buffer account from funk. Returns 1 if the
142 : buffer account does not exist or is not owned by the upgradeable
143 : loader. Returns 0 on success.
144 :
145 : https://github.com/anza-xyz/agave/blob/v2.3.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L22-L49 */
146 : static int
147 : fd_source_buffer_account_new( fd_exec_slot_ctx_t * slot_ctx,
148 : fd_txn_account_t * buffer_account,
149 : fd_pubkey_t const * buffer_address,
150 0 : fd_funk_rec_prepare_t * prepare ) {
151 : /* The buffer account should exist.
152 : https://github.com/anza-xyz/agave/blob/v2.3.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L27-L29 */
153 0 : if( FD_UNLIKELY( fd_txn_account_init_from_funk_mutable( buffer_account, buffer_address, slot_ctx->funk, slot_ctx->funk_txn, 0, 0UL, prepare )!=FD_ACC_MGR_SUCCESS ) ) {
154 0 : FD_LOG_DEBUG(( "Buffer account %s does not exist, skipping migration...", FD_BASE58_ENC_32_ALLOCA( buffer_address ) ));
155 0 : return 1;
156 0 : }
157 :
158 : /* The buffer account should be owned by the upgradeable loader.
159 : https://github.com/anza-xyz/agave/blob/v2.3.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L31-L34 */
160 0 : if( FD_UNLIKELY( memcmp( fd_txn_account_get_owner( buffer_account ), fd_solana_bpf_loader_upgradeable_program_id.uc, sizeof(fd_pubkey_t) ) ) ) {
161 0 : FD_LOG_DEBUG(( "Buffer account %s is not owned by the upgradeable loader, skipping migration...", FD_BASE58_ENC_32_ALLOCA( buffer_address ) ));
162 0 : return 1;
163 0 : }
164 :
165 : /* The buffer account should have the correct state. We already check
166 : the buffer account state in fd_new_target_program_data_account(),
167 : so we can skip the checks here.
168 : https://github.com/anza-xyz/agave/blob/v2.3.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L37-L47 */
169 :
170 0 : return 0;
171 0 : }
172 :
173 : /* Similar to fd_source_buffer_account_new() but also checks the build
174 : hash of the buffer account for verification. verified_build_hash must
175 : be valid and non-NULL. Returns 1 if fd_source_buffer_account_new()
176 : fails, the buffer dlen is too small, or if the build hash mismatches.
177 : Returns 0 on success.
178 :
179 : https://github.com/anza-xyz/agave/blob/v2.3.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L51-L75 */
180 : static int
181 : fd_source_buffer_account_new_with_hash( fd_exec_slot_ctx_t * slot_ctx,
182 : fd_txn_account_t * buffer_account,
183 : fd_pubkey_t const * buffer_address,
184 : fd_hash_t const * verified_build_hash,
185 0 : fd_funk_rec_prepare_t * prepare ) {
186 : /* https://github.com/anza-xyz/agave/blob/v2.3.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L58 */
187 0 : int err = fd_source_buffer_account_new( slot_ctx, buffer_account, buffer_address, prepare );
188 0 : if( FD_UNLIKELY( err ) ) {
189 0 : return err;
190 0 : }
191 :
192 : /* https://github.com/anza-xyz/agave/blob/v2.3.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L59 */
193 0 : uchar const * data = fd_txn_account_get_data( buffer_account );
194 0 : ulong data_len = fd_txn_account_get_data_len( buffer_account );
195 :
196 : /* https://github.com/anza-xyz/agave/blob/v2.3.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L61 */
197 0 : ulong offset = BUFFER_METADATA_SIZE;
198 0 : if( FD_UNLIKELY( data_len<offset ) ) {
199 0 : return 1;
200 0 : }
201 :
202 : /* Search for the first nonzero byte in the buffer account data starting
203 : from the right.
204 : https://github.com/anza-xyz/agave/blob/v2.3.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L62 */
205 0 : ulong end_offset = offset;
206 0 : for( ulong i=data_len-1UL; i>=offset; i-- ) {
207 0 : if( data[i]!=0 ) {
208 0 : end_offset = i+1UL;
209 0 : break;
210 0 : }
211 0 : }
212 :
213 : /* Compute and verify the hash.
214 : https://github.com/anza-xyz/agave/blob/v2.3.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L64-L71 */
215 0 : fd_hash_t hash[1];
216 0 : fd_sha256_hash( data+offset, end_offset-offset, hash );
217 0 : if( FD_UNLIKELY( memcmp( verified_build_hash, hash, sizeof(fd_hash_t) ) ) ) {
218 0 : FD_LOG_WARNING(( "Mismatching build hash for Buffer account %s (expected=%s, actual=%s). Skipping migration...", FD_BASE58_ENC_32_ALLOCA( buffer_address ), FD_BASE58_ENC_32_ALLOCA( verified_build_hash ), FD_BASE58_ENC_32_ALLOCA( hash ) ));
219 0 : return 1;
220 0 : }
221 :
222 0 : return 0;
223 0 : }
224 :
225 : /* Mimics migrate_builtin_to_core_bpf().
226 : https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L235-L318 */
227 : void
228 : fd_migrate_builtin_to_core_bpf( fd_exec_slot_ctx_t * slot_ctx,
229 : fd_core_bpf_migration_config_t const * config,
230 0 : fd_spad_t * runtime_spad ) {
231 0 : int err;
232 :
233 : /* Initialize local variables from the config */
234 0 : fd_pubkey_t const * source_buffer_address = config->source_buffer_address;
235 0 : fd_pubkey_t * upgrade_authority_address = config->upgrade_authority_address;
236 0 : uchar stateless = !!( config->migration_target==FD_CORE_BPF_MIGRATION_TARGET_STATELESS );
237 0 : fd_pubkey_t const * builtin_program_id = config->builtin_program_id;
238 0 : fd_hash_t const * verified_build_hash = config->verified_build_hash;
239 :
240 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L242-L243
241 :
242 : The below logic is used to obtain a TargetBuiltin account. There
243 : are three fields of TargetBuiltin returned:
244 : - target.program_address: builtin_program_id
245 : - target.program_account:
246 : - if stateless: an AccountSharedData::default() (i.e. system
247 : program id, 0 lamports, 0 data, non-executable, system
248 : program owner)
249 : - if NOT stateless: the existing account (for us its called
250 : target_program_account)
251 : - target.program_data_address: target_program_data_address for
252 : us, derived below. */
253 :
254 : /* These checks will fail if the core program has already been migrated to BPF, since the account will exist + the program owner
255 : will no longer be the native loader.
256 : https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L23-L50 */
257 0 : FD_TXN_ACCOUNT_DECL( target_program_account );
258 0 : uchar program_exists = ( fd_txn_account_init_from_funk_readonly( target_program_account, builtin_program_id, slot_ctx->funk, slot_ctx->funk_txn )==FD_ACC_MGR_SUCCESS );
259 0 : if( !stateless ) {
260 : /* The program account should exist.
261 : https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L30-L33 */
262 0 : if( FD_UNLIKELY( !program_exists ) ) {
263 0 : FD_LOG_DEBUG(( "Builtin program %s does not exist, skipping migration...", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
264 0 : return;
265 0 : }
266 :
267 : /* The program account should be owned by the native loader.
268 : https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L35-L38 */
269 0 : if( FD_UNLIKELY( memcmp( fd_txn_account_get_owner( target_program_account ), fd_solana_native_loader_id.uc, sizeof(fd_pubkey_t) ) ) ) {
270 0 : FD_LOG_DEBUG(( "Builtin program %s is not owned by the native loader, skipping migration...", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
271 0 : return;
272 0 : }
273 0 : } else {
274 : /* The program account should _not_ exist.
275 : https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L42-L46 */
276 0 : if( FD_UNLIKELY( program_exists ) ) {
277 0 : FD_LOG_DEBUG(( "Stateless program %s already exists, skipping migration...", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
278 0 : return;
279 0 : }
280 0 : }
281 :
282 : /* The program data account should not exist.
283 : https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L52-L62 */
284 0 : uint custom_err = UINT_MAX;
285 0 : fd_pubkey_t target_program_data_address[ 1UL ];
286 0 : uchar const * seeds[ 1UL ];
287 0 : seeds[ 0UL ] = (uchar const *)builtin_program_id;
288 0 : ulong seed_sz = sizeof(fd_pubkey_t);
289 0 : uchar bump_seed = 0;
290 0 : err = fd_pubkey_find_program_address( &fd_solana_bpf_loader_upgradeable_program_id, 1UL, seeds, &seed_sz, target_program_data_address, &bump_seed, &custom_err );
291 0 : if( FD_UNLIKELY( err ) ) {
292 : /* TODO: We should handle these errors more gracefully instead of just killing the client. */
293 0 : FD_LOG_ERR(( "Unable to find a viable program address bump seed" )); // Solana panics, error code is undefined
294 0 : return;
295 0 : }
296 0 : FD_TXN_ACCOUNT_DECL( program_data_account );
297 0 : if( FD_UNLIKELY( fd_txn_account_init_from_funk_readonly( program_data_account, target_program_data_address, slot_ctx->funk, slot_ctx->funk_txn )==FD_ACC_MGR_SUCCESS ) ) {
298 0 : FD_LOG_WARNING(( "Program data account %s already exists, skipping migration...", FD_BASE58_ENC_32_ALLOCA( target_program_data_address ) ));
299 0 : return;
300 0 : }
301 :
302 : /* https://github.com/anza-xyz/agave/blob/v2.3.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L221-L229
303 :
304 : Obtains a SourceBuffer account. There are two fields returned:
305 : - source.buffer_address: source_buffer_address
306 : - source.buffer_account: the existing buffer account
307 : Depending on if the verified build hash is provided, */
308 0 : FD_TXN_ACCOUNT_DECL( source_buffer_account );
309 0 : fd_funk_rec_prepare_t source_buffer_prepare = {0};
310 0 : if( verified_build_hash!=NULL ) {
311 0 : if( FD_UNLIKELY( fd_source_buffer_account_new_with_hash( slot_ctx, source_buffer_account, source_buffer_address, verified_build_hash, &source_buffer_prepare ) ) ) {
312 0 : return;
313 0 : }
314 0 : } else {
315 0 : if( FD_UNLIKELY( fd_source_buffer_account_new( slot_ctx, source_buffer_account, source_buffer_address, &source_buffer_prepare ) ) ) {
316 0 : return;
317 0 : }
318 0 : }
319 :
320 0 : fd_lthash_value_t prev_source_buffer_hash[1];
321 0 : fd_hashes_account_lthash(
322 0 : source_buffer_address,
323 0 : fd_txn_account_get_meta( source_buffer_account ),
324 0 : fd_txn_account_get_data( source_buffer_account ),
325 0 : prev_source_buffer_hash );
326 :
327 : /* This check is done a bit prematurely because we calculate the
328 : previous account state's lamports. We use 0 for starting lamports
329 : for stateless accounts because they don't yet exist.
330 :
331 : https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L277-L280 */
332 0 : ulong lamports_to_burn = ( stateless ? 0UL : fd_txn_account_get_lamports( target_program_account ) ) + fd_txn_account_get_lamports( source_buffer_account );
333 :
334 : /* Start a funk write txn */
335 0 : fd_funk_txn_t * parent_txn = slot_ctx->funk_txn;
336 0 : fd_funk_txn_xid_t migration_xid = fd_funk_generate_xid();
337 0 : fd_funk_txn_start_write( slot_ctx->funk );
338 0 : slot_ctx->funk_txn = fd_funk_txn_prepare( slot_ctx->funk, slot_ctx->funk_txn, &migration_xid, 0UL );
339 0 : fd_funk_txn_end_write( slot_ctx->funk );
340 :
341 : /* Attempt serialization of program account. If the program is
342 : stateless, we want to create the account. Otherwise, we want a
343 : writable handle to modify the existing account.
344 : https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L246-L249 */
345 0 : FD_TXN_ACCOUNT_DECL( new_target_program_account );
346 0 : fd_funk_rec_prepare_t new_target_program_prepare = {0};
347 0 : err = fd_txn_account_init_from_funk_mutable(
348 0 : new_target_program_account,
349 0 : builtin_program_id,
350 0 : slot_ctx->funk,
351 0 : slot_ctx->funk_txn,
352 0 : stateless,
353 0 : SIZE_OF_PROGRAM,
354 0 : &new_target_program_prepare );
355 0 : if( FD_UNLIKELY( err ) ) {
356 0 : FD_LOG_WARNING(( "Builtin program ID %s does not exist", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
357 0 : goto fail;
358 0 : }
359 0 : fd_lthash_value_t prev_new_target_program_account_hash[1];
360 0 : fd_hashes_account_lthash(
361 0 : builtin_program_id,
362 0 : fd_txn_account_get_meta( new_target_program_account ),
363 0 : fd_txn_account_get_data( new_target_program_account ),
364 0 : prev_new_target_program_account_hash );
365 0 : fd_txn_account_set_data_len( new_target_program_account, SIZE_OF_PROGRAM );
366 0 : fd_txn_account_set_slot( new_target_program_account, fd_bank_slot_get( slot_ctx->bank ) );
367 :
368 : /* Create a new target program account. This modifies the existing record. */
369 0 : err = fd_new_target_program_account( slot_ctx, target_program_data_address, new_target_program_account );
370 0 : if( FD_UNLIKELY( err ) ) {
371 0 : FD_LOG_WARNING(( "Failed to write new program state to %s", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
372 0 : goto fail;
373 0 : }
374 :
375 0 : fd_hashes_update_lthash(
376 0 : new_target_program_account,
377 0 : prev_new_target_program_account_hash,
378 0 : slot_ctx->bank,
379 0 : slot_ctx->capture_ctx );
380 0 : fd_txn_account_mutable_fini( new_target_program_account, slot_ctx->funk, slot_ctx->funk_txn, &new_target_program_prepare );
381 :
382 : /* Create a new target program data account. */
383 0 : ulong new_target_program_data_account_sz = PROGRAMDATA_METADATA_SIZE - BUFFER_METADATA_SIZE + fd_txn_account_get_data_len( source_buffer_account );
384 0 : FD_TXN_ACCOUNT_DECL( new_target_program_data_account );
385 0 : fd_funk_rec_prepare_t new_target_program_data_prepare = {0};
386 0 : err = fd_txn_account_init_from_funk_mutable(
387 0 : new_target_program_data_account,
388 0 : target_program_data_address,
389 0 : slot_ctx->funk,
390 0 : slot_ctx->funk_txn,
391 0 : 1,
392 0 : new_target_program_data_account_sz,
393 0 : &new_target_program_data_prepare );
394 0 : if( FD_UNLIKELY( err ) ) {
395 0 : FD_LOG_WARNING(( "Failed to create new program data account to %s", FD_BASE58_ENC_32_ALLOCA( target_program_data_address ) ));
396 0 : goto fail;
397 0 : }
398 0 : fd_lthash_value_t prev_new_target_program_data_account_hash[1];
399 0 : fd_hashes_account_lthash(
400 0 : target_program_data_address,
401 0 : fd_txn_account_get_meta( new_target_program_data_account ),
402 0 : fd_txn_account_get_data( new_target_program_data_account ),
403 0 : prev_new_target_program_data_account_hash );
404 0 : fd_txn_account_set_data_len( new_target_program_data_account, new_target_program_data_account_sz );
405 0 : fd_txn_account_set_slot( new_target_program_data_account, fd_bank_slot_get( slot_ctx->bank ) );
406 :
407 0 : err = fd_new_target_program_data_account( slot_ctx,
408 0 : upgrade_authority_address,
409 0 : source_buffer_account,
410 0 : new_target_program_data_account,
411 0 : runtime_spad );
412 0 : if( FD_UNLIKELY( err ) ) {
413 0 : FD_LOG_WARNING(( "Failed to write new program data state to %s", FD_BASE58_ENC_32_ALLOCA( target_program_data_address ) ));
414 0 : goto fail;
415 0 : }
416 :
417 0 : fd_hashes_update_lthash(
418 0 : new_target_program_data_account,
419 0 : prev_new_target_program_data_account_hash,
420 0 : slot_ctx->bank,
421 0 : slot_ctx->capture_ctx );
422 0 : fd_txn_account_mutable_fini( new_target_program_data_account, slot_ctx->funk, slot_ctx->funk_txn, &new_target_program_data_prepare );
423 :
424 : /* Deploy the new target Core BPF program.
425 : https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L268-L271 */
426 0 : err = fd_directly_invoke_loader_v3_deploy( slot_ctx,
427 0 : builtin_program_id,
428 0 : fd_txn_account_get_data( new_target_program_data_account ) + PROGRAMDATA_METADATA_SIZE,
429 0 : fd_txn_account_get_data_len( new_target_program_data_account ) - PROGRAMDATA_METADATA_SIZE,
430 0 : runtime_spad );
431 0 : if( FD_UNLIKELY( err ) ) {
432 0 : FD_LOG_WARNING(( "Failed to deploy program %s", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
433 0 : goto fail;
434 0 : }
435 :
436 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L281-L284 */
437 0 : ulong lamports_to_fund = fd_txn_account_get_lamports( new_target_program_account ) + fd_txn_account_get_lamports( new_target_program_data_account );
438 :
439 : /* Update capitalization.
440 : https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L286-L297 */
441 0 : if( lamports_to_burn>lamports_to_fund ) {
442 0 : fd_bank_capitalization_set( slot_ctx->bank, fd_bank_capitalization_get( slot_ctx->bank ) - ( lamports_to_burn - lamports_to_fund ) );
443 0 : } else {
444 0 : fd_bank_capitalization_set( slot_ctx->bank, fd_bank_capitalization_get( slot_ctx->bank ) + ( lamports_to_fund - lamports_to_burn ) );
445 0 : }
446 :
447 : /* Reclaim the source buffer account
448 : https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L305 */
449 0 : fd_txn_account_set_lamports( source_buffer_account, 0 );
450 0 : fd_txn_account_set_data_len( source_buffer_account, 0 );
451 0 : fd_txn_account_clear_owner( source_buffer_account );
452 :
453 0 : fd_hashes_update_lthash(
454 0 : source_buffer_account,
455 0 : prev_source_buffer_hash,
456 0 : slot_ctx->bank,
457 0 : slot_ctx->capture_ctx );
458 0 : fd_txn_account_mutable_fini( source_buffer_account, slot_ctx->funk, slot_ctx->funk_txn, &source_buffer_prepare );
459 :
460 : /* Publish the in-preparation transaction into the parent. We should not have to create
461 : a BPF cache entry here because the program is technically "delayed visibility", so the program
462 : should not be invokable until the next slot. The cache entry will be created at the end of the
463 : block as a part of the finalize routine. */
464 0 : fd_funk_txn_start_write( slot_ctx->funk );
465 0 : fd_funk_txn_publish_into_parent( slot_ctx->funk, slot_ctx->funk_txn, 1 );
466 0 : fd_funk_txn_end_write( slot_ctx->funk );
467 0 : slot_ctx->funk_txn = parent_txn;
468 0 : return;
469 :
470 0 : fail:
471 : /* Cancel the in-preparation transaction and discard any in-progress changes. */
472 0 : fd_funk_txn_start_write( slot_ctx->funk );
473 0 : fd_funk_txn_cancel( slot_ctx->funk, slot_ctx->funk_txn, 0UL );
474 0 : fd_funk_txn_end_write( slot_ctx->funk );
475 0 : slot_ctx->funk_txn = parent_txn;
476 0 : }
|