Skip to content

Commit e11aa03

Browse files
flamenco, runtime: implement replace_spl_token_with_p_token
1 parent 110a9d5 commit e11aa03

File tree

9 files changed

+229
-3
lines changed

9 files changed

+229
-3
lines changed

src/flamenco/features/fd_features_generated.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1807,6 +1807,12 @@ fd_feature_id_t const ids[] = {
18071807
.name = "create_account_allow_prefund",
18081808
.cleaned_up = 0 },
18091809

1810+
{ .index = offsetof(fd_features_t, replace_spl_token_with_p_token)>>3,
1811+
.id = {"\x0c\x44\xe0\x0e\x03\x6f\xc8\xa4\xc4\x7e\xdb\x02\xb0\x8d\xff\x3b\xc2\x2f\x04\x5a\x7d\x01\xa1\x00\x07\x1f\x85\x5f\x21\x09\x7a\x6a"},
1812+
/* ptokFjwyJtrwCa9Kgo9xoDS59V4QccBGEaRFnRPnSdP */
1813+
.name = "replace_spl_token_with_p_token",
1814+
.cleaned_up = 0 },
1815+
18101816
{ .index = ULONG_MAX }
18111817
};
18121818

@@ -2088,6 +2094,7 @@ typedef struct fd_feature_id_lookup_entry fd_feature_id_lookup_entry_t;
20882094
#define MAP_PERFECT_262 0x8b0786cd93f63607UL, .val = &ids[262]
20892095
#define MAP_PERFECT_263 0x4908ae0360664f6dUL, .val = &ids[263]
20902096
#define MAP_PERFECT_264 0x632b9b7c9e9a3257UL, .val = &ids[264]
2097+
#define MAP_PERFECT_265 0xa4c86f030ee0440cUL, .val = &ids[265]
20912098

20922099
#include "../../util/tmpl/fd_map_perfect.c"
20932100

