Add encoding and filters parameters to rpc Subscriptions (#11065)
* Plumb account configs and enable encoding * Enable filters for pubsub program accounts * Update docs
This commit is contained in:
		| @@ -5,7 +5,10 @@ use jsonrpc_core::{Error, ErrorCode, Result}; | ||||
| use jsonrpc_derive::rpc; | ||||
| use jsonrpc_pubsub::{typed::Subscriber, Session, SubscriptionId}; | ||||
| use solana_account_decoder::UiAccount; | ||||
| use solana_client::rpc_response::{Response as RpcResponse, RpcKeyedAccount, RpcSignatureResult}; | ||||
| use solana_client::{ | ||||
|     rpc_config::{RpcAccountInfoConfig, RpcProgramAccountsConfig}, | ||||
|     rpc_response::{Response as RpcResponse, RpcKeyedAccount, RpcSignatureResult}, | ||||
| }; | ||||
| #[cfg(test)] | ||||
| use solana_runtime::bank_forks::BankForks; | ||||
| use solana_sdk::{ | ||||
| @@ -38,7 +41,7 @@ pub trait RpcSolPubSub { | ||||
|         meta: Self::Metadata, | ||||
|         subscriber: Subscriber<RpcResponse<UiAccount>>, | ||||
|         pubkey_str: String, | ||||
|         commitment: Option<CommitmentConfig>, | ||||
|         config: Option<RpcAccountInfoConfig>, | ||||
|     ); | ||||
|  | ||||
|     // Unsubscribe from account notification subscription. | ||||
| @@ -62,7 +65,7 @@ pub trait RpcSolPubSub { | ||||
|         meta: Self::Metadata, | ||||
|         subscriber: Subscriber<RpcResponse<RpcKeyedAccount>>, | ||||
|         pubkey_str: String, | ||||
|         commitment: Option<CommitmentConfig>, | ||||
|         config: Option<RpcProgramAccountsConfig>, | ||||
|     ); | ||||
|  | ||||
|     // Unsubscribe from account notification subscription. | ||||
| @@ -173,7 +176,7 @@ impl RpcSolPubSub for RpcSolPubSubImpl { | ||||
|         _meta: Self::Metadata, | ||||
|         subscriber: Subscriber<RpcResponse<UiAccount>>, | ||||
|         pubkey_str: String, | ||||
|         commitment: Option<CommitmentConfig>, | ||||
|         config: Option<RpcAccountInfoConfig>, | ||||
|     ) { | ||||
|         match param::<Pubkey>(&pubkey_str, "pubkey") { | ||||
|             Ok(pubkey) => { | ||||
| @@ -181,7 +184,7 @@ impl RpcSolPubSub for RpcSolPubSubImpl { | ||||
|                 let sub_id = SubscriptionId::Number(id as u64); | ||||
|                 info!("account_subscribe: account={:?} id={:?}", pubkey, sub_id); | ||||
|                 self.subscriptions | ||||
|                     .add_account_subscription(pubkey, commitment, sub_id, subscriber) | ||||
|                     .add_account_subscription(pubkey, config, sub_id, subscriber) | ||||
|             } | ||||
|             Err(e) => subscriber.reject(e).unwrap(), | ||||
|         } | ||||
| @@ -209,7 +212,7 @@ impl RpcSolPubSub for RpcSolPubSubImpl { | ||||
|         _meta: Self::Metadata, | ||||
|         subscriber: Subscriber<RpcResponse<RpcKeyedAccount>>, | ||||
|         pubkey_str: String, | ||||
|         commitment: Option<CommitmentConfig>, | ||||
|         config: Option<RpcProgramAccountsConfig>, | ||||
|     ) { | ||||
|         match param::<Pubkey>(&pubkey_str, "pubkey") { | ||||
|             Ok(pubkey) => { | ||||
| @@ -217,7 +220,7 @@ impl RpcSolPubSub for RpcSolPubSubImpl { | ||||
|                 let sub_id = SubscriptionId::Number(id as u64); | ||||
|                 info!("program_subscribe: account={:?} id={:?}", pubkey, sub_id); | ||||
|                 self.subscriptions | ||||
|                     .add_program_subscription(pubkey, commitment, sub_id, subscriber) | ||||
|                     .add_program_subscription(pubkey, config, sub_id, subscriber) | ||||
|             } | ||||
|             Err(e) => subscriber.reject(e).unwrap(), | ||||
|         } | ||||
| @@ -355,6 +358,7 @@ mod tests { | ||||
|     use jsonrpc_core::{futures::sync::mpsc, Response}; | ||||
|     use jsonrpc_pubsub::{PubSubHandler, Session}; | ||||
|     use serial_test_derive::serial; | ||||
|     use solana_account_decoder::{parse_account_data::parse_account_data, UiAccountEncoding}; | ||||
|     use solana_budget_program::{self, budget_instruction}; | ||||
|     use solana_runtime::{ | ||||
|         bank::Bank, | ||||
| @@ -370,7 +374,7 @@ mod tests { | ||||
|         message::Message, | ||||
|         pubkey::Pubkey, | ||||
|         signature::{Keypair, Signer}, | ||||
|         system_program, system_transaction, | ||||
|         system_instruction, system_program, system_transaction, | ||||
|         transaction::{self, Transaction}, | ||||
|     }; | ||||
|     use solana_vote_program::vote_transaction; | ||||
| @@ -537,7 +541,10 @@ mod tests { | ||||
|             session, | ||||
|             subscriber, | ||||
|             contract_state.pubkey().to_string(), | ||||
|             Some(CommitmentConfig::recent()), | ||||
|             Some(RpcAccountInfoConfig { | ||||
|                 commitment: Some(CommitmentConfig::recent()), | ||||
|                 encoding: None, | ||||
|             }), | ||||
|         ); | ||||
|  | ||||
|         let tx = system_transaction::transfer(&alice, &contract_funds.pubkey(), 51, blockhash); | ||||
| @@ -610,6 +617,88 @@ mod tests { | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     #[serial] | ||||
|     fn test_account_subscribe_with_encoding() { | ||||
|         let GenesisConfigInfo { | ||||
|             genesis_config, | ||||
|             mint_keypair: alice, | ||||
|             .. | ||||
|         } = create_genesis_config(10_000); | ||||
|  | ||||
|         let nonce_account = Keypair::new(); | ||||
|         let bank = Bank::new(&genesis_config); | ||||
|         let blockhash = bank.last_blockhash(); | ||||
|         let bank_forks = Arc::new(RwLock::new(BankForks::new(bank))); | ||||
|         let bank0 = bank_forks.read().unwrap().get(0).unwrap().clone(); | ||||
|         let bank1 = Bank::new_from_parent(&bank0, &Pubkey::default(), 1); | ||||
|         bank_forks.write().unwrap().insert(bank1); | ||||
|  | ||||
|         let rpc = RpcSolPubSubImpl { | ||||
|             subscriptions: Arc::new(RpcSubscriptions::new( | ||||
|                 &Arc::new(AtomicBool::new(false)), | ||||
|                 bank_forks.clone(), | ||||
|                 Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests_with_slots( | ||||
|                     1, 1, | ||||
|                 ))), | ||||
|             )), | ||||
|             uid: Arc::new(atomic::AtomicUsize::default()), | ||||
|         }; | ||||
|         let session = create_session(); | ||||
|         let (subscriber, _id_receiver, receiver) = Subscriber::new_test("accountNotification"); | ||||
|         rpc.account_subscribe( | ||||
|             session, | ||||
|             subscriber, | ||||
|             nonce_account.pubkey().to_string(), | ||||
|             Some(RpcAccountInfoConfig { | ||||
|                 commitment: Some(CommitmentConfig::recent()), | ||||
|                 encoding: Some(UiAccountEncoding::JsonParsed), | ||||
|             }), | ||||
|         ); | ||||
|  | ||||
|         let ixs = system_instruction::create_nonce_account( | ||||
|             &alice.pubkey(), | ||||
|             &nonce_account.pubkey(), | ||||
|             &alice.pubkey(), | ||||
|             100, | ||||
|         ); | ||||
|         let message = Message::new(&ixs, Some(&alice.pubkey())); | ||||
|         let tx = Transaction::new(&[&alice, &nonce_account], message, blockhash); | ||||
|         process_transaction_and_notify(&bank_forks, &tx, &rpc.subscriptions, 1).unwrap(); | ||||
|         sleep(Duration::from_millis(200)); | ||||
|  | ||||
|         // Test signature confirmation notification #1 | ||||
|         let expected_data = bank_forks | ||||
|             .read() | ||||
|             .unwrap() | ||||
|             .get(1) | ||||
|             .unwrap() | ||||
|             .get_account(&nonce_account.pubkey()) | ||||
|             .unwrap() | ||||
|             .data; | ||||
|         let expected_data = parse_account_data(&system_program::id(), &expected_data).unwrap(); | ||||
|         let expected = json!({ | ||||
|            "jsonrpc": "2.0", | ||||
|            "method": "accountNotification", | ||||
|            "params": { | ||||
|                "result": { | ||||
|                    "context": { "slot": 1 }, | ||||
|                    "value": { | ||||
|                        "owner": system_program::id().to_string(), | ||||
|                        "lamports": 100, | ||||
|                        "data": expected_data, | ||||
|                        "executable": false, | ||||
|                        "rentEpoch": 1, | ||||
|                    }, | ||||
|                }, | ||||
|                "subscription": 0, | ||||
|            } | ||||
|         }); | ||||
|  | ||||
|         let (response, _) = robust_poll_or_panic(receiver); | ||||
|         assert_eq!(serde_json::to_string(&expected).unwrap(), response); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     #[serial] | ||||
|     fn test_account_unsubscribe() { | ||||
| @@ -675,7 +764,10 @@ mod tests { | ||||
|             session, | ||||
|             subscriber, | ||||
|             bob.pubkey().to_string(), | ||||
|             Some(CommitmentConfig::root()), | ||||
|             Some(RpcAccountInfoConfig { | ||||
|                 commitment: Some(CommitmentConfig::root()), | ||||
|                 encoding: None, | ||||
|             }), | ||||
|         ); | ||||
|  | ||||
|         let tx = system_transaction::transfer(&alice, &bob.pubkey(), 100, blockhash); | ||||
| @@ -721,7 +813,10 @@ mod tests { | ||||
|             session, | ||||
|             subscriber, | ||||
|             bob.pubkey().to_string(), | ||||
|             Some(CommitmentConfig::root()), | ||||
|             Some(RpcAccountInfoConfig { | ||||
|                 commitment: Some(CommitmentConfig::root()), | ||||
|                 encoding: None, | ||||
|             }), | ||||
|         ); | ||||
|  | ||||
|         let tx = system_transaction::transfer(&alice, &bob.pubkey(), 100, blockhash); | ||||
|   | ||||
| @@ -8,8 +8,10 @@ use jsonrpc_pubsub::{ | ||||
| }; | ||||
| use serde::Serialize; | ||||
| use solana_account_decoder::{UiAccount, UiAccountEncoding}; | ||||
| use solana_client::rpc_response::{ | ||||
|     Response, RpcKeyedAccount, RpcResponseContext, RpcSignatureResult, | ||||
| use solana_client::{ | ||||
|     rpc_config::{RpcAccountInfoConfig, RpcProgramAccountsConfig}, | ||||
|     rpc_filter::RpcFilterType, | ||||
|     rpc_response::{Response, RpcKeyedAccount, RpcResponseContext, RpcSignatureResult}, | ||||
| }; | ||||
| use solana_runtime::{ | ||||
|     bank::Bank, | ||||
| @@ -79,29 +81,44 @@ impl std::fmt::Debug for NotificationEntry { | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct SubscriptionData<S> { | ||||
| struct SubscriptionData<S, T> { | ||||
|     sink: Sink<S>, | ||||
|     commitment: CommitmentConfig, | ||||
|     last_notified_slot: RwLock<Slot>, | ||||
|     config: Option<T>, | ||||
| } | ||||
| type RpcAccountSubscriptions = | ||||
|     RwLock<HashMap<Pubkey, HashMap<SubscriptionId, SubscriptionData<Response<UiAccount>>>>>; | ||||
| type RpcProgramSubscriptions = | ||||
|     RwLock<HashMap<Pubkey, HashMap<SubscriptionId, SubscriptionData<Response<RpcKeyedAccount>>>>>; | ||||
| #[derive(Default, Clone)] | ||||
| struct ProgramConfig { | ||||
|     filters: Vec<RpcFilterType>, | ||||
|     encoding: Option<UiAccountEncoding>, | ||||
| } | ||||
| type RpcAccountSubscriptions = RwLock< | ||||
|     HashMap< | ||||
|         Pubkey, | ||||
|         HashMap<SubscriptionId, SubscriptionData<Response<UiAccount>, UiAccountEncoding>>, | ||||
|     >, | ||||
| >; | ||||
| type RpcProgramSubscriptions = RwLock< | ||||
|     HashMap< | ||||
|         Pubkey, | ||||
|         HashMap<SubscriptionId, SubscriptionData<Response<RpcKeyedAccount>, ProgramConfig>>, | ||||
|     >, | ||||
| >; | ||||
| type RpcSignatureSubscriptions = RwLock< | ||||
|     HashMap<Signature, HashMap<SubscriptionId, SubscriptionData<Response<RpcSignatureResult>>>>, | ||||
|     HashMap<Signature, HashMap<SubscriptionId, SubscriptionData<Response<RpcSignatureResult>, ()>>>, | ||||
| >; | ||||
| type RpcSlotSubscriptions = RwLock<HashMap<SubscriptionId, Sink<SlotInfo>>>; | ||||
| type RpcVoteSubscriptions = RwLock<HashMap<SubscriptionId, Sink<RpcVote>>>; | ||||
| type RpcRootSubscriptions = RwLock<HashMap<SubscriptionId, Sink<Slot>>>; | ||||
|  | ||||
| fn add_subscription<K, S>( | ||||
|     subscriptions: &mut HashMap<K, HashMap<SubscriptionId, SubscriptionData<S>>>, | ||||
| fn add_subscription<K, S, T>( | ||||
|     subscriptions: &mut HashMap<K, HashMap<SubscriptionId, SubscriptionData<S, T>>>, | ||||
|     hashmap_key: K, | ||||
|     commitment: Option<CommitmentConfig>, | ||||
|     sub_id: SubscriptionId, | ||||
|     subscriber: Subscriber<S>, | ||||
|     last_notified_slot: Slot, | ||||
|     config: Option<T>, | ||||
| ) where | ||||
|     K: Eq + Hash, | ||||
|     S: Clone, | ||||
| @@ -112,6 +129,7 @@ fn add_subscription<K, S>( | ||||
|         sink, | ||||
|         commitment, | ||||
|         last_notified_slot: RwLock::new(last_notified_slot), | ||||
|         config, | ||||
|     }; | ||||
|     if let Some(current_hashmap) = subscriptions.get_mut(&hashmap_key) { | ||||
|         current_hashmap.insert(sub_id, subscription_data); | ||||
| @@ -122,8 +140,8 @@ fn add_subscription<K, S>( | ||||
|     subscriptions.insert(hashmap_key, hashmap); | ||||
| } | ||||
|  | ||||
| fn remove_subscription<K, S>( | ||||
|     subscriptions: &mut HashMap<K, HashMap<SubscriptionId, SubscriptionData<S>>>, | ||||
| fn remove_subscription<K, S, T>( | ||||
|     subscriptions: &mut HashMap<K, HashMap<SubscriptionId, SubscriptionData<S, T>>>, | ||||
|     sub_id: &SubscriptionId, | ||||
| ) -> bool | ||||
| where | ||||
| @@ -145,8 +163,8 @@ where | ||||
| } | ||||
|  | ||||
| #[allow(clippy::type_complexity)] | ||||
| fn check_commitment_and_notify<K, S, B, F, X>( | ||||
|     subscriptions: &HashMap<K, HashMap<SubscriptionId, SubscriptionData<Response<S>>>>, | ||||
| fn check_commitment_and_notify<K, S, B, F, X, T>( | ||||
|     subscriptions: &HashMap<K, HashMap<SubscriptionId, SubscriptionData<Response<S>, T>>>, | ||||
|     hashmap_key: &K, | ||||
|     bank_forks: &Arc<RwLock<BankForks>>, | ||||
|     commitment_slots: &CommitmentSlots, | ||||
| @@ -158,8 +176,9 @@ where | ||||
|     K: Eq + Hash + Clone + Copy, | ||||
|     S: Clone + Serialize, | ||||
|     B: Fn(&Bank, &K) -> X, | ||||
|     F: Fn(X, Slot) -> (Box<dyn Iterator<Item = S>>, Slot), | ||||
|     F: Fn(X, Slot, Option<T>) -> (Box<dyn Iterator<Item = S>>, Slot), | ||||
|     X: Clone + Serialize + Default, | ||||
|     T: Clone, | ||||
| { | ||||
|     let mut notified_set: HashSet<SubscriptionId> = HashSet::new(); | ||||
|     if let Some(hashmap) = subscriptions.get(hashmap_key) { | ||||
| @@ -169,6 +188,7 @@ where | ||||
|                 sink, | ||||
|                 commitment, | ||||
|                 last_notified_slot, | ||||
|                 config, | ||||
|             }, | ||||
|         ) in hashmap.iter() | ||||
|         { | ||||
| @@ -188,7 +208,8 @@ where | ||||
|                     .unwrap_or_default() | ||||
|             }; | ||||
|             let mut w_last_notified_slot = last_notified_slot.write().unwrap(); | ||||
|             let (filter_results, result_slot) = filter_results(results, *w_last_notified_slot); | ||||
|             let (filter_results, result_slot) = | ||||
|                 filter_results(results, *w_last_notified_slot, config.as_ref().cloned()); | ||||
|             for result in filter_results { | ||||
|                 notifier.notify( | ||||
|                     Response { | ||||
| @@ -220,16 +241,15 @@ impl RpcNotifier { | ||||
| fn filter_account_result( | ||||
|     result: Option<(Account, Slot)>, | ||||
|     last_notified_slot: Slot, | ||||
|     encoding: Option<UiAccountEncoding>, | ||||
| ) -> (Box<dyn Iterator<Item = UiAccount>>, Slot) { | ||||
|     if let Some((account, fork)) = result { | ||||
|         // If fork < last_notified_slot this means that we last notified for a fork | ||||
|         // and should notify that the account state has been reverted. | ||||
|         if fork != last_notified_slot { | ||||
|             let encoding = encoding.unwrap_or(UiAccountEncoding::Binary); | ||||
|             return ( | ||||
|                 Box::new(iter::once(UiAccount::encode( | ||||
|                     account, | ||||
|                     UiAccountEncoding::Binary, | ||||
|                 ))), | ||||
|                 Box::new(iter::once(UiAccount::encode(account, encoding))), | ||||
|                 fork, | ||||
|             ); | ||||
|         } | ||||
| @@ -240,6 +260,7 @@ fn filter_account_result( | ||||
| fn filter_signature_result( | ||||
|     result: Option<transaction::Result<()>>, | ||||
|     last_notified_slot: Slot, | ||||
|     _config: Option<()>, | ||||
| ) -> (Box<dyn Iterator<Item = RpcSignatureResult>>, Slot) { | ||||
|     ( | ||||
|         Box::new( | ||||
| @@ -254,14 +275,24 @@ fn filter_signature_result( | ||||
| fn filter_program_results( | ||||
|     accounts: Vec<(Pubkey, Account)>, | ||||
|     last_notified_slot: Slot, | ||||
|     config: Option<ProgramConfig>, | ||||
| ) -> (Box<dyn Iterator<Item = RpcKeyedAccount>>, Slot) { | ||||
|     let config = config.unwrap_or_default(); | ||||
|     let encoding = config.encoding.unwrap_or(UiAccountEncoding::Binary); | ||||
|     let filters = config.filters; | ||||
|     ( | ||||
|         Box::new( | ||||
|             accounts | ||||
|                 .into_iter() | ||||
|                 .map(|(pubkey, account)| RpcKeyedAccount { | ||||
|                 .filter(move |(_, account)| { | ||||
|                     filters.iter().all(|filter_type| match filter_type { | ||||
|                         RpcFilterType::DataSize(size) => account.data.len() as u64 == *size, | ||||
|                         RpcFilterType::Memcmp(compare) => compare.bytes_match(&account.data), | ||||
|                     }) | ||||
|                 }) | ||||
|                 .map(move |(pubkey, account)| RpcKeyedAccount { | ||||
|                     pubkey: pubkey.to_string(), | ||||
|                     account: UiAccount::encode(account, UiAccountEncoding::Binary), | ||||
|                     account: UiAccount::encode(account, encoding.clone()), | ||||
|                 }), | ||||
|         ), | ||||
|         last_notified_slot, | ||||
| @@ -448,11 +479,13 @@ impl RpcSubscriptions { | ||||
|     pub fn add_account_subscription( | ||||
|         &self, | ||||
|         pubkey: Pubkey, | ||||
|         commitment: Option<CommitmentConfig>, | ||||
|         config: Option<RpcAccountInfoConfig>, | ||||
|         sub_id: SubscriptionId, | ||||
|         subscriber: Subscriber<Response<UiAccount>>, | ||||
|     ) { | ||||
|         let commitment_level = commitment | ||||
|         let config = config.unwrap_or_default(); | ||||
|         let commitment_level = config | ||||
|             .commitment | ||||
|             .unwrap_or_else(CommitmentConfig::single) | ||||
|             .commitment; | ||||
|         let slot = match commitment_level { | ||||
| @@ -498,10 +531,11 @@ impl RpcSubscriptions { | ||||
|         add_subscription( | ||||
|             &mut subscriptions, | ||||
|             pubkey, | ||||
|             commitment, | ||||
|             config.commitment, | ||||
|             sub_id, | ||||
|             subscriber, | ||||
|             last_notified_slot, | ||||
|             config.encoding, | ||||
|         ); | ||||
|     } | ||||
|  | ||||
| @@ -522,11 +556,14 @@ impl RpcSubscriptions { | ||||
|     pub fn add_program_subscription( | ||||
|         &self, | ||||
|         program_id: Pubkey, | ||||
|         commitment: Option<CommitmentConfig>, | ||||
|         config: Option<RpcProgramAccountsConfig>, | ||||
|         sub_id: SubscriptionId, | ||||
|         subscriber: Subscriber<Response<RpcKeyedAccount>>, | ||||
|     ) { | ||||
|         let commitment_level = commitment | ||||
|         let config = config.unwrap_or_default(); | ||||
|         let commitment_level = config | ||||
|             .account_config | ||||
|             .commitment | ||||
|             .unwrap_or_else(CommitmentConfig::recent) | ||||
|             .commitment; | ||||
|         let mut subscriptions = if commitment_level == CommitmentLevel::SingleGossip { | ||||
| @@ -540,10 +577,14 @@ impl RpcSubscriptions { | ||||
|         add_subscription( | ||||
|             &mut subscriptions, | ||||
|             program_id, | ||||
|             commitment, | ||||
|             config.account_config.commitment, | ||||
|             sub_id, | ||||
|             subscriber, | ||||
|             0, // last_notified_slot is not utilized for program subscriptions | ||||
|             Some(ProgramConfig { | ||||
|                 filters: config.filters.unwrap_or_default(), | ||||
|                 encoding: config.account_config.encoding, | ||||
|             }), | ||||
|         ); | ||||
|     } | ||||
|  | ||||
| @@ -586,6 +627,7 @@ impl RpcSubscriptions { | ||||
|             sub_id, | ||||
|             subscriber, | ||||
|             0, // last_notified_slot is not utilized for signature subscriptions | ||||
|             None, | ||||
|         ); | ||||
|     } | ||||
|  | ||||
| @@ -964,7 +1006,10 @@ pub(crate) mod tests { | ||||
|         ); | ||||
|         subscriptions.add_account_subscription( | ||||
|             alice.pubkey(), | ||||
|             Some(CommitmentConfig::recent()), | ||||
|             Some(RpcAccountInfoConfig { | ||||
|                 commitment: Some(CommitmentConfig::recent()), | ||||
|                 encoding: None, | ||||
|             }), | ||||
|             sub_id.clone(), | ||||
|             subscriber, | ||||
|         ); | ||||
| @@ -1368,7 +1413,7 @@ pub(crate) mod tests { | ||||
|     #[test] | ||||
|     #[serial] | ||||
|     fn test_add_and_remove_subscription() { | ||||
|         let mut subscriptions: HashMap<u64, HashMap<SubscriptionId, SubscriptionData<()>>> = | ||||
|         let mut subscriptions: HashMap<u64, HashMap<SubscriptionId, SubscriptionData<(), ()>>> = | ||||
|             HashMap::new(); | ||||
|  | ||||
|         let num_keys = 5; | ||||
| @@ -1376,7 +1421,7 @@ pub(crate) mod tests { | ||||
|             let (subscriber, _id_receiver, _transport_receiver) = | ||||
|                 Subscriber::new_test("notification"); | ||||
|             let sub_id = SubscriptionId::Number(key); | ||||
|             add_subscription(&mut subscriptions, key, None, sub_id, subscriber, 0); | ||||
|             add_subscription(&mut subscriptions, key, None, sub_id, subscriber, 0, None); | ||||
|         } | ||||
|  | ||||
|         // Add another subscription to the "0" key | ||||
| @@ -1389,6 +1434,7 @@ pub(crate) mod tests { | ||||
|             extra_sub_id.clone(), | ||||
|             subscriber, | ||||
|             0, | ||||
|             None, | ||||
|         ); | ||||
|  | ||||
|         assert_eq!(subscriptions.len(), num_keys as usize); | ||||
| @@ -1444,7 +1490,10 @@ pub(crate) mod tests { | ||||
|         let sub_id0 = SubscriptionId::Number(0 as u64); | ||||
|         subscriptions.add_account_subscription( | ||||
|             alice.pubkey(), | ||||
|             Some(CommitmentConfig::single_gossip()), | ||||
|             Some(RpcAccountInfoConfig { | ||||
|                 commitment: Some(CommitmentConfig::single_gossip()), | ||||
|                 encoding: None, | ||||
|             }), | ||||
|             sub_id0.clone(), | ||||
|             subscriber0, | ||||
|         ); | ||||
| @@ -1509,7 +1558,10 @@ pub(crate) mod tests { | ||||
|         let sub_id1 = SubscriptionId::Number(1 as u64); | ||||
|         subscriptions.add_account_subscription( | ||||
|             alice.pubkey(), | ||||
|             Some(CommitmentConfig::single_gossip()), | ||||
|             Some(RpcAccountInfoConfig { | ||||
|                 commitment: Some(CommitmentConfig::single_gossip()), | ||||
|                 encoding: None, | ||||
|             }), | ||||
|             sub_id1.clone(), | ||||
|             subscriber1, | ||||
|         ); | ||||
|   | ||||
| @@ -1256,7 +1256,10 @@ Subscribe to an account to receive notifications when the lamports or data for a | ||||
| #### Parameters: | ||||
|  | ||||
| - `<string>` - account Pubkey, as base-58 encoded string | ||||
| - `<object>` - (optional) Configuration object containing the following optional fields: | ||||
|   - `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) | ||||
|   - (optional) `encoding: <string>` - encoding for Account data, either "binary" or jsonParsed". If parameter not provided, the default encoding is binary. | ||||
|     Parsed-JSON encoding attempts to use program-specific state parsers to return more human-readable and explicit account state data. If parsed-JSON is requested but a parser cannot be found, the field falls back to binary encoding, detectable when the `data` field is type `<string>`. | ||||
|  | ||||
| #### Results: | ||||
|  | ||||
| @@ -1270,13 +1273,16 @@ Subscribe to an account to receive notifications when the lamports or data for a | ||||
|  | ||||
| {"jsonrpc":"2.0", "id":1, "method":"accountSubscribe", "params":["CM78CPUeXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNH12", {"commitment": "single"}]} | ||||
|  | ||||
| {"jsonrpc":"2.0", "id":1, "method":"accountSubscribe", "params":["CM78CPUeXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNH12", {"encoding":"jsonParsed"}]} | ||||
|  | ||||
| // Result | ||||
| {"jsonrpc": "2.0","result": 0,"id": 1} | ||||
| {"jsonrpc": "2.0","result": 23784,"id": 1} | ||||
| ``` | ||||
|  | ||||
| #### Notification Format: | ||||
|  | ||||
| ```bash | ||||
| // Binary encoding | ||||
| { | ||||
|   "jsonrpc": "2.0", | ||||
|   "method": "accountNotification", | ||||
| @@ -1286,10 +1292,41 @@ Subscribe to an account to receive notifications when the lamports or data for a | ||||
|         "slot": 5199307 | ||||
|       }, | ||||
|       "value": { | ||||
|         "data": "9qRxMDwy1ntDhBBoiy4Na9uDLbRTSzUS989mpwz", | ||||
|         "data": "11116bv5nS2h3y12kD1yUKeMZvGcKLSjQgX6BeV7u1FrjeJcKfsHPXHRDEHrBesJhZyqnnq9qJeUuF7WHxiuLuL5twc38w2TXNLxnDbjmuR", | ||||
|         "executable": false, | ||||
|         "lamports": 33594, | ||||
|         "owner": "H9oaJujXETwkmjyweuqKPFtk2no4SumoU9A3hi3dC8U6", | ||||
|         "owner": "11111111111111111111111111111111", | ||||
|         "rentEpoch": 635 | ||||
|       } | ||||
|     }, | ||||
|     "subscription": 23784 | ||||
|   } | ||||
| } | ||||
|  | ||||
| // Parsed-JSON encoding | ||||
| { | ||||
|   "jsonrpc": "2.0", | ||||
|   "method": "accountNotification", | ||||
|   "params": { | ||||
|     "result": { | ||||
|       "context": { | ||||
|         "slot": 5199307 | ||||
|       }, | ||||
|       "value": { | ||||
|         "data": { | ||||
|            "nonce": { | ||||
|               "initialized": { | ||||
|                  "authority": "Bbqg1M4YVVfbhEzwA9SpC9FhsaG83YMTYoR4a8oTDLX", | ||||
|                  "blockhash": "LUaQTmM7WbMRiATdMMHaRGakPtCkc2GHtH57STKXs6k", | ||||
|                  "feeCalculator": { | ||||
|                     "lamportsPerSignature": 5000 | ||||
|                  } | ||||
|               } | ||||
|            } | ||||
|         }, | ||||
|         "executable": false, | ||||
|         "lamports": 33594, | ||||
|         "owner": "11111111111111111111111111111111", | ||||
|         "rentEpoch": 635 | ||||
|       } | ||||
|     }, | ||||
| @@ -1327,7 +1364,11 @@ Subscribe to a program to receive notifications when the lamports or data for a | ||||
| #### Parameters: | ||||
|  | ||||
| - `<string>` - program_id Pubkey, as base-58 encoded string | ||||
| - `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) | ||||
| - `<object>` - (optional) Configuration object containing the following optional fields: | ||||
|   - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) | ||||
|   - (optional) `encoding: <string>` - encoding for Account data, either "binary" or jsonParsed". If parameter not provided, the default encoding is binary. | ||||
|     Parsed-JSON encoding attempts to use program-specific state parsers to return more human-readable and explicit account state data. If parsed-JSON is requested but a parser cannot be found, the field falls back to binary encoding, detectable when the `data` field is type `<string>`. | ||||
|   - (optional) `filters: <array>` - filter results using various [filter objects](jsonrpc-api.md#filters); account must meet all filter criteria to be included in results | ||||
|  | ||||
| #### Results: | ||||
|  | ||||
| @@ -1337,17 +1378,22 @@ Subscribe to a program to receive notifications when the lamports or data for a | ||||
|  | ||||
| ```bash | ||||
| // Request | ||||
| {"jsonrpc":"2.0", "id":1, "method":"programSubscribe", "params":["7BwE8yitxiWkD8jVPFvPmV7rs2Znzi4NHzJGLu2dzpUq"]} | ||||
| {"jsonrpc":"2.0", "id":1, "method":"programSubscribe", "params":["11111111111111111111111111111111"]} | ||||
|  | ||||
| {"jsonrpc":"2.0", "id":1, "method":"programSubscribe", "params":["7BwE8yitxiWkD8jVPFvPmV7rs2Znzi4NHzJGLu2dzpUq", {"commitment": "single"}]} | ||||
| {"jsonrpc":"2.0", "id":1, "method":"programSubscribe", "params":["11111111111111111111111111111111", {"commitment": "single"}]} | ||||
|  | ||||
| {"jsonrpc":"2.0", "id":1, "method":"programSubscribe", "params":["11111111111111111111111111111111", {"encoding":"jsonParsed"}]} | ||||
|  | ||||
| {"jsonrpc":"2.0", "id":1, "method":"programSubscribe", "params":["11111111111111111111111111111111", {"filters":[{"dataSize":80}]}]} | ||||
|  | ||||
| // Result | ||||
| {"jsonrpc": "2.0","result": 0,"id": 1} | ||||
| {"jsonrpc": "2.0","result": 24040,"id": 1} | ||||
| ``` | ||||
|  | ||||
| #### Notification Format: | ||||
|  | ||||
| ```bash | ||||
| // Binary encoding | ||||
| { | ||||
|   "jsonrpc": "2.0", | ||||
|   "method": "programNotification", | ||||
| @@ -1359,10 +1405,44 @@ Subscribe to a program to receive notifications when the lamports or data for a | ||||
|       "value": { | ||||
|         "pubkey": "H4vnBqifaSACnKa7acsxstsY1iV1bvJNxsCY7enrd1hq" | ||||
|         "account": { | ||||
|           "data": "9qRxMDwy1ntDhBBoiy4Na9uDLbRTSzUS989m", | ||||
|           "data": "11116bv5nS2h3y12kD1yUKeMZvGcKLSjQgX6BeV7u1FrjeJcKfsHPXHRDEHrBesJhZyqnnq9qJeUuF7WHxiuLuL5twc38w2TXNLxnDbjmuR", | ||||
|           "executable": false, | ||||
|           "lamports": 33594, | ||||
|           "owner": "7BwE8yitxiWkD8jVPFvPmV7rs2Znzi4NHzJGLu2dzpUq", | ||||
|           "owner": "11111111111111111111111111111111", | ||||
|           "rentEpoch": 636 | ||||
|         }, | ||||
|       } | ||||
|     }, | ||||
|     "subscription": 24040 | ||||
|   } | ||||
| } | ||||
|  | ||||
| // Parsed-JSON encoding | ||||
| { | ||||
|   "jsonrpc": "2.0", | ||||
|   "method": "programNotification", | ||||
|   "params": { | ||||
|     "result": { | ||||
|       "context": { | ||||
|         "slot": 5208469 | ||||
|       }, | ||||
|       "value": { | ||||
|         "pubkey": "H4vnBqifaSACnKa7acsxstsY1iV1bvJNxsCY7enrd1hq" | ||||
|         "account": { | ||||
|           "data": { | ||||
|              "nonce": { | ||||
|                 "initialized": { | ||||
|                    "authority": "Bbqg1M4YVVfbhEzwA9SpC9FhsaG83YMTYoR4a8oTDLX", | ||||
|                    "blockhash": "LUaQTmM7WbMRiATdMMHaRGakPtCkc2GHtH57STKXs6k", | ||||
|                    "feeCalculator": { | ||||
|                       "lamportsPerSignature": 5000 | ||||
|                    } | ||||
|                 } | ||||
|              } | ||||
|           }, | ||||
|           "executable": false, | ||||
|           "lamports": 33594, | ||||
|           "owner": "11111111111111111111111111111111", | ||||
|           "rentEpoch": 636 | ||||
|         }, | ||||
|       } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user