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