@@ -2292,16 +2292,38 @@ impl Full for SurfpoolFullRpc {
22922292 & self ,
22932293 meta : Self :: Metadata ,
22942294 encoded : String ,
2295- _config : Option < RpcContextConfig > , // TODO: use config
2295+ config : Option < RpcContextConfig > ,
22962296 ) -> Result < RpcResponse < Option < u64 > > > {
22972297 let ( _, message) =
22982298 decode_and_deserialize :: < VersionedMessage > ( encoded, TransactionBinaryEncoding :: Base64 ) ?;
22992299
2300- meta. with_svm_reader ( |svm_reader| RpcResponse {
2301- context : RpcResponseContext :: new ( svm_reader. get_latest_absolute_slot ( ) ) ,
2300+ let RpcContextConfig {
2301+ commitment,
2302+ min_context_slot,
2303+ } = config. unwrap_or_default ( ) ;
2304+ let min_ctx_slot = min_context_slot. unwrap_or_default ( ) ;
2305+
2306+ let svm_locker = meta. get_svm_locker ( ) ?;
2307+
2308+ let slot = if let Some ( commitment_config) = commitment {
2309+ svm_locker. get_slot_for_commitment ( & commitment_config)
2310+ } else {
2311+ svm_locker. get_latest_absolute_slot ( )
2312+ } ;
2313+
2314+ if let Some ( min_slot) = min_context_slot
2315+ && slot < min_slot
2316+ {
2317+ return Err ( RpcCustomError :: MinContextSlotNotReached {
2318+ context_slot : min_ctx_slot,
2319+ }
2320+ . into ( ) ) ;
2321+ }
2322+
2323+ Ok ( RpcResponse {
2324+ context : RpcResponseContext :: new ( slot) ,
23022325 value : Some ( ( message. header ( ) . num_required_signatures as u64 ) * 5000 ) ,
23032326 } )
2304- . map_err ( Into :: into)
23052327 }
23062328
23072329 fn get_stake_minimum_delegation (
@@ -2509,6 +2531,7 @@ mod tests {
25092531 use std:: thread:: JoinHandle ;
25102532
25112533 use base64:: { Engine , prelude:: BASE64_STANDARD } ;
2534+ use bincode:: Options ;
25122535 use crossbeam_channel:: Receiver ;
25132536 use solana_account_decoder:: { UiAccount , UiAccountData , UiAccountEncoding } ;
25142537 use solana_client:: rpc_config:: RpcSimulateTransactionAccountsConfig ;
@@ -2521,7 +2544,10 @@ mod tests {
25212544 } ;
25222545 use solana_pubkey:: Pubkey ;
25232546 use solana_signer:: Signer ;
2524- use solana_system_interface:: { instruction as system_instruction, program as system_program} ;
2547+ use solana_system_interface:: {
2548+ instruction:: { self as system_instruction, transfer} ,
2549+ program as system_program,
2550+ } ;
25252551 use solana_transaction:: {
25262552 Transaction ,
25272553 versioned:: { Legacy , TransactionVersion } ,
@@ -2645,6 +2671,115 @@ mod tests {
26452671 }
26462672 }
26472673
2674+ #[ tokio:: test( flavor = "multi_thread" ) ]
2675+ async fn test_get_fee_for_message ( ) {
2676+ let setup = TestSetup :: new ( SurfpoolFullRpc ) ;
2677+ let runloop_context = setup. context ;
2678+ let rpc_server = setup. rpc ;
2679+ let payer = Keypair :: new ( ) ;
2680+ let recipient = Pubkey :: new_unique ( ) ;
2681+ let lamports_to_send = 5 * LAMPORTS_PER_SOL ;
2682+ let commitment_config_to_use = CommitmentConfig :: confirmed ( ) ;
2683+
2684+ let wrong_comm_min_ctx_slot = runloop_context
2685+ . svm_locker
2686+ . get_slot_for_commitment ( & commitment_config_to_use)
2687+ + 10 ;
2688+
2689+ let wrong_min_slot = runloop_context. svm_locker . get_latest_absolute_slot ( ) + 10 ;
2690+ let rpc_ctx_config_with_wrong_commitment = RpcContextConfig {
2691+ commitment : Some ( commitment_config_to_use) ,
2692+ min_context_slot : Some ( wrong_comm_min_ctx_slot) ,
2693+ } ;
2694+ let rpc_ctx_config_with_wrong_min_slot = RpcContextConfig {
2695+ commitment : None ,
2696+ min_context_slot : Some ( wrong_min_slot) ,
2697+ } ;
2698+
2699+ let instruction = transfer ( & payer. pubkey ( ) , & recipient, lamports_to_send) ;
2700+
2701+ let latest_blockhash = runloop_context
2702+ . svm_locker
2703+ . with_svm_reader ( |svm| svm. latest_blockhash ( ) ) ;
2704+ let message = solana_message:: Message :: new_with_blockhash (
2705+ & [ instruction] ,
2706+ Some ( & payer. pubkey ( ) ) ,
2707+ & latest_blockhash,
2708+ ) ;
2709+ let num_required_signatures = message. header . num_required_signatures as u64 ;
2710+ let transaction =
2711+ VersionedTransaction :: try_new ( VersionedMessage :: Legacy ( message) , & [ & payer] ) . unwrap ( ) ;
2712+
2713+ let message_bytes = bincode:: options ( )
2714+ . with_fixint_encoding ( )
2715+ . serialize ( & transaction. message )
2716+ . expect ( "message serialization" ) ;
2717+ let encoded_message = base64:: engine:: general_purpose:: STANDARD . encode ( & message_bytes) ;
2718+
2719+ let get_fee_with_correct_config_pass_result = rpc_server. get_fee_for_message (
2720+ Some ( runloop_context. clone ( ) ) ,
2721+ encoded_message. clone ( ) ,
2722+ None ,
2723+ ) ;
2724+
2725+ assert ! (
2726+ get_fee_with_correct_config_pass_result. is_ok( ) ,
2727+ "Expected get_fee_for_message to pass with correct configs"
2728+ ) ;
2729+ assert_eq ! (
2730+ get_fee_with_correct_config_pass_result
2731+ . unwrap( )
2732+ . value
2733+ . unwrap( ) ,
2734+ ( num_required_signatures as u64 ) * 5_000 ,
2735+ "Invalid return value"
2736+ ) ;
2737+
2738+ let get_fee_with_wrong_commitment_fail_result = rpc_server. get_fee_for_message (
2739+ Some ( runloop_context. clone ( ) ) ,
2740+ encoded_message. clone ( ) ,
2741+ Some ( rpc_ctx_config_with_wrong_commitment) ,
2742+ ) ;
2743+
2744+ let wrong_comm_expected_err: Result < ( ) > = Result :: Err (
2745+ RpcCustomError :: MinContextSlotNotReached {
2746+ context_slot : wrong_comm_min_ctx_slot,
2747+ }
2748+ . into ( ) ,
2749+ ) ;
2750+
2751+ assert ! (
2752+ get_fee_with_wrong_commitment_fail_result. is_err( ) ,
2753+ "expected this txn to fail when min_ctx_slot > slot_for_commitment"
2754+ ) ;
2755+
2756+ assert_eq ! (
2757+ get_fee_with_wrong_commitment_fail_result. err( ) . unwrap( ) ,
2758+ wrong_comm_expected_err. err( ) . unwrap( )
2759+ ) ;
2760+
2761+ let get_fee_with_wrong_mint_slot_fail_result = rpc_server. get_fee_for_message (
2762+ Some ( runloop_context. clone ( ) ) ,
2763+ encoded_message,
2764+ Some ( rpc_ctx_config_with_wrong_min_slot) ,
2765+ ) ;
2766+
2767+ let wrong_min_slot_expected_err: Result < ( ) > = Result :: Err (
2768+ RpcCustomError :: MinContextSlotNotReached {
2769+ context_slot : wrong_min_slot,
2770+ }
2771+ . into ( ) ,
2772+ ) ;
2773+ assert ! (
2774+ get_fee_with_wrong_mint_slot_fail_result. is_err( ) ,
2775+ "expected this txn to fail when min_ctx_slot > absolute_latest_slot"
2776+ ) ;
2777+ assert_eq ! (
2778+ get_fee_with_wrong_mint_slot_fail_result. err( ) . unwrap( ) ,
2779+ wrong_min_slot_expected_err. err( ) . unwrap( )
2780+ ) ;
2781+ }
2782+
26482783 #[ tokio:: test( flavor = "multi_thread" ) ]
26492784 async fn test_get_signature_statuses ( ) {
26502785 let pks = ( 0 ..10 ) . map ( |_| Pubkey :: new_unique ( ) ) ;
0 commit comments