Compare commits

...

33 Commits

Author SHA1 Message Date
mergify[bot]
0707290bbf Fix serialization of parameters in RpcClient::get_block_production_with_config (backport #18998) (#19000)
* Fix serialization of parameters in RpcClient::get_block_production_with_config (#18998)

Params must be an array or null.

(cherry picked from commit 58f395257b)

# Conflicts:
#	client/src/mock_sender.rs

* Fix conflict

Co-authored-by: Brian Anderson <andersrb@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2021-07-31 02:00:32 +00:00
mergify[bot]
8934a3961f Add get_clock to BanksClient (#18591)
(cherry picked from commit d30a36641e)

Co-authored-by: Kirill Fomichev <fanatid@ya.ru>
2021-07-30 18:29:42 +00:00
mergify[bot]
b142ef5f8b Improve target CPU feature checks (backport #18940) (#18958)
* Improve check for Apple M1 silicon under Rosetta

(cherry picked from commit 59641623d1)

# Conflicts:
#	core/src/validator.rs

* test-validator: start logging asap

(cherry picked from commit ee65ffb3c8)

* test-validator: move feature check earlier in startup

(cherry picked from commit e641f257ef)

* validator: add avx2 runtime check

(cherry picked from commit c435f7b3e3)

* validator: start logging asap

(cherry picked from commit ed8285c096)

* validator: check target CPU features earlier

(cherry picked from commit 8ed0cd0fff)

* validator: remove disused cuda config argument

(cherry picked from commit 71f6d839f9)

Co-authored-by: Trent Nelson <trent@solana.com>
2021-07-30 06:47:22 +00:00
mergify[bot]
f2dc9dd96e Fix unstable retransmit-num_nodes (#18970) (#18974)
(cherry picked from commit da480bdb5f)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2021-07-30 10:52:37 +09:00
mergify[bot]
20ad3005b5 allows private addresses by default in test-validator (backport #18976) (#18980)
* allows private addresses by default in test-validator (#18976)

(cherry picked from commit 1cef6fd4b4)

# Conflicts:
#	validator/src/bin/solana-test-validator.rs

* removes backport merge conflicts

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2021-07-30 00:05:19 +00:00
mergify[bot]
eacc69efba adds validator flag to allow private ip addresses (backport #18850) (#18975)
* adds validator flag to allow private ip addresses (#18850)

(cherry picked from commit d2d5f36a3c)

# Conflicts:
#	accounts-cluster-bench/Cargo.toml
#	bench-tps/Cargo.toml
#	cli/Cargo.toml
#	core/benches/cluster_info.rs
#	core/src/banking_stage.rs
#	core/src/broadcast_stage.rs
#	core/src/broadcast_stage/broadcast_duplicates_run.rs
#	core/src/broadcast_stage/fail_entry_verification_broadcast_run.rs
#	core/src/broadcast_stage/standard_broadcast_run.rs
#	core/src/cluster_slots_service.rs
#	core/src/repair_service.rs
#	core/src/tvu.rs
#	core/src/validator.rs
#	dos/Cargo.toml
#	gossip/src/cluster_info.rs
#	gossip/src/crds_gossip_pull.rs
#	gossip/src/crds_gossip_push.rs
#	gossip/src/gossip_service.rs
#	local-cluster/Cargo.toml
#	local-cluster/src/cluster_tests.rs
#	local-cluster/tests/local_cluster.rs
#	rpc/Cargo.toml
#	rpc/src/rpc.rs
#	tokens/Cargo.toml
#	validator/Cargo.toml
#	validator/src/main.rs

* removes backport merge conflicts

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2021-07-29 21:43:24 +00:00
mergify[bot]
3c200ae45a Tiny typo fix (#18978) (#18979)
(cherry picked from commit 46fdf8a4d2)

Co-authored-by: NearlyAlwaysThere <87867628+NearlyAlwaysThere@users.noreply.github.com>
2021-07-29 14:22:10 -06:00
mergify[bot]
74e7c7cbd0 Cli: app and wallet command reorg (backport #18955) (#18959)
* Cli: app and wallet command reorg (#18955)

* Clean up wallet commands

* Move global args

(cherry picked from commit 662ccfa558)

# Conflicts:
#	cli/src/cli.rs

* Fix conflicts

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2021-07-29 09:46:48 -06:00
mergify[bot]
6bd6d6212c Fix erroneous default start_slot (#18948) (#18952)
(cherry picked from commit 578f2aa22b)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-07-29 00:11:35 +00:00
mergify[bot]
4d2e66cf9c Added missing method to index and updated token account methods docs. (#18906) (#18954)
(cherry picked from commit 4d0cd9b283)

Co-authored-by: hugo <murlux@protonmail.com>
2021-07-28 22:35:55 +00:00
mergify[bot]
65fe00b7ad Auto-generate shell completions for solana-cli (issue #8879 and #14005) (#18931) (#18945)
* Auto-generate shell completions for solana-cli (issue #8879 and #14005)

Implement `completion` SubCommand for solana-cli, which outputs
completion script to stdout and exits the process. The script generation
handled completely by clap.

In order to implement the generation, one minor design change was
necessary regarding the creation of clap `App`.

Previously: One part of App initialization was in the `app` function,
and some other arguments and subcommands were added later directly in
the `main` function.

Now: The whole construction of App was moved to `get_clap_app` function.

P.S. I wasn't sure if constructing App separately had visual importance,
so both constructing parts are still separate in `base_clap_app` and
`final_clap_app` functions. But they sure could be in one single
function.

* Dereplicode match expr, fix clippy warning.

* Move clap App construction into separate module

Also join two parts of the construction into a single function

* Fix tests

* Apply rustfmt lints

(cherry picked from commit 9d0a937a05)

Co-authored-by: theonekeyg <34949189+theonekeyg@users.noreply.github.com>
2021-07-28 17:26:19 +00:00
mergify[bot]
02c509390a Add cli-output helpers (backport #18933) (#18935)
* Add cli-output helpers (#18933)

* Add OutputFormat helper to reduce copy-pasta

* Add CliSignOnlyData constructor

(cherry picked from commit 467c18e0d1)

# Conflicts:
#	Cargo.lock
#	programs/bpf/Cargo.lock

* Fix conflicts

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2021-07-28 15:25:56 +00:00
mergify[bot]
49b0d1792b filters crds values in parallel when responding to gossip pull-requests (backport #18877) (#18901)
* filters crds values in parallel when responding to gossip pull-requests (#18877)

When responding to gossip pull-requests, filter_crds_values takes a lot of time
while holding onto read-lock:
https://github.com/solana-labs/solana/blob/f51d64868/gossip/src/crds_gossip_pull.rs#L509-L566

This commit will filter-crds-values in parallel using rayon thread-pools.

(cherry picked from commit f1198fc6d5)

# Conflicts:
#	gossip/src/cluster_info.rs
#	gossip/src/crds_gossip_pull.rs

* removes backport merge conflicts

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2021-07-26 21:30:24 +00:00
Tyera Eulberg
9511031490 v1.7: backport new column families from master (#18897)
* backport new column families from master to 1.6 (#18743)

* backporting bank_hash and program_costs column families from master to 1.6 for rocksdb backward compatibility

* missed a line to allow dead code

* include code for purge

* Exclude stubbed ProgramCosts column from compaction (#18840)

Co-authored-by: Tao Zhu <82401714+taozhu-chicago@users.noreply.github.com>
2021-07-26 13:38:47 -06:00
Michael Vines
04ee86e93c rebase 2021-07-24 19:48:12 -07:00
Alfonso Subiotto Marques
82f25f982e Put is_x86_feature_detected behind a target_arch check
This change adds a target arch check so that cargo build can be run from an
Apple M1 mac.

(cherry picked from commit 7ee5b02bc5)
2021-07-24 19:48:12 -07:00
Alfonso Subiotto Marques
1cc32c9cda Disable reed-solomon-erasure/simd-accel feature on aarch64 (Apple M1)
This feature requires clang to support -march=native on M1. Before this change,
cargo build would output a build error when building for aarch64.

(cherry picked from commit 8a9b7f5ef2)

# Conflicts:
#	ledger/Cargo.toml
2021-07-24 19:48:12 -07:00
mergify[bot]
aedcab846c RpcClient::send<T> now supports client-defined RPC methods via RpcRequest::Custom (#18882)
(cherry picked from commit f264511585)

Co-authored-by: Michael Vines <mvines@gmail.com>
2021-07-24 19:15:51 +00:00
Michael Vines
548ddff7ed Bump version to v1.7.9 2021-07-24 11:23:44 -06:00
sakridge
7aced9e772 Sigverify refactor (#18872) 2021-07-23 22:23:57 +02:00
Ryo Onodera
1cc8de0fed Bump version to v1.7.8 (#18866) 2021-07-24 01:14:03 +09:00
mergify[bot]
f08c7b2294 Rpc: getProgramAccounts, restruct base58 account data size (#18852) (#18855)
* Refactor account encoding to povide helper w/out querying Bank

* Use new method in get_program_accounts to properly return length err

(cherry picked from commit 3eecb6f4ae)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-07-23 05:52:53 +00:00
mergify[bot]
cc58d36de6 Really start caching by fixing swapped CAS... (#18842) (#18853)
(cherry picked from commit 611af87fdb)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2021-07-23 03:51:12 +00:00
mergify[bot]
128393da54 Clarify entities in docs (#18821) (#18845)
* Update docs

* Change History

(cherry picked from commit e7a687d7ee)

Co-authored-by: Ryan M. Shea <8948187+rmshea@users.noreply.github.com>
2021-07-22 20:25:38 +00:00
mergify[bot]
e7964a0b89 token: Swap new token program id for consistency on all networks (#18823) (#18837)
(cherry picked from commit d6f5945653)

Co-authored-by: Jon Cinque <jon.cinque@gmail.com>
2021-07-22 11:03:41 +00:00
mergify[bot]
77dc3746a3 docs: fix link to Native Programs page in transactions.md (#18830) (#18832)
(cherry picked from commit 71bd434297)

Co-authored-by: Stephen Peterkins <stephen@peterkins.ca>
2021-07-22 03:59:45 +00:00
mergify[bot]
e88f4d689c Fixed broken links and added more context to getTokenAccountBalance docs (#18811)
(cherry picked from commit d751d5b6e8)

Co-authored-by: murlux <murlux@protonmail.com>
2021-07-21 18:32:58 +00:00
mergify[bot]
f1858c74a4 Disambiguate archive_snapshot_package IO error sources (#18798)
(cherry picked from commit a4c3db51fc)

Co-authored-by: Trent Nelson <trent@solana.com>
2021-07-21 17:17:57 +00:00
mergify[bot]
7427dafc36 feature: add new token program feature (backport #18780) (#18803)
* feature: add new token program feature (#18780)

* feature: add new token program feature

* Fixup test

* Update to spl-token v3.2.0

* Update Cargo.lock + fmt

* Update token program version in fetch-spl.sh

* Bump associated token program to 1.0.3

* Add aToken so

(cherry picked from commit 51f3b9aa7c)

# Conflicts:
#	Cargo.lock
#	account-decoder/Cargo.toml
#	accounts-cluster-bench/Cargo.toml
#	programs/bpf/Cargo.lock
#	rpc/Cargo.toml
#	sdk/src/feature_set.rs
#	tokens/Cargo.toml
#	transaction-status/Cargo.toml

* Fix merge issues

* Re-generate lock files

Co-authored-by: Jon Cinque <jon.cinque@gmail.com>
2021-07-21 15:05:45 +00:00
mergify[bot]
1cdcabf7cf Bump BPF tools to v1.9 (#18766)
(cherry picked from commit 47861fc373)

Co-authored-by: Dmitri Makarov <dmakarov@alumni.stanford.edu>
2021-07-19 20:46:59 +00:00
mergify[bot]
ea192b3c83 prevent excess allocation with AccountsIndexIterator (#18605) (#18642)
(cherry picked from commit 0bd8710d34)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-07-19 20:15:40 +00:00
mergify[bot]
927057df26 correct typo (backport #18750) (#18751)
* correct typo

(cherry picked from commit 1507477306)

* fix another typo

(cherry picked from commit 269028360c)

Co-authored-by: Yang Li <yangli1990@live.com.au>
2021-07-18 13:52:57 +00:00
Trent Nelson
19049ca91b Bump version to v1.7.7 2021-07-17 08:42:22 +00:00
228 changed files with 4298 additions and 2874 deletions

428
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"]

View File

@@ -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;

View File

@@ -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"]

View File

@@ -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,

View File

@@ -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"]

View File

@@ -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.

View File

@@ -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]

View File

@@ -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"] }

View File

@@ -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"]

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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"]

View File

@@ -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"]

View File

@@ -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 {

View 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(

View File

@@ -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"

View File

@@ -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/"

View File

@@ -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]

View File

@@ -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
);
}
}

View File

@@ -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
View 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")
)
)
}

View File

@@ -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");

View 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();

View File

@@ -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;

View File

@@ -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())

View File

@@ -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();

View File

@@ -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");

View 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();

View File

@@ -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",

View File

@@ -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
View 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)
}
}

View File

@@ -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();

View File

@@ -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());

View File

@@ -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();

View File

@@ -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());

View File

@@ -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());

View File

@@ -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());

View File

@@ -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"]

View File

@@ -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)

View File

@@ -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(())
}
}

View File

@@ -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)

View File

@@ -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"] }

View File

@@ -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(

View File

@@ -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();
});

View File

@@ -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 {

View File

@@ -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();

View File

@@ -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,

View File

@@ -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);

View File

@@ -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();
}
});
}
}

View File

@@ -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,

View File

@@ -145,6 +145,7 @@ impl BroadcastRun for FailEntryVerificationBroadcastRun {
&cluster_nodes,
&Arc::new(AtomicU64::new(0)),
&mut TransmitShredsStats::default(),
cluster_info.socket_addr_space(),
)?;
Ok(())

View File

@@ -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;

View File

@@ -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();

View File

@@ -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 = {

View File

@@ -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;

View File

@@ -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());

View File

@@ -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()]);

View File

@@ -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 =

View File

@@ -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,

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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,

View File

@@ -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();

View File

@@ -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(),

View File

@@ -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(

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -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"]

View File

@@ -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);

View File

@@ -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]

View File

@@ -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"] }

View File

@@ -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

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"]

View File

@@ -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]]

View File

@@ -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]

View File

@@ -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);

View File

@@ -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]

View File

@@ -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

View File

@@ -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,

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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();

View File

@@ -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

View File

@@ -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| {

View File

@@ -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"

View File

@@ -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]]

View File

@@ -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"] }

View File

@@ -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