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