Gixing websocket subscriptions for signatures and slot (#403)

* Updating send transaction method signature

* fixing issues with pubsub

* remove test code
This commit is contained in:
galactus 2024-06-28 17:00:45 +02:00 committed by GitHub
parent f990f857ab
commit 129d6a830e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 196 additions and 183 deletions

View File

@ -182,6 +182,7 @@ fn create_grpc_multiplex_block_info_task(
.expect("block_time from geyser block meta")
.timestamp
as u64,
parent: block_meta.parent_slot,
};
let send_started_at = Instant::now();

View File

@ -324,5 +324,6 @@ fn map_block_info(produced_block: &ProducedBlock) -> BlockInfo {
blockhash: produced_block.blockhash,
commitment_config: produced_block.commitment_config,
block_time: produced_block.block_time,
parent: produced_block.parent_slot,
}
}

View File

@ -1,6 +1,6 @@
use solana_sdk::commitment_config::{CommitmentConfig, CommitmentLevel};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub enum Commitment {
Processed = 0,

View File

@ -1,45 +1,29 @@
use crate::commitment_utils::Commitment;
use crate::{structures::produced_block::TransactionInfo, types::SubscptionHanderSink};
use dashmap::DashMap;
use solana_client::rpc_response::{ProcessedSignatureResult, RpcSignatureResult};
use solana_sdk::signature::Signature;
use solana_sdk::{
commitment_config::{CommitmentConfig, CommitmentLevel},
slot_history::Slot,
};
use solana_sdk::{commitment_config::CommitmentConfig, slot_history::Slot};
use std::{sync::Arc, time::Duration};
use tokio::time::Instant;
#[derive(Clone, Default)]
pub struct SubscriptionStore {
pub signature_subscribers:
Arc<DashMap<(Signature, CommitmentConfig), (SubscptionHanderSink, Instant)>>,
Arc<DashMap<(Signature, Commitment), (SubscptionHanderSink, Instant)>>,
}
impl SubscriptionStore {
#[allow(deprecated)]
pub fn get_supported_commitment_config(
commitment_config: CommitmentConfig,
) -> CommitmentConfig {
match commitment_config.commitment {
CommitmentLevel::Finalized | CommitmentLevel::Root | CommitmentLevel::Max => {
CommitmentConfig {
commitment: CommitmentLevel::Finalized,
}
}
_ => CommitmentConfig {
commitment: CommitmentLevel::Confirmed,
},
}
}
pub fn signature_subscribe(
&self,
signature: Signature,
commitment_config: CommitmentConfig,
sink: SubscptionHanderSink,
) {
let commitment_config = Self::get_supported_commitment_config(commitment_config);
self.signature_subscribers
.insert((signature, commitment_config), (sink, Instant::now()));
self.signature_subscribers.insert(
(signature, Commitment::from(commitment_config)),
(sink, Instant::now()),
);
}
pub fn signature_un_subscribe(
@ -47,9 +31,8 @@ impl SubscriptionStore {
signature: Signature,
commitment_config: CommitmentConfig,
) {
let commitment_config = Self::get_supported_commitment_config(commitment_config);
self.signature_subscribers
.remove(&(signature, commitment_config));
.remove(&(signature, Commitment::from(commitment_config)));
}
pub async fn notify(
@ -58,13 +41,20 @@ impl SubscriptionStore {
transaction_info: &TransactionInfo,
commitment_config: CommitmentConfig,
) {
if let Some((_sig, (sink, _))) = self
.signature_subscribers
.remove(&(transaction_info.signature, commitment_config))
{
if let Some((_sig, (sink, _))) = self.signature_subscribers.remove(&(
transaction_info.signature,
Commitment::from(commitment_config),
)) {
let signature_result =
RpcSignatureResult::ProcessedSignature(ProcessedSignatureResult {
err: transaction_info.err.clone(),
});
// none if transaction succeeded
sink.send(slot, serde_json::json!({ "err": transaction_info.err }))
.await;
sink.send(
slot,
serde_json::to_value(signature_result).expect("Should be serializable in json"),
)
.await;
}
}

View File

@ -4,6 +4,7 @@ use solana_sdk::hash::Hash;
#[derive(Clone, Debug)]
pub struct BlockInfo {
pub slot: u64,
pub parent: u64,
pub block_height: u64,
pub blockhash: Hash,
pub commitment_config: CommitmentConfig,

View File

@ -3,10 +3,11 @@ use jsonrpsee::core::RpcResult;
use prometheus::{opts, register_int_counter, IntCounter};
use solana_account_decoder::UiAccount;
use solana_lite_rpc_accounts::account_service::AccountService;
use solana_lite_rpc_core::encoding::{BASE58, BASE64};
use solana_lite_rpc_prioritization_fees::account_prio_service::AccountPrioService;
use solana_lite_rpc_prioritization_fees::prioritization_fee_calculation_method::PrioritizationFeeCalculationMethod;
use solana_rpc_client::nonblocking::rpc_client::RpcClient;
use solana_rpc_client_api::config::RpcAccountInfoConfig;
use solana_rpc_client_api::config::{RpcAccountInfoConfig, RpcSendTransactionConfig};
use solana_rpc_client_api::response::{OptionalContext, RpcKeyedAccount};
use solana_rpc_client_api::{
config::{
@ -21,28 +22,27 @@ use solana_rpc_client_api::{
},
};
use solana_sdk::epoch_info::EpochInfo;
use solana_sdk::packet::PACKET_DATA_SIZE;
use solana_sdk::signature::Signature;
use solana_sdk::{commitment_config::CommitmentConfig, pubkey::Pubkey, slot_history::Slot};
use solana_transaction_status::{TransactionStatus, UiConfirmedBlock};
use solana_transaction_status::{
TransactionBinaryEncoding, TransactionStatus, UiConfirmedBlock, UiTransactionEncoding,
};
use std::collections::HashMap;
use std::str::FromStr;
use std::sync::Arc;
use solana_lite_rpc_blockstore::history::History;
use solana_lite_rpc_core::solana_utils::hash_from_str;
use solana_lite_rpc_core::{
encoding,
stores::{block_information_store::BlockInformation, data_cache::DataCache},
use solana_lite_rpc_core::stores::{
block_information_store::BlockInformation, data_cache::DataCache,
};
use solana_lite_rpc_services::{
transaction_service::TransactionService, tx_sender::TXS_IN_CHANNEL,
};
use crate::rpc_errors::RpcErrors;
use crate::{
configs::{IsBlockHashValidConfig, SendTransactionConfig},
rpc::LiteRpcServer,
};
use crate::{configs::IsBlockHashValidConfig, rpc::LiteRpcServer};
use solana_lite_rpc_prioritization_fees::rpc_data::{AccountPrioFeesStats, PrioFeesStats};
use solana_lite_rpc_prioritization_fees::PrioFeesService;
@ -347,7 +347,7 @@ impl LiteRpcServer for LiteBridge {
async fn send_transaction(
&self,
tx: String,
send_transaction_config: Option<SendTransactionConfig>,
send_transaction_config: Option<RpcSendTransactionConfig>,
) -> RpcResult<String> {
RPC_SEND_TX.inc();
@ -355,29 +355,51 @@ impl LiteRpcServer for LiteBridge {
const MAX_BASE58_SIZE: usize = 1683;
const MAX_BASE64_SIZE: usize = 1644;
let SendTransactionConfig {
let RpcSendTransactionConfig {
encoding,
max_retries,
..
} = send_transaction_config.unwrap_or_default();
let encoding = encoding.unwrap_or(UiTransactionEncoding::Base58);
let expected_size = match encoding {
encoding::BinaryEncoding::Base58 => MAX_BASE58_SIZE,
encoding::BinaryEncoding::Base64 => MAX_BASE64_SIZE,
UiTransactionEncoding::Base58 => MAX_BASE58_SIZE,
UiTransactionEncoding::Base64 => MAX_BASE64_SIZE,
_ => usize::MAX,
};
if tx.len() > expected_size {
return Err(jsonrpsee::types::error::ErrorCode::OversizedRequest.into());
}
let raw_tx = match encoding.decode(tx) {
Ok(raw_tx) => raw_tx,
Err(_) => {
return Err(jsonrpsee::types::error::ErrorCode::InvalidParams.into());
let binary_encoding = encoding
.into_binary_encoding()
.ok_or(jsonrpsee::types::error::ErrorCode::InvalidParams)?;
let wire_output = match binary_encoding {
TransactionBinaryEncoding::Base58 => {
if tx.len() > MAX_BASE58_SIZE {
return Err(jsonrpsee::types::error::ErrorCode::OversizedRequest.into());
}
BASE58
.decode(tx)
.map_err(|_| jsonrpsee::types::error::ErrorCode::InvalidParams)?
}
TransactionBinaryEncoding::Base64 => {
if tx.len() > MAX_BASE64_SIZE {
return Err(jsonrpsee::types::error::ErrorCode::OversizedRequest.into());
}
BASE64
.decode(tx)
.map_err(|_| jsonrpsee::types::error::ErrorCode::InvalidParams)?
}
};
if wire_output.len() > PACKET_DATA_SIZE {
return Err(jsonrpsee::types::error::ErrorCode::OversizedRequest.into());
}
let max_retries = max_retries.map(|x| x as u16);
match self
.transaction_service
.send_wire_transaction(raw_tx, max_retries)
.send_wire_transaction(wire_output, max_retries)
.await
{
Ok(sig) => {

View File

@ -1,8 +1,10 @@
use prometheus::{opts, register_int_counter, IntCounter};
use solana_lite_rpc_accounts::account_service::AccountService;
use solana_lite_rpc_core::{
commitment_utils::Commitment, stores::data_cache::DataCache,
structures::account_data::AccountNotificationMessage, types::BlockStream,
commitment_utils::Commitment,
stores::data_cache::DataCache,
structures::account_data::AccountNotificationMessage,
types::{BlockInfoStream, BlockStream},
};
use std::{str::FromStr, sync::Arc, time::Duration};
use tokio::sync::broadcast::error::RecvError::{Closed, Lagged};
@ -48,7 +50,8 @@ pub struct LitePubSubBridge {
data_cache: DataCache,
prio_fees_service: PrioFeesService,
account_priofees_service: AccountPrioService,
block_stream: BlockStream,
_block_stream: BlockStream,
block_info_stream: BlockInfoStream,
accounts_service: Option<AccountService>,
}
@ -58,13 +61,15 @@ impl LitePubSubBridge {
prio_fees_service: PrioFeesService,
account_priofees_service: AccountPrioService,
block_stream: BlockStream,
block_info_stream: BlockInfoStream,
accounts_service: Option<AccountService>,
) -> Self {
Self {
data_cache,
prio_fees_service,
account_priofees_service,
block_stream,
_block_stream: block_stream,
block_info_stream,
accounts_service,
}
}
@ -74,17 +79,14 @@ impl LitePubSubBridge {
impl LiteRpcPubSubServer for LitePubSubBridge {
async fn slot_subscribe(&self, pending: PendingSubscriptionSink) -> SubscriptionResult {
let sink = pending.accept().await?;
let mut block_stream = self.block_stream.resubscribe();
let mut block_info_stream = self.block_info_stream.resubscribe();
tokio::spawn(async move {
loop {
match block_stream.recv().await {
Ok(produced_block) => {
if !produced_block.commitment_config.is_processed() {
continue;
}
match block_info_stream.recv().await {
Ok(block_info) => {
let slot_info = SlotInfo {
slot: produced_block.slot,
parent: produced_block.parent_slot,
slot: block_info.slot,
parent: block_info.parent,
root: 0,
};
let result_message = jsonrpsee::SubscriptionMessage::from_json(&slot_info);
@ -137,10 +139,11 @@ impl LiteRpcPubSubServer for LitePubSubBridge {
async fn signature_subscribe(
&self,
pending: PendingSubscriptionSink,
signature: Signature,
signature: String,
config: RpcSignatureSubscribeConfig,
) -> SubscriptionResult {
RPC_SIGNATURE_SUBSCRIBE.inc();
let signature = Signature::from_str(&signature)?;
let sink = pending.accept().await?;
let jsonrpsee_sink = JsonRpseeSubscriptionHandlerSink::new(sink);

View File

@ -356,6 +356,7 @@ pub async fn start_lite_rpc(args: Config, rpc_client: Arc<RpcClient>) -> anyhow:
block_priofees_service,
account_priofees_service,
blocks_notifier,
blockinfo_notifier,
accounts_service.clone(),
);

View File

@ -1,4 +1,4 @@
use crate::configs::{IsBlockHashValidConfig, SendTransactionConfig};
use crate::configs::IsBlockHashValidConfig;
use jsonrpsee::core::RpcResult;
use jsonrpsee::proc_macros::rpc;
use solana_account_decoder::UiAccount;
@ -7,7 +7,7 @@ use solana_lite_rpc_prioritization_fees::rpc_data::{AccountPrioFeesStats, PrioFe
use solana_rpc_client_api::config::{
RpcAccountInfoConfig, RpcBlocksConfigWrapper, RpcContextConfig, RpcGetVoteAccountsConfig,
RpcLeaderScheduleConfig, RpcProgramAccountsConfig, RpcRequestAirdropConfig,
RpcSignatureStatusConfig, RpcSignaturesForAddressConfig,
RpcSendTransactionConfig, RpcSignatureStatusConfig, RpcSignaturesForAddressConfig,
};
use solana_rpc_client_api::response::{
OptionalContext, Response as RpcResponse, RpcBlockhash,
@ -133,7 +133,7 @@ pub trait LiteRpc {
async fn send_transaction(
&self,
tx: String,
send_transaction_config: Option<SendTransactionConfig>,
send_transaction_config: Option<RpcSendTransactionConfig>,
) -> RpcResult<String>;
// ***********************

View File

@ -5,7 +5,6 @@ use solana_rpc_client_api::config::{
RpcProgramAccountsConfig, RpcSignatureSubscribeConfig, RpcTransactionLogsConfig,
RpcTransactionLogsFilter,
};
use solana_sdk::signature::Signature;
pub type Result<T> = std::result::Result<T, jsonrpsee::core::Error>;
@ -15,29 +14,21 @@ pub trait LiteRpcPubSub {
// Direct Subscription Domain
// ***********************
#[subscription(name = "slotSubscribe" => "slotNotification", unsubscribe="slotUnsubscribe", item=Slot)]
#[subscription(name = "slotSubscribe" => "slotNotification", unsubscribe="slotUnsubscribe", item=String)]
async fn slot_subscribe(&self) -> SubscriptionResult;
#[subscription(name = "blockSubscribe" => "blockNotification", unsubscribe="blockUnsubscribe", item=RpcResponse<UiConfirmedBlock>)]
#[subscription(name = "blockSubscribe" => "blockNotification", unsubscribe="blockUnsubscribe", item=String)]
async fn block_subscribe(
&self,
filter: RpcBlockSubscribeFilter,
config: Option<RpcBlockSubscribeConfig>,
) -> SubscriptionResult;
// [transactionSubscribe](https://github.com/solana-foundation/solana-improvement-documents/pull/69)
//
//#[subscription(name = "transactionSubscribe" => "transactionNotification", unsubscribe="transactionUnsubscribe", item=RpcResponse<RpcConfirmedTransactionStatusWithSignature>)]
//async fn transaction_subscribe(
// &self,
// commitment_config: CommitmentConfig,
//) -> SubscriptionResult;
// ***********************
// Indirect Subscription Domain
// ***********************
#[subscription(name = "logsSubscribe" => "logsNotification", unsubscribe="logsUnsubscribe", item=RpcResponse<RpcLogsResponse>)]
#[subscription(name = "logsSubscribe" => "logsNotification", unsubscribe="logsUnsubscribe", item=String)]
async fn logs_subscribe(
&self,
filter: RpcTransactionLogsFilter,
@ -45,34 +36,34 @@ pub trait LiteRpcPubSub {
) -> SubscriptionResult;
// WARN: enable_received_notification: bool is ignored
#[subscription(name = "signatureSubscribe" => "signatureNotification", unsubscribe="signatureUnsubscribe", item=RpcResponse<serde_json::Value>)]
#[subscription(name = "signatureSubscribe" => "signatureNotification", unsubscribe="signatureUnsubscribe", item=String)]
async fn signature_subscribe(
&self,
signature: Signature,
signature: String,
config: RpcSignatureSubscribeConfig,
) -> SubscriptionResult;
#[subscription(name = "slotUpdatesSubscribe" => "slotUpdatesNotification", unsubscribe="slotUpdatesUnsubscribe", item=SlotUpdate)]
#[subscription(name = "slotUpdatesSubscribe" => "slotUpdatesNotification", unsubscribe="slotUpdatesUnsubscribe", item=String)]
async fn slot_updates_subscribe(&self) -> SubscriptionResult;
#[subscription(name = "voteSubscribe" => "voteNotification", unsubscribe="voteUnsubscribe", item=RpcVote)]
#[subscription(name = "voteSubscribe" => "voteNotification", unsubscribe="voteUnsubscribe", item=String)]
async fn vote_subscribe(&self) -> SubscriptionResult;
/// subscribe to prio fees distribution per block; uses confirmation level "confirmed"
#[subscription(name = "blockPrioritizationFeesSubscribe" => "blockPrioritizationFeesNotification", unsubscribe="blockPrioritizationFeesUnsubscribe", item=PrioFeesStats)]
#[subscription(name = "blockPrioritizationFeesSubscribe" => "blockPrioritizationFeesNotification", unsubscribe="blockPrioritizationFeesUnsubscribe", item=String)]
async fn latest_block_priofees_subscribe(&self) -> SubscriptionResult;
#[subscription(name = "accountPrioritizationFeesSubscribe" => "accountPrioritizationFeesNotification", unsubscribe="accountPrioritizationFeesUnsubscribe", item=AccountPrioFeesStats)]
#[subscription(name = "accountPrioritizationFeesSubscribe" => "accountPrioritizationFeesNotification", unsubscribe="accountPrioritizationFeesUnsubscribe", item=String)]
async fn latest_account_priofees_subscribe(&self, account: String) -> SubscriptionResult;
#[subscription(name = "accountSubscribe" => "accountNotification", unsubscribe="accountUnsubscribe", item=RpcResponse<UiAccount>)]
#[subscription(name = "accountSubscribe" => "accountNotification", unsubscribe="accountUnsubscribe", item=String)]
async fn account_subscribe(
&self,
account: String,
config: Option<RpcAccountInfoConfig>,
) -> SubscriptionResult;
#[subscription(name = "programSubscribe" => "programNotification", unsubscribe="programUnsubscribe", item=RpcResponse<RpcKeyedAccount>)]
#[subscription(name = "programSubscribe" => "programNotification", unsubscribe="programUnsubscribe", item=String)]
async fn program_subscribe(
&self,
pubkey_str: String,

View File

@ -16,6 +16,8 @@ pub struct ServerConfiguration {
pub max_response_body_size: u32,
pub max_connection: u32,
pub max_subscriptions_per_connection: u32,
}
impl Default for ServerConfiguration {
@ -24,6 +26,7 @@ impl Default for ServerConfiguration {
max_request_body_size: 50 * (1 << 10), // 50kb
max_response_body_size: 500_000 * (1 << 10), // 500MB response size
max_connection: 1000000,
max_subscriptions_per_connection: 1000,
}
}
}
@ -41,6 +44,8 @@ pub async fn start_servers(
let ws_server_handle = ServerBuilder::default()
.ws_only()
.max_connections(server_configuration.max_connection)
.max_subscriptions_per_connection(server_configuration.max_subscriptions_per_connection)
.build(ws_addr.clone())
.await?
.start(pubsub);

View File

@ -18,7 +18,7 @@ solana-sdk = { workspace = true }
solana-streamer = { workspace = true }
solana-transaction-status = { workspace = true }
solana-net-utils = { workspace = true }
rustls = { workspace = true, features = ["dangerous_configuration"]}
rustls = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
bincode = { workspace = true }

View File

@ -17,7 +17,6 @@ use solana_client::rpc_response::RpcVoteAccountStatus;
use solana_lite_rpc_core::stores::data_cache::DataCache;
use solana_lite_rpc_core::structures::leaderschedule::CalculatedSchedule;
use solana_lite_rpc_core::structures::leaderschedule::LeaderScheduleData;
use solana_program::slot_history::Slot;
use solana_sdk::account::Account;
use solana_sdk::commitment_config::CommitmentConfig;
use solana_sdk::epoch_info::EpochInfo;

View File

@ -1,4 +1,4 @@
import { Connection, Keypair, sendAndConfirmTransaction, Transaction, PublicKey, TransactionInstruction, BlockheightBasedTransactionConfirmationStrategy } from "@solana/web3.js";
import { Connection, Keypair, sendAndConfirmTransaction, Transaction, PublicKey, TransactionInstruction, BlockheightBasedTransactionConfirmationStrategy, TransactionMessage, VersionedTransaction } from "@solana/web3.js";
import * as fs from "fs";
import * as os from "os";
import * as crypto from "crypto";
@ -6,7 +6,7 @@ import * as crypto from "crypto";
jest.setTimeout(60000);
const MEMO_PROGRAM_ID = new PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr");
const connection = new Connection('http://0.0.0.0:8899', 'confirmed');
const connection = new Connection('http://0.0.0.0:8890', 'confirmed');
const keypair_file = fs.readFileSync(`${os.homedir}/.config/solana/id.json`, 'utf-8');
const payer = Keypair.fromSecretKey(Uint8Array.from(JSON.parse(keypair_file)));
@ -27,16 +27,22 @@ function createTransaction(): Transaction {
test('send and confirm transaction BlockheightBasedTransactionConfirmationStrategy', async () => {
const tx = createTransaction();
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
const signature = await connection.sendTransaction(tx, [payer]);
console.log(`https://explorer.solana.com/tx/${signature}`);
await connection.confirmTransaction({
blockhash,
lastValidBlockHeight,
signature,
abortSignal: undefined
});
});
console.log(`https://explorer.solana.com/tx/${signature}`)
test('send and confirm transaction legacy confrim', async () => {
const tx = createTransaction();
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
const signature = await connection.sendTransaction(tx, [payer]);
console.log(`https://explorer.solana.com/tx/${signature}`);
await connection.confirmTransaction(signature);
});
test('send and confirm transaction', async () => {
@ -46,45 +52,45 @@ test('send and confirm transaction', async () => {
});
test('get epoch info', async () => {
{
const {epoch, absoluteSlot, slotIndex, slotsInEpoch} = await connection.getEpochInfo();
expect(Math.floor(absoluteSlot/slotsInEpoch)).toBe(epoch);
}
// test('get epoch info', async () => {
// {
// const {epoch, absoluteSlot, slotIndex, slotsInEpoch} = await connection.getEpochInfo();
// expect(Math.floor(absoluteSlot/slotsInEpoch)).toBe(epoch);
// }
let process_absoluteSlot;
{
const {epoch, absoluteSlot, slotIndex, slotsInEpoch} = await connection.getEpochInfo({ commitment: 'processed' });
expect(Math.floor(absoluteSlot/slotsInEpoch)).toBe(epoch);
process_absoluteSlot = absoluteSlot;
}
// let process_absoluteSlot;
// {
// const {epoch, absoluteSlot, slotIndex, slotsInEpoch} = await connection.getEpochInfo({ commitment: 'processed' });
// expect(Math.floor(absoluteSlot/slotsInEpoch)).toBe(epoch);
// process_absoluteSlot = absoluteSlot;
// }
let confirmed_absoluteSlot;
{
const {epoch, absoluteSlot, slotIndex, slotsInEpoch} = await connection.getEpochInfo({ commitment: 'confirmed' });
expect(Math.floor(absoluteSlot/slotsInEpoch)).toBe(epoch);
confirmed_absoluteSlot = absoluteSlot;
}
expect(confirmed_absoluteSlot >= process_absoluteSlot);
// let confirmed_absoluteSlot;
// {
// const {epoch, absoluteSlot, slotIndex, slotsInEpoch} = await connection.getEpochInfo({ commitment: 'confirmed' });
// expect(Math.floor(absoluteSlot/slotsInEpoch)).toBe(epoch);
// confirmed_absoluteSlot = absoluteSlot;
// }
// expect(confirmed_absoluteSlot >= process_absoluteSlot);
let finalized_absoluteSlot;
{
const {epoch, absoluteSlot, slotIndex, slotsInEpoch} = await connection.getEpochInfo({ commitment: 'finalized' });
expect(Math.floor(absoluteSlot/slotsInEpoch)).toBe(epoch);
finalized_absoluteSlot = absoluteSlot;
}
expect(process_absoluteSlot > finalized_absoluteSlot);
expect(confirmed_absoluteSlot > finalized_absoluteSlot);
// let finalized_absoluteSlot;
// {
// const {epoch, absoluteSlot, slotIndex, slotsInEpoch} = await connection.getEpochInfo({ commitment: 'finalized' });
// expect(Math.floor(absoluteSlot/slotsInEpoch)).toBe(epoch);
// finalized_absoluteSlot = absoluteSlot;
// }
// expect(process_absoluteSlot > finalized_absoluteSlot);
// expect(confirmed_absoluteSlot > finalized_absoluteSlot);
});
// });
test('get leader schedule', async () => {
{
const leaderSchedule = await connection.getLeaderSchedule();
expect(Object.keys(leaderSchedule).length > 0);
}
});
// test('get leader schedule', async () => {
// {
// const leaderSchedule = await connection.getLeaderSchedule();
// expect(Object.keys(leaderSchedule).length > 0);
// }
// });

105
yarn.lock
View File

@ -22,7 +22,7 @@
resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.10.tgz"
integrity sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg==
"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.8.0", "@babel/core@>=7.0.0-beta.0 <8":
"@babel/core@^7.11.6", "@babel/core@^7.12.3":
version "7.20.12"
resolved "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz"
integrity sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==
@ -501,7 +501,7 @@
slash "^3.0.0"
write-file-atomic "^4.0.1"
"@jest/types@^29.0.0", "@jest/types@^29.3.1":
"@jest/types@^29.3.1":
version "29.3.1"
resolved "https://registry.npmjs.org/@jest/types/-/types-29.3.1.tgz"
integrity sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==
@ -540,7 +540,7 @@
resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz"
integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@1.4.14":
"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10":
version "1.4.14"
resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz"
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
@ -613,7 +613,7 @@
"@solana/buffer-layout-utils" "^0.2.0"
buffer "^6.0.3"
"@solana/web3.js@^1.32.0", "@solana/web3.js@^1.47.4", "@solana/web3.js@^1.73.0":
"@solana/web3.js@^1.32.0", "@solana/web3.js@^1.73.0":
version "1.73.0"
resolved "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.73.0.tgz"
integrity sha512-YrgX3Py7ylh8NYkbanoINUPCj//bWUjYZ5/WPy9nQ9SK3Cl7QWCR+NmbDjmC/fTspZGR+VO9LTQslM++jr5PRw==
@ -743,6 +743,14 @@
dependencies:
"@types/yargs-parser" "*"
JSONStream@^1.3.5:
version "1.3.5"
resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz"
integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==
dependencies:
jsonparse "^1.2.0"
through ">=2.2.7 <3"
agentkeepalive@^4.2.1:
version "4.2.1"
resolved "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz"
@ -798,7 +806,7 @@ argparse@^1.0.7:
dependencies:
sprintf-js "~1.0.2"
babel-jest@^29.0.0, babel-jest@^29.3.1:
babel-jest@^29.3.1:
version "29.3.1"
resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-29.3.1.tgz"
integrity sha512-aard+xnMoxgjwV70t0L6wkW/3HQQtV+O0PEimxKgzNqCJnbYmroPojdP2tqKSOAt8QAKV/uSZU8851M7B5+fcA==
@ -923,7 +931,7 @@ braces@^3.0.2:
dependencies:
fill-range "^7.0.1"
browserslist@^4.21.3, "browserslist@>= 4.21.0":
browserslist@^4.21.3:
version "4.21.4"
resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz"
integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==
@ -959,14 +967,6 @@ buffer-from@^1.0.0:
resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz"
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
buffer@^6.0.3, buffer@~6.0.3:
version "6.0.3"
resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz"
integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
dependencies:
base64-js "^1.3.1"
ieee754 "^1.2.1"
buffer@6.0.1:
version "6.0.1"
resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.1.tgz"
@ -975,6 +975,14 @@ buffer@6.0.1:
base64-js "^1.3.1"
ieee754 "^1.2.1"
buffer@^6.0.3, buffer@~6.0.3:
version "6.0.3"
resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz"
integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
dependencies:
base64-js "^1.3.1"
ieee754 "^1.2.1"
bufferutil@^4.0.1:
version "4.0.7"
resolved "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.7.tgz"
@ -1067,16 +1075,16 @@ color-convert@^2.0.1:
dependencies:
color-name "~1.1.4"
color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
color-name@1.1.3:
version "1.1.3"
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz"
integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
commander@^2.20.3:
version "2.20.3"
resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz"
@ -1087,12 +1095,7 @@ concat-map@0.0.1:
resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
convert-source-map@^1.6.0:
version "1.9.0"
resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz"
integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==
convert-source-map@^1.7.0:
convert-source-map@^1.6.0, convert-source-map@^1.7.0:
version "1.9.0"
resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz"
integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==
@ -1248,7 +1251,7 @@ eyes@^0.1.8:
resolved "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz"
integrity sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==
fast-json-stable-stringify@^2.1.0, fast-json-stable-stringify@2.x:
fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.1.0:
version "2.1.0"
resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz"
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
@ -1290,6 +1293,11 @@ fs.realpath@^1.0.0:
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
fsevents@^2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz"
@ -1494,13 +1502,13 @@ jayson@^3.4.4:
"@types/connect" "^3.4.33"
"@types/node" "^12.12.54"
"@types/ws" "^7.4.4"
JSONStream "^1.3.5"
commander "^2.20.3"
delay "^5.0.0"
es6-promisify "^5.0.0"
eyes "^0.1.8"
isomorphic-ws "^4.0.1"
json-stringify-safe "^5.0.1"
JSONStream "^1.3.5"
lodash "^4.17.20"
uuid "^8.3.2"
ws "^7.4.5"
@ -1708,7 +1716,7 @@ jest-resolve-dependencies@^29.3.1:
jest-regex-util "^29.2.0"
jest-snapshot "^29.3.1"
jest-resolve@*, jest-resolve@^29.3.1:
jest-resolve@^29.3.1:
version "29.3.1"
resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.3.1.tgz"
integrity sha512-amXJgH/Ng712w3Uz5gqzFBBjxV8WFLSmNjoreBGMqxgCz5cH7swmBZzgBaCIOsvb0NbpJ0vgaSFdJqMdT+rADw==
@ -1856,7 +1864,7 @@ jest-worker@^29.3.1:
merge-stream "^2.0.0"
supports-color "^8.0.0"
jest@^29.0.0, jest@^29.3.1:
jest@^29.3.1:
version "29.3.1"
resolved "https://registry.npmjs.org/jest/-/jest-29.3.1.tgz"
integrity sha512-6iWfL5DTT0Np6UYs/y5Niu7WIfNv/wRTtN5RSXt2DIEft3dx3zPuw/3WJQBCJfmEzvDiEKwoqMbGD9n49+qLSA==
@ -1904,14 +1912,6 @@ jsonparse@^1.2.0:
resolved "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz"
integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==
JSONStream@^1.3.5:
version "1.3.5"
resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz"
integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==
dependencies:
jsonparse "^1.2.0"
through ">=2.2.7 <3"
kleur@^3.0.3:
version "3.0.3"
resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz"
@ -2002,7 +2002,7 @@ minimatch@^3.0.4, minimatch@^3.1.1:
dependencies:
brace-expansion "^1.1.7"
ms@^2.0.0, ms@2.1.2:
ms@2.1.2, ms@^2.0.0:
version "2.1.2"
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
@ -2214,25 +2214,18 @@ safe-buffer@^5.0.1:
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
semver@7.x, semver@^7.3.5:
version "7.3.8"
resolved "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz"
integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==
dependencies:
lru-cache "^6.0.0"
semver@^6.0.0, semver@^6.3.0:
version "6.3.0"
resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
semver@^7.3.5:
version "7.3.8"
resolved "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz"
integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==
dependencies:
lru-cache "^6.0.0"
semver@7.x:
version "7.3.8"
resolved "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz"
integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==
dependencies:
lru-cache "^6.0.0"
shebang-command@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz"
@ -2420,9 +2413,9 @@ type-fest@^0.21.3:
resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz"
integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
typescript@^4.9.5, typescript@>=4.3:
typescript@^4.9.4:
version "4.9.5"
resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a"
integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==
update-browserslist-db@^1.0.9:
@ -2433,7 +2426,7 @@ update-browserslist-db@^1.0.9:
escalade "^3.1.1"
picocolors "^1.0.0"
utf-8-validate@^5.0.2, utf-8-validate@>=5.0.2:
utf-8-validate@^5.0.2:
version "5.0.10"
resolved "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz"
integrity sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==
@ -2503,7 +2496,7 @@ write-file-atomic@^4.0.1:
imurmurhash "^0.1.4"
signal-exit "^3.0.7"
ws@*, ws@^7.4.5:
ws@^7.4.5:
version "7.5.9"
resolved "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz"
integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==