Compare commits
33 Commits
Author | SHA1 | Date | |
---|---|---|---|
45612bc988 | |||
51ce98badd | |||
075e784bef | |||
c1b587c6e4 | |||
94e2d0b5c2 | |||
c2be9fdf0e | |||
c2b17c7d3f | |||
31544f2a82 | |||
3a88190e4e | |||
3f30354d1a | |||
11f15c0708 | |||
a83bf85bb3 | |||
02877814fa | |||
29cdfd6bc9 | |||
9dffc3abe4 | |||
b4eb81546e | |||
489fd3058f | |||
e5872ef1c1 | |||
cb9d18316a | |||
c3ac85828b | |||
5fbddd5894 | |||
90af35737d | |||
58cb21402b | |||
824b894977 | |||
2295a5e512 | |||
83a322a211 | |||
a008748d9d | |||
72cb0b7c9e | |||
ede3781f91 | |||
e3ac6fac1e | |||
e30561f8a0 | |||
8d59bef561 | |||
897e1fc5d6 |
682
Cargo.lock
generated
682
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -126,10 +126,7 @@ Remote Testnets
|
||||
---
|
||||
|
||||
We maintain several testnets:
|
||||
|
||||
* `testnet` - public stable testnet accessible via testnet.solana.com. Runs 24/7
|
||||
* `testnet-beta` - public beta channel testnet accessible via beta.testnet.solana.com. Runs 24/7
|
||||
* `testnet-edge` - public edge channel testnet accessible via edge.testnet.solana.com. Runs 24/7
|
||||
* `testnet` - public stable testnet accessible via devnet.solana.com. Runs 24/7
|
||||
|
||||
## Deploy process
|
||||
|
||||
|
@ -140,9 +140,9 @@ TODO: Documentation update procedure is WIP as we move to gitbook
|
||||
|
||||
Document the new recommended version by updating `book/src/running-archiver.md` and `book/src/validator-testnet.md` on the release (beta) branch to point at the `solana-install` for the upcoming release version.
|
||||
|
||||
### Update software on testnet.solana.com
|
||||
### Update software on devnet.solana.com
|
||||
|
||||
The testnet running on testnet.solana.com is set to use a fixed release tag
|
||||
The testnet running on devnet.solana.com is set to use a fixed release tag
|
||||
which is set in the Buildkite testnet-management pipeline.
|
||||
This tag needs to be updated and the testnet restarted after a new release
|
||||
tag is created.
|
||||
@ -182,4 +182,4 @@ TESTNET_OP=create-and-start
|
||||
### Alert the community
|
||||
|
||||
Notify Discord users on #validator-support that a new release for
|
||||
testnet.solana.com is available
|
||||
devnet.solana.com is available
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-archiver-lib"
|
||||
version = "0.23.5"
|
||||
version = "0.23.6"
|
||||
description = "Solana Archiver Library"
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@ -15,22 +15,22 @@ ed25519-dalek = "=1.0.0-pre.1"
|
||||
log = "0.4.8"
|
||||
rand = "0.6.5"
|
||||
rand_chacha = "0.1.1"
|
||||
solana-client = { path = "../client", version = "0.23.5" }
|
||||
solana-storage-program = { path = "../programs/storage", version = "0.23.5" }
|
||||
solana-client = { path = "../client", version = "0.23.6" }
|
||||
solana-storage-program = { path = "../programs/storage", version = "0.23.6" }
|
||||
thiserror = "1.0"
|
||||
serde = "1.0.104"
|
||||
serde_json = "1.0.44"
|
||||
serde_derive = "1.0.103"
|
||||
solana-net-utils = { path = "../net-utils", version = "0.23.5" }
|
||||
solana-chacha = { path = "../chacha", version = "0.23.5" }
|
||||
solana-chacha-sys = { path = "../chacha-sys", version = "0.23.5" }
|
||||
solana-ledger = { path = "../ledger", version = "0.23.5" }
|
||||
solana-logger = { path = "../logger", version = "0.23.5" }
|
||||
solana-perf = { path = "../perf", version = "0.23.5" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.5" }
|
||||
solana-core = { path = "../core", version = "0.23.5" }
|
||||
solana-archiver-utils = { path = "../archiver-utils", version = "0.23.5" }
|
||||
solana-metrics = { path = "../metrics", version = "0.23.5" }
|
||||
solana-net-utils = { path = "../net-utils", version = "0.23.6" }
|
||||
solana-chacha = { path = "../chacha", version = "0.23.6" }
|
||||
solana-chacha-sys = { path = "../chacha-sys", version = "0.23.6" }
|
||||
solana-ledger = { path = "../ledger", version = "0.23.6" }
|
||||
solana-logger = { path = "../logger", version = "0.23.6" }
|
||||
solana-perf = { path = "../perf", version = "0.23.6" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.6" }
|
||||
solana-core = { path = "../core", version = "0.23.6" }
|
||||
solana-archiver-utils = { path = "../archiver-utils", version = "0.23.6" }
|
||||
solana-metrics = { path = "../metrics", version = "0.23.6" }
|
||||
|
||||
[dev-dependencies]
|
||||
hex = "0.4.0"
|
||||
|
@ -36,7 +36,7 @@ use solana_sdk::{
|
||||
commitment_config::CommitmentConfig,
|
||||
hash::Hash,
|
||||
message::Message,
|
||||
signature::{Keypair, KeypairUtil, Signature},
|
||||
signature::{Keypair, Signature, Signer},
|
||||
timing::timestamp,
|
||||
transaction::Transaction,
|
||||
transport::TransportError,
|
||||
@ -384,7 +384,7 @@ impl Archiver {
|
||||
);
|
||||
let message =
|
||||
Message::new_with_payer(vec![ix], Some(&archiver_keypair.pubkey()));
|
||||
if let Err(e) = client.send_message(&[&archiver_keypair], message) {
|
||||
if let Err(e) = client.send_message(&[archiver_keypair.as_ref()], message) {
|
||||
error!("unable to redeem reward, tx failed: {:?}", e);
|
||||
} else {
|
||||
info!(
|
||||
@ -671,7 +671,7 @@ impl Archiver {
|
||||
blockhash,
|
||||
);
|
||||
if let Err(err) = client.send_and_confirm_transaction(
|
||||
&[&archiver_keypair, &storage_keypair],
|
||||
&[archiver_keypair.as_ref(), storage_keypair.as_ref()],
|
||||
&mut transaction,
|
||||
10,
|
||||
0,
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-archiver-utils"
|
||||
version = "0.23.5"
|
||||
version = "0.23.6"
|
||||
description = "Solana Archiver Utils"
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@ -12,12 +12,12 @@ edition = "2018"
|
||||
log = "0.4.8"
|
||||
rand = "0.6.5"
|
||||
rand_chacha = "0.1.1"
|
||||
solana-chacha = { path = "../chacha", version = "0.23.5" }
|
||||
solana-chacha-sys = { path = "../chacha-sys", version = "0.23.5" }
|
||||
solana-ledger = { path = "../ledger", version = "0.23.5" }
|
||||
solana-logger = { path = "../logger", version = "0.23.5" }
|
||||
solana-perf = { path = "../perf", version = "0.23.5" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.5" }
|
||||
solana-chacha = { path = "../chacha", version = "0.23.6" }
|
||||
solana-chacha-sys = { path = "../chacha-sys", version = "0.23.6" }
|
||||
solana-ledger = { path = "../ledger", version = "0.23.6" }
|
||||
solana-logger = { path = "../logger", version = "0.23.6" }
|
||||
solana-perf = { path = "../perf", version = "0.23.6" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.6" }
|
||||
|
||||
[dev-dependencies]
|
||||
hex = "0.4.0"
|
||||
|
@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
edition = "2018"
|
||||
name = "solana-archiver"
|
||||
version = "0.23.5"
|
||||
version = "0.23.6"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@ -10,11 +10,11 @@ homepage = "https://solana.com/"
|
||||
[dependencies]
|
||||
clap = "2.33.0"
|
||||
console = "0.9.1"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "0.23.5" }
|
||||
solana-core = { path = "../core", version = "0.23.5" }
|
||||
solana-logger = { path = "../logger", version = "0.23.5" }
|
||||
solana-metrics = { path = "../metrics", version = "0.23.5" }
|
||||
solana-archiver-lib = { path = "../archiver-lib", version = "0.23.5" }
|
||||
solana-net-utils = { path = "../net-utils", version = "0.23.5" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.5" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "0.23.6" }
|
||||
solana-core = { path = "../core", version = "0.23.6" }
|
||||
solana-logger = { path = "../logger", version = "0.23.6" }
|
||||
solana-metrics = { path = "../metrics", version = "0.23.6" }
|
||||
solana-archiver-lib = { path = "../archiver-lib", version = "0.23.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "0.23.6" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.6" }
|
||||
|
||||
|
@ -12,7 +12,7 @@ use solana_core::{
|
||||
cluster_info::{Node, VALIDATOR_PORT_RANGE},
|
||||
contact_info::ContactInfo,
|
||||
};
|
||||
use solana_sdk::{commitment_config::CommitmentConfig, signature::KeypairUtil};
|
||||
use solana_sdk::{commitment_config::CommitmentConfig, signature::Signer};
|
||||
use std::{net::SocketAddr, path::PathBuf, process::exit, sync::Arc};
|
||||
|
||||
fn main() {
|
||||
|
@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
edition = "2018"
|
||||
name = "solana-banking-bench"
|
||||
version = "0.23.5"
|
||||
version = "0.23.6"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@ -10,11 +10,11 @@ homepage = "https://solana.com/"
|
||||
[dependencies]
|
||||
log = "0.4.6"
|
||||
rayon = "1.2.0"
|
||||
solana-core = { path = "../core", version = "0.23.5" }
|
||||
solana-ledger = { path = "../ledger", version = "0.23.5" }
|
||||
solana-logger = { path = "../logger", version = "0.23.5" }
|
||||
solana-runtime = { path = "../runtime", version = "0.23.5" }
|
||||
solana-measure = { path = "../measure", version = "0.23.5" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.5" }
|
||||
solana-core = { path = "../core", version = "0.23.6" }
|
||||
solana-ledger = { path = "../ledger", version = "0.23.6" }
|
||||
solana-logger = { path = "../logger", version = "0.23.6" }
|
||||
solana-runtime = { path = "../runtime", version = "0.23.6" }
|
||||
solana-measure = { path = "../measure", version = "0.23.6" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.6" }
|
||||
rand = "0.6.5"
|
||||
crossbeam-channel = "0.3"
|
||||
|
@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
edition = "2018"
|
||||
name = "solana-bench-exchange"
|
||||
version = "0.23.5"
|
||||
version = "0.23.6"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@ -23,19 +23,19 @@ serde = "1.0.104"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.44"
|
||||
serde_yaml = "0.8.11"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "0.23.5" }
|
||||
solana-core = { path = "../core", version = "0.23.5" }
|
||||
solana-genesis = { path = "../genesis", version = "0.23.5" }
|
||||
solana-client = { path = "../client", version = "0.23.5" }
|
||||
solana-faucet = { path = "../faucet", version = "0.23.5" }
|
||||
solana-exchange-program = { path = "../programs/exchange", version = "0.23.5" }
|
||||
solana-logger = { path = "../logger", version = "0.23.5" }
|
||||
solana-metrics = { path = "../metrics", version = "0.23.5" }
|
||||
solana-net-utils = { path = "../net-utils", version = "0.23.5" }
|
||||
solana-runtime = { path = "../runtime", version = "0.23.5" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.5" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "0.23.6" }
|
||||
solana-core = { path = "../core", version = "0.23.6" }
|
||||
solana-genesis = { path = "../genesis", version = "0.23.6" }
|
||||
solana-client = { path = "../client", version = "0.23.6" }
|
||||
solana-faucet = { path = "../faucet", version = "0.23.6" }
|
||||
solana-exchange-program = { path = "../programs/exchange", version = "0.23.6" }
|
||||
solana-logger = { path = "../logger", version = "0.23.6" }
|
||||
solana-metrics = { path = "../metrics", version = "0.23.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "0.23.6" }
|
||||
solana-runtime = { path = "../runtime", version = "0.23.6" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.6" }
|
||||
untrusted = "0.7.0"
|
||||
ws = "0.9.1"
|
||||
|
||||
[dev-dependencies]
|
||||
solana-local-cluster = { path = "../local-cluster", version = "0.23.5" }
|
||||
solana-local-cluster = { path = "../local-cluster", version = "0.23.6" }
|
||||
|
@ -15,7 +15,7 @@ use solana_sdk::{
|
||||
client::{Client, SyncClient},
|
||||
commitment_config::CommitmentConfig,
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, KeypairUtil},
|
||||
signature::{Keypair, Signer},
|
||||
timing::{duration_as_ms, duration_as_s},
|
||||
transaction::Transaction,
|
||||
{system_instruction, system_program},
|
||||
@ -701,7 +701,7 @@ fn verify_funding_transfer<T: SyncClient + ?Sized>(
|
||||
false
|
||||
}
|
||||
|
||||
pub fn fund_keys(client: &dyn Client, source: &Keypair, dests: &[Arc<Keypair>], lamports: u64) {
|
||||
pub fn fund_keys<T: Client>(client: &T, source: &Keypair, dests: &[Arc<Keypair>], lamports: u64) {
|
||||
let total = lamports * (dests.len() as u64 + 1);
|
||||
let mut funded: Vec<(&Keypair, u64)> = vec![(source, total)];
|
||||
let mut notfunded: Vec<&Arc<Keypair>> = dests.iter().collect();
|
||||
@ -824,7 +824,11 @@ pub fn fund_keys(client: &dyn Client, source: &Keypair, dests: &[Arc<Keypair>],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_token_accounts(client: &dyn Client, signers: &[Arc<Keypair>], accounts: &[Keypair]) {
|
||||
pub fn create_token_accounts<T: Client>(
|
||||
client: &T,
|
||||
signers: &[Arc<Keypair>],
|
||||
accounts: &[Keypair],
|
||||
) {
|
||||
let mut notfunded: Vec<(&Arc<Keypair>, &Keypair)> = signers.iter().zip(accounts).collect();
|
||||
|
||||
while !notfunded.is_empty() {
|
||||
@ -968,7 +972,12 @@ fn generate_keypairs(num: u64) -> Vec<Keypair> {
|
||||
rnd.gen_n_keypairs(num)
|
||||
}
|
||||
|
||||
pub fn airdrop_lamports(client: &dyn Client, faucet_addr: &SocketAddr, id: &Keypair, amount: u64) {
|
||||
pub fn airdrop_lamports<T: Client>(
|
||||
client: &T,
|
||||
faucet_addr: &SocketAddr,
|
||||
id: &Keypair,
|
||||
amount: u64,
|
||||
) {
|
||||
let balance = client.get_balance_with_commitment(&id.pubkey(), CommitmentConfig::recent());
|
||||
let balance = balance.unwrap_or(0);
|
||||
if balance >= amount {
|
||||
|
@ -5,7 +5,7 @@ pub mod order_book;
|
||||
use crate::bench::{airdrop_lamports, create_client_accounts_file, do_bench_exchange, Config};
|
||||
use log::*;
|
||||
use solana_core::gossip_service::{discover_cluster, get_multi_client};
|
||||
use solana_sdk::signature::KeypairUtil;
|
||||
use solana_sdk::signature::Signer;
|
||||
|
||||
fn main() {
|
||||
solana_logger::setup();
|
||||
|
@ -10,7 +10,7 @@ use solana_local_cluster::local_cluster::{ClusterConfig, LocalCluster};
|
||||
use solana_runtime::bank::Bank;
|
||||
use solana_runtime::bank_client::BankClient;
|
||||
use solana_sdk::genesis_config::create_genesis_config;
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use std::process::exit;
|
||||
use std::sync::mpsc::channel;
|
||||
use std::time::Duration;
|
||||
|
@ -2,14 +2,14 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
edition = "2018"
|
||||
name = "solana-bench-streamer"
|
||||
version = "0.23.5"
|
||||
version = "0.23.6"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33.0"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "0.23.5" }
|
||||
solana-core = { path = "../core", version = "0.23.5" }
|
||||
solana-logger = { path = "../logger", version = "0.23.5" }
|
||||
solana-net-utils = { path = "../net-utils", version = "0.23.5" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "0.23.6" }
|
||||
solana-core = { path = "../core", version = "0.23.6" }
|
||||
solana-logger = { path = "../logger", version = "0.23.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "0.23.6" }
|
||||
|
@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
edition = "2018"
|
||||
name = "solana-bench-tps"
|
||||
version = "0.23.5"
|
||||
version = "0.23.6"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@ -16,24 +16,24 @@ serde = "1.0.104"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.44"
|
||||
serde_yaml = "0.8.11"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "0.23.5" }
|
||||
solana-core = { path = "../core", version = "0.23.5" }
|
||||
solana-genesis = { path = "../genesis", version = "0.23.5" }
|
||||
solana-client = { path = "../client", version = "0.23.5" }
|
||||
solana-faucet = { path = "../faucet", version = "0.23.5" }
|
||||
solana-librapay = { path = "../programs/librapay", version = "0.23.5", optional = true }
|
||||
solana-logger = { path = "../logger", version = "0.23.5" }
|
||||
solana-metrics = { path = "../metrics", version = "0.23.5" }
|
||||
solana-measure = { path = "../measure", version = "0.23.5" }
|
||||
solana-net-utils = { path = "../net-utils", version = "0.23.5" }
|
||||
solana-runtime = { path = "../runtime", version = "0.23.5" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.5" }
|
||||
solana-move-loader-program = { path = "../programs/move_loader", version = "0.23.5", optional = true }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "0.23.6" }
|
||||
solana-core = { path = "../core", version = "0.23.6" }
|
||||
solana-genesis = { path = "../genesis", version = "0.23.6" }
|
||||
solana-client = { path = "../client", version = "0.23.6" }
|
||||
solana-faucet = { path = "../faucet", version = "0.23.6" }
|
||||
solana-librapay = { path = "../programs/librapay", version = "0.23.6", optional = true }
|
||||
solana-logger = { path = "../logger", version = "0.23.6" }
|
||||
solana-metrics = { path = "../metrics", version = "0.23.6" }
|
||||
solana-measure = { path = "../measure", version = "0.23.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "0.23.6" }
|
||||
solana-runtime = { path = "../runtime", version = "0.23.6" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.6" }
|
||||
solana-move-loader-program = { path = "../programs/move_loader", version = "0.23.6", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
serial_test = "0.3.2"
|
||||
serial_test_derive = "0.3.1"
|
||||
solana-local-cluster = { path = "../local-cluster", version = "0.23.5" }
|
||||
solana-local-cluster = { path = "../local-cluster", version = "0.23.6" }
|
||||
|
||||
[features]
|
||||
move = ["solana-librapay", "solana-move-loader-program"]
|
||||
|
@ -7,7 +7,7 @@ use solana_faucet::faucet::request_airdrop_transaction;
|
||||
#[cfg(feature = "move")]
|
||||
use solana_librapay::{create_genesis, upload_mint_script, upload_payment_script};
|
||||
use solana_measure::measure::Measure;
|
||||
use solana_metrics::{self, datapoint_debug};
|
||||
use solana_metrics::{self, datapoint_info};
|
||||
use solana_sdk::{
|
||||
client::Client,
|
||||
clock::{DEFAULT_TICKS_PER_SECOND, DEFAULT_TICKS_PER_SLOT, MAX_PROCESSING_AGE},
|
||||
@ -15,7 +15,7 @@ use solana_sdk::{
|
||||
fee_calculator::FeeCalculator,
|
||||
hash::Hash,
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, KeypairUtil},
|
||||
signature::{Keypair, Signer},
|
||||
system_instruction, system_transaction,
|
||||
timing::{duration_as_ms, duration_as_s, duration_as_us, timestamp},
|
||||
transaction::Transaction,
|
||||
@ -244,7 +244,7 @@ where
|
||||
|
||||
fn metrics_submit_lamport_balance(lamport_balance: u64) {
|
||||
info!("Token balance: {}", lamport_balance);
|
||||
datapoint_debug!(
|
||||
datapoint_info!(
|
||||
"bench-tps-lamport_balance",
|
||||
("balance", lamport_balance, i64)
|
||||
);
|
||||
@ -375,7 +375,7 @@ fn generate_txs(
|
||||
duration_as_ms(&duration),
|
||||
blockhash,
|
||||
);
|
||||
datapoint_debug!(
|
||||
datapoint_info!(
|
||||
"bench-tps-generate_txs",
|
||||
("duration", duration_as_us(&duration), i64)
|
||||
);
|
||||
@ -481,7 +481,7 @@ fn do_tx_transfers<T: Client>(
|
||||
duration_as_ms(&transfer_start.elapsed()),
|
||||
tx_len as f32 / duration_as_s(&transfer_start.elapsed()),
|
||||
);
|
||||
datapoint_debug!(
|
||||
datapoint_info!(
|
||||
"bench-tps-do_tx_transfers",
|
||||
("duration", duration_as_us(&transfer_start.elapsed()), i64),
|
||||
("count", tx_len, i64)
|
||||
|
@ -4,7 +4,7 @@ use solana_bench_tps::cli;
|
||||
use solana_core::gossip_service::{discover_cluster, get_client, get_multi_client};
|
||||
use solana_genesis::Base64Account;
|
||||
use solana_sdk::fee_calculator::FeeCalculator;
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use solana_sdk::system_program;
|
||||
use std::{collections::HashMap, fs::File, io::prelude::*, path::Path, process::exit, sync::Arc};
|
||||
|
||||
|
@ -8,7 +8,7 @@ use solana_faucet::faucet::run_local_faucet;
|
||||
use solana_local_cluster::local_cluster::{ClusterConfig, LocalCluster};
|
||||
#[cfg(feature = "move")]
|
||||
use solana_sdk::move_loader::solana_move_loader_program;
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use std::sync::{mpsc::channel, Arc};
|
||||
use std::time::Duration;
|
||||
|
||||
|
@ -830,7 +830,7 @@ The result field will be a JSON object with the following fields:
|
||||
// Request
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getVersion"}' http://localhost:8899
|
||||
// Result
|
||||
{"jsonrpc":"2.0","result":{"solana-core": "0.23.5"},"id":1}
|
||||
{"jsonrpc":"2.0","result":{"solana-core": "0.23.6"},"id":1}
|
||||
```
|
||||
|
||||
### getVoteAccounts
|
||||
|
@ -154,7 +154,7 @@ The stream will output a series of JSON objects:
|
||||
In this example the client connects to our public testnet. To run validators on the testnet you would need to open udp ports `8000-10000`.
|
||||
|
||||
```bash
|
||||
$ NDEBUG=1 ./multinode-demo/bench-tps.sh --entrypoint testnet.solana.com:8001 --faucet testnet.solana.com:9900 --duration 60 --tx_count 50
|
||||
$ NDEBUG=1 ./multinode-demo/bench-tps.sh --entrypoint devnet.solana.com:8001 --faucet devnet.solana.com:9900 --duration 60 --tx_count 50
|
||||
```
|
||||
|
||||
You can observe the effects of your client's transactions on our [dashboard](https://metrics.solana.com:3000/d/testnet/testnet-hud?orgId=2&from=now-30m&to=now&refresh=5s&var-testnet=testnet)
|
||||
|
@ -22,12 +22,6 @@ $ solana airdrop 2
|
||||
|
||||
// Return
|
||||
"2.00000000 SOL"
|
||||
|
||||
// Command
|
||||
$ solana airdrop 123 --lamports
|
||||
|
||||
// Return
|
||||
"123 lamports"
|
||||
```
|
||||
|
||||
### Get Balance
|
||||
|
@ -177,7 +177,7 @@ $ solana send-timestamp <PUBKEY> <PROCESS_ID> --date 2018-12-24T23:59:00
|
||||
## Usage
|
||||
### solana-cli
|
||||
```text
|
||||
solana-cli 0.23.5 [channel=unknown commit=unknown]
|
||||
solana-cli 0.23.6 [channel=unknown commit=unknown]
|
||||
Blockchain, Rebuilt for Scale
|
||||
|
||||
USAGE:
|
||||
@ -191,12 +191,17 @@ FLAGS:
|
||||
-v, --verbose Show extra information header
|
||||
|
||||
OPTIONS:
|
||||
--ask-seed-phrase <KEYPAIR NAME> Recover a keypair using a seed phrase and optional passphrase [possible
|
||||
values: keypair]
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
--ask-seed-phrase <KEYPAIR NAME>
|
||||
Recover a keypair using a seed phrase and optional passphrase [possible values: keypair]
|
||||
|
||||
-C, --config <PATH>
|
||||
Configuration file to use [default: ~/.config/solana/cli/config.yml]
|
||||
|
||||
--derivation-path <ACCOUNT or ACCOUNT/CHANGE>
|
||||
Derivation path to use: m/44'/501'/ACCOUNT'/CHANGE'; default key is device base pubkey: m/44'/501'/0'
|
||||
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
|
||||
SUBCOMMANDS:
|
||||
account Show the contents of an account
|
||||
@ -226,6 +231,8 @@ SUBCOMMANDS:
|
||||
genesis-hash Get the genesis hash
|
||||
gossip Show the current gossip network nodes
|
||||
help Prints this message or the help of the given subcommand(s)
|
||||
leader-schedule Display leader schedule
|
||||
live-slots Show information about the current slot progression
|
||||
new-nonce Generate a new nonce, rendering the existing nonce useless
|
||||
nonce Get the current nonce value
|
||||
nonce-account Show the contents of a nonce account
|
||||
@ -233,14 +240,17 @@ SUBCOMMANDS:
|
||||
ping Submit transactions sequentially
|
||||
send-signature Send a signature to authorize a transfer
|
||||
send-timestamp Send a timestamp to unlock a transfer
|
||||
show-stake-account Show the contents of a stake account
|
||||
slot Get current slot
|
||||
split-stake Split a stake account
|
||||
stake-account Show the contents of a stake account
|
||||
stake-authorize-staker Authorize a new stake signing keypair for the given stake account
|
||||
stake-authorize-withdrawer Authorize a new withdraw signing keypair for the given stake account
|
||||
stake-history Show the stake history
|
||||
stake-set-lockup Set Lockup for the stake account
|
||||
stakes Show stake account information
|
||||
storage-account Show the contents of a storage account
|
||||
transaction-count Get current transaction count
|
||||
transfer Transfer funds between system accounts
|
||||
validator-info Publish/get Validator info on Solana
|
||||
validators Show summary information about the current validators
|
||||
vote-account Show the contents of a vote account
|
||||
@ -273,7 +283,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
-o, --output <FILE> Write the account data to this file
|
||||
|
||||
ARGS:
|
||||
@ -301,7 +311,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
```
|
||||
|
||||
#### solana-airdrop
|
||||
@ -310,7 +320,7 @@ solana-airdrop
|
||||
Request lamports
|
||||
|
||||
USAGE:
|
||||
solana airdrop [FLAGS] [OPTIONS] <AMOUNT> [UNIT]
|
||||
solana airdrop [FLAGS] [OPTIONS] <AMOUNT> [PUBKEY]
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
@ -327,11 +337,11 @@ OPTIONS:
|
||||
--faucet-host <HOST> Faucet host to use [default: the --url host]
|
||||
--faucet-port <PORT> Faucet port to use [default: 9900]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
|
||||
ARGS:
|
||||
<AMOUNT> The airdrop amount to request (default unit SOL)
|
||||
<UNIT> Specify unit to use for request and balance display [possible values: SOL, lamports]
|
||||
<AMOUNT> The airdrop amount to request, in SOL
|
||||
<PUBKEY> The pubkey of airdrop recipient
|
||||
```
|
||||
|
||||
#### solana-authorize-nonce-account
|
||||
@ -357,7 +367,7 @@ OPTIONS:
|
||||
Configuration file to use [default: ~/.config/solana/cli/config.yml]
|
||||
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
--nonce-authority <KEYPAIR or PUBKEY>
|
||||
Provide the nonce authority keypair to use when signing a nonced transaction
|
||||
|
||||
@ -392,7 +402,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
|
||||
ARGS:
|
||||
<PUBKEY> The public key of the balance to check
|
||||
@ -420,7 +430,7 @@ OPTIONS:
|
||||
~/.config/solana/cli/config.yml]
|
||||
--epoch <epoch> Epoch to show block production for [default: current epoch]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
--slot-limit <slot_limit> Limit results to this many slots from the end of the epoch [default: full
|
||||
epoch]
|
||||
```
|
||||
@ -446,7 +456,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
|
||||
ARGS:
|
||||
<SLOT> Slot number of the block to query
|
||||
@ -473,7 +483,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
|
||||
ARGS:
|
||||
<PROCESS ID> The process id of the transfer to cancel
|
||||
@ -500,7 +510,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
|
||||
ARGS:
|
||||
<PUBKEY> Identity pubkey of the validator
|
||||
@ -527,7 +537,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
|
||||
ARGS:
|
||||
<NODE PUBKEY> The node account to credit the rewards to
|
||||
@ -555,7 +565,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
```
|
||||
|
||||
#### solana-config
|
||||
@ -579,7 +589,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
|
||||
SUBCOMMANDS:
|
||||
get Get current config settings
|
||||
@ -608,7 +618,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
|
||||
ARGS:
|
||||
<SIGNATURE> The transaction signature to confirm
|
||||
@ -636,7 +646,7 @@ OPTIONS:
|
||||
~/.config/solana/cli/config.yml]
|
||||
--from <PUBKEY> From (base) key, defaults to client keypair.
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
|
||||
ARGS:
|
||||
<SEED_STRING> The seed. Must not take more than 32 bytes to encode as utf-8
|
||||
@ -665,7 +675,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
|
||||
ARGS:
|
||||
<STORAGE ACCOUNT OWNER PUBKEY>
|
||||
@ -678,7 +688,7 @@ solana-create-nonce-account
|
||||
Create a nonce account
|
||||
|
||||
USAGE:
|
||||
solana create-nonce-account [FLAGS] [OPTIONS] <NONCE ACCOUNT> <AMOUNT> [UNIT]
|
||||
solana create-nonce-account [FLAGS] [OPTIONS] <NONCE ACCOUNT> <AMOUNT>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
@ -693,13 +703,12 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
--nonce-authority <BASE58_PUBKEY> Assign noncing authority to another entity
|
||||
|
||||
ARGS:
|
||||
<NONCE ACCOUNT> Keypair of the nonce account to fund
|
||||
<AMOUNT> The amount to load the nonce account with (default unit SOL)
|
||||
<UNIT> Specify unit to use for request [possible values: SOL, lamports]
|
||||
<AMOUNT> The amount to load the nonce account with, in SOL
|
||||
```
|
||||
|
||||
#### solana-create-stake-account
|
||||
@ -708,34 +717,56 @@ solana-create-stake-account
|
||||
Create a stake account
|
||||
|
||||
USAGE:
|
||||
solana create-stake-account [FLAGS] [OPTIONS] <STAKE ACCOUNT> <AMOUNT> [UNIT]
|
||||
solana create-stake-account [FLAGS] [OPTIONS] <STAKE ACCOUNT> <AMOUNT>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
--sign-only Sign the transaction offline
|
||||
--skip-seed-phrase-validation Skip validation of seed phrases. Use this if your phrase does not use the BIP39
|
||||
official English word list
|
||||
-V, --version Prints version information
|
||||
-v, --verbose Show extra information header
|
||||
|
||||
OPTIONS:
|
||||
--ask-seed-phrase <KEYPAIR NAME> Recover a keypair using a seed phrase and optional passphrase [possible
|
||||
values: keypair]
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
--custodian <PUBKEY> Identity of the custodian (can withdraw before lockup expires)
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
--lockup-date <RFC3339 DATE TIME> The date and time at which this account will be available for withdrawal
|
||||
--lockup-epoch <EPOCH> The epoch height at which this account will be available for withdrawal
|
||||
--seed <SEED STRING> Seed for address generation; if specified, the resulting account will be at
|
||||
a derived address of the STAKE ACCOUNT pubkey
|
||||
--stake-authority <PUBKEY> Public key of authorized staker (defaults to cli config pubkey)
|
||||
--withdraw-authority <PUBKEY> Public key of authorized withdrawer (defaults to cli config pubkey)
|
||||
--ask-seed-phrase <KEYPAIR NAME>
|
||||
Recover a keypair using a seed phrase and optional passphrase [possible values: keypair]
|
||||
|
||||
--blockhash <BLOCKHASH> Use the supplied blockhash
|
||||
-C, --config <PATH>
|
||||
Configuration file to use [default: ~/.config/solana/cli/config.yml]
|
||||
|
||||
--custodian <KEYPAIR or PUBKEY> Identity of the custodian (can withdraw before lockup expires)
|
||||
--fee-payer <KEYPAIR or PUBKEY>
|
||||
Specify the fee-payer account. This may be a keypair file, the ASK keyword
|
||||
or the pubkey of an offline signer, provided an appropriate --signer argument
|
||||
is also passed. Defaults to the client keypair.
|
||||
--from <KEYPAIR or PUBKEY> Source account of funds (if different from client local account)
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
--lockup-date <RFC3339 DATE TIME>
|
||||
The date and time at which this account will be available for withdrawal
|
||||
|
||||
--lockup-epoch <EPOCH>
|
||||
The epoch height at which this account will be available for withdrawal
|
||||
|
||||
--nonce <PUBKEY>
|
||||
Provide the nonce account to use when creating a nonced
|
||||
transaction. Nonced transactions are useful when a transaction
|
||||
requires a lengthy signing process. Learn more about nonced
|
||||
transactions at https://docs.solana.com/offline-signing/durable-nonce
|
||||
--nonce-authority <KEYPAIR or PUBKEY>
|
||||
Provide the nonce authority keypair to use when signing a nonced transaction
|
||||
|
||||
--seed <SEED STRING>
|
||||
Seed for address generation; if specified, the resulting account will be at a derived address of the STAKE
|
||||
ACCOUNT pubkey
|
||||
--signer <BASE58_PUBKEY=BASE58_SIG>... Provide a public-key/signature pair for the transaction
|
||||
--stake-authority <PUBKEY> Public key of authorized staker (defaults to cli config pubkey)
|
||||
--withdraw-authority <PUBKEY> Public key of authorized withdrawer (defaults to cli config pubkey)
|
||||
|
||||
ARGS:
|
||||
<STAKE ACCOUNT> Keypair of the stake account to fund
|
||||
<AMOUNT> The amount of send to the vote account (default unit SOL)
|
||||
<UNIT> Specify unit to use for request [possible values: SOL, lamports]
|
||||
<STAKE ACCOUNT> Signing authority of the stake address to fund
|
||||
<AMOUNT> The amount of send to the vote account, in SOL
|
||||
```
|
||||
|
||||
#### solana-create-validator-storage-account
|
||||
@ -759,7 +790,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
|
||||
ARGS:
|
||||
<STORAGE ACCOUNT OWNER PUBKEY>
|
||||
@ -790,7 +821,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
--seed <SEED STRING> Seed for address generation; if specified, the resulting account will be at
|
||||
a derived address of the VOTE ACCOUNT pubkey
|
||||
|
||||
@ -819,12 +850,16 @@ OPTIONS:
|
||||
--ask-seed-phrase <KEYPAIR NAME>
|
||||
Recover a keypair using a seed phrase and optional passphrase [possible values: keypair]
|
||||
|
||||
--blockhash <BLOCKHASH> Use the supplied blockhash
|
||||
--blockhash <BLOCKHASH> Use the supplied blockhash
|
||||
-C, --config <PATH>
|
||||
Configuration file to use [default: ~/.config/solana/cli/config.yml]
|
||||
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
--fee-payer <KEYPAIR or PUBKEY>
|
||||
Specify the fee-payer account. This may be a keypair file, the ASK keyword
|
||||
or the pubkey of an offline signer, provided an appropriate --signer argument
|
||||
is also passed. Defaults to the client keypair.
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
--nonce <PUBKEY>
|
||||
Provide the nonce account to use when creating a nonced
|
||||
transaction. Nonced transactions are useful when a transaction
|
||||
@ -833,8 +868,8 @@ OPTIONS:
|
||||
--nonce-authority <KEYPAIR or PUBKEY>
|
||||
Provide the nonce authority keypair to use when signing a nonced transaction
|
||||
|
||||
--signer <PUBKEY=BASE58_SIG>... Provide a public-key/signature pair for the transaction
|
||||
--stake-authority <KEYPAIR of PUBKEY> Public key of authorized staker (defaults to cli config pubkey)
|
||||
--signer <BASE58_PUBKEY=BASE58_SIG>... Provide a public-key/signature pair for the transaction
|
||||
--stake-authority <KEYPAIR or PUBKEY> Public key of authorized staker (defaults to cli config pubkey)
|
||||
|
||||
ARGS:
|
||||
<STAKE ACCOUNT> Stake account to be deactivated.
|
||||
@ -860,12 +895,16 @@ OPTIONS:
|
||||
--ask-seed-phrase <KEYPAIR NAME>
|
||||
Recover a keypair using a seed phrase and optional passphrase [possible values: keypair]
|
||||
|
||||
--blockhash <BLOCKHASH> Use the supplied blockhash
|
||||
--blockhash <BLOCKHASH> Use the supplied blockhash
|
||||
-C, --config <PATH>
|
||||
Configuration file to use [default: ~/.config/solana/cli/config.yml]
|
||||
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
--fee-payer <KEYPAIR or PUBKEY>
|
||||
Specify the fee-payer account. This may be a keypair file, the ASK keyword
|
||||
or the pubkey of an offline signer, provided an appropriate --signer argument
|
||||
is also passed. Defaults to the client keypair.
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
--nonce <PUBKEY>
|
||||
Provide the nonce account to use when creating a nonced
|
||||
transaction. Nonced transactions are useful when a transaction
|
||||
@ -874,8 +913,8 @@ OPTIONS:
|
||||
--nonce-authority <KEYPAIR or PUBKEY>
|
||||
Provide the nonce authority keypair to use when signing a nonced transaction
|
||||
|
||||
--signer <PUBKEY=BASE58_SIG>... Provide a public-key/signature pair for the transaction
|
||||
--stake-authority <KEYPAIR of PUBKEY> Public key of authorized staker (defaults to cli config pubkey)
|
||||
--signer <BASE58_PUBKEY=BASE58_SIG>... Provide a public-key/signature pair for the transaction
|
||||
--stake-authority <KEYPAIR or PUBKEY> Public key of authorized staker (defaults to cli config pubkey)
|
||||
|
||||
ARGS:
|
||||
<STAKE ACCOUNT> Stake account to delegate
|
||||
@ -903,7 +942,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
|
||||
ARGS:
|
||||
<PATH TO BPF PROGRAM> /path/to/program.o
|
||||
@ -931,7 +970,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
```
|
||||
|
||||
#### solana-fees
|
||||
@ -955,7 +994,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
```
|
||||
|
||||
#### solana-genesis-hash
|
||||
@ -979,7 +1018,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
```
|
||||
|
||||
#### solana-gossip
|
||||
@ -1003,7 +1042,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
```
|
||||
|
||||
#### solana-help
|
||||
@ -1018,6 +1057,55 @@ ARGS:
|
||||
<subcommand>... The subcommand whose help message to display
|
||||
```
|
||||
|
||||
#### solana-leader-schedule
|
||||
```text
|
||||
solana-leader-schedule
|
||||
Display leader schedule
|
||||
|
||||
USAGE:
|
||||
solana leader-schedule [FLAGS] [OPTIONS]
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
--skip-seed-phrase-validation Skip validation of seed phrases. Use this if your phrase does not use the BIP39
|
||||
official English word list
|
||||
-V, --version Prints version information
|
||||
-v, --verbose Show extra information header
|
||||
|
||||
OPTIONS:
|
||||
--ask-seed-phrase <KEYPAIR NAME> Recover a keypair using a seed phrase and optional passphrase [possible
|
||||
values: keypair]
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
```
|
||||
|
||||
#### solana-live-slots
|
||||
```text
|
||||
solana-live-slots
|
||||
Show information about the current slot progression
|
||||
|
||||
USAGE:
|
||||
solana live-slots [FLAGS] [OPTIONS]
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
--skip-seed-phrase-validation Skip validation of seed phrases. Use this if your phrase does not use the BIP39
|
||||
official English word list
|
||||
-V, --version Prints version information
|
||||
-v, --verbose Show extra information header
|
||||
|
||||
OPTIONS:
|
||||
--ask-seed-phrase <KEYPAIR NAME> Recover a keypair using a seed phrase and optional passphrase [possible
|
||||
values: keypair]
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
-w, --ws <URL> WebSocket URL for PubSub RPC connection [default: ws://127.0.0.1:8900]
|
||||
```
|
||||
|
||||
#### solana-new-nonce
|
||||
```text
|
||||
solana-new-nonce
|
||||
@ -1041,7 +1129,7 @@ OPTIONS:
|
||||
Configuration file to use [default: ~/.config/solana/cli/config.yml]
|
||||
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
--nonce-authority <KEYPAIR or PUBKEY>
|
||||
Provide the nonce authority keypair to use when signing a nonced transaction
|
||||
|
||||
@ -1071,7 +1159,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
|
||||
ARGS:
|
||||
<NONCE ACCOUNT> Address of the nonce account to display
|
||||
@ -1099,7 +1187,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
|
||||
ARGS:
|
||||
<NONCE ACCOUNT> Address of the nonce account to display
|
||||
@ -1111,7 +1199,7 @@ solana-pay
|
||||
Send a payment
|
||||
|
||||
USAGE:
|
||||
solana pay [FLAGS] [OPTIONS] <TO PUBKEY> <AMOUNT> [--] [UNIT]
|
||||
solana pay [FLAGS] [OPTIONS] <TO PUBKEY> <AMOUNT>
|
||||
|
||||
FLAGS:
|
||||
--cancelable
|
||||
@ -1126,12 +1214,12 @@ OPTIONS:
|
||||
--ask-seed-phrase <KEYPAIR NAME>
|
||||
Recover a keypair using a seed phrase and optional passphrase [possible values: keypair]
|
||||
|
||||
--blockhash <BLOCKHASH> Use the supplied blockhash
|
||||
--blockhash <BLOCKHASH> Use the supplied blockhash
|
||||
-C, --config <PATH>
|
||||
Configuration file to use [default: ~/.config/solana/cli/config.yml]
|
||||
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
--nonce <PUBKEY>
|
||||
Provide the nonce account to use when creating a nonced
|
||||
transaction. Nonced transactions are useful when a transaction
|
||||
@ -1140,15 +1228,14 @@ OPTIONS:
|
||||
--nonce-authority <KEYPAIR or PUBKEY>
|
||||
Provide the nonce authority keypair to use when signing a nonced transaction
|
||||
|
||||
--signer <PUBKEY=BASE58_SIG>... Provide a public-key/signature pair for the transaction
|
||||
--after <DATETIME> A timestamp after which transaction will execute
|
||||
--require-timestamp-from <PUBKEY> Require timestamp from this third party
|
||||
--require-signature-from <PUBKEY>... Any third party signatures required to unlock the lamports
|
||||
--signer <BASE58_PUBKEY=BASE58_SIG>... Provide a public-key/signature pair for the transaction
|
||||
--after <DATETIME> A timestamp after which transaction will execute
|
||||
--require-timestamp-from <PUBKEY> Require timestamp from this third party
|
||||
--require-signature-from <PUBKEY>... Any third party signatures required to unlock the lamports
|
||||
|
||||
ARGS:
|
||||
<TO PUBKEY> The pubkey of recipient
|
||||
<AMOUNT> The amount to send (default unit SOL)
|
||||
<UNIT> Specify unit to use for request [possible values: SOL, lamports]
|
||||
<AMOUNT> The amount to send, in SOL
|
||||
```
|
||||
|
||||
#### solana-ping
|
||||
@ -1175,7 +1262,7 @@ OPTIONS:
|
||||
-c, --count <NUMBER> Stop after submitting count transactions
|
||||
-i, --interval <SECONDS> Wait interval seconds between submitting the next transaction [default: 2]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
--lamports <NUMBER> Number of lamports to transfer for each transaction [default: 1]
|
||||
-t, --timeout <SECONDS> Wait up to timeout seconds for transaction confirmation [default: 15]
|
||||
```
|
||||
@ -1201,7 +1288,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
|
||||
ARGS:
|
||||
<PUBKEY> The pubkey of recipient
|
||||
@ -1230,41 +1317,13 @@ OPTIONS:
|
||||
~/.config/solana/cli/config.yml]
|
||||
--date <DATETIME> Optional arbitrary timestamp to apply
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
|
||||
ARGS:
|
||||
<PUBKEY> The pubkey of recipient
|
||||
<PROCESS ID> The process id of the transfer to unlock
|
||||
```
|
||||
|
||||
#### solana-show-stake-account
|
||||
```text
|
||||
solana-show-stake-account
|
||||
Show the contents of a stake account
|
||||
|
||||
USAGE:
|
||||
solana show-stake-account [FLAGS] [OPTIONS] <STAKE ACCOUNT>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
--lamports Display balance in lamports instead of SOL
|
||||
--skip-seed-phrase-validation Skip validation of seed phrases. Use this if your phrase does not use the BIP39
|
||||
official English word list
|
||||
-V, --version Prints version information
|
||||
-v, --verbose Show extra information header
|
||||
|
||||
OPTIONS:
|
||||
--ask-seed-phrase <KEYPAIR NAME> Recover a keypair using a seed phrase and optional passphrase [possible
|
||||
values: keypair]
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
|
||||
ARGS:
|
||||
<STAKE ACCOUNT> Address of the stake account to display
|
||||
```
|
||||
|
||||
#### solana-slot
|
||||
```text
|
||||
solana-slot
|
||||
@ -1287,7 +1346,85 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
```
|
||||
|
||||
#### solana-split-stake
|
||||
```text
|
||||
solana-split-stake
|
||||
Split a stake account
|
||||
|
||||
USAGE:
|
||||
solana split-stake [FLAGS] [OPTIONS] <STAKE ACCOUNT> <SPLIT STAKE ACCOUNT> <AMOUNT>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
--sign-only Sign the transaction offline
|
||||
--skip-seed-phrase-validation Skip validation of seed phrases. Use this if your phrase does not use the BIP39
|
||||
official English word list
|
||||
-V, --version Prints version information
|
||||
-v, --verbose Show extra information header
|
||||
|
||||
OPTIONS:
|
||||
--ask-seed-phrase <KEYPAIR NAME>
|
||||
Recover a keypair using a seed phrase and optional passphrase [possible values: keypair]
|
||||
|
||||
--blockhash <BLOCKHASH> Use the supplied blockhash
|
||||
-C, --config <PATH>
|
||||
Configuration file to use [default: ~/.config/solana/cli/config.yml]
|
||||
|
||||
--fee-payer <KEYPAIR or PUBKEY>
|
||||
Specify the fee-payer account. This may be a keypair file, the ASK keyword
|
||||
or the pubkey of an offline signer, provided an appropriate --signer argument
|
||||
is also passed. Defaults to the client keypair.
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
--nonce <PUBKEY>
|
||||
Provide the nonce account to use when creating a nonced
|
||||
transaction. Nonced transactions are useful when a transaction
|
||||
requires a lengthy signing process. Learn more about nonced
|
||||
transactions at https://docs.solana.com/offline-signing/durable-nonce
|
||||
--nonce-authority <KEYPAIR or PUBKEY>
|
||||
Provide the nonce authority keypair to use when signing a nonced transaction
|
||||
|
||||
--seed <SEED STRING>
|
||||
Seed for address generation; if specified, the resulting account will be at a derived address of the SPLIT
|
||||
STAKE ACCOUNT pubkey
|
||||
--signer <BASE58_PUBKEY=BASE58_SIG>... Provide a public-key/signature pair for the transaction
|
||||
--stake-authority <KEYPAIR or PUBKEY> Public key of authorized staker (defaults to cli config pubkey)
|
||||
|
||||
ARGS:
|
||||
<STAKE ACCOUNT> Stake account to be split
|
||||
<SPLIT STAKE ACCOUNT> Keypair of the new stake account to split funds into
|
||||
<AMOUNT> The amount to move into the new stake account, in unit SOL
|
||||
```
|
||||
|
||||
#### solana-stake-account
|
||||
```text
|
||||
solana-stake-account
|
||||
Show the contents of a stake account
|
||||
|
||||
USAGE:
|
||||
solana stake-account [FLAGS] [OPTIONS] <STAKE ACCOUNT>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
--lamports Display balance in lamports instead of SOL
|
||||
--skip-seed-phrase-validation Skip validation of seed phrases. Use this if your phrase does not use the BIP39
|
||||
official English word list
|
||||
-V, --version Prints version information
|
||||
-v, --verbose Show extra information header
|
||||
|
||||
OPTIONS:
|
||||
--ask-seed-phrase <KEYPAIR NAME> Recover a keypair using a seed phrase and optional passphrase [possible
|
||||
values: keypair]
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
|
||||
ARGS:
|
||||
<STAKE ACCOUNT> Address of the stake account to display
|
||||
```
|
||||
|
||||
#### solana-stake-authorize-staker
|
||||
@ -1310,12 +1447,16 @@ OPTIONS:
|
||||
--ask-seed-phrase <KEYPAIR NAME>
|
||||
Recover a keypair using a seed phrase and optional passphrase [possible values: keypair]
|
||||
|
||||
--blockhash <BLOCKHASH> Use the supplied blockhash
|
||||
--blockhash <BLOCKHASH> Use the supplied blockhash
|
||||
-C, --config <PATH>
|
||||
Configuration file to use [default: ~/.config/solana/cli/config.yml]
|
||||
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
--fee-payer <KEYPAIR or PUBKEY>
|
||||
Specify the fee-payer account. This may be a keypair file, the ASK keyword
|
||||
or the pubkey of an offline signer, provided an appropriate --signer argument
|
||||
is also passed. Defaults to the client keypair.
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
--nonce <PUBKEY>
|
||||
Provide the nonce account to use when creating a nonced
|
||||
transaction. Nonced transactions are useful when a transaction
|
||||
@ -1324,8 +1465,8 @@ OPTIONS:
|
||||
--nonce-authority <KEYPAIR or PUBKEY>
|
||||
Provide the nonce authority keypair to use when signing a nonced transaction
|
||||
|
||||
--signer <PUBKEY=BASE58_SIG>... Provide a public-key/signature pair for the transaction
|
||||
--stake-authority <KEYPAIR of PUBKEY> Public key of authorized staker (defaults to cli config pubkey)
|
||||
--signer <BASE58_PUBKEY=BASE58_SIG>... Provide a public-key/signature pair for the transaction
|
||||
--stake-authority <KEYPAIR or PUBKEY> Public key of authorized staker (defaults to cli config pubkey)
|
||||
|
||||
ARGS:
|
||||
<STAKE ACCOUNT> Stake account in which to set the authorized staker
|
||||
@ -1356,8 +1497,12 @@ OPTIONS:
|
||||
-C, --config <PATH>
|
||||
Configuration file to use [default: ~/.config/solana/cli/config.yml]
|
||||
|
||||
--fee-payer <KEYPAIR or PUBKEY>
|
||||
Specify the fee-payer account. This may be a keypair file, the ASK keyword
|
||||
or the pubkey of an offline signer, provided an appropriate --signer argument
|
||||
is also passed. Defaults to the client keypair.
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
--nonce <PUBKEY>
|
||||
Provide the nonce account to use when creating a nonced
|
||||
transaction. Nonced transactions are useful when a transaction
|
||||
@ -1366,7 +1511,7 @@ OPTIONS:
|
||||
--nonce-authority <KEYPAIR or PUBKEY>
|
||||
Provide the nonce authority keypair to use when signing a nonced transaction
|
||||
|
||||
--signer <PUBKEY=BASE58_SIG>... Provide a public-key/signature pair for the transaction
|
||||
--signer <BASE58_PUBKEY=BASE58_SIG>... Provide a public-key/signature pair for the transaction
|
||||
--withdraw-authority <KEYPAIR or PUBKEY> Public key of authorized withdrawer (defaults to cli config pubkey)
|
||||
|
||||
ARGS:
|
||||
@ -1396,7 +1541,61 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
```
|
||||
|
||||
#### solana-stake-set-lockup
|
||||
```text
|
||||
solana-stake-set-lockup
|
||||
Set Lockup for the stake account
|
||||
|
||||
USAGE:
|
||||
solana stake-set-lockup [FLAGS] [OPTIONS] <STAKE ACCOUNT>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
--sign-only Sign the transaction offline
|
||||
--skip-seed-phrase-validation Skip validation of seed phrases. Use this if your phrase does not use the BIP39
|
||||
official English word list
|
||||
-V, --version Prints version information
|
||||
-v, --verbose Show extra information header
|
||||
|
||||
OPTIONS:
|
||||
--ask-seed-phrase <KEYPAIR NAME>
|
||||
Recover a keypair using a seed phrase and optional passphrase [possible values: keypair]
|
||||
|
||||
--blockhash <BLOCKHASH> Use the supplied blockhash
|
||||
-C, --config <PATH>
|
||||
Configuration file to use [default: ~/.config/solana/cli/config.yml]
|
||||
|
||||
--custodian <KEYPAIR or PUBKEY> Public key of signing custodian (defaults to cli config pubkey)
|
||||
--fee-payer <KEYPAIR or PUBKEY>
|
||||
Specify the fee-payer account. This may be a keypair file, the ASK keyword
|
||||
or the pubkey of an offline signer, provided an appropriate --signer argument
|
||||
is also passed. Defaults to the client keypair.
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
--lockup-date <RFC3339 DATE TIME>
|
||||
The date and time at which this account will be available for withdrawal
|
||||
|
||||
--lockup-epoch <EPOCH>
|
||||
The epoch height at which this account will be available for withdrawal
|
||||
|
||||
--new-custodian <KEYPAIR or PUBKEY>
|
||||
Identity of the new lockup custodian (can withdraw before lockup expires)
|
||||
|
||||
--nonce <PUBKEY>
|
||||
Provide the nonce account to use when creating a nonced
|
||||
transaction. Nonced transactions are useful when a transaction
|
||||
requires a lengthy signing process. Learn more about nonced
|
||||
transactions at https://docs.solana.com/offline-signing/durable-nonce
|
||||
--nonce-authority <KEYPAIR or PUBKEY>
|
||||
Provide the nonce authority keypair to use when signing a nonced transaction
|
||||
|
||||
--signer <BASE58_PUBKEY=BASE58_SIG>... Provide a public-key/signature pair for the transaction
|
||||
|
||||
ARGS:
|
||||
<STAKE ACCOUNT> Stake account for which to set Lockup
|
||||
```
|
||||
|
||||
#### solana-stakes
|
||||
@ -1421,7 +1620,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
|
||||
ARGS:
|
||||
<VOTE ACCOUNT PUBKEYS>... Only show stake accounts delegated to the provided vote accounts
|
||||
@ -1448,7 +1647,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
|
||||
ARGS:
|
||||
<STORAGE ACCOUNT PUBKEY> Storage account pubkey
|
||||
@ -1476,7 +1675,53 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
```
|
||||
|
||||
#### solana-transfer
|
||||
```text
|
||||
solana-transfer
|
||||
Transfer funds between system accounts
|
||||
|
||||
USAGE:
|
||||
solana transfer [FLAGS] [OPTIONS] <TO PUBKEY> <AMOUNT>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
--sign-only Sign the transaction offline
|
||||
--skip-seed-phrase-validation Skip validation of seed phrases. Use this if your phrase does not use the BIP39
|
||||
official English word list
|
||||
-V, --version Prints version information
|
||||
-v, --verbose Show extra information header
|
||||
|
||||
OPTIONS:
|
||||
--ask-seed-phrase <KEYPAIR NAME>
|
||||
Recover a keypair using a seed phrase and optional passphrase [possible values: keypair]
|
||||
|
||||
--blockhash <BLOCKHASH> Use the supplied blockhash
|
||||
-C, --config <PATH>
|
||||
Configuration file to use [default: ~/.config/solana/cli/config.yml]
|
||||
|
||||
--fee-payer <KEYPAIR or PUBKEY>
|
||||
Specify the fee-payer account. This may be a keypair file, the ASK keyword
|
||||
or the pubkey of an offline signer, provided an appropriate --signer argument
|
||||
is also passed. Defaults to the client keypair.
|
||||
--from <KEYPAIR or PUBKEY> Source account of funds (if different from client local account)
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
--nonce <PUBKEY>
|
||||
Provide the nonce account to use when creating a nonced
|
||||
transaction. Nonced transactions are useful when a transaction
|
||||
requires a lengthy signing process. Learn more about nonced
|
||||
transactions at https://docs.solana.com/offline-signing/durable-nonce
|
||||
--nonce-authority <KEYPAIR or PUBKEY>
|
||||
Provide the nonce authority keypair to use when signing a nonced transaction
|
||||
|
||||
--signer <BASE58_PUBKEY=BASE58_SIG>... Provide a public-key/signature pair for the transaction
|
||||
|
||||
ARGS:
|
||||
<TO PUBKEY> The pubkey of recipient
|
||||
<AMOUNT> The amount to send, in SOL
|
||||
```
|
||||
|
||||
#### solana-validator-info
|
||||
@ -1500,7 +1745,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
|
||||
SUBCOMMANDS:
|
||||
get Get and parse Solana Validator info
|
||||
@ -1530,7 +1775,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
```
|
||||
|
||||
#### solana-vote-account
|
||||
@ -1555,7 +1800,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
|
||||
ARGS:
|
||||
<VOTE ACCOUNT PUBKEY> Vote account pubkey
|
||||
@ -1582,7 +1827,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
|
||||
ARGS:
|
||||
<VOTE ACCOUNT PUBKEY> Vote account in which to set the authorized voter
|
||||
@ -1610,7 +1855,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
|
||||
ARGS:
|
||||
<VOTE ACCOUNT PUBKEY> Vote account in which to set the authorized withdrawer
|
||||
@ -1638,7 +1883,7 @@ OPTIONS:
|
||||
-C, --config <PATH> Configuration file to use [default:
|
||||
~/.config/solana/cli/config.yml]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
|
||||
ARGS:
|
||||
<VOTE ACCOUNT PUBKEY> Vote account to update
|
||||
@ -1652,7 +1897,7 @@ solana-withdraw-from-nonce-account
|
||||
Withdraw lamports from the nonce account
|
||||
|
||||
USAGE:
|
||||
solana withdraw-from-nonce-account [FLAGS] [OPTIONS] <NONCE ACCOUNT> <DESTINATION ACCOUNT> <AMOUNT> [UNIT]
|
||||
solana withdraw-from-nonce-account [FLAGS] [OPTIONS] <NONCE ACCOUNT> <DESTINATION ACCOUNT> <AMOUNT>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
@ -1669,7 +1914,7 @@ OPTIONS:
|
||||
Configuration file to use [default: ~/.config/solana/cli/config.yml]
|
||||
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
--nonce-authority <KEYPAIR or PUBKEY>
|
||||
Provide the nonce authority keypair to use when signing a nonced transaction
|
||||
|
||||
@ -1677,8 +1922,7 @@ OPTIONS:
|
||||
ARGS:
|
||||
<NONCE ACCOUNT> Nonce account from to withdraw from
|
||||
<DESTINATION ACCOUNT> The account to which the lamports should be transferred
|
||||
<AMOUNT> The amount to withdraw from the nonce account (default unit SOL)
|
||||
<UNIT> Specify unit to use for request [possible values: SOL, lamports]
|
||||
<AMOUNT> The amount to withdraw from the nonce account, in SOL
|
||||
```
|
||||
|
||||
#### solana-withdraw-stake
|
||||
@ -1687,10 +1931,11 @@ solana-withdraw-stake
|
||||
Withdraw the unstaked lamports from the stake account
|
||||
|
||||
USAGE:
|
||||
solana withdraw-stake [FLAGS] [OPTIONS] <STAKE ACCOUNT> <DESTINATION ACCOUNT> <AMOUNT> [UNIT]
|
||||
solana withdraw-stake [FLAGS] [OPTIONS] <STAKE ACCOUNT> <DESTINATION ACCOUNT> <AMOUNT>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
--sign-only Sign the transaction offline
|
||||
--skip-seed-phrase-validation Skip validation of seed phrases. Use this if your phrase does not use the BIP39
|
||||
official English word list
|
||||
-V, --version Prints version information
|
||||
@ -1700,17 +1945,29 @@ OPTIONS:
|
||||
--ask-seed-phrase <KEYPAIR NAME>
|
||||
Recover a keypair using a seed phrase and optional passphrase [possible values: keypair]
|
||||
|
||||
--blockhash <BLOCKHASH> Use the supplied blockhash
|
||||
-C, --config <PATH>
|
||||
Configuration file to use [default: ~/.config/solana/cli/config.yml]
|
||||
|
||||
--fee-payer <KEYPAIR or PUBKEY>
|
||||
Specify the fee-payer account. This may be a keypair file, the ASK keyword
|
||||
or the pubkey of an offline signer, provided an appropriate --signer argument
|
||||
is also passed. Defaults to the client keypair.
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster
|
||||
-k, --keypair <PATH> /path/to/id.json
|
||||
-k, --keypair <PATH> /path/to/id.json or usb://remote/wallet/path
|
||||
--nonce <PUBKEY>
|
||||
Provide the nonce account to use when creating a nonced
|
||||
transaction. Nonced transactions are useful when a transaction
|
||||
requires a lengthy signing process. Learn more about nonced
|
||||
transactions at https://docs.solana.com/offline-signing/durable-nonce
|
||||
--nonce-authority <KEYPAIR or PUBKEY>
|
||||
Provide the nonce authority keypair to use when signing a nonced transaction
|
||||
|
||||
--signer <BASE58_PUBKEY=BASE58_SIG>... Provide a public-key/signature pair for the transaction
|
||||
--withdraw-authority <KEYPAIR or PUBKEY> Public key of authorized withdrawer (defaults to cli config pubkey)
|
||||
|
||||
ARGS:
|
||||
<STAKE ACCOUNT> Stake account from which to withdraw
|
||||
<DESTINATION ACCOUNT> The account to which the lamports should be transferred
|
||||
<AMOUNT> The amount to withdraw from the stake account (default unit SOL)
|
||||
<UNIT> Specify unit to use for request [possible values: SOL, lamports]
|
||||
<AMOUNT> The amount to withdraw from the stake account, in SOL
|
||||
```
|
||||
|
||||
|
@ -154,7 +154,7 @@ FLAGS:
|
||||
|
||||
OPTIONS:
|
||||
-d, --data_dir <PATH> Directory to store install data [default: .../Library/Application Support/solana]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster [default: http://testnet.solana.com:8899]
|
||||
-u, --url <URL> JSON RPC URL for the solana cluster [default: http://devnet.solana.com:8899]
|
||||
-p, --pubkey <PUBKEY> Public key of the update manifest [default: 9XX329sPuskWhH4DQh6k16c87dHKhXLBZTL3Gxmve8Gp]
|
||||
```
|
||||
|
||||
|
@ -22,7 +22,7 @@ At present, the following commands support offline signing:
|
||||
|
||||
To sign a transaction offline, pass the following arguments on the command line
|
||||
1) `--sign-only`, prevents the client from submitting the signed transaction
|
||||
to the network. Instead, the pubkey/signature pairs are printed to stdout.
|
||||
to the network. Instead, the pubkey/signature pairs are printed to stdout.
|
||||
2) `--blockhash BASE58_HASH`, allows the caller to specify the value used to
|
||||
fill the transaction's `recent_blockhash` field. This serves a number of
|
||||
purposes, namely:
|
||||
@ -37,7 +37,7 @@ Command
|
||||
|
||||
```bash
|
||||
solana@offline$ solana pay --sign-only --blockhash 5Tx8F3jgSHx21CbtjwmdaKPLM5tWmreWAnPrbqHomSJF \
|
||||
recipient-keypair.json 1 SOL
|
||||
recipient-keypair.json 1
|
||||
```
|
||||
|
||||
Output
|
||||
@ -67,7 +67,7 @@ Command
|
||||
```bash
|
||||
solana@online$ solana pay --blockhash 5Tx8F3jgSHx21CbtjwmdaKPLM5tWmreWAnPrbqHomSJF \
|
||||
--signer FhtzLVsmcV7S5XqGD79ErgoseCLhZYmEZnz9kQg1Rp7j=4vC38p4bz7XyiXrk6HtaooUqwxTWKocf45cstASGtmrD398biNJnmTcUCVEojE7wVQvgdYbjHJqRFZPpzfCQpmUN
|
||||
recipient-keypair.json 1 SOL
|
||||
recipient-keypair.json 1
|
||||
```
|
||||
|
||||
Output
|
||||
|
@ -36,7 +36,7 @@ A nonce account is created by first generating a new keypair, then create the ac
|
||||
|
||||
```bash
|
||||
solana-keygen new -o nonce-keypair.json
|
||||
solana create-nonce-account nonce-keypair.json 1 SOL
|
||||
solana create-nonce-account nonce-keypair.json 1
|
||||
```
|
||||
|
||||
- Output
|
||||
@ -64,7 +64,7 @@ presently stored nonce value with
|
||||
- Command
|
||||
|
||||
```bash
|
||||
solana nonce nonce-keypair.json
|
||||
solana nonce nonce-keypair.json
|
||||
```
|
||||
|
||||
- Output
|
||||
@ -105,7 +105,7 @@ Inspect a nonce account in a more human friendly format with
|
||||
- Command
|
||||
|
||||
```bash
|
||||
solana nonce-account nonce-keypair.json
|
||||
solana nonce-account nonce-keypair.json
|
||||
```
|
||||
|
||||
- Output
|
||||
@ -127,7 +127,7 @@ Withdraw funds from a nonce account with
|
||||
- Command
|
||||
|
||||
```bash
|
||||
solana withdraw-from-nonce-account nonce-keypair.json ~/.config/solana/id.json 0.5 SOL
|
||||
solana withdraw-from-nonce-account nonce-keypair.json ~/.config/solana/id.json 0.5
|
||||
```
|
||||
|
||||
- Output
|
||||
@ -151,7 +151,7 @@ Reassign the authority of a nonce account after creation with
|
||||
- Command
|
||||
|
||||
```bash
|
||||
solana authorize-nonce-account nonce-keypair.json nonce-authority.json
|
||||
solana authorize-nonce-account nonce-keypair.json nonce-authority.json
|
||||
```
|
||||
|
||||
- Output
|
||||
@ -197,7 +197,7 @@ Alice will need some funds to create a nonce account and send to Bob. Airdrop
|
||||
her some SOL
|
||||
|
||||
```bash
|
||||
$ solana airdrop -k alice.json 10 SOL
|
||||
$ solana airdrop -k alice.json 10
|
||||
10 SOL
|
||||
```
|
||||
|
||||
@ -211,7 +211,7 @@ has full authority over the nonce account
|
||||
{% endhint %}
|
||||
|
||||
```bash
|
||||
$ solana create-nonce-account -k alice.json nonce.json 1 SOL
|
||||
$ solana create-nonce-account -k alice.json nonce.json 1
|
||||
3KPZr96BTsL3hqera9up82KAU462Gz31xjqJ6eHUAjF935Yf8i1kmfEbo6SVbNaACKE5z6gySrNjVRvmS8DcPuwV
|
||||
```
|
||||
|
||||
@ -221,7 +221,7 @@ Alice attempts to pay Bob, but takes too long to sign. The specified blockhash
|
||||
expires and the transaction fails
|
||||
|
||||
```bash
|
||||
$ solana pay -k alice.json --blockhash expiredDTaxfagttWjQweib42b6ZHADSx94Tw8gHx3W7 bob.json 1 SOL
|
||||
$ solana pay -k alice.json --blockhash expiredDTaxfagttWjQweib42b6ZHADSx94Tw8gHx3W7 bob.json 1
|
||||
[2020-01-02T18:48:28.462911000Z ERROR solana_cli::cli] Io(Custom { kind: Other, error: "Transaction \"33gQQaoPc9jWePMvDAeyJpcnSPiGUAdtVg8zREWv4GiKjkcGNufgpcbFyRKRrA25NkgjZySEeKue5rawyeH5TzsV\" failed: None" })
|
||||
Error: Io(Custom { kind: Other, error: "Transaction \"33gQQaoPc9jWePMvDAeyJpcnSPiGUAdtVg8zREWv4GiKjkcGNufgpcbFyRKRrA25NkgjZySEeKue5rawyeH5TzsV\" failed: None" })
|
||||
```
|
||||
@ -236,13 +236,13 @@ Remember, `alice.json` is the [nonce authority](#nonce-authority) in this exampl
|
||||
{% endhint %}
|
||||
|
||||
```bash
|
||||
$ solana nonce-account nonce.json
|
||||
$ solana nonce-account nonce.json
|
||||
balance: 1 SOL
|
||||
minimum balance required: 0.00136416 SOL
|
||||
nonce: F7vmkY3DTaxfagttWjQweib42b6ZHADSx94Tw8gHx3W7
|
||||
```
|
||||
```bash
|
||||
$ solana pay -k alice.json --blockhash F7vmkY3DTaxfagttWjQweib42b6ZHADSx94Tw8gHx3W7 --nonce nonce.json bob.json 1 SOL
|
||||
$ solana pay -k alice.json --blockhash F7vmkY3DTaxfagttWjQweib42b6ZHADSx94Tw8gHx3W7 --nonce nonce.json bob.json 1
|
||||
HR1368UKHVZyenmH7yVz5sBAijV6XAPeWbEiXEGVYQorRMcoijeNAbzZqEZiH8cDB8tk65ckqeegFjK8dHwNFgQ
|
||||
```
|
||||
|
||||
@ -256,7 +256,7 @@ $ solana balance -k bob.json
|
||||
1 SOL
|
||||
```
|
||||
```bash
|
||||
$ solana nonce-account nonce.json
|
||||
$ solana nonce-account nonce.json
|
||||
balance: 1 SOL
|
||||
minimum balance required: 0.00136416 SOL
|
||||
nonce: 6bjroqDcZgTv6Vavhqf81oBHTv3aMnX19UTB51YhAZnN
|
||||
|
@ -90,6 +90,91 @@ For full usage details run:
|
||||
```bash
|
||||
solana-keygen pubkey --help
|
||||
```
|
||||
|
||||
## Verifying the Keypair
|
||||
|
||||
A keypair can be verified by following a variation on the
|
||||
[offline signing](../offline-signing/README.md) procedure with a dummy transaction.
|
||||
|
||||
### Create and Sign a Dummy Transaction
|
||||
|
||||
Use offline signing to acquire the signature of a dummy transaction that can
|
||||
be verified in the next step. A 0 Lamport [transfer](../cli/usage.md#solana-transfer)
|
||||
is used to prevent inadvertent loss of funds. Additionally, an improbable _blockhash_
|
||||
value is specified, as well as using the address of the _system program_ for the `TO`
|
||||
argument, to ensure the transaction would be rejected by the _cluster_ should
|
||||
it be submitted in error.
|
||||
|
||||
Command
|
||||
|
||||
```text
|
||||
solana transfer 11111111111111111111111111111111 0 --sign-only \
|
||||
--ask-seed-phrase keypair --blockhash 11111111111111111111111111111111
|
||||
```
|
||||
|
||||
Prompt for seed phrase
|
||||
|
||||
```text
|
||||
[keypair] seed phrase:
|
||||
[keypair] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue:
|
||||
Recovered pubkey `AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi`. Continue? (y/n): y
|
||||
```
|
||||
|
||||
Output
|
||||
|
||||
```text
|
||||
Blockhash: 11111111111111111111111111111111
|
||||
Signers (Pubkey=Signature):
|
||||
AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi=3uZndChSmPoYfaCihC993E7EAHKDsuu53Ge6Dk1K6ULwhJkgcgiHNm9J1Geqq2azW6PKxQTFjC8rMm5bGxRcYWA
|
||||
|
||||
{"blockhash":"11111111111111111111111111111111","signers":["AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi=3uZndChSmPoYfaCihC993E7EAHKDsuu53Ge6Dk1K6ULwhJkgcgiHNm9J1Geqq2azW6PKxQTFjC8rMm5bGxRcYWA"]}
|
||||
```
|
||||
|
||||
### Verify the Signature
|
||||
|
||||
Using the _Signers_ output from the [previous step](#create-and-sign-a-dummy-transaction)
|
||||
to reconstruct the transaction, this time specifying the _pubkey_ and _signature_
|
||||
as in the submission step of [offline signing](../offline-signing/README.md). That is, the `--from` and
|
||||
`--fee-payer` are explicitly set to the _pubkey_ rather than being taken from
|
||||
the keypair (which is not queried this time).
|
||||
|
||||
Command
|
||||
|
||||
```text
|
||||
solana transfer 11111111111111111111111111111111 0 --sign-only --from AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi \
|
||||
--signer AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi=3uZndChSmPoYfaCihC993E7EAHKDsuu53Ge6Dk1K6ULwhJkgcgiHNm9J1Geqq2azW6PKxQTFjC8rMm5bGxRcYWA \
|
||||
--blockhash 11111111111111111111111111111111 --fee-payer AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi
|
||||
```
|
||||
|
||||
Output
|
||||
|
||||
```text
|
||||
Blockhash: 11111111111111111111111111111111
|
||||
Signers (Pubkey=Signature):
|
||||
AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi=3uZndChSmPoYfaCihC993E7EAHKDsuu53Ge6Dk1K6ULwhJkgcgiHNm9J1Geqq2azW6PKxQTFjC8rMm5bGxRcYWA
|
||||
|
||||
{"blockhash":"11111111111111111111111111111111","signers":["AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi=3uZndChSmPoYfaCihC993E7EAHKDsuu53Ge6Dk1K6ULwhJkgcgiHNm9J1Geqq2azW6PKxQTFjC8rMm5bGxRcYWA"]}
|
||||
```
|
||||
|
||||
### An Example of Failure
|
||||
|
||||
To simulate an error the [verification step](#verify-the-signature) is repeated,
|
||||
but with a corrupted _signature_ (the last letter is changed from "A" to "B").
|
||||
|
||||
Command
|
||||
|
||||
```text
|
||||
solana transfer 11111111111111111111111111111111 0 --sign-only --from AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi \
|
||||
--signer AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi=3uZndChSmPoYfaCihC993E7EAHKDsuu53Ge6Dk1K6ULwhJkgcgiHNm9J1Geqq2azW6PKxQTFjC8rMm5bGxRcYWB \
|
||||
--blockhash 11111111111111111111111111111111 --fee-payer AjTz9EX6vXB6EboKpFm7SwrbDannb6icjvEE632D3rfi
|
||||
```
|
||||
|
||||
Output (Error)
|
||||
|
||||
```text
|
||||
Error: BadParameter("Transaction construction failed, incorrect signature or public key provided")
|
||||
```
|
||||
|
||||
## Checking Account Balance
|
||||
|
||||
All that is needed to check an account balance is the public key of an account.
|
||||
@ -102,7 +187,7 @@ networked machine.
|
||||
Next, configure the `solana` CLI tool to connect to a particular cluster:
|
||||
|
||||
```bash
|
||||
solana config set --url <CLUSTER URL> # (i.e. http://testnet.solana.com:8899)
|
||||
solana config set --url <CLUSTER URL> # (i.e. http://devnet.solana.com:8899)
|
||||
```
|
||||
|
||||
Finally, to check the balance, run the following command:
|
||||
@ -165,7 +250,7 @@ trigger a seed phrase input prompt for the stake account and use
|
||||
`--ask-seed-phrase keypair` to securely input the funding keypair.
|
||||
|
||||
```bash
|
||||
solana create-stake-account ASK 1 SOL --ask-seed-phrase keypair
|
||||
solana create-stake-account ASK 1 --ask-seed-phrase keypair
|
||||
|
||||
[stake_account] seed phrase: 🔒
|
||||
[stake_account] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue:
|
||||
|
@ -22,7 +22,7 @@ Each CTF test starts with an opaque entry point and a funded keypair. The test s
|
||||
|
||||
```text
|
||||
use crate::contact_info::ContactInfo;
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
pub fn test_this_behavior(
|
||||
entry_point_info: &ContactInfo,
|
||||
funding_keypair: &Keypair,
|
||||
|
@ -8,9 +8,9 @@ Please note some of the information and instructions described here may change i
|
||||
|
||||
Archivers are specialized light clients. They download a part of the ledger \(a.k.a Segment\) and store it. They earn rewards for storing segments.
|
||||
|
||||
The testnet features a validator running at testnet.solana.com, which serves as the entrypoint to the cluster for your archiver node.
|
||||
The testnet features a validator running at devnet.solana.com, which serves as the entrypoint to the cluster for your archiver node.
|
||||
|
||||
Additionally there is a blockexplorer available at [http://testnet.solana.com/](http://testnet.solana.com/).
|
||||
Additionally there is a blockexplorer available at [http://devnet.solana.com/](http://devnet.solana.com/).
|
||||
|
||||
The testnet is configured to reset the ledger daily, or sooner should the hourly automated cluster sanity test fail.
|
||||
|
||||
@ -29,10 +29,10 @@ Before starting an archiver node, sanity check that the cluster is accessible to
|
||||
Fetch the current transaction count over JSON RPC:
|
||||
|
||||
```bash
|
||||
curl -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1, "method":"getTransactionCount"}' http://testnet.solana.com:8899
|
||||
curl -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1, "method":"getTransactionCount"}' http://devnet.solana.com:8899
|
||||
```
|
||||
|
||||
Inspect the blockexplorer at [http://testnet.solana.com/](http://testnet.solana.com/) for activity.
|
||||
Inspect the blockexplorer at [http://devnet.solana.com/](http://devnet.solana.com/) for activity.
|
||||
|
||||
View the [metrics dashboard](https://metrics.solana.com:3000/d/testnet-beta/testnet-monitor-beta?var-testnet=testnet) for more detail on cluster activity.
|
||||
|
||||
@ -95,7 +95,7 @@ Download the binaries by navigating to [https://github.com/solana-labs/solana/re
|
||||
Try running following command to join the gossip network and view all the other nodes in the cluster:
|
||||
|
||||
```bash
|
||||
solana-gossip spy --entrypoint testnet.solana.com:8001
|
||||
solana-gossip spy --entrypoint devnet.solana.com:8001
|
||||
# Press ^C to exit
|
||||
```
|
||||
|
||||
@ -129,7 +129,7 @@ Use solana-keygen to show the public keys for each of the keypairs, they will be
|
||||
```text
|
||||
Then set up the storage accounts for your archiver by running:
|
||||
```bash
|
||||
solana --keypair archiver-keypair.json airdrop 100000 lamports
|
||||
solana --keypair archiver-keypair.json airdrop .0001
|
||||
solana --keypair archiver-keypair.json create-archiver-storage-account $ARCHIVER_IDENTITY $STORAGE_IDENTITY
|
||||
```
|
||||
|
||||
@ -138,7 +138,7 @@ Note: Every time the testnet restarts, run the steps to setup the archiver accou
|
||||
To start the archiver:
|
||||
|
||||
```bash
|
||||
solana-archiver --entrypoint testnet.solana.com:8001 --identity-keypair archiver-keypair.json --storage-keypair storage-keypair.json --ledger archiver-ledger
|
||||
solana-archiver --entrypoint devnet.solana.com:8001 --identity-keypair archiver-keypair.json --storage-keypair storage-keypair.json --ledger archiver-ledger
|
||||
```
|
||||
|
||||
## Verify Archiver Setup
|
||||
@ -146,7 +146,7 @@ solana-archiver --entrypoint testnet.solana.com:8001 --identity-keypair archiver
|
||||
From another console, confirm the IP address and **identity pubkey** of your archiver is visible in the gossip network by running:
|
||||
|
||||
```bash
|
||||
solana-gossip spy --entrypoint testnet.solana.com:8001
|
||||
solana-gossip spy --entrypoint devnet.solana.com:8001
|
||||
```
|
||||
|
||||
Provide the **storage account pubkey** to the `solana storage-account` command to view the recent mining activity from your archiver:
|
||||
|
@ -13,9 +13,7 @@ serve as the entrypoint to the cluster for your validator.
|
||||
|
||||
Current testnet entrypoints:
|
||||
|
||||
* Stable, testnet.solana.com
|
||||
* Beta, beta.testnet.solana.com
|
||||
* Edge, edge.testnet.solana.com
|
||||
* Developer testnet, devnet.solana.com
|
||||
|
||||
Solana may launch special testnets for validator participation; we will provide
|
||||
you with a specific entrypoint URL to use.
|
||||
|
@ -6,7 +6,7 @@ Confirm the IP address and **identity pubkey** of your validator is visible in
|
||||
the gossip network by running:
|
||||
|
||||
```bash
|
||||
solana-gossip spy --entrypoint testnet.solana.com:8001
|
||||
solana-gossip spy --entrypoint devnet.solana.com:8001
|
||||
```
|
||||
|
||||
## Check Your Balance
|
||||
@ -35,13 +35,13 @@ cluster, as well as the health of the cluster:
|
||||
|
||||
```bash
|
||||
# Similar to solana-gossip, you should see your validator in the list of cluster nodes
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getClusterNodes"}' http://testnet.solana.com:8899
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getClusterNodes"}' http://devnet.solana.com:8899
|
||||
# If your validator is properly voting, it should appear in the list of `current` vote accounts. If staked, `stake` should be > 0
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getVoteAccounts"}' http://testnet.solana.com:8899
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getVoteAccounts"}' http://devnet.solana.com:8899
|
||||
# Returns the current leader schedule
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getLeaderSchedule"}' http://testnet.solana.com:8899
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getLeaderSchedule"}' http://devnet.solana.com:8899
|
||||
# Returns info about the current epoch. slotIndex should progress on subsequent calls.
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getEpochInfo"}' http://testnet.solana.com:8899
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getEpochInfo"}' http://devnet.solana.com:8899
|
||||
```
|
||||
|
||||
|
||||
|
@ -35,7 +35,7 @@ solana-keygen new -o ~/validator-stake-keypair.json
|
||||
Now delegate 1 SOL to your validator by first creating your stake account:
|
||||
|
||||
```bash
|
||||
solana create-stake-account ~/validator-stake-keypair.json 1 SOL
|
||||
solana create-stake-account ~/validator-stake-keypair.json 1
|
||||
```
|
||||
|
||||
and then delegating that stake to your validator:
|
||||
|
@ -6,11 +6,11 @@ The solana cli includes `get` and `set` configuration commands to automatically
|
||||
set the `--url` argument for cli commands. For example:
|
||||
|
||||
```bash
|
||||
solana config set --url http://testnet.solana.com:8899
|
||||
solana config set --url http://devnet.solana.com:8899
|
||||
```
|
||||
|
||||
\(You can always override the set configuration by explicitly passing the
|
||||
`--url` argument with a command, eg: `solana --url http://beta.testnet.solana.com:8899 balance`\)
|
||||
`--url` argument with a command, eg: `solana --url http://beta.devnet.solana.com:8899 balance`\)
|
||||
|
||||
## Confirm The Testnet Is Reachable
|
||||
|
||||
@ -33,7 +33,7 @@ Try running following command to join the gossip network and view all the other
|
||||
nodes in the cluster:
|
||||
|
||||
```bash
|
||||
solana-gossip spy --entrypoint testnet.solana.com:8001
|
||||
solana-gossip spy --entrypoint devnet.solana.com:8001
|
||||
# Press ^C to exit
|
||||
```
|
||||
|
||||
@ -107,7 +107,7 @@ You should see the following output:
|
||||
|
||||
```text
|
||||
Wallet Config Updated: /home/solana/.config/solana/wallet/config.yml
|
||||
* url: http://testnet.solana.com:8899
|
||||
* url: http://devnet.solana.com:8899
|
||||
* keypair: /home/solana/validator-keypair.json
|
||||
```
|
||||
|
||||
@ -155,7 +155,7 @@ Connect to a testnet cluster by running:
|
||||
|
||||
```bash
|
||||
solana-validator --identity-keypair ~/validator-keypair.json --voting-keypair ~/validator-vote-keypair.json \
|
||||
--ledger ~/validator-ledger --rpc-port 8899 --entrypoint testnet.solana.com:8001 \
|
||||
--ledger ~/validator-ledger --rpc-port 8899 --entrypoint devnet.solana.com:8001 \
|
||||
--limit-ledger-size
|
||||
```
|
||||
|
||||
@ -166,7 +166,7 @@ Confirm your validator connected to the network by opening a new terminal and
|
||||
running:
|
||||
|
||||
```bash
|
||||
solana-gossip spy --entrypoint testnet.solana.com:8001
|
||||
solana-gossip spy --entrypoint devnet.solana.com:8001
|
||||
```
|
||||
|
||||
If your validator is connected, its public key and IP address will appear in the list.
|
||||
|
@ -5,8 +5,7 @@ that serves as an entrypoint to the cluster.
|
||||
|
||||
Current testnet entrypoints:
|
||||
|
||||
* Stable: testnet.solana.com
|
||||
* Beta: beta.testnet.solana.com
|
||||
* Stable: devnet.solana.com
|
||||
|
||||
Application developers should target the Stable testnet. Key differences
|
||||
between the Stable testnet and what will be mainnet:
|
||||
@ -28,13 +27,13 @@ You can submit a JSON-RPC request to see the specific software version of the
|
||||
cluster. Use this to specify [the software version to install](validator-software.md).
|
||||
|
||||
```bash
|
||||
curl -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1, "method":"getVersion"}' testnet.solana.com:8899
|
||||
curl -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1, "method":"getVersion"}' devnet.solana.com:8899
|
||||
```
|
||||
Example result:
|
||||
`{"jsonrpc":"2.0","result":{"solana-core":"0.21.0"},"id":1}`
|
||||
|
||||
## Using a Different Testnet
|
||||
|
||||
This guide is written in the context of testnet.solana.com, our most stable
|
||||
This guide is written in the context of devnet.solana.com, our most stable
|
||||
cluster. To participate in another testnet, modify the commands in the following
|
||||
pages, replacing `testnet.solana.com` with your desired testnet.
|
||||
pages, replacing `devnet.solana.com` with your desired testnet.
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-chacha-cuda"
|
||||
version = "0.23.5"
|
||||
version = "0.23.6"
|
||||
description = "Solana Chacha Cuda APIs"
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@ -10,12 +10,12 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.8"
|
||||
solana-archiver-utils = { path = "../archiver-utils", version = "0.23.5" }
|
||||
solana-chacha = { path = "../chacha", version = "0.23.5" }
|
||||
solana-ledger = { path = "../ledger", version = "0.23.5" }
|
||||
solana-logger = { path = "../logger", version = "0.23.5" }
|
||||
solana-perf = { path = "../perf", version = "0.23.5" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.5" }
|
||||
solana-archiver-utils = { path = "../archiver-utils", version = "0.23.6" }
|
||||
solana-chacha = { path = "../chacha", version = "0.23.6" }
|
||||
solana-ledger = { path = "../ledger", version = "0.23.6" }
|
||||
solana-logger = { path = "../logger", version = "0.23.6" }
|
||||
solana-perf = { path = "../perf", version = "0.23.6" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.6" }
|
||||
|
||||
[dev-dependencies]
|
||||
hex-literal = "0.2.1"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-chacha-sys"
|
||||
version = "0.23.5"
|
||||
version = "0.23.6"
|
||||
description = "Solana chacha-sys"
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-chacha"
|
||||
version = "0.23.5"
|
||||
version = "0.23.6"
|
||||
description = "Solana Chacha APIs"
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@ -12,11 +12,11 @@ edition = "2018"
|
||||
log = "0.4.8"
|
||||
rand = "0.6.5"
|
||||
rand_chacha = "0.1.1"
|
||||
solana-chacha-sys = { path = "../chacha-sys", version = "0.23.5" }
|
||||
solana-ledger = { path = "../ledger", version = "0.23.5" }
|
||||
solana-logger = { path = "../logger", version = "0.23.5" }
|
||||
solana-perf = { path = "../perf", version = "0.23.5" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.5" }
|
||||
solana-chacha-sys = { path = "../chacha-sys", version = "0.23.6" }
|
||||
solana-ledger = { path = "../ledger", version = "0.23.6" }
|
||||
solana-logger = { path = "../logger", version = "0.23.6" }
|
||||
solana-perf = { path = "../perf", version = "0.23.6" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.6" }
|
||||
|
||||
[dev-dependencies]
|
||||
hex-literal = "0.2.1"
|
||||
|
@ -81,7 +81,7 @@ mod tests {
|
||||
use solana_ledger::get_tmp_ledger_path;
|
||||
use solana_sdk::hash::{hash, Hash, Hasher};
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use solana_sdk::system_transaction;
|
||||
use std::fs::remove_file;
|
||||
use std::fs::File;
|
||||
|
@ -234,7 +234,7 @@ sanity() {
|
||||
(
|
||||
set -x
|
||||
NO_INSTALL_CHECK=1 \
|
||||
ci/testnet-sanity.sh edge-testnet-solana-com gce -P us-west1-b
|
||||
ci/testnet-sanity.sh edge-devnet-solana-com gce -P us-west1-b
|
||||
maybe_deploy_software
|
||||
)
|
||||
;;
|
||||
@ -249,7 +249,7 @@ sanity() {
|
||||
(
|
||||
set -x
|
||||
NO_INSTALL_CHECK=1 \
|
||||
ci/testnet-sanity.sh beta-testnet-solana-com gce -P us-west1-b
|
||||
ci/testnet-sanity.sh beta-devnet-solana-com gce -P us-west1-b
|
||||
maybe_deploy_software --deploy-if-newer
|
||||
)
|
||||
;;
|
||||
@ -263,7 +263,7 @@ sanity() {
|
||||
testnet)
|
||||
(
|
||||
set -x
|
||||
ci/testnet-sanity.sh testnet-solana-com gce -P us-west1-b
|
||||
ci/testnet-sanity.sh devnet-solana-com gce -P us-west1-b
|
||||
)
|
||||
;;
|
||||
testnet-perf)
|
||||
@ -327,9 +327,9 @@ deploy() {
|
||||
testnet-edge)
|
||||
(
|
||||
set -x
|
||||
ci/testnet-deploy.sh -p edge-testnet-solana-com -C gce -z us-west1-b \
|
||||
ci/testnet-deploy.sh -p edge-devnet-solana-com -C gce -z us-west1-b \
|
||||
-t "$CHANNEL_OR_TAG" -n 3 -c 0 -u -P \
|
||||
-a edge-testnet-solana-com --letsencrypt edge.testnet.solana.com \
|
||||
-a edge-devnet-solana-com --letsencrypt edge.devnet.solana.com \
|
||||
--limit-ledger-size \
|
||||
${skipCreate:+-e} \
|
||||
${skipStart:+-s} \
|
||||
@ -352,9 +352,9 @@ deploy() {
|
||||
testnet-beta)
|
||||
(
|
||||
set -x
|
||||
ci/testnet-deploy.sh -p beta-testnet-solana-com -C gce -z us-west1-b \
|
||||
ci/testnet-deploy.sh -p beta-devnet-solana-com -C gce -z us-west1-b \
|
||||
-t "$CHANNEL_OR_TAG" -n 3 -c 0 -u -P \
|
||||
-a beta-testnet-solana-com --letsencrypt beta.testnet.solana.com \
|
||||
-a beta-devnet-solana-com --letsencrypt beta.devnet.solana.com \
|
||||
--limit-ledger-size \
|
||||
${skipCreate:+-e} \
|
||||
${skipStart:+-s} \
|
||||
@ -377,9 +377,9 @@ deploy() {
|
||||
testnet)
|
||||
(
|
||||
set -x
|
||||
ci/testnet-deploy.sh -p testnet-solana-com -C gce -z us-west1-b \
|
||||
ci/testnet-deploy.sh -p devnet-solana-com -C gce -z us-west1-b \
|
||||
-t "$CHANNEL_OR_TAG" -n 0 -c 0 -u -P \
|
||||
-a testnet-solana-com --letsencrypt testnet.solana.com \
|
||||
-a testnet-solana-com --letsencrypt devnet.solana.com \
|
||||
--limit-ledger-size \
|
||||
${skipCreate:+-e} \
|
||||
${skipStart:+-s} \
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-clap-utils"
|
||||
version = "0.23.5"
|
||||
version = "0.23.6"
|
||||
description = "Solana utilities for the clap"
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@ -11,8 +11,8 @@ edition = "2018"
|
||||
[dependencies]
|
||||
clap = "2.33.0"
|
||||
rpassword = "4.0"
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "0.23.5" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.5" }
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "0.23.6" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.6" }
|
||||
tiny-bip39 = "0.7.0"
|
||||
url = "2.1.0"
|
||||
chrono = "0.4"
|
||||
|
@ -6,7 +6,7 @@ use solana_sdk::{
|
||||
clock::UnixTimestamp,
|
||||
native_token::sol_to_lamports,
|
||||
pubkey::Pubkey,
|
||||
signature::{read_keypair_file, Keypair, KeypairUtil, Signature},
|
||||
signature::{read_keypair_file, Keypair, Signature, Signer},
|
||||
};
|
||||
use std::str::FromStr;
|
||||
|
||||
@ -93,12 +93,8 @@ pub fn pubkeys_sigs_of(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<(Pubk
|
||||
})
|
||||
}
|
||||
|
||||
pub fn amount_of(matches: &ArgMatches<'_>, name: &str, unit: &str) -> Option<u64> {
|
||||
if matches.value_of(unit) == Some("lamports") {
|
||||
value_of(matches, name)
|
||||
} else {
|
||||
value_of(matches, name).map(sol_to_lamports)
|
||||
}
|
||||
pub fn lamports_of_sol(matches: &ArgMatches<'_>, name: &str) -> Option<u64> {
|
||||
value_of(matches, name).map(sol_to_lamports)
|
||||
}
|
||||
|
||||
pub fn derivation_of(matches: &ArgMatches<'_>, name: &str) -> Option<DerivationPath> {
|
||||
@ -269,24 +265,21 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_amount_of() {
|
||||
fn test_lamports_of_sol() {
|
||||
let matches = app()
|
||||
.clone()
|
||||
.get_matches_from(vec!["test", "--single", "50", "--unit", "lamports"]);
|
||||
assert_eq!(amount_of(&matches, "single", "unit"), Some(50));
|
||||
assert_eq!(amount_of(&matches, "multiple", "unit"), None);
|
||||
.get_matches_from(vec!["test", "--single", "50"]);
|
||||
assert_eq!(lamports_of_sol(&matches, "single"), Some(50000000000));
|
||||
assert_eq!(lamports_of_sol(&matches, "multiple"), None);
|
||||
let matches = app()
|
||||
.clone()
|
||||
.get_matches_from(vec!["test", "--single", "50", "--unit", "SOL"]);
|
||||
assert_eq!(amount_of(&matches, "single", "unit"), Some(50000000000));
|
||||
.get_matches_from(vec!["test", "--single", "1.5"]);
|
||||
assert_eq!(lamports_of_sol(&matches, "single"), Some(1500000000));
|
||||
assert_eq!(lamports_of_sol(&matches, "multiple"), None);
|
||||
let matches = app()
|
||||
.clone()
|
||||
.get_matches_from(vec!["test", "--single", "1.5", "--unit", "SOL"]);
|
||||
assert_eq!(amount_of(&matches, "single", "unit"), Some(1500000000));
|
||||
let matches = app()
|
||||
.clone()
|
||||
.get_matches_from(vec!["test", "--single", "1.5", "--unit", "lamports"]);
|
||||
assert_eq!(amount_of(&matches, "single", "unit"), None);
|
||||
.get_matches_from(vec!["test", "--single", "0.03"]);
|
||||
assert_eq!(lamports_of_sol(&matches, "single"), Some(30000000));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -3,8 +3,7 @@ use bip39::{Language, Mnemonic, Seed};
|
||||
use clap::values_t;
|
||||
use rpassword::prompt_password_stderr;
|
||||
use solana_sdk::signature::{
|
||||
keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair_file, Keypair,
|
||||
KeypairUtil,
|
||||
keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair_file, Keypair, Signer,
|
||||
};
|
||||
use std::{
|
||||
error,
|
||||
|
@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
edition = "2018"
|
||||
name = "solana-cli-config"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "0.23.5"
|
||||
version = "0.23.6"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
|
@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
edition = "2018"
|
||||
name = "solana-cli"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "0.23.5"
|
||||
version = "0.23.6"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@ -26,27 +26,27 @@ reqwest = { version = "0.10.1", default-features = false, features = ["blocking"
|
||||
serde = "1.0.104"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.44"
|
||||
solana-budget-program = { path = "../programs/budget", version = "0.23.5" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "0.23.5" }
|
||||
solana-cli-config = { path = "../cli-config", version = "0.23.5" }
|
||||
solana-client = { path = "../client", version = "0.23.5" }
|
||||
solana-config-program = { path = "../programs/config", version = "0.23.5" }
|
||||
solana-faucet = { path = "../faucet", version = "0.23.5" }
|
||||
solana-logger = { path = "../logger", version = "0.23.5" }
|
||||
solana-net-utils = { path = "../net-utils", version = "0.23.5" }
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "0.23.5" }
|
||||
solana-runtime = { path = "../runtime", version = "0.23.5" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.5" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "0.23.5" }
|
||||
solana-storage-program = { path = "../programs/storage", version = "0.23.5" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "0.23.5" }
|
||||
solana-vote-signer = { path = "../vote-signer", version = "0.23.5" }
|
||||
solana-budget-program = { path = "../programs/budget", version = "0.23.6" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "0.23.6" }
|
||||
solana-cli-config = { path = "../cli-config", version = "0.23.6" }
|
||||
solana-client = { path = "../client", version = "0.23.6" }
|
||||
solana-config-program = { path = "../programs/config", version = "0.23.6" }
|
||||
solana-faucet = { path = "../faucet", version = "0.23.6" }
|
||||
solana-logger = { path = "../logger", version = "0.23.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "0.23.6" }
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "0.23.6" }
|
||||
solana-runtime = { path = "../runtime", version = "0.23.6" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.6" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "0.23.6" }
|
||||
solana-storage-program = { path = "../programs/storage", version = "0.23.6" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "0.23.6" }
|
||||
solana-vote-signer = { path = "../vote-signer", version = "0.23.6" }
|
||||
titlecase = "1.1.0"
|
||||
url = "2.1.1"
|
||||
|
||||
[dev-dependencies]
|
||||
solana-core = { path = "../core", version = "0.23.5" }
|
||||
solana-budget-program = { path = "../programs/budget", version = "0.23.5" }
|
||||
solana-core = { path = "../core", version = "0.23.6" }
|
||||
solana-budget-program = { path = "../programs/budget", version = "0.23.6" }
|
||||
tempfile = "3.1.0"
|
||||
|
||||
[[bin]]
|
||||
|
184
cli/src/cli.rs
184
cli/src/cli.rs
@ -36,7 +36,7 @@ use solana_sdk::{
|
||||
message::Message,
|
||||
native_token::lamports_to_sol,
|
||||
pubkey::Pubkey,
|
||||
signature::{keypair_from_seed, Keypair, KeypairUtil, Signature},
|
||||
signature::{keypair_from_seed, Keypair, Signature, Signer, SignerError},
|
||||
system_instruction::{self, create_address_with_seed, SystemError, MAX_ADDRESS_SEED_LEN},
|
||||
system_transaction,
|
||||
transaction::{Transaction, TransactionError},
|
||||
@ -407,8 +407,8 @@ pub enum CliCommand {
|
||||
Airdrop {
|
||||
faucet_host: Option<IpAddr>,
|
||||
faucet_port: u16,
|
||||
pubkey: Option<Pubkey>,
|
||||
lamports: u64,
|
||||
use_lamports_unit: bool,
|
||||
},
|
||||
Balance {
|
||||
pubkey: Option<Pubkey>,
|
||||
@ -640,14 +640,14 @@ pub fn parse_command(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, Box<dyn
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let lamports = required_lamports_from(matches, "amount", "unit")?;
|
||||
let use_lamports_unit = matches.value_of("unit") == Some("lamports");
|
||||
let pubkey = pubkey_of(&matches, "to");
|
||||
let lamports = lamports_of_sol(matches, "amount").unwrap();
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::Airdrop {
|
||||
faucet_host,
|
||||
faucet_port,
|
||||
pubkey,
|
||||
lamports,
|
||||
use_lamports_unit,
|
||||
},
|
||||
require_keypair: true,
|
||||
})
|
||||
@ -680,7 +680,7 @@ pub fn parse_command(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, Box<dyn
|
||||
}
|
||||
},
|
||||
("pay", Some(matches)) => {
|
||||
let lamports = required_lamports_from(matches, "amount", "unit")?;
|
||||
let lamports = lamports_of_sol(matches, "amount").unwrap();
|
||||
let to = pubkey_of(&matches, "to").unwrap();
|
||||
let timestamp = if matches.is_present("timestamp") {
|
||||
// Parse input for serde_json
|
||||
@ -764,7 +764,7 @@ pub fn parse_command(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, Box<dyn
|
||||
})
|
||||
}
|
||||
("transfer", Some(matches)) => {
|
||||
let lamports = required_lamports_from(matches, "amount", "unit")?;
|
||||
let lamports = lamports_of_sol(matches, "amount").unwrap();
|
||||
let to = pubkey_of(&matches, "to").unwrap();
|
||||
let sign_only = matches.is_present(SIGN_ONLY_ARG.name);
|
||||
let signers = pubkeys_sigs_of(&matches, SIGNER_ARG.name);
|
||||
@ -942,13 +942,13 @@ fn process_airdrop(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
faucet_addr: &SocketAddr,
|
||||
pubkey: &Option<Pubkey>,
|
||||
lamports: u64,
|
||||
use_lamports_unit: bool,
|
||||
) -> ProcessResult {
|
||||
let pubkey = config.pubkey()?;
|
||||
let pubkey = pubkey.unwrap_or(config.pubkey()?);
|
||||
println!(
|
||||
"Requesting airdrop of {} from {}",
|
||||
build_balance_message(lamports, use_lamports_unit, true),
|
||||
build_balance_message(lamports, false, true),
|
||||
faucet_addr
|
||||
);
|
||||
let previous_balance = match rpc_client.retry_get_balance(&pubkey, 5)? {
|
||||
@ -967,11 +967,7 @@ fn process_airdrop(
|
||||
.retry_get_balance(&pubkey, 5)?
|
||||
.unwrap_or(previous_balance);
|
||||
|
||||
Ok(build_balance_message(
|
||||
current_balance,
|
||||
use_lamports_unit,
|
||||
true,
|
||||
))
|
||||
Ok(build_balance_message(current_balance, false, true))
|
||||
}
|
||||
|
||||
fn process_balance(
|
||||
@ -1852,8 +1848,8 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
||||
CliCommand::Airdrop {
|
||||
faucet_host,
|
||||
faucet_port,
|
||||
pubkey,
|
||||
lamports,
|
||||
use_lamports_unit,
|
||||
} => {
|
||||
let faucet_addr = SocketAddr::new(
|
||||
faucet_host.unwrap_or_else(|| {
|
||||
@ -1869,13 +1865,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
||||
*faucet_port,
|
||||
);
|
||||
|
||||
process_airdrop(
|
||||
&rpc_client,
|
||||
config,
|
||||
&faucet_addr,
|
||||
*lamports,
|
||||
*use_lamports_unit,
|
||||
)
|
||||
process_airdrop(&rpc_client, config, &faucet_addr, pubkey, *lamports)
|
||||
}
|
||||
// Check client balance
|
||||
CliCommand::Balance {
|
||||
@ -1979,13 +1969,13 @@ impl FaucetKeypair {
|
||||
}
|
||||
}
|
||||
|
||||
impl KeypairUtil for FaucetKeypair {
|
||||
impl Signer for FaucetKeypair {
|
||||
/// Return the public key of the keypair used to sign votes
|
||||
fn pubkey(&self) -> Pubkey {
|
||||
self.transaction.message().account_keys[0]
|
||||
}
|
||||
|
||||
fn try_pubkey(&self) -> Result<Pubkey, Box<dyn error::Error>> {
|
||||
fn try_pubkey(&self) -> Result<Pubkey, SignerError> {
|
||||
Ok(self.pubkey())
|
||||
}
|
||||
|
||||
@ -1993,7 +1983,7 @@ impl KeypairUtil for FaucetKeypair {
|
||||
self.transaction.signatures[0]
|
||||
}
|
||||
|
||||
fn try_sign_message(&self, message: &[u8]) -> Result<Signature, Box<dyn error::Error>> {
|
||||
fn try_sign_message(&self, message: &[u8]) -> Result<Signature, SignerError> {
|
||||
Ok(self.sign_message(message))
|
||||
}
|
||||
}
|
||||
@ -2044,22 +2034,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// If clap arg `name` is_required, and specifies an amount of either lamports or SOL, the only way
|
||||
// `amount_of()` can return None is if `name` is an f64 and `unit`== "lamports". This method
|
||||
// catches that case and converts it to an Error.
|
||||
pub(crate) fn required_lamports_from(
|
||||
matches: &ArgMatches<'_>,
|
||||
name: &str,
|
||||
unit: &str,
|
||||
) -> Result<u64, CliError> {
|
||||
amount_of(matches, name, unit).ok_or_else(|| {
|
||||
CliError::BadParameter(format!(
|
||||
"Lamports cannot be fractional: {}",
|
||||
matches.value_of("amount").unwrap()
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn build_balance_message(
|
||||
lamports: u64,
|
||||
use_lamports_unit: bool,
|
||||
@ -2117,15 +2091,15 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
|
||||
.takes_value(true)
|
||||
.validator(is_amount)
|
||||
.required(true)
|
||||
.help("The airdrop amount to request (default unit SOL)"),
|
||||
.help("The airdrop amount to request, in SOL"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("unit")
|
||||
Arg::with_name("to")
|
||||
.index(2)
|
||||
.value_name("UNIT")
|
||||
.value_name("PUBKEY")
|
||||
.takes_value(true)
|
||||
.possible_values(&["SOL", "lamports"])
|
||||
.help("Specify unit to use for request and balance display"),
|
||||
.validator(is_pubkey_or_keypair)
|
||||
.help("The pubkey of airdrop recipient"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
@ -2234,15 +2208,7 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
|
||||
.takes_value(true)
|
||||
.validator(is_amount)
|
||||
.required(true)
|
||||
.help("The amount to send (default unit SOL)"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("unit")
|
||||
.index(3)
|
||||
.value_name("UNIT")
|
||||
.takes_value(true)
|
||||
.possible_values(&["SOL", "lamports"])
|
||||
.help("Specify unit to use for request"),
|
||||
.help("The amount to send, in SOL"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("timestamp")
|
||||
@ -2347,15 +2313,7 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
|
||||
.takes_value(true)
|
||||
.validator(is_amount)
|
||||
.required(true)
|
||||
.help("The amount to send (default unit SOL)"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("unit")
|
||||
.index(3)
|
||||
.value_name("UNIT")
|
||||
.takes_value(true)
|
||||
.possible_values(&["SOL", "lamports"])
|
||||
.help("Specify unit to use for request"),
|
||||
.help("The amount to send, in SOL"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("from")
|
||||
@ -2455,17 +2413,18 @@ mod tests {
|
||||
let dt = Utc.ymd(2018, 9, 19).and_hms(17, 30, 59);
|
||||
|
||||
// Test Airdrop Subcommand
|
||||
let test_airdrop = test_commands
|
||||
.clone()
|
||||
.get_matches_from(vec!["test", "airdrop", "50", "lamports"]);
|
||||
let test_airdrop =
|
||||
test_commands
|
||||
.clone()
|
||||
.get_matches_from(vec!["test", "airdrop", "50", &pubkey_string]);
|
||||
assert_eq!(
|
||||
parse_command(&test_airdrop).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Airdrop {
|
||||
faucet_host: None,
|
||||
faucet_port: solana_faucet::faucet::FAUCET_PORT,
|
||||
lamports: 50,
|
||||
use_lamports_unit: true,
|
||||
pubkey: Some(pubkey),
|
||||
lamports: 50_000_000_000,
|
||||
},
|
||||
require_keypair: true,
|
||||
}
|
||||
@ -2613,18 +2572,15 @@ mod tests {
|
||||
);
|
||||
|
||||
// Test Simple Pay Subcommand
|
||||
let test_pay = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
"pay",
|
||||
&pubkey_string,
|
||||
"50",
|
||||
"lamports",
|
||||
]);
|
||||
let test_pay =
|
||||
test_commands
|
||||
.clone()
|
||||
.get_matches_from(vec!["test", "pay", &pubkey_string, "50"]);
|
||||
assert_eq!(
|
||||
parse_command(&test_pay).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Pay(PayCommand {
|
||||
lamports: 50,
|
||||
lamports: 50_000_000_000,
|
||||
to: pubkey,
|
||||
..PayCommand::default()
|
||||
}),
|
||||
@ -2638,7 +2594,6 @@ mod tests {
|
||||
"pay",
|
||||
&pubkey_string,
|
||||
"50",
|
||||
"lamports",
|
||||
"--require-signature-from",
|
||||
&witness0_string,
|
||||
"--require-signature-from",
|
||||
@ -2648,7 +2603,7 @@ mod tests {
|
||||
parse_command(&test_pay_multiple_witnesses).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Pay(PayCommand {
|
||||
lamports: 50,
|
||||
lamports: 50_000_000_000,
|
||||
to: pubkey,
|
||||
witnesses: Some(vec![witness0, witness1]),
|
||||
..PayCommand::default()
|
||||
@ -2661,7 +2616,6 @@ mod tests {
|
||||
"pay",
|
||||
&pubkey_string,
|
||||
"50",
|
||||
"lamports",
|
||||
"--require-signature-from",
|
||||
&witness0_string,
|
||||
]);
|
||||
@ -2669,7 +2623,7 @@ mod tests {
|
||||
parse_command(&test_pay_single_witness).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Pay(PayCommand {
|
||||
lamports: 50,
|
||||
lamports: 50_000_000_000,
|
||||
to: pubkey,
|
||||
witnesses: Some(vec![witness0]),
|
||||
..PayCommand::default()
|
||||
@ -2684,7 +2638,6 @@ mod tests {
|
||||
"pay",
|
||||
&pubkey_string,
|
||||
"50",
|
||||
"lamports",
|
||||
"--after",
|
||||
"2018-09-19T17:30:59",
|
||||
"--require-timestamp-from",
|
||||
@ -2694,7 +2647,7 @@ mod tests {
|
||||
parse_command(&test_pay_timestamp).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Pay(PayCommand {
|
||||
lamports: 50,
|
||||
lamports: 50_000_000_000,
|
||||
to: pubkey,
|
||||
timestamp: Some(dt),
|
||||
timestamp_pubkey: Some(witness0),
|
||||
@ -2712,7 +2665,6 @@ mod tests {
|
||||
"pay",
|
||||
&pubkey_string,
|
||||
"50",
|
||||
"lamports",
|
||||
"--blockhash",
|
||||
&blockhash_string,
|
||||
"--sign-only",
|
||||
@ -2721,7 +2673,7 @@ mod tests {
|
||||
parse_command(&test_pay).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Pay(PayCommand {
|
||||
lamports: 50,
|
||||
lamports: 50_000_000_000,
|
||||
to: pubkey,
|
||||
blockhash_query: BlockhashQuery::None(blockhash, FeeCalculator::default()),
|
||||
sign_only: true,
|
||||
@ -2740,7 +2692,6 @@ mod tests {
|
||||
"pay",
|
||||
&pubkey_string,
|
||||
"50",
|
||||
"lamports",
|
||||
"--blockhash",
|
||||
&blockhash_string,
|
||||
"--signer",
|
||||
@ -2750,7 +2701,7 @@ mod tests {
|
||||
parse_command(&test_pay).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Pay(PayCommand {
|
||||
lamports: 50,
|
||||
lamports: 50_000_000_000,
|
||||
to: pubkey,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash),
|
||||
signers: Some(vec![(key1, sig1)]),
|
||||
@ -2769,7 +2720,6 @@ mod tests {
|
||||
"pay",
|
||||
&pubkey_string,
|
||||
"50",
|
||||
"lamports",
|
||||
"--blockhash",
|
||||
&blockhash_string,
|
||||
"--signer",
|
||||
@ -2781,7 +2731,7 @@ mod tests {
|
||||
parse_command(&test_pay).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Pay(PayCommand {
|
||||
lamports: 50,
|
||||
lamports: 50_000_000_000,
|
||||
to: pubkey,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash),
|
||||
signers: Some(vec![(key1, sig1), (key2, sig2)]),
|
||||
@ -2797,7 +2747,6 @@ mod tests {
|
||||
"pay",
|
||||
&pubkey_string,
|
||||
"50",
|
||||
"lamports",
|
||||
"--blockhash",
|
||||
&blockhash_string,
|
||||
]);
|
||||
@ -2805,7 +2754,7 @@ mod tests {
|
||||
parse_command(&test_pay).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Pay(PayCommand {
|
||||
lamports: 50,
|
||||
lamports: 50_000_000_000,
|
||||
to: pubkey,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash),
|
||||
..PayCommand::default()
|
||||
@ -2822,7 +2771,6 @@ mod tests {
|
||||
"pay",
|
||||
&pubkey_string,
|
||||
"50",
|
||||
"lamports",
|
||||
"--blockhash",
|
||||
&blockhash_string,
|
||||
"--nonce",
|
||||
@ -2832,7 +2780,7 @@ mod tests {
|
||||
parse_command(&test_pay).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Pay(PayCommand {
|
||||
lamports: 50,
|
||||
lamports: 50_000_000_000,
|
||||
to: pubkey,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash),
|
||||
nonce_account: Some(pubkey),
|
||||
@ -2851,7 +2799,6 @@ mod tests {
|
||||
"pay",
|
||||
&pubkey_string,
|
||||
"50",
|
||||
"lamports",
|
||||
"--blockhash",
|
||||
&blockhash_string,
|
||||
"--nonce",
|
||||
@ -2863,7 +2810,7 @@ mod tests {
|
||||
parse_command(&test_pay).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Pay(PayCommand {
|
||||
lamports: 50,
|
||||
lamports: 50_000_000_000,
|
||||
to: pubkey,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash),
|
||||
nonce_account: Some(pubkey),
|
||||
@ -2885,7 +2832,6 @@ mod tests {
|
||||
"pay",
|
||||
&pubkey_string,
|
||||
"50",
|
||||
"lamports",
|
||||
"--blockhash",
|
||||
&blockhash_string,
|
||||
"--nonce",
|
||||
@ -2899,7 +2845,7 @@ mod tests {
|
||||
parse_command(&test_pay).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Pay(PayCommand {
|
||||
lamports: 50,
|
||||
lamports: 50_000_000_000,
|
||||
to: pubkey,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash),
|
||||
nonce_account: Some(pubkey),
|
||||
@ -2923,7 +2869,6 @@ mod tests {
|
||||
"pay",
|
||||
&pubkey_string,
|
||||
"50",
|
||||
"lamports",
|
||||
"--blockhash",
|
||||
&blockhash_string,
|
||||
"--nonce",
|
||||
@ -2954,7 +2899,6 @@ mod tests {
|
||||
"pay",
|
||||
&pubkey_string,
|
||||
"50",
|
||||
"lamports",
|
||||
"--after",
|
||||
"2018-09-19T17:30:59",
|
||||
"--require-signature-from",
|
||||
@ -2968,7 +2912,7 @@ mod tests {
|
||||
parse_command(&test_pay_multiple_witnesses).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Pay(PayCommand {
|
||||
lamports: 50,
|
||||
lamports: 50_000_000_000,
|
||||
to: pubkey,
|
||||
timestamp: Some(dt),
|
||||
timestamp_pubkey: Some(witness0),
|
||||
@ -3269,11 +3213,12 @@ mod tests {
|
||||
assert_eq!(signature.unwrap(), SIGNATURE.to_string());
|
||||
|
||||
// Need airdrop cases
|
||||
let to = Pubkey::new_rand();
|
||||
config.command = CliCommand::Airdrop {
|
||||
faucet_host: None,
|
||||
faucet_port: 1234,
|
||||
pubkey: Some(to),
|
||||
lamports: 50,
|
||||
use_lamports_unit: true,
|
||||
};
|
||||
assert!(process_command(&config).is_ok());
|
||||
|
||||
@ -3310,8 +3255,8 @@ mod tests {
|
||||
config.command = CliCommand::Airdrop {
|
||||
faucet_host: None,
|
||||
faucet_port: 1234,
|
||||
pubkey: None,
|
||||
lamports: 50,
|
||||
use_lamports_unit: true,
|
||||
};
|
||||
assert!(process_command(&config).is_err());
|
||||
|
||||
@ -3420,35 +3365,13 @@ mod tests {
|
||||
fn test_parse_transfer_subcommand() {
|
||||
let test_commands = app("test", "desc", "version");
|
||||
|
||||
//Test Transfer Subcommand, lamports
|
||||
//Test Transfer Subcommand, SOL
|
||||
let from_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
||||
let from_pubkey = from_keypair.pubkey();
|
||||
let from_string = from_pubkey.to_string();
|
||||
let to_keypair = keypair_from_seed(&[1u8; 32]).unwrap();
|
||||
let to_pubkey = to_keypair.pubkey();
|
||||
let to_string = to_pubkey.to_string();
|
||||
let test_transfer = test_commands
|
||||
.clone()
|
||||
.get_matches_from(vec!["test", "transfer", &to_string, "42", "lamports"]);
|
||||
assert_eq!(
|
||||
parse_command(&test_transfer).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Transfer {
|
||||
lamports: 42,
|
||||
to: to_pubkey,
|
||||
from: None,
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::All,
|
||||
nonce_account: None,
|
||||
nonce_authority: None,
|
||||
fee_payer: None,
|
||||
},
|
||||
require_keypair: true,
|
||||
}
|
||||
);
|
||||
|
||||
//Test Transfer Subcommand, SOL
|
||||
let test_transfer = test_commands
|
||||
.clone()
|
||||
.get_matches_from(vec!["test", "transfer", &to_string, "42"]);
|
||||
@ -3478,7 +3401,6 @@ mod tests {
|
||||
"transfer",
|
||||
&to_string,
|
||||
"42",
|
||||
"lamports",
|
||||
"--blockhash",
|
||||
&blockhash_string,
|
||||
"--sign-only",
|
||||
@ -3487,7 +3409,7 @@ mod tests {
|
||||
parse_command(&test_transfer).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Transfer {
|
||||
lamports: 42,
|
||||
lamports: 42_000_000_000,
|
||||
to: to_pubkey,
|
||||
from: None,
|
||||
sign_only: true,
|
||||
@ -3509,7 +3431,6 @@ mod tests {
|
||||
"transfer",
|
||||
&to_string,
|
||||
"42",
|
||||
"lamports",
|
||||
"--from",
|
||||
&from_string,
|
||||
"--fee-payer",
|
||||
@ -3523,7 +3444,7 @@ mod tests {
|
||||
parse_command(&test_transfer).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Transfer {
|
||||
lamports: 42,
|
||||
lamports: 42_000_000_000,
|
||||
to: to_pubkey,
|
||||
from: Some(from_pubkey.into()),
|
||||
sign_only: false,
|
||||
@ -3548,7 +3469,6 @@ mod tests {
|
||||
"transfer",
|
||||
&to_string,
|
||||
"42",
|
||||
"lamports",
|
||||
"--blockhash",
|
||||
&blockhash_string,
|
||||
"--nonce",
|
||||
@ -3560,7 +3480,7 @@ mod tests {
|
||||
parse_command(&test_transfer).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Transfer {
|
||||
lamports: 42,
|
||||
lamports: 42_000_000_000,
|
||||
to: to_pubkey,
|
||||
from: None,
|
||||
sign_only: false,
|
||||
|
@ -17,7 +17,7 @@ use solana_sdk::{
|
||||
epoch_schedule::Epoch,
|
||||
hash::Hash,
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, KeypairUtil},
|
||||
signature::{Keypair, Signer},
|
||||
system_transaction,
|
||||
};
|
||||
use std::{
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::cli::{
|
||||
build_balance_message, check_account_for_fee, check_unique_pubkeys,
|
||||
log_instruction_custom_error, required_lamports_from, CliCommand, CliCommandInfo, CliConfig,
|
||||
CliError, ProcessResult, SigningAuthority,
|
||||
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult,
|
||||
SigningAuthority,
|
||||
};
|
||||
use crate::offline::BLOCKHASH_ARG;
|
||||
use clap::{App, Arg, ArgMatches, SubCommand};
|
||||
@ -13,7 +13,7 @@ use solana_sdk::{
|
||||
hash::Hash,
|
||||
nonce_state::{Meta, NonceState},
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, KeypairUtil},
|
||||
signature::{Keypair, Signer},
|
||||
system_instruction::{
|
||||
advance_nonce_account, authorize_nonce_account, create_address_with_seed,
|
||||
create_nonce_account, create_nonce_account_with_seed, withdraw_nonce_account, NonceError,
|
||||
@ -121,15 +121,7 @@ impl NonceSubCommands for App<'_, '_> {
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.validator(is_amount)
|
||||
.help("The amount to load the nonce account with (default unit SOL)"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("unit")
|
||||
.index(3)
|
||||
.value_name("UNIT")
|
||||
.takes_value(true)
|
||||
.possible_values(&["SOL", "lamports"])
|
||||
.help("Specify unit to use for request"),
|
||||
.help("The amount to load the nonce account with, in SOL"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(NONCE_AUTHORITY_ARG.name)
|
||||
@ -216,15 +208,7 @@ impl NonceSubCommands for App<'_, '_> {
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.validator(is_amount)
|
||||
.help("The amount to withdraw from the nonce account (default unit SOL)"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("unit")
|
||||
.index(4)
|
||||
.value_name("UNIT")
|
||||
.takes_value(true)
|
||||
.possible_values(&["SOL", "lamports"])
|
||||
.help("Specify unit to use for request"),
|
||||
.help("The amount to withdraw from the nonce account, in SOL"),
|
||||
)
|
||||
.arg(nonce_authority_arg()),
|
||||
)
|
||||
@ -250,7 +234,7 @@ pub fn parse_authorize_nonce_account(matches: &ArgMatches<'_>) -> Result<CliComm
|
||||
pub fn parse_nonce_create_account(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||
let nonce_account = keypair_of(matches, "nonce_account_keypair").unwrap();
|
||||
let seed = matches.value_of("seed").map(|s| s.to_string());
|
||||
let lamports = required_lamports_from(matches, "amount", "unit")?;
|
||||
let lamports = lamports_of_sol(matches, "amount").unwrap();
|
||||
let nonce_authority = pubkey_of(matches, NONCE_AUTHORITY_ARG.name);
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
@ -305,7 +289,7 @@ pub fn parse_withdraw_from_nonce_account(
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let nonce_account = pubkey_of(matches, "nonce_account_keypair").unwrap();
|
||||
let destination_account_pubkey = pubkey_of(matches, "destination_account_pubkey").unwrap();
|
||||
let lamports = required_lamports_from(matches, "amount", "unit")?;
|
||||
let lamports = lamports_of_sol(matches, "amount").unwrap();
|
||||
let nonce_authority =
|
||||
SigningAuthority::new_from_matches(&matches, NONCE_AUTHORITY_ARG.name, None)?;
|
||||
|
||||
@ -689,7 +673,6 @@ mod tests {
|
||||
"create-nonce-account",
|
||||
&keypair_file,
|
||||
"50",
|
||||
"lamports",
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_create_nonce_account).unwrap(),
|
||||
@ -698,7 +681,7 @@ mod tests {
|
||||
nonce_account: read_keypair_file(&keypair_file).unwrap().into(),
|
||||
seed: None,
|
||||
nonce_authority: None,
|
||||
lamports: 50,
|
||||
lamports: 50_000_000_000,
|
||||
},
|
||||
require_keypair: true
|
||||
}
|
||||
@ -710,7 +693,6 @@ mod tests {
|
||||
"create-nonce-account",
|
||||
&keypair_file,
|
||||
"50",
|
||||
"lamports",
|
||||
"--nonce-authority",
|
||||
&authority_keypair_file,
|
||||
]);
|
||||
@ -723,7 +705,7 @@ mod tests {
|
||||
nonce_authority: Some(
|
||||
read_keypair_file(&authority_keypair_file).unwrap().pubkey()
|
||||
),
|
||||
lamports: 50,
|
||||
lamports: 50_000_000_000,
|
||||
},
|
||||
require_keypair: true
|
||||
}
|
||||
@ -806,7 +788,6 @@ mod tests {
|
||||
&keypair_file,
|
||||
&nonce_account_string,
|
||||
"42",
|
||||
"lamports",
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_withdraw_from_nonce_account).unwrap(),
|
||||
@ -815,7 +796,7 @@ mod tests {
|
||||
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
|
||||
nonce_authority: None,
|
||||
destination_account_pubkey: nonce_account_pubkey,
|
||||
lamports: 42
|
||||
lamports: 42_000_000_000
|
||||
},
|
||||
require_keypair: true
|
||||
}
|
||||
@ -827,7 +808,6 @@ mod tests {
|
||||
&keypair_file,
|
||||
&nonce_account_string,
|
||||
"42",
|
||||
"SOL",
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_withdraw_from_nonce_account).unwrap(),
|
||||
@ -849,7 +829,6 @@ mod tests {
|
||||
&keypair_file,
|
||||
&nonce_account_string,
|
||||
"42",
|
||||
"lamports",
|
||||
"--nonce-authority",
|
||||
&authority_keypair_file,
|
||||
]);
|
||||
@ -862,7 +841,7 @@ mod tests {
|
||||
read_keypair_file(&authority_keypair_file).unwrap().into()
|
||||
),
|
||||
destination_account_pubkey: nonce_account_pubkey,
|
||||
lamports: 42
|
||||
lamports: 42_000_000_000
|
||||
},
|
||||
require_keypair: true
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use crate::{
|
||||
cli::{
|
||||
build_balance_message, check_account_for_fee, check_unique_pubkeys, fee_payer_arg,
|
||||
log_instruction_custom_error, nonce_authority_arg, replace_signatures,
|
||||
required_lamports_from, return_signers, CliCommand, CliCommandInfo, CliConfig, CliError,
|
||||
ProcessResult, SigningAuthority, FEE_PAYER_ARG,
|
||||
log_instruction_custom_error, nonce_authority_arg, replace_signatures, return_signers,
|
||||
CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult, SigningAuthority,
|
||||
FEE_PAYER_ARG,
|
||||
},
|
||||
nonce::{check_nonce_account, nonce_arg, NONCE_ARG, NONCE_AUTHORITY_ARG},
|
||||
offline::*,
|
||||
@ -16,7 +16,7 @@ use solana_sdk::signature::{Keypair, Signature};
|
||||
use solana_sdk::{
|
||||
account_utils::StateMut,
|
||||
pubkey::Pubkey,
|
||||
signature::KeypairUtil,
|
||||
signature::Signer,
|
||||
system_instruction::{create_address_with_seed, SystemError},
|
||||
sysvar::{
|
||||
stake_history::{self, StakeHistory},
|
||||
@ -86,15 +86,7 @@ impl StakeSubCommands for App<'_, '_> {
|
||||
.takes_value(true)
|
||||
.validator(is_amount)
|
||||
.required(true)
|
||||
.help("The amount of send to the vote account (default unit SOL)")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("unit")
|
||||
.index(3)
|
||||
.value_name("UNIT")
|
||||
.takes_value(true)
|
||||
.possible_values(&["SOL", "lamports"])
|
||||
.help("Specify unit to use for request")
|
||||
.help("The amount of send to the vote account, in SOL")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("custodian")
|
||||
@ -287,15 +279,7 @@ impl StakeSubCommands for App<'_, '_> {
|
||||
.takes_value(true)
|
||||
.validator(is_amount)
|
||||
.required(true)
|
||||
.help("The amount to move into the new stake account (default unit SOL)")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("unit")
|
||||
.index(4)
|
||||
.value_name("UNIT")
|
||||
.takes_value(true)
|
||||
.possible_values(&["SOL", "lamports"])
|
||||
.help("Specify unit to use for request")
|
||||
.help("The amount to move into the new stake account, in unit SOL")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("seed")
|
||||
@ -338,15 +322,7 @@ impl StakeSubCommands for App<'_, '_> {
|
||||
.takes_value(true)
|
||||
.validator(is_amount)
|
||||
.required(true)
|
||||
.help("The amount to withdraw from the stake account (default unit SOL)")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("unit")
|
||||
.index(4)
|
||||
.value_name("UNIT")
|
||||
.takes_value(true)
|
||||
.possible_values(&["SOL", "lamports"])
|
||||
.help("Specify unit to use for request")
|
||||
.help("The amount to withdraw from the stake account, in SOL")
|
||||
)
|
||||
.arg(withdraw_authority_arg())
|
||||
.offline_args()
|
||||
@ -443,7 +419,7 @@ pub fn parse_stake_create_account(matches: &ArgMatches<'_>) -> Result<CliCommand
|
||||
let custodian = pubkey_of(matches, "custodian").unwrap_or_default();
|
||||
let staker = pubkey_of(matches, STAKE_AUTHORITY_ARG.name);
|
||||
let withdrawer = pubkey_of(matches, WITHDRAW_AUTHORITY_ARG.name);
|
||||
let lamports = required_lamports_from(matches, "amount", "unit")?;
|
||||
let lamports = lamports_of_sol(matches, "amount").unwrap();
|
||||
let sign_only = matches.is_present(SIGN_ONLY_ARG.name);
|
||||
let signers = pubkeys_sigs_of(&matches, SIGNER_ARG.name);
|
||||
let blockhash_query = BlockhashQuery::new_from_matches(matches);
|
||||
@ -557,7 +533,7 @@ pub fn parse_stake_authorize(
|
||||
pub fn parse_split_stake(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||
let stake_account_pubkey = pubkey_of(matches, "stake_account_pubkey").unwrap();
|
||||
let split_stake_account = keypair_of(matches, "split_stake_account").unwrap();
|
||||
let lamports = required_lamports_from(matches, "amount", "unit")?;
|
||||
let lamports = lamports_of_sol(matches, "amount").unwrap();
|
||||
let seed = matches.value_of("seed").map(|s| s.to_string());
|
||||
|
||||
let sign_only = matches.is_present(SIGN_ONLY_ARG.name);
|
||||
@ -622,7 +598,7 @@ pub fn parse_stake_deactivate_stake(matches: &ArgMatches<'_>) -> Result<CliComma
|
||||
pub fn parse_stake_withdraw_stake(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||
let stake_account_pubkey = pubkey_of(matches, "stake_account_pubkey").unwrap();
|
||||
let destination_account_pubkey = pubkey_of(matches, "destination_account_pubkey").unwrap();
|
||||
let lamports = required_lamports_from(matches, "amount", "unit")?;
|
||||
let lamports = lamports_of_sol(matches, "amount").unwrap();
|
||||
let sign_only = matches.is_present(SIGN_ONLY_ARG.name);
|
||||
let signers = pubkeys_sigs_of(&matches, SIGNER_ARG.name);
|
||||
let blockhash_query = BlockhashQuery::new_from_matches(matches);
|
||||
@ -1523,7 +1499,7 @@ mod tests {
|
||||
use solana_sdk::{
|
||||
fee_calculator::FeeCalculator,
|
||||
hash::Hash,
|
||||
signature::{keypair_from_seed, read_keypair_file, write_keypair, KeypairUtil},
|
||||
signature::{keypair_from_seed, read_keypair_file, write_keypair, Signer},
|
||||
};
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
@ -1865,7 +1841,6 @@ mod tests {
|
||||
&custodian_string,
|
||||
"--lockup-epoch",
|
||||
"43",
|
||||
"lamports",
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_create_stake_account).unwrap(),
|
||||
@ -1880,7 +1855,7 @@ mod tests {
|
||||
unix_timestamp: 0,
|
||||
custodian,
|
||||
},
|
||||
lamports: 50,
|
||||
lamports: 50_000_000_000,
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::All,
|
||||
@ -1904,7 +1879,6 @@ mod tests {
|
||||
"create-stake-account",
|
||||
&keypair_file,
|
||||
"50",
|
||||
"lamports",
|
||||
]);
|
||||
|
||||
assert_eq!(
|
||||
@ -1916,7 +1890,7 @@ mod tests {
|
||||
staker: None,
|
||||
withdrawer: None,
|
||||
lockup: Lockup::default(),
|
||||
lamports: 50,
|
||||
lamports: 50_000_000_000,
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
blockhash_query: BlockhashQuery::All,
|
||||
@ -1944,7 +1918,6 @@ mod tests {
|
||||
"create-stake-account",
|
||||
&keypair_file,
|
||||
"50",
|
||||
"lamports",
|
||||
"--blockhash",
|
||||
&nonce_hash_string,
|
||||
"--nonce",
|
||||
@ -1968,7 +1941,7 @@ mod tests {
|
||||
staker: None,
|
||||
withdrawer: None,
|
||||
lockup: Lockup::default(),
|
||||
lamports: 50,
|
||||
lamports: 50_000_000_000,
|
||||
sign_only: false,
|
||||
signers: Some(vec![(offline_pubkey, offline_sig)]),
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(nonce_hash),
|
||||
@ -2270,7 +2243,6 @@ mod tests {
|
||||
&stake_account_string,
|
||||
&stake_account_string,
|
||||
"42",
|
||||
"lamports",
|
||||
]);
|
||||
|
||||
assert_eq!(
|
||||
@ -2279,7 +2251,7 @@ mod tests {
|
||||
command: CliCommand::WithdrawStake {
|
||||
stake_account_pubkey,
|
||||
destination_account_pubkey: stake_account_pubkey,
|
||||
lamports: 42,
|
||||
lamports: 42_000_000_000,
|
||||
withdraw_authority: None,
|
||||
sign_only: false,
|
||||
signers: None,
|
||||
@ -2299,7 +2271,6 @@ mod tests {
|
||||
&stake_account_string,
|
||||
&stake_account_string,
|
||||
"42",
|
||||
"lamports",
|
||||
"--withdraw-authority",
|
||||
&stake_authority_keypair_file,
|
||||
]);
|
||||
@ -2310,7 +2281,7 @@ mod tests {
|
||||
command: CliCommand::WithdrawStake {
|
||||
stake_account_pubkey,
|
||||
destination_account_pubkey: stake_account_pubkey,
|
||||
lamports: 42,
|
||||
lamports: 42_000_000_000,
|
||||
withdraw_authority: Some(
|
||||
read_keypair_file(&stake_authority_keypair_file)
|
||||
.unwrap()
|
||||
@ -2334,7 +2305,6 @@ mod tests {
|
||||
&stake_account_string,
|
||||
&stake_account_string,
|
||||
"42",
|
||||
"lamports",
|
||||
"--withdraw-authority",
|
||||
&stake_authority_keypair_file,
|
||||
"--blockhash",
|
||||
@ -2355,7 +2325,7 @@ mod tests {
|
||||
command: CliCommand::WithdrawStake {
|
||||
stake_account_pubkey,
|
||||
destination_account_pubkey: stake_account_pubkey,
|
||||
lamports: 42,
|
||||
lamports: 42_000_000_000,
|
||||
withdraw_authority: Some(
|
||||
read_keypair_file(&stake_authority_keypair_file)
|
||||
.unwrap()
|
||||
@ -2608,7 +2578,6 @@ mod tests {
|
||||
&keypair_file,
|
||||
&split_stake_account_keypair_file,
|
||||
"50",
|
||||
"lamports",
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_split_stake_account).unwrap(),
|
||||
@ -2625,7 +2594,7 @@ mod tests {
|
||||
.unwrap()
|
||||
.into(),
|
||||
seed: None,
|
||||
lamports: 50,
|
||||
lamports: 50_000_000_000,
|
||||
fee_payer: None,
|
||||
},
|
||||
require_keypair: true
|
||||
@ -2654,7 +2623,6 @@ mod tests {
|
||||
&keypair_file,
|
||||
&split_stake_account_keypair_file,
|
||||
"50",
|
||||
"lamports",
|
||||
"--stake-authority",
|
||||
&stake_auth_string,
|
||||
"--blockhash",
|
||||
@ -2688,7 +2656,7 @@ mod tests {
|
||||
.unwrap()
|
||||
.into(),
|
||||
seed: None,
|
||||
lamports: 50,
|
||||
lamports: 50_000_000_000,
|
||||
fee_payer: Some(nonce_auth_pubkey.into()),
|
||||
},
|
||||
require_keypair: false,
|
||||
|
@ -7,7 +7,7 @@ use solana_clap_utils::{input_parsers::*, input_validators::*};
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_sdk::signature::Keypair;
|
||||
use solana_sdk::{
|
||||
account_utils::StateMut, message::Message, pubkey::Pubkey, signature::KeypairUtil,
|
||||
account_utils::StateMut, message::Message, pubkey::Pubkey, signature::Signer,
|
||||
system_instruction::SystemError, transaction::Transaction,
|
||||
};
|
||||
use solana_storage_program::storage_instruction::{self, StorageAccountType};
|
||||
|
@ -19,7 +19,7 @@ use solana_sdk::{
|
||||
commitment_config::CommitmentConfig,
|
||||
message::Message,
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, KeypairUtil},
|
||||
signature::{Keypair, Signer},
|
||||
transaction::Transaction,
|
||||
};
|
||||
use std::error;
|
||||
|
@ -9,7 +9,7 @@ use solana_sdk::{
|
||||
account::Account,
|
||||
pubkey::Pubkey,
|
||||
signature::Keypair,
|
||||
signature::KeypairUtil,
|
||||
signature::Signer,
|
||||
system_instruction::{create_address_with_seed, SystemError},
|
||||
transaction::Transaction,
|
||||
};
|
||||
|
@ -42,8 +42,8 @@ fn test_cli_deploy_program() {
|
||||
config.command = CliCommand::Airdrop {
|
||||
faucet_host: None,
|
||||
faucet_port: faucet_addr.port(),
|
||||
pubkey: None,
|
||||
lamports: minimum_balance_for_rent_exemption + 1, // min balance for rent exemption + leftover for tx processing
|
||||
use_lamports_unit: true,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
|
||||
|
@ -6,7 +6,7 @@ use solana_faucet::faucet::run_local_faucet;
|
||||
use solana_sdk::{
|
||||
hash::Hash,
|
||||
pubkey::Pubkey,
|
||||
signature::{read_keypair_file, write_keypair, Keypair, KeypairUtil},
|
||||
signature::{read_keypair_file, write_keypair, Keypair, Signer},
|
||||
system_instruction::create_address_with_seed,
|
||||
system_program,
|
||||
};
|
||||
|
@ -11,7 +11,7 @@ use solana_sdk::{
|
||||
fee_calculator::FeeCalculator,
|
||||
nonce_state::NonceState,
|
||||
pubkey::Pubkey,
|
||||
signature::{read_keypair_file, write_keypair, Keypair, KeypairUtil},
|
||||
signature::{read_keypair_file, write_keypair, Keypair, Signer},
|
||||
};
|
||||
use std::fs::remove_dir_all;
|
||||
use std::sync::mpsc::channel;
|
||||
|
@ -2,7 +2,7 @@ use solana_cli::cli::{process_command, CliCommand, CliConfig};
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_core::validator::new_validator_for_tests;
|
||||
use solana_faucet::faucet::run_local_faucet;
|
||||
use solana_sdk::signature::KeypairUtil;
|
||||
use solana_sdk::signature::Signer;
|
||||
use std::fs::remove_dir_all;
|
||||
use std::sync::mpsc::channel;
|
||||
|
||||
@ -18,8 +18,8 @@ fn test_cli_request_airdrop() {
|
||||
bob_config.command = CliCommand::Airdrop {
|
||||
faucet_host: None,
|
||||
faucet_port: faucet_addr.port(),
|
||||
pubkey: None,
|
||||
lamports: 50,
|
||||
use_lamports_unit: true,
|
||||
};
|
||||
|
||||
let sig_response = process_command(&bob_config);
|
||||
|
@ -9,7 +9,7 @@ use solana_sdk::{
|
||||
fee_calculator::FeeCalculator,
|
||||
nonce_state::NonceState,
|
||||
pubkey::Pubkey,
|
||||
signature::{keypair_from_seed, read_keypair_file, write_keypair, Keypair, KeypairUtil},
|
||||
signature::{keypair_from_seed, read_keypair_file, write_keypair, Keypair, Signer},
|
||||
system_instruction::create_address_with_seed,
|
||||
};
|
||||
use solana_stake_program::stake_state::{Lockup, StakeAuthorize, StakeState};
|
||||
|
@ -9,7 +9,7 @@ use solana_sdk::{
|
||||
fee_calculator::FeeCalculator,
|
||||
nonce_state::NonceState,
|
||||
pubkey::Pubkey,
|
||||
signature::{keypair_from_seed, read_keypair_file, write_keypair, KeypairUtil},
|
||||
signature::{keypair_from_seed, read_keypair_file, write_keypair, Signer},
|
||||
};
|
||||
use std::fs::remove_dir_all;
|
||||
use std::sync::mpsc::channel;
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-client"
|
||||
version = "0.23.5"
|
||||
version = "0.23.6"
|
||||
description = "Solana Client"
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@ -19,11 +19,12 @@ reqwest = { version = "0.10.1", default-features = false, features = ["blocking"
|
||||
serde = "1.0.104"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.44"
|
||||
solana-net-utils = { path = "../net-utils", version = "0.23.5" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.5" }
|
||||
solana-net-utils = { path = "../net-utils", version = "0.23.6" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.6" }
|
||||
thiserror = "1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_matches = "1.3.0"
|
||||
jsonrpc-core = "14.0.5"
|
||||
jsonrpc-http-server = "14.0.5"
|
||||
solana-logger = { path = "../logger", version = "0.23.5" }
|
||||
solana-logger = { path = "../logger", version = "0.23.6" }
|
||||
|
@ -1,14 +1,16 @@
|
||||
use crate::rpc_request;
|
||||
use solana_sdk::transaction::TransactionError;
|
||||
use solana_sdk::{signature::SignerError, transaction::TransactionError};
|
||||
use std::{fmt, io};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ClientError {
|
||||
Io(io::Error),
|
||||
Reqwest(reqwest::Error),
|
||||
RpcError(rpc_request::RpcError),
|
||||
SerdeJson(serde_json::error::Error),
|
||||
TransactionError(TransactionError),
|
||||
Io(#[from] io::Error),
|
||||
Reqwest(#[from] reqwest::Error),
|
||||
RpcError(#[from] rpc_request::RpcError),
|
||||
SerdeJson(#[from] serde_json::error::Error),
|
||||
SigningError(#[from] SignerError),
|
||||
TransactionError(#[from] TransactionError),
|
||||
}
|
||||
|
||||
impl fmt::Display for ClientError {
|
||||
@ -16,35 +18,3 @@ impl fmt::Display for ClientError {
|
||||
write!(f, "solana client error")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ClientError {}
|
||||
|
||||
impl From<io::Error> for ClientError {
|
||||
fn from(err: io::Error) -> ClientError {
|
||||
ClientError::Io(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<reqwest::Error> for ClientError {
|
||||
fn from(err: reqwest::Error) -> ClientError {
|
||||
ClientError::Reqwest(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<rpc_request::RpcError> for ClientError {
|
||||
fn from(err: rpc_request::RpcError) -> ClientError {
|
||||
ClientError::RpcError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<serde_json::error::Error> for ClientError {
|
||||
fn from(err: serde_json::error::Error) -> ClientError {
|
||||
ClientError::SerdeJson(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TransactionError> for ClientError {
|
||||
fn from(err: TransactionError) -> ClientError {
|
||||
ClientError::TransactionError(err)
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,8 @@ use solana_sdk::{
|
||||
hash::Hash,
|
||||
inflation::Inflation,
|
||||
pubkey::Pubkey,
|
||||
signature::{KeypairUtil, Signature},
|
||||
signature::Signature,
|
||||
signers::Signers,
|
||||
transaction::{self, Transaction, TransactionError},
|
||||
};
|
||||
use std::{
|
||||
@ -405,10 +406,10 @@ impl RpcClient {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn send_and_confirm_transaction<T: KeypairUtil>(
|
||||
pub fn send_and_confirm_transaction<T: Signers>(
|
||||
&self,
|
||||
transaction: &mut Transaction,
|
||||
signer_keys: &[&T],
|
||||
signer_keys: &T,
|
||||
) -> Result<String, ClientError> {
|
||||
let mut send_retries = 20;
|
||||
loop {
|
||||
@ -456,10 +457,10 @@ impl RpcClient {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_and_confirm_transactions<T: KeypairUtil>(
|
||||
pub fn send_and_confirm_transactions<T: Signers>(
|
||||
&self,
|
||||
mut transactions: Vec<Transaction>,
|
||||
signer_keys: &[&T],
|
||||
signer_keys: &T,
|
||||
) -> Result<(), Box<dyn error::Error>> {
|
||||
let mut send_retries = 5;
|
||||
loop {
|
||||
@ -516,24 +517,22 @@ impl RpcClient {
|
||||
// Re-sign any failed transactions with a new blockhash and retry
|
||||
let (blockhash, _fee_calculator) =
|
||||
self.get_new_blockhash(&transactions_signatures[0].0.message().recent_blockhash)?;
|
||||
transactions = transactions_signatures
|
||||
.into_iter()
|
||||
.map(|(mut transaction, _)| {
|
||||
transaction.sign(signer_keys, blockhash);
|
||||
transaction
|
||||
})
|
||||
.collect();
|
||||
transactions = vec![];
|
||||
for (mut transaction, _) in transactions_signatures.into_iter() {
|
||||
transaction.try_sign(signer_keys, blockhash)?;
|
||||
transactions.push(transaction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resign_transaction<T: KeypairUtil>(
|
||||
pub fn resign_transaction<T: Signers>(
|
||||
&self,
|
||||
tx: &mut Transaction,
|
||||
signer_keys: &[&T],
|
||||
signer_keys: &T,
|
||||
) -> Result<(), ClientError> {
|
||||
let (blockhash, _fee_calculator) =
|
||||
self.get_new_blockhash(&tx.message().recent_blockhash)?;
|
||||
tx.sign(signer_keys, blockhash);
|
||||
tx.try_sign(signer_keys, blockhash)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,8 @@ use solana_sdk::{
|
||||
message::Message,
|
||||
packet::PACKET_DATA_SIZE,
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, KeypairUtil, Signature},
|
||||
signature::{Keypair, Signature, Signer},
|
||||
signers::Signers,
|
||||
system_instruction,
|
||||
timing::duration_as_ms,
|
||||
transaction::{self, Transaction},
|
||||
@ -202,9 +203,9 @@ impl ThinClient {
|
||||
}
|
||||
|
||||
/// Retry sending a signed Transaction to the server for processing
|
||||
pub fn send_and_confirm_transaction(
|
||||
pub fn send_and_confirm_transaction<T: Signers>(
|
||||
&self,
|
||||
keypairs: &[&Keypair],
|
||||
keypairs: &T,
|
||||
transaction: &mut Transaction,
|
||||
tries: usize,
|
||||
pending_confirmations: usize,
|
||||
@ -351,9 +352,13 @@ impl Client for ThinClient {
|
||||
}
|
||||
|
||||
impl SyncClient for ThinClient {
|
||||
fn send_message(&self, keypairs: &[&Keypair], message: Message) -> TransportResult<Signature> {
|
||||
fn send_message<T: Signers>(
|
||||
&self,
|
||||
keypairs: &T,
|
||||
message: Message,
|
||||
) -> TransportResult<Signature> {
|
||||
let (blockhash, _fee_calculator) = self.get_recent_blockhash()?;
|
||||
let mut transaction = Transaction::new(&keypairs, message, blockhash);
|
||||
let mut transaction = Transaction::new(keypairs, message, blockhash);
|
||||
let signature = self.send_and_confirm_transaction(keypairs, &mut transaction, 5, 0)?;
|
||||
Ok(signature)
|
||||
}
|
||||
@ -561,13 +566,13 @@ impl AsyncClient for ThinClient {
|
||||
.send_to(&buf[..], &self.tpu_addr())?;
|
||||
Ok(transaction.signatures[0])
|
||||
}
|
||||
fn async_send_message(
|
||||
fn async_send_message<T: Signers>(
|
||||
&self,
|
||||
keypairs: &[&Keypair],
|
||||
keypairs: &T,
|
||||
message: Message,
|
||||
recent_blockhash: Hash,
|
||||
) -> io::Result<Signature> {
|
||||
let transaction = Transaction::new(&keypairs, message, recent_blockhash);
|
||||
let transaction = Transaction::new(keypairs, message, recent_blockhash);
|
||||
self.async_send_transaction(transaction)
|
||||
}
|
||||
fn async_send_instruction(
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "solana-core"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "0.23.5"
|
||||
version = "0.23.6"
|
||||
documentation = "https://docs.rs/solana"
|
||||
homepage = "https://solana.com/"
|
||||
readme = "../README.md"
|
||||
@ -18,6 +18,7 @@ bincode = "1.2.1"
|
||||
bs58 = "0.3.0"
|
||||
byteorder = "1.3.2"
|
||||
chrono = { version = "0.4.10", features = ["serde"] }
|
||||
compression = "0.1.5"
|
||||
core_affinity = "0.5.10"
|
||||
crc = { version = "1.8.1", optional = true }
|
||||
crossbeam-channel = "0.3"
|
||||
@ -40,26 +41,26 @@ rayon = "1.2.0"
|
||||
serde = "1.0.104"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.44"
|
||||
solana-budget-program = { path = "../programs/budget", version = "0.23.5" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "0.23.5" }
|
||||
solana-client = { path = "../client", version = "0.23.5" }
|
||||
solana-faucet = { path = "../faucet", version = "0.23.5" }
|
||||
solana-budget-program = { path = "../programs/budget", version = "0.23.6" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "0.23.6" }
|
||||
solana-client = { path = "../client", version = "0.23.6" }
|
||||
solana-faucet = { path = "../faucet", version = "0.23.6" }
|
||||
ed25519-dalek = "=1.0.0-pre.1"
|
||||
solana-ledger = { path = "../ledger", version = "0.23.5" }
|
||||
solana-logger = { path = "../logger", version = "0.23.5" }
|
||||
solana-merkle-tree = { path = "../merkle-tree", version = "0.23.5" }
|
||||
solana-metrics = { path = "../metrics", version = "0.23.5" }
|
||||
solana-measure = { path = "../measure", version = "0.23.5" }
|
||||
solana-net-utils = { path = "../net-utils", version = "0.23.5" }
|
||||
solana-chacha-cuda = { path = "../chacha-cuda", version = "0.23.5" }
|
||||
solana-perf = { path = "../perf", version = "0.23.5" }
|
||||
solana-runtime = { path = "../runtime", version = "0.23.5" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.5" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "0.23.5" }
|
||||
solana-storage-program = { path = "../programs/storage", version = "0.23.5" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "0.23.5" }
|
||||
solana-vote-signer = { path = "../vote-signer", version = "0.23.5" }
|
||||
solana-sys-tuner = { path = "../sys-tuner", version = "0.23.5" }
|
||||
solana-ledger = { path = "../ledger", version = "0.23.6" }
|
||||
solana-logger = { path = "../logger", version = "0.23.6" }
|
||||
solana-merkle-tree = { path = "../merkle-tree", version = "0.23.6" }
|
||||
solana-metrics = { path = "../metrics", version = "0.23.6" }
|
||||
solana-measure = { path = "../measure", version = "0.23.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "0.23.6" }
|
||||
solana-chacha-cuda = { path = "../chacha-cuda", version = "0.23.6" }
|
||||
solana-perf = { path = "../perf", version = "0.23.6" }
|
||||
solana-runtime = { path = "../runtime", version = "0.23.6" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.6" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "0.23.6" }
|
||||
solana-storage-program = { path = "../programs/storage", version = "0.23.6" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "0.23.6" }
|
||||
solana-vote-signer = { path = "../vote-signer", version = "0.23.6" }
|
||||
solana-sys-tuner = { path = "../sys-tuner", version = "0.23.6" }
|
||||
symlink = "0.1.0"
|
||||
sys-info = "0.5.8"
|
||||
tempfile = "3.1.0"
|
||||
@ -69,7 +70,7 @@ tokio-codec = "0.1"
|
||||
tokio-fs = "0.1"
|
||||
tokio-io = "0.1"
|
||||
untrusted = "0.7.0"
|
||||
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "0.23.5" }
|
||||
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "0.23.6" }
|
||||
reed-solomon-erasure = { package = "solana-reed-solomon-erasure", version = "4.0.1-3", features = ["simd-accel"] }
|
||||
|
||||
[dev-dependencies]
|
||||
|
@ -21,8 +21,8 @@ use solana_sdk::genesis_config::GenesisConfig;
|
||||
use solana_sdk::hash::Hash;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::signature::Keypair;
|
||||
use solana_sdk::signature::KeypairUtil;
|
||||
use solana_sdk::signature::Signature;
|
||||
use solana_sdk::signature::Signer;
|
||||
use solana_sdk::system_instruction;
|
||||
use solana_sdk::system_transaction;
|
||||
use solana_sdk::timing::{duration_as_us, timestamp};
|
||||
|
@ -3,7 +3,7 @@ extern crate test;
|
||||
|
||||
use solana_ledger::entry::{next_entry_mut, Entry, EntrySlice};
|
||||
use solana_sdk::hash::{hash, Hash};
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use solana_sdk::system_transaction;
|
||||
use test::Bencher;
|
||||
|
||||
|
@ -9,7 +9,7 @@ use solana_ledger::shred::{
|
||||
};
|
||||
use solana_perf::test_tx;
|
||||
use solana_sdk::hash::Hash;
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use std::sync::Arc;
|
||||
use test::Bencher;
|
||||
|
||||
|
@ -11,7 +11,7 @@ use solana_core::sigverify::TransactionSigVerifier;
|
||||
use solana_core::sigverify_stage::SigVerifyStage;
|
||||
use solana_perf::test_tx::test_tx;
|
||||
use solana_sdk::hash::Hash;
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use solana_sdk::system_transaction;
|
||||
use solana_sdk::timing::duration_as_ms;
|
||||
use std::sync::mpsc::channel;
|
||||
|
@ -1029,7 +1029,7 @@ mod tests {
|
||||
use solana_runtime::bank::HashAgeKind;
|
||||
use solana_sdk::{
|
||||
instruction::InstructionError,
|
||||
signature::{Keypair, KeypairUtil},
|
||||
signature::{Keypair, Signer},
|
||||
system_transaction,
|
||||
transaction::TransactionError,
|
||||
};
|
||||
|
@ -180,7 +180,7 @@ mod test {
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
use serde_json::Value;
|
||||
use solana_sdk::hash::Hash;
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use solana_sdk::system_transaction;
|
||||
use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
|
@ -107,7 +107,7 @@ mod test {
|
||||
use solana_ledger::create_new_tmp_ledger;
|
||||
use solana_ledger::entry::{create_ticks, Entry};
|
||||
use solana_sdk::hash::Hash;
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use solana_sdk::system_transaction;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::mpsc::channel;
|
||||
|
@ -259,7 +259,7 @@ mod test {
|
||||
use solana_runtime::bank::Bank;
|
||||
use solana_sdk::hash::Hash;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use std::path::Path;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::mpsc::channel;
|
||||
|
@ -82,7 +82,7 @@ impl BroadcastRun for FailEntryVerificationBroadcastRun {
|
||||
// Broadcast data
|
||||
let all_shred_bufs: Vec<Vec<u8>> = shreds.to_vec().into_iter().map(|s| s.payload).collect();
|
||||
cluster_info
|
||||
.read()
|
||||
.write()
|
||||
.unwrap()
|
||||
.broadcast_shreds(sock, all_shred_bufs, &all_seeds, stakes)?;
|
||||
Ok(())
|
||||
|
@ -264,7 +264,7 @@ impl StandardBroadcastRun {
|
||||
trace!("Broadcasting {:?} shreds", shred_bufs.len());
|
||||
|
||||
cluster_info
|
||||
.read()
|
||||
.write()
|
||||
.unwrap()
|
||||
.broadcast_shreds(sock, shred_bufs, &seeds, stakes)?;
|
||||
|
||||
@ -362,7 +362,7 @@ mod test {
|
||||
use solana_sdk::{
|
||||
clock::Slot,
|
||||
genesis_config::GenesisConfig,
|
||||
signature::{Keypair, KeypairUtil},
|
||||
signature::{Keypair, Signer},
|
||||
};
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::time::Duration;
|
||||
|
@ -12,6 +12,8 @@
|
||||
//! * layer 2 - Everyone else, if layer 1 is `2^10`, layer 2 should be able to fit `2^20` number of nodes.
|
||||
//!
|
||||
//! Bank needs to provide an interface for us to query the stake weight
|
||||
use crate::crds_value::CompressionType::*;
|
||||
use crate::crds_value::EpochIncompleteSlots;
|
||||
use crate::packet::limited_deserialize;
|
||||
use crate::streamer::{PacketReceiver, PacketSender};
|
||||
use crate::{
|
||||
@ -19,15 +21,19 @@ use crate::{
|
||||
crds_gossip::CrdsGossip,
|
||||
crds_gossip_error::CrdsGossipError,
|
||||
crds_gossip_pull::{CrdsFilter, CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS},
|
||||
crds_value::{self, CrdsData, CrdsValue, CrdsValueLabel, EpochSlots, Vote},
|
||||
crds_value::{self, CrdsData, CrdsValue, CrdsValueLabel, EpochSlots, SnapshotHash, Vote},
|
||||
packet::{Packet, PACKET_DATA_SIZE},
|
||||
result::{Error, Result},
|
||||
sendmmsg::{multicast, send_mmsg},
|
||||
weighted_shuffle::{weighted_best, weighted_shuffle},
|
||||
};
|
||||
use bincode::{serialize, serialized_size};
|
||||
use compression::prelude::*;
|
||||
use core::cmp;
|
||||
use itertools::Itertools;
|
||||
use rayon::iter::IntoParallelIterator;
|
||||
use rayon::iter::ParallelIterator;
|
||||
use rayon::ThreadPool;
|
||||
use solana_ledger::{bank_forks::BankForks, staking_utils};
|
||||
use solana_measure::thread_mem_usage;
|
||||
use solana_metrics::{datapoint_debug, inc_new_counter_debug, inc_new_counter_error};
|
||||
@ -36,6 +42,9 @@ use solana_net_utils::{
|
||||
multi_bind_in_range, PortRange,
|
||||
};
|
||||
use solana_perf::packet::{to_packets_with_destination, Packets, PacketsRecycler};
|
||||
use solana_rayon_threadlimit::get_thread_count;
|
||||
use solana_sdk::hash::Hash;
|
||||
use solana_sdk::timing::duration_as_s;
|
||||
use solana_sdk::{
|
||||
clock::{Slot, DEFAULT_MS_PER_SLOT},
|
||||
pubkey::Pubkey,
|
||||
@ -67,6 +76,13 @@ pub const MAX_BLOOM_SIZE: usize = 1018;
|
||||
const MAX_PROTOCOL_PAYLOAD_SIZE: u64 = PACKET_DATA_SIZE as u64 - MAX_PROTOCOL_HEADER_SIZE;
|
||||
/// The largest protocol header size
|
||||
const MAX_PROTOCOL_HEADER_SIZE: u64 = 214;
|
||||
/// A hard limit on incoming gossip messages
|
||||
/// Chosen to be able to handle 1Gbps of pure gossip traffic
|
||||
/// 128MB/PACKET_DATA_SIZE
|
||||
const MAX_GOSSIP_TRAFFIC: usize = 128_000_000 / PACKET_DATA_SIZE;
|
||||
|
||||
const NUM_BITS_PER_BYTE: u64 = 8;
|
||||
const MIN_SIZE_TO_COMPRESS_GZIP: u64 = 64;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum ClusterInfoError {
|
||||
@ -83,6 +99,7 @@ pub struct ClusterInfo {
|
||||
pub(crate) keypair: Arc<Keypair>,
|
||||
/// The network entrypoint
|
||||
entrypoint: Option<ContactInfo>,
|
||||
last_datapoint_submit: Instant,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
@ -182,6 +199,7 @@ impl ClusterInfo {
|
||||
gossip: CrdsGossip::default(),
|
||||
keypair,
|
||||
entrypoint: None,
|
||||
last_datapoint_submit: Instant::now(),
|
||||
};
|
||||
let id = contact_info.id;
|
||||
me.gossip.set_self(&id);
|
||||
@ -307,10 +325,125 @@ impl ClusterInfo {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn push_epoch_slots(&mut self, id: Pubkey, root: Slot, min: Slot, slots: BTreeSet<Slot>) {
|
||||
pub fn compress_incomplete_slots(incomplete_slots: &BTreeSet<Slot>) -> EpochIncompleteSlots {
|
||||
if !incomplete_slots.is_empty() {
|
||||
let first_slot = incomplete_slots
|
||||
.iter()
|
||||
.next()
|
||||
.expect("expected to find at least one slot");
|
||||
let last_slot = incomplete_slots
|
||||
.iter()
|
||||
.next_back()
|
||||
.expect("expected to find last slot");
|
||||
let num_uncompressed_bits = last_slot.saturating_sub(*first_slot) + 1;
|
||||
let num_uncompressed_bytes = if num_uncompressed_bits % NUM_BITS_PER_BYTE > 0 {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
} + num_uncompressed_bits / NUM_BITS_PER_BYTE;
|
||||
let mut uncompressed = vec![0u8; num_uncompressed_bytes as usize];
|
||||
incomplete_slots.iter().for_each(|slot| {
|
||||
let offset_from_first_slot = slot.saturating_sub(*first_slot);
|
||||
let index = offset_from_first_slot / NUM_BITS_PER_BYTE;
|
||||
let bit_index = offset_from_first_slot % NUM_BITS_PER_BYTE;
|
||||
uncompressed[index as usize] |= 1 << bit_index;
|
||||
});
|
||||
if num_uncompressed_bytes >= MIN_SIZE_TO_COMPRESS_GZIP {
|
||||
if let Ok(compressed) = uncompressed
|
||||
.iter()
|
||||
.cloned()
|
||||
.encode(&mut GZipEncoder::new(), Action::Finish)
|
||||
.collect::<std::result::Result<Vec<u8>, _>>()
|
||||
{
|
||||
return EpochIncompleteSlots {
|
||||
first: *first_slot,
|
||||
compression: GZip,
|
||||
compressed_list: compressed,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return EpochIncompleteSlots {
|
||||
first: *first_slot,
|
||||
compression: Uncompressed,
|
||||
compressed_list: uncompressed,
|
||||
};
|
||||
}
|
||||
}
|
||||
EpochIncompleteSlots::default()
|
||||
}
|
||||
|
||||
fn bitmap_to_slot_list(first: Slot, bitmap: &[u8]) -> BTreeSet<Slot> {
|
||||
let mut old_incomplete_slots: BTreeSet<Slot> = BTreeSet::new();
|
||||
bitmap.iter().enumerate().for_each(|(i, val)| {
|
||||
if *val != 0 {
|
||||
(0..8).for_each(|bit_index| {
|
||||
if (1 << bit_index & *val) != 0 {
|
||||
let slot = first + i as u64 * NUM_BITS_PER_BYTE + bit_index as u64;
|
||||
old_incomplete_slots.insert(slot);
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
old_incomplete_slots
|
||||
}
|
||||
|
||||
pub fn decompress_incomplete_slots(slots: &EpochIncompleteSlots) -> BTreeSet<Slot> {
|
||||
match slots.compression {
|
||||
Uncompressed => Self::bitmap_to_slot_list(slots.first, &slots.compressed_list),
|
||||
GZip => {
|
||||
if let Ok(decompressed) = slots
|
||||
.compressed_list
|
||||
.iter()
|
||||
.cloned()
|
||||
.decode(&mut GZipDecoder::new())
|
||||
.collect::<std::result::Result<Vec<u8>, _>>()
|
||||
{
|
||||
Self::bitmap_to_slot_list(slots.first, &decompressed)
|
||||
} else {
|
||||
BTreeSet::new()
|
||||
}
|
||||
}
|
||||
BZip2 => {
|
||||
if let Ok(decompressed) = slots
|
||||
.compressed_list
|
||||
.iter()
|
||||
.cloned()
|
||||
.decode(&mut BZip2Decoder::new())
|
||||
.collect::<std::result::Result<Vec<u8>, _>>()
|
||||
{
|
||||
Self::bitmap_to_slot_list(slots.first, &decompressed)
|
||||
} else {
|
||||
BTreeSet::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_epoch_slots(
|
||||
&mut self,
|
||||
id: Pubkey,
|
||||
root: Slot,
|
||||
min: Slot,
|
||||
slots: BTreeSet<Slot>,
|
||||
incomplete_slots: &BTreeSet<Slot>,
|
||||
) {
|
||||
let compressed = Self::compress_incomplete_slots(incomplete_slots);
|
||||
let now = timestamp();
|
||||
let entry = CrdsValue::new_signed(
|
||||
CrdsData::EpochSlots(EpochSlots::new(id, root, min, slots, now)),
|
||||
CrdsData::EpochSlots(
|
||||
0,
|
||||
EpochSlots::new(id, root, min, slots, vec![compressed], now),
|
||||
),
|
||||
&self.keypair,
|
||||
);
|
||||
self.gossip
|
||||
.process_push_message(&self.id(), vec![entry], now);
|
||||
}
|
||||
|
||||
pub fn push_snapshot_hashes(&mut self, snapshot_hashes: Vec<(Slot, Hash)>) {
|
||||
let now = timestamp();
|
||||
let entry = CrdsValue::new_signed(
|
||||
CrdsData::SnapshotHash(SnapshotHash::new(self.id(), snapshot_hashes, now)),
|
||||
&self.keypair,
|
||||
);
|
||||
self.gossip
|
||||
@ -357,6 +490,31 @@ impl ClusterInfo {
|
||||
(txs, max_ts)
|
||||
}
|
||||
|
||||
pub fn get_snapshot_hash(&self, slot: Slot) -> Vec<(Pubkey, Hash)> {
|
||||
self.gossip
|
||||
.crds
|
||||
.table
|
||||
.values()
|
||||
.filter_map(|x| x.value.snapshot_hash().map(|v| v))
|
||||
.filter_map(|x| {
|
||||
for (table_slot, hash) in &x.hashes {
|
||||
if *table_slot == slot {
|
||||
return Some((x.from, *hash));
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_snapshot_hash_for_node(&self, pubkey: &Pubkey) -> Option<&Vec<(Slot, Hash)>> {
|
||||
self.gossip
|
||||
.crds
|
||||
.table
|
||||
.get(&CrdsValueLabel::SnapshotHash(*pubkey))
|
||||
.map(|x| &x.value.snapshot_hash().unwrap().hashes)
|
||||
}
|
||||
|
||||
pub fn get_epoch_state_for_node(
|
||||
&self,
|
||||
pubkey: &Pubkey,
|
||||
@ -374,19 +532,6 @@ impl ClusterInfo {
|
||||
.map(|x| (x.value.epoch_slots().unwrap(), x.insert_timestamp))
|
||||
}
|
||||
|
||||
pub fn get_gossiped_root_for_node(&self, pubkey: &Pubkey, since: Option<u64>) -> Option<u64> {
|
||||
self.gossip
|
||||
.crds
|
||||
.table
|
||||
.get(&CrdsValueLabel::EpochSlots(*pubkey))
|
||||
.filter(|x| {
|
||||
since
|
||||
.map(|since| x.insert_timestamp > since)
|
||||
.unwrap_or(true)
|
||||
})
|
||||
.map(|x| x.value.epoch_slots().unwrap().root)
|
||||
}
|
||||
|
||||
pub fn get_contact_info_for_node(&self, pubkey: &Pubkey) -> Option<&ContactInfo> {
|
||||
self.gossip
|
||||
.crds
|
||||
@ -772,7 +917,7 @@ impl ClusterInfo {
|
||||
/// broadcast messages from the leader to layer 1 nodes
|
||||
/// # Remarks
|
||||
pub fn broadcast_shreds(
|
||||
&self,
|
||||
&mut self,
|
||||
s: &UdpSocket,
|
||||
shreds: Vec<Vec<u8>>,
|
||||
seeds: &[[u8; 32]],
|
||||
@ -781,11 +926,14 @@ impl ClusterInfo {
|
||||
let (peers, peers_and_stakes) = self.sorted_tvu_peers_and_stakes(stakes);
|
||||
let broadcast_len = peers_and_stakes.len();
|
||||
if broadcast_len == 0 {
|
||||
datapoint_debug!(
|
||||
"cluster_info-num_nodes",
|
||||
("live_count", 1, i64),
|
||||
("broadcast_count", 1, i64)
|
||||
);
|
||||
if duration_as_s(&Instant::now().duration_since(self.last_datapoint_submit)) >= 1.0 {
|
||||
datapoint_info!(
|
||||
"cluster_info-num_nodes",
|
||||
("live_count", 1, i64),
|
||||
("broadcast_count", 1, i64)
|
||||
);
|
||||
self.last_datapoint_submit = Instant::now();
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
let mut packets: Vec<_> = shreds
|
||||
@ -815,11 +963,14 @@ impl ClusterInfo {
|
||||
num_live_peers += 1;
|
||||
}
|
||||
});
|
||||
datapoint_debug!(
|
||||
"cluster_info-num_nodes",
|
||||
("live_count", num_live_peers, i64),
|
||||
("broadcast_count", broadcast_len + 1, i64)
|
||||
);
|
||||
if duration_as_s(&Instant::now().duration_since(self.last_datapoint_submit)) >= 1.0 {
|
||||
datapoint_info!(
|
||||
"cluster_info-num_nodes",
|
||||
("live_count", num_live_peers, i64),
|
||||
("broadcast_count", broadcast_len + 1, i64)
|
||||
);
|
||||
self.last_datapoint_submit = Instant::now();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -1361,10 +1512,22 @@ impl ClusterInfo {
|
||||
bank_forks: Option<&Arc<RwLock<BankForks>>>,
|
||||
requests_receiver: &PacketReceiver,
|
||||
response_sender: &PacketSender,
|
||||
thread_pool: &ThreadPool,
|
||||
) -> Result<()> {
|
||||
//TODO cache connections
|
||||
let timeout = Duration::new(1, 0);
|
||||
let reqs = requests_receiver.recv_timeout(timeout)?;
|
||||
let mut requests = vec![requests_receiver.recv_timeout(timeout)?];
|
||||
let mut num_requests = requests.last().unwrap().packets.len();
|
||||
while let Ok(more_reqs) = requests_receiver.try_recv() {
|
||||
if num_requests >= MAX_GOSSIP_TRAFFIC {
|
||||
continue;
|
||||
}
|
||||
num_requests += more_reqs.packets.len();
|
||||
requests.push(more_reqs)
|
||||
}
|
||||
if num_requests >= MAX_GOSSIP_TRAFFIC {
|
||||
warn!("Too much gossip traffic, ignoring some messages");
|
||||
}
|
||||
let epoch_ms;
|
||||
let stakes: HashMap<_, _> = match bank_forks {
|
||||
Some(ref bank_forks) => {
|
||||
@ -1380,8 +1543,13 @@ impl ClusterInfo {
|
||||
HashMap::new()
|
||||
}
|
||||
};
|
||||
let sender = response_sender.clone();
|
||||
thread_pool.install(|| {
|
||||
requests.into_par_iter().for_each_with(sender, |s, reqs| {
|
||||
Self::handle_packets(obj, &recycler, &stakes, reqs, s, epoch_ms)
|
||||
});
|
||||
});
|
||||
|
||||
Self::handle_packets(obj, &recycler, &stakes, reqs, response_sender, epoch_ms);
|
||||
Ok(())
|
||||
}
|
||||
pub fn listen(
|
||||
@ -1395,26 +1563,33 @@ impl ClusterInfo {
|
||||
let recycler = PacketsRecycler::default();
|
||||
Builder::new()
|
||||
.name("solana-listen".to_string())
|
||||
.spawn(move || loop {
|
||||
let e = Self::run_listen(
|
||||
&me,
|
||||
&recycler,
|
||||
bank_forks.as_ref(),
|
||||
&requests_receiver,
|
||||
&response_sender,
|
||||
);
|
||||
if exit.load(Ordering::Relaxed) {
|
||||
return;
|
||||
}
|
||||
if e.is_err() {
|
||||
let me = me.read().unwrap();
|
||||
debug!(
|
||||
"{}: run_listen timeout, table size: {}",
|
||||
me.gossip.id,
|
||||
me.gossip.crds.table.len()
|
||||
.spawn(move || {
|
||||
let thread_pool = rayon::ThreadPoolBuilder::new()
|
||||
.num_threads(get_thread_count())
|
||||
.build()
|
||||
.unwrap();
|
||||
loop {
|
||||
let e = Self::run_listen(
|
||||
&me,
|
||||
&recycler,
|
||||
bank_forks.as_ref(),
|
||||
&requests_receiver,
|
||||
&response_sender,
|
||||
&thread_pool,
|
||||
);
|
||||
if exit.load(Ordering::Relaxed) {
|
||||
return;
|
||||
}
|
||||
if e.is_err() {
|
||||
let me = me.read().unwrap();
|
||||
debug!(
|
||||
"{}: run_listen timeout, table size: {}",
|
||||
me.gossip.id,
|
||||
me.gossip.crds.table.len()
|
||||
);
|
||||
}
|
||||
thread_mem_usage::datapoint("solana-listen");
|
||||
}
|
||||
thread_mem_usage::datapoint("solana-listen");
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
@ -1730,7 +1905,7 @@ mod tests {
|
||||
use crate::crds_value::CrdsValueLabel;
|
||||
use rayon::prelude::*;
|
||||
use solana_perf::test_tx::test_tx;
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use std::collections::HashSet;
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
use std::sync::{Arc, RwLock};
|
||||
@ -2133,13 +2308,17 @@ mod tests {
|
||||
for i in 0..128 {
|
||||
btree_slots.insert(i);
|
||||
}
|
||||
let value = CrdsValue::new_unsigned(CrdsData::EpochSlots(EpochSlots {
|
||||
from: Pubkey::default(),
|
||||
root: 0,
|
||||
lowest: 0,
|
||||
slots: btree_slots,
|
||||
wallclock: 0,
|
||||
}));
|
||||
let value = CrdsValue::new_unsigned(CrdsData::EpochSlots(
|
||||
0,
|
||||
EpochSlots {
|
||||
from: Pubkey::default(),
|
||||
root: 0,
|
||||
lowest: 0,
|
||||
slots: btree_slots,
|
||||
stash: vec![],
|
||||
wallclock: 0,
|
||||
},
|
||||
));
|
||||
test_split_messages(value);
|
||||
}
|
||||
|
||||
@ -2150,13 +2329,17 @@ mod tests {
|
||||
let payload: Vec<CrdsValue> = vec![];
|
||||
let vec_size = serialized_size(&payload).unwrap();
|
||||
let desired_size = MAX_PROTOCOL_PAYLOAD_SIZE - vec_size;
|
||||
let mut value = CrdsValue::new_unsigned(CrdsData::EpochSlots(EpochSlots {
|
||||
from: Pubkey::default(),
|
||||
root: 0,
|
||||
lowest: 0,
|
||||
slots: BTreeSet::new(),
|
||||
wallclock: 0,
|
||||
}));
|
||||
let mut value = CrdsValue::new_unsigned(CrdsData::EpochSlots(
|
||||
0,
|
||||
EpochSlots {
|
||||
from: Pubkey::default(),
|
||||
root: 0,
|
||||
lowest: 0,
|
||||
slots: BTreeSet::new(),
|
||||
stash: vec![],
|
||||
wallclock: 0,
|
||||
},
|
||||
));
|
||||
|
||||
let mut i = 0;
|
||||
while value.size() <= desired_size {
|
||||
@ -2168,13 +2351,17 @@ mod tests {
|
||||
desired_size
|
||||
);
|
||||
}
|
||||
value.data = CrdsData::EpochSlots(EpochSlots {
|
||||
from: Pubkey::default(),
|
||||
root: 0,
|
||||
lowest: 0,
|
||||
slots,
|
||||
wallclock: 0,
|
||||
});
|
||||
value.data = CrdsData::EpochSlots(
|
||||
0,
|
||||
EpochSlots {
|
||||
from: Pubkey::default(),
|
||||
root: 0,
|
||||
lowest: 0,
|
||||
slots,
|
||||
stash: vec![],
|
||||
wallclock: 0,
|
||||
},
|
||||
);
|
||||
i += 1;
|
||||
}
|
||||
let split = ClusterInfo::split_gossip_messages(vec![value.clone()]);
|
||||
@ -2314,13 +2501,17 @@ mod tests {
|
||||
let other_node_pubkey = Pubkey::new_rand();
|
||||
let other_node = ContactInfo::new_localhost(&other_node_pubkey, timestamp());
|
||||
cluster_info.insert_info(other_node.clone());
|
||||
let value = CrdsValue::new_unsigned(CrdsData::EpochSlots(EpochSlots::new(
|
||||
other_node_pubkey,
|
||||
peer_root,
|
||||
peer_lowest,
|
||||
BTreeSet::new(),
|
||||
timestamp(),
|
||||
)));
|
||||
let value = CrdsValue::new_unsigned(CrdsData::EpochSlots(
|
||||
0,
|
||||
EpochSlots::new(
|
||||
other_node_pubkey,
|
||||
peer_root,
|
||||
peer_lowest,
|
||||
BTreeSet::new(),
|
||||
vec![],
|
||||
timestamp(),
|
||||
),
|
||||
));
|
||||
let _ = cluster_info.gossip.crds.insert(value, timestamp());
|
||||
}
|
||||
// only half the visible peers should be eligible to serve this repair
|
||||
@ -2380,4 +2571,38 @@ mod tests {
|
||||
serialized_size(&protocol).expect("unable to serialize gossip protocol") as usize;
|
||||
PACKET_DATA_SIZE - (protocol_size - filter_size)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compress_incomplete_slots() {
|
||||
let mut incomplete_slots: BTreeSet<Slot> = BTreeSet::new();
|
||||
|
||||
assert_eq!(
|
||||
EpochIncompleteSlots::default(),
|
||||
ClusterInfo::compress_incomplete_slots(&incomplete_slots)
|
||||
);
|
||||
|
||||
incomplete_slots.insert(100);
|
||||
let compressed = ClusterInfo::compress_incomplete_slots(&incomplete_slots);
|
||||
assert_eq!(100, compressed.first);
|
||||
let decompressed = ClusterInfo::decompress_incomplete_slots(&compressed);
|
||||
assert_eq!(incomplete_slots, decompressed);
|
||||
|
||||
incomplete_slots.insert(104);
|
||||
let compressed = ClusterInfo::compress_incomplete_slots(&incomplete_slots);
|
||||
assert_eq!(100, compressed.first);
|
||||
let decompressed = ClusterInfo::decompress_incomplete_slots(&compressed);
|
||||
assert_eq!(incomplete_slots, decompressed);
|
||||
|
||||
incomplete_slots.insert(80);
|
||||
let compressed = ClusterInfo::compress_incomplete_slots(&incomplete_slots);
|
||||
assert_eq!(80, compressed.first);
|
||||
let decompressed = ClusterInfo::decompress_incomplete_slots(&compressed);
|
||||
assert_eq!(incomplete_slots, decompressed);
|
||||
|
||||
incomplete_slots.insert(10000);
|
||||
let compressed = ClusterInfo::compress_incomplete_slots(&incomplete_slots);
|
||||
assert_eq!(80, compressed.first);
|
||||
let decompressed = ClusterInfo::decompress_incomplete_slots(&compressed);
|
||||
assert_eq!(incomplete_slots, decompressed);
|
||||
}
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ impl ClusterInfoVoteListener {
|
||||
mod tests {
|
||||
use crate::packet;
|
||||
use solana_sdk::hash::Hash;
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use solana_sdk::transaction::Transaction;
|
||||
use solana_vote_program::vote_instruction;
|
||||
use solana_vote_program::vote_state::Vote;
|
||||
|
@ -1,6 +1,5 @@
|
||||
use chrono::prelude::*;
|
||||
use solana_ledger::bank_forks::BankForks;
|
||||
use solana_metrics::datapoint_debug;
|
||||
use solana_runtime::bank::Bank;
|
||||
use solana_sdk::{
|
||||
account::Account,
|
||||
@ -118,7 +117,7 @@ impl Tower {
|
||||
vote_state.nth_recent_vote(0).map(|v| v.slot).unwrap_or(0) as i64
|
||||
);
|
||||
debug!("observed root {}", vote_state.root_slot.unwrap_or(0) as i64);
|
||||
datapoint_debug!(
|
||||
datapoint_info!(
|
||||
"tower-observed",
|
||||
(
|
||||
"slot",
|
||||
@ -237,7 +236,7 @@ impl Tower {
|
||||
self.lockouts.process_vote_unchecked(&vote);
|
||||
self.last_vote = vote;
|
||||
|
||||
datapoint_debug!(
|
||||
datapoint_info!(
|
||||
"tower-vote",
|
||||
("latest", slot, i64),
|
||||
("root", self.lockouts.root_slot.unwrap_or(0), i64)
|
||||
|
@ -2,7 +2,7 @@ use solana_sdk::pubkey::Pubkey;
|
||||
#[cfg(test)]
|
||||
use solana_sdk::rpc_port;
|
||||
#[cfg(test)]
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use solana_sdk::timing::timestamp;
|
||||
use std::cmp::{Ord, Ordering, PartialEq, PartialOrd};
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
|
@ -3,11 +3,13 @@
|
||||
//! designed to run with a simulator or over a UDP network connection with messages up to a
|
||||
//! packet::PACKET_DATA_SIZE size.
|
||||
|
||||
use crate::crds::{Crds, VersionedCrdsValue};
|
||||
use crate::crds_gossip_error::CrdsGossipError;
|
||||
use crate::crds_gossip_pull::{CrdsFilter, CrdsGossipPull};
|
||||
use crate::crds_gossip_push::{CrdsGossipPush, CRDS_GOSSIP_NUM_ACTIVE};
|
||||
use crate::crds_value::{CrdsValue, CrdsValueLabel};
|
||||
use crate::{
|
||||
crds::{Crds, VersionedCrdsValue},
|
||||
crds_gossip_error::CrdsGossipError,
|
||||
crds_gossip_pull::{CrdsFilter, CrdsGossipPull},
|
||||
crds_gossip_push::{CrdsGossipPush, CRDS_GOSSIP_NUM_ACTIVE},
|
||||
crds_value::{CrdsValue, CrdsValueLabel},
|
||||
};
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
|
@ -8,25 +8,24 @@
|
||||
//! the local nodes wallclock window they are drooped silently.
|
||||
//! 2. The prune set is stored in a Bloom filter.
|
||||
|
||||
use crate::contact_info::ContactInfo;
|
||||
use crate::crds::{Crds, VersionedCrdsValue};
|
||||
use crate::crds_gossip::{get_stake, get_weight, CRDS_GOSSIP_DEFAULT_BLOOM_ITEMS};
|
||||
use crate::crds_gossip_error::CrdsGossipError;
|
||||
use crate::crds_value::{CrdsValue, CrdsValueLabel};
|
||||
use crate::weighted_shuffle::weighted_shuffle;
|
||||
use crate::{
|
||||
contact_info::ContactInfo,
|
||||
crds::{Crds, VersionedCrdsValue},
|
||||
crds_gossip::{get_stake, get_weight, CRDS_GOSSIP_DEFAULT_BLOOM_ITEMS},
|
||||
crds_gossip_error::CrdsGossipError,
|
||||
crds_value::{CrdsValue, CrdsValueLabel},
|
||||
weighted_shuffle::weighted_shuffle,
|
||||
};
|
||||
use bincode::serialized_size;
|
||||
use indexmap::map::IndexMap;
|
||||
use itertools::Itertools;
|
||||
use rand;
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::{thread_rng, RngCore};
|
||||
use rand::{self, seq::SliceRandom, thread_rng, RngCore};
|
||||
use solana_runtime::bloom::Bloom;
|
||||
use solana_sdk::hash::Hash;
|
||||
use solana_sdk::packet::PACKET_DATA_SIZE;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::timing::timestamp;
|
||||
use std::cmp;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use solana_sdk::{hash::Hash, packet::PACKET_DATA_SIZE, pubkey::Pubkey, timing::timestamp};
|
||||
use std::{
|
||||
cmp,
|
||||
collections::{HashMap, HashSet},
|
||||
};
|
||||
|
||||
pub const CRDS_GOSSIP_NUM_ACTIVE: usize = 30;
|
||||
pub const CRDS_GOSSIP_PUSH_FANOUT: usize = 6;
|
||||
|
@ -1,18 +1,23 @@
|
||||
use crate::contact_info::ContactInfo;
|
||||
use bincode::{serialize, serialized_size};
|
||||
use solana_sdk::clock::Slot;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::signature::{Keypair, Signable, Signature};
|
||||
use solana_sdk::transaction::Transaction;
|
||||
use std::borrow::Borrow;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::HashSet;
|
||||
use std::fmt;
|
||||
use solana_sdk::{
|
||||
clock::Slot,
|
||||
hash::Hash,
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, Signable, Signature},
|
||||
transaction::Transaction,
|
||||
};
|
||||
use std::{
|
||||
borrow::{Borrow, Cow},
|
||||
collections::{BTreeSet, HashSet},
|
||||
fmt,
|
||||
};
|
||||
|
||||
pub type VoteIndex = u8;
|
||||
pub const MAX_VOTES: VoteIndex = 32;
|
||||
|
||||
pub type EpochSlotIndex = u8;
|
||||
|
||||
/// CrdsValue that is replicated across the cluster
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
pub struct CrdsValue {
|
||||
@ -50,15 +55,51 @@ impl Signable for CrdsValue {
|
||||
}
|
||||
|
||||
/// CrdsData that defines the different types of items CrdsValues can hold
|
||||
/// * Merge Strategy - Latest wallclock is picked
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
pub enum CrdsData {
|
||||
/// * Merge Strategy - Latest wallclock is picked
|
||||
ContactInfo(ContactInfo),
|
||||
/// * Merge Strategy - Latest wallclock is picked
|
||||
Vote(VoteIndex, Vote),
|
||||
/// * Merge Strategy - Latest wallclock is picked
|
||||
EpochSlots(EpochSlots),
|
||||
EpochSlots(EpochSlotIndex, EpochSlots),
|
||||
SnapshotHash(SnapshotHash),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
pub enum CompressionType {
|
||||
Uncompressed,
|
||||
GZip,
|
||||
BZip2,
|
||||
}
|
||||
|
||||
impl Default for CompressionType {
|
||||
fn default() -> Self {
|
||||
Self::Uncompressed
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
|
||||
pub struct EpochIncompleteSlots {
|
||||
pub first: Slot,
|
||||
pub compression: CompressionType,
|
||||
pub compressed_list: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
pub struct SnapshotHash {
|
||||
pub from: Pubkey,
|
||||
pub hashes: Vec<(Slot, Hash)>,
|
||||
pub wallclock: u64,
|
||||
}
|
||||
|
||||
impl SnapshotHash {
|
||||
pub fn new(from: Pubkey, hashes: Vec<(Slot, Hash)>, wallclock: u64) -> Self {
|
||||
Self {
|
||||
from,
|
||||
hashes,
|
||||
wallclock,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
@ -67,6 +108,7 @@ pub struct EpochSlots {
|
||||
pub root: Slot,
|
||||
pub lowest: Slot,
|
||||
pub slots: BTreeSet<Slot>,
|
||||
pub stash: Vec<EpochIncompleteSlots>,
|
||||
pub wallclock: u64,
|
||||
}
|
||||
|
||||
@ -76,6 +118,7 @@ impl EpochSlots {
|
||||
root: Slot,
|
||||
lowest: Slot,
|
||||
slots: BTreeSet<Slot>,
|
||||
stash: Vec<EpochIncompleteSlots>,
|
||||
wallclock: u64,
|
||||
) -> Self {
|
||||
Self {
|
||||
@ -83,6 +126,7 @@ impl EpochSlots {
|
||||
root,
|
||||
lowest,
|
||||
slots,
|
||||
stash,
|
||||
wallclock,
|
||||
}
|
||||
}
|
||||
@ -112,6 +156,7 @@ pub enum CrdsValueLabel {
|
||||
ContactInfo(Pubkey),
|
||||
Vote(VoteIndex, Pubkey),
|
||||
EpochSlots(Pubkey),
|
||||
SnapshotHash(Pubkey),
|
||||
}
|
||||
|
||||
impl fmt::Display for CrdsValueLabel {
|
||||
@ -120,6 +165,7 @@ impl fmt::Display for CrdsValueLabel {
|
||||
CrdsValueLabel::ContactInfo(_) => write!(f, "ContactInfo({})", self.pubkey()),
|
||||
CrdsValueLabel::Vote(ix, _) => write!(f, "Vote({}, {})", ix, self.pubkey()),
|
||||
CrdsValueLabel::EpochSlots(_) => write!(f, "EpochSlots({})", self.pubkey()),
|
||||
CrdsValueLabel::SnapshotHash(_) => write!(f, "SnapshotHash({})", self.pubkey()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -130,6 +176,7 @@ impl CrdsValueLabel {
|
||||
CrdsValueLabel::ContactInfo(p) => *p,
|
||||
CrdsValueLabel::Vote(_, p) => *p,
|
||||
CrdsValueLabel::EpochSlots(p) => *p,
|
||||
CrdsValueLabel::SnapshotHash(p) => *p,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -154,21 +201,24 @@ impl CrdsValue {
|
||||
match &self.data {
|
||||
CrdsData::ContactInfo(contact_info) => contact_info.wallclock,
|
||||
CrdsData::Vote(_, vote) => vote.wallclock,
|
||||
CrdsData::EpochSlots(vote) => vote.wallclock,
|
||||
CrdsData::EpochSlots(_, vote) => vote.wallclock,
|
||||
CrdsData::SnapshotHash(hash) => hash.wallclock,
|
||||
}
|
||||
}
|
||||
pub fn pubkey(&self) -> Pubkey {
|
||||
match &self.data {
|
||||
CrdsData::ContactInfo(contact_info) => contact_info.id,
|
||||
CrdsData::Vote(_, vote) => vote.from,
|
||||
CrdsData::EpochSlots(slots) => slots.from,
|
||||
CrdsData::EpochSlots(_, slots) => slots.from,
|
||||
CrdsData::SnapshotHash(hash) => hash.from,
|
||||
}
|
||||
}
|
||||
pub fn label(&self) -> CrdsValueLabel {
|
||||
match &self.data {
|
||||
CrdsData::ContactInfo(_) => CrdsValueLabel::ContactInfo(self.pubkey()),
|
||||
CrdsData::Vote(ix, _) => CrdsValueLabel::Vote(*ix, self.pubkey()),
|
||||
CrdsData::EpochSlots(_) => CrdsValueLabel::EpochSlots(self.pubkey()),
|
||||
CrdsData::EpochSlots(_, _) => CrdsValueLabel::EpochSlots(self.pubkey()),
|
||||
CrdsData::SnapshotHash(_) => CrdsValueLabel::SnapshotHash(self.pubkey()),
|
||||
}
|
||||
}
|
||||
pub fn contact_info(&self) -> Option<&ContactInfo> {
|
||||
@ -193,15 +243,24 @@ impl CrdsValue {
|
||||
|
||||
pub fn epoch_slots(&self) -> Option<&EpochSlots> {
|
||||
match &self.data {
|
||||
CrdsData::EpochSlots(slots) => Some(slots),
|
||||
CrdsData::EpochSlots(_, slots) => Some(slots),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn snapshot_hash(&self) -> Option<&SnapshotHash> {
|
||||
match &self.data {
|
||||
CrdsData::SnapshotHash(slots) => Some(slots),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return all the possible labels for a record identified by Pubkey.
|
||||
pub fn record_labels(key: &Pubkey) -> Vec<CrdsValueLabel> {
|
||||
let mut labels = vec![
|
||||
CrdsValueLabel::ContactInfo(*key),
|
||||
CrdsValueLabel::EpochSlots(*key),
|
||||
CrdsValueLabel::SnapshotHash(*key),
|
||||
];
|
||||
labels.extend((0..MAX_VOTES).map(|ix| CrdsValueLabel::Vote(ix, *key)));
|
||||
labels
|
||||
@ -246,18 +305,19 @@ mod test {
|
||||
use crate::contact_info::ContactInfo;
|
||||
use bincode::deserialize;
|
||||
use solana_perf::test_tx::test_tx;
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use solana_sdk::timing::timestamp;
|
||||
|
||||
#[test]
|
||||
fn test_labels() {
|
||||
let mut hits = [false; 2 + MAX_VOTES as usize];
|
||||
let mut hits = [false; 3 + MAX_VOTES as usize];
|
||||
// this method should cover all the possible labels
|
||||
for v in &CrdsValue::record_labels(&Pubkey::default()) {
|
||||
match v {
|
||||
CrdsValueLabel::ContactInfo(_) => hits[0] = true,
|
||||
CrdsValueLabel::EpochSlots(_) => hits[1] = true,
|
||||
CrdsValueLabel::Vote(ix, _) => hits[*ix as usize + 2] = true,
|
||||
CrdsValueLabel::SnapshotHash(_) => hits[2] = true,
|
||||
CrdsValueLabel::Vote(ix, _) => hits[*ix as usize + 3] = true,
|
||||
}
|
||||
}
|
||||
assert!(hits.iter().all(|x| *x));
|
||||
@ -277,13 +337,10 @@ mod test {
|
||||
let key = v.clone().vote().unwrap().from;
|
||||
assert_eq!(v.label(), CrdsValueLabel::Vote(0, key));
|
||||
|
||||
let v = CrdsValue::new_unsigned(CrdsData::EpochSlots(EpochSlots::new(
|
||||
Pubkey::default(),
|
||||
let v = CrdsValue::new_unsigned(CrdsData::EpochSlots(
|
||||
0,
|
||||
0,
|
||||
BTreeSet::new(),
|
||||
0,
|
||||
)));
|
||||
EpochSlots::new(Pubkey::default(), 0, 0, BTreeSet::new(), vec![], 0),
|
||||
));
|
||||
assert_eq!(v.wallclock(), 0);
|
||||
let key = v.clone().epoch_slots().unwrap().from;
|
||||
assert_eq!(v.label(), CrdsValueLabel::EpochSlots(key));
|
||||
@ -304,13 +361,10 @@ mod test {
|
||||
));
|
||||
verify_signatures(&mut v, &keypair, &wrong_keypair);
|
||||
let btreeset: BTreeSet<Slot> = vec![1, 2, 3, 6, 8].into_iter().collect();
|
||||
v = CrdsValue::new_unsigned(CrdsData::EpochSlots(EpochSlots::new(
|
||||
keypair.pubkey(),
|
||||
v = CrdsValue::new_unsigned(CrdsData::EpochSlots(
|
||||
0,
|
||||
0,
|
||||
btreeset,
|
||||
timestamp(),
|
||||
)));
|
||||
EpochSlots::new(keypair.pubkey(), 0, 0, btreeset, vec![], timestamp()),
|
||||
));
|
||||
verify_signatures(&mut v, &keypair, &wrong_keypair);
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,7 @@ impl GenKeys {
|
||||
mod tests {
|
||||
use super::*;
|
||||
pub use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::signature::KeypairUtil;
|
||||
use solana_sdk::signature::Signer;
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[test]
|
||||
|
@ -8,7 +8,7 @@ use solana_client::thin_client::{create_client, ThinClient};
|
||||
use solana_ledger::bank_forks::BankForks;
|
||||
use solana_perf::recycler::Recycler;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use std::net::{SocketAddr, TcpListener, UdpSocket};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::mpsc::channel;
|
||||
|
@ -12,11 +12,11 @@ use std::thread::{Builder, JoinHandle};
|
||||
use std::time::Duration;
|
||||
|
||||
// This is chosen to allow enough time for
|
||||
// - To try and keep the RocksDB size under 128GB at 50k tps (100 slots take ~2GB).
|
||||
// - To try and keep the RocksDB size under 512GB at 50k tps (100 slots take ~2GB).
|
||||
// - A validator to download a snapshot from a peer and boot from it
|
||||
// - To make sure that if a validator needs to reboot from its own snapshot, it has enough slots locally
|
||||
// to catch back up to where it was when it stopped
|
||||
pub const DEFAULT_MAX_LEDGER_SLOTS: u64 = 6400;
|
||||
pub const DEFAULT_MAX_LEDGER_SLOTS: u64 = 270_000;
|
||||
// Remove a fixed number of slots at a time, it's more efficient than doing it one-by-one
|
||||
pub const DEFAULT_PURGE_BATCH_SIZE: u64 = 256;
|
||||
|
||||
|
@ -9,11 +9,12 @@ use solana_ledger::{
|
||||
bank_forks::BankForks,
|
||||
blockstore::{Blockstore, CompletedSlotsReceiver, SlotMeta},
|
||||
};
|
||||
use solana_sdk::clock::DEFAULT_SLOTS_PER_EPOCH;
|
||||
use solana_sdk::{clock::Slot, epoch_schedule::EpochSchedule, pubkey::Pubkey};
|
||||
use std::{
|
||||
collections::BTreeSet,
|
||||
net::UdpSocket,
|
||||
ops::Bound::{Excluded, Unbounded},
|
||||
ops::Bound::{Included, Unbounded},
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
sync::{Arc, RwLock},
|
||||
thread::sleep,
|
||||
@ -25,6 +26,9 @@ pub const MAX_REPAIR_LENGTH: usize = 512;
|
||||
pub const REPAIR_MS: u64 = 100;
|
||||
pub const MAX_ORPHANS: usize = 5;
|
||||
|
||||
const MAX_COMPLETED_SLOT_CACHE_LEN: usize = 256;
|
||||
const COMPLETED_SLOT_CACHE_FLUSH_TRIGGER: usize = 512;
|
||||
|
||||
pub enum RepairStrategy {
|
||||
RepairRange(RepairSlotRange),
|
||||
RepairAll {
|
||||
@ -85,17 +89,18 @@ impl RepairService {
|
||||
) {
|
||||
let serve_repair = ServeRepair::new(cluster_info.clone());
|
||||
let mut epoch_slots: BTreeSet<Slot> = BTreeSet::new();
|
||||
let mut old_incomplete_slots: BTreeSet<Slot> = BTreeSet::new();
|
||||
let id = cluster_info.read().unwrap().id();
|
||||
let mut current_root = 0;
|
||||
if let RepairStrategy::RepairAll {
|
||||
ref epoch_schedule, ..
|
||||
} = repair_strategy
|
||||
{
|
||||
current_root = blockstore.last_root();
|
||||
let current_root = blockstore.last_root();
|
||||
Self::initialize_epoch_slots(
|
||||
id,
|
||||
blockstore,
|
||||
&mut epoch_slots,
|
||||
&old_incomplete_slots,
|
||||
current_root,
|
||||
epoch_schedule,
|
||||
cluster_info,
|
||||
@ -127,8 +132,8 @@ impl RepairService {
|
||||
id,
|
||||
new_root,
|
||||
lowest_slot,
|
||||
&mut current_root,
|
||||
&mut epoch_slots,
|
||||
&mut old_incomplete_slots,
|
||||
&cluster_info,
|
||||
completed_slots_receiver,
|
||||
);
|
||||
@ -292,6 +297,7 @@ impl RepairService {
|
||||
id: Pubkey,
|
||||
blockstore: &Blockstore,
|
||||
slots_in_gossip: &mut BTreeSet<Slot>,
|
||||
old_incomplete_slots: &BTreeSet<Slot>,
|
||||
root: Slot,
|
||||
epoch_schedule: &EpochSchedule,
|
||||
cluster_info: &RwLock<ClusterInfo>,
|
||||
@ -307,6 +313,7 @@ impl RepairService {
|
||||
root,
|
||||
blockstore.lowest_slot(),
|
||||
slots_in_gossip.clone(),
|
||||
old_incomplete_slots,
|
||||
);
|
||||
}
|
||||
|
||||
@ -316,44 +323,83 @@ impl RepairService {
|
||||
id: Pubkey,
|
||||
latest_known_root: Slot,
|
||||
lowest_slot: Slot,
|
||||
prev_root: &mut Slot,
|
||||
slots_in_gossip: &mut BTreeSet<Slot>,
|
||||
completed_slot_cache: &mut BTreeSet<Slot>,
|
||||
incomplete_slot_stash: &mut BTreeSet<Slot>,
|
||||
cluster_info: &RwLock<ClusterInfo>,
|
||||
completed_slots_receiver: &CompletedSlotsReceiver,
|
||||
) {
|
||||
// If the latest known root is different, update gossip.
|
||||
let mut should_update = latest_known_root != *prev_root;
|
||||
let mut should_update = false;
|
||||
while let Ok(completed_slots) = completed_slots_receiver.try_recv() {
|
||||
for slot in completed_slots {
|
||||
// If the newly completed slot > root, and the set did not contain this value
|
||||
// before, we should update gossip.
|
||||
if slot > latest_known_root {
|
||||
should_update |= slots_in_gossip.insert(slot);
|
||||
let last_slot_in_stash = *incomplete_slot_stash.iter().next_back().unwrap_or(&0);
|
||||
let removed_from_stash = incomplete_slot_stash.remove(&slot);
|
||||
// If the newly completed slot was not being tracked in stash, and is > last
|
||||
// slot being tracked in stash, add it to cache. Also, update gossip
|
||||
if !removed_from_stash && slot >= last_slot_in_stash {
|
||||
should_update |= completed_slot_cache.insert(slot);
|
||||
}
|
||||
// If the slot was removed from stash, update gossip
|
||||
should_update |= removed_from_stash;
|
||||
}
|
||||
}
|
||||
|
||||
if should_update {
|
||||
// Filter out everything <= root
|
||||
if latest_known_root != *prev_root {
|
||||
*prev_root = latest_known_root;
|
||||
Self::retain_slots_greater_than_root(slots_in_gossip, latest_known_root);
|
||||
if completed_slot_cache.len() >= COMPLETED_SLOT_CACHE_FLUSH_TRIGGER {
|
||||
Self::stash_old_incomplete_slots(completed_slot_cache, incomplete_slot_stash);
|
||||
let lowest_completed_slot_in_cache =
|
||||
*completed_slot_cache.iter().next().unwrap_or(&0);
|
||||
Self::prune_incomplete_slot_stash(
|
||||
incomplete_slot_stash,
|
||||
lowest_completed_slot_in_cache,
|
||||
);
|
||||
}
|
||||
|
||||
cluster_info.write().unwrap().push_epoch_slots(
|
||||
id,
|
||||
latest_known_root,
|
||||
lowest_slot,
|
||||
slots_in_gossip.clone(),
|
||||
completed_slot_cache.clone(),
|
||||
incomplete_slot_stash,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn retain_slots_greater_than_root(slot_set: &mut BTreeSet<Slot>, root: Slot) {
|
||||
*slot_set = slot_set
|
||||
.range((Excluded(&root), Unbounded))
|
||||
.cloned()
|
||||
.collect();
|
||||
fn stash_old_incomplete_slots(cache: &mut BTreeSet<Slot>, stash: &mut BTreeSet<Slot>) {
|
||||
if cache.len() > MAX_COMPLETED_SLOT_CACHE_LEN {
|
||||
let mut prev = *cache.iter().next().expect("Expected to find some slot");
|
||||
cache.remove(&prev);
|
||||
while cache.len() >= MAX_COMPLETED_SLOT_CACHE_LEN {
|
||||
let next = *cache.iter().next().expect("Expected to find some slot");
|
||||
cache.remove(&next);
|
||||
// Prev slot and next slot are not included in incomplete slot list.
|
||||
(prev + 1..next).for_each(|slot| {
|
||||
stash.insert(slot);
|
||||
});
|
||||
prev = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn prune_incomplete_slot_stash(
|
||||
stash: &mut BTreeSet<Slot>,
|
||||
lowest_completed_slot_in_cache: Slot,
|
||||
) {
|
||||
if let Some(oldest_incomplete_slot) = stash.iter().next() {
|
||||
// Prune old slots
|
||||
// Prune in batches to reduce overhead. Pruning starts when oldest slot is 1.5 epochs
|
||||
// earlier than the new root. But, we prune all the slots that are older than 1 epoch.
|
||||
// So slots in a batch of half epoch are getting pruned
|
||||
if oldest_incomplete_slot + DEFAULT_SLOTS_PER_EPOCH + DEFAULT_SLOTS_PER_EPOCH / 2
|
||||
< lowest_completed_slot_in_cache
|
||||
{
|
||||
let oldest_slot_to_retain =
|
||||
lowest_completed_slot_in_cache.saturating_sub(DEFAULT_SLOTS_PER_EPOCH);
|
||||
*stash = stash
|
||||
.range((Included(&oldest_slot_to_retain), Unbounded))
|
||||
.cloned()
|
||||
.collect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn join(self) -> thread::Result<()> {
|
||||
@ -373,7 +419,6 @@ mod test {
|
||||
};
|
||||
use solana_ledger::shred::max_ticks_per_n_shreds;
|
||||
use solana_ledger::{blockstore::Blockstore, get_tmp_ledger_path};
|
||||
use std::sync::mpsc::channel;
|
||||
use std::thread::Builder;
|
||||
|
||||
#[test]
|
||||
@ -703,13 +748,14 @@ mod test {
|
||||
node_info.info.clone(),
|
||||
));
|
||||
|
||||
let mut old_incomplete_slots: BTreeSet<Slot> = BTreeSet::new();
|
||||
while completed_slots.len() < num_slots as usize {
|
||||
RepairService::update_epoch_slots(
|
||||
Pubkey::default(),
|
||||
root,
|
||||
blockstore.lowest_slot(),
|
||||
&mut root.clone(),
|
||||
&mut completed_slots,
|
||||
&mut old_incomplete_slots,
|
||||
&cluster_info,
|
||||
&completed_slots_receiver,
|
||||
);
|
||||
@ -726,13 +772,12 @@ mod test {
|
||||
Pubkey::default(),
|
||||
root,
|
||||
0,
|
||||
&mut 0,
|
||||
&mut completed_slots,
|
||||
&mut old_incomplete_slots,
|
||||
&cluster_info,
|
||||
&completed_slots_receiver,
|
||||
);
|
||||
expected.insert(num_slots + 2);
|
||||
RepairService::retain_slots_greater_than_root(&mut expected, root);
|
||||
assert_eq!(completed_slots, expected);
|
||||
writer.join().unwrap();
|
||||
}
|
||||
@ -740,95 +785,133 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update_epoch_slots_new_root() {
|
||||
let mut current_root = 0;
|
||||
fn test_stash_old_incomplete_slots() {
|
||||
let mut cache: BTreeSet<Slot> = BTreeSet::new();
|
||||
let mut stash: BTreeSet<Slot> = BTreeSet::new();
|
||||
|
||||
let mut completed_slots = BTreeSet::new();
|
||||
let node_info = Node::new_localhost_with_pubkey(&Pubkey::default());
|
||||
let cluster_info = RwLock::new(ClusterInfo::new_with_invalid_keypair(
|
||||
node_info.info.clone(),
|
||||
));
|
||||
let my_pubkey = Pubkey::new_rand();
|
||||
let (completed_slots_sender, completed_slots_receiver) = channel();
|
||||
// When cache is empty.
|
||||
RepairService::stash_old_incomplete_slots(&mut cache, &mut stash);
|
||||
assert_eq!(stash.len(), 0);
|
||||
|
||||
// Send a new slot before the root is updated
|
||||
let newly_completed_slot = 63;
|
||||
completed_slots_sender
|
||||
.send(vec![newly_completed_slot])
|
||||
.unwrap();
|
||||
RepairService::update_epoch_slots(
|
||||
my_pubkey.clone(),
|
||||
current_root,
|
||||
0,
|
||||
&mut current_root.clone(),
|
||||
&mut completed_slots,
|
||||
&cluster_info,
|
||||
&completed_slots_receiver,
|
||||
// Insert some slots in cache ( < MAX_COMPLETED_SLOT_CACHE_LEN + 1)
|
||||
cache.insert(101);
|
||||
cache.insert(102);
|
||||
cache.insert(104);
|
||||
cache.insert(105);
|
||||
|
||||
// Not enough slots in cache. So stash should remain empty.
|
||||
RepairService::stash_old_incomplete_slots(&mut cache, &mut stash);
|
||||
assert_eq!(stash.len(), 0);
|
||||
assert_eq!(cache.len(), 4);
|
||||
|
||||
// Insert slots in cache ( = MAX_COMPLETED_SLOT_CACHE_LEN)
|
||||
let mut cache: BTreeSet<Slot> = BTreeSet::new();
|
||||
(0..MAX_COMPLETED_SLOT_CACHE_LEN as u64)
|
||||
.into_iter()
|
||||
.for_each(|slot| {
|
||||
cache.insert(slot);
|
||||
});
|
||||
|
||||
// Not enough slots in cache. So stash should remain empty.
|
||||
RepairService::stash_old_incomplete_slots(&mut cache, &mut stash);
|
||||
assert_eq!(stash.len(), 0);
|
||||
assert_eq!(cache.len(), MAX_COMPLETED_SLOT_CACHE_LEN);
|
||||
|
||||
// Insert 1 more to cross the threshold
|
||||
cache.insert(MAX_COMPLETED_SLOT_CACHE_LEN as u64);
|
||||
RepairService::stash_old_incomplete_slots(&mut cache, &mut stash);
|
||||
// Stash is still empty, as no missing slots
|
||||
assert_eq!(stash.len(), 0);
|
||||
// It removed some entries from cache
|
||||
assert_eq!(cache.len(), MAX_COMPLETED_SLOT_CACHE_LEN - 1);
|
||||
|
||||
// Insert more slots to create a missing slot
|
||||
let mut cache: BTreeSet<Slot> = BTreeSet::new();
|
||||
cache.insert(0);
|
||||
(2..=MAX_COMPLETED_SLOT_CACHE_LEN as u64 + 2)
|
||||
.into_iter()
|
||||
.for_each(|slot| {
|
||||
cache.insert(slot);
|
||||
});
|
||||
RepairService::stash_old_incomplete_slots(&mut cache, &mut stash);
|
||||
|
||||
// Stash is not empty
|
||||
assert!(stash.contains(&1));
|
||||
// It removed some entries from cache
|
||||
assert_eq!(cache.len(), MAX_COMPLETED_SLOT_CACHE_LEN - 1);
|
||||
|
||||
// Test multiple missing slots at dispersed locations
|
||||
let mut cache: BTreeSet<Slot> = BTreeSet::new();
|
||||
(0..MAX_COMPLETED_SLOT_CACHE_LEN as u64 * 2)
|
||||
.into_iter()
|
||||
.for_each(|slot| {
|
||||
cache.insert(slot);
|
||||
});
|
||||
|
||||
cache.remove(&10);
|
||||
cache.remove(&11);
|
||||
|
||||
cache.remove(&28);
|
||||
cache.remove(&29);
|
||||
|
||||
cache.remove(&148);
|
||||
cache.remove(&149);
|
||||
cache.remove(&150);
|
||||
cache.remove(&151);
|
||||
|
||||
RepairService::stash_old_incomplete_slots(&mut cache, &mut stash);
|
||||
|
||||
// Stash is not empty
|
||||
assert!(stash.contains(&10));
|
||||
assert!(stash.contains(&11));
|
||||
assert!(stash.contains(&28));
|
||||
assert!(stash.contains(&29));
|
||||
assert!(stash.contains(&148));
|
||||
assert!(stash.contains(&149));
|
||||
assert!(stash.contains(&150));
|
||||
assert!(stash.contains(&151));
|
||||
|
||||
assert!(!stash.contains(&147));
|
||||
assert!(!stash.contains(&152));
|
||||
// It removed some entries from cache
|
||||
assert_eq!(cache.len(), MAX_COMPLETED_SLOT_CACHE_LEN - 1);
|
||||
(MAX_COMPLETED_SLOT_CACHE_LEN + 1..MAX_COMPLETED_SLOT_CACHE_LEN * 2)
|
||||
.into_iter()
|
||||
.for_each(|slot| {
|
||||
let slot: u64 = slot as u64;
|
||||
assert!(cache.contains(&slot));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prune_incomplete_slot_stash() {
|
||||
// Prune empty stash
|
||||
let mut stash: BTreeSet<Slot> = BTreeSet::new();
|
||||
RepairService::prune_incomplete_slot_stash(&mut stash, 0);
|
||||
assert!(stash.is_empty());
|
||||
|
||||
// Prune stash with slots < DEFAULT_SLOTS_PER_EPOCH
|
||||
stash.insert(0);
|
||||
stash.insert(10);
|
||||
stash.insert(11);
|
||||
stash.insert(50);
|
||||
assert_eq!(stash.len(), 4);
|
||||
RepairService::prune_incomplete_slot_stash(&mut stash, 100);
|
||||
assert_eq!(stash.len(), 4);
|
||||
|
||||
// Prune stash with slots > DEFAULT_SLOTS_PER_EPOCH, but < 1.5 * DEFAULT_SLOTS_PER_EPOCH
|
||||
stash.insert(DEFAULT_SLOTS_PER_EPOCH + 50);
|
||||
assert_eq!(stash.len(), 5);
|
||||
RepairService::prune_incomplete_slot_stash(&mut stash, DEFAULT_SLOTS_PER_EPOCH + 100);
|
||||
assert_eq!(stash.len(), 5);
|
||||
|
||||
// Prune stash with slots > 1.5 * DEFAULT_SLOTS_PER_EPOCH
|
||||
stash.insert(DEFAULT_SLOTS_PER_EPOCH + DEFAULT_SLOTS_PER_EPOCH / 2);
|
||||
assert_eq!(stash.len(), 6);
|
||||
RepairService::prune_incomplete_slot_stash(
|
||||
&mut stash,
|
||||
DEFAULT_SLOTS_PER_EPOCH + DEFAULT_SLOTS_PER_EPOCH / 2 + 1,
|
||||
);
|
||||
|
||||
// We should see epoch state update
|
||||
let (my_epoch_slots_in_gossip, updated_ts) = {
|
||||
let r_cluster_info = cluster_info.read().unwrap();
|
||||
|
||||
let (my_epoch_slots_in_gossip, updated_ts) = r_cluster_info
|
||||
.get_epoch_state_for_node(&my_pubkey, None)
|
||||
.clone()
|
||||
.unwrap();
|
||||
|
||||
(my_epoch_slots_in_gossip.clone(), updated_ts)
|
||||
};
|
||||
|
||||
assert_eq!(my_epoch_slots_in_gossip.root, 0);
|
||||
assert_eq!(current_root, 0);
|
||||
assert_eq!(my_epoch_slots_in_gossip.slots.len(), 1);
|
||||
assert!(my_epoch_slots_in_gossip
|
||||
.slots
|
||||
.contains(&newly_completed_slot));
|
||||
|
||||
// Calling update again with no updates to either the roots or set of completed slots
|
||||
// should not update gossip
|
||||
RepairService::update_epoch_slots(
|
||||
my_pubkey.clone(),
|
||||
current_root,
|
||||
0,
|
||||
&mut current_root,
|
||||
&mut completed_slots,
|
||||
&cluster_info,
|
||||
&completed_slots_receiver,
|
||||
);
|
||||
|
||||
assert!(cluster_info
|
||||
.read()
|
||||
.unwrap()
|
||||
.get_epoch_state_for_node(&my_pubkey, Some(updated_ts))
|
||||
.is_none());
|
||||
|
||||
sleep(Duration::from_millis(10));
|
||||
// Updating just the root again should update gossip (simulates replay stage updating root
|
||||
// after a slot has been signaled as completed)
|
||||
RepairService::update_epoch_slots(
|
||||
my_pubkey.clone(),
|
||||
current_root + 1,
|
||||
0,
|
||||
&mut current_root,
|
||||
&mut completed_slots,
|
||||
&cluster_info,
|
||||
&completed_slots_receiver,
|
||||
);
|
||||
|
||||
let r_cluster_info = cluster_info.read().unwrap();
|
||||
|
||||
let (my_epoch_slots_in_gossip, _) = r_cluster_info
|
||||
.get_epoch_state_for_node(&my_pubkey, Some(updated_ts))
|
||||
.clone()
|
||||
.unwrap();
|
||||
|
||||
// Check the root was updated correctly
|
||||
assert_eq!(my_epoch_slots_in_gossip.root, 1);
|
||||
assert_eq!(current_root, 1);
|
||||
assert_eq!(my_epoch_slots_in_gossip.slots.len(), 1);
|
||||
assert!(my_epoch_slots_in_gossip
|
||||
.slots
|
||||
.contains(&newly_completed_slot));
|
||||
assert_eq!(stash.len(), 2);
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ use solana_sdk::{
|
||||
clock::Slot,
|
||||
hash::Hash,
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, KeypairUtil},
|
||||
signature::{Keypair, Signer},
|
||||
timing::{self, duration_as_ms},
|
||||
transaction::Transaction,
|
||||
};
|
||||
@ -489,7 +489,7 @@ impl ReplayStage {
|
||||
return;
|
||||
}
|
||||
|
||||
datapoint_debug!(
|
||||
datapoint_info!(
|
||||
"replay_stage-new_leader",
|
||||
("slot", poh_slot, i64),
|
||||
("leader", next_leader.to_string(), String),
|
||||
@ -1039,7 +1039,7 @@ pub(crate) mod tests {
|
||||
instruction::InstructionError,
|
||||
packet::PACKET_DATA_SIZE,
|
||||
rent::Rent,
|
||||
signature::{Keypair, KeypairUtil, Signature},
|
||||
signature::{Keypair, Signature, Signer},
|
||||
system_transaction,
|
||||
transaction::TransactionError,
|
||||
};
|
||||
|
@ -1121,7 +1121,7 @@ pub mod tests {
|
||||
hash::{hash, Hash},
|
||||
instruction::InstructionError,
|
||||
rpc_port,
|
||||
signature::{Keypair, KeypairUtil},
|
||||
signature::{Keypair, Signer},
|
||||
system_transaction,
|
||||
transaction::TransactionError,
|
||||
};
|
||||
|
@ -288,7 +288,7 @@ mod tests {
|
||||
use solana_runtime::bank::Bank;
|
||||
use solana_sdk::{
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, KeypairUtil},
|
||||
signature::{Keypair, Signer},
|
||||
system_program, system_transaction,
|
||||
transaction::{self, Transaction},
|
||||
};
|
||||
|
@ -181,7 +181,7 @@ mod tests {
|
||||
};
|
||||
use solana_ledger::get_tmp_ledger_path;
|
||||
use solana_runtime::bank::Bank;
|
||||
use solana_sdk::signature::KeypairUtil;
|
||||
use solana_sdk::signature::Signer;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
||||
|
@ -506,7 +506,7 @@ pub(crate) mod tests {
|
||||
use jsonrpc_pubsub::typed::Subscriber;
|
||||
use solana_budget_program;
|
||||
use solana_sdk::{
|
||||
signature::{Keypair, KeypairUtil},
|
||||
signature::{Keypair, Signer},
|
||||
system_transaction,
|
||||
};
|
||||
use tokio::prelude::{Async, Stream};
|
||||
|
@ -14,7 +14,7 @@ use solana_metrics::{datapoint_debug, inc_new_counter_debug};
|
||||
use solana_perf::packet::{Packets, PacketsRecycler};
|
||||
use solana_sdk::{
|
||||
clock::Slot,
|
||||
signature::{Keypair, KeypairUtil},
|
||||
signature::{Keypair, Signer},
|
||||
timing::duration_as_ms,
|
||||
};
|
||||
use std::{
|
||||
@ -298,7 +298,7 @@ impl ServeRepair {
|
||||
Ok(self.window_index_request_bytes(*slot, *shred_index)?)
|
||||
}
|
||||
RepairType::HighestShred(slot, shred_index) => {
|
||||
datapoint_debug!(
|
||||
datapoint_info!(
|
||||
"serve_repair-repair_highest",
|
||||
("repair-highest-slot", *slot, i64),
|
||||
("repair-highest-ix", *shred_index, i64)
|
||||
@ -306,7 +306,7 @@ impl ServeRepair {
|
||||
Ok(self.window_highest_index_request_bytes(*slot, *shred_index)?)
|
||||
}
|
||||
RepairType::Orphan(slot) => {
|
||||
datapoint_debug!("serve_repair-repair_orphan", ("repair-orphan", *slot, i64));
|
||||
datapoint_info!("serve_repair-repair_orphan", ("repair-orphan", *slot, i64));
|
||||
Ok(self.orphan_bytes(*slot)?)
|
||||
}
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ pub mod tests {
|
||||
use crate::packet::Packet;
|
||||
use solana_ledger::shred::{Shred, Shredder};
|
||||
use solana_runtime::bank::Bank;
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
|
||||
#[test]
|
||||
fn test_sigverify_shreds_read_slots() {
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::cluster_info::ClusterInfo;
|
||||
use solana_ledger::{
|
||||
snapshot_package::SnapshotPackageReceiver, snapshot_utils::archive_snapshot_package,
|
||||
};
|
||||
@ -5,7 +6,7 @@ use std::{
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
mpsc::RecvTimeoutError,
|
||||
Arc,
|
||||
Arc, RwLock,
|
||||
},
|
||||
thread::{self, Builder, JoinHandle},
|
||||
time::Duration,
|
||||
@ -15,25 +16,42 @@ pub struct SnapshotPackagerService {
|
||||
t_snapshot_packager: JoinHandle<()>,
|
||||
}
|
||||
|
||||
const MAX_SNAPSHOT_HASHES: usize = 24;
|
||||
|
||||
impl SnapshotPackagerService {
|
||||
pub fn new(snapshot_package_receiver: SnapshotPackageReceiver, exit: &Arc<AtomicBool>) -> Self {
|
||||
pub fn new(
|
||||
snapshot_package_receiver: SnapshotPackageReceiver,
|
||||
exit: &Arc<AtomicBool>,
|
||||
cluster_info: &Arc<RwLock<ClusterInfo>>,
|
||||
) -> Self {
|
||||
let exit = exit.clone();
|
||||
let cluster_info = cluster_info.clone();
|
||||
let t_snapshot_packager = Builder::new()
|
||||
.name("solana-snapshot-packager".to_string())
|
||||
.spawn(move || loop {
|
||||
let mut hashes = vec![];
|
||||
if exit.load(Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
|
||||
match snapshot_package_receiver.recv_timeout(Duration::from_secs(1)) {
|
||||
Ok(mut snapshot_package) => {
|
||||
hashes.push((snapshot_package.root, snapshot_package.hash));
|
||||
// Only package the latest
|
||||
while let Ok(new_snapshot_package) = snapshot_package_receiver.try_recv() {
|
||||
snapshot_package = new_snapshot_package;
|
||||
hashes.push((snapshot_package.root, snapshot_package.hash));
|
||||
}
|
||||
if let Err(err) = archive_snapshot_package(&snapshot_package) {
|
||||
warn!("Failed to create snapshot archive: {}", err);
|
||||
}
|
||||
while hashes.len() > MAX_SNAPSHOT_HASHES {
|
||||
hashes.remove(0);
|
||||
}
|
||||
cluster_info
|
||||
.write()
|
||||
.unwrap()
|
||||
.push_snapshot_hashes(hashes.clone());
|
||||
}
|
||||
Err(RecvTimeoutError::Disconnected) => break,
|
||||
Err(RecvTimeoutError::Timeout) => (),
|
||||
@ -59,10 +77,9 @@ mod tests {
|
||||
snapshot_utils::{self, SNAPSHOT_STATUS_CACHE_FILE_NAME},
|
||||
};
|
||||
use solana_runtime::{
|
||||
accounts_db::AccountStorageEntry, bank::MAX_SNAPSHOT_DATA_FILE_SIZE,
|
||||
status_cache::SlotDelta,
|
||||
accounts_db::AccountStorageEntry, bank::BankSlotDelta, bank::MAX_SNAPSHOT_DATA_FILE_SIZE,
|
||||
};
|
||||
use solana_sdk::transaction;
|
||||
use solana_sdk::hash::Hash;
|
||||
use std::{
|
||||
fs::{self, remove_dir_all, OpenOptions},
|
||||
io::Write,
|
||||
@ -139,8 +156,9 @@ mod tests {
|
||||
5,
|
||||
vec![],
|
||||
link_snapshots_dir,
|
||||
storage_entries.clone(),
|
||||
vec![storage_entries],
|
||||
output_tar_path.clone(),
|
||||
Hash::default(),
|
||||
);
|
||||
|
||||
// Make tarball from packageable snapshot
|
||||
@ -149,7 +167,7 @@ mod tests {
|
||||
// before we compare, stick an empty status_cache in this dir so that the package comparision works
|
||||
// This is needed since the status_cache is added by the packager and is not collected from
|
||||
// the source dir for snapshots
|
||||
let dummy_slot_deltas: Vec<SlotDelta<transaction::Result<()>>> = vec![];
|
||||
let dummy_slot_deltas: Vec<BankSlotDelta> = vec![];
|
||||
snapshot_utils::serialize_snapshot_data_file(
|
||||
&snapshots_dir.join(SNAPSHOT_STATUS_CACHE_FILE_NAME),
|
||||
MAX_SNAPSHOT_DATA_FILE_SIZE,
|
||||
|
@ -20,7 +20,7 @@ use solana_sdk::{
|
||||
instruction::Instruction,
|
||||
message::Message,
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, KeypairUtil, Signature},
|
||||
signature::{Keypair, Signature, Signer},
|
||||
transaction::Transaction,
|
||||
};
|
||||
use solana_storage_program::{
|
||||
@ -649,7 +649,7 @@ mod tests {
|
||||
use rayon::prelude::*;
|
||||
use solana_runtime::bank::Bank;
|
||||
use solana_sdk::hash::Hasher;
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use std::cmp::{max, min};
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
use std::sync::mpsc::channel;
|
||||
|
@ -26,7 +26,7 @@ use solana_ledger::{
|
||||
};
|
||||
use solana_sdk::{
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, KeypairUtil},
|
||||
signature::{Keypair, Signer},
|
||||
};
|
||||
use std::{
|
||||
net::UdpSocket,
|
||||
@ -153,7 +153,8 @@ impl Tvu {
|
||||
if snapshot_config.is_some() {
|
||||
// Start a snapshot packaging service
|
||||
let (sender, receiver) = channel();
|
||||
let snapshot_packager_service = SnapshotPackagerService::new(receiver, exit);
|
||||
let snapshot_packager_service =
|
||||
SnapshotPackagerService::new(receiver, exit, &cluster_info.clone());
|
||||
(Some(snapshot_packager_service), Some(sender))
|
||||
} else {
|
||||
(None, None)
|
||||
|
@ -38,9 +38,8 @@ use solana_sdk::{
|
||||
clock::{Slot, DEFAULT_SLOTS_PER_TURN},
|
||||
genesis_config::GenesisConfig,
|
||||
hash::Hash,
|
||||
poh_config::PohConfig,
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, KeypairUtil},
|
||||
signature::{Keypair, Signer},
|
||||
timing::timestamp,
|
||||
};
|
||||
use std::{
|
||||
@ -159,14 +158,14 @@ impl Validator {
|
||||
|
||||
info!("creating bank...");
|
||||
let (
|
||||
genesis_hash,
|
||||
genesis_config,
|
||||
bank_forks,
|
||||
bank_forks_info,
|
||||
blockstore,
|
||||
ledger_signal_receiver,
|
||||
completed_slots_receiver,
|
||||
leader_schedule_cache,
|
||||
poh_config,
|
||||
snapshot_hash,
|
||||
) = new_banks_from_blockstore(
|
||||
config.expected_genesis_hash,
|
||||
ledger_path,
|
||||
@ -196,8 +195,10 @@ impl Validator {
|
||||
let validator_exit = Arc::new(RwLock::new(Some(validator_exit)));
|
||||
|
||||
node.info.wallclock = timestamp();
|
||||
node.info.shred_version =
|
||||
compute_shred_version(&genesis_hash, Some(&bank.hard_forks().read().unwrap()));
|
||||
node.info.shred_version = compute_shred_version(
|
||||
&genesis_config.hash(),
|
||||
Some(&bank.hard_forks().read().unwrap()),
|
||||
);
|
||||
Self::print_node_info(&node);
|
||||
|
||||
if let Some(expected_shred_version) = config.expected_shred_version {
|
||||
@ -241,7 +242,7 @@ impl Validator {
|
||||
block_commitment_cache.clone(),
|
||||
blockstore.clone(),
|
||||
cluster_info.clone(),
|
||||
genesis_hash,
|
||||
genesis_config.hash(),
|
||||
ledger_path,
|
||||
storage_state.clone(),
|
||||
validator_exit.clone(),
|
||||
@ -299,7 +300,7 @@ impl Validator {
|
||||
std::thread::park();
|
||||
}
|
||||
|
||||
let poh_config = Arc::new(poh_config);
|
||||
let poh_config = Arc::new(genesis_config.poh_config);
|
||||
let (mut poh_recorder, entry_receiver) = PohRecorder::new_with_clear_signal(
|
||||
bank.tick_height(),
|
||||
bank.last_blockhash(),
|
||||
@ -343,6 +344,14 @@ impl Validator {
|
||||
.set_entrypoint(entrypoint_info.clone());
|
||||
}
|
||||
|
||||
// If the node was loaded from a snapshot, advertise it in gossip
|
||||
if let Some(snapshot_hash) = snapshot_hash {
|
||||
cluster_info
|
||||
.write()
|
||||
.unwrap()
|
||||
.push_snapshot_hashes(vec![snapshot_hash]);
|
||||
}
|
||||
|
||||
wait_for_supermajority(config, &bank, &cluster_info);
|
||||
|
||||
let voting_keypair = if config.voting_disabled {
|
||||
@ -505,20 +514,21 @@ impl Validator {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn new_banks_from_blockstore(
|
||||
expected_genesis_hash: Option<Hash>,
|
||||
blockstore_path: &Path,
|
||||
poh_verify: bool,
|
||||
config: &ValidatorConfig,
|
||||
) -> (
|
||||
Hash,
|
||||
GenesisConfig,
|
||||
BankForks,
|
||||
Vec<BankForksInfo>,
|
||||
Blockstore,
|
||||
Receiver<bool>,
|
||||
CompletedSlotsReceiver,
|
||||
LeaderScheduleCache,
|
||||
PohConfig,
|
||||
Option<(Slot, Hash)>,
|
||||
) {
|
||||
let genesis_config = GenesisConfig::load(blockstore_path).unwrap_or_else(|err| {
|
||||
error!("Failed to load genesis from {:?}: {}", blockstore_path, err);
|
||||
@ -548,31 +558,32 @@ fn new_banks_from_blockstore(
|
||||
..blockstore_processor::ProcessOptions::default()
|
||||
};
|
||||
|
||||
let (mut bank_forks, bank_forks_info, mut leader_schedule_cache) = bank_forks_utils::load(
|
||||
&genesis_config,
|
||||
&blockstore,
|
||||
config.account_paths.clone(),
|
||||
config.snapshot_config.as_ref(),
|
||||
process_options,
|
||||
)
|
||||
.unwrap_or_else(|err| {
|
||||
error!("Failed to load ledger: {:?}", err);
|
||||
std::process::exit(1);
|
||||
});
|
||||
let (mut bank_forks, bank_forks_info, mut leader_schedule_cache, snapshot_hash) =
|
||||
bank_forks_utils::load(
|
||||
&genesis_config,
|
||||
&blockstore,
|
||||
config.account_paths.clone(),
|
||||
config.snapshot_config.as_ref(),
|
||||
process_options,
|
||||
)
|
||||
.unwrap_or_else(|err| {
|
||||
error!("Failed to load ledger: {:?}", err);
|
||||
std::process::exit(1);
|
||||
});
|
||||
|
||||
leader_schedule_cache.set_fixed_leader_schedule(config.fixed_leader_schedule.clone());
|
||||
|
||||
bank_forks.set_snapshot_config(config.snapshot_config.clone());
|
||||
|
||||
(
|
||||
genesis_hash,
|
||||
genesis_config,
|
||||
bank_forks,
|
||||
bank_forks_info,
|
||||
blockstore,
|
||||
ledger_signal_receiver,
|
||||
completed_slots_receiver,
|
||||
leader_schedule_cache,
|
||||
genesis_config.poh_config,
|
||||
snapshot_hash,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -494,7 +494,7 @@ mod test {
|
||||
clock::Slot,
|
||||
epoch_schedule::MINIMUM_SLOTS_PER_EPOCH,
|
||||
hash::Hash,
|
||||
signature::{Keypair, KeypairUtil},
|
||||
signature::{Keypair, Signer},
|
||||
};
|
||||
use std::{
|
||||
net::UdpSocket,
|
||||
|
@ -5,6 +5,8 @@ mod tests {
|
||||
use bincode::serialize_into;
|
||||
use fs_extra::dir::CopyOptions;
|
||||
use itertools::Itertools;
|
||||
use solana_core::cluster_info::ClusterInfo;
|
||||
use solana_core::contact_info::ContactInfo;
|
||||
use solana_core::{
|
||||
genesis_utils::{create_genesis_config, GenesisConfigInfo},
|
||||
snapshot_packager_service::SnapshotPackagerService,
|
||||
@ -14,16 +16,17 @@ mod tests {
|
||||
snapshot_utils,
|
||||
};
|
||||
use solana_runtime::{
|
||||
bank::Bank,
|
||||
status_cache::{SlotDelta, MAX_CACHE_ENTRIES},
|
||||
bank::{Bank, BankSlotDelta},
|
||||
status_cache::MAX_CACHE_ENTRIES,
|
||||
};
|
||||
use solana_sdk::{
|
||||
clock::Slot,
|
||||
hash::hashv,
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, KeypairUtil},
|
||||
system_transaction, transaction,
|
||||
signature::{Keypair, Signer},
|
||||
system_transaction,
|
||||
};
|
||||
use std::sync::RwLock;
|
||||
use std::{fs, path::PathBuf, sync::atomic::AtomicBool, sync::mpsc::channel, sync::Arc};
|
||||
use tempfile::TempDir;
|
||||
|
||||
@ -128,6 +131,7 @@ mod tests {
|
||||
}
|
||||
// Generate a snapshot package for last bank
|
||||
let last_bank = bank_forks.get(last_slot).unwrap();
|
||||
let storages: Vec<_> = last_bank.get_snapshot_storages();
|
||||
let slot_snapshot_paths =
|
||||
snapshot_utils::get_snapshot_paths(&snapshot_config.snapshot_path);
|
||||
let snapshot_package = snapshot_utils::package_snapshot(
|
||||
@ -140,6 +144,7 @@ mod tests {
|
||||
),
|
||||
&snapshot_config.snapshot_path,
|
||||
&last_bank.src.roots(),
|
||||
storages,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@ -199,7 +204,8 @@ mod tests {
|
||||
|
||||
// Take snapshot of zeroth bank
|
||||
let bank0 = bank_forks.get(0).unwrap();
|
||||
snapshot_utils::add_snapshot(&snapshot_config.snapshot_path, bank0).unwrap();
|
||||
let storages: Vec<_> = bank0.get_snapshot_storages();
|
||||
snapshot_utils::add_snapshot(&snapshot_config.snapshot_path, bank0, &storages).unwrap();
|
||||
|
||||
// Set up snapshotting channels
|
||||
let (sender, receiver) = channel();
|
||||
@ -296,7 +302,13 @@ mod tests {
|
||||
// correctly construct the earlier snapshots because the SnapshotPackage's on the
|
||||
// channel hold hard links to these deleted snapshots. We verify this is the case below.
|
||||
let exit = Arc::new(AtomicBool::new(false));
|
||||
let snapshot_packager_service = SnapshotPackagerService::new(receiver, &exit);
|
||||
|
||||
let cluster_info = Arc::new(RwLock::new(ClusterInfo::new_with_invalid_keypair(
|
||||
ContactInfo::default(),
|
||||
)));
|
||||
|
||||
let snapshot_packager_service =
|
||||
SnapshotPackagerService::new(receiver, &exit, &cluster_info);
|
||||
|
||||
// Close the channel so that the package service will exit after reading all the
|
||||
// packages off the channel
|
||||
@ -312,7 +324,7 @@ mod tests {
|
||||
// before we compare, stick an empty status_cache in this dir so that the package comparision works
|
||||
// This is needed since the status_cache is added by the packager and is not collected from
|
||||
// the source dir for snapshots
|
||||
let dummy_slot_deltas: Vec<SlotDelta<transaction::Result<()>>> = vec![];
|
||||
let dummy_slot_deltas: Vec<BankSlotDelta> = vec![];
|
||||
snapshot_utils::serialize_snapshot_data_file(
|
||||
&saved_snapshots_dir
|
||||
.path()
|
||||
|
@ -1,12 +1,13 @@
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_core::validator::new_validator_for_tests;
|
||||
use solana_sdk::commitment_config::CommitmentConfig;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::signature::KeypairUtil;
|
||||
use solana_sdk::system_transaction;
|
||||
use std::fs::remove_dir_all;
|
||||
use std::thread::sleep;
|
||||
use std::time::{Duration, Instant};
|
||||
use solana_sdk::{
|
||||
commitment_config::CommitmentConfig, pubkey::Pubkey, signature::Signer, system_transaction,
|
||||
};
|
||||
use std::{
|
||||
fs::remove_dir_all,
|
||||
thread::sleep,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_rpc_client() {
|
||||
|
@ -6,7 +6,7 @@ use solana_core::cluster_info::{ClusterInfo, Node};
|
||||
use solana_core::gossip_service::GossipService;
|
||||
|
||||
use solana_core::packet::Packet;
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use solana_sdk::timing::timestamp;
|
||||
use std::net::UdpSocket;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
@ -15,7 +15,7 @@ mod tests {
|
||||
use solana_sdk::hash::Hash;
|
||||
use solana_sdk::message::Message;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use solana_sdk::transaction::Transaction;
|
||||
use solana_storage_program::storage_instruction;
|
||||
use solana_storage_program::storage_instruction::StorageAccountType;
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-crate-features"
|
||||
version = "0.23.5"
|
||||
version = "0.23.6"
|
||||
description = "Solana Crate Features"
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-faucet"
|
||||
version = "0.23.5"
|
||||
version = "0.23.6"
|
||||
description = "Solana Faucet"
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@ -19,10 +19,10 @@ clap = "2.33"
|
||||
log = "0.4.8"
|
||||
serde = "1.0.104"
|
||||
serde_derive = "1.0.103"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "0.23.5" }
|
||||
solana-logger = { path = "../logger", version = "0.23.5" }
|
||||
solana-metrics = { path = "../metrics", version = "0.23.5" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.5" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "0.23.6" }
|
||||
solana-logger = { path = "../logger", version = "0.23.6" }
|
||||
solana-metrics = { path = "../metrics", version = "0.23.6" }
|
||||
solana-sdk = { path = "../sdk", version = "0.23.6" }
|
||||
tokio = "0.1"
|
||||
tokio-codec = "0.1"
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user