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:
Tyera Eulberg
2020-07-23 13:38:28 -06:00
committed by GitHub
parent ca6480a8ac
commit c90de8978d
3 changed files with 281 additions and 54 deletions

View File

@ -5,7 +5,10 @@ use jsonrpc_core::{Error, ErrorCode, Result};
use jsonrpc_derive::rpc; use jsonrpc_derive::rpc;
use jsonrpc_pubsub::{typed::Subscriber, Session, SubscriptionId}; use jsonrpc_pubsub::{typed::Subscriber, Session, SubscriptionId};
use solana_account_decoder::UiAccount; 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)] #[cfg(test)]
use solana_runtime::bank_forks::BankForks; use solana_runtime::bank_forks::BankForks;
use solana_sdk::{ use solana_sdk::{
@ -38,7 +41,7 @@ pub trait RpcSolPubSub {
meta: Self::Metadata, meta: Self::Metadata,
subscriber: Subscriber<RpcResponse<UiAccount>>, subscriber: Subscriber<RpcResponse<UiAccount>>,
pubkey_str: String, pubkey_str: String,
commitment: Option<CommitmentConfig>, config: Option<RpcAccountInfoConfig>,
); );
// Unsubscribe from account notification subscription. // Unsubscribe from account notification subscription.
@ -62,7 +65,7 @@ pub trait RpcSolPubSub {
meta: Self::Metadata, meta: Self::Metadata,
subscriber: Subscriber<RpcResponse<RpcKeyedAccount>>, subscriber: Subscriber<RpcResponse<RpcKeyedAccount>>,
pubkey_str: String, pubkey_str: String,
commitment: Option<CommitmentConfig>, config: Option<RpcProgramAccountsConfig>,
); );
// Unsubscribe from account notification subscription. // Unsubscribe from account notification subscription.
@ -173,7 +176,7 @@ impl RpcSolPubSub for RpcSolPubSubImpl {
_meta: Self::Metadata, _meta: Self::Metadata,
subscriber: Subscriber<RpcResponse<UiAccount>>, subscriber: Subscriber<RpcResponse<UiAccount>>,
pubkey_str: String, pubkey_str: String,
commitment: Option<CommitmentConfig>, config: Option<RpcAccountInfoConfig>,
) { ) {
match param::<Pubkey>(&pubkey_str, "pubkey") { match param::<Pubkey>(&pubkey_str, "pubkey") {
Ok(pubkey) => { Ok(pubkey) => {
@ -181,7 +184,7 @@ impl RpcSolPubSub for RpcSolPubSubImpl {
let sub_id = SubscriptionId::Number(id as u64); let sub_id = SubscriptionId::Number(id as u64);
info!("account_subscribe: account={:?} id={:?}", pubkey, sub_id); info!("account_subscribe: account={:?} id={:?}", pubkey, sub_id);
self.subscriptions self.subscriptions
.add_account_subscription(pubkey, commitment, sub_id, subscriber) .add_account_subscription(pubkey, config, sub_id, subscriber)
} }
Err(e) => subscriber.reject(e).unwrap(), Err(e) => subscriber.reject(e).unwrap(),
} }
@ -209,7 +212,7 @@ impl RpcSolPubSub for RpcSolPubSubImpl {
_meta: Self::Metadata, _meta: Self::Metadata,
subscriber: Subscriber<RpcResponse<RpcKeyedAccount>>, subscriber: Subscriber<RpcResponse<RpcKeyedAccount>>,
pubkey_str: String, pubkey_str: String,
commitment: Option<CommitmentConfig>, config: Option<RpcProgramAccountsConfig>,
) { ) {
match param::<Pubkey>(&pubkey_str, "pubkey") { match param::<Pubkey>(&pubkey_str, "pubkey") {
Ok(pubkey) => { Ok(pubkey) => {
@ -217,7 +220,7 @@ impl RpcSolPubSub for RpcSolPubSubImpl {
let sub_id = SubscriptionId::Number(id as u64); let sub_id = SubscriptionId::Number(id as u64);
info!("program_subscribe: account={:?} id={:?}", pubkey, sub_id); info!("program_subscribe: account={:?} id={:?}", pubkey, sub_id);
self.subscriptions self.subscriptions
.add_program_subscription(pubkey, commitment, sub_id, subscriber) .add_program_subscription(pubkey, config, sub_id, subscriber)
} }
Err(e) => subscriber.reject(e).unwrap(), Err(e) => subscriber.reject(e).unwrap(),
} }
@ -355,6 +358,7 @@ mod tests {
use jsonrpc_core::{futures::sync::mpsc, Response}; use jsonrpc_core::{futures::sync::mpsc, Response};
use jsonrpc_pubsub::{PubSubHandler, Session}; use jsonrpc_pubsub::{PubSubHandler, Session};
use serial_test_derive::serial; 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_budget_program::{self, budget_instruction};
use solana_runtime::{ use solana_runtime::{
bank::Bank, bank::Bank,
@ -370,7 +374,7 @@ mod tests {
message::Message, message::Message,
pubkey::Pubkey, pubkey::Pubkey,
signature::{Keypair, Signer}, signature::{Keypair, Signer},
system_program, system_transaction, system_instruction, system_program, system_transaction,
transaction::{self, Transaction}, transaction::{self, Transaction},
}; };
use solana_vote_program::vote_transaction; use solana_vote_program::vote_transaction;
@ -537,7 +541,10 @@ mod tests {
session, session,
subscriber, subscriber,
contract_state.pubkey().to_string(), 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); 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] #[test]
#[serial] #[serial]
fn test_account_unsubscribe() { fn test_account_unsubscribe() {
@ -675,7 +764,10 @@ mod tests {
session, session,
subscriber, subscriber,
bob.pubkey().to_string(), 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); let tx = system_transaction::transfer(&alice, &bob.pubkey(), 100, blockhash);
@ -721,7 +813,10 @@ mod tests {
session, session,
subscriber, subscriber,
bob.pubkey().to_string(), 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); let tx = system_transaction::transfer(&alice, &bob.pubkey(), 100, blockhash);

View File

@ -8,8 +8,10 @@ use jsonrpc_pubsub::{
}; };
use serde::Serialize; use serde::Serialize;
use solana_account_decoder::{UiAccount, UiAccountEncoding}; use solana_account_decoder::{UiAccount, UiAccountEncoding};
use solana_client::rpc_response::{ use solana_client::{
Response, RpcKeyedAccount, RpcResponseContext, RpcSignatureResult, rpc_config::{RpcAccountInfoConfig, RpcProgramAccountsConfig},
rpc_filter::RpcFilterType,
rpc_response::{Response, RpcKeyedAccount, RpcResponseContext, RpcSignatureResult},
}; };
use solana_runtime::{ use solana_runtime::{
bank::Bank, bank::Bank,
@ -79,29 +81,44 @@ impl std::fmt::Debug for NotificationEntry {
} }
} }
struct SubscriptionData<S> { struct SubscriptionData<S, T> {
sink: Sink<S>, sink: Sink<S>,
commitment: CommitmentConfig, commitment: CommitmentConfig,
last_notified_slot: RwLock<Slot>, last_notified_slot: RwLock<Slot>,
config: Option<T>,
} }
type RpcAccountSubscriptions = #[derive(Default, Clone)]
RwLock<HashMap<Pubkey, HashMap<SubscriptionId, SubscriptionData<Response<UiAccount>>>>>; struct ProgramConfig {
type RpcProgramSubscriptions = filters: Vec<RpcFilterType>,
RwLock<HashMap<Pubkey, HashMap<SubscriptionId, SubscriptionData<Response<RpcKeyedAccount>>>>>; 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< 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 RpcSlotSubscriptions = RwLock<HashMap<SubscriptionId, Sink<SlotInfo>>>;
type RpcVoteSubscriptions = RwLock<HashMap<SubscriptionId, Sink<RpcVote>>>; type RpcVoteSubscriptions = RwLock<HashMap<SubscriptionId, Sink<RpcVote>>>;
type RpcRootSubscriptions = RwLock<HashMap<SubscriptionId, Sink<Slot>>>; type RpcRootSubscriptions = RwLock<HashMap<SubscriptionId, Sink<Slot>>>;
fn add_subscription<K, S>( fn add_subscription<K, S, T>(
subscriptions: &mut HashMap<K, HashMap<SubscriptionId, SubscriptionData<S>>>, subscriptions: &mut HashMap<K, HashMap<SubscriptionId, SubscriptionData<S, T>>>,
hashmap_key: K, hashmap_key: K,
commitment: Option<CommitmentConfig>, commitment: Option<CommitmentConfig>,
sub_id: SubscriptionId, sub_id: SubscriptionId,
subscriber: Subscriber<S>, subscriber: Subscriber<S>,
last_notified_slot: Slot, last_notified_slot: Slot,
config: Option<T>,
) where ) where
K: Eq + Hash, K: Eq + Hash,
S: Clone, S: Clone,
@ -112,6 +129,7 @@ fn add_subscription<K, S>(
sink, sink,
commitment, commitment,
last_notified_slot: RwLock::new(last_notified_slot), last_notified_slot: RwLock::new(last_notified_slot),
config,
}; };
if let Some(current_hashmap) = subscriptions.get_mut(&hashmap_key) { if let Some(current_hashmap) = subscriptions.get_mut(&hashmap_key) {
current_hashmap.insert(sub_id, subscription_data); current_hashmap.insert(sub_id, subscription_data);
@ -122,8 +140,8 @@ fn add_subscription<K, S>(
subscriptions.insert(hashmap_key, hashmap); subscriptions.insert(hashmap_key, hashmap);
} }
fn remove_subscription<K, S>( fn remove_subscription<K, S, T>(
subscriptions: &mut HashMap<K, HashMap<SubscriptionId, SubscriptionData<S>>>, subscriptions: &mut HashMap<K, HashMap<SubscriptionId, SubscriptionData<S, T>>>,
sub_id: &SubscriptionId, sub_id: &SubscriptionId,
) -> bool ) -> bool
where where
@ -145,8 +163,8 @@ where
} }
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
fn check_commitment_and_notify<K, S, B, F, X>( fn check_commitment_and_notify<K, S, B, F, X, T>(
subscriptions: &HashMap<K, HashMap<SubscriptionId, SubscriptionData<Response<S>>>>, subscriptions: &HashMap<K, HashMap<SubscriptionId, SubscriptionData<Response<S>, T>>>,
hashmap_key: &K, hashmap_key: &K,
bank_forks: &Arc<RwLock<BankForks>>, bank_forks: &Arc<RwLock<BankForks>>,
commitment_slots: &CommitmentSlots, commitment_slots: &CommitmentSlots,
@ -158,8 +176,9 @@ where
K: Eq + Hash + Clone + Copy, K: Eq + Hash + Clone + Copy,
S: Clone + Serialize, S: Clone + Serialize,
B: Fn(&Bank, &K) -> X, 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, X: Clone + Serialize + Default,
T: Clone,
{ {
let mut notified_set: HashSet<SubscriptionId> = HashSet::new(); let mut notified_set: HashSet<SubscriptionId> = HashSet::new();
if let Some(hashmap) = subscriptions.get(hashmap_key) { if let Some(hashmap) = subscriptions.get(hashmap_key) {
@ -169,6 +188,7 @@ where
sink, sink,
commitment, commitment,
last_notified_slot, last_notified_slot,
config,
}, },
) in hashmap.iter() ) in hashmap.iter()
{ {
@ -188,7 +208,8 @@ where
.unwrap_or_default() .unwrap_or_default()
}; };
let mut w_last_notified_slot = last_notified_slot.write().unwrap(); 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 { for result in filter_results {
notifier.notify( notifier.notify(
Response { Response {
@ -220,16 +241,15 @@ impl RpcNotifier {
fn filter_account_result( fn filter_account_result(
result: Option<(Account, Slot)>, result: Option<(Account, Slot)>,
last_notified_slot: Slot, last_notified_slot: Slot,
encoding: Option<UiAccountEncoding>,
) -> (Box<dyn Iterator<Item = UiAccount>>, Slot) { ) -> (Box<dyn Iterator<Item = UiAccount>>, Slot) {
if let Some((account, fork)) = result { if let Some((account, fork)) = result {
// If fork < last_notified_slot this means that we last notified for a fork // If fork < last_notified_slot this means that we last notified for a fork
// and should notify that the account state has been reverted. // and should notify that the account state has been reverted.
if fork != last_notified_slot { if fork != last_notified_slot {
let encoding = encoding.unwrap_or(UiAccountEncoding::Binary);
return ( return (
Box::new(iter::once(UiAccount::encode( Box::new(iter::once(UiAccount::encode(account, encoding))),
account,
UiAccountEncoding::Binary,
))),
fork, fork,
); );
} }
@ -240,6 +260,7 @@ fn filter_account_result(
fn filter_signature_result( fn filter_signature_result(
result: Option<transaction::Result<()>>, result: Option<transaction::Result<()>>,
last_notified_slot: Slot, last_notified_slot: Slot,
_config: Option<()>,
) -> (Box<dyn Iterator<Item = RpcSignatureResult>>, Slot) { ) -> (Box<dyn Iterator<Item = RpcSignatureResult>>, Slot) {
( (
Box::new( Box::new(
@ -254,14 +275,24 @@ fn filter_signature_result(
fn filter_program_results( fn filter_program_results(
accounts: Vec<(Pubkey, Account)>, accounts: Vec<(Pubkey, Account)>,
last_notified_slot: Slot, last_notified_slot: Slot,
config: Option<ProgramConfig>,
) -> (Box<dyn Iterator<Item = RpcKeyedAccount>>, Slot) { ) -> (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( Box::new(
accounts accounts
.into_iter() .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(), pubkey: pubkey.to_string(),
account: UiAccount::encode(account, UiAccountEncoding::Binary), account: UiAccount::encode(account, encoding.clone()),
}), }),
), ),
last_notified_slot, last_notified_slot,
@ -448,11 +479,13 @@ impl RpcSubscriptions {
pub fn add_account_subscription( pub fn add_account_subscription(
&self, &self,
pubkey: Pubkey, pubkey: Pubkey,
commitment: Option<CommitmentConfig>, config: Option<RpcAccountInfoConfig>,
sub_id: SubscriptionId, sub_id: SubscriptionId,
subscriber: Subscriber<Response<UiAccount>>, subscriber: Subscriber<Response<UiAccount>>,
) { ) {
let commitment_level = commitment let config = config.unwrap_or_default();
let commitment_level = config
.commitment
.unwrap_or_else(CommitmentConfig::single) .unwrap_or_else(CommitmentConfig::single)
.commitment; .commitment;
let slot = match commitment_level { let slot = match commitment_level {
@ -498,10 +531,11 @@ impl RpcSubscriptions {
add_subscription( add_subscription(
&mut subscriptions, &mut subscriptions,
pubkey, pubkey,
commitment, config.commitment,
sub_id, sub_id,
subscriber, subscriber,
last_notified_slot, last_notified_slot,
config.encoding,
); );
} }
@ -522,11 +556,14 @@ impl RpcSubscriptions {
pub fn add_program_subscription( pub fn add_program_subscription(
&self, &self,
program_id: Pubkey, program_id: Pubkey,
commitment: Option<CommitmentConfig>, config: Option<RpcProgramAccountsConfig>,
sub_id: SubscriptionId, sub_id: SubscriptionId,
subscriber: Subscriber<Response<RpcKeyedAccount>>, 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) .unwrap_or_else(CommitmentConfig::recent)
.commitment; .commitment;
let mut subscriptions = if commitment_level == CommitmentLevel::SingleGossip { let mut subscriptions = if commitment_level == CommitmentLevel::SingleGossip {
@ -540,10 +577,14 @@ impl RpcSubscriptions {
add_subscription( add_subscription(
&mut subscriptions, &mut subscriptions,
program_id, program_id,
commitment, config.account_config.commitment,
sub_id, sub_id,
subscriber, subscriber,
0, // last_notified_slot is not utilized for program subscriptions 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, sub_id,
subscriber, subscriber,
0, // last_notified_slot is not utilized for signature subscriptions 0, // last_notified_slot is not utilized for signature subscriptions
None,
); );
} }
@ -964,7 +1006,10 @@ pub(crate) mod tests {
); );
subscriptions.add_account_subscription( subscriptions.add_account_subscription(
alice.pubkey(), alice.pubkey(),
Some(CommitmentConfig::recent()), Some(RpcAccountInfoConfig {
commitment: Some(CommitmentConfig::recent()),
encoding: None,
}),
sub_id.clone(), sub_id.clone(),
subscriber, subscriber,
); );
@ -1368,7 +1413,7 @@ pub(crate) mod tests {
#[test] #[test]
#[serial] #[serial]
fn test_add_and_remove_subscription() { fn test_add_and_remove_subscription() {
let mut subscriptions: HashMap<u64, HashMap<SubscriptionId, SubscriptionData<()>>> = let mut subscriptions: HashMap<u64, HashMap<SubscriptionId, SubscriptionData<(), ()>>> =
HashMap::new(); HashMap::new();
let num_keys = 5; let num_keys = 5;
@ -1376,7 +1421,7 @@ pub(crate) mod tests {
let (subscriber, _id_receiver, _transport_receiver) = let (subscriber, _id_receiver, _transport_receiver) =
Subscriber::new_test("notification"); Subscriber::new_test("notification");
let sub_id = SubscriptionId::Number(key); 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 // Add another subscription to the "0" key
@ -1389,6 +1434,7 @@ pub(crate) mod tests {
extra_sub_id.clone(), extra_sub_id.clone(),
subscriber, subscriber,
0, 0,
None,
); );
assert_eq!(subscriptions.len(), num_keys as usize); assert_eq!(subscriptions.len(), num_keys as usize);
@ -1444,7 +1490,10 @@ pub(crate) mod tests {
let sub_id0 = SubscriptionId::Number(0 as u64); let sub_id0 = SubscriptionId::Number(0 as u64);
subscriptions.add_account_subscription( subscriptions.add_account_subscription(
alice.pubkey(), alice.pubkey(),
Some(CommitmentConfig::single_gossip()), Some(RpcAccountInfoConfig {
commitment: Some(CommitmentConfig::single_gossip()),
encoding: None,
}),
sub_id0.clone(), sub_id0.clone(),
subscriber0, subscriber0,
); );
@ -1509,7 +1558,10 @@ pub(crate) mod tests {
let sub_id1 = SubscriptionId::Number(1 as u64); let sub_id1 = SubscriptionId::Number(1 as u64);
subscriptions.add_account_subscription( subscriptions.add_account_subscription(
alice.pubkey(), alice.pubkey(),
Some(CommitmentConfig::single_gossip()), Some(RpcAccountInfoConfig {
commitment: Some(CommitmentConfig::single_gossip()),
encoding: None,
}),
sub_id1.clone(), sub_id1.clone(),
subscriber1, subscriber1,
); );

View File

@ -1256,7 +1256,10 @@ Subscribe to an account to receive notifications when the lamports or data for a
#### Parameters: #### Parameters:
- `<string>` - account Pubkey, as base-58 encoded string - `<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) - `<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: #### 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", {"commitment": "single"}]}
{"jsonrpc":"2.0", "id":1, "method":"accountSubscribe", "params":["CM78CPUeXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNH12", {"encoding":"jsonParsed"}]}
// Result // Result
{"jsonrpc": "2.0","result": 0,"id": 1} {"jsonrpc": "2.0","result": 23784,"id": 1}
``` ```
#### Notification Format: #### Notification Format:
```bash ```bash
// Binary encoding
{ {
"jsonrpc": "2.0", "jsonrpc": "2.0",
"method": "accountNotification", "method": "accountNotification",
@ -1286,10 +1292,41 @@ Subscribe to an account to receive notifications when the lamports or data for a
"slot": 5199307 "slot": 5199307
}, },
"value": { "value": {
"data": "9qRxMDwy1ntDhBBoiy4Na9uDLbRTSzUS989mpwz", "data": "11116bv5nS2h3y12kD1yUKeMZvGcKLSjQgX6BeV7u1FrjeJcKfsHPXHRDEHrBesJhZyqnnq9qJeUuF7WHxiuLuL5twc38w2TXNLxnDbjmuR",
"executable": false, "executable": false,
"lamports": 33594, "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 "rentEpoch": 635
} }
}, },
@ -1327,7 +1364,11 @@ Subscribe to a program to receive notifications when the lamports or data for a
#### Parameters: #### Parameters:
- `<string>` - program_id Pubkey, as base-58 encoded string - `<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: #### Results:
@ -1337,17 +1378,22 @@ Subscribe to a program to receive notifications when the lamports or data for a
```bash ```bash
// Request // 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 // Result
{"jsonrpc": "2.0","result": 0,"id": 1} {"jsonrpc": "2.0","result": 24040,"id": 1}
``` ```
#### Notification Format: #### Notification Format:
```bash ```bash
// Binary encoding
{ {
"jsonrpc": "2.0", "jsonrpc": "2.0",
"method": "programNotification", "method": "programNotification",
@ -1359,10 +1405,44 @@ Subscribe to a program to receive notifications when the lamports or data for a
"value": { "value": {
"pubkey": "H4vnBqifaSACnKa7acsxstsY1iV1bvJNxsCY7enrd1hq" "pubkey": "H4vnBqifaSACnKa7acsxstsY1iV1bvJNxsCY7enrd1hq"
"account": { "account": {
"data": "9qRxMDwy1ntDhBBoiy4Na9uDLbRTSzUS989m", "data": "11116bv5nS2h3y12kD1yUKeMZvGcKLSjQgX6BeV7u1FrjeJcKfsHPXHRDEHrBesJhZyqnnq9qJeUuF7WHxiuLuL5twc38w2TXNLxnDbjmuR",
"executable": false, "executable": false,
"lamports": 33594, "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 "rentEpoch": 636
}, },
} }