@@ -2363,4 +2370,5 @@ FD_STATIC_ASSERT( offsetof( fd_features_t, limit_instruction_accounts
23632370
FD_STATIC_ASSERT( offsetof( fd_features_t, validator_admission_ticket )>>3==262UL, layout );
23642371
FD_STATIC_ASSERT( offsetof( fd_features_t, discard_unexpected_data_complete_shreds )>>3==263UL, layout );
23652372
FD_STATIC_ASSERT( offsetof( fd_features_t, create_account_allow_prefund )>>3==264UL, layout );
2373+
FD_STATIC_ASSERT( offsetof( fd_features_t, replace_spl_token_with_p_token )>>3==265UL, layout );
23662374
FD_STATIC_ASSERT( sizeof( fd_features_t )>>3==FD_FEATURE_ID_CNT, layout );

src/flamenco/features/fd_features_generated.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
#endif
99

1010
/* FEATURE_ID_CNT is the number of features in ids */
11-
#define FD_FEATURE_ID_CNT (265UL)
11+
#define FD_FEATURE_ID_CNT (266UL)
1212

1313
/* Feature set ID calculated from all feature names */
14-
#define FD_FEATURE_SET_ID (2642541842U)
14+
#define FD_FEATURE_SET_ID (1894134510U)
1515

1616
union fd_features {
1717
ulong f[ FD_FEATURE_ID_CNT ];
@@ -281,5 +281,6 @@ union fd_features {
281281
/* 0x8b0786cd93f63607 */ ulong validator_admission_ticket;
282282
/* 0x4908ae0360664f6d */ ulong discard_unexpected_data_complete_shreds;
283283
/* 0x632b9b7c9e9a3257 */ ulong create_account_allow_prefund;
284+
/* 0xa4c86f030ee0440c */ ulong replace_spl_token_with_p_token;
284285
};
285286
};

src/flamenco/features/feature_map.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,5 +263,6 @@
263263
{"name":"limit_instruction_accounts","pubkey":"DqbnFPASg7tHmZ6qfpdrt2M6MWoSeiicWPXxPhxqFCQ"},
264264
{"name":"validator_admission_ticket","pubkey":"VATtb1DepUwdPh5bFVasdtkbeDNsftZSRzr2aKpKWJA"},
265265
{"name":"discard_unexpected_data_complete_shreds","pubkey":"8MhfKhoZEoiySpVe248bDkisyEcBA7JQLyUS94xoTSqN"},
266-
{"name":"create_account_allow_prefund","pubkey":"6sPDzwyARRExKH52LECxcGoqziH8G7SZofwuxi8Ja331"}
266+
{"name":"create_account_allow_prefund","pubkey":"6sPDzwyARRExKH52LECxcGoqziH8G7SZofwuxi8Ja331"},
267+
{"name":"replace_spl_token_with_p_token","pubkey":"ptokFjwyJtrwCa9Kgo9xoDS59V4QccBGEaRFnRPnSdP"}
267268
]

src/flamenco/runtime/fd_core_bpf_migration.c

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#include "fd_hashes.h"
99
#include "../accdb/fd_accdb_sync.h"
1010
#include "../../ballet/sha256/fd_sha256.h"
11+
#include "../../ballet/sbpf/fd_sbpf_loader.h"
12+
#include "../progcache/fd_prog_load.h"
1113
#include <assert.h>
1214

1315
static fd_pubkey_t
@@ -257,6 +259,80 @@ target_core_bpf_new_checked( target_core_bpf_t * target_core_bpf,
257259
return target_core_bpf;
258260
}
259261

262+
/* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/target_bpf_v2.rs#L25-L82
263+
264+
Agave uses a separate TargetBpfV2 struct, but it has the
265+
same layout as target_builtin_t so we reuse that. */
266+
static target_builtin_t *
267+
target_bpf_v2_new_checked( target_builtin_t * target_bpf_v2,
268+
fd_pubkey_t const * program_address,
269+
int allow_prefunded,
270+
fd_accdb_user_t * accdb,
271+
fd_funk_txn_xid_t const * xid,
272+
fd_runtime_stack_t * runtime_stack ) {
273+
274+
/* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/target_bpf_v2.rs#L30-L33 */
275+
fd_tmp_account_t * program_account = &runtime_stack->bpf_migration.program_account;
276+
if( FD_UNLIKELY( !tmp_account_read( program_account, accdb, xid, program_address ) ) ) {
277+
/* CoreBpfMigrationError::AccountNotFound(*program_address) */
278+
return NULL;
279+
}
280+
281+
/* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/target_bpf_v2.rs#L35-L38 */
282+
if( FD_UNLIKELY( 0!=memcmp( program_account->meta.owner, &fd_solana_bpf_loader_program_id, FD_PUBKEY_FOOTPRINT ) ) ) {
283+
/* CoreBpfMigrationError::IncorrectOwner(*program_address) */
284+
return NULL;
285+
}
286+
287+
/* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/target_bpf_v2.rs#L40-L45 */
288+
if( FD_UNLIKELY( !program_account->meta.executable ) ) {
289+
/* CoreBpfMigrationError::ProgramAccountNotExecutable(*program_address) */
290+
return NULL;
291+
}
292+
293+
/* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/target_bpf_v2.rs#L47 */
294+
fd_pubkey_t program_data_address = get_program_data_address( program_address );
295+
296+
/* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/target_bpf_v2.rs#L49-L74 */
297+
ulong program_data_account_lamports = 0UL;
298+
do {
299+
fd_accdb_ro_t ro[1];
300+
int progdata_exists = !!fd_accdb_open_ro( accdb, ro, xid, &program_data_address );
301+
302+
/* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/target_bpf_v2.rs#L49-L74 */
303+
if( FD_LIKELY( allow_prefunded ) ) {
304+
/* The program data account should not exist, but a system
305+
account with funded lamports is acceptable.
306+
307+
https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/target_bpf_v2.rs#L50-L61 */
308+
if( FD_UNLIKELY( progdata_exists ) ) {
309+
if( FD_UNLIKELY( 0!=memcmp( fd_accdb_ref_owner( ro ), &fd_solana_system_program_id, FD_PUBKEY_FOOTPRINT ) ) ) {
310+
/* CoreBpfMigrationError::ProgramHasDataAccount(*program_address) */
311+
fd_accdb_close_ro( accdb, ro );
312+
return NULL;
313+
} else {
314+
program_data_account_lamports = fd_accdb_ref_lamports( ro );
315+
fd_accdb_close_ro( accdb, ro );
316+
}
317+
}
318+
} else {
319+
/* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/target_bpf_v2.rs#L62-L74 */
320+
if( FD_UNLIKELY( progdata_exists ) ) {
321+
/* CoreBpfMigrationError::ProgramHasDataAccount(*program_address) */
322+
fd_accdb_close_ro( accdb, ro );
323+
return NULL;
324+
}
325+
}
326+
} while(0);
327+
328+
*target_bpf_v2 = (target_builtin_t) {
329+
.program_account = program_account,
330+
.program_data_address = program_data_address,
331+
.program_data_account_lamports = program_data_account_lamports
332+
};
333+
return target_bpf_v2;
334+
}
335+
260336
/* https://github.com/anza-xyz/agave/blob/v3.1.7/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L51-L75 */
261337

262338
static fd_tmp_account_t *
@@ -624,3 +700,115 @@ fd_upgrade_core_bpf_program( fd_bank_t * bank,
624700

625701
fd_memset( &runtime_stack->bpf_migration, 0, sizeof(runtime_stack->bpf_migration) );
626702
}
703+
704+
/* Mimics upgrade_loader_v2_program_with_loader_v3_program().
705+
https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L402-L474 */
706+
void
707+
fd_upgrade_loader_v2_program_with_loader_v3_program( fd_bank_t * bank,
708+
fd_accdb_user_t * accdb,
709+
fd_funk_txn_xid_t const * xid,
710+
fd_runtime_stack_t * runtime_stack,
711+
fd_pubkey_t const * loader_v2_program_address,
712+
fd_pubkey_t const * source_buffer_address,
713+
int allow_prefunded,
714+
fd_capture_ctx_t * capture_ctx ) {
715+
716+
/* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L411-L412 */
717+
target_builtin_t target[1];
718+
if( FD_UNLIKELY( !target_bpf_v2_new_checked(
719+
target,
720+
loader_v2_program_address,
721+
allow_prefunded,
722+
accdb,
723+
xid,
724+
runtime_stack ) ) )
725+
return;
726+
727+
/* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L413 */
728+
fd_tmp_account_t * source = &runtime_stack->bpf_migration.source;
729+
if( FD_UNLIKELY( !source_buffer_new_checked( source, accdb, xid, source_buffer_address, NULL ) ) )
730+
return;
731+
732+
fd_rent_t const * rent = fd_bank_rent_query( bank );
733+
ulong const slot = fd_bank_slot_get ( bank );
734+
735+
/* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L416-L417 */
736+
fd_tmp_account_t * new_target_program = &runtime_stack->bpf_migration.new_target_program;
737+
if( FD_UNLIKELY( !new_target_program_account( new_target_program, target, rent ) ) )
738+
return;
739+
new_target_program->addr = *loader_v2_program_address;
740+
741+
/* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L419-L421 */
742+
fd_tmp_account_t * new_target_program_data = &runtime_stack->bpf_migration.new_target_program_data;
743+
if( FD_UNLIKELY( !new_target_program_data_account( new_target_program_data, source, NULL, rent, slot ) ) ) {
744+
return;
745+
}
746+
new_target_program_data->addr = target->program_data_address;
747+
748+
/* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L427-L435 */
749+
ulong old_data_sz;
750+
if( FD_UNLIKELY( fd_ulong_checked_add( target->program_account->data_sz, source->data_sz, &old_data_sz ) ) ) return;
751+
752+
/* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L432-L435 */
753+
ulong new_data_sz;
754+
if( FD_UNLIKELY( fd_ulong_checked_add( new_target_program->data_sz, new_target_program_data->data_sz, &new_data_sz ) ) ) return;
755+
756+
assert( new_target_program_data->data_sz>=PROGRAMDATA_METADATA_SIZE );
757+
758+
/* Validate the ELF. Agave calls directly_invoke_loader_v3_deploy,
759+
which validates the ELF and aborts the migration if the ELF is
760+
invalid.
761+
762+
We don't invalidate the program cache here, because upgrades are
763+
done at the epoch boundary, so the cache entry is invalidated
764+
automatically.
765+
766+
https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L437-L443 */
767+
do {
768+
uchar const * elf = (uchar const *)new_target_program_data->data + PROGRAMDATA_METADATA_SIZE;
769+
ulong elf_sz = new_target_program_data->data_sz - PROGRAMDATA_METADATA_SIZE;
770+
771+
fd_prog_versions_t versions = fd_prog_versions( fd_bank_features_query( bank ), slot );
772+
fd_sbpf_loader_config_t loader_config = {
773+
.sbpf_min_version = versions.min_sbpf_version,
774+
.sbpf_max_version = versions.max_sbpf_version,
775+
};
776+
fd_sbpf_elf_info_t elf_info[1];
777+
if( FD_UNLIKELY( fd_sbpf_elf_peek( elf_info, elf, elf_sz, &loader_config )!=FD_SBPF_ELF_SUCCESS ) ) {
778+
return;
779+
}
780+
} while(0);
781+
782+
/* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L451-L459 */
783+
ulong lamports_to_burn;
784+
if( FD_UNLIKELY( fd_ulong_checked_add( target->program_account->meta.lamports, source->meta.lamports, &lamports_to_burn ) ) ) return;
785+
if( FD_UNLIKELY( fd_ulong_checked_add( lamports_to_burn, target->program_data_account_lamports, &lamports_to_burn ) ) ) return;
786+
787+
ulong lamports_to_fund;
788+
if( FD_UNLIKELY( fd_ulong_checked_add( new_target_program->meta.lamports, new_target_program_data->meta.lamports, &lamports_to_fund ) ) ) return;
789+
790+
/* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L460 */
791+
if( FD_UNLIKELY( fd_update_capitalization( bank, lamports_to_burn, lamports_to_fund ) ) ) {
792+
FD_LOG_CRIT(( "invariant violation: capitalization overflow while upgrading Loader v2 program to Loader v3" ));
793+
}
794+
795+
/* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L462-L468 */
796+
tmp_account_store( new_target_program, accdb, xid, bank, capture_ctx );
797+
tmp_account_store( new_target_program_data, accdb, xid, bank, capture_ctx );
798+
799+
fd_tmp_account_t * empty = &runtime_stack->bpf_migration.empty;
800+
tmp_account_new( empty, 0UL );
801+
empty->addr = source->addr;
802+
tmp_account_store( empty, accdb, xid, bank, capture_ctx );
803+
804+
/* TODO: Agave updates "delta_off_chain", using these two fields,
805+
which is not consensus-critical so we don't update this in our
806+
migration code.
807+
808+
However, if we ever enable Firedancer-produced snapshots we will
809+
need to track this correctly. */
810+
(void)old_data_sz;
811+
(void)new_data_sz;
812+
813+
fd_memset( &runtime_stack->bpf_migration, 0, sizeof(runtime_stack->bpf_migration) );
814+
}

src/flamenco/runtime/fd_runtime.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,19 @@ fd_compute_and_apply_new_feature_activations( fd_bank_t * bank,
514514
if( FD_UNLIKELY( FD_FEATURE_JUST_ACTIVATED_BANK( bank, vote_state_v4 ) ) ) {
515515
fd_upgrade_core_bpf_program( bank, accdb, xid, runtime_stack, &fd_solana_stake_program_id, &fd_solana_stake_program_vote_state_v4_buffer_address, capture_ctx );
516516
}
517+
518+
/* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank.rs#L5703-L5716 */
519+
if( FD_UNLIKELY( FD_FEATURE_JUST_ACTIVATED_BANK( bank, replace_spl_token_with_p_token ) ) ) {
520+
fd_upgrade_loader_v2_program_with_loader_v3_program(
521+
bank,
522+
accdb,
523+
xid,
524+
runtime_stack,
525+
&fd_solana_spl_token_id,
526+
&fd_solana_ptoken_program_buffer_address,
527+
FD_FEATURE_ACTIVE_BANK( bank, relax_programdata_account_check_migration ),
528+
capture_ctx );
529+
}
517530
}
518531

519532
/* Starting a new epoch.

src/flamenco/runtime/fd_system_ids.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ const fd_pubkey_t fd_solana_feature_program_buffer_address = { .u
4444
const fd_pubkey_t fd_solana_stake_program_buffer_address = { .uc = { STAKE_PROG_BUFFER_ID } };
4545
const fd_pubkey_t fd_solana_stake_program_vote_state_v4_buffer_address = { .uc = { STAKE_PROG_VOTE_STATE_V4_BUFFER_ID } };
4646
const fd_pubkey_t fd_solana_slashing_program_buffer_address = { .uc = { SLASHING_PROG_BUFFER_ID } };
47+
const fd_pubkey_t fd_solana_ptoken_program_buffer_address = { .uc = { PTOKEN_PROG_BUFFER_ID } };
4748

4849
const fd_pubkey_t fd_solana_migration_authority = { .uc = { MIGRATION_AUTHORITY_ID } };
4950

src/flamenco/runtime/fd_system_ids.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ extern const fd_pubkey_t fd_solana_feature_program_buffer_address;
5353
extern const fd_pubkey_t fd_solana_stake_program_buffer_address;
5454
extern const fd_pubkey_t fd_solana_stake_program_vote_state_v4_buffer_address;
5555
extern const fd_pubkey_t fd_solana_slashing_program_buffer_address;
56+
extern const fd_pubkey_t fd_solana_ptoken_program_buffer_address;
5657

5758
/* BPF migration authority
5859
https://github.com/anza-xyz/agave/blob/v2.2.6/programs/bpf_loader/src/lib.rs#L399-L401 */

src/flamenco/runtime/fd_system_ids_pp.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,5 +85,7 @@
8585
0x0dU,0x79U,0xd4U,0x08U,0xa8U,0x96U,0xe6U,0x1aU,0xe2U,0x65U,0xf9U,0x8fU,0x23U,0xd4U,0x52U,0x64U
8686
#define SLASHING_PROG_BUFFER_ID 0x06U,0x68U,0x2eU,0x32U,0xc0U,0x4cU,0xe7U,0x71U,0xa4U,0x71U,0xc5U,0x09U,0xd0U,0xf0U,0xc6U,0xa8U, \
8787
0x30U,0x2bU,0x19U,0x20U,0x7bU,0xc3U,0x8aU,0x70U,0x77U,0x0aU,0xddU,0xc6U,0x16U,0x48U,0xb6U,0x3fU
88+
#define PTOKEN_PROG_BUFFER_ID 0x0cU,0x44U,0xe0U,0x0aU,0xa3U,0xddU,0x2cU,0xf3U,0xb5U,0x43U,0x4dU,0x54U,0x3fU,0x9eU,0xA7U,0xafU, \
89+
0x85U,0x76U,0x97U,0x50U,0xefU,0xc7U,0x5fU,0x16U,0xadU,0xe0U,0xadU,0x88U,0x59U,0xdfU,0xdcU,0x14U
8890
#define MIGRATION_AUTHORITY_ID 0x24U,0x47U,0x35U,0xd0U,0x66U,0xd1U,0x46U,0x66U,0x76U,0x6dU,0xb4U,0x43U,0xd0U,0xf2U,0xcaU,0xe8U, \
8991
0x0aU,0xcbU,0xb0U,0x23U,0x6fU,0x8cU,0x76U,0x21U,0xebU,0xeeU,0x3eU,0x0bU,0xa3U,0xceU,0x8dU,0xa3U

src/flamenco/runtime/program/fd_builtin_programs.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,17 @@ fd_upgrade_core_bpf_program( fd_bank_t * bank,
111111
fd_pubkey_t const * source_buffer_address,
112112
fd_capture_ctx_t * capture_ctx );
113113

114+
/* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L402-L408 */
115+
void
116+
fd_upgrade_loader_v2_program_with_loader_v3_program( fd_bank_t * bank,
117+
fd_accdb_user_t * accdb,
118+
fd_funk_txn_xid_t const * xid,
119+
fd_runtime_stack_t * runtime_stack,
120+
fd_pubkey_t const * loader_v2_program_address,
121+
fd_pubkey_t const * source_buffer_address,
122+
int allow_prefunded,
123+
fd_capture_ctx_t * capture_ctx );
124+
114125
FD_PROTOTYPES_END
115126

116127
#endif /* HEADER_fd_src_flamenco_runtime_program_fd_builtin_programs_h */

0 commit comments

Comments
 (0)