diff --git a/src/flamenco/features/fd_features_generated.c b/src/flamenco/features/fd_features_generated.c index 38a1429156..79f2b75de2 100644 --- a/src/flamenco/features/fd_features_generated.c +++ b/src/flamenco/features/fd_features_generated.c @@ -1289,6 +1289,12 @@ fd_feature_id_t const ids[] = { .name = "migrate_address_lookup_table_program_to_core_bpf", .cleaned_up = {UINT_MAX, UINT_MAX, UINT_MAX} }, + { .index = offsetof(fd_features_t, enable_get_epoch_stake_syscall)>>3, + .id = {"\x64\x88\xa2\xd0\x5a\xcc\xbb\xae\xa0\x4e\xa8\xaf\xeb\x15\xfb\x71\xa7\x5b\x27\x71\x96\x6c\x2f\x05\x0d\xfe\xf3\x44\xbb\x07\x3b\x21"}, + /* 7mScTYkJXsbdrcwTQRs7oeCSXoJm4WjzBsRyf8bCU3Np */ + .name = "enable_get_epoch_stake_syscall", + .cleaned_up = {UINT_MAX, UINT_MAX, UINT_MAX} }, + { .index = ULONG_MAX } }; @@ -1513,6 +1519,7 @@ fd_feature_id_query( ulong prefix ) { case 0xf46b1f18665c4236: return &ids[ 210 ]; case 0xa9a90df1904da912: return &ids[ 211 ]; case 0x2434a84be5b684a5: return &ids[ 212 ]; + case 0xaebbcc5ad0a28864: return &ids[ 213 ]; default: break; } @@ -1734,5 +1741,6 @@ FD_STATIC_ASSERT( offsetof( fd_features_t, enable_sbpf_v3_deployment_and_executi FD_STATIC_ASSERT( offsetof( fd_features_t, migrate_feature_gate_program_to_core_bpf )>>3==210UL, layout ); FD_STATIC_ASSERT( offsetof( fd_features_t, migrate_config_program_to_core_bpf )>>3==211UL, layout ); FD_STATIC_ASSERT( offsetof( fd_features_t, migrate_address_lookup_table_program_to_core_bpf )>>3==212UL, layout ); +FD_STATIC_ASSERT( offsetof( fd_features_t, enable_get_epoch_stake_syscall )>>3==213UL, layout ); FD_STATIC_ASSERT( sizeof( fd_features_t )>>3==FD_FEATURE_ID_CNT, layout ); diff --git a/src/flamenco/features/fd_features_generated.h b/src/flamenco/features/fd_features_generated.h index b1e95f8da1..69e437b52b 100644 --- a/src/flamenco/features/fd_features_generated.h +++ b/src/flamenco/features/fd_features_generated.h @@ -6,7 +6,7 @@ /* FEATURE_ID_CNT is the number of features in ids */ -#define FD_FEATURE_ID_CNT (213UL) +#define FD_FEATURE_ID_CNT (214UL) union fd_features { @@ -226,6 +226,7 @@ union fd_features { /* 0xf46b1f18665c4236 */ ulong migrate_feature_gate_program_to_core_bpf; /* 0xa9a90df1904da912 */ ulong migrate_config_program_to_core_bpf; /* 0x2434a84be5b684a5 */ ulong migrate_address_lookup_table_program_to_core_bpf; + /* 0xaebbcc5ad0a28864 */ ulong enable_get_epoch_stake_syscall; }; }; diff --git a/src/flamenco/features/feature_map.json b/src/flamenco/features/feature_map.json index 2146cfc3f4..4fd7ffb099 100644 --- a/src/flamenco/features/feature_map.json +++ b/src/flamenco/features/feature_map.json @@ -211,5 +211,6 @@ {"name":"enable_sbpf_v3_deployment_and_execution", "pubkey": "C8XZNs1bfzaiT3YDeXZJ7G5swQWQv7tVzDnCxtHvnSpw"}, {"name":"migrate_feature_gate_program_to_core_bpf","pubkey":"4eohviozzEeivk1y9UbrnekbAFMDQyJz5JjA9Y6gyvky"}, {"name":"migrate_config_program_to_core_bpf","pubkey":"2Fr57nzzkLYXW695UdDxDeR5fhnZWSttZeZYemrnpGFV"}, - {"name":"migrate_address_lookup_table_program_to_core_bpf","pubkey":"C97eKZygrkU4JxJsZdjgbUY7iQR7rKTr4NyDWo2E5pRm"} + {"name":"migrate_address_lookup_table_program_to_core_bpf","pubkey":"C97eKZygrkU4JxJsZdjgbUY7iQR7rKTr4NyDWo2E5pRm"}, + {"name":"enable_get_epoch_stake_syscall","pubkey":"7mScTYkJXsbdrcwTQRs7oeCSXoJm4WjzBsRyf8bCU3Np"} ] diff --git a/src/flamenco/runtime/context/fd_exec_epoch_ctx.c b/src/flamenco/runtime/context/fd_exec_epoch_ctx.c index e39192a792..9202effc3b 100644 --- a/src/flamenco/runtime/context/fd_exec_epoch_ctx.c +++ b/src/flamenco/runtime/context/fd_exec_epoch_ctx.c @@ -267,6 +267,7 @@ void fd_exec_epoch_ctx_from_prev( fd_exec_epoch_ctx_t * self, fd_exec_epoch_ctx_t * prev ) { fd_memcpy( &self->features, &prev->features, sizeof(fd_features_t) ); self->bank_hash_cmp = prev->bank_hash_cmp; + self->total_epoch_stake = 0UL; fd_epoch_bank_t * old_epoch_bank = fd_exec_epoch_ctx_epoch_bank( prev ); fd_epoch_bank_t * new_epoch_bank = fd_exec_epoch_ctx_bank_mem_setup( self ); diff --git a/src/flamenco/runtime/context/fd_exec_epoch_ctx.h b/src/flamenco/runtime/context/fd_exec_epoch_ctx.h index 085a6c2947..9a1133923d 100644 --- a/src/flamenco/runtime/context/fd_exec_epoch_ctx.h +++ b/src/flamenco/runtime/context/fd_exec_epoch_ctx.h @@ -32,6 +32,8 @@ struct __attribute__((aligned(64UL))) fd_exec_epoch_ctx { fd_epoch_bank_t epoch_bank; fd_bank_hash_cmp_t * bank_hash_cmp; + + ulong total_epoch_stake; }; #define FD_EXEC_EPOCH_CTX_ALIGN (4096UL) diff --git a/src/flamenco/runtime/fd_runtime.c b/src/flamenco/runtime/fd_runtime.c index 87826d8e4d..da06006bc7 100644 --- a/src/flamenco/runtime/fd_runtime.c +++ b/src/flamenco/runtime/fd_runtime.c @@ -3997,6 +3997,7 @@ FD_SCRATCH_SCOPE_BEGIN { // Update the epoch bank vote_accounts with the latest values from the slot bank // FIXME: resize the vote_accounts_pool if necessary + ulong total_epoch_stake = 0UL; for ( fd_vote_accounts_pair_t_mapnode_t * n = fd_vote_accounts_pair_t_map_minimum( slot_ctx->slot_bank.vote_account_keys.vote_accounts_pool, slot_ctx->slot_bank.vote_account_keys.vote_accounts_root ); @@ -4008,17 +4009,19 @@ FD_SCRATCH_SCOPE_BEGIN { fd_memcpy( &key.elem.key, &n->elem.key, FD_PUBKEY_FOOTPRINT ); fd_vote_accounts_pair_t_mapnode_t * epoch_cache_node = fd_vote_accounts_pair_t_map_find( stakes->vote_accounts.vote_accounts_pool, stakes->vote_accounts.vote_accounts_root, &key ); if( epoch_cache_node == NULL ) { - fd_vote_accounts_pair_t_mapnode_t * new_entry = fd_vote_accounts_pair_t_map_acquire( stakes->vote_accounts.vote_accounts_pool ); + epoch_cache_node = fd_vote_accounts_pair_t_map_acquire( stakes->vote_accounts.vote_accounts_pool ); - fd_memcpy(&new_entry->elem.key, &n->elem.key, sizeof(fd_pubkey_t)); - fd_memcpy(&new_entry->elem.stake, &n->elem.stake, sizeof(ulong)); - fd_memcpy(&new_entry->elem.value, &n->elem.value, sizeof(fd_solana_account_t)); + fd_memcpy(&epoch_cache_node->elem.key, &n->elem.key, sizeof(fd_pubkey_t)); + fd_memcpy(&epoch_cache_node->elem.stake, &n->elem.stake, sizeof(ulong)); + fd_memcpy(&epoch_cache_node->elem.value, &n->elem.value, sizeof(fd_solana_account_t)); - fd_vote_accounts_pair_t_map_insert( stakes->vote_accounts.vote_accounts_pool, &stakes->vote_accounts.vote_accounts_root, new_entry ); + fd_vote_accounts_pair_t_map_insert( stakes->vote_accounts.vote_accounts_pool, &stakes->vote_accounts.vote_accounts_root, epoch_cache_node ); } else { epoch_cache_node->elem.stake = n->elem.stake; } + total_epoch_stake += epoch_cache_node->elem.stake; } + slot_ctx->epoch_ctx->total_epoch_stake = total_epoch_stake; fd_bincode_destroy_ctx_t destroy_slot = {.valloc = slot_ctx->valloc}; fd_vote_accounts_destroy( &slot_ctx->slot_bank.vote_account_keys, &destroy_slot ); diff --git a/src/flamenco/runtime/program/fd_vote_program.c b/src/flamenco/runtime/program/fd_vote_program.c index 5a4a06a1d8..1ceb033551 100644 --- a/src/flamenco/runtime/program/fd_vote_program.c +++ b/src/flamenco/runtime/program/fd_vote_program.c @@ -1933,8 +1933,8 @@ do_process_vote_state_update( fd_vote_state_t * vote_state, } // ?? -static ulong -query_pubkey_stake( fd_pubkey_t const * pubkey, fd_vote_accounts_t const * vote_accounts ) { +ulong +fd_query_pubkey_stake( fd_pubkey_t const * pubkey, fd_vote_accounts_t const * vote_accounts ) { fd_vote_accounts_pair_t_mapnode_t key = { 0 }; key.elem.key = *pubkey; fd_vote_accounts_pair_t_mapnode_t * vote_node = fd_vote_accounts_pair_t_map_find( @@ -1965,7 +1965,7 @@ process_vote_state_update( ulong vote_acct_idx, lockout->slot, &vote_state_update->hash, 0, - query_pubkey_stake( vote_account->pubkey, + fd_query_pubkey_stake( vote_account->pubkey, &ctx->epoch_ctx->epoch_bank.stakes.vote_accounts ) ); if( FD_LIKELY( vote_state_update->has_root ) ) { diff --git a/src/flamenco/runtime/program/fd_vote_program.h b/src/flamenco/runtime/program/fd_vote_program.h index 622ea2aed1..219e8765ee 100644 --- a/src/flamenco/runtime/program/fd_vote_program.h +++ b/src/flamenco/runtime/program/fd_vote_program.h @@ -46,6 +46,11 @@ FD_PROTOTYPES_BEGIN int fd_vote_program_execute( fd_exec_instr_ctx_t * ctx ); +/* Queries the delegated stake amount for the given vote account pubkey, + given the vote accounts map. Returns 0 if nonexistent. */ +ulong +fd_query_pubkey_stake( fd_pubkey_t const * pubkey, fd_vote_accounts_t const * vote_accounts ); + int fd_vote_get_state( fd_borrowed_account_t const * self, fd_valloc_t valloc, diff --git a/src/flamenco/runtime/tests/run_ledger_tests_all.txt b/src/flamenco/runtime/tests/run_ledger_tests_all.txt index e9a8cb6c04..70d8a15c50 100644 --- a/src/flamenco/runtime/tests/run_ledger_tests_all.txt +++ b/src/flamenco/runtime/tests/run_ledger_tests_all.txt @@ -75,3 +75,4 @@ src/flamenco/runtime/tests/run_ledger_test.sh -l testnet-302734641 -s snapshot-3 src/flamenco/runtime/tests/run_ledger_test.sh -l migrate-feature-program -s snapshot-500-Cz6iotFK7kNcGTpyUZeX4CHbT2uff2uzVW8RXXysBnVk.tar.zst -p 50 -y 16 -m 5000000 -e 675 -c 2.1.0 -o 4eohviozzEeivk1y9UbrnekbAFMDQyJz5JjA9Y6gyvky src/flamenco/runtime/tests/run_ledger_test.sh -l migrate-config-program -s snapshot-500-HCg5AHVPC6JAqZo4p6iMcDmo586ZjkxxPpxfAwCHqroy.tar.zst -p 50 -y 16 -m 5000000 -e 675 -c 2.1.0 -o 2Fr57nzzkLYXW695UdDxDeR5fhnZWSttZeZYemrnpGFV src/flamenco/runtime/tests/run_ledger_test.sh -l per-stake-history-new-rate-activation-epoch -s snapshot-50-47DbuHpi6gXmZKQ1FUSAAfkgnuWrkfTAtx3Hk5Gqsu5W.tar.zst -p 50 -y 16 -m 5000000 -e 675 -c 2.1.0 +src/flamenco/runtime/tests/run_ledger_test.sh -l enable-get-epoch-stake-syscall -s snapshot-50-FfJQ3ePJx2s5BDymDoiLVRHDHj5doYNQBzyrqus8LocL.tar.zst -p 50 -y 16 -m 5000000 -e 686 -c 2.1.0 -o 7mScTYkJXsbdrcwTQRs7oeCSXoJm4WjzBsRyf8bCU3Np diff --git a/src/flamenco/vm/syscall/fd_vm_syscall.c b/src/flamenco/vm/syscall/fd_vm_syscall.c index 2721d30696..ab2ab55b55 100644 --- a/src/flamenco/vm/syscall/fd_vm_syscall.c +++ b/src/flamenco/vm/syscall/fd_vm_syscall.c @@ -28,6 +28,7 @@ fd_vm_syscall_register_slot( fd_sbpf_syscalls_t * syscalls, int enable_alt_bn128_compression_syscall = 0; int enable_last_restart_slot_syscall = 0; int enable_get_sysvar_syscall = 0; + int enable_get_epoch_stake_syscall = 0; int disable_fees_sysvar = 0; @@ -40,6 +41,7 @@ fd_vm_syscall_register_slot( fd_sbpf_syscalls_t * syscalls, enable_alt_bn128_compression_syscall = FD_FEATURE_ACTIVE( slot_ctx, enable_alt_bn128_compression_syscall ); enable_last_restart_slot_syscall = FD_FEATURE_ACTIVE( slot_ctx, last_restart_slot_sysvar ); enable_get_sysvar_syscall = FD_FEATURE_ACTIVE( slot_ctx, get_sysvar_syscall_enabled ); + enable_get_epoch_stake_syscall = FD_FEATURE_ACTIVE( slot_ctx, enable_get_epoch_stake_syscall ); disable_fees_sysvar = !FD_FEATURE_ACTIVE( slot_ctx, disable_fees_sysvar ); @@ -52,6 +54,7 @@ fd_vm_syscall_register_slot( fd_sbpf_syscalls_t * syscalls, enable_alt_bn128_compression_syscall = 1; enable_last_restart_slot_syscall = 1; enable_get_sysvar_syscall = 1; + enable_get_epoch_stake_syscall = 1; } @@ -110,6 +113,10 @@ fd_vm_syscall_register_slot( fd_sbpf_syscalls_t * syscalls, REGISTER( "sol_get_sysvar", fd_vm_syscall_sol_get_sysvar ); } + if( enable_get_epoch_stake_syscall ) { + REGISTER( "sol_get_epoch_stake", fd_vm_syscall_sol_get_epoch_stake ); + } + REGISTER( "sol_memcpy_", fd_vm_syscall_sol_memcpy ); REGISTER( "sol_memmove_", fd_vm_syscall_sol_memmove ); REGISTER( "sol_memcmp_", fd_vm_syscall_sol_memcmp ); diff --git a/src/flamenco/vm/syscall/fd_vm_syscall.h b/src/flamenco/vm/syscall/fd_vm_syscall.h index 4e34644457..36fe171b6d 100644 --- a/src/flamenco/vm/syscall/fd_vm_syscall.h +++ b/src/flamenco/vm/syscall/fd_vm_syscall.h @@ -16,6 +16,27 @@ https://github.com/solana-labs/solana/blob/2afde1b028ed4593da5b6c735729d8994c4bfac6/sdk/program/src/pubkey.rs#L19 */ #define FD_VM_PDA_SEED_MEM_MAX (32UL) +/* PYTHON3 CODE FOR COMPUTING THE SYSCALL MURMUR3 HASH (e.g. sol_get_epoch_stake): + ``` + import mmh3 + import ctypes + + def compute_murmur3_hash(input_string): + # Compute the Murmur3 hash of the input string + hash_value = mmh3.hash(input_string) + # Convert the hash value to a 32-bit unsigned integer + u32_hash_value = ctypes.c_uint32(hash_value).value + return u32_hash_value + + input_string = b"sol_get_epoch_stake" + hash_value = compute_murmur3_hash(input_string) + print(f"The Murmur3 hash of '{input_string}' as u32 is: {hex(hash_value)}") + + Output: + The Murmur3 hash of 'b'sol_get_epoch_stake'' as u32 is: 0x5be92f4a + ``` +*/ + /* https://github.com/solana-labs/solana/blob/2afde1b028ed4593da5b6c735729d8994c4bfac6/sdk/program/src/pubkey.rs#L22 */ /* FIXME: CONSIDER NOT PREFIXING SYSCALLS WITH SOL_? (OR MAYBE THIS @@ -478,12 +499,44 @@ FD_VM_SYSCALL_DECL( sol_get_last_restart_slot_sysvar ); - *_ret = 2 if sysvar id is not in {clock,schedule,rewards,rent,slot hashes,stake history,last restart slot} OR sysvar account does not exist. - *_ret = 1 if [offset,offset+sz) is outside of sysvar data buffer. - = *_ret = 0 if success. + - *_ret = 0 if success. On return, sz bytes of appropriate offset sysvar data will be copied into haddr belonging to out_vaddr. */ FD_VM_SYSCALL_DECL( sol_get_sysvar ); +/* syscall(5be92f4a) "sol_get_epoch_stake" + + Gets a vote account's delegated stake for the current epoch, or the total active stake + on the cluster for the current epoch. + + Inputs: + + r1 - var_addr, vote pubkey VM pointer + r2 - ignored + r3 - ignored + r4 - ignored + r5 - ignored + + Return: + + FD_VM_ERR_SIGCALL: the VM is not running within the Solana runtime. + *_ret unchanged. vm->cu unchanged. + + FD_VM_ERR_SIGCOST: insufficient compute budget. *_ret unchanged. + vm->cu==0. + + FD_VM_ERR_SIGSEGV: bad var_addr. _ret unchanged. vm->cu unchanged. + + FD_VM_ERR_ABORT: offset+sz overflow. *_ret unchanged. + + FD_VM_SUCCESS: success. vm->cu decremented and vm->cu>0. + - *_ret = 0 if success. + - if var_addr == 0, returns the total active stake on the cluster. Otherwise, + returns the vote account's delegated stake. */ + +FD_VM_SYSCALL_DECL( sol_get_epoch_stake ); + /* syscall(85532d94) "sol_get_stack_height" Inputs: diff --git a/src/flamenco/vm/syscall/fd_vm_syscall_runtime.c b/src/flamenco/vm/syscall/fd_vm_syscall_runtime.c index e89bafdc5c..3262249041 100644 --- a/src/flamenco/vm/syscall/fd_vm_syscall_runtime.c +++ b/src/flamenco/vm/syscall/fd_vm_syscall_runtime.c @@ -1,4 +1,5 @@ #include "fd_vm_syscall.h" +#include "../../runtime/program/fd_vote_program.h" #include "../../runtime/sysvar/fd_sysvar.h" #include "../../runtime/sysvar/fd_sysvar_clock.h" #include "../../runtime/sysvar/fd_sysvar_epoch_schedule.h" @@ -248,6 +249,40 @@ fd_vm_syscall_sol_get_sysvar( /**/ void * _vm, return FD_VM_SUCCESS; } +/* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/mod.rs#L2043-L2118 */ +int +fd_vm_syscall_sol_get_epoch_stake( /**/ void * _vm, + /**/ ulong var_addr, + FD_PARAM_UNUSED ulong r2, + FD_PARAM_UNUSED ulong r3, + FD_PARAM_UNUSED ulong r4, + FD_PARAM_UNUSED ulong r5, + /**/ ulong * _ret ) { + fd_vm_t * vm = (fd_vm_t *)_vm; + + /* Var addr of 0 returns the total active stake on the cluster. + + https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/mod.rs#L2057-L2075 */ + if( FD_UNLIKELY( var_addr==0UL ) ) { + /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/mod.rs#L2065-L2066 */ + FD_VM_CU_UPDATE( vm, FD_VM_SYSCALL_BASE_COST ); + + /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/mod.rs#L2074 */ + *_ret = vm->instr_ctx->epoch_ctx->total_epoch_stake; + return FD_VM_SUCCESS; + } + + /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/mod.rs#L2083-L2091 */ + FD_VM_CU_UPDATE( vm, fd_ulong_sat_add( FD_VM_MEM_OP_BASE_COST, + fd_ulong_sat_add( FD_VM_SYSCALL_BASE_COST, FD_PUBKEY_FOOTPRINT / FD_VM_CPI_BYTES_PER_UNIT ) ) ); + + /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/mod.rs#L2103-L2104 */ + const fd_pubkey_t * vote_address = FD_VM_MEM_HADDR_LD( vm, var_addr, FD_VM_ALIGN_RUST_PUBKEY, FD_PUBKEY_FOOTPRINT ); + *_ret = fd_query_pubkey_stake( vote_address, &vm->instr_ctx->epoch_ctx->epoch_bank.stakes.vote_accounts ); + + return FD_VM_SUCCESS; +} + int fd_vm_syscall_sol_get_stack_height( /**/ void * _vm, FD_PARAM_UNUSED ulong r1,