Add Confirmations parameter to RPC Subscriptions (#4154)
* Add optional depth parameter to pubsub, and store in subscriptions * Pass bank_forks into rpc_subscription; add method to check depth before notify and impl for account subscriptions * Impl check-depth for signature subscriptions * Impl check-depth for program subscriptions * Plumb fork id through accounts * Use fork id and root to prevent repeated account notifications; also s/Depth/Confirmations * Write tests in terms of bank_forks * Fixup accounts tests * Add pubsub-confirmations tests * Update pubsub documentation
This commit is contained in:
parent
0139e5db21
commit
71f9b44687
|
@ -316,6 +316,14 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "m
|
||||||
After connect to the RPC PubSub websocket at `ws://<ADDRESS>/`:
|
After connect to the RPC PubSub websocket at `ws://<ADDRESS>/`:
|
||||||
- Submit subscription requests to the websocket using the methods below
|
- Submit subscription requests to the websocket using the methods below
|
||||||
- Multiple subscriptions may be active at once
|
- Multiple subscriptions may be active at once
|
||||||
|
- All subscriptions take an optional `confirmations` parameter, which defines
|
||||||
|
how many confirmed blocks the node should wait before sending a notification.
|
||||||
|
The greater the number, the more likely the notification is to represent
|
||||||
|
consensus across the cluster, and the less likely it is to be affected by
|
||||||
|
forking or rollbacks. If unspecified, the default value is 0; the node will
|
||||||
|
send a notification as soon as it witnesses the event. The maximum
|
||||||
|
`confirmations` wait length is the cluster's `MAX_LOCKOUT_HISTORY`, which
|
||||||
|
represents the economic finality of the chain.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -325,6 +333,8 @@ for a given account public key changes
|
||||||
|
|
||||||
##### Parameters:
|
##### Parameters:
|
||||||
* `string` - account Pubkey, as base-58 encoded string
|
* `string` - account Pubkey, as base-58 encoded string
|
||||||
|
* `integer` - optional, number of confirmed blocks to wait before notification.
|
||||||
|
Default: 0, Max: `MAX_LOCKOUT_HISTORY` (greater integers rounded down)
|
||||||
|
|
||||||
##### Results:
|
##### Results:
|
||||||
* `integer` - Subscription id (needed to unsubscribe)
|
* `integer` - Subscription id (needed to unsubscribe)
|
||||||
|
@ -334,6 +344,8 @@ for a given account public key changes
|
||||||
// Request
|
// Request
|
||||||
{"jsonrpc":"2.0", "id":1, "method":"accountSubscribe", "params":["CM78CPUeXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNH12"]}
|
{"jsonrpc":"2.0", "id":1, "method":"accountSubscribe", "params":["CM78CPUeXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNH12"]}
|
||||||
|
|
||||||
|
{"jsonrpc":"2.0", "id":1, "method":"accountSubscribe", "params":["CM78CPUeXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNH12", 15]}
|
||||||
|
|
||||||
// Result
|
// Result
|
||||||
{"jsonrpc": "2.0","result": 0,"id": 1}
|
{"jsonrpc": "2.0","result": 0,"id": 1}
|
||||||
```
|
```
|
||||||
|
@ -371,6 +383,8 @@ for a given account owned by the program changes
|
||||||
|
|
||||||
##### Parameters:
|
##### Parameters:
|
||||||
* `string` - program_id Pubkey, as base-58 encoded string
|
* `string` - program_id Pubkey, as base-58 encoded string
|
||||||
|
* `integer` - optional, number of confirmed blocks to wait before notification.
|
||||||
|
Default: 0, Max: `MAX_LOCKOUT_HISTORY` (greater integers rounded down)
|
||||||
|
|
||||||
##### Results:
|
##### Results:
|
||||||
* `integer` - Subscription id (needed to unsubscribe)
|
* `integer` - Subscription id (needed to unsubscribe)
|
||||||
|
@ -380,6 +394,8 @@ for a given account owned by the program changes
|
||||||
// Request
|
// Request
|
||||||
{"jsonrpc":"2.0", "id":1, "method":"programSubscribe", "params":["9gZbPtbtHrs6hEWgd6MbVY9VPFtS5Z8xKtnYwA2NynHV"]}
|
{"jsonrpc":"2.0", "id":1, "method":"programSubscribe", "params":["9gZbPtbtHrs6hEWgd6MbVY9VPFtS5Z8xKtnYwA2NynHV"]}
|
||||||
|
|
||||||
|
{"jsonrpc":"2.0", "id":1, "method":"programSubscribe", "params":["9gZbPtbtHrs6hEWgd6MbVY9VPFtS5Z8xKtnYwA2NynHV", 15]}
|
||||||
|
|
||||||
// Result
|
// Result
|
||||||
{"jsonrpc": "2.0","result": 0,"id": 1}
|
{"jsonrpc": "2.0","result": 0,"id": 1}
|
||||||
```
|
```
|
||||||
|
@ -419,6 +435,8 @@ On `signatureNotification`, the subscription is automatically cancelled
|
||||||
|
|
||||||
##### Parameters:
|
##### Parameters:
|
||||||
* `string` - Transaction Signature, as base-58 encoded string
|
* `string` - Transaction Signature, as base-58 encoded string
|
||||||
|
* `integer` - optional, number of confirmed blocks to wait before notification.
|
||||||
|
Default: 0, Max: `MAX_LOCKOUT_HISTORY` (greater integers rounded down)
|
||||||
|
|
||||||
##### Results:
|
##### Results:
|
||||||
* `integer` - subscription id (needed to unsubscribe)
|
* `integer` - subscription id (needed to unsubscribe)
|
||||||
|
@ -428,6 +446,8 @@ On `signatureNotification`, the subscription is automatically cancelled
|
||||||
// Request
|
// Request
|
||||||
{"jsonrpc":"2.0", "id":1, "method":"signatureSubscribe", "params":["2EBVM6cB8vAAD93Ktr6Vd8p67XPbQzCJX47MpReuiCXJAtcjaxpvWpcg9Ege1Nr5Tk3a2GFrByT7WPBjdsTycY9b"]}
|
{"jsonrpc":"2.0", "id":1, "method":"signatureSubscribe", "params":["2EBVM6cB8vAAD93Ktr6Vd8p67XPbQzCJX47MpReuiCXJAtcjaxpvWpcg9Ege1Nr5Tk3a2GFrByT7WPBjdsTycY9b"]}
|
||||||
|
|
||||||
|
{"jsonrpc":"2.0", "id":1, "method":"signatureSubscribe", "params":["2EBVM6cB8vAAD93Ktr6Vd8p67XPbQzCJX47MpReuiCXJAtcjaxpvWpcg9Ege1Nr5Tk3a2GFrByT7WPBjdsTycY9b", 15]}
|
||||||
|
|
||||||
// Result
|
// Result
|
||||||
{"jsonrpc": "2.0","result": 0,"id": 1}
|
{"jsonrpc": "2.0","result": 0,"id": 1}
|
||||||
```
|
```
|
||||||
|
|
|
@ -145,7 +145,7 @@ impl ReplayStage {
|
||||||
Self::generate_votable_banks(&bank_forks, &locktower, &mut progress);
|
Self::generate_votable_banks(&bank_forks, &locktower, &mut progress);
|
||||||
|
|
||||||
if let Some((_, bank)) = votable.last() {
|
if let Some((_, bank)) = votable.last() {
|
||||||
subscriptions.notify_subscribers(&bank);
|
subscriptions.notify_subscribers(bank.slot(), &bank_forks);
|
||||||
|
|
||||||
Self::handle_votable_bank(
|
Self::handle_votable_bank(
|
||||||
&bank,
|
&bank,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! The `pubsub` module implements a threaded subscription service on client RPC request
|
//! The `pubsub` module implements a threaded subscription service on client RPC request
|
||||||
|
|
||||||
use crate::rpc_subscriptions::RpcSubscriptions;
|
use crate::rpc_subscriptions::{Confirmations, RpcSubscriptions};
|
||||||
use bs58;
|
use bs58;
|
||||||
use jsonrpc_core::{Error, ErrorCode, Result};
|
use jsonrpc_core::{Error, ErrorCode, Result};
|
||||||
use jsonrpc_derive::rpc;
|
use jsonrpc_derive::rpc;
|
||||||
|
@ -24,7 +24,13 @@ pub trait RpcSolPubSub {
|
||||||
subscribe,
|
subscribe,
|
||||||
name = "accountSubscribe"
|
name = "accountSubscribe"
|
||||||
)]
|
)]
|
||||||
fn account_subscribe(&self, _: Self::Metadata, _: Subscriber<Account>, _: String);
|
fn account_subscribe(
|
||||||
|
&self,
|
||||||
|
_: Self::Metadata,
|
||||||
|
_: Subscriber<Account>,
|
||||||
|
_: String,
|
||||||
|
_: Option<Confirmations>,
|
||||||
|
);
|
||||||
|
|
||||||
// Unsubscribe from account notification subscription.
|
// Unsubscribe from account notification subscription.
|
||||||
#[pubsub(
|
#[pubsub(
|
||||||
|
@ -41,7 +47,13 @@ pub trait RpcSolPubSub {
|
||||||
subscribe,
|
subscribe,
|
||||||
name = "programSubscribe"
|
name = "programSubscribe"
|
||||||
)]
|
)]
|
||||||
fn program_subscribe(&self, _: Self::Metadata, _: Subscriber<(String, Account)>, _: String);
|
fn program_subscribe(
|
||||||
|
&self,
|
||||||
|
_: Self::Metadata,
|
||||||
|
_: Subscriber<(String, Account)>,
|
||||||
|
_: String,
|
||||||
|
_: Option<Confirmations>,
|
||||||
|
);
|
||||||
|
|
||||||
// Unsubscribe from account notification subscription.
|
// Unsubscribe from account notification subscription.
|
||||||
#[pubsub(
|
#[pubsub(
|
||||||
|
@ -61,8 +73,9 @@ pub trait RpcSolPubSub {
|
||||||
fn signature_subscribe(
|
fn signature_subscribe(
|
||||||
&self,
|
&self,
|
||||||
_: Self::Metadata,
|
_: Self::Metadata,
|
||||||
_: Subscriber<Option<transaction::Result<()>>>,
|
_: Subscriber<transaction::Result<()>>,
|
||||||
_: String,
|
_: String,
|
||||||
|
_: Option<Confirmations>,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Unsubscribe from signature notification subscription.
|
// Unsubscribe from signature notification subscription.
|
||||||
|
@ -95,6 +108,7 @@ impl RpcSolPubSub for RpcSolPubSubImpl {
|
||||||
_meta: Self::Metadata,
|
_meta: Self::Metadata,
|
||||||
subscriber: Subscriber<Account>,
|
subscriber: Subscriber<Account>,
|
||||||
pubkey_str: String,
|
pubkey_str: String,
|
||||||
|
confirmations: Option<Confirmations>,
|
||||||
) {
|
) {
|
||||||
let pubkey_vec = bs58::decode(pubkey_str).into_vec().unwrap();
|
let pubkey_vec = bs58::decode(pubkey_str).into_vec().unwrap();
|
||||||
if pubkey_vec.len() != mem::size_of::<Pubkey>() {
|
if pubkey_vec.len() != mem::size_of::<Pubkey>() {
|
||||||
|
@ -115,7 +129,7 @@ impl RpcSolPubSub for RpcSolPubSubImpl {
|
||||||
let sink = subscriber.assign_id(sub_id.clone()).unwrap();
|
let sink = subscriber.assign_id(sub_id.clone()).unwrap();
|
||||||
|
|
||||||
self.subscriptions
|
self.subscriptions
|
||||||
.add_account_subscription(&pubkey, &sub_id, &sink)
|
.add_account_subscription(&pubkey, confirmations, &sub_id, &sink)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn account_unsubscribe(
|
fn account_unsubscribe(
|
||||||
|
@ -140,6 +154,7 @@ impl RpcSolPubSub for RpcSolPubSubImpl {
|
||||||
_meta: Self::Metadata,
|
_meta: Self::Metadata,
|
||||||
subscriber: Subscriber<(String, Account)>,
|
subscriber: Subscriber<(String, Account)>,
|
||||||
pubkey_str: String,
|
pubkey_str: String,
|
||||||
|
confirmations: Option<Confirmations>,
|
||||||
) {
|
) {
|
||||||
let pubkey_vec = bs58::decode(pubkey_str).into_vec().unwrap();
|
let pubkey_vec = bs58::decode(pubkey_str).into_vec().unwrap();
|
||||||
if pubkey_vec.len() != mem::size_of::<Pubkey>() {
|
if pubkey_vec.len() != mem::size_of::<Pubkey>() {
|
||||||
|
@ -160,7 +175,7 @@ impl RpcSolPubSub for RpcSolPubSubImpl {
|
||||||
let sink = subscriber.assign_id(sub_id.clone()).unwrap();
|
let sink = subscriber.assign_id(sub_id.clone()).unwrap();
|
||||||
|
|
||||||
self.subscriptions
|
self.subscriptions
|
||||||
.add_program_subscription(&pubkey, &sub_id, &sink)
|
.add_program_subscription(&pubkey, confirmations, &sub_id, &sink)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn program_unsubscribe(
|
fn program_unsubscribe(
|
||||||
|
@ -183,8 +198,9 @@ impl RpcSolPubSub for RpcSolPubSubImpl {
|
||||||
fn signature_subscribe(
|
fn signature_subscribe(
|
||||||
&self,
|
&self,
|
||||||
_meta: Self::Metadata,
|
_meta: Self::Metadata,
|
||||||
subscriber: Subscriber<Option<transaction::Result<()>>>,
|
subscriber: Subscriber<transaction::Result<()>>,
|
||||||
signature_str: String,
|
signature_str: String,
|
||||||
|
confirmations: Option<Confirmations>,
|
||||||
) {
|
) {
|
||||||
info!("signature_subscribe");
|
info!("signature_subscribe");
|
||||||
let signature_vec = bs58::decode(signature_str).into_vec().unwrap();
|
let signature_vec = bs58::decode(signature_str).into_vec().unwrap();
|
||||||
|
@ -199,12 +215,17 @@ impl RpcSolPubSub for RpcSolPubSubImpl {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let signature = Signature::new(&signature_vec);
|
let signature = Signature::new(&signature_vec);
|
||||||
|
|
||||||
let id = self.uid.fetch_add(1, atomic::Ordering::SeqCst);
|
let id = self.uid.fetch_add(1, atomic::Ordering::SeqCst);
|
||||||
let sub_id = SubscriptionId::Number(id as u64);
|
let sub_id = SubscriptionId::Number(id as u64);
|
||||||
|
info!(
|
||||||
|
"signature_subscribe: signature={:?} id={:?}",
|
||||||
|
signature, sub_id
|
||||||
|
);
|
||||||
let sink = subscriber.assign_id(sub_id.clone()).unwrap();
|
let sink = subscriber.assign_id(sub_id.clone()).unwrap();
|
||||||
|
|
||||||
self.subscriptions
|
self.subscriptions
|
||||||
.add_signature_subscription(&signature, &sub_id, &sink);
|
.add_signature_subscription(&signature, confirmations, &sub_id, &sink);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn signature_unsubscribe(
|
fn signature_unsubscribe(
|
||||||
|
@ -228,6 +249,7 @@ impl RpcSolPubSub for RpcSolPubSubImpl {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::bank_forks::BankForks;
|
||||||
use jsonrpc_core::futures::sync::mpsc;
|
use jsonrpc_core::futures::sync::mpsc;
|
||||||
use jsonrpc_core::Response;
|
use jsonrpc_core::Response;
|
||||||
use jsonrpc_pubsub::{PubSubHandler, Session};
|
use jsonrpc_pubsub::{PubSubHandler, Session};
|
||||||
|
@ -237,26 +259,27 @@ mod tests {
|
||||||
use solana_sdk::genesis_block::GenesisBlock;
|
use solana_sdk::genesis_block::GenesisBlock;
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey::Pubkey;
|
||||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||||
|
use solana_sdk::system_program;
|
||||||
use solana_sdk::system_transaction;
|
use solana_sdk::system_transaction;
|
||||||
use solana_sdk::transaction::{self, Transaction};
|
use solana_sdk::transaction::{self, Transaction};
|
||||||
|
use std::sync::RwLock;
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::prelude::{Async, Stream};
|
use tokio::prelude::{Async, Stream};
|
||||||
|
|
||||||
fn process_transaction_and_notify(
|
fn process_transaction_and_notify(
|
||||||
bank: &Arc<Bank>,
|
bank_forks: &Arc<RwLock<BankForks>>,
|
||||||
tx: &Transaction,
|
tx: &Transaction,
|
||||||
subscriptions: &RpcSubscriptions,
|
subscriptions: &RpcSubscriptions,
|
||||||
) -> transaction::Result<Arc<Bank>> {
|
) -> transaction::Result<()> {
|
||||||
bank.process_transaction(tx)?;
|
bank_forks
|
||||||
subscriptions.notify_subscribers(&bank);
|
.write()
|
||||||
|
.unwrap()
|
||||||
// Simulate a block boundary
|
.get(0)
|
||||||
Ok(Arc::new(Bank::new_from_parent(
|
.unwrap()
|
||||||
&bank,
|
.process_transaction(tx)?;
|
||||||
&Pubkey::default(),
|
subscriptions.notify_subscribers(0, &bank_forks);
|
||||||
bank.slot() + 1,
|
Ok(())
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_session() -> Arc<Session> {
|
fn create_session() -> Arc<Session> {
|
||||||
|
@ -269,8 +292,8 @@ mod tests {
|
||||||
let bob = Keypair::new();
|
let bob = Keypair::new();
|
||||||
let bob_pubkey = bob.pubkey();
|
let bob_pubkey = bob.pubkey();
|
||||||
let bank = Bank::new(&genesis_block);
|
let bank = Bank::new(&genesis_block);
|
||||||
let arc_bank = Arc::new(bank);
|
let blockhash = bank.last_blockhash();
|
||||||
let blockhash = arc_bank.last_blockhash();
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(0, bank)));
|
||||||
|
|
||||||
let rpc = RpcSolPubSubImpl::default();
|
let rpc = RpcSolPubSubImpl::default();
|
||||||
|
|
||||||
|
@ -280,9 +303,9 @@ mod tests {
|
||||||
let session = create_session();
|
let session = create_session();
|
||||||
let (subscriber, _id_receiver, mut receiver) =
|
let (subscriber, _id_receiver, mut receiver) =
|
||||||
Subscriber::new_test("signatureNotification");
|
Subscriber::new_test("signatureNotification");
|
||||||
rpc.signature_subscribe(session, subscriber, tx.signatures[0].to_string());
|
rpc.signature_subscribe(session, subscriber, tx.signatures[0].to_string(), None);
|
||||||
|
|
||||||
process_transaction_and_notify(&arc_bank, &tx, &rpc.subscriptions).unwrap();
|
process_transaction_and_notify(&bank_forks, &tx, &rpc.subscriptions).unwrap();
|
||||||
sleep(Duration::from_millis(200));
|
sleep(Duration::from_millis(200));
|
||||||
|
|
||||||
// Test signature confirmation notification
|
// Test signature confirmation notification
|
||||||
|
@ -354,13 +377,18 @@ mod tests {
|
||||||
let budget_program_id = solana_budget_api::id();
|
let budget_program_id = solana_budget_api::id();
|
||||||
let executable = false; // TODO
|
let executable = false; // TODO
|
||||||
let bank = Bank::new(&genesis_block);
|
let bank = Bank::new(&genesis_block);
|
||||||
let arc_bank = Arc::new(bank);
|
let blockhash = bank.last_blockhash();
|
||||||
let blockhash = arc_bank.last_blockhash();
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(0, bank)));
|
||||||
|
|
||||||
let rpc = RpcSolPubSubImpl::default();
|
let rpc = RpcSolPubSubImpl::default();
|
||||||
let session = create_session();
|
let session = create_session();
|
||||||
let (subscriber, _id_receiver, mut receiver) = Subscriber::new_test("accountNotification");
|
let (subscriber, _id_receiver, mut receiver) = Subscriber::new_test("accountNotification");
|
||||||
rpc.account_subscribe(session, subscriber, contract_state.pubkey().to_string());
|
rpc.account_subscribe(
|
||||||
|
session,
|
||||||
|
subscriber,
|
||||||
|
contract_state.pubkey().to_string(),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
let tx = system_transaction::create_user_account(
|
let tx = system_transaction::create_user_account(
|
||||||
&alice,
|
&alice,
|
||||||
|
@ -369,7 +397,7 @@ mod tests {
|
||||||
blockhash,
|
blockhash,
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
let arc_bank = process_transaction_and_notify(&arc_bank, &tx, &rpc.subscriptions).unwrap();
|
process_transaction_and_notify(&bank_forks, &tx, &rpc.subscriptions).unwrap();
|
||||||
|
|
||||||
let ixs = budget_instruction::when_signed(
|
let ixs = budget_instruction::when_signed(
|
||||||
&contract_funds.pubkey(),
|
&contract_funds.pubkey(),
|
||||||
|
@ -380,12 +408,19 @@ mod tests {
|
||||||
51,
|
51,
|
||||||
);
|
);
|
||||||
let tx = Transaction::new_signed_instructions(&[&contract_funds], ixs, blockhash);
|
let tx = Transaction::new_signed_instructions(&[&contract_funds], ixs, blockhash);
|
||||||
let arc_bank = process_transaction_and_notify(&arc_bank, &tx, &rpc.subscriptions).unwrap();
|
process_transaction_and_notify(&bank_forks, &tx, &rpc.subscriptions).unwrap();
|
||||||
sleep(Duration::from_millis(200));
|
sleep(Duration::from_millis(200));
|
||||||
|
|
||||||
// Test signature confirmation notification #1
|
// Test signature confirmation notification #1
|
||||||
let string = receiver.poll();
|
let string = receiver.poll();
|
||||||
let expected_data = arc_bank.get_account(&contract_state.pubkey()).unwrap().data;
|
let expected_data = bank_forks
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(0)
|
||||||
|
.unwrap()
|
||||||
|
.get_account(&contract_state.pubkey())
|
||||||
|
.unwrap()
|
||||||
|
.data;
|
||||||
let expected = json!({
|
let expected = json!({
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"method": "accountNotification",
|
"method": "accountNotification",
|
||||||
|
@ -406,7 +441,7 @@ mod tests {
|
||||||
|
|
||||||
let tx =
|
let tx =
|
||||||
system_transaction::create_user_account(&alice, &witness.pubkey(), 1, blockhash, 0);
|
system_transaction::create_user_account(&alice, &witness.pubkey(), 1, blockhash, 0);
|
||||||
let arc_bank = process_transaction_and_notify(&arc_bank, &tx, &rpc.subscriptions).unwrap();
|
process_transaction_and_notify(&bank_forks, &tx, &rpc.subscriptions).unwrap();
|
||||||
sleep(Duration::from_millis(200));
|
sleep(Duration::from_millis(200));
|
||||||
let ix = budget_instruction::apply_signature(
|
let ix = budget_instruction::apply_signature(
|
||||||
&witness.pubkey(),
|
&witness.pubkey(),
|
||||||
|
@ -414,10 +449,18 @@ mod tests {
|
||||||
&bob_pubkey,
|
&bob_pubkey,
|
||||||
);
|
);
|
||||||
let tx = Transaction::new_signed_instructions(&[&witness], vec![ix], blockhash);
|
let tx = Transaction::new_signed_instructions(&[&witness], vec![ix], blockhash);
|
||||||
let arc_bank = process_transaction_and_notify(&arc_bank, &tx, &rpc.subscriptions).unwrap();
|
process_transaction_and_notify(&bank_forks, &tx, &rpc.subscriptions).unwrap();
|
||||||
sleep(Duration::from_millis(200));
|
sleep(Duration::from_millis(200));
|
||||||
|
|
||||||
assert_eq!(arc_bank.get_account(&contract_state.pubkey()), None);
|
assert_eq!(
|
||||||
|
bank_forks
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(0)
|
||||||
|
.unwrap()
|
||||||
|
.get_account(&contract_state.pubkey()),
|
||||||
|
None
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -456,4 +499,80 @@ mod tests {
|
||||||
let result: Response = serde_json::from_str(&res.unwrap()).unwrap();
|
let result: Response = serde_json::from_str(&res.unwrap()).unwrap();
|
||||||
assert_eq!(expected, result);
|
assert_eq!(expected, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn test_account_confirmations_not_fulfilled() {
|
||||||
|
let (genesis_block, alice) = GenesisBlock::new(10_000);
|
||||||
|
let bank = Bank::new(&genesis_block);
|
||||||
|
let blockhash = bank.last_blockhash();
|
||||||
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(0, bank)));
|
||||||
|
let bob = Keypair::new();
|
||||||
|
|
||||||
|
let rpc = RpcSolPubSubImpl::default();
|
||||||
|
let session = create_session();
|
||||||
|
let (subscriber, _id_receiver, mut receiver) = Subscriber::new_test("accountNotification");
|
||||||
|
rpc.account_subscribe(session, subscriber, bob.pubkey().to_string(), Some(2));
|
||||||
|
|
||||||
|
let tx = system_transaction::transfer(&alice, &bob.pubkey(), 100, blockhash, 0);
|
||||||
|
bank_forks
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.get(0)
|
||||||
|
.unwrap()
|
||||||
|
.process_transaction(&tx)
|
||||||
|
.unwrap();
|
||||||
|
rpc.subscriptions.notify_subscribers(0, &bank_forks);
|
||||||
|
let _panic = receiver.poll();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_account_confirmations() {
|
||||||
|
let (genesis_block, alice) = GenesisBlock::new(10_000);
|
||||||
|
let bank = Bank::new(&genesis_block);
|
||||||
|
let blockhash = bank.last_blockhash();
|
||||||
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(0, bank)));
|
||||||
|
let bob = Keypair::new();
|
||||||
|
|
||||||
|
let rpc = RpcSolPubSubImpl::default();
|
||||||
|
let session = create_session();
|
||||||
|
let (subscriber, _id_receiver, mut receiver) = Subscriber::new_test("accountNotification");
|
||||||
|
rpc.account_subscribe(session, subscriber, bob.pubkey().to_string(), Some(2));
|
||||||
|
|
||||||
|
let tx = system_transaction::transfer(&alice, &bob.pubkey(), 100, blockhash, 0);
|
||||||
|
bank_forks
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.get(0)
|
||||||
|
.unwrap()
|
||||||
|
.process_transaction(&tx)
|
||||||
|
.unwrap();
|
||||||
|
rpc.subscriptions.notify_subscribers(0, &bank_forks);
|
||||||
|
|
||||||
|
let bank0 = bank_forks.read().unwrap()[0].clone();
|
||||||
|
let bank1 = Bank::new_from_parent(&bank0, &Pubkey::default(), 1);
|
||||||
|
bank_forks.write().unwrap().insert(bank1);
|
||||||
|
rpc.subscriptions.notify_subscribers(1, &bank_forks);
|
||||||
|
let bank1 = bank_forks.read().unwrap()[1].clone();
|
||||||
|
let bank2 = Bank::new_from_parent(&bank1, &Pubkey::default(), 2);
|
||||||
|
bank_forks.write().unwrap().insert(bank2);
|
||||||
|
rpc.subscriptions.notify_subscribers(2, &bank_forks);
|
||||||
|
let string = receiver.poll();
|
||||||
|
let expected = json!({
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "accountNotification",
|
||||||
|
"params": {
|
||||||
|
"result": {
|
||||||
|
"owner": system_program::id(),
|
||||||
|
"lamports": 100,
|
||||||
|
"data": [],
|
||||||
|
"executable": false,
|
||||||
|
},
|
||||||
|
"subscription": 0,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if let Async::Ready(Some(response)) = string.unwrap() {
|
||||||
|
assert_eq!(serde_json::to_string(&expected).unwrap(), response);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,44 +1,58 @@
|
||||||
//! The `pubsub` module implements a threaded subscription service on client RPC request
|
//! The `pubsub` module implements a threaded subscription service on client RPC request
|
||||||
|
|
||||||
|
use crate::bank_forks::BankForks;
|
||||||
use bs58;
|
use bs58;
|
||||||
use core::hash::Hash;
|
use core::hash::Hash;
|
||||||
use jsonrpc_core::futures::Future;
|
use jsonrpc_core::futures::Future;
|
||||||
use jsonrpc_pubsub::typed::Sink;
|
use jsonrpc_pubsub::typed::Sink;
|
||||||
use jsonrpc_pubsub::SubscriptionId;
|
use jsonrpc_pubsub::SubscriptionId;
|
||||||
|
use serde::Serialize;
|
||||||
use solana_runtime::bank::Bank;
|
use solana_runtime::bank::Bank;
|
||||||
use solana_sdk::account::Account;
|
use solana_sdk::account::Account;
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey::Pubkey;
|
||||||
use solana_sdk::signature::Signature;
|
use solana_sdk::signature::Signature;
|
||||||
use solana_sdk::transaction;
|
use solana_sdk::transaction;
|
||||||
|
use solana_vote_api::vote_state::MAX_LOCKOUT_HISTORY;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::RwLock;
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
type RpcAccountSubscriptions = RwLock<HashMap<Pubkey, HashMap<SubscriptionId, Sink<Account>>>>;
|
pub type Confirmations = usize;
|
||||||
|
|
||||||
|
type RpcAccountSubscriptions =
|
||||||
|
RwLock<HashMap<Pubkey, HashMap<SubscriptionId, (Sink<Account>, Confirmations)>>>;
|
||||||
type RpcProgramSubscriptions =
|
type RpcProgramSubscriptions =
|
||||||
RwLock<HashMap<Pubkey, HashMap<SubscriptionId, Sink<(String, Account)>>>>;
|
RwLock<HashMap<Pubkey, HashMap<SubscriptionId, (Sink<(String, Account)>, Confirmations)>>>;
|
||||||
type RpcSignatureSubscriptions =
|
type RpcSignatureSubscriptions = RwLock<
|
||||||
RwLock<HashMap<Signature, HashMap<SubscriptionId, Sink<Option<transaction::Result<()>>>>>>;
|
HashMap<Signature, HashMap<SubscriptionId, (Sink<transaction::Result<()>>, Confirmations)>>,
|
||||||
|
>;
|
||||||
|
|
||||||
fn add_subscription<K, S>(
|
fn add_subscription<K, S>(
|
||||||
subscriptions: &mut HashMap<K, HashMap<SubscriptionId, Sink<S>>>,
|
subscriptions: &mut HashMap<K, HashMap<SubscriptionId, (Sink<S>, Confirmations)>>,
|
||||||
hashmap_key: &K,
|
hashmap_key: &K,
|
||||||
|
confirmations: Option<Confirmations>,
|
||||||
sub_id: &SubscriptionId,
|
sub_id: &SubscriptionId,
|
||||||
sink: &Sink<S>,
|
sink: &Sink<S>,
|
||||||
) where
|
) where
|
||||||
K: Eq + Hash + Clone + Copy,
|
K: Eq + Hash + Clone + Copy,
|
||||||
S: Clone,
|
S: Clone,
|
||||||
{
|
{
|
||||||
|
let confirmations = confirmations.unwrap_or(0);
|
||||||
|
let confirmations = if confirmations > MAX_LOCKOUT_HISTORY {
|
||||||
|
MAX_LOCKOUT_HISTORY
|
||||||
|
} else {
|
||||||
|
confirmations
|
||||||
|
};
|
||||||
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.clone(), sink.clone());
|
current_hashmap.insert(sub_id.clone(), (sink.clone(), confirmations));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let mut hashmap = HashMap::new();
|
let mut hashmap = HashMap::new();
|
||||||
hashmap.insert(sub_id.clone(), sink.clone());
|
hashmap.insert(sub_id.clone(), (sink.clone(), confirmations));
|
||||||
subscriptions.insert(*hashmap_key, hashmap);
|
subscriptions.insert(*hashmap_key, hashmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_subscription<K, S>(
|
fn remove_subscription<K, S>(
|
||||||
subscriptions: &mut HashMap<K, HashMap<SubscriptionId, Sink<S>>>,
|
subscriptions: &mut HashMap<K, HashMap<SubscriptionId, (Sink<S>, Confirmations)>>,
|
||||||
sub_id: &SubscriptionId,
|
sub_id: &SubscriptionId,
|
||||||
) -> bool
|
) -> bool
|
||||||
where
|
where
|
||||||
|
@ -58,6 +72,84 @@ where
|
||||||
found
|
found
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_confirmations_and_notify<K, S, F, N, X>(
|
||||||
|
subscriptions: &HashMap<K, HashMap<SubscriptionId, (Sink<S>, Confirmations)>>,
|
||||||
|
hashmap_key: &K,
|
||||||
|
current_slot: u64,
|
||||||
|
bank_forks: &Arc<RwLock<BankForks>>,
|
||||||
|
bank_method: F,
|
||||||
|
notify: N,
|
||||||
|
) where
|
||||||
|
K: Eq + Hash + Clone + Copy,
|
||||||
|
S: Clone + Serialize,
|
||||||
|
F: Fn(&Bank, &K) -> X,
|
||||||
|
N: Fn(X, &Sink<S>, u64),
|
||||||
|
X: Clone + Serialize,
|
||||||
|
{
|
||||||
|
let current_ancestors = bank_forks
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(current_slot)
|
||||||
|
.unwrap()
|
||||||
|
.ancestors
|
||||||
|
.clone();
|
||||||
|
if let Some(hashmap) = subscriptions.get(hashmap_key) {
|
||||||
|
for (_bank_sub_id, (sink, confirmations)) in hashmap.iter() {
|
||||||
|
let desired_slot: Vec<u64> = current_ancestors
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, &v)| v == *confirmations)
|
||||||
|
.map(|(k, _)| k)
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
let root: Vec<u64> = current_ancestors
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, &v)| v == 32)
|
||||||
|
.map(|(k, _)| k)
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
let root = if root.len() == 1 { root[0] } else { 0 };
|
||||||
|
if desired_slot.len() == 1 {
|
||||||
|
let desired_bank = bank_forks
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(desired_slot[0])
|
||||||
|
.unwrap()
|
||||||
|
.clone();
|
||||||
|
let result = bank_method(&desired_bank, hashmap_key);
|
||||||
|
notify(result, &sink, root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify_account<S>(result: Option<(S, u64)>, sink: &Sink<S>, root: u64)
|
||||||
|
where
|
||||||
|
S: Clone + Serialize,
|
||||||
|
{
|
||||||
|
if let Some((account, fork)) = result {
|
||||||
|
if fork >= root {
|
||||||
|
sink.notify(Ok(account)).wait().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify_signature<S>(result: Option<S>, sink: &Sink<S>, _root: u64)
|
||||||
|
where
|
||||||
|
S: Clone + Serialize,
|
||||||
|
{
|
||||||
|
if let Some(result) = result {
|
||||||
|
sink.notify(Ok(result)).wait().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify_program(accounts: Vec<(Pubkey, Account)>, sink: &Sink<(String, Account)>, _root: u64) {
|
||||||
|
for (pubkey, account) in accounts.iter() {
|
||||||
|
sink.notify(Ok((bs58::encode(pubkey).into_string(), account.clone())))
|
||||||
|
.wait()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct RpcSubscriptions {
|
pub struct RpcSubscriptions {
|
||||||
account_subscriptions: RpcAccountSubscriptions,
|
account_subscriptions: RpcAccountSubscriptions,
|
||||||
program_subscriptions: RpcProgramSubscriptions,
|
program_subscriptions: RpcProgramSubscriptions,
|
||||||
|
@ -75,44 +167,67 @@ impl Default for RpcSubscriptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RpcSubscriptions {
|
impl RpcSubscriptions {
|
||||||
pub fn check_account(&self, pubkey: &Pubkey, account: &Account) {
|
pub fn check_account(
|
||||||
|
&self,
|
||||||
|
pubkey: &Pubkey,
|
||||||
|
current_slot: u64,
|
||||||
|
bank_forks: &Arc<RwLock<BankForks>>,
|
||||||
|
) {
|
||||||
let subscriptions = self.account_subscriptions.read().unwrap();
|
let subscriptions = self.account_subscriptions.read().unwrap();
|
||||||
if let Some(hashmap) = subscriptions.get(pubkey) {
|
check_confirmations_and_notify(
|
||||||
for (_bank_sub_id, sink) in hashmap.iter() {
|
&subscriptions,
|
||||||
sink.notify(Ok(account.clone())).wait().unwrap();
|
pubkey,
|
||||||
}
|
current_slot,
|
||||||
}
|
bank_forks,
|
||||||
|
Bank::get_account_modified_since_parent,
|
||||||
|
notify_account,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_program(&self, program_id: &Pubkey, pubkey: &Pubkey, account: &Account) {
|
pub fn check_program(
|
||||||
|
&self,
|
||||||
|
program_id: &Pubkey,
|
||||||
|
current_slot: u64,
|
||||||
|
bank_forks: &Arc<RwLock<BankForks>>,
|
||||||
|
) {
|
||||||
let subscriptions = self.program_subscriptions.write().unwrap();
|
let subscriptions = self.program_subscriptions.write().unwrap();
|
||||||
if let Some(hashmap) = subscriptions.get(program_id) {
|
check_confirmations_and_notify(
|
||||||
for (_bank_sub_id, sink) in hashmap.iter() {
|
&subscriptions,
|
||||||
sink.notify(Ok((bs58::encode(pubkey).into_string(), account.clone())))
|
program_id,
|
||||||
.wait()
|
current_slot,
|
||||||
.unwrap();
|
bank_forks,
|
||||||
}
|
Bank::get_program_accounts_modified_since_parent,
|
||||||
}
|
notify_program,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_signature(&self, signature: &Signature, bank_error: &transaction::Result<()>) {
|
pub fn check_signature(
|
||||||
|
&self,
|
||||||
|
signature: &Signature,
|
||||||
|
current_slot: u64,
|
||||||
|
bank_forks: &Arc<RwLock<BankForks>>,
|
||||||
|
) {
|
||||||
let mut subscriptions = self.signature_subscriptions.write().unwrap();
|
let mut subscriptions = self.signature_subscriptions.write().unwrap();
|
||||||
if let Some(hashmap) = subscriptions.get(signature) {
|
check_confirmations_and_notify(
|
||||||
for (_bank_sub_id, sink) in hashmap.iter() {
|
&subscriptions,
|
||||||
sink.notify(Ok(Some(bank_error.clone()))).wait().unwrap();
|
signature,
|
||||||
}
|
current_slot,
|
||||||
}
|
bank_forks,
|
||||||
|
Bank::get_signature_status,
|
||||||
|
notify_signature,
|
||||||
|
);
|
||||||
subscriptions.remove(&signature);
|
subscriptions.remove(&signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_account_subscription(
|
pub fn add_account_subscription(
|
||||||
&self,
|
&self,
|
||||||
pubkey: &Pubkey,
|
pubkey: &Pubkey,
|
||||||
|
confirmations: Option<Confirmations>,
|
||||||
sub_id: &SubscriptionId,
|
sub_id: &SubscriptionId,
|
||||||
sink: &Sink<Account>,
|
sink: &Sink<Account>,
|
||||||
) {
|
) {
|
||||||
let mut subscriptions = self.account_subscriptions.write().unwrap();
|
let mut subscriptions = self.account_subscriptions.write().unwrap();
|
||||||
add_subscription(&mut subscriptions, pubkey, sub_id, sink);
|
add_subscription(&mut subscriptions, pubkey, confirmations, sub_id, sink);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_account_subscription(&self, id: &SubscriptionId) -> bool {
|
pub fn remove_account_subscription(&self, id: &SubscriptionId) -> bool {
|
||||||
|
@ -123,11 +238,12 @@ impl RpcSubscriptions {
|
||||||
pub fn add_program_subscription(
|
pub fn add_program_subscription(
|
||||||
&self,
|
&self,
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
|
confirmations: Option<Confirmations>,
|
||||||
sub_id: &SubscriptionId,
|
sub_id: &SubscriptionId,
|
||||||
sink: &Sink<(String, Account)>,
|
sink: &Sink<(String, Account)>,
|
||||||
) {
|
) {
|
||||||
let mut subscriptions = self.program_subscriptions.write().unwrap();
|
let mut subscriptions = self.program_subscriptions.write().unwrap();
|
||||||
add_subscription(&mut subscriptions, program_id, sub_id, sink);
|
add_subscription(&mut subscriptions, program_id, confirmations, sub_id, sink);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_program_subscription(&self, id: &SubscriptionId) -> bool {
|
pub fn remove_program_subscription(&self, id: &SubscriptionId) -> bool {
|
||||||
|
@ -138,11 +254,12 @@ impl RpcSubscriptions {
|
||||||
pub fn add_signature_subscription(
|
pub fn add_signature_subscription(
|
||||||
&self,
|
&self,
|
||||||
signature: &Signature,
|
signature: &Signature,
|
||||||
|
confirmations: Option<Confirmations>,
|
||||||
sub_id: &SubscriptionId,
|
sub_id: &SubscriptionId,
|
||||||
sink: &Sink<Option<transaction::Result<()>>>,
|
sink: &Sink<transaction::Result<()>>,
|
||||||
) {
|
) {
|
||||||
let mut subscriptions = self.signature_subscriptions.write().unwrap();
|
let mut subscriptions = self.signature_subscriptions.write().unwrap();
|
||||||
add_subscription(&mut subscriptions, signature, sub_id, sink);
|
add_subscription(&mut subscriptions, signature, confirmations, sub_id, sink);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_signature_subscription(&self, id: &SubscriptionId) -> bool {
|
pub fn remove_signature_subscription(&self, id: &SubscriptionId) -> bool {
|
||||||
|
@ -152,15 +269,13 @@ impl RpcSubscriptions {
|
||||||
|
|
||||||
/// Notify subscribers of changes to any accounts or new signatures since
|
/// Notify subscribers of changes to any accounts or new signatures since
|
||||||
/// the bank's last checkpoint.
|
/// the bank's last checkpoint.
|
||||||
pub fn notify_subscribers(&self, bank: &Bank) {
|
pub fn notify_subscribers(&self, current_slot: u64, bank_forks: &Arc<RwLock<BankForks>>) {
|
||||||
let pubkeys: Vec<_> = {
|
let pubkeys: Vec<_> = {
|
||||||
let subs = self.account_subscriptions.read().unwrap();
|
let subs = self.account_subscriptions.read().unwrap();
|
||||||
subs.keys().cloned().collect()
|
subs.keys().cloned().collect()
|
||||||
};
|
};
|
||||||
for pubkey in &pubkeys {
|
for pubkey in &pubkeys {
|
||||||
if let Some(account) = &bank.get_account_modified_since_parent(pubkey) {
|
self.check_account(pubkey, current_slot, bank_forks);
|
||||||
self.check_account(pubkey, account);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let programs: Vec<_> = {
|
let programs: Vec<_> = {
|
||||||
|
@ -168,10 +283,7 @@ impl RpcSubscriptions {
|
||||||
subs.keys().cloned().collect()
|
subs.keys().cloned().collect()
|
||||||
};
|
};
|
||||||
for program_id in &programs {
|
for program_id in &programs {
|
||||||
let accounts = &bank.get_program_accounts_modified_since_parent(program_id);
|
self.check_program(program_id, current_slot, bank_forks);
|
||||||
for (pubkey, account) in accounts.iter() {
|
|
||||||
self.check_program(program_id, pubkey, account);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let signatures: Vec<_> = {
|
let signatures: Vec<_> = {
|
||||||
|
@ -179,8 +291,7 @@ impl RpcSubscriptions {
|
||||||
subs.keys().cloned().collect()
|
subs.keys().cloned().collect()
|
||||||
};
|
};
|
||||||
for signature in &signatures {
|
for signature in &signatures {
|
||||||
let status = bank.get_signature_status(signature).unwrap();
|
self.check_signature(signature, current_slot, bank_forks);
|
||||||
self.check_signature(signature, &status);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,8 +310,9 @@ mod tests {
|
||||||
fn test_check_account_subscribe() {
|
fn test_check_account_subscribe() {
|
||||||
let (genesis_block, mint_keypair) = GenesisBlock::new(100);
|
let (genesis_block, mint_keypair) = GenesisBlock::new(100);
|
||||||
let bank = Bank::new(&genesis_block);
|
let bank = Bank::new(&genesis_block);
|
||||||
let alice = Keypair::new();
|
|
||||||
let blockhash = bank.last_blockhash();
|
let blockhash = bank.last_blockhash();
|
||||||
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(0, bank)));
|
||||||
|
let alice = Keypair::new();
|
||||||
let tx = system_transaction::create_account(
|
let tx = system_transaction::create_account(
|
||||||
&mint_keypair,
|
&mint_keypair,
|
||||||
&alice.pubkey(),
|
&alice.pubkey(),
|
||||||
|
@ -210,14 +322,20 @@ mod tests {
|
||||||
&solana_budget_api::id(),
|
&solana_budget_api::id(),
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
bank.process_transaction(&tx).unwrap();
|
bank_forks
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.get(0)
|
||||||
|
.unwrap()
|
||||||
|
.process_transaction(&tx)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let (subscriber, _id_receiver, mut transport_receiver) =
|
let (subscriber, _id_receiver, mut transport_receiver) =
|
||||||
Subscriber::new_test("accountNotification");
|
Subscriber::new_test("accountNotification");
|
||||||
let sub_id = SubscriptionId::Number(0 as u64);
|
let sub_id = SubscriptionId::Number(0 as u64);
|
||||||
let sink = subscriber.assign_id(sub_id.clone()).unwrap();
|
let sink = subscriber.assign_id(sub_id.clone()).unwrap();
|
||||||
let subscriptions = RpcSubscriptions::default();
|
let subscriptions = RpcSubscriptions::default();
|
||||||
subscriptions.add_account_subscription(&alice.pubkey(), &sub_id, &sink);
|
subscriptions.add_account_subscription(&alice.pubkey(), None, &sub_id, &sink);
|
||||||
|
|
||||||
assert!(subscriptions
|
assert!(subscriptions
|
||||||
.account_subscriptions
|
.account_subscriptions
|
||||||
|
@ -225,8 +343,7 @@ mod tests {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.contains_key(&alice.pubkey()));
|
.contains_key(&alice.pubkey()));
|
||||||
|
|
||||||
let account = bank.get_account(&alice.pubkey()).unwrap();
|
subscriptions.check_account(&alice.pubkey(), 0, &bank_forks);
|
||||||
subscriptions.check_account(&alice.pubkey(), &account);
|
|
||||||
let string = transport_receiver.poll();
|
let string = transport_receiver.poll();
|
||||||
if let Async::Ready(Some(response)) = string.unwrap() {
|
if let Async::Ready(Some(response)) = string.unwrap() {
|
||||||
let expected = format!(r#"{{"jsonrpc":"2.0","method":"accountNotification","params":{{"result":{{"data":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"executable":false,"lamports":1,"owner":[2,203,81,223,225,24,34,35,203,214,138,130,144,208,35,77,63,16,87,51,47,198,115,123,98,188,19,160,0,0,0,0]}},"subscription":0}}}}"#);
|
let expected = format!(r#"{{"jsonrpc":"2.0","method":"accountNotification","params":{{"result":{{"data":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"executable":false,"lamports":1,"owner":[2,203,81,223,225,24,34,35,203,214,138,130,144,208,35,77,63,16,87,51,47,198,115,123,98,188,19,160,0,0,0,0]}},"subscription":0}}}}"#);
|
||||||
|
@ -245,8 +362,9 @@ mod tests {
|
||||||
fn test_check_program_subscribe() {
|
fn test_check_program_subscribe() {
|
||||||
let (genesis_block, mint_keypair) = GenesisBlock::new(100);
|
let (genesis_block, mint_keypair) = GenesisBlock::new(100);
|
||||||
let bank = Bank::new(&genesis_block);
|
let bank = Bank::new(&genesis_block);
|
||||||
let alice = Keypair::new();
|
|
||||||
let blockhash = bank.last_blockhash();
|
let blockhash = bank.last_blockhash();
|
||||||
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(0, bank)));
|
||||||
|
let alice = Keypair::new();
|
||||||
let tx = system_transaction::create_account(
|
let tx = system_transaction::create_account(
|
||||||
&mint_keypair,
|
&mint_keypair,
|
||||||
&alice.pubkey(),
|
&alice.pubkey(),
|
||||||
|
@ -256,14 +374,20 @@ mod tests {
|
||||||
&solana_budget_api::id(),
|
&solana_budget_api::id(),
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
bank.process_transaction(&tx).unwrap();
|
bank_forks
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.get(0)
|
||||||
|
.unwrap()
|
||||||
|
.process_transaction(&tx)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let (subscriber, _id_receiver, mut transport_receiver) =
|
let (subscriber, _id_receiver, mut transport_receiver) =
|
||||||
Subscriber::new_test("programNotification");
|
Subscriber::new_test("programNotification");
|
||||||
let sub_id = SubscriptionId::Number(0 as u64);
|
let sub_id = SubscriptionId::Number(0 as u64);
|
||||||
let sink = subscriber.assign_id(sub_id.clone()).unwrap();
|
let sink = subscriber.assign_id(sub_id.clone()).unwrap();
|
||||||
let subscriptions = RpcSubscriptions::default();
|
let subscriptions = RpcSubscriptions::default();
|
||||||
subscriptions.add_program_subscription(&solana_budget_api::id(), &sub_id, &sink);
|
subscriptions.add_program_subscription(&solana_budget_api::id(), None, &sub_id, &sink);
|
||||||
|
|
||||||
assert!(subscriptions
|
assert!(subscriptions
|
||||||
.program_subscriptions
|
.program_subscriptions
|
||||||
|
@ -271,8 +395,7 @@ mod tests {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.contains_key(&solana_budget_api::id()));
|
.contains_key(&solana_budget_api::id()));
|
||||||
|
|
||||||
let account = bank.get_account(&alice.pubkey()).unwrap();
|
subscriptions.check_program(&solana_budget_api::id(), 0, &bank_forks);
|
||||||
subscriptions.check_program(&solana_budget_api::id(), &alice.pubkey(), &account);
|
|
||||||
let string = transport_receiver.poll();
|
let string = transport_receiver.poll();
|
||||||
if let Async::Ready(Some(response)) = string.unwrap() {
|
if let Async::Ready(Some(response)) = string.unwrap() {
|
||||||
let expected = format!(r#"{{"jsonrpc":"2.0","method":"programNotification","params":{{"result":["{:?}",{{"data":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"executable":false,"lamports":1,"owner":[2,203,81,223,225,24,34,35,203,214,138,130,144,208,35,77,63,16,87,51,47,198,115,123,98,188,19,160,0,0,0,0]}}],"subscription":0}}}}"#, alice.pubkey());
|
let expected = format!(r#"{{"jsonrpc":"2.0","method":"programNotification","params":{{"result":["{:?}",{{"data":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"executable":false,"lamports":1,"owner":[2,203,81,223,225,24,34,35,203,214,138,130,144,208,35,77,63,16,87,51,47,198,115,123,98,188,19,160,0,0,0,0]}}],"subscription":0}}}}"#, alice.pubkey());
|
||||||
|
@ -290,18 +413,25 @@ mod tests {
|
||||||
fn test_check_signature_subscribe() {
|
fn test_check_signature_subscribe() {
|
||||||
let (genesis_block, mint_keypair) = GenesisBlock::new(100);
|
let (genesis_block, mint_keypair) = GenesisBlock::new(100);
|
||||||
let bank = Bank::new(&genesis_block);
|
let bank = Bank::new(&genesis_block);
|
||||||
let alice = Keypair::new();
|
|
||||||
let blockhash = bank.last_blockhash();
|
let blockhash = bank.last_blockhash();
|
||||||
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(0, bank)));
|
||||||
|
let alice = Keypair::new();
|
||||||
let tx = system_transaction::transfer(&mint_keypair, &alice.pubkey(), 20, blockhash, 0);
|
let tx = system_transaction::transfer(&mint_keypair, &alice.pubkey(), 20, blockhash, 0);
|
||||||
let signature = tx.signatures[0];
|
let signature = tx.signatures[0];
|
||||||
bank.process_transaction(&tx).unwrap();
|
bank_forks
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.get(0)
|
||||||
|
.unwrap()
|
||||||
|
.process_transaction(&tx)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let (subscriber, _id_receiver, mut transport_receiver) =
|
let (subscriber, _id_receiver, mut transport_receiver) =
|
||||||
Subscriber::new_test("signatureNotification");
|
Subscriber::new_test("signatureNotification");
|
||||||
let sub_id = SubscriptionId::Number(0 as u64);
|
let sub_id = SubscriptionId::Number(0 as u64);
|
||||||
let sink = subscriber.assign_id(sub_id.clone()).unwrap();
|
let sink = subscriber.assign_id(sub_id.clone()).unwrap();
|
||||||
let subscriptions = RpcSubscriptions::default();
|
let subscriptions = RpcSubscriptions::default();
|
||||||
subscriptions.add_signature_subscription(&signature, &sub_id, &sink);
|
subscriptions.add_signature_subscription(&signature, None, &sub_id, &sink);
|
||||||
|
|
||||||
assert!(subscriptions
|
assert!(subscriptions
|
||||||
.signature_subscriptions
|
.signature_subscriptions
|
||||||
|
@ -309,7 +439,7 @@ mod tests {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.contains_key(&signature));
|
.contains_key(&signature));
|
||||||
|
|
||||||
subscriptions.check_signature(&signature, &Ok(()));
|
subscriptions.check_signature(&signature, 0, &bank_forks);
|
||||||
let string = transport_receiver.poll();
|
let string = transport_receiver.poll();
|
||||||
if let Async::Ready(Some(response)) = string.unwrap() {
|
if let Async::Ready(Some(response)) = string.unwrap() {
|
||||||
let expected_res: Option<transaction::Result<()>> = Some(Ok(()));
|
let expected_res: Option<transaction::Result<()>> = Some(Ok(()));
|
||||||
|
|
|
@ -163,7 +163,9 @@ impl Accounts {
|
||||||
let mut called_accounts: Vec<Account> = vec![];
|
let mut called_accounts: Vec<Account> = vec![];
|
||||||
for key in &message.account_keys {
|
for key in &message.account_keys {
|
||||||
called_accounts.push(
|
called_accounts.push(
|
||||||
AccountsDB::load(storage, ancestors, accounts_index, key).unwrap_or_default(),
|
AccountsDB::load(storage, ancestors, accounts_index, key)
|
||||||
|
.map(|(account, _)| account)
|
||||||
|
.unwrap_or_default(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if called_accounts.is_empty() || called_accounts[0].lamports == 0 {
|
if called_accounts.is_empty() || called_accounts[0].lamports == 0 {
|
||||||
|
@ -201,7 +203,9 @@ impl Accounts {
|
||||||
}
|
}
|
||||||
depth += 1;
|
depth += 1;
|
||||||
|
|
||||||
let program = match AccountsDB::load(storage, ancestors, accounts_index, &program_id) {
|
let program = match AccountsDB::load(storage, ancestors, accounts_index, &program_id)
|
||||||
|
.map(|(account, _)| account)
|
||||||
|
{
|
||||||
Some(program) => program,
|
Some(program) => program,
|
||||||
None => {
|
None => {
|
||||||
error_counters.account_not_found += 1;
|
error_counters.account_not_found += 1;
|
||||||
|
@ -289,10 +293,14 @@ impl Accounts {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Slow because lock is held for 1 operation instead of many
|
/// Slow because lock is held for 1 operation instead of many
|
||||||
pub fn load_slow(&self, ancestors: &HashMap<Fork, usize>, pubkey: &Pubkey) -> Option<Account> {
|
pub fn load_slow(
|
||||||
|
&self,
|
||||||
|
ancestors: &HashMap<Fork, usize>,
|
||||||
|
pubkey: &Pubkey,
|
||||||
|
) -> Option<(Account, Fork)> {
|
||||||
self.accounts_db
|
self.accounts_db
|
||||||
.load_slow(ancestors, pubkey)
|
.load_slow(ancestors, pubkey)
|
||||||
.filter(|acc| acc.lamports != 0)
|
.filter(|(acc, _)| acc.lamports != 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_by_program(&self, fork: Fork, program_id: &Pubkey) -> Vec<(Pubkey, Account)> {
|
pub fn load_by_program(&self, fork: Fork, program_id: &Pubkey) -> Vec<(Pubkey, Account)> {
|
||||||
|
|
|
@ -259,15 +259,20 @@ impl AccountsDB {
|
||||||
ancestors: &HashMap<Fork, usize>,
|
ancestors: &HashMap<Fork, usize>,
|
||||||
accounts_index: &AccountsIndex<AccountInfo>,
|
accounts_index: &AccountsIndex<AccountInfo>,
|
||||||
pubkey: &Pubkey,
|
pubkey: &Pubkey,
|
||||||
) -> Option<Account> {
|
) -> Option<(Account, Fork)> {
|
||||||
let info = accounts_index.get(pubkey, ancestors)?;
|
let (info, fork) = accounts_index.get(pubkey, ancestors)?;
|
||||||
//TODO: thread this as a ref
|
//TODO: thread this as a ref
|
||||||
storage
|
storage
|
||||||
.get(&info.id)
|
.get(&info.id)
|
||||||
.and_then(|store| Some(store.accounts.get_account(info.offset)?.0.clone_account()))
|
.and_then(|store| Some(store.accounts.get_account(info.offset)?.0.clone_account()))
|
||||||
|
.map(|account| (account, fork))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_slow(&self, ancestors: &HashMap<Fork, usize>, pubkey: &Pubkey) -> Option<Account> {
|
pub fn load_slow(
|
||||||
|
&self,
|
||||||
|
ancestors: &HashMap<Fork, usize>,
|
||||||
|
pubkey: &Pubkey,
|
||||||
|
) -> Option<(Account, Fork)> {
|
||||||
let accounts_index = self.accounts_index.read().unwrap();
|
let accounts_index = self.accounts_index.read().unwrap();
|
||||||
let storage = self.storage.read().unwrap();
|
let storage = self.storage.read().unwrap();
|
||||||
Self::load(&storage, ancestors, &accounts_index, pubkey)
|
Self::load(&storage, ancestors, &accounts_index, pubkey)
|
||||||
|
@ -480,7 +485,7 @@ mod tests {
|
||||||
db.store(0, &[(&key, &account0)]);
|
db.store(0, &[(&key, &account0)]);
|
||||||
db.add_root(0);
|
db.add_root(0);
|
||||||
let ancestors = vec![(1, 1)].into_iter().collect();
|
let ancestors = vec![(1, 1)].into_iter().collect();
|
||||||
assert_eq!(db.load_slow(&ancestors, &key), Some(account0));
|
assert_eq!(db.load_slow(&ancestors, &key), Some((account0, 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -497,10 +502,10 @@ mod tests {
|
||||||
db.store(1, &[(&key, &account1)]);
|
db.store(1, &[(&key, &account1)]);
|
||||||
|
|
||||||
let ancestors = vec![(1, 1)].into_iter().collect();
|
let ancestors = vec![(1, 1)].into_iter().collect();
|
||||||
assert_eq!(&db.load_slow(&ancestors, &key).unwrap(), &account1);
|
assert_eq!(&db.load_slow(&ancestors, &key).unwrap().0, &account1);
|
||||||
|
|
||||||
let ancestors = vec![(1, 1), (0, 0)].into_iter().collect();
|
let ancestors = vec![(1, 1), (0, 0)].into_iter().collect();
|
||||||
assert_eq!(&db.load_slow(&ancestors, &key).unwrap(), &account1);
|
assert_eq!(&db.load_slow(&ancestors, &key).unwrap().0, &account1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -518,10 +523,10 @@ mod tests {
|
||||||
db.add_root(0);
|
db.add_root(0);
|
||||||
|
|
||||||
let ancestors = vec![(1, 1)].into_iter().collect();
|
let ancestors = vec![(1, 1)].into_iter().collect();
|
||||||
assert_eq!(&db.load_slow(&ancestors, &key).unwrap(), &account1);
|
assert_eq!(&db.load_slow(&ancestors, &key).unwrap().0, &account1);
|
||||||
|
|
||||||
let ancestors = vec![(1, 1), (0, 0)].into_iter().collect();
|
let ancestors = vec![(1, 1), (0, 0)].into_iter().collect();
|
||||||
assert_eq!(&db.load_slow(&ancestors, &key).unwrap(), &account1);
|
assert_eq!(&db.load_slow(&ancestors, &key).unwrap().0, &account1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -552,18 +557,18 @@ mod tests {
|
||||||
// original account (but could also accept "None", which is implemented
|
// original account (but could also accept "None", which is implemented
|
||||||
// at the Accounts level)
|
// at the Accounts level)
|
||||||
let ancestors = vec![(0, 0), (1, 1)].into_iter().collect();
|
let ancestors = vec![(0, 0), (1, 1)].into_iter().collect();
|
||||||
assert_eq!(&db.load_slow(&ancestors, &key).unwrap(), &account1);
|
assert_eq!(&db.load_slow(&ancestors, &key).unwrap().0, &account1);
|
||||||
|
|
||||||
// we should see 1 token in fork 2
|
// we should see 1 token in fork 2
|
||||||
let ancestors = vec![(0, 0), (2, 2)].into_iter().collect();
|
let ancestors = vec![(0, 0), (2, 2)].into_iter().collect();
|
||||||
assert_eq!(&db.load_slow(&ancestors, &key).unwrap(), &account0);
|
assert_eq!(&db.load_slow(&ancestors, &key).unwrap().0, &account0);
|
||||||
|
|
||||||
db.add_root(0);
|
db.add_root(0);
|
||||||
|
|
||||||
let ancestors = vec![(1, 1)].into_iter().collect();
|
let ancestors = vec![(1, 1)].into_iter().collect();
|
||||||
assert_eq!(db.load_slow(&ancestors, &key), Some(account1));
|
assert_eq!(db.load_slow(&ancestors, &key), Some((account1, 1)));
|
||||||
let ancestors = vec![(2, 2)].into_iter().collect();
|
let ancestors = vec![(2, 2)].into_iter().collect();
|
||||||
assert_eq!(db.load_slow(&ancestors, &key), Some(account0)); // original value
|
assert_eq!(db.load_slow(&ancestors, &key), Some((account0, 0))); // original value
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -579,7 +584,7 @@ mod tests {
|
||||||
let account = db.load_slow(&ancestors, &pubkeys[idx]).unwrap();
|
let account = db.load_slow(&ancestors, &pubkeys[idx]).unwrap();
|
||||||
let mut default_account = Account::default();
|
let mut default_account = Account::default();
|
||||||
default_account.lamports = (idx + 1) as u64;
|
default_account.lamports = (idx + 1) as u64;
|
||||||
assert_eq!(default_account, account);
|
assert_eq!((default_account, 0), account);
|
||||||
}
|
}
|
||||||
|
|
||||||
db.add_root(0);
|
db.add_root(0);
|
||||||
|
@ -593,8 +598,8 @@ mod tests {
|
||||||
let account1 = db.load_slow(&ancestors, &pubkeys[idx]).unwrap();
|
let account1 = db.load_slow(&ancestors, &pubkeys[idx]).unwrap();
|
||||||
let mut default_account = Account::default();
|
let mut default_account = Account::default();
|
||||||
default_account.lamports = (idx + 1) as u64;
|
default_account.lamports = (idx + 1) as u64;
|
||||||
assert_eq!(&default_account, &account0);
|
assert_eq!(&default_account, &account0.0);
|
||||||
assert_eq!(&default_account, &account1);
|
assert_eq!(&default_account, &account1.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -650,9 +655,9 @@ mod tests {
|
||||||
// masking accounts is done at the Accounts level, at accountsDB we see
|
// masking accounts is done at the Accounts level, at accountsDB we see
|
||||||
// original account
|
// original account
|
||||||
let ancestors = vec![(0, 0), (1, 1)].into_iter().collect();
|
let ancestors = vec![(0, 0), (1, 1)].into_iter().collect();
|
||||||
assert_eq!(db0.load_slow(&ancestors, &key), Some(account1));
|
assert_eq!(db0.load_slow(&ancestors, &key), Some((account1, 1)));
|
||||||
let ancestors = vec![(0, 0)].into_iter().collect();
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
||||||
assert_eq!(db0.load_slow(&ancestors, &key), Some(account0));
|
assert_eq!(db0.load_slow(&ancestors, &key), Some((account0, 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_account(
|
fn create_account(
|
||||||
|
@ -685,7 +690,7 @@ mod tests {
|
||||||
for _ in 1..1000 {
|
for _ in 1..1000 {
|
||||||
let idx = thread_rng().gen_range(0, range);
|
let idx = thread_rng().gen_range(0, range);
|
||||||
let ancestors = vec![(fork, 0)].into_iter().collect();
|
let ancestors = vec![(fork, 0)].into_iter().collect();
|
||||||
if let Some(mut account) = accounts.load_slow(&ancestors, &pubkeys[idx]) {
|
if let Some((mut account, _)) = accounts.load_slow(&ancestors, &pubkeys[idx]) {
|
||||||
account.lamports = account.lamports + 1;
|
account.lamports = account.lamports + 1;
|
||||||
accounts.store(fork, &[(&pubkeys[idx], &account)]);
|
accounts.store(fork, &[(&pubkeys[idx], &account)]);
|
||||||
if account.lamports == 0 {
|
if account.lamports == 0 {
|
||||||
|
@ -714,7 +719,7 @@ mod tests {
|
||||||
let account = accounts.load_slow(&ancestors, &pubkeys[idx]).unwrap();
|
let account = accounts.load_slow(&ancestors, &pubkeys[idx]).unwrap();
|
||||||
let mut default_account = Account::default();
|
let mut default_account = Account::default();
|
||||||
default_account.lamports = (idx + 1) as u64;
|
default_account.lamports = (idx + 1) as u64;
|
||||||
assert_eq!(default_account, account);
|
assert_eq!((default_account, 0), account);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -728,7 +733,7 @@ mod tests {
|
||||||
let account = accounts.load_slow(&ancestors, &pubkeys[0]).unwrap();
|
let account = accounts.load_slow(&ancestors, &pubkeys[0]).unwrap();
|
||||||
let mut default_account = Account::default();
|
let mut default_account = Account::default();
|
||||||
default_account.lamports = 1;
|
default_account.lamports = 1;
|
||||||
assert_eq!(default_account, account);
|
assert_eq!((default_account, 0), account);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -765,7 +770,7 @@ mod tests {
|
||||||
for (i, key) in keys.iter().enumerate() {
|
for (i, key) in keys.iter().enumerate() {
|
||||||
let ancestors = vec![(0, 0)].into_iter().collect();
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
accounts.load_slow(&ancestors, &key).unwrap().lamports,
|
accounts.load_slow(&ancestors, &key).unwrap().0.lamports,
|
||||||
(i as u64) + 1
|
(i as u64) + 1
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -810,8 +815,14 @@ mod tests {
|
||||||
assert_eq!(stores[&1].status(), AccountStorageStatus::StorageAvailable);
|
assert_eq!(stores[&1].status(), AccountStorageStatus::StorageAvailable);
|
||||||
}
|
}
|
||||||
let ancestors = vec![(0, 0)].into_iter().collect();
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
||||||
assert_eq!(accounts.load_slow(&ancestors, &pubkey1).unwrap(), account1);
|
assert_eq!(
|
||||||
assert_eq!(accounts.load_slow(&ancestors, &pubkey2).unwrap(), account2);
|
accounts.load_slow(&ancestors, &pubkey1).unwrap().0,
|
||||||
|
account1
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
accounts.load_slow(&ancestors, &pubkey2).unwrap().0,
|
||||||
|
account2
|
||||||
|
);
|
||||||
|
|
||||||
// lots of stores, but 3 storages should be enough for everything
|
// lots of stores, but 3 storages should be enough for everything
|
||||||
for i in 0..25 {
|
for i in 0..25 {
|
||||||
|
@ -828,8 +839,14 @@ mod tests {
|
||||||
assert_eq!(stores[&2].status(), status[0]);
|
assert_eq!(stores[&2].status(), status[0]);
|
||||||
}
|
}
|
||||||
let ancestors = vec![(0, 0)].into_iter().collect();
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
||||||
assert_eq!(accounts.load_slow(&ancestors, &pubkey1).unwrap(), account1);
|
assert_eq!(
|
||||||
assert_eq!(accounts.load_slow(&ancestors, &pubkey2).unwrap(), account2);
|
accounts.load_slow(&ancestors, &pubkey1).unwrap().0,
|
||||||
|
account1
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
accounts.load_slow(&ancestors, &pubkey2).unwrap().0,
|
||||||
|
account2
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -875,6 +892,7 @@ mod tests {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get(&pubkey, &ancestors)
|
.get(&pubkey, &ancestors)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
.0
|
||||||
.clone();
|
.clone();
|
||||||
//fork 0 is behind root, but it is not root, therefore it is purged
|
//fork 0 is behind root, but it is not root, therefore it is purged
|
||||||
accounts.add_root(1);
|
accounts.add_root(1);
|
||||||
|
@ -891,7 +909,7 @@ mod tests {
|
||||||
|
|
||||||
//new value is there
|
//new value is there
|
||||||
let ancestors = vec![(1, 1)].into_iter().collect();
|
let ancestors = vec![(1, 1)].into_iter().collect();
|
||||||
assert_eq!(accounts.load_slow(&ancestors, &pubkey), Some(account));;
|
assert_eq!(accounts.load_slow(&ancestors, &pubkey), Some((account, 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,14 +15,14 @@ pub struct AccountsIndex<T> {
|
||||||
impl<T: Clone> AccountsIndex<T> {
|
impl<T: Clone> AccountsIndex<T> {
|
||||||
/// Get an account
|
/// Get an account
|
||||||
/// The latest account that appears in `ancestors` or `roots` is returned.
|
/// The latest account that appears in `ancestors` or `roots` is returned.
|
||||||
pub fn get(&self, pubkey: &Pubkey, ancestors: &HashMap<Fork, usize>) -> Option<&T> {
|
pub fn get(&self, pubkey: &Pubkey, ancestors: &HashMap<Fork, usize>) -> Option<(&T, Fork)> {
|
||||||
let list = self.account_maps.get(pubkey)?;
|
let list = self.account_maps.get(pubkey)?;
|
||||||
let mut max = 0;
|
let mut max = 0;
|
||||||
let mut rv = None;
|
let mut rv = None;
|
||||||
for e in list.iter().rev() {
|
for e in list.iter().rev() {
|
||||||
if e.0 >= max && (ancestors.get(&e.0).is_some() || self.is_root(e.0)) {
|
if e.0 >= max && (ancestors.get(&e.0).is_some() || self.is_root(e.0)) {
|
||||||
trace!("GET {} {:?}", e.0, ancestors);
|
trace!("GET {} {:?}", e.0, ancestors);
|
||||||
rv = Some(&e.1);
|
rv = Some((&e.1, e.0));
|
||||||
max = e.0;
|
max = e.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ mod tests {
|
||||||
assert!(gc.is_empty());
|
assert!(gc.is_empty());
|
||||||
|
|
||||||
let ancestors = vec![(0, 0)].into_iter().collect();
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
||||||
assert_eq!(index.get(&key.pubkey(), &ancestors), Some(&true));
|
assert_eq!(index.get(&key.pubkey(), &ancestors), Some((&true, 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -143,7 +143,7 @@ mod tests {
|
||||||
|
|
||||||
let ancestors = vec![].into_iter().collect();
|
let ancestors = vec![].into_iter().collect();
|
||||||
index.add_root(0);
|
index.add_root(0);
|
||||||
assert_eq!(index.get(&key.pubkey(), &ancestors), Some(&true));
|
assert_eq!(index.get(&key.pubkey(), &ancestors), Some((&true, 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -199,11 +199,11 @@ mod tests {
|
||||||
let ancestors = vec![(0, 0)].into_iter().collect();
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
||||||
let gc = index.insert(0, &key.pubkey(), true);
|
let gc = index.insert(0, &key.pubkey(), true);
|
||||||
assert!(gc.is_empty());
|
assert!(gc.is_empty());
|
||||||
assert_eq!(index.get(&key.pubkey(), &ancestors), Some(&true));
|
assert_eq!(index.get(&key.pubkey(), &ancestors), Some((&true, 0)));
|
||||||
|
|
||||||
let gc = index.insert(0, &key.pubkey(), false);
|
let gc = index.insert(0, &key.pubkey(), false);
|
||||||
assert_eq!(gc, vec![(0, true)]);
|
assert_eq!(gc, vec![(0, true)]);
|
||||||
assert_eq!(index.get(&key.pubkey(), &ancestors), Some(&false));
|
assert_eq!(index.get(&key.pubkey(), &ancestors), Some((&false, 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -215,9 +215,9 @@ mod tests {
|
||||||
assert!(gc.is_empty());
|
assert!(gc.is_empty());
|
||||||
let gc = index.insert(1, &key.pubkey(), false);
|
let gc = index.insert(1, &key.pubkey(), false);
|
||||||
assert!(gc.is_empty());
|
assert!(gc.is_empty());
|
||||||
assert_eq!(index.get(&key.pubkey(), &ancestors), Some(&true));
|
assert_eq!(index.get(&key.pubkey(), &ancestors), Some((&true, 0)));
|
||||||
let ancestors = vec![(1, 0)].into_iter().collect();
|
let ancestors = vec![(1, 0)].into_iter().collect();
|
||||||
assert_eq!(index.get(&key.pubkey(), &ancestors), Some(&false));
|
assert_eq!(index.get(&key.pubkey(), &ancestors), Some((&false, 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -230,6 +230,6 @@ mod tests {
|
||||||
let gc = index.insert(1, &key.pubkey(), false);
|
let gc = index.insert(1, &key.pubkey(), false);
|
||||||
assert_eq!(gc, vec![(0, true)]);
|
assert_eq!(gc, vec![(0, true)]);
|
||||||
let ancestors = vec![].into_iter().collect();
|
let ancestors = vec![].into_iter().collect();
|
||||||
assert_eq!(index.get(&key.pubkey(), &ancestors), Some(&false));
|
assert_eq!(index.get(&key.pubkey(), &ancestors), Some((&false, 1)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
use crate::accounts::Accounts;
|
use crate::accounts::Accounts;
|
||||||
use crate::accounts_db::{ErrorCounters, InstructionAccounts, InstructionLoaders};
|
use crate::accounts_db::{ErrorCounters, InstructionAccounts, InstructionLoaders};
|
||||||
|
use crate::accounts_index::Fork;
|
||||||
use crate::blockhash_queue::BlockhashQueue;
|
use crate::blockhash_queue::BlockhashQueue;
|
||||||
use crate::locked_accounts_results::LockedAccountsResults;
|
use crate::locked_accounts_results::LockedAccountsResults;
|
||||||
use crate::message_processor::{MessageProcessor, ProcessInstruction};
|
use crate::message_processor::{MessageProcessor, ProcessInstruction};
|
||||||
|
@ -888,7 +889,9 @@ impl Bank {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_account(&self, pubkey: &Pubkey) -> Option<Account> {
|
pub fn get_account(&self, pubkey: &Pubkey) -> Option<Account> {
|
||||||
self.accounts.load_slow(&self.ancestors, pubkey)
|
self.accounts
|
||||||
|
.load_slow(&self.ancestors, pubkey)
|
||||||
|
.map(|(account, _)| account)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_program_accounts_modified_since_parent(
|
pub fn get_program_accounts_modified_since_parent(
|
||||||
|
@ -898,7 +901,7 @@ impl Bank {
|
||||||
self.accounts.load_by_program(self.slot(), program_id)
|
self.accounts.load_by_program(self.slot(), program_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_account_modified_since_parent(&self, pubkey: &Pubkey) -> Option<Account> {
|
pub fn get_account_modified_since_parent(&self, pubkey: &Pubkey) -> Option<(Account, Fork)> {
|
||||||
let just_self: HashMap<u64, usize> = vec![(self.slot(), 0)].into_iter().collect();
|
let just_self: HashMap<u64, usize> = vec![(self.slot(), 0)].into_iter().collect();
|
||||||
self.accounts.load_slow(&just_self, pubkey)
|
self.accounts.load_slow(&just_self, pubkey)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue