Compare commits

..

1 Commits

Author SHA1 Message Date
dependabot[bot]
3b9c95bd52 chore: bump url-parse from 1.5.1 to 1.5.10 in /docs
Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.5.1 to 1.5.10.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.5.1...1.5.10)

---
updated-dependencies:
- dependency-name: url-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-28 04:35:05 +00:00
447 changed files with 13915 additions and 23504 deletions

View File

@@ -1,9 +1,5 @@
#### Problem
#### Summary of Changes
Fixes #

View File

@@ -17,7 +17,8 @@ jobs:
vercel-token: ${{ secrets.VERCEL_TOKEN }} # Required
github-token: ${{ secrets.GITHUB_TOKEN }} #Optional
vercel-org-id: ${{ secrets.ORG_ID}} #Required
vercel-project-id: ${{ secrets.PROJECT_ID}} #Required
vercel-project-id: ${{ secrets.PROJECT_ID}} #Required
working-directory: ./explorer
scope: ${{ secrets.TEAM_ID }}
- name: vercel url
@@ -35,24 +36,17 @@ jobs:
#filtered_url=$(cat vercelfile2.txt )
#echo "$filtered_url" >> .env.preview1
- name: Fetching Vercel Preview Deployment Link
- name: Run tests
uses: mathiasvr/command-output@v1
id: test1
id: tests2
with:
run: |
echo "$(cat .env.preview1)"
- name: Fetching PR commit URL
uses: mathiasvr/command-output@v1
id: test2
with:
run: |
HEAD_SHA=$(curl -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/solana-labs/solana/pulls | jq .[0] | jq -r .head.sha)
USER_NAME=$(curl -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/solana-labs/solana/pulls | jq .[0] | jq -r .head.user.login)
echo "github.com/$USER_NAME/solana/commit/$HEAD_SHA"
- name: Slack Notification4
- name: Slack Notification1
uses: rtCamp/action-slack-notify@master
env:
SLACK_MESSAGE: ' Vercel Link: ${{ steps.test1.outputs.stdout }} PR Commit: ${{steps.test2.outputs.stdout}}'
SLACK_TITLE: Vercel "Explorer" Preview Deployment Link , PR Commit
SLACK_MESSAGE: ${{ steps.tests2.outputs.stdout }}
SLACK_TITLE: Vercel "Explorer" Preview Deployment Link
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}

View File

@@ -30,8 +30,7 @@ jobs:
runs-on: ubuntu-latest
defaults:
run:
working-directory: explorer
working-directory: explorer
steps:
- uses: actions/checkout@v2
with:
@@ -39,8 +38,9 @@ jobs:
- uses: amondnet/vercel-action@v20
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }} # Required
github-token: ${{ secrets.PAT }} #Optional
github-token: ${{ secrets.GITHUB_TOKEN }} #Optional
vercel-args: '--prod' #for production
vercel-org-id: ${{ secrets.ORG_ID}} #Required
vercel-project-id: ${{ secrets.PROJECT_ID}} #Required
working-directory: ./explorer
scope: ${{ secrets.TEAM_ID }}

View File

@@ -93,7 +93,6 @@ pull_request_rules:
- author=mergify[bot]
- head~=^mergify/bp/
- "#status-failure=0"
- "-merged"
actions:
label:
add:

