Compare commits
33 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
0707290bbf | ||
|
8934a3961f | ||
|
b142ef5f8b | ||
|
f2dc9dd96e | ||
|
20ad3005b5 | ||
|
eacc69efba | ||
|
3c200ae45a | ||
|
74e7c7cbd0 | ||
|
6bd6d6212c | ||
|
4d2e66cf9c | ||
|
65fe00b7ad | ||
|
02c509390a | ||
|
49b0d1792b | ||
|
9511031490 | ||
|
04ee86e93c | ||
|
82f25f982e | ||
|
1cc32c9cda | ||
|
aedcab846c | ||
|
548ddff7ed | ||
|
7aced9e772 | ||
|
1cc8de0fed | ||
|
f08c7b2294 | ||
|
cc58d36de6 | ||
|
128393da54 | ||
|
e7964a0b89 | ||
|
77dc3746a3 | ||
|
e88f4d689c | ||
|
f1858c74a4 | ||
|
7427dafc36 | ||
|
1cdcabf7cf | ||
|
ea192b3c83 | ||
|
927057df26 | ||
|
19049ca91b |
428
Cargo.lock
generated
428
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -76,5 +76,10 @@ exclude = [
|
||||
"programs/bpf",
|
||||
]
|
||||
|
||||
# TODO: Remove once the "simd-accel" feature from the reed-solomon-erasure
|
||||
# dependency is supported on Apple M1. v2 of the feature resolver is needed to
|
||||
# specify arch-specific features.
|
||||
resolver = "2"
|
||||
|
||||
[profile.dev]
|
||||
split-debuginfo = "unpacked"
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-account-decoder"
|
||||
version = "1.7.6"
|
||||
version = "1.7.9"
|
||||
description = "Solana account decoder"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -19,10 +19,10 @@ lazy_static = "1.4.0"
|
||||
serde = "1.0.122"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.56"
|
||||
solana-config-program = { path = "../programs/config", version = "=1.7.6" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.6" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.7.6" }
|
||||
spl-token-v2-0 = { package = "spl-token", version = "=3.1.1", features = ["no-entrypoint"] }
|
||||
solana-config-program = { path = "../programs/config", version = "=1.7.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.9" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.7.9" }
|
||||
spl-token-v2-0 = { package = "spl-token", version = "=3.2.0", features = ["no-entrypoint"] }
|
||||
thiserror = "1.0"
|
||||
zstd = "0.5.1"
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-accounts-bench"
|
||||
version = "1.7.6"
|
||||
version = "1.7.9"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -11,11 +11,11 @@ publish = false
|
||||
[dependencies]
|
||||
log = "0.4.11"
|
||||
rayon = "1.5.0"
|
||||
solana-logger = { path = "../logger", version = "=1.7.6" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.6" }
|
||||
solana-measure = { path = "../measure", version = "=1.7.6" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.6" }
|
||||
solana-version = { path = "../version", version = "=1.7.6" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.9" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.9" }
|
||||
solana-measure = { path = "../measure", version = "=1.7.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.9" }
|
||||
solana-version = { path = "../version", version = "=1.7.9" }
|
||||
rand = "0.7.0"
|
||||
clap = "2.33.1"
|
||||
crossbeam-channel = "0.4"
|
||||
|
@@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-accounts-cluster-bench"
|
||||
version = "1.7.6"
|
||||
version = "1.7.9"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -13,23 +13,24 @@ clap = "2.33.1"
|
||||
log = "0.4.11"
|
||||
rand = "0.7.0"
|
||||
rayon = "1.4.1"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.7.6" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.6" }
|
||||
solana-client = { path = "../client", version = "=1.7.6" }
|
||||
solana-core = { path = "../core", version = "=1.7.6" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.7.6" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.7.6" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.6" }
|
||||
solana-measure = { path = "../measure", version = "=1.7.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.6" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.6" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.6" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.7.6" }
|
||||
solana-version = { path = "../version", version = "=1.7.6" }
|
||||
spl-token-v2-0 = { package = "spl-token", version = "=3.1.1", features = ["no-entrypoint"] }
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.7.9" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.9" }
|
||||
solana-client = { path = "../client", version = "=1.7.9" }
|
||||
solana-core = { path = "../core", version = "=1.7.9" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.7.9" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.7.9" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.9" }
|
||||
solana-measure = { path = "../measure", version = "=1.7.9" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.9" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.9" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.7.9" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.7.9" }
|
||||
solana-version = { path = "../version", version = "=1.7.9" }
|
||||
spl-token-v2-0 = { package = "spl-token", version = "=3.2.0", features = ["no-entrypoint"] }
|
||||
|
||||
[dev-dependencies]
|
||||
solana-local-cluster = { path = "../local-cluster", version = "=1.7.6" }
|
||||
solana-local-cluster = { path = "../local-cluster", version = "=1.7.9" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -20,6 +20,7 @@ use solana_sdk::{
|
||||
timing::timestamp,
|
||||
transaction::Transaction,
|
||||
};
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
use solana_transaction_status::parse_token::spl_token_v2_0_instruction;
|
||||
use std::{
|
||||
net::SocketAddr,
|
||||
@@ -670,6 +671,7 @@ fn main() {
|
||||
Some(&entrypoint_addr), // find_node_by_gossip_addr
|
||||
None, // my_gossip_addr
|
||||
0, // my_shred_version
|
||||
SocketAddrSpace::Unspecified,
|
||||
)
|
||||
.unwrap_or_else(|err| {
|
||||
eprintln!("Failed to discover {} node: {:?}", entrypoint_addr, err);
|
||||
@@ -721,7 +723,7 @@ pub mod test {
|
||||
};
|
||||
|
||||
let faucet_addr = SocketAddr::from(([127, 0, 0, 1], 9900));
|
||||
let cluster = LocalCluster::new(&mut config);
|
||||
let cluster = LocalCluster::new(&mut config, SocketAddrSpace::Unspecified);
|
||||
let iterations = 10;
|
||||
let maybe_space = None;
|
||||
let batch_size = 100;
|
||||
|
@@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-banking-bench"
|
||||
version = "1.7.6"
|
||||
version = "1.7.9"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -14,18 +14,18 @@ crossbeam-channel = "0.4"
|
||||
log = "0.4.11"
|
||||
rand = "0.7.0"
|
||||
rayon = "1.5.0"
|
||||
solana-core = { path = "../core", version = "=1.7.6" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.6" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.7.6" }
|
||||
solana-ledger = { path = "../ledger", version = "=1.7.6" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.6" }
|
||||
solana-measure = { path = "../measure", version = "=1.7.6" }
|
||||
solana-perf = { path = "../perf", version = "=1.7.6" }
|
||||
solana-poh = { path = "../poh", version = "=1.7.6" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.6" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.7.6" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.6" }
|
||||
solana-version = { path = "../version", version = "=1.7.6" }
|
||||
solana-core = { path = "../core", version = "=1.7.9" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.9" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.7.9" }
|
||||
solana-ledger = { path = "../ledger", version = "=1.7.9" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.9" }
|
||||
solana-measure = { path = "../measure", version = "=1.7.9" }
|
||||
solana-perf = { path = "../perf", version = "=1.7.9" }
|
||||
solana-poh = { path = "../poh", version = "=1.7.9" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.9" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.7.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.9" }
|
||||
solana-version = { path = "../version", version = "=1.7.9" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -25,6 +25,7 @@ use solana_sdk::{
|
||||
timing::{duration_as_us, timestamp},
|
||||
transaction::Transaction,
|
||||
};
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
use std::{
|
||||
sync::{atomic::Ordering, mpsc::Receiver, Arc, Mutex},
|
||||
thread::sleep,
|
||||
@@ -215,7 +216,11 @@ fn main() {
|
||||
);
|
||||
let (exit, poh_recorder, poh_service, signal_receiver) =
|
||||
create_test_recorder(&bank, &blockstore, None);
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(Node::new_localhost().info);
|
||||
let cluster_info = ClusterInfo::new(
|
||||
Node::new_localhost().info,
|
||||
Arc::new(Keypair::new()),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
let cluster_info = Arc::new(cluster_info);
|
||||
let banking_stage = BankingStage::new(
|
||||
&cluster_info,
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-banks-client"
|
||||
version = "1.7.6"
|
||||
version = "1.7.9"
|
||||
description = "Solana banks client"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -15,16 +15,16 @@ borsh = "0.9.0"
|
||||
borsh-derive = "0.9.0"
|
||||
futures = "0.3"
|
||||
mio = "0.7.6"
|
||||
solana-banks-interface = { path = "../banks-interface", version = "=1.7.6" }
|
||||
solana-program = { path = "../sdk/program", version = "=1.7.6" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.6" }
|
||||
solana-banks-interface = { path = "../banks-interface", version = "=1.7.9" }
|
||||
solana-program = { path = "../sdk/program", version = "=1.7.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.9" }
|
||||
tarpc = { version = "0.24.1", features = ["full"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-serde = { version = "0.8", features = ["bincode"] }
|
||||
|
||||
[dev-dependencies]
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.6" }
|
||||
solana-banks-server = { path = "../banks-server", version = "=1.7.6" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.9" }
|
||||
solana-banks-server = { path = "../banks-server", version = "=1.7.9" }
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib"]
|
||||
|
@@ -10,8 +10,14 @@ use futures::{future::join_all, Future, FutureExt};
|
||||
pub use solana_banks_interface::{BanksClient as TarpcClient, TransactionStatus};
|
||||
use solana_banks_interface::{BanksRequest, BanksResponse};
|
||||
use solana_program::{
|
||||
clock::Slot, fee_calculator::FeeCalculator, hash::Hash, program_pack::Pack, pubkey::Pubkey,
|
||||
rent::Rent, sysvar::Sysvar,
|
||||
clock::Clock,
|
||||
clock::Slot,
|
||||
fee_calculator::FeeCalculator,
|
||||
hash::Hash,
|
||||
program_pack::Pack,
|
||||
pubkey::Pubkey,
|
||||
rent::Rent,
|
||||
sysvar::{self, Sysvar},
|
||||
};
|
||||
use solana_sdk::{
|
||||
account::{from_account, Account},
|
||||
@@ -115,6 +121,17 @@ impl BanksClient {
|
||||
self.send_transaction_with_context(context::current(), transaction)
|
||||
}
|
||||
|
||||
/// Return the cluster clock
|
||||
pub fn get_clock(&mut self) -> impl Future<Output = io::Result<Clock>> + '_ {
|
||||
self.get_account(sysvar::clock::id()).map(|result| {
|
||||
let clock_sysvar = result?
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Clock sysvar not present"))?;
|
||||
from_account::<Clock, _>(&clock_sysvar).ok_or_else(|| {
|
||||
io::Error::new(io::ErrorKind::Other, "Failed to deserialize Clock sysvar")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Return the fee parameters associated with a recent, rooted blockhash. The cluster
|
||||
/// will use the transaction's blockhash to look up these same fee parameters and
|
||||
/// use them to calculate the transaction fee.
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-banks-interface"
|
||||
version = "1.7.6"
|
||||
version = "1.7.9"
|
||||
description = "Solana banks RPC interface"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -12,7 +12,7 @@ edition = "2018"
|
||||
[dependencies]
|
||||
mio = "0.7.6"
|
||||
serde = { version = "1.0.122", features = ["derive"] }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.6" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.9" }
|
||||
tarpc = { version = "0.24.1", features = ["full"] }
|
||||
|
||||
[dev-dependencies]
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-banks-server"
|
||||
version = "1.7.6"
|
||||
version = "1.7.9"
|
||||
description = "Solana banks server"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -14,10 +14,10 @@ bincode = "1.3.1"
|
||||
futures = "0.3"
|
||||
log = "0.4.11"
|
||||
mio = "0.7.6"
|
||||
solana-banks-interface = { path = "../banks-interface", version = "=1.7.6" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.6" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.6" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.7.6" }
|
||||
solana-banks-interface = { path = "../banks-interface", version = "=1.7.9" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.9" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.7.9" }
|
||||
tarpc = { version = "0.24.1", features = ["full"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-serde = { version = "0.8", features = ["bincode"] }
|
||||
|
@@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-bench-exchange"
|
||||
version = "1.7.6"
|
||||
version = "1.7.9"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -18,22 +18,23 @@ rand = "0.7.0"
|
||||
rayon = "1.5.0"
|
||||
serde_json = "1.0.56"
|
||||
serde_yaml = "0.8.13"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.6" }
|
||||
solana-core = { path = "../core", version = "=1.7.6" }
|
||||
solana-genesis = { path = "../genesis", version = "=1.7.6" }
|
||||
solana-client = { path = "../client", version = "=1.7.6" }
|
||||
solana-exchange-program = { path = "../programs/exchange", version = "=1.7.6" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.7.6" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.7.6" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.6" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.7.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.6" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.6" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.6" }
|
||||
solana-version = { path = "../version", version = "=1.7.6" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.9" }
|
||||
solana-core = { path = "../core", version = "=1.7.9" }
|
||||
solana-genesis = { path = "../genesis", version = "=1.7.9" }
|
||||
solana-client = { path = "../client", version = "=1.7.9" }
|
||||
solana-exchange-program = { path = "../programs/exchange", version = "=1.7.9" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.7.9" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.7.9" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.9" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.7.9" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.9" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.9" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.7.9" }
|
||||
solana-version = { path = "../version", version = "=1.7.9" }
|
||||
|
||||
[dev-dependencies]
|
||||
solana-local-cluster = { path = "../local-cluster", version = "=1.7.6" }
|
||||
solana-local-cluster = { path = "../local-cluster", version = "=1.7.9" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -7,6 +7,7 @@ use crate::bench::{airdrop_lamports, create_client_accounts_file, do_bench_excha
|
||||
use log::*;
|
||||
use solana_gossip::gossip_service::{discover_cluster, get_multi_client};
|
||||
use solana_sdk::signature::Signer;
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
|
||||
fn main() {
|
||||
solana_logger::setup();
|
||||
@@ -55,11 +56,12 @@ fn main() {
|
||||
);
|
||||
} else {
|
||||
info!("Connecting to the cluster");
|
||||
let nodes = discover_cluster(&entrypoint_addr, num_nodes).unwrap_or_else(|_| {
|
||||
panic!("Failed to discover nodes");
|
||||
});
|
||||
let nodes = discover_cluster(&entrypoint_addr, num_nodes, SocketAddrSpace::Unspecified)
|
||||
.unwrap_or_else(|_| {
|
||||
panic!("Failed to discover nodes");
|
||||
});
|
||||
|
||||
let (client, num_clients) = get_multi_client(&nodes);
|
||||
let (client, num_clients) = get_multi_client(&nodes, &SocketAddrSpace::Unspecified);
|
||||
|
||||
info!("{} nodes found", num_clients);
|
||||
if num_clients < num_nodes {
|
||||
|
@@ -15,6 +15,7 @@ use solana_sdk::{
|
||||
genesis_config::create_genesis_config,
|
||||
signature::{Keypair, Signer},
|
||||
};
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
use std::{process::exit, sync::mpsc::channel, time::Duration};
|
||||
|
||||
#[test]
|
||||
@@ -43,13 +44,19 @@ fn test_exchange_local_cluster() {
|
||||
} = config;
|
||||
let accounts_in_groups = batch_size * account_groups;
|
||||
|
||||
let cluster = LocalCluster::new(&mut ClusterConfig {
|
||||
node_stakes: vec![100_000; NUM_NODES],
|
||||
cluster_lamports: 100_000_000_000_000,
|
||||
validator_configs: make_identical_validator_configs(&ValidatorConfig::default(), NUM_NODES),
|
||||
native_instruction_processors: [solana_exchange_program!()].to_vec(),
|
||||
..ClusterConfig::default()
|
||||
});
|
||||
let cluster = LocalCluster::new(
|
||||
&mut ClusterConfig {
|
||||
node_stakes: vec![100_000; NUM_NODES],
|
||||
cluster_lamports: 100_000_000_000_000,
|
||||
validator_configs: make_identical_validator_configs(
|
||||
&ValidatorConfig::default(),
|
||||
NUM_NODES,
|
||||
),
|
||||
native_instruction_processors: [solana_exchange_program!()].to_vec(),
|
||||
..ClusterConfig::default()
|
||||
},
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
|
||||
let faucet_keypair = Keypair::new();
|
||||
cluster.transfer(
|
||||
@@ -66,13 +73,17 @@ fn test_exchange_local_cluster() {
|
||||
.expect("faucet_addr");
|
||||
|
||||
info!("Connecting to the cluster");
|
||||
let nodes =
|
||||
discover_cluster(&cluster.entry_point_info.gossip, NUM_NODES).unwrap_or_else(|err| {
|
||||
error!("Failed to discover {} nodes: {:?}", NUM_NODES, err);
|
||||
exit(1);
|
||||
});
|
||||
let nodes = discover_cluster(
|
||||
&cluster.entry_point_info.gossip,
|
||||
NUM_NODES,
|
||||
SocketAddrSpace::Unspecified,
|
||||
)
|
||||
.unwrap_or_else(|err| {
|
||||
error!("Failed to discover {} nodes: {:?}", NUM_NODES, err);
|
||||
exit(1);
|
||||
});
|
||||
|
||||
let (client, num_clients) = get_multi_client(&nodes);
|
||||
let (client, num_clients) = get_multi_client(&nodes, &SocketAddrSpace::Unspecified);
|
||||
|
||||
info!("clients: {}", num_clients);
|
||||
assert!(num_clients >= NUM_NODES);
|
||||
|
@@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-bench-streamer"
|
||||
version = "1.7.6"
|
||||
version = "1.7.9"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -10,11 +10,11 @@ publish = false
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33.1"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.6" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.7.6" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.6" }
|
||||
solana-version = { path = "../version", version = "=1.7.6" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.9" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.7.9" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.9" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.9" }
|
||||
solana-version = { path = "../version", version = "=1.7.9" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-bench-tps"
|
||||
version = "1.7.6"
|
||||
version = "1.7.9"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -15,23 +15,24 @@ log = "0.4.11"
|
||||
rayon = "1.5.0"
|
||||
serde_json = "1.0.56"
|
||||
serde_yaml = "0.8.13"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.6" }
|
||||
solana-core = { path = "../core", version = "=1.7.6" }
|
||||
solana-genesis = { path = "../genesis", version = "=1.7.6" }
|
||||
solana-client = { path = "../client", version = "=1.7.6" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.7.6" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.7.6" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.6" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.7.6" }
|
||||
solana-measure = { path = "../measure", version = "=1.7.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.6" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.6" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.6" }
|
||||
solana-version = { path = "../version", version = "=1.7.6" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.9" }
|
||||
solana-core = { path = "../core", version = "=1.7.9" }
|
||||
solana-genesis = { path = "../genesis", version = "=1.7.9" }
|
||||
solana-client = { path = "../client", version = "=1.7.9" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.7.9" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.7.9" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.9" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.7.9" }
|
||||
solana-measure = { path = "../measure", version = "=1.7.9" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.9" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.9" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.7.9" }
|
||||
solana-version = { path = "../version", version = "=1.7.9" }
|
||||
|
||||
[dev-dependencies]
|
||||
serial_test = "0.4.0"
|
||||
solana-local-cluster = { path = "../local-cluster", version = "=1.7.6" }
|
||||
solana-local-cluster = { path = "../local-cluster", version = "=1.7.9" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -7,6 +7,7 @@ use solana_gossip::gossip_service::{discover_cluster, get_client, get_multi_clie
|
||||
use solana_sdk::fee_calculator::FeeRateGovernor;
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use solana_sdk::system_program;
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
use std::{collections::HashMap, fs::File, io::prelude::*, path::Path, process::exit, sync::Arc};
|
||||
|
||||
/// Number of signatures for all transactions in ~1 week at ~100K TPS
|
||||
@@ -68,13 +69,14 @@ fn main() {
|
||||
}
|
||||
|
||||
info!("Connecting to the cluster");
|
||||
let nodes = discover_cluster(entrypoint_addr, *num_nodes).unwrap_or_else(|err| {
|
||||
eprintln!("Failed to discover {} nodes: {:?}", num_nodes, err);
|
||||
exit(1);
|
||||
});
|
||||
let nodes = discover_cluster(entrypoint_addr, *num_nodes, SocketAddrSpace::Unspecified)
|
||||
.unwrap_or_else(|err| {
|
||||
eprintln!("Failed to discover {} nodes: {:?}", num_nodes, err);
|
||||
exit(1);
|
||||
});
|
||||
|
||||
let client = if *multi_client {
|
||||
let (client, num_clients) = get_multi_client(&nodes);
|
||||
let (client, num_clients) = get_multi_client(&nodes, &SocketAddrSpace::Unspecified);
|
||||
if nodes.len() < num_clients {
|
||||
eprintln!(
|
||||
"Error: Insufficient nodes discovered. Expecting {} or more",
|
||||
@@ -88,7 +90,7 @@ fn main() {
|
||||
let mut target_client = None;
|
||||
for node in nodes {
|
||||
if node.id == *target_node {
|
||||
target_client = Some(Arc::new(get_client(&[node])));
|
||||
target_client = Some(Arc::new(get_client(&[node], &SocketAddrSpace::Unspecified)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -97,7 +99,7 @@ fn main() {
|
||||
exit(1);
|
||||
})
|
||||
} else {
|
||||
Arc::new(get_client(&nodes))
|
||||
Arc::new(get_client(&nodes, &SocketAddrSpace::Unspecified))
|
||||
};
|
||||
|
||||
let keypairs = if *read_from_client_file {
|
||||
|
@@ -13,6 +13,7 @@ use solana_local_cluster::{
|
||||
validator_configs::make_identical_validator_configs,
|
||||
};
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
use std::{
|
||||
sync::{mpsc::channel, Arc},
|
||||
time::Duration,
|
||||
@@ -23,13 +24,19 @@ fn test_bench_tps_local_cluster(config: Config) {
|
||||
|
||||
solana_logger::setup();
|
||||
const NUM_NODES: usize = 1;
|
||||
let cluster = LocalCluster::new(&mut ClusterConfig {
|
||||
node_stakes: vec![999_990; NUM_NODES],
|
||||
cluster_lamports: 200_000_000,
|
||||
validator_configs: make_identical_validator_configs(&ValidatorConfig::default(), NUM_NODES),
|
||||
native_instruction_processors,
|
||||
..ClusterConfig::default()
|
||||
});
|
||||
let cluster = LocalCluster::new(
|
||||
&mut ClusterConfig {
|
||||
node_stakes: vec![999_990; NUM_NODES],
|
||||
cluster_lamports: 200_000_000,
|
||||
validator_configs: make_identical_validator_configs(
|
||||
&ValidatorConfig::default(),
|
||||
NUM_NODES,
|
||||
),
|
||||
native_instruction_processors,
|
||||
..ClusterConfig::default()
|
||||
},
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
|
||||
let faucet_keypair = Keypair::new();
|
||||
cluster.transfer(
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-clap-utils"
|
||||
version = "1.7.6"
|
||||
version = "1.7.9"
|
||||
description = "Solana utilities for the clap"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -12,8 +12,8 @@ edition = "2018"
|
||||
[dependencies]
|
||||
clap = "2.33.0"
|
||||
rpassword = "4.0"
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "=1.7.6" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.6" }
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "=1.7.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.9" }
|
||||
thiserror = "1.0.21"
|
||||
tiny-bip39 = "0.8.0"
|
||||
uriparse = "0.6.3"
|
||||
|
@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-cli-config"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.7.6"
|
||||
version = "1.7.9"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
|
@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-cli-output"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.7.6"
|
||||
version = "1.7.9"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -12,6 +12,7 @@ documentation = "https://docs.rs/solana-cli-output"
|
||||
[dependencies]
|
||||
base64 = "0.13.0"
|
||||
chrono = { version = "0.4.11", features = ["serde"] }
|
||||
clap = "2.33.0"
|
||||
console = "0.14.1"
|
||||
humantime = "2.0.1"
|
||||
Inflector = "0.11.4"
|
||||
@@ -19,12 +20,12 @@ indicatif = "0.15.0"
|
||||
serde = "1.0.122"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.56"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.7.6" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.6" }
|
||||
solana-client = { path = "../client", version = "=1.7.6" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.6" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.7.6" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.7.6" }
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.7.9" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.9" }
|
||||
solana-client = { path = "../client", version = "=1.7.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.9" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.7.9" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.7.9" }
|
||||
spl-memo = { version = "=3.0.1", features = ["no-entrypoint"] }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
|
@@ -8,6 +8,7 @@ use {
|
||||
QuietDisplay, VerboseDisplay,
|
||||
},
|
||||
chrono::{Local, TimeZone},
|
||||
clap::ArgMatches,
|
||||
console::{style, Emoji},
|
||||
inflector::cases::titlecase::to_title_case,
|
||||
serde::{Deserialize, Serialize},
|
||||
@@ -47,7 +48,7 @@ use {
|
||||
|
||||
static WARNING: Emoji = Emoji("⚠️", "!");
|
||||
|
||||
#[derive(PartialEq)]
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum OutputFormat {
|
||||
Display,
|
||||
Json,
|
||||
@@ -77,6 +78,21 @@ impl OutputFormat {
|
||||
OutputFormat::JsonCompact => serde_json::to_value(item).unwrap().to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_matches(matches: &ArgMatches<'_>, output_name: &str, verbose: bool) -> Self {
|
||||
matches
|
||||
.value_of(output_name)
|
||||
.map(|value| match value {
|
||||
"json" => OutputFormat::Json,
|
||||
"json-compact" => OutputFormat::JsonCompact,
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.unwrap_or(if verbose {
|
||||
OutputFormat::DisplayVerbose
|
||||
} else {
|
||||
OutputFormat::Display
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
@@ -1572,7 +1588,7 @@ impl fmt::Display for CliInflation {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Default)]
|
||||
#[derive(Serialize, Deserialize, Default, Debug, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliSignOnlyData {
|
||||
pub blockhash: String,
|
||||
@@ -2024,6 +2040,11 @@ pub fn return_signers_with_config(
|
||||
output_format: &OutputFormat,
|
||||
config: &ReturnSignersConfig,
|
||||
) -> Result<String, Box<dyn std::error::Error>> {
|
||||
let cli_command = return_signers_data(tx, config);
|
||||
Ok(output_format.formatted_string(&cli_command))
|
||||
}
|
||||
|
||||
pub fn return_signers_data(tx: &Transaction, config: &ReturnSignersConfig) -> CliSignOnlyData {
|
||||
let verify_results = tx.verify_with_results();
|
||||
let mut signers = Vec::new();
|
||||
let mut absent = Vec::new();
|
||||
@@ -2048,15 +2069,13 @@ pub fn return_signers_with_config(
|
||||
None
|
||||
};
|
||||
|
||||
let cli_command = CliSignOnlyData {
|
||||
CliSignOnlyData {
|
||||
blockhash: tx.message.recent_blockhash.to_string(),
|
||||
message,
|
||||
signers,
|
||||
absent,
|
||||
bad_sig,
|
||||
};
|
||||
|
||||
Ok(output_format.formatted_string(&cli_command))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_sign_only_reply_string(reply: &str) -> SignOnly {
|
||||
@@ -2438,6 +2457,7 @@ impl VerboseDisplay for CliGossipNodes {}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use clap::{App, Arg};
|
||||
use solana_sdk::{
|
||||
message::Message,
|
||||
pubkey::Pubkey,
|
||||
@@ -2498,6 +2518,22 @@ mod tests {
|
||||
assert_eq!(sign_only.absent_signers[0], absent.pubkey());
|
||||
assert_eq!(sign_only.bad_signers[0], bad.pubkey());
|
||||
|
||||
let res_data = return_signers_data(&tx, &ReturnSignersConfig::default());
|
||||
assert_eq!(
|
||||
res_data,
|
||||
CliSignOnlyData {
|
||||
blockhash: blockhash.to_string(),
|
||||
message: None,
|
||||
signers: vec![format!(
|
||||
"{}={}",
|
||||
present.pubkey().to_string(),
|
||||
tx.signatures[1]
|
||||
)],
|
||||
absent: vec![absent.pubkey().to_string()],
|
||||
bad_sig: vec![bad.pubkey().to_string()],
|
||||
}
|
||||
);
|
||||
|
||||
let expected_msg = "AwECBwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDgTl3Dqh9\
|
||||
F19Wo1Rmw0x+zMuNipG07jeiXfYPW4/Js5QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE\
|
||||
BAQEBAYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBQUFBQUFBQUFBQUFBQUFBQUF\
|
||||
@@ -2511,10 +2547,26 @@ mod tests {
|
||||
let res = return_signers_with_config(&tx, &OutputFormat::JsonCompact, &config).unwrap();
|
||||
let sign_only = parse_sign_only_reply_string(&res);
|
||||
assert_eq!(sign_only.blockhash, blockhash);
|
||||
assert_eq!(sign_only.message, Some(expected_msg));
|
||||
assert_eq!(sign_only.message, Some(expected_msg.clone()));
|
||||
assert_eq!(sign_only.present_signers[0].0, present.pubkey());
|
||||
assert_eq!(sign_only.absent_signers[0], absent.pubkey());
|
||||
assert_eq!(sign_only.bad_signers[0], bad.pubkey());
|
||||
|
||||
let res_data = return_signers_data(&tx, &config);
|
||||
assert_eq!(
|
||||
res_data,
|
||||
CliSignOnlyData {
|
||||
blockhash: blockhash.to_string(),
|
||||
message: Some(expected_msg),
|
||||
signers: vec![format!(
|
||||
"{}={}",
|
||||
present.pubkey().to_string(),
|
||||
tx.signatures[1]
|
||||
)],
|
||||
absent: vec![absent.pubkey().to_string()],
|
||||
bad_sig: vec![bad.pubkey().to_string()],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2563,4 +2615,50 @@ mod tests {
|
||||
"verbose"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_output_format_from_matches() {
|
||||
let app = App::new("test").arg(
|
||||
Arg::with_name("output_format")
|
||||
.long("output")
|
||||
.value_name("FORMAT")
|
||||
.global(true)
|
||||
.takes_value(true)
|
||||
.possible_values(&["json", "json-compact"])
|
||||
.help("Return information in specified output format"),
|
||||
);
|
||||
let matches = app
|
||||
.clone()
|
||||
.get_matches_from(vec!["test", "--output", "json"]);
|
||||
assert_eq!(
|
||||
OutputFormat::from_matches(&matches, "output_format", false),
|
||||
OutputFormat::Json
|
||||
);
|
||||
assert_eq!(
|
||||
OutputFormat::from_matches(&matches, "output_format", true),
|
||||
OutputFormat::Json
|
||||
);
|
||||
|
||||
let matches = app
|
||||
.clone()
|
||||
.get_matches_from(vec!["test", "--output", "json-compact"]);
|
||||
assert_eq!(
|
||||
OutputFormat::from_matches(&matches, "output_format", false),
|
||||
OutputFormat::JsonCompact
|
||||
);
|
||||
assert_eq!(
|
||||
OutputFormat::from_matches(&matches, "output_format", true),
|
||||
OutputFormat::JsonCompact
|
||||
);
|
||||
|
||||
let matches = app.clone().get_matches_from(vec!["test"]);
|
||||
assert_eq!(
|
||||
OutputFormat::from_matches(&matches, "output_format", false),
|
||||
OutputFormat::Display
|
||||
);
|
||||
assert_eq!(
|
||||
OutputFormat::from_matches(&matches, "output_format", true),
|
||||
OutputFormat::DisplayVerbose
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-cli"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.7.6"
|
||||
version = "1.7.9"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -29,29 +29,30 @@ reqwest = { version = "0.11.2", default-features = false, features = ["blocking"
|
||||
serde = "1.0.122"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.56"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.7.6" }
|
||||
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.7.6" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.6" }
|
||||
solana-cli-config = { path = "../cli-config", version = "=1.7.6" }
|
||||
solana-cli-output = { path = "../cli-output", version = "=1.7.6" }
|
||||
solana-client = { path = "../client", version = "=1.7.6" }
|
||||
solana-config-program = { path = "../programs/config", version = "=1.7.6" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.7.6" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.6" }
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.7.9" }
|
||||
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.7.9" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.9" }
|
||||
solana-cli-config = { path = "../cli-config", version = "=1.7.9" }
|
||||
solana-cli-output = { path = "../cli-output", version = "=1.7.9" }
|
||||
solana-client = { path = "../client", version = "=1.7.9" }
|
||||
solana-config-program = { path = "../programs/config", version = "=1.7.9" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.7.9" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.9" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.9" }
|
||||
solana_rbpf = "=0.2.11"
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "=1.7.6" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.6" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.7.6" }
|
||||
solana-version = { path = "../version", version = "=1.7.6" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.7.6" }
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "=1.7.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.9" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.7.9" }
|
||||
solana-version = { path = "../version", version = "=1.7.9" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.7.9" }
|
||||
spl-memo = { version = "=3.0.1", features = ["no-entrypoint"] }
|
||||
thiserror = "1.0.21"
|
||||
tiny-bip39 = "0.7.0"
|
||||
url = "2.1.1"
|
||||
|
||||
[dev-dependencies]
|
||||
solana-core = { path = "../core", version = "=1.7.6" }
|
||||
solana-core = { path = "../core", version = "=1.7.9" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.7.9" }
|
||||
tempfile = "3.1.0"
|
||||
|
||||
[[bin]]
|
||||
|
202
cli/src/clap_app.rs
Normal file
202
cli/src/clap_app.rs
Normal file
@@ -0,0 +1,202 @@
|
||||
use crate::{
|
||||
cli::*, cluster_query::*, feature::*, inflation::*, nonce::*, program::*, stake::*,
|
||||
validator_info::*, vote::*, wallet::*,
|
||||
};
|
||||
use clap::{App, AppSettings, Arg, ArgGroup, SubCommand};
|
||||
use solana_clap_utils::{self, input_validators::*, keypair::*};
|
||||
use solana_cli_config::CONFIG_FILE;
|
||||
|
||||
pub fn get_clap_app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, 'v> {
|
||||
App::new(name)
|
||||
.about(about)
|
||||
.version(version)
|
||||
.setting(AppSettings::SubcommandRequiredElseHelp)
|
||||
.arg({
|
||||
let arg = Arg::with_name("config_file")
|
||||
.short("C")
|
||||
.long("config")
|
||||
.value_name("FILEPATH")
|
||||
.takes_value(true)
|
||||
.global(true)
|
||||
.help("Configuration file to use");
|
||||
if let Some(ref config_file) = *CONFIG_FILE {
|
||||
arg.default_value(config_file)
|
||||
} else {
|
||||
arg
|
||||
}
|
||||
})
|
||||
.arg(
|
||||
Arg::with_name("json_rpc_url")
|
||||
.short("u")
|
||||
.long("url")
|
||||
.value_name("URL_OR_MONIKER")
|
||||
.takes_value(true)
|
||||
.global(true)
|
||||
.validator(is_url_or_moniker)
|
||||
.help(
|
||||
"URL for Solana's JSON RPC or moniker (or their first letter): \
|
||||
[mainnet-beta, testnet, devnet, localhost]",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("websocket_url")
|
||||
.long("ws")
|
||||
.value_name("URL")
|
||||
.takes_value(true)
|
||||
.global(true)
|
||||
.validator(is_url)
|
||||
.help("WebSocket URL for the solana cluster"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("keypair")
|
||||
.short("k")
|
||||
.long("keypair")
|
||||
.value_name("KEYPAIR")
|
||||
.global(true)
|
||||
.takes_value(true)
|
||||
.help("Filepath or URL to a keypair"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("commitment")
|
||||
.long("commitment")
|
||||
.takes_value(true)
|
||||
.possible_values(&[
|
||||
"processed",
|
||||
"confirmed",
|
||||
"finalized",
|
||||
"recent", // Deprecated as of v1.5.5
|
||||
"single", // Deprecated as of v1.5.5
|
||||
"singleGossip", // Deprecated as of v1.5.5
|
||||
"root", // Deprecated as of v1.5.5
|
||||
"max", // Deprecated as of v1.5.5
|
||||
])
|
||||
.value_name("COMMITMENT_LEVEL")
|
||||
.hide_possible_values(true)
|
||||
.global(true)
|
||||
.help("Return information at the selected commitment level [possible values: processed, confirmed, finalized]"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("verbose")
|
||||
.long("verbose")
|
||||
.short("v")
|
||||
.global(true)
|
||||
.help("Show additional information"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("no_address_labels")
|
||||
.long("no-address-labels")
|
||||
.global(true)
|
||||
.help("Do not use address labels in the output"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("output_format")
|
||||
.long("output")
|
||||
.value_name("FORMAT")
|
||||
.global(true)
|
||||
.takes_value(true)
|
||||
.possible_values(&["json", "json-compact"])
|
||||
.help("Return information in specified output format"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(SKIP_SEED_PHRASE_VALIDATION_ARG.name)
|
||||
.long(SKIP_SEED_PHRASE_VALIDATION_ARG.long)
|
||||
.global(true)
|
||||
.help(SKIP_SEED_PHRASE_VALIDATION_ARG.help),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("rpc_timeout")
|
||||
.long("rpc-timeout")
|
||||
.value_name("SECONDS")
|
||||
.takes_value(true)
|
||||
.default_value(DEFAULT_RPC_TIMEOUT_SECONDS)
|
||||
.global(true)
|
||||
.hidden(true)
|
||||
.help("Timeout value for RPC requests"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("confirm_transaction_initial_timeout")
|
||||
.long("confirm-timeout")
|
||||
.value_name("SECONDS")
|
||||
.takes_value(true)
|
||||
.default_value(DEFAULT_CONFIRM_TX_TIMEOUT_SECONDS)
|
||||
.global(true)
|
||||
.hidden(true)
|
||||
.help("Timeout value for initial transaction status"),
|
||||
)
|
||||
.cluster_query_subcommands()
|
||||
.feature_subcommands()
|
||||
.inflation_subcommands()
|
||||
.nonce_subcommands()
|
||||
.program_subcommands()
|
||||
.stake_subcommands()
|
||||
.validator_info_subcommands()
|
||||
.vote_subcommands()
|
||||
.wallet_subcommands()
|
||||
.subcommand(
|
||||
SubCommand::with_name("config")
|
||||
.about("Solana command-line tool configuration settings")
|
||||
.aliases(&["get", "set"])
|
||||
.setting(AppSettings::SubcommandRequiredElseHelp)
|
||||
.subcommand(
|
||||
SubCommand::with_name("get")
|
||||
.about("Get current config settings")
|
||||
.arg(
|
||||
Arg::with_name("specific_setting")
|
||||
.index(1)
|
||||
.value_name("CONFIG_FIELD")
|
||||
.takes_value(true)
|
||||
.possible_values(&[
|
||||
"json_rpc_url",
|
||||
"websocket_url",
|
||||
"keypair",
|
||||
"commitment",
|
||||
])
|
||||
.help("Return a specific config setting"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("set")
|
||||
.about("Set a config setting")
|
||||
.group(
|
||||
ArgGroup::with_name("config_settings")
|
||||
.args(&["json_rpc_url", "websocket_url", "keypair", "commitment"])
|
||||
.multiple(true)
|
||||
.required(true),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("import-address-labels")
|
||||
.about("Import a list of address labels")
|
||||
.arg(
|
||||
Arg::with_name("filename")
|
||||
.index(1)
|
||||
.value_name("FILENAME")
|
||||
.takes_value(true)
|
||||
.help("YAML file of address labels"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("export-address-labels")
|
||||
.about("Export the current address labels")
|
||||
.arg(
|
||||
Arg::with_name("filename")
|
||||
.index(1)
|
||||
.value_name("FILENAME")
|
||||
.takes_value(true)
|
||||
.help("YAML file to receive the current address labels"),
|
||||
),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("completion")
|
||||
.about("Generate completion scripts for various shells")
|
||||
.arg(
|
||||
Arg::with_name("shell")
|
||||
.long("shell")
|
||||
.short("s")
|
||||
.takes_value(true)
|
||||
.possible_values(&["bash", "fish", "zsh", "powershell", "elvish"])
|
||||
.default_value("bash")
|
||||
)
|
||||
)
|
||||
}
|
865
cli/src/cli.rs
865
cli/src/cli.rs
@@ -1,38 +1,21 @@
|
||||
use crate::{
|
||||
cluster_query::*, feature::*, inflation::*, memo::*, nonce::*, program::*, spend_utils::*,
|
||||
stake::*, validator_info::*, vote::*,
|
||||
clap_app::*, cluster_query::*, feature::*, inflation::*, nonce::*, program::*, spend_utils::*,
|
||||
stake::*, validator_info::*, vote::*, wallet::*,
|
||||
};
|
||||
use clap::{value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand};
|
||||
use clap::{crate_description, crate_name, value_t_or_exit, ArgMatches, Shell};
|
||||
use log::*;
|
||||
use num_traits::FromPrimitive;
|
||||
use serde_json::{self, Value};
|
||||
use solana_account_decoder::{UiAccount, UiAccountEncoding};
|
||||
use solana_clap_utils::{
|
||||
self,
|
||||
fee_payer::{fee_payer_arg, FEE_PAYER_ARG},
|
||||
input_parsers::*,
|
||||
input_validators::*,
|
||||
keypair::*,
|
||||
memo::{memo_arg, MEMO_ARG},
|
||||
nonce::*,
|
||||
offline::*,
|
||||
};
|
||||
use solana_clap_utils::{self, input_parsers::*, input_validators::*, keypair::*};
|
||||
use solana_cli_output::{
|
||||
display::{build_balance_message, println_name_value},
|
||||
return_signers_with_config, CliAccount, CliSignature, CliSignatureVerificationStatus,
|
||||
CliTransaction, CliTransactionConfirmation, CliValidatorsSortOrder, OutputFormat,
|
||||
ReturnSignersConfig,
|
||||
display::println_name_value, CliSignature, CliValidatorsSortOrder, OutputFormat,
|
||||
};
|
||||
use solana_client::{
|
||||
blockhash_query::BlockhashQuery,
|
||||
client_error::{ClientError, Result as ClientResult},
|
||||
nonce_utils,
|
||||
rpc_client::RpcClient,
|
||||
rpc_config::{
|
||||
RpcLargestAccountsFilter, RpcSendTransactionConfig, RpcTransactionConfig,
|
||||
RpcTransactionLogsFilter,
|
||||
},
|
||||
rpc_response::RpcKeyedAccount,
|
||||
rpc_config::{RpcLargestAccountsFilter, RpcSendTransactionConfig, RpcTransactionLogsFilter},
|
||||
};
|
||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||
use solana_sdk::{
|
||||
@@ -41,20 +24,13 @@ use solana_sdk::{
|
||||
decode_error::DecodeError,
|
||||
hash::Hash,
|
||||
instruction::InstructionError,
|
||||
message::Message,
|
||||
pubkey::Pubkey,
|
||||
signature::{Signature, Signer, SignerError},
|
||||
stake::{self, instruction::LockupArgs, state::Lockup},
|
||||
system_instruction::{self, SystemError},
|
||||
system_program,
|
||||
stake::{instruction::LockupArgs, state::Lockup},
|
||||
transaction::{Transaction, TransactionError},
|
||||
};
|
||||
use solana_transaction_status::{EncodedTransaction, UiTransactionEncoding};
|
||||
use solana_vote_program::vote_state::VoteAuthorize;
|
||||
use std::{
|
||||
collections::HashMap, error, fmt::Write as FmtWrite, fs::File, io::Write, str::FromStr,
|
||||
sync::Arc, time::Duration,
|
||||
};
|
||||
use std::{collections::HashMap, error, io::stdout, str::FromStr, sync::Arc, time::Duration};
|
||||
use thiserror::Error;
|
||||
|
||||
pub const DEFAULT_RPC_TIMEOUT_SECONDS: &str = "30";
|
||||
@@ -74,11 +50,6 @@ pub enum CliCommand {
|
||||
},
|
||||
ClusterDate,
|
||||
ClusterVersion,
|
||||
CreateAddressWithSeed {
|
||||
from_pubkey: Option<Pubkey>,
|
||||
seed: String,
|
||||
program_id: Pubkey,
|
||||
},
|
||||
Feature(FeatureCliCommand),
|
||||
Inflation(InflationCliCommand),
|
||||
Fees {
|
||||
@@ -368,6 +339,11 @@ pub enum CliCommand {
|
||||
use_lamports_unit: bool,
|
||||
},
|
||||
Confirm(Signature),
|
||||
CreateAddressWithSeed {
|
||||
from_pubkey: Option<Pubkey>,
|
||||
seed: String,
|
||||
program_id: Pubkey,
|
||||
},
|
||||
DecodeTransaction(Transaction),
|
||||
ResolveSigner(Option<String>),
|
||||
ShowAccount {
|
||||
@@ -615,7 +591,31 @@ pub fn parse_command(
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, Box<dyn error::Error>> {
|
||||
let response = match matches.subcommand() {
|
||||
// Autocompletion Command
|
||||
("completion", Some(matches)) => {
|
||||
let shell_choice = match matches.value_of("shell") {
|
||||
Some("bash") => Shell::Bash,
|
||||
Some("fish") => Shell::Fish,
|
||||
Some("zsh") => Shell::Zsh,
|
||||
Some("powershell") => Shell::PowerShell,
|
||||
Some("elvish") => Shell::Elvish,
|
||||
// This is safe, since we assign default_value and possible_values
|
||||
// are restricted
|
||||
_ => unreachable!(),
|
||||
};
|
||||
get_clap_app(
|
||||
crate_name!(),
|
||||
crate_description!(),
|
||||
solana_version::version!(),
|
||||
)
|
||||
.gen_completions_to("solana", shell_choice, &mut stdout());
|
||||
std::process::exit(0);
|
||||
}
|
||||
// Cluster Query Commands
|
||||
("block", Some(matches)) => parse_get_block(matches),
|
||||
("block-height", Some(matches)) => parse_get_block_height(matches),
|
||||
("block-production", Some(matches)) => parse_show_block_production(matches),
|
||||
("block-time", Some(matches)) => parse_get_block_time(matches),
|
||||
("catchup", Some(matches)) => parse_catchup(matches, wallet_manager),
|
||||
("cluster-date", Some(_matches)) => Ok(CliCommandInfo {
|
||||
command: CliCommand::ClusterDate,
|
||||
@@ -625,9 +625,8 @@ pub fn parse_command(
|
||||
command: CliCommand::ClusterVersion,
|
||||
signers: vec![],
|
||||
}),
|
||||
("create-address-with-seed", Some(matches)) => {
|
||||
parse_create_address_with_seed(matches, default_signer, wallet_manager)
|
||||
}
|
||||
("epoch", Some(matches)) => parse_get_epoch(matches),
|
||||
("epoch-info", Some(matches)) => parse_get_epoch_info(matches),
|
||||
("feature", Some(matches)) => {
|
||||
parse_feature_subcommand(matches, default_signer, wallet_manager)
|
||||
}
|
||||
@@ -642,40 +641,47 @@ pub fn parse_command(
|
||||
command: CliCommand::FirstAvailableBlock,
|
||||
signers: vec![],
|
||||
}),
|
||||
("block", Some(matches)) => parse_get_block(matches),
|
||||
("block-time", Some(matches)) => parse_get_block_time(matches),
|
||||
("epoch-info", Some(matches)) => parse_get_epoch_info(matches),
|
||||
("genesis-hash", Some(_matches)) => Ok(CliCommandInfo {
|
||||
command: CliCommand::GetGenesisHash,
|
||||
signers: vec![],
|
||||
}),
|
||||
("epoch", Some(matches)) => parse_get_epoch(matches),
|
||||
("slot", Some(matches)) => parse_get_slot(matches),
|
||||
("block-height", Some(matches)) => parse_get_block_height(matches),
|
||||
("gossip", Some(_matches)) => Ok(CliCommandInfo {
|
||||
command: CliCommand::ShowGossip,
|
||||
signers: vec![],
|
||||
}),
|
||||
("inflation", Some(matches)) => {
|
||||
parse_inflation_subcommand(matches, default_signer, wallet_manager)
|
||||
}
|
||||
("largest-accounts", Some(matches)) => parse_largest_accounts(matches),
|
||||
("supply", Some(matches)) => parse_supply(matches),
|
||||
("total-supply", Some(matches)) => parse_total_supply(matches),
|
||||
("transaction-count", Some(matches)) => parse_get_transaction_count(matches),
|
||||
("leader-schedule", Some(matches)) => parse_leader_schedule(matches),
|
||||
("ping", Some(matches)) => parse_cluster_ping(matches, default_signer, wallet_manager),
|
||||
("live-slots", Some(_matches)) => Ok(CliCommandInfo {
|
||||
command: CliCommand::LiveSlots,
|
||||
signers: vec![],
|
||||
}),
|
||||
("logs", Some(matches)) => parse_logs(matches, wallet_manager),
|
||||
("block-production", Some(matches)) => parse_show_block_production(matches),
|
||||
("gossip", Some(_matches)) => Ok(CliCommandInfo {
|
||||
command: CliCommand::ShowGossip,
|
||||
signers: vec![],
|
||||
}),
|
||||
("ping", Some(matches)) => parse_cluster_ping(matches, default_signer, wallet_manager),
|
||||
("rent", Some(matches)) => {
|
||||
let data_length = value_of::<RentLengthValue>(matches, "data_length")
|
||||
.unwrap()
|
||||
.length();
|
||||
let use_lamports_unit = matches.is_present("lamports");
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::Rent {
|
||||
data_length,
|
||||
use_lamports_unit,
|
||||
},
|
||||
signers: vec![],
|
||||
})
|
||||
}
|
||||
("slot", Some(matches)) => parse_get_slot(matches),
|
||||
("stakes", Some(matches)) => parse_show_stakes(matches, wallet_manager),
|
||||
("validators", Some(matches)) => parse_show_validators(matches),
|
||||
("supply", Some(matches)) => parse_supply(matches),
|
||||
("total-supply", Some(matches)) => parse_total_supply(matches),
|
||||
("transaction-count", Some(matches)) => parse_get_transaction_count(matches),
|
||||
("transaction-history", Some(matches)) => {
|
||||
parse_transaction_history(matches, wallet_manager)
|
||||
}
|
||||
("validators", Some(matches)) => parse_show_validators(matches),
|
||||
// Nonce Commands
|
||||
("authorize-nonce-account", Some(matches)) => {
|
||||
parse_authorize_nonce_account(matches, default_signer, wallet_manager)
|
||||
@@ -805,38 +811,13 @@ pub fn parse_command(
|
||||
parse_withdraw_from_vote_account(matches, default_signer, wallet_manager)
|
||||
}
|
||||
// Wallet Commands
|
||||
("account", Some(matches)) => parse_account(matches, wallet_manager),
|
||||
("address", Some(matches)) => Ok(CliCommandInfo {
|
||||
command: CliCommand::Address,
|
||||
signers: vec![default_signer.signer_from_path(matches, wallet_manager)?],
|
||||
}),
|
||||
("airdrop", Some(matches)) => {
|
||||
let pubkey = pubkey_of_signer(matches, "to", wallet_manager)?;
|
||||
let signers = if pubkey.is_some() {
|
||||
vec![]
|
||||
} else {
|
||||
vec![default_signer.signer_from_path(matches, wallet_manager)?]
|
||||
};
|
||||
let lamports = lamports_of_sol(matches, "amount").unwrap();
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::Airdrop { pubkey, lamports },
|
||||
signers,
|
||||
})
|
||||
}
|
||||
("balance", Some(matches)) => {
|
||||
let pubkey = pubkey_of_signer(matches, "pubkey", wallet_manager)?;
|
||||
let signers = if pubkey.is_some() {
|
||||
vec![]
|
||||
} else {
|
||||
vec![default_signer.signer_from_path(matches, wallet_manager)?]
|
||||
};
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::Balance {
|
||||
pubkey,
|
||||
use_lamports_unit: matches.is_present("lamports"),
|
||||
},
|
||||
signers,
|
||||
})
|
||||
}
|
||||
("airdrop", Some(matches)) => parse_airdrop(matches, default_signer, wallet_manager),
|
||||
("balance", Some(matches)) => parse_balance(matches, default_signer, wallet_manager),
|
||||
("confirm", Some(matches)) => match matches.value_of("signature").unwrap().parse() {
|
||||
Ok(signature) => Ok(CliCommandInfo {
|
||||
command: CliCommand::Confirm(signature),
|
||||
@@ -844,40 +825,10 @@ pub fn parse_command(
|
||||
}),
|
||||
_ => Err(CliError::BadParameter("Invalid signature".to_string())),
|
||||
},
|
||||
("decode-transaction", Some(matches)) => {
|
||||
let blob = value_t_or_exit!(matches, "transaction", String);
|
||||
let encoding = match matches.value_of("encoding").unwrap() {
|
||||
"base58" => UiTransactionEncoding::Base58,
|
||||
"base64" => UiTransactionEncoding::Base64,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let encoded_transaction = EncodedTransaction::Binary(blob, encoding);
|
||||
if let Some(transaction) = encoded_transaction.decode() {
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::DecodeTransaction(transaction),
|
||||
signers: vec![],
|
||||
})
|
||||
} else {
|
||||
Err(CliError::BadParameter(
|
||||
"Unable to decode transaction".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
("account", Some(matches)) => {
|
||||
let account_pubkey =
|
||||
pubkey_of_signer(matches, "account_pubkey", wallet_manager)?.unwrap();
|
||||
let output_file = matches.value_of("output_file");
|
||||
let use_lamports_unit = matches.is_present("lamports");
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::ShowAccount {
|
||||
pubkey: account_pubkey,
|
||||
output_file: output_file.map(ToString::to_string),
|
||||
use_lamports_unit,
|
||||
},
|
||||
signers: vec![],
|
||||
})
|
||||
("create-address-with-seed", Some(matches)) => {
|
||||
parse_create_address_with_seed(matches, default_signer, wallet_manager)
|
||||
}
|
||||
("decode-transaction", Some(matches)) => parse_decode_transaction(matches),
|
||||
("resolve-signer", Some(matches)) => {
|
||||
let signer_path = resolve_signer(matches, "signer", wallet_manager)?;
|
||||
Ok(CliCommandInfo {
|
||||
@@ -885,69 +836,7 @@ pub fn parse_command(
|
||||
signers: vec![],
|
||||
})
|
||||
}
|
||||
("transfer", Some(matches)) => {
|
||||
let amount = SpendAmount::new_from_matches(matches, "amount");
|
||||
let to = pubkey_of_signer(matches, "to", wallet_manager)?.unwrap();
|
||||
let sign_only = matches.is_present(SIGN_ONLY_ARG.name);
|
||||
let dump_transaction_message = matches.is_present(DUMP_TRANSACTION_MESSAGE.name);
|
||||
let no_wait = matches.is_present("no_wait");
|
||||
let blockhash_query = BlockhashQuery::new_from_matches(matches);
|
||||
let nonce_account = pubkey_of_signer(matches, NONCE_ARG.name, wallet_manager)?;
|
||||
let (nonce_authority, nonce_authority_pubkey) =
|
||||
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
|
||||
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
|
||||
let (fee_payer, fee_payer_pubkey) =
|
||||
signer_of(matches, FEE_PAYER_ARG.name, wallet_manager)?;
|
||||
let (from, from_pubkey) = signer_of(matches, "from", wallet_manager)?;
|
||||
let allow_unfunded_recipient = matches.is_present("allow_unfunded_recipient");
|
||||
|
||||
let mut bulk_signers = vec![fee_payer, from];
|
||||
if nonce_account.is_some() {
|
||||
bulk_signers.push(nonce_authority);
|
||||
}
|
||||
|
||||
let signer_info =
|
||||
default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
|
||||
|
||||
let derived_address_seed = matches
|
||||
.value_of("derived_address_seed")
|
||||
.map(|s| s.to_string());
|
||||
let derived_address_program_id =
|
||||
resolve_derived_address_program_id(matches, "derived_address_program_id");
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::Transfer {
|
||||
amount,
|
||||
to,
|
||||
sign_only,
|
||||
dump_transaction_message,
|
||||
allow_unfunded_recipient,
|
||||
no_wait,
|
||||
blockhash_query,
|
||||
nonce_account,
|
||||
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
|
||||
memo,
|
||||
fee_payer: signer_info.index_of(fee_payer_pubkey).unwrap(),
|
||||
from: signer_info.index_of(from_pubkey).unwrap(),
|
||||
derived_address_seed,
|
||||
derived_address_program_id,
|
||||
},
|
||||
signers: signer_info.signers,
|
||||
})
|
||||
}
|
||||
("rent", Some(matches)) => {
|
||||
let data_length = value_of::<RentLengthValue>(matches, "data_length")
|
||||
.unwrap()
|
||||
.length();
|
||||
let use_lamports_unit = matches.is_present("lamports");
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::Rent {
|
||||
data_length,
|
||||
use_lamports_unit,
|
||||
},
|
||||
signers: vec![],
|
||||
})
|
||||
}
|
||||
("transfer", Some(matches)) => parse_transfer(matches, default_signer, wallet_manager),
|
||||
//
|
||||
("", None) => {
|
||||
eprintln!("{}", matches.usage());
|
||||
@@ -962,349 +851,6 @@ pub fn parse_command(
|
||||
|
||||
pub type ProcessResult = Result<String, Box<dyn std::error::Error>>;
|
||||
|
||||
fn resolve_derived_address_program_id(matches: &ArgMatches<'_>, arg_name: &str) -> Option<Pubkey> {
|
||||
matches.value_of(arg_name).and_then(|v| match v {
|
||||
"NONCE" => Some(system_program::id()),
|
||||
"STAKE" => Some(stake::program::id()),
|
||||
"VOTE" => Some(solana_vote_program::id()),
|
||||
_ => pubkey_of(matches, arg_name),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_create_address_with_seed(
|
||||
matches: &ArgMatches<'_>,
|
||||
default_signer: &DefaultSigner,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let from_pubkey = pubkey_of_signer(matches, "from", wallet_manager)?;
|
||||
let signers = if from_pubkey.is_some() {
|
||||
vec![]
|
||||
} else {
|
||||
vec![default_signer.signer_from_path(matches, wallet_manager)?]
|
||||
};
|
||||
|
||||
let program_id = resolve_derived_address_program_id(matches, "program_id").unwrap();
|
||||
|
||||
let seed = matches.value_of("seed").unwrap().to_string();
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::CreateAddressWithSeed {
|
||||
from_pubkey,
|
||||
seed,
|
||||
program_id,
|
||||
},
|
||||
signers,
|
||||
})
|
||||
}
|
||||
|
||||
fn process_create_address_with_seed(
|
||||
config: &CliConfig,
|
||||
from_pubkey: Option<&Pubkey>,
|
||||
seed: &str,
|
||||
program_id: &Pubkey,
|
||||
) -> ProcessResult {
|
||||
let from_pubkey = if let Some(pubkey) = from_pubkey {
|
||||
*pubkey
|
||||
} else {
|
||||
config.pubkey()?
|
||||
};
|
||||
let address = Pubkey::create_with_seed(&from_pubkey, seed, program_id)?;
|
||||
Ok(address.to_string())
|
||||
}
|
||||
|
||||
fn process_airdrop(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
pubkey: &Option<Pubkey>,
|
||||
lamports: u64,
|
||||
) -> ProcessResult {
|
||||
let pubkey = if let Some(pubkey) = pubkey {
|
||||
*pubkey
|
||||
} else {
|
||||
config.pubkey()?
|
||||
};
|
||||
println!(
|
||||
"Requesting airdrop of {}",
|
||||
build_balance_message(lamports, false, true),
|
||||
);
|
||||
|
||||
let pre_balance = rpc_client.get_balance(&pubkey)?;
|
||||
|
||||
let result = request_and_confirm_airdrop(rpc_client, config, &pubkey, lamports);
|
||||
if let Ok(signature) = result {
|
||||
let signature_cli_message = log_instruction_custom_error::<SystemError>(result, config)?;
|
||||
println!("{}", signature_cli_message);
|
||||
|
||||
let current_balance = rpc_client.get_balance(&pubkey)?;
|
||||
|
||||
if current_balance < pre_balance.saturating_add(lamports) {
|
||||
println!("Balance unchanged");
|
||||
println!("Run `solana confirm -v {:?}` for more info", signature);
|
||||
Ok("".to_string())
|
||||
} else {
|
||||
Ok(build_balance_message(current_balance, false, true))
|
||||
}
|
||||
} else {
|
||||
log_instruction_custom_error::<SystemError>(result, config)
|
||||
}
|
||||
}
|
||||
|
||||
fn process_balance(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
pubkey: &Option<Pubkey>,
|
||||
use_lamports_unit: bool,
|
||||
) -> ProcessResult {
|
||||
let pubkey = if let Some(pubkey) = pubkey {
|
||||
*pubkey
|
||||
} else {
|
||||
config.pubkey()?
|
||||
};
|
||||
let balance = rpc_client.get_balance(&pubkey)?;
|
||||
Ok(build_balance_message(balance, use_lamports_unit, true))
|
||||
}
|
||||
|
||||
fn process_confirm(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
signature: &Signature,
|
||||
) -> ProcessResult {
|
||||
match rpc_client.get_signature_statuses_with_history(&[*signature]) {
|
||||
Ok(status) => {
|
||||
let cli_transaction = if let Some(transaction_status) = &status.value[0] {
|
||||
let mut transaction = None;
|
||||
let mut get_transaction_error = None;
|
||||
if config.verbose {
|
||||
match rpc_client.get_transaction_with_config(
|
||||
signature,
|
||||
RpcTransactionConfig {
|
||||
encoding: Some(UiTransactionEncoding::Base64),
|
||||
commitment: Some(CommitmentConfig::confirmed()),
|
||||
},
|
||||
) {
|
||||
Ok(confirmed_transaction) => {
|
||||
let decoded_transaction = confirmed_transaction
|
||||
.transaction
|
||||
.transaction
|
||||
.decode()
|
||||
.expect("Successful decode");
|
||||
let json_transaction = EncodedTransaction::encode(
|
||||
decoded_transaction.clone(),
|
||||
UiTransactionEncoding::Json,
|
||||
);
|
||||
|
||||
transaction = Some(CliTransaction {
|
||||
transaction: json_transaction,
|
||||
meta: confirmed_transaction.transaction.meta,
|
||||
block_time: confirmed_transaction.block_time,
|
||||
slot: Some(confirmed_transaction.slot),
|
||||
decoded_transaction,
|
||||
prefix: " ".to_string(),
|
||||
sigverify_status: vec![],
|
||||
});
|
||||
}
|
||||
Err(err) => {
|
||||
get_transaction_error = Some(format!("{:?}", err));
|
||||
}
|
||||
}
|
||||
}
|
||||
CliTransactionConfirmation {
|
||||
confirmation_status: Some(transaction_status.confirmation_status()),
|
||||
transaction,
|
||||
get_transaction_error,
|
||||
err: transaction_status.err.clone(),
|
||||
}
|
||||
} else {
|
||||
CliTransactionConfirmation {
|
||||
confirmation_status: None,
|
||||
transaction: None,
|
||||
get_transaction_error: None,
|
||||
err: None,
|
||||
}
|
||||
};
|
||||
Ok(config.output_format.formatted_string(&cli_transaction))
|
||||
}
|
||||
Err(err) => Err(CliError::RpcRequestError(format!("Unable to confirm: {}", err)).into()),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
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: EncodedTransaction::encode(transaction.clone(), UiTransactionEncoding::Json),
|
||||
meta: None,
|
||||
block_time: None,
|
||||
slot: None,
|
||||
prefix: "".to_string(),
|
||||
sigverify_status,
|
||||
};
|
||||
Ok(config.output_format.formatted_string(&decode_transaction))
|
||||
}
|
||||
|
||||
fn process_show_account(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
account_pubkey: &Pubkey,
|
||||
output_file: &Option<String>,
|
||||
use_lamports_unit: bool,
|
||||
) -> ProcessResult {
|
||||
let account = rpc_client.get_account(account_pubkey)?;
|
||||
let data = account.data.clone();
|
||||
let cli_account = CliAccount {
|
||||
keyed_account: RpcKeyedAccount {
|
||||
pubkey: account_pubkey.to_string(),
|
||||
account: UiAccount::encode(
|
||||
account_pubkey,
|
||||
&account,
|
||||
UiAccountEncoding::Base64,
|
||||
None,
|
||||
None,
|
||||
),
|
||||
},
|
||||
use_lamports_unit,
|
||||
};
|
||||
|
||||
let mut account_string = config.output_format.formatted_string(&cli_account);
|
||||
|
||||
if config.output_format == OutputFormat::Display
|
||||
|| config.output_format == OutputFormat::DisplayVerbose
|
||||
{
|
||||
if let Some(output_file) = output_file {
|
||||
let mut f = File::create(output_file)?;
|
||||
f.write_all(&data)?;
|
||||
writeln!(&mut account_string)?;
|
||||
writeln!(&mut account_string, "Wrote account data to {}", output_file)?;
|
||||
} else if !data.is_empty() {
|
||||
use pretty_hex::*;
|
||||
writeln!(&mut account_string, "{:?}", data.hex_dump())?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(account_string)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn process_transfer(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
amount: SpendAmount,
|
||||
to: &Pubkey,
|
||||
from: SignerIndex,
|
||||
sign_only: bool,
|
||||
dump_transaction_message: bool,
|
||||
allow_unfunded_recipient: bool,
|
||||
no_wait: bool,
|
||||
blockhash_query: &BlockhashQuery,
|
||||
nonce_account: Option<&Pubkey>,
|
||||
nonce_authority: SignerIndex,
|
||||
memo: Option<&String>,
|
||||
fee_payer: SignerIndex,
|
||||
derived_address_seed: Option<String>,
|
||||
derived_address_program_id: Option<&Pubkey>,
|
||||
) -> ProcessResult {
|
||||
let from = config.signers[from];
|
||||
let mut from_pubkey = from.pubkey();
|
||||
|
||||
let (recent_blockhash, fee_calculator) =
|
||||
blockhash_query.get_blockhash_and_fee_calculator(rpc_client, config.commitment)?;
|
||||
|
||||
if !sign_only && !allow_unfunded_recipient {
|
||||
let recipient_balance = rpc_client
|
||||
.get_balance_with_commitment(to, config.commitment)?
|
||||
.value;
|
||||
if recipient_balance == 0 {
|
||||
return Err(format!(
|
||||
"The recipient address ({}) is not funded. \
|
||||
Add `--allow-unfunded-recipient` to complete the transfer \
|
||||
",
|
||||
to
|
||||
)
|
||||
.into());
|
||||
}
|
||||
}
|
||||
|
||||
let nonce_authority = config.signers[nonce_authority];
|
||||
let fee_payer = config.signers[fee_payer];
|
||||
|
||||
let derived_parts = derived_address_seed.zip(derived_address_program_id);
|
||||
let with_seed = if let Some((seed, program_id)) = derived_parts {
|
||||
let base_pubkey = from_pubkey;
|
||||
from_pubkey = Pubkey::create_with_seed(&base_pubkey, &seed, program_id)?;
|
||||
Some((base_pubkey, seed, program_id, from_pubkey))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let build_message = |lamports| {
|
||||
let ixs = if let Some((base_pubkey, seed, program_id, from_pubkey)) = with_seed.as_ref() {
|
||||
vec![system_instruction::transfer_with_seed(
|
||||
from_pubkey,
|
||||
base_pubkey,
|
||||
seed.clone(),
|
||||
program_id,
|
||||
to,
|
||||
lamports,
|
||||
)]
|
||||
.with_memo(memo)
|
||||
} else {
|
||||
vec![system_instruction::transfer(&from_pubkey, to, lamports)].with_memo(memo)
|
||||
};
|
||||
|
||||
if let Some(nonce_account) = &nonce_account {
|
||||
Message::new_with_nonce(
|
||||
ixs,
|
||||
Some(&fee_payer.pubkey()),
|
||||
nonce_account,
|
||||
&nonce_authority.pubkey(),
|
||||
)
|
||||
} else {
|
||||
Message::new(&ixs, Some(&fee_payer.pubkey()))
|
||||
}
|
||||
};
|
||||
|
||||
let (message, _) = resolve_spend_tx_and_check_account_balances(
|
||||
rpc_client,
|
||||
sign_only,
|
||||
amount,
|
||||
&fee_calculator,
|
||||
&from_pubkey,
|
||||
&fee_payer.pubkey(),
|
||||
build_message,
|
||||
config.commitment,
|
||||
)?;
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
|
||||
if sign_only {
|
||||
tx.try_partial_sign(&config.signers, recent_blockhash)?;
|
||||
return_signers_with_config(
|
||||
&tx,
|
||||
&config.output_format,
|
||||
&ReturnSignersConfig {
|
||||
dump_transaction_message,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
if let Some(nonce_account) = &nonce_account {
|
||||
let nonce_account = nonce_utils::get_account_with_commitment(
|
||||
rpc_client,
|
||||
nonce_account,
|
||||
config.commitment,
|
||||
)?;
|
||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
||||
}
|
||||
|
||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||
let result = if no_wait {
|
||||
rpc_client.send_transaction(&tx)
|
||||
} else {
|
||||
rpc_client.send_and_confirm_transaction_with_spinner(&tx)
|
||||
};
|
||||
log_instruction_custom_error::<SystemError>(result, config)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_command(config: &CliConfig) -> ProcessResult {
|
||||
if config.verbose && config.output_format == OutputFormat::DisplayVerbose {
|
||||
println_name_value("RPC URL:", &config.json_rpc_url);
|
||||
@@ -2037,274 +1583,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, 'v> {
|
||||
App::new(name)
|
||||
.about(about)
|
||||
.version(version)
|
||||
.setting(AppSettings::SubcommandRequiredElseHelp)
|
||||
.subcommand(
|
||||
SubCommand::with_name("address")
|
||||
.about("Get your public key")
|
||||
.arg(
|
||||
Arg::with_name("confirm_key")
|
||||
.long("confirm-key")
|
||||
.takes_value(false)
|
||||
.help("Confirm key on device; only relevant if using remote wallet"),
|
||||
),
|
||||
)
|
||||
.cluster_query_subcommands()
|
||||
.feature_subcommands()
|
||||
.inflation_subcommands()
|
||||
.nonce_subcommands()
|
||||
.program_subcommands()
|
||||
.stake_subcommands()
|
||||
.subcommand(
|
||||
SubCommand::with_name("airdrop")
|
||||
.about("Request SOL from a faucet")
|
||||
.arg(
|
||||
Arg::with_name("amount")
|
||||
.index(1)
|
||||
.value_name("AMOUNT")
|
||||
.takes_value(true)
|
||||
.validator(is_amount)
|
||||
.required(true)
|
||||
.help("The airdrop amount to request, in SOL"),
|
||||
)
|
||||
.arg(
|
||||
pubkey!(Arg::with_name("to")
|
||||
.index(2)
|
||||
.value_name("RECIPIENT_ADDRESS"),
|
||||
"The account address of airdrop recipient. "),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("balance")
|
||||
.about("Get your balance")
|
||||
.arg(
|
||||
pubkey!(Arg::with_name("pubkey")
|
||||
.index(1)
|
||||
.value_name("ACCOUNT_ADDRESS"),
|
||||
"The account address of the balance to check. ")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("lamports")
|
||||
.long("lamports")
|
||||
.takes_value(false)
|
||||
.help("Display balance in lamports instead of SOL"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("confirm")
|
||||
.about("Confirm transaction by signature")
|
||||
.arg(
|
||||
Arg::with_name("signature")
|
||||
.index(1)
|
||||
.value_name("TRANSACTION_SIGNATURE")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.help("The transaction signature to confirm"),
|
||||
)
|
||||
.after_help(// Formatted specifically for the manually-indented heredoc string
|
||||
"Note: This will show more detailed information for finalized transactions with verbose mode (-v/--verbose).\
|
||||
\n\
|
||||
\nAccount modes:\
|
||||
\n |srwx|\
|
||||
\n s: signed\
|
||||
\n r: readable (always true)\
|
||||
\n w: writable\
|
||||
\n x: program account (inner instructions excluded)\
|
||||
"
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("decode-transaction")
|
||||
.about("Decode a serialized transaction")
|
||||
.arg(
|
||||
Arg::with_name("transaction")
|
||||
.index(1)
|
||||
.value_name("TRANSACTION")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.help("transaction to decode"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("encoding")
|
||||
.index(2)
|
||||
.value_name("ENCODING")
|
||||
.possible_values(&["base58", "base64"]) // Subset of `UiTransactionEncoding` enum
|
||||
.default_value("base58")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.help("transaction encoding"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("create-address-with-seed")
|
||||
.about("Generate a derived account address with a seed")
|
||||
.arg(
|
||||
Arg::with_name("seed")
|
||||
.index(1)
|
||||
.value_name("SEED_STRING")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.validator(is_derived_address_seed)
|
||||
.help("The seed. Must not take more than 32 bytes to encode as utf-8"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("program_id")
|
||||
.index(2)
|
||||
.value_name("PROGRAM_ID")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.help(
|
||||
"The program_id that the address will ultimately be used for, \n\
|
||||
or one of NONCE, STAKE, and VOTE keywords",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
pubkey!(Arg::with_name("from")
|
||||
.long("from")
|
||||
.value_name("FROM_PUBKEY")
|
||||
.required(false),
|
||||
"From (base) key, [default: cli config keypair]. "),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("deploy")
|
||||
.about("Deploy a program")
|
||||
.arg(
|
||||
Arg::with_name("program_location")
|
||||
.index(1)
|
||||
.value_name("PROGRAM_FILEPATH")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.help("/path/to/program.o"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("address_signer")
|
||||
.index(2)
|
||||
.value_name("PROGRAM_ADDRESS_SIGNER")
|
||||
.takes_value(true)
|
||||
.validator(is_valid_signer)
|
||||
.help("The signer for the desired address of the program [default: new random address]")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("use_deprecated_loader")
|
||||
.long("use-deprecated-loader")
|
||||
.takes_value(false)
|
||||
.hidden(true) // Don't document this argument to discourage its use
|
||||
.help("Use the deprecated BPF loader")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("allow_excessive_balance")
|
||||
.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")
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("resolve-signer")
|
||||
.about("Checks that a signer is valid, and returns its specific path; useful for signers that may be specified generally, eg. usb://ledger")
|
||||
.arg(
|
||||
Arg::with_name("signer")
|
||||
.index(1)
|
||||
.value_name("SIGNER_KEYPAIR")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.validator(is_valid_signer)
|
||||
.help("The signer path to resolve")
|
||||
)
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("transfer")
|
||||
.about("Transfer funds between system accounts")
|
||||
.alias("pay")
|
||||
.arg(
|
||||
pubkey!(Arg::with_name("to")
|
||||
.index(1)
|
||||
.value_name("RECIPIENT_ADDRESS")
|
||||
.required(true),
|
||||
"The account address of recipient. "),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("amount")
|
||||
.index(2)
|
||||
.value_name("AMOUNT")
|
||||
.takes_value(true)
|
||||
.validator(is_amount_or_all)
|
||||
.required(true)
|
||||
.help("The amount to send, in SOL; accepts keyword ALL"),
|
||||
)
|
||||
.arg(
|
||||
pubkey!(Arg::with_name("from")
|
||||
.long("from")
|
||||
.value_name("FROM_ADDRESS"),
|
||||
"Source account of funds (if different from client local account). "),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("no_wait")
|
||||
.long("no-wait")
|
||||
.takes_value(false)
|
||||
.help("Return signature immediately after submitting the transaction, instead of waiting for confirmations"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("derived_address_seed")
|
||||
.long("derived-address-seed")
|
||||
.takes_value(true)
|
||||
.value_name("SEED_STRING")
|
||||
.requires("derived_address_program_id")
|
||||
.validator(is_derived_address_seed)
|
||||
.hidden(true)
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("derived_address_program_id")
|
||||
.long("derived-address-program-id")
|
||||
.takes_value(true)
|
||||
.value_name("PROGRAM_ID")
|
||||
.requires("derived_address_seed")
|
||||
.hidden(true)
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("allow_unfunded_recipient")
|
||||
.long("allow-unfunded-recipient")
|
||||
.takes_value(false)
|
||||
.help("Complete the transfer even if the recipient address is not funded")
|
||||
)
|
||||
.offline_args()
|
||||
.nonce_args(false)
|
||||
.arg(memo_arg())
|
||||
.arg(fee_payer_arg()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("account")
|
||||
.about("Show the contents of an account")
|
||||
.alias("account")
|
||||
.arg(
|
||||
pubkey!(Arg::with_name("account_pubkey")
|
||||
.index(1)
|
||||
.value_name("ACCOUNT_ADDRESS")
|
||||
.required(true),
|
||||
"Account key URI. ")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("output_file")
|
||||
.long("output-file")
|
||||
.short("o")
|
||||
.value_name("FILEPATH")
|
||||
.takes_value(true)
|
||||
.help("Write the account data to this file"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("lamports")
|
||||
.long("lamports")
|
||||
.takes_value(false)
|
||||
.help("Display balance in lamports instead of SOL"),
|
||||
),
|
||||
)
|
||||
.validator_info_subcommands()
|
||||
.vote_subcommands()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -2318,6 +1596,7 @@ mod tests {
|
||||
use solana_sdk::{
|
||||
pubkey::Pubkey,
|
||||
signature::{keypair_from_seed, read_keypair_file, write_keypair_file, Keypair, Presigner},
|
||||
stake, system_program,
|
||||
transaction::TransactionError,
|
||||
};
|
||||
use solana_transaction_status::TransactionConfirmationStatus;
|
||||
@@ -2414,7 +1693,7 @@ mod tests {
|
||||
#[test]
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn test_cli_parse_command() {
|
||||
let test_commands = app("test", "desc", "version");
|
||||
let test_commands = get_clap_app("test", "desc", "version");
|
||||
|
||||
let pubkey = solana_sdk::pubkey::new_rand();
|
||||
let pubkey_string = format!("{}", pubkey);
|
||||
@@ -2951,7 +2230,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_parse_transfer_subcommand() {
|
||||
let test_commands = app("test", "desc", "version");
|
||||
let test_commands = get_clap_app("test", "desc", "version");
|
||||
|
||||
let default_keypair = Keypair::new();
|
||||
let default_keypair_file = make_tmp_path("keypair_file");
|
||||
|
@@ -2146,7 +2146,7 @@ pub fn process_calculate_rent(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::cli::{app, parse_command};
|
||||
use crate::{clap_app::get_clap_app, cli::parse_command};
|
||||
use solana_sdk::signature::{write_keypair, Keypair};
|
||||
use std::str::FromStr;
|
||||
use tempfile::NamedTempFile;
|
||||
@@ -2158,7 +2158,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_parse_command() {
|
||||
let test_commands = app("test", "desc", "version");
|
||||
let test_commands = get_clap_app("test", "desc", "version");
|
||||
let default_keypair = Keypair::new();
|
||||
let (default_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
|
@@ -24,6 +24,7 @@ extern crate const_format;
|
||||
extern crate serde_derive;
|
||||
|
||||
pub mod checks;
|
||||
pub mod clap_app;
|
||||
pub mod cli;
|
||||
pub mod cluster_query;
|
||||
pub mod feature;
|
||||
@@ -36,3 +37,4 @@ pub mod stake;
|
||||
pub mod test_utils;
|
||||
pub mod validator_info;
|
||||
pub mod vote;
|
||||
pub mod wallet;
|
||||
|
199
cli/src/main.rs
199
cli/src/main.rs
@@ -1,18 +1,15 @@
|
||||
use clap::{
|
||||
crate_description, crate_name, value_t_or_exit, AppSettings, Arg, ArgGroup, ArgMatches,
|
||||
SubCommand,
|
||||
};
|
||||
use clap::{crate_description, crate_name, value_t_or_exit, ArgMatches};
|
||||
use console::style;
|
||||
use solana_clap_utils::{
|
||||
input_validators::{is_url, is_url_or_moniker, normalize_to_url_if_moniker},
|
||||
keypair::{CliSigners, DefaultSigner, SKIP_SEED_PHRASE_VALIDATION_ARG},
|
||||
input_validators::normalize_to_url_if_moniker,
|
||||
keypair::{CliSigners, DefaultSigner},
|
||||
DisplayError,
|
||||
};
|
||||
use solana_cli::cli::{
|
||||
app, parse_command, process_command, CliCommandInfo, CliConfig, SettingType,
|
||||
DEFAULT_CONFIRM_TX_TIMEOUT_SECONDS, DEFAULT_RPC_TIMEOUT_SECONDS,
|
||||
use solana_cli::{
|
||||
clap_app::get_clap_app,
|
||||
cli::{parse_command, process_command, CliCommandInfo, CliConfig, SettingType},
|
||||
};
|
||||
use solana_cli_config::{Config, CONFIG_FILE};
|
||||
use solana_cli_config::Config;
|
||||
use solana_cli_output::{display::println_name_value, OutputFormat};
|
||||
use solana_client::rpc_config::RpcSendTransactionConfig;
|
||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||
@@ -200,18 +197,7 @@ pub fn parse_args<'a>(
|
||||
}
|
||||
|
||||
let verbose = matches.is_present("verbose");
|
||||
let output_format = matches
|
||||
.value_of("output_format")
|
||||
.map(|value| match value {
|
||||
"json" => OutputFormat::Json,
|
||||
"json-compact" => OutputFormat::JsonCompact,
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.unwrap_or(if verbose {
|
||||
OutputFormat::DisplayVerbose
|
||||
} else {
|
||||
OutputFormat::Display
|
||||
});
|
||||
let output_format = OutputFormat::from_matches(matches, "output_format", verbose);
|
||||
|
||||
let (_, commitment) = CliConfig::compute_commitment_config(
|
||||
matches.value_of("commitment").unwrap_or(""),
|
||||
@@ -249,178 +235,11 @@ pub fn parse_args<'a>(
|
||||
|
||||
fn main() -> Result<(), Box<dyn error::Error>> {
|
||||
solana_logger::setup_with_default("off");
|
||||
let matches = app(
|
||||
let matches = get_clap_app(
|
||||
crate_name!(),
|
||||
crate_description!(),
|
||||
solana_version::version!(),
|
||||
)
|
||||
.arg({
|
||||
let arg = Arg::with_name("config_file")
|
||||
.short("C")
|
||||
.long("config")
|
||||
.value_name("FILEPATH")
|
||||
.takes_value(true)
|
||||
.global(true)
|
||||
.help("Configuration file to use");
|
||||
if let Some(ref config_file) = *CONFIG_FILE {
|
||||
arg.default_value(config_file)
|
||||
} else {
|
||||
arg
|
||||
}
|
||||
})
|
||||
.arg(
|
||||
Arg::with_name("json_rpc_url")
|
||||
.short("u")
|
||||
.long("url")
|
||||
.value_name("URL_OR_MONIKER")
|
||||
.takes_value(true)
|
||||
.global(true)
|
||||
.validator(is_url_or_moniker)
|
||||
.help(
|
||||
"URL for Solana's JSON RPC or moniker (or their first letter): \
|
||||
[mainnet-beta, testnet, devnet, localhost]",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("websocket_url")
|
||||
.long("ws")
|
||||
.value_name("URL")
|
||||
.takes_value(true)
|
||||
.global(true)
|
||||
.validator(is_url)
|
||||
.help("WebSocket URL for the solana cluster"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("keypair")
|
||||
.short("k")
|
||||
.long("keypair")
|
||||
.value_name("KEYPAIR")
|
||||
.global(true)
|
||||
.takes_value(true)
|
||||
.help("Filepath or URL to a keypair"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("commitment")
|
||||
.long("commitment")
|
||||
.takes_value(true)
|
||||
.possible_values(&[
|
||||
"processed",
|
||||
"confirmed",
|
||||
"finalized",
|
||||
"recent", // Deprecated as of v1.5.5
|
||||
"single", // Deprecated as of v1.5.5
|
||||
"singleGossip", // Deprecated as of v1.5.5
|
||||
"root", // Deprecated as of v1.5.5
|
||||
"max", // Deprecated as of v1.5.5
|
||||
])
|
||||
.value_name("COMMITMENT_LEVEL")
|
||||
.hide_possible_values(true)
|
||||
.global(true)
|
||||
.help("Return information at the selected commitment level [possible values: processed, confirmed, finalized]"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("verbose")
|
||||
.long("verbose")
|
||||
.short("v")
|
||||
.global(true)
|
||||
.help("Show additional information"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("no_address_labels")
|
||||
.long("no-address-labels")
|
||||
.global(true)
|
||||
.help("Do not use address labels in the output"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("output_format")
|
||||
.long("output")
|
||||
.value_name("FORMAT")
|
||||
.global(true)
|
||||
.takes_value(true)
|
||||
.possible_values(&["json", "json-compact"])
|
||||
.help("Return information in specified output format"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(SKIP_SEED_PHRASE_VALIDATION_ARG.name)
|
||||
.long(SKIP_SEED_PHRASE_VALIDATION_ARG.long)
|
||||
.global(true)
|
||||
.help(SKIP_SEED_PHRASE_VALIDATION_ARG.help),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("rpc_timeout")
|
||||
.long("rpc-timeout")
|
||||
.value_name("SECONDS")
|
||||
.takes_value(true)
|
||||
.default_value(DEFAULT_RPC_TIMEOUT_SECONDS)
|
||||
.global(true)
|
||||
.hidden(true)
|
||||
.help("Timeout value for RPC requests"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("confirm_transaction_initial_timeout")
|
||||
.long("confirm-timeout")
|
||||
.value_name("SECONDS")
|
||||
.takes_value(true)
|
||||
.default_value(DEFAULT_CONFIRM_TX_TIMEOUT_SECONDS)
|
||||
.global(true)
|
||||
.hidden(true)
|
||||
.help("Timeout value for initial transaction status"),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("config")
|
||||
.about("Solana command-line tool configuration settings")
|
||||
.aliases(&["get", "set"])
|
||||
.setting(AppSettings::SubcommandRequiredElseHelp)
|
||||
.subcommand(
|
||||
SubCommand::with_name("get")
|
||||
.about("Get current config settings")
|
||||
.arg(
|
||||
Arg::with_name("specific_setting")
|
||||
.index(1)
|
||||
.value_name("CONFIG_FIELD")
|
||||
.takes_value(true)
|
||||
.possible_values(&[
|
||||
"json_rpc_url",
|
||||
"websocket_url",
|
||||
"keypair",
|
||||
"commitment",
|
||||
])
|
||||
.help("Return a specific config setting"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("set")
|
||||
.about("Set a config setting")
|
||||
.group(
|
||||
ArgGroup::with_name("config_settings")
|
||||
.args(&["json_rpc_url", "websocket_url", "keypair", "commitment"])
|
||||
.multiple(true)
|
||||
.required(true),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("import-address-labels")
|
||||
.about("Import a list of address labels")
|
||||
.arg(
|
||||
Arg::with_name("filename")
|
||||
.index(1)
|
||||
.value_name("FILENAME")
|
||||
.takes_value(true)
|
||||
.help("YAML file of address labels"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("export-address-labels")
|
||||
.about("Export the current address labels")
|
||||
.arg(
|
||||
Arg::with_name("filename")
|
||||
.index(1)
|
||||
.value_name("FILENAME")
|
||||
.takes_value(true)
|
||||
.help("YAML file to receive the current address labels"),
|
||||
),
|
||||
),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
do_main(&matches).map_err(|err| DisplayError::new_as_boxed(err).into())
|
||||
|
@@ -651,7 +651,7 @@ pub fn process_withdraw_from_nonce_account(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::cli::{app, parse_command};
|
||||
use crate::{clap_app::get_clap_app, cli::parse_command};
|
||||
use solana_sdk::{
|
||||
account::Account,
|
||||
account_utils::StateMut,
|
||||
@@ -671,7 +671,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_parse_command() {
|
||||
let test_commands = app("test", "desc", "version");
|
||||
let test_commands = get_clap_app("test", "desc", "version");
|
||||
let default_keypair = Keypair::new();
|
||||
let (default_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
|
@@ -330,7 +330,7 @@ impl ProgramSubCommands for App<'_, '_> {
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("close")
|
||||
.about("Close an acount and withdraw all lamports")
|
||||
.about("Close an account and withdraw all lamports")
|
||||
.arg(
|
||||
Arg::with_name("account")
|
||||
.index(1)
|
||||
@@ -367,6 +367,39 @@ impl ProgramSubCommands for App<'_, '_> {
|
||||
),
|
||||
)
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("deploy")
|
||||
.about("Deploy a program")
|
||||
.arg(
|
||||
Arg::with_name("program_location")
|
||||
.index(1)
|
||||
.value_name("PROGRAM_FILEPATH")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.help("/path/to/program.o"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("address_signer")
|
||||
.index(2)
|
||||
.value_name("PROGRAM_ADDRESS_SIGNER")
|
||||
.takes_value(true)
|
||||
.validator(is_valid_signer)
|
||||
.help("The signer for the desired address of the program [default: new random address]")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("use_deprecated_loader")
|
||||
.long("use-deprecated-loader")
|
||||
.takes_value(false)
|
||||
.hidden(true) // Don't document this argument to discourage its use
|
||||
.help("Use the deprecated BPF loader")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("allow_excessive_balance")
|
||||
.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")
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2109,7 +2142,10 @@ fn send_and_confirm_transactions_with_spinner<T: Signers>(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::cli::{app, parse_command, process_command};
|
||||
use crate::{
|
||||
clap_app::get_clap_app,
|
||||
cli::{parse_command, process_command},
|
||||
};
|
||||
use serde_json::Value;
|
||||
use solana_cli_output::OutputFormat;
|
||||
use solana_sdk::signature::write_keypair_file;
|
||||
@@ -2131,7 +2167,7 @@ mod tests {
|
||||
#[test]
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn test_cli_parse_deploy() {
|
||||
let test_commands = app("test", "desc", "version");
|
||||
let test_commands = get_clap_app("test", "desc", "version");
|
||||
|
||||
let default_keypair = Keypair::new();
|
||||
let keypair_file = make_tmp_path("keypair_file");
|
||||
@@ -2339,7 +2375,7 @@ mod tests {
|
||||
#[test]
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn test_cli_parse_write_buffer() {
|
||||
let test_commands = app("test", "desc", "version");
|
||||
let test_commands = get_clap_app("test", "desc", "version");
|
||||
|
||||
let default_keypair = Keypair::new();
|
||||
let keypair_file = make_tmp_path("keypair_file");
|
||||
@@ -2487,7 +2523,7 @@ mod tests {
|
||||
#[test]
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn test_cli_parse_set_upgrade_authority() {
|
||||
let test_commands = app("test", "desc", "version");
|
||||
let test_commands = get_clap_app("test", "desc", "version");
|
||||
|
||||
let default_keypair = Keypair::new();
|
||||
let keypair_file = make_tmp_path("keypair_file");
|
||||
@@ -2595,7 +2631,7 @@ mod tests {
|
||||
#[test]
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn test_cli_parse_set_buffer_authority() {
|
||||
let test_commands = app("test", "desc", "version");
|
||||
let test_commands = get_clap_app("test", "desc", "version");
|
||||
|
||||
let default_keypair = Keypair::new();
|
||||
let keypair_file = make_tmp_path("keypair_file");
|
||||
@@ -2652,7 +2688,7 @@ mod tests {
|
||||
#[test]
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn test_cli_parse_show() {
|
||||
let test_commands = app("test", "desc", "version");
|
||||
let test_commands = get_clap_app("test", "desc", "version");
|
||||
|
||||
let default_keypair = Keypair::new();
|
||||
let keypair_file = make_tmp_path("keypair_file");
|
||||
@@ -2751,7 +2787,7 @@ mod tests {
|
||||
#[test]
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn test_cli_parse_close() {
|
||||
let test_commands = app("test", "desc", "version");
|
||||
let test_commands = get_clap_app("test", "desc", "version");
|
||||
|
||||
let default_keypair = Keypair::new();
|
||||
let keypair_file = make_tmp_path("keypair_file");
|
||||
|
@@ -2358,7 +2358,7 @@ pub fn is_stake_program_v2_enabled(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::cli::{app, parse_command};
|
||||
use crate::{clap_app::get_clap_app, cli::parse_command};
|
||||
use solana_client::blockhash_query;
|
||||
use solana_sdk::{
|
||||
hash::Hash,
|
||||
@@ -2376,7 +2376,7 @@ mod tests {
|
||||
#[test]
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn test_parse_command() {
|
||||
let test_commands = app("test", "desc", "version");
|
||||
let test_commands = get_clap_app("test", "desc", "version");
|
||||
let default_keypair = Keypair::new();
|
||||
let (default_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
|
@@ -408,7 +408,7 @@ pub fn process_get_validator_info(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::cli::app;
|
||||
use crate::clap_app::get_clap_app;
|
||||
use bincode::{serialize, serialized_size};
|
||||
use serde_json::json;
|
||||
|
||||
@@ -432,7 +432,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_parse_args() {
|
||||
let matches = app("test", "desc", "version").get_matches_from(vec![
|
||||
let matches = get_clap_app("test", "desc", "version").get_matches_from(vec![
|
||||
"test",
|
||||
"validator-info",
|
||||
"publish",
|
||||
|
@@ -880,7 +880,7 @@ pub fn process_withdraw_from_vote_account(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::cli::{app, parse_command};
|
||||
use crate::{clap_app::get_clap_app, cli::parse_command};
|
||||
use solana_sdk::signature::{read_keypair_file, write_keypair, Keypair, Signer};
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
@@ -891,7 +891,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_parse_command() {
|
||||
let test_commands = app("test", "desc", "version");
|
||||
let test_commands = get_clap_app("test", "desc", "version");
|
||||
let keypair = Keypair::new();
|
||||
let pubkey = keypair.pubkey();
|
||||
let pubkey_string = pubkey.to_string();
|
||||
|
744
cli/src/wallet.rs
Normal file
744
cli/src/wallet.rs
Normal file
@@ -0,0 +1,744 @@
|
||||
use crate::{
|
||||
cli::{
|
||||
log_instruction_custom_error, request_and_confirm_airdrop, CliCommand, CliCommandInfo,
|
||||
CliConfig, CliError, ProcessResult,
|
||||
},
|
||||
memo::WithMemo,
|
||||
nonce::check_nonce_account,
|
||||
spend_utils::{resolve_spend_tx_and_check_account_balances, SpendAmount},
|
||||
};
|
||||
use clap::{value_t_or_exit, App, Arg, ArgMatches, SubCommand};
|
||||
use solana_account_decoder::{UiAccount, UiAccountEncoding};
|
||||
use solana_clap_utils::{
|
||||
fee_payer::*,
|
||||
input_parsers::*,
|
||||
input_validators::*,
|
||||
keypair::{DefaultSigner, SignerIndex},
|
||||
memo::*,
|
||||
nonce::*,
|
||||
offline::*,
|
||||
};
|
||||
use solana_cli_output::{
|
||||
display::build_balance_message, return_signers_with_config, CliAccount,
|
||||
CliSignatureVerificationStatus, CliTransaction, CliTransactionConfirmation, OutputFormat,
|
||||
ReturnSignersConfig,
|
||||
};
|
||||
use solana_client::{
|
||||
blockhash_query::BlockhashQuery, nonce_utils, rpc_client::RpcClient,
|
||||
rpc_config::RpcTransactionConfig, rpc_response::RpcKeyedAccount,
|
||||
};
|
||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||
use solana_sdk::{
|
||||
commitment_config::CommitmentConfig,
|
||||
message::Message,
|
||||
pubkey::Pubkey,
|
||||
signature::Signature,
|
||||
stake,
|
||||
system_instruction::{self, SystemError},
|
||||
system_program,
|
||||
transaction::Transaction,
|
||||
};
|
||||
use solana_transaction_status::{EncodedTransaction, UiTransactionEncoding};
|
||||
use std::{fmt::Write as FmtWrite, fs::File, io::Write, sync::Arc};
|
||||
|
||||
pub trait WalletSubCommands {
|
||||
fn wallet_subcommands(self) -> Self;
|
||||
}
|
||||
|
||||
impl WalletSubCommands for App<'_, '_> {
|
||||
fn wallet_subcommands(self) -> Self {
|
||||
self.subcommand(
|
||||
SubCommand::with_name("account")
|
||||
.about("Show the contents of an account")
|
||||
.alias("account")
|
||||
.arg(
|
||||
pubkey!(Arg::with_name("account_pubkey")
|
||||
.index(1)
|
||||
.value_name("ACCOUNT_ADDRESS")
|
||||
.required(true),
|
||||
"Account key URI. ")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("output_file")
|
||||
.long("output-file")
|
||||
.short("o")
|
||||
.value_name("FILEPATH")
|
||||
.takes_value(true)
|
||||
.help("Write the account data to this file"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("lamports")
|
||||
.long("lamports")
|
||||
.takes_value(false)
|
||||
.help("Display balance in lamports instead of SOL"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("address")
|
||||
.about("Get your public key")
|
||||
.arg(
|
||||
Arg::with_name("confirm_key")
|
||||
.long("confirm-key")
|
||||
.takes_value(false)
|
||||
.help("Confirm key on device; only relevant if using remote wallet"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("airdrop")
|
||||
.about("Request SOL from a faucet")
|
||||
.arg(
|
||||
Arg::with_name("amount")
|
||||
.index(1)
|
||||
.value_name("AMOUNT")
|
||||
.takes_value(true)
|
||||
.validator(is_amount)
|
||||
.required(true)
|
||||
.help("The airdrop amount to request, in SOL"),
|
||||
)
|
||||
.arg(
|
||||
pubkey!(Arg::with_name("to")
|
||||
.index(2)
|
||||
.value_name("RECIPIENT_ADDRESS"),
|
||||
"The account address of airdrop recipient. "),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("balance")
|
||||
.about("Get your balance")
|
||||
.arg(
|
||||
pubkey!(Arg::with_name("pubkey")
|
||||
.index(1)
|
||||
.value_name("ACCOUNT_ADDRESS"),
|
||||
"The account address of the balance to check. ")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("lamports")
|
||||
.long("lamports")
|
||||
.takes_value(false)
|
||||
.help("Display balance in lamports instead of SOL"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("confirm")
|
||||
.about("Confirm transaction by signature")
|
||||
.arg(
|
||||
Arg::with_name("signature")
|
||||
.index(1)
|
||||
.value_name("TRANSACTION_SIGNATURE")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.help("The transaction signature to confirm"),
|
||||
)
|
||||
.after_help(// Formatted specifically for the manually-indented heredoc string
|
||||
"Note: This will show more detailed information for finalized transactions with verbose mode (-v/--verbose).\
|
||||
\n\
|
||||
\nAccount modes:\
|
||||
\n |srwx|\
|
||||
\n s: signed\
|
||||
\n r: readable (always true)\
|
||||
\n w: writable\
|
||||
\n x: program account (inner instructions excluded)\
|
||||
"
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("create-address-with-seed")
|
||||
.about("Generate a derived account address with a seed")
|
||||
.arg(
|
||||
Arg::with_name("seed")
|
||||
.index(1)
|
||||
.value_name("SEED_STRING")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.validator(is_derived_address_seed)
|
||||
.help("The seed. Must not take more than 32 bytes to encode as utf-8"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("program_id")
|
||||
.index(2)
|
||||
.value_name("PROGRAM_ID")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.help(
|
||||
"The program_id that the address will ultimately be used for, \n\
|
||||
or one of NONCE, STAKE, and VOTE keywords",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
pubkey!(Arg::with_name("from")
|
||||
.long("from")
|
||||
.value_name("FROM_PUBKEY")
|
||||
.required(false),
|
||||
"From (base) key, [default: cli config keypair]. "),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("decode-transaction")
|
||||
.about("Decode a serialized transaction")
|
||||
.arg(
|
||||
Arg::with_name("transaction")
|
||||
.index(1)
|
||||
.value_name("TRANSACTION")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.help("transaction to decode"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("encoding")
|
||||
.index(2)
|
||||
.value_name("ENCODING")
|
||||
.possible_values(&["base58", "base64"]) // Subset of `UiTransactionEncoding` enum
|
||||
.default_value("base58")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.help("transaction encoding"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("resolve-signer")
|
||||
.about("Checks that a signer is valid, and returns its specific path; useful for signers that may be specified generally, eg. usb://ledger")
|
||||
.arg(
|
||||
Arg::with_name("signer")
|
||||
.index(1)
|
||||
.value_name("SIGNER_KEYPAIR")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.validator(is_valid_signer)
|
||||
.help("The signer path to resolve")
|
||||
)
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("transfer")
|
||||
.about("Transfer funds between system accounts")
|
||||
.alias("pay")
|
||||
.arg(
|
||||
pubkey!(Arg::with_name("to")
|
||||
.index(1)
|
||||
.value_name("RECIPIENT_ADDRESS")
|
||||
.required(true),
|
||||
"The account address of recipient. "),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("amount")
|
||||
.index(2)
|
||||
.value_name("AMOUNT")
|
||||
.takes_value(true)
|
||||
.validator(is_amount_or_all)
|
||||
.required(true)
|
||||
.help("The amount to send, in SOL; accepts keyword ALL"),
|
||||
)
|
||||
.arg(
|
||||
pubkey!(Arg::with_name("from")
|
||||
.long("from")
|
||||
.value_name("FROM_ADDRESS"),
|
||||
"Source account of funds (if different from client local account). "),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("no_wait")
|
||||
.long("no-wait")
|
||||
.takes_value(false)
|
||||
.help("Return signature immediately after submitting the transaction, instead of waiting for confirmations"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("derived_address_seed")
|
||||
.long("derived-address-seed")
|
||||
.takes_value(true)
|
||||
.value_name("SEED_STRING")
|
||||
.requires("derived_address_program_id")
|
||||
.validator(is_derived_address_seed)
|
||||
.hidden(true)
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("derived_address_program_id")
|
||||
.long("derived-address-program-id")
|
||||
.takes_value(true)
|
||||
.value_name("PROGRAM_ID")
|
||||
.requires("derived_address_seed")
|
||||
.hidden(true)
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("allow_unfunded_recipient")
|
||||
.long("allow-unfunded-recipient")
|
||||
.takes_value(false)
|
||||
.help("Complete the transfer even if the recipient address is not funded")
|
||||
)
|
||||
.offline_args()
|
||||
.nonce_args(false)
|
||||
.arg(memo_arg())
|
||||
.arg(fee_payer_arg()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_derived_address_program_id(matches: &ArgMatches<'_>, arg_name: &str) -> Option<Pubkey> {
|
||||
matches.value_of(arg_name).and_then(|v| match v {
|
||||
"NONCE" => Some(system_program::id()),
|
||||
"STAKE" => Some(stake::program::id()),
|
||||
"VOTE" => Some(solana_vote_program::id()),
|
||||
_ => pubkey_of(matches, arg_name),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_account(
|
||||
matches: &ArgMatches<'_>,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let account_pubkey = pubkey_of_signer(matches, "account_pubkey", wallet_manager)?.unwrap();
|
||||
let output_file = matches.value_of("output_file");
|
||||
let use_lamports_unit = matches.is_present("lamports");
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::ShowAccount {
|
||||
pubkey: account_pubkey,
|
||||
output_file: output_file.map(ToString::to_string),
|
||||
use_lamports_unit,
|
||||
},
|
||||
signers: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_airdrop(
|
||||
matches: &ArgMatches<'_>,
|
||||
default_signer: &DefaultSigner,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let pubkey = pubkey_of_signer(matches, "to", wallet_manager)?;
|
||||
let signers = if pubkey.is_some() {
|
||||
vec![]
|
||||
} else {
|
||||
vec![default_signer.signer_from_path(matches, wallet_manager)?]
|
||||
};
|
||||
let lamports = lamports_of_sol(matches, "amount").unwrap();
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::Airdrop { pubkey, lamports },
|
||||
signers,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_balance(
|
||||
matches: &ArgMatches<'_>,
|
||||
default_signer: &DefaultSigner,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let pubkey = pubkey_of_signer(matches, "pubkey", wallet_manager)?;
|
||||
let signers = if pubkey.is_some() {
|
||||
vec![]
|
||||
} else {
|
||||
vec![default_signer.signer_from_path(matches, wallet_manager)?]
|
||||
};
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::Balance {
|
||||
pubkey,
|
||||
use_lamports_unit: matches.is_present("lamports"),
|
||||
},
|
||||
signers,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_decode_transaction(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||
let blob = value_t_or_exit!(matches, "transaction", String);
|
||||
let encoding = match matches.value_of("encoding").unwrap() {
|
||||
"base58" => UiTransactionEncoding::Base58,
|
||||
"base64" => UiTransactionEncoding::Base64,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let encoded_transaction = EncodedTransaction::Binary(blob, encoding);
|
||||
if let Some(transaction) = encoded_transaction.decode() {
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::DecodeTransaction(transaction),
|
||||
signers: vec![],
|
||||
})
|
||||
} else {
|
||||
Err(CliError::BadParameter(
|
||||
"Unable to decode transaction".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_create_address_with_seed(
|
||||
matches: &ArgMatches<'_>,
|
||||
default_signer: &DefaultSigner,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let from_pubkey = pubkey_of_signer(matches, "from", wallet_manager)?;
|
||||
let signers = if from_pubkey.is_some() {
|
||||
vec![]
|
||||
} else {
|
||||
vec![default_signer.signer_from_path(matches, wallet_manager)?]
|
||||
};
|
||||
|
||||
let program_id = resolve_derived_address_program_id(matches, "program_id").unwrap();
|
||||
|
||||
let seed = matches.value_of("seed").unwrap().to_string();
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::CreateAddressWithSeed {
|
||||
from_pubkey,
|
||||
seed,
|
||||
program_id,
|
||||
},
|
||||
signers,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_transfer(
|
||||
matches: &ArgMatches<'_>,
|
||||
default_signer: &DefaultSigner,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let amount = SpendAmount::new_from_matches(matches, "amount");
|
||||
let to = pubkey_of_signer(matches, "to", wallet_manager)?.unwrap();
|
||||
let sign_only = matches.is_present(SIGN_ONLY_ARG.name);
|
||||
let dump_transaction_message = matches.is_present(DUMP_TRANSACTION_MESSAGE.name);
|
||||
let no_wait = matches.is_present("no_wait");
|
||||
let blockhash_query = BlockhashQuery::new_from_matches(matches);
|
||||
let nonce_account = pubkey_of_signer(matches, NONCE_ARG.name, wallet_manager)?;
|
||||
let (nonce_authority, nonce_authority_pubkey) =
|
||||
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
|
||||
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
|
||||
let (fee_payer, fee_payer_pubkey) = signer_of(matches, FEE_PAYER_ARG.name, wallet_manager)?;
|
||||
let (from, from_pubkey) = signer_of(matches, "from", wallet_manager)?;
|
||||
let allow_unfunded_recipient = matches.is_present("allow_unfunded_recipient");
|
||||
|
||||
let mut bulk_signers = vec![fee_payer, from];
|
||||
if nonce_account.is_some() {
|
||||
bulk_signers.push(nonce_authority);
|
||||
}
|
||||
|
||||
let signer_info =
|
||||
default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
|
||||
|
||||
let derived_address_seed = matches
|
||||
.value_of("derived_address_seed")
|
||||
.map(|s| s.to_string());
|
||||
let derived_address_program_id =
|
||||
resolve_derived_address_program_id(matches, "derived_address_program_id");
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::Transfer {
|
||||
amount,
|
||||
to,
|
||||
sign_only,
|
||||
dump_transaction_message,
|
||||
allow_unfunded_recipient,
|
||||
no_wait,
|
||||
blockhash_query,
|
||||
nonce_account,
|
||||
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
|
||||
memo,
|
||||
fee_payer: signer_info.index_of(fee_payer_pubkey).unwrap(),
|
||||
from: signer_info.index_of(from_pubkey).unwrap(),
|
||||
derived_address_seed,
|
||||
derived_address_program_id,
|
||||
},
|
||||
signers: signer_info.signers,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn process_show_account(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
account_pubkey: &Pubkey,
|
||||
output_file: &Option<String>,
|
||||
use_lamports_unit: bool,
|
||||
) -> ProcessResult {
|
||||
let account = rpc_client.get_account(account_pubkey)?;
|
||||
let data = account.data.clone();
|
||||
let cli_account = CliAccount {
|
||||
keyed_account: RpcKeyedAccount {
|
||||
pubkey: account_pubkey.to_string(),
|
||||
account: UiAccount::encode(
|
||||
account_pubkey,
|
||||
&account,
|
||||
UiAccountEncoding::Base64,
|
||||
None,
|
||||
None,
|
||||
),
|
||||
},
|
||||
use_lamports_unit,
|
||||
};
|
||||
|
||||
let mut account_string = config.output_format.formatted_string(&cli_account);
|
||||
|
||||
if config.output_format == OutputFormat::Display
|
||||
|| config.output_format == OutputFormat::DisplayVerbose
|
||||
{
|
||||
if let Some(output_file) = output_file {
|
||||
let mut f = File::create(output_file)?;
|
||||
f.write_all(&data)?;
|
||||
writeln!(&mut account_string)?;
|
||||
writeln!(&mut account_string, "Wrote account data to {}", output_file)?;
|
||||
} else if !data.is_empty() {
|
||||
use pretty_hex::*;
|
||||
writeln!(&mut account_string, "{:?}", data.hex_dump())?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(account_string)
|
||||
}
|
||||
|
||||
pub fn process_airdrop(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
pubkey: &Option<Pubkey>,
|
||||
lamports: u64,
|
||||
) -> ProcessResult {
|
||||
let pubkey = if let Some(pubkey) = pubkey {
|
||||
*pubkey
|
||||
} else {
|
||||
config.pubkey()?
|
||||
};
|
||||
println!(
|
||||
"Requesting airdrop of {}",
|
||||
build_balance_message(lamports, false, true),
|
||||
);
|
||||
|
||||
let pre_balance = rpc_client.get_balance(&pubkey)?;
|
||||
|
||||
let result = request_and_confirm_airdrop(rpc_client, config, &pubkey, lamports);
|
||||
if let Ok(signature) = result {
|
||||
let signature_cli_message = log_instruction_custom_error::<SystemError>(result, config)?;
|
||||
println!("{}", signature_cli_message);
|
||||
|
||||
let current_balance = rpc_client.get_balance(&pubkey)?;
|
||||
|
||||
if current_balance < pre_balance.saturating_add(lamports) {
|
||||
println!("Balance unchanged");
|
||||
println!("Run `solana confirm -v {:?}` for more info", signature);
|
||||
Ok("".to_string())
|
||||
} else {
|
||||
Ok(build_balance_message(current_balance, false, true))
|
||||
}
|
||||
} else {
|
||||
log_instruction_custom_error::<SystemError>(result, config)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_balance(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
pubkey: &Option<Pubkey>,
|
||||
use_lamports_unit: bool,
|
||||
) -> ProcessResult {
|
||||
let pubkey = if let Some(pubkey) = pubkey {
|
||||
*pubkey
|
||||
} else {
|
||||
config.pubkey()?
|
||||
};
|
||||
let balance = rpc_client.get_balance(&pubkey)?;
|
||||
Ok(build_balance_message(balance, use_lamports_unit, true))
|
||||
}
|
||||
|
||||
pub fn process_confirm(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
signature: &Signature,
|
||||
) -> ProcessResult {
|
||||
match rpc_client.get_signature_statuses_with_history(&[*signature]) {
|
||||
Ok(status) => {
|
||||
let cli_transaction = if let Some(transaction_status) = &status.value[0] {
|
||||
let mut transaction = None;
|
||||
let mut get_transaction_error = None;
|
||||
if config.verbose {
|
||||
match rpc_client.get_transaction_with_config(
|
||||
signature,
|
||||
RpcTransactionConfig {
|
||||
encoding: Some(UiTransactionEncoding::Base64),
|
||||
commitment: Some(CommitmentConfig::confirmed()),
|
||||
},
|
||||
) {
|
||||
Ok(confirmed_transaction) => {
|
||||
let decoded_transaction = confirmed_transaction
|
||||
.transaction
|
||||
.transaction
|
||||
.decode()
|
||||
.expect("Successful decode");
|
||||
let json_transaction = EncodedTransaction::encode(
|
||||
decoded_transaction.clone(),
|
||||
UiTransactionEncoding::Json,
|
||||
);
|
||||
|
||||
transaction = Some(CliTransaction {
|
||||
transaction: json_transaction,
|
||||
meta: confirmed_transaction.transaction.meta,
|
||||
block_time: confirmed_transaction.block_time,
|
||||
slot: Some(confirmed_transaction.slot),
|
||||
decoded_transaction,
|
||||
prefix: " ".to_string(),
|
||||
sigverify_status: vec![],
|
||||
});
|
||||
}
|
||||
Err(err) => {
|
||||
get_transaction_error = Some(format!("{:?}", err));
|
||||
}
|
||||
}
|
||||
}
|
||||
CliTransactionConfirmation {
|
||||
confirmation_status: Some(transaction_status.confirmation_status()),
|
||||
transaction,
|
||||
get_transaction_error,
|
||||
err: transaction_status.err.clone(),
|
||||
}
|
||||
} else {
|
||||
CliTransactionConfirmation {
|
||||
confirmation_status: None,
|
||||
transaction: None,
|
||||
get_transaction_error: None,
|
||||
err: None,
|
||||
}
|
||||
};
|
||||
Ok(config.output_format.formatted_string(&cli_transaction))
|
||||
}
|
||||
Err(err) => Err(CliError::RpcRequestError(format!("Unable to confirm: {}", err)).into()),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
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: EncodedTransaction::encode(transaction.clone(), UiTransactionEncoding::Json),
|
||||
meta: None,
|
||||
block_time: None,
|
||||
slot: None,
|
||||
prefix: "".to_string(),
|
||||
sigverify_status,
|
||||
};
|
||||
Ok(config.output_format.formatted_string(&decode_transaction))
|
||||
}
|
||||
|
||||
pub fn process_create_address_with_seed(
|
||||
config: &CliConfig,
|
||||
from_pubkey: Option<&Pubkey>,
|
||||
seed: &str,
|
||||
program_id: &Pubkey,
|
||||
) -> ProcessResult {
|
||||
let from_pubkey = if let Some(pubkey) = from_pubkey {
|
||||
*pubkey
|
||||
} else {
|
||||
config.pubkey()?
|
||||
};
|
||||
let address = Pubkey::create_with_seed(&from_pubkey, seed, program_id)?;
|
||||
Ok(address.to_string())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn process_transfer(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
amount: SpendAmount,
|
||||
to: &Pubkey,
|
||||
from: SignerIndex,
|
||||
sign_only: bool,
|
||||
dump_transaction_message: bool,
|
||||
allow_unfunded_recipient: bool,
|
||||
no_wait: bool,
|
||||
blockhash_query: &BlockhashQuery,
|
||||
nonce_account: Option<&Pubkey>,
|
||||
nonce_authority: SignerIndex,
|
||||
memo: Option<&String>,
|
||||
fee_payer: SignerIndex,
|
||||
derived_address_seed: Option<String>,
|
||||
derived_address_program_id: Option<&Pubkey>,
|
||||
) -> ProcessResult {
|
||||
let from = config.signers[from];
|
||||
let mut from_pubkey = from.pubkey();
|
||||
|
||||
let (recent_blockhash, fee_calculator) =
|
||||
blockhash_query.get_blockhash_and_fee_calculator(rpc_client, config.commitment)?;
|
||||
|
||||
if !sign_only && !allow_unfunded_recipient {
|
||||
let recipient_balance = rpc_client
|
||||
.get_balance_with_commitment(to, config.commitment)?
|
||||
.value;
|
||||
if recipient_balance == 0 {
|
||||
return Err(format!(
|
||||
"The recipient address ({}) is not funded. \
|
||||
Add `--allow-unfunded-recipient` to complete the transfer \
|
||||
",
|
||||
to
|
||||
)
|
||||
.into());
|
||||
}
|
||||
}
|
||||
|
||||
let nonce_authority = config.signers[nonce_authority];
|
||||
let fee_payer = config.signers[fee_payer];
|
||||
|
||||
let derived_parts = derived_address_seed.zip(derived_address_program_id);
|
||||
let with_seed = if let Some((seed, program_id)) = derived_parts {
|
||||
let base_pubkey = from_pubkey;
|
||||
from_pubkey = Pubkey::create_with_seed(&base_pubkey, &seed, program_id)?;
|
||||
Some((base_pubkey, seed, program_id, from_pubkey))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let build_message = |lamports| {
|
||||
let ixs = if let Some((base_pubkey, seed, program_id, from_pubkey)) = with_seed.as_ref() {
|
||||
vec![system_instruction::transfer_with_seed(
|
||||
from_pubkey,
|
||||
base_pubkey,
|
||||
seed.clone(),
|
||||
program_id,
|
||||
to,
|
||||
lamports,
|
||||
)]
|
||||
.with_memo(memo)
|
||||
} else {
|
||||
vec![system_instruction::transfer(&from_pubkey, to, lamports)].with_memo(memo)
|
||||
};
|
||||
|
||||
if let Some(nonce_account) = &nonce_account {
|
||||
Message::new_with_nonce(
|
||||
ixs,
|
||||
Some(&fee_payer.pubkey()),
|
||||
nonce_account,
|
||||
&nonce_authority.pubkey(),
|
||||
)
|
||||
} else {
|
||||
Message::new(&ixs, Some(&fee_payer.pubkey()))
|
||||
}
|
||||
};
|
||||
|
||||
let (message, _) = resolve_spend_tx_and_check_account_balances(
|
||||
rpc_client,
|
||||
sign_only,
|
||||
amount,
|
||||
&fee_calculator,
|
||||
&from_pubkey,
|
||||
&fee_payer.pubkey(),
|
||||
build_message,
|
||||
config.commitment,
|
||||
)?;
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
|
||||
if sign_only {
|
||||
tx.try_partial_sign(&config.signers, recent_blockhash)?;
|
||||
return_signers_with_config(
|
||||
&tx,
|
||||
&config.output_format,
|
||||
&ReturnSignersConfig {
|
||||
dump_transaction_message,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
if let Some(nonce_account) = &nonce_account {
|
||||
let nonce_account = nonce_utils::get_account_with_commitment(
|
||||
rpc_client,
|
||||
nonce_account,
|
||||
config.commitment,
|
||||
)?;
|
||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
||||
}
|
||||
|
||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||
let result = if no_wait {
|
||||
rpc_client.send_transaction(&tx)
|
||||
} else {
|
||||
rpc_client.send_and_confirm_transaction_with_spinner(&tx)
|
||||
};
|
||||
log_instruction_custom_error::<SystemError>(result, config)
|
||||
}
|
||||
}
|
@@ -18,13 +18,15 @@ use solana_sdk::{
|
||||
signature::{keypair_from_seed, Keypair, Signer},
|
||||
system_program,
|
||||
};
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
|
||||
#[test]
|
||||
fn test_nonce() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
|
||||
|
||||
full_battery_tests(test_validator, None, false);
|
||||
}
|
||||
@@ -34,7 +36,8 @@ fn test_nonce_with_seed() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
|
||||
|
||||
full_battery_tests(test_validator, Some(String::from("seed")), false);
|
||||
}
|
||||
@@ -44,7 +47,8 @@ fn test_nonce_with_authority() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
|
||||
|
||||
full_battery_tests(test_validator, None, true);
|
||||
}
|
||||
@@ -216,7 +220,12 @@ fn test_create_account_with_seed() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
|
||||
let test_validator = TestValidator::with_custom_fees(
|
||||
mint_pubkey,
|
||||
1,
|
||||
Some(faucet_addr),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
|
||||
let offline_nonce_authority_signer = keypair_from_seed(&[1u8; 32]).unwrap();
|
||||
let online_nonce_creator_signer = keypair_from_seed(&[2u8; 32]).unwrap();
|
||||
|
@@ -15,6 +15,7 @@ use solana_sdk::{
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, Signer},
|
||||
};
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
use std::{env, fs::File, io::Read, path::PathBuf, str::FromStr};
|
||||
|
||||
#[test]
|
||||
@@ -30,7 +31,8 @@ fn test_cli_program_deploy_non_upgradeable() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -146,7 +148,8 @@ fn test_cli_program_deploy_no_authority() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -229,7 +232,8 @@ fn test_cli_program_deploy_with_authority() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -557,7 +561,8 @@ fn test_cli_program_write_buffer() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -839,7 +844,8 @@ fn test_cli_program_set_buffer_authority() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -952,7 +958,8 @@ fn test_cli_program_mismatch_buffer_authority() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -1041,7 +1048,8 @@ fn test_cli_program_show() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -1221,7 +1229,8 @@ fn test_cli_program_dump() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
|
@@ -6,13 +6,15 @@ use solana_sdk::{
|
||||
commitment_config::CommitmentConfig,
|
||||
signature::{Keypair, Signer},
|
||||
};
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
|
||||
#[test]
|
||||
fn test_cli_request_airdrop() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
|
||||
|
||||
let mut bob_config = CliConfig::recent_for_tests();
|
||||
bob_config.json_rpc_url = test_validator.rpc_url();
|
||||
|
@@ -24,13 +24,15 @@ use solana_sdk::{
|
||||
state::{Lockup, StakeAuthorize, StakeState},
|
||||
},
|
||||
};
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
|
||||
#[test]
|
||||
fn test_stake_delegation_force() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -120,7 +122,8 @@ fn test_seed_stake_delegation_and_deactivation() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -206,7 +209,8 @@ fn test_stake_delegation_and_deactivation() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -288,7 +292,8 @@ fn test_offline_stake_delegation_and_deactivation() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -431,7 +436,8 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -548,7 +554,8 @@ fn test_stake_authorize() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -862,7 +869,12 @@ fn test_stake_authorize_with_fee_payer() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(mint_pubkey, SIG_FEE, Some(faucet_addr));
|
||||
let test_validator = TestValidator::with_custom_fees(
|
||||
mint_pubkey,
|
||||
SIG_FEE,
|
||||
Some(faucet_addr),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -1010,7 +1022,12 @@ fn test_stake_split() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
|
||||
let test_validator = TestValidator::with_custom_fees(
|
||||
mint_pubkey,
|
||||
1,
|
||||
Some(faucet_addr),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -1154,7 +1171,12 @@ fn test_stake_set_lockup() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
|
||||
let test_validator = TestValidator::with_custom_fees(
|
||||
mint_pubkey,
|
||||
1,
|
||||
Some(faucet_addr),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -1420,7 +1442,8 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -1644,7 +1667,8 @@ fn test_stake_checked_instructions() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
|
@@ -18,6 +18,7 @@ use solana_sdk::{
|
||||
signature::{keypair_from_seed, Keypair, NullSigner, Signer},
|
||||
stake,
|
||||
};
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
|
||||
#[test]
|
||||
fn test_transfer() {
|
||||
@@ -25,7 +26,12 @@ fn test_transfer() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
|
||||
let test_validator = TestValidator::with_custom_fees(
|
||||
mint_pubkey,
|
||||
1,
|
||||
Some(faucet_addr),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -277,7 +283,12 @@ fn test_transfer_multisession_signing() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
|
||||
let test_validator = TestValidator::with_custom_fees(
|
||||
mint_pubkey,
|
||||
1,
|
||||
Some(faucet_addr),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
|
||||
let to_pubkey = Pubkey::new(&[1u8; 32]);
|
||||
let offline_from_signer = keypair_from_seed(&[2u8; 32]).unwrap();
|
||||
@@ -404,7 +415,12 @@ fn test_transfer_all() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
|
||||
let test_validator = TestValidator::with_custom_fees(
|
||||
mint_pubkey,
|
||||
1,
|
||||
Some(faucet_addr),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -452,7 +468,12 @@ fn test_transfer_unfunded_recipient() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
|
||||
let test_validator = TestValidator::with_custom_fees(
|
||||
mint_pubkey,
|
||||
1,
|
||||
Some(faucet_addr),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -500,7 +521,12 @@ fn test_transfer_with_seed() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
|
||||
let test_validator = TestValidator::with_custom_fees(
|
||||
mint_pubkey,
|
||||
1,
|
||||
Some(faucet_addr),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
|
@@ -14,6 +14,7 @@ use solana_sdk::{
|
||||
commitment_config::CommitmentConfig,
|
||||
signature::{Keypair, Signer},
|
||||
};
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
use solana_vote_program::vote_state::{VoteAuthorize, VoteState, VoteStateVersions};
|
||||
|
||||
#[test]
|
||||
@@ -21,7 +22,8 @@ fn test_vote_authorize_and_withdraw() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-client"
|
||||
version = "1.7.6"
|
||||
version = "1.7.9"
|
||||
description = "Solana Client"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -24,14 +24,14 @@ semver = "0.11.0"
|
||||
serde = "1.0.122"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.56"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.7.6" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.6" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.7.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.6" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.6" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.7.6" }
|
||||
solana-version = { path = "../version", version = "=1.7.6" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.7.6" }
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.7.9" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.9" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.7.9" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.9" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.7.9" }
|
||||
solana-version = { path = "../version", version = "=1.7.9" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.7.9" }
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tungstenite = "0.10.1"
|
||||
@@ -40,7 +40,7 @@ url = "2.1.1"
|
||||
[dev-dependencies]
|
||||
assert_matches = "1.3.0"
|
||||
jsonrpc-http-server = "17.0.0"
|
||||
solana-logger = { path = "../logger", version = "=1.7.6" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.9" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -2,7 +2,10 @@ use {
|
||||
crate::{
|
||||
client_error::Result,
|
||||
rpc_request::RpcRequest,
|
||||
rpc_response::{Response, RpcResponseContext, RpcVersionInfo},
|
||||
rpc_response::{
|
||||
Response, RpcBlockProduction, RpcBlockProductionRange, RpcResponseContext,
|
||||
RpcVersionInfo,
|
||||
},
|
||||
rpc_sender::RpcSender,
|
||||
},
|
||||
serde_json::{json, Number, Value},
|
||||
@@ -49,23 +52,26 @@ impl RpcSender for MockSender {
|
||||
if self.url == "fails" {
|
||||
return Ok(Value::Null);
|
||||
}
|
||||
let val = match request {
|
||||
RpcRequest::GetAccountInfo => serde_json::to_value(Response {
|
||||
|
||||
let method = &request.build_request_json(42, params.clone())["method"];
|
||||
|
||||
let val = match method.as_str().unwrap() {
|
||||
"getAccountInfo" => serde_json::to_value(Response {
|
||||
context: RpcResponseContext { slot: 1 },
|
||||
value: Value::Null,
|
||||
})?,
|
||||
RpcRequest::GetBalance => serde_json::to_value(Response {
|
||||
"getBalance" => serde_json::to_value(Response {
|
||||
context: RpcResponseContext { slot: 1 },
|
||||
value: Value::Number(Number::from(50)),
|
||||
})?,
|
||||
RpcRequest::GetRecentBlockhash => serde_json::to_value(Response {
|
||||
"getRecentBlockhash" => serde_json::to_value(Response {
|
||||
context: RpcResponseContext { slot: 1 },
|
||||
value: (
|
||||
Value::String(PUBKEY.to_string()),
|
||||
serde_json::to_value(FeeCalculator::default()).unwrap(),
|
||||
),
|
||||
})?,
|
||||
RpcRequest::GetEpochInfo => serde_json::to_value(EpochInfo {
|
||||
"getEpochInfo" => serde_json::to_value(EpochInfo {
|
||||
epoch: 1,
|
||||
slot_index: 2,
|
||||
slots_in_epoch: 32,
|
||||
@@ -73,7 +79,7 @@ impl RpcSender for MockSender {
|
||||
block_height: 34,
|
||||
transaction_count: Some(123),
|
||||
})?,
|
||||
RpcRequest::GetFeeCalculatorForBlockhash => {
|
||||
"getFeeCalculatorForBlockhash" => {
|
||||
let value = if self.url == "blockhash_expired" {
|
||||
Value::Null
|
||||
} else {
|
||||
@@ -84,11 +90,11 @@ impl RpcSender for MockSender {
|
||||
value,
|
||||
})?
|
||||
}
|
||||
RpcRequest::GetFeeRateGovernor => serde_json::to_value(Response {
|
||||
"getFeeRateGovernor" => serde_json::to_value(Response {
|
||||
context: RpcResponseContext { slot: 1 },
|
||||
value: serde_json::to_value(FeeRateGovernor::default()).unwrap(),
|
||||
})?,
|
||||
RpcRequest::GetSignatureStatuses => {
|
||||
"getSignatureStatuses" => {
|
||||
let status: transaction::Result<()> = if self.url == "account_in_use" {
|
||||
Err(TransactionError::AccountInUse)
|
||||
} else if self.url == "instruction_error" {
|
||||
@@ -122,11 +128,11 @@ impl RpcSender for MockSender {
|
||||
value: statuses,
|
||||
})?
|
||||
}
|
||||
RpcRequest::GetTransactionCount => Value::Number(Number::from(1234)),
|
||||
RpcRequest::GetSlot => Value::Number(Number::from(0)),
|
||||
RpcRequest::GetMaxShredInsertSlot => Value::Number(Number::from(0)),
|
||||
RpcRequest::RequestAirdrop => Value::String(Signature::new(&[8; 64]).to_string()),
|
||||
RpcRequest::SendTransaction => {
|
||||
"getTransactionCount" => json![1234],
|
||||
"getSlot" => json![0],
|
||||
"getMaxShredInsertSlot" => json![0],
|
||||
"requestAirdrop" => Value::String(Signature::new(&[8; 64]).to_string()),
|
||||
"sendTransaction" => {
|
||||
let signature = if self.url == "malicious" {
|
||||
Signature::new(&[8; 64]).to_string()
|
||||
} else {
|
||||
@@ -137,14 +143,27 @@ impl RpcSender for MockSender {
|
||||
};
|
||||
Value::String(signature)
|
||||
}
|
||||
RpcRequest::GetMinimumBalanceForRentExemption => Value::Number(Number::from(20)),
|
||||
RpcRequest::GetVersion => {
|
||||
"getMinimumBalanceForRentExemption" => json![20],
|
||||
"getVersion" => {
|
||||
let version = Version::default();
|
||||
json!(RpcVersionInfo {
|
||||
solana_core: version.to_string(),
|
||||
feature_set: Some(version.feature_set),
|
||||
})
|
||||
}
|
||||
"getBlockProduction" => {
|
||||
let map = vec![(PUBKEY.to_string(), (1, 1))].into_iter().collect();
|
||||
json!(Response {
|
||||
context: RpcResponseContext { slot: 1 },
|
||||
value: RpcBlockProduction {
|
||||
by_identity: map,
|
||||
range: RpcBlockProductionRange {
|
||||
first_slot: 0,
|
||||
last_slot: 0,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
_ => Value::Null,
|
||||
};
|
||||
Ok(val)
|
||||
|
@@ -482,7 +482,7 @@ impl RpcClient {
|
||||
&self,
|
||||
config: RpcBlockProductionConfig,
|
||||
) -> RpcResult<RpcBlockProduction> {
|
||||
self.send(RpcRequest::GetBlockProduction, json!(config))
|
||||
self.send(RpcRequest::GetBlockProduction, json!([config]))
|
||||
}
|
||||
|
||||
pub fn get_stake_activation(
|
||||
@@ -2162,6 +2162,7 @@ mod tests {
|
||||
let signature = rpc_client.send_transaction(&tx);
|
||||
assert!(signature.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_recent_blockhash() {
|
||||
let rpc_client = RpcClient::new_mock("succeeds".to_string());
|
||||
@@ -2176,6 +2177,20 @@ mod tests {
|
||||
assert!(rpc_client.get_recent_blockhash().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_custom_request() {
|
||||
let rpc_client = RpcClient::new_mock("succeeds".to_string());
|
||||
|
||||
let slot = rpc_client.get_slot().unwrap();
|
||||
assert_eq!(slot, 0);
|
||||
|
||||
let custom_slot = rpc_client
|
||||
.send::<Slot>(RpcRequest::Custom { method: "getSlot" }, Value::Null)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(slot, custom_slot);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_signature_status() {
|
||||
let signature = Signature::default();
|
||||
@@ -2230,4 +2245,23 @@ mod tests {
|
||||
let rpc_client = RpcClient::new_mock("succeeds".to_string());
|
||||
thread::spawn(move || rpc_client);
|
||||
}
|
||||
|
||||
// Regression test that the get_block_production_with_config
|
||||
// method internally creates the json params array correctly.
|
||||
#[test]
|
||||
fn get_block_production_with_config_no_error() -> ClientResult<()> {
|
||||
let rpc_client = RpcClient::new_mock("succeeds".to_string());
|
||||
|
||||
let config = RpcBlockProductionConfig {
|
||||
identity: None,
|
||||
range: None,
|
||||
commitment: None,
|
||||
};
|
||||
|
||||
let prod = rpc_client.get_block_production_with_config(config)?.value;
|
||||
|
||||
assert!(!prod.by_identity.is_empty());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@@ -86,6 +86,9 @@ pub enum RpcRequest {
|
||||
SendTransaction,
|
||||
SimulateTransaction,
|
||||
SignVote,
|
||||
Custom {
|
||||
method: &'static str,
|
||||
},
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
@@ -154,6 +157,7 @@ impl fmt::Display for RpcRequest {
|
||||
RpcRequest::SendTransaction => "sendTransaction",
|
||||
RpcRequest::SimulateTransaction => "simulateTransaction",
|
||||
RpcRequest::SignVote => "signVote",
|
||||
RpcRequest::Custom { method } => method,
|
||||
};
|
||||
|
||||
write!(f, "{}", method)
|
||||
|
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "solana-core"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.7.6"
|
||||
version = "1.7.9"
|
||||
homepage = "https://solana.com/"
|
||||
documentation = "https://docs.rs/solana-core"
|
||||
readme = "../README.md"
|
||||
@@ -43,33 +43,33 @@ retain_mut = "0.1.2"
|
||||
serde = "1.0.122"
|
||||
serde_bytes = "0.11"
|
||||
serde_derive = "1.0.103"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.7.6" }
|
||||
solana-banks-server = { path = "../banks-server", version = "=1.7.6" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.6" }
|
||||
solana-client = { path = "../client", version = "=1.7.6" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.7.6" }
|
||||
solana-ledger = { path = "../ledger", version = "=1.7.6" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.6" }
|
||||
solana-merkle-tree = { path = "../merkle-tree", version = "=1.7.6" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.7.6" }
|
||||
solana-measure = { path = "../measure", version = "=1.7.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.6" }
|
||||
solana-perf = { path = "../perf", version = "=1.7.6" }
|
||||
solana-poh = { path = "../poh", version = "=1.7.6" }
|
||||
solana-program-test = { path = "../program-test", version = "=1.7.6" }
|
||||
solana-rpc = { path = "../rpc", version = "=1.7.6" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.6" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.6" }
|
||||
solana-frozen-abi = { path = "../frozen-abi", version = "=1.7.6" }
|
||||
solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.7.6" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.7.6" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.7.6" }
|
||||
solana-version = { path = "../version", version = "=1.7.6" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.7.6" }
|
||||
spl-token-v2-0 = { package = "spl-token", version = "=3.1.1", features = ["no-entrypoint"] }
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.7.9" }
|
||||
solana-banks-server = { path = "../banks-server", version = "=1.7.9" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.9" }
|
||||
solana-client = { path = "../client", version = "=1.7.9" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.7.9" }
|
||||
solana-ledger = { path = "../ledger", version = "=1.7.9" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.9" }
|
||||
solana-merkle-tree = { path = "../merkle-tree", version = "=1.7.9" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.7.9" }
|
||||
solana-measure = { path = "../measure", version = "=1.7.9" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.9" }
|
||||
solana-perf = { path = "../perf", version = "=1.7.9" }
|
||||
solana-poh = { path = "../poh", version = "=1.7.9" }
|
||||
solana-program-test = { path = "../program-test", version = "=1.7.9" }
|
||||
solana-rpc = { path = "../rpc", version = "=1.7.9" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.9" }
|
||||
solana-frozen-abi = { path = "../frozen-abi", version = "=1.7.9" }
|
||||
solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.7.9" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.7.9" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.7.9" }
|
||||
solana-version = { path = "../version", version = "=1.7.9" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.7.9" }
|
||||
spl-token-v2-0 = { package = "spl-token", version = "=3.2.0", features = ["no-entrypoint"] }
|
||||
tempfile = "3.1.0"
|
||||
thiserror = "1.0"
|
||||
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.7.6" }
|
||||
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.7.9" }
|
||||
trees = "0.2.1"
|
||||
|
||||
[dev-dependencies]
|
||||
@@ -80,8 +80,8 @@ num_cpus = "1.13.0"
|
||||
reqwest = { version = "0.11.2", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
serde_json = "1.0.56"
|
||||
serial_test = "0.4.0"
|
||||
solana-stake-program = { path = "../programs/stake", version = "=1.7.6" }
|
||||
solana-version = { path = "../version", version = "=1.7.6" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "=1.7.9" }
|
||||
solana-version = { path = "../version", version = "=1.7.9" }
|
||||
symlink = "0.1.0"
|
||||
systemstat = "0.1.5"
|
||||
tokio_02 = { version = "0.2", package = "tokio", features = ["full"] }
|
||||
|
@@ -29,6 +29,7 @@ use solana_sdk::system_instruction;
|
||||
use solana_sdk::system_transaction;
|
||||
use solana_sdk::timing::{duration_as_us, timestamp};
|
||||
use solana_sdk::transaction::Transaction;
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::mpsc::Receiver;
|
||||
@@ -201,7 +202,11 @@ fn bench_banking(bencher: &mut Bencher, tx_type: TransactionType) {
|
||||
);
|
||||
let (exit, poh_recorder, poh_service, signal_receiver) =
|
||||
create_test_recorder(&bank, &blockstore, None);
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(Node::new_localhost().info);
|
||||
let cluster_info = ClusterInfo::new(
|
||||
Node::new_localhost().info,
|
||||
Arc::new(Keypair::new()),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
let cluster_info = Arc::new(cluster_info);
|
||||
let (s, _r) = unbounded();
|
||||
let _banking_stage = BankingStage::new(
|
||||
|
@@ -12,8 +12,8 @@ use solana_gossip::{
|
||||
contact_info::ContactInfo,
|
||||
};
|
||||
use solana_ledger::shred::Shred;
|
||||
use solana_sdk::pubkey;
|
||||
use solana_sdk::timing::timestamp;
|
||||
use solana_sdk::{pubkey, signature::Keypair, timing::timestamp};
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
net::UdpSocket,
|
||||
@@ -26,7 +26,11 @@ fn broadcast_shreds_bench(bencher: &mut Bencher) {
|
||||
solana_logger::setup();
|
||||
let leader_pubkey = pubkey::new_rand();
|
||||
let leader_info = Node::new_localhost_with_pubkey(&leader_pubkey);
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(leader_info.info);
|
||||
let cluster_info = ClusterInfo::new(
|
||||
leader_info.info,
|
||||
Arc::new(Keypair::new()),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
|
||||
|
||||
const NUM_SHREDS: usize = 32;
|
||||
@@ -51,6 +55,7 @@ fn broadcast_shreds_bench(bencher: &mut Bencher) {
|
||||
&cluster_nodes,
|
||||
&last_datapoint,
|
||||
&mut TransmitShredsStats::default(),
|
||||
&SocketAddrSpace::Unspecified,
|
||||
)
|
||||
.unwrap();
|
||||
});
|
||||
|
@@ -21,6 +21,7 @@ use solana_sdk::pubkey;
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use solana_sdk::system_transaction;
|
||||
use solana_sdk::timing::timestamp;
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
use std::net::UdpSocket;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::mpsc::channel;
|
||||
@@ -35,7 +36,11 @@ use test::Bencher;
|
||||
#[allow(clippy::same_item_push)]
|
||||
fn bench_retransmitter(bencher: &mut Bencher) {
|
||||
solana_logger::setup();
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(Node::new_localhost().info);
|
||||
let cluster_info = ClusterInfo::new(
|
||||
Node::new_localhost().info,
|
||||
Arc::new(Keypair::new()),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
const NUM_PEERS: usize = 4;
|
||||
let mut peer_sockets = Vec::new();
|
||||
for _ in 0..NUM_PEERS {
|
||||
|
@@ -223,13 +223,22 @@ mod tests {
|
||||
hash::hash,
|
||||
signature::{Keypair, Signer},
|
||||
};
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
|
||||
fn new_test_cluster_info(contact_info: ContactInfo) -> ClusterInfo {
|
||||
ClusterInfo::new(
|
||||
contact_info,
|
||||
Arc::new(Keypair::new()),
|
||||
SocketAddrSpace::Unspecified,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_should_halt() {
|
||||
let keypair = Keypair::new();
|
||||
|
||||
let contact_info = ContactInfo::new_localhost(&keypair.pubkey(), 0);
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(contact_info);
|
||||
let cluster_info = new_test_cluster_info(contact_info);
|
||||
let cluster_info = Arc::new(cluster_info);
|
||||
|
||||
let mut trusted_validators = HashSet::new();
|
||||
@@ -265,7 +274,7 @@ mod tests {
|
||||
let keypair = Keypair::new();
|
||||
|
||||
let contact_info = ContactInfo::new_localhost(&keypair.pubkey(), 0);
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(contact_info);
|
||||
let cluster_info = new_test_cluster_info(contact_info);
|
||||
let cluster_info = Arc::new(cluster_info);
|
||||
|
||||
let trusted_validators = HashSet::new();
|
||||
|
@@ -1388,7 +1388,7 @@ mod tests {
|
||||
use super::*;
|
||||
use crossbeam_channel::unbounded;
|
||||
use itertools::Itertools;
|
||||
use solana_gossip::cluster_info::Node;
|
||||
use solana_gossip::{cluster_info::Node, contact_info::ContactInfo};
|
||||
use solana_ledger::{
|
||||
blockstore::{entries_to_test_shreds, Blockstore},
|
||||
entry::{next_entry, Entry, EntrySlice},
|
||||
@@ -1411,6 +1411,7 @@ mod tests {
|
||||
system_transaction,
|
||||
transaction::TransactionError,
|
||||
};
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
use solana_transaction_status::TransactionWithStatusMeta;
|
||||
use std::{
|
||||
net::SocketAddr,
|
||||
@@ -1422,6 +1423,14 @@ mod tests {
|
||||
thread::sleep,
|
||||
};
|
||||
|
||||
fn new_test_cluster_info(contact_info: ContactInfo) -> ClusterInfo {
|
||||
ClusterInfo::new(
|
||||
contact_info,
|
||||
Arc::new(Keypair::new()),
|
||||
SocketAddrSpace::Unspecified,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_banking_stage_shutdown1() {
|
||||
let genesis_config = create_genesis_config(2).genesis_config;
|
||||
@@ -1437,7 +1446,7 @@ mod tests {
|
||||
);
|
||||
let (exit, poh_recorder, poh_service, _entry_receiever) =
|
||||
create_test_recorder(&bank, &blockstore, None);
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(Node::new_localhost().info);
|
||||
let cluster_info = new_test_cluster_info(Node::new_localhost().info);
|
||||
let cluster_info = Arc::new(cluster_info);
|
||||
let banking_stage = BankingStage::new(
|
||||
&cluster_info,
|
||||
@@ -1480,7 +1489,7 @@ mod tests {
|
||||
};
|
||||
let (exit, poh_recorder, poh_service, entry_receiver) =
|
||||
create_test_recorder(&bank, &blockstore, Some(poh_config));
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(Node::new_localhost().info);
|
||||
let cluster_info = new_test_cluster_info(Node::new_localhost().info);
|
||||
let cluster_info = Arc::new(cluster_info);
|
||||
let (gossip_vote_sender, _gossip_vote_receiver) = unbounded();
|
||||
|
||||
@@ -1549,7 +1558,7 @@ mod tests {
|
||||
};
|
||||
let (exit, poh_recorder, poh_service, entry_receiver) =
|
||||
create_test_recorder(&bank, &blockstore, Some(poh_config));
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(Node::new_localhost().info);
|
||||
let cluster_info = new_test_cluster_info(Node::new_localhost().info);
|
||||
let cluster_info = Arc::new(cluster_info);
|
||||
let (gossip_vote_sender, _gossip_vote_receiver) = unbounded();
|
||||
|
||||
@@ -1697,8 +1706,7 @@ mod tests {
|
||||
};
|
||||
let (exit, poh_recorder, poh_service, entry_receiver) =
|
||||
create_test_recorder(&bank, &blockstore, Some(poh_config));
|
||||
let cluster_info =
|
||||
ClusterInfo::new_with_invalid_keypair(Node::new_localhost().info);
|
||||
let cluster_info = new_test_cluster_info(Node::new_localhost().info);
|
||||
let cluster_info = Arc::new(cluster_info);
|
||||
let _banking_stage = BankingStage::new_num_threads(
|
||||
&cluster_info,
|
||||
|
@@ -22,7 +22,7 @@ use solana_poh::poh_recorder::WorkingBankEntry;
|
||||
use solana_runtime::bank::Bank;
|
||||
use solana_sdk::timing::timestamp;
|
||||
use solana_sdk::{clock::Slot, pubkey::Pubkey};
|
||||
use solana_streamer::{sendmmsg::send_mmsg, socket::is_global};
|
||||
use solana_streamer::{sendmmsg::send_mmsg, socket::SocketAddrSpace};
|
||||
use std::sync::atomic::AtomicU64;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
@@ -386,6 +386,7 @@ pub fn broadcast_shreds(
|
||||
cluster_nodes: &ClusterNodes<BroadcastStage>,
|
||||
last_datapoint_submit: &Arc<AtomicU64>,
|
||||
transmit_stats: &mut TransmitShredsStats,
|
||||
socket_addr_space: &SocketAddrSpace,
|
||||
) -> Result<()> {
|
||||
let broadcast_len = cluster_nodes.num_peers();
|
||||
if broadcast_len == 0 {
|
||||
@@ -397,7 +398,7 @@ pub fn broadcast_shreds(
|
||||
.iter()
|
||||
.filter_map(|shred| {
|
||||
let node = cluster_nodes.get_broadcast_peer(shred.seed())?;
|
||||
if is_global(&node.tvu) {
|
||||
if socket_addr_space.check(&node.tvu) {
|
||||
Some((&shred.payload, &node.tvu))
|
||||
} else {
|
||||
None
|
||||
@@ -585,7 +586,11 @@ pub mod test {
|
||||
let broadcast_buddy = Node::new_localhost_with_pubkey(&buddy_keypair.pubkey());
|
||||
|
||||
// Fill the cluster_info with the buddy's info
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(leader_info.info.clone());
|
||||
let cluster_info = ClusterInfo::new(
|
||||
leader_info.info.clone(),
|
||||
Arc::new(Keypair::new()),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
cluster_info.insert_info(broadcast_buddy.info);
|
||||
let cluster_info = Arc::new(cluster_info);
|
||||
|
||||
|
@@ -301,11 +301,14 @@ impl BroadcastRun for BroadcastDuplicatesRun {
|
||||
|
||||
let ((stakes, shreds), _) = receiver.lock().unwrap().recv()?;
|
||||
let stakes = stakes.unwrap();
|
||||
let socket_addr_space = cluster_info.socket_addr_space();
|
||||
for peer in cluster_info.tvu_peers() {
|
||||
// Forward shreds to circumvent gossip
|
||||
if stakes.get(&peer.id).is_some() {
|
||||
shreds.iter().for_each(|shred| {
|
||||
sock.send_to(&shred.payload, &peer.tvu_forwards).unwrap();
|
||||
if socket_addr_space.check(&peer.tvu_forwards) {
|
||||
sock.send_to(&shred.payload, &peer.tvu_forwards).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -313,7 +316,9 @@ impl BroadcastRun for BroadcastDuplicatesRun {
|
||||
if let Some(shreds) = delayed_shreds.as_ref() {
|
||||
if Some(peer.id) == delayed_recipient {
|
||||
shreds.iter().for_each(|shred| {
|
||||
sock.send_to(&shred.payload, &peer.tvu).unwrap();
|
||||
if socket_addr_space.check(&peer.tvu) {
|
||||
sock.send_to(&shred.payload, &peer.tvu).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -139,14 +139,16 @@ impl BroadcastRun for BroadcastFakeShredsRun {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use solana_gossip::contact_info::ContactInfo;
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
|
||||
#[test]
|
||||
fn test_tvu_peers_ordering() {
|
||||
let cluster = ClusterInfo::new_with_invalid_keypair(ContactInfo::new_localhost(
|
||||
&solana_sdk::pubkey::new_rand(),
|
||||
0,
|
||||
));
|
||||
let cluster = ClusterInfo::new(
|
||||
ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0),
|
||||
Arc::new(Keypair::new()),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
cluster.insert_info(ContactInfo::new_with_socketaddr(&SocketAddr::new(
|
||||
IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)),
|
||||
8080,
|
||||
|
@@ -145,6 +145,7 @@ impl BroadcastRun for FailEntryVerificationBroadcastRun {
|
||||
&cluster_nodes,
|
||||
&Arc::new(AtomicU64::new(0)),
|
||||
&mut TransmitShredsStats::default(),
|
||||
cluster_info.socket_addr_space(),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
|
@@ -345,7 +345,7 @@ impl StandardBroadcastRun {
|
||||
if now - last > BROADCAST_PEER_UPDATE_INTERVAL_MS
|
||||
&& self
|
||||
.last_peer_update
|
||||
.compare_and_swap(now, last, Ordering::Relaxed)
|
||||
.compare_and_swap(last, now, Ordering::Relaxed)
|
||||
== last
|
||||
{
|
||||
*self.cluster_nodes.write().unwrap() = ClusterNodes::<BroadcastStage>::new(
|
||||
@@ -365,6 +365,7 @@ impl StandardBroadcastRun {
|
||||
&cluster_nodes,
|
||||
&self.last_datapoint_submit,
|
||||
&mut transmit_stats,
|
||||
cluster_info.socket_addr_space(),
|
||||
)?;
|
||||
drop(cluster_nodes);
|
||||
transmit_time.stop();
|
||||
@@ -498,6 +499,7 @@ mod test {
|
||||
genesis_config::GenesisConfig,
|
||||
signature::{Keypair, Signer},
|
||||
};
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -519,7 +521,11 @@ mod test {
|
||||
let leader_keypair = Arc::new(Keypair::new());
|
||||
let leader_pubkey = leader_keypair.pubkey();
|
||||
let leader_info = Node::new_localhost_with_pubkey(&leader_pubkey);
|
||||
let cluster_info = Arc::new(ClusterInfo::new_with_invalid_keypair(leader_info.info));
|
||||
let cluster_info = Arc::new(ClusterInfo::new(
|
||||
leader_info.info,
|
||||
Arc::new(Keypair::new()),
|
||||
SocketAddrSpace::Unspecified,
|
||||
));
|
||||
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
|
||||
let mut genesis_config = create_genesis_config(10_000).genesis_config;
|
||||
genesis_config.ticks_per_slot = max_ticks_per_n_shreds(num_shreds_per_slot, None) + 1;
|
||||
|
@@ -246,8 +246,9 @@ mod tests {
|
||||
sorted_stakes_with_index,
|
||||
},
|
||||
},
|
||||
solana_sdk::timing::timestamp,
|
||||
std::iter::repeat_with,
|
||||
solana_sdk::{signature::Keypair, timing::timestamp},
|
||||
solana_streamer::socket::SocketAddrSpace,
|
||||
std::{iter::repeat_with, sync::Arc},
|
||||
};
|
||||
|
||||
// Legacy methods copied for testing backward compatibility.
|
||||
@@ -293,7 +294,11 @@ mod tests {
|
||||
.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_with_invalid_keypair(this_node);
|
||||
let cluster_info = ClusterInfo::new(
|
||||
this_node,
|
||||
Arc::new(Keypair::new()),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
{
|
||||
let now = timestamp();
|
||||
let mut gossip = cluster_info.gossip.write().unwrap();
|
||||
|
@@ -180,14 +180,19 @@ mod test {
|
||||
use {
|
||||
super::*,
|
||||
solana_gossip::{cluster_info::Node, crds_value::CrdsValueLabel},
|
||||
solana_sdk::pubkey::Pubkey,
|
||||
solana_sdk::{pubkey::Pubkey, signature::Keypair},
|
||||
solana_streamer::socket::SocketAddrSpace,
|
||||
};
|
||||
|
||||
#[test]
|
||||
pub fn test_update_lowest_slot() {
|
||||
let pubkey = Pubkey::new_unique();
|
||||
let node_info = Node::new_localhost_with_pubkey(&pubkey);
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(node_info.info);
|
||||
let cluster_info = ClusterInfo::new(
|
||||
node_info.info,
|
||||
Arc::new(Keypair::new()),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
ClusterSlotsService::update_lowest_slot(5, &cluster_info);
|
||||
cluster_info.flush_push_queue();
|
||||
let lowest = {
|
||||
|
@@ -676,17 +676,27 @@ impl RepairService {
|
||||
mod test {
|
||||
use super::*;
|
||||
use crossbeam_channel::unbounded;
|
||||
use solana_gossip::cluster_info::Node;
|
||||
use solana_gossip::{cluster_info::Node, contact_info::ContactInfo};
|
||||
use solana_ledger::blockstore::{
|
||||
make_chaining_slot_entries, make_many_slot_entries, make_slot_entries,
|
||||
};
|
||||
use solana_ledger::shred::max_ticks_per_n_shreds;
|
||||
use solana_ledger::{blockstore::Blockstore, get_tmp_ledger_path};
|
||||
use solana_runtime::genesis_utils::{self, GenesisConfigInfo, ValidatorVoteKeypairs};
|
||||
use solana_sdk::signature::Keypair;
|
||||
use solana_sdk::signature::Signer;
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
use solana_vote_program::vote_transaction;
|
||||
use std::collections::HashSet;
|
||||
|
||||
fn new_test_cluster_info(contact_info: ContactInfo) -> ClusterInfo {
|
||||
ClusterInfo::new(
|
||||
contact_info,
|
||||
Arc::new(Keypair::new()),
|
||||
SocketAddrSpace::Unspecified,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_repair_orphan() {
|
||||
let blockstore_path = get_tmp_ledger_path!();
|
||||
@@ -978,7 +988,8 @@ mod test {
|
||||
let blockstore_path = get_tmp_ledger_path!();
|
||||
let blockstore = Blockstore::open(&blockstore_path).unwrap();
|
||||
let cluster_slots = ClusterSlots::default();
|
||||
let serve_repair = ServeRepair::new_with_invalid_keypair(Node::new_localhost().info);
|
||||
let serve_repair =
|
||||
ServeRepair::new(Arc::new(new_test_cluster_info(Node::new_localhost().info)));
|
||||
let mut duplicate_slot_repair_statuses = HashMap::new();
|
||||
let dead_slot = 9;
|
||||
let receive_socket = &UdpSocket::bind("0.0.0.0:0").unwrap();
|
||||
@@ -1060,9 +1071,7 @@ mod test {
|
||||
Pubkey::default(),
|
||||
UdpSocket::bind("0.0.0.0:0").unwrap().local_addr().unwrap(),
|
||||
));
|
||||
let cluster_info = Arc::new(ClusterInfo::new_with_invalid_keypair(
|
||||
Node::new_localhost().info,
|
||||
));
|
||||
let cluster_info = Arc::new(new_test_cluster_info(Node::new_localhost().info));
|
||||
let serve_repair = ServeRepair::new(cluster_info.clone());
|
||||
let valid_repair_peer = Node::new_localhost().info;
|
||||
|
||||
@@ -1122,7 +1131,8 @@ mod test {
|
||||
let blockstore_path = get_tmp_ledger_path!();
|
||||
let blockstore = Blockstore::open(&blockstore_path).unwrap();
|
||||
let cluster_slots = ClusterSlots::default();
|
||||
let serve_repair = ServeRepair::new_with_invalid_keypair(Node::new_localhost().info);
|
||||
let serve_repair =
|
||||
ServeRepair::new(Arc::new(new_test_cluster_info(Node::new_localhost().info)));
|
||||
let mut duplicate_slot_repair_statuses = HashMap::new();
|
||||
let duplicate_slot = 9;
|
||||
|
||||
|
@@ -2544,6 +2544,7 @@ mod tests {
|
||||
system_transaction,
|
||||
transaction::TransactionError,
|
||||
};
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
use solana_transaction_status::TransactionWithStatusMeta;
|
||||
use solana_vote_program::{
|
||||
vote_state::{VoteState, VoteStateVersions},
|
||||
@@ -2617,6 +2618,7 @@ mod tests {
|
||||
let cluster_info = ClusterInfo::new(
|
||||
Node::new_localhost_with_pubkey(&my_pubkey).info,
|
||||
Arc::new(Keypair::from_bytes(&my_keypairs.node_keypair.to_bytes()).unwrap()),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
assert_eq!(my_pubkey, cluster_info.id());
|
||||
|
||||
|
@@ -332,10 +332,11 @@ fn retransmit(
|
||||
}
|
||||
}
|
||||
let cluster_nodes = cluster_nodes.read().unwrap();
|
||||
let mut peers_len = 0;
|
||||
let peers_len = cluster_nodes.num_peers();
|
||||
epoch_cache_update.stop();
|
||||
|
||||
let my_id = cluster_info.id();
|
||||
let socket_addr_space = cluster_info.socket_addr_space();
|
||||
let mut discard_total = 0;
|
||||
let mut repair_total = 0;
|
||||
let mut retransmit_total = 0;
|
||||
@@ -384,7 +385,6 @@ fn retransmit(
|
||||
// TODO: Consider forwarding the packet to the root node here.
|
||||
retransmit_tree_mismatch += 1;
|
||||
}
|
||||
peers_len = peers_len.max(cluster_nodes.num_peers());
|
||||
compute_turbine_peers.stop();
|
||||
compute_turbine_peers_total += compute_turbine_peers.as_us();
|
||||
|
||||
@@ -405,6 +405,7 @@ fn retransmit(
|
||||
packet,
|
||||
sock,
|
||||
/*forward socket=*/ true,
|
||||
socket_addr_space,
|
||||
);
|
||||
}
|
||||
ClusterInfo::retransmit_to(
|
||||
@@ -412,6 +413,7 @@ fn retransmit(
|
||||
packet,
|
||||
sock,
|
||||
!anchor_node, // send to forward socket!
|
||||
socket_addr_space,
|
||||
);
|
||||
retransmit_time.stop();
|
||||
retransmit_total += retransmit_time.as_us();
|
||||
@@ -643,6 +645,8 @@ mod tests {
|
||||
use solana_ledger::shred::Shred;
|
||||
use solana_net_utils::find_available_port_in_range;
|
||||
use solana_perf::packet::{Packet, Packets};
|
||||
use solana_sdk::signature::Keypair;
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
|
||||
#[test]
|
||||
@@ -679,7 +683,11 @@ mod tests {
|
||||
.find(|pk| me.id < *pk)
|
||||
.unwrap();
|
||||
let other = ContactInfo::new_localhost(&other, 0);
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(other);
|
||||
let cluster_info = ClusterInfo::new(
|
||||
other,
|
||||
Arc::new(Keypair::new()),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
cluster_info.insert_info(me);
|
||||
|
||||
let retransmit_socket = Arc::new(vec![UdpSocket::bind("0.0.0.0:0").unwrap()]);
|
||||
|
@@ -149,13 +149,6 @@ impl RepairPeers {
|
||||
}
|
||||
|
||||
impl ServeRepair {
|
||||
/// Without a valid keypair gossip will not function. Only useful for tests.
|
||||
pub fn new_with_invalid_keypair(contact_info: ContactInfo) -> Self {
|
||||
Self::new(Arc::new(ClusterInfo::new_with_invalid_keypair(
|
||||
contact_info,
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn new(cluster_info: Arc<ClusterInfo>) -> Self {
|
||||
let (keypair, my_info) = { (cluster_info.keypair.clone(), cluster_info.my_contact_info()) };
|
||||
Self {
|
||||
@@ -652,7 +645,8 @@ mod tests {
|
||||
shred::{max_ticks_per_n_shreds, Shred},
|
||||
};
|
||||
use solana_perf::packet::Packet;
|
||||
use solana_sdk::{hash::Hash, pubkey::Pubkey, timing::timestamp};
|
||||
use solana_sdk::{hash::Hash, pubkey::Pubkey, signature::Keypair, timing::timestamp};
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
|
||||
#[test]
|
||||
fn test_run_highest_window_request() {
|
||||
@@ -797,11 +791,19 @@ mod tests {
|
||||
Blockstore::destroy(&ledger_path).expect("Expected successful database destruction");
|
||||
}
|
||||
|
||||
fn new_test_cluster_info(contact_info: ContactInfo) -> ClusterInfo {
|
||||
ClusterInfo::new(
|
||||
contact_info,
|
||||
Arc::new(Keypair::new()),
|
||||
SocketAddrSpace::Unspecified,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn window_index_request() {
|
||||
let cluster_slots = ClusterSlots::default();
|
||||
let me = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), timestamp());
|
||||
let cluster_info = Arc::new(ClusterInfo::new_with_invalid_keypair(me));
|
||||
let cluster_info = Arc::new(new_test_cluster_info(me));
|
||||
let serve_repair = ServeRepair::new(cluster_info.clone());
|
||||
let mut outstanding_requests = OutstandingRepairs::default();
|
||||
let rv = serve_repair.repair_request(
|
||||
@@ -1028,7 +1030,7 @@ mod tests {
|
||||
fn test_repair_with_repair_validators() {
|
||||
let cluster_slots = ClusterSlots::default();
|
||||
let me = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), timestamp());
|
||||
let cluster_info = Arc::new(ClusterInfo::new_with_invalid_keypair(me.clone()));
|
||||
let cluster_info = Arc::new(new_test_cluster_info(me.clone()));
|
||||
|
||||
// Insert two peers on the network
|
||||
let contact_info2 =
|
||||
|
@@ -1,7 +1,7 @@
|
||||
use crate::serve_repair::ServeRepair;
|
||||
use solana_ledger::blockstore::Blockstore;
|
||||
use solana_perf::recycler::Recycler;
|
||||
use solana_streamer::streamer;
|
||||
use solana_streamer::{socket::SocketAddrSpace, streamer};
|
||||
use std::net::UdpSocket;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::mpsc::channel;
|
||||
@@ -17,6 +17,7 @@ impl ServeRepairService {
|
||||
serve_repair: &Arc<RwLock<ServeRepair>>,
|
||||
blockstore: Option<Arc<Blockstore>>,
|
||||
serve_repair_socket: UdpSocket,
|
||||
socket_addr_space: SocketAddrSpace,
|
||||
exit: &Arc<AtomicBool>,
|
||||
) -> Self {
|
||||
let (request_sender, request_receiver) = channel();
|
||||
@@ -36,8 +37,12 @@ impl ServeRepairService {
|
||||
false,
|
||||
);
|
||||
let (response_sender, response_receiver) = channel();
|
||||
let t_responder =
|
||||
streamer::responder("serve-repairs", serve_repair_socket, response_receiver);
|
||||
let t_responder = streamer::responder(
|
||||
"serve-repairs",
|
||||
serve_repair_socket,
|
||||
response_receiver,
|
||||
socket_addr_space,
|
||||
);
|
||||
let t_listen = ServeRepair::listen(
|
||||
serve_repair.clone(),
|
||||
blockstore,
|
||||
|
@@ -24,6 +24,7 @@ use {
|
||||
rent::Rent,
|
||||
signature::{read_keypair_file, write_keypair_file, Keypair, Signer},
|
||||
},
|
||||
solana_streamer::socket::SocketAddrSpace,
|
||||
std::{
|
||||
collections::HashMap,
|
||||
fs::remove_dir_all,
|
||||
@@ -263,8 +264,9 @@ impl TestValidatorGenesis {
|
||||
pub fn start_with_mint_address(
|
||||
&self,
|
||||
mint_address: Pubkey,
|
||||
socket_addr_space: SocketAddrSpace,
|
||||
) -> Result<TestValidator, Box<dyn std::error::Error>> {
|
||||
TestValidator::start(mint_address, self)
|
||||
TestValidator::start(mint_address, self, socket_addr_space)
|
||||
}
|
||||
|
||||
/// Start a test validator
|
||||
@@ -273,9 +275,9 @@ impl TestValidatorGenesis {
|
||||
/// created at genesis.
|
||||
///
|
||||
/// This function panics on initialization failure.
|
||||
pub fn start(&self) -> (TestValidator, Keypair) {
|
||||
pub fn start(&self, socket_addr_space: SocketAddrSpace) -> (TestValidator, Keypair) {
|
||||
let mint_keypair = Keypair::new();
|
||||
TestValidator::start(mint_keypair.pubkey(), self)
|
||||
TestValidator::start(mint_keypair.pubkey(), self, socket_addr_space)
|
||||
.map(|test_validator| (test_validator, mint_keypair))
|
||||
.expect("Test validator failed to start")
|
||||
}
|
||||
@@ -297,7 +299,11 @@ impl TestValidator {
|
||||
/// Faucet optional.
|
||||
///
|
||||
/// This function panics on initialization failure.
|
||||
pub fn with_no_fees(mint_address: Pubkey, faucet_addr: Option<SocketAddr>) -> Self {
|
||||
pub fn with_no_fees(
|
||||
mint_address: Pubkey,
|
||||
faucet_addr: Option<SocketAddr>,
|
||||
socket_addr_space: SocketAddrSpace,
|
||||
) -> Self {
|
||||
TestValidatorGenesis::default()
|
||||
.fee_rate_governor(FeeRateGovernor::new(0, 0))
|
||||
.rent(Rent {
|
||||
@@ -306,7 +312,7 @@ impl TestValidator {
|
||||
..Rent::default()
|
||||
})
|
||||
.faucet_addr(faucet_addr)
|
||||
.start_with_mint_address(mint_address)
|
||||
.start_with_mint_address(mint_address, socket_addr_space)
|
||||
.expect("validator start failed")
|
||||
}
|
||||
|
||||
@@ -318,6 +324,7 @@ impl TestValidator {
|
||||
mint_address: Pubkey,
|
||||
target_lamports_per_signature: u64,
|
||||
faucet_addr: Option<SocketAddr>,
|
||||
socket_addr_space: SocketAddrSpace,
|
||||
) -> Self {
|
||||
TestValidatorGenesis::default()
|
||||
.fee_rate_governor(FeeRateGovernor::new(target_lamports_per_signature, 0))
|
||||
@@ -327,7 +334,7 @@ impl TestValidator {
|
||||
..Rent::default()
|
||||
})
|
||||
.faucet_addr(faucet_addr)
|
||||
.start_with_mint_address(mint_address)
|
||||
.start_with_mint_address(mint_address, socket_addr_space)
|
||||
.expect("validator start failed")
|
||||
}
|
||||
|
||||
@@ -430,6 +437,7 @@ impl TestValidator {
|
||||
fn start(
|
||||
mint_address: Pubkey,
|
||||
config: &TestValidatorGenesis,
|
||||
socket_addr_space: SocketAddrSpace,
|
||||
) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let preserve_ledger = config.ledger_path.is_some();
|
||||
let ledger_path = TestValidator::initialize_ledger(mint_address, config)?;
|
||||
@@ -513,11 +521,12 @@ impl TestValidator {
|
||||
&validator_config,
|
||||
true, // should_check_duplicate_instance
|
||||
config.start_progress.clone(),
|
||||
socket_addr_space,
|
||||
));
|
||||
|
||||
// Needed to avoid panics in `solana-responder-gossip` in tests that create a number of
|
||||
// test validators concurrently...
|
||||
discover_cluster(&gossip, 1)
|
||||
discover_cluster(&gossip, 1, socket_addr_space)
|
||||
.map_err(|err| format!("TestValidator startup failed: {:?}", err))?;
|
||||
|
||||
// This is a hack to delay until the fees are non-zero for test consistency
|
||||
|
@@ -362,6 +362,8 @@ pub mod tests {
|
||||
use solana_poh::poh_recorder::create_test_recorder;
|
||||
use solana_rpc::optimistically_confirmed_bank_tracker::OptimisticallyConfirmedBank;
|
||||
use solana_runtime::bank::Bank;
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
#[ignore]
|
||||
@@ -379,7 +381,11 @@ pub mod tests {
|
||||
let bank_forks = BankForks::new(Bank::new(&genesis_config));
|
||||
|
||||
//start cluster_info1
|
||||
let cluster_info1 = ClusterInfo::new_with_invalid_keypair(target1.info.clone());
|
||||
let cluster_info1 = ClusterInfo::new(
|
||||
target1.info.clone(),
|
||||
Arc::new(Keypair::new()),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
cluster_info1.insert_info(leader.info);
|
||||
let cref1 = Arc::new(cluster_info1);
|
||||
|
||||
|
@@ -70,6 +70,7 @@ use solana_sdk::{
|
||||
signature::{Keypair, Signer},
|
||||
timing::timestamp,
|
||||
};
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
use solana_vote_program::vote_state::VoteState;
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
@@ -119,7 +120,6 @@ pub struct ValidatorConfig {
|
||||
pub max_genesis_archive_unpacked_size: u64,
|
||||
pub wal_recovery_mode: Option<BlockstoreRecoveryMode>,
|
||||
pub poh_verify: bool, // Perform PoH verification during blockstore processing at boo
|
||||
pub cuda: bool,
|
||||
pub require_tower: bool,
|
||||
pub tower_path: Option<PathBuf>,
|
||||
pub debug_keys: Option<Arc<HashSet<Pubkey>>>,
|
||||
@@ -176,7 +176,6 @@ impl Default for ValidatorConfig {
|
||||
max_genesis_archive_unpacked_size: MAX_GENESIS_ARCHIVE_UNPACKED_SIZE,
|
||||
wal_recovery_mode: None,
|
||||
poh_verify: true,
|
||||
cuda: false,
|
||||
require_tower: false,
|
||||
tower_path: None,
|
||||
debug_keys: None,
|
||||
@@ -273,6 +272,7 @@ pub(crate) fn abort() -> ! {
|
||||
}
|
||||
|
||||
impl Validator {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
mut node: Node,
|
||||
identity_keypair: &Arc<Keypair>,
|
||||
@@ -283,6 +283,7 @@ impl Validator {
|
||||
config: &ValidatorConfig,
|
||||
should_check_duplicate_instance: bool,
|
||||
start_progress: Arc<RwLock<ValidatorStartProgress>>,
|
||||
socket_addr_space: SocketAddrSpace,
|
||||
) -> Self {
|
||||
let id = identity_keypair.pubkey();
|
||||
assert_eq!(id, node.info.id);
|
||||
@@ -299,8 +300,6 @@ impl Validator {
|
||||
}
|
||||
}
|
||||
|
||||
report_target_features();
|
||||
|
||||
for cluster_entrypoint in &cluster_entrypoints {
|
||||
info!("entrypoint: {:?}", cluster_entrypoint);
|
||||
}
|
||||
@@ -433,7 +432,11 @@ impl Validator {
|
||||
}
|
||||
}
|
||||
|
||||
let mut cluster_info = ClusterInfo::new(node.info.clone(), identity_keypair.clone());
|
||||
let mut cluster_info = ClusterInfo::new(
|
||||
node.info.clone(),
|
||||
identity_keypair.clone(),
|
||||
socket_addr_space,
|
||||
);
|
||||
cluster_info.set_contact_debug_interval(config.contact_debug_interval);
|
||||
cluster_info.set_entrypoints(cluster_entrypoints);
|
||||
cluster_info.restore_contact_info(ledger_path, config.contact_save_interval);
|
||||
@@ -506,10 +509,16 @@ impl Validator {
|
||||
optimistically_confirmed_bank_tracker,
|
||||
bank_notification_sender,
|
||||
) = if let Some((rpc_addr, rpc_pubsub_addr)) = config.rpc_addrs {
|
||||
if ContactInfo::is_valid_address(&node.info.rpc) {
|
||||
assert!(ContactInfo::is_valid_address(&node.info.rpc_pubsub));
|
||||
if ContactInfo::is_valid_address(&node.info.rpc, &socket_addr_space) {
|
||||
assert!(ContactInfo::is_valid_address(
|
||||
&node.info.rpc_pubsub,
|
||||
&socket_addr_space
|
||||
));
|
||||
} else {
|
||||
assert!(!ContactInfo::is_valid_address(&node.info.rpc_pubsub));
|
||||
assert!(!ContactInfo::is_valid_address(
|
||||
&node.info.rpc_pubsub,
|
||||
&socket_addr_space
|
||||
));
|
||||
}
|
||||
let (bank_notification_sender, bank_notification_receiver) = unbounded();
|
||||
(
|
||||
@@ -590,6 +599,7 @@ impl Validator {
|
||||
&serve_repair,
|
||||
Some(blockstore.clone()),
|
||||
node.sockets.serve_repair,
|
||||
socket_addr_space,
|
||||
&exit,
|
||||
);
|
||||
|
||||
@@ -1405,7 +1415,26 @@ fn wait_for_supermajority(
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn report_target_features() {
|
||||
fn is_rosetta_emulated() -> bool {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
use std::str::FromStr;
|
||||
std::process::Command::new("sysctl")
|
||||
.args(&["-in", "sysctl.proc_translated"])
|
||||
.output()
|
||||
.map_err(|_| ())
|
||||
.and_then(|output| String::from_utf8(output.stdout).map_err(|_| ()))
|
||||
.and_then(|stdout| u8::from_str(stdout.trim()).map_err(|_| ()))
|
||||
.map(|enabled| enabled == 1)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
{
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn report_target_features() {
|
||||
warn!(
|
||||
"CUDA is {}abled",
|
||||
if solana_perf::perf_libs::api().is_some() {
|
||||
@@ -1415,40 +1444,47 @@ fn report_target_features() {
|
||||
}
|
||||
);
|
||||
|
||||
// We exclude Mac OS here to be compatible with computers that have Mac M1 chips.
|
||||
// For these computers, one must install rust/cargo/brew etc. using Rosetta 2,
|
||||
// which allows them to run software targeted for x86_64 on an aarch64.
|
||||
// Hence the code below will run on these machines (target_arch="x86_64")
|
||||
// if we don't exclude with target_os="macos".
|
||||
//
|
||||
// It's going to require more more work to get Solana building
|
||||
// on Mac M1's without Rosetta,
|
||||
// and when that happens we should remove this
|
||||
// (the feature flag for code targeting that is target_arch="aarch64")
|
||||
#[cfg(all(
|
||||
any(target_arch = "x86", target_arch = "x86_64"),
|
||||
not(target_os = "macos")
|
||||
))]
|
||||
{
|
||||
if !is_rosetta_emulated() {
|
||||
unsafe { check_avx() };
|
||||
unsafe { check_avx2() };
|
||||
}
|
||||
}
|
||||
|
||||
// Validator binaries built on a machine with AVX support will generate invalid opcodes
|
||||
// when run on machines without AVX causing a non-obvious process abort. Instead detect
|
||||
// the mismatch and error cleanly.
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
#[target_feature(enable = "avx")]
|
||||
unsafe fn check_avx() {
|
||||
if is_x86_feature_detected!("avx") {
|
||||
info!("AVX detected");
|
||||
} else {
|
||||
error!(
|
||||
"Your machine does not have AVX support, please rebuild from source on your machine"
|
||||
"Incompatible CPU detected: missing AVX support. Please build from source on the target"
|
||||
);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
||||
unsafe fn check_avx() {}
|
||||
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
#[target_feature(enable = "avx2")]
|
||||
unsafe fn check_avx2() {
|
||||
if is_x86_feature_detected!("avx2") {
|
||||
info!("AVX2 detected");
|
||||
} else {
|
||||
error!(
|
||||
"Incompatible CPU detected: missing AVX2 support. Please build from source on the target"
|
||||
);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
||||
unsafe fn check_avx2() {}
|
||||
|
||||
// Get the activated stake percentage (based on the provided bank) that is visible in gossip
|
||||
fn get_stake_percent_in_gossip(bank: &Bank, cluster_info: &ClusterInfo, log: bool) -> u64 {
|
||||
let mut online_stake = 0;
|
||||
@@ -1591,6 +1627,7 @@ mod tests {
|
||||
&config,
|
||||
true, // should_check_duplicate_instance
|
||||
start_progress.clone(),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
assert_eq!(
|
||||
*start_progress.read().unwrap(),
|
||||
@@ -1669,6 +1706,7 @@ mod tests {
|
||||
&config,
|
||||
true, // should_check_duplicate_instance
|
||||
Arc::new(RwLock::new(ValidatorStartProgress::default())),
|
||||
SocketAddrSpace::Unspecified,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
@@ -1694,6 +1732,7 @@ mod tests {
|
||||
let cluster_info = ClusterInfo::new(
|
||||
ContactInfo::new_localhost(&node_keypair.pubkey(), timestamp()),
|
||||
node_keypair,
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
|
||||
let (genesis_config, _mint_keypair) = create_genesis_config(1);
|
||||
|
@@ -626,6 +626,7 @@ mod test {
|
||||
signature::{Keypair, Signer},
|
||||
timing::timestamp,
|
||||
};
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
use std::sync::Arc;
|
||||
|
||||
fn local_entries_to_shred(
|
||||
@@ -776,7 +777,11 @@ mod test {
|
||||
assert!(!blockstore.has_duplicate_shreds_in_slot(duplicate_shred_slot));
|
||||
let keypair = Keypair::new();
|
||||
let contact_info = ContactInfo::new_localhost(&keypair.pubkey(), timestamp());
|
||||
let cluster_info = ClusterInfo::new(contact_info, Arc::new(keypair));
|
||||
let cluster_info = ClusterInfo::new(
|
||||
contact_info,
|
||||
Arc::new(keypair),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
run_check_duplicate(
|
||||
&cluster_info,
|
||||
&blockstore,
|
||||
|
@@ -18,6 +18,7 @@ use solana_sdk::{
|
||||
signature::{Keypair, Signer},
|
||||
system_transaction,
|
||||
};
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
use std::{
|
||||
net::{IpAddr, SocketAddr},
|
||||
sync::{
|
||||
@@ -34,7 +35,8 @@ fn test_rpc_client() {
|
||||
solana_logger::setup();
|
||||
|
||||
let alice = Keypair::new();
|
||||
let test_validator = TestValidator::with_no_fees(alice.pubkey(), None);
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(alice.pubkey(), None, SocketAddrSpace::Unspecified);
|
||||
|
||||
let bob_pubkey = solana_sdk::pubkey::new_rand();
|
||||
|
||||
|
@@ -21,6 +21,7 @@ use solana_sdk::{
|
||||
system_transaction,
|
||||
transaction::Transaction,
|
||||
};
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
use solana_transaction_status::TransactionStatus;
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
@@ -58,7 +59,8 @@ fn test_rpc_send_tx() {
|
||||
solana_logger::setup();
|
||||
|
||||
let alice = Keypair::new();
|
||||
let test_validator = TestValidator::with_no_fees(alice.pubkey(), None);
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(alice.pubkey(), None, SocketAddrSpace::Unspecified);
|
||||
let rpc_url = test_validator.rpc_url();
|
||||
|
||||
let bob_pubkey = solana_sdk::pubkey::new_rand();
|
||||
@@ -122,7 +124,8 @@ fn test_rpc_invalid_requests() {
|
||||
solana_logger::setup();
|
||||
|
||||
let alice = Keypair::new();
|
||||
let test_validator = TestValidator::with_no_fees(alice.pubkey(), None);
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(alice.pubkey(), None, SocketAddrSpace::Unspecified);
|
||||
let rpc_url = test_validator.rpc_url();
|
||||
|
||||
let bob_pubkey = solana_sdk::pubkey::new_rand();
|
||||
@@ -153,7 +156,8 @@ fn test_rpc_invalid_requests() {
|
||||
fn test_rpc_slot_updates() {
|
||||
solana_logger::setup();
|
||||
|
||||
let test_validator = TestValidator::with_no_fees(Pubkey::new_unique(), None);
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(Pubkey::new_unique(), None, SocketAddrSpace::Unspecified);
|
||||
|
||||
// Create the pub sub runtime
|
||||
let rt = Runtime::new().unwrap();
|
||||
@@ -218,7 +222,8 @@ fn test_rpc_subscriptions() {
|
||||
solana_logger::setup();
|
||||
|
||||
let alice = Keypair::new();
|
||||
let test_validator = TestValidator::with_no_fees(alice.pubkey(), None);
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(alice.pubkey(), None, SocketAddrSpace::Unspecified);
|
||||
|
||||
let transactions_socket = UdpSocket::bind("0.0.0.0:0").unwrap();
|
||||
transactions_socket.connect(test_validator.tpu()).unwrap();
|
||||
@@ -385,7 +390,8 @@ fn test_rpc_subscriptions() {
|
||||
fn test_tpu_send_transaction() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, None);
|
||||
let test_validator =
|
||||
TestValidator::with_no_fees(mint_pubkey, None, SocketAddrSpace::Unspecified);
|
||||
let rpc_client = Arc::new(RpcClient::new_with_commitment(
|
||||
test_validator.rpc_url(),
|
||||
CommitmentConfig::processed(),
|
||||
|
@@ -60,6 +60,7 @@ mod tests {
|
||||
signature::{Keypair, Signer},
|
||||
system_transaction,
|
||||
};
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
fs,
|
||||
@@ -442,7 +443,11 @@ mod tests {
|
||||
// channel hold hard links to these deleted snapshots. We verify this is the case below.
|
||||
let exit = Arc::new(AtomicBool::new(false));
|
||||
|
||||
let cluster_info = Arc::new(ClusterInfo::new_with_invalid_keypair(ContactInfo::default()));
|
||||
let cluster_info = Arc::new(ClusterInfo::new(
|
||||
ContactInfo::default(),
|
||||
Arc::new(Keypair::new()),
|
||||
SocketAddrSpace::Unspecified,
|
||||
));
|
||||
|
||||
let pending_snapshot_package = PendingSnapshotPackage::default();
|
||||
let snapshot_packager_service = SnapshotPackagerService::new(
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-crate-features"
|
||||
version = "1.7.6"
|
||||
version = "1.7.9"
|
||||
description = "Solana Crate Features"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
|
@@ -55,6 +55,7 @@ gives a convenient interface for the RPC methods.
|
||||
- [getSlotLeader](jsonrpc-api.md#getslotleader)
|
||||
- [getSlotLeaders](jsonrpc-api.md#getslotleaders)
|
||||
- [getStakeActivation](jsonrpc-api.md#getstakeactivation)
|
||||
- [getSnapshotSlot](jsonrpc-api.md#getsnapshotslot)
|
||||
- [getSupply](jsonrpc-api.md#getsupply)
|
||||
- [getTokenAccountBalance](jsonrpc-api.md#gettokenaccountbalance)
|
||||
- [getTokenAccountsByDelegate](jsonrpc-api.md#gettokenaccountsbydelegate)
|
||||
@@ -2474,6 +2475,9 @@ The result will be an RpcResponse JSON object with `value` equal to a JSON objec
|
||||
- `uiAmount: <number | null>` - the balance, using mint-prescribed decimals **DEPRECATED**
|
||||
- `uiAmountString: <string>` - the balance as a string, using mint-prescribed decimals
|
||||
|
||||
For more details on returned data: The
|
||||
[Token Balances Structure](jsonrpc-api.md#token-balances-structure) response from [getBlock](jsonrpc-api.md#getblock) follows a similar structure.
|
||||
|
||||
#### Example:
|
||||
|
||||
Request:
|
||||
@@ -2530,6 +2534,8 @@ The result will be an RpcResponse JSON object with `value` equal to an array of
|
||||
- `executable: <bool>`, boolean indicating if the account contains a program \(and is strictly read-only\)
|
||||
- `rentEpoch: <u64>`, the epoch at which this account will next owe rent, as u64
|
||||
|
||||
When the data is requested with the `jsonParsed` encoding a format similar to that of the [Token Balances Structure](jsonrpc-api.md#token-balances-structure) can be expected inside the structure, both for the `tokenAmount` and the `delegatedAmount`, with the latter being an optional object.
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
@@ -2564,7 +2570,6 @@ Result:
|
||||
"data": {
|
||||
"program": "spl-token",
|
||||
"parsed": {
|
||||
"accountType": "account",
|
||||
"info": {
|
||||
"tokenAmount": {
|
||||
"amount": "1",
|
||||
@@ -2573,13 +2578,20 @@ Result:
|
||||
"uiAmountString": "0.1",
|
||||
},
|
||||
"delegate": "4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T",
|
||||
"delegatedAmount": 1,
|
||||
"isInitialized": true,
|
||||
"delegatedAmount": {
|
||||
"amount": "1",
|
||||
"decimals": 1,
|
||||
"uiAmount": 0.1,
|
||||
"uiAmountString": "0.1",
|
||||
},
|
||||
"state": "initialized",
|
||||
"isNative": false,
|
||||
"mint": "3wyAj7Rt1TWVPZVteFJPLa26JmLvdb1CAKEFZm3NY75E",
|
||||
"owner": "CnPoSPKXu7wJqxe59Fs72tkBeALovhsCxYeFwPCQH9TD"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "account"
|
||||
},
|
||||
"space": 165
|
||||
},
|
||||
"executable": false,
|
||||
"lamports": 1726080,
|
||||
@@ -2620,6 +2632,8 @@ The result will be an RpcResponse JSON object with `value` equal to an array of
|
||||
- `executable: <bool>`, boolean indicating if the account contains a program \(and is strictly read-only\)
|
||||
- `rentEpoch: <u64>`, the epoch at which this account will next owe rent, as u64
|
||||
|
||||
When the data is requested with the `jsonParsed` encoding a format similar to that of the [Token Balances Structure](jsonrpc-api.md#token-balances-structure) can be expected inside the structure, both for the `tokenAmount` and the `delegatedAmount`, with the latter being an optional object.
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
@@ -2662,14 +2676,21 @@ Result:
|
||||
"uiAmount": 0.1,
|
||||
"uiAmountString": "0.1",
|
||||
},
|
||||
"delegate": null,
|
||||
"delegatedAmount": 1,
|
||||
"isInitialized": true,
|
||||
"delegate": "4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T",
|
||||
"delegatedAmount": {
|
||||
"amount": "1",
|
||||
"decimals": 1,
|
||||
"uiAmount": 0.1,
|
||||
"uiAmountString": "0.1",
|
||||
},
|
||||
"state": "initialized",
|
||||
"isNative": false,
|
||||
"mint": "3wyAj7Rt1TWVPZVteFJPLa26JmLvdb1CAKEFZm3NY75E",
|
||||
"owner": "4Qkev8aNZcqFNSRhQzwyLMFSsi94jHqE8WNVTJzTP99F"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "account"
|
||||
},
|
||||
"space": 165
|
||||
},
|
||||
"executable": false,
|
||||
"lamports": 1726080,
|
||||
@@ -3016,7 +3037,7 @@ curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d '
|
||||
|
||||
Result:
|
||||
```json
|
||||
{"jsonrpc":"2.0","result":{"solana-core": "1.7.6"},"id":1}
|
||||
{"jsonrpc":"2.0","result":{"solana-core": "1.7.9"},"id":1}
|
||||
```
|
||||
|
||||
### getVoteAccounts
|
||||
@@ -4313,9 +4334,9 @@ Result:
|
||||
```
|
||||
|
||||
For more details on returned data:
|
||||
[Transaction Structure](jsonrpc-api.md#transactionstructure)
|
||||
[Inner Instructions Structure](jsonrpc-api.md#innerinstructionsstructure)
|
||||
[Token Balances Structure](jsonrpc-api.md#tokenbalancesstructure)
|
||||
[Transaction Structure](jsonrpc-api.md#transaction-structure)
|
||||
[Inner Instructions Structure](jsonrpc-api.md#inner-instructions-structure)
|
||||
[Token Balances Structure](jsonrpc-api.md#token-balances-structure)
|
||||
|
||||
### getConfirmedBlocks
|
||||
|
||||
|
@@ -94,7 +94,7 @@ attempts to use a float operation that is not supported, the runtime will report
|
||||
an unresolved symbol error.
|
||||
|
||||
Float operations are performed via software libraries, specifically LLVM's float
|
||||
builtins. Due to be software emulated they consume more compute units than
|
||||
builtins. Due to the software emulated they consume more compute units than
|
||||
integer operations. In general, fixed point operations are recommended where
|
||||
possible.
|
||||
|
||||
@@ -108,7 +108,7 @@ To run the test, sync the repo, and run:
|
||||
|
||||
Recent results show the float operations take more instructions compared to
|
||||
integers equivalents. Fixed point implementations may vary but will also be
|
||||
less then the float equivalents:
|
||||
less than the float equivalents:
|
||||
|
||||
```
|
||||
u64 f32
|
||||
|
@@ -139,7 +139,7 @@ accounts are permanently marked as executable by the loader once they are
|
||||
successfully deployed. The runtime will reject transactions that specify programs
|
||||
that are not executable.
|
||||
|
||||
Unlike on-chain programs, [Native Programs](developing/runtime-facilities/programs)
|
||||
Unlike on-chain programs, [Native Programs](developing/runtime-facilities/programs.md)
|
||||
are handled differently in that they are built directly into the Solana runtime.
|
||||
|
||||
### Accounts
|
||||
|
@@ -46,8 +46,8 @@ people were confused about whether they were the same project. The Loom team
|
||||
decided it would rebrand. They chose the name Solana, a nod to a small beach
|
||||
town North of San Diego called Solana Beach, where Anatoly, Greg and Stephen
|
||||
lived and surfed for three years when they worked for Qualcomm. On March 28th,
|
||||
the team created the Solana Labs GitHub organization and renamed Greg's
|
||||
prototype Silk to Solana.
|
||||
the team created the Solana GitHub organization and renamed Greg's prototype
|
||||
Silk to Solana.
|
||||
|
||||
In June of 2018, the team scaled up the technology to run on cloud-based
|
||||
networks and on July 19th, published a 50-node, permissioned, public testnet
|
||||
|
@@ -82,8 +82,7 @@ will be of a different type than one used to simply send and receive tokens.
|
||||
|
||||
Follow the wallet's instructions for selecting a validator. You can get
|
||||
information about potentially performant validators from the links below.
|
||||
The Solana Labs team and the Solana Foundation do not recommend any particular
|
||||
validator.
|
||||
The Solana Foundation does not recommend any particular validator.
|
||||
|
||||
The Mainnet Beta validators introduce themselves and their services on this
|
||||
Solana Forum thread:
|
||||
|
@@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-dos"
|
||||
version = "1.7.6"
|
||||
version = "1.7.9"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -14,16 +14,17 @@ clap = "2.33.1"
|
||||
log = "0.4.11"
|
||||
rand = "0.7.0"
|
||||
rayon = "1.5.0"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.6" }
|
||||
solana-core = { path = "../core", version = "=1.7.6" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.7.6" }
|
||||
solana-ledger = { path = "../ledger", version = "=1.7.6" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.6" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.6" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.6" }
|
||||
solana-version = { path = "../version", version = "=1.7.6" }
|
||||
solana-client = { path = "../client", version = "=1.7.6" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.9" }
|
||||
solana-core = { path = "../core", version = "=1.7.9" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.7.9" }
|
||||
solana-ledger = { path = "../ledger", version = "=1.7.9" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.9" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.9" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.9" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.7.9" }
|
||||
solana-version = { path = "../version", version = "=1.7.9" }
|
||||
solana-client = { path = "../client", version = "=1.7.9" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -6,6 +6,7 @@ use solana_client::rpc_client::RpcClient;
|
||||
use solana_core::serve_repair::RepairProtocol;
|
||||
use solana_gossip::{contact_info::ContactInfo, gossip_service::discover};
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_streamer::socket::SocketAddrSpace;
|
||||
use std::net::{SocketAddr, UdpSocket};
|
||||
use std::process::exit;
|
||||
use std::str::FromStr;
|
||||
@@ -197,6 +198,13 @@ fn main() {
|
||||
.long("skip-gossip")
|
||||
.help("Just use entrypoint address directly"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("allow_private_addr")
|
||||
.long("allow-private-addr")
|
||||
.takes_value(false)
|
||||
.help("Allow contacting private ip addresses")
|
||||
.hidden(true),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
let mut entrypoint_addr = SocketAddr::from(([127, 0, 0, 1], 8001));
|
||||
@@ -216,6 +224,7 @@ fn main() {
|
||||
let mut nodes = vec![];
|
||||
if !skip_gossip {
|
||||
info!("Finding cluster entry: {:?}", entrypoint_addr);
|
||||
let socket_addr_space = SocketAddrSpace::new(matches.is_present("allow_private_addr"));
|
||||
let (gossip_nodes, _validators) = discover(
|
||||
None, // keypair
|
||||
Some(&entrypoint_addr),
|
||||
@@ -225,6 +234,7 @@ fn main() {
|
||||
Some(&entrypoint_addr), // find_node_by_gossip_addr
|
||||
None, // my_gossip_addr
|
||||
0, // my_shred_version
|
||||
socket_addr_space,
|
||||
)
|
||||
.unwrap_or_else(|err| {
|
||||
eprintln!("Failed to discover {} node: {:?}", entrypoint_addr, err);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-download-utils"
|
||||
version = "1.7.6"
|
||||
version = "1.7.9"
|
||||
description = "Solana Download Utils"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -15,8 +15,8 @@ console = "0.14.1"
|
||||
indicatif = "0.15.0"
|
||||
log = "0.4.11"
|
||||
reqwest = { version = "0.11.2", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.6" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.6" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.9" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.9" }
|
||||
tar = "0.4.28"
|
||||
|
||||
[lib]
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-faucet"
|
||||
version = "1.7.6"
|
||||
version = "1.7.9"
|
||||
description = "Solana Faucet"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -16,12 +16,12 @@ clap = "2.33"
|
||||
log = "0.4.11"
|
||||
serde = "1.0.122"
|
||||
serde_derive = "1.0.103"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.6" }
|
||||
solana-cli-config = { path = "../cli-config", version = "=1.7.6" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.6" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.7.6" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.6" }
|
||||
solana-version = { path = "../version", version = "=1.7.6" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.9" }
|
||||
solana-cli-config = { path = "../cli-config", version = "=1.7.9" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.9" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.7.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.9" }
|
||||
solana-version = { path = "../version", version = "=1.7.9" }
|
||||
spl-memo = { version = "=3.0.1", features = ["no-entrypoint"] }
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
|
@@ -38,10 +38,10 @@ fetch_program() {
|
||||
|
||||
}
|
||||
|
||||
fetch_program token 3.1.0 TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA BPFLoader2111111111111111111111111111111111
|
||||
fetch_program token 3.2.0 TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA BPFLoader2111111111111111111111111111111111
|
||||
fetch_program memo 1.0.0 Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo BPFLoader1111111111111111111111111111111111
|
||||
fetch_program memo 3.0.0 MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr BPFLoader2111111111111111111111111111111111
|
||||
fetch_program associated-token-account 1.0.1 ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL BPFLoader2111111111111111111111111111111111
|
||||
fetch_program associated-token-account 1.0.3 ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL BPFLoader2111111111111111111111111111111111
|
||||
fetch_program feature-proposal 1.0.0 Feat1YXHhH6t1juaWF74WLcfv4XoNocjXA6sPWHNgAse BPFLoader2111111111111111111111111111111111
|
||||
|
||||
echo "${genesis_args[@]}" > spl-genesis-args.sh
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-frozen-abi"
|
||||
version = "1.7.6"
|
||||
version = "1.7.9"
|
||||
description = "Solana Frozen ABI"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -16,11 +16,11 @@ log = "0.4.11"
|
||||
serde = "1.0.122"
|
||||
serde_derive = "1.0.103"
|
||||
sha2 = "0.9.2"
|
||||
solana-frozen-abi-macro = { path = "macro", version = "=1.7.6" }
|
||||
solana-frozen-abi-macro = { path = "macro", version = "=1.7.9" }
|
||||
thiserror = "1.0"
|
||||
|
||||
[target.'cfg(not(target_arch = "bpf"))'.dependencies]
|
||||
solana-logger = { path = "../logger", version = "=1.7.6" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.9" }
|
||||
generic-array = { version = "0.14.3", default-features = false, features = ["serde", "more_lengths"]}
|
||||
memmap2 = "0.1.0"
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-frozen-abi-macro"
|
||||
version = "1.7.6"
|
||||
version = "1.7.9"
|
||||
description = "Solana Frozen ABI Macro"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-genesis-utils"
|
||||
version = "1.7.6"
|
||||
version = "1.7.9"
|
||||
description = "Solana Genesis Utils"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -10,9 +10,9 @@ documentation = "https://docs.rs/solana-download-utils"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.6" }
|
||||
solana-download-utils = { path = "../download-utils", version = "=1.7.6" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.6" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.9" }
|
||||
solana-download-utils = { path = "../download-utils", version = "=1.7.9" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.9" }
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib"]
|
||||
|
@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-genesis"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.7.6"
|
||||
version = "1.7.9"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -16,16 +16,16 @@ chrono = "0.4"
|
||||
serde = "1.0.122"
|
||||
serde_json = "1.0.56"
|
||||
serde_yaml = "0.8.13"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.6" }
|
||||
solana-cli-config = { path = "../cli-config", version = "=1.7.6" }
|
||||
solana-exchange-program = { path = "../programs/exchange", version = "=1.7.6" }
|
||||
solana-ledger = { path = "../ledger", version = "=1.7.6" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.6" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.6" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.6" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "=1.7.6" }
|
||||
solana-version = { path = "../version", version = "=1.7.6" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.7.6" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.9" }
|
||||
solana-cli-config = { path = "../cli-config", version = "=1.7.9" }
|
||||
solana-exchange-program = { path = "../programs/exchange", version = "=1.7.9" }
|
||||
solana-ledger = { path = "../ledger", version = "=1.7.9" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.9" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.9" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "=1.7.9" }
|
||||
solana-version = { path = "../version", version = "=1.7.9" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.7.9" }
|
||||
tempfile = "3.1.0"
|
||||
|
||||
[[bin]]
|
||||
|
@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-gossip"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.7.6"
|
||||
version = "1.7.9"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -26,22 +26,22 @@ rayon = "1.5.0"
|
||||
serde = "1.0.122"
|
||||
serde_bytes = "0.11"
|
||||
serde_derive = "1.0.103"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.6" }
|
||||
solana-client = { path = "../client", version = "=1.7.6" }
|
||||
solana-frozen-abi = { path = "../frozen-abi", version = "=1.7.6" }
|
||||
solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.7.6" }
|
||||
solana-ledger = { path = "../ledger", version = "=1.7.6" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.6" }
|
||||
solana-measure = { path = "../measure", version = "=1.7.6" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.7.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.6" }
|
||||
solana-perf = { path = "../perf", version = "=1.7.6" }
|
||||
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.7.6" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.6" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.7.6" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.6" }
|
||||
solana-version = { path = "../version", version = "=1.7.6" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.7.6" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.9" }
|
||||
solana-client = { path = "../client", version = "=1.7.9" }
|
||||
solana-frozen-abi = { path = "../frozen-abi", version = "=1.7.9" }
|
||||
solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.7.9" }
|
||||
solana-ledger = { path = "../ledger", version = "=1.7.9" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.9" }
|
||||
solana-measure = { path = "../measure", version = "=1.7.9" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.7.9" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.9" }
|
||||
solana-perf = { path = "../perf", version = "=1.7.9" }
|
||||
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.7.9" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.9" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.7.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.9" }
|
||||
solana-version = { path = "../version", version = "=1.7.9" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.7.9" }
|
||||
thiserror = "1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
|
@@ -64,7 +64,7 @@ use {
|
||||
solana_streamer::{
|
||||
packet,
|
||||
sendmmsg::multicast,
|
||||
socket::is_global,
|
||||
socket::SocketAddrSpace,
|
||||
streamer::{PacketReceiver, PacketSender},
|
||||
},
|
||||
solana_vote_program::vote_state::MAX_LOCKOUT_HISTORY,
|
||||
@@ -158,12 +158,7 @@ pub struct ClusterInfo {
|
||||
contact_save_interval: u64, // milliseconds, 0 = disabled
|
||||
instance: NodeInstance,
|
||||
contact_info_path: PathBuf,
|
||||
}
|
||||
|
||||
impl Default for ClusterInfo {
|
||||
fn default() -> Self {
|
||||
Self::new_with_invalid_keypair(ContactInfo::default())
|
||||
}
|
||||
socket_addr_space: SocketAddrSpace,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize, AbiExample)]
|
||||
@@ -392,12 +387,11 @@ fn retain_staked(values: &mut Vec<CrdsValue>, stakes: &HashMap<Pubkey, u64>) {
|
||||
}
|
||||
|
||||
impl ClusterInfo {
|
||||
/// Without a valid keypair gossip will not function. Only useful for tests.
|
||||
pub fn new_with_invalid_keypair(contact_info: ContactInfo) -> Self {
|
||||
Self::new(contact_info, Arc::new(Keypair::new()))
|
||||
}
|
||||
|
||||
pub fn new(contact_info: ContactInfo, keypair: Arc<Keypair>) -> Self {
|
||||
pub fn new(
|
||||
contact_info: ContactInfo,
|
||||
keypair: Arc<Keypair>,
|
||||
socket_addr_space: SocketAddrSpace,
|
||||
) -> Self {
|
||||
let id = contact_info.id;
|
||||
let me = Self {
|
||||
gossip: RwLock::new(CrdsGossip::default()),
|
||||
@@ -417,6 +411,7 @@ impl ClusterInfo {
|
||||
instance: NodeInstance::new(&mut thread_rng(), id, timestamp()),
|
||||
contact_info_path: PathBuf::default(),
|
||||
contact_save_interval: 0, // disabled
|
||||
socket_addr_space,
|
||||
};
|
||||
me.insert_self();
|
||||
me.push_self(&HashMap::new(), None);
|
||||
@@ -448,6 +443,7 @@ impl ClusterInfo {
|
||||
instance: NodeInstance::new(&mut thread_rng(), *new_id, timestamp()),
|
||||
contact_info_path: PathBuf::default(),
|
||||
contact_save_interval: 0, // disabled
|
||||
..*self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -455,6 +451,10 @@ impl ClusterInfo {
|
||||
self.contact_debug_interval = new;
|
||||
}
|
||||
|
||||
pub fn socket_addr_space(&self) -> &SocketAddrSpace {
|
||||
&self.socket_addr_space
|
||||
}
|
||||
|
||||
fn push_self(
|
||||
&self,
|
||||
stakes: &HashMap<Pubkey, u64>,
|
||||
@@ -483,6 +483,7 @@ impl ClusterInfo {
|
||||
shred_version,
|
||||
stakes,
|
||||
gossip_validators,
|
||||
&self.socket_addr_space,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -665,7 +666,7 @@ impl ClusterInfo {
|
||||
.all_peers()
|
||||
.into_iter()
|
||||
.filter_map(|(node, last_updated)| {
|
||||
if !ContactInfo::is_valid_address(&node.rpc) {
|
||||
if !ContactInfo::is_valid_address(&node.rpc, &self.socket_addr_space) {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -676,8 +677,8 @@ impl ClusterInfo {
|
||||
return None;
|
||||
}
|
||||
|
||||
fn addr_to_string(default_ip: &IpAddr, addr: &SocketAddr) -> String {
|
||||
if ContactInfo::is_valid_address(addr) {
|
||||
let addr_to_string = |default_ip: &IpAddr, addr: &SocketAddr| -> String {
|
||||
if ContactInfo::is_valid_address(addr, &self.socket_addr_space) {
|
||||
if &addr.ip() == default_ip {
|
||||
addr.port().to_string()
|
||||
} else {
|
||||
@@ -686,7 +687,7 @@ impl ClusterInfo {
|
||||
} else {
|
||||
"none".to_string()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let rpc_addr = node.rpc.ip();
|
||||
Some(format!(
|
||||
@@ -730,7 +731,7 @@ impl ClusterInfo {
|
||||
.all_peers()
|
||||
.into_iter()
|
||||
.filter_map(|(node, last_updated)| {
|
||||
let is_spy_node = Self::is_spy_node(&node);
|
||||
let is_spy_node = Self::is_spy_node(&node, &self.socket_addr_space);
|
||||
if is_spy_node {
|
||||
total_spy_nodes = total_spy_nodes.saturating_add(1);
|
||||
}
|
||||
@@ -743,8 +744,8 @@ impl ClusterInfo {
|
||||
if is_spy_node {
|
||||
shred_spy_nodes = shred_spy_nodes.saturating_add(1);
|
||||
}
|
||||
fn addr_to_string(default_ip: &IpAddr, addr: &SocketAddr) -> String {
|
||||
if ContactInfo::is_valid_address(addr) {
|
||||
let addr_to_string = |default_ip: &IpAddr, addr: &SocketAddr| -> String {
|
||||
if ContactInfo::is_valid_address(addr, &self.socket_addr_space) {
|
||||
if &addr.ip() == default_ip {
|
||||
addr.port().to_string()
|
||||
} else {
|
||||
@@ -753,11 +754,11 @@ impl ClusterInfo {
|
||||
} else {
|
||||
"none".to_string()
|
||||
}
|
||||
}
|
||||
};
|
||||
let ip_addr = node.gossip.ip();
|
||||
Some(format!(
|
||||
"{:15} {:2}| {:5} | {:44} |{:^9}| {:5}| {:5}| {:5}| {:5}| {:5}| {:5}| {:5}| {}\n",
|
||||
if ContactInfo::is_valid_address(&node.gossip) {
|
||||
if ContactInfo::is_valid_address(&node.gossip, &self.socket_addr_space) {
|
||||
ip_addr.to_string()
|
||||
} else {
|
||||
"none".to_string()
|
||||
@@ -1138,12 +1139,16 @@ impl ClusterInfo {
|
||||
|
||||
/// all validators that have a valid rpc port regardless of `shred_version`.
|
||||
pub fn all_rpc_peers(&self) -> Vec<ContactInfo> {
|
||||
let self_pubkey = self.id();
|
||||
self.gossip
|
||||
.read()
|
||||
.unwrap()
|
||||
.crds
|
||||
.get_nodes_contact_info()
|
||||
.filter(|x| x.id != self.id() && ContactInfo::is_valid_address(&x.rpc))
|
||||
.filter(|x| {
|
||||
x.id != self_pubkey
|
||||
&& ContactInfo::is_valid_address(&x.rpc, &self.socket_addr_space)
|
||||
})
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
@@ -1167,17 +1172,23 @@ impl ClusterInfo {
|
||||
.crds
|
||||
.get_nodes_contact_info()
|
||||
// shred_version not considered for gossip peers (ie, spy nodes do not set shred_version)
|
||||
.filter(|x| x.id != me && ContactInfo::is_valid_address(&x.gossip))
|
||||
.filter(|x| {
|
||||
x.id != me && ContactInfo::is_valid_address(&x.gossip, &self.socket_addr_space)
|
||||
})
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// all validators that have a valid tvu port regardless of `shred_version`.
|
||||
pub fn all_tvu_peers(&self) -> Vec<ContactInfo> {
|
||||
let self_pubkey = self.id();
|
||||
self.time_gossip_read_lock("all_tvu_peers", &self.stats.all_tvu_peers)
|
||||
.crds
|
||||
.get_nodes_contact_info()
|
||||
.filter(|x| ContactInfo::is_valid_address(&x.tvu) && x.id != self.id())
|
||||
.filter(|x| {
|
||||
ContactInfo::is_valid_address(&x.tvu, &self.socket_addr_space)
|
||||
&& x.id != self_pubkey
|
||||
})
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
@@ -1210,7 +1221,7 @@ impl ClusterInfo {
|
||||
nodes
|
||||
.into_iter()
|
||||
.filter(|node| {
|
||||
ContactInfo::is_valid_address(&node.serve_repair)
|
||||
ContactInfo::is_valid_address(&node.serve_repair, &self.socket_addr_space)
|
||||
&& match gossip.crds.get_lowest_slot(node.id) {
|
||||
None => true, // fallback to legacy behavior
|
||||
Some(lowest_slot) => lowest_slot.lowest <= slot,
|
||||
@@ -1222,20 +1233,24 @@ impl ClusterInfo {
|
||||
nodes
|
||||
}
|
||||
|
||||
fn is_spy_node(contact_info: &ContactInfo) -> bool {
|
||||
!ContactInfo::is_valid_address(&contact_info.tpu)
|
||||
|| !ContactInfo::is_valid_address(&contact_info.gossip)
|
||||
|| !ContactInfo::is_valid_address(&contact_info.tvu)
|
||||
fn is_spy_node(contact_info: &ContactInfo, socket_addr_space: &SocketAddrSpace) -> bool {
|
||||
!ContactInfo::is_valid_address(&contact_info.tpu, socket_addr_space)
|
||||
|| !ContactInfo::is_valid_address(&contact_info.gossip, socket_addr_space)
|
||||
|| !ContactInfo::is_valid_address(&contact_info.tvu, socket_addr_space)
|
||||
}
|
||||
|
||||
/// compute broadcast table
|
||||
pub fn tpu_peers(&self) -> Vec<ContactInfo> {
|
||||
let self_pubkey = self.id();
|
||||
self.gossip
|
||||
.read()
|
||||
.unwrap()
|
||||
.crds
|
||||
.get_nodes_contact_info()
|
||||
.filter(|x| x.id != self.id() && ContactInfo::is_valid_address(&x.tpu))
|
||||
.filter(|x| {
|
||||
x.id != self_pubkey
|
||||
&& ContactInfo::is_valid_address(&x.tpu, &self.socket_addr_space)
|
||||
})
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
@@ -1243,20 +1258,25 @@ impl ClusterInfo {
|
||||
/// retransmit messages to a list of nodes
|
||||
/// # Remarks
|
||||
/// We need to avoid having obj locked while doing a io, such as the `send_to`
|
||||
pub fn retransmit_to(peers: &[&ContactInfo], packet: &Packet, s: &UdpSocket, forwarded: bool) {
|
||||
pub fn retransmit_to(
|
||||
peers: &[&ContactInfo],
|
||||
packet: &Packet,
|
||||
s: &UdpSocket,
|
||||
forwarded: bool,
|
||||
socket_addr_space: &SocketAddrSpace,
|
||||
) {
|
||||
trace!("retransmit orders {}", peers.len());
|
||||
let dests: Vec<_> = if forwarded {
|
||||
peers
|
||||
.iter()
|
||||
.map(|peer| &peer.tvu_forwards)
|
||||
.filter(|addr| ContactInfo::is_valid_address(addr))
|
||||
.filter(|addr| is_global(addr))
|
||||
.filter(|addr| ContactInfo::is_valid_address(addr, socket_addr_space))
|
||||
.collect()
|
||||
} else {
|
||||
peers
|
||||
.iter()
|
||||
.map(|peer| &peer.tvu)
|
||||
.filter(|addr| is_global(addr))
|
||||
.filter(|addr| socket_addr_space.check(addr))
|
||||
.collect()
|
||||
};
|
||||
let mut dests = &dests[..];
|
||||
@@ -1406,6 +1426,7 @@ impl ClusterInfo {
|
||||
MAX_BLOOM_SIZE,
|
||||
&self.ping_cache,
|
||||
&mut pings,
|
||||
&self.socket_addr_space,
|
||||
) {
|
||||
Err(_) => Vec::default(),
|
||||
Ok((peer, filters)) => vec![(peer, filters)],
|
||||
@@ -1640,9 +1661,8 @@ impl ClusterInfo {
|
||||
bank_forks: Option<Arc<RwLock<BankForks>>>,
|
||||
sender: PacketSender,
|
||||
gossip_validators: Option<HashSet<Pubkey>>,
|
||||
exit: &Arc<AtomicBool>,
|
||||
exit: Arc<AtomicBool>,
|
||||
) -> JoinHandle<()> {
|
||||
let exit = exit.clone();
|
||||
let thread_pool = ThreadPoolBuilder::new()
|
||||
.num_threads(std::cmp::min(get_thread_count(), 8))
|
||||
.thread_name(|i| format!("ClusterInfo::gossip-{}", i))
|
||||
@@ -1815,8 +1835,13 @@ impl ClusterInfo {
|
||||
self.stats
|
||||
.pull_requests_count
|
||||
.add_relaxed(requests.len() as u64);
|
||||
let response =
|
||||
self.handle_pull_requests(recycler, requests, stakes, require_stake_for_gossip);
|
||||
let response = self.handle_pull_requests(
|
||||
thread_pool,
|
||||
recycler,
|
||||
requests,
|
||||
stakes,
|
||||
require_stake_for_gossip,
|
||||
);
|
||||
if !response.is_empty() {
|
||||
self.stats
|
||||
.packets_sent_pull_responses_count
|
||||
@@ -1875,7 +1900,7 @@ impl ClusterInfo {
|
||||
// incoming pull-requests, pings are also sent to request.from_addr (as
|
||||
// opposed to caller.gossip address).
|
||||
move |request| {
|
||||
ContactInfo::is_valid_address(&request.from_addr) && {
|
||||
ContactInfo::is_valid_address(&request.from_addr, &self.socket_addr_space) && {
|
||||
let node = (request.caller.pubkey(), request.from_addr);
|
||||
*cache.entry(node).or_insert_with(|| hard_check(node))
|
||||
}
|
||||
@@ -1886,6 +1911,7 @@ impl ClusterInfo {
|
||||
// and tries to send back to them the values it detects are missing.
|
||||
fn handle_pull_requests(
|
||||
&self,
|
||||
thread_pool: &ThreadPool,
|
||||
recycler: &PacketsRecycler,
|
||||
requests: Vec<PullData>,
|
||||
stakes: &HashMap<Pubkey, u64>,
|
||||
@@ -1918,7 +1944,7 @@ impl ClusterInfo {
|
||||
"generate_pull_responses",
|
||||
&self.stats.generate_pull_responses,
|
||||
)
|
||||
.generate_pull_responses(&caller_and_filters, output_size_limit, now);
|
||||
.generate_pull_responses(thread_pool, &caller_and_filters, output_size_limit, now);
|
||||
if require_stake_for_gossip {
|
||||
for resp in &mut pull_responses {
|
||||
retain_staked(resp, stakes);
|
||||
@@ -2241,7 +2267,7 @@ impl ClusterInfo {
|
||||
let new_push_requests = self.new_push_requests(stakes, require_stake_for_gossip);
|
||||
inc_new_counter_debug!("cluster_info-push_message-pushes", new_push_requests.len());
|
||||
for (address, request) in new_push_requests {
|
||||
if ContactInfo::is_valid_address(&address) {
|
||||
if ContactInfo::is_valid_address(&address, &self.socket_addr_space) {
|
||||
match Packet::from_data(Some(&address), &request) {
|
||||
Ok(packet) => packets.packets.push(packet),
|
||||
Err(err) => error!("failed to write push-request packet: {:?}", err),
|
||||
@@ -2514,6 +2540,9 @@ impl ClusterInfo {
|
||||
match self.run_socket_consume(&receiver, &sender, &thread_pool) {
|
||||
Err(GossipError::RecvTimeoutError(RecvTimeoutError::Disconnected)) => break,
|
||||
Err(GossipError::RecvTimeoutError(RecvTimeoutError::Timeout)) => (),
|
||||
// A send operation can only fail if the receiving end of a
|
||||
// channel is disconnected.
|
||||
Err(GossipError::SendError) => break,
|
||||
Err(err) => error!("gossip consume: {}", err),
|
||||
Ok(()) => (),
|
||||
}
|
||||
@@ -2529,19 +2558,18 @@ impl ClusterInfo {
|
||||
requests_receiver: Receiver<Vec<(/*from:*/ SocketAddr, Protocol)>>,
|
||||
response_sender: PacketSender,
|
||||
should_check_duplicate_instance: bool,
|
||||
exit: &Arc<AtomicBool>,
|
||||
exit: Arc<AtomicBool>,
|
||||
) -> JoinHandle<()> {
|
||||
let exit = exit.clone();
|
||||
let mut last_print = Instant::now();
|
||||
let recycler = PacketsRecycler::default();
|
||||
let thread_pool = ThreadPoolBuilder::new()
|
||||
.num_threads(get_thread_count().min(8))
|
||||
.thread_name(|i| format!("sol-gossip-work-{}", i))
|
||||
.build()
|
||||
.unwrap();
|
||||
Builder::new()
|
||||
.name("solana-listen".to_string())
|
||||
.spawn(move || {
|
||||
let thread_pool = ThreadPoolBuilder::new()
|
||||
.num_threads(std::cmp::min(get_thread_count(), 8))
|
||||
.thread_name(|i| format!("sol-gossip-work-{}", i))
|
||||
.build()
|
||||
.unwrap();
|
||||
let mut last_print = Instant::now();
|
||||
while !exit.load(Ordering::Relaxed) {
|
||||
if let Err(err) = self.run_listen(
|
||||
&recycler,
|
||||
@@ -2889,13 +2917,14 @@ pub fn push_messages_to_peer(
|
||||
messages: Vec<CrdsValue>,
|
||||
self_id: Pubkey,
|
||||
peer_gossip: SocketAddr,
|
||||
socket_addr_space: &SocketAddrSpace,
|
||||
) -> Result<(), GossipError> {
|
||||
let reqs: Vec<_> = ClusterInfo::split_gossip_messages(PUSH_MESSAGE_MAX_PAYLOAD_SIZE, messages)
|
||||
.map(move |payload| (peer_gossip, Protocol::PushMessage(self_id, payload)))
|
||||
.collect();
|
||||
let packets = to_packets_with_destination(PacketsRecycler::default(), &reqs);
|
||||
let sock = UdpSocket::bind("0.0.0.0:0").unwrap();
|
||||
packet::send_to(&packets, &sock)?;
|
||||
packet::send_to(&packets, &sock, socket_addr_space)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -2989,20 +3018,30 @@ mod tests {
|
||||
fn test_gossip_node() {
|
||||
//check that a gossip nodes always show up as spies
|
||||
let (node, _, _) = ClusterInfo::spy_node(&solana_sdk::pubkey::new_rand(), 0);
|
||||
assert!(ClusterInfo::is_spy_node(&node));
|
||||
assert!(ClusterInfo::is_spy_node(
|
||||
&node,
|
||||
&SocketAddrSpace::Unspecified
|
||||
));
|
||||
let (node, _, _) = ClusterInfo::gossip_node(
|
||||
&solana_sdk::pubkey::new_rand(),
|
||||
&"1.1.1.1:1111".parse().unwrap(),
|
||||
0,
|
||||
);
|
||||
assert!(ClusterInfo::is_spy_node(&node));
|
||||
assert!(ClusterInfo::is_spy_node(
|
||||
&node,
|
||||
&SocketAddrSpace::Unspecified
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_handle_pull() {
|
||||
solana_logger::setup();
|
||||
let node = Node::new_localhost();
|
||||
let cluster_info = Arc::new(ClusterInfo::new_with_invalid_keypair(node.info));
|
||||
let cluster_info = Arc::new(ClusterInfo::new(
|
||||
node.info,
|
||||
Arc::new(Keypair::new()),
|
||||
SocketAddrSpace::Unspecified,
|
||||
));
|
||||
|
||||
let entrypoint_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let data = test_crds_values(entrypoint_pubkey);
|
||||
@@ -3059,6 +3098,7 @@ mod tests {
|
||||
let cluster_info = ClusterInfo::new(
|
||||
ContactInfo::new_localhost(&this_node.pubkey(), timestamp()),
|
||||
this_node.clone(),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
let remote_nodes: Vec<(Keypair, SocketAddr)> =
|
||||
repeat_with(|| new_rand_remote_node(&mut rng))
|
||||
@@ -3113,6 +3153,7 @@ mod tests {
|
||||
let cluster_info = ClusterInfo::new(
|
||||
ContactInfo::new_localhost(&this_node.pubkey(), timestamp()),
|
||||
this_node.clone(),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
let remote_nodes: Vec<(Keypair, SocketAddr)> =
|
||||
repeat_with(|| new_rand_remote_node(&mut rng))
|
||||
@@ -3273,7 +3314,11 @@ mod tests {
|
||||
//check that gossip doesn't try to push to invalid addresses
|
||||
let node = Node::new_localhost();
|
||||
let (spy, _, _) = ClusterInfo::spy_node(&solana_sdk::pubkey::new_rand(), 0);
|
||||
let cluster_info = Arc::new(ClusterInfo::new_with_invalid_keypair(node.info));
|
||||
let cluster_info = Arc::new(ClusterInfo::new(
|
||||
node.info,
|
||||
Arc::new(Keypair::new()),
|
||||
SocketAddrSpace::Unspecified,
|
||||
));
|
||||
cluster_info.insert_info(spy);
|
||||
{
|
||||
let mut gossip = cluster_info.gossip.write().unwrap();
|
||||
@@ -3282,6 +3327,7 @@ mod tests {
|
||||
cluster_info.my_shred_version(),
|
||||
&HashMap::new(), // stakes
|
||||
None, // gossip validators
|
||||
&SocketAddrSpace::Unspecified,
|
||||
);
|
||||
}
|
||||
let reqs = cluster_info.generate_new_gossip_requests(
|
||||
@@ -3293,7 +3339,7 @@ mod tests {
|
||||
);
|
||||
//assert none of the addrs are invalid.
|
||||
reqs.iter().all(|(addr, _)| {
|
||||
let res = ContactInfo::is_valid_address(addr);
|
||||
let res = ContactInfo::is_valid_address(addr, &SocketAddrSpace::Unspecified);
|
||||
assert!(res);
|
||||
res
|
||||
});
|
||||
@@ -3302,14 +3348,19 @@ mod tests {
|
||||
#[test]
|
||||
fn test_cluster_info_new() {
|
||||
let d = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), timestamp());
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(d.clone());
|
||||
let cluster_info = ClusterInfo::new(
|
||||
d.clone(),
|
||||
Arc::new(Keypair::new()),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
assert_eq!(d.id, cluster_info.id());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_info_test() {
|
||||
let d = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), timestamp());
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(d);
|
||||
let cluster_info =
|
||||
ClusterInfo::new(d, Arc::new(Keypair::new()), SocketAddrSpace::Unspecified);
|
||||
let d = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), timestamp());
|
||||
let label = CrdsValueLabel::ContactInfo(d.id);
|
||||
cluster_info.insert_info(d);
|
||||
@@ -3388,7 +3439,11 @@ mod tests {
|
||||
let peer_keypair = Keypair::new();
|
||||
let contact_info = ContactInfo::new_localhost(&keypair.pubkey(), 0);
|
||||
let peer = ContactInfo::new_localhost(&peer_keypair.pubkey(), 0);
|
||||
let cluster_info = ClusterInfo::new(contact_info, Arc::new(keypair));
|
||||
let cluster_info = ClusterInfo::new(
|
||||
contact_info,
|
||||
Arc::new(keypair),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
cluster_info
|
||||
.ping_cache
|
||||
.lock()
|
||||
@@ -3402,6 +3457,7 @@ mod tests {
|
||||
cluster_info.my_shred_version(),
|
||||
&HashMap::new(), // stakes
|
||||
None, // gossip validators
|
||||
&SocketAddrSpace::Unspecified,
|
||||
);
|
||||
}
|
||||
//check that all types of gossip messages are signed correctly
|
||||
@@ -3431,6 +3487,7 @@ mod tests {
|
||||
MAX_BLOOM_SIZE,
|
||||
&cluster_info.ping_cache,
|
||||
&mut pings,
|
||||
&cluster_info.socket_addr_space,
|
||||
)
|
||||
.ok()
|
||||
.unwrap();
|
||||
@@ -3440,7 +3497,11 @@ mod tests {
|
||||
fn test_refresh_vote() {
|
||||
let keys = Keypair::new();
|
||||
let contact_info = ContactInfo::new_localhost(&keys.pubkey(), 0);
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(contact_info);
|
||||
let cluster_info = ClusterInfo::new(
|
||||
contact_info,
|
||||
Arc::new(Keypair::new()),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
|
||||
// Construct and push a vote for some other slot
|
||||
let unrefresh_slot = 5;
|
||||
@@ -3527,7 +3588,11 @@ mod tests {
|
||||
let mut rng = rand::thread_rng();
|
||||
let keys = Keypair::new();
|
||||
let contact_info = ContactInfo::new_localhost(&keys.pubkey(), 0);
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(contact_info);
|
||||
let cluster_info = ClusterInfo::new(
|
||||
contact_info,
|
||||
Arc::new(Keypair::new()),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
|
||||
// make sure empty crds is handled correctly
|
||||
let mut cursor = Cursor::default();
|
||||
@@ -3598,7 +3663,11 @@ mod tests {
|
||||
let mut rng = rand::thread_rng();
|
||||
let keys = Keypair::new();
|
||||
let contact_info = ContactInfo::new_localhost(&keys.pubkey(), 0);
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(contact_info);
|
||||
let cluster_info = ClusterInfo::new(
|
||||
contact_info,
|
||||
Arc::new(Keypair::new()),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
let mut tower = Vec::new();
|
||||
for k in 0..MAX_LOCKOUT_HISTORY {
|
||||
let slot = k as Slot;
|
||||
@@ -3644,7 +3713,11 @@ mod tests {
|
||||
fn test_push_epoch_slots() {
|
||||
let keys = Keypair::new();
|
||||
let contact_info = ContactInfo::new_localhost(&keys.pubkey(), 0);
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(contact_info);
|
||||
let cluster_info = ClusterInfo::new(
|
||||
contact_info,
|
||||
Arc::new(Keypair::new()),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
let slots = cluster_info.get_epoch_slots(&mut Cursor::default());
|
||||
assert!(slots.is_empty());
|
||||
cluster_info.push_epoch_slots(&[0]);
|
||||
@@ -3701,6 +3774,7 @@ mod tests {
|
||||
let cluster_info = ClusterInfo::new(
|
||||
ContactInfo::new_localhost(&node_keypair.pubkey(), timestamp()),
|
||||
node_keypair,
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
let entrypoint_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let entrypoint = ContactInfo::new_localhost(&entrypoint_pubkey, timestamp());
|
||||
@@ -3854,7 +3928,11 @@ mod tests {
|
||||
#[test]
|
||||
fn test_tvu_peers_and_stakes() {
|
||||
let d = ContactInfo::new_localhost(&Pubkey::new(&[0; 32]), timestamp());
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(d.clone());
|
||||
let cluster_info = ClusterInfo::new(
|
||||
d.clone(),
|
||||
Arc::new(Keypair::new()),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
let mut stakes = HashMap::new();
|
||||
|
||||
// no stake
|
||||
@@ -3895,6 +3973,7 @@ mod tests {
|
||||
let cluster_info = ClusterInfo::new(
|
||||
ContactInfo::new_localhost(&node_keypair.pubkey(), timestamp()),
|
||||
node_keypair,
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
let entrypoint_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let mut entrypoint = ContactInfo::new_localhost(&entrypoint_pubkey, timestamp());
|
||||
@@ -3950,6 +4029,7 @@ mod tests {
|
||||
let cluster_info = ClusterInfo::new(
|
||||
ContactInfo::new_localhost(&node_keypair.pubkey(), timestamp()),
|
||||
node_keypair,
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
for i in 0..10 {
|
||||
// make these invalid for the upcoming repair request
|
||||
@@ -4025,6 +4105,7 @@ mod tests {
|
||||
let cluster_info = ClusterInfo::new(
|
||||
ContactInfo::new_localhost(&node_keypair.pubkey(), timestamp()),
|
||||
node_keypair,
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
let mut range: Vec<Slot> = vec![];
|
||||
//random should be hard to compress
|
||||
@@ -4072,6 +4153,7 @@ mod tests {
|
||||
let cluster_info = Arc::new(ClusterInfo::new(
|
||||
ContactInfo::new_localhost(&node_keypair.pubkey(), timestamp()),
|
||||
node_keypair,
|
||||
SocketAddrSpace::Unspecified,
|
||||
));
|
||||
assert_eq!(cluster_info.my_shred_version(), 0);
|
||||
|
||||
@@ -4155,6 +4237,7 @@ mod tests {
|
||||
contact_info
|
||||
},
|
||||
node_keypair,
|
||||
SocketAddrSpace::Unspecified,
|
||||
));
|
||||
assert_eq!(cluster_info.my_shred_version(), 2);
|
||||
|
||||
@@ -4326,7 +4409,11 @@ mod tests {
|
||||
#[ignore] // TODO: debug why this is flaky on buildkite!
|
||||
fn test_pull_request_time_pruning() {
|
||||
let node = Node::new_localhost();
|
||||
let cluster_info = Arc::new(ClusterInfo::new_with_invalid_keypair(node.info));
|
||||
let cluster_info = Arc::new(ClusterInfo::new(
|
||||
node.info,
|
||||
Arc::new(Keypair::new()),
|
||||
SocketAddrSpace::Unspecified,
|
||||
));
|
||||
let entrypoint_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let entrypoint = ContactInfo::new_localhost(&entrypoint_pubkey, timestamp());
|
||||
cluster_info.set_entrypoint(entrypoint);
|
||||
|
@@ -7,6 +7,7 @@ use {
|
||||
signature::{Keypair, Signer},
|
||||
timing::timestamp,
|
||||
},
|
||||
solana_streamer::socket::SocketAddrSpace,
|
||||
std::net::{IpAddr, SocketAddr},
|
||||
};
|
||||
|
||||
@@ -200,16 +201,22 @@ impl ContactInfo {
|
||||
(addr.port() != 0) && Self::is_valid_ip(addr.ip())
|
||||
}
|
||||
|
||||
pub fn is_valid_address(addr: &SocketAddr) -> bool {
|
||||
Self::is_valid_tvu_address(addr) && solana_streamer::socket::is_global(addr)
|
||||
// TODO: Replace this entirely with streamer SocketAddrSpace.
|
||||
pub fn is_valid_address(addr: &SocketAddr, socket_addr_space: &SocketAddrSpace) -> bool {
|
||||
Self::is_valid_tvu_address(addr) && socket_addr_space.check(addr)
|
||||
}
|
||||
|
||||
pub fn client_facing_addr(&self) -> (SocketAddr, SocketAddr) {
|
||||
(self.rpc, self.tpu)
|
||||
}
|
||||
|
||||
pub fn valid_client_facing_addr(&self) -> Option<(SocketAddr, SocketAddr)> {
|
||||
if ContactInfo::is_valid_address(&self.rpc) && ContactInfo::is_valid_address(&self.tpu) {
|
||||
pub fn valid_client_facing_addr(
|
||||
&self,
|
||||
socket_addr_space: &SocketAddrSpace,
|
||||
) -> Option<(SocketAddr, SocketAddr)> {
|
||||
if ContactInfo::is_valid_address(&self.rpc, socket_addr_space)
|
||||
&& ContactInfo::is_valid_address(&self.tpu, socket_addr_space)
|
||||
{
|
||||
Some((self.rpc, self.tpu))
|
||||
} else {
|
||||
None
|
||||
@@ -224,13 +231,25 @@ mod tests {
|
||||
#[test]
|
||||
fn test_is_valid_address() {
|
||||
let bad_address_port = socketaddr!("127.0.0.1:0");
|
||||
assert!(!ContactInfo::is_valid_address(&bad_address_port));
|
||||
assert!(!ContactInfo::is_valid_address(
|
||||
&bad_address_port,
|
||||
&SocketAddrSpace::Unspecified
|
||||
));
|
||||
let bad_address_unspecified = socketaddr!(0, 1234);
|
||||
assert!(!ContactInfo::is_valid_address(&bad_address_unspecified));
|
||||
assert!(!ContactInfo::is_valid_address(
|
||||
&bad_address_unspecified,
|
||||
&SocketAddrSpace::Unspecified
|
||||
));
|
||||
let bad_address_multicast = socketaddr!([224, 254, 0, 0], 1234);
|
||||
assert!(!ContactInfo::is_valid_address(&bad_address_multicast));
|
||||
assert!(!ContactInfo::is_valid_address(
|
||||
&bad_address_multicast,
|
||||
&SocketAddrSpace::Unspecified
|
||||
));
|
||||
let loopback = socketaddr!("127.0.0.1:1234");
|
||||
assert!(ContactInfo::is_valid_address(&loopback));
|
||||
assert!(ContactInfo::is_valid_address(
|
||||
&loopback,
|
||||
&SocketAddrSpace::Unspecified
|
||||
));
|
||||
// assert!(!ContactInfo::is_valid_ip_internal(loopback.ip(), false));
|
||||
}
|
||||
|
||||
@@ -313,11 +332,19 @@ mod tests {
|
||||
#[test]
|
||||
fn test_valid_client_facing() {
|
||||
let mut ci = ContactInfo::default();
|
||||
assert_eq!(ci.valid_client_facing_addr(), None);
|
||||
assert_eq!(
|
||||
ci.valid_client_facing_addr(&SocketAddrSpace::Unspecified),
|
||||
None
|
||||
);
|
||||
ci.tpu = socketaddr!("127.0.0.1:123");
|
||||
assert_eq!(ci.valid_client_facing_addr(), None);
|
||||
assert_eq!(
|
||||
ci.valid_client_facing_addr(&SocketAddrSpace::Unspecified),
|
||||
None
|
||||
);
|
||||
ci.rpc = socketaddr!("127.0.0.1:234");
|
||||
assert!(ci.valid_client_facing_addr().is_some());
|
||||
assert!(ci
|
||||
.valid_client_facing_addr(&SocketAddrSpace::Unspecified)
|
||||
.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@@ -24,6 +24,7 @@ use {
|
||||
signature::{Keypair, Signer},
|
||||
timing::timestamp,
|
||||
},
|
||||
solana_streamer::socket::SocketAddrSpace,
|
||||
std::{
|
||||
collections::{HashMap, HashSet},
|
||||
net::SocketAddr,
|
||||
@@ -186,6 +187,7 @@ impl CrdsGossip {
|
||||
self_shred_version: u16,
|
||||
stakes: &HashMap<Pubkey, u64>,
|
||||
gossip_validators: Option<&HashSet<Pubkey>>,
|
||||
socket_addr_space: &SocketAddrSpace,
|
||||
) {
|
||||
self.push.refresh_push_active_set(
|
||||
&self.crds,
|
||||
@@ -195,6 +197,7 @@ impl CrdsGossip {
|
||||
self_shred_version,
|
||||
self.crds.num_nodes(),
|
||||
CRDS_GOSSIP_NUM_ACTIVE,
|
||||
socket_addr_space,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -211,6 +214,7 @@ impl CrdsGossip {
|
||||
bloom_size: usize,
|
||||
ping_cache: &Mutex<PingCache>,
|
||||
pings: &mut Vec<(SocketAddr, Ping)>,
|
||||
socket_addr_space: &SocketAddrSpace,
|
||||
) -> Result<(ContactInfo, Vec<CrdsFilter>), CrdsGossipError> {
|
||||
self.pull.new_pull_request(
|
||||
thread_pool,
|
||||
@@ -223,6 +227,7 @@ impl CrdsGossip {
|
||||
bloom_size,
|
||||
ping_cache,
|
||||
pings,
|
||||
socket_addr_space,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -243,11 +248,18 @@ impl CrdsGossip {
|
||||
|
||||
pub fn generate_pull_responses(
|
||||
&self,
|
||||
thread_pool: &ThreadPool,
|
||||
filters: &[(CrdsValue, CrdsFilter)],
|
||||
output_size_limit: usize, // Limit number of crds values returned.
|
||||
now: u64,
|
||||
) -> Vec<Vec<CrdsValue>> {
|
||||
CrdsGossipPull::generate_pull_responses(&self.crds, filters, output_size_limit, now)
|
||||
CrdsGossipPull::generate_pull_responses(
|
||||
thread_pool,
|
||||
&self.crds,
|
||||
filters,
|
||||
output_size_limit,
|
||||
now,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn filter_pull_responses(
|
||||
@@ -372,6 +384,7 @@ mod test {
|
||||
0, // shred version
|
||||
&HashMap::new(), // stakes
|
||||
None, // gossip validators
|
||||
&SocketAddrSpace::Unspecified,
|
||||
);
|
||||
let now = timestamp();
|
||||
//incorrect dest
|
||||
|
@@ -20,7 +20,6 @@ use {
|
||||
ping_pong::PingCache,
|
||||
weighted_shuffle::WeightedShuffle,
|
||||
},
|
||||
itertools::Itertools,
|
||||
lru::LruCache,
|
||||
rand::Rng,
|
||||
rayon::{prelude::*, ThreadPool},
|
||||
@@ -30,12 +29,16 @@ use {
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, Signer},
|
||||
},
|
||||
solana_streamer::socket::SocketAddrSpace,
|
||||
std::{
|
||||
collections::{HashMap, HashSet, VecDeque},
|
||||
convert::TryInto,
|
||||
iter::repeat_with,
|
||||
net::SocketAddr,
|
||||
sync::Mutex,
|
||||
sync::{
|
||||
atomic::{AtomicI64, AtomicUsize, Ordering},
|
||||
Mutex,
|
||||
},
|
||||
time::{Duration, Instant},
|
||||
},
|
||||
};
|
||||
@@ -221,6 +224,7 @@ impl CrdsGossipPull {
|
||||
bloom_size: usize,
|
||||
ping_cache: &Mutex<PingCache>,
|
||||
pings: &mut Vec<(SocketAddr, Ping)>,
|
||||
socket_addr_space: &SocketAddrSpace,
|
||||
) -> Result<(ContactInfo, Vec<CrdsFilter>), CrdsGossipError> {
|
||||
let (weights, peers): (Vec<_>, Vec<_>) = self
|
||||
.pull_options(
|
||||
@@ -230,6 +234,7 @@ impl CrdsGossipPull {
|
||||
now,
|
||||
gossip_validators,
|
||||
stakes,
|
||||
socket_addr_space,
|
||||
)
|
||||
.into_iter()
|
||||
.unzip();
|
||||
@@ -271,6 +276,7 @@ impl CrdsGossipPull {
|
||||
now: u64,
|
||||
gossip_validators: Option<&HashSet<Pubkey>>,
|
||||
stakes: &HashMap<Pubkey, u64>,
|
||||
socket_addr_space: &SocketAddrSpace,
|
||||
) -> Vec<(u64, &'a ContactInfo)> {
|
||||
let mut rng = rand::thread_rng();
|
||||
let active_cutoff = now.saturating_sub(PULL_ACTIVE_TIMEOUT_MS);
|
||||
@@ -290,7 +296,7 @@ impl CrdsGossipPull {
|
||||
})
|
||||
.filter(|v| {
|
||||
v.id != *self_id
|
||||
&& ContactInfo::is_valid_address(&v.gossip)
|
||||
&& ContactInfo::is_valid_address(&v.gossip, socket_addr_space)
|
||||
&& (self_shred_version == 0 || self_shred_version == v.shred_version)
|
||||
&& gossip_validators
|
||||
.map_or(true, |gossip_validators| gossip_validators.contains(&v.id))
|
||||
@@ -334,12 +340,13 @@ impl CrdsGossipPull {
|
||||
|
||||
/// Create gossip responses to pull requests
|
||||
pub(crate) fn generate_pull_responses(
|
||||
thread_pool: &ThreadPool,
|
||||
crds: &Crds,
|
||||
requests: &[(CrdsValue, CrdsFilter)],
|
||||
output_size_limit: usize, // Limit number of crds values returned.
|
||||
now: u64,
|
||||
) -> Vec<Vec<CrdsValue>> {
|
||||
Self::filter_crds_values(crds, requests, output_size_limit, now)
|
||||
Self::filter_crds_values(thread_pool, crds, requests, output_size_limit, now)
|
||||
}
|
||||
|
||||
// Checks if responses should be inserted and
|
||||
@@ -472,9 +479,10 @@ impl CrdsGossipPull {
|
||||
|
||||
/// filter values that fail the bloom filter up to max_bytes
|
||||
fn filter_crds_values(
|
||||
thread_pool: &ThreadPool,
|
||||
crds: &Crds,
|
||||
filters: &[(CrdsValue, CrdsFilter)],
|
||||
mut output_size_limit: usize, // Limit number of crds values returned.
|
||||
output_size_limit: usize, // Limit number of crds values returned.
|
||||
now: u64,
|
||||
) -> Vec<Vec<CrdsValue>> {
|
||||
let msg_timeout = CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS;
|
||||
@@ -482,49 +490,56 @@ impl CrdsGossipPull {
|
||||
//skip filters from callers that are too old
|
||||
let caller_wallclock_window =
|
||||
now.saturating_sub(msg_timeout)..now.saturating_add(msg_timeout);
|
||||
let mut dropped_requests = 0;
|
||||
let mut total_skipped = 0;
|
||||
let ret: Vec<_> = filters
|
||||
.iter()
|
||||
.map(|(caller, filter)| {
|
||||
if output_size_limit == 0 {
|
||||
return None;
|
||||
let dropped_requests = AtomicUsize::default();
|
||||
let total_skipped = AtomicUsize::default();
|
||||
let output_size_limit = output_size_limit.try_into().unwrap_or(i64::MAX);
|
||||
let output_size_limit = AtomicI64::new(output_size_limit);
|
||||
let apply_filter = |caller: &CrdsValue, filter: &CrdsFilter| {
|
||||
if output_size_limit.load(Ordering::Relaxed) <= 0 {
|
||||
return Vec::default();
|
||||
}
|
||||
let caller_wallclock = caller.wallclock();
|
||||
if !caller_wallclock_window.contains(&caller_wallclock) {
|
||||
dropped_requests.fetch_add(1, Ordering::Relaxed);
|
||||
return Vec::default();
|
||||
}
|
||||
let caller_pubkey = caller.pubkey();
|
||||
let caller_wallclock = caller_wallclock.checked_add(jitter).unwrap_or(0);
|
||||
let pred = |entry: &&VersionedCrdsValue| {
|
||||
debug_assert!(filter.test_mask(&entry.value_hash));
|
||||
// Skip values that are too new.
|
||||
if entry.value.wallclock() > caller_wallclock {
|
||||
total_skipped.fetch_add(1, Ordering::Relaxed);
|
||||
false
|
||||
} else {
|
||||
!filter.filter_contains(&entry.value_hash)
|
||||
&& (entry.value.pubkey() != caller_pubkey
|
||||
|| entry.value.should_force_push(&caller_pubkey))
|
||||
}
|
||||
let caller_wallclock = caller.wallclock();
|
||||
if !caller_wallclock_window.contains(&caller_wallclock) {
|
||||
dropped_requests += 1;
|
||||
return Some(vec![]);
|
||||
}
|
||||
let caller_pubkey = caller.pubkey();
|
||||
let caller_wallclock = caller_wallclock.checked_add(jitter).unwrap_or(0);
|
||||
let pred = |entry: &&VersionedCrdsValue| {
|
||||
debug_assert!(filter.test_mask(&entry.value_hash));
|
||||
// Skip values that are too new.
|
||||
if entry.value.wallclock() > caller_wallclock {
|
||||
total_skipped += 1;
|
||||
false
|
||||
} else {
|
||||
!filter.filter_contains(&entry.value_hash)
|
||||
&& (entry.value.pubkey() != caller_pubkey
|
||||
|| entry.value.should_force_push(&caller_pubkey))
|
||||
}
|
||||
};
|
||||
let out: Vec<_> = crds
|
||||
.filter_bitmask(filter.mask, filter.mask_bits)
|
||||
.filter(pred)
|
||||
.map(|entry| entry.value.clone())
|
||||
.take(output_size_limit)
|
||||
.collect();
|
||||
output_size_limit -= out.len();
|
||||
Some(out)
|
||||
})
|
||||
.while_some()
|
||||
.collect();
|
||||
};
|
||||
let out: Vec<_> = crds
|
||||
.filter_bitmask(filter.mask, filter.mask_bits)
|
||||
.filter(pred)
|
||||
.map(|entry| entry.value.clone())
|
||||
.take(output_size_limit.load(Ordering::Relaxed).max(0) as usize)
|
||||
.collect();
|
||||
output_size_limit.fetch_sub(out.len() as i64, Ordering::Relaxed);
|
||||
out
|
||||
};
|
||||
let ret: Vec<_> = thread_pool.install(|| {
|
||||
filters
|
||||
.par_iter()
|
||||
.map(|(caller, filter)| apply_filter(caller, filter))
|
||||
.collect()
|
||||
});
|
||||
inc_new_counter_info!(
|
||||
"gossip_filter_crds_values-dropped_requests",
|
||||
dropped_requests + filters.len() - ret.len()
|
||||
dropped_requests.into_inner()
|
||||
);
|
||||
inc_new_counter_info!(
|
||||
"gossip_filter_crds_values-dropped_values",
|
||||
total_skipped.into_inner()
|
||||
);
|
||||
inc_new_counter_info!("gossip_filter_crds_values-dropped_values", total_skipped);
|
||||
ret
|
||||
}
|
||||
|
||||
@@ -687,7 +702,15 @@ pub(crate) mod tests {
|
||||
stakes.insert(id, i * 100);
|
||||
}
|
||||
let now = 1024;
|
||||
let mut options = node.pull_options(&crds, &me.label().pubkey(), 0, now, None, &stakes);
|
||||
let mut options = node.pull_options(
|
||||
&crds,
|
||||
&me.label().pubkey(),
|
||||
0,
|
||||
now,
|
||||
None,
|
||||
&stakes,
|
||||
&SocketAddrSpace::Unspecified,
|
||||
);
|
||||
assert!(!options.is_empty());
|
||||
options.sort_by(|(weight_l, _), (weight_r, _)| weight_r.partial_cmp(weight_l).unwrap());
|
||||
// check that the highest stake holder is also the heaviest weighted.
|
||||
@@ -737,7 +760,15 @@ pub(crate) mod tests {
|
||||
|
||||
// shred version 123 should ignore nodes with versions 0 and 456
|
||||
let options = node
|
||||
.pull_options(&crds, &me.label().pubkey(), 123, 0, None, &stakes)
|
||||
.pull_options(
|
||||
&crds,
|
||||
&me.label().pubkey(),
|
||||
123,
|
||||
0,
|
||||
None,
|
||||
&stakes,
|
||||
&SocketAddrSpace::Unspecified,
|
||||
)
|
||||
.iter()
|
||||
.map(|(_, c)| c.id)
|
||||
.collect::<Vec<_>>();
|
||||
@@ -747,7 +778,15 @@ pub(crate) mod tests {
|
||||
|
||||
// spy nodes will see all
|
||||
let options = node
|
||||
.pull_options(&crds, &spy.label().pubkey(), 0, 0, None, &stakes)
|
||||
.pull_options(
|
||||
&crds,
|
||||
&spy.label().pubkey(),
|
||||
0,
|
||||
0,
|
||||
None,
|
||||
&stakes,
|
||||
&SocketAddrSpace::Unspecified,
|
||||
)
|
||||
.iter()
|
||||
.map(|(_, c)| c.id)
|
||||
.collect::<Vec<_>>();
|
||||
@@ -787,6 +826,7 @@ pub(crate) mod tests {
|
||||
0,
|
||||
Some(&gossip_validators),
|
||||
&stakes,
|
||||
&SocketAddrSpace::Unspecified,
|
||||
);
|
||||
assert!(options.is_empty());
|
||||
|
||||
@@ -799,6 +839,7 @@ pub(crate) mod tests {
|
||||
0,
|
||||
Some(&gossip_validators),
|
||||
&stakes,
|
||||
&SocketAddrSpace::Unspecified,
|
||||
);
|
||||
assert!(options.is_empty());
|
||||
|
||||
@@ -811,6 +852,7 @@ pub(crate) mod tests {
|
||||
0,
|
||||
Some(&gossip_validators),
|
||||
&stakes,
|
||||
&SocketAddrSpace::Unspecified,
|
||||
);
|
||||
assert_eq!(options.len(), 1);
|
||||
assert_eq!(options[0].1.id, node_123.pubkey());
|
||||
@@ -929,6 +971,7 @@ pub(crate) mod tests {
|
||||
PACKET_DATA_SIZE,
|
||||
&ping_cache,
|
||||
&mut pings,
|
||||
&SocketAddrSpace::Unspecified,
|
||||
),
|
||||
Err(CrdsGossipError::NoPeers)
|
||||
);
|
||||
@@ -946,6 +989,7 @@ pub(crate) mod tests {
|
||||
PACKET_DATA_SIZE,
|
||||
&ping_cache,
|
||||
&mut pings,
|
||||
&SocketAddrSpace::Unspecified,
|
||||
),
|
||||
Err(CrdsGossipError::NoPeers)
|
||||
);
|
||||
@@ -968,6 +1012,7 @@ pub(crate) mod tests {
|
||||
PACKET_DATA_SIZE,
|
||||
&ping_cache,
|
||||
&mut pings,
|
||||
&SocketAddrSpace::Unspecified,
|
||||
);
|
||||
let (peer, _) = req.unwrap();
|
||||
assert_eq!(peer, *new.contact_info().unwrap());
|
||||
@@ -987,6 +1032,7 @@ pub(crate) mod tests {
|
||||
PACKET_DATA_SIZE,
|
||||
&ping_cache,
|
||||
&mut pings,
|
||||
&SocketAddrSpace::Unspecified,
|
||||
);
|
||||
// Even though the offline node should have higher weight, we shouldn't request from it
|
||||
// until we receive a ping.
|
||||
@@ -1041,6 +1087,7 @@ pub(crate) mod tests {
|
||||
PACKET_DATA_SIZE, // bloom_size
|
||||
&ping_cache,
|
||||
&mut pings,
|
||||
&SocketAddrSpace::Unspecified,
|
||||
)
|
||||
.unwrap();
|
||||
peer
|
||||
@@ -1118,16 +1165,18 @@ pub(crate) mod tests {
|
||||
PACKET_DATA_SIZE,
|
||||
&Mutex::new(ping_cache),
|
||||
&mut pings,
|
||||
&SocketAddrSpace::Unspecified,
|
||||
);
|
||||
|
||||
let mut dest_crds = Crds::default();
|
||||
let (_, filters) = req.unwrap();
|
||||
let mut filters: Vec<_> = filters.into_iter().map(|f| (caller.clone(), f)).collect();
|
||||
let rsp = CrdsGossipPull::generate_pull_responses(
|
||||
&thread_pool,
|
||||
&dest_crds,
|
||||
&filters,
|
||||
/*output_size_limit=*/ usize::MAX,
|
||||
0,
|
||||
usize::MAX, // output_size_limit
|
||||
0, // now
|
||||
);
|
||||
|
||||
assert_eq!(rsp[0].len(), 0);
|
||||
@@ -1142,10 +1191,11 @@ pub(crate) mod tests {
|
||||
|
||||
//should skip new value since caller is to old
|
||||
let rsp = CrdsGossipPull::generate_pull_responses(
|
||||
&thread_pool,
|
||||
&dest_crds,
|
||||
&filters,
|
||||
/*output_size_limit=*/ usize::MAX,
|
||||
CRDS_GOSSIP_PULL_MSG_TIMEOUT_MS,
|
||||
usize::MAX, // output_size_limit
|
||||
CRDS_GOSSIP_PULL_MSG_TIMEOUT_MS, // now
|
||||
);
|
||||
assert_eq!(rsp[0].len(), 0);
|
||||
assert_eq!(filters.len(), MIN_NUM_BLOOM_FILTERS);
|
||||
@@ -1160,9 +1210,10 @@ pub(crate) mod tests {
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
let rsp = CrdsGossipPull::generate_pull_responses(
|
||||
&thread_pool,
|
||||
&dest_crds,
|
||||
&filters,
|
||||
/*output_size_limit=*/ usize::MAX,
|
||||
usize::MAX, // output_size_limit
|
||||
CRDS_GOSSIP_PULL_MSG_TIMEOUT_MS,
|
||||
);
|
||||
assert_eq!(rsp.len(), 2 * MIN_NUM_BLOOM_FILTERS);
|
||||
@@ -1205,16 +1256,18 @@ pub(crate) mod tests {
|
||||
PACKET_DATA_SIZE,
|
||||
&Mutex::new(ping_cache),
|
||||
&mut pings,
|
||||
&SocketAddrSpace::Unspecified,
|
||||
);
|
||||
|
||||
let mut dest_crds = Crds::default();
|
||||
let (_, filters) = req.unwrap();
|
||||
let filters: Vec<_> = filters.into_iter().map(|f| (caller.clone(), f)).collect();
|
||||
let rsp = CrdsGossipPull::generate_pull_responses(
|
||||
&thread_pool,
|
||||
&dest_crds,
|
||||
&filters,
|
||||
/*output_size_limit=*/ usize::MAX,
|
||||
0,
|
||||
usize::MAX, // output_size_limit
|
||||
0, // now
|
||||
);
|
||||
CrdsGossipPull::process_pull_requests(
|
||||
&mut dest_crds,
|
||||
@@ -1278,14 +1331,16 @@ pub(crate) mod tests {
|
||||
PACKET_DATA_SIZE,
|
||||
&ping_cache,
|
||||
&mut pings,
|
||||
&SocketAddrSpace::Unspecified,
|
||||
);
|
||||
let (_, filters) = req.unwrap();
|
||||
let filters: Vec<_> = filters.into_iter().map(|f| (caller.clone(), f)).collect();
|
||||
let rsp = CrdsGossipPull::generate_pull_responses(
|
||||
&thread_pool,
|
||||
&dest_crds,
|
||||
&filters,
|
||||
/*output_size_limit=*/ usize::MAX,
|
||||
0,
|
||||
usize::MAX, // output_size_limit
|
||||
0, // now
|
||||
);
|
||||
CrdsGossipPull::process_pull_requests(
|
||||
&mut dest_crds,
|
||||
|
@@ -24,6 +24,7 @@ use {
|
||||
rand::{seq::SliceRandom, Rng},
|
||||
solana_runtime::bloom::{AtomicBloom, Bloom},
|
||||
solana_sdk::{packet::PACKET_DATA_SIZE, pubkey::Pubkey, timing::timestamp},
|
||||
solana_streamer::socket::SocketAddrSpace,
|
||||
std::{
|
||||
cmp,
|
||||
collections::{HashMap, HashSet},
|
||||
@@ -263,6 +264,7 @@ impl CrdsGossipPush {
|
||||
self_shred_version: u16,
|
||||
network_size: usize,
|
||||
ratio: usize,
|
||||
socket_addr_space: &SocketAddrSpace,
|
||||
) {
|
||||
const BLOOM_FALSE_RATE: f64 = 0.1;
|
||||
const BLOOM_MAX_BITS: usize = 1024 * 8 * 4;
|
||||
@@ -274,7 +276,14 @@ impl CrdsGossipPush {
|
||||
let need = Self::compute_need(self.num_active, self.active_set.len(), ratio);
|
||||
let mut new_items = HashMap::new();
|
||||
let (weights, peers): (Vec<_>, Vec<_>) = self
|
||||
.push_options(crds, self_id, self_shred_version, stakes, gossip_validators)
|
||||
.push_options(
|
||||
crds,
|
||||
self_id,
|
||||
self_shred_version,
|
||||
stakes,
|
||||
gossip_validators,
|
||||
socket_addr_space,
|
||||
)
|
||||
.into_iter()
|
||||
.unzip();
|
||||
if peers.is_empty() {
|
||||
@@ -315,6 +324,7 @@ impl CrdsGossipPush {
|
||||
self_shred_version: u16,
|
||||
stakes: &HashMap<Pubkey, u64>,
|
||||
gossip_validators: Option<&HashSet<Pubkey>>,
|
||||
socket_addr_space: &SocketAddrSpace,
|
||||
) -> Vec<(u64, &'a ContactInfo)> {
|
||||
let now = timestamp();
|
||||
let mut rng = rand::thread_rng();
|
||||
@@ -336,7 +346,7 @@ impl CrdsGossipPush {
|
||||
})
|
||||
.filter(|info| {
|
||||
info.id != *self_id
|
||||
&& ContactInfo::is_valid_address(&info.gossip)
|
||||
&& ContactInfo::is_valid_address(&info.gossip, socket_addr_space)
|
||||
&& self_shred_version == info.shred_version
|
||||
&& gossip_validators.map_or(true, |gossip_validators| {
|
||||
gossip_validators.contains(&info.id)
|
||||
@@ -547,7 +557,16 @@ mod test {
|
||||
)));
|
||||
|
||||
assert_eq!(crds.insert(value1.clone(), now), Ok(()));
|
||||
push.refresh_push_active_set(&crds, &HashMap::new(), None, &Pubkey::default(), 0, 1, 1);
|
||||
push.refresh_push_active_set(
|
||||
&crds,
|
||||
&HashMap::new(),
|
||||
None,
|
||||
&Pubkey::default(),
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
&SocketAddrSpace::Unspecified,
|
||||
);
|
||||
|
||||
assert!(push.active_set.get(&value1.label().pubkey()).is_some());
|
||||
let value2 = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost(
|
||||
@@ -557,7 +576,16 @@ mod test {
|
||||
assert!(push.active_set.get(&value2.label().pubkey()).is_none());
|
||||
assert_eq!(crds.insert(value2.clone(), now), Ok(()));
|
||||
for _ in 0..30 {
|
||||
push.refresh_push_active_set(&crds, &HashMap::new(), None, &Pubkey::default(), 0, 1, 1);
|
||||
push.refresh_push_active_set(
|
||||
&crds,
|
||||
&HashMap::new(),
|
||||
None,
|
||||
&Pubkey::default(),
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
&SocketAddrSpace::Unspecified,
|
||||
);
|
||||
if push.active_set.get(&value2.label().pubkey()).is_some() {
|
||||
break;
|
||||
}
|
||||
@@ -570,7 +598,16 @@ mod test {
|
||||
));
|
||||
assert_eq!(crds.insert(value2.clone(), now), Ok(()));
|
||||
}
|
||||
push.refresh_push_active_set(&crds, &HashMap::new(), None, &Pubkey::default(), 0, 1, 1);
|
||||
push.refresh_push_active_set(
|
||||
&crds,
|
||||
&HashMap::new(),
|
||||
None,
|
||||
&Pubkey::default(),
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
&SocketAddrSpace::Unspecified,
|
||||
);
|
||||
assert_eq!(push.active_set.len(), push.num_active);
|
||||
}
|
||||
#[test]
|
||||
@@ -590,7 +627,14 @@ mod test {
|
||||
stakes.insert(id, i * 100);
|
||||
push.last_pushed_to.put(id, time);
|
||||
}
|
||||
let mut options = push.push_options(&crds, &Pubkey::default(), 0, &stakes, None);
|
||||
let mut options = push.push_options(
|
||||
&crds,
|
||||
&Pubkey::default(),
|
||||
0,
|
||||
&stakes,
|
||||
None,
|
||||
&SocketAddrSpace::Unspecified,
|
||||
);
|
||||
assert!(!options.is_empty());
|
||||
options.sort_by(|(weight_l, _), (weight_r, _)| weight_r.partial_cmp(weight_l).unwrap());
|
||||
// check that the highest stake holder is also the heaviest weighted.
|
||||
@@ -641,7 +685,14 @@ mod test {
|
||||
|
||||
// shred version 123 should ignore nodes with versions 0 and 456
|
||||
let options = node
|
||||
.push_options(&crds, &me.label().pubkey(), 123, &stakes, None)
|
||||
.push_options(
|
||||
&crds,
|
||||
&me.label().pubkey(),
|
||||
123,
|
||||
&stakes,
|
||||
None,
|
||||
&SocketAddrSpace::Unspecified,
|
||||
)
|
||||
.iter()
|
||||
.map(|(_, c)| c.id)
|
||||
.collect::<Vec<_>>();
|
||||
@@ -651,7 +702,14 @@ mod test {
|
||||
|
||||
// spy nodes should not push to people on different shred versions
|
||||
let options = node
|
||||
.push_options(&crds, &spy.label().pubkey(), 0, &stakes, None)
|
||||
.push_options(
|
||||
&crds,
|
||||
&spy.label().pubkey(),
|
||||
0,
|
||||
&stakes,
|
||||
None,
|
||||
&SocketAddrSpace::Unspecified,
|
||||
)
|
||||
.iter()
|
||||
.map(|(_, c)| c.id)
|
||||
.collect::<Vec<_>>();
|
||||
@@ -688,6 +746,7 @@ mod test {
|
||||
0,
|
||||
&stakes,
|
||||
Some(&gossip_validators),
|
||||
&SocketAddrSpace::Unspecified,
|
||||
);
|
||||
|
||||
assert!(options.is_empty());
|
||||
@@ -700,6 +759,7 @@ mod test {
|
||||
0,
|
||||
&stakes,
|
||||
Some(&gossip_validators),
|
||||
&SocketAddrSpace::Unspecified,
|
||||
);
|
||||
assert!(options.is_empty());
|
||||
|
||||
@@ -711,6 +771,7 @@ mod test {
|
||||
0,
|
||||
&stakes,
|
||||
Some(&gossip_validators),
|
||||
&SocketAddrSpace::Unspecified,
|
||||
);
|
||||
|
||||
assert_eq!(options.len(), 1);
|
||||
@@ -727,7 +788,16 @@ mod test {
|
||||
0,
|
||||
)));
|
||||
assert_eq!(crds.insert(peer.clone(), now), Ok(()));
|
||||
push.refresh_push_active_set(&crds, &HashMap::new(), None, &Pubkey::default(), 0, 1, 1);
|
||||
push.refresh_push_active_set(
|
||||
&crds,
|
||||
&HashMap::new(),
|
||||
None,
|
||||
&Pubkey::default(),
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
&SocketAddrSpace::Unspecified,
|
||||
);
|
||||
|
||||
let new_msg = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost(
|
||||
&solana_sdk::pubkey::new_rand(),
|
||||
@@ -762,7 +832,16 @@ mod test {
|
||||
push.process_push_message(&mut crds, &Pubkey::default(), peers[2].clone(), now),
|
||||
Ok(())
|
||||
);
|
||||
push.refresh_push_active_set(&crds, &HashMap::new(), None, &Pubkey::default(), 0, 1, 1);
|
||||
push.refresh_push_active_set(
|
||||
&crds,
|
||||
&HashMap::new(),
|
||||
None,
|
||||
&Pubkey::default(),
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
&SocketAddrSpace::Unspecified,
|
||||
);
|
||||
|
||||
// push 3's contact info to 1 and 2 and 3
|
||||
let expected: HashMap<_, _> = vec![
|
||||
@@ -784,7 +863,16 @@ mod test {
|
||||
0,
|
||||
)));
|
||||
assert_eq!(crds.insert(peer.clone(), 0), Ok(()));
|
||||
push.refresh_push_active_set(&crds, &HashMap::new(), None, &Pubkey::default(), 0, 1, 1);
|
||||
push.refresh_push_active_set(
|
||||
&crds,
|
||||
&HashMap::new(),
|
||||
None,
|
||||
&Pubkey::default(),
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
&SocketAddrSpace::Unspecified,
|
||||
);
|
||||
|
||||
let new_msg = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost(
|
||||
&solana_sdk::pubkey::new_rand(),
|
||||
@@ -811,7 +899,16 @@ mod test {
|
||||
0,
|
||||
)));
|
||||
assert_eq!(crds.insert(peer, 0), Ok(()));
|
||||
push.refresh_push_active_set(&crds, &HashMap::new(), None, &Pubkey::default(), 0, 1, 1);
|
||||
push.refresh_push_active_set(
|
||||
&crds,
|
||||
&HashMap::new(),
|
||||
None,
|
||||
&Pubkey::default(),
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
&SocketAddrSpace::Unspecified,
|
||||
);
|
||||
|
||||
let mut ci = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0);
|
||||
ci.wallclock = 1;
|
||||
|
@@ -13,6 +13,7 @@ use {
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, Signer},
|
||||
},
|
||||
solana_streamer::socket::SocketAddrSpace,
|
||||
solana_streamer::streamer,
|
||||
std::{
|
||||
collections::HashSet,
|
||||
@@ -47,6 +48,7 @@ impl GossipService {
|
||||
&cluster_info.id(),
|
||||
gossip_socket.local_addr().unwrap()
|
||||
);
|
||||
let socket_addr_space = *cluster_info.socket_addr_space();
|
||||
let t_receiver = streamer::receiver(
|
||||
gossip_socket.clone(),
|
||||
exit,
|
||||
@@ -56,33 +58,38 @@ impl GossipService {
|
||||
1,
|
||||
false,
|
||||
);
|
||||
let (response_sender, response_receiver) = channel();
|
||||
let (consume_sender, listen_receiver) = channel();
|
||||
// https://github.com/rust-lang/rust/issues/39364#issuecomment-634545136
|
||||
let _consume_sender = consume_sender.clone();
|
||||
let t_socket_consume = cluster_info.clone().start_socket_consume_thread(
|
||||
request_receiver,
|
||||
consume_sender,
|
||||
exit.clone(),
|
||||
);
|
||||
let t_listen = ClusterInfo::listen(
|
||||
cluster_info.clone(),
|
||||
let (response_sender, response_receiver) = channel();
|
||||
let t_listen = cluster_info.clone().listen(
|
||||
bank_forks.clone(),
|
||||
listen_receiver,
|
||||
response_sender.clone(),
|
||||
should_check_duplicate_instance,
|
||||
exit,
|
||||
exit.clone(),
|
||||
);
|
||||
let t_gossip = ClusterInfo::gossip(
|
||||
cluster_info.clone(),
|
||||
let t_gossip = cluster_info.clone().gossip(
|
||||
bank_forks,
|
||||
response_sender,
|
||||
gossip_validators,
|
||||
exit,
|
||||
exit.clone(),
|
||||
);
|
||||
// To work around:
|
||||
// https://github.com/rust-lang/rust/issues/54267
|
||||
// responder thread should start after response_sender.clone(). see:
|
||||
// https://github.com/rust-lang/rust/issues/39364#issuecomment-381446873
|
||||
let t_responder = streamer::responder("gossip", gossip_socket, response_receiver);
|
||||
let t_responder = streamer::responder(
|
||||
"gossip",
|
||||
gossip_socket,
|
||||
response_receiver,
|
||||
socket_addr_space,
|
||||
);
|
||||
let thread_hdls = vec![
|
||||
t_receiver,
|
||||
t_responder,
|
||||
@@ -105,6 +112,7 @@ impl GossipService {
|
||||
pub fn discover_cluster(
|
||||
entrypoint: &SocketAddr,
|
||||
num_nodes: usize,
|
||||
socket_addr_space: SocketAddrSpace,
|
||||
) -> std::io::Result<Vec<ContactInfo>> {
|
||||
const DISCOVER_CLUSTER_TIMEOUT: Duration = Duration::from_secs(120);
|
||||
let (_all_peers, validators) = discover(
|
||||
@@ -116,6 +124,7 @@ pub fn discover_cluster(
|
||||
None, // find_node_by_gossip_addr
|
||||
None, // my_gossip_addr
|
||||
0, // my_shred_version
|
||||
socket_addr_space,
|
||||
)?;
|
||||
Ok(validators)
|
||||
}
|
||||
@@ -129,6 +138,7 @@ pub fn discover(
|
||||
find_node_by_gossip_addr: Option<&SocketAddr>,
|
||||
my_gossip_addr: Option<&SocketAddr>,
|
||||
my_shred_version: u16,
|
||||
socket_addr_space: SocketAddrSpace,
|
||||
) -> std::io::Result<(
|
||||
Vec<ContactInfo>, // all gossip peers
|
||||
Vec<ContactInfo>, // tvu peers (validators)
|
||||
@@ -143,6 +153,7 @@ pub fn discover(
|
||||
my_gossip_addr,
|
||||
my_shred_version,
|
||||
true, // should_check_duplicate_instance,
|
||||
socket_addr_space,
|
||||
);
|
||||
|
||||
let id = spy_ref.id();
|
||||
@@ -189,28 +200,31 @@ pub fn discover(
|
||||
}
|
||||
|
||||
/// Creates a ThinClient per valid node
|
||||
pub fn get_clients(nodes: &[ContactInfo]) -> Vec<ThinClient> {
|
||||
pub fn get_clients(nodes: &[ContactInfo], socket_addr_space: &SocketAddrSpace) -> Vec<ThinClient> {
|
||||
nodes
|
||||
.iter()
|
||||
.filter_map(ContactInfo::valid_client_facing_addr)
|
||||
.filter_map(|node| ContactInfo::valid_client_facing_addr(node, socket_addr_space))
|
||||
.map(|addrs| create_client(addrs, VALIDATOR_PORT_RANGE))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Creates a ThinClient by selecting a valid node at random
|
||||
pub fn get_client(nodes: &[ContactInfo]) -> ThinClient {
|
||||
pub fn get_client(nodes: &[ContactInfo], socket_addr_space: &SocketAddrSpace) -> ThinClient {
|
||||
let nodes: Vec<_> = nodes
|
||||
.iter()
|
||||
.filter_map(ContactInfo::valid_client_facing_addr)
|
||||
.filter_map(|node| ContactInfo::valid_client_facing_addr(node, socket_addr_space))
|
||||
.collect();
|
||||
let select = thread_rng().gen_range(0, nodes.len());
|
||||
create_client(nodes[select], VALIDATOR_PORT_RANGE)
|
||||
}
|
||||
|
||||
pub fn get_multi_client(nodes: &[ContactInfo]) -> (ThinClient, usize) {
|
||||
pub fn get_multi_client(
|
||||
nodes: &[ContactInfo],
|
||||
socket_addr_space: &SocketAddrSpace,
|
||||
) -> (ThinClient, usize) {
|
||||
let addrs: Vec<_> = nodes
|
||||
.iter()
|
||||
.filter_map(ContactInfo::valid_client_facing_addr)
|
||||
.filter_map(|node| ContactInfo::valid_client_facing_addr(node, socket_addr_space))
|
||||
.collect();
|
||||
let rpc_addrs: Vec<_> = addrs.iter().map(|addr| addr.0).collect();
|
||||
let tpu_addrs: Vec<_> = addrs.iter().map(|addr| addr.1).collect();
|
||||
@@ -301,13 +315,14 @@ fn make_gossip_node(
|
||||
gossip_addr: Option<&SocketAddr>,
|
||||
shred_version: u16,
|
||||
should_check_duplicate_instance: bool,
|
||||
socket_addr_space: SocketAddrSpace,
|
||||
) -> (GossipService, Option<TcpListener>, Arc<ClusterInfo>) {
|
||||
let (node, gossip_socket, ip_echo) = if let Some(gossip_addr) = gossip_addr {
|
||||
ClusterInfo::gossip_node(&keypair.pubkey(), gossip_addr, shred_version)
|
||||
} else {
|
||||
ClusterInfo::spy_node(&keypair.pubkey(), shred_version)
|
||||
};
|
||||
let cluster_info = ClusterInfo::new(node, keypair);
|
||||
let cluster_info = ClusterInfo::new(node, keypair, socket_addr_space);
|
||||
if let Some(entrypoint) = entrypoint {
|
||||
cluster_info.set_entrypoint(ContactInfo::new_gossip_entry_point(entrypoint));
|
||||
}
|
||||
@@ -337,7 +352,11 @@ mod tests {
|
||||
fn test_exit() {
|
||||
let exit = Arc::new(AtomicBool::new(false));
|
||||
let tn = Node::new_localhost();
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(tn.info.clone());
|
||||
let cluster_info = ClusterInfo::new(
|
||||
tn.info.clone(),
|
||||
Arc::new(Keypair::new()),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
let c = Arc::new(cluster_info);
|
||||
let d = GossipService::new(
|
||||
&c,
|
||||
@@ -360,7 +379,11 @@ mod tests {
|
||||
let contact_info = ContactInfo::new_localhost(&keypair.pubkey(), 0);
|
||||
let peer0_info = ContactInfo::new_localhost(&peer0, 0);
|
||||
let peer1_info = ContactInfo::new_localhost(&peer1, 0);
|
||||
let cluster_info = ClusterInfo::new(contact_info, Arc::new(keypair));
|
||||
let cluster_info = ClusterInfo::new(
|
||||
contact_info,
|
||||
Arc::new(keypair),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
cluster_info.insert_info(peer0_info.clone());
|
||||
cluster_info.insert_info(peer1_info);
|
||||
|
||||
|
@@ -11,6 +11,7 @@ use {
|
||||
},
|
||||
solana_gossip::{contact_info::ContactInfo, gossip_service::discover},
|
||||
solana_sdk::pubkey::Pubkey,
|
||||
solana_streamer::socket::SocketAddrSpace,
|
||||
std::{
|
||||
error,
|
||||
net::{IpAddr, Ipv4Addr, SocketAddr},
|
||||
@@ -32,6 +33,13 @@ fn parse_matches() -> ArgMatches<'static> {
|
||||
.about(crate_description!())
|
||||
.version(solana_version::version!())
|
||||
.setting(AppSettings::SubcommandRequiredElseHelp)
|
||||
.arg(
|
||||
Arg::with_name("allow_private_addr")
|
||||
.long("allow-private-addr")
|
||||
.takes_value(false)
|
||||
.help("Allow contacting private ip addresses")
|
||||
.hidden(true),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("rpc-url")
|
||||
.about("Get an RPC URL for the cluster")
|
||||
@@ -224,6 +232,7 @@ fn process_spy(matches: &ArgMatches) -> std::io::Result<()> {
|
||||
let pubkey = matches
|
||||
.value_of("node_pubkey")
|
||||
.map(|pubkey_str| pubkey_str.parse::<Pubkey>().unwrap());
|
||||
let socket_addr_space = SocketAddrSpace::new(matches.is_present("allow_private_addr"));
|
||||
let shred_version = value_t_or_exit!(matches, "shred_version", u16);
|
||||
let identity_keypair = keypair_of(matches, "identity").map(Arc::new);
|
||||
|
||||
@@ -251,6 +260,7 @@ fn process_spy(matches: &ArgMatches) -> std::io::Result<()> {
|
||||
None, // find_node_by_gossip_addr
|
||||
Some(&gossip_addr), // my_gossip_addr
|
||||
shred_version,
|
||||
socket_addr_space,
|
||||
)?;
|
||||
|
||||
process_spy_results(timeout, validators, num_nodes, num_nodes_exactly, pubkey);
|
||||
@@ -273,6 +283,7 @@ fn process_rpc_url(matches: &ArgMatches) -> std::io::Result<()> {
|
||||
let entrypoint_addr = parse_entrypoint(matches);
|
||||
let timeout = value_t_or_exit!(matches, "timeout", u64);
|
||||
let shred_version = value_t_or_exit!(matches, "shred_version", u16);
|
||||
let socket_addr_space = SocketAddrSpace::new(matches.is_present("allow_private_addr"));
|
||||
let (_all_peers, validators) = discover(
|
||||
None, // keypair
|
||||
entrypoint_addr.as_ref(),
|
||||
@@ -282,13 +293,14 @@ fn process_rpc_url(matches: &ArgMatches) -> std::io::Result<()> {
|
||||
entrypoint_addr.as_ref(), // find_node_by_gossip_addr
|
||||
None, // my_gossip_addr
|
||||
shred_version,
|
||||
socket_addr_space,
|
||||
)?;
|
||||
|
||||
let rpc_addrs: Vec<_> = validators
|
||||
.iter()
|
||||
.filter_map(|contact_info| {
|
||||
if (any || all || Some(contact_info.gossip) == entrypoint_addr)
|
||||
&& ContactInfo::is_valid_address(&contact_info.rpc)
|
||||
&& ContactInfo::is_valid_address(&contact_info.rpc, &socket_addr_space)
|
||||
{
|
||||
return Some(contact_info.rpc);
|
||||
}
|
||||
|
@@ -7,7 +7,8 @@ use {
|
||||
contact_info::ContactInfo,
|
||||
deprecated::{shuffle_peers_and_index, sorted_retransmit_peers_and_stakes},
|
||||
},
|
||||
solana_sdk::pubkey::Pubkey,
|
||||
solana_sdk::{pubkey::Pubkey, signer::keypair::Keypair},
|
||||
solana_streamer::socket::SocketAddrSpace,
|
||||
std::{
|
||||
collections::{HashMap, HashSet},
|
||||
sync::{
|
||||
@@ -79,7 +80,11 @@ fn run_simulation(stakes: &[u64], fanout: usize) {
|
||||
|
||||
// describe the leader
|
||||
let leader_info = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0);
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(leader_info.clone());
|
||||
let cluster_info = ClusterInfo::new(
|
||||
leader_info.clone(),
|
||||
Arc::new(Keypair::new()),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
|
||||
// setup staked nodes
|
||||
let mut staked_nodes = HashMap::new();
|
||||
|
@@ -21,6 +21,7 @@ use {
|
||||
signature::{Keypair, Signer},
|
||||
timing::timestamp,
|
||||
},
|
||||
solana_streamer::socket::SocketAddrSpace,
|
||||
std::{
|
||||
collections::{HashMap, HashSet},
|
||||
ops::Deref,
|
||||
@@ -252,6 +253,7 @@ fn network_simulator(thread_pool: &ThreadPool, network: &mut Network, max_conver
|
||||
0, // shred version
|
||||
&HashMap::new(), // stakes
|
||||
None, // gossip validators
|
||||
&SocketAddrSpace::Unspecified,
|
||||
);
|
||||
});
|
||||
let mut total_bytes = bytes_tx;
|
||||
@@ -415,6 +417,7 @@ fn network_run_push(
|
||||
0, // shred version
|
||||
&HashMap::new(), // stakes
|
||||
None, // gossip validators
|
||||
&SocketAddrSpace::Unspecified,
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -489,6 +492,7 @@ fn network_run_pull(
|
||||
cluster_info::MAX_BLOOM_SIZE,
|
||||
from.ping_cache.deref(),
|
||||
&mut pings,
|
||||
&SocketAddrSpace::Unspecified,
|
||||
)
|
||||
.ok()?;
|
||||
let from_pubkey = from.keypair.pubkey();
|
||||
@@ -500,7 +504,7 @@ fn network_run_pull(
|
||||
.collect()
|
||||
};
|
||||
let transfered: Vec<_> = requests
|
||||
.into_par_iter()
|
||||
.into_iter()
|
||||
.map(|(to, filters, caller_info)| {
|
||||
let mut bytes: usize = 0;
|
||||
let mut msgs: usize = 0;
|
||||
@@ -523,8 +527,9 @@ fn network_run_pull(
|
||||
.lock()
|
||||
.unwrap()
|
||||
.generate_pull_responses(
|
||||
thread_pool,
|
||||
&filters,
|
||||
/*output_size_limit=*/ usize::MAX,
|
||||
usize::MAX, // output_size_limit
|
||||
now,
|
||||
)
|
||||
.into_iter()
|
||||
@@ -708,6 +713,7 @@ fn test_prune_errors() {
|
||||
0, // shred version
|
||||
&HashMap::new(), // stakes
|
||||
None, // gossip validators
|
||||
&SocketAddrSpace::Unspecified,
|
||||
);
|
||||
let now = timestamp();
|
||||
//incorrect dest
|
||||
|
@@ -18,6 +18,7 @@ use {
|
||||
timing::timestamp,
|
||||
transaction::Transaction,
|
||||
},
|
||||
solana_streamer::socket::SocketAddrSpace,
|
||||
solana_vote_program::{vote_instruction, vote_state::Vote},
|
||||
std::{
|
||||
net::UdpSocket,
|
||||
@@ -33,7 +34,11 @@ use {
|
||||
fn test_node(exit: &Arc<AtomicBool>) -> (Arc<ClusterInfo>, GossipService, UdpSocket) {
|
||||
let keypair = Arc::new(Keypair::new());
|
||||
let mut test_node = Node::new_localhost_with_pubkey(&keypair.pubkey());
|
||||
let cluster_info = Arc::new(ClusterInfo::new(test_node.info.clone(), keypair));
|
||||
let cluster_info = Arc::new(ClusterInfo::new(
|
||||
test_node.info.clone(),
|
||||
keypair,
|
||||
SocketAddrSpace::Unspecified,
|
||||
));
|
||||
let gossip_service = GossipService::new(
|
||||
&cluster_info,
|
||||
None,
|
||||
@@ -56,7 +61,11 @@ fn test_node_with_bank(
|
||||
bank_forks: Arc<RwLock<BankForks>>,
|
||||
) -> (Arc<ClusterInfo>, GossipService, UdpSocket) {
|
||||
let mut test_node = Node::new_localhost_with_pubkey(&node_keypair.pubkey());
|
||||
let cluster_info = Arc::new(ClusterInfo::new(test_node.info.clone(), node_keypair));
|
||||
let cluster_info = Arc::new(ClusterInfo::new(
|
||||
test_node.info.clone(),
|
||||
node_keypair,
|
||||
SocketAddrSpace::Unspecified,
|
||||
));
|
||||
let gossip_service = GossipService::new(
|
||||
&cluster_info,
|
||||
Some(bank_forks),
|
||||
@@ -209,7 +218,13 @@ pub fn cluster_info_retransmit() {
|
||||
p.meta.size = 10;
|
||||
let peers = c1.tvu_peers();
|
||||
let retransmit_peers: Vec<_> = peers.iter().collect();
|
||||
ClusterInfo::retransmit_to(&retransmit_peers, &p, &tn1, false);
|
||||
ClusterInfo::retransmit_to(
|
||||
&retransmit_peers,
|
||||
&p,
|
||||
&tn1,
|
||||
false,
|
||||
&SocketAddrSpace::Unspecified,
|
||||
);
|
||||
let res: Vec<_> = [tn1, tn2, tn3]
|
||||
.into_par_iter()
|
||||
.map(|s| {
|
||||
|
@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-install"
|
||||
description = "The solana cluster software installer"
|
||||
version = "1.7.6"
|
||||
version = "1.7.9"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -25,12 +25,12 @@ reqwest = { version = "0.11.2", default-features = false, features = ["blocking"
|
||||
serde = { version = "1.0.122", features = ["derive"] }
|
||||
serde_json = "1.0.62"
|
||||
serde_yaml = "0.8.13"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.6" }
|
||||
solana-client = { path = "../client", version = "=1.7.6" }
|
||||
solana-config-program = { path = "../programs/config", version = "=1.7.6" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.6" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.6" }
|
||||
solana-version = { path = "../version", version = "=1.7.6" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.9" }
|
||||
solana-client = { path = "../client", version = "=1.7.9" }
|
||||
solana-config-program = { path = "../programs/config", version = "=1.7.9" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.9" }
|
||||
solana-version = { path = "../version", version = "=1.7.9" }
|
||||
semver = "0.9.0"
|
||||
tar = "0.4.28"
|
||||
tempfile = "3.1.0"
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-keygen"
|
||||
version = "1.7.6"
|
||||
version = "1.7.9"
|
||||
description = "Solana key generation utility"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -14,11 +14,11 @@ bs58 = "0.3.1"
|
||||
clap = "2.33"
|
||||
dirs-next = "2.0.0"
|
||||
num_cpus = "1.13.0"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.6" }
|
||||
solana-cli-config = { path = "../cli-config", version = "=1.7.6" }
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "=1.7.6" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.6" }
|
||||
solana-version = { path = "../version", version = "=1.7.6" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.9" }
|
||||
solana-cli-config = { path = "../cli-config", version = "=1.7.9" }
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "=1.7.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.9" }
|
||||
solana-version = { path = "../version", version = "=1.7.9" }
|
||||
tiny-bip39 = "0.7.0"
|
||||
|
||||
[[bin]]
|
||||
|
@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-ledger-tool"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.7.6"
|
||||
version = "1.7.9"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -23,18 +23,18 @@ regex = "1"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0.56"
|
||||
serde_yaml = "0.8.13"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.6" }
|
||||
solana-cli-output = { path = "../cli-output", version = "=1.7.6" }
|
||||
solana-ledger = { path = "../ledger", version = "=1.7.6" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.6" }
|
||||
solana-measure = { path = "../measure", version = "=1.7.6" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.6" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.6" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "=1.7.6" }
|
||||
solana-storage-bigtable = { path = "../storage-bigtable", version = "=1.7.6" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.7.6" }
|
||||
solana-version = { path = "../version", version = "=1.7.6" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.7.6" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.9" }
|
||||
solana-cli-output = { path = "../cli-output", version = "=1.7.9" }
|
||||
solana-ledger = { path = "../ledger", version = "=1.7.9" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.9" }
|
||||
solana-measure = { path = "../measure", version = "=1.7.9" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.9" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.9" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "=1.7.9" }
|
||||
solana-storage-bigtable = { path = "../storage-bigtable", version = "=1.7.9" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.7.9" }
|
||||
solana-version = { path = "../version", version = "=1.7.9" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.7.9" }
|
||||
tempfile = "3.1.0"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
|
||||
|
@@ -385,18 +385,7 @@ pub fn bigtable_process_command(ledger_path: &Path, matches: &ArgMatches<'_>) {
|
||||
let runtime = tokio::runtime::Runtime::new().unwrap();
|
||||
|
||||
let verbose = matches.is_present("verbose");
|
||||
let output_format = matches
|
||||
.value_of("output_format")
|
||||
.map(|value| match value {
|
||||
"json" => OutputFormat::Json,
|
||||
"json-compact" => OutputFormat::JsonCompact,
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.unwrap_or(if verbose {
|
||||
OutputFormat::DisplayVerbose
|
||||
} else {
|
||||
OutputFormat::Display
|
||||
});
|
||||
let output_format = OutputFormat::from_matches(matches, "output_format", verbose);
|
||||
|
||||
let future = match matches.subcommand() {
|
||||
("upload", Some(arg_matches)) => {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user