1623
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,68 +1,63 @@
[workspace]
members = [
"account-decoder",
"accounts-bench",
"accountsdb-plugin-interface",
"accountsdb-plugin-manager",
"accounts-cluster-bench",
"bench-streamer",
"bench-tps",
"accounts-bench",
"banking-bench",
"banks-client",
"banks-interface",
"banks-server",
"bench-streamer",
"bench-tps",
"bloom",
"bucket_map",
"bloom",
"clap-utils",
"cli",
"cli-config",
"cli-output",
"client",
"client-test",
"core",
"dos",
"download-utils",
"entry",
"faucet",
"frozen-abi",
"perf",
"validator",
"genesis",
"genesis-utils",
"geyser-plugin-interface",
"geyser-plugin-manager",
"gossip",
"install",
"keygen",
"ledger",
"ledger-tool",
"local-cluster",
"log-analyzer",
"logger",
"measure",
"log-analyzer",
"merkle-root-bench",
"merkle-tree",
"storage-bigtable",
"storage-proto",
"streamer",
"measure",
"metrics",
"net-shaper",
"net-utils",
"notifier",
"perf",
"poh",
"poh-bench",
"program-test",
"programs/address-lookup-table",
"programs/address-lookup-table-tests",
"programs/ed25519-tests",
"programs/bpf_loader",
"programs/bpf_loader/gen-syscall-list",
"programs/compute-budget",
"programs/config",
"programs/ed25519-tests",
"programs/stake",
"programs/vote",
"programs/zk-token-proof",
"rayon-threadlimit",
"rbpf-cli",
"remote-wallet",
"replica-lib",
"replica-node",
"rpc",
"rpc-test",
"runtime",
"runtime/store-tool",
"sdk",
@@ -70,19 +65,24 @@ members = [
"sdk/cargo-test-bpf",
"send-transaction-service",
"stake-accounts",
"storage-bigtable",
"storage-proto",
"streamer",
"sys-tuner",
"test-validator",
"tokens",
"transaction-dos",
"transaction-status",
"account-decoder",
"upload-perf",
"validator",
"net-utils",
"version",
"cli",
"rayon-threadlimit",
"watchtower",
"replica-node",
"replica-lib",
"test-validator",
"rpc-test",
"client-test",
"zk-token-sdk",
"programs/zk-token-proof",
]
exclude = [

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-account-decoder"
version = "1.10.6"
version = "1.10.0"
description = "Solana account decoder"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
@@ -10,21 +10,21 @@ license = "Apache-2.0"
edition = "2021"
[dependencies]
Inflector = "0.11.4"
base64 = "0.13.0"
base64 = "0.12.3"
bincode = "1.3.3"
bs58 = "0.4.0"
bv = "0.11.1"
Inflector = "0.11.4"
lazy_static = "1.4.0"
serde = "1.0.136"
serde_derive = "1.0.103"
serde_json = "1.0.79"
solana-config-program = { path = "../programs/config", version = "=1.10.6" }
solana-sdk = { path = "../sdk", version = "=1.10.6" }
solana-vote-program = { path = "../programs/vote", version = "=1.10.6" }
serde_json = "1.0.78"
solana-config-program = { path = "../programs/config", version = "=1.10.0" }
solana-sdk = { path = "../sdk", version = "=1.10.0" }
solana-vote-program = { path = "../programs/vote", version = "=1.10.0" }
spl-token = { version = "=3.2.0", features = ["no-entrypoint"] }
thiserror = "1.0"
zstd = "0.11.1"
zstd = "0.10.0"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -33,7 +33,7 @@ pub type StringDecimals = String;
pub const MAX_BASE58_BYTES: usize = 128;
/// A duplicate representation of an Account for pretty JSON serialization
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct UiAccount {
pub lamports: u64,

View File

@@ -2,21 +2,21 @@
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2021"
name = "solana-accounts-bench"
version = "1.10.6"
version = "1.10.0"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
publish = false
[dependencies]
clap = "2.33.1"
log = "0.4.14"
rayon = "1.5.1"
solana-logger = { path = "../logger", version = "=1.10.6" }
solana-measure = { path = "../measure", version = "=1.10.6" }
solana-runtime = { path = "../runtime", version = "=1.10.6" }
solana-sdk = { path = "../sdk", version = "=1.10.6" }
solana-version = { path = "../version", version = "=1.10.6" }
solana-logger = { path = "../logger", version = "=1.10.0" }
solana-runtime = { path = "../runtime", version = "=1.10.0" }
solana-measure = { path = "../measure", version = "=1.10.0" }
solana-sdk = { path = "../sdk", version = "=1.10.0" }
solana-version = { path = "../version", version = "=1.10.0" }
clap = "2.33.1"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -2,7 +2,7 @@
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2021"
name = "solana-accounts-cluster-bench"
version = "1.10.6"
version = "1.10.0"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@@ -13,25 +13,25 @@ clap = "2.33.1"
log = "0.4.14"
rand = "0.7.0"
rayon = "1.5.1"
solana-account-decoder = { path = "../account-decoder", version = "=1.10.6" }
solana-clap-utils = { path = "../clap-utils", version = "=1.10.6" }
solana-client = { path = "../client", version = "=1.10.6" }
solana-faucet = { path = "../faucet", version = "=1.10.6" }
solana-gossip = { path = "../gossip", version = "=1.10.6" }
solana-logger = { path = "../logger", version = "=1.10.6" }
solana-measure = { path = "../measure", version = "=1.10.6" }
solana-net-utils = { path = "../net-utils", version = "=1.10.6" }
solana-runtime = { path = "../runtime", version = "=1.10.6" }
solana-sdk = { path = "../sdk", version = "=1.10.6" }
solana-streamer = { path = "../streamer", version = "=1.10.6" }
solana-transaction-status = { path = "../transaction-status", version = "=1.10.6" }
solana-version = { path = "../version", version = "=1.10.6" }
solana-account-decoder = { path = "../account-decoder", version = "=1.10.0" }
solana-clap-utils = { path = "../clap-utils", version = "=1.10.0" }
solana-client = { path = "../client", version = "=1.10.0" }
solana-faucet = { path = "../faucet", version = "=1.10.0" }
solana-gossip = { path = "../gossip", version = "=1.10.0" }
solana-logger = { path = "../logger", version = "=1.10.0" }
solana-measure = { path = "../measure", version = "=1.10.0" }
solana-net-utils = { path = "../net-utils", version = "=1.10.0" }
solana-runtime = { path = "../runtime", version = "=1.10.0" }
solana-sdk = { path = "../sdk", version = "=1.10.0" }
solana-streamer = { path = "../streamer", version = "=1.10.0" }
solana-transaction-status = { path = "../transaction-status", version = "=1.10.0" }
solana-version = { path = "../version", version = "=1.10.0" }
spl-token = { version = "=3.2.0", features = ["no-entrypoint"] }
[dev-dependencies]
solana-core = { path = "../core", version = "=1.10.6" }
solana-local-cluster = { path = "../local-cluster", version = "=1.10.6" }
solana-test-validator = { path = "../test-validator", version = "=1.10.6" }
solana-core = { path = "../core", version = "=1.10.0" }
solana-local-cluster = { path = "../local-cluster", version = "=1.10.0" }
solana-test-validator = { path = "../test-validator", version = "=1.10.0" }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -1,19 +1,19 @@
[package]
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2021"
name = "solana-geyser-plugin-interface"
description = "The Solana Geyser plugin interface."
version = "1.10.6"
name = "solana-accountsdb-plugin-interface"
description = "The Solana AccountsDb plugin interface."
version = "1.10.0"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
documentation = "https://docs.rs/solana-geyser-plugin-interface"
documentation = "https://docs.rs/solana-accountsdb-plugin-interface"
[dependencies]
log = "0.4.11"
solana-sdk = { path = "../sdk", version = "=1.10.6" }
solana-transaction-status = { path = "../transaction-status", version = "=1.10.6" }
thiserror = "1.0.30"
solana-sdk = { path = "../sdk", version = "=1.10.0" }
solana-transaction-status = { path = "../transaction-status", version = "=1.10.0" }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -0,0 +1,20 @@
<p align="center">
<a href="https://solana.com">
<img alt="Solana" src="https://i.imgur.com/IKyzQ6T.png" width="250" />
</a>
</p>
# Solana AccountsDb Plugin Interface
This crate enables an AccountsDb plugin to be plugged into the Solana Validator runtime to take actions
at the time of each account update; for example, saving the account state to an external database. The plugin must implement the `AccountsDbPlugin` trait. Please see the detail of the `accountsdb_plugin_interface.rs` for the interface definition.
The plugin should produce a `cdylib` dynamic library, which must expose a `C` function `_create_plugin()` that
instantiates the implementation of the interface.
The `solana-accountsdb-plugin-postgres` crate provides an example of how to create a plugin which saves the accounts data into an
external PostgreSQL databases.
More information about Solana is available in the [Solana documentation](https://docs.solana.com/).
Still have questions? Ask us on [Discord](https://discordapp.com/invite/pquxPsq)

View File

@@ -1,5 +1,5 @@
/// The interface for Geyser plugins. A plugin must implement
/// the GeyserPlugin trait to work with the runtime.
/// The interface for AccountsDb plugins. A plugin must implement
/// the AccountsDbPlugin trait to work with the runtime.
/// In addition, the dynamic library must export a "C" function _create_plugin which
/// creates the implementation of the plugin.
use {
@@ -9,7 +9,9 @@ use {
thiserror::Error,
};
#[derive(Debug, Clone, PartialEq, Eq)]
impl Eq for ReplicaAccountInfo<'_> {}
#[derive(Clone, PartialEq, Debug)]
/// Information about an account being updated
pub struct ReplicaAccountInfo<'a> {
/// The Pubkey for the account
@@ -85,7 +87,7 @@ pub enum ReplicaBlockInfoVersions<'a> {
/// Errors returned by plugin calls
#[derive(Error, Debug)]
pub enum GeyserPluginError {
pub enum AccountsDbPluginError {
/// Error opening the configuration file; for example, when the file
/// is not found or when the validator process has no permission to read it.
#[error("Error opening config file. Error detail: ({0}).")]
@@ -110,7 +112,7 @@ pub enum GeyserPluginError {
}
/// The current status of a slot
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Clone)]
pub enum SlotStatus {
/// The highest slot of the heaviest fork processed by the node. Ledger state at this slot is
/// not derived from a confirmed or finalized block, but if multiple forks are present, is from
@@ -134,12 +136,12 @@ impl SlotStatus {
}
}
pub type Result<T> = std::result::Result<T, GeyserPluginError>;
pub type Result<T> = std::result::Result<T, AccountsDbPluginError>;
/// Defines a Geyser plugin, to stream data from the runtime.
/// Geyser plugins must describe desired behavior for load and unload,
/// Defines an AccountsDb plugin, to stream data from the runtime.
/// AccountsDb plugins must describe desired behavior for load and unload,
/// as well as how they will handle streamed data.
pub trait GeyserPlugin: Any + Send + Sync + std::fmt::Debug {
pub trait AccountsDbPlugin: Any + Send + Sync + std::fmt::Debug {
fn name(&self) -> &'static str;
/// The callback called when a plugin is loaded by the system,

View File

@@ -0,0 +1 @@
pub mod accountsdb_plugin_interface;

View File

@@ -0,0 +1,29 @@
[package]
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2021"
name = "solana-accountsdb-plugin-manager"
description = "The Solana AccountsDb plugin manager."
version = "1.10.0"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
documentation = "https://docs.rs/solana-validator"
[dependencies]
bs58 = "0.4.0"
crossbeam-channel = "0.5"
json5 = "0.4.1"
libloading = "0.7.3"
log = "0.4.11"
serde_json = "1.0.78"
solana-accountsdb-plugin-interface = { path = "../accountsdb-plugin-interface", version = "=1.10.0" }
solana-measure = { path = "../measure", version = "=1.10.0" }
solana-metrics = { path = "../metrics", version = "=1.10.0" }
solana-rpc = { path = "../rpc", version = "=1.10.0" }
solana-runtime = { path = "../runtime", version = "=1.10.0" }
solana-sdk = { path = "../sdk", version = "=1.10.0" }
solana-transaction-status = { path = "../transaction-status", version = "=1.10.0" }
thiserror = "1.0.30"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -1,8 +1,8 @@
/// Module responsible for notifying plugins of account updates
use {
crate::geyser_plugin_manager::GeyserPluginManager,
crate::accountsdb_plugin_manager::AccountsDbPluginManager,
log::*,
solana_geyser_plugin_interface::geyser_plugin_interface::{
solana_accountsdb_plugin_interface::accountsdb_plugin_interface::{
ReplicaAccountInfo, ReplicaAccountInfoVersions,
},
solana_measure::measure::Measure,
@@ -19,7 +19,7 @@ use {
};
#[derive(Debug)]
pub(crate) struct AccountsUpdateNotifierImpl {
plugin_manager: Arc<RwLock<GeyserPluginManager>>,
plugin_manager: Arc<RwLock<AccountsDbPluginManager>>,
}
impl AccountsUpdateNotifierInterface for AccountsUpdateNotifierImpl {
@@ -30,14 +30,14 @@ impl AccountsUpdateNotifierInterface for AccountsUpdateNotifierImpl {
}
fn notify_account_restore_from_snapshot(&self, slot: Slot, account: &StoredAccountMeta) {
let mut measure_all = Measure::start("geyser-plugin-notify-account-restore-all");
let mut measure_copy = Measure::start("geyser-plugin-copy-stored-account-info");
let mut measure_all = Measure::start("accountsdb-plugin-notify-account-restore-all");
let mut measure_copy = Measure::start("accountsdb-plugin-copy-stored-account-info");
let account = self.accountinfo_from_stored_account_meta(account);
measure_copy.stop();
inc_new_counter_debug!(
"geyser-plugin-copy-stored-account-info-us",
"accountsdb-plugin-copy-stored-account-info-us",
measure_copy.as_us() as usize,
100000,
100000
@@ -49,7 +49,7 @@ impl AccountsUpdateNotifierInterface for AccountsUpdateNotifierImpl {
measure_all.stop();
inc_new_counter_debug!(
"geyser-plugin-notify-account-restore-all-us",
"accountsdb-plugin-notify-account-restore-all-us",
measure_all.as_us() as usize,
100000,
100000
@@ -63,7 +63,7 @@ impl AccountsUpdateNotifierInterface for AccountsUpdateNotifierImpl {
}
for plugin in plugin_manager.plugins.iter_mut() {
let mut measure = Measure::start("geyser-plugin-end-of-restore-from-snapshot");
let mut measure = Measure::start("accountsdb-plugin-end-of-restore-from-snapshot");
match plugin.notify_end_of_startup() {
Err(err) => {
error!(
@@ -81,7 +81,7 @@ impl AccountsUpdateNotifierInterface for AccountsUpdateNotifierImpl {
}
measure.stop();
inc_new_counter_debug!(
"geyser-plugin-end-of-restore-from-snapshot",
"accountsdb-plugin-end-of-restore-from-snapshot",
measure.as_us() as usize
);
}
@@ -89,7 +89,7 @@ impl AccountsUpdateNotifierInterface for AccountsUpdateNotifierImpl {
}
impl AccountsUpdateNotifierImpl {
pub fn new(plugin_manager: Arc<RwLock<GeyserPluginManager>>) -> Self {
pub fn new(plugin_manager: Arc<RwLock<AccountsDbPluginManager>>) -> Self {
AccountsUpdateNotifierImpl { plugin_manager }
}
@@ -130,14 +130,14 @@ impl AccountsUpdateNotifierImpl {
slot: Slot,
is_startup: bool,
) {
let mut measure2 = Measure::start("geyser-plugin-notify_plugins_of_account_update");
let mut measure2 = Measure::start("accountsdb-plugin-notify_plugins_of_account_update");
let mut plugin_manager = self.plugin_manager.write().unwrap();
if plugin_manager.plugins.is_empty() {
return;
}
for plugin in plugin_manager.plugins.iter_mut() {
let mut measure = Measure::start("geyser-plugin-update-account");
let mut measure = Measure::start("accountsdb-plugin-update-account");
match plugin.update_account(
ReplicaAccountInfoVersions::V0_0_1(&account),
slot,
@@ -163,7 +163,7 @@ impl AccountsUpdateNotifierImpl {
}
measure.stop();
inc_new_counter_debug!(
"geyser-plugin-update-account-us",
"accountsdb-plugin-update-account-us",
measure.as_us() as usize,
100000,
100000
@@ -171,7 +171,7 @@ impl AccountsUpdateNotifierImpl {
}
measure2.stop();
inc_new_counter_debug!(
"geyser-plugin-notify_plugins_of_account_update-us",
"accountsdb-plugin-notify_plugins_of_account_update-us",
measure2.as_us() as usize,
100000,
100000

View File

@@ -1,20 +1,20 @@
/// Managing the Geyser plugins
/// Managing the AccountsDb plugins
use {
libloading::{Library, Symbol},
log::*,
solana_geyser_plugin_interface::geyser_plugin_interface::GeyserPlugin,
solana_accountsdb_plugin_interface::accountsdb_plugin_interface::AccountsDbPlugin,
std::error::Error,
};
#[derive(Default, Debug)]
pub struct GeyserPluginManager {
pub plugins: Vec<Box<dyn GeyserPlugin>>,
pub struct AccountsDbPluginManager {
pub plugins: Vec<Box<dyn AccountsDbPlugin>>,
libs: Vec<Library>,
}
impl GeyserPluginManager {
impl AccountsDbPluginManager {
pub fn new() -> Self {
GeyserPluginManager {
AccountsDbPluginManager {
plugins: Vec::default(),
libs: Vec::default(),
}
@@ -29,7 +29,7 @@ impl GeyserPluginManager {
libpath: &str,
config_file: &str,
) -> Result<(), Box<dyn Error>> {
type PluginConstructor = unsafe fn() -> *mut dyn GeyserPlugin;
type PluginConstructor = unsafe fn() -> *mut dyn AccountsDbPlugin;
let lib = Library::new(libpath)?;
let constructor: Symbol<PluginConstructor> = lib.get(b"_create_plugin")?;
let plugin_raw = constructor();

View File

@@ -1,10 +1,11 @@
use {
crate::{
accounts_update_notifier::AccountsUpdateNotifierImpl,
accountsdb_plugin_manager::AccountsDbPluginManager,
block_metadata_notifier::BlockMetadataNotifierImpl,
block_metadata_notifier_interface::BlockMetadataNotifierLock,
geyser_plugin_manager::GeyserPluginManager, slot_status_notifier::SlotStatusNotifierImpl,
slot_status_observer::SlotStatusObserver, transaction_notifier::TransactionNotifierImpl,
slot_status_notifier::SlotStatusNotifierImpl, slot_status_observer::SlotStatusObserver,
transaction_notifier::TransactionNotifierImpl,
},
crossbeam_channel::Receiver,
log::*,
@@ -24,7 +25,7 @@ use {
};
#[derive(Error, Debug)]
pub enum GeyserPluginServiceError {
pub enum AccountsdbPluginServiceError {
#[error("Cannot open the the plugin config file")]
CannotOpenConfigFile(String),
@@ -44,41 +45,41 @@ pub enum GeyserPluginServiceError {
PluginLoadError(String),
}
/// The service managing the Geyser plugin workflow.
pub struct GeyserPluginService {
/// The service managing the AccountsDb plugin workflow.
pub struct AccountsDbPluginService {
slot_status_observer: Option<SlotStatusObserver>,
plugin_manager: Arc<RwLock<GeyserPluginManager>>,
plugin_manager: Arc<RwLock<AccountsDbPluginManager>>,
accounts_update_notifier: Option<AccountsUpdateNotifier>,
transaction_notifier: Option<TransactionNotifierLock>,
block_metadata_notifier: Option<BlockMetadataNotifierLock>,
}
impl GeyserPluginService {
/// Creates and returns the GeyserPluginService.
impl AccountsDbPluginService {
/// Creates and returns the AccountsDbPluginService.
/// # Arguments
/// * `confirmed_bank_receiver` - The receiver for confirmed bank notification
/// * `geyser_plugin_config_file` - The config file path for the plugin. The
/// * `accountsdb_plugin_config_file` - The config file path for the plugin. The
/// config file controls the plugin responsible
/// for transporting the data to external data stores. It is defined in JSON format.
/// The `libpath` field should be pointed to the full path of the dynamic shared library
/// (.so file) to be loaded. The shared library must implement the `GeyserPlugin`
/// (.so file) to be loaded. The shared library must implement the `AccountsDbPlugin`
/// trait. And the shared library shall export a `C` function `_create_plugin` which
/// shall create the implementation of `GeyserPlugin` and returns to the caller.
/// shall create the implementation of `AccountsDbPlugin` and returns to the caller.
/// The rest of the JSON fields' definition is up to to the concrete plugin implementation
/// It is usually used to configure the connection information for the external data store.
pub fn new(
confirmed_bank_receiver: Receiver<BankNotification>,
geyser_plugin_config_files: &[PathBuf],
) -> Result<Self, GeyserPluginServiceError> {
accountsdb_plugin_config_files: &[PathBuf],
) -> Result<Self, AccountsdbPluginServiceError> {
info!(
"Starting GeyserPluginService from config files: {:?}",
geyser_plugin_config_files
"Starting AccountsDbPluginService from config files: {:?}",
accountsdb_plugin_config_files
);
let mut plugin_manager = GeyserPluginManager::new();
let mut plugin_manager = AccountsDbPluginManager::new();
for geyser_plugin_config_file in geyser_plugin_config_files {
Self::load_plugin(&mut plugin_manager, geyser_plugin_config_file)?;
for accountsdb_plugin_config_file in accountsdb_plugin_config_files {
Self::load_plugin(&mut plugin_manager, accountsdb_plugin_config_file)?;
}
let account_data_notifications_enabled =
plugin_manager.account_data_notifications_enabled();
@@ -122,8 +123,8 @@ impl GeyserPluginService {
(None, None)
};
info!("Started GeyserPluginService");
Ok(GeyserPluginService {
info!("Started AccountsDbPluginService");
Ok(AccountsDbPluginService {
slot_status_observer,
plugin_manager,
accounts_update_notifier,
@@ -133,55 +134,57 @@ impl GeyserPluginService {
}
fn load_plugin(
plugin_manager: &mut GeyserPluginManager,
geyser_plugin_config_file: &Path,
) -> Result<(), GeyserPluginServiceError> {
let mut file = match File::open(geyser_plugin_config_file) {
plugin_manager: &mut AccountsDbPluginManager,
accountsdb_plugin_config_file: &Path,
) -> Result<(), AccountsdbPluginServiceError> {
let mut file = match File::open(accountsdb_plugin_config_file) {
Ok(file) => file,
Err(err) => {
return Err(GeyserPluginServiceError::CannotOpenConfigFile(format!(
return Err(AccountsdbPluginServiceError::CannotOpenConfigFile(format!(
"Failed to open the plugin config file {:?}, error: {:?}",
geyser_plugin_config_file, err
accountsdb_plugin_config_file, err
)));
}
};
let mut contents = String::new();
if let Err(err) = file.read_to_string(&mut contents) {
return Err(GeyserPluginServiceError::CannotReadConfigFile(format!(
return Err(AccountsdbPluginServiceError::CannotReadConfigFile(format!(
"Failed to read the plugin config file {:?}, error: {:?}",
geyser_plugin_config_file, err
accountsdb_plugin_config_file, err
)));
}
let result: serde_json::Value = match json5::from_str(&contents) {
Ok(value) => value,
Err(err) => {
return Err(GeyserPluginServiceError::InvalidConfigFileFormat(format!(
"The config file {:?} is not in a valid Json5 format, error: {:?}",
geyser_plugin_config_file, err
)));
return Err(AccountsdbPluginServiceError::InvalidConfigFileFormat(
format!(
"The config file {:?} is not in a valid Json5 format, error: {:?}",
accountsdb_plugin_config_file, err
),
));
}
};
let libpath = result["libpath"]
.as_str()
.ok_or(GeyserPluginServiceError::LibPathNotSet)?;
.ok_or(AccountsdbPluginServiceError::LibPathNotSet)?;
let mut libpath = PathBuf::from(libpath);
if libpath.is_relative() {
let config_dir = geyser_plugin_config_file.parent().ok_or_else(|| {
GeyserPluginServiceError::CannotOpenConfigFile(format!(
let config_dir = accountsdb_plugin_config_file.parent().ok_or_else(|| {
AccountsdbPluginServiceError::CannotOpenConfigFile(format!(
"Failed to resolve parent of {:?}",
geyser_plugin_config_file,
accountsdb_plugin_config_file,
))
})?;
libpath = config_dir.join(libpath);
}
let config_file = geyser_plugin_config_file
let config_file = accountsdb_plugin_config_file
.as_os_str()
.to_str()
.ok_or(GeyserPluginServiceError::InvalidPluginPath)?;
.ok_or(AccountsdbPluginServiceError::InvalidPluginPath)?;
unsafe {
let result = plugin_manager.load_plugin(libpath.to_str().unwrap(), config_file);
@@ -190,7 +193,7 @@ impl GeyserPluginService {
"Failed to load the plugin library: {:?}, error: {:?}",
libpath, err
);
return Err(GeyserPluginServiceError::PluginLoadError(msg));
return Err(AccountsdbPluginServiceError::PluginLoadError(msg));
}
}
Ok(())

View File

@@ -1,10 +1,10 @@
use {
crate::{
accountsdb_plugin_manager::AccountsDbPluginManager,
block_metadata_notifier_interface::BlockMetadataNotifier,
geyser_plugin_manager::GeyserPluginManager,
},
log::*,
solana_geyser_plugin_interface::geyser_plugin_interface::{
solana_accountsdb_plugin_interface::accountsdb_plugin_interface::{
ReplicaBlockInfo, ReplicaBlockInfoVersions,
},
solana_measure::measure::Measure,
@@ -16,7 +16,7 @@ use {
};
pub(crate) struct BlockMetadataNotifierImpl {
plugin_manager: Arc<RwLock<GeyserPluginManager>>,
plugin_manager: Arc<RwLock<AccountsDbPluginManager>>,
}
impl BlockMetadataNotifier for BlockMetadataNotifierImpl {
@@ -36,7 +36,7 @@ impl BlockMetadataNotifier for BlockMetadataNotifierImpl {
let rewards = Self::build_rewards(rewards);
for plugin in plugin_manager.plugins.iter_mut() {
let mut measure = Measure::start("geyser-plugin-update-slot");
let mut measure = Measure::start("accountsdb-plugin-update-slot");
let block_info =
Self::build_replica_block_info(slot, blockhash, &rewards, block_time, block_height);
let block_info = ReplicaBlockInfoVersions::V0_0_1(&block_info);
@@ -59,7 +59,7 @@ impl BlockMetadataNotifier for BlockMetadataNotifierImpl {
}
measure.stop();
inc_new_counter_debug!(
"geyser-plugin-update-block-metadata-us",
"accountsdb-plugin-update-block-metadata-us",
measure.as_us() as usize,
1000,
1000
@@ -99,7 +99,7 @@ impl BlockMetadataNotifierImpl {
}
}
pub fn new(plugin_manager: Arc<RwLock<GeyserPluginManager>>) -> Self {
pub fn new(plugin_manager: Arc<RwLock<AccountsDbPluginManager>>) -> Self {
Self { plugin_manager }
}
}

View File

@@ -1,8 +1,8 @@
pub mod accounts_update_notifier;
pub mod accountsdb_plugin_manager;
pub mod accountsdb_plugin_service;
pub mod block_metadata_notifier;
pub mod block_metadata_notifier_interface;
pub mod geyser_plugin_manager;
pub mod geyser_plugin_service;
pub mod slot_status_notifier;
pub mod slot_status_observer;
pub mod transaction_notifier;

View File

@@ -1,7 +1,7 @@
use {
crate::geyser_plugin_manager::GeyserPluginManager,
crate::accountsdb_plugin_manager::AccountsDbPluginManager,
log::*,
solana_geyser_plugin_interface::geyser_plugin_interface::SlotStatus,
solana_accountsdb_plugin_interface::accountsdb_plugin_interface::SlotStatus,
solana_measure::measure::Measure,
solana_metrics::*,
solana_sdk::clock::Slot,
@@ -22,7 +22,7 @@ pub trait SlotStatusNotifierInterface {
pub type SlotStatusNotifier = Arc<RwLock<dyn SlotStatusNotifierInterface + Sync + Send>>;
pub struct SlotStatusNotifierImpl {
plugin_manager: Arc<RwLock<GeyserPluginManager>>,
plugin_manager: Arc<RwLock<AccountsDbPluginManager>>,
}
impl SlotStatusNotifierInterface for SlotStatusNotifierImpl {
@@ -40,7 +40,7 @@ impl SlotStatusNotifierInterface for SlotStatusNotifierImpl {
}
impl SlotStatusNotifierImpl {
pub fn new(plugin_manager: Arc<RwLock<GeyserPluginManager>>) -> Self {
pub fn new(plugin_manager: Arc<RwLock<AccountsDbPluginManager>>) -> Self {
Self { plugin_manager }
}
@@ -51,8 +51,8 @@ impl SlotStatusNotifierImpl {
}
for plugin in plugin_manager.plugins.iter_mut() {
let mut measure = Measure::start("geyser-plugin-update-slot");
match plugin.update_slot_status(slot, parent, slot_status) {
let mut measure = Measure::start("accountsdb-plugin-update-slot");
match plugin.update_slot_status(slot, parent, slot_status.clone()) {
Err(err) => {
error!(
"Failed to update slot status at slot {}, error: {} to plugin {}",
@@ -71,7 +71,7 @@ impl SlotStatusNotifierImpl {
}
measure.stop();
inc_new_counter_debug!(
"geyser-plugin-update-slot-us",
"accountsdb-plugin-update-slot-us",
measure.as_us() as usize,
1000,
1000

View File

@@ -1,8 +1,8 @@
/// Module responsible for notifying plugins of transactions
use {
crate::geyser_plugin_manager::GeyserPluginManager,
crate::accountsdb_plugin_manager::AccountsDbPluginManager,
log::*,
solana_geyser_plugin_interface::geyser_plugin_interface::{
solana_accountsdb_plugin_interface::accountsdb_plugin_interface::{
ReplicaTransactionInfo, ReplicaTransactionInfoVersions,
},
solana_measure::measure::Measure,
@@ -16,9 +16,9 @@ use {
/// This implementation of TransactionNotifier is passed to the rpc's TransactionStatusService
/// at the validator startup. TransactionStatusService invokes the notify_transaction method
/// for new transactions. The implementation in turn invokes the notify_transaction of each
/// plugin enabled with transaction notification managed by the GeyserPluginManager.
/// plugin enabled with transaction notification managed by the AccountsDbPluginManager.
pub(crate) struct TransactionNotifierImpl {
plugin_manager: Arc<RwLock<GeyserPluginManager>>,
plugin_manager: Arc<RwLock<AccountsDbPluginManager>>,
}
impl TransactionNotifier for TransactionNotifierImpl {
@@ -29,7 +29,7 @@ impl TransactionNotifier for TransactionNotifierImpl {
transaction_status_meta: &TransactionStatusMeta,
transaction: &SanitizedTransaction,
) {
let mut measure = Measure::start("geyser-plugin-notify_plugins_of_transaction_info");
let mut measure = Measure::start("accountsdb-plugin-notify_plugins_of_transaction_info");
let transaction_log_info =
Self::build_replica_transaction_info(signature, transaction_status_meta, transaction);
@@ -64,7 +64,7 @@ impl TransactionNotifier for TransactionNotifierImpl {
}
measure.stop();
inc_new_counter_debug!(
"geyser-plugin-notify_plugins_of_transaction_info-us",
"accountsdb-plugin-notify_plugins_of_transaction_info-us",
measure.as_us() as usize,
10000,
10000
@@ -73,7 +73,7 @@ impl TransactionNotifier for TransactionNotifierImpl {
}
impl TransactionNotifierImpl {
pub fn new(plugin_manager: Arc<RwLock<GeyserPluginManager>>) -> Self {
pub fn new(plugin_manager: Arc<RwLock<AccountsDbPluginManager>>) -> Self {
Self { plugin_manager }
}

View File

@@ -2,7 +2,7 @@
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2021"
name = "solana-banking-bench"
version = "1.10.6"
version = "1.10.0"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@@ -14,17 +14,17 @@ crossbeam-channel = "0.5"
log = "0.4.14"
rand = "0.7.0"
rayon = "1.5.1"
solana-core = { path = "../core", version = "=1.10.6" }
solana-gossip = { path = "../gossip", version = "=1.10.6" }
solana-ledger = { path = "../ledger", version = "=1.10.6" }
solana-logger = { path = "../logger", version = "=1.10.6" }
solana-measure = { path = "../measure", version = "=1.10.6" }
solana-perf = { path = "../perf", version = "=1.10.6" }
solana-poh = { path = "../poh", version = "=1.10.6" }
solana-runtime = { path = "../runtime", version = "=1.10.6" }
solana-sdk = { path = "../sdk", version = "=1.10.6" }
solana-streamer = { path = "../streamer", version = "=1.10.6" }
solana-version = { path = "../version", version = "=1.10.6" }
solana-core = { path = "../core", version = "=1.10.0" }
solana-gossip = { path = "../gossip", version = "=1.10.0" }
solana-ledger = { path = "../ledger", version = "=1.10.0" }
solana-logger = { path = "../logger", version = "=1.10.0" }
solana-measure = { path = "../measure", version = "=1.10.0" }
solana-perf = { path = "../perf", version = "=1.10.0" }
solana-poh = { path = "../poh", version = "=1.10.0" }
solana-runtime = { path = "../runtime", version = "=1.10.0" }
solana-streamer = { path = "../streamer", version = "=1.10.0" }
solana-sdk = { path = "../sdk", version = "=1.10.0" }
solana-version = { path = "../version", version = "=1.10.0" }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-banks-client"
version = "1.10.6"
version = "1.10.0"
description = "Solana banks client"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
@@ -12,17 +12,17 @@ edition = "2021"
[dependencies]
borsh = "0.9.3"
futures = "0.3"
solana-banks-interface = { path = "../banks-interface", version = "=1.10.6" }
solana-program = { path = "../sdk/program", version = "=1.10.6" }
solana-sdk = { path = "../sdk", version = "=1.10.6" }
solana-banks-interface = { path = "../banks-interface", version = "=1.10.0" }
solana-program = { path = "../sdk/program", version = "=1.10.0" }
solana-sdk = { path = "../sdk", version = "=1.10.0" }
tarpc = { version = "0.27.2", features = ["full"] }
thiserror = "1.0"
tokio = { version = "1", features = ["full"] }
tokio-serde = { version = "0.8", features = ["bincode"] }
[dev-dependencies]
solana-banks-server = { path = "../banks-server", version = "=1.10.6" }
solana-runtime = { path = "../runtime", version = "=1.10.6" }
solana-runtime = { path = "../runtime", version = "=1.10.0" }
solana-banks-server = { path = "../banks-server", version = "=1.10.0" }
[lib]
crate-type = ["lib"]

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-banks-interface"
version = "1.10.6"
version = "1.10.0"
description = "Solana banks RPC interface"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
@@ -11,7 +11,7 @@ edition = "2021"
[dependencies]
serde = { version = "1.0.136", features = ["derive"] }
solana-sdk = { path = "../sdk", version = "=1.10.6" }
solana-sdk = { path = "../sdk", version = "=1.10.0" }
tarpc = { version = "0.27.2", features = ["full"] }
[lib]

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-banks-server"
version = "1.10.6"
version = "1.10.0"
description = "Solana banks server"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
@@ -13,10 +13,10 @@ edition = "2021"
bincode = "1.3.3"
crossbeam-channel = "0.5"
futures = "0.3"
solana-banks-interface = { path = "../banks-interface", version = "=1.10.6" }
solana-runtime = { path = "../runtime", version = "=1.10.6" }
solana-sdk = { path = "../sdk", version = "=1.10.6" }
solana-send-transaction-service = { path = "../send-transaction-service", version = "=1.10.6" }
solana-banks-interface = { path = "../banks-interface", version = "=1.10.0" }
solana-runtime = { path = "../runtime", version = "=1.10.0" }
solana-sdk = { path = "../sdk", version = "=1.10.0" }
solana-send-transaction-service = { path = "../send-transaction-service", version = "=1.10.0" }
tarpc = { version = "0.27.2", features = ["full"] }
tokio = { version = "1", features = ["full"] }
tokio-serde = { version = "0.8", features = ["bincode"] }

View File

@@ -24,7 +24,7 @@ use {
transaction::{self, SanitizedTransaction, Transaction},
},
solana_send_transaction_service::{
send_transaction_service::{SendTransactionService, TransactionInfo, DEFAULT_TPU_USE_QUIC},
send_transaction_service::{SendTransactionService, TransactionInfo},
tpu_info::NullTpuInfo,
},
std::{
@@ -399,7 +399,6 @@ pub async fn start_tcp_server(
receiver,
5_000,
0,
DEFAULT_TPU_USE_QUIC,
);
let server = BanksServer::new(

View File

@@ -2,18 +2,18 @@
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2021"
name = "solana-bench-streamer"
version = "1.10.6"
version = "1.10.0"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
publish = false
[dependencies]
clap = "2.33.1"
crossbeam-channel = "0.5"
solana-net-utils = { path = "../net-utils", version = "=1.10.6" }
solana-streamer = { path = "../streamer", version = "=1.10.6" }
solana-version = { path = "../version", version = "=1.10.6" }
clap = "2.33.1"
solana-streamer = { path = "../streamer", version = "=1.10.0" }
solana-net-utils = { path = "../net-utils", version = "=1.10.0" }
solana-version = { path = "../version", version = "=1.10.0" }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -2,7 +2,7 @@
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2021"
name = "solana-bench-tps"
version = "1.10.6"
version = "1.10.0"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@@ -13,25 +13,25 @@ clap = "2.33.1"
crossbeam-channel = "0.5"
log = "0.4.14"
rayon = "1.5.1"
serde_json = "1.0.79"
serde_json = "1.0.78"
serde_yaml = "0.8.23"
solana-client = { path = "../client", version = "=1.10.6" }
solana-core = { path = "../core", version = "=1.10.6" }
solana-faucet = { path = "../faucet", version = "=1.10.6" }
solana-genesis = { path = "../genesis", version = "=1.10.6" }
solana-gossip = { path = "../gossip", version = "=1.10.6" }
solana-logger = { path = "../logger", version = "=1.10.6" }
solana-measure = { path = "../measure", version = "=1.10.6" }
solana-metrics = { path = "../metrics", version = "=1.10.6" }
solana-net-utils = { path = "../net-utils", version = "=1.10.6" }
solana-runtime = { path = "../runtime", version = "=1.10.6" }
solana-sdk = { path = "../sdk", version = "=1.10.6" }
solana-streamer = { path = "../streamer", version = "=1.10.6" }
solana-version = { path = "../version", version = "=1.10.6" }
solana-core = { path = "../core", version = "=1.10.0" }
solana-genesis = { path = "../genesis", version = "=1.10.0" }
solana-client = { path = "../client", version = "=1.10.0" }
solana-faucet = { path = "../faucet", version = "=1.10.0" }
solana-gossip = { path = "../gossip", version = "=1.10.0" }
solana-logger = { path = "../logger", version = "=1.10.0" }
solana-metrics = { path = "../metrics", version = "=1.10.0" }
solana-measure = { path = "../measure", version = "=1.10.0" }
solana-net-utils = { path = "../net-utils", version = "=1.10.0" }
solana-runtime = { path = "../runtime", version = "=1.10.0" }
solana-sdk = { path = "../sdk", version = "=1.10.0" }
solana-streamer = { path = "../streamer", version = "=1.10.0" }
solana-version = { path = "../version", version = "=1.10.0" }
[dev-dependencies]
serial_test = "0.6.0"
solana-local-cluster = { path = "../local-cluster", version = "=1.10.6" }
serial_test = "0.5.1"
solana-local-cluster = { path = "../local-cluster", version = "=1.10.0" }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -475,7 +475,6 @@ fn do_tx_transfers<T: Client>(
let tx_len = txs0.len();
let transfer_start = Instant::now();
let mut old_transactions = false;
let mut transactions = Vec::<_>::new();
for tx in txs0 {
let now = timestamp();
// Transactions that are too old will be rejected by the cluster Don't bother
@@ -484,13 +483,10 @@ fn do_tx_transfers<T: Client>(
old_transactions = true;
continue;
}
transactions.push(tx.0);
client
.async_send_transaction(tx.0)
.expect("async_send_transaction in do_tx_transfers");
}
if let Err(error) = client.async_send_batch(transactions) {
warn!("send_batch_sync in do_tx_transfers failed: {}", error);
}
if old_transactions {
let mut shared_txs_wl = shared_txs.write().expect("write lock in do_tx_transfers");
shared_txs_wl.clear();

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-bloom"
version = "1.10.6"
version = "1.10.0"
description = "Solana bloom filter"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
@@ -12,14 +12,14 @@ edition = "2021"
[dependencies]
bv = { version = "0.11.1", features = ["serde"] }
fnv = "1.0.7"
log = "0.4.14"
rand = "0.7.0"
rayon = "1.5.1"
serde = { version = "1.0.136", features = ["rc"] }
rayon = "1.5.1"
serde_derive = "1.0.103"
solana-frozen-abi = { path = "../frozen-abi", version = "=1.10.6" }
solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.10.6" }
solana-sdk = { path = "../sdk", version = "=1.10.6" }
solana-frozen-abi = { path = "../frozen-abi", version = "=1.10.0" }
solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.10.0" }
solana-sdk = { path = "../sdk", version = "=1.10.0" }
log = "0.4.14"
[lib]
crate-type = ["lib"]

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-bucket-map"
version = "1.10.6"
version = "1.10.0"
description = "solana-bucket-map"
homepage = "https://solana.com/"
documentation = "https://docs.rs/solana-bucket-map"
@@ -11,18 +11,18 @@ license = "Apache-2.0"
edition = "2021"
[dependencies]
solana-sdk = { path = "../sdk", version = "=1.10.0" }
memmap2 = "0.5.2"
log = { version = "0.4.11" }
memmap2 = "0.5.3"
modular-bitfield = "0.11.2"
solana-measure = { path = "../measure", version = "=1.10.0" }
rand = "0.7.0"
solana-measure = { path = "../measure", version = "=1.10.6" }
solana-sdk = { path = "../sdk", version = "=1.10.6" }
tempfile = "3.3.0"
modular-bitfield = "0.11.2"
[dev-dependencies]
fs_extra = "1.2.0"
rayon = "1.5.0"
solana-logger = { path = "../logger", version = "=1.10.6" }
solana-logger = { path = "../logger", version = "=1.10.0" }
[lib]
crate-type = ["lib"]

View File

@@ -137,7 +137,7 @@ all_test_steps() {
^ci/test-coverage.sh \
^scripts/coverage.sh \
; then
command_step coverage ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_nightly_docker_image ci/test-coverage.sh" 50
command_step coverage ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_nightly_docker_image ci/test-coverage.sh" 40
wait_step
else
annotate --style info --context test-coverage \
@@ -152,14 +152,14 @@ all_test_steps() {
^ci/test-coverage.sh \
^scripts/coverage-in-disk.sh \
; then
command_step coverage-in-disk ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_nightly_docker_image ci/test-coverage.sh" 50
command_step coverage-in-disk ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_nightly_docker_image ci/test-coverage.sh" 40
wait_step
else
annotate --style info --context test-coverage \
"Coverage skipped as no .rs files were modified"
fi
# Full test suite
command_step stable ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_stable_docker_image ci/test-stable.sh" 70
command_step stable ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_stable_docker_image ci/test-stable.sh" 60
wait_step
# BPF test suite

View File

@@ -102,8 +102,6 @@ command_step() {
command: "$2"
timeout_in_minutes: $3
artifact_paths: "log-*.txt"
agents:
- "queue=solana"
EOF
}
@@ -139,7 +137,7 @@ all_test_steps() {
^ci/test-coverage.sh \
^scripts/coverage.sh \
; then
command_step coverage ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_nightly_docker_image ci/test-coverage.sh" 50
command_step coverage ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_nightly_docker_image ci/test-coverage.sh" 40
wait_step
else
annotate --style info --context test-coverage \
@@ -147,7 +145,7 @@ all_test_steps() {
fi
# Full test suite
command_step stable ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_stable_docker_image ci/test-stable.sh" 70
command_step stable ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_stable_docker_image ci/test-stable.sh" 60
wait_step
# BPF test suite
@@ -170,7 +168,7 @@ all_test_steps() {
timeout_in_minutes: 20
artifact_paths: "bpf-dumps.tar.bz2"
agents:
- "queue=solana"
- "queue=default"
EOF
else
annotate --style info \
@@ -223,8 +221,6 @@ EOF
- command: "scripts/build-downstream-projects.sh"
name: "downstream-projects"
timeout_in_minutes: 30
agents:
- "queue=solana"
EOF
else
annotate --style info \
@@ -250,8 +246,6 @@ EOF
- command: "scripts/build-downstream-anchor-projects.sh"
name: "downstream-anchor-projects"
timeout_in_minutes: 10
agents:
- "queue=solana"
EOF
else
annotate --style info \

View File

@@ -8,6 +8,11 @@ src_root="$(readlink -f "${here}/..")"
cd "${src_root}"
cargo_audit_ignores=(
# failure is officially deprecated/unmaintained
#
# Blocked on multiple upstream crates removing their `failure` dependency.
--ignore RUSTSEC-2020-0036
# `net2` crate has been deprecated; use `socket2` instead
#
# Blocked on https://github.com/paritytech/jsonrpc/issues/575
@@ -25,10 +30,22 @@ cargo_audit_ignores=(
# generic-array: arr! macro erases lifetimes
#
# Blocked on new spl dependencies on solana-program v1.9
# due to curve25519-dalek dependency
# Blocked on libsecp256k1 releasing with upgraded dependencies
# https://github.com/paritytech/libsecp256k1/issues/66
--ignore RUSTSEC-2020-0146
# hyper: Lenient `hyper` header parsing of `Content-Length` could allow request smuggling
#
# Blocked on jsonrpc removing dependency on unmaintained `websocket`
# https://github.com/paritytech/jsonrpc/issues/605
--ignore RUSTSEC-2021-0078
# hyper: Integer overflow in `hyper`'s parsing of the `Transfer-Encoding` header leads to data loss
#
# Blocked on jsonrpc removing dependency on unmaintained `websocket`
# https://github.com/paritytech/jsonrpc/issues/605
--ignore RUSTSEC-2021-0079
# chrono: Potential segfault in `localtime_r` invocations
#
# Blocked due to no safe upgrade

View File

@@ -70,7 +70,7 @@ for Cargo_toml in $Cargo_tomls; do
rm -rf crate-test
"$cargo" stable init crate-test
cd crate-test/
echo "${crate_name} = \"=${expectedCrateVersion}\"" >> Cargo.toml
echo "${crate_name} = \"${expectedCrateVersion}\"" >> Cargo.toml
echo "[workspace]" >> Cargo.toml
"$cargo" stable check
) && really_uploaded=1

View File

@@ -57,20 +57,26 @@ if [[ $CI_BASE_BRANCH = "$EDGE_CHANNEL" ]]; then
exit "$check_status"
fi
# Ensure nightly and --benches
# Ensure nightly and --benches
_ scripts/cargo-for-all-lock-files.sh nightly check --locked --all-targets
else
echo "Note: cargo-for-all-lock-files.sh skipped because $CI_BASE_BRANCH != $EDGE_CHANNEL"
fi
_ ci/order-crates-for-publishing.py
_ ci/order-crates-for-publishing.py
# -Z... is needed because of clippy bug: https://github.com/rust-lang/rust-clippy/issues/4612
# run nightly clippy for `sdk/` as there's a moderate amount of nightly-only code there
_ scripts/cargo-for-all-lock-files.sh -- nightly clippy -Zunstable-options --all-targets -- --deny=warnings --deny=clippy::integer_arithmetic
_ "$cargo" nightly clippy -Zunstable-options --workspace --all-targets -- --deny=warnings --deny=clippy::integer_arithmetic
_ scripts/cargo-for-all-lock-files.sh -- nightly fmt --all -- --check
_ "$cargo" nightly fmt --all -- --check
_ ci/do-audit.sh
_ ci/do-audit.sh
{
cd programs/bpf
_ "$cargo" nightly clippy --all -- --deny=warnings --allow=clippy::missing_safety_doc
_ "$cargo" nightly fmt --all -- --check
}
echo --- ok

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-clap-utils"
version = "1.10.6"
version = "1.10.0"
description = "Solana utilities for the clap"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
@@ -10,16 +10,16 @@ documentation = "https://docs.rs/solana-clap-utils"
edition = "2021"
[dependencies]
chrono = "0.4"
clap = "2.33.0"
rpassword = "6.0"
solana-perf = { path = "../perf", version = "=1.10.6" }
solana-remote-wallet = { path = "../remote-wallet", version = "=1.10.6", default-features = false }
solana-sdk = { path = "../sdk", version = "=1.10.6" }
rpassword = "5.0"
solana-perf = { path = "../perf", version = "=1.10.0" }
solana-remote-wallet = { path = "../remote-wallet", version = "=1.10.0", default-features = false}
solana-sdk = { path = "../sdk", version = "=1.10.0" }
thiserror = "1.0.30"
tiny-bip39 = "0.8.2"
uriparse = "0.6.3"
url = "2.2.2"
chrono = "0.4"
[dev-dependencies]
tempfile = "3.3.0"

View File

@@ -17,7 +17,7 @@ use {
},
bip39::{Language, Mnemonic, Seed},
clap::ArgMatches,
rpassword::prompt_password,
rpassword::prompt_password_stderr,
solana_remote_wallet::{
locator::{Locator as RemoteWalletLocator, LocatorError as RemoteWalletLocatorError},
remote_keypair::generate_remote_keypair,
@@ -945,9 +945,9 @@ pub const SKIP_SEED_PHRASE_VALIDATION_ARG: ArgConstant<'static> = ArgConstant {
/// Prompts user for a passphrase and then asks for confirmirmation to check for mistakes
pub fn prompt_passphrase(prompt: &str) -> Result<String, Box<dyn error::Error>> {
let passphrase = prompt_password(prompt)?;
let passphrase = prompt_password_stderr(prompt)?;
if !passphrase.is_empty() {
let confirmed = rpassword::prompt_password("Enter same passphrase again: ")?;
let confirmed = rpassword::prompt_password_stderr("Enter same passphrase again: ")?;
if confirmed != passphrase {
return Err("Passphrases did not match".into());
}
@@ -1055,7 +1055,7 @@ pub fn keypair_from_seed_phrase(
derivation_path: Option<DerivationPath>,
legacy: bool,
) -> Result<Keypair, Box<dyn error::Error>> {
let seed_phrase = prompt_password(&format!("[{}] seed phrase: ", keypair_name))?;
let seed_phrase = prompt_password_stderr(&format!("[{}] seed phrase: ", keypair_name))?;
let seed_phrase = seed_phrase.trim();
let passphrase_prompt = format!(
"[{}] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue: ",

View File

@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2021"
name = "solana-cli-config"
description = "Blockchain, Rebuilt for Scale"
version = "1.10.6"
version = "1.10.0"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@@ -18,7 +18,7 @@ serde_yaml = "0.8.23"
url = "2.2.2"
[dev-dependencies]
anyhow = "1.0.56"
anyhow = "1.0.53"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -3,32 +3,29 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2021"
name = "solana-cli-output"
description = "Blockchain, Rebuilt for Scale"
version = "1.10.6"
version = "1.10.0"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
documentation = "https://docs.rs/solana-cli-output"
[dependencies]
Inflector = "0.11.4"
base64 = "0.13.0"
chrono = { version = "0.4.11", features = ["serde"] }
clap = "2.33.0"
console = "0.15.0"
humantime = "2.0.1"
Inflector = "0.11.4"
indicatif = "0.16.2"
serde = "1.0.136"
serde_json = "1.0.79"
solana-account-decoder = { path = "../account-decoder", version = "=1.10.6" }
solana-clap-utils = { path = "../clap-utils", version = "=1.10.6" }
solana-client = { path = "../client", version = "=1.10.6" }
solana-sdk = { path = "../sdk", version = "=1.10.6" }
solana-transaction-status = { path = "../transaction-status", version = "=1.10.6" }
solana-vote-program = { path = "../programs/vote", version = "=1.10.6" }
serde_json = "1.0.78"
solana-account-decoder = { path = "../account-decoder", version = "=1.10.0" }
solana-clap-utils = { path = "../clap-utils", version = "=1.10.0" }
solana-client = { path = "../client", version = "=1.10.0" }
solana-sdk = { path = "../sdk", version = "=1.10.0" }
solana-transaction-status = { path = "../transaction-status", version = "=1.10.0" }
solana-vote-program = { path = "../programs/vote", version = "=1.10.0" }
spl-memo = { version = "=3.0.1", features = ["no-entrypoint"] }
[dev-dependencies]
ed25519-dalek = "=1.0.1"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -28,7 +28,7 @@ use {
signature::Signature,
stake::state::{Authorized, Lockup},
stake_history::StakeHistoryEntry,
transaction::{Transaction, TransactionError, VersionedTransaction},
transaction::{Transaction, TransactionError},
},
solana_transaction_status::{
EncodedConfirmedBlock, EncodedTransaction, TransactionConfirmationStatus,
@@ -2218,7 +2218,7 @@ pub enum CliSignatureVerificationStatus {
}
impl CliSignatureVerificationStatus {
pub fn verify_transaction(tx: &VersionedTransaction) -> Vec<Self> {
pub fn verify_transaction(tx: &Transaction) -> Vec<Self> {
tx.verify_with_results()
.iter()
.zip(&tx.signatures)
@@ -2335,7 +2335,7 @@ impl fmt::Display for CliBlock {
writeln_transaction(
f,
&transaction_with_meta.transaction.decode().unwrap(),
transaction_with_meta.meta.as_ref(),
&transaction_with_meta.meta,
" ",
None,
None,
@@ -2354,7 +2354,7 @@ pub struct CliTransaction {
#[serde(skip_serializing)]
pub slot: Option<Slot>,
#[serde(skip_serializing)]
pub decoded_transaction: VersionedTransaction,
pub decoded_transaction: Transaction,
#[serde(skip_serializing)]
pub prefix: String,
#[serde(skip_serializing_if = "Vec::is_empty")]
@@ -2369,7 +2369,7 @@ impl fmt::Display for CliTransaction {
writeln_transaction(
f,
&self.decoded_transaction,
self.meta.as_ref(),
&self.meta,
&self.prefix,
if !self.sigverify_status.is_empty() {
Some(&self.sigverify_status)
@@ -2776,10 +2776,10 @@ mod tests {
let expected_msg = "AwECBwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDgTl3Dqh9\
F19Wo1Rmw0x+zMuNipG07jeiXfYPW4/Js5QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE\
BAQEBAUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBgYGBgYGBgYGBgYGBgYGBgYG\
BgYGBgYGBgYGBgYGBgYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAan1RcZLFaO\
4IqEX3PSl4jPA1wxRbIas0TYBi6pQAAABwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcH\
BwcCBQMEBgIEBAAAAAUCAQMMAgAAACoAAAAAAAAA"
BAQEBAYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBQUFBQUFBQUFBQUFBQUFBQUF\
BQUFBQUFBQUFBQUFBQUGp9UXGSxWjuCKhF9z0peIzwNcMUWyGrNE2AYuqUAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcH\
BwcCBgMDBQIEBAAAAAYCAQQMAgAAACoAAAAAAAAA"
.to_string();
let config = ReturnSignersConfig {
dump_transaction_message: true,

View File

@@ -4,18 +4,10 @@ use {
console::style,
indicatif::{ProgressBar, ProgressStyle},
solana_sdk::{
clock::UnixTimestamp,
hash::Hash,
instruction::CompiledInstruction,
message::v0::MessageAddressTableLookup,
native_token::lamports_to_sol,
program_utils::limited_deserialize,
pubkey::Pubkey,
signature::Signature,
stake,
transaction::{TransactionError, TransactionVersion, VersionedTransaction},
clock::UnixTimestamp, hash::Hash, message::Message, native_token::lamports_to_sol,
program_utils::limited_deserialize, pubkey::Pubkey, stake, transaction::Transaction,
},
solana_transaction_status::{Rewards, UiTransactionStatusMeta},
solana_transaction_status::UiTransactionStatusMeta,
spl_memo::{id as spl_memo_id, v1::id as spl_memo_v1_id},
std::{collections::HashMap, fmt, io},
};
@@ -139,28 +131,22 @@ pub fn println_signers(
println!();
}
struct CliAccountMeta {
is_signer: bool,
is_writable: bool,
is_invoked: bool,
}
fn format_account_mode(meta: CliAccountMeta) -> String {
fn format_account_mode(message: &Message, index: usize) -> String {
format!(
"{}r{}{}", // accounts are always readable...
if meta.is_signer {
if message.is_signer(index) {
"s" // stands for signer
} else {
"-"
},
if meta.is_writable {
if message.is_writable(index) {
"w" // comment for consistent rust fmt (no joking; lol)
} else {
"-"
},
// account may be executable on-chain while not being
// designated as a program-id in the message
if meta.is_invoked {
if message.maybe_executable(index) {
"x"
} else {
// programs to be executed via CPI cannot be identified as
@@ -170,178 +156,41 @@ fn format_account_mode(meta: CliAccountMeta) -> String {
)
}
fn write_transaction<W: io::Write>(
pub fn write_transaction<W: io::Write>(
w: &mut W,
transaction: &VersionedTransaction,
transaction_status: Option<&UiTransactionStatusMeta>,
transaction: &Transaction,
transaction_status: &Option<UiTransactionStatusMeta>,
prefix: &str,
sigverify_status: Option<&[CliSignatureVerificationStatus]>,
block_time: Option<UnixTimestamp>,
timezone: CliTimezone,
) -> io::Result<()> {
write_block_time(w, block_time, timezone, prefix)?;
let message = &transaction.message;
let account_keys: Vec<AccountKeyType> = {
let static_keys_iter = message
.static_account_keys()
.iter()
.map(AccountKeyType::Known);
let dynamic_keys: Vec<AccountKeyType> = message
.address_table_lookups()
.map(transform_lookups_to_unknown_keys)
.unwrap_or_default();
static_keys_iter.chain(dynamic_keys).collect()
};
write_version(w, transaction.version(), prefix)?;
write_recent_blockhash(w, message.recent_blockhash(), prefix)?;
write_signatures(w, &transaction.signatures, sigverify_status, prefix)?;
let mut fee_payer_index = None;
for (account_index, account) in account_keys.iter().enumerate() {
if fee_payer_index.is_none() && message.is_non_loader_key(account_index) {
fee_payer_index = Some(account_index)
}
let account_meta = CliAccountMeta {
is_signer: message.is_signer(account_index),
is_writable: message.is_maybe_writable(account_index),
is_invoked: message.is_invoked(account_index),
};
write_account(
w,
account_index,
*account,
format_account_mode(account_meta),
Some(account_index) == fee_payer_index,
prefix,
)?;
}
for (instruction_index, instruction) in message.instructions().iter().enumerate() {
let program_pubkey = account_keys[instruction.program_id_index as usize];
let instruction_accounts = instruction
.accounts
.iter()
.map(|account_index| (account_keys[*account_index as usize], *account_index));
write_instruction(
w,
instruction_index,
program_pubkey,
instruction,
instruction_accounts,
prefix,
)?;
}
if let Some(address_table_lookups) = message.address_table_lookups() {
write_address_table_lookups(w, address_table_lookups, prefix)?;
}
if let Some(transaction_status) = transaction_status {
write_status(w, &transaction_status.status, prefix)?;
write_fees(w, transaction_status.fee, prefix)?;
write_balances(w, transaction_status, prefix)?;
write_log_messages(w, transaction_status.log_messages.as_ref(), prefix)?;
write_rewards(w, transaction_status.rewards.as_ref(), prefix)?;
} else {
writeln!(w, "{}Status: Unavailable", prefix)?;
}
Ok(())
}
fn transform_lookups_to_unknown_keys(lookups: &[MessageAddressTableLookup]) -> Vec<AccountKeyType> {
let unknown_writable_keys = lookups
.iter()
.enumerate()
.flat_map(|(lookup_index, lookup)| {
lookup
.writable_indexes
.iter()
.map(move |table_index| AccountKeyType::Unknown {
lookup_index,
table_index: *table_index,
})
});
let unknown_readonly_keys = lookups
.iter()
.enumerate()
.flat_map(|(lookup_index, lookup)| {
lookup
.readonly_indexes
.iter()
.map(move |table_index| AccountKeyType::Unknown {
lookup_index,
table_index: *table_index,
})
});
unknown_writable_keys.chain(unknown_readonly_keys).collect()
}
enum CliTimezone {
Local,
#[allow(dead_code)]
Utc,
}
fn write_block_time<W: io::Write>(
w: &mut W,
block_time: Option<UnixTimestamp>,
timezone: CliTimezone,
prefix: &str,
) -> io::Result<()> {
if let Some(block_time) = block_time {
let block_time_output = match timezone {
CliTimezone::Local => format!("{:?}", Local.timestamp(block_time, 0)),
CliTimezone::Utc => format!("{:?}", Utc.timestamp(block_time, 0)),
};
writeln!(w, "{}Block Time: {}", prefix, block_time_output,)?;
writeln!(
w,
"{}Block Time: {:?}",
prefix,
Local.timestamp(block_time, 0)
)?;
}
Ok(())
}
fn write_version<W: io::Write>(
w: &mut W,
version: TransactionVersion,
prefix: &str,
) -> io::Result<()> {
let version = match version {
TransactionVersion::Legacy(_) => "legacy".to_string(),
TransactionVersion::Number(number) => number.to_string(),
};
writeln!(w, "{}Version: {}", prefix, version)
}
fn write_recent_blockhash<W: io::Write>(
w: &mut W,
recent_blockhash: &Hash,
prefix: &str,
) -> io::Result<()> {
writeln!(w, "{}Recent Blockhash: {:?}", prefix, recent_blockhash)
}
fn write_signatures<W: io::Write>(
w: &mut W,
signatures: &[Signature],
sigverify_status: Option<&[CliSignatureVerificationStatus]>,
prefix: &str,
) -> io::Result<()> {
writeln!(
w,
"{}Recent Blockhash: {:?}",
prefix, message.recent_blockhash
)?;
let sigverify_statuses = if let Some(sigverify_status) = sigverify_status {
sigverify_status
.iter()
.map(|s| format!(" ({})", s))
.collect()
} else {
vec!["".to_string(); signatures.len()]
vec!["".to_string(); transaction.signatures.len()]
};
for (signature_index, (signature, sigverify_status)) in
signatures.iter().zip(&sigverify_statuses).enumerate()
for (signature_index, (signature, sigverify_status)) in transaction
.signatures
.iter()
.zip(&sigverify_statuses)
.enumerate()
{
writeln!(
w,
@@ -349,80 +198,44 @@ fn write_signatures<W: io::Write>(
prefix, signature_index, signature, sigverify_status,
)?;
}
Ok(())
}
#[derive(Debug, Clone, Copy, PartialEq)]
enum AccountKeyType<'a> {
Known(&'a Pubkey),
Unknown {
lookup_index: usize,
table_index: u8,
},
}
impl fmt::Display for AccountKeyType<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Known(address) => write!(f, "{}", address),
Self::Unknown {
lookup_index,
table_index,
} => {
write!(
f,
"Unknown Address (uses lookup {} and index {})",
lookup_index, table_index
)
}
let mut fee_payer_index = None;
for (account_index, account) in message.account_keys.iter().enumerate() {
if fee_payer_index.is_none() && message.is_non_loader_key(account_index) {
fee_payer_index = Some(account_index)
}
}
}
fn write_account<W: io::Write>(
w: &mut W,
account_index: usize,
account_address: AccountKeyType,
account_mode: String,
is_fee_payer: bool,
prefix: &str,
) -> io::Result<()> {
writeln!(
w,
"{}Account {}: {} {}{}",
prefix,
account_index,
account_mode,
account_address,
if is_fee_payer { " (fee payer)" } else { "" },
)
}
fn write_instruction<'a, W: io::Write>(
w: &mut W,
instruction_index: usize,
program_pubkey: AccountKeyType,
instruction: &CompiledInstruction,
instruction_accounts: impl Iterator<Item = (AccountKeyType<'a>, u8)>,
prefix: &str,
) -> io::Result<()> {
writeln!(w, "{}Instruction {}", prefix, instruction_index)?;
writeln!(
w,
"{} Program: {} ({})",
prefix, program_pubkey, instruction.program_id_index
)?;
for (index, (account_address, account_index)) in instruction_accounts.enumerate() {
writeln!(
w,
"{} Account {}: {} ({})",
prefix, index, account_address, account_index
"{}Account {}: {} {}{}",
prefix,
account_index,
format_account_mode(message, account_index),
account,
if Some(account_index) == fee_payer_index {
" (fee payer)"
} else {
""
},
)?;
}
for (instruction_index, instruction) in message.instructions.iter().enumerate() {
let program_pubkey = message.account_keys[instruction.program_id_index as usize];
writeln!(w, "{}Instruction {}", prefix, instruction_index)?;
writeln!(
w,
"{} Program: {} ({})",
prefix, program_pubkey, instruction.program_id_index
)?;
for (account_index, account) in instruction.accounts.iter().enumerate() {
let account_pubkey = message.account_keys[*account as usize];
writeln!(
w,
"{} Account {}: {} ({})",
prefix, account_index, account_pubkey, account
)?;
}
let mut raw = true;
if let AccountKeyType::Known(program_pubkey) = program_pubkey {
if program_pubkey == &solana_vote_program::id() {
let mut raw = true;
if program_pubkey == solana_vote_program::id() {
if let Ok(vote_instruction) = limited_deserialize::<
solana_vote_program::vote_instruction::VoteInstruction,
>(&instruction.data)
@@ -430,14 +243,14 @@ fn write_instruction<'a, W: io::Write>(
writeln!(w, "{} {:?}", prefix, vote_instruction)?;
raw = false;
}
} else if program_pubkey == &stake::program::id() {
} else if program_pubkey == stake::program::id() {
if let Ok(stake_instruction) =
limited_deserialize::<stake::instruction::StakeInstruction>(&instruction.data)
{
writeln!(w, "{} {:?}", prefix, stake_instruction)?;
raw = false;
}
} else if program_pubkey == &solana_sdk::system_program::id() {
} else if program_pubkey == solana_sdk::system_program::id() {
if let Ok(system_instruction) = limited_deserialize::<
solana_sdk::system_instruction::SystemInstruction,
>(&instruction.data)
@@ -445,156 +258,110 @@ fn write_instruction<'a, W: io::Write>(
writeln!(w, "{} {:?}", prefix, system_instruction)?;
raw = false;
}
} else if is_memo_program(program_pubkey) {
} else if is_memo_program(&program_pubkey) {
if let Ok(s) = std::str::from_utf8(&instruction.data) {
writeln!(w, "{} Data: \"{}\"", prefix, s)?;
raw = false;
}
}
if raw {
writeln!(w, "{} Data: {:?}", prefix, instruction.data)?;
}
}
if raw {
writeln!(w, "{} Data: {:?}", prefix, instruction.data)?;
}
Ok(())
}
fn write_address_table_lookups<W: io::Write>(
w: &mut W,
address_table_lookups: &[MessageAddressTableLookup],
prefix: &str,
) -> io::Result<()> {
for (lookup_index, lookup) in address_table_lookups.iter().enumerate() {
writeln!(w, "{}Address Table Lookup {}", prefix, lookup_index,)?;
writeln!(w, "{} Table Account: {}", prefix, lookup.account_key,)?;
if let Some(transaction_status) = transaction_status {
writeln!(
w,
"{} Writable Indexes: {:?}",
"{}Status: {}",
prefix,
&lookup.writable_indexes[..],
match &transaction_status.status {
Ok(_) => "Ok".into(),
Err(err) => err.to_string(),
}
)?;
writeln!(
w,
"{} Readonly Indexes: {:?}",
"{} Fee: ◎{}",
prefix,
&lookup.readonly_indexes[..],
lamports_to_sol(transaction_status.fee)
)?;
}
Ok(())
}
fn write_rewards<W: io::Write>(
w: &mut W,
rewards: Option<&Rewards>,
prefix: &str,
) -> io::Result<()> {
if let Some(rewards) = rewards {
if !rewards.is_empty() {
writeln!(w, "{}Rewards:", prefix,)?;
writeln!(
w,
"{} {:<44} {:^15} {:<16} {:<20}",
prefix, "Address", "Type", "Amount", "New Balance"
)?;
for reward in rewards {
let sign = if reward.lamports < 0 { "-" } else { "" };
assert_eq!(
transaction_status.pre_balances.len(),
transaction_status.post_balances.len()
);
for (i, (pre, post)) in transaction_status
.pre_balances
.iter()
.zip(transaction_status.post_balances.iter())
.enumerate()
{
if pre == post {
writeln!(
w,
"{} {:<44} {:^15} {}◎{:<14.9} ◎{:<18.9}",
"{} Account {} balance: ◎{}",
prefix,
reward.pubkey,
if let Some(reward_type) = reward.reward_type {
format!("{}", reward_type)
} else {
"-".to_string()
},
sign,
lamports_to_sol(reward.lamports.abs() as u64),
lamports_to_sol(reward.post_balance)
i,
lamports_to_sol(*pre)
)?;
} else {
writeln!(
w,
"{} Account {} balance: ◎{} -> ◎{}",
prefix,
i,
lamports_to_sol(*pre),
lamports_to_sol(*post)
)?;
}
}
}
Ok(())
}
fn write_status<W: io::Write>(
w: &mut W,
transaction_status: &Result<(), TransactionError>,
prefix: &str,
) -> io::Result<()> {
writeln!(
w,
"{}Status: {}",
prefix,
match transaction_status {
Ok(_) => "Ok".into(),
Err(err) => err.to_string(),
}
)
}
fn write_fees<W: io::Write>(w: &mut W, transaction_fee: u64, prefix: &str) -> io::Result<()> {
writeln!(w, "{} Fee: ◎{}", prefix, lamports_to_sol(transaction_fee))
}
fn write_balances<W: io::Write>(
w: &mut W,
transaction_status: &UiTransactionStatusMeta,
prefix: &str,
) -> io::Result<()> {
assert_eq!(
transaction_status.pre_balances.len(),
transaction_status.post_balances.len()
);
for (i, (pre, post)) in transaction_status
.pre_balances
.iter()
.zip(transaction_status.post_balances.iter())
.enumerate()
{
if pre == post {
writeln!(
w,
"{} Account {} balance: ◎{}",
prefix,
i,
lamports_to_sol(*pre)
)?;
} else {
writeln!(
w,
"{} Account {} balance: ◎{} -> ◎{}",
prefix,
i,
lamports_to_sol(*pre),
lamports_to_sol(*post)
)?;
}
}
Ok(())
}
fn write_log_messages<W: io::Write>(
w: &mut W,
log_messages: Option<&Vec<String>>,
prefix: &str,
) -> io::Result<()> {
if let Some(log_messages) = log_messages {
if !log_messages.is_empty() {
writeln!(w, "{}Log Messages:", prefix,)?;
for log_message in log_messages {
writeln!(w, "{} {}", prefix, log_message)?;
if let Some(log_messages) = &transaction_status.log_messages {
if !log_messages.is_empty() {
writeln!(w, "{}Log Messages:", prefix,)?;
for log_message in log_messages {
writeln!(w, "{} {}", prefix, log_message)?;
}
}
}
if let Some(rewards) = &transaction_status.rewards {
if !rewards.is_empty() {
writeln!(w, "{}Rewards:", prefix,)?;
writeln!(
w,
"{} {:<44} {:^15} {:<15} {:<20}",
prefix, "Address", "Type", "Amount", "New Balance"
)?;
for reward in rewards {
let sign = if reward.lamports < 0 { "-" } else { "" };
writeln!(
w,
"{} {:<44} {:^15} {}◎{:<14.9} ◎{:<18.9}",
prefix,
reward.pubkey,
if let Some(reward_type) = reward.reward_type {
format!("{}", reward_type)
} else {
"-".to_string()
},
sign,
lamports_to_sol(reward.lamports.abs() as u64),
lamports_to_sol(reward.post_balance)
)?;
}
}
}
} else {
writeln!(w, "{}Status: Unavailable", prefix)?;
}
Ok(())
}
pub fn println_transaction(
transaction: &VersionedTransaction,
transaction_status: Option<&UiTransactionStatusMeta>,
transaction: &Transaction,
transaction_status: &Option<UiTransactionStatusMeta>,
prefix: &str,
sigverify_status: Option<&[CliSignatureVerificationStatus]>,
block_time: Option<UnixTimestamp>,
@@ -607,7 +374,6 @@ pub fn println_transaction(
prefix,
sigverify_status,
block_time,
CliTimezone::Local,
)
.is_ok()
{
@@ -619,24 +385,23 @@ pub fn println_transaction(
pub fn writeln_transaction(
f: &mut dyn fmt::Write,
transaction: &VersionedTransaction,
transaction_status: Option<&UiTransactionStatusMeta>,
transaction: &Transaction,
transaction_status: &Option<UiTransactionStatusMeta>,
prefix: &str,
sigverify_status: Option<&[CliSignatureVerificationStatus]>,
block_time: Option<UnixTimestamp>,
) -> fmt::Result {
let mut w = Vec::new();
let write_result = write_transaction(
if write_transaction(
&mut w,
transaction,
transaction_status,
prefix,
sigverify_status,
block_time,
CliTimezone::Local,
);
if write_result.is_ok() {
)
.is_ok()
{
if let Ok(s) = String::from_utf8(w) {
write!(f, "{}", s)?;
}
@@ -662,215 +427,7 @@ pub fn unix_timestamp_to_string(unix_timestamp: UnixTimestamp) -> String {
#[cfg(test)]
mod test {
use {
super::*,
solana_sdk::{
message::{
v0::{self, LoadedAddresses},
Message as LegacyMessage, MessageHeader, VersionedMessage,
},
pubkey::Pubkey,
signature::{Keypair, Signer},
transaction::Transaction,
},
solana_transaction_status::{Reward, RewardType, TransactionStatusMeta},
std::io::BufWriter,
};
fn new_test_keypair() -> Keypair {
let secret = ed25519_dalek::SecretKey::from_bytes(&[0u8; 32]).unwrap();
let public = ed25519_dalek::PublicKey::from(&secret);
let keypair = ed25519_dalek::Keypair { secret, public };
Keypair::from_bytes(&keypair.to_bytes()).unwrap()
}
fn new_test_v0_transaction() -> VersionedTransaction {
let keypair = new_test_keypair();
let account_key = Pubkey::new_from_array([1u8; 32]);
let address_table_key = Pubkey::new_from_array([2u8; 32]);
VersionedTransaction::try_new(
VersionedMessage::V0(v0::Message {
header: MessageHeader {
num_required_signatures: 1,
num_readonly_signed_accounts: 0,
num_readonly_unsigned_accounts: 1,
},
recent_blockhash: Hash::default(),
account_keys: vec![keypair.pubkey(), account_key],
address_table_lookups: vec![MessageAddressTableLookup {
account_key: address_table_key,
writable_indexes: vec![0],
readonly_indexes: vec![1],
}],
instructions: vec![CompiledInstruction::new_from_raw_parts(
3,
vec![],
vec![1, 2],
)],
}),
&[&keypair],
)
.unwrap()
}
#[test]
fn test_write_legacy_transaction() {
let keypair = new_test_keypair();
let account_key = Pubkey::new_from_array([1u8; 32]);
let transaction = VersionedTransaction::from(Transaction::new(
&[&keypair],
LegacyMessage {
header: MessageHeader {
num_required_signatures: 1,
num_readonly_signed_accounts: 0,
num_readonly_unsigned_accounts: 1,
},
recent_blockhash: Hash::default(),
account_keys: vec![keypair.pubkey(), account_key],
instructions: vec![CompiledInstruction::new_from_raw_parts(1, vec![], vec![0])],
},
Hash::default(),
));
let sigverify_status = CliSignatureVerificationStatus::verify_transaction(&transaction);
let meta = TransactionStatusMeta {
status: Ok(()),
fee: 5000,
pre_balances: vec![5000, 10_000],
post_balances: vec![0, 9_900],
inner_instructions: None,
log_messages: Some(vec!["Test message".to_string()]),
pre_token_balances: None,
post_token_balances: None,
rewards: Some(vec![Reward {
pubkey: account_key.to_string(),
lamports: -100,
post_balance: 9_900,
reward_type: Some(RewardType::Rent),
commission: None,
}]),
loaded_addresses: LoadedAddresses::default(),
};
let output = {
let mut write_buffer = BufWriter::new(Vec::new());
write_transaction(
&mut write_buffer,
&transaction,
Some(&meta.into()),
"",
Some(&sigverify_status),
Some(1628633791),
CliTimezone::Utc,
)
.unwrap();
let bytes = write_buffer.into_inner().unwrap();
String::from_utf8(bytes).unwrap()
};
assert_eq!(
output,
r#"Block Time: 2021-08-10T22:16:31Z
Version: legacy
Recent Blockhash: 11111111111111111111111111111111
Signature 0: 5pkjrE4VBa3Bu9CMKXgh1U345cT1gGo8QBVRTzHAo6gHeiPae5BTbShP15g6NgqRMNqu8Qrhph1ATmrfC1Ley3rx (pass)
Account 0: srw- 4zvwRjXUKGfvwnParsHAS3HuSVzV5cA4McphgmoCtajS (fee payer)
Account 1: -r-x 4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLKi
Instruction 0
Program: 4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLKi (1)
Account 0: 4zvwRjXUKGfvwnParsHAS3HuSVzV5cA4McphgmoCtajS (0)
Data: []
Status: Ok
Fee: ◎0.000005
Account 0 balance: ◎0.000005 -> ◎0
Account 1 balance: ◎0.00001 -> ◎0.0000099
Log Messages:
Test message
Rewards:
Address Type Amount New Balance \0
4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLKi rent -◎0.000000100 ◎0.000009900 \0
"#.replace("\\0", "") // replace marker used to subvert trailing whitespace linter on CI
);
}
#[test]
fn test_write_v0_transaction() {
let versioned_tx = new_test_v0_transaction();
let sigverify_status = CliSignatureVerificationStatus::verify_transaction(&versioned_tx);
let address_table_entry1 = Pubkey::new_from_array([3u8; 32]);
let address_table_entry2 = Pubkey::new_from_array([4u8; 32]);
let loaded_addresses = LoadedAddresses {
writable: vec![address_table_entry1],
readonly: vec![address_table_entry2],
};
let meta = TransactionStatusMeta {
status: Ok(()),
fee: 5000,
pre_balances: vec![5000, 10_000, 15_000, 20_000],
post_balances: vec![0, 10_000, 14_900, 20_000],
inner_instructions: None,
log_messages: Some(vec!["Test message".to_string()]),
pre_token_balances: None,
post_token_balances: None,
rewards: Some(vec![Reward {
pubkey: address_table_entry1.to_string(),
lamports: -100,
post_balance: 14_900,
reward_type: Some(RewardType::Rent),
commission: None,
}]),
loaded_addresses,
};
let output = {
let mut write_buffer = BufWriter::new(Vec::new());
write_transaction(
&mut write_buffer,
&versioned_tx,
Some(&meta.into()),
"",
Some(&sigverify_status),
Some(1628633791),
CliTimezone::Utc,
)
.unwrap();
let bytes = write_buffer.into_inner().unwrap();
String::from_utf8(bytes).unwrap()
};
assert_eq!(
output,
r#"Block Time: 2021-08-10T22:16:31Z
Version: 0
Recent Blockhash: 11111111111111111111111111111111
Signature 0: 5iEy3TT3ZhTA1NkuCY8GrQGNVY8d5m1bpjdh5FT3Ca4Py81fMipAZjafDuKJKrkw5q5UAAd8oPcgZ4nyXpHt4Fp7 (pass)
Account 0: srw- 4zvwRjXUKGfvwnParsHAS3HuSVzV5cA4McphgmoCtajS (fee payer)
Account 1: -r-- 4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLKi
Account 2: -rw- Unknown Address (uses lookup 0 and index 0)
Account 3: -r-x Unknown Address (uses lookup 0 and index 1)
Instruction 0
Program: Unknown Address (uses lookup 0 and index 1) (3)
Account 0: 4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLKi (1)
Account 1: Unknown Address (uses lookup 0 and index 0) (2)
Data: []
Address Table Lookup 0
Table Account: 8qbHbw2BbbTHBW1sbeqakYXVKRQM8Ne7pLK7m6CVfeR
Writable Indexes: [0]
Readonly Indexes: [1]
Status: Ok
Fee: ◎0.000005
Account 0 balance: ◎0.000005 -> ◎0
Account 1 balance: ◎0.00001
Account 2 balance: ◎0.000015 -> ◎0.0000149
Account 3 balance: ◎0.00002
Log Messages:
Test message
Rewards:
Address Type Amount New Balance \0
CktRuQ2mttgRGkXJtyksdKHjUdc2C4TgDzyB98oEzy8 rent -◎0.000000100 ◎0.000014900 \0
"#.replace("\\0", "") // replace marker used to subvert trailing whitespace linter on CI
);
}
use {super::*, solana_sdk::pubkey::Pubkey};
#[test]
fn test_format_labeled_address() {

View File

@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2021"
name = "solana-cli"
description = "Blockchain, Rebuilt for Scale"
version = "1.10.6"
version = "1.10.0"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@@ -13,43 +13,43 @@ documentation = "https://docs.rs/solana-cli"
bincode = "1.3.3"
bs58 = "0.4.0"
clap = "2.33.1"
criterion-stats = "0.3.0"
ctrlc = { version = "3.2.1", features = ["termination"] }
console = "0.15.0"
const_format = "0.2.22"
criterion-stats = "0.3.0"
crossbeam-channel = "0.5"
ctrlc = { version = "3.2.1", features = ["termination"] }
humantime = "2.0.1"
log = "0.4.14"
humantime = "2.0.1"
num-traits = "0.2"
pretty-hex = "0.2.1"
reqwest = { version = "0.11.10", default-features = false, features = ["blocking", "rustls-tls", "json"] }
semver = "1.0.6"
reqwest = { version = "0.11.9", default-features = false, features = ["blocking", "rustls-tls", "json"] }
semver = "1.0.5"
serde = "1.0.136"
serde_derive = "1.0.103"
serde_json = "1.0.79"
solana-account-decoder = { path = "../account-decoder", version = "=1.10.6" }
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.10.6" }
solana-clap-utils = { path = "../clap-utils", version = "=1.10.6" }
solana-cli-config = { path = "../cli-config", version = "=1.10.6" }
solana-cli-output = { path = "../cli-output", version = "=1.10.6" }
solana-client = { path = "../client", version = "=1.10.6" }
solana-config-program = { path = "../programs/config", version = "=1.10.6" }
solana-faucet = { path = "../faucet", version = "=1.10.6" }
solana-logger = { path = "../logger", version = "=1.10.6" }
solana-program-runtime = { path = "../program-runtime", version = "=1.10.6" }
solana-remote-wallet = { path = "../remote-wallet", version = "=1.10.6" }
solana-sdk = { path = "../sdk", version = "=1.10.6" }
solana-transaction-status = { path = "../transaction-status", version = "=1.10.6" }
solana-version = { path = "../version", version = "=1.10.6" }
solana-vote-program = { path = "../programs/vote", version = "=1.10.6" }
serde_json = "1.0.78"
solana-account-decoder = { path = "../account-decoder", version = "=1.10.0" }
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.10.0" }
solana-clap-utils = { path = "../clap-utils", version = "=1.10.0" }
solana-cli-config = { path = "../cli-config", version = "=1.10.0" }
solana-cli-output = { path = "../cli-output", version = "=1.10.0" }
solana-client = { path = "../client", version = "=1.10.0" }
solana-config-program = { path = "../programs/config", version = "=1.10.0" }
solana-faucet = { path = "../faucet", version = "=1.10.0" }
solana-logger = { path = "../logger", version = "=1.10.0" }
solana-program-runtime = { path = "../program-runtime", version = "=1.10.0" }
solana_rbpf = "=0.2.24"
solana-remote-wallet = { path = "../remote-wallet", version = "=1.10.0" }
solana-sdk = { path = "../sdk", version = "=1.10.0" }
solana-transaction-status = { path = "../transaction-status", version = "=1.10.0" }
solana-version = { path = "../version", version = "=1.10.0" }
solana-vote-program = { path = "../programs/vote", version = "=1.10.0" }
spl-memo = { version = "=3.0.1", features = ["no-entrypoint"] }
thiserror = "1.0.30"
tiny-bip39 = "0.8.2"
[dev-dependencies]
solana-streamer = { path = "../streamer", version = "=1.10.6" }
solana-test-validator = { path = "../test-validator", version = "=1.10.6" }
solana-streamer = { path = "../streamer", version = "=1.10.0" }
solana-test-validator = { path = "../test-validator", version = "=1.10.0" }
tempfile = "3.3.0"
[[bin]]

View File

@@ -30,7 +30,7 @@ use {
pubkey::Pubkey,
signature::{Signature, Signer, SignerError},
stake::{instruction::LockupArgs, state::Lockup},
transaction::{TransactionError, VersionedTransaction},
transaction::{Transaction, TransactionError},
},
solana_vote_program::vote_state::VoteAuthorize,
std::{collections::HashMap, error, io::stdout, str::FromStr, sync::Arc, time::Duration},
@@ -88,7 +88,6 @@ pub enum CliCommand {
timeout: Duration,
blockhash: Option<Hash>,
print_timestamp: bool,
additional_fee: Option<u32>,
},
Rent {
data_length: usize,
@@ -162,7 +161,6 @@ pub enum CliCommand {
address: Option<SignerIndex>,
use_deprecated_loader: bool,
allow_excessive_balance: bool,
skip_fee_check: bool,
},
Program(ProgramCliCommand),
// Stake Commands
@@ -386,7 +384,7 @@ pub enum CliCommand {
seed: String,
program_id: Pubkey,
},
DecodeTransaction(VersionedTransaction),
DecodeTransaction(Transaction),
ResolveSigner(Option<String>),
ShowAccount {
pubkey: Pubkey,
@@ -745,7 +743,6 @@ pub fn parse_command(
signers.push(signer);
1
});
let skip_fee_check = matches.is_present("skip_fee_check");
Ok(CliCommandInfo {
command: CliCommand::Deploy {
@@ -753,7 +750,6 @@ pub fn parse_command(
address,
use_deprecated_loader: matches.is_present("use_deprecated_loader"),
allow_excessive_balance: matches.is_present("allow_excessive_balance"),
skip_fee_check,
},
signers,
})
@@ -981,7 +977,6 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
timeout,
blockhash,
print_timestamp,
additional_fee,
} => process_ping(
&rpc_client,
config,
@@ -990,7 +985,6 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
timeout,
blockhash,
*print_timestamp,
additional_fee,
),
CliCommand::Rent {
data_length,
@@ -1132,7 +1126,6 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
address,
use_deprecated_loader,
allow_excessive_balance,
skip_fee_check,
} => process_deploy(
rpc_client,
config,
@@ -1140,7 +1133,6 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
*address,
*use_deprecated_loader,
*allow_excessive_balance,
*skip_fee_check,
),
CliCommand::Program(program_subcommand) => {
process_program_subcommand(rpc_client, config, program_subcommand)
@@ -1972,7 +1964,6 @@ mod tests {
address: None,
use_deprecated_loader: false,
allow_excessive_balance: false,
skip_fee_check: false,
},
signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
}
@@ -1995,7 +1986,6 @@ mod tests {
address: Some(1),
use_deprecated_loader: false,
allow_excessive_balance: false,
skip_fee_check: false,
},
signers: vec![
read_keypair_file(&keypair_file).unwrap().into(),
@@ -2389,7 +2379,6 @@ mod tests {
address: None,
use_deprecated_loader: false,
allow_excessive_balance: false,
skip_fee_check: false,
};
config.output_format = OutputFormat::JsonCompact;
let result = process_command(&config);
@@ -2410,7 +2399,6 @@ mod tests {
address: None,
use_deprecated_loader: false,
allow_excessive_balance: false,
skip_fee_check: false,
};
assert!(process_command(&config).is_err());
}

View File

@@ -33,14 +33,12 @@ use {
rpc_request::DELINQUENT_VALIDATOR_SLOT_DISTANCE,
rpc_response::SlotInfo,
},
solana_program_runtime::compute_budget,
solana_remote_wallet::remote_wallet::RemoteWalletManager,
solana_sdk::{
account::from_account,
account_utils::StateMut,
clock::{self, Clock, Slot},
commitment_config::CommitmentConfig,
compute_budget::ComputeBudgetInstruction,
epoch_schedule::Epoch,
hash::Hash,
message::Message,
@@ -271,13 +269,6 @@ impl ClusterQuerySubCommands for App<'_, '_> {
.default_value("15")
.help("Wait up to timeout seconds for transaction confirmation"),
)
.arg(
Arg::with_name("additional_fee")
.long("additional-fee")
.value_name("NUMBER")
.takes_value(true)
.help("Request additional-fee for transaction"),
)
.arg(blockhash_arg()),
)
.subcommand(
@@ -522,7 +513,6 @@ pub fn parse_cluster_ping(
let timeout = Duration::from_secs(value_t_or_exit!(matches, "timeout", u64));
let blockhash = value_of(matches, BLOCKHASH_ARG.name);
let print_timestamp = matches.is_present("print_timestamp");
let additional_fee = value_of(matches, "additional_fee");
Ok(CliCommandInfo {
command: CliCommand::Ping {
interval,
@@ -530,7 +520,6 @@ pub fn parse_cluster_ping(
timeout,
blockhash,
print_timestamp,
additional_fee,
},
signers: vec![default_signer.signer_from_path(matches, wallet_manager)?],
})
@@ -1058,7 +1047,6 @@ pub fn process_get_block(
RpcBlockConfig {
encoding: Some(UiTransactionEncoding::Base64),
commitment: Some(CommitmentConfig::confirmed()),
max_supported_transaction_version: Some(0),
..RpcBlockConfig::default()
},
)?
@@ -1362,7 +1350,6 @@ pub fn process_ping(
timeout: &Duration,
fixed_blockhash: &Option<Hash>,
print_timestamp: bool,
additional_fee: &Option<u32>,
) -> ProcessResult {
let (signal_sender, signal_receiver) = unbounded();
ctrlc::set_handler(move || {
@@ -1387,7 +1374,6 @@ pub fn process_ping(
blockhash_from_cluster = true;
}
}
'mainloop: for seq in 0..count.unwrap_or(std::u64::MAX) {
let now = Instant::now();
if fixed_blockhash.is_none() && now.duration_since(blockhash_acquired).as_secs() > 60 {
@@ -1402,18 +1388,8 @@ pub fn process_ping(
lamports += 1;
let build_message = |lamports| {
let mut ixs = vec![system_instruction::transfer(
&config.signers[0].pubkey(),
&to,
lamports,
)];
if let Some(additional_fee) = additional_fee {
ixs.push(ComputeBudgetInstruction::request_units(
compute_budget::DEFAULT_UNITS,
*additional_fee,
));
}
Message::new(&ixs, Some(&config.signers[0].pubkey()))
let ix = system_instruction::transfer(&config.signers[0].pubkey(), &to, lamports);
Message::new(&[ix], Some(&config.signers[0].pubkey()))
};
let (message, _) = resolve_spend_tx_and_check_account_balance(
rpc_client,
@@ -2043,7 +2019,6 @@ pub fn process_transaction_history(
RpcTransactionConfig {
encoding: Some(UiTransactionEncoding::Base64),
commitment: Some(CommitmentConfig::confirmed()),
max_supported_transaction_version: Some(0),
},
) {
Ok(confirmed_transaction) => {
@@ -2053,7 +2028,7 @@ pub fn process_transaction_history(
.transaction
.decode()
.expect("Successful decode"),
confirmed_transaction.transaction.meta.as_ref(),
&confirmed_transaction.transaction.meta,
" ",
None,
None,
@@ -2337,7 +2312,6 @@ mod tests {
Hash::from_str("4CCNp28j6AhGq7PkjPDP4wbQWBS8LLbQin2xV5n8frKX").unwrap()
),
print_timestamp: true,
additional_fee: None,
},
signers: vec![default_keypair.into()],
}

View File

@@ -66,7 +66,6 @@ pub enum ProgramCliCommand {
is_final: bool,
max_len: Option<usize>,
allow_excessive_balance: bool,
skip_fee_check: bool,
},
WriteBuffer {
program_location: String,
@@ -74,7 +73,6 @@ pub enum ProgramCliCommand {
buffer_pubkey: Option<Pubkey>,
buffer_authority_signer_index: Option<SignerIndex>,
max_len: Option<usize>,
skip_fee_check: bool,
},
SetBufferAuthority {
buffer_pubkey: Pubkey,
@@ -116,13 +114,6 @@ impl ProgramSubCommands for App<'_, '_> {
SubCommand::with_name("program")
.about("Program management")
.setting(AppSettings::SubcommandRequiredElseHelp)
.arg(
Arg::with_name("skip_fee_check")
.long("skip-fee-check")
.hidden(true)
.takes_value(false)
.global(true)
)
.subcommand(
SubCommand::with_name("deploy")
.about("Deploy a program")
@@ -415,12 +406,6 @@ impl ProgramSubCommands for App<'_, '_> {
.long("allow-excessive-deploy-account-balance")
.takes_value(false)
.help("Use the designated program id, even if the account already holds a large balance of SOL")
)
.arg(
Arg::with_name("skip_fee_check")
.long("skip-fee-check")
.hidden(true)
.takes_value(false)
),
)
}
@@ -431,14 +416,7 @@ pub fn parse_program_subcommand(
default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let (subcommand, sub_matches) = matches.subcommand();
let matches_skip_fee_check = matches.is_present("skip_fee_check");
let sub_matches_skip_fee_check = sub_matches
.map(|m| m.is_present("skip_fee_check"))
.unwrap_or(false);
let skip_fee_check = matches_skip_fee_check || sub_matches_skip_fee_check;
let response = match (subcommand, sub_matches) {
let response = match matches.subcommand() {
("deploy", Some(matches)) => {
let mut bulk_signers = vec![Some(
default_signer.signer_from_path(matches, wallet_manager)?,
@@ -498,7 +476,6 @@ pub fn parse_program_subcommand(
is_final: matches.is_present("final"),
max_len,
allow_excessive_balance: matches.is_present("allow_excessive_balance"),
skip_fee_check,
}),
signers: signer_info.signers,
}
@@ -544,7 +521,6 @@ pub fn parse_program_subcommand(
buffer_authority_signer_index: signer_info
.index_of_or_none(buffer_authority_pubkey),
max_len,
skip_fee_check,
}),
signers: signer_info.signers,
}
@@ -693,7 +669,6 @@ pub fn process_program_subcommand(
is_final,
max_len,
allow_excessive_balance,
skip_fee_check,
} => process_program_deploy(
rpc_client,
config,
@@ -706,7 +681,6 @@ pub fn process_program_subcommand(
*is_final,
*max_len,
*allow_excessive_balance,
*skip_fee_check,
),
ProgramCliCommand::WriteBuffer {
program_location,
@@ -714,7 +688,6 @@ pub fn process_program_subcommand(
buffer_pubkey,
buffer_authority_signer_index,
max_len,
skip_fee_check,
} => process_write_buffer(
rpc_client,
config,
@@ -723,7 +696,6 @@ pub fn process_program_subcommand(
*buffer_pubkey,
*buffer_authority_signer_index,
*max_len,
*skip_fee_check,
),
ProgramCliCommand::SetBufferAuthority {
buffer_pubkey,
@@ -821,7 +793,6 @@ fn process_program_deploy(
is_final: bool,
max_len: Option<usize>,
allow_excessive_balance: bool,
skip_fee_check: bool,
) -> ProcessResult {
let (words, mnemonic, buffer_keypair) = create_ephemeral_keypair()?;
let (buffer_provided, buffer_signer, buffer_pubkey) = if let Some(i) = buffer_signer_index {
@@ -976,7 +947,6 @@ fn process_program_deploy(
&buffer_pubkey,
Some(upgrade_authority_signer),
allow_excessive_balance,
skip_fee_check,
)
} else {
do_process_program_upgrade(
@@ -987,7 +957,6 @@ fn process_program_deploy(
config.signers[upgrade_authority_signer_index],
&buffer_pubkey,
buffer_signer,
skip_fee_check,
)
};
if result.is_ok() && is_final {
@@ -1014,7 +983,6 @@ fn process_write_buffer(
buffer_pubkey: Option<Pubkey>,
buffer_authority_signer_index: Option<SignerIndex>,
max_len: Option<usize>,
skip_fee_check: bool,
) -> ProcessResult {
// Create ephemeral keypair to use for Buffer account, if not provided
let (words, mnemonic, buffer_keypair) = create_ephemeral_keypair()?;
@@ -1082,7 +1050,6 @@ fn process_write_buffer(
&buffer_pubkey,
Some(buffer_authority),
true,
skip_fee_check,
);
if result.is_err() && buffer_signer_index.is_none() && buffer_signer.is_some() {
@@ -1669,7 +1636,6 @@ pub fn process_deploy(
buffer_signer_index: Option<SignerIndex>,
use_deprecated_loader: bool,
allow_excessive_balance: bool,
skip_fee_check: bool,
) -> ProcessResult {
// Create ephemeral keypair to use for Buffer account, if not provided
let (words, mnemonic, buffer_keypair) = create_ephemeral_keypair()?;
@@ -1700,7 +1666,6 @@ pub fn process_deploy(
&buffer_signer.pubkey(),
Some(buffer_signer),
allow_excessive_balance,
skip_fee_check,
);
if result.is_err() && buffer_signer_index.is_none() {
report_ephemeral_mnemonic(words, mnemonic);
@@ -1739,7 +1704,6 @@ fn do_process_program_write_and_deploy(
buffer_pubkey: &Pubkey,
buffer_authority_signer: Option<&dyn Signer>,
allow_excessive_balance: bool,
skip_fee_check: bool,
) -> ProcessResult {
// Build messages to calculate fees
let mut messages: Vec<&Message> = Vec::new();
@@ -1870,9 +1834,7 @@ fn do_process_program_write_and_deploy(
messages.push(message);
}
if !skip_fee_check {
check_payer(&rpc_client, config, balance_needed, &messages)?;
}
check_payer(&rpc_client, config, balance_needed, &messages)?;
send_deploy_messages(
rpc_client,
@@ -1906,7 +1868,6 @@ fn do_process_program_upgrade(
upgrade_authority: &dyn Signer,
buffer_pubkey: &Pubkey,
buffer_signer: Option<&dyn Signer>,
skip_fee_check: bool,
) -> ProcessResult {
let loader_id = bpf_loader_upgradeable::id();
let data_len = program_data.len();
@@ -2006,10 +1967,7 @@ fn do_process_program_upgrade(
);
messages.push(&final_message);
if !skip_fee_check {
check_payer(&rpc_client, config, balance_needed, &messages)?;
}
check_payer(&rpc_client, config, balance_needed, &messages)?;
send_deploy_messages(
rpc_client,
config,
@@ -2297,7 +2255,6 @@ mod tests {
is_final: false,
max_len: None,
allow_excessive_balance: false,
skip_fee_check: false,
}),
signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
}
@@ -2324,7 +2281,6 @@ mod tests {
is_final: false,
max_len: Some(42),
allow_excessive_balance: false,
skip_fee_check: false,
}),
signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
}
@@ -2353,7 +2309,6 @@ mod tests {
is_final: false,
max_len: None,
allow_excessive_balance: false,
skip_fee_check: false,
}),
signers: vec![
read_keypair_file(&keypair_file).unwrap().into(),
@@ -2384,7 +2339,6 @@ mod tests {
is_final: false,
max_len: None,
allow_excessive_balance: false,
skip_fee_check: false,
}),
signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
}
@@ -2414,7 +2368,6 @@ mod tests {
is_final: false,
max_len: None,
allow_excessive_balance: false,
skip_fee_check: false,
}),
signers: vec![
read_keypair_file(&keypair_file).unwrap().into(),
@@ -2447,7 +2400,6 @@ mod tests {
is_final: false,
max_len: None,
allow_excessive_balance: false,
skip_fee_check: false,
}),
signers: vec![
read_keypair_file(&keypair_file).unwrap().into(),
@@ -2475,7 +2427,6 @@ mod tests {
upgrade_authority_signer_index: 0,
is_final: true,
max_len: None,
skip_fee_check: false,
allow_excessive_balance: false,
}),
signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
@@ -2509,7 +2460,6 @@ mod tests {
buffer_pubkey: None,
buffer_authority_signer_index: Some(0),
max_len: None,
skip_fee_check: false,
}),
signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
}
@@ -2533,7 +2483,6 @@ mod tests {
buffer_pubkey: None,
buffer_authority_signer_index: Some(0),
max_len: Some(42),
skip_fee_check: false,
}),
signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
}
@@ -2560,7 +2509,6 @@ mod tests {
buffer_pubkey: Some(buffer_keypair.pubkey()),
buffer_authority_signer_index: Some(0),
max_len: None,
skip_fee_check: false,
}),
signers: vec![
read_keypair_file(&keypair_file).unwrap().into(),
@@ -2590,7 +2538,6 @@ mod tests {
buffer_pubkey: None,
buffer_authority_signer_index: Some(1),
max_len: None,
skip_fee_check: false,
}),
signers: vec![
read_keypair_file(&keypair_file).unwrap().into(),
@@ -2625,7 +2572,6 @@ mod tests {
buffer_pubkey: Some(buffer_keypair.pubkey()),
buffer_authority_signer_index: Some(2),
max_len: None,
skip_fee_check: false,
}),
signers: vec![
read_keypair_file(&keypair_file).unwrap().into(),
@@ -3068,7 +3014,6 @@ mod tests {
is_final: false,
max_len: None,
allow_excessive_balance: false,
skip_fee_check: false,
}),
signers: vec![&default_keypair],
output_format: OutputFormat::JsonCompact,

View File

@@ -1383,12 +1383,17 @@ pub fn process_stake_authorize(
};
if let Some(authorized) = authorized {
match authorization_type {
StakeAuthorize::Staker => check_current_authority(
&[authorized.withdrawer, authorized.staker],
&authority.pubkey(),
)?,
StakeAuthorize::Staker => {
// first check authorized withdrawer
check_current_authority(&authorized.withdrawer, &authority.pubkey())
.or_else(|_| {
// ...then check authorized staker. If neither matches, error will
// print the stake key as `expected`
check_current_authority(&authorized.staker, &authority.pubkey())
})?;
}
StakeAuthorize::Withdrawer => {
check_current_authority(&[authorized.withdrawer], &authority.pubkey())?;
check_current_authority(&authorized.withdrawer, &authority.pubkey())?;
}
}
} else {
@@ -1930,7 +1935,7 @@ pub fn process_stake_set_lockup(
};
if let Some(lockup) = lockup {
if lockup.custodian != Pubkey::default() {
check_current_authority(&[lockup.custodian], &custodian.pubkey())?;
check_current_authority(&lockup.custodian, &custodian.pubkey())?;
}
} else {
return Err(CliError::RpcRequestError(format!(
@@ -2114,13 +2119,13 @@ fn get_stake_account_state(
}
pub(crate) fn check_current_authority(
permitted_authorities: &[Pubkey],
account_current_authority: &Pubkey,
provided_current_authority: &Pubkey,
) -> Result<(), CliError> {
if !permitted_authorities.contains(provided_current_authority) {
if account_current_authority != provided_current_authority {
Err(CliError::RpcRequestError(format!(
"Invalid authority provided: {:?}, expected {:?}",
provided_current_authority, permitted_authorities
"Invalid current authority provided: {:?}, expected {:?}",
provided_current_authority, account_current_authority
)))
} else {
Ok(())

View File

@@ -291,12 +291,8 @@ pub fn process_set_validator_info(
// Check existence of validator-info account
let balance = rpc_client.get_balance(&info_pubkey).unwrap_or(0);
let keys = vec![
(validator_info::id(), false),
(config.signers[0].pubkey(), true),
];
let data_len = ValidatorInfo::max_space() + ConfigKeys::serialized_size(keys.clone());
let lamports = rpc_client.get_minimum_balance_for_rent_exemption(data_len as usize)?;
let lamports =
rpc_client.get_minimum_balance_for_rent_exemption(ValidatorInfo::max_space() as usize)?;
let signers = if balance == 0 {
if info_pubkey != info_keypair.pubkey() {
@@ -312,7 +308,10 @@ pub fn process_set_validator_info(
};
let build_message = |lamports| {
let keys = keys.clone();
let keys = vec![
(validator_info::id(), false),
(config.signers[0].pubkey(), true),
];
if balance == 0 {
println!(
"Publishing info for Validator {:?}",

View File

@@ -910,10 +910,7 @@ pub fn process_vote_authorize(
"Invalid vote account state; no authorized voters found".to_string(),
)
})?;
check_current_authority(
&[current_authorized_voter, vote_state.authorized_withdrawer],
&authorized.pubkey(),
)?;
check_current_authority(&current_authorized_voter, &authorized.pubkey())?;
if let Some(signer) = new_authorized_signer {
if signer.is_interactive() {
return Err(CliError::BadParameter(format!(
@@ -930,7 +927,7 @@ pub fn process_vote_authorize(
(new_authorized_pubkey, "new_authorized_pubkey".to_string()),
)?;
if let Some(vote_state) = vote_state {
check_current_authority(&[vote_state.authorized_withdrawer], &authorized.pubkey())?
check_current_authority(&vote_state.authorized_withdrawer, &authorized.pubkey())?
}
}
}

View File

@@ -37,12 +37,9 @@ use {
stake,
system_instruction::{self, SystemError},
system_program,
transaction::{Transaction, VersionedTransaction},
},
solana_transaction_status::{
EncodableWithMeta, EncodedConfirmedTransactionWithStatusMeta, EncodedTransaction,
TransactionBinaryEncoding, UiTransactionEncoding,
transaction::Transaction,
},
solana_transaction_status::{Encodable, EncodedTransaction, UiTransactionEncoding},
std::{fmt::Write as FmtWrite, fs::File, io::Write, sync::Arc},
};
@@ -192,7 +189,7 @@ impl WalletSubCommands for App<'_, '_> {
Arg::with_name("encoding")
.index(2)
.value_name("ENCODING")
.possible_values(&["base58", "base64"]) // Variants of `TransactionBinaryEncoding` enum
.possible_values(&["base58", "base64"]) // Subset of `UiTransactionEncoding` enum
.default_value("base58")
.takes_value(true)
.required(true)
@@ -344,13 +341,13 @@ pub fn parse_balance(
pub fn parse_decode_transaction(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
let blob = value_t_or_exit!(matches, "transaction", String);
let binary_encoding = match matches.value_of("encoding").unwrap() {
"base58" => TransactionBinaryEncoding::Base58,
"base64" => TransactionBinaryEncoding::Base64,
let encoding = match matches.value_of("encoding").unwrap() {
"base58" => UiTransactionEncoding::Base58,
"base64" => UiTransactionEncoding::Base64,
_ => unreachable!(),
};
let encoded_transaction = EncodedTransaction::Binary(blob, binary_encoding);
let encoded_transaction = EncodedTransaction::Binary(blob, encoding);
if let Some(transaction) = encoded_transaction.decode() {
Ok(CliCommandInfo {
command: CliCommand::DecodeTransaction(transaction),
@@ -562,25 +559,22 @@ pub fn process_confirm(
RpcTransactionConfig {
encoding: Some(UiTransactionEncoding::Base64),
commitment: Some(CommitmentConfig::confirmed()),
max_supported_transaction_version: Some(0),
},
) {
Ok(confirmed_transaction) => {
let EncodedConfirmedTransactionWithStatusMeta {
block_time,
slot,
transaction: transaction_with_meta,
} = confirmed_transaction;
let decoded_transaction =
transaction_with_meta.transaction.decode().unwrap();
let json_transaction = decoded_transaction.json_encode();
let decoded_transaction = confirmed_transaction
.transaction
.transaction
.decode()
.expect("Successful decode");
let json_transaction =
decoded_transaction.encode(UiTransactionEncoding::Json);
transaction = Some(CliTransaction {
transaction: json_transaction,
meta: transaction_with_meta.meta,
block_time,
slot: Some(slot),
meta: confirmed_transaction.transaction.meta,
block_time: confirmed_transaction.block_time,
slot: Some(confirmed_transaction.slot),
decoded_transaction,
prefix: " ".to_string(),
sigverify_status: vec![],
@@ -612,14 +606,11 @@ pub fn process_confirm(
}
#[allow(clippy::unnecessary_wraps)]
pub fn process_decode_transaction(
config: &CliConfig,
transaction: &VersionedTransaction,
) -> ProcessResult {
pub fn process_decode_transaction(config: &CliConfig, transaction: &Transaction) -> ProcessResult {
let sigverify_status = CliSignatureVerificationStatus::verify_transaction(transaction);
let decode_transaction = CliTransaction {
decoded_transaction: transaction.clone(),
transaction: transaction.json_encode(),
transaction: transaction.encode(UiTransactionEncoding::Json),
meta: None,
block_time: None,
slot: None,

View File

@@ -62,7 +62,6 @@ fn test_cli_program_deploy_non_upgradeable() {
address: None,
use_deprecated_loader: false,
allow_excessive_balance: false,
skip_fee_check: false,
};
config.output_format = OutputFormat::JsonCompact;
let response = process_command(&config);
@@ -92,7 +91,6 @@ fn test_cli_program_deploy_non_upgradeable() {
address: Some(1),
use_deprecated_loader: false,
allow_excessive_balance: false,
skip_fee_check: false,
};
process_command(&config).unwrap();
let account1 = rpc_client
@@ -120,7 +118,6 @@ fn test_cli_program_deploy_non_upgradeable() {
address: Some(1),
use_deprecated_loader: false,
allow_excessive_balance: false,
skip_fee_check: false,
};
process_command(&config).unwrap_err();
@@ -130,7 +127,6 @@ fn test_cli_program_deploy_non_upgradeable() {
address: Some(1),
use_deprecated_loader: false,
allow_excessive_balance: true,
skip_fee_check: false,
};
process_command(&config).unwrap();
let account2 = rpc_client
@@ -197,7 +193,6 @@ fn test_cli_program_deploy_no_authority() {
upgrade_authority_signer_index: 1,
is_final: true,
max_len: None,
skip_fee_check: false,
});
config.output_format = OutputFormat::JsonCompact;
let response = process_command(&config);
@@ -223,7 +218,6 @@ fn test_cli_program_deploy_no_authority() {
upgrade_authority_signer_index: 1,
is_final: false,
max_len: None,
skip_fee_check: false,
});
process_command(&config).unwrap_err();
}
@@ -284,7 +278,6 @@ fn test_cli_program_deploy_with_authority() {
upgrade_authority_signer_index: 1,
is_final: false,
max_len: Some(max_len),
skip_fee_check: false,
});
config.output_format = OutputFormat::JsonCompact;
let response = process_command(&config);
@@ -332,7 +325,6 @@ fn test_cli_program_deploy_with_authority() {
upgrade_authority_signer_index: 1,
is_final: false,
max_len: Some(max_len),
skip_fee_check: false,
});
let response = process_command(&config);
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
@@ -374,7 +366,6 @@ fn test_cli_program_deploy_with_authority() {
upgrade_authority_signer_index: 1,
is_final: false,
max_len: Some(max_len),
skip_fee_check: false,
});
process_command(&config).unwrap();
let program_account = rpc_client.get_account(&program_pubkey).unwrap();
@@ -429,7 +420,6 @@ fn test_cli_program_deploy_with_authority() {
upgrade_authority_signer_index: 1,
is_final: false,
max_len: None,
skip_fee_check: false,
});
process_command(&config).unwrap();
let program_account = rpc_client.get_account(&program_pubkey).unwrap();
@@ -504,7 +494,6 @@ fn test_cli_program_deploy_with_authority() {
upgrade_authority_signer_index: 1,
is_final: false,
max_len: None,
skip_fee_check: false,
});
process_command(&config).unwrap_err();
@@ -520,7 +509,6 @@ fn test_cli_program_deploy_with_authority() {
upgrade_authority_signer_index: 1,
is_final: true,
max_len: None,
skip_fee_check: false,
});
let response = process_command(&config);
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
@@ -623,7 +611,6 @@ fn test_cli_program_close_program() {
upgrade_authority_signer_index: 1,
is_final: false,
max_len: Some(max_len),
skip_fee_check: false,
});
config.output_format = OutputFormat::JsonCompact;
process_command(&config).unwrap();
@@ -708,7 +695,6 @@ fn test_cli_program_write_buffer() {
buffer_pubkey: None,
buffer_authority_signer_index: None,
max_len: None,
skip_fee_check: false,
});
config.output_format = OutputFormat::JsonCompact;
let response = process_command(&config);
@@ -743,7 +729,6 @@ fn test_cli_program_write_buffer() {
buffer_pubkey: Some(buffer_keypair.pubkey()),
buffer_authority_signer_index: None,
max_len: Some(max_len),
skip_fee_check: false,
});
let response = process_command(&config);
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
@@ -805,7 +790,6 @@ fn test_cli_program_write_buffer() {
buffer_pubkey: Some(buffer_keypair.pubkey()),
buffer_authority_signer_index: Some(2),
max_len: None,
skip_fee_check: false,
});
let response = process_command(&config);
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
@@ -843,7 +827,6 @@ fn test_cli_program_write_buffer() {
buffer_pubkey: None,
buffer_authority_signer_index: Some(2),
max_len: None,
skip_fee_check: false,
});
let response = process_command(&config);
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
@@ -916,7 +899,6 @@ fn test_cli_program_write_buffer() {
buffer_pubkey: None,
buffer_authority_signer_index: None,
max_len: None,
skip_fee_check: false,
});
config.output_format = OutputFormat::JsonCompact;
let response = process_command(&config);
@@ -956,7 +938,6 @@ fn test_cli_program_write_buffer() {
buffer_pubkey: Some(buffer_keypair.pubkey()),
buffer_authority_signer_index: None,
max_len: None, //Some(max_len),
skip_fee_check: false,
});
process_command(&config).unwrap();
config.signers = vec![&keypair, &buffer_keypair];
@@ -970,7 +951,6 @@ fn test_cli_program_write_buffer() {
upgrade_authority_signer_index: 1,
is_final: true,
max_len: None,
skip_fee_check: false,
});
config.output_format = OutputFormat::JsonCompact;
let error = process_command(&config).unwrap_err();
@@ -1028,7 +1008,6 @@ fn test_cli_program_set_buffer_authority() {
buffer_pubkey: Some(buffer_keypair.pubkey()),
buffer_authority_signer_index: None,
max_len: None,
skip_fee_check: false,
});
process_command(&config).unwrap();
let buffer_account = rpc_client.get_account(&buffer_keypair.pubkey()).unwrap();
@@ -1144,7 +1123,6 @@ fn test_cli_program_mismatch_buffer_authority() {
buffer_pubkey: Some(buffer_keypair.pubkey()),
buffer_authority_signer_index: Some(2),
max_len: None,
skip_fee_check: false,
});
process_command(&config).unwrap();
let buffer_account = rpc_client.get_account(&buffer_keypair.pubkey()).unwrap();
@@ -1167,7 +1145,6 @@ fn test_cli_program_mismatch_buffer_authority() {
upgrade_authority_signer_index: 1,
is_final: true,
max_len: None,
skip_fee_check: false,
});
process_command(&config).unwrap_err();
@@ -1183,7 +1160,6 @@ fn test_cli_program_mismatch_buffer_authority() {
upgrade_authority_signer_index: 1,
is_final: true,
max_len: None,
skip_fee_check: false,
});
process_command(&config).unwrap();
}
@@ -1240,7 +1216,6 @@ fn test_cli_program_show() {
buffer_pubkey: Some(buffer_keypair.pubkey()),
buffer_authority_signer_index: Some(2),
max_len: None,
skip_fee_check: false,
});
process_command(&config).unwrap();
@@ -1300,7 +1275,6 @@ fn test_cli_program_show() {
upgrade_authority_signer_index: 1,
is_final: false,
max_len: Some(max_len),
skip_fee_check: false,
});
config.output_format = OutputFormat::JsonCompact;
let min_slot = rpc_client.get_slot().unwrap();
@@ -1427,7 +1401,6 @@ fn test_cli_program_dump() {
buffer_pubkey: Some(buffer_keypair.pubkey()),
buffer_authority_signer_index: Some(2),
max_len: None,
skip_fee_check: false,
});
process_command(&config).unwrap();

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-client-test"
version = "1.10.6"
version = "1.10.0"
description = "Solana RPC Test"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
@@ -8,31 +8,30 @@ license = "Apache-2.0"
homepage = "https://solana.com/"
documentation = "https://docs.rs/solana-client-test"
edition = "2021"
publish = false
[dependencies]
futures-util = "0.3.21"
serde_json = "1.0.79"
serial_test = "0.6.0"
solana-client = { path = "../client", version = "=1.10.6" }
solana-ledger = { path = "../ledger", version = "=1.10.6" }
solana-measure = { path = "../measure", version = "=1.10.6" }
solana-merkle-tree = { path = "../merkle-tree", version = "=1.10.6" }
solana-metrics = { path = "../metrics", version = "=1.10.6" }
solana-perf = { path = "../perf", version = "=1.10.6" }
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.10.6" }
solana-rpc = { path = "../rpc", version = "=1.10.6" }
solana-runtime = { path = "../runtime", version = "=1.10.6" }
solana-sdk = { path = "../sdk", version = "=1.10.6" }
solana-streamer = { path = "../streamer", version = "=1.10.6" }
solana-test-validator = { path = "../test-validator", version = "=1.10.6" }
solana-transaction-status = { path = "../transaction-status", version = "=1.10.6" }
solana-version = { path = "../version", version = "=1.10.6" }
futures-util = "0.3.19"
serde_json = "1.0.78"
serial_test = "0.5.1"
solana-client = { path = "../client", version = "=1.10.0" }
solana-ledger = { path = "../ledger", version = "=1.10.0" }
solana-measure = { path = "../measure", version = "=1.10.0" }
solana-merkle-tree = { path = "../merkle-tree", version = "=1.10.0" }
solana-metrics = { path = "../metrics", version = "=1.10.0" }
solana-perf = { path = "../perf", version = "=1.10.0" }
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.10.0" }
solana-rpc = { path = "../rpc", version = "=1.10.0" }
solana-runtime = { path = "../runtime", version = "=1.10.0" }
solana-sdk = { path = "../sdk", version = "=1.10.0" }
solana-streamer = { path = "../streamer", version = "=1.10.0" }
solana-test-validator = { path = "../test-validator", version = "=1.10.0" }
solana-transaction-status = { path = "../transaction-status", version = "=1.10.0" }
solana-version = { path = "../version", version = "=1.10.0" }
systemstat = "0.1.10"
tokio = { version = "1", features = ["full"] }
[dev-dependencies]
solana-logger = { path = "../logger", version = "=1.10.6" }
solana-logger = { path = "../logger", version = "=1.10.0" }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -15,7 +15,7 @@ use {
solana_ledger::{blockstore::Blockstore, get_tmp_ledger_path},
solana_rpc::{
optimistically_confirmed_bank_tracker::OptimisticallyConfirmedBank,
rpc::{create_test_transaction_entries, populate_blockstore_for_tests},
rpc::create_test_transactions_and_populate_blockstore,
rpc_pubsub_service::{PubSubConfig, PubSubService},
rpc_subscriptions::RpcSubscriptions,
},
@@ -36,9 +36,7 @@ use {
},
solana_streamer::socket::SocketAddrSpace,
solana_test_validator::TestValidator,
solana_transaction_status::{
BlockEncodingOptions, ConfirmedBlock, TransactionDetails, UiTransactionEncoding,
},
solana_transaction_status::{ConfirmedBlock, TransactionDetails, UiTransactionEncoding},
std::{
collections::HashSet,
net::{IpAddr, SocketAddr},
@@ -232,12 +230,9 @@ fn test_block_subscription() {
let max_complete_transaction_status_slot = Arc::new(AtomicU64::new(blockstore.max_root()));
bank.transfer(rent_exempt_amount, &alice, &keypair2.pubkey())
.unwrap();
populate_blockstore_for_tests(
create_test_transaction_entries(
vec![&alice, &keypair1, &keypair2, &keypair3],
bank.clone(),
)
.0,
let _confirmed_block_signatures = create_test_transactions_and_populate_blockstore(
vec![&alice, &keypair1, &keypair2, &keypair3],
0,
bank,
blockstore.clone(),
max_complete_transaction_status_slot,
@@ -275,7 +270,6 @@ fn test_block_subscription() {
encoding: Some(UiTransactionEncoding::Json),
transaction_details: Some(TransactionDetails::Signatures),
show_rewards: None,
max_supported_transaction_version: None,
}),
)
.unwrap();
@@ -287,17 +281,14 @@ fn test_block_subscription() {
match maybe_actual {
Ok(actual) => {
let versioned_block = blockstore.get_complete_block(slot, false).unwrap();
let confirmed_block = ConfirmedBlock::from(versioned_block);
let block = confirmed_block
.encode_with_options(
UiTransactionEncoding::Json,
BlockEncodingOptions {
transaction_details: TransactionDetails::Signatures,
show_rewards: false,
max_supported_transaction_version: None,
},
)
let legacy_block = ConfirmedBlock::from(versioned_block)
.into_legacy_block()
.unwrap();
let block = legacy_block.configure(
UiTransactionEncoding::Json,
TransactionDetails::Signatures,
false,
);
assert_eq!(actual.value.slot, slot);
assert!(block.eq(&actual.value.block.unwrap()));
}

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-client"
version = "1.10.6"
version = "1.10.0"
description = "Solana Client"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
@@ -10,41 +10,31 @@ license = "Apache-2.0"
edition = "2021"
[dependencies]
async-mutex = "1.4.0"
async-trait = "0.1.52"
base64 = "0.13.0"
bincode = "1.3.3"
bs58 = "0.4.0"
bytes = "1.1.0"
clap = "2.33.0"
crossbeam-channel = "0.5"
futures = "0.3"
futures-util = "0.3.21"
futures-util = "0.3.19"
indicatif = "0.16.2"
itertools = "0.10.2"
jsonrpc-core = "18.0.0"
lazy_static = "1.4.0"
log = "0.4.14"
quinn = "0.8.0"
rand = "0.7.0"
rand_chacha = "0.2.2"
rayon = "1.5.1"
reqwest = { version = "0.11.10", default-features = false, features = ["blocking", "rustls-tls", "json"] }
rustls = { version = "0.20.2", features = ["dangerous_configuration"] }
semver = "1.0.6"
reqwest = { version = "0.11.9", default-features = false, features = ["blocking", "rustls-tls", "json"] }
semver = "1.0.5"
serde = "1.0.136"
serde_derive = "1.0.103"
serde_json = "1.0.79"
solana-account-decoder = { path = "../account-decoder", version = "=1.10.6" }
solana-clap-utils = { path = "../clap-utils", version = "=1.10.6" }
solana-faucet = { path = "../faucet", version = "=1.10.6" }
solana-measure = { path = "../measure", version = "=1.10.6" }
solana-net-utils = { path = "../net-utils", version = "=1.10.6" }
solana-sdk = { path = "../sdk", version = "=1.10.6" }
solana-streamer = { path = "../streamer", version = "=1.10.6" }
solana-transaction-status = { path = "../transaction-status", version = "=1.10.6" }
solana-version = { path = "../version", version = "=1.10.6" }
solana-vote-program = { path = "../programs/vote", version = "=1.10.6" }
serde_json = "1.0.78"
solana-account-decoder = { path = "../account-decoder", version = "=1.10.0" }
solana-clap-utils = { path = "../clap-utils", version = "=1.10.0" }
solana-faucet = { path = "../faucet", version = "=1.10.0" }
solana-net-utils = { path = "../net-utils", version = "=1.10.0" }
solana-measure = { path = "../measure", version = "=1.10.0" }
solana-sdk = { path = "../sdk", version = "=1.10.0" }
solana-transaction-status = { path = "../transaction-status", version = "=1.10.0" }
solana-version = { path = "../version", version = "=1.10.0" }
solana-vote-program = { path = "../programs/vote", version = "=1.10.0" }
thiserror = "1.0"
tokio = { version = "1", features = ["full"] }
tokio-stream = "0.1.8"
@@ -55,7 +45,7 @@ url = "2.2.2"
[dev-dependencies]
assert_matches = "1.5.0"
jsonrpc-http-server = "18.0.0"
solana-logger = { path = "../logger", version = "=1.10.6" }
solana-logger = { path = "../logger", version = "=1.10.0" }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -1,7 +1,6 @@
pub use reqwest;
use {
crate::{rpc_request, rpc_response},
quinn::{ConnectError, WriteError},
solana_faucet::faucet::FaucetError,
solana_sdk::{
signature::SignerError, transaction::TransactionError, transport::TransportError,
@@ -73,18 +72,6 @@ impl From<ClientErrorKind> for TransportError {
}
}
impl From<WriteError> for ClientErrorKind {
fn from(write_error: WriteError) -> Self {
Self::Custom(format!("{:?}", write_error))
}
}
impl From<ConnectError> for ClientErrorKind {
fn from(connect_error: ConnectError) -> Self {
Self::Custom(format!("{:?}", connect_error))
}
}
#[derive(Error, Debug)]
#[error("{kind}")]
pub struct ClientError {

View File

@@ -1,241 +0,0 @@
use {
crate::{
quic_client::QuicTpuConnection, tpu_connection::TpuConnection, udp_client::UdpTpuConnection,
},
lazy_static::lazy_static,
solana_sdk::{transaction::VersionedTransaction, transport::TransportError},
std::{
collections::{hash_map::Entry, BTreeMap, HashMap},
net::{SocketAddr, UdpSocket},
sync::{Arc, Mutex},
},
};
// Should be non-zero
static MAX_CONNECTIONS: usize = 64;
#[derive(Clone)]
enum Connection {
Udp(Arc<UdpTpuConnection>),
Quic(Arc<QuicTpuConnection>),
}
struct ConnMap {
// Keeps track of the connection associated with an addr and the last time it was used
map: HashMap<SocketAddr, (Connection, u64)>,
// Helps to find the least recently used connection. The search and inserts are O(log(n))
// but since we're bounding the size of the collections, this should be constant
// (and hopefully negligible) time. In theory, we can do this in constant time
// with a queue implemented as a doubly-linked list (and all the
// HashMap entries holding a "pointer" to the corresponding linked-list node),
// so we can push, pop and bump a used connection back to the end of the queue in O(1) time, but
// that seems non-"Rust-y" and low bang/buck. This is still pretty terrible though...
last_used_times: BTreeMap<u64, SocketAddr>,
ticks: u64,
use_quic: bool,
}
impl ConnMap {
pub fn new() -> Self {
Self {
map: HashMap::new(),
last_used_times: BTreeMap::new(),
ticks: 0,
use_quic: false,
}
}
pub fn set_use_quic(&mut self, use_quic: bool) {
self.use_quic = use_quic;
}
}
lazy_static! {
static ref CONNECTION_MAP: Mutex<ConnMap> = Mutex::new(ConnMap::new());
}
pub fn set_use_quic(use_quic: bool) {
let mut map = (*CONNECTION_MAP).lock().unwrap();
map.set_use_quic(use_quic);
}
#[allow(dead_code)]
// TODO: see https://github.com/solana-labs/solana/issues/23661
// remove lazy_static and optimize and refactor this
fn get_connection(addr: &SocketAddr) -> Connection {
let mut map = (*CONNECTION_MAP).lock().unwrap();
let ticks = map.ticks;
let use_quic = map.use_quic;
let (conn, target_ticks) = match map.map.entry(*addr) {
Entry::Occupied(mut entry) => {
let mut pair = entry.get_mut();
let old_ticks = pair.1;
pair.1 = ticks;
(pair.0.clone(), old_ticks)
}
Entry::Vacant(entry) => {
let send_socket = UdpSocket::bind("0.0.0.0:0").unwrap();
// TODO: see https://github.com/solana-labs/solana/issues/23659
// make it configurable (e.g. via the command line) whether to use UDP or Quic
let conn = if use_quic {
Connection::Quic(Arc::new(QuicTpuConnection::new(send_socket, *addr)))
} else {
Connection::Udp(Arc::new(UdpTpuConnection::new(send_socket, *addr)))
};
entry.insert((conn.clone(), ticks));
(conn, ticks)
}
};
let num_connections = map.map.len();
if num_connections > MAX_CONNECTIONS {
let (old_ticks, target_addr) = {
let (old_ticks, target_addr) = map.last_used_times.iter().next().unwrap();
(*old_ticks, *target_addr)
};
map.map.remove(&target_addr);
map.last_used_times.remove(&old_ticks);
}
if target_ticks != ticks {
map.last_used_times.remove(&target_ticks);
}
map.last_used_times.insert(ticks, *addr);
map.ticks += 1;
conn
}
// TODO: see https://github.com/solana-labs/solana/issues/23851
// use enum_dispatch and get rid of this tedious code.
// The main blocker to using enum_dispatch right now is that
// the it doesn't work with static methods like TpuConnection::new
// which is used by thin_client. This will be eliminated soon
// once thin_client is moved to using this connection cache.
// Once that is done, we will migrate to using enum_dispatch
// This will be done in a followup to
// https://github.com/solana-labs/solana/pull/23817
pub fn send_wire_transaction_batch(
packets: &[&[u8]],
addr: &SocketAddr,
) -> Result<(), TransportError> {
let conn = get_connection(addr);
match conn {
Connection::Udp(conn) => conn.send_wire_transaction_batch(packets),
Connection::Quic(conn) => conn.send_wire_transaction_batch(packets),
}
}
pub fn send_wire_transaction(
wire_transaction: &[u8],
addr: &SocketAddr,
) -> Result<(), TransportError> {
let conn = get_connection(addr);
match conn {
Connection::Udp(conn) => conn.send_wire_transaction(wire_transaction),
Connection::Quic(conn) => conn.send_wire_transaction(wire_transaction),
}
}
pub fn serialize_and_send_transaction(
transaction: &VersionedTransaction,
addr: &SocketAddr,
) -> Result<(), TransportError> {
let conn = get_connection(addr);
match conn {
Connection::Udp(conn) => conn.serialize_and_send_transaction(transaction),
Connection::Quic(conn) => conn.serialize_and_send_transaction(transaction),
}
}
pub fn par_serialize_and_send_transaction_batch(
transactions: &[VersionedTransaction],
addr: &SocketAddr,
) -> Result<(), TransportError> {
let conn = get_connection(addr);
match conn {
Connection::Udp(conn) => conn.par_serialize_and_send_transaction_batch(transactions),
Connection::Quic(conn) => conn.par_serialize_and_send_transaction_batch(transactions),
}
}
#[cfg(test)]
mod tests {
use {
crate::{
connection_cache::{get_connection, Connection, CONNECTION_MAP, MAX_CONNECTIONS},
tpu_connection::TpuConnection,
},
rand::{Rng, SeedableRng},
rand_chacha::ChaChaRng,
std::net::{IpAddr, SocketAddr},
};
fn get_addr(rng: &mut ChaChaRng) -> SocketAddr {
let a = rng.gen_range(1, 255);
let b = rng.gen_range(1, 255);
let c = rng.gen_range(1, 255);
let d = rng.gen_range(1, 255);
let addr_str = format!("{}.{}.{}.{}:80", a, b, c, d);
addr_str.parse().expect("Invalid address")
}
fn ip(conn: Connection) -> IpAddr {
match conn {
Connection::Udp(conn) => conn.tpu_addr().ip(),
Connection::Quic(conn) => conn.tpu_addr().ip(),
}
}
#[test]
fn test_connection_cache() {
// Allow the test to run deterministically
// with the same pseudorandom sequence between runs
// and on different platforms - the cryptographic security
// property isn't important here but ChaChaRng provides a way
// to get the same pseudorandom sequence on different platforms
let mut rng = ChaChaRng::seed_from_u64(42);
// Generate a bunch of random addresses and create TPUConnections to them
// Since TPUConnection::new is infallible, it should't matter whether or not
// we can actually connect to those addresses - TPUConnection implementations should either
// be lazy and not connect until first use or handle connection errors somehow
// (without crashing, as would be required in a real practical validator)
let first_addr = get_addr(&mut rng);
assert!(ip(get_connection(&first_addr)) == first_addr.ip());
let addrs = (0..MAX_CONNECTIONS)
.into_iter()
.map(|_| {
let addr = get_addr(&mut rng);
get_connection(&addr);
addr
})
.collect::<Vec<_>>();
{
let map = (*CONNECTION_MAP).lock().unwrap();
addrs.iter().for_each(|a| {
let conn = map.map.get(a).expect("Address not found");
assert!(a.ip() == ip(conn.0.clone()));
});
assert!(map.map.get(&first_addr).is_none());
}
// Test that get_connection updates which connection is next up for eviction
// when an existing connection is used. Initially, addrs[0] should be next up for eviction, since
// it was the earliest added. But we do get_connection(&addrs[0]), thereby using
// that connection, and bumping it back to the end of the queue. So addrs[1] should be
// the next up for eviction. So we add a new connection, and test that addrs[0] is not
// evicted but addrs[1] is.
get_connection(&addrs[0]);
get_connection(&get_addr(&mut rng));
let map = (*CONNECTION_MAP).lock().unwrap();
assert!(map.map.get(&addrs[0]).is_some());
assert!(map.map.get(&addrs[1]).is_none());
}
}

View File

@@ -4,14 +4,12 @@ extern crate serde_derive;
pub mod blockhash_query;
pub mod client_error;
pub mod connection_cache;
pub(crate) mod http_sender;
pub(crate) mod mock_sender;
pub mod nonblocking;
pub mod nonce_utils;
pub mod perf_utils;
pub mod pubsub_client;
pub mod quic_client;
pub mod rpc_cache;
pub mod rpc_client;
pub mod rpc_config;
@@ -20,13 +18,11 @@ pub mod rpc_deprecated_config;
pub mod rpc_filter;
pub mod rpc_request;
pub mod rpc_response;
pub mod rpc_sender;
pub(crate) mod rpc_sender;
pub mod spinner;
pub mod thin_client;
pub mod tpu_client;
pub mod tpu_connection;
pub mod transaction_executor;
pub mod udp_client;
pub mod mock_sender_for_cli {
/// Magic `SIGNATURE` value used by `solana-cli` unit tests.

View File

@@ -28,13 +28,13 @@ use {
pubkey::Pubkey,
signature::Signature,
sysvar::epoch_schedule::EpochSchedule,
transaction::{self, Transaction, TransactionError, TransactionVersion},
transaction::{self, Transaction, TransactionError},
},
solana_transaction_status::{
EncodedConfirmedBlock, EncodedConfirmedTransactionWithStatusMeta, EncodedTransaction,
EncodedTransactionWithStatusMeta, Rewards, TransactionBinaryEncoding,
TransactionConfirmationStatus, TransactionStatus, UiCompiledInstruction, UiMessage,
UiRawMessage, UiTransaction, UiTransactionStatusMeta,
EncodedTransactionWithStatusMeta, Rewards, TransactionConfirmationStatus,
TransactionStatus, UiCompiledInstruction, UiMessage, UiRawMessage, UiTransaction,
UiTransactionEncoding, UiTransactionStatusMeta,
},
solana_version::Version,
std::{collections::HashMap, net::SocketAddr, str::FromStr, sync::RwLock},
@@ -192,7 +192,6 @@ impl RpcSender for MockSender {
"getTransaction" => serde_json::to_value(EncodedConfirmedTransactionWithStatusMeta {
slot: 2,
transaction: EncodedTransactionWithStatusMeta {
version: Some(TransactionVersion::LEGACY),
transaction: EncodedTransaction::Json(
UiTransaction {
signatures: vec!["3AsdoALgZFuq2oUVWrDYhg2pNeaLJKPLf8hU2mQ6U8qJxeJ6hsrPVpMn9ma39DtfYCrDQSvngWRP8NnTpEhezJpE".to_string()],
@@ -214,7 +213,6 @@ impl RpcSender for MockSender {
accounts: vec![0, 1],
data: "3Bxs49DitAvXtoDR".to_string(),
}],
address_table_lookups: None,
})
}),
meta: Some(UiTransactionStatusMeta {
@@ -228,7 +226,6 @@ impl RpcSender for MockSender {
pre_token_balances: None,
post_token_balances: None,
rewards: None,
loaded_addresses: None,
}),
},
block_time: Some(1628633791),
@@ -381,10 +378,9 @@ impl RpcSender for MockSender {
pLHxcaShD81xBNaFDgnA2nkkdHnKtZt4hVSfKAmw3VRZbjrZ7L2fKZBx21CwsG\
hD6onjM2M3qZW5C8J6d1pj41MxKmZgPBSha3MyKkNLkAGFASK"
.to_string(),
TransactionBinaryEncoding::Base58,
UiTransactionEncoding::Base58,
),
meta: None,
version: Some(TransactionVersion::LEGACY),
}],
rewards: Rewards::new(),
block_time: None,

View File

@@ -6,7 +6,6 @@
//!
//! [JSON-RPC]: https://www.jsonrpc.org/specification
pub use crate::mock_sender::Mocks;
#[allow(deprecated)]
use crate::rpc_deprecated_config::{
RpcConfirmedBlockConfig, RpcConfirmedTransactionConfig,
@@ -16,7 +15,7 @@ use {
crate::{
client_error::{ClientError, ClientErrorKind, Result as ClientResult},
http_sender::HttpSender,
mock_sender::MockSender,
mock_sender::{MockSender, Mocks},
rpc_client::{GetConfirmedSignaturesForAddress2Config, RpcClientConfig},
rpc_config::{RpcAccountInfoConfig, *},
rpc_request::{RpcError, RpcRequest, RpcResponseErrorData, TokenAccountsFilter},
@@ -147,9 +146,9 @@ impl RpcClient {
///
/// This is the basic constructor, allowing construction with any type of
/// `RpcSender`. Most applications should use one of the other constructors,
/// such as [`RpcClient::new`], [`RpcClient::new_with_commitment`] or
/// [`RpcClient::new_with_timeout`].
pub fn new_sender<T: RpcSender + Send + Sync + 'static>(
/// such as [`new`] and [`new_mock`], which create an `RpcClient`
/// encapsulating an [`HttpSender`] and [`MockSender`] respectively.
pub(crate) fn new_sender<T: RpcSender + Send + Sync + 'static>(
sender: T,
config: RpcClientConfig,
) -> Self {
@@ -315,34 +314,8 @@ impl RpcClient {
/// Create a mock `RpcClient`.
///
/// A mock `RpcClient` contains an implementation of [`RpcSender`] that does
/// not use the network, and instead returns synthetic responses, for use in
/// tests.
///
/// It is primarily for internal use, with limited customizability, and
/// behaviors determined by internal Solana test cases. New users should
/// consider implementing `RpcSender` themselves and constructing
/// `RpcClient` with [`RpcClient::new_sender`] to get mock behavior.
///
/// Unless directed otherwise, a mock `RpcClient` will generally return a
/// reasonable default response to any request, at least for [`RpcRequest`]
/// values for which responses have been implemented.
///
/// This mock can be customized by changing the `url` argument, which is not
/// actually a URL, but a simple string directive that changes the mock
/// behavior in specific scenarios:
///
/// - It is customary to set the `url` to "succeeds" for mocks that should
/// return sucessfully, though this value is not actually interpreted.
///
/// - If `url` is "fails" then any call to `send` will return `Ok(Value::Null)`.
///
/// - Other possible values of `url` are specific to different `RpcRequest`
/// values. Read the implementation of (non-public) `MockSender` for
/// details.
///
/// The [`RpcClient::new_mock_with_mocks`] function offers further
/// customization options.
/// See the [`MockSender`] documentation for an explanation of
/// how it treats the `url` argument.
///
/// # Examples
///
@@ -368,43 +341,8 @@ impl RpcClient {
/// Create a mock `RpcClient`.
///
/// A mock `RpcClient` contains an implementation of [`RpcSender`] that does
/// not use the network, and instead returns synthetic responses, for use in
/// tests.
///
/// It is primarily for internal use, with limited customizability, and
/// behaviors determined by internal Solana test cases. New users should
/// consider implementing `RpcSender` themselves and constructing
/// `RpcClient` with [`RpcClient::new_sender`] to get mock behavior.
///
/// Unless directed otherwise, a mock `RpcClient` will generally return a
/// reasonable default response to any request, at least for [`RpcRequest`]
/// values for which responses have been implemented.
///
/// This mock can be customized in two ways:
///
/// 1) By changing the `url` argument, which is not actually a URL, but a
/// simple string directive that changes the mock behavior in specific
/// scenarios.
///
/// It is customary to set the `url` to "succeeds" for mocks that should
/// return sucessfully, though this value is not actually interpreted.
///
/// If `url` is "fails" then any call to `send` will return `Ok(Value::Null)`.
///
/// Other possible values of `url` are specific to different `RpcRequest`
/// values. Read the implementation of `MockSender` (which is non-public)
/// for details.
///
/// 2) Custom responses can be configured by providing [`Mocks`]. This type
/// is a [`HashMap`] from [`RpcRequest`] to a JSON [`Value`] response,
/// Any entries in this map override the default behavior for the given
/// request.
///
/// The [`RpcClient::new_mock_with_mocks`] function offers further
/// customization options.
///
/// [`HashMap`]: std::collections::HashMap
/// See the [`MockSender`] documentation for an explanation of how it treats
/// the `url` argument.
///
/// # Examples
///
@@ -2473,7 +2411,6 @@ impl RpcClient {
/// transaction_details: Some(TransactionDetails::None),
/// rewards: Some(true),
/// commitment: None,
/// max_supported_transaction_version: Some(0),
/// };
/// let block = rpc_client.get_block_with_config(
/// slot,
@@ -3114,7 +3051,6 @@ impl RpcClient {
/// let config = RpcTransactionConfig {
/// encoding: Some(UiTransactionEncoding::Json),
/// commitment: Some(CommitmentConfig::confirmed()),
/// max_supported_transaction_version: Some(0),
/// };
/// let transaction = rpc_client.get_transaction_with_config(
/// &signature,

View File

@@ -1,213 +0,0 @@
//! Simple client that connects to a given UDP port with the QUIC protocol and provides
//! an interface for sending transactions which is restricted by the server's flow control.
use {
crate::{client_error::ClientErrorKind, tpu_connection::TpuConnection},
async_mutex::Mutex,
futures::future::join_all,
itertools::Itertools,
quinn::{ClientConfig, Endpoint, EndpointConfig, NewConnection, WriteError},
solana_sdk::{
quic::{QUIC_MAX_CONCURRENT_STREAMS, QUIC_PORT_OFFSET},
transport::Result as TransportResult,
},
std::{
net::{SocketAddr, UdpSocket},
sync::Arc,
},
tokio::runtime::Runtime,
};
struct SkipServerVerification;
impl SkipServerVerification {
pub fn new() -> Arc<Self> {
Arc::new(Self)
}
}
impl rustls::client::ServerCertVerifier for SkipServerVerification {
fn verify_server_cert(
&self,
_end_entity: &rustls::Certificate,
_intermediates: &[rustls::Certificate],
_server_name: &rustls::ServerName,
_scts: &mut dyn Iterator<Item = &[u8]>,
_ocsp_response: &[u8],
_now: std::time::SystemTime,
) -> Result<rustls::client::ServerCertVerified, rustls::Error> {
Ok(rustls::client::ServerCertVerified::assertion())
}
}
struct QuicClient {
runtime: Runtime,
endpoint: Endpoint,
connection: Arc<Mutex<Option<Arc<NewConnection>>>>,
addr: SocketAddr,
}
pub struct QuicTpuConnection {
client: Arc<QuicClient>,
}
impl TpuConnection for QuicTpuConnection {
fn new(client_socket: UdpSocket, tpu_addr: SocketAddr) -> Self {
let tpu_addr = SocketAddr::new(tpu_addr.ip(), tpu_addr.port() + QUIC_PORT_OFFSET);
let client = Arc::new(QuicClient::new(client_socket, tpu_addr));
Self { client }
}
fn tpu_addr(&self) -> &SocketAddr {
&self.client.addr
}
fn send_wire_transaction<T>(&self, wire_transaction: T) -> TransportResult<()>
where
T: AsRef<[u8]>,
{
let _guard = self.client.runtime.enter();
let send_buffer = self.client.send_buffer(wire_transaction);
self.client.runtime.block_on(send_buffer)?;
Ok(())
}
fn send_wire_transaction_batch<T>(&self, buffers: &[T]) -> TransportResult<()>
where
T: AsRef<[u8]>,
{
let _guard = self.client.runtime.enter();
let send_batch = self.client.send_batch(buffers);
self.client.runtime.block_on(send_batch)?;
Ok(())
}
}
impl QuicClient {
pub fn new(client_socket: UdpSocket, addr: SocketAddr) -> Self {
let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
let _guard = runtime.enter();
let crypto = rustls::ClientConfig::builder()
.with_safe_defaults()
.with_custom_certificate_verifier(SkipServerVerification::new())
.with_no_client_auth();
let create_endpoint = QuicClient::create_endpoint(EndpointConfig::default(), client_socket);
let mut endpoint = runtime.block_on(create_endpoint);
endpoint.set_default_client_config(ClientConfig::new(Arc::new(crypto)));
Self {
runtime,
endpoint,
connection: Arc::new(Mutex::new(None)),
addr,
}
}
// If this function becomes public, it should be changed to
// not expose details of the specific Quic implementation we're using
async fn create_endpoint(config: EndpointConfig, client_socket: UdpSocket) -> Endpoint {
quinn::Endpoint::new(config, None, client_socket).unwrap().0
}
async fn _send_buffer_using_conn(
data: &[u8],
connection: &NewConnection,
) -> Result<(), WriteError> {
let mut send_stream = connection.connection.open_uni().await?;
send_stream.write_all(data).await?;
send_stream.finish().await?;
Ok(())
}
// Attempts to send data, connecting/reconnecting as necessary
// On success, returns the connection used to successfully send the data
async fn _send_buffer(&self, data: &[u8]) -> Result<Arc<NewConnection>, WriteError> {
let connection = {
let mut conn_guard = self.connection.lock().await;
let maybe_conn = (*conn_guard).clone();
match maybe_conn {
Some(conn) => conn.clone(),
None => {
let connecting = self.endpoint.connect(self.addr, "connect").unwrap();
let connection = Arc::new(connecting.await?);
*conn_guard = Some(connection.clone());
connection
}
}
};
match Self::_send_buffer_using_conn(data, &connection).await {
Ok(()) => Ok(connection),
_ => {
let connection = {
let connecting = self.endpoint.connect(self.addr, "connect").unwrap();
let connection = Arc::new(connecting.await?);
let mut conn_guard = self.connection.lock().await;
*conn_guard = Some(connection.clone());
connection
};
Self::_send_buffer_using_conn(data, &connection).await?;
Ok(connection)
}
}
}
pub async fn send_buffer<T>(&self, data: T) -> Result<(), ClientErrorKind>
where
T: AsRef<[u8]>,
{
self._send_buffer(data.as_ref()).await?;
Ok(())
}
pub async fn send_batch<T>(&self, buffers: &[T]) -> Result<(), ClientErrorKind>
where
T: AsRef<[u8]>,
{
// Start off by "testing" the connection by sending the first transaction
// This will also connect to the server if not already connected
// and reconnect and retry if the first send attempt failed
// (for example due to a timed out connection), returning an error
// or the connection that was used to successfully send the transaction.
// We will use the returned connection to send the rest of the transactions in the batch
// to avoid touching the mutex in self, and not bother reconnecting if we fail along the way
// since testing even in the ideal GCE environment has found no cases
// where reconnecting and retrying in the middle of a batch send
// (i.e. we encounter a connection error in the middle of a batch send, which presumably cannot
// be due to a timed out connection) has succeeded
if buffers.is_empty() {
return Ok(());
}
let connection = self._send_buffer(buffers[0].as_ref()).await?;
// Used to avoid dereferencing the Arc multiple times below
// by just getting a reference to the NewConnection once
let connection_ref: &NewConnection = &connection;
let chunks = buffers[1..buffers.len()]
.iter()
.chunks(QUIC_MAX_CONCURRENT_STREAMS);
let futures = chunks.into_iter().map(|buffs| {
join_all(
buffs
.into_iter()
.map(|buf| Self::_send_buffer_using_conn(buf.as_ref(), connection_ref)),
)
});
for f in futures {
f.await.into_iter().try_for_each(|res| res)?;
}
Ok(())
}
}

View File

@@ -6,14 +6,13 @@
//!
//! [JSON-RPC]: https://www.jsonrpc.org/specification
pub use crate::mock_sender::Mocks;
#[allow(deprecated)]
use crate::rpc_deprecated_config::{RpcConfirmedBlockConfig, RpcConfirmedTransactionConfig};
use {
crate::{
client_error::Result as ClientResult,
http_sender::HttpSender,
mock_sender::MockSender,
mock_sender::{MockSender, Mocks},
nonblocking::{self, rpc_client::get_rpc_request_str},
rpc_config::{RpcAccountInfoConfig, *},
rpc_request::{RpcRequest, TokenAccountsFilter},
@@ -104,8 +103,8 @@ pub struct GetConfirmedSignaturesForAddress2Config {
/// [`Processed`] commitment level. These exceptions are noted in the method
/// documentation.
///
/// [`Finalized`]: solana_sdk::commitment_config::CommitmentLevel::Finalized
/// [`Processed`]: solana_sdk::commitment_config::CommitmentLevel::Processed
/// [`Finalized`]: CommitmentLevel::Finalized
/// [`Processed`]: CommitmentLevel::Processed
/// [jsonprot]: https://docs.solana.com/developing/clients/jsonrpc-api
/// [JSON-RPC]: https://www.jsonrpc.org/specification
/// [slots]: https://docs.solana.com/terminology#slot
@@ -146,10 +145,6 @@ pub struct GetConfirmedSignaturesForAddress2Config {
/// [`is_timeout`](crate::client_error::reqwest::Error::is_timeout) method
/// returns `true`. The default timeout is 30 seconds, and may be changed by
/// calling an appropriate constructor with a `timeout` parameter.
///
/// [`ClientError`]: crate::client_error::ClientError
/// [`ClientErrorKind`]: crate::client_error::ClientErrorKind
/// [`ClientErrorKind::Reqwest`]: crate::client_error::ClientErrorKind::Reqwest
pub struct RpcClient {
rpc_client: nonblocking::rpc_client::RpcClient,
runtime: Option<tokio::runtime::Runtime>,
@@ -166,9 +161,9 @@ impl RpcClient {
///
/// This is the basic constructor, allowing construction with any type of
/// `RpcSender`. Most applications should use one of the other constructors,
/// such as [`RpcClient::new`], [`RpcClient::new_with_commitment`] or
/// [`RpcClient::new_with_timeout`].
pub fn new_sender<T: RpcSender + Send + Sync + 'static>(
/// such as [`new`] and [`new_mock`], which create an `RpcClient`
/// encapsulating an [`HttpSender`] and [`MockSender`] respectively.
fn new_sender<T: RpcSender + Send + Sync + 'static>(
sender: T,
config: RpcClientConfig,
) -> Self {
@@ -191,10 +186,9 @@ impl RpcClient {
/// "http://localhost:8899".
///
/// The client has a default timeout of 30 seconds, and a default [commitment
/// level][cl] of [`Finalized`].
/// level][cl] of [`Finalized`](CommitmentLevel::Finalized).
///
/// [cl]: https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment
/// [`Finalized`]: solana_sdk::commitment_config::CommitmentLevel::Finalized
///
/// # Examples
///
@@ -217,8 +211,6 @@ impl RpcClient {
/// The client has a default timeout of 30 seconds, and a user-specified
/// [`CommitmentLevel`] via [`CommitmentConfig`].
///
/// [`CommitmentLevel`]: solana_sdk::commitment_config::CommitmentLevel
///
/// # Examples
///
/// ```
@@ -241,10 +233,9 @@ impl RpcClient {
/// "http://localhost:8899".
///
/// The client has and a default [commitment level][cl] of
/// [`Finalized`].
/// [`Finalized`](CommitmentLevel::Finalized).
///
/// [cl]: https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment
/// [`Finalized`]: solana_sdk::commitment_config::CommitmentLevel::Finalized
///
/// # Examples
///
@@ -344,34 +335,8 @@ impl RpcClient {
/// Create a mock `RpcClient`.
///
/// A mock `RpcClient` contains an implementation of [`RpcSender`] that does
/// not use the network, and instead returns synthetic responses, for use in
/// tests.
///
/// It is primarily for internal use, with limited customizability, and
/// behaviors determined by internal Solana test cases. New users should
/// consider implementing `RpcSender` themselves and constructing
/// `RpcClient` with [`RpcClient::new_sender`] to get mock behavior.
///
/// Unless directed otherwise, a mock `RpcClient` will generally return a
/// reasonable default response to any request, at least for [`RpcRequest`]
/// values for which responses have been implemented.
///
/// This mock can be customized by changing the `url` argument, which is not
/// actually a URL, but a simple string directive that changes the mock
/// behavior in specific scenarios:
///
/// - It is customary to set the `url` to "succeeds" for mocks that should
/// return sucessfully, though this value is not actually interpreted.
///
/// - If `url` is "fails" then any call to `send` will return `Ok(Value::Null)`.
///
/// - Other possible values of `url` are specific to different `RpcRequest`
/// values. Read the implementation of (non-public) `MockSender` for
/// details.
///
/// The [`RpcClient::new_mock_with_mocks`] function offers further
/// customization options.
/// See the [`MockSender`] documentation for an explanation of
/// how it treats the `url` argument.
///
/// # Examples
///
@@ -397,43 +362,8 @@ impl RpcClient {
/// Create a mock `RpcClient`.
///
/// A mock `RpcClient` contains an implementation of [`RpcSender`] that does
/// not use the network, and instead returns synthetic responses, for use in
/// tests.
///
/// It is primarily for internal use, with limited customizability, and
/// behaviors determined by internal Solana test cases. New users should
/// consider implementing `RpcSender` themselves and constructing
/// `RpcClient` with [`RpcClient::new_sender`] to get mock behavior.
///
/// Unless directed otherwise, a mock `RpcClient` will generally return a
/// reasonable default response to any request, at least for [`RpcRequest`]
/// values for which responses have been implemented.
///
/// This mock can be customized in two ways:
///
/// 1) By changing the `url` argument, which is not actually a URL, but a
/// simple string directive that changes the mock behavior in specific
/// scenarios.
///
/// It is customary to set the `url` to "succeeds" for mocks that should
/// return sucessfully, though this value is not actually interpreted.
///
/// If `url` is "fails" then any call to `send` will return `Ok(Value::Null)`.
///
/// Other possible values of `url` are specific to different `RpcRequest`
/// values. Read the implementation of `MockSender` (which is non-public)
/// for details.
///
/// 2) Custom responses can be configured by providing [`Mocks`]. This type
/// is a [`HashMap`] from [`RpcRequest`] to a JSON [`Value`] response,
/// Any entries in this map override the default behavior for the given
/// request.
///
/// The [`RpcClient::new_mock_with_mocks`] function offers further
/// customization options.
///
/// [`HashMap`]: std::collections::HashMap
/// See the [`MockSender`] documentation for an explanation of how it treats
/// the `url` argument.
///
/// # Examples
///
@@ -467,10 +397,9 @@ impl RpcClient {
/// Create an HTTP `RpcClient` from a [`SocketAddr`].
///
/// The client has a default timeout of 30 seconds, and a default [commitment
/// level][cl] of [`Finalized`].
/// level][cl] of [`Finalized`](CommitmentLevel::Finalized).
///
/// [cl]: https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment
/// [`Finalized`]: solana_sdk::commitment_config::CommitmentLevel::Finalized
///
/// # Examples
///
@@ -491,8 +420,6 @@ impl RpcClient {
/// The client has a default timeout of 30 seconds, and a user-specified
/// [`CommitmentLevel`] via [`CommitmentConfig`].
///
/// [`CommitmentLevel`]: solana_sdk::commitment_config::CommitmentLevel
///
/// # Examples
///
/// ```
@@ -515,10 +442,9 @@ impl RpcClient {
/// Create an HTTP `RpcClient` from a [`SocketAddr`] with specified timeout.
///
/// The client has a default [commitment level][cl] of [`Finalized`].
/// The client has a default [commitment level][cl] of [`Finalized`](CommitmentLevel::Finalized).
///
/// [cl]: https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment
/// [`Finalized`]: solana_sdk::commitment_config::CommitmentLevel::Finalized
///
/// # Examples
///
@@ -543,9 +469,7 @@ impl RpcClient {
/// determines how thoroughly committed a transaction must be when waiting
/// for its confirmation or otherwise checking for confirmation. If not
/// specified, the default commitment level is
/// [`Finalized`].
///
/// [`Finalized`]: solana_sdk::commitment_config::CommitmentLevel::Finalized
/// [`Finalized`](CommitmentLevel::Finalized).
///
/// The default commitment level is overridden when calling methods that
/// explicitly provide a [`CommitmentConfig`], like
@@ -579,8 +503,7 @@ impl RpcClient {
/// containing an [`RpcResponseError`] with `code` set to
/// [`JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY`].
///
/// [`RpcError`]: crate::rpc_request::RpcError
/// [`RpcResponseError`]: crate::rpc_request::RpcError::RpcResponseError
/// [`RpcResponseError`]: RpcError::RpcResponseError
/// [`JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE`]: crate::rpc_custom_error::JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE
/// [`JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE`]: crate::rpc_custom_error::JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE
/// [`JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY`]: crate::rpc_custom_error::JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY
@@ -693,8 +616,7 @@ impl RpcClient {
/// containing an [`RpcResponseError`] with `code` set to
/// [`JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY`].
///
/// [`RpcError`]: crate::rpc_request::RpcError
/// [`RpcResponseError`]: crate::rpc_request::RpcError::RpcResponseError
/// [`RpcResponseError`]: RpcError::RpcResponseError
/// [`JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE`]: crate::rpc_custom_error::JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE
/// [`JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE`]: crate::rpc_custom_error::JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE
/// [`JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY`]: crate::rpc_custom_error::JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY
@@ -768,8 +690,7 @@ impl RpcClient {
/// containing an [`RpcResponseError`] with `code` set to
/// [`JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY`].
///
/// [`RpcError`]: crate::rpc_request::RpcError
/// [`RpcResponseError`]: crate::rpc_request::RpcError::RpcResponseError
/// [`RpcResponseError`]: RpcError::RpcResponseError
/// [`JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE`]: crate::rpc_custom_error::JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE
/// [`JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE`]: crate::rpc_custom_error::JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE
/// [`JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY`]: crate::rpc_custom_error::JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY
@@ -2113,7 +2034,6 @@ impl RpcClient {
/// transaction_details: Some(TransactionDetails::None),
/// rewards: Some(true),
/// commitment: None,
/// max_supported_transaction_version: Some(0),
/// };
/// let block = rpc_client.get_block_with_config(
/// slot,
@@ -2181,7 +2101,7 @@ impl RpcClient {
///
/// This method uses the [`Finalized`] [commitment level][cl].
///
/// [`Finalized`]: solana_sdk::commitment_config::CommitmentLevel::Finalized
/// [`Finalized`]: CommitmentLevel::Finalized
/// [`get_blocks_with_limit`]: RpcClient::get_blocks_with_limit.
/// [cl]: https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment
///
@@ -2240,7 +2160,7 @@ impl RpcClient {
/// This method returns an error if the given commitment level is below
/// [`Confirmed`].
///
/// [`Confirmed`]: solana_sdk::commitment_config::CommitmentLevel::Confirmed
/// [`Confirmed`]: CommitmentLevel::Confirmed
///
/// # RPC Reference
///
@@ -2333,7 +2253,7 @@ impl RpcClient {
/// [`Confirmed`].
///
/// [cl]: https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment
/// [`Confirmed`]: solana_sdk::commitment_config::CommitmentLevel::Confirmed
/// [`Confirmed`]: CommitmentLevel::Confirmed
///
/// # RPC Reference
///
@@ -2494,7 +2414,7 @@ impl RpcClient {
/// [`Confirmed`].
///
/// [cl]: https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment
/// [`Confirmed`]: solana_sdk::commitment_config::CommitmentLevel::Confirmed
/// [`Confirmed`]: CommitmentLevel::Confirmed
///
/// # RPC Reference
///
@@ -2584,7 +2504,7 @@ impl RpcClient {
///
/// This method uses the [`Finalized`] [commitment level][cl].
///
/// [`Finalized`]: solana_sdk::commitment_config::CommitmentLevel::Finalized
/// [`Finalized`]: CommitmentLevel::Finalized
/// [cl]: https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment
///
/// # RPC Reference
@@ -2639,7 +2559,7 @@ impl RpcClient {
/// [`Confirmed`].
///
/// [cl]: https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment
/// [`Confirmed`]: solana_sdk::commitment_config::CommitmentLevel::Confirmed
/// [`Confirmed`]: CommitmentLevel::Confirmed
///
/// # RPC Reference
///
@@ -2676,7 +2596,6 @@ impl RpcClient {
/// let config = RpcTransactionConfig {
/// encoding: Some(UiTransactionEncoding::Json),
/// commitment: Some(CommitmentConfig::confirmed()),
/// max_supported_transaction_version: Some(0),
/// };
/// let transaction = rpc_client.get_transaction_with_config(
/// &signature,
@@ -3005,7 +2924,7 @@ impl RpcClient {
///
/// This method uses the [`Finalized`] [commitment level][cl].
///
/// [`Finalized`]: solana_sdk::commitment_config::CommitmentLevel::Finalized
/// [`Finalized`]: CommitmentLevel::Finalized
/// [cl]: https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment
///
/// # RPC Reference
@@ -3163,7 +3082,6 @@ impl RpcClient {
/// [`RpcError::ForUser`]. This is unlike [`get_account_with_commitment`],
/// which returns `Ok(None)` if the account does not exist.
///
/// [`RpcError::ForUser`]: crate::rpc_request::RpcError::ForUser
/// [`get_account_with_commitment`]: RpcClient::get_account_with_commitment
///
/// # RPC Reference

View File

@@ -197,7 +197,6 @@ pub struct RpcBlockSubscribeConfig {
pub encoding: Option<UiTransactionEncoding>,
pub transaction_details: Option<TransactionDetails>,
pub show_rewards: Option<bool>,
pub max_supported_transaction_version: Option<u8>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
@@ -249,7 +248,6 @@ pub struct RpcBlockConfig {
pub rewards: Option<bool>,
#[serde(flatten)]
pub commitment: Option<CommitmentConfig>,
pub max_supported_transaction_version: Option<u8>,
}
impl EncodingConfig for RpcBlockConfig {
@@ -290,7 +288,6 @@ pub struct RpcTransactionConfig {
pub encoding: Option<UiTransactionEncoding>,
#[serde(flatten)]
pub commitment: Option<CommitmentConfig>,
pub max_supported_transaction_version: Option<u8>,
}
impl EncodingConfig for RpcTransactionConfig {

View File

@@ -3,7 +3,6 @@ use {
crate::rpc_response::RpcSimulateTransactionResult,
jsonrpc_core::{Error, ErrorCode},
solana_sdk::clock::Slot,
solana_transaction_status::EncodeError,
thiserror::Error,
};
@@ -60,7 +59,7 @@ pub enum RpcCustomError {
#[error("BlockStatusNotAvailableYet")]
BlockStatusNotAvailableYet { slot: Slot },
#[error("UnsupportedTransactionVersion")]
UnsupportedTransactionVersion(u8),
UnsupportedTransactionVersion,
}
#[derive(Debug, Serialize, Deserialize)]
@@ -69,16 +68,6 @@ pub struct NodeUnhealthyErrorData {
pub num_slots_behind: Option<Slot>,
}
impl From<EncodeError> for RpcCustomError {
fn from(err: EncodeError) -> Self {
match err {
EncodeError::UnsupportedTransactionVersion(version) => {
Self::UnsupportedTransactionVersion(version)
}
}
}
}
impl From<RpcCustomError> for Error {
fn from(e: RpcCustomError) -> Self {
match e {
@@ -183,9 +172,9 @@ impl From<RpcCustomError> for Error {
message: format!("Block status not yet available for slot {}", slot),
data: None,
},
RpcCustomError::UnsupportedTransactionVersion(version) => Self {
RpcCustomError::UnsupportedTransactionVersion => Self {
code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_UNSUPPORTED_TRANSACTION_VERSION),
message: format!("Transaction version ({}) is not supported", version),
message: "Versioned transactions are not supported".to_string(),
data: None,
},
}

View File

@@ -71,7 +71,6 @@ impl From<RpcConfirmedBlockConfig> for RpcBlockConfig {
transaction_details: config.transaction_details,
rewards: config.rewards,
commitment: config.commitment,
max_supported_transaction_version: None,
}
}
}
@@ -99,7 +98,6 @@ impl From<RpcConfirmedTransactionConfig> for RpcTransactionConfig {
Self {
encoding: config.encoding,
commitment: config.commitment,
max_supported_transaction_version: None,
}
}
}

View File

@@ -117,7 +117,7 @@ pub struct RpcInflationRate {
pub epoch: Epoch,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct RpcKeyedAccount {
pub pubkey: String,
@@ -246,7 +246,7 @@ pub struct RpcBlockProductionRange {
pub last_slot: Slot,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[derive(Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct RpcBlockProduction {
/// Map of leader base58 identity pubkeys to a tuple of `(number of leader slots, number of blocks produced)`
@@ -363,7 +363,7 @@ pub struct RpcAccountBalance {
pub lamports: u64,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct RpcSupply {
pub total: u64,
@@ -432,8 +432,8 @@ pub enum RpcBlockUpdateError {
#[error("block store error")]
BlockStoreError,
#[error("unsupported transaction version ({0})")]
UnsupportedTransactionVersion(u8),
#[error("unsupported transaction version")]
UnsupportedTransactionVersion,
}
#[derive(Serialize, Deserialize, Debug)]

View File

@@ -23,9 +23,13 @@ pub struct RpcTransportStats {
/// `RpcSender` implements the underlying transport of requests to, and
/// responses from, a Solana node, and is used primarily by [`RpcClient`].
///
/// [`RpcClient`]: crate::rpc_client::RpcClient
/// It is typically implemented by [`HttpSender`] in production, and
/// [`MockSender`] in unit tests.
///
/// [`HttpSender`]: crate::http_sender::HttpSender
/// [`MockSender`]: crate::mock_sender::MockSender
#[async_trait]
pub trait RpcSender {
pub(crate) trait RpcSender {
async fn send(
&self,
request: RpcRequest,

View File

@@ -4,10 +4,8 @@
//! unstable and may change in future releases.
use {
crate::{
rpc_client::RpcClient, rpc_config::RpcProgramAccountsConfig, rpc_response::Response,
tpu_connection::TpuConnection, udp_client::UdpTpuConnection,
},
crate::{rpc_client::RpcClient, rpc_config::RpcProgramAccountsConfig, rpc_response::Response},
bincode::{serialize_into, serialized_size},
log::*,
solana_sdk::{
account::Account,
@@ -19,12 +17,13 @@ use {
hash::Hash,
instruction::Instruction,
message::Message,
packet::PACKET_DATA_SIZE,
pubkey::Pubkey,
signature::{Keypair, Signature, Signer},
signers::Signers,
system_instruction,
timing::duration_as_ms,
transaction::{self, Transaction, VersionedTransaction},
transaction::{self, Transaction},
transport::Result as TransportResult,
},
std::{
@@ -118,20 +117,22 @@ impl ClientOptimizer {
}
/// An object for querying and sending transactions to the network.
pub struct ThinClient<C: 'static + TpuConnection> {
pub struct ThinClient {
transactions_socket: UdpSocket,
tpu_addrs: Vec<SocketAddr>,
rpc_clients: Vec<RpcClient>,
tpu_connections: Vec<C>,
optimizer: ClientOptimizer,
}
impl<C: 'static + TpuConnection> ThinClient<C> {
impl ThinClient {
/// Create a new ThinClient that will interface with the Rpc at `rpc_addr` using TCP
/// and the Tpu at `tpu_addr` over `transactions_socket` using Quic or UDP
/// (currently hardcoded to UDP)
/// and the Tpu at `tpu_addr` over `transactions_socket` using UDP.
pub fn new(rpc_addr: SocketAddr, tpu_addr: SocketAddr, transactions_socket: UdpSocket) -> Self {
let tpu_connection = C::new(transactions_socket, tpu_addr);
Self::new_from_client(RpcClient::new_socket(rpc_addr), tpu_connection)
Self::new_from_client(
tpu_addr,
transactions_socket,
RpcClient::new_socket(rpc_addr),
)
}
pub fn new_socket_with_timeout(
@@ -141,14 +142,18 @@ impl<C: 'static + TpuConnection> ThinClient<C> {
timeout: Duration,
) -> Self {
let rpc_client = RpcClient::new_socket_with_timeout(rpc_addr, timeout);
let tpu_connection = C::new(transactions_socket, tpu_addr);
Self::new_from_client(rpc_client, tpu_connection)
Self::new_from_client(tpu_addr, transactions_socket, rpc_client)
}
fn new_from_client(rpc_client: RpcClient, tpu_connection: C) -> Self {
fn new_from_client(
tpu_addr: SocketAddr,
transactions_socket: UdpSocket,
rpc_client: RpcClient,
) -> Self {
Self {
transactions_socket,
tpu_addrs: vec![tpu_addr],
rpc_clients: vec![rpc_client],
tpu_connections: vec![tpu_connection],
optimizer: ClientOptimizer::new(0),
}
}
@@ -163,19 +168,16 @@ impl<C: 'static + TpuConnection> ThinClient<C> {
let rpc_clients: Vec<_> = rpc_addrs.into_iter().map(RpcClient::new_socket).collect();
let optimizer = ClientOptimizer::new(rpc_clients.len());
let tpu_connections: Vec<_> = tpu_addrs
.into_iter()
.map(|tpu_addr| C::new(transactions_socket.try_clone().unwrap(), tpu_addr))
.collect();
Self {
transactions_socket,
tpu_addrs,
rpc_clients,
tpu_connections,
optimizer,
}
}
fn tpu_connection(&self) -> &C {
&self.tpu_connections[self.optimizer.best()]
fn tpu_addr(&self) -> &SocketAddr {
&self.tpu_addrs[self.optimizer.best()]
}
fn rpc_client(&self) -> &RpcClient {
@@ -203,6 +205,7 @@ impl<C: 'static + TpuConnection> ThinClient<C> {
self.send_and_confirm_transaction(&[keypair], transaction, tries, 0)
}
/// Retry sending a signed Transaction to the server for processing
pub fn send_and_confirm_transaction<T: Signers>(
&self,
keypairs: &T,
@@ -212,16 +215,18 @@ impl<C: 'static + TpuConnection> ThinClient<C> {
) -> TransportResult<Signature> {
for x in 0..tries {
let now = Instant::now();
let mut buf = vec![0; serialized_size(&transaction).unwrap() as usize];
let mut wr = std::io::Cursor::new(&mut buf[..]);
let mut num_confirmed = 0;
let mut wait_time = MAX_PROCESSING_AGE;
serialize_into(&mut wr, &transaction)
.expect("serialize Transaction in pub fn transfer_signed");
// resend the same transaction until the transaction has no chance of succeeding
let wire_transaction =
bincode::serialize(&transaction).expect("transaction serialization failed");
while now.elapsed().as_secs() < wait_time as u64 {
if num_confirmed == 0 {
// Send the transaction if there has been no confirmation (e.g. the first time)
self.tpu_connection()
.send_wire_transaction(&wire_transaction)?;
self.transactions_socket
.send_to(&buf[..], &self.tpu_addr())?;
}
if let Ok(confirmed_blocks) = self.poll_for_signature_confirmation(
@@ -316,13 +321,13 @@ impl<C: 'static + TpuConnection> ThinClient<C> {
}
}
impl<C: 'static + TpuConnection> Client for ThinClient<C> {
impl Client for ThinClient {
fn tpu_addr(&self) -> String {
self.tpu_connection().tpu_addr().to_string()
self.tpu_addr().to_string()
}
}
impl<C: 'static + TpuConnection> SyncClient for ThinClient<C> {
impl SyncClient for ThinClient {
fn send_and_confirm_message<T: Signers>(
&self,
keypairs: &T,
@@ -602,21 +607,17 @@ impl<C: 'static + TpuConnection> SyncClient for ThinClient<C> {
}
}
impl<C: 'static + TpuConnection> AsyncClient for ThinClient<C> {
impl AsyncClient for ThinClient {
fn async_send_transaction(&self, transaction: Transaction) -> TransportResult<Signature> {
let transaction = VersionedTransaction::from(transaction);
self.tpu_connection()
.serialize_and_send_transaction(&transaction)?;
let mut buf = vec![0; serialized_size(&transaction).unwrap() as usize];
let mut wr = std::io::Cursor::new(&mut buf[..]);
serialize_into(&mut wr, &transaction)
.expect("serialize Transaction in pub fn transfer_signed");
assert!(buf.len() < PACKET_DATA_SIZE);
self.transactions_socket
.send_to(&buf[..], &self.tpu_addr())?;
Ok(transaction.signatures[0])
}
fn async_send_batch(&self, transactions: Vec<Transaction>) -> TransportResult<()> {
let batch: Vec<VersionedTransaction> = transactions.into_iter().map(Into::into).collect();
self.tpu_connection()
.par_serialize_and_send_transaction_batch(&batch[..])?;
Ok(())
}
fn async_send_message<T: Signers>(
&self,
keypairs: &T,
@@ -648,23 +649,20 @@ impl<C: 'static + TpuConnection> AsyncClient for ThinClient<C> {
}
}
pub fn create_client(
(rpc, tpu): (SocketAddr, SocketAddr),
range: (u16, u16),
) -> ThinClient<UdpTpuConnection> {
pub fn create_client((rpc, tpu): (SocketAddr, SocketAddr), range: (u16, u16)) -> ThinClient {
let (_, transactions_socket) =
solana_net_utils::bind_in_range(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), range).unwrap();
ThinClient::<UdpTpuConnection>::new(rpc, tpu, transactions_socket)
ThinClient::new(rpc, tpu, transactions_socket)
}
pub fn create_client_with_timeout(
(rpc, tpu): (SocketAddr, SocketAddr),
range: (u16, u16),
timeout: Duration,
) -> ThinClient<UdpTpuConnection> {
) -> ThinClient {
let (_, transactions_socket) =
solana_net_utils::bind_in_range(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), range).unwrap();
ThinClient::<UdpTpuConnection>::new_socket_with_timeout(rpc, tpu, transactions_socket, timeout)
ThinClient::new_socket_with_timeout(rpc, tpu, transactions_socket, timeout)
}
#[cfg(test)]

View File

@@ -1,40 +0,0 @@
use {
rayon::iter::{IntoParallelIterator, ParallelIterator},
solana_sdk::{transaction::VersionedTransaction, transport::Result as TransportResult},
std::net::{SocketAddr, UdpSocket},
};
pub trait TpuConnection {
fn new(client_socket: UdpSocket, tpu_addr: SocketAddr) -> Self;
fn tpu_addr(&self) -> &SocketAddr;
fn serialize_and_send_transaction(
&self,
transaction: &VersionedTransaction,
) -> TransportResult<()> {
let wire_transaction =
bincode::serialize(transaction).expect("serialize Transaction in send_batch");
self.send_wire_transaction(&wire_transaction)
}
fn send_wire_transaction<T>(&self, wire_transaction: T) -> TransportResult<()>
where
T: AsRef<[u8]>;
fn par_serialize_and_send_transaction_batch(
&self,
transactions: &[VersionedTransaction],
) -> TransportResult<()> {
let buffers = transactions
.into_par_iter()
.map(|tx| bincode::serialize(&tx).expect("serialize Transaction in send_batch"))
.collect::<Vec<_>>();
self.send_wire_transaction_batch(&buffers)
}
fn send_wire_transaction_batch<T>(&self, buffers: &[T]) -> TransportResult<()>
where
T: AsRef<[u8]>;
}

View File

@@ -1,45 +0,0 @@
//! Simple TPU client that communicates with the given UDP port with UDP and provides
//! an interface for sending transactions
use {
crate::tpu_connection::TpuConnection,
core::iter::repeat,
solana_sdk::transport::Result as TransportResult,
solana_streamer::sendmmsg::batch_send,
std::net::{SocketAddr, UdpSocket},
};
pub struct UdpTpuConnection {
socket: UdpSocket,
addr: SocketAddr,
}
impl TpuConnection for UdpTpuConnection {
fn new(client_socket: UdpSocket, tpu_addr: SocketAddr) -> Self {
Self {
socket: client_socket,
addr: tpu_addr,
}
}
fn tpu_addr(&self) -> &SocketAddr {
&self.addr
}
fn send_wire_transaction<T>(&self, wire_transaction: T) -> TransportResult<()>
where
T: AsRef<[u8]>,
{
self.socket.send_to(wire_transaction.as_ref(), self.addr)?;
Ok(())
}
fn send_wire_transaction_batch<T>(&self, buffers: &[T]) -> TransportResult<()>
where
T: AsRef<[u8]>,
{
let pkts: Vec<_> = buffers.iter().zip(repeat(self.tpu_addr())).collect();
batch_send(&self.socket, &pkts)?;
Ok(())
}
}

View File

@@ -1,7 +1,7 @@
[package]
name = "solana-core"
description = "Blockchain, Rebuilt for Scale"
version = "1.10.6"
version = "1.10.0"
homepage = "https://solana.com/"
documentation = "https://docs.rs/solana-core"
readme = "../README.md"
@@ -15,69 +15,73 @@ codecov = { repository = "solana-labs/solana", branch = "master", service = "git
[dependencies]
ahash = "0.7.6"
base64 = "0.13.0"
base64 = "0.12.3"
bincode = "1.3.3"
bs58 = "0.4.0"
chrono = { version = "0.4.11", features = ["serde"] }
crossbeam-channel = "0.5"
dashmap = { version = "4.0.2", features = ["rayon", "raw-api"] }
etcd-client = { version = "0.8.4", features = ["tls"] }
etcd-client = { version = "0.8.3", features = ["tls"]}
fs_extra = "1.2.0"
histogram = "0.6.9"
itertools = "0.10.3"
log = "0.4.14"
lru = "0.7.3"
lru = "0.7.2"
rand = "0.7.0"
rand_chacha = "0.2.2"
rayon = "1.5.1"
retain_mut = "0.1.7"
retain_mut = "0.1.5"
serde = "1.0.136"
serde_derive = "1.0.103"
solana-address-lookup-table-program = { path = "../programs/address-lookup-table", version = "=1.10.6" }
solana-bloom = { path = "../bloom", version = "=1.10.6" }
solana-client = { path = "../client", version = "=1.10.6" }
solana-entry = { path = "../entry", version = "=1.10.6" }
solana-frozen-abi = { path = "../frozen-abi", version = "=1.10.6" }
solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.10.6" }
solana-geyser-plugin-manager = { path = "../geyser-plugin-manager", version = "=1.10.6" }
solana-gossip = { path = "../gossip", version = "=1.10.6" }
solana-ledger = { path = "../ledger", version = "=1.10.6" }
solana-measure = { path = "../measure", version = "=1.10.6" }
solana-metrics = { path = "../metrics", version = "=1.10.6" }
solana-net-utils = { path = "../net-utils", version = "=1.10.6" }
solana-perf = { path = "../perf", version = "=1.10.6" }
solana-poh = { path = "../poh", version = "=1.10.6" }
solana-program-runtime = { path = "../program-runtime", version = "=1.10.6" }
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.10.6" }
solana-replica-lib = { path = "../replica-lib", version = "=1.10.6" }
solana-rpc = { path = "../rpc", version = "=1.10.6" }
solana-runtime = { path = "../runtime", version = "=1.10.6" }
solana-sdk = { path = "../sdk", version = "=1.10.6" }
solana-send-transaction-service = { path = "../send-transaction-service", version = "=1.10.6" }
solana-streamer = { path = "../streamer", version = "=1.10.6" }
solana-transaction-status = { path = "../transaction-status", version = "=1.10.6" }
solana-vote-program = { path = "../programs/vote", version = "=1.10.6" }
sys-info = "0.9.1"
solana-address-lookup-table-program = { path = "../programs/address-lookup-table", version = "=1.10.0" }
solana-bloom = { path = "../bloom", version = "=1.10.0" }
solana-accountsdb-plugin-manager = { path = "../accountsdb-plugin-manager", version = "=1.10.0" }
solana-client = { path = "../client", version = "=1.10.0" }
solana-entry = { path = "../entry", version = "=1.10.0" }
solana-gossip = { path = "../gossip", version = "=1.10.0" }
solana-ledger = { path = "../ledger", version = "=1.10.0" }
solana-measure = { path = "../measure", version = "=1.10.0" }
solana-metrics = { path = "../metrics", version = "=1.10.0" }
solana-net-utils = { path = "../net-utils", version = "=1.10.0" }
solana-perf = { path = "../perf", version = "=1.10.0" }
solana-poh = { path = "../poh", version = "=1.10.0" }
solana-program-runtime = { path = "../program-runtime", version = "=1.10.0" }
solana-rpc = { path = "../rpc", version = "=1.10.0" }
solana-replica-lib = { path = "../replica-lib", version = "=1.10.0" }
solana-runtime = { path = "../runtime", version = "=1.10.0" }
solana-sdk = { path = "../sdk", version = "=1.10.0" }
solana-frozen-abi = { path = "../frozen-abi", version = "=1.10.0" }
solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.10.0" }
solana-send-transaction-service = { path = "../send-transaction-service", version = "=1.10.0" }
solana-streamer = { path = "../streamer", version = "=1.10.0" }
solana-transaction-status = { path = "../transaction-status", version = "=1.10.0" }
solana-vote-program = { path = "../programs/vote", version = "=1.10.0" }
tempfile = "3.3.0"
thiserror = "1.0"
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.10.0" }
sys-info = "0.9.1"
tokio = { version = "1", features = ["full"] }
trees = "0.4.2"
[dev-dependencies]
jsonrpc-core = "18.0.0"
jsonrpc-core-client = { version = "18.0.0", features = ["ipc", "ws"] }
jsonrpc-derive = "18.0.0"
jsonrpc-pubsub = "18.0.0"
matches = "0.1.9"
raptorq = "1.6.5"
reqwest = { version = "0.11.10", default-features = false, features = ["blocking", "rustls-tls", "json"] }
serde_json = "1.0.79"
serial_test = "0.6.0"
solana-logger = { path = "../logger", version = "=1.10.6" }
solana-program-runtime = { path = "../program-runtime", version = "=1.10.6" }
solana-stake-program = { path = "../programs/stake", version = "=1.10.6" }
solana-version = { path = "../version", version = "=1.10.6" }
reqwest = { version = "0.11.9", default-features = false, features = ["blocking", "rustls-tls", "json"] }
serde_json = "1.0.78"
serial_test = "0.5.1"
solana-logger = { path = "../logger", version = "=1.10.0" }
solana-program-runtime = { path = "../program-runtime", version = "=1.10.0" }
solana-stake-program = { path = "../programs/stake", version = "=1.10.0" }
solana-version = { path = "../version", version = "=1.10.0" }
static_assertions = "1.1.0"
systemstat = "0.1.10"
[target."cfg(unix)".dependencies]
sysctl = "0.4.4"
sysctl = "0.4.3"
[build-dependencies]
rustc_version = "0.4"

View File

@@ -12,7 +12,6 @@ use {
banking_stage::{BankingStage, BankingStageStats},
leader_slot_banking_stage_metrics::LeaderSlotMetricsTracker,
qos_service::QosService,
unprocessed_packet_batches::*,
},
solana_entry::entry::{next_hash, Entry},
solana_gossip::cluster_info::{ClusterInfo, Node},
@@ -37,6 +36,7 @@ use {
},
solana_streamer::socket::SocketAddrSpace,
std::{
collections::VecDeque,
sync::{atomic::Ordering, Arc, RwLock},
time::{Duration, Instant},
},
@@ -79,14 +79,10 @@ fn bench_consume_buffered(bencher: &mut Bencher) {
let len = 4096;
let chunk_size = 1024;
let batches = to_packet_batches(&vec![tx; len], chunk_size);
let mut packet_batches = UnprocessedPacketBatches::new();
let mut packet_batches = VecDeque::new();
for batch in batches {
let batch_len = batch.packets.len();
packet_batches.push_back(DeserializedPacketBatch::new(
batch,
vec![0usize; batch_len],
false,
));
packet_batches.push_back((batch, vec![0usize; batch_len], false));
}
let (s, _r) = unbounded();
// This tests the performance of buffering packets.

View File

@@ -1,112 +0,0 @@
#![feature(test)]
extern crate test;
use {
rand::{seq::SliceRandom, Rng},
solana_core::{
cluster_nodes::{make_test_cluster, new_cluster_nodes, ClusterNodes},
retransmit_stage::RetransmitStage,
},
solana_gossip::contact_info::ContactInfo,
solana_sdk::{clock::Slot, hash::hashv, pubkey::Pubkey, signature::Signature},
test::Bencher,
};
const NUM_SIMULATED_SHREDS: usize = 4;
fn make_cluster_nodes<R: Rng>(
rng: &mut R,
unstaked_ratio: Option<(u32, u32)>,
) -> (Vec<ContactInfo>, ClusterNodes<RetransmitStage>) {
let (nodes, stakes, cluster_info) = make_test_cluster(rng, 5_000, unstaked_ratio);
let cluster_nodes = new_cluster_nodes::<RetransmitStage>(&cluster_info, &stakes);
(nodes, cluster_nodes)
}
fn get_retransmit_peers_deterministic(
cluster_nodes: &ClusterNodes<RetransmitStage>,
slot: &Slot,
slot_leader: &Pubkey,
num_simulated_shreds: usize,
) {
for i in 0..num_simulated_shreds {
// see Shred::seed
let shred_seed = hashv(&[
&slot.to_le_bytes(),
&(i as u32).to_le_bytes(),
&slot_leader.to_bytes(),
])
.to_bytes();
let (_neighbors, _children) = cluster_nodes.get_retransmit_peers_deterministic(
shred_seed,
solana_gossip::cluster_info::DATA_PLANE_FANOUT,
*slot_leader,
);
}
}
fn get_retransmit_peers_compat(
cluster_nodes: &ClusterNodes<RetransmitStage>,
slot_leader: &Pubkey,
signatures: &[Signature],
) {
for signature in signatures.iter() {
// see Shred::seed
let signature = signature.as_ref();
let offset = signature.len().checked_sub(32).unwrap();
let shred_seed = signature[offset..].try_into().unwrap();
let (_neighbors, _children) = cluster_nodes.get_retransmit_peers_compat(
shred_seed,
solana_gossip::cluster_info::DATA_PLANE_FANOUT,
*slot_leader,
);
}
}
fn get_retransmit_peers_deterministic_wrapper(b: &mut Bencher, unstaked_ratio: Option<(u32, u32)>) {
let mut rng = rand::thread_rng();
let (nodes, cluster_nodes) = make_cluster_nodes(&mut rng, unstaked_ratio);
let slot_leader = nodes[1..].choose(&mut rng).unwrap().id;
let slot = rand::random::<u64>();
b.iter(|| {
get_retransmit_peers_deterministic(
&cluster_nodes,
&slot,
&slot_leader,
NUM_SIMULATED_SHREDS,
)
});
}
fn get_retransmit_peers_compat_wrapper(b: &mut Bencher, unstaked_ratio: Option<(u32, u32)>) {
let mut rng = rand::thread_rng();
let (nodes, cluster_nodes) = make_cluster_nodes(&mut rng, unstaked_ratio);
let slot_leader = nodes[1..].choose(&mut rng).unwrap().id;
let signatures: Vec<_> = std::iter::repeat_with(Signature::new_unique)
.take(NUM_SIMULATED_SHREDS)
.collect();
b.iter(|| get_retransmit_peers_compat(&cluster_nodes, &slot_leader, &signatures));
}
#[bench]
fn bench_get_retransmit_peers_deterministic_unstaked_ratio_1_2(b: &mut Bencher) {
get_retransmit_peers_deterministic_wrapper(b, Some((1, 2)));
}
#[bench]
fn bench_get_retransmit_peers_compat_unstaked_ratio_1_2(b: &mut Bencher) {
get_retransmit_peers_compat_wrapper(b, Some((1, 2)));
}
#[bench]
fn bench_get_retransmit_peers_deterministic_unstaked_ratio_1_32(b: &mut Bencher) {
get_retransmit_peers_deterministic_wrapper(b, Some((1, 32)));
}
#[bench]
fn bench_get_retransmit_peers_compat_unstaked_ratio_1_32(b: &mut Bencher) {
get_retransmit_peers_compat_wrapper(b, Some((1, 32)));
}

View File

@@ -159,7 +159,7 @@ fn bench_sigverify_stage(bencher: &mut Bencher) {
for _ in 0..batches.len() {
if let Some(batch) = batches.pop() {
sent_len += batch.packets.len();
packet_s.send(vec![batch]).unwrap();
packet_s.send(batch).unwrap();
}
}
let mut received = 0;

View File

@@ -3,7 +3,6 @@ use {
cluster_slots::ClusterSlots,
duplicate_repair_status::{DeadSlotAncestorRequestStatus, DuplicateAncestorDecision},
outstanding_requests::OutstandingRequests,
packet_threshold::DynamicPacketToProcessThreshold,
repair_response::{self},
repair_service::{DuplicateSlotsResetSender, RepairInfo, RepairStatsGroup},
replay_stage::DUPLICATE_THRESHOLD,
@@ -13,6 +12,7 @@ use {
crossbeam_channel::{unbounded, Receiver, Sender},
dashmap::{mapref::entry::Entry::Occupied, DashMap},
solana_ledger::{blockstore::Blockstore, shred::SIZE_OF_NONCE},
solana_measure::measure::Measure,
solana_perf::{
packet::{limited_deserialize, Packet, PacketBatch},
recycler::Recycler,
@@ -208,7 +208,7 @@ impl AncestorHashesService {
.spawn(move || {
let mut last_stats_report = Instant::now();
let mut stats = AncestorHashesResponsesStats::default();
let mut packet_threshold = DynamicPacketToProcessThreshold::default();
let mut max_packets = 1024;
loop {
let result = Self::process_new_packets_from_channel(
&ancestor_hashes_request_statuses,
@@ -216,13 +216,13 @@ impl AncestorHashesService {
&blockstore,
&outstanding_requests,
&mut stats,
&mut packet_threshold,
&mut max_packets,
&duplicate_slots_reset_sender,
&retryable_slots_sender,
);
match result {
Err(Error::RecvTimeout(_)) | Ok(_) => {}
Err(err) => info!("ancestors hashes responses listener error: {:?}", err),
Err(err) => info!("ancestors hashes reponses listener error: {:?}", err),
};
if exit.load(Ordering::Relaxed) {
return;
@@ -243,7 +243,7 @@ impl AncestorHashesService {
blockstore: &Blockstore,
outstanding_requests: &RwLock<OutstandingAncestorHashesRepairs>,
stats: &mut AncestorHashesResponsesStats,
packet_threshold: &mut DynamicPacketToProcessThreshold,
max_packets: &mut usize,
duplicate_slots_reset_sender: &DuplicateSlotsResetSender,
retryable_slots_sender: &RetryableSlotsSender,
) -> Result<()> {
@@ -254,17 +254,18 @@ impl AncestorHashesService {
let mut dropped_packets = 0;
while let Ok(batch) = response_receiver.try_recv() {
total_packets += batch.packets.len();
if packet_threshold.should_drop(total_packets) {
dropped_packets += batch.packets.len();
} else {
if total_packets < *max_packets {
// Drop the rest in the channel in case of DOS
packet_batches.push(batch);
} else {
dropped_packets += batch.packets.len();
}
}
stats.dropped_packets += dropped_packets;
stats.total_packets += total_packets;
let timer = Instant::now();
let mut time = Measure::start("ancestor_hashes::handle_packets");
for packet_batch in packet_batches {
Self::process_packet_batch(
ancestor_hashes_request_statuses,
@@ -276,7 +277,14 @@ impl AncestorHashesService {
retryable_slots_sender,
);
}
packet_threshold.update(total_packets, timer.elapsed());
time.stop();
if total_packets >= *max_packets {
if time.as_ms() > 1000 {
*max_packets = (*max_packets * 9) / 10;
} else {
*max_packets = (*max_packets * 10) / 9;
}
}
Ok(())
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,14 +2,12 @@ use {
crate::{broadcast_stage::BroadcastStage, retransmit_stage::RetransmitStage},
itertools::Itertools,
lru::LruCache,
rand::{seq::SliceRandom, Rng, SeedableRng},
rand::SeedableRng,
rand_chacha::ChaChaRng,
solana_gossip::{
cluster_info::{compute_retransmit_peers, ClusterInfo},
contact_info::ContactInfo,
crds::GossipRoute,
crds_gossip_pull::CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS,
crds_value::{CrdsData, CrdsValue},
weighted_shuffle::{weighted_best, weighted_shuffle, WeightedShuffle},
},
solana_ledger::shred::Shred,
@@ -18,7 +16,6 @@ use {
clock::{Epoch, Slot},
feature_set,
pubkey::Pubkey,
signature::Keypair,
timing::timestamp,
},
solana_streamer::socket::SocketAddrSpace,
@@ -26,7 +23,6 @@ use {
any::TypeId,
cmp::Reverse,
collections::HashMap,
iter::repeat_with,
marker::PhantomData,
net::SocketAddr,
ops::Deref,
@@ -43,7 +39,7 @@ enum NodeId {
Pubkey(Pubkey),
}
pub struct Node {
struct Node {
node: NodeId,
stake: u64,
}
@@ -237,18 +233,6 @@ impl ClusterNodes<RetransmitStage> {
if !enable_turbine_peers_shuffle_patch(shred.slot(), root_bank) {
return self.get_retransmit_peers_compat(shred_seed, fanout, slot_leader);
}
self.get_retransmit_peers_deterministic(shred_seed, fanout, slot_leader)
}
pub fn get_retransmit_peers_deterministic(
&self,
shred_seed: [u8; 32],
fanout: usize,
slot_leader: Pubkey,
) -> (
Vec<&Node>, // neighbors
Vec<&Node>, // children
) {
let mut weighted_shuffle = self.weighted_shuffle.clone();
// Exclude slot leader from list of nodes.
if slot_leader == self.pubkey {
@@ -272,7 +256,7 @@ impl ClusterNodes<RetransmitStage> {
(neighbors, children)
}
pub fn get_retransmit_peers_compat(
fn get_retransmit_peers_compat(
&self,
shred_seed: [u8; 32],
fanout: usize,
@@ -313,7 +297,7 @@ impl ClusterNodes<RetransmitStage> {
}
}
pub fn new_cluster_nodes<T: 'static>(
fn new_cluster_nodes<T: 'static>(
cluster_info: &ClusterInfo,
stakes: &HashMap<Pubkey, u64>,
) -> ClusterNodes<T> {
@@ -326,7 +310,7 @@ pub fn new_cluster_nodes<T: 'static>(
.collect();
let broadcast = TypeId::of::<T>() == TypeId::of::<BroadcastStage>();
let stakes: Vec<u64> = nodes.iter().map(|node| node.stake).collect();
let mut weighted_shuffle = WeightedShuffle::new("cluster-nodes", &stakes);
let mut weighted_shuffle = WeightedShuffle::new(&stakes).unwrap();
if broadcast {
weighted_shuffle.remove_index(index[&self_pubkey]);
}
@@ -478,61 +462,22 @@ impl From<Pubkey> for NodeId {
}
}
pub fn make_test_cluster<R: Rng>(
rng: &mut R,
num_nodes: usize,
unstaked_ratio: Option<(u32, u32)>,
) -> (
Vec<ContactInfo>,
HashMap<Pubkey, u64>, // stakes
ClusterInfo,
) {
let (unstaked_numerator, unstaked_denominator) = unstaked_ratio.unwrap_or((1, 7));
let mut nodes: Vec<_> = repeat_with(|| ContactInfo::new_rand(rng, None))
.take(num_nodes)
.collect();
nodes.shuffle(rng);
let this_node = nodes[0].clone();
let mut stakes: HashMap<Pubkey, u64> = nodes
.iter()
.filter_map(|node| {
if rng.gen_ratio(unstaked_numerator, unstaked_denominator) {
None // No stake for some of the nodes.
} else {
Some((node.id, rng.gen_range(0, 20)))
}
})
.collect();
// Add some staked nodes with no contact-info.
stakes.extend(repeat_with(|| (Pubkey::new_unique(), rng.gen_range(0, 20))).take(100));
let cluster_info = ClusterInfo::new(
this_node,
Arc::new(Keypair::new()),
SocketAddrSpace::Unspecified,
);
{
let now = timestamp();
let mut gossip_crds = cluster_info.gossip.crds.write().unwrap();
// First node is pushed to crds table by ClusterInfo constructor.
for node in nodes.iter().skip(1) {
let node = CrdsData::ContactInfo(node.clone());
let node = CrdsValue::new_unsigned(node);
assert_eq!(
gossip_crds.insert(node, now, GossipRoute::LocalMessage),
Ok(())
);
}
}
(nodes, stakes, cluster_info)
}
#[cfg(test)]
mod tests {
use {
super::*,
solana_gossip::deprecated::{
shuffle_peers_and_index, sorted_retransmit_peers_and_stakes, sorted_stakes_with_index,
rand::{seq::SliceRandom, Rng},
solana_gossip::{
crds::GossipRoute,
crds_value::{CrdsData, CrdsValue},
deprecated::{
shuffle_peers_and_index, sorted_retransmit_peers_and_stakes,
sorted_stakes_with_index,
},
},
solana_sdk::{signature::Keypair, timing::timestamp},
solana_streamer::socket::SocketAddrSpace,
std::{iter::repeat_with, sync::Arc},
};
// Legacy methods copied for testing backward compatibility.
@@ -554,10 +499,55 @@ mod tests {
sorted_stakes_with_index(peers, stakes)
}
fn make_cluster<R: Rng>(
rng: &mut R,
) -> (
Vec<ContactInfo>,
HashMap<Pubkey, u64>, // stakes
ClusterInfo,
) {
let mut nodes: Vec<_> = repeat_with(|| ContactInfo::new_rand(rng, None))
.take(1000)
.collect();
nodes.shuffle(rng);
let this_node = nodes[0].clone();
let mut stakes: HashMap<Pubkey, u64> = nodes
.iter()
.filter_map(|node| {
if rng.gen_ratio(1, 7) {
None // No stake for some of the nodes.
} else {
Some((node.id, rng.gen_range(0, 20)))
}
})
.collect();
// Add some staked nodes with no contact-info.
stakes.extend(repeat_with(|| (Pubkey::new_unique(), rng.gen_range(0, 20))).take(100));
let cluster_info = ClusterInfo::new(
this_node,
Arc::new(Keypair::new()),
SocketAddrSpace::Unspecified,
);
{
let now = timestamp();
let mut gossip_crds = cluster_info.gossip.crds.write().unwrap();
// First node is pushed to crds table by ClusterInfo constructor.
for node in nodes.iter().skip(1) {
let node = CrdsData::ContactInfo(node.clone());
let node = CrdsValue::new_unsigned(node);
assert_eq!(
gossip_crds.insert(node, now, GossipRoute::LocalMessage),
Ok(())
);
}
}
(nodes, stakes, cluster_info)
}
#[test]
fn test_cluster_nodes_retransmit() {
let mut rng = rand::thread_rng();
let (nodes, stakes, cluster_info) = make_test_cluster(&mut rng, 1_000, None);
let (nodes, stakes, cluster_info) = make_cluster(&mut rng);
let this_node = cluster_info.my_contact_info();
// ClusterInfo::tvu_peers excludes the node itself.
assert_eq!(cluster_info.tvu_peers().len(), nodes.len() - 1);
@@ -638,7 +628,7 @@ mod tests {
#[test]
fn test_cluster_nodes_broadcast() {
let mut rng = rand::thread_rng();
let (nodes, stakes, cluster_info) = make_test_cluster(&mut rng, 1_000, None);
let (nodes, stakes, cluster_info) = make_cluster(&mut rng);
// ClusterInfo::tvu_peers excludes the node itself.
assert_eq!(cluster_info.tvu_peers().len(), nodes.len() - 1);
let cluster_nodes = ClusterNodes::<BroadcastStage>::new(&cluster_info, &stakes);

View File

@@ -16,24 +16,39 @@ use {
},
};
// Update blockstore persistence storage when accumulated cost_table updates count exceeds the threshold
const PERSIST_THRESHOLD: u64 = 1_000;
#[derive(Default)]
pub struct CostUpdateServiceTiming {
last_print: u64,
update_cost_model_count: u64,
update_cost_model_elapsed: u64,
persist_cost_table_elapsed: u64,
}
impl CostUpdateServiceTiming {
fn update(&mut self, update_cost_model_count: u64, update_cost_model_elapsed: u64) {
self.update_cost_model_count += update_cost_model_count;
self.update_cost_model_elapsed += update_cost_model_elapsed;
fn update(
&mut self,
update_cost_model_count: Option<u64>,
update_cost_model_elapsed: Option<u64>,
persist_cost_table_elapsed: Option<u64>,
) {
if let Some(update_cost_model_count) = update_cost_model_count {
self.update_cost_model_count += update_cost_model_count;
}
if let Some(update_cost_model_elapsed) = update_cost_model_elapsed {
self.update_cost_model_elapsed += update_cost_model_elapsed;
}
if let Some(persist_cost_table_elapsed) = persist_cost_table_elapsed {
self.persist_cost_table_elapsed += persist_cost_table_elapsed;
}
let now = timestamp();
let elapsed_ms = now - self.last_print;
if elapsed_ms > 1000 {
datapoint_info!(
"cost-update-service-stats",
("total_elapsed_us", elapsed_ms * 1000, i64),
(
"update_cost_model_count",
self.update_cost_model_count as i64,
@@ -44,6 +59,11 @@ impl CostUpdateServiceTiming {
self.update_cost_model_elapsed as i64,
i64
),
(
"persist_cost_table_elapsed",
self.persist_cost_table_elapsed as i64,
i64
),
);
*self = CostUpdateServiceTiming::default();
@@ -89,11 +109,13 @@ impl CostUpdateService {
}
fn service_loop(
_blockstore: Arc<Blockstore>,
blockstore: Arc<Blockstore>,
cost_model: Arc<RwLock<CostModel>>,
cost_update_receiver: CostUpdateReceiver,
) {
let mut cost_update_service_timing = CostUpdateServiceTiming::default();
let mut update_count = 0_u64;
for cost_update in cost_update_receiver.iter() {
match cost_update {
CostUpdate::FrozenBank { bank } => {
@@ -102,17 +124,33 @@ impl CostUpdateService {
CostUpdate::ExecuteTiming {
mut execute_timings,
} => {
let (update_count, update_cost_model_time) = Measure::this(
|_| Self::update_cost_model(&cost_model, &mut execute_timings),
(),
"update_cost_model_time",
let mut update_cost_model_time = Measure::start("update_cost_model_time");
update_count += Self::update_cost_model(&cost_model, &mut execute_timings);
update_cost_model_time.stop();
cost_update_service_timing.update(
Some(update_count),
Some(update_cost_model_time.as_us()),
None,
);
cost_update_service_timing.update(update_count, update_cost_model_time.as_us());
if update_count > PERSIST_THRESHOLD {
let mut persist_cost_table_time = Measure::start("persist_cost_table_time");
Self::persist_cost_table(&blockstore, &cost_model);
update_count = 0_u64;
persist_cost_table_time.stop();
cost_update_service_timing.update(
None,
None,
Some(persist_cost_table_time.as_us()),
);
}
}
}
}
}
// Normalize `program_timings` with current estimated cost, update instruction_cost table
// Returns number of updates applied
fn update_cost_model(
cost_model: &RwLock<CostModel>,
execute_timings: &mut ExecuteTimings,
@@ -133,9 +171,38 @@ impl CostUpdateService {
.unwrap()
.upsert_instruction_cost(program_id, units);
update_count += 1;
debug!(
"After replayed into bank, updated cost for instruction {:?}, update_value {}, pre_aggregated_value {}",
program_id, units, current_estimated_program_cost
);
}
update_count
}
// 1. Remove obsolete program entries from persisted table to limit its size
// 2. Update persisted program cost. This involves EMA cost calculation at
// execute_cost_table.get_cost()
fn persist_cost_table(blockstore: &Blockstore, cost_model: &RwLock<CostModel>) {
let db_records = blockstore.read_program_costs().expect("read programs");
let cost_model = cost_model.read().unwrap();
let active_program_keys = cost_model.get_program_keys();
// delete records from blockstore if they are no longer in cost_table
db_records.iter().for_each(|(pubkey, _)| {
if !active_program_keys.contains(&pubkey) {
blockstore
.delete_program_cost(pubkey)
.expect("delete old program");
}
});
active_program_keys.iter().for_each(|program_id| {
let cost = cost_model.find_instruction_cost(program_id);
blockstore
.write_program_cost(program_id, &cost)
.expect("persist program costs to blockstore");
});
}
}
#[cfg(test)]
@@ -146,10 +213,9 @@ mod tests {
fn test_update_cost_model_with_empty_execute_timings() {
let cost_model = Arc::new(RwLock::new(CostModel::default()));
let mut empty_execute_timings = ExecuteTimings::default();
assert_eq!(
0,
CostUpdateService::update_cost_model(&cost_model, &mut empty_execute_timings),
0
);
}
@@ -167,7 +233,7 @@ mod tests {
let accumulated_units: u64 = 100;
let total_errored_units = 0;
let count: u32 = 10;
expected_cost = accumulated_units / count as u64;
expected_cost = accumulated_units / count as u64; // = 10
execute_timings.details.per_program_timings.insert(
program_key_1,
@@ -179,10 +245,9 @@ mod tests {
total_errored_units,
},
);
assert_eq!(
1,
CostUpdateService::update_cost_model(&cost_model, &mut execute_timings),
);
let update_count =
CostUpdateService::update_cost_model(&cost_model, &mut execute_timings);
assert_eq!(1, update_count);
assert_eq!(
expected_cost,
cost_model
@@ -197,8 +262,8 @@ mod tests {
let accumulated_us: u64 = 2000;
let accumulated_units: u64 = 200;
let count: u32 = 10;
// to expect new cost is Average(new_value, existing_value)
expected_cost = ((accumulated_units / count as u64) + expected_cost) / 2;
// to expect new cost = (mean + 2 * std) of [10, 20]
expected_cost = 13;
execute_timings.details.per_program_timings.insert(
program_key_1,
@@ -210,10 +275,9 @@ mod tests {
total_errored_units: 0,
},
);
assert_eq!(
1,
CostUpdateService::update_cost_model(&cost_model, &mut execute_timings),
);
let update_count =
CostUpdateService::update_cost_model(&cost_model, &mut execute_timings);
assert_eq!(1, update_count);
assert_eq!(
expected_cost,
cost_model
@@ -264,10 +328,9 @@ mod tests {
total_errored_units: 0,
},
);
assert_eq!(
CostUpdateService::update_cost_model(&cost_model, &mut execute_timings),
1
);
let update_count =
CostUpdateService::update_cost_model(&cost_model, &mut execute_timings);
assert_eq!(1, update_count);
assert_eq!(
current_program_cost,
cost_model
@@ -281,8 +344,12 @@ mod tests {
// greater than the current instruction cost for the program. Should update with the
// new erroring compute costs
let cost_per_error = 1000;
// the expect cost is (previous_cost + new_cost)/2 = (100 + 1000)/2 = 550
let expected_units = 550;
// expected_cost = (mean + 2*std) of data points:
// [
// 100, // original program_cost
// 1000, // cost_per_error
// ]
let expected_cost = 289u64;
{
let errored_txs_compute_consumed = vec![cost_per_error; 3];
let total_errored_units = errored_txs_compute_consumed.iter().sum();
@@ -296,12 +363,12 @@ mod tests {
total_errored_units,
},
);
let update_count =
CostUpdateService::update_cost_model(&cost_model, &mut execute_timings);
assert_eq!(1, update_count);
assert_eq!(
CostUpdateService::update_cost_model(&cost_model, &mut execute_timings),
1
);
assert_eq!(
expected_units,
expected_cost,
cost_model
.read()
.unwrap()
@@ -312,7 +379,7 @@ mod tests {
// Test updating cost model with only erroring compute costs where the error cost is
// `smaller_cost_per_error`, less than the current instruction cost for the program.
// The cost should not decrease for these new lesser errors
let smaller_cost_per_error = expected_units - 10;
let smaller_cost_per_error = expected_cost - 10;
{
let errored_txs_compute_consumed = vec![smaller_cost_per_error; 3];
let total_errored_units = errored_txs_compute_consumed.iter().sum();
@@ -326,12 +393,19 @@ mod tests {
total_errored_units,
},
);
let update_count =
CostUpdateService::update_cost_model(&cost_model, &mut execute_timings);
// expected_cost = (mean = 2*std) of data points:
// [
// 100, // original program cost,
// 1000, // cost_per_error from above test
// 289, // the smaller_cost_per_error will be coalesced to prev cost
// ]
let expected_cost = 293u64;
assert_eq!(1, update_count);
assert_eq!(
CostUpdateService::update_cost_model(&cost_model, &mut execute_timings),
1
);
assert_eq!(
expected_units,
expected_cost,
cost_model
.read()
.unwrap()

View File

@@ -1,185 +0,0 @@
use {
crossbeam_channel::{Receiver, RecvTimeoutError, Sender},
rayon::{prelude::*, ThreadPool},
solana_gossip::cluster_info::ClusterInfo,
solana_measure::measure::Measure,
solana_perf::packet::PacketBatch,
solana_rayon_threadlimit::get_thread_count,
solana_runtime::bank_forks::BankForks,
solana_sdk::timing::timestamp,
solana_streamer::streamer::{self, StreamerError},
std::{
cell::RefCell,
collections::HashMap,
net::IpAddr,
sync::{Arc, RwLock},
thread::{self, Builder, JoinHandle},
time::{Duration, Instant},
},
};
const IP_TO_STAKE_REFRESH_DURATION: Duration = Duration::from_secs(5);
thread_local!(static PAR_THREAD_POOL: RefCell<ThreadPool> = RefCell::new(rayon::ThreadPoolBuilder::new()
.num_threads(get_thread_count())
.thread_name(|ix| format!("transaction_sender_stake_stage_{}", ix))
.build()
.unwrap()));
pub type FindPacketSenderStakeSender = Sender<Vec<PacketBatch>>;
pub type FindPacketSenderStakeReceiver = Receiver<Vec<PacketBatch>>;
#[derive(Debug, Default)]
struct FindPacketSenderStakeStats {
last_print: u64,
refresh_ip_to_stake_time: u64,
apply_sender_stakes_time: u64,
send_batches_time: u64,
receive_batches_time: u64,
total_batches: u64,
total_packets: u64,
}
impl FindPacketSenderStakeStats {
fn report(&mut self) {
let now = timestamp();
let elapsed_ms = now - self.last_print;
if elapsed_ms > 2000 {
datapoint_info!(
"find_packet_sender_stake-services_stats",
(
"refresh_ip_to_stake_time",
self.refresh_ip_to_stake_time as i64,
i64
),
(
"apply_sender_stakes_time",
self.apply_sender_stakes_time as i64,
i64
),
("send_batches_time", self.send_batches_time as i64, i64),
(
"receive_batches_time",
self.receive_batches_time as i64,
i64
),
("total_batches", self.total_batches as i64, i64),
("total_packets", self.total_packets as i64, i64),
);
*self = FindPacketSenderStakeStats::default();
self.last_print = now;
}
}
}
pub struct FindPacketSenderStakeStage {
thread_hdl: JoinHandle<()>,
}
impl FindPacketSenderStakeStage {
pub fn new(
packet_receiver: streamer::PacketBatchReceiver,
sender: FindPacketSenderStakeSender,
bank_forks: Arc<RwLock<BankForks>>,
cluster_info: Arc<ClusterInfo>,
) -> Self {
let mut stats = FindPacketSenderStakeStats::default();
let thread_hdl = Builder::new()
.name("find-packet-sender-stake".to_string())
.spawn(move || {
let mut last_stakes = Instant::now();
let mut ip_to_stake: HashMap<IpAddr, u64> = HashMap::new();
loop {
let mut refresh_ip_to_stake_time = Measure::start("refresh_ip_to_stake_time");
Self::try_refresh_ip_to_stake(
&mut last_stakes,
&mut ip_to_stake,
bank_forks.clone(),
cluster_info.clone(),
);
refresh_ip_to_stake_time.stop();
stats.refresh_ip_to_stake_time = stats
.refresh_ip_to_stake_time
.saturating_add(refresh_ip_to_stake_time.as_us());
match streamer::recv_packet_batches(&packet_receiver) {
Ok((mut batches, num_packets, recv_duration)) => {
let num_batches = batches.len();
let mut apply_sender_stakes_time =
Measure::start("apply_sender_stakes_time");
Self::apply_sender_stakes(&mut batches, &ip_to_stake);
apply_sender_stakes_time.stop();
let mut send_batches_time = Measure::start("send_batches_time");
if let Err(e) = sender.send(batches) {
info!("Sender error: {:?}", e);
}
send_batches_time.stop();
stats.apply_sender_stakes_time = stats
.apply_sender_stakes_time
.saturating_add(apply_sender_stakes_time.as_us());
stats.send_batches_time = stats
.send_batches_time
.saturating_add(send_batches_time.as_us());
stats.receive_batches_time = stats
.receive_batches_time
.saturating_add(recv_duration.as_nanos() as u64);
stats.total_batches =
stats.total_batches.saturating_add(num_batches as u64);
stats.total_packets =
stats.total_packets.saturating_add(num_packets as u64);
}
Err(e) => match e {
StreamerError::RecvTimeout(RecvTimeoutError::Disconnected) => break,
StreamerError::RecvTimeout(RecvTimeoutError::Timeout) => (),
_ => error!("error: {:?}", e),
},
}
stats.report();
}
})
.unwrap();
Self { thread_hdl }
}
fn try_refresh_ip_to_stake(
last_stakes: &mut Instant,
ip_to_stake: &mut HashMap<IpAddr, u64>,
bank_forks: Arc<RwLock<BankForks>>,
cluster_info: Arc<ClusterInfo>,
) {
if last_stakes.elapsed() > IP_TO_STAKE_REFRESH_DURATION {
let root_bank = bank_forks.read().unwrap().root_bank();
let staked_nodes = root_bank.staked_nodes();
*ip_to_stake = cluster_info
.tvu_peers()
.into_iter()
.filter_map(|node| {
let stake = staked_nodes.get(&node.id)?;
Some((node.tvu.ip(), *stake))
})
.collect();
*last_stakes = Instant::now();
}
}
fn apply_sender_stakes(batches: &mut [PacketBatch], ip_to_stake: &HashMap<IpAddr, u64>) {
PAR_THREAD_POOL.with(|thread_pool| {
thread_pool.borrow().install(|| {
batches
.into_par_iter()
.flat_map(|batch| batch.packets.par_iter_mut())
.for_each(|packet| {
packet.meta.sender_stake =
*ip_to_stake.get(&packet.meta.addr().ip()).unwrap_or(&0);
});
})
});
}
pub fn join(self) -> thread::Result<()> {
self.thread_hdl.join()
}
}

View File

@@ -1,44 +0,0 @@
//! The `ledger_metric_report_service` periodically reports ledger store metrics.
use {
solana_ledger::blockstore::Blockstore,
std::{
string::ToString,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
thread::{self, Builder, JoinHandle},
time::Duration,
},
};
// Determines how often we report blockstore metrics.
const BLOCKSTORE_METRICS_REPORT_PERIOD_MILLIS: u64 = 10000;
pub struct LedgerMetricReportService {
t_cf_metric: JoinHandle<()>,
}
impl LedgerMetricReportService {
pub fn new(blockstore: Arc<Blockstore>, exit: &Arc<AtomicBool>) -> Self {
let exit_signal = exit.clone();
let t_cf_metric = Builder::new()
.name("metric_report_rocksdb_cf_metrics".to_string())
.spawn(move || loop {
if exit_signal.load(Ordering::Relaxed) {
break;
}
thread::sleep(Duration::from_millis(
BLOCKSTORE_METRICS_REPORT_PERIOD_MILLIS,
));
blockstore.submit_rocksdb_cf_metrics_for_all_cfs();
})
.unwrap();
Self { t_cf_metric }
}
pub fn join(self) -> thread::Result<()> {
self.t_cf_metric.join()
}
}

View File

@@ -24,7 +24,6 @@ pub mod cost_update_service;
pub mod drop_bank_service;
pub mod duplicate_repair_status;
pub mod fetch_stage;
pub mod find_packet_sender_stake_stage;
pub mod fork_choice;
pub mod gen_keys;
pub mod heaviest_subtree_fork_choice;
@@ -32,11 +31,9 @@ pub mod latest_validator_votes_for_frozen_banks;
pub mod leader_slot_banking_stage_metrics;
pub mod leader_slot_banking_stage_timing_metrics;
pub mod ledger_cleanup_service;
pub mod ledger_metric_report_service;
pub mod optimistic_confirmation_verifier;
pub mod outstanding_requests;
pub mod packet_hasher;
pub mod packet_threshold;
pub mod progress_map;
pub mod qos_service;
pub mod repair_generic_traversal;
@@ -65,7 +62,6 @@ pub mod tpu;
pub mod tree_diff;
pub mod tvu;
pub mod unfrozen_gossip_verified_vote_hashes;
pub mod unprocessed_packet_batches;
pub mod validator;
pub mod verified_vote_packets;
pub mod vote_simulator;

View File

@@ -1,84 +0,0 @@
use std::time::Duration;
enum PacketThresholdUpdate {
Increase,
Decrease,
}
impl PacketThresholdUpdate {
const PERCENTAGE: usize = 90;
fn calculate(&self, current: usize) -> usize {
match *self {
PacketThresholdUpdate::Increase => {
current.saturating_mul(100).saturating_div(Self::PERCENTAGE)
}
PacketThresholdUpdate::Decrease => {
current.saturating_mul(Self::PERCENTAGE).saturating_div(100)
}
}
}
}
#[derive(Debug)]
pub struct DynamicPacketToProcessThreshold {
max_packets: usize,
}
impl Default for DynamicPacketToProcessThreshold {
fn default() -> Self {
Self {
max_packets: Self::DEFAULT_MAX_PACKETS,
}
}
}
impl DynamicPacketToProcessThreshold {
const DEFAULT_MAX_PACKETS: usize = 1024;
const TIME_THRESHOLD: Duration = Duration::from_secs(1);
pub fn update(&mut self, total_packets: usize, compute_time: Duration) {
if total_packets >= self.max_packets {
let threshold_update = if compute_time > Self::TIME_THRESHOLD {
PacketThresholdUpdate::Decrease
} else {
PacketThresholdUpdate::Increase
};
self.max_packets = threshold_update.calculate(self.max_packets);
}
}
pub fn should_drop(&self, total: usize) -> bool {
total >= self.max_packets
}
}
#[cfg(test)]
mod test {
use {super::DynamicPacketToProcessThreshold, std::time::Duration};
#[test]
fn test_dynamic_packet_threshold() {
let mut threshold = DynamicPacketToProcessThreshold::default();
assert_eq!(
threshold.max_packets,
DynamicPacketToProcessThreshold::DEFAULT_MAX_PACKETS
);
assert!(!threshold.should_drop(10));
assert!(threshold.should_drop(2000));
let old = threshold.max_packets;
// Increase
let total = 2000;
let compute_time = Duration::from_millis(500);
threshold.update(total, compute_time);
assert!(threshold.max_packets > old);
// Decrease
let compute_time = Duration::from_millis(2000);
threshold.update(total, compute_time);
assert_eq!(threshold.max_packets, old - 1); // due to rounding error, there is a difference of 1
}
}

View File

@@ -339,7 +339,7 @@ pub struct RetransmitInfo {
impl RetransmitInfo {
pub fn reached_retransmit_threshold(&self) -> bool {
let backoff = std::cmp::min(self.retry_iteration, RETRANSMIT_BACKOFF_CAP);
let backoff_duration_ms = (1_u64 << backoff) * RETRANSMIT_BASE_DELAY_MS;
let backoff_duration_ms = 2_u64.pow(backoff) * RETRANSMIT_BASE_DELAY_MS;
self.retry_time
.map(|time| time.elapsed().as_millis() > backoff_duration_ms.into())
.unwrap_or(true)

View File

@@ -3,7 +3,7 @@
//! how transactions are included in blocks, and optimize those blocks.
//!
use {
crate::banking_stage::BatchedTransactionDetails,
crate::banking_stage::BatchedTransactionCostDetails,
crossbeam_channel::{unbounded, Receiver, Sender},
solana_measure::measure::Measure,
solana_runtime::{
@@ -68,8 +68,8 @@ impl QosService {
let running_flag = Arc::new(AtomicBool::new(true));
let metrics = Arc::new(QosServiceMetrics::new(id));
let running_flag_clone = Arc::clone(&running_flag);
let metrics_clone = Arc::clone(&metrics);
let running_flag_clone = running_flag.clone();
let metrics_clone = metrics.clone();
let reporting_thread = Some(
Builder::new()
.name("solana-qos-service-metrics-repoting".to_string())
@@ -109,11 +109,9 @@ impl QosService {
.collect();
compute_cost_time.stop();
self.metrics
.stats
.compute_cost_time
.fetch_add(compute_cost_time.as_us(), Ordering::Relaxed);
self.metrics
.stats
.compute_cost_count
.fetch_add(txs_costs.len() as u64, Ordering::Relaxed);
txs_costs
@@ -136,7 +134,7 @@ impl QosService {
.map(|(tx, cost)| match cost_tracker.try_add(tx, cost) {
Ok(current_block_cost) => {
debug!("slot {:?}, transaction {:?}, cost {:?}, fit into current block, current block cost {}", bank.slot(), tx, cost, current_block_cost);
self.metrics.stats.selected_txs_count.fetch_add(1, Ordering::Relaxed);
self.metrics.selected_txs_count.fetch_add(1, Ordering::Relaxed);
num_included += 1;
Ok(())
},
@@ -144,19 +142,20 @@ impl QosService {
debug!("slot {:?}, transaction {:?}, cost {:?}, not fit into current block, '{:?}'", bank.slot(), tx, cost, e);
match e {
CostTrackerError::WouldExceedBlockMaxLimit => {
self.metrics.retried_txs_per_block_limit_count.fetch_add(1, Ordering::Relaxed);
Err(TransactionError::WouldExceedMaxBlockCostLimit)
}
CostTrackerError::WouldExceedVoteMaxLimit => {
self.metrics.retried_txs_per_vote_limit_count.fetch_add(1, Ordering::Relaxed);
Err(TransactionError::WouldExceedMaxVoteCostLimit)
}
CostTrackerError::WouldExceedAccountMaxLimit => {
self.metrics.retried_txs_per_account_limit_count.fetch_add(1, Ordering::Relaxed);
Err(TransactionError::WouldExceedMaxAccountCostLimit)
}
CostTrackerError::WouldExceedAccountDataBlockLimit => {
Err(TransactionError::WouldExceedAccountDataBlockLimit)
}
CostTrackerError::WouldExceedAccountDataTotalLimit => {
Err(TransactionError::WouldExceedAccountDataTotalLimit)
CostTrackerError::WouldExceedAccountDataMaxLimit => {
self.metrics.retried_txs_per_account_data_limit_count.fetch_add(1, Ordering::Relaxed);
Err(TransactionError::WouldExceedMaxAccountDataCostLimit)
}
}
}
@@ -164,7 +163,6 @@ impl QosService {
.collect();
cost_tracking_time.stop();
self.metrics
.stats
.cost_tracking_time
.fetch_add(cost_tracking_time.as_us(), Ordering::Relaxed);
(select_results, num_included)
@@ -179,82 +177,30 @@ impl QosService {
pub fn accumulate_estimated_transaction_costs(
&self,
batched_transaction_details: &BatchedTransactionDetails,
cost_details: &BatchedTransactionCostDetails,
) {
self.metrics.stats.estimated_signature_cu.fetch_add(
batched_transaction_details.costs.batched_signature_cost,
Ordering::Relaxed,
);
self.metrics.stats.estimated_write_lock_cu.fetch_add(
batched_transaction_details.costs.batched_write_lock_cost,
Ordering::Relaxed,
);
self.metrics.stats.estimated_data_bytes_cu.fetch_add(
batched_transaction_details.costs.batched_data_bytes_cost,
Ordering::Relaxed,
);
self.metrics.stats.estimated_execute_cu.fetch_add(
batched_transaction_details.costs.batched_execute_cost,
Ordering::Relaxed,
);
self.metrics
.errors
.retried_txs_per_block_limit_count
.fetch_add(
batched_transaction_details
.errors
.batched_retried_txs_per_block_limit_count,
Ordering::Relaxed,
);
.estimated_signature_cu
.fetch_add(cost_details.batched_signature_cost, Ordering::Relaxed);
self.metrics
.errors
.retried_txs_per_vote_limit_count
.fetch_add(
batched_transaction_details
.errors
.batched_retried_txs_per_vote_limit_count,
Ordering::Relaxed,
);
.estimated_write_lock_cu
.fetch_add(cost_details.batched_write_lock_cost, Ordering::Relaxed);
self.metrics
.errors
.retried_txs_per_account_limit_count
.fetch_add(
batched_transaction_details
.errors
.batched_retried_txs_per_account_limit_count,
Ordering::Relaxed,
);
.estimated_data_bytes_cu
.fetch_add(cost_details.batched_data_bytes_cost, Ordering::Relaxed);
self.metrics
.errors
.retried_txs_per_account_data_block_limit_count
.fetch_add(
batched_transaction_details
.errors
.batched_retried_txs_per_account_data_block_limit_count,
Ordering::Relaxed,
);
self.metrics
.errors
.dropped_txs_per_account_data_total_limit_count
.fetch_add(
batched_transaction_details
.errors
.batched_dropped_txs_per_account_data_total_limit_count,
Ordering::Relaxed,
);
.estimated_execute_cu
.fetch_add(cost_details.batched_execute_cost, Ordering::Relaxed);
}
pub fn accumulate_actual_execute_cu(&self, units: u64) {
self.metrics
.stats
.actual_execute_cu
.fetch_add(units, Ordering::Relaxed);
}
pub fn accumulate_actual_execute_time(&self, micro_sec: u64) {
self.metrics
.stats
.actual_execute_time_us
.fetch_add(micro_sec, Ordering::Relaxed);
}
@@ -277,75 +223,61 @@ impl QosService {
}
}
#[derive(Debug, Default)]
#[derive(Default)]
struct QosServiceMetrics {
/// banking_stage creates one QosService instance per working threads, that is uniquely
/// identified by id. This field allows to categorize metrics for gossip votes, TPU votes
/// and other transactions.
// banking_stage creates one QosService instance per working threads, that is uniquely
// identified by id. This field allows to categorize metrics for gossip votes, TPU votes
// and other transactions.
id: u32,
/// aggregate metrics per slot
// aggregate metrics per slot
slot: AtomicU64,
stats: QosServiceMetricsStats,
errors: QosServiceMetricsErrors,
}
#[derive(Debug, Default)]
struct QosServiceMetricsStats {
/// accumulated time in micro-sec spent in computing transaction cost. It is the main performance
/// overhead introduced by cost_model
// accumulated time in micro-sec spent in computing transaction cost. It is the main performance
// overhead introduced by cost_model
compute_cost_time: AtomicU64,
/// total nummber of transactions in the reporting period to be computed for theit cost. It is
/// usually the number of sanitized transactions leader receives.
// total nummber of transactions in the reporting period to be computed for theit cost. It is
// usually the number of sanitized transactions leader receives.
compute_cost_count: AtomicU64,
/// acumulated time in micro-sec spent in tracking each bank's cost. It is the second part of
/// overhead introduced
// acumulated time in micro-sec spent in tracking each bank's cost. It is the second part of
// overhead introduced
cost_tracking_time: AtomicU64,
/// number of transactions to be included in blocks
// number of transactions to be included in blocks
selected_txs_count: AtomicU64,
/// accumulated estimated signature Compute Unites to be packed into block
estimated_signature_cu: AtomicU64,
/// accumulated estimated write locks Compute Units to be packed into block
estimated_write_lock_cu: AtomicU64,
/// accumulated estimated instructino data Compute Units to be packed into block
estimated_data_bytes_cu: AtomicU64,
/// accumulated estimated program Compute Units to be packed into block
estimated_execute_cu: AtomicU64,
/// accumulated actual program Compute Units that have been packed into block
actual_execute_cu: AtomicU64,
/// accumulated actual program execute micro-sec that have been packed into block
actual_execute_time_us: AtomicU64,
}
#[derive(Debug, Default)]
struct QosServiceMetricsErrors {
/// number of transactions to be queued for retry due to their potential to breach block limit
// number of transactions to be queued for retry due to its potential to breach block limit
retried_txs_per_block_limit_count: AtomicU64,
/// number of transactions to be queued for retry due to their potential to breach vote limit
// number of transactions to be queued for retry due to its potential to breach vote limit
retried_txs_per_vote_limit_count: AtomicU64,
/// number of transactions to be queued for retry due to their potential to breach writable
/// account limit
// number of transactions to be queued for retry due to its potential to breach writable
// account limit
retried_txs_per_account_limit_count: AtomicU64,
/// number of transactions to be queued for retry due to their potential to breach account data
/// block limits
retried_txs_per_account_data_block_limit_count: AtomicU64,
// number of transactions to be queued for retry due to its account data limits
retried_txs_per_account_data_limit_count: AtomicU64,
/// number of transactions to be dropped due to their potential to breach account data total
/// limits
dropped_txs_per_account_data_total_limit_count: AtomicU64,
// accumulated estimated signature Compute Unites to be packed into block
estimated_signature_cu: AtomicU64,
// accumulated estimated write locks Compute Units to be packed into block
estimated_write_lock_cu: AtomicU64,
// accumulated estimated instructino data Compute Units to be packed into block
estimated_data_bytes_cu: AtomicU64,
// accumulated estimated program Compute Units to be packed into block
estimated_execute_cu: AtomicU64,
// accumulated actual program Compute Units that have been packed into block
actual_execute_cu: AtomicU64,
// accumulated actual program execute micro-sec that have been packed into block
actual_execute_time_us: AtomicU64,
}
impl QosServiceMetrics {
@@ -364,96 +296,76 @@ impl QosServiceMetrics {
("bank_slot", bank_slot as i64, i64),
(
"compute_cost_time",
self.stats.compute_cost_time.swap(0, Ordering::Relaxed) as i64,
self.compute_cost_time.swap(0, Ordering::Relaxed) as i64,
i64
),
(
"compute_cost_count",
self.stats.compute_cost_count.swap(0, Ordering::Relaxed) as i64,
self.compute_cost_count.swap(0, Ordering::Relaxed) as i64,
i64
),
(
"cost_tracking_time",
self.stats.cost_tracking_time.swap(0, Ordering::Relaxed) as i64,
self.cost_tracking_time.swap(0, Ordering::Relaxed) as i64,
i64
),
(
"selected_txs_count",
self.stats.selected_txs_count.swap(0, Ordering::Relaxed) as i64,
self.selected_txs_count.swap(0, Ordering::Relaxed) as i64,
i64
),
(
"estimated_signature_cu",
self.stats.estimated_signature_cu.swap(0, Ordering::Relaxed) as i64,
i64
),
(
"estimated_write_lock_cu",
self.stats
.estimated_write_lock_cu
.swap(0, Ordering::Relaxed) as i64,
i64
),
(
"estimated_data_bytes_cu",
self.stats
.estimated_data_bytes_cu
.swap(0, Ordering::Relaxed) as i64,
i64
),
(
"estimated_execute_cu",
self.stats.estimated_execute_cu.swap(0, Ordering::Relaxed) as i64,
i64
),
(
"actual_execute_cu",
self.stats.actual_execute_cu.swap(0, Ordering::Relaxed) as i64,
i64
),
(
"actual_execute_time_us",
self.stats.actual_execute_time_us.swap(0, Ordering::Relaxed) as i64,
i64
),
);
datapoint_info!(
"qos-service-errors",
("id", self.id as i64, i64),
("bank_slot", bank_slot as i64, i64),
(
"retried_txs_per_block_limit_count",
self.errors
.retried_txs_per_block_limit_count
self.retried_txs_per_block_limit_count
.swap(0, Ordering::Relaxed) as i64,
i64
),
(
"retried_txs_per_vote_limit_count",
self.errors
.retried_txs_per_vote_limit_count
self.retried_txs_per_vote_limit_count
.swap(0, Ordering::Relaxed) as i64,
i64
),
(
"retried_txs_per_account_limit_count",
self.errors
.retried_txs_per_account_limit_count
self.retried_txs_per_account_limit_count
.swap(0, Ordering::Relaxed) as i64,
i64
),
(
"retried_txs_per_account_data_block_limit_count",
self.errors
.retried_txs_per_account_data_block_limit_count
"retried_txs_per_account_data_limit_count",
self.retried_txs_per_account_data_limit_count
.swap(0, Ordering::Relaxed) as i64,
i64
),
(
"dropped_txs_per_account_data_total_limit_count",
self.errors
.dropped_txs_per_account_data_total_limit_count
.swap(0, Ordering::Relaxed) as i64,
"estimated_signature_cu",
self.estimated_signature_cu.swap(0, Ordering::Relaxed) as i64,
i64
),
(
"estimated_write_lock_cu",
self.estimated_write_lock_cu.swap(0, Ordering::Relaxed) as i64,
i64
),
(
"estimated_data_bytes_cu",
self.estimated_data_bytes_cu.swap(0, Ordering::Relaxed) as i64,
i64
),
(
"estimated_execute_cu",
self.estimated_execute_cu.swap(0, Ordering::Relaxed) as i64,
i64
),
(
"actual_execute_cu",
self.actual_execute_cu.swap(0, Ordering::Relaxed) as i64,
i64
),
(
"actual_execute_time_us",
self.actual_execute_time_us.swap(0, Ordering::Relaxed) as i64,
i64
),
);

View File

@@ -27,9 +27,9 @@ use {
window_service::DuplicateSlotReceiver,
},
crossbeam_channel::{Receiver, RecvTimeoutError, Sender},
solana_accountsdb_plugin_manager::block_metadata_notifier_interface::BlockMetadataNotifierLock,
solana_client::rpc_response::SlotUpdate,
solana_entry::entry::VerifyRecyclers,
solana_geyser_plugin_manager::block_metadata_notifier_interface::BlockMetadataNotifierLock,
solana_gossip::cluster_info::ClusterInfo,
solana_ledger::{
block_error::BlockError,
@@ -735,16 +735,11 @@ impl ReplayStage {
restored_tower.adjust_lockouts_after_replay(root_bank.slot(), &slot_history)
}).
unwrap_or_else(|err| {
if err.is_file_missing() {
Tower::new_from_bankforks(
&bank_forks.read().unwrap(),
&my_pubkey,
&vote_account,
)
} else {
error!("Failed to load tower for {}: {}", my_pubkey, err);
std::process::exit(1);
}
// It's a fatal error if the tower is not present. This is
// necessary to prevent the validator from violating
// lockouts for its new identity
error!("Failed to load tower for {}: {}", my_pubkey, err);
std::process::exit(1);
});
// Ensure the validator can land votes with the new identity before
@@ -1734,7 +1729,7 @@ impl ReplayStage {
replay_timing: &mut ReplayTiming,
voting_sender: &Sender<VoteOp>,
epoch_slots_frozen_slots: &mut EpochSlotsFrozenSlots,
drop_bank_sender: &Sender<Vec<Arc<Bank>>>,
bank_drop_sender: &Sender<Vec<Arc<Bank>>>,
wait_to_vote_slot: Option<Slot>,
) {
if bank.is_empty() {
@@ -1786,7 +1781,7 @@ impl ReplayStage {
has_new_vote_been_rooted,
vote_signatures,
epoch_slots_frozen_slots,
drop_bank_sender,
bank_drop_sender,
);
rpc_subscriptions.notify_roots(rooted_slots);
if let Some(sender) = bank_notification_sender {
@@ -2924,14 +2919,14 @@ impl ReplayStage {
has_new_vote_been_rooted: &mut bool,
voted_signatures: &mut Vec<Signature>,
epoch_slots_frozen_slots: &mut EpochSlotsFrozenSlots,
drop_bank_sender: &Sender<Vec<Arc<Bank>>>,
bank_drop_sender: &Sender<Vec<Arc<Bank>>>,
) {
let removed_banks = bank_forks.write().unwrap().set_root(
new_root,
accounts_background_request_sender,
highest_confirmed_root,
);
drop_bank_sender
bank_drop_sender
.send(removed_banks)
.unwrap_or_else(|err| warn!("bank drop failed: {:?}", err));
@@ -3134,7 +3129,7 @@ pub mod tests {
},
solana_rpc::{
optimistically_confirmed_bank_tracker::OptimisticallyConfirmedBank,
rpc::{create_test_transaction_entries, populate_blockstore_for_tests},
rpc::create_test_transactions_and_populate_blockstore,
},
solana_runtime::{
accounts_background_service::AbsRequestSender,
@@ -3442,7 +3437,7 @@ pub mod tests {
.into_iter()
.map(|slot| (slot, Hash::default()))
.collect();
let (drop_bank_sender, _drop_bank_receiver) = unbounded();
let (bank_drop_sender, _bank_drop_receiver) = unbounded();
ReplayStage::handle_new_root(
root,
&bank_forks,
@@ -3456,7 +3451,7 @@ pub mod tests {
&mut true,
&mut Vec::new(),
&mut epoch_slots_frozen_slots,
&drop_bank_sender,
&bank_drop_sender,
);
assert_eq!(bank_forks.read().unwrap().root(), root);
assert_eq!(progress.len(), 1);
@@ -3523,7 +3518,7 @@ pub mod tests {
for i in 0..=root {
progress.insert(i, ForkProgress::new(Hash::default(), None, None, 0, 0));
}
let (drop_bank_sender, _drop_bank_receiver) = unbounded();
let (bank_drop_sender, _bank_drop_receiver) = unbounded();
ReplayStage::handle_new_root(
root,
&bank_forks,
@@ -3537,7 +3532,7 @@ pub mod tests {
&mut true,
&mut Vec::new(),
&mut EpochSlotsFrozenSlots::default(),
&drop_bank_sender,
&bank_drop_sender,
);
assert_eq!(bank_forks.read().unwrap().root(), root);
assert!(bank_forks.read().unwrap().get(confirmed_root).is_some());
@@ -4003,18 +3998,15 @@ pub mod tests {
let bank1 = Arc::new(Bank::new_from_parent(&bank0, &Pubkey::default(), 1));
let slot = bank1.slot();
let (entries, test_signatures) = create_test_transaction_entries(
let mut test_signatures_iter = create_test_transactions_and_populate_blockstore(
vec![&mint_keypair, &keypair1, &keypair2, &keypair3],
bank1.clone(),
);
populate_blockstore_for_tests(
entries,
bank0.slot(),
bank1,
blockstore.clone(),
Arc::new(AtomicU64::default()),
);
)
.into_iter();
let mut test_signatures_iter = test_signatures.into_iter();
let confirmed_block = blockstore.get_rooted_block(slot, false).unwrap();
let actual_tx_results: Vec<_> = confirmed_block
.transactions

View File

@@ -416,7 +416,7 @@ pub fn retransmitter(
.unwrap()
}
pub struct RetransmitStage {
pub(crate) struct RetransmitStage {
retransmit_thread_handle: JoinHandle<()>,
window_service: WindowService,
cluster_slots_service: ClusterSlotsService,
@@ -529,7 +529,7 @@ mod tests {
super::*,
solana_gossip::contact_info::ContactInfo,
solana_ledger::{
blockstore_processor::{test_process_blockstore, ProcessOptions},
blockstore_processor::{process_blockstore, ProcessOptions},
create_new_tmp_ledger,
genesis_utils::{create_genesis_config, GenesisConfigInfo},
},
@@ -550,9 +550,19 @@ mod tests {
full_leader_cache: true,
..ProcessOptions::default()
};
let (bank_forks, leader_schedule_cache) =
test_process_blockstore(&genesis_config, &blockstore, opts);
let leader_schedule_cache = Arc::new(leader_schedule_cache);
let (accounts_package_sender, _) = unbounded();
let (bank_forks, cached_leader_schedule, _) = process_blockstore(
&genesis_config,
&blockstore,
Vec::new(),
opts,
None,
None,
accounts_package_sender,
None,
)
.unwrap();
let leader_schedule_cache = Arc::new(cached_leader_schedule);
let bank_forks = Arc::new(RwLock::new(bank_forks));
let mut me = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0);

View File

@@ -2,7 +2,6 @@ use {
crate::{
cluster_slots::ClusterSlots,
duplicate_repair_status::ANCESTOR_HASH_REPAIR_SAMPLE_SIZE,
packet_threshold::DynamicPacketToProcessThreshold,
repair_response,
repair_service::{OutstandingShredRepairs, RepairStats},
request_response::RequestResponse,
@@ -24,6 +23,7 @@ use {
blockstore::Blockstore,
shred::{Nonce, Shred, SIZE_OF_NONCE},
},
solana_measure::measure::Measure,
solana_metrics::inc_new_counter_debug,
solana_perf::packet::{limited_deserialize, PacketBatch, PacketBatchRecycler},
solana_sdk::{
@@ -322,7 +322,7 @@ impl ServeRepair {
requests_receiver: &PacketBatchReceiver,
response_sender: &PacketBatchSender,
stats: &mut ServeRepairStats,
packet_threshold: &mut DynamicPacketToProcessThreshold,
max_packets: &mut usize,
) -> Result<()> {
//TODO cache connections
let timeout = Duration::new(1, 0);
@@ -332,21 +332,29 @@ impl ServeRepair {
let mut dropped_packets = 0;
while let Ok(more) = requests_receiver.try_recv() {
total_packets += more.packets.len();
if packet_threshold.should_drop(total_packets) {
dropped_packets += more.packets.len();
} else {
if total_packets < *max_packets {
// Drop the rest in the channel in case of dos
reqs_v.push(more);
} else {
dropped_packets += more.packets.len();
}
}
stats.dropped_packets += dropped_packets;
stats.total_packets += total_packets;
let timer = Instant::now();
let mut time = Measure::start("repair::handle_packets");
for reqs in reqs_v {
Self::handle_packets(obj, recycler, blockstore, reqs, response_sender, stats);
}
packet_threshold.update(total_packets, timer.elapsed());
time.stop();
if total_packets >= *max_packets {
if time.as_ms() > 1000 {
*max_packets = (*max_packets * 9) / 10;
} else {
*max_packets = (*max_packets * 10) / 9;
}
}
Ok(())
}
@@ -395,7 +403,7 @@ impl ServeRepair {
.spawn(move || {
let mut last_print = Instant::now();
let mut stats = ServeRepairStats::default();
let mut packet_threshold = DynamicPacketToProcessThreshold::default();
let mut max_packets = 1024;
loop {
let result = Self::run_listen(
&me,
@@ -404,7 +412,7 @@ impl ServeRepair {
&requests_receiver,
&response_sender,
&mut stats,
&mut packet_threshold,
&mut max_packets,
);
match result {
Err(Error::RecvTimeout(_)) | Ok(_) => {}

View File

@@ -2,17 +2,17 @@
use {
crate::packet_hasher::PacketHasher,
crossbeam_channel::{unbounded, Sender},
crossbeam_channel::unbounded,
lru::LruCache,
solana_ledger::shred::{get_shred_slot_index_type, ShredFetchStats},
solana_perf::{
cuda_runtime::PinnedVec,
packet::{Packet, PacketBatch, PacketBatchRecycler, PacketFlags},
packet::{Packet, PacketBatchRecycler, PacketFlags},
recycler::Recycler,
},
solana_runtime::bank_forks::BankForks,
solana_sdk::clock::{Slot, DEFAULT_MS_PER_SLOT},
solana_streamer::streamer::{self, PacketBatchReceiver},
solana_streamer::streamer::{self, PacketBatchReceiver, PacketBatchSender},
std::{
net::UdpSocket,
sync::{atomic::AtomicBool, Arc, RwLock},
@@ -65,7 +65,7 @@ impl ShredFetchStage {
// updates packets received on a channel and sends them on another channel
fn modify_packets<F>(
recvr: PacketBatchReceiver,
sendr: Sender<Vec<PacketBatch>>,
sendr: PacketBatchSender,
bank_forks: Option<Arc<RwLock<BankForks>>>,
name: &'static str,
modify: F,
@@ -125,7 +125,7 @@ impl ShredFetchStage {
stats = ShredFetchStats::default();
last_stats = Instant::now();
}
if sendr.send(vec![packet_batch]).is_err() {
if sendr.send(packet_batch).is_err() {
break;
}
}
@@ -134,7 +134,7 @@ impl ShredFetchStage {
fn packet_modifier<F>(
sockets: Vec<Arc<UdpSocket>>,
exit: &Arc<AtomicBool>,
sender: Sender<Vec<PacketBatch>>,
sender: PacketBatchSender,
recycler: Recycler<PinnedVec<Packet>>,
bank_forks: Option<Arc<RwLock<BankForks>>>,
name: &'static str,
@@ -170,7 +170,7 @@ impl ShredFetchStage {
sockets: Vec<Arc<UdpSocket>>,
forward_sockets: Vec<Arc<UdpSocket>>,
repair_socket: Arc<UdpSocket>,
sender: &Sender<Vec<PacketBatch>>,
sender: &PacketBatchSender,
bank_forks: Option<Arc<RwLock<BankForks>>>,
exit: &Arc<AtomicBool>,
) -> Self {

View File

@@ -6,9 +6,9 @@
//! if perf-libs are available
use {
crate::{find_packet_sender_stake_stage, sigverify},
crate::sigverify,
core::time::Duration,
crossbeam_channel::{RecvTimeoutError, SendError, Sender},
crossbeam_channel::{Receiver, RecvTimeoutError, SendError, Sender},
itertools::Itertools,
solana_measure::measure::Measure,
solana_perf::{
@@ -16,7 +16,7 @@ use {
sigverify::{count_valid_packets, shrink_batches, Deduper},
},
solana_sdk::timing,
solana_streamer::streamer::{self, StreamerError},
solana_streamer::streamer::{self, PacketBatchReceiver, StreamerError},
std::{
thread::{self, Builder, JoinHandle},
time::Instant,
@@ -192,7 +192,7 @@ impl SigVerifier for DisabledSigVerifier {
impl SigVerifyStage {
#[allow(clippy::new_ret_no_self)]
pub fn new<T: SigVerifier + 'static + Send + Clone>(
packet_receiver: find_packet_sender_stake_stage::FindPacketSenderStakeReceiver,
packet_receiver: Receiver<PacketBatch>,
verified_sender: Sender<Vec<PacketBatch>>,
verifier: T,
) -> Self {
@@ -227,12 +227,12 @@ impl SigVerifyStage {
fn verifier<T: SigVerifier>(
deduper: &Deduper,
recvr: &find_packet_sender_stake_stage::FindPacketSenderStakeReceiver,
recvr: &PacketBatchReceiver,
sendr: &Sender<Vec<PacketBatch>>,
verifier: &T,
stats: &mut SigVerifierStats,
) -> Result<()> {
let (mut batches, num_packets, recv_duration) = streamer::recv_vec_packet_batches(recvr)?;
let (mut batches, num_packets, recv_duration) = streamer::recv_packet_batches(recvr)?;
let batches_len = batches.len();
debug!(
@@ -312,7 +312,7 @@ impl SigVerifyStage {
}
fn verifier_service<T: SigVerifier + 'static + Send + Clone>(
packet_receiver: find_packet_sender_stake_stage::FindPacketSenderStakeReceiver,
packet_receiver: PacketBatchReceiver,
verified_sender: Sender<Vec<PacketBatch>>,
verifier: &T,
) -> JoinHandle<()> {
@@ -358,7 +358,7 @@ impl SigVerifyStage {
}
fn verifier_services<T: SigVerifier + 'static + Send + Clone>(
packet_receiver: find_packet_sender_stake_stage::FindPacketSenderStakeReceiver,
packet_receiver: PacketBatchReceiver,
verified_sender: Sender<Vec<PacketBatch>>,
verifier: T,
) -> JoinHandle<()> {
@@ -445,7 +445,7 @@ mod tests {
for _ in 0..batches.len() {
if let Some(batch) = batches.pop() {
sent_len += batch.packets.len();
packet_s.send(vec![batch]).unwrap();
packet_s.send(batch).unwrap();
}
}
let mut received = 0;

View File

@@ -219,7 +219,7 @@ mod tests {
snapshot_archive_info::SnapshotArchiveInfo,
snapshot_package::{SnapshotPackage, SnapshotType},
snapshot_utils::{
self, ArchiveFormat, SnapshotVersion, SNAPSHOT_STATUS_CACHE_FILENAME,
self, ArchiveFormat, SnapshotVersion, SNAPSHOT_STATUS_CACHE_FILE_NAME,
},
},
solana_sdk::hash::Hash,
@@ -335,7 +335,7 @@ mod tests {
// the source dir for snapshots
let dummy_slot_deltas: Vec<BankSlotDelta> = vec![];
snapshot_utils::serialize_snapshot_data_file(
&snapshots_dir.join(SNAPSHOT_STATUS_CACHE_FILENAME),
&snapshots_dir.join(SNAPSHOT_STATUS_CACHE_FILE_NAME),
|stream| {
serialize_into(stream, &dummy_slot_deltas)?;
Ok(())

View File

@@ -117,16 +117,12 @@ pub fn verify_udp_stats_access() -> Result<(), String> {
}
impl SystemMonitorService {
pub fn new(
exit: Arc<AtomicBool>,
report_os_memory_stats: bool,
report_os_network_stats: bool,
) -> Self {
pub fn new(exit: Arc<AtomicBool>, report_os_network_stats: bool) -> Self {
info!("Starting SystemMonitorService");
let thread_hdl = Builder::new()
.name("system-monitor".to_string())
.spawn(move || {
Self::run(exit, report_os_memory_stats, report_os_network_stats);
Self::run(exit, report_os_network_stats);
})
.unwrap();
@@ -335,7 +331,7 @@ impl SystemMonitorService {
}
}
pub fn run(exit: Arc<AtomicBool>, report_os_memory_stats: bool, report_os_network_stats: bool) {
pub fn run(exit: Arc<AtomicBool>, report_os_network_stats: bool) {
let mut udp_stats = None;
let network_limits_timer = AtomicInterval::default();
let udp_timer = AtomicInterval::default();
@@ -353,7 +349,7 @@ impl SystemMonitorService {
Self::process_udp_stats(&mut udp_stats);
}
}
if report_os_memory_stats && mem_timer.should_update(SAMPLE_INTERVAL_MEM_MS) {
if mem_timer.should_update(SAMPLE_INTERVAL_MEM_MS) {
Self::report_mem_stats();
}
sleep(SLEEP_INTERVAL);

View File

@@ -10,7 +10,6 @@ use {
GossipVerifiedVoteHashSender, VerifiedVoteSender, VoteTracker,
},
fetch_stage::FetchStage,
find_packet_sender_stake_stage::FindPacketSenderStakeStage,
sigverify::TransactionSigVerifier,
sigverify_stage::SigVerifyStage,
},
@@ -37,9 +36,6 @@ use {
pub const DEFAULT_TPU_COALESCE_MS: u64 = 5;
// allow multiple connections for NAT and any open/close overlap
pub const MAX_QUIC_CONNECTIONS_PER_IP: usize = 8;
pub struct TpuSockets {
pub transactions: Vec<UdpSocket>,
pub transaction_forwards: Vec<UdpSocket>,
@@ -56,8 +52,6 @@ pub struct Tpu {
cluster_info_vote_listener: ClusterInfoVoteListener,
broadcast_stage: BroadcastStage,
tpu_quic_t: thread::JoinHandle<()>,
find_packet_sender_stake_stage: FindPacketSenderStakeStage,
vote_find_packet_sender_stake_stage: FindPacketSenderStakeStage,
}
impl Tpu {
@@ -106,26 +100,6 @@ impl Tpu {
poh_recorder,
tpu_coalesce_ms,
);
let (find_packet_sender_stake_sender, find_packet_sender_stake_receiver) = unbounded();
let find_packet_sender_stake_stage = FindPacketSenderStakeStage::new(
packet_receiver,
find_packet_sender_stake_sender,
bank_forks.clone(),
cluster_info.clone(),
);
let (vote_find_packet_sender_stake_sender, vote_find_packet_sender_stake_receiver) =
unbounded();
let vote_find_packet_sender_stake_stage = FindPacketSenderStakeStage::new(
vote_packet_receiver,
vote_find_packet_sender_stake_sender,
bank_forks.clone(),
cluster_info.clone(),
);
let (verified_sender, verified_receiver) = unbounded();
let tpu_quic_t = solana_streamer::quic::spawn_server(
@@ -134,13 +108,12 @@ impl Tpu {
cluster_info.my_contact_info().tpu.ip(),
packet_sender,
exit.clone(),
MAX_QUIC_CONNECTIONS_PER_IP,
)
.unwrap();
let sigverify_stage = {
let verifier = TransactionSigVerifier::default();
SigVerifyStage::new(find_packet_sender_stake_receiver, verified_sender, verifier)
SigVerifyStage::new(packet_receiver, verified_sender, verifier)
};
let (verified_tpu_vote_packets_sender, verified_tpu_vote_packets_receiver) = unbounded();
@@ -148,7 +121,7 @@ impl Tpu {
let vote_sigverify_stage = {
let verifier = TransactionSigVerifier::new_reject_non_vote();
SigVerifyStage::new(
vote_find_packet_sender_stake_receiver,
vote_packet_receiver,
verified_tpu_vote_packets_sender,
verifier,
)
@@ -202,8 +175,6 @@ impl Tpu {
cluster_info_vote_listener,
broadcast_stage,
tpu_quic_t,
find_packet_sender_stake_stage,
vote_find_packet_sender_stake_stage,
}
}
@@ -214,8 +185,6 @@ impl Tpu {
self.vote_sigverify_stage.join(),
self.cluster_info_vote_listener.join(),
self.banking_stage.join(),
self.find_packet_sender_stake_stage.join(),
self.vote_find_packet_sender_stake_stage.join(),
];
self.tpu_quic_t.join()?;
let broadcast_result = self.broadcast_stage.join();

View File

@@ -16,7 +16,6 @@ use {
cost_update_service::CostUpdateService,
drop_bank_service::DropBankService,
ledger_cleanup_service::LedgerCleanupService,
ledger_metric_report_service::LedgerMetricReportService,
replay_stage::{ReplayStage, ReplayStageConfig},
retransmit_stage::RetransmitStage,
rewards_recorder_service::RewardsRecorderSender,
@@ -27,7 +26,7 @@ use {
voting_service::VotingService,
},
crossbeam_channel::{unbounded, Receiver},
solana_geyser_plugin_manager::block_metadata_notifier_interface::BlockMetadataNotifierLock,
solana_accountsdb_plugin_manager::block_metadata_notifier_interface::BlockMetadataNotifierLock,
solana_gossip::cluster_info::ClusterInfo,
solana_ledger::{
blockstore::Blockstore, blockstore_processor::TransactionStatusSender,
@@ -71,7 +70,6 @@ pub struct Tvu {
retransmit_stage: RetransmitStage,
replay_stage: ReplayStage,
ledger_cleanup_service: Option<LedgerCleanupService>,
ledger_metric_report_service: LedgerMetricReportService,
accounts_background_service: AccountsBackgroundService,
accounts_hash_verifier: AccountsHashVerifier,
cost_update_service: CostUpdateService,
@@ -215,6 +213,14 @@ impl Tvu {
let (ledger_cleanup_slot_sender, ledger_cleanup_slot_receiver) = unbounded();
let snapshot_interval_slots = {
if let Some(config) = bank_forks.read().unwrap().snapshot_config() {
config.full_snapshot_archive_interval_slots
} else {
std::u64::MAX
}
};
info!("snapshot_interval_slots: {}", snapshot_interval_slots);
let (snapshot_config, pending_snapshot_package) = snapshot_config_and_pending_package
.map(|(snapshot_config, pending_snapshot_package)| {
(Some(snapshot_config), Some(pending_snapshot_package))
@@ -351,8 +357,6 @@ impl Tvu {
)
});
let ledger_metric_report_service = LedgerMetricReportService::new(blockstore, exit);
let accounts_background_service = AccountsBackgroundService::new(
bank_forks.clone(),
exit,
@@ -369,7 +373,6 @@ impl Tvu {
retransmit_stage,
replay_stage,
ledger_cleanup_service,
ledger_metric_report_service,
accounts_background_service,
accounts_hash_verifier,
cost_update_service,
@@ -386,7 +389,6 @@ impl Tvu {
if self.ledger_cleanup_service.is_some() {
self.ledger_cleanup_service.unwrap().join()?;
}
self.ledger_metric_report_service.join()?;
self.accounts_background_service.join()?;
self.replay_stage.join()?;
self.accounts_hash_verifier.join()?;

View File

@@ -1,337 +0,0 @@
use {
retain_mut::RetainMut,
solana_perf::packet::{limited_deserialize, Packet, PacketBatch},
solana_sdk::{
hash::Hash, message::Message, short_vec::decode_shortu16_len, signature::Signature,
transaction::VersionedTransaction,
},
std::{
collections::{HashMap, VecDeque},
mem::size_of,
},
};
/// Holds deserialized messages, as well as computed message_hash and other things needed to create
/// SanitizedTransaction
#[derive(Debug, Default)]
pub struct DeserializedPacket {
#[allow(dead_code)]
versioned_transaction: VersionedTransaction,
#[allow(dead_code)]
message_hash: Hash,
#[allow(dead_code)]
is_simple_vote: bool,
}
/// Defines the type of entry in `UnprocessedPacketBatches`, it holds original packet_batch
/// for forwarding, as well as `forwarded` flag;
/// Each packet in packet_batch are deserialized upon receiving, the result are stored in
/// `DeserializedPacket` in the same order as packets in `packet_batch`.
#[derive(Debug, Default)]
pub struct DeserializedPacketBatch {
pub packet_batch: PacketBatch,
pub forwarded: bool,
// indexes of valid packets in batch, and their corresponding deserialized_packet
pub unprocessed_packets: HashMap<usize, DeserializedPacket>,
}
/// References to a packet in `UnprocessedPacketBatches`, where
/// - batch_index references to `DeserializedPacketBatch`,
/// - packet_index references to `packet` within `DeserializedPacketBatch.packet_batch`
#[derive(Debug, Default)]
pub struct PacketLocator {
#[allow(dead_code)]
batch_index: usize,
#[allow(dead_code)]
packet_index: usize,
}
/// Currently each banking_stage thread has a `UnprocessedPacketBatches` buffer to store
/// PacketBatch's received from sigverify. Banking thread continuously scans the buffer
/// to pick proper packets to add to the block.
#[derive(Default)]
pub struct UnprocessedPacketBatches(VecDeque<DeserializedPacketBatch>);
impl std::ops::Deref for UnprocessedPacketBatches {
type Target = VecDeque<DeserializedPacketBatch>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for UnprocessedPacketBatches {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl RetainMut<DeserializedPacketBatch> for UnprocessedPacketBatches {
fn retain_mut<F>(&mut self, f: F)
where
F: FnMut(&mut DeserializedPacketBatch) -> bool,
{
RetainMut::retain_mut(&mut self.0, f);
}
}
impl FromIterator<DeserializedPacketBatch> for UnprocessedPacketBatches {
fn from_iter<I: IntoIterator<Item = DeserializedPacketBatch>>(iter: I) -> Self {
Self(iter.into_iter().collect())
}
}
impl UnprocessedPacketBatches {
pub fn new() -> Self {
Self::default()
}
pub fn with_capacity(capacity: usize) -> Self {
UnprocessedPacketBatches(VecDeque::with_capacity(capacity))
}
/// Returns total number of all packets (including unprocessed and processed) in buffer
#[allow(dead_code)]
fn get_packets_count(&self) -> usize {
self.iter()
.map(|deserialized_packet_batch| deserialized_packet_batch.packet_batch.packets.len())
.sum()
}
/// Returns total number of unprocessed packets in buffer
#[allow(dead_code)]
fn get_unprocessed_packets_count(&self) -> usize {
self.iter()
.map(|deserialized_packet_batch| deserialized_packet_batch.unprocessed_packets.len())
.sum()
}
/// Iterates the inner `Vec<DeserializedPacketBatch>`.
/// Returns the flattened result of mapping each
/// `DeserializedPacketBatch` to a list the batch's inner
/// packets' sender's stake and their `PacketLocator`'s within the
/// `Vec<DeserializedPacketBatch>`.
#[allow(dead_code)]
fn get_stakes_and_locators(&self) -> (Vec<u64>, Vec<PacketLocator>) {
self.iter()
.enumerate()
.flat_map(|(batch_index, deserialized_packet_batch)| {
let packet_batch = &deserialized_packet_batch.packet_batch;
deserialized_packet_batch
.unprocessed_packets
.keys()
.map(move |packet_index| {
let p = &packet_batch.packets[*packet_index];
(
p.meta.sender_stake,
PacketLocator {
batch_index,
packet_index: *packet_index,
},
)
})
})
.unzip()
}
}
impl DeserializedPacketBatch {
pub fn new(packet_batch: PacketBatch, packet_indexes: Vec<usize>, forwarded: bool) -> Self {
let unprocessed_packets = Self::deserialize_packets(&packet_batch, &packet_indexes);
Self {
packet_batch,
unprocessed_packets,
forwarded,
}
}
fn deserialize_packets(
packet_batch: &PacketBatch,
packet_indexes: &[usize],
) -> HashMap<usize, DeserializedPacket> {
packet_indexes
.iter()
.filter_map(|packet_index| {
let deserialized_packet =
Self::deserialize_packet(&packet_batch.packets[*packet_index])?;
Some((*packet_index, deserialized_packet))
})
.collect()
}
fn deserialize_packet(packet: &Packet) -> Option<DeserializedPacket> {
let versioned_transaction: VersionedTransaction =
match limited_deserialize(&packet.data[0..packet.meta.size]) {
Ok(tx) => tx,
Err(_) => return None,
};
if let Some(message_bytes) = Self::packet_message(packet) {
let message_hash = Message::hash_raw_message(message_bytes);
let is_simple_vote = packet.meta.is_simple_vote_tx();
Some(DeserializedPacket {
versioned_transaction,
message_hash,
is_simple_vote,
})
} else {
None
}
}
/// Read the transaction message from packet data
pub fn packet_message(packet: &Packet) -> Option<&[u8]> {
let (sig_len, sig_size) = decode_shortu16_len(&packet.data).ok()?;
let msg_start = sig_len
.checked_mul(size_of::<Signature>())
.and_then(|v| v.checked_add(sig_size))?;
let msg_end = packet.meta.size;
Some(&packet.data[msg_start..msg_end])
}
/// Returns whether the given `PacketBatch` has any more remaining unprocessed
/// transactions
pub fn update_buffered_packets_with_new_unprocessed(
&mut self,
_original_unprocessed_indexes: &[usize],
new_unprocessed_indexes: &[usize],
) -> bool {
let has_more_unprocessed_transactions = !new_unprocessed_indexes.is_empty();
if has_more_unprocessed_transactions {
self.unprocessed_packets
.retain(|index, _| new_unprocessed_indexes.contains(index));
} else {
self.unprocessed_packets.clear();
}
has_more_unprocessed_transactions
}
}
#[cfg(test)]
mod tests {
use {
super::*,
solana_sdk::{signature::Keypair, system_transaction},
std::net::IpAddr,
};
fn packet_with_sender_stake(sender_stake: u64, ip: Option<IpAddr>) -> Packet {
let tx = system_transaction::transfer(
&Keypair::new(),
&solana_sdk::pubkey::new_rand(),
1,
Hash::new_unique(),
);
let mut packet = Packet::from_data(None, &tx).unwrap();
packet.meta.sender_stake = sender_stake;
if let Some(ip) = ip {
packet.meta.addr = ip;
}
packet
}
#[test]
fn test_packet_message() {
let keypair = Keypair::new();
let pubkey = solana_sdk::pubkey::new_rand();
let blockhash = Hash::new_unique();
let transaction = system_transaction::transfer(&keypair, &pubkey, 1, blockhash);
let packet = Packet::from_data(None, &transaction).unwrap();
assert_eq!(
DeserializedPacketBatch::packet_message(&packet)
.unwrap()
.to_vec(),
transaction.message_data()
);
}
#[test]
fn test_get_packets_count() {
// create a buffer with 3 batches, each has 2 packets but only first one is valid
let batch_size = 2usize;
let batch_count = 3usize;
let unprocessed_packet_batches: UnprocessedPacketBatches = (0..batch_count)
.map(|_batch_index| {
DeserializedPacketBatch::new(
PacketBatch::new(
(0..batch_size)
.map(|packet_index| packet_with_sender_stake(packet_index as u64, None))
.collect(),
),
vec![0],
false,
)
})
.collect();
// Assert total packets count, and unprocessed packets count
assert_eq!(
batch_size * batch_count,
unprocessed_packet_batches.get_packets_count()
);
assert_eq!(
batch_count,
unprocessed_packet_batches.get_unprocessed_packets_count()
);
}
#[test]
fn test_get_stakes_and_locators_from_empty_buffer() {
let unprocessed_packet_batches = UnprocessedPacketBatches::default();
let (stakes, locators) = unprocessed_packet_batches.get_stakes_and_locators();
assert!(stakes.is_empty());
assert!(locators.is_empty());
}
#[test]
fn test_get_stakes_and_locators() {
solana_logger::setup();
// setup senders' address and stake
let senders: Vec<(IpAddr, u64)> = vec![
(IpAddr::from([127, 0, 0, 1]), 1),
(IpAddr::from([127, 0, 0, 2]), 2),
(IpAddr::from([127, 0, 0, 3]), 3),
];
// create a buffer with 3 batches, each has 2 packet from above sender.
// buffer looks like:
// [127.0.0.1, 127.0.0.2]
// [127.0.0.3, 127.0.0.1]
// [127.0.0.2, 127.0.0.3]
let batch_size = 2usize;
let batch_count = 3usize;
let unprocessed_packet_batches: UnprocessedPacketBatches = (0..batch_count)
.map(|batch_index| {
DeserializedPacketBatch::new(
PacketBatch::new(
(0..batch_size)
.map(|packet_index| {
let n = (batch_index * batch_size + packet_index) % senders.len();
packet_with_sender_stake(senders[n].1, Some(senders[n].0))
})
.collect(),
),
(0..batch_size).collect(),
false,
)
})
.collect();
let (stakes, locators) = unprocessed_packet_batches.get_stakes_and_locators();
// Produced stakes and locators should both have "batch_size * batch_count" entries;
assert_eq!(batch_size * batch_count, stakes.len());
assert_eq!(batch_size * batch_count, locators.len());
// Assert stakes and locators are in good order
locators.iter().enumerate().for_each(|(index, locator)| {
assert_eq!(
stakes[index],
senders[(locator.batch_index * batch_size + locator.packet_index) % senders.len()]
.1
);
});
}
}

View File

@@ -22,8 +22,8 @@ use {
},
crossbeam_channel::{bounded, unbounded, Receiver},
rand::{thread_rng, Rng},
solana_accountsdb_plugin_manager::accountsdb_plugin_service::AccountsDbPluginService,
solana_entry::poh::compute_hash_time_ns,
solana_geyser_plugin_manager::geyser_plugin_service::GeyserPluginService,
solana_gossip::{
cluster_info::{
ClusterInfo, Node, DEFAULT_CONTACT_DEBUG_INTERVAL_MILLIS,
@@ -35,10 +35,8 @@ use {
},
solana_ledger::{
bank_forks_utils,
blockstore::{
Blockstore, BlockstoreError, BlockstoreSignals, CompletedSlotsReceiver, PurgeType,
},
blockstore_db::{BlockstoreOptions, BlockstoreRecoveryMode, LedgerColumnOptions},
blockstore::{Blockstore, BlockstoreSignals, CompletedSlotsReceiver, PurgeType},
blockstore_db::{BlockstoreOptions, BlockstoreRecoveryMode},
blockstore_processor::{self, TransactionStatusSender},
leader_schedule::FixedSchedule,
leader_schedule_cache::LeaderScheduleCache,
@@ -121,7 +119,7 @@ pub struct ValidatorConfig {
pub account_shrink_paths: Option<Vec<PathBuf>>,
pub rpc_config: JsonRpcConfig,
pub accountsdb_repl_service_config: Option<AccountsDbReplServiceConfig>,
pub geyser_plugin_config_files: Option<Vec<PathBuf>>,
pub accountsdb_plugin_config_files: Option<Vec<PathBuf>>,
pub rpc_addrs: Option<(SocketAddr, SocketAddr)>, // (JsonRpc, JsonRpcPubSub)
pub pubsub_config: PubSubConfig,
pub snapshot_config: Option<SnapshotConfig>,
@@ -152,7 +150,6 @@ pub struct ValidatorConfig {
pub bpf_jit: bool,
pub send_transaction_service_config: send_transaction_service::Config,
pub no_poh_speed_test: bool,
pub no_os_memory_stats_reporting: bool,
pub no_os_network_stats_reporting: bool,
pub poh_pinned_cpu_core: usize,
pub poh_hashes_per_batch: u64,
@@ -168,7 +165,6 @@ pub struct ValidatorConfig {
pub no_wait_for_vote_to_start_leader: bool,
pub accounts_shrink_ratio: AccountShrinkThreshold,
pub wait_to_vote_slot: Option<Slot>,
pub ledger_column_options: LedgerColumnOptions,
}
impl Default for ValidatorConfig {
@@ -184,7 +180,7 @@ impl Default for ValidatorConfig {
account_shrink_paths: None,
rpc_config: JsonRpcConfig::default(),
accountsdb_repl_service_config: None,
geyser_plugin_config_files: None,
accountsdb_plugin_config_files: None,
rpc_addrs: None,
pubsub_config: PubSubConfig::default(),
snapshot_config: None,
@@ -214,7 +210,6 @@ impl Default for ValidatorConfig {
bpf_jit: false,
send_transaction_service_config: send_transaction_service::Config::default(),
no_poh_speed_test: true,
no_os_memory_stats_reporting: true,
no_os_network_stats_reporting: true,
poh_pinned_cpu_core: poh_service::DEFAULT_PINNED_CPU_CORE,
poh_hashes_per_batch: poh_service::DEFAULT_HASHES_PER_BATCH,
@@ -230,7 +225,6 @@ impl Default for ValidatorConfig {
accounts_shrink_ratio: AccountShrinkThreshold::default(),
accounts_db_config: None,
wait_to_vote_slot: None,
ledger_column_options: LedgerColumnOptions::default(),
}
}
}
@@ -270,39 +264,6 @@ impl Default for ValidatorStartProgress {
}
}
struct BlockstoreRootScan {
thread: Option<JoinHandle<Result<(), BlockstoreError>>>,
}
impl BlockstoreRootScan {
fn new(config: &ValidatorConfig, blockstore: &Arc<Blockstore>, exit: &Arc<AtomicBool>) -> Self {
let thread = if config.rpc_addrs.is_some()
&& config.rpc_config.enable_rpc_transaction_history
&& config.rpc_config.rpc_scan_and_fix_roots
{
let blockstore = blockstore.clone();
let exit = exit.clone();
Some(
Builder::new()
.name("blockstore-root-scan".to_string())
.spawn(move || blockstore.scan_and_fix_roots(&exit))
.unwrap(),
)
} else {
None
};
Self { thread }
}
fn join(self) {
if let Some(blockstore_root_scan) = self.thread {
if let Err(err) = blockstore_root_scan.join() {
warn!("blockstore_root_scan failed to join {:?}", err);
}
}
}
}
#[derive(Default)]
struct TransactionHistoryServices {
transaction_status_sender: Option<TransactionStatusSender>,
@@ -338,7 +299,7 @@ pub struct Validator {
pub cluster_info: Arc<ClusterInfo>,
pub bank_forks: Arc<RwLock<BankForks>>,
accountsdb_repl_service: Option<AccountsDbReplService>,
geyser_plugin_service: Option<GeyserPluginService>,
accountsdb_plugin_service: Option<AccountsDbPluginService>,
}
// in the distant future, get rid of ::new()/exit() and use Result properly...
@@ -377,16 +338,18 @@ impl Validator {
let mut bank_notification_senders = Vec::new();
let geyser_plugin_service =
if let Some(geyser_plugin_config_files) = &config.geyser_plugin_config_files {
let accountsdb_plugin_service =
if let Some(accountsdb_plugin_config_files) = &config.accountsdb_plugin_config_files {
let (confirmed_bank_sender, confirmed_bank_receiver) = unbounded();
bank_notification_senders.push(confirmed_bank_sender);
let result =
GeyserPluginService::new(confirmed_bank_receiver, geyser_plugin_config_files);
let result = AccountsDbPluginService::new(
confirmed_bank_receiver,
accountsdb_plugin_config_files,
);
match result {
Ok(geyser_plugin_service) => Some(geyser_plugin_service),
Ok(accountsdb_plugin_service) => Some(accountsdb_plugin_service),
Err(err) => {
error!("Failed to load the Geyser plugin: {:?}", err);
error!("Failed to load the AccountsDb plugin: {:?}", err);
abort();
}
}
@@ -460,37 +423,46 @@ impl Validator {
let accounts_package_channel = unbounded();
let accounts_update_notifier = geyser_plugin_service
.as_ref()
.and_then(|geyser_plugin_service| geyser_plugin_service.get_accounts_update_notifier());
let accounts_update_notifier =
accountsdb_plugin_service
.as_ref()
.and_then(|accountsdb_plugin_service| {
accountsdb_plugin_service.get_accounts_update_notifier()
});
let transaction_notifier = geyser_plugin_service
.as_ref()
.and_then(|geyser_plugin_service| geyser_plugin_service.get_transaction_notifier());
let transaction_notifier =
accountsdb_plugin_service
.as_ref()
.and_then(|accountsdb_plugin_service| {
accountsdb_plugin_service.get_transaction_notifier()
});
let block_metadata_notifier = geyser_plugin_service
.as_ref()
.and_then(|geyser_plugin_service| geyser_plugin_service.get_block_metadata_notifier());
let block_metadata_notifier =
accountsdb_plugin_service
.as_ref()
.and_then(|accountsdb_plugin_service| {
accountsdb_plugin_service.get_block_metadata_notifier()
});
info!(
"Geyser plugin: accounts_update_notifier: {} transaction_notifier: {}",
"AccountsDb plugin: accounts_update_notifier: {} transaction_notifier: {}",
accounts_update_notifier.is_some(),
transaction_notifier.is_some()
);
let system_monitor_service = Some(SystemMonitorService::new(
Arc::clone(&exit),
!config.no_os_memory_stats_reporting,
!config.no_os_network_stats_reporting,
));
let (
genesis_config,
mut bank_forks,
bank_forks,
blockstore,
ledger_signal_receiver,
completed_slots_receiver,
leader_schedule_cache,
last_full_snapshot_slot,
starting_snapshot_hashes,
TransactionHistoryServices {
transaction_status_sender,
@@ -501,46 +473,22 @@ impl Validator {
cache_block_meta_sender,
cache_block_meta_service,
},
blockstore_process_options,
blockstore_root_scan,
) = load_blockstore(
tower,
) = new_banks_from_ledger(
&id,
vote_account,
config,
ledger_path,
config.poh_verify,
&exit,
config.enforce_ulimit_nofile,
&start_progress,
config.no_poh_speed_test,
accounts_package_channel.0.clone(),
accounts_update_notifier,
transaction_notifier,
);
let last_full_snapshot_slot = process_blockstore(
&blockstore,
&mut bank_forks,
&leader_schedule_cache,
&blockstore_process_options,
transaction_status_sender.as_ref(),
cache_block_meta_sender.as_ref(),
config.snapshot_config.as_ref(),
accounts_package_channel.0.clone(),
blockstore_root_scan,
);
let last_full_snapshot_slot =
last_full_snapshot_slot.or_else(|| starting_snapshot_hashes.map(|x| x.full.hash.0));
maybe_warp_slot(config, ledger_path, &mut bank_forks, &leader_schedule_cache);
let tower = {
let restored_tower = Tower::restore(config.tower_storage.as_ref(), &id);
if let Ok(tower) = &restored_tower {
reconcile_blockstore_roots_with_tower(tower, &blockstore).unwrap_or_else(|err| {
error!("Failed to reconcile blockstore with tower: {:?}", err);
abort()
});
}
post_process_restored_tower(restored_tower, &id, vote_account, config, &bank_forks)
};
info!("Tower state: {:?}", tower);
*start_progress.write().unwrap() = ValidatorStartProgress::StartingServices;
if !config.no_os_network_stats_reporting {
@@ -551,6 +499,10 @@ impl Validator {
}
let leader_schedule_cache = Arc::new(leader_schedule_cache);
let bank = bank_forks.working_bank();
if let Some(ref shrink_paths) = config.account_shrink_paths {
bank.set_shrink_paths(shrink_paths.clone());
}
let bank_forks = Arc::new(RwLock::new(bank_forks));
let sample_performance_service =
@@ -564,7 +516,6 @@ impl Validator {
None
};
let bank = bank_forks.read().unwrap().working_bank();
info!("Starting validator with working bank slot {}", bank.slot());
{
let hard_forks: Vec<_> = bank.hard_forks().read().unwrap().iter().copied().collect();
@@ -858,8 +809,7 @@ impl Validator {
let vote_tracker = Arc::<VoteTracker>::default();
let mut cost_model = CostModel::default();
// initialize cost model with built-in instruction costs only
cost_model.initialize_cost_table(&[]);
cost_model.initialize_cost_table(&blockstore.read_program_costs().unwrap());
let cost_model = Arc::new(RwLock::new(cost_model));
let (retransmit_slots_sender, retransmit_slots_receiver) = unbounded();
@@ -987,7 +937,7 @@ impl Validator {
cluster_info,
bank_forks,
accountsdb_repl_service,
geyser_plugin_service,
accountsdb_plugin_service,
}
}
@@ -1109,8 +1059,10 @@ impl Validator {
.expect("accountsdb_repl_service");
}
if let Some(geyser_plugin_service) = self.geyser_plugin_service {
geyser_plugin_service.join().expect("geyser_plugin_service");
if let Some(accountsdb_plugin_service) = self.accountsdb_plugin_service {
accountsdb_plugin_service
.join()
.expect("accountsdb_plugin_service");
}
}
}
@@ -1242,12 +1194,18 @@ fn post_process_restored_tower(
})
}
#[allow(clippy::type_complexity)]
fn load_blockstore(
#[allow(clippy::type_complexity, clippy::too_many_arguments)]
fn new_banks_from_ledger(
validator_identity: &Pubkey,
vote_account: &Pubkey,
config: &ValidatorConfig,
ledger_path: &Path,
poh_verify: bool,
exit: &Arc<AtomicBool>,
enforce_ulimit_nofile: bool,
start_progress: &Arc<RwLock<ValidatorStartProgress>>,
no_poh_speed_test: bool,
accounts_package_sender: AccountsPackageSender,
accounts_update_notifier: Option<AccountsUpdateNotifier>,
transaction_notifier: Option<TransactionNotifierLock>,
) -> (
@@ -1257,10 +1215,10 @@ fn load_blockstore(
Receiver<bool>,
CompletedSlotsReceiver,
LeaderScheduleCache,
Option<Slot>,
Option<StartingSnapshotHashes>,
TransactionHistoryServices,
blockstore_processor::ProcessOptions,
BlockstoreRootScan,
Tower,
) {
info!("loading ledger from {:?}...", ledger_path);
*start_progress.write().unwrap() = ValidatorStartProgress::LoadingLedger;
@@ -1284,7 +1242,7 @@ fn load_blockstore(
}
}
if !config.no_poh_speed_test {
if !no_poh_speed_test {
check_poh_speed(&genesis_config, None);
}
@@ -1297,20 +1255,41 @@ fn load_blockstore(
ledger_path,
BlockstoreOptions {
recovery_mode: config.wal_recovery_mode.clone(),
column_options: config.ledger_column_options.clone(),
enforce_ulimit_nofile: config.enforce_ulimit_nofile,
enforce_ulimit_nofile,
..BlockstoreOptions::default()
},
)
.expect("Failed to open ledger database");
blockstore.set_no_compaction(config.no_rocksdb_compaction);
let restored_tower = Tower::restore(config.tower_storage.as_ref(), validator_identity);
if let Ok(tower) = &restored_tower {
reconcile_blockstore_roots_with_tower(tower, &blockstore).unwrap_or_else(|err| {
error!("Failed to reconcile blockstore with tower: {:?}", err);
abort()
});
}
let blockstore = Arc::new(blockstore);
let blockstore_root_scan = BlockstoreRootScan::new(config, &blockstore, exit);
let blockstore_root_scan = if config.rpc_addrs.is_some()
&& config.rpc_config.enable_rpc_transaction_history
&& config.rpc_config.rpc_scan_and_fix_roots
{
let blockstore = blockstore.clone();
let exit = exit.clone();
Some(
Builder::new()
.name("blockstore-root-scan".to_string())
.spawn(move || blockstore.scan_and_fix_roots(&exit))
.unwrap(),
)
} else {
None
};
let process_options = blockstore_processor::ProcessOptions {
bpf_jit: config.bpf_jit,
poh_verify: config.poh_verify,
poh_verify,
dev_halt_at_slot: config.dev_halt_at_slot,
new_hard_forks: config.new_hard_forks.clone(),
debug_keys: config.debug_keys.clone(),
@@ -1339,80 +1318,32 @@ fn load_blockstore(
TransactionHistoryServices::default()
};
let (mut bank_forks, mut leader_schedule_cache, starting_snapshot_hashes) =
bank_forks_utils::load_bank_forks(
&genesis_config,
&blockstore,
config.account_paths.clone(),
config.account_shrink_paths.clone(),
config.snapshot_config.as_ref(),
&process_options,
transaction_history_services
.cache_block_meta_sender
.as_ref(),
accounts_update_notifier,
);
leader_schedule_cache.set_fixed_leader_schedule(config.fixed_leader_schedule.clone());
bank_forks.set_snapshot_config(config.snapshot_config.clone());
bank_forks.set_accounts_hash_interval_slots(config.accounts_hash_interval_slots);
if let Some(ref shrink_paths) = config.account_shrink_paths {
bank_forks
.working_bank()
.set_shrink_paths(shrink_paths.clone());
}
(
genesis_config,
bank_forks,
blockstore,
ledger_signal_receiver,
completed_slots_receiver,
leader_schedule_cache,
let (
mut bank_forks,
mut leader_schedule_cache,
last_full_snapshot_slot,
starting_snapshot_hashes,
transaction_history_services,
) = bank_forks_utils::load(
&genesis_config,
&blockstore,
config.account_paths.clone(),
config.account_shrink_paths.clone(),
config.snapshot_config.as_ref(),
process_options,
blockstore_root_scan,
)
}
fn process_blockstore(
blockstore: &Blockstore,
bank_forks: &mut BankForks,
leader_schedule_cache: &LeaderScheduleCache,
process_options: &blockstore_processor::ProcessOptions,
transaction_status_sender: Option<&TransactionStatusSender>,
cache_block_meta_sender: Option<&CacheBlockMetaSender>,
snapshot_config: Option<&SnapshotConfig>,
accounts_package_sender: AccountsPackageSender,
blockstore_root_scan: BlockstoreRootScan,
) -> Option<Slot> {
let last_full_snapshot_slot = blockstore_processor::process_blockstore_from_root(
blockstore,
bank_forks,
leader_schedule_cache,
process_options,
transaction_status_sender,
cache_block_meta_sender,
snapshot_config,
transaction_history_services
.transaction_status_sender
.as_ref(),
transaction_history_services
.cache_block_meta_sender
.as_ref(),
accounts_package_sender,
accounts_update_notifier,
)
.unwrap_or_else(|err| {
error!("Failed to load ledger: {:?}", err);
abort()
});
blockstore_root_scan.join();
last_full_snapshot_slot
}
fn maybe_warp_slot(
config: &ValidatorConfig,
ledger_path: &Path,
bank_forks: &mut BankForks,
leader_schedule_cache: &LeaderScheduleCache,
) {
if let Some(warp_slot) = config.warp_slot {
let snapshot_config = config.snapshot_config.as_ref().unwrap_or_else(|| {
error!("warp slot requires a snapshot config");
@@ -1461,6 +1392,40 @@ fn maybe_warp_slot(
full_snapshot_archive_info.path().display()
);
}
let tower = post_process_restored_tower(
restored_tower,
validator_identity,
vote_account,
config,
&bank_forks,
);
info!("Tower state: {:?}", tower);
leader_schedule_cache.set_fixed_leader_schedule(config.fixed_leader_schedule.clone());
bank_forks.set_snapshot_config(config.snapshot_config.clone());
bank_forks.set_accounts_hash_interval_slots(config.accounts_hash_interval_slots);
if let Some(blockstore_root_scan) = blockstore_root_scan {
if let Err(err) = blockstore_root_scan.join() {
warn!("blockstore_root_scan failed to join {:?}", err);
}
}
(
genesis_config,
bank_forks,
blockstore,
ledger_signal_receiver,
completed_slots_receiver,
leader_schedule_cache,
last_full_snapshot_slot,
starting_snapshot_hashes,
transaction_history_services,
tower,
)
}
fn blockstore_contains_bad_shred_version(

View File

@@ -1,13 +1,13 @@
//! Fork Selection Simulation
//!
//! Description of the algorithm can be found in [docs/src/cluster/managing-forks.md](docs/src/cluster/managing-forks.md).
//! Description of the algorithm can be found in [docs/src/fork-selection.md](docs/src/fork-selection.md).
//!
//! A test library function exists for configuring networks.
//! ```
//! /// * num_partitions - 1 to 100 partitions
//! /// * fail_rate - 0 to 1.0 rate of packet receive failure
//! /// * delay_count - number of forks to observe before voting
//! /// * parasite_rate - percentage of parasite nodes that vote opposite the greedy choice
//! /// * parasite_rate - number of parasite nodes that vote opposite the greedy choice
//! fn test_with_partitions(num_partitions: usize, fail_rate: f64, delay_count: usize, parasite_rate: f64);
//! ```
//! Modify the test function
@@ -497,7 +497,7 @@ fn test_no_partitions() {
/// * num_partitions - 1 to 100 partitions
/// * fail_rate - 0 to 1.0 rate of packet receive failure
/// * delay_count - number of forks to observe before voting
/// * parasite_rate - percentage of parasite nodes that vote opposite the greedy choice
/// * parasite_rate - number of parasite nodes that vote opposite the greedy choice
fn test_with_partitions(
num_partitions: usize,
fail_rate: f64,

View File

@@ -9,10 +9,7 @@ mod tests {
solana_core::ledger_cleanup_service::LedgerCleanupService,
solana_ledger::{
blockstore::{make_many_slot_shreds, Blockstore},
blockstore_db::{
BlockstoreOptions, BlockstoreRocksFifoOptions, LedgerColumnOptions,
ShredStorageType,
},
blockstore_db::{BlockstoreOptions, BlockstoreRocksFifoOptions, ShredStorageType},
get_tmp_ledger_path,
},
solana_measure::measure::Measure,
@@ -297,7 +294,7 @@ mod tests {
*time_previous = time_now;
*storage_previous = storage_now;
*data_shred_storage_previous = data_shred_storage_now.try_into().unwrap();
*data_shred_storage_previous = data_shred_storage_now;
}
/// Helper function of the benchmark `test_ledger_cleanup_compaction` which
@@ -351,14 +348,10 @@ mod tests {
&ledger_path,
if config.fifo_compaction {
BlockstoreOptions {
column_options: LedgerColumnOptions {
shred_storage_type: ShredStorageType::RocksFifo(
BlockstoreRocksFifoOptions {
shred_data_cf_size: config.shred_data_cf_size,
..BlockstoreRocksFifoOptions::default()
},
),
},
shred_storage_type: ShredStorageType::RocksFifo(BlockstoreRocksFifoOptions {
shred_data_cf_size: config.shred_data_cf_size,
..BlockstoreRocksFifoOptions::default()
}),
..BlockstoreOptions::default()
}
} else {

View File

@@ -175,7 +175,7 @@ mod tests {
let check_hash_calculation = false;
let full_snapshot_archive_path = snapshot_utils::build_full_snapshot_archive_path(
snapshot_archives_dir,
snapshot_archives_dir.to_path_buf(),
old_last_bank.slot(),
&old_last_bank.get_accounts_hash(),
ArchiveFormat::TarBzip2,
@@ -432,7 +432,7 @@ mod tests {
// Only save off the files returned by `get_snapshot_storages`. This is because
// some of the storage entries in the accounts directory may be filtered out by
// `get_snapshot_storages()` and will not be included in the snapshot. Ultimately,
// this means copying natively everything in `accounts_dir` to the `saved_accounts_dir`
// this means copying naitvely everything in `accounts_dir` to the `saved_accounts_dir`
// will lead to test failure by mismatch when `saved_accounts_dir` is compared to
// the unpacked snapshot later in this test's call to `verify_snapshot_archive()`.
for file in snapshot_storage_files {
@@ -461,7 +461,7 @@ mod tests {
fs_extra::dir::copy(&last_snapshot_path, &saved_snapshots_dir, &options).unwrap();
saved_archive_path = Some(snapshot_utils::build_full_snapshot_archive_path(
snapshot_archives_dir,
snapshot_archives_dir.to_path_buf(),
slot,
&accounts_hash,
ArchiveFormat::TarBzip2,
@@ -544,7 +544,7 @@ mod tests {
snapshot_utils::serialize_snapshot_data_file(
&saved_snapshots_dir
.path()
.join(snapshot_utils::SNAPSHOT_STATUS_CACHE_FILENAME),
.join(snapshot_utils::SNAPSHOT_STATUS_CACHE_FILE_NAME),
|stream| {
serialize_into(stream, &[] as &[BankSlotDelta])?;
Ok(())

Some files were not shown because too many files have changed in this diff Show More