Compare commits
70 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
ee5aa0c1a2 | ||
|
55dee2901e | ||
|
2b0824d18b | ||
|
b0709ea0ac | ||
|
81b5499f7a | ||
|
c96ce99705 | ||
|
8dcd2d11e1 | ||
|
777aae9059 | ||
|
4dd1340236 | ||
|
889b06e1d4 | ||
|
f511296ee8 | ||
|
c19eb717b4 | ||
|
dd54369e1b | ||
|
bb563b4835 | ||
|
d061fadede | ||
|
577cd2bd3a | ||
|
9d1c8657e2 | ||
|
67216ac7f5 | ||
|
e63b24c39f | ||
|
d963f7afb4 | ||
|
a07bf4870a | ||
|
8422d4b3fb | ||
|
ff4731cce2 | ||
|
659aaafff6 | ||
|
175651c497 | ||
|
085e773f27 | ||
|
5d3140c040 | ||
|
d8d7238920 | ||
|
418c3cd4cf | ||
|
9f532cb50f | ||
|
c35f4927cd | ||
|
2d0f4b5c8c | ||
|
fe59ee61e6 | ||
|
bda2acb06d | ||
|
ac3fe1da02 | ||
|
57813041d2 | ||
|
7e446da82c | ||
|
d94f5c94a3 | ||
|
4d9aee4794 | ||
|
f32c152bce | ||
|
5caf9110e0 | ||
|
d1c80143ea | ||
|
bb132df121 | ||
|
b3af1c7e57 | ||
|
14cba53338 | ||
|
dc8abbe9e3 | ||
|
dd06001ed8 | ||
|
298b7de2e2 | ||
|
74cbc6953f | ||
|
27e5203078 | ||
|
73787e162c | ||
|
74ae40be41 | ||
|
8dd58e9cea | ||
|
7d86179c60 | ||
|
8115cf1360 | ||
|
3e5d45053d | ||
|
061319f35a | ||
|
dc75837500 | ||
|
dfe26f5275 | ||
|
f4385f7ad2 | ||
|
8df4cf2895 | ||
|
dad62e132e | ||
|
0d4131ae68 | ||
|
a2539e1892 | ||
|
210659e6c3 | ||
|
15a0fb1fa9 | ||
|
4db31f5b48 | ||
|
b38a535c63 | ||
|
218b02aaf8 | ||
|
d6e7cbd4e8 |
CONTRIBUTING.mdCargo.lock
archiver-lib
archiver-utils
archiver
banking-bench
bench-exchange
bench-streamer
bench-tps
chacha-cuda
chacha-sys
chacha
ci
clap-utils
cli-config
cli
client
core
Cargo.toml
src
cluster_info.rsgossip_service.rslocal_vote_signer_service.rsrepair_service.rsreplay_stage.rsretransmit_stage.rsrpc.rsrpc_service.rssnapshot_packager_service.rsstreamer.rstransaction_status_service.rstvu.rsvalidator.rs
tests
crate-features
docs
offline-cmd-md-links.sh
src
SUMMARY.md
apps
building-from-source.mdcli
cluster
install-solana.mdintroduction.mdoffline-signing
paper-wallet
remote-wallet
running-archiver.mdrunning-validator
tour-de-sol
registration
faucet
genesis-programs
genesis
gossip
install
keygen
ledger-tool
ledger
local-cluster
log-analyzer
logger
measure
merkle-tree
metrics
multinode-demo
net-shaper
net-utils
net/remote
perf
programs
bpf
Cargo.toml
rust
128bit
128bit_dep
alloc
dep_crate
dup_accounts
error_handling
external_spend
iter
many_args
many_args_dep
noop
panic
param_passing
param_passing_dep
sysval
bpf_loader
btc_spv
btc_spv_bin
budget
config
exchange
failure
librapay
move_loader
noop
ownable
stake
storage
vest
vote
rayon-threadlimit
remote-wallet
run.shruntime
Cargo.toml
src
scripts
sdk-c
sdk
sys-tuner
upload-perf
validator
vote-signer
watchtower
@@ -45,7 +45,7 @@ $ git pull --rebase upstream master
|
|||||||
|
|
||||||
If there are no functional changes, PRs can be very large and that's no
|
If there are no functional changes, PRs can be very large and that's no
|
||||||
problem. If, however, your changes are making meaningful changes or additions,
|
problem. If, however, your changes are making meaningful changes or additions,
|
||||||
then about 1.0.1 lines of changes is about the most you should ask a Solana
|
then about 1.0.5 lines of changes is about the most you should ask a Solana
|
||||||
maintainer to review.
|
maintainer to review.
|
||||||
|
|
||||||
### Should I send small PRs as I develop large, new components?
|
### Should I send small PRs as I develop large, new components?
|
||||||
|
670
Cargo.lock
generated
670
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-archiver-lib"
|
name = "solana-archiver-lib"
|
||||||
version = "1.0.1"
|
version = "1.0.5"
|
||||||
description = "Solana Archiver Library"
|
description = "Solana Archiver Library"
|
||||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
@@ -15,22 +15,22 @@ ed25519-dalek = "=1.0.0-pre.1"
|
|||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
rand = "0.6.5"
|
rand = "0.6.5"
|
||||||
rand_chacha = "0.1.1"
|
rand_chacha = "0.1.1"
|
||||||
solana-client = { path = "../client", version = "1.0.1" }
|
solana-client = { path = "../client", version = "1.0.5" }
|
||||||
solana-storage-program = { path = "../programs/storage", version = "1.0.1" }
|
solana-storage-program = { path = "../programs/storage", version = "1.0.5" }
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
serde = "1.0.104"
|
serde = "1.0.104"
|
||||||
serde_json = "1.0.46"
|
serde_json = "1.0.46"
|
||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
solana-net-utils = { path = "../net-utils", version = "1.0.1" }
|
solana-net-utils = { path = "../net-utils", version = "1.0.5" }
|
||||||
solana-chacha = { path = "../chacha", version = "1.0.1" }
|
solana-chacha = { path = "../chacha", version = "1.0.5" }
|
||||||
solana-chacha-sys = { path = "../chacha-sys", version = "1.0.1" }
|
solana-chacha-sys = { path = "../chacha-sys", version = "1.0.5" }
|
||||||
solana-ledger = { path = "../ledger", version = "1.0.1" }
|
solana-ledger = { path = "../ledger", version = "1.0.5" }
|
||||||
solana-logger = { path = "../logger", version = "1.0.1" }
|
solana-logger = { path = "../logger", version = "1.0.5" }
|
||||||
solana-perf = { path = "../perf", version = "1.0.1" }
|
solana-perf = { path = "../perf", version = "1.0.5" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.0.1" }
|
solana-sdk = { path = "../sdk", version = "1.0.5" }
|
||||||
solana-core = { path = "../core", version = "1.0.1" }
|
solana-core = { path = "../core", version = "1.0.5" }
|
||||||
solana-archiver-utils = { path = "../archiver-utils", version = "1.0.1" }
|
solana-archiver-utils = { path = "../archiver-utils", version = "1.0.5" }
|
||||||
solana-metrics = { path = "../metrics", version = "1.0.1" }
|
solana-metrics = { path = "../metrics", version = "1.0.5" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hex = "0.4.0"
|
hex = "0.4.0"
|
||||||
|
@@ -47,7 +47,7 @@ use solana_storage_program::{
|
|||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
io::{self, ErrorKind},
|
io::{self, ErrorKind},
|
||||||
net::{SocketAddr, UdpSocket},
|
net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
result,
|
result,
|
||||||
sync::atomic::{AtomicBool, Ordering},
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
@@ -211,12 +211,9 @@ impl Archiver {
|
|||||||
let client = solana_core::gossip_service::get_client(&nodes);
|
let client = solana_core::gossip_service::get_client(&nodes);
|
||||||
|
|
||||||
info!("Setting up mining account...");
|
info!("Setting up mining account...");
|
||||||
if let Err(e) = Self::setup_mining_account(
|
if let Err(e) =
|
||||||
&client,
|
Self::setup_mining_account(&client, &keypair, &storage_keypair, client_commitment)
|
||||||
&keypair,
|
{
|
||||||
&storage_keypair,
|
|
||||||
client_commitment.clone(),
|
|
||||||
) {
|
|
||||||
//shutdown services before exiting
|
//shutdown services before exiting
|
||||||
exit.store(true, Ordering::Relaxed);
|
exit.store(true, Ordering::Relaxed);
|
||||||
gossip_service.join()?;
|
gossip_service.join()?;
|
||||||
@@ -358,7 +355,7 @@ impl Archiver {
|
|||||||
&cluster_info,
|
&cluster_info,
|
||||||
archiver_keypair,
|
archiver_keypair,
|
||||||
storage_keypair,
|
storage_keypair,
|
||||||
meta.client_commitment.clone(),
|
meta.client_commitment,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
exit.store(true, Ordering::Relaxed);
|
exit.store(true, Ordering::Relaxed);
|
||||||
@@ -374,7 +371,7 @@ impl Archiver {
|
|||||||
let client = solana_core::gossip_service::get_client(&nodes);
|
let client = solana_core::gossip_service::get_client(&nodes);
|
||||||
|
|
||||||
if let Ok(Some(account)) =
|
if let Ok(Some(account)) =
|
||||||
client.get_account_with_commitment(&storage_keypair.pubkey(), client_commitment.clone())
|
client.get_account_with_commitment(&storage_keypair.pubkey(), client_commitment)
|
||||||
{
|
{
|
||||||
if let Ok(StorageContract::ArchiverStorage { validations, .. }) = account.state() {
|
if let Ok(StorageContract::ArchiverStorage { validations, .. }) = account.state() {
|
||||||
if !validations.is_empty() {
|
if !validations.is_empty() {
|
||||||
@@ -415,7 +412,7 @@ impl Archiver {
|
|||||||
slot_sender: Sender<u64>,
|
slot_sender: Sender<u64>,
|
||||||
) -> Result<WindowService> {
|
) -> Result<WindowService> {
|
||||||
let slots_per_segment =
|
let slots_per_segment =
|
||||||
match Self::get_segment_config(&cluster_info, meta.client_commitment.clone()) {
|
match Self::get_segment_config(&cluster_info, meta.client_commitment) {
|
||||||
Ok(slots_per_segment) => slots_per_segment,
|
Ok(slots_per_segment) => slots_per_segment,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("unable to get segment size configuration, exiting...");
|
error!("unable to get segment size configuration, exiting...");
|
||||||
@@ -581,7 +578,7 @@ impl Archiver {
|
|||||||
&keypair.pubkey(),
|
&keypair.pubkey(),
|
||||||
&Duration::from_millis(100),
|
&Duration::from_millis(100),
|
||||||
&Duration::from_secs(5),
|
&Duration::from_secs(5),
|
||||||
client_commitment.clone(),
|
client_commitment,
|
||||||
)? == 0
|
)? == 0
|
||||||
{
|
{
|
||||||
return Err(ArchiverError::EmptyStorageAccountBalance);
|
return Err(ArchiverError::EmptyStorageAccountBalance);
|
||||||
@@ -589,11 +586,10 @@ impl Archiver {
|
|||||||
|
|
||||||
info!("checking storage account keypair...");
|
info!("checking storage account keypair...");
|
||||||
// check if the storage account exists
|
// check if the storage account exists
|
||||||
let balance = client
|
let balance =
|
||||||
.poll_get_balance_with_commitment(&storage_keypair.pubkey(), client_commitment.clone());
|
client.poll_get_balance_with_commitment(&storage_keypair.pubkey(), client_commitment);
|
||||||
if balance.is_err() || balance.unwrap() == 0 {
|
if balance.is_err() || balance.unwrap() == 0 {
|
||||||
let blockhash =
|
let blockhash = match client.get_recent_blockhash_with_commitment(client_commitment) {
|
||||||
match client.get_recent_blockhash_with_commitment(client_commitment.clone()) {
|
|
||||||
Ok((blockhash, _)) => blockhash,
|
Ok((blockhash, _)) => blockhash,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(ArchiverError::TransportError(e));
|
return Err(ArchiverError::TransportError(e));
|
||||||
@@ -631,26 +627,21 @@ impl Archiver {
|
|||||||
// No point if we've got no storage account...
|
// No point if we've got no storage account...
|
||||||
let nodes = cluster_info.read().unwrap().tvu_peers();
|
let nodes = cluster_info.read().unwrap().tvu_peers();
|
||||||
let client = solana_core::gossip_service::get_client(&nodes);
|
let client = solana_core::gossip_service::get_client(&nodes);
|
||||||
let storage_balance = client.poll_get_balance_with_commitment(
|
let storage_balance = client
|
||||||
&storage_keypair.pubkey(),
|
.poll_get_balance_with_commitment(&storage_keypair.pubkey(), meta.client_commitment);
|
||||||
meta.client_commitment.clone(),
|
|
||||||
);
|
|
||||||
if storage_balance.is_err() || storage_balance.unwrap() == 0 {
|
if storage_balance.is_err() || storage_balance.unwrap() == 0 {
|
||||||
error!("Unable to submit mining proof, no storage account");
|
error!("Unable to submit mining proof, no storage account");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// ...or no lamports for fees
|
// ...or no lamports for fees
|
||||||
let balance = client.poll_get_balance_with_commitment(
|
let balance = client
|
||||||
&archiver_keypair.pubkey(),
|
.poll_get_balance_with_commitment(&archiver_keypair.pubkey(), meta.client_commitment);
|
||||||
meta.client_commitment.clone(),
|
|
||||||
);
|
|
||||||
if balance.is_err() || balance.unwrap() == 0 {
|
if balance.is_err() || balance.unwrap() == 0 {
|
||||||
error!("Unable to submit mining proof, insufficient Archiver Account balance");
|
error!("Unable to submit mining proof, insufficient Archiver Account balance");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let blockhash =
|
let blockhash = match client.get_recent_blockhash_with_commitment(meta.client_commitment) {
|
||||||
match client.get_recent_blockhash_with_commitment(meta.client_commitment.clone()) {
|
|
||||||
Ok((blockhash, _)) => blockhash,
|
Ok((blockhash, _)) => blockhash,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
error!("unable to get recent blockhash, can't submit proof");
|
error!("unable to get recent blockhash, can't submit proof");
|
||||||
@@ -813,14 +804,15 @@ impl Archiver {
|
|||||||
blockstore: &Arc<Blockstore>,
|
blockstore: &Arc<Blockstore>,
|
||||||
slots_per_segment: u64,
|
slots_per_segment: u64,
|
||||||
) -> Result<u64> {
|
) -> Result<u64> {
|
||||||
|
let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
|
||||||
// Create a client which downloads from the archiver and see that it
|
// Create a client which downloads from the archiver and see that it
|
||||||
// can respond with shreds.
|
// can respond with shreds.
|
||||||
let start_slot = Self::get_archiver_segment_slot(archiver_info.storage_addr);
|
let start_slot = Self::get_archiver_segment_slot(ip_addr, archiver_info.storage_addr);
|
||||||
info!("Archiver download: start at {}", start_slot);
|
info!("Archiver download: start at {}", start_slot);
|
||||||
|
|
||||||
let exit = Arc::new(AtomicBool::new(false));
|
let exit = Arc::new(AtomicBool::new(false));
|
||||||
let (s_reader, r_reader) = channel();
|
let (s_reader, r_reader) = channel();
|
||||||
let repair_socket = Arc::new(bind_in_range(VALIDATOR_PORT_RANGE).unwrap().1);
|
let repair_socket = Arc::new(bind_in_range(ip_addr, VALIDATOR_PORT_RANGE).unwrap().1);
|
||||||
let t_receiver = receiver(
|
let t_receiver = receiver(
|
||||||
repair_socket.clone(),
|
repair_socket.clone(),
|
||||||
&exit,
|
&exit,
|
||||||
@@ -917,8 +909,8 @@ impl Archiver {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_archiver_segment_slot(to: SocketAddr) -> u64 {
|
fn get_archiver_segment_slot(bind_ip_addr: IpAddr, to: SocketAddr) -> u64 {
|
||||||
let (_port, socket) = bind_in_range(VALIDATOR_PORT_RANGE).unwrap();
|
let (_port, socket) = bind_in_range(bind_ip_addr, VALIDATOR_PORT_RANGE).unwrap();
|
||||||
socket
|
socket
|
||||||
.set_read_timeout(Some(Duration::from_secs(5)))
|
.set_read_timeout(Some(Duration::from_secs(5)))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-archiver-utils"
|
name = "solana-archiver-utils"
|
||||||
version = "1.0.1"
|
version = "1.0.5"
|
||||||
description = "Solana Archiver Utils"
|
description = "Solana Archiver Utils"
|
||||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
@@ -11,12 +11,12 @@ edition = "2018"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
rand = "0.6.5"
|
rand = "0.6.5"
|
||||||
solana-chacha = { path = "../chacha", version = "1.0.1" }
|
solana-chacha = { path = "../chacha", version = "1.0.5" }
|
||||||
solana-chacha-sys = { path = "../chacha-sys", version = "1.0.1" }
|
solana-chacha-sys = { path = "../chacha-sys", version = "1.0.5" }
|
||||||
solana-ledger = { path = "../ledger", version = "1.0.1" }
|
solana-ledger = { path = "../ledger", version = "1.0.5" }
|
||||||
solana-logger = { path = "../logger", version = "1.0.1" }
|
solana-logger = { path = "../logger", version = "1.0.5" }
|
||||||
solana-perf = { path = "../perf", version = "1.0.1" }
|
solana-perf = { path = "../perf", version = "1.0.5" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.0.1" }
|
solana-sdk = { path = "../sdk", version = "1.0.5" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hex = "0.4.0"
|
hex = "0.4.0"
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "solana-archiver"
|
name = "solana-archiver"
|
||||||
version = "1.0.1"
|
version = "1.0.5"
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
homepage = "https://solana.com/"
|
homepage = "https://solana.com/"
|
||||||
@@ -10,11 +10,11 @@ homepage = "https://solana.com/"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33.0"
|
clap = "2.33.0"
|
||||||
console = "0.9.2"
|
console = "0.9.2"
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "1.0.1" }
|
solana-clap-utils = { path = "../clap-utils", version = "1.0.5" }
|
||||||
solana-core = { path = "../core", version = "1.0.1" }
|
solana-core = { path = "../core", version = "1.0.5" }
|
||||||
solana-logger = { path = "../logger", version = "1.0.1" }
|
solana-logger = { path = "../logger", version = "1.0.5" }
|
||||||
solana-metrics = { path = "../metrics", version = "1.0.1" }
|
solana-metrics = { path = "../metrics", version = "1.0.5" }
|
||||||
solana-archiver-lib = { path = "../archiver-lib", version = "1.0.1" }
|
solana-archiver-lib = { path = "../archiver-lib", version = "1.0.5" }
|
||||||
solana-net-utils = { path = "../net-utils", version = "1.0.1" }
|
solana-net-utils = { path = "../net-utils", version = "1.0.5" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.0.1" }
|
solana-sdk = { path = "../sdk", version = "1.0.5" }
|
||||||
|
|
||||||
|
@@ -2,18 +2,22 @@ use clap::{crate_description, crate_name, App, Arg};
|
|||||||
use console::style;
|
use console::style;
|
||||||
use solana_archiver_lib::archiver::Archiver;
|
use solana_archiver_lib::archiver::Archiver;
|
||||||
use solana_clap_utils::{
|
use solana_clap_utils::{
|
||||||
input_validators::is_keypair,
|
input_parsers::keypair_of, input_validators::is_keypair_or_ask_keyword,
|
||||||
keypair::{
|
keypair::SKIP_SEED_PHRASE_VALIDATION_ARG,
|
||||||
self, keypair_input, KeypairWithSource, ASK_SEED_PHRASE_ARG,
|
|
||||||
SKIP_SEED_PHRASE_VALIDATION_ARG,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use solana_core::{
|
use solana_core::{
|
||||||
cluster_info::{Node, VALIDATOR_PORT_RANGE},
|
cluster_info::{Node, VALIDATOR_PORT_RANGE},
|
||||||
contact_info::ContactInfo,
|
contact_info::ContactInfo,
|
||||||
};
|
};
|
||||||
use solana_sdk::{commitment_config::CommitmentConfig, signature::Signer};
|
use solana_sdk::{
|
||||||
use std::{net::SocketAddr, path::PathBuf, process::exit, sync::Arc};
|
commitment_config::CommitmentConfig,
|
||||||
|
signature::{Keypair, Signer},
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
net::{IpAddr, Ipv4Addr, SocketAddr},
|
||||||
|
path::PathBuf,
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
@@ -27,7 +31,7 @@ fn main() {
|
|||||||
.long("identity-keypair")
|
.long("identity-keypair")
|
||||||
.value_name("PATH")
|
.value_name("PATH")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.validator(is_keypair)
|
.validator(is_keypair_or_ask_keyword)
|
||||||
.help("File containing an identity (keypair)"),
|
.help("File containing an identity (keypair)"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
@@ -55,48 +59,27 @@ fn main() {
|
|||||||
.long("storage-keypair")
|
.long("storage-keypair")
|
||||||
.value_name("PATH")
|
.value_name("PATH")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.validator(is_keypair)
|
.validator(is_keypair_or_ask_keyword)
|
||||||
.help("File containing the storage account keypair"),
|
.help("File containing the storage account keypair"),
|
||||||
)
|
)
|
||||||
.arg(
|
|
||||||
Arg::with_name(ASK_SEED_PHRASE_ARG.name)
|
|
||||||
.long(ASK_SEED_PHRASE_ARG.long)
|
|
||||||
.value_name("KEYPAIR NAME")
|
|
||||||
.multiple(true)
|
|
||||||
.takes_value(true)
|
|
||||||
.possible_values(&["identity-keypair", "storage-keypair"])
|
|
||||||
.help(ASK_SEED_PHRASE_ARG.help),
|
|
||||||
)
|
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(SKIP_SEED_PHRASE_VALIDATION_ARG.name)
|
Arg::with_name(SKIP_SEED_PHRASE_VALIDATION_ARG.name)
|
||||||
.long(SKIP_SEED_PHRASE_VALIDATION_ARG.long)
|
.long(SKIP_SEED_PHRASE_VALIDATION_ARG.long)
|
||||||
.requires(ASK_SEED_PHRASE_ARG.name)
|
|
||||||
.help(SKIP_SEED_PHRASE_VALIDATION_ARG.help),
|
.help(SKIP_SEED_PHRASE_VALIDATION_ARG.help),
|
||||||
)
|
)
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
let ledger_path = PathBuf::from(matches.value_of("ledger").unwrap());
|
let ledger_path = PathBuf::from(matches.value_of("ledger").unwrap());
|
||||||
|
|
||||||
let identity_keypair = keypair_input(&matches, "identity_keypair")
|
let identity_keypair = keypair_of(&matches, "identity_keypair").unwrap_or_else(Keypair::new);
|
||||||
.unwrap_or_else(|err| {
|
|
||||||
eprintln!("Identity keypair input failed: {}", err);
|
let storage_keypair = keypair_of(&matches, "storage_keypair").unwrap_or_else(|| {
|
||||||
exit(1);
|
|
||||||
})
|
|
||||||
.keypair;
|
|
||||||
let KeypairWithSource {
|
|
||||||
keypair: storage_keypair,
|
|
||||||
source: storage_keypair_source,
|
|
||||||
} = keypair_input(&matches, "storage_keypair").unwrap_or_else(|err| {
|
|
||||||
eprintln!("Storage keypair input failed: {}", err);
|
|
||||||
exit(1);
|
|
||||||
});
|
|
||||||
if storage_keypair_source == keypair::Source::Generated {
|
|
||||||
clap::Error::with_description(
|
clap::Error::with_description(
|
||||||
"The `storage-keypair` argument was not found",
|
"The `storage-keypair` argument was not found",
|
||||||
clap::ErrorKind::ArgumentNotFound,
|
clap::ErrorKind::ArgumentNotFound,
|
||||||
)
|
)
|
||||||
.exit();
|
.exit();
|
||||||
}
|
});
|
||||||
|
|
||||||
let entrypoint_addr = matches
|
let entrypoint_addr = matches
|
||||||
.value_of("entrypoint")
|
.value_of("entrypoint")
|
||||||
@@ -116,6 +99,7 @@ fn main() {
|
|||||||
&identity_keypair.pubkey(),
|
&identity_keypair.pubkey(),
|
||||||
&gossip_addr,
|
&gossip_addr,
|
||||||
VALIDATOR_PORT_RANGE,
|
VALIDATOR_PORT_RANGE,
|
||||||
|
IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
|
||||||
);
|
);
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "solana-banking-bench"
|
name = "solana-banking-bench"
|
||||||
version = "1.0.1"
|
version = "1.0.5"
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
homepage = "https://solana.com/"
|
homepage = "https://solana.com/"
|
||||||
@@ -10,11 +10,11 @@ homepage = "https://solana.com/"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.4.6"
|
log = "0.4.6"
|
||||||
rayon = "1.2.0"
|
rayon = "1.2.0"
|
||||||
solana-core = { path = "../core", version = "1.0.1" }
|
solana-core = { path = "../core", version = "1.0.5" }
|
||||||
solana-ledger = { path = "../ledger", version = "1.0.1" }
|
solana-ledger = { path = "../ledger", version = "1.0.5" }
|
||||||
solana-logger = { path = "../logger", version = "1.0.1" }
|
solana-logger = { path = "../logger", version = "1.0.5" }
|
||||||
solana-runtime = { path = "../runtime", version = "1.0.1" }
|
solana-runtime = { path = "../runtime", version = "1.0.5" }
|
||||||
solana-measure = { path = "../measure", version = "1.0.1" }
|
solana-measure = { path = "../measure", version = "1.0.5" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.0.1" }
|
solana-sdk = { path = "../sdk", version = "1.0.5" }
|
||||||
rand = "0.6.5"
|
rand = "0.6.5"
|
||||||
crossbeam-channel = "0.3"
|
crossbeam-channel = "0.3"
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "solana-bench-exchange"
|
name = "solana-bench-exchange"
|
||||||
version = "1.0.1"
|
version = "1.0.5"
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
homepage = "https://solana.com/"
|
homepage = "https://solana.com/"
|
||||||
@@ -18,17 +18,17 @@ rand = "0.6.5"
|
|||||||
rayon = "1.2.0"
|
rayon = "1.2.0"
|
||||||
serde_json = "1.0.46"
|
serde_json = "1.0.46"
|
||||||
serde_yaml = "0.8.11"
|
serde_yaml = "0.8.11"
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "1.0.1" }
|
solana-clap-utils = { path = "../clap-utils", version = "1.0.5" }
|
||||||
solana-core = { path = "../core", version = "1.0.1" }
|
solana-core = { path = "../core", version = "1.0.5" }
|
||||||
solana-genesis = { path = "../genesis", version = "1.0.1" }
|
solana-genesis = { path = "../genesis", version = "1.0.5" }
|
||||||
solana-client = { path = "../client", version = "1.0.1" }
|
solana-client = { path = "../client", version = "1.0.5" }
|
||||||
solana-faucet = { path = "../faucet", version = "1.0.1" }
|
solana-faucet = { path = "../faucet", version = "1.0.5" }
|
||||||
solana-exchange-program = { path = "../programs/exchange", version = "1.0.1" }
|
solana-exchange-program = { path = "../programs/exchange", version = "1.0.5" }
|
||||||
solana-logger = { path = "../logger", version = "1.0.1" }
|
solana-logger = { path = "../logger", version = "1.0.5" }
|
||||||
solana-metrics = { path = "../metrics", version = "1.0.1" }
|
solana-metrics = { path = "../metrics", version = "1.0.5" }
|
||||||
solana-net-utils = { path = "../net-utils", version = "1.0.1" }
|
solana-net-utils = { path = "../net-utils", version = "1.0.5" }
|
||||||
solana-runtime = { path = "../runtime", version = "1.0.1" }
|
solana-runtime = { path = "../runtime", version = "1.0.5" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.0.1" }
|
solana-sdk = { path = "../sdk", version = "1.0.5" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
solana-local-cluster = { path = "../local-cluster", version = "1.0.1" }
|
solana-local-cluster = { path = "../local-cluster", version = "1.0.5" }
|
||||||
|
@@ -2,14 +2,14 @@
|
|||||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "solana-bench-streamer"
|
name = "solana-bench-streamer"
|
||||||
version = "1.0.1"
|
version = "1.0.5"
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
homepage = "https://solana.com/"
|
homepage = "https://solana.com/"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33.0"
|
clap = "2.33.0"
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "1.0.1" }
|
solana-clap-utils = { path = "../clap-utils", version = "1.0.5" }
|
||||||
solana-core = { path = "../core", version = "1.0.1" }
|
solana-core = { path = "../core", version = "1.0.5" }
|
||||||
solana-logger = { path = "../logger", version = "1.0.1" }
|
solana-logger = { path = "../logger", version = "1.0.5" }
|
||||||
solana-net-utils = { path = "../net-utils", version = "1.0.1" }
|
solana-net-utils = { path = "../net-utils", version = "1.0.5" }
|
||||||
|
@@ -67,7 +67,8 @@ fn main() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut port = 0;
|
let mut port = 0;
|
||||||
let mut addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0);
|
let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
|
||||||
|
let mut addr = SocketAddr::new(ip_addr, 0);
|
||||||
|
|
||||||
let exit = Arc::new(AtomicBool::new(false));
|
let exit = Arc::new(AtomicBool::new(false));
|
||||||
|
|
||||||
@@ -75,7 +76,7 @@ fn main() -> Result<()> {
|
|||||||
let mut read_threads = Vec::new();
|
let mut read_threads = Vec::new();
|
||||||
let recycler = PacketsRecycler::default();
|
let recycler = PacketsRecycler::default();
|
||||||
for _ in 0..num_sockets {
|
for _ in 0..num_sockets {
|
||||||
let read = solana_net_utils::bind_to(port, false).unwrap();
|
let read = solana_net_utils::bind_to(ip_addr, port, false).unwrap();
|
||||||
read.set_read_timeout(Some(Duration::new(1, 0))).unwrap();
|
read.set_read_timeout(Some(Duration::new(1, 0))).unwrap();
|
||||||
|
|
||||||
addr = read.local_addr().unwrap();
|
addr = read.local_addr().unwrap();
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "solana-bench-tps"
|
name = "solana-bench-tps"
|
||||||
version = "1.0.1"
|
version = "1.0.5"
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
homepage = "https://solana.com/"
|
homepage = "https://solana.com/"
|
||||||
@@ -14,24 +14,24 @@ log = "0.4.8"
|
|||||||
rayon = "1.2.0"
|
rayon = "1.2.0"
|
||||||
serde_json = "1.0.46"
|
serde_json = "1.0.46"
|
||||||
serde_yaml = "0.8.11"
|
serde_yaml = "0.8.11"
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "1.0.1" }
|
solana-clap-utils = { path = "../clap-utils", version = "1.0.5" }
|
||||||
solana-core = { path = "../core", version = "1.0.1" }
|
solana-core = { path = "../core", version = "1.0.5" }
|
||||||
solana-genesis = { path = "../genesis", version = "1.0.1" }
|
solana-genesis = { path = "../genesis", version = "1.0.5" }
|
||||||
solana-client = { path = "../client", version = "1.0.1" }
|
solana-client = { path = "../client", version = "1.0.5" }
|
||||||
solana-faucet = { path = "../faucet", version = "1.0.1" }
|
solana-faucet = { path = "../faucet", version = "1.0.5" }
|
||||||
solana-librapay = { path = "../programs/librapay", version = "1.0.1", optional = true }
|
solana-librapay = { path = "../programs/librapay", version = "1.0.5", optional = true }
|
||||||
solana-logger = { path = "../logger", version = "1.0.1" }
|
solana-logger = { path = "../logger", version = "1.0.5" }
|
||||||
solana-metrics = { path = "../metrics", version = "1.0.1" }
|
solana-metrics = { path = "../metrics", version = "1.0.5" }
|
||||||
solana-measure = { path = "../measure", version = "1.0.1" }
|
solana-measure = { path = "../measure", version = "1.0.5" }
|
||||||
solana-net-utils = { path = "../net-utils", version = "1.0.1" }
|
solana-net-utils = { path = "../net-utils", version = "1.0.5" }
|
||||||
solana-runtime = { path = "../runtime", version = "1.0.1" }
|
solana-runtime = { path = "../runtime", version = "1.0.5" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.0.1" }
|
solana-sdk = { path = "../sdk", version = "1.0.5" }
|
||||||
solana-move-loader-program = { path = "../programs/move_loader", version = "1.0.1", optional = true }
|
solana-move-loader-program = { path = "../programs/move_loader", version = "1.0.5", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serial_test = "0.3.2"
|
serial_test = "0.3.2"
|
||||||
serial_test_derive = "0.4.0"
|
serial_test_derive = "0.4.0"
|
||||||
solana-local-cluster = { path = "../local-cluster", version = "1.0.1" }
|
solana-local-cluster = { path = "../local-cluster", version = "1.0.5" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
move = ["solana-librapay", "solana-move-loader-program"]
|
move = ["solana-librapay", "solana-move-loader-program"]
|
||||||
|
@@ -1059,8 +1059,8 @@ pub fn generate_and_fund_keypairs<T: 'static + Client + Send + Sync>(
|
|||||||
// pay for the transaction fees in a new run.
|
// pay for the transaction fees in a new run.
|
||||||
let enough_lamports = 8 * lamports_per_account / 10;
|
let enough_lamports = 8 * lamports_per_account / 10;
|
||||||
if first_keypair_balance < enough_lamports || last_keypair_balance < enough_lamports {
|
if first_keypair_balance < enough_lamports || last_keypair_balance < enough_lamports {
|
||||||
let (_blockhash, fee_calculator) = get_recent_blockhash(client.as_ref());
|
let fee_rate_governor = client.get_fee_rate_governor().unwrap();
|
||||||
let max_fee = fee_calculator.max_lamports_per_signature;
|
let max_fee = fee_rate_governor.max_lamports_per_signature;
|
||||||
let extra_fees = extra * max_fee;
|
let extra_fees = extra * max_fee;
|
||||||
let total_keypairs = keypairs.len() as u64 + 1; // Add one for funding keypair
|
let total_keypairs = keypairs.len() as u64 + 1; // Add one for funding keypair
|
||||||
let mut total = lamports_per_account * total_keypairs + extra_fees;
|
let mut total = lamports_per_account * total_keypairs + extra_fees;
|
||||||
@@ -1134,7 +1134,7 @@ mod tests {
|
|||||||
use solana_runtime::bank::Bank;
|
use solana_runtime::bank::Bank;
|
||||||
use solana_runtime::bank_client::BankClient;
|
use solana_runtime::bank_client::BankClient;
|
||||||
use solana_sdk::client::SyncClient;
|
use solana_sdk::client::SyncClient;
|
||||||
use solana_sdk::fee_calculator::FeeCalculator;
|
use solana_sdk::fee_calculator::FeeRateGovernor;
|
||||||
use solana_sdk::genesis_config::create_genesis_config;
|
use solana_sdk::genesis_config::create_genesis_config;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1181,8 +1181,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_bench_tps_fund_keys_with_fees() {
|
fn test_bench_tps_fund_keys_with_fees() {
|
||||||
let (mut genesis_config, id) = create_genesis_config(10_000);
|
let (mut genesis_config, id) = create_genesis_config(10_000);
|
||||||
let fee_calculator = FeeCalculator::new(11, 0);
|
let fee_rate_governor = FeeRateGovernor::new(11, 0);
|
||||||
genesis_config.fee_calculator = fee_calculator;
|
genesis_config.fee_rate_governor = fee_rate_governor;
|
||||||
let bank = Bank::new(&genesis_config);
|
let bank = Bank::new(&genesis_config);
|
||||||
let client = Arc::new(BankClient::new(bank));
|
let client = Arc::new(BankClient::new(bank));
|
||||||
let keypair_count = 20;
|
let keypair_count = 20;
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
use clap::{crate_description, crate_name, App, Arg, ArgMatches};
|
use clap::{crate_description, crate_name, App, Arg, ArgMatches};
|
||||||
use solana_faucet::faucet::FAUCET_PORT;
|
use solana_faucet::faucet::FAUCET_PORT;
|
||||||
use solana_sdk::fee_calculator::FeeCalculator;
|
use solana_sdk::fee_calculator::FeeRateGovernor;
|
||||||
use solana_sdk::signature::{read_keypair_file, Keypair};
|
use solana_sdk::signature::{read_keypair_file, Keypair};
|
||||||
use std::{net::SocketAddr, process::exit, time::Duration};
|
use std::{net::SocketAddr, process::exit, time::Duration};
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ impl Default for Config {
|
|||||||
client_ids_and_stake_file: String::new(),
|
client_ids_and_stake_file: String::new(),
|
||||||
write_to_client_file: false,
|
write_to_client_file: false,
|
||||||
read_from_client_file: false,
|
read_from_client_file: false,
|
||||||
target_lamports_per_signature: FeeCalculator::default().target_lamports_per_signature,
|
target_lamports_per_signature: FeeRateGovernor::default().target_lamports_per_signature,
|
||||||
multi_client: true,
|
multi_client: true,
|
||||||
use_move: false,
|
use_move: false,
|
||||||
num_lamports_per_account: NUM_LAMPORTS_PER_ACCOUNT_DEFAULT,
|
num_lamports_per_account: NUM_LAMPORTS_PER_ACCOUNT_DEFAULT,
|
||||||
|
@@ -3,7 +3,7 @@ use solana_bench_tps::bench::{do_bench_tps, generate_and_fund_keypairs, generate
|
|||||||
use solana_bench_tps::cli;
|
use solana_bench_tps::cli;
|
||||||
use solana_core::gossip_service::{discover_cluster, get_client, get_multi_client};
|
use solana_core::gossip_service::{discover_cluster, get_client, get_multi_client};
|
||||||
use solana_genesis::Base64Account;
|
use solana_genesis::Base64Account;
|
||||||
use solana_sdk::fee_calculator::FeeCalculator;
|
use solana_sdk::fee_calculator::FeeRateGovernor;
|
||||||
use solana_sdk::signature::{Keypair, Signer};
|
use solana_sdk::signature::{Keypair, Signer};
|
||||||
use solana_sdk::system_program;
|
use solana_sdk::system_program;
|
||||||
use std::{collections::HashMap, fs::File, io::prelude::*, path::Path, process::exit, sync::Arc};
|
use std::{collections::HashMap, fs::File, io::prelude::*, path::Path, process::exit, sync::Arc};
|
||||||
@@ -41,7 +41,7 @@ fn main() {
|
|||||||
let (keypairs, _) = generate_keypairs(&id, keypair_count as u64);
|
let (keypairs, _) = generate_keypairs(&id, keypair_count as u64);
|
||||||
let num_accounts = keypairs.len() as u64;
|
let num_accounts = keypairs.len() as u64;
|
||||||
let max_fee =
|
let max_fee =
|
||||||
FeeCalculator::new(*target_lamports_per_signature, 0).max_lamports_per_signature;
|
FeeRateGovernor::new(*target_lamports_per_signature, 0).max_lamports_per_signature;
|
||||||
let num_lamports_per_account = (num_accounts - 1 + NUM_SIGNATURES_FOR_TXS * max_fee)
|
let num_lamports_per_account = (num_accounts - 1 + NUM_SIGNATURES_FOR_TXS * max_fee)
|
||||||
/ num_accounts
|
/ num_accounts
|
||||||
+ num_lamports_per_account;
|
+ num_lamports_per_account;
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-chacha-cuda"
|
name = "solana-chacha-cuda"
|
||||||
version = "1.0.1"
|
version = "1.0.5"
|
||||||
description = "Solana Chacha Cuda APIs"
|
description = "Solana Chacha Cuda APIs"
|
||||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
@@ -10,12 +10,12 @@ edition = "2018"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
solana-archiver-utils = { path = "../archiver-utils", version = "1.0.1" }
|
solana-archiver-utils = { path = "../archiver-utils", version = "1.0.5" }
|
||||||
solana-chacha = { path = "../chacha", version = "1.0.1" }
|
solana-chacha = { path = "../chacha", version = "1.0.5" }
|
||||||
solana-ledger = { path = "../ledger", version = "1.0.1" }
|
solana-ledger = { path = "../ledger", version = "1.0.5" }
|
||||||
solana-logger = { path = "../logger", version = "1.0.1" }
|
solana-logger = { path = "../logger", version = "1.0.5" }
|
||||||
solana-perf = { path = "../perf", version = "1.0.1" }
|
solana-perf = { path = "../perf", version = "1.0.5" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.0.1" }
|
solana-sdk = { path = "../sdk", version = "1.0.5" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hex-literal = "0.2.1"
|
hex-literal = "0.2.1"
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-chacha-sys"
|
name = "solana-chacha-sys"
|
||||||
version = "1.0.1"
|
version = "1.0.5"
|
||||||
description = "Solana chacha-sys"
|
description = "Solana chacha-sys"
|
||||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-chacha"
|
name = "solana-chacha"
|
||||||
version = "1.0.1"
|
version = "1.0.5"
|
||||||
description = "Solana Chacha APIs"
|
description = "Solana Chacha APIs"
|
||||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
@@ -12,11 +12,11 @@ edition = "2018"
|
|||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
rand = "0.6.5"
|
rand = "0.6.5"
|
||||||
rand_chacha = "0.1.1"
|
rand_chacha = "0.1.1"
|
||||||
solana-chacha-sys = { path = "../chacha-sys", version = "1.0.1" }
|
solana-chacha-sys = { path = "../chacha-sys", version = "1.0.5" }
|
||||||
solana-ledger = { path = "../ledger", version = "1.0.1" }
|
solana-ledger = { path = "../ledger", version = "1.0.5" }
|
||||||
solana-logger = { path = "../logger", version = "1.0.1" }
|
solana-logger = { path = "../logger", version = "1.0.5" }
|
||||||
solana-perf = { path = "../perf", version = "1.0.1" }
|
solana-perf = { path = "../perf", version = "1.0.5" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.0.1" }
|
solana-sdk = { path = "../sdk", version = "1.0.5" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hex-literal = "0.2.1"
|
hex-literal = "0.2.1"
|
||||||
|
@@ -11,10 +11,11 @@ if [[ -n $CI_BRANCH ]]; then
|
|||||||
set -x
|
set -x
|
||||||
(
|
(
|
||||||
. ci/rust-version.sh stable
|
. ci/rust-version.sh stable
|
||||||
ci/docker-run.sh "$rust_stable_docker_image" make -C docs -B svg
|
ci/docker-run.sh "$rust_stable_docker_image" make -C docs
|
||||||
)
|
)
|
||||||
# make a local commit for the svgs
|
# make a local commit for the svgs
|
||||||
git add -A -f docs/src/.gitbook/assets/.
|
git add -A -f docs/src/.gitbook/assets/.
|
||||||
|
git add -f docs/src/cli/usage.md
|
||||||
if ! git diff-index --quiet HEAD; then
|
if ! git diff-index --quiet HEAD; then
|
||||||
git config user.email maintainers@solana.com
|
git config user.email maintainers@solana.com
|
||||||
git config user.name "$me"
|
git config user.name "$me"
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-clap-utils"
|
name = "solana-clap-utils"
|
||||||
version = "1.0.1"
|
version = "1.0.5"
|
||||||
description = "Solana utilities for the clap"
|
description = "Solana utilities for the clap"
|
||||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
@@ -11,8 +11,8 @@ edition = "2018"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33.0"
|
clap = "2.33.0"
|
||||||
rpassword = "4.0"
|
rpassword = "4.0"
|
||||||
solana-remote-wallet = { path = "../remote-wallet", version = "1.0.1" }
|
solana-remote-wallet = { path = "../remote-wallet", version = "1.0.5" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.0.1" }
|
solana-sdk = { path = "../sdk", version = "1.0.5" }
|
||||||
tiny-bip39 = "0.7.0"
|
tiny-bip39 = "0.7.0"
|
||||||
url = "2.1.0"
|
url = "2.1.0"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
|
@@ -1,9 +1,10 @@
|
|||||||
use crate::keypair::{
|
use crate::keypair::{
|
||||||
keypair_from_seed_phrase, signer_from_path, ASK_KEYWORD, SKIP_SEED_PHRASE_VALIDATION_ARG,
|
keypair_from_seed_phrase, pubkey_from_path, signer_from_path, ASK_KEYWORD,
|
||||||
|
SKIP_SEED_PHRASE_VALIDATION_ARG,
|
||||||
};
|
};
|
||||||
use chrono::DateTime;
|
use chrono::DateTime;
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
use solana_remote_wallet::remote_wallet::{DerivationPath, RemoteWalletManager};
|
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
clock::UnixTimestamp,
|
clock::UnixTimestamp,
|
||||||
native_token::sol_to_lamports,
|
native_token::sol_to_lamports,
|
||||||
@@ -111,18 +112,25 @@ pub fn signer_of(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lamports_of_sol(matches: &ArgMatches<'_>, name: &str) -> Option<u64> {
|
pub fn pubkey_of_signer(
|
||||||
value_of(matches, name).map(sol_to_lamports)
|
matches: &ArgMatches<'_>,
|
||||||
|
name: &str,
|
||||||
|
wallet_manager: Option<&Arc<RemoteWalletManager>>,
|
||||||
|
) -> Result<Option<Pubkey>, Box<dyn std::error::Error>> {
|
||||||
|
if let Some(location) = matches.value_of(name) {
|
||||||
|
Ok(Some(pubkey_from_path(
|
||||||
|
matches,
|
||||||
|
location,
|
||||||
|
name,
|
||||||
|
wallet_manager,
|
||||||
|
)?))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn derivation_of(matches: &ArgMatches<'_>, name: &str) -> Option<DerivationPath> {
|
pub fn lamports_of_sol(matches: &ArgMatches<'_>, name: &str) -> Option<u64> {
|
||||||
matches.value_of(name).map(|derivation_str| {
|
value_of(matches, name).map(sol_to_lamports)
|
||||||
let derivation_str = derivation_str.replace("'", "");
|
|
||||||
let mut parts = derivation_str.split('/');
|
|
||||||
let account = parts.next().map(|account| account.parse::<u32>().unwrap());
|
|
||||||
let change = parts.next().map(|change| change.parse::<u32>().unwrap());
|
|
||||||
DerivationPath { account, change }
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -299,40 +307,4 @@ mod tests {
|
|||||||
.get_matches_from(vec!["test", "--single", "0.03"]);
|
.get_matches_from(vec!["test", "--single", "0.03"]);
|
||||||
assert_eq!(lamports_of_sol(&matches, "single"), Some(30000000));
|
assert_eq!(lamports_of_sol(&matches, "single"), Some(30000000));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_derivation_of() {
|
|
||||||
let matches = app()
|
|
||||||
.clone()
|
|
||||||
.get_matches_from(vec!["test", "--single", "2/3"]);
|
|
||||||
assert_eq!(
|
|
||||||
derivation_of(&matches, "single"),
|
|
||||||
Some(DerivationPath {
|
|
||||||
account: Some(2),
|
|
||||||
change: Some(3)
|
|
||||||
})
|
|
||||||
);
|
|
||||||
assert_eq!(derivation_of(&matches, "another"), None);
|
|
||||||
let matches = app()
|
|
||||||
.clone()
|
|
||||||
.get_matches_from(vec!["test", "--single", "2"]);
|
|
||||||
assert_eq!(
|
|
||||||
derivation_of(&matches, "single"),
|
|
||||||
Some(DerivationPath {
|
|
||||||
account: Some(2),
|
|
||||||
change: None
|
|
||||||
})
|
|
||||||
);
|
|
||||||
assert_eq!(derivation_of(&matches, "another"), None);
|
|
||||||
let matches = app()
|
|
||||||
.clone()
|
|
||||||
.get_matches_from(vec!["test", "--single", "2'/3'"]);
|
|
||||||
assert_eq!(
|
|
||||||
derivation_of(&matches, "single"),
|
|
||||||
Some(DerivationPath {
|
|
||||||
account: Some(2),
|
|
||||||
change: Some(3)
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
use crate::keypair::{parse_keypair_path, KeypairUrl, ASK_KEYWORD};
|
use crate::keypair::{parse_keypair_path, KeypairUrl, ASK_KEYWORD};
|
||||||
use chrono::DateTime;
|
use chrono::DateTime;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
|
clock::Slot,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{read_keypair_file, Signature},
|
signature::{read_keypair_file, Signature},
|
||||||
@@ -93,6 +94,12 @@ pub fn is_url(string: String) -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_slot(slot: String) -> Result<(), String> {
|
||||||
|
slot.parse::<Slot>()
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(|e| format!("{:?}", e))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_port(port: String) -> Result<(), String> {
|
pub fn is_port(port: String) -> Result<(), String> {
|
||||||
port.parse::<u16>()
|
port.parse::<u16>()
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
|
@@ -1,10 +1,6 @@
|
|||||||
use crate::{
|
use crate::{input_parsers::pubkeys_sigs_of, offline::SIGNER_ARG, ArgConstant};
|
||||||
input_parsers::{derivation_of, pubkeys_sigs_of},
|
|
||||||
offline::SIGNER_ARG,
|
|
||||||
ArgConstant,
|
|
||||||
};
|
|
||||||
use bip39::{Language, Mnemonic, Seed};
|
use bip39::{Language, Mnemonic, Seed};
|
||||||
use clap::{values_t, ArgMatches, Error, ErrorKind};
|
use clap::{ArgMatches, Error, ErrorKind};
|
||||||
use rpassword::prompt_password_stderr;
|
use rpassword::prompt_password_stderr;
|
||||||
use solana_remote_wallet::{
|
use solana_remote_wallet::{
|
||||||
remote_keypair::generate_remote_keypair,
|
remote_keypair::generate_remote_keypair,
|
||||||
@@ -84,9 +80,9 @@ pub fn signer_from_path(
|
|||||||
if let Some(wallet_manager) = wallet_manager {
|
if let Some(wallet_manager) = wallet_manager {
|
||||||
Ok(Box::new(generate_remote_keypair(
|
Ok(Box::new(generate_remote_keypair(
|
||||||
path,
|
path,
|
||||||
derivation_of(matches, "derivation_path"),
|
|
||||||
wallet_manager,
|
wallet_manager,
|
||||||
matches.is_present("confirm_key"),
|
matches.is_present("confirm_key"),
|
||||||
|
keypair_name,
|
||||||
)?))
|
)?))
|
||||||
} else {
|
} else {
|
||||||
Err(RemoteWalletError::NoDeviceFound.into())
|
Err(RemoteWalletError::NoDeviceFound.into())
|
||||||
@@ -109,39 +105,27 @@ pub fn signer_from_path(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pubkey_from_path(
|
||||||
|
matches: &ArgMatches,
|
||||||
|
path: &str,
|
||||||
|
keypair_name: &str,
|
||||||
|
wallet_manager: Option<&Arc<RemoteWalletManager>>,
|
||||||
|
) -> Result<Pubkey, Box<dyn error::Error>> {
|
||||||
|
match parse_keypair_path(path) {
|
||||||
|
KeypairUrl::Pubkey(pubkey) => Ok(pubkey),
|
||||||
|
_ => Ok(signer_from_path(matches, path, keypair_name, wallet_manager)?.pubkey()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Keyword used to indicate that the user should be asked for a keypair seed phrase
|
// Keyword used to indicate that the user should be asked for a keypair seed phrase
|
||||||
pub const ASK_KEYWORD: &str = "ASK";
|
pub const ASK_KEYWORD: &str = "ASK";
|
||||||
|
|
||||||
pub const ASK_SEED_PHRASE_ARG: ArgConstant<'static> = ArgConstant {
|
|
||||||
long: "ask-seed-phrase",
|
|
||||||
name: "ask_seed_phrase",
|
|
||||||
help: "Recover a keypair using a seed phrase and optional passphrase",
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const SKIP_SEED_PHRASE_VALIDATION_ARG: ArgConstant<'static> = ArgConstant {
|
pub const SKIP_SEED_PHRASE_VALIDATION_ARG: ArgConstant<'static> = ArgConstant {
|
||||||
long: "skip-seed-phrase-validation",
|
long: "skip-seed-phrase-validation",
|
||||||
name: "skip_seed_phrase_validation",
|
name: "skip_seed_phrase_validation",
|
||||||
help: "Skip validation of seed phrases. Use this if your phrase does not use the BIP39 official English word list",
|
help: "Skip validation of seed phrases. Use this if your phrase does not use the BIP39 official English word list",
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub enum Source {
|
|
||||||
Generated,
|
|
||||||
Path,
|
|
||||||
SeedPhrase,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct KeypairWithSource {
|
|
||||||
pub keypair: Keypair,
|
|
||||||
pub source: Source,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl KeypairWithSource {
|
|
||||||
fn new(keypair: Keypair, source: Source) -> Self {
|
|
||||||
Self { keypair, source }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Prompts user for a passphrase and then asks for confirmirmation to check for mistakes
|
/// Prompts user for a passphrase and then asks for confirmirmation to check for mistakes
|
||||||
pub fn prompt_passphrase(prompt: &str) -> Result<String, Box<dyn error::Error>> {
|
pub fn prompt_passphrase(prompt: &str) -> Result<String, Box<dyn error::Error>> {
|
||||||
let passphrase = prompt_password_stderr(&prompt)?;
|
let passphrase = prompt_password_stderr(&prompt)?;
|
||||||
@@ -195,47 +179,6 @@ pub fn keypair_from_seed_phrase(
|
|||||||
Ok(keypair)
|
Ok(keypair)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks CLI arguments to determine whether a keypair should be:
|
|
||||||
/// - inputted securely via stdin,
|
|
||||||
/// - read in from a file,
|
|
||||||
/// - or newly generated
|
|
||||||
pub fn keypair_input(
|
|
||||||
matches: &clap::ArgMatches,
|
|
||||||
keypair_name: &str,
|
|
||||||
) -> Result<KeypairWithSource, Box<dyn error::Error>> {
|
|
||||||
let ask_seed_phrase_matches =
|
|
||||||
values_t!(matches.values_of(ASK_SEED_PHRASE_ARG.name), String).unwrap_or_default();
|
|
||||||
let keypair_match_name = keypair_name.replace('-', "_");
|
|
||||||
if ask_seed_phrase_matches
|
|
||||||
.iter()
|
|
||||||
.any(|s| s.as_str() == keypair_name)
|
|
||||||
{
|
|
||||||
if matches.value_of(keypair_match_name).is_some() {
|
|
||||||
clap::Error::with_description(
|
|
||||||
&format!(
|
|
||||||
"`--{} {}` cannot be used with `{} <PATH>`",
|
|
||||||
ASK_SEED_PHRASE_ARG.long, keypair_name, keypair_name
|
|
||||||
),
|
|
||||||
clap::ErrorKind::ArgumentConflict,
|
|
||||||
)
|
|
||||||
.exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
|
||||||
keypair_from_seed_phrase(keypair_name, skip_validation, true)
|
|
||||||
.map(|keypair| KeypairWithSource::new(keypair, Source::SeedPhrase))
|
|
||||||
} else if let Some(keypair_file) = matches.value_of(keypair_match_name) {
|
|
||||||
if keypair_file.starts_with("usb://") {
|
|
||||||
Ok(KeypairWithSource::new(Keypair::new(), Source::Path))
|
|
||||||
} else {
|
|
||||||
read_keypair_file(keypair_file)
|
|
||||||
.map(|keypair| KeypairWithSource::new(keypair, Source::Path))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(KeypairWithSource::new(Keypair::new(), Source::Generated))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sanitize_seed_phrase(seed_phrase: &str) -> String {
|
fn sanitize_seed_phrase(seed_phrase: &str) -> String {
|
||||||
seed_phrase
|
seed_phrase
|
||||||
.split_whitespace()
|
.split_whitespace()
|
||||||
@@ -246,14 +189,6 @@ fn sanitize_seed_phrase(seed_phrase: &str) -> String {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use clap::ArgMatches;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_keypair_input() {
|
|
||||||
let arg_matches = ArgMatches::default();
|
|
||||||
let KeypairWithSource { source, .. } = keypair_input(&arg_matches, "").unwrap();
|
|
||||||
assert_eq!(source, Source::Generated);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sanitize_seed_phrase() {
|
fn test_sanitize_seed_phrase() {
|
||||||
|
@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.com>"]
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "solana-cli-config"
|
name = "solana-cli-config"
|
||||||
description = "Blockchain, Rebuilt for Scale"
|
description = "Blockchain, Rebuilt for Scale"
|
||||||
version = "1.0.1"
|
version = "1.0.5"
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
homepage = "https://solana.com/"
|
homepage = "https://solana.com/"
|
||||||
@@ -14,3 +14,4 @@ lazy_static = "1.4.0"
|
|||||||
serde = "1.0.104"
|
serde = "1.0.104"
|
||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
serde_yaml = "0.8.11"
|
serde_yaml = "0.8.11"
|
||||||
|
url = "2.1.1"
|
||||||
|
@@ -5,6 +5,7 @@ use std::{
|
|||||||
io::{self, Write},
|
io::{self, Write},
|
||||||
path::Path,
|
path::Path,
|
||||||
};
|
};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref CONFIG_FILE: Option<String> = {
|
pub static ref CONFIG_FILE: Option<String> = {
|
||||||
@@ -15,20 +16,32 @@ lazy_static! {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Default, Debug, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub url: String,
|
pub json_rpc_url: String,
|
||||||
|
pub websocket_url: String,
|
||||||
pub keypair_path: String,
|
pub keypair_path: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Default for Config {
|
||||||
pub fn new(url: &str, keypair_path: &str) -> Self {
|
fn default() -> Self {
|
||||||
|
let keypair_path = {
|
||||||
|
let mut keypair_path = dirs::home_dir().expect("home directory");
|
||||||
|
keypair_path.extend(&[".config", "solana", "id.json"]);
|
||||||
|
keypair_path.to_str().unwrap().to_string()
|
||||||
|
};
|
||||||
|
let json_rpc_url = "http://127.0.0.1:8899".to_string();
|
||||||
|
let websocket_url = Self::compute_websocket_url(&json_rpc_url);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
url: url.to_string(),
|
json_rpc_url,
|
||||||
keypair_path: keypair_path.to_string(),
|
websocket_url,
|
||||||
|
keypair_path,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
pub fn load(config_file: &str) -> Result<Self, io::Error> {
|
pub fn load(config_file: &str) -> Result<Self, io::Error> {
|
||||||
let file = File::open(config_file.to_string())?;
|
let file = File::open(config_file.to_string())?;
|
||||||
let config = serde_yaml::from_reader(file)
|
let config = serde_yaml::from_reader(file)
|
||||||
@@ -48,4 +61,29 @@ impl Config {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn compute_websocket_url(json_rpc_url: &str) -> String {
|
||||||
|
let json_rpc_url: Option<Url> = json_rpc_url.parse().ok();
|
||||||
|
if json_rpc_url.is_none() {
|
||||||
|
return "".to_string();
|
||||||
|
}
|
||||||
|
let json_rpc_url = json_rpc_url.unwrap();
|
||||||
|
let is_secure = json_rpc_url.scheme().to_ascii_lowercase() == "https";
|
||||||
|
let mut ws_url = json_rpc_url.clone();
|
||||||
|
ws_url
|
||||||
|
.set_scheme(if is_secure { "wss" } else { "ws" })
|
||||||
|
.expect("unable to set scheme");
|
||||||
|
let ws_port = match json_rpc_url.port() {
|
||||||
|
Some(port) => port + 1,
|
||||||
|
None => {
|
||||||
|
if is_secure {
|
||||||
|
8901
|
||||||
|
} else {
|
||||||
|
8900
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ws_url.set_port(Some(ws_port)).expect("unable to set port");
|
||||||
|
ws_url.to_string()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
|
|
||||||
pub mod config;
|
mod config;
|
||||||
|
pub use config::{Config, CONFIG_FILE};
|
||||||
|
@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.com>"]
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "solana-cli"
|
name = "solana-cli"
|
||||||
description = "Blockchain, Rebuilt for Scale"
|
description = "Blockchain, Rebuilt for Scale"
|
||||||
version = "1.0.1"
|
version = "1.0.5"
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
homepage = "https://solana.com/"
|
homepage = "https://solana.com/"
|
||||||
@@ -26,27 +26,27 @@ reqwest = { version = "0.10.1", default-features = false, features = ["blocking"
|
|||||||
serde = "1.0.104"
|
serde = "1.0.104"
|
||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
serde_json = "1.0.46"
|
serde_json = "1.0.46"
|
||||||
solana-budget-program = { path = "../programs/budget", version = "1.0.1" }
|
solana-budget-program = { path = "../programs/budget", version = "1.0.5" }
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "1.0.1" }
|
solana-clap-utils = { path = "../clap-utils", version = "1.0.5" }
|
||||||
solana-cli-config = { path = "../cli-config", version = "1.0.1" }
|
solana-cli-config = { path = "../cli-config", version = "1.0.5" }
|
||||||
solana-client = { path = "../client", version = "1.0.1" }
|
solana-client = { path = "../client", version = "1.0.5" }
|
||||||
solana-config-program = { path = "../programs/config", version = "1.0.1" }
|
solana-config-program = { path = "../programs/config", version = "1.0.5" }
|
||||||
solana-faucet = { path = "../faucet", version = "1.0.1" }
|
solana-faucet = { path = "../faucet", version = "1.0.5" }
|
||||||
solana-logger = { path = "../logger", version = "1.0.1" }
|
solana-logger = { path = "../logger", version = "1.0.5" }
|
||||||
solana-net-utils = { path = "../net-utils", version = "1.0.1" }
|
solana-net-utils = { path = "../net-utils", version = "1.0.5" }
|
||||||
solana-remote-wallet = { path = "../remote-wallet", version = "1.0.1" }
|
solana-remote-wallet = { path = "../remote-wallet", version = "1.0.5" }
|
||||||
solana-runtime = { path = "../runtime", version = "1.0.1" }
|
solana-runtime = { path = "../runtime", version = "1.0.5" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.0.1" }
|
solana-sdk = { path = "../sdk", version = "1.0.5" }
|
||||||
solana-stake-program = { path = "../programs/stake", version = "1.0.1" }
|
solana-stake-program = { path = "../programs/stake", version = "1.0.5" }
|
||||||
solana-storage-program = { path = "../programs/storage", version = "1.0.1" }
|
solana-storage-program = { path = "../programs/storage", version = "1.0.5" }
|
||||||
solana-vote-program = { path = "../programs/vote", version = "1.0.1" }
|
solana-vote-program = { path = "../programs/vote", version = "1.0.5" }
|
||||||
solana-vote-signer = { path = "../vote-signer", version = "1.0.1" }
|
solana-vote-signer = { path = "../vote-signer", version = "1.0.5" }
|
||||||
titlecase = "1.1.0"
|
titlecase = "1.1.0"
|
||||||
url = "2.1.1"
|
url = "2.1.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
solana-core = { path = "../core", version = "1.0.1" }
|
solana-core = { path = "../core", version = "1.0.5" }
|
||||||
solana-budget-program = { path = "../programs/budget", version = "1.0.1" }
|
solana-budget-program = { path = "../programs/budget", version = "1.0.5" }
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
204
cli/src/cli.rs
204
cli/src/cli.rs
@@ -23,7 +23,7 @@ use solana_client::{client_error::ClientError, rpc_client::RpcClient};
|
|||||||
use solana_faucet::faucet::request_airdrop_transaction;
|
use solana_faucet::faucet::request_airdrop_transaction;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use solana_faucet::faucet_mock::request_airdrop_transaction;
|
use solana_faucet::faucet_mock::request_airdrop_transaction;
|
||||||
use solana_remote_wallet::remote_wallet::{DerivationPath, RemoteWalletManager};
|
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
bpf_loader,
|
bpf_loader,
|
||||||
clock::{Epoch, Slot},
|
clock::{Epoch, Slot},
|
||||||
@@ -40,7 +40,10 @@ use solana_sdk::{
|
|||||||
system_instruction::{self, create_address_with_seed, SystemError, MAX_ADDRESS_SEED_LEN},
|
system_instruction::{self, create_address_with_seed, SystemError, MAX_ADDRESS_SEED_LEN},
|
||||||
transaction::{Transaction, TransactionError},
|
transaction::{Transaction, TransactionError},
|
||||||
};
|
};
|
||||||
use solana_stake_program::stake_state::{Lockup, StakeAuthorize};
|
use solana_stake_program::{
|
||||||
|
stake_instruction::LockupArgs,
|
||||||
|
stake_state::{Lockup, StakeAuthorize},
|
||||||
|
};
|
||||||
use solana_storage_program::storage_instruction::StorageAccountType;
|
use solana_storage_program::storage_instruction::StorageAccountType;
|
||||||
use solana_vote_program::vote_state::VoteAuthorize;
|
use solana_vote_program::vote_state::VoteAuthorize;
|
||||||
use std::{
|
use std::{
|
||||||
@@ -52,6 +55,7 @@ use std::{
|
|||||||
time::Duration,
|
time::Duration,
|
||||||
{error, fmt},
|
{error, fmt},
|
||||||
};
|
};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
pub type CliSigners = Vec<Box<dyn Signer>>;
|
pub type CliSigners = Vec<Box<dyn Signer>>;
|
||||||
pub type SignerIndex = usize;
|
pub type SignerIndex = usize;
|
||||||
@@ -163,6 +167,7 @@ pub enum CliCommand {
|
|||||||
// Cluster Query Commands
|
// Cluster Query Commands
|
||||||
Catchup {
|
Catchup {
|
||||||
node_pubkey: Pubkey,
|
node_pubkey: Pubkey,
|
||||||
|
node_json_rpc_url: Option<String>,
|
||||||
},
|
},
|
||||||
ClusterVersion,
|
ClusterVersion,
|
||||||
CreateAddressWithSeed {
|
CreateAddressWithSeed {
|
||||||
@@ -185,9 +190,7 @@ pub enum CliCommand {
|
|||||||
commitment_config: CommitmentConfig,
|
commitment_config: CommitmentConfig,
|
||||||
},
|
},
|
||||||
LeaderSchedule,
|
LeaderSchedule,
|
||||||
LiveSlots {
|
LiveSlots,
|
||||||
url: String,
|
|
||||||
},
|
|
||||||
Ping {
|
Ping {
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
interval: Duration,
|
interval: Duration,
|
||||||
@@ -206,6 +209,7 @@ pub enum CliCommand {
|
|||||||
},
|
},
|
||||||
ShowValidators {
|
ShowValidators {
|
||||||
use_lamports_unit: bool,
|
use_lamports_unit: bool,
|
||||||
|
commitment_config: CommitmentConfig,
|
||||||
},
|
},
|
||||||
// Nonce commands
|
// Nonce commands
|
||||||
AuthorizeNonceAccount {
|
AuthorizeNonceAccount {
|
||||||
@@ -303,7 +307,7 @@ pub enum CliCommand {
|
|||||||
},
|
},
|
||||||
StakeSetLockup {
|
StakeSetLockup {
|
||||||
stake_account_pubkey: Pubkey,
|
stake_account_pubkey: Pubkey,
|
||||||
lockup: Lockup,
|
lockup: LockupArgs,
|
||||||
custodian: SignerIndex,
|
custodian: SignerIndex,
|
||||||
sign_only: bool,
|
sign_only: bool,
|
||||||
blockhash_query: BlockhashQuery,
|
blockhash_query: BlockhashQuery,
|
||||||
@@ -351,6 +355,7 @@ pub enum CliCommand {
|
|||||||
ShowVoteAccount {
|
ShowVoteAccount {
|
||||||
pubkey: Pubkey,
|
pubkey: Pubkey,
|
||||||
use_lamports_unit: bool,
|
use_lamports_unit: bool,
|
||||||
|
commitment_config: CommitmentConfig,
|
||||||
},
|
},
|
||||||
VoteAuthorize {
|
VoteAuthorize {
|
||||||
vote_account_pubkey: Pubkey,
|
vote_account_pubkey: Pubkey,
|
||||||
@@ -435,25 +440,85 @@ impl From<Box<dyn error::Error>> for CliError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum SettingType {
|
||||||
|
Explicit,
|
||||||
|
Computed,
|
||||||
|
SystemDefault,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct CliConfig<'a> {
|
pub struct CliConfig<'a> {
|
||||||
pub command: CliCommand,
|
pub command: CliCommand,
|
||||||
pub json_rpc_url: String,
|
pub json_rpc_url: String,
|
||||||
|
pub websocket_url: String,
|
||||||
pub signers: Vec<&'a dyn Signer>,
|
pub signers: Vec<&'a dyn Signer>,
|
||||||
pub keypair_path: String,
|
pub keypair_path: String,
|
||||||
pub derivation_path: Option<DerivationPath>,
|
|
||||||
pub rpc_client: Option<RpcClient>,
|
pub rpc_client: Option<RpcClient>,
|
||||||
pub verbose: bool,
|
pub verbose: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CliConfig<'_> {
|
impl CliConfig<'_> {
|
||||||
pub fn default_keypair_path() -> String {
|
fn default_keypair_path() -> String {
|
||||||
let mut keypair_path = dirs::home_dir().expect("home directory");
|
solana_cli_config::Config::default().keypair_path
|
||||||
keypair_path.extend(&[".config", "solana", "id.json"]);
|
|
||||||
keypair_path.to_str().unwrap().to_string()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn default_json_rpc_url() -> String {
|
fn default_json_rpc_url() -> String {
|
||||||
"http://127.0.0.1:8899".to_string()
|
solana_cli_config::Config::default().json_rpc_url
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_websocket_url() -> String {
|
||||||
|
solana_cli_config::Config::default().websocket_url
|
||||||
|
}
|
||||||
|
|
||||||
|
fn first_nonempty_setting(
|
||||||
|
settings: std::vec::Vec<(SettingType, String)>,
|
||||||
|
) -> (SettingType, String) {
|
||||||
|
settings
|
||||||
|
.into_iter()
|
||||||
|
.find(|(_, value)| value != "")
|
||||||
|
.expect("no nonempty setting")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compute_websocket_url_setting(
|
||||||
|
websocket_cmd_url: &str,
|
||||||
|
websocket_cfg_url: &str,
|
||||||
|
json_rpc_cmd_url: &str,
|
||||||
|
json_rpc_cfg_url: &str,
|
||||||
|
) -> (SettingType, String) {
|
||||||
|
Self::first_nonempty_setting(vec![
|
||||||
|
(SettingType::Explicit, websocket_cmd_url.to_string()),
|
||||||
|
(SettingType::Explicit, websocket_cfg_url.to_string()),
|
||||||
|
(
|
||||||
|
SettingType::Computed,
|
||||||
|
solana_cli_config::Config::compute_websocket_url(json_rpc_cmd_url),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
SettingType::Computed,
|
||||||
|
solana_cli_config::Config::compute_websocket_url(json_rpc_cfg_url),
|
||||||
|
),
|
||||||
|
(SettingType::SystemDefault, Self::default_websocket_url()),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compute_json_rpc_url_setting(
|
||||||
|
json_rpc_cmd_url: &str,
|
||||||
|
json_rpc_cfg_url: &str,
|
||||||
|
) -> (SettingType, String) {
|
||||||
|
Self::first_nonempty_setting(vec![
|
||||||
|
(SettingType::Explicit, json_rpc_cmd_url.to_string()),
|
||||||
|
(SettingType::Explicit, json_rpc_cfg_url.to_string()),
|
||||||
|
(SettingType::SystemDefault, Self::default_json_rpc_url()),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compute_keypair_path_setting(
|
||||||
|
keypair_cmd_path: &str,
|
||||||
|
keypair_cfg_path: &str,
|
||||||
|
) -> (SettingType, String) {
|
||||||
|
Self::first_nonempty_setting(vec![
|
||||||
|
(SettingType::Explicit, keypair_cmd_path.to_string()),
|
||||||
|
(SettingType::Explicit, keypair_cfg_path.to_string()),
|
||||||
|
(SettingType::SystemDefault, Self::default_keypair_path()),
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn pubkey(&self) -> Result<Pubkey, SignerError> {
|
pub(crate) fn pubkey(&self) -> Result<Pubkey, SignerError> {
|
||||||
@@ -475,9 +540,9 @@ impl Default for CliConfig<'_> {
|
|||||||
use_lamports_unit: false,
|
use_lamports_unit: false,
|
||||||
},
|
},
|
||||||
json_rpc_url: Self::default_json_rpc_url(),
|
json_rpc_url: Self::default_json_rpc_url(),
|
||||||
|
websocket_url: Self::default_websocket_url(),
|
||||||
signers: Vec::new(),
|
signers: Vec::new(),
|
||||||
keypair_path: Self::default_keypair_path(),
|
keypair_path: Self::default_keypair_path(),
|
||||||
derivation_path: None,
|
|
||||||
rpc_client: None,
|
rpc_client: None,
|
||||||
verbose: false,
|
verbose: false,
|
||||||
}
|
}
|
||||||
@@ -496,7 +561,9 @@ pub fn parse_command(
|
|||||||
command: CliCommand::ClusterVersion,
|
command: CliCommand::ClusterVersion,
|
||||||
signers: vec![],
|
signers: vec![],
|
||||||
}),
|
}),
|
||||||
("create-address-with-seed", Some(matches)) => parse_create_address_with_seed(matches),
|
("create-address-with-seed", Some(matches)) => {
|
||||||
|
parse_create_address_with_seed(matches, default_signer_path, wallet_manager)
|
||||||
|
}
|
||||||
("fees", Some(_matches)) => Ok(CliCommandInfo {
|
("fees", Some(_matches)) => Ok(CliCommandInfo {
|
||||||
command: CliCommand::Fees,
|
command: CliCommand::Fees,
|
||||||
signers: vec![],
|
signers: vec![],
|
||||||
@@ -514,7 +581,10 @@ pub fn parse_command(
|
|||||||
signers: vec![],
|
signers: vec![],
|
||||||
}),
|
}),
|
||||||
("ping", Some(matches)) => parse_cluster_ping(matches, default_signer_path, wallet_manager),
|
("ping", Some(matches)) => parse_cluster_ping(matches, default_signer_path, wallet_manager),
|
||||||
("live-slots", Some(matches)) => parse_live_slots(matches),
|
("live-slots", Some(_matches)) => Ok(CliCommandInfo {
|
||||||
|
command: CliCommand::LiveSlots,
|
||||||
|
signers: vec![],
|
||||||
|
}),
|
||||||
("block-production", Some(matches)) => parse_show_block_production(matches),
|
("block-production", Some(matches)) => parse_show_block_production(matches),
|
||||||
("gossip", Some(_matches)) => Ok(CliCommandInfo {
|
("gossip", Some(_matches)) => Ok(CliCommandInfo {
|
||||||
command: CliCommand::ShowGossip,
|
command: CliCommand::ShowGossip,
|
||||||
@@ -939,8 +1009,20 @@ pub fn return_signers(tx: &Transaction) -> ProcessResult {
|
|||||||
|
|
||||||
pub fn parse_create_address_with_seed(
|
pub fn parse_create_address_with_seed(
|
||||||
matches: &ArgMatches<'_>,
|
matches: &ArgMatches<'_>,
|
||||||
|
default_signer_path: &str,
|
||||||
|
wallet_manager: Option<&Arc<RemoteWalletManager>>,
|
||||||
) -> Result<CliCommandInfo, CliError> {
|
) -> Result<CliCommandInfo, CliError> {
|
||||||
let from_pubkey = pubkey_of(matches, "from");
|
let from_pubkey = pubkey_of_signer(matches, "from", wallet_manager)?;
|
||||||
|
let signers = if from_pubkey.is_some() {
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
|
vec![signer_from_path(
|
||||||
|
matches,
|
||||||
|
default_signer_path,
|
||||||
|
"keypair",
|
||||||
|
wallet_manager,
|
||||||
|
)?]
|
||||||
|
};
|
||||||
|
|
||||||
let program_id = match matches.value_of("program_id").unwrap() {
|
let program_id = match matches.value_of("program_id").unwrap() {
|
||||||
"STAKE" => solana_stake_program::id(),
|
"STAKE" => solana_stake_program::id(),
|
||||||
@@ -963,7 +1045,7 @@ pub fn parse_create_address_with_seed(
|
|||||||
seed,
|
seed,
|
||||||
program_id,
|
program_id,
|
||||||
},
|
},
|
||||||
signers: vec![],
|
signers,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -973,9 +1055,12 @@ fn process_create_address_with_seed(
|
|||||||
seed: &str,
|
seed: &str,
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let config_pubkey = config.pubkey()?;
|
let from_pubkey = if let Some(pubkey) = from_pubkey {
|
||||||
let from_pubkey = from_pubkey.unwrap_or(&config_pubkey);
|
*pubkey
|
||||||
let address = create_address_with_seed(from_pubkey, seed, program_id)?;
|
} else {
|
||||||
|
config.pubkey()?
|
||||||
|
};
|
||||||
|
let address = create_address_with_seed(&from_pubkey, seed, program_id)?;
|
||||||
Ok(address.to_string())
|
Ok(address.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1454,7 +1539,10 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
|||||||
CliCommand::Address => Ok(format!("{}", config.pubkey()?)),
|
CliCommand::Address => Ok(format!("{}", config.pubkey()?)),
|
||||||
|
|
||||||
// Return software version of solana-cli and cluster entrypoint node
|
// Return software version of solana-cli and cluster entrypoint node
|
||||||
CliCommand::Catchup { node_pubkey } => process_catchup(&rpc_client, node_pubkey),
|
CliCommand::Catchup {
|
||||||
|
node_pubkey,
|
||||||
|
node_json_rpc_url,
|
||||||
|
} => process_catchup(&rpc_client, node_pubkey, node_json_rpc_url),
|
||||||
CliCommand::ClusterVersion => process_cluster_version(&rpc_client),
|
CliCommand::ClusterVersion => process_cluster_version(&rpc_client),
|
||||||
CliCommand::CreateAddressWithSeed {
|
CliCommand::CreateAddressWithSeed {
|
||||||
from_pubkey,
|
from_pubkey,
|
||||||
@@ -1465,16 +1553,16 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
|||||||
CliCommand::GetBlockTime { slot } => process_get_block_time(&rpc_client, *slot),
|
CliCommand::GetBlockTime { slot } => process_get_block_time(&rpc_client, *slot),
|
||||||
CliCommand::GetGenesisHash => process_get_genesis_hash(&rpc_client),
|
CliCommand::GetGenesisHash => process_get_genesis_hash(&rpc_client),
|
||||||
CliCommand::GetEpochInfo { commitment_config } => {
|
CliCommand::GetEpochInfo { commitment_config } => {
|
||||||
process_get_epoch_info(&rpc_client, commitment_config)
|
process_get_epoch_info(&rpc_client, *commitment_config)
|
||||||
}
|
}
|
||||||
CliCommand::GetSlot { commitment_config } => {
|
CliCommand::GetSlot { commitment_config } => {
|
||||||
process_get_slot(&rpc_client, commitment_config)
|
process_get_slot(&rpc_client, *commitment_config)
|
||||||
}
|
}
|
||||||
CliCommand::GetTransactionCount { commitment_config } => {
|
CliCommand::GetTransactionCount { commitment_config } => {
|
||||||
process_get_transaction_count(&rpc_client, commitment_config)
|
process_get_transaction_count(&rpc_client, *commitment_config)
|
||||||
}
|
}
|
||||||
CliCommand::LeaderSchedule => process_leader_schedule(&rpc_client),
|
CliCommand::LeaderSchedule => process_leader_schedule(&rpc_client),
|
||||||
CliCommand::LiveSlots { url } => process_live_slots(&url),
|
CliCommand::LiveSlots => process_live_slots(&config.websocket_url),
|
||||||
CliCommand::Ping {
|
CliCommand::Ping {
|
||||||
lamports,
|
lamports,
|
||||||
interval,
|
interval,
|
||||||
@@ -1488,7 +1576,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
|||||||
interval,
|
interval,
|
||||||
count,
|
count,
|
||||||
timeout,
|
timeout,
|
||||||
commitment_config,
|
*commitment_config,
|
||||||
),
|
),
|
||||||
CliCommand::ShowBlockProduction { epoch, slot_limit } => {
|
CliCommand::ShowBlockProduction { epoch, slot_limit } => {
|
||||||
process_show_block_production(&rpc_client, config, *epoch, *slot_limit)
|
process_show_block_production(&rpc_client, config, *epoch, *slot_limit)
|
||||||
@@ -1502,9 +1590,10 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
|||||||
*use_lamports_unit,
|
*use_lamports_unit,
|
||||||
vote_account_pubkeys.as_deref(),
|
vote_account_pubkeys.as_deref(),
|
||||||
),
|
),
|
||||||
CliCommand::ShowValidators { use_lamports_unit } => {
|
CliCommand::ShowValidators {
|
||||||
process_show_validators(&rpc_client, *use_lamports_unit)
|
use_lamports_unit,
|
||||||
}
|
commitment_config,
|
||||||
|
} => process_show_validators(&rpc_client, *use_lamports_unit, *commitment_config),
|
||||||
|
|
||||||
// Nonce Commands
|
// Nonce Commands
|
||||||
|
|
||||||
@@ -1816,11 +1905,13 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
|||||||
CliCommand::ShowVoteAccount {
|
CliCommand::ShowVoteAccount {
|
||||||
pubkey: vote_account_pubkey,
|
pubkey: vote_account_pubkey,
|
||||||
use_lamports_unit,
|
use_lamports_unit,
|
||||||
|
commitment_config,
|
||||||
} => process_show_vote_account(
|
} => process_show_vote_account(
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
config,
|
config,
|
||||||
&vote_account_pubkey,
|
&vote_account_pubkey,
|
||||||
*use_lamports_unit,
|
*use_lamports_unit,
|
||||||
|
*commitment_config,
|
||||||
),
|
),
|
||||||
CliCommand::VoteAuthorize {
|
CliCommand::VoteAuthorize {
|
||||||
vote_account_pubkey,
|
vote_account_pubkey,
|
||||||
@@ -1854,7 +1945,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
|||||||
} => {
|
} => {
|
||||||
let faucet_addr = SocketAddr::new(
|
let faucet_addr = SocketAddr::new(
|
||||||
faucet_host.unwrap_or_else(|| {
|
faucet_host.unwrap_or_else(|| {
|
||||||
let faucet_host = url::Url::parse(&config.json_rpc_url)
|
let faucet_host = Url::parse(&config.json_rpc_url)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.host()
|
.host()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -2185,7 +2276,7 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
|
|||||||
.value_name("PUBKEY")
|
.value_name("PUBKEY")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.required(false)
|
.required(false)
|
||||||
.validator(is_pubkey_or_keypair)
|
.validator(is_valid_signer)
|
||||||
.help("From (base) key, defaults to client keypair."),
|
.help("From (base) key, defaults to client keypair."),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -2383,7 +2474,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::Account,
|
account::Account,
|
||||||
nonce_state::{Meta as NonceMeta, NonceState},
|
nonce,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{keypair_from_seed, read_keypair_file, write_keypair_file, Presigner},
|
signature::{keypair_from_seed, read_keypair_file, write_keypair_file, Presigner},
|
||||||
system_program,
|
system_program,
|
||||||
@@ -2618,14 +2709,14 @@ mod tests {
|
|||||||
"STAKE",
|
"STAKE",
|
||||||
]);
|
]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_create_address_with_seed, "", None).unwrap(),
|
parse_command(&test_create_address_with_seed, &keypair_file, None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::CreateAddressWithSeed {
|
command: CliCommand::CreateAddressWithSeed {
|
||||||
from_pubkey: None,
|
from_pubkey: None,
|
||||||
seed: "seed".to_string(),
|
seed: "seed".to_string(),
|
||||||
program_id: solana_stake_program::id(),
|
program_id: solana_stake_program::id(),
|
||||||
},
|
},
|
||||||
signers: vec![],
|
signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -3162,18 +3253,16 @@ mod tests {
|
|||||||
|
|
||||||
// Nonced pay
|
// Nonced pay
|
||||||
let blockhash = Hash::default();
|
let blockhash = Hash::default();
|
||||||
|
let data =
|
||||||
|
nonce::state::Versions::new_current(nonce::State::Initialized(nonce::state::Data {
|
||||||
|
authority: config.signers[0].pubkey(),
|
||||||
|
blockhash,
|
||||||
|
fee_calculator: FeeCalculator::default(),
|
||||||
|
}));
|
||||||
let nonce_response = json!(Response {
|
let nonce_response = json!(Response {
|
||||||
context: RpcResponseContext { slot: 1 },
|
context: RpcResponseContext { slot: 1 },
|
||||||
value: json!(RpcAccount::encode(
|
value: json!(RpcAccount::encode(
|
||||||
Account::new_data(
|
Account::new_data(1, &data, &system_program::ID,).unwrap()
|
||||||
1,
|
|
||||||
&NonceState::Initialized(
|
|
||||||
NonceMeta::new(&config.signers[0].pubkey()),
|
|
||||||
blockhash
|
|
||||||
),
|
|
||||||
&system_program::ID,
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
)),
|
)),
|
||||||
});
|
});
|
||||||
let mut mocks = HashMap::new();
|
let mut mocks = HashMap::new();
|
||||||
@@ -3193,15 +3282,16 @@ mod tests {
|
|||||||
let bob_keypair = Keypair::new();
|
let bob_keypair = Keypair::new();
|
||||||
let bob_pubkey = bob_keypair.pubkey();
|
let bob_pubkey = bob_keypair.pubkey();
|
||||||
let blockhash = Hash::default();
|
let blockhash = Hash::default();
|
||||||
|
let data =
|
||||||
|
nonce::state::Versions::new_current(nonce::State::Initialized(nonce::state::Data {
|
||||||
|
authority: bob_pubkey,
|
||||||
|
blockhash,
|
||||||
|
fee_calculator: FeeCalculator::default(),
|
||||||
|
}));
|
||||||
let nonce_authority_response = json!(Response {
|
let nonce_authority_response = json!(Response {
|
||||||
context: RpcResponseContext { slot: 1 },
|
context: RpcResponseContext { slot: 1 },
|
||||||
value: json!(RpcAccount::encode(
|
value: json!(RpcAccount::encode(
|
||||||
Account::new_data(
|
Account::new_data(1, &data, &system_program::ID,).unwrap()
|
||||||
1,
|
|
||||||
&NonceState::Initialized(NonceMeta::new(&bob_pubkey), blockhash),
|
|
||||||
&system_program::ID,
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
)),
|
)),
|
||||||
});
|
});
|
||||||
let mut mocks = HashMap::new();
|
let mut mocks = HashMap::new();
|
||||||
@@ -3230,8 +3320,22 @@ mod tests {
|
|||||||
let signature = process_command(&config);
|
let signature = process_command(&config);
|
||||||
assert_eq!(signature.unwrap(), SIGNATURE.to_string());
|
assert_eq!(signature.unwrap(), SIGNATURE.to_string());
|
||||||
|
|
||||||
|
// CreateAddressWithSeed
|
||||||
|
let from_pubkey = Pubkey::new_rand();
|
||||||
|
config.signers = vec![];
|
||||||
|
config.command = CliCommand::CreateAddressWithSeed {
|
||||||
|
from_pubkey: Some(from_pubkey),
|
||||||
|
seed: "seed".to_string(),
|
||||||
|
program_id: solana_stake_program::id(),
|
||||||
|
};
|
||||||
|
let address = process_command(&config);
|
||||||
|
let expected_address =
|
||||||
|
create_address_with_seed(&from_pubkey, "seed", &solana_stake_program::id()).unwrap();
|
||||||
|
assert_eq!(address.unwrap(), expected_address.to_string());
|
||||||
|
|
||||||
// Need airdrop cases
|
// Need airdrop cases
|
||||||
let to = Pubkey::new_rand();
|
let to = Pubkey::new_rand();
|
||||||
|
config.signers = vec![&keypair];
|
||||||
config.command = CliCommand::Airdrop {
|
config.command = CliCommand::Airdrop {
|
||||||
faucet_host: None,
|
faucet_host: None,
|
||||||
faucet_port: 1234,
|
faucet_port: 1234,
|
||||||
|
@@ -59,6 +59,14 @@ impl ClusterQuerySubCommands for App<'_, '_> {
|
|||||||
.validator(is_pubkey_or_keypair)
|
.validator(is_pubkey_or_keypair)
|
||||||
.required(true)
|
.required(true)
|
||||||
.help("Identity pubkey of the validator"),
|
.help("Identity pubkey of the validator"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("node_json_rpc_url")
|
||||||
|
.index(2)
|
||||||
|
.value_name("URL")
|
||||||
|
.takes_value(true)
|
||||||
|
.validator(is_url)
|
||||||
|
.help("JSON RPC URL for validator, which is useful for validators with a private RPC service")
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
@@ -170,16 +178,7 @@ impl ClusterQuerySubCommands for App<'_, '_> {
|
|||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("live-slots")
|
SubCommand::with_name("live-slots")
|
||||||
.about("Show information about the current slot progression")
|
.about("Show information about the current slot progression"),
|
||||||
.arg(
|
|
||||||
Arg::with_name("websocket_url")
|
|
||||||
.short("w")
|
|
||||||
.long("ws")
|
|
||||||
.value_name("URL")
|
|
||||||
.takes_value(true)
|
|
||||||
.default_value("ws://127.0.0.1:8900")
|
|
||||||
.help("WebSocket URL for PubSub RPC connection"),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("block-production")
|
SubCommand::with_name("block-production")
|
||||||
@@ -226,6 +225,14 @@ impl ClusterQuerySubCommands for App<'_, '_> {
|
|||||||
SubCommand::with_name("validators")
|
SubCommand::with_name("validators")
|
||||||
.about("Show summary information about the current validators")
|
.about("Show summary information about the current validators")
|
||||||
.alias("show-validators")
|
.alias("show-validators")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("confirmed")
|
||||||
|
.long("confirmed")
|
||||||
|
.takes_value(false)
|
||||||
|
.help(
|
||||||
|
"Return information at maximum-lockout commitment level",
|
||||||
|
),
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("lamports")
|
Arg::with_name("lamports")
|
||||||
.long("lamports")
|
.long("lamports")
|
||||||
@@ -238,8 +245,12 @@ impl ClusterQuerySubCommands for App<'_, '_> {
|
|||||||
|
|
||||||
pub fn parse_catchup(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
pub fn parse_catchup(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||||
let node_pubkey = pubkey_of(matches, "node_pubkey").unwrap();
|
let node_pubkey = pubkey_of(matches, "node_pubkey").unwrap();
|
||||||
|
let node_json_rpc_url = value_t!(matches, "node_json_rpc_url", String).ok();
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::Catchup { node_pubkey },
|
command: CliCommand::Catchup {
|
||||||
|
node_pubkey,
|
||||||
|
node_json_rpc_url,
|
||||||
|
},
|
||||||
signers: vec![],
|
signers: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -279,14 +290,6 @@ pub fn parse_cluster_ping(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_live_slots(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
|
||||||
let url: String = value_t_or_exit!(matches, "websocket_url", String);
|
|
||||||
Ok(CliCommandInfo {
|
|
||||||
command: CliCommand::LiveSlots { url },
|
|
||||||
signers: vec![],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_get_block_time(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
pub fn parse_get_block_time(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||||
let slot = value_t_or_exit!(matches, "slot", u64);
|
let slot = value_t_or_exit!(matches, "slot", u64);
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
@@ -346,9 +349,17 @@ pub fn parse_show_stakes(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, Cli
|
|||||||
|
|
||||||
pub fn parse_show_validators(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
pub fn parse_show_validators(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||||
let use_lamports_unit = matches.is_present("lamports");
|
let use_lamports_unit = matches.is_present("lamports");
|
||||||
|
let commitment_config = if matches.is_present("confirmed") {
|
||||||
|
CommitmentConfig::default()
|
||||||
|
} else {
|
||||||
|
CommitmentConfig::recent()
|
||||||
|
};
|
||||||
|
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::ShowValidators { use_lamports_unit },
|
command: CliCommand::ShowValidators {
|
||||||
|
use_lamports_unit,
|
||||||
|
commitment_config,
|
||||||
|
},
|
||||||
signers: vec![],
|
signers: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -362,20 +373,42 @@ fn new_spinner_progress_bar() -> ProgressBar {
|
|||||||
progress_bar
|
progress_bar
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_catchup(rpc_client: &RpcClient, node_pubkey: &Pubkey) -> ProcessResult {
|
pub fn process_catchup(
|
||||||
|
rpc_client: &RpcClient,
|
||||||
|
node_pubkey: &Pubkey,
|
||||||
|
node_json_rpc_url: &Option<String>,
|
||||||
|
) -> ProcessResult {
|
||||||
let cluster_nodes = rpc_client.get_cluster_nodes()?;
|
let cluster_nodes = rpc_client.get_cluster_nodes()?;
|
||||||
|
|
||||||
let rpc_addr = cluster_nodes
|
let node_client = if let Some(node_json_rpc_url) = node_json_rpc_url {
|
||||||
|
RpcClient::new(node_json_rpc_url.to_string())
|
||||||
|
} else {
|
||||||
|
RpcClient::new_socket(
|
||||||
|
cluster_nodes
|
||||||
.iter()
|
.iter()
|
||||||
.find(|contact_info| contact_info.pubkey == node_pubkey.to_string())
|
.find(|contact_info| contact_info.pubkey == node_pubkey.to_string())
|
||||||
.ok_or_else(|| format!("Contact information not found for {}", node_pubkey))?
|
.ok_or_else(|| format!("Contact information not found for {}", node_pubkey))?
|
||||||
.rpc
|
.rpc
|
||||||
.ok_or_else(|| format!("RPC service not found for {}", node_pubkey))?;
|
.ok_or_else(|| format!("RPC service not found for {}", node_pubkey))?,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let reported_node_pubkey = node_client.get_identity()?;
|
||||||
|
if reported_node_pubkey != *node_pubkey {
|
||||||
|
return Err(format!(
|
||||||
|
"The identity reported by node RPC URL does not match. Expected: {:?}. Reported: {:?}",
|
||||||
|
node_pubkey, reported_node_pubkey
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if rpc_client.get_identity()? == *node_pubkey {
|
||||||
|
return Err("Both RPC URLs reference the same node, unable to monitor for catchup. Try a different --url".into());
|
||||||
|
}
|
||||||
|
|
||||||
let progress_bar = new_spinner_progress_bar();
|
let progress_bar = new_spinner_progress_bar();
|
||||||
progress_bar.set_message("Connecting...");
|
progress_bar.set_message("Connecting...");
|
||||||
|
|
||||||
let node_client = RpcClient::new_socket(rpc_addr);
|
|
||||||
let mut previous_rpc_slot = std::u64::MAX;
|
let mut previous_rpc_slot = std::u64::MAX;
|
||||||
let mut previous_slot_distance = 0;
|
let mut previous_slot_distance = 0;
|
||||||
let sleep_interval = 5;
|
let sleep_interval = 5;
|
||||||
@@ -483,7 +516,7 @@ fn slot_to_human_time(slot: Slot) -> String {
|
|||||||
|
|
||||||
pub fn process_get_epoch_info(
|
pub fn process_get_epoch_info(
|
||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
commitment_config: &CommitmentConfig,
|
commitment_config: CommitmentConfig,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let epoch_info = rpc_client.get_epoch_info_with_commitment(commitment_config.clone())?;
|
let epoch_info = rpc_client.get_epoch_info_with_commitment(commitment_config.clone())?;
|
||||||
println!();
|
println!();
|
||||||
@@ -529,7 +562,7 @@ pub fn process_get_genesis_hash(rpc_client: &RpcClient) -> ProcessResult {
|
|||||||
|
|
||||||
pub fn process_get_slot(
|
pub fn process_get_slot(
|
||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
commitment_config: &CommitmentConfig,
|
commitment_config: CommitmentConfig,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let slot = rpc_client.get_slot_with_commitment(commitment_config.clone())?;
|
let slot = rpc_client.get_slot_with_commitment(commitment_config.clone())?;
|
||||||
Ok(slot.to_string())
|
Ok(slot.to_string())
|
||||||
@@ -718,7 +751,7 @@ pub fn process_show_block_production(
|
|||||||
|
|
||||||
pub fn process_get_transaction_count(
|
pub fn process_get_transaction_count(
|
||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
commitment_config: &CommitmentConfig,
|
commitment_config: CommitmentConfig,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let transaction_count =
|
let transaction_count =
|
||||||
rpc_client.get_transaction_count_with_commitment(commitment_config.clone())?;
|
rpc_client.get_transaction_count_with_commitment(commitment_config.clone())?;
|
||||||
@@ -732,7 +765,7 @@ pub fn process_ping(
|
|||||||
interval: &Duration,
|
interval: &Duration,
|
||||||
count: &Option<u64>,
|
count: &Option<u64>,
|
||||||
timeout: &Duration,
|
timeout: &Duration,
|
||||||
commitment_config: &CommitmentConfig,
|
commitment_config: CommitmentConfig,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let to = Keypair::new().pubkey();
|
let to = Keypair::new().pubkey();
|
||||||
|
|
||||||
@@ -873,6 +906,9 @@ pub fn process_live_slots(url: &str) -> ProcessResult {
|
|||||||
let (mut client, receiver) = PubsubClient::slot_subscribe(url)?;
|
let (mut client, receiver) = PubsubClient::slot_subscribe(url)?;
|
||||||
slot_progress.set_message("Connected.");
|
slot_progress.set_message("Connected.");
|
||||||
|
|
||||||
|
let spacer = "|";
|
||||||
|
slot_progress.println(spacer);
|
||||||
|
|
||||||
let mut last_root = std::u64::MAX;
|
let mut last_root = std::u64::MAX;
|
||||||
let mut last_root_update = Instant::now();
|
let mut last_root_update = Instant::now();
|
||||||
let mut slots_per_second = std::f64::NAN;
|
let mut slots_per_second = std::f64::NAN;
|
||||||
@@ -918,11 +954,19 @@ pub fn process_live_slots(url: &str) -> ProcessResult {
|
|||||||
//
|
//
|
||||||
if slot_delta != root_delta {
|
if slot_delta != root_delta {
|
||||||
let prev_root = format!(
|
let prev_root = format!(
|
||||||
"|<- {} <- … <- {} <- {}",
|
"|<--- {} <- … <- {} <- {} (prev)",
|
||||||
previous.root, previous.parent, previous.slot
|
previous.root, previous.parent, previous.slot
|
||||||
)
|
);
|
||||||
.to_owned();
|
|
||||||
slot_progress.println(&prev_root);
|
slot_progress.println(&prev_root);
|
||||||
|
|
||||||
|
let new_root = format!(
|
||||||
|
"| '- {} <- … <- {} <- {} (next)",
|
||||||
|
new_info.root, new_info.parent, new_info.slot
|
||||||
|
);
|
||||||
|
|
||||||
|
slot_progress.println(prev_root);
|
||||||
|
slot_progress.println(new_root);
|
||||||
|
slot_progress.println(spacer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
current = Some(new_info);
|
current = Some(new_info);
|
||||||
@@ -1012,9 +1056,13 @@ pub fn process_show_stakes(
|
|||||||
Ok("".to_string())
|
Ok("".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_show_validators(rpc_client: &RpcClient, use_lamports_unit: bool) -> ProcessResult {
|
pub fn process_show_validators(
|
||||||
let epoch_info = rpc_client.get_epoch_info()?;
|
rpc_client: &RpcClient,
|
||||||
let vote_accounts = rpc_client.get_vote_accounts()?;
|
use_lamports_unit: bool,
|
||||||
|
commitment_config: CommitmentConfig,
|
||||||
|
) -> ProcessResult {
|
||||||
|
let epoch_info = rpc_client.get_epoch_info_with_commitment(commitment_config)?;
|
||||||
|
let vote_accounts = rpc_client.get_vote_accounts_with_commitment(commitment_config)?;
|
||||||
let total_active_stake = vote_accounts
|
let total_active_stake = vote_accounts
|
||||||
.current
|
.current
|
||||||
.iter()
|
.iter()
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::cli::SettingType;
|
||||||
use console::style;
|
use console::style;
|
||||||
use solana_sdk::transaction::Transaction;
|
use solana_sdk::transaction::Transaction;
|
||||||
|
|
||||||
@@ -11,17 +12,19 @@ pub fn println_name_value(name: &str, value: &str) {
|
|||||||
println!("{} {}", style(name).bold(), styled_value);
|
println!("{} {}", style(name).bold(), styled_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn println_name_value_or(name: &str, value: &str, default_value: &str) {
|
pub fn println_name_value_or(name: &str, value: &str, setting_type: SettingType) {
|
||||||
if value == "" {
|
let description = match setting_type {
|
||||||
|
SettingType::Explicit => "",
|
||||||
|
SettingType::Computed => "(computed)",
|
||||||
|
SettingType::SystemDefault => "(default)",
|
||||||
|
};
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"{} {} {}",
|
"{} {} {}",
|
||||||
style(name).bold(),
|
style(name).bold(),
|
||||||
style(default_value),
|
style(value),
|
||||||
style("(default)").italic()
|
style(description).italic(),
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
println!("{} {}", style(name).bold(), style(value));
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn println_signers(tx: &Transaction) {
|
pub fn println_signers(tx: &Transaction) {
|
||||||
|
122
cli/src/main.rs
122
cli/src/main.rs
@@ -1,16 +1,12 @@
|
|||||||
use clap::{crate_description, crate_name, AppSettings, Arg, ArgGroup, ArgMatches, SubCommand};
|
use clap::{crate_description, crate_name, AppSettings, Arg, ArgGroup, ArgMatches, SubCommand};
|
||||||
use console::style;
|
use console::style;
|
||||||
|
|
||||||
use solana_clap_utils::{
|
use solana_clap_utils::{input_validators::is_url, keypair::SKIP_SEED_PHRASE_VALIDATION_ARG};
|
||||||
input_parsers::derivation_of,
|
|
||||||
input_validators::{is_derivation, is_url},
|
|
||||||
keypair::SKIP_SEED_PHRASE_VALIDATION_ARG,
|
|
||||||
};
|
|
||||||
use solana_cli::{
|
use solana_cli::{
|
||||||
cli::{app, parse_command, process_command, CliCommandInfo, CliConfig, CliSigners},
|
cli::{app, parse_command, process_command, CliCommandInfo, CliConfig, CliSigners},
|
||||||
display::{println_name_value, println_name_value_or},
|
display::{println_name_value, println_name_value_or},
|
||||||
};
|
};
|
||||||
use solana_cli_config::config::{Config, CONFIG_FILE};
|
use solana_cli_config::{Config, CONFIG_FILE};
|
||||||
use solana_remote_wallet::remote_wallet::{maybe_wallet_manager, RemoteWalletManager};
|
use solana_remote_wallet::remote_wallet::{maybe_wallet_manager, RemoteWalletManager};
|
||||||
use std::{error, sync::Arc};
|
use std::{error, sync::Arc};
|
||||||
|
|
||||||
@@ -20,29 +16,31 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
|
|||||||
("get", Some(subcommand_matches)) => {
|
("get", Some(subcommand_matches)) => {
|
||||||
if let Some(config_file) = matches.value_of("config_file") {
|
if let Some(config_file) = matches.value_of("config_file") {
|
||||||
let config = Config::load(config_file).unwrap_or_default();
|
let config = Config::load(config_file).unwrap_or_default();
|
||||||
|
|
||||||
|
let (url_setting_type, json_rpc_url) =
|
||||||
|
CliConfig::compute_json_rpc_url_setting("", &config.json_rpc_url);
|
||||||
|
let (ws_setting_type, websocket_url) = CliConfig::compute_websocket_url_setting(
|
||||||
|
"",
|
||||||
|
&config.websocket_url,
|
||||||
|
"",
|
||||||
|
&config.json_rpc_url,
|
||||||
|
);
|
||||||
|
let (keypair_setting_type, keypair_path) =
|
||||||
|
CliConfig::compute_keypair_path_setting("", &config.keypair_path);
|
||||||
|
|
||||||
if let Some(field) = subcommand_matches.value_of("specific_setting") {
|
if let Some(field) = subcommand_matches.value_of("specific_setting") {
|
||||||
let (field_name, value, default_value) = match field {
|
let (field_name, value, setting_type) = match field {
|
||||||
"url" => ("RPC URL", config.url, CliConfig::default_json_rpc_url()),
|
"json_rpc_url" => ("RPC URL", json_rpc_url, url_setting_type),
|
||||||
"keypair" => (
|
"websocket_url" => ("WS URL", websocket_url, ws_setting_type),
|
||||||
"Key Path",
|
"keypair" => ("Key Path", keypair_path, keypair_setting_type),
|
||||||
config.keypair_path,
|
|
||||||
CliConfig::default_keypair_path(),
|
|
||||||
),
|
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
println_name_value_or(&format!("{}:", field_name), &value, &default_value);
|
println_name_value_or(&format!("{}:", field_name), &value, setting_type);
|
||||||
} else {
|
} else {
|
||||||
println_name_value("Config File:", config_file);
|
println_name_value("Config File:", config_file);
|
||||||
println_name_value_or(
|
println_name_value_or("RPC URL:", &json_rpc_url, url_setting_type);
|
||||||
"RPC URL:",
|
println_name_value_or("WS URL:", &websocket_url, ws_setting_type);
|
||||||
&config.url,
|
println_name_value_or("Keypair Path:", &keypair_path, keypair_setting_type);
|
||||||
&CliConfig::default_json_rpc_url(),
|
|
||||||
);
|
|
||||||
println_name_value_or(
|
|
||||||
"Keypair Path:",
|
|
||||||
&config.keypair_path,
|
|
||||||
&CliConfig::default_keypair_path(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
println!(
|
println!(
|
||||||
@@ -56,15 +54,31 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
|
|||||||
if let Some(config_file) = matches.value_of("config_file") {
|
if let Some(config_file) = matches.value_of("config_file") {
|
||||||
let mut config = Config::load(config_file).unwrap_or_default();
|
let mut config = Config::load(config_file).unwrap_or_default();
|
||||||
if let Some(url) = subcommand_matches.value_of("json_rpc_url") {
|
if let Some(url) = subcommand_matches.value_of("json_rpc_url") {
|
||||||
config.url = url.to_string();
|
config.json_rpc_url = url.to_string();
|
||||||
|
}
|
||||||
|
if let Some(url) = subcommand_matches.value_of("websocket_url") {
|
||||||
|
config.websocket_url = url.to_string();
|
||||||
}
|
}
|
||||||
if let Some(keypair) = subcommand_matches.value_of("keypair") {
|
if let Some(keypair) = subcommand_matches.value_of("keypair") {
|
||||||
config.keypair_path = keypair.to_string();
|
config.keypair_path = keypair.to_string();
|
||||||
}
|
}
|
||||||
config.save(config_file)?;
|
config.save(config_file)?;
|
||||||
|
|
||||||
|
let (url_setting_type, json_rpc_url) =
|
||||||
|
CliConfig::compute_json_rpc_url_setting("", &config.json_rpc_url);
|
||||||
|
let (ws_setting_type, websocket_url) = CliConfig::compute_websocket_url_setting(
|
||||||
|
"",
|
||||||
|
&config.websocket_url,
|
||||||
|
"",
|
||||||
|
&config.json_rpc_url,
|
||||||
|
);
|
||||||
|
let (keypair_setting_type, keypair_path) =
|
||||||
|
CliConfig::compute_keypair_path_setting("", &config.keypair_path);
|
||||||
|
|
||||||
println_name_value("Config File:", config_file);
|
println_name_value("Config File:", config_file);
|
||||||
println_name_value("RPC URL:", &config.url);
|
println_name_value_or("RPC URL:", &json_rpc_url, url_setting_type);
|
||||||
println_name_value("Keypair Path:", &config.keypair_path);
|
println_name_value_or("WS URL:", &websocket_url, ws_setting_type);
|
||||||
|
println_name_value_or("Keypair Path:", &keypair_path, keypair_setting_type);
|
||||||
} else {
|
} else {
|
||||||
println!(
|
println!(
|
||||||
"{} Either provide the `--config` arg or ensure home directory exists to use the default config location",
|
"{} Either provide the `--config` arg or ensure home directory exists to use the default config location",
|
||||||
@@ -89,22 +103,20 @@ pub fn parse_args<'a>(
|
|||||||
} else {
|
} else {
|
||||||
Config::default()
|
Config::default()
|
||||||
};
|
};
|
||||||
let json_rpc_url = if let Some(url) = matches.value_of("json_rpc_url") {
|
let (_, json_rpc_url) = CliConfig::compute_json_rpc_url_setting(
|
||||||
url.to_string()
|
matches.value_of("json_rpc_url").unwrap_or(""),
|
||||||
} else if config.url != "" {
|
&config.json_rpc_url,
|
||||||
config.url
|
);
|
||||||
} else {
|
let (_, websocket_url) = CliConfig::compute_websocket_url_setting(
|
||||||
let default = CliConfig::default();
|
matches.value_of("websocket_url").unwrap_or(""),
|
||||||
default.json_rpc_url
|
&config.websocket_url,
|
||||||
};
|
matches.value_of("json_rpc_url").unwrap_or(""),
|
||||||
|
&config.json_rpc_url,
|
||||||
let default_signer_path = if matches.is_present("keypair") {
|
);
|
||||||
matches.value_of("keypair").unwrap().to_string()
|
let (_, default_signer_path) = CliConfig::compute_keypair_path_setting(
|
||||||
} else if config.keypair_path != "" {
|
matches.value_of("keypair").unwrap_or(""),
|
||||||
config.keypair_path
|
&config.keypair_path,
|
||||||
} else {
|
);
|
||||||
CliConfig::default_keypair_path()
|
|
||||||
};
|
|
||||||
|
|
||||||
let CliCommandInfo { command, signers } =
|
let CliCommandInfo { command, signers } =
|
||||||
parse_command(&matches, &default_signer_path, wallet_manager.as_ref())?;
|
parse_command(&matches, &default_signer_path, wallet_manager.as_ref())?;
|
||||||
@@ -113,9 +125,9 @@ pub fn parse_args<'a>(
|
|||||||
CliConfig {
|
CliConfig {
|
||||||
command,
|
command,
|
||||||
json_rpc_url,
|
json_rpc_url,
|
||||||
|
websocket_url,
|
||||||
signers: vec![],
|
signers: vec![],
|
||||||
keypair_path: default_signer_path,
|
keypair_path: default_signer_path,
|
||||||
derivation_path: derivation_of(matches, "derivation_path"),
|
|
||||||
rpc_client: None,
|
rpc_client: None,
|
||||||
verbose: matches.is_present("verbose"),
|
verbose: matches.is_present("verbose"),
|
||||||
},
|
},
|
||||||
@@ -154,6 +166,15 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||||||
.validator(is_url)
|
.validator(is_url)
|
||||||
.help("JSON RPC URL for the solana cluster"),
|
.help("JSON RPC URL for the solana cluster"),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("websocket_url")
|
||||||
|
.long("ws")
|
||||||
|
.value_name("URL")
|
||||||
|
.takes_value(true)
|
||||||
|
.global(true)
|
||||||
|
.validator(is_url)
|
||||||
|
.help("WebSocket URL for the solana cluster"),
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("keypair")
|
Arg::with_name("keypair")
|
||||||
.short("k")
|
.short("k")
|
||||||
@@ -163,15 +184,6 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.help("/path/to/id.json or usb://remote/wallet/path"),
|
.help("/path/to/id.json or usb://remote/wallet/path"),
|
||||||
)
|
)
|
||||||
.arg(
|
|
||||||
Arg::with_name("derivation_path")
|
|
||||||
.long("derivation-path")
|
|
||||||
.value_name("ACCOUNT or ACCOUNT/CHANGE")
|
|
||||||
.global(true)
|
|
||||||
.takes_value(true)
|
|
||||||
.validator(is_derivation)
|
|
||||||
.help("Derivation path to use: m/44'/501'/ACCOUNT'/CHANGE'; default key is device base pubkey: m/44'/501'/0'")
|
|
||||||
)
|
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("verbose")
|
Arg::with_name("verbose")
|
||||||
.long("verbose")
|
.long("verbose")
|
||||||
@@ -198,7 +210,7 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||||||
.index(1)
|
.index(1)
|
||||||
.value_name("CONFIG_FIELD")
|
.value_name("CONFIG_FIELD")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.possible_values(&["url", "keypair"])
|
.possible_values(&["json_rpc_url", "websocket_url", "keypair"])
|
||||||
.help("Return a specific config setting"),
|
.help("Return a specific config setting"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -207,7 +219,7 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||||||
.about("Set a config setting")
|
.about("Set a config setting")
|
||||||
.group(
|
.group(
|
||||||
ArgGroup::with_name("config_settings")
|
ArgGroup::with_name("config_settings")
|
||||||
.args(&["json_rpc_url", "keypair"])
|
.args(&["json_rpc_url", "websocket_url", "keypair"])
|
||||||
.multiple(true)
|
.multiple(true)
|
||||||
.required(true),
|
.required(true),
|
||||||
),
|
),
|
||||||
|
@@ -14,7 +14,7 @@ use solana_sdk::{
|
|||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
message::Message,
|
message::Message,
|
||||||
nonce_state::{Meta, NonceState},
|
nonce::{self, state::Versions, State},
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
system_instruction::{
|
system_instruction::{
|
||||||
advance_nonce_account, authorize_nonce_account, create_address_with_seed,
|
advance_nonce_account, authorize_nonce_account, create_address_with_seed,
|
||||||
@@ -363,22 +363,20 @@ pub fn check_nonce_account(
|
|||||||
if nonce_account.owner != system_program::ID {
|
if nonce_account.owner != system_program::ID {
|
||||||
return Err(CliError::InvalidNonce(CliNonceError::InvalidAccountOwner).into());
|
return Err(CliError::InvalidNonce(CliNonceError::InvalidAccountOwner).into());
|
||||||
}
|
}
|
||||||
let nonce_state: NonceState = nonce_account
|
let nonce_state = StateMut::<Versions>::state(nonce_account)
|
||||||
.state()
|
.map(|v| v.convert_to_current())
|
||||||
.map_err(|_| Box::new(CliError::InvalidNonce(CliNonceError::InvalidAccountData)))?;
|
.map_err(|_| Box::new(CliError::InvalidNonce(CliNonceError::InvalidAccountData)))?;
|
||||||
match nonce_state {
|
match nonce_state {
|
||||||
NonceState::Initialized(meta, hash) => {
|
State::Initialized(ref data) => {
|
||||||
if &hash != nonce_hash {
|
if &data.blockhash != nonce_hash {
|
||||||
Err(CliError::InvalidNonce(CliNonceError::InvalidHash).into())
|
Err(CliError::InvalidNonce(CliNonceError::InvalidHash).into())
|
||||||
} else if nonce_authority != &meta.nonce_authority {
|
} else if nonce_authority != &data.authority {
|
||||||
Err(CliError::InvalidNonce(CliNonceError::InvalidAuthority).into())
|
Err(CliError::InvalidNonce(CliNonceError::InvalidAuthority).into())
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NonceState::Uninitialized => {
|
State::Uninitialized => Err(CliError::InvalidNonce(CliNonceError::InvalidState).into()),
|
||||||
Err(CliError::InvalidNonce(CliNonceError::InvalidState).into())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,7 +427,7 @@ pub fn process_create_nonce_account(
|
|||||||
|
|
||||||
if let Ok(nonce_account) = rpc_client.get_account(&nonce_account_address) {
|
if let Ok(nonce_account) = rpc_client.get_account(&nonce_account_address) {
|
||||||
let err_msg = if nonce_account.owner == system_program::id()
|
let err_msg = if nonce_account.owner == system_program::id()
|
||||||
&& StateMut::<NonceState>::state(&nonce_account).is_ok()
|
&& StateMut::<Versions>::state(&nonce_account).is_ok()
|
||||||
{
|
{
|
||||||
format!("Nonce account {} already exists", nonce_account_address)
|
format!("Nonce account {} already exists", nonce_account_address)
|
||||||
} else {
|
} else {
|
||||||
@@ -441,7 +439,7 @@ pub fn process_create_nonce_account(
|
|||||||
return Err(CliError::BadParameter(err_msg).into());
|
return Err(CliError::BadParameter(err_msg).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let minimum_balance = rpc_client.get_minimum_balance_for_rent_exemption(NonceState::size())?;
|
let minimum_balance = rpc_client.get_minimum_balance_for_rent_exemption(State::size())?;
|
||||||
if lamports < minimum_balance {
|
if lamports < minimum_balance {
|
||||||
return Err(CliError::BadParameter(format!(
|
return Err(CliError::BadParameter(format!(
|
||||||
"need at least {} lamports for nonce account to be rent exempt, provided lamports: {}",
|
"need at least {} lamports for nonce account to be rent exempt, provided lamports: {}",
|
||||||
@@ -495,9 +493,10 @@ pub fn process_get_nonce(rpc_client: &RpcClient, nonce_account_pubkey: &Pubkey)
|
|||||||
))
|
))
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
match nonce_account.state() {
|
let nonce_state = StateMut::<Versions>::state(&nonce_account).map(|v| v.convert_to_current());
|
||||||
Ok(NonceState::Uninitialized) => Ok("Nonce account is uninitialized".to_string()),
|
match nonce_state {
|
||||||
Ok(NonceState::Initialized(_, hash)) => Ok(format!("{:?}", hash)),
|
Ok(State::Uninitialized) => Ok("Nonce account is uninitialized".to_string()),
|
||||||
|
Ok(State::Initialized(ref data)) => Ok(format!("{:?}", data.blockhash)),
|
||||||
Err(err) => Err(CliError::RpcRequestError(format!(
|
Err(err) => Err(CliError::RpcRequestError(format!(
|
||||||
"Account data could not be deserialized to nonce state: {:?}",
|
"Account data could not be deserialized to nonce state: {:?}",
|
||||||
err
|
err
|
||||||
@@ -554,7 +553,7 @@ pub fn process_show_nonce_account(
|
|||||||
))
|
))
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
let print_account = |data: Option<(Meta, Hash)>| {
|
let print_account = |data: Option<&nonce::state::Data>| {
|
||||||
println!(
|
println!(
|
||||||
"Balance: {}",
|
"Balance: {}",
|
||||||
build_balance_message(nonce_account.lamports, use_lamports_unit, true)
|
build_balance_message(nonce_account.lamports, use_lamports_unit, true)
|
||||||
@@ -562,26 +561,32 @@ pub fn process_show_nonce_account(
|
|||||||
println!(
|
println!(
|
||||||
"Minimum Balance Required: {}",
|
"Minimum Balance Required: {}",
|
||||||
build_balance_message(
|
build_balance_message(
|
||||||
rpc_client.get_minimum_balance_for_rent_exemption(NonceState::size())?,
|
rpc_client.get_minimum_balance_for_rent_exemption(State::size())?,
|
||||||
use_lamports_unit,
|
use_lamports_unit,
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
match data {
|
match data {
|
||||||
Some((meta, hash)) => {
|
Some(ref data) => {
|
||||||
println!("Nonce: {}", hash);
|
println!("Nonce: {}", data.blockhash);
|
||||||
println!("Authority: {}", meta.nonce_authority);
|
println!(
|
||||||
|
"Fee: {} lamports per signature",
|
||||||
|
data.fee_calculator.lamports_per_signature
|
||||||
|
);
|
||||||
|
println!("Authority: {}", data.authority);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
println!("Nonce: uninitialized");
|
println!("Nonce: uninitialized");
|
||||||
|
println!("Fees: uninitialized");
|
||||||
println!("Authority: uninitialized");
|
println!("Authority: uninitialized");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok("".to_string())
|
Ok("".to_string())
|
||||||
};
|
};
|
||||||
match nonce_account.state() {
|
let nonce_state = StateMut::<Versions>::state(&nonce_account).map(|v| v.convert_to_current());
|
||||||
Ok(NonceState::Uninitialized) => print_account(None),
|
match nonce_state {
|
||||||
Ok(NonceState::Initialized(meta, hash)) => print_account(Some((meta, hash))),
|
Ok(State::Uninitialized) => print_account(None),
|
||||||
|
Ok(State::Initialized(ref data)) => print_account(Some(data)),
|
||||||
Err(err) => Err(CliError::RpcRequestError(format!(
|
Err(err) => Err(CliError::RpcRequestError(format!(
|
||||||
"Account data could not be deserialized to nonce state: {:?}",
|
"Account data could not be deserialized to nonce state: {:?}",
|
||||||
err
|
err
|
||||||
@@ -626,8 +631,9 @@ mod tests {
|
|||||||
use crate::cli::{app, parse_command};
|
use crate::cli::{app, parse_command};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::Account,
|
account::Account,
|
||||||
|
fee_calculator::FeeCalculator,
|
||||||
hash::hash,
|
hash::hash,
|
||||||
nonce_state::{Meta as NonceMeta, NonceState},
|
nonce::{self, State},
|
||||||
signature::{read_keypair_file, write_keypair, Keypair, Signer},
|
signature::{read_keypair_file, write_keypair, Keypair, Signer},
|
||||||
system_program,
|
system_program,
|
||||||
};
|
};
|
||||||
@@ -903,18 +909,15 @@ mod tests {
|
|||||||
fn test_check_nonce_account() {
|
fn test_check_nonce_account() {
|
||||||
let blockhash = Hash::default();
|
let blockhash = Hash::default();
|
||||||
let nonce_pubkey = Pubkey::new_rand();
|
let nonce_pubkey = Pubkey::new_rand();
|
||||||
let valid = Account::new_data(
|
let data = Versions::new_current(State::Initialized(nonce::state::Data {
|
||||||
1,
|
authority: nonce_pubkey,
|
||||||
&NonceState::Initialized(NonceMeta::new(&nonce_pubkey), blockhash),
|
blockhash,
|
||||||
&system_program::ID,
|
fee_calculator: FeeCalculator::default(),
|
||||||
);
|
}));
|
||||||
|
let valid = Account::new_data(1, &data, &system_program::ID);
|
||||||
assert!(check_nonce_account(&valid.unwrap(), &nonce_pubkey, &blockhash).is_ok());
|
assert!(check_nonce_account(&valid.unwrap(), &nonce_pubkey, &blockhash).is_ok());
|
||||||
|
|
||||||
let invalid_owner = Account::new_data(
|
let invalid_owner = Account::new_data(1, &data, &Pubkey::new(&[1u8; 32]));
|
||||||
1,
|
|
||||||
&NonceState::Initialized(NonceMeta::new(&nonce_pubkey), blockhash),
|
|
||||||
&Pubkey::new(&[1u8; 32]),
|
|
||||||
);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
check_nonce_account(&invalid_owner.unwrap(), &nonce_pubkey, &blockhash),
|
check_nonce_account(&invalid_owner.unwrap(), &nonce_pubkey, &blockhash),
|
||||||
Err(Box::new(CliError::InvalidNonce(
|
Err(Box::new(CliError::InvalidNonce(
|
||||||
@@ -930,21 +933,23 @@ mod tests {
|
|||||||
))),
|
))),
|
||||||
);
|
);
|
||||||
|
|
||||||
let invalid_hash = Account::new_data(
|
let data = Versions::new_current(State::Initialized(nonce::state::Data {
|
||||||
1,
|
authority: nonce_pubkey,
|
||||||
&NonceState::Initialized(NonceMeta::new(&nonce_pubkey), hash(b"invalid")),
|
blockhash: hash(b"invalid"),
|
||||||
&system_program::ID,
|
fee_calculator: FeeCalculator::default(),
|
||||||
);
|
}));
|
||||||
|
let invalid_hash = Account::new_data(1, &data, &system_program::ID);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
check_nonce_account(&invalid_hash.unwrap(), &nonce_pubkey, &blockhash),
|
check_nonce_account(&invalid_hash.unwrap(), &nonce_pubkey, &blockhash),
|
||||||
Err(Box::new(CliError::InvalidNonce(CliNonceError::InvalidHash))),
|
Err(Box::new(CliError::InvalidNonce(CliNonceError::InvalidHash))),
|
||||||
);
|
);
|
||||||
|
|
||||||
let invalid_authority = Account::new_data(
|
let data = Versions::new_current(State::Initialized(nonce::state::Data {
|
||||||
1,
|
authority: Pubkey::new_rand(),
|
||||||
&NonceState::Initialized(NonceMeta::new(&Pubkey::new_rand()), blockhash),
|
blockhash,
|
||||||
&system_program::ID,
|
fee_calculator: FeeCalculator::default(),
|
||||||
);
|
}));
|
||||||
|
let invalid_authority = Account::new_data(1, &data, &system_program::ID);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
check_nonce_account(&invalid_authority.unwrap(), &nonce_pubkey, &blockhash),
|
check_nonce_account(&invalid_authority.unwrap(), &nonce_pubkey, &blockhash),
|
||||||
Err(Box::new(CliError::InvalidNonce(
|
Err(Box::new(CliError::InvalidNonce(
|
||||||
@@ -952,7 +957,8 @@ mod tests {
|
|||||||
))),
|
))),
|
||||||
);
|
);
|
||||||
|
|
||||||
let invalid_state = Account::new_data(1, &NonceState::Uninitialized, &system_program::ID);
|
let data = Versions::new_current(State::Uninitialized);
|
||||||
|
let invalid_state = Account::new_data(1, &data, &system_program::ID);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
check_nonce_account(&invalid_state.unwrap(), &nonce_pubkey, &blockhash),
|
check_nonce_account(&invalid_state.unwrap(), &nonce_pubkey, &blockhash),
|
||||||
Err(Box::new(CliError::InvalidNonce(
|
Err(Box::new(CliError::InvalidNonce(
|
||||||
|
@@ -201,7 +201,7 @@ mod tests {
|
|||||||
fn test_blockhashspec_get_blockhash_fee_calc() {
|
fn test_blockhashspec_get_blockhash_fee_calc() {
|
||||||
let test_blockhash = hash(&[0u8]);
|
let test_blockhash = hash(&[0u8]);
|
||||||
let rpc_blockhash = hash(&[1u8]);
|
let rpc_blockhash = hash(&[1u8]);
|
||||||
let rpc_fee_calc = FeeCalculator::new(42, 42);
|
let rpc_fee_calc = FeeCalculator::new(42);
|
||||||
let get_recent_blockhash_response = json!(Response {
|
let get_recent_blockhash_response = json!(Response {
|
||||||
context: RpcResponseContext { slot: 1 },
|
context: RpcResponseContext { slot: 1 },
|
||||||
value: json!((
|
value: json!((
|
||||||
|
@@ -7,7 +7,8 @@ use crate::{
|
|||||||
nonce::{check_nonce_account, nonce_arg, NONCE_ARG, NONCE_AUTHORITY_ARG},
|
nonce::{check_nonce_account, nonce_arg, NONCE_ARG, NONCE_AUTHORITY_ARG},
|
||||||
offline::*,
|
offline::*,
|
||||||
};
|
};
|
||||||
use clap::{App, Arg, ArgMatches, SubCommand};
|
use chrono::{DateTime, NaiveDateTime, SecondsFormat, Utc};
|
||||||
|
use clap::{App, Arg, ArgGroup, ArgMatches, SubCommand};
|
||||||
use console::style;
|
use console::style;
|
||||||
use solana_clap_utils::{input_parsers::*, input_validators::*, offline::*, ArgConstant};
|
use solana_clap_utils::{input_parsers::*, input_validators::*, offline::*, ArgConstant};
|
||||||
use solana_client::rpc_client::RpcClient;
|
use solana_client::rpc_client::RpcClient;
|
||||||
@@ -24,7 +25,7 @@ use solana_sdk::{
|
|||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
};
|
};
|
||||||
use solana_stake_program::{
|
use solana_stake_program::{
|
||||||
stake_instruction::{self, StakeError},
|
stake_instruction::{self, LockupArgs, StakeError},
|
||||||
stake_state::{Authorized, Lockup, Meta, StakeAuthorize, StakeState},
|
stake_state::{Authorized, Lockup, Meta, StakeAuthorize, StakeState},
|
||||||
};
|
};
|
||||||
use solana_vote_program::vote_state::VoteState;
|
use solana_vote_program::vote_state::VoteState;
|
||||||
@@ -364,6 +365,9 @@ impl StakeSubCommands for App<'_, '_> {
|
|||||||
.validator(is_pubkey_or_keypair)
|
.validator(is_pubkey_or_keypair)
|
||||||
.help("Identity of the new lockup custodian (can withdraw before lockup expires)")
|
.help("Identity of the new lockup custodian (can withdraw before lockup expires)")
|
||||||
)
|
)
|
||||||
|
.group(ArgGroup::with_name("lockup_details")
|
||||||
|
.args(&["lockup_epoch", "lockup_date", "new_custodian"])
|
||||||
|
.required(true))
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("custodian")
|
Arg::with_name("custodian")
|
||||||
.long("custodian")
|
.long("custodian")
|
||||||
@@ -672,9 +676,9 @@ pub fn parse_stake_set_lockup(
|
|||||||
wallet_manager: Option<&Arc<RemoteWalletManager>>,
|
wallet_manager: Option<&Arc<RemoteWalletManager>>,
|
||||||
) -> Result<CliCommandInfo, CliError> {
|
) -> Result<CliCommandInfo, CliError> {
|
||||||
let stake_account_pubkey = pubkey_of(matches, "stake_account_pubkey").unwrap();
|
let stake_account_pubkey = pubkey_of(matches, "stake_account_pubkey").unwrap();
|
||||||
let epoch = value_of(matches, "lockup_epoch").unwrap_or(0);
|
let epoch = value_of(matches, "lockup_epoch");
|
||||||
let unix_timestamp = unix_timestamp_from_rfc3339_datetime(matches, "lockup_date").unwrap_or(0);
|
let unix_timestamp = unix_timestamp_from_rfc3339_datetime(matches, "lockup_date");
|
||||||
let new_custodian = pubkey_of(matches, "new_custodian").unwrap_or_default();
|
let new_custodian = pubkey_of(matches, "new_custodian");
|
||||||
|
|
||||||
let sign_only = matches.is_present(SIGN_ONLY_ARG.name);
|
let sign_only = matches.is_present(SIGN_ONLY_ARG.name);
|
||||||
let blockhash_query = BlockhashQuery::new_from_matches(matches);
|
let blockhash_query = BlockhashQuery::new_from_matches(matches);
|
||||||
@@ -695,7 +699,7 @@ pub fn parse_stake_set_lockup(
|
|||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::StakeSetLockup {
|
command: CliCommand::StakeSetLockup {
|
||||||
stake_account_pubkey,
|
stake_account_pubkey,
|
||||||
lockup: Lockup {
|
lockup: LockupArgs {
|
||||||
custodian: new_custodian,
|
custodian: new_custodian,
|
||||||
epoch,
|
epoch,
|
||||||
unix_timestamp,
|
unix_timestamp,
|
||||||
@@ -1155,7 +1159,7 @@ pub fn process_stake_set_lockup(
|
|||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
config: &CliConfig,
|
config: &CliConfig,
|
||||||
stake_account_pubkey: &Pubkey,
|
stake_account_pubkey: &Pubkey,
|
||||||
lockup: &mut Lockup,
|
lockup: &mut LockupArgs,
|
||||||
custodian: SignerIndex,
|
custodian: SignerIndex,
|
||||||
sign_only: bool,
|
sign_only: bool,
|
||||||
blockhash_query: &BlockhashQuery,
|
blockhash_query: &BlockhashQuery,
|
||||||
@@ -1166,10 +1170,7 @@ pub fn process_stake_set_lockup(
|
|||||||
let (recent_blockhash, fee_calculator) =
|
let (recent_blockhash, fee_calculator) =
|
||||||
blockhash_query.get_blockhash_fee_calculator(rpc_client)?;
|
blockhash_query.get_blockhash_fee_calculator(rpc_client)?;
|
||||||
let custodian = config.signers[custodian];
|
let custodian = config.signers[custodian];
|
||||||
// If new custodian is not explicitly set, default to current custodian
|
|
||||||
if lockup.custodian == Pubkey::default() {
|
|
||||||
lockup.custodian = custodian.pubkey();
|
|
||||||
}
|
|
||||||
let ixs = vec![stake_instruction::set_lockup(
|
let ixs = vec![stake_instruction::set_lockup(
|
||||||
stake_account_pubkey,
|
stake_account_pubkey,
|
||||||
lockup,
|
lockup,
|
||||||
@@ -1215,6 +1216,12 @@ pub fn print_stake_state(stake_lamports: u64, stake_state: &StakeState, use_lamp
|
|||||||
println!("Authorized Withdrawer: {}", authorized.withdrawer);
|
println!("Authorized Withdrawer: {}", authorized.withdrawer);
|
||||||
}
|
}
|
||||||
fn show_lockup(lockup: &Lockup) {
|
fn show_lockup(lockup: &Lockup) {
|
||||||
|
println!(
|
||||||
|
"Lockup Timestamp: {} (UnixTimestamp: {})",
|
||||||
|
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(lockup.unix_timestamp, 0), Utc)
|
||||||
|
.to_rfc3339_opts(SecondsFormat::Secs, true),
|
||||||
|
lockup.unix_timestamp
|
||||||
|
);
|
||||||
println!("Lockup Epoch: {}", lockup.epoch);
|
println!("Lockup Epoch: {}", lockup.epoch);
|
||||||
println!("Lockup Custodian: {}", lockup.custodian);
|
println!("Lockup Custodian: {}", lockup.custodian);
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,7 @@ use solana_client::rpc_client::RpcClient;
|
|||||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::Account,
|
account::Account,
|
||||||
|
commitment_config::CommitmentConfig,
|
||||||
message::Message,
|
message::Message,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
system_instruction::{create_address_with_seed, SystemError},
|
system_instruction::{create_address_with_seed, SystemError},
|
||||||
@@ -158,6 +159,14 @@ impl VoteSubCommands for App<'_, '_> {
|
|||||||
SubCommand::with_name("vote-account")
|
SubCommand::with_name("vote-account")
|
||||||
.about("Show the contents of a vote account")
|
.about("Show the contents of a vote account")
|
||||||
.alias("show-vote-account")
|
.alias("show-vote-account")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("confirmed")
|
||||||
|
.long("confirmed")
|
||||||
|
.takes_value(false)
|
||||||
|
.help(
|
||||||
|
"Return information at maximum-lockout commitment level",
|
||||||
|
),
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("vote_account_pubkey")
|
Arg::with_name("vote_account_pubkey")
|
||||||
.index(1)
|
.index(1)
|
||||||
@@ -267,10 +276,16 @@ pub fn parse_vote_get_account_command(
|
|||||||
) -> Result<CliCommandInfo, CliError> {
|
) -> Result<CliCommandInfo, CliError> {
|
||||||
let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap();
|
let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap();
|
||||||
let use_lamports_unit = matches.is_present("lamports");
|
let use_lamports_unit = matches.is_present("lamports");
|
||||||
|
let commitment_config = if matches.is_present("confirmed") {
|
||||||
|
CommitmentConfig::default()
|
||||||
|
} else {
|
||||||
|
CommitmentConfig::recent()
|
||||||
|
};
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::ShowVoteAccount {
|
command: CliCommand::ShowVoteAccount {
|
||||||
pubkey: vote_account_pubkey,
|
pubkey: vote_account_pubkey,
|
||||||
use_lamports_unit,
|
use_lamports_unit,
|
||||||
|
commitment_config,
|
||||||
},
|
},
|
||||||
signers: vec![],
|
signers: vec![],
|
||||||
})
|
})
|
||||||
@@ -423,8 +438,14 @@ pub fn process_vote_update_validator(
|
|||||||
fn get_vote_account(
|
fn get_vote_account(
|
||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
vote_account_pubkey: &Pubkey,
|
vote_account_pubkey: &Pubkey,
|
||||||
|
commitment_config: CommitmentConfig,
|
||||||
) -> Result<(Account, VoteState), Box<dyn std::error::Error>> {
|
) -> Result<(Account, VoteState), Box<dyn std::error::Error>> {
|
||||||
let vote_account = rpc_client.get_account(vote_account_pubkey)?;
|
let vote_account = rpc_client
|
||||||
|
.get_account_with_commitment(vote_account_pubkey, commitment_config)?
|
||||||
|
.value
|
||||||
|
.ok_or_else(|| {
|
||||||
|
CliError::RpcRequestError(format!("{:?} account does not exist", vote_account_pubkey))
|
||||||
|
})?;
|
||||||
|
|
||||||
if vote_account.owner != solana_vote_program::id() {
|
if vote_account.owner != solana_vote_program::id() {
|
||||||
return Err(CliError::RpcRequestError(format!(
|
return Err(CliError::RpcRequestError(format!(
|
||||||
@@ -447,8 +468,10 @@ pub fn process_show_vote_account(
|
|||||||
_config: &CliConfig,
|
_config: &CliConfig,
|
||||||
vote_account_pubkey: &Pubkey,
|
vote_account_pubkey: &Pubkey,
|
||||||
use_lamports_unit: bool,
|
use_lamports_unit: bool,
|
||||||
|
commitment_config: CommitmentConfig,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let (vote_account, vote_state) = get_vote_account(rpc_client, vote_account_pubkey)?;
|
let (vote_account, vote_state) =
|
||||||
|
get_vote_account(rpc_client, vote_account_pubkey, commitment_config)?;
|
||||||
|
|
||||||
let epoch_schedule = rpc_client.get_epoch_schedule()?;
|
let epoch_schedule = rpc_client.get_epoch_schedule()?;
|
||||||
|
|
||||||
|
@@ -11,7 +11,7 @@ use solana_faucet::faucet::run_local_faucet;
|
|||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
fee_calculator::FeeCalculator,
|
fee_calculator::FeeCalculator,
|
||||||
nonce_state::NonceState,
|
nonce,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{Keypair, Signer},
|
signature::{Keypair, Signer},
|
||||||
};
|
};
|
||||||
@@ -377,7 +377,7 @@ fn test_nonced_pay_tx() {
|
|||||||
config.signers = vec![&default_signer];
|
config.signers = vec![&default_signer];
|
||||||
|
|
||||||
let minimum_nonce_balance = rpc_client
|
let minimum_nonce_balance = rpc_client
|
||||||
.get_minimum_balance_for_rent_exemption(NonceState::size())
|
.get_minimum_balance_for_rent_exemption(nonce::State::size())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
request_and_confirm_airdrop(
|
request_and_confirm_airdrop(
|
||||||
@@ -409,9 +409,11 @@ fn test_nonced_pay_tx() {
|
|||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
||||||
let nonce_state: NonceState = account.state().unwrap();
|
let nonce_state = StateMut::<nonce::state::Versions>::state(&account)
|
||||||
|
.unwrap()
|
||||||
|
.convert_to_current();
|
||||||
let nonce_hash = match nonce_state {
|
let nonce_hash = match nonce_state {
|
||||||
NonceState::Initialized(_meta, hash) => hash,
|
nonce::State::Initialized(ref data) => data.blockhash,
|
||||||
_ => panic!("Nonce is not initialized"),
|
_ => panic!("Nonce is not initialized"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -431,9 +433,11 @@ fn test_nonced_pay_tx() {
|
|||||||
|
|
||||||
// Verify that nonce has been used
|
// Verify that nonce has been used
|
||||||
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
||||||
let nonce_state: NonceState = account.state().unwrap();
|
let nonce_state = StateMut::<nonce::state::Versions>::state(&account)
|
||||||
|
.unwrap()
|
||||||
|
.convert_to_current();
|
||||||
match nonce_state {
|
match nonce_state {
|
||||||
NonceState::Initialized(_meta, hash) => assert_ne!(hash, nonce_hash),
|
nonce::State::Initialized(ref data) => assert_ne!(data.blockhash, nonce_hash),
|
||||||
_ => assert!(false, "Nonce is not initialized"),
|
_ => assert!(false, "Nonce is not initialized"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -9,12 +9,15 @@ use solana_faucet::faucet::run_local_faucet;
|
|||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
fee_calculator::FeeCalculator,
|
fee_calculator::FeeCalculator,
|
||||||
nonce_state::NonceState,
|
nonce,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{keypair_from_seed, Keypair, Signer},
|
signature::{keypair_from_seed, Keypair, Signer},
|
||||||
system_instruction::create_address_with_seed,
|
system_instruction::create_address_with_seed,
|
||||||
};
|
};
|
||||||
use solana_stake_program::stake_state::{Lockup, StakeAuthorize, StakeState};
|
use solana_stake_program::{
|
||||||
|
stake_instruction::LockupArgs,
|
||||||
|
stake_state::{Lockup, StakeAuthorize, StakeState},
|
||||||
|
};
|
||||||
use std::{fs::remove_dir_all, sync::mpsc::channel, thread::sleep, time::Duration};
|
use std::{fs::remove_dir_all, sync::mpsc::channel, thread::sleep, time::Duration};
|
||||||
|
|
||||||
fn check_balance(expected_balance: u64, client: &RpcClient, pubkey: &Pubkey) {
|
fn check_balance(expected_balance: u64, client: &RpcClient, pubkey: &Pubkey) {
|
||||||
@@ -454,7 +457,7 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
|||||||
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
|
|
||||||
let minimum_nonce_balance = rpc_client
|
let minimum_nonce_balance = rpc_client
|
||||||
.get_minimum_balance_for_rent_exemption(NonceState::size())
|
.get_minimum_balance_for_rent_exemption(nonce::State::size())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
request_and_confirm_airdrop(
|
request_and_confirm_airdrop(
|
||||||
@@ -497,9 +500,11 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
|||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
||||||
let nonce_state: NonceState = account.state().unwrap();
|
let nonce_state = StateMut::<nonce::state::Versions>::state(&account)
|
||||||
|
.unwrap()
|
||||||
|
.convert_to_current();
|
||||||
let nonce_hash = match nonce_state {
|
let nonce_hash = match nonce_state {
|
||||||
NonceState::Initialized(_meta, hash) => hash,
|
nonce::State::Initialized(ref data) => data.blockhash,
|
||||||
_ => panic!("Nonce is not initialized"),
|
_ => panic!("Nonce is not initialized"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -520,9 +525,11 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
|||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
||||||
let nonce_state: NonceState = account.state().unwrap();
|
let nonce_state = StateMut::<nonce::state::Versions>::state(&account)
|
||||||
|
.unwrap()
|
||||||
|
.convert_to_current();
|
||||||
let nonce_hash = match nonce_state {
|
let nonce_hash = match nonce_state {
|
||||||
NonceState::Initialized(_meta, hash) => hash,
|
nonce::State::Initialized(ref data) => data.blockhash,
|
||||||
_ => panic!("Nonce is not initialized"),
|
_ => panic!("Nonce is not initialized"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -697,7 +704,7 @@ fn test_stake_authorize() {
|
|||||||
|
|
||||||
// Create nonce account
|
// Create nonce account
|
||||||
let minimum_nonce_balance = rpc_client
|
let minimum_nonce_balance = rpc_client
|
||||||
.get_minimum_balance_for_rent_exemption(NonceState::size())
|
.get_minimum_balance_for_rent_exemption(nonce::State::size())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let nonce_account = Keypair::new();
|
let nonce_account = Keypair::new();
|
||||||
config.signers = vec![&default_signer, &nonce_account];
|
config.signers = vec![&default_signer, &nonce_account];
|
||||||
@@ -711,9 +718,11 @@ fn test_stake_authorize() {
|
|||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
||||||
let nonce_state: NonceState = account.state().unwrap();
|
let nonce_state = StateMut::<nonce::state::Versions>::state(&account)
|
||||||
|
.unwrap()
|
||||||
|
.convert_to_current();
|
||||||
let nonce_hash = match nonce_state {
|
let nonce_hash = match nonce_state {
|
||||||
NonceState::Initialized(_meta, hash) => hash,
|
nonce::State::Initialized(ref data) => data.blockhash,
|
||||||
_ => panic!("Nonce is not initialized"),
|
_ => panic!("Nonce is not initialized"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -760,9 +769,11 @@ fn test_stake_authorize() {
|
|||||||
};
|
};
|
||||||
assert_eq!(current_authority, online_authority_pubkey);
|
assert_eq!(current_authority, online_authority_pubkey);
|
||||||
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
||||||
let nonce_state: NonceState = account.state().unwrap();
|
let nonce_state = StateMut::<nonce::state::Versions>::state(&account)
|
||||||
|
.unwrap()
|
||||||
|
.convert_to_current();
|
||||||
let new_nonce_hash = match nonce_state {
|
let new_nonce_hash = match nonce_state {
|
||||||
NonceState::Initialized(_meta, hash) => hash,
|
nonce::State::Initialized(ref data) => data.blockhash,
|
||||||
_ => panic!("Nonce is not initialized"),
|
_ => panic!("Nonce is not initialized"),
|
||||||
};
|
};
|
||||||
assert_ne!(nonce_hash, new_nonce_hash);
|
assert_ne!(nonce_hash, new_nonce_hash);
|
||||||
@@ -979,7 +990,7 @@ fn test_stake_split() {
|
|||||||
|
|
||||||
// Create nonce account
|
// Create nonce account
|
||||||
let minimum_nonce_balance = rpc_client
|
let minimum_nonce_balance = rpc_client
|
||||||
.get_minimum_balance_for_rent_exemption(NonceState::size())
|
.get_minimum_balance_for_rent_exemption(nonce::State::size())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let nonce_account = keypair_from_seed(&[1u8; 32]).unwrap();
|
let nonce_account = keypair_from_seed(&[1u8; 32]).unwrap();
|
||||||
config.signers = vec![&default_signer, &nonce_account];
|
config.signers = vec![&default_signer, &nonce_account];
|
||||||
@@ -994,9 +1005,11 @@ fn test_stake_split() {
|
|||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
||||||
let nonce_state: NonceState = account.state().unwrap();
|
let nonce_state = StateMut::<nonce::state::Versions>::state(&account)
|
||||||
|
.unwrap()
|
||||||
|
.convert_to_current();
|
||||||
let nonce_hash = match nonce_state {
|
let nonce_hash = match nonce_state {
|
||||||
NonceState::Initialized(_meta, hash) => hash,
|
nonce::State::Initialized(ref data) => data.blockhash,
|
||||||
_ => panic!("Nonce is not initialized"),
|
_ => panic!("Nonce is not initialized"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1128,10 +1141,10 @@ fn test_stake_set_lockup() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Online set lockup
|
// Online set lockup
|
||||||
let mut lockup = Lockup {
|
let lockup = LockupArgs {
|
||||||
unix_timestamp: 1581534570,
|
unix_timestamp: Some(1581534570),
|
||||||
epoch: 200,
|
epoch: Some(200),
|
||||||
custodian: Pubkey::default(),
|
custodian: None,
|
||||||
};
|
};
|
||||||
config.signers.pop();
|
config.signers.pop();
|
||||||
config.command = CliCommand::StakeSetLockup {
|
config.command = CliCommand::StakeSetLockup {
|
||||||
@@ -1151,17 +1164,21 @@ fn test_stake_set_lockup() {
|
|||||||
StakeState::Initialized(meta) => meta.lockup,
|
StakeState::Initialized(meta) => meta.lockup,
|
||||||
_ => panic!("Unexpected stake state!"),
|
_ => panic!("Unexpected stake state!"),
|
||||||
};
|
};
|
||||||
lockup.custodian = config.signers[0].pubkey(); // Default new_custodian is config.signers[0]
|
assert_eq!(
|
||||||
assert_eq!(current_lockup, lockup);
|
current_lockup.unix_timestamp,
|
||||||
|
lockup.unix_timestamp.unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(current_lockup.epoch, lockup.epoch.unwrap());
|
||||||
|
assert_eq!(current_lockup.custodian, config.signers[0].pubkey());
|
||||||
|
|
||||||
// Set custodian to another pubkey
|
// Set custodian to another pubkey
|
||||||
let online_custodian = Keypair::new();
|
let online_custodian = Keypair::new();
|
||||||
let online_custodian_pubkey = online_custodian.pubkey();
|
let online_custodian_pubkey = online_custodian.pubkey();
|
||||||
|
|
||||||
let lockup = Lockup {
|
let lockup = LockupArgs {
|
||||||
unix_timestamp: 1581534571,
|
unix_timestamp: Some(1581534571),
|
||||||
epoch: 201,
|
epoch: Some(201),
|
||||||
custodian: online_custodian_pubkey,
|
custodian: Some(online_custodian_pubkey),
|
||||||
};
|
};
|
||||||
config.command = CliCommand::StakeSetLockup {
|
config.command = CliCommand::StakeSetLockup {
|
||||||
stake_account_pubkey,
|
stake_account_pubkey,
|
||||||
@@ -1175,10 +1192,10 @@ fn test_stake_set_lockup() {
|
|||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
|
|
||||||
let mut lockup = Lockup {
|
let lockup = LockupArgs {
|
||||||
unix_timestamp: 1581534572,
|
unix_timestamp: Some(1581534572),
|
||||||
epoch: 202,
|
epoch: Some(202),
|
||||||
custodian: Pubkey::default(),
|
custodian: None,
|
||||||
};
|
};
|
||||||
config.signers = vec![&default_signer, &online_custodian];
|
config.signers = vec![&default_signer, &online_custodian];
|
||||||
config.command = CliCommand::StakeSetLockup {
|
config.command = CliCommand::StakeSetLockup {
|
||||||
@@ -1198,14 +1215,18 @@ fn test_stake_set_lockup() {
|
|||||||
StakeState::Initialized(meta) => meta.lockup,
|
StakeState::Initialized(meta) => meta.lockup,
|
||||||
_ => panic!("Unexpected stake state!"),
|
_ => panic!("Unexpected stake state!"),
|
||||||
};
|
};
|
||||||
lockup.custodian = online_custodian_pubkey; // Default new_custodian is designated custodian
|
assert_eq!(
|
||||||
assert_eq!(current_lockup, lockup);
|
current_lockup.unix_timestamp,
|
||||||
|
lockup.unix_timestamp.unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(current_lockup.epoch, lockup.epoch.unwrap());
|
||||||
|
assert_eq!(current_lockup.custodian, online_custodian_pubkey);
|
||||||
|
|
||||||
// Set custodian to offline pubkey
|
// Set custodian to offline pubkey
|
||||||
let lockup = Lockup {
|
let lockup = LockupArgs {
|
||||||
unix_timestamp: 1581534573,
|
unix_timestamp: Some(1581534573),
|
||||||
epoch: 203,
|
epoch: Some(203),
|
||||||
custodian: offline_pubkey,
|
custodian: Some(offline_pubkey),
|
||||||
};
|
};
|
||||||
config.command = CliCommand::StakeSetLockup {
|
config.command = CliCommand::StakeSetLockup {
|
||||||
stake_account_pubkey,
|
stake_account_pubkey,
|
||||||
@@ -1221,7 +1242,7 @@ fn test_stake_set_lockup() {
|
|||||||
|
|
||||||
// Create nonce account
|
// Create nonce account
|
||||||
let minimum_nonce_balance = rpc_client
|
let minimum_nonce_balance = rpc_client
|
||||||
.get_minimum_balance_for_rent_exemption(NonceState::size())
|
.get_minimum_balance_for_rent_exemption(nonce::State::size())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let nonce_account = keypair_from_seed(&[1u8; 32]).unwrap();
|
let nonce_account = keypair_from_seed(&[1u8; 32]).unwrap();
|
||||||
let nonce_account_pubkey = nonce_account.pubkey();
|
let nonce_account_pubkey = nonce_account.pubkey();
|
||||||
@@ -1237,17 +1258,19 @@ fn test_stake_set_lockup() {
|
|||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let account = rpc_client.get_account(&nonce_account_pubkey).unwrap();
|
let account = rpc_client.get_account(&nonce_account_pubkey).unwrap();
|
||||||
let nonce_state: NonceState = account.state().unwrap();
|
let nonce_state = StateMut::<nonce::state::Versions>::state(&account)
|
||||||
|
.unwrap()
|
||||||
|
.convert_to_current();
|
||||||
let nonce_hash = match nonce_state {
|
let nonce_hash = match nonce_state {
|
||||||
NonceState::Initialized(_meta, hash) => hash,
|
nonce::State::Initialized(ref data) => data.blockhash,
|
||||||
_ => panic!("Nonce is not initialized"),
|
_ => panic!("Nonce is not initialized"),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Nonced offline set lockup
|
// Nonced offline set lockup
|
||||||
let lockup = Lockup {
|
let lockup = LockupArgs {
|
||||||
unix_timestamp: 1581534576,
|
unix_timestamp: Some(1581534576),
|
||||||
epoch: 222,
|
epoch: Some(222),
|
||||||
custodian: offline_pubkey,
|
custodian: None,
|
||||||
};
|
};
|
||||||
config_offline.command = CliCommand::StakeSetLockup {
|
config_offline.command = CliCommand::StakeSetLockup {
|
||||||
stake_account_pubkey,
|
stake_account_pubkey,
|
||||||
@@ -1280,7 +1303,12 @@ fn test_stake_set_lockup() {
|
|||||||
StakeState::Initialized(meta) => meta.lockup,
|
StakeState::Initialized(meta) => meta.lockup,
|
||||||
_ => panic!("Unexpected stake state!"),
|
_ => panic!("Unexpected stake state!"),
|
||||||
};
|
};
|
||||||
assert_eq!(current_lockup, lockup);
|
assert_eq!(
|
||||||
|
current_lockup.unix_timestamp,
|
||||||
|
lockup.unix_timestamp.unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(current_lockup.epoch, lockup.epoch.unwrap());
|
||||||
|
assert_eq!(current_lockup.custodian, offline_pubkey);
|
||||||
|
|
||||||
server.close().unwrap();
|
server.close().unwrap();
|
||||||
remove_dir_all(ledger_path).unwrap();
|
remove_dir_all(ledger_path).unwrap();
|
||||||
@@ -1331,7 +1359,7 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
|||||||
|
|
||||||
// Create nonce account
|
// Create nonce account
|
||||||
let minimum_nonce_balance = rpc_client
|
let minimum_nonce_balance = rpc_client
|
||||||
.get_minimum_balance_for_rent_exemption(NonceState::size())
|
.get_minimum_balance_for_rent_exemption(nonce::State::size())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let nonce_account = keypair_from_seed(&[3u8; 32]).unwrap();
|
let nonce_account = keypair_from_seed(&[3u8; 32]).unwrap();
|
||||||
let nonce_pubkey = nonce_account.pubkey();
|
let nonce_pubkey = nonce_account.pubkey();
|
||||||
@@ -1346,9 +1374,11 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
|||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
||||||
let nonce_state: NonceState = account.state().unwrap();
|
let nonce_state = StateMut::<nonce::state::Versions>::state(&account)
|
||||||
|
.unwrap()
|
||||||
|
.convert_to_current();
|
||||||
let nonce_hash = match nonce_state {
|
let nonce_hash = match nonce_state {
|
||||||
NonceState::Initialized(_meta, hash) => hash,
|
nonce::State::Initialized(ref data) => data.blockhash,
|
||||||
_ => panic!("Nonce is not initialized"),
|
_ => panic!("Nonce is not initialized"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1394,9 +1424,11 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
|||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
||||||
let nonce_state: NonceState = account.state().unwrap();
|
let nonce_state = StateMut::<nonce::state::Versions>::state(&account)
|
||||||
|
.unwrap()
|
||||||
|
.convert_to_current();
|
||||||
let nonce_hash = match nonce_state {
|
let nonce_hash = match nonce_state {
|
||||||
NonceState::Initialized(_meta, hash) => hash,
|
nonce::State::Initialized(ref data) => data.blockhash,
|
||||||
_ => panic!("Nonce is not initialized"),
|
_ => panic!("Nonce is not initialized"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1435,9 +1467,11 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
|||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
||||||
let nonce_state: NonceState = account.state().unwrap();
|
let nonce_state = StateMut::<nonce::state::Versions>::state(&account)
|
||||||
|
.unwrap()
|
||||||
|
.convert_to_current();
|
||||||
let nonce_hash = match nonce_state {
|
let nonce_hash = match nonce_state {
|
||||||
NonceState::Initialized(_meta, hash) => hash,
|
nonce::State::Initialized(ref data) => data.blockhash,
|
||||||
_ => panic!("Nonce is not initialized"),
|
_ => panic!("Nonce is not initialized"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -9,7 +9,7 @@ use solana_faucet::faucet::run_local_faucet;
|
|||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
fee_calculator::FeeCalculator,
|
fee_calculator::FeeCalculator,
|
||||||
nonce_state::NonceState,
|
nonce,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{keypair_from_seed, Keypair, Signer},
|
signature::{keypair_from_seed, Keypair, Signer},
|
||||||
};
|
};
|
||||||
@@ -120,7 +120,7 @@ fn test_transfer() {
|
|||||||
// Create nonce account
|
// Create nonce account
|
||||||
let nonce_account = keypair_from_seed(&[3u8; 32]).unwrap();
|
let nonce_account = keypair_from_seed(&[3u8; 32]).unwrap();
|
||||||
let minimum_nonce_balance = rpc_client
|
let minimum_nonce_balance = rpc_client
|
||||||
.get_minimum_balance_for_rent_exemption(NonceState::size())
|
.get_minimum_balance_for_rent_exemption(nonce::State::size())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
config.signers = vec![&default_signer, &nonce_account];
|
config.signers = vec![&default_signer, &nonce_account];
|
||||||
config.command = CliCommand::CreateNonceAccount {
|
config.command = CliCommand::CreateNonceAccount {
|
||||||
@@ -134,9 +134,11 @@ fn test_transfer() {
|
|||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
||||||
let nonce_state: NonceState = account.state().unwrap();
|
let nonce_state = StateMut::<nonce::state::Versions>::state(&account)
|
||||||
|
.unwrap()
|
||||||
|
.convert_to_current();
|
||||||
let nonce_hash = match nonce_state {
|
let nonce_hash = match nonce_state {
|
||||||
NonceState::Initialized(_meta, hash) => hash,
|
nonce::State::Initialized(ref data) => data.blockhash,
|
||||||
_ => panic!("Nonce is not initialized"),
|
_ => panic!("Nonce is not initialized"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -156,9 +158,11 @@ fn test_transfer() {
|
|||||||
check_balance(49_976 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
|
check_balance(49_976 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
|
||||||
check_balance(30, &rpc_client, &recipient_pubkey);
|
check_balance(30, &rpc_client, &recipient_pubkey);
|
||||||
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
||||||
let nonce_state: NonceState = account.state().unwrap();
|
let nonce_state = StateMut::<nonce::state::Versions>::state(&account)
|
||||||
|
.unwrap()
|
||||||
|
.convert_to_current();
|
||||||
let new_nonce_hash = match nonce_state {
|
let new_nonce_hash = match nonce_state {
|
||||||
NonceState::Initialized(_meta, hash) => hash,
|
nonce::State::Initialized(ref data) => data.blockhash,
|
||||||
_ => panic!("Nonce is not initialized"),
|
_ => panic!("Nonce is not initialized"),
|
||||||
};
|
};
|
||||||
assert_ne!(nonce_hash, new_nonce_hash);
|
assert_ne!(nonce_hash, new_nonce_hash);
|
||||||
@@ -175,9 +179,11 @@ fn test_transfer() {
|
|||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
let account = rpc_client.get_account(&nonce_account.pubkey()).unwrap();
|
||||||
let nonce_state: NonceState = account.state().unwrap();
|
let nonce_state = StateMut::<nonce::state::Versions>::state(&account)
|
||||||
|
.unwrap()
|
||||||
|
.convert_to_current();
|
||||||
let nonce_hash = match nonce_state {
|
let nonce_hash = match nonce_state {
|
||||||
NonceState::Initialized(_meta, hash) => hash,
|
nonce::State::Initialized(ref data) => data.blockhash,
|
||||||
_ => panic!("Nonce is not initialized"),
|
_ => panic!("Nonce is not initialized"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-client"
|
name = "solana-client"
|
||||||
version = "1.0.1"
|
version = "1.0.5"
|
||||||
description = "Solana Client"
|
description = "Solana Client"
|
||||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
@@ -18,8 +18,8 @@ reqwest = { version = "0.10.1", default-features = false, features = ["blocking"
|
|||||||
serde = "1.0.104"
|
serde = "1.0.104"
|
||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
serde_json = "1.0.46"
|
serde_json = "1.0.46"
|
||||||
solana-net-utils = { path = "../net-utils", version = "1.0.1" }
|
solana-net-utils = { path = "../net-utils", version = "1.0.5" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.0.1" }
|
solana-sdk = { path = "../sdk", version = "1.0.5" }
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
tungstenite = "0.10.1"
|
tungstenite = "0.10.1"
|
||||||
url = "2.1.1"
|
url = "2.1.1"
|
||||||
@@ -28,4 +28,4 @@ url = "2.1.1"
|
|||||||
assert_matches = "1.3.0"
|
assert_matches = "1.3.0"
|
||||||
jsonrpc-core = "14.0.5"
|
jsonrpc-core = "14.0.5"
|
||||||
jsonrpc-http-server = "14.0.6"
|
jsonrpc-http-server = "14.0.6"
|
||||||
solana-logger = { path = "../logger", version = "1.0.1" }
|
solana-logger = { path = "../logger", version = "1.0.5" }
|
||||||
|
@@ -6,7 +6,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use serde_json::{Number, Value};
|
use serde_json::{Number, Value};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
fee_calculator::FeeCalculator,
|
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
||||||
instruction::InstructionError,
|
instruction::InstructionError,
|
||||||
transaction::{self, TransactionError},
|
transaction::{self, TransactionError},
|
||||||
};
|
};
|
||||||
@@ -71,6 +71,21 @@ impl GenericRpcClientRequest for MockRpcClientRequest {
|
|||||||
serde_json::to_value(FeeCalculator::default()).unwrap(),
|
serde_json::to_value(FeeCalculator::default()).unwrap(),
|
||||||
),
|
),
|
||||||
})?,
|
})?,
|
||||||
|
RpcRequest::GetFeeCalculatorForBlockhash => {
|
||||||
|
let value = if self.url == "blockhash_expired" {
|
||||||
|
Value::Null
|
||||||
|
} else {
|
||||||
|
serde_json::to_value(Some(FeeCalculator::default())).unwrap()
|
||||||
|
};
|
||||||
|
serde_json::to_value(Response {
|
||||||
|
context: RpcResponseContext { slot: 1 },
|
||||||
|
value,
|
||||||
|
})?
|
||||||
|
}
|
||||||
|
RpcRequest::GetFeeRateGovernor => serde_json::to_value(Response {
|
||||||
|
context: RpcResponseContext { slot: 1 },
|
||||||
|
value: serde_json::to_value(FeeRateGovernor::default()).unwrap(),
|
||||||
|
})?,
|
||||||
RpcRequest::GetSignatureStatus => {
|
RpcRequest::GetSignatureStatus => {
|
||||||
let response: Option<transaction::Result<()>> = if self.url == "account_in_use" {
|
let response: Option<transaction::Result<()>> = if self.url == "account_in_use" {
|
||||||
Some(Err(TransactionError::AccountInUse))
|
Some(Err(TransactionError::AccountInUse))
|
||||||
|
@@ -6,8 +6,8 @@ use crate::{
|
|||||||
rpc_request::RpcRequest,
|
rpc_request::RpcRequest,
|
||||||
rpc_response::{
|
rpc_response::{
|
||||||
Response, RpcAccount, RpcBlockhashFeeCalculator, RpcConfirmedBlock, RpcContactInfo,
|
Response, RpcAccount, RpcBlockhashFeeCalculator, RpcConfirmedBlock, RpcContactInfo,
|
||||||
RpcEpochInfo, RpcKeyedAccount, RpcLeaderSchedule, RpcResponse, RpcVersionInfo,
|
RpcEpochInfo, RpcFeeCalculator, RpcFeeRateGovernor, RpcIdentity, RpcKeyedAccount,
|
||||||
RpcVoteAccountStatus,
|
RpcLeaderSchedule, RpcResponse, RpcVersionInfo, RpcVoteAccountStatus,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use bincode::serialize;
|
use bincode::serialize;
|
||||||
@@ -18,7 +18,7 @@ use solana_sdk::{
|
|||||||
clock::{Slot, UnixTimestamp, DEFAULT_TICKS_PER_SECOND, DEFAULT_TICKS_PER_SLOT},
|
clock::{Slot, UnixTimestamp, DEFAULT_TICKS_PER_SECOND, DEFAULT_TICKS_PER_SLOT},
|
||||||
commitment_config::CommitmentConfig,
|
commitment_config::CommitmentConfig,
|
||||||
epoch_schedule::EpochSchedule,
|
epoch_schedule::EpochSchedule,
|
||||||
fee_calculator::FeeCalculator,
|
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
inflation::Inflation,
|
inflation::Inflation,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
@@ -165,9 +165,16 @@ impl RpcClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_vote_accounts(&self) -> io::Result<RpcVoteAccountStatus> {
|
pub fn get_vote_accounts(&self) -> io::Result<RpcVoteAccountStatus> {
|
||||||
|
self.get_vote_accounts_with_commitment(CommitmentConfig::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_vote_accounts_with_commitment(
|
||||||
|
&self,
|
||||||
|
commitment_config: CommitmentConfig,
|
||||||
|
) -> io::Result<RpcVoteAccountStatus> {
|
||||||
let response = self
|
let response = self
|
||||||
.client
|
.client
|
||||||
.send(&RpcRequest::GetVoteAccounts, Value::Null, 0)
|
.send(&RpcRequest::GetVoteAccounts, json!([commitment_config]), 0)
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
io::Error::new(
|
io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
@@ -349,6 +356,34 @@ impl RpcClient {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_identity(&self) -> io::Result<Pubkey> {
|
||||||
|
let response = self
|
||||||
|
.client
|
||||||
|
.send(&RpcRequest::GetIdentity, Value::Null, 0)
|
||||||
|
.map_err(|err| {
|
||||||
|
io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
format!("GetIdentity request failure: {:?}", err),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
serde_json::from_value(response)
|
||||||
|
.map_err(|err| {
|
||||||
|
io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
format!("GetIdentity failure: {:?}", err),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.and_then(|rpc_identity: RpcIdentity| {
|
||||||
|
rpc_identity.identity.parse::<Pubkey>().map_err(|err| {
|
||||||
|
io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
format!("GetIdentity invalid pubkey failure: {:?}", err),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_inflation(&self) -> io::Result<Inflation> {
|
pub fn get_inflation(&self) -> io::Result<Inflation> {
|
||||||
let response = self
|
let response = self
|
||||||
.client
|
.client
|
||||||
@@ -804,6 +839,60 @@ impl RpcClient {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_fee_calculator_for_blockhash(
|
||||||
|
&self,
|
||||||
|
blockhash: &Hash,
|
||||||
|
) -> io::Result<Option<FeeCalculator>> {
|
||||||
|
let response = self
|
||||||
|
.client
|
||||||
|
.send(
|
||||||
|
&RpcRequest::GetFeeCalculatorForBlockhash,
|
||||||
|
json!([blockhash.to_string()]),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.map_err(|e| {
|
||||||
|
io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
format!("GetFeeCalculatorForBlockhash request failure: {:?}", e),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let Response { value, .. } = serde_json::from_value::<Response<Option<RpcFeeCalculator>>>(
|
||||||
|
response,
|
||||||
|
)
|
||||||
|
.map_err(|e| {
|
||||||
|
io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
format!("GetFeeCalculatorForBlockhash parse failure: {:?}", e),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
Ok(value.map(|rf| rf.fee_calculator))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_fee_rate_governor(&self) -> RpcResponse<FeeRateGovernor> {
|
||||||
|
let response = self
|
||||||
|
.client
|
||||||
|
.send(&RpcRequest::GetFeeRateGovernor, Value::Null, 0)
|
||||||
|
.map_err(|e| {
|
||||||
|
io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
format!("GetFeeRateGovernor request failure: {:?}", e),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let Response {
|
||||||
|
context,
|
||||||
|
value: RpcFeeRateGovernor { fee_rate_governor },
|
||||||
|
} = serde_json::from_value::<Response<RpcFeeRateGovernor>>(response).map_err(|e| {
|
||||||
|
io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
format!("GetFeeRateGovernor parse failure: {:?}", e),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
Ok(Response {
|
||||||
|
context,
|
||||||
|
value: fee_rate_governor,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_new_blockhash(&self, blockhash: &Hash) -> io::Result<(Hash, FeeCalculator)> {
|
pub fn get_new_blockhash(&self, blockhash: &Hash) -> io::Result<(Hash, FeeCalculator)> {
|
||||||
let mut num_retries = 0;
|
let mut num_retries = 0;
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
@@ -15,11 +15,14 @@ pub enum RpcRequest {
|
|||||||
GetEpochInfo,
|
GetEpochInfo,
|
||||||
GetEpochSchedule,
|
GetEpochSchedule,
|
||||||
GetGenesisHash,
|
GetGenesisHash,
|
||||||
|
GetIdentity,
|
||||||
GetInflation,
|
GetInflation,
|
||||||
GetLeaderSchedule,
|
GetLeaderSchedule,
|
||||||
GetNumBlocksSinceSignatureConfirmation,
|
GetNumBlocksSinceSignatureConfirmation,
|
||||||
GetProgramAccounts,
|
GetProgramAccounts,
|
||||||
GetRecentBlockhash,
|
GetRecentBlockhash,
|
||||||
|
GetFeeCalculatorForBlockhash,
|
||||||
|
GetFeeRateGovernor,
|
||||||
GetSignatureStatus,
|
GetSignatureStatus,
|
||||||
GetSlot,
|
GetSlot,
|
||||||
GetSlotLeader,
|
GetSlotLeader,
|
||||||
@@ -54,6 +57,7 @@ impl RpcRequest {
|
|||||||
RpcRequest::GetEpochInfo => "getEpochInfo",
|
RpcRequest::GetEpochInfo => "getEpochInfo",
|
||||||
RpcRequest::GetEpochSchedule => "getEpochSchedule",
|
RpcRequest::GetEpochSchedule => "getEpochSchedule",
|
||||||
RpcRequest::GetGenesisHash => "getGenesisHash",
|
RpcRequest::GetGenesisHash => "getGenesisHash",
|
||||||
|
RpcRequest::GetIdentity => "getIdentity",
|
||||||
RpcRequest::GetInflation => "getInflation",
|
RpcRequest::GetInflation => "getInflation",
|
||||||
RpcRequest::GetLeaderSchedule => "getLeaderSchedule",
|
RpcRequest::GetLeaderSchedule => "getLeaderSchedule",
|
||||||
RpcRequest::GetNumBlocksSinceSignatureConfirmation => {
|
RpcRequest::GetNumBlocksSinceSignatureConfirmation => {
|
||||||
@@ -61,6 +65,8 @@ impl RpcRequest {
|
|||||||
}
|
}
|
||||||
RpcRequest::GetProgramAccounts => "getProgramAccounts",
|
RpcRequest::GetProgramAccounts => "getProgramAccounts",
|
||||||
RpcRequest::GetRecentBlockhash => "getRecentBlockhash",
|
RpcRequest::GetRecentBlockhash => "getRecentBlockhash",
|
||||||
|
RpcRequest::GetFeeCalculatorForBlockhash => "getFeeCalculatorForBlockhash",
|
||||||
|
RpcRequest::GetFeeRateGovernor => "getFeeRateGovernor",
|
||||||
RpcRequest::GetSignatureStatus => "getSignatureStatus",
|
RpcRequest::GetSignatureStatus => "getSignatureStatus",
|
||||||
RpcRequest::GetSlot => "getSlot",
|
RpcRequest::GetSlot => "getSlot",
|
||||||
RpcRequest::GetSlotLeader => "getSlotLeader",
|
RpcRequest::GetSlotLeader => "getSlotLeader",
|
||||||
@@ -123,7 +129,7 @@ mod tests {
|
|||||||
assert_eq!(request["params"], json!([addr]));
|
assert_eq!(request["params"], json!([addr]));
|
||||||
|
|
||||||
let test_request = RpcRequest::GetBalance;
|
let test_request = RpcRequest::GetBalance;
|
||||||
let request = test_request.build_request_json(1, json!([addr]));
|
let request = test_request.build_request_json(1, json!([addr.clone()]));
|
||||||
assert_eq!(request["method"], "getBalance");
|
assert_eq!(request["method"], "getBalance");
|
||||||
|
|
||||||
let test_request = RpcRequest::GetEpochInfo;
|
let test_request = RpcRequest::GetEpochInfo;
|
||||||
@@ -138,6 +144,14 @@ mod tests {
|
|||||||
let request = test_request.build_request_json(1, Value::Null);
|
let request = test_request.build_request_json(1, Value::Null);
|
||||||
assert_eq!(request["method"], "getRecentBlockhash");
|
assert_eq!(request["method"], "getRecentBlockhash");
|
||||||
|
|
||||||
|
let test_request = RpcRequest::GetFeeCalculatorForBlockhash;
|
||||||
|
let request = test_request.build_request_json(1, json!([addr.clone()]));
|
||||||
|
assert_eq!(request["method"], "getFeeCalculatorForBlockhash");
|
||||||
|
|
||||||
|
let test_request = RpcRequest::GetFeeRateGovernor;
|
||||||
|
let request = test_request.build_request_json(1, Value::Null);
|
||||||
|
assert_eq!(request["method"], "getFeeRateGovernor");
|
||||||
|
|
||||||
let test_request = RpcRequest::GetSlot;
|
let test_request = RpcRequest::GetSlot;
|
||||||
let request = test_request.build_request_json(1, Value::Null);
|
let request = test_request.build_request_json(1, Value::Null);
|
||||||
assert_eq!(request["method"], "getSlot");
|
assert_eq!(request["method"], "getSlot");
|
||||||
|
@@ -4,7 +4,7 @@ use jsonrpc_core::Result as JsonResult;
|
|||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::Account,
|
account::Account,
|
||||||
clock::{Epoch, Slot},
|
clock::{Epoch, Slot},
|
||||||
fee_calculator::FeeCalculator,
|
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
||||||
message::MessageHeader,
|
message::MessageHeader,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
transaction::{Result, Transaction},
|
transaction::{Result, Transaction},
|
||||||
@@ -152,6 +152,18 @@ pub struct RpcBlockhashFeeCalculator {
|
|||||||
pub fee_calculator: FeeCalculator,
|
pub fee_calculator: FeeCalculator,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct RpcFeeCalculator {
|
||||||
|
pub fee_calculator: FeeCalculator,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct RpcFeeRateGovernor {
|
||||||
|
pub fee_rate_governor: FeeRateGovernor,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct RpcKeyedAccount {
|
pub struct RpcKeyedAccount {
|
||||||
@@ -235,6 +247,13 @@ pub struct RpcVersionInfo {
|
|||||||
pub solana_core: String,
|
pub solana_core: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
|
pub struct RpcIdentity {
|
||||||
|
/// The current node identity pubkey
|
||||||
|
pub identity: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct RpcVoteAccountStatus {
|
pub struct RpcVoteAccountStatus {
|
||||||
|
@@ -11,7 +11,7 @@ use solana_sdk::{
|
|||||||
client::{AsyncClient, Client, SyncClient},
|
client::{AsyncClient, Client, SyncClient},
|
||||||
clock::MAX_PROCESSING_AGE,
|
clock::MAX_PROCESSING_AGE,
|
||||||
commitment_config::CommitmentConfig,
|
commitment_config::CommitmentConfig,
|
||||||
fee_calculator::FeeCalculator,
|
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
instruction::Instruction,
|
instruction::Instruction,
|
||||||
message::Message,
|
message::Message,
|
||||||
@@ -26,7 +26,7 @@ use solana_sdk::{
|
|||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
io,
|
io,
|
||||||
net::{SocketAddr, UdpSocket},
|
net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket},
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicBool, AtomicUsize, Ordering},
|
atomic::{AtomicBool, AtomicUsize, Ordering},
|
||||||
RwLock,
|
RwLock,
|
||||||
@@ -445,6 +445,21 @@ impl SyncClient for ThinClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_fee_calculator_for_blockhash(
|
||||||
|
&self,
|
||||||
|
blockhash: &Hash,
|
||||||
|
) -> TransportResult<Option<FeeCalculator>> {
|
||||||
|
let fee_calculator = self
|
||||||
|
.rpc_client()
|
||||||
|
.get_fee_calculator_for_blockhash(blockhash)?;
|
||||||
|
Ok(fee_calculator)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_fee_rate_governor(&self) -> TransportResult<FeeRateGovernor> {
|
||||||
|
let fee_rate_governor = self.rpc_client().get_fee_rate_governor()?;
|
||||||
|
Ok(fee_rate_governor.value)
|
||||||
|
}
|
||||||
|
|
||||||
fn get_signature_status(
|
fn get_signature_status(
|
||||||
&self,
|
&self,
|
||||||
signature: &Signature,
|
signature: &Signature,
|
||||||
@@ -598,7 +613,8 @@ impl AsyncClient for ThinClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_client((rpc, tpu): (SocketAddr, SocketAddr), range: (u16, u16)) -> ThinClient {
|
pub fn create_client((rpc, tpu): (SocketAddr, SocketAddr), range: (u16, u16)) -> ThinClient {
|
||||||
let (_, transactions_socket) = solana_net_utils::bind_in_range(range).unwrap();
|
let (_, transactions_socket) =
|
||||||
|
solana_net_utils::bind_in_range(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), range).unwrap();
|
||||||
ThinClient::new(rpc, tpu, transactions_socket)
|
ThinClient::new(rpc, tpu, transactions_socket)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -607,7 +623,8 @@ pub fn create_client_with_timeout(
|
|||||||
range: (u16, u16),
|
range: (u16, u16),
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
) -> ThinClient {
|
) -> ThinClient {
|
||||||
let (_, transactions_socket) = solana_net_utils::bind_in_range(range).unwrap();
|
let (_, transactions_socket) =
|
||||||
|
solana_net_utils::bind_in_range(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), range).unwrap();
|
||||||
ThinClient::new_socket_with_timeout(rpc, tpu, transactions_socket, timeout)
|
ThinClient::new_socket_with_timeout(rpc, tpu, transactions_socket, timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-core"
|
name = "solana-core"
|
||||||
description = "Blockchain, Rebuilt for Scale"
|
description = "Blockchain, Rebuilt for Scale"
|
||||||
version = "1.0.1"
|
version = "1.0.5"
|
||||||
documentation = "https://docs.rs/solana"
|
documentation = "https://docs.rs/solana"
|
||||||
homepage = "https://solana.com/"
|
homepage = "https://solana.com/"
|
||||||
readme = "../README.md"
|
readme = "../README.md"
|
||||||
@@ -41,26 +41,26 @@ regex = "1.3.4"
|
|||||||
serde = "1.0.104"
|
serde = "1.0.104"
|
||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
serde_json = "1.0.46"
|
serde_json = "1.0.46"
|
||||||
solana-budget-program = { path = "../programs/budget", version = "1.0.1" }
|
solana-budget-program = { path = "../programs/budget", version = "1.0.5" }
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "1.0.1" }
|
solana-clap-utils = { path = "../clap-utils", version = "1.0.5" }
|
||||||
solana-client = { path = "../client", version = "1.0.1" }
|
solana-client = { path = "../client", version = "1.0.5" }
|
||||||
solana-faucet = { path = "../faucet", version = "1.0.1" }
|
solana-faucet = { path = "../faucet", version = "1.0.5" }
|
||||||
ed25519-dalek = "=1.0.0-pre.1"
|
ed25519-dalek = "=1.0.0-pre.1"
|
||||||
solana-ledger = { path = "../ledger", version = "1.0.1" }
|
solana-ledger = { path = "../ledger", version = "1.0.5" }
|
||||||
solana-logger = { path = "../logger", version = "1.0.1" }
|
solana-logger = { path = "../logger", version = "1.0.5" }
|
||||||
solana-merkle-tree = { path = "../merkle-tree", version = "1.0.1" }
|
solana-merkle-tree = { path = "../merkle-tree", version = "1.0.5" }
|
||||||
solana-metrics = { path = "../metrics", version = "1.0.1" }
|
solana-metrics = { path = "../metrics", version = "1.0.5" }
|
||||||
solana-measure = { path = "../measure", version = "1.0.1" }
|
solana-measure = { path = "../measure", version = "1.0.5" }
|
||||||
solana-net-utils = { path = "../net-utils", version = "1.0.1" }
|
solana-net-utils = { path = "../net-utils", version = "1.0.5" }
|
||||||
solana-chacha-cuda = { path = "../chacha-cuda", version = "1.0.1" }
|
solana-chacha-cuda = { path = "../chacha-cuda", version = "1.0.5" }
|
||||||
solana-perf = { path = "../perf", version = "1.0.1" }
|
solana-perf = { path = "../perf", version = "1.0.5" }
|
||||||
solana-runtime = { path = "../runtime", version = "1.0.1" }
|
solana-runtime = { path = "../runtime", version = "1.0.5" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.0.1" }
|
solana-sdk = { path = "../sdk", version = "1.0.5" }
|
||||||
solana-stake-program = { path = "../programs/stake", version = "1.0.1" }
|
solana-stake-program = { path = "../programs/stake", version = "1.0.5" }
|
||||||
solana-storage-program = { path = "../programs/storage", version = "1.0.1" }
|
solana-storage-program = { path = "../programs/storage", version = "1.0.5" }
|
||||||
solana-vote-program = { path = "../programs/vote", version = "1.0.1" }
|
solana-vote-program = { path = "../programs/vote", version = "1.0.5" }
|
||||||
solana-vote-signer = { path = "../vote-signer", version = "1.0.1" }
|
solana-vote-signer = { path = "../vote-signer", version = "1.0.5" }
|
||||||
solana-sys-tuner = { path = "../sys-tuner", version = "1.0.1" }
|
solana-sys-tuner = { path = "../sys-tuner", version = "1.0.5" }
|
||||||
sys-info = "0.5.9"
|
sys-info = "0.5.9"
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
@@ -68,7 +68,7 @@ tokio = "0.1"
|
|||||||
tokio-codec = "0.1"
|
tokio-codec = "0.1"
|
||||||
tokio-fs = "0.1"
|
tokio-fs = "0.1"
|
||||||
tokio-io = "0.1"
|
tokio-io = "0.1"
|
||||||
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.0.1" }
|
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.0.5" }
|
||||||
trees = "0.2.1"
|
trees = "0.2.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
@@ -1635,8 +1635,9 @@ impl ClusterInfo {
|
|||||||
id: &Pubkey,
|
id: &Pubkey,
|
||||||
gossip_addr: &SocketAddr,
|
gossip_addr: &SocketAddr,
|
||||||
) -> (ContactInfo, UdpSocket, Option<TcpListener>) {
|
) -> (ContactInfo, UdpSocket, Option<TcpListener>) {
|
||||||
|
let bind_ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
|
||||||
let (port, (gossip_socket, ip_echo)) =
|
let (port, (gossip_socket, ip_echo)) =
|
||||||
Node::get_gossip_port(gossip_addr, VALIDATOR_PORT_RANGE);
|
Node::get_gossip_port(gossip_addr, VALIDATOR_PORT_RANGE, bind_ip_addr);
|
||||||
let contact_info = Self::gossip_contact_info(id, SocketAddr::new(gossip_addr.ip(), port));
|
let contact_info = Self::gossip_contact_info(id, SocketAddr::new(gossip_addr.ip(), port));
|
||||||
|
|
||||||
(contact_info, gossip_socket, Some(ip_echo))
|
(contact_info, gossip_socket, Some(ip_echo))
|
||||||
@@ -1644,7 +1645,8 @@ impl ClusterInfo {
|
|||||||
|
|
||||||
/// A Node with dummy ports to spy on gossip via pull requests
|
/// A Node with dummy ports to spy on gossip via pull requests
|
||||||
pub fn spy_node(id: &Pubkey) -> (ContactInfo, UdpSocket, Option<TcpListener>) {
|
pub fn spy_node(id: &Pubkey) -> (ContactInfo, UdpSocket, Option<TcpListener>) {
|
||||||
let (_, gossip_socket) = bind_in_range(VALIDATOR_PORT_RANGE).unwrap();
|
let bind_ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
|
||||||
|
let (_, gossip_socket) = bind_in_range(bind_ip_addr, VALIDATOR_PORT_RANGE).unwrap();
|
||||||
let contact_info = Self::spy_contact_info(id);
|
let contact_info = Self::spy_contact_info(id);
|
||||||
|
|
||||||
(contact_info, gossip_socket, None)
|
(contact_info, gossip_socket, None)
|
||||||
@@ -1759,16 +1761,18 @@ impl Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn new_localhost_with_pubkey(pubkey: &Pubkey) -> Self {
|
pub fn new_localhost_with_pubkey(pubkey: &Pubkey) -> Self {
|
||||||
|
let bind_ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
|
||||||
let tpu = UdpSocket::bind("127.0.0.1:0").unwrap();
|
let tpu = UdpSocket::bind("127.0.0.1:0").unwrap();
|
||||||
let (gossip_port, (gossip, ip_echo)) = bind_common_in_range((1024, 65535)).unwrap();
|
let (gossip_port, (gossip, ip_echo)) =
|
||||||
|
bind_common_in_range(bind_ip_addr, (1024, 65535)).unwrap();
|
||||||
let gossip_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), gossip_port);
|
let gossip_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), gossip_port);
|
||||||
let tvu = UdpSocket::bind("127.0.0.1:0").unwrap();
|
let tvu = UdpSocket::bind("127.0.0.1:0").unwrap();
|
||||||
let tvu_forwards = UdpSocket::bind("127.0.0.1:0").unwrap();
|
let tvu_forwards = UdpSocket::bind("127.0.0.1:0").unwrap();
|
||||||
let tpu_forwards = UdpSocket::bind("127.0.0.1:0").unwrap();
|
let tpu_forwards = UdpSocket::bind("127.0.0.1:0").unwrap();
|
||||||
let repair = UdpSocket::bind("127.0.0.1:0").unwrap();
|
let repair = UdpSocket::bind("127.0.0.1:0").unwrap();
|
||||||
let rpc_port = find_available_port_in_range((1024, 65535)).unwrap();
|
let rpc_port = find_available_port_in_range(bind_ip_addr, (1024, 65535)).unwrap();
|
||||||
let rpc_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), rpc_port);
|
let rpc_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), rpc_port);
|
||||||
let rpc_pubsub_port = find_available_port_in_range((1024, 65535)).unwrap();
|
let rpc_pubsub_port = find_available_port_in_range(bind_ip_addr, (1024, 65535)).unwrap();
|
||||||
let rpc_pubsub_addr =
|
let rpc_pubsub_addr =
|
||||||
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), rpc_pubsub_port);
|
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), rpc_pubsub_port);
|
||||||
|
|
||||||
@@ -1811,45 +1815,52 @@ impl Node {
|
|||||||
fn get_gossip_port(
|
fn get_gossip_port(
|
||||||
gossip_addr: &SocketAddr,
|
gossip_addr: &SocketAddr,
|
||||||
port_range: PortRange,
|
port_range: PortRange,
|
||||||
|
bind_ip_addr: IpAddr,
|
||||||
) -> (u16, (UdpSocket, TcpListener)) {
|
) -> (u16, (UdpSocket, TcpListener)) {
|
||||||
if gossip_addr.port() != 0 {
|
if gossip_addr.port() != 0 {
|
||||||
(
|
(
|
||||||
gossip_addr.port(),
|
gossip_addr.port(),
|
||||||
bind_common(gossip_addr.port(), false).unwrap_or_else(|e| {
|
bind_common(bind_ip_addr, gossip_addr.port(), false).unwrap_or_else(|e| {
|
||||||
panic!("gossip_addr bind_to port {}: {}", gossip_addr.port(), e)
|
panic!("gossip_addr bind_to port {}: {}", gossip_addr.port(), e)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
bind_common_in_range(port_range).expect("Failed to bind")
|
bind_common_in_range(bind_ip_addr, port_range).expect("Failed to bind")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn bind(port_range: PortRange) -> (u16, UdpSocket) {
|
fn bind(bind_ip_addr: IpAddr, port_range: PortRange) -> (u16, UdpSocket) {
|
||||||
bind_in_range(port_range).expect("Failed to bind")
|
bind_in_range(bind_ip_addr, port_range).expect("Failed to bind")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_external_ip(
|
pub fn new_with_external_ip(
|
||||||
pubkey: &Pubkey,
|
pubkey: &Pubkey,
|
||||||
gossip_addr: &SocketAddr,
|
gossip_addr: &SocketAddr,
|
||||||
port_range: PortRange,
|
port_range: PortRange,
|
||||||
|
bind_ip_addr: IpAddr,
|
||||||
) -> Node {
|
) -> Node {
|
||||||
let (gossip_port, (gossip, ip_echo)) = Self::get_gossip_port(gossip_addr, port_range);
|
let (gossip_port, (gossip, ip_echo)) =
|
||||||
|
Self::get_gossip_port(gossip_addr, port_range, bind_ip_addr);
|
||||||
|
|
||||||
let (tvu_port, tvu_sockets) = multi_bind_in_range(port_range, 8).expect("tvu multi_bind");
|
let (tvu_port, tvu_sockets) =
|
||||||
|
multi_bind_in_range(bind_ip_addr, port_range, 8).expect("tvu multi_bind");
|
||||||
|
|
||||||
let (tvu_forwards_port, tvu_forwards_sockets) =
|
let (tvu_forwards_port, tvu_forwards_sockets) =
|
||||||
multi_bind_in_range(port_range, 8).expect("tvu_forwards multi_bind");
|
multi_bind_in_range(bind_ip_addr, port_range, 8).expect("tvu_forwards multi_bind");
|
||||||
|
|
||||||
let (tpu_port, tpu_sockets) = multi_bind_in_range(port_range, 32).expect("tpu multi_bind");
|
let (tpu_port, tpu_sockets) =
|
||||||
|
multi_bind_in_range(bind_ip_addr, port_range, 32).expect("tpu multi_bind");
|
||||||
|
|
||||||
let (tpu_forwards_port, tpu_forwards_sockets) =
|
let (tpu_forwards_port, tpu_forwards_sockets) =
|
||||||
multi_bind_in_range(port_range, 8).expect("tpu_forwards multi_bind");
|
multi_bind_in_range(bind_ip_addr, port_range, 8).expect("tpu_forwards multi_bind");
|
||||||
|
|
||||||
let (_, retransmit_sockets) =
|
let (_, retransmit_sockets) =
|
||||||
multi_bind_in_range(port_range, 8).expect("retransmit multi_bind");
|
multi_bind_in_range(bind_ip_addr, port_range, 8).expect("retransmit multi_bind");
|
||||||
|
|
||||||
let (repair_port, repair) = Self::bind(port_range);
|
let (repair_port, repair) = Self::bind(bind_ip_addr, port_range);
|
||||||
let (serve_repair_port, serve_repair) = Self::bind(port_range);
|
let (serve_repair_port, serve_repair) = Self::bind(bind_ip_addr, port_range);
|
||||||
|
|
||||||
let (_, broadcast) = multi_bind_in_range(port_range, 4).expect("broadcast multi_bind");
|
let (_, broadcast) =
|
||||||
|
multi_bind_in_range(bind_ip_addr, port_range, 4).expect("broadcast multi_bind");
|
||||||
|
|
||||||
let info = ContactInfo {
|
let info = ContactInfo {
|
||||||
id: *pubkey,
|
id: *pubkey,
|
||||||
@@ -1889,9 +1900,10 @@ impl Node {
|
|||||||
pubkey: &Pubkey,
|
pubkey: &Pubkey,
|
||||||
gossip_addr: &SocketAddr,
|
gossip_addr: &SocketAddr,
|
||||||
port_range: PortRange,
|
port_range: PortRange,
|
||||||
|
bind_ip_addr: IpAddr,
|
||||||
) -> Node {
|
) -> Node {
|
||||||
let mut new = Self::new_with_external_ip(pubkey, gossip_addr, port_range);
|
let mut new = Self::new_with_external_ip(pubkey, gossip_addr, port_range, bind_ip_addr);
|
||||||
let (storage_port, storage_socket) = Self::bind(port_range);
|
let (storage_port, storage_socket) = Self::bind(bind_ip_addr, port_range);
|
||||||
|
|
||||||
new.info.storage_addr = SocketAddr::new(gossip_addr.ip(), storage_port);
|
new.info.storage_addr = SocketAddr::new(gossip_addr.ip(), storage_port);
|
||||||
new.sockets.storage = Some(storage_socket);
|
new.sockets.storage = Some(storage_socket);
|
||||||
@@ -2025,6 +2037,7 @@ mod tests {
|
|||||||
&Pubkey::new_rand(),
|
&Pubkey::new_rand(),
|
||||||
&socketaddr!(ip, 0),
|
&socketaddr!(ip, 0),
|
||||||
VALIDATOR_PORT_RANGE,
|
VALIDATOR_PORT_RANGE,
|
||||||
|
IpAddr::V4(ip),
|
||||||
);
|
);
|
||||||
|
|
||||||
check_node_sockets(&node, IpAddr::V4(ip), VALIDATOR_PORT_RANGE);
|
check_node_sockets(&node, IpAddr::V4(ip), VALIDATOR_PORT_RANGE);
|
||||||
@@ -2034,7 +2047,7 @@ mod tests {
|
|||||||
fn new_with_external_ip_test_gossip() {
|
fn new_with_external_ip_test_gossip() {
|
||||||
let ip = IpAddr::V4(Ipv4Addr::from(0));
|
let ip = IpAddr::V4(Ipv4Addr::from(0));
|
||||||
let port = {
|
let port = {
|
||||||
bind_in_range(VALIDATOR_PORT_RANGE)
|
bind_in_range(ip, VALIDATOR_PORT_RANGE)
|
||||||
.expect("Failed to bind")
|
.expect("Failed to bind")
|
||||||
.0
|
.0
|
||||||
};
|
};
|
||||||
@@ -2042,6 +2055,7 @@ mod tests {
|
|||||||
&Pubkey::new_rand(),
|
&Pubkey::new_rand(),
|
||||||
&socketaddr!(0, port),
|
&socketaddr!(0, port),
|
||||||
VALIDATOR_PORT_RANGE,
|
VALIDATOR_PORT_RANGE,
|
||||||
|
ip,
|
||||||
);
|
);
|
||||||
|
|
||||||
check_node_sockets(&node, ip, VALIDATOR_PORT_RANGE);
|
check_node_sockets(&node, ip, VALIDATOR_PORT_RANGE);
|
||||||
@@ -2056,6 +2070,7 @@ mod tests {
|
|||||||
&Pubkey::new_rand(),
|
&Pubkey::new_rand(),
|
||||||
&socketaddr!(ip, 0),
|
&socketaddr!(ip, 0),
|
||||||
VALIDATOR_PORT_RANGE,
|
VALIDATOR_PORT_RANGE,
|
||||||
|
IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
|
||||||
);
|
);
|
||||||
|
|
||||||
let ip = IpAddr::V4(ip);
|
let ip = IpAddr::V4(ip);
|
||||||
|
@@ -9,7 +9,7 @@ use solana_ledger::bank_forks::BankForks;
|
|||||||
use solana_perf::recycler::Recycler;
|
use solana_perf::recycler::Recycler;
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey::Pubkey;
|
||||||
use solana_sdk::signature::{Keypair, Signer};
|
use solana_sdk::signature::{Keypair, Signer};
|
||||||
use std::net::{SocketAddr, TcpListener, UdpSocket};
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener, UdpSocket};
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::mpsc::channel;
|
use std::sync::mpsc::channel;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
@@ -163,7 +163,11 @@ pub fn get_multi_client(nodes: &[ContactInfo]) -> (ThinClient, usize) {
|
|||||||
.collect();
|
.collect();
|
||||||
let rpc_addrs: Vec<_> = addrs.iter().map(|addr| addr.0).collect();
|
let rpc_addrs: Vec<_> = addrs.iter().map(|addr| addr.0).collect();
|
||||||
let tpu_addrs: Vec<_> = addrs.iter().map(|addr| addr.1).collect();
|
let tpu_addrs: Vec<_> = addrs.iter().map(|addr| addr.1).collect();
|
||||||
let (_, transactions_socket) = solana_net_utils::bind_in_range(VALIDATOR_PORT_RANGE).unwrap();
|
let (_, transactions_socket) = solana_net_utils::bind_in_range(
|
||||||
|
IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
|
||||||
|
VALIDATOR_PORT_RANGE,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
let num_nodes = tpu_addrs.len();
|
let num_nodes = tpu_addrs.len();
|
||||||
(
|
(
|
||||||
ThinClient::new_from_addrs(rpc_addrs, tpu_addrs, transactions_socket),
|
ThinClient::new_from_addrs(rpc_addrs, tpu_addrs, transactions_socket),
|
||||||
|
@@ -15,9 +15,12 @@ pub struct LocalVoteSignerService {
|
|||||||
impl LocalVoteSignerService {
|
impl LocalVoteSignerService {
|
||||||
#[allow(clippy::new_ret_no_self)]
|
#[allow(clippy::new_ret_no_self)]
|
||||||
pub fn new(port_range: PortRange) -> (Self, SocketAddr) {
|
pub fn new(port_range: PortRange) -> (Self, SocketAddr) {
|
||||||
let addr = solana_net_utils::find_available_port_in_range(port_range)
|
let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
|
||||||
.map(|port| SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), port))
|
let addr = SocketAddr::new(
|
||||||
.expect("Failed to find an available port for local vote signer service");
|
ip_addr,
|
||||||
|
solana_net_utils::find_available_port_in_range(ip_addr, port_range)
|
||||||
|
.expect("Failed to find an available port for local vote signer service"),
|
||||||
|
);
|
||||||
let exit = Arc::new(AtomicBool::new(false));
|
let exit = Arc::new(AtomicBool::new(false));
|
||||||
let thread_exit = exit.clone();
|
let thread_exit = exit.clone();
|
||||||
let thread = Builder::new()
|
let thread = Builder::new()
|
||||||
|
@@ -12,7 +12,8 @@ use solana_ledger::{
|
|||||||
use solana_sdk::clock::DEFAULT_SLOTS_PER_EPOCH;
|
use solana_sdk::clock::DEFAULT_SLOTS_PER_EPOCH;
|
||||||
use solana_sdk::{clock::Slot, epoch_schedule::EpochSchedule, pubkey::Pubkey};
|
use solana_sdk::{clock::Slot, epoch_schedule::EpochSchedule, pubkey::Pubkey};
|
||||||
use std::{
|
use std::{
|
||||||
collections::BTreeSet,
|
collections::{BTreeSet, HashSet},
|
||||||
|
iter::Iterator,
|
||||||
net::UdpSocket,
|
net::UdpSocket,
|
||||||
ops::Bound::{Included, Unbounded},
|
ops::Bound::{Included, Unbounded},
|
||||||
sync::atomic::{AtomicBool, Ordering},
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
@@ -209,10 +210,8 @@ impl RepairService {
|
|||||||
// TODO: Incorporate gossip to determine priorities for repair?
|
// TODO: Incorporate gossip to determine priorities for repair?
|
||||||
|
|
||||||
// Try to resolve orphans in blockstore
|
// Try to resolve orphans in blockstore
|
||||||
let mut orphans = blockstore.get_orphans(Some(MAX_ORPHANS));
|
let orphans = blockstore.orphans_iterator(root + 1).unwrap();
|
||||||
orphans.retain(|x| *x > root);
|
Self::generate_repairs_for_orphans(orphans, &mut repairs);
|
||||||
|
|
||||||
Self::generate_repairs_for_orphans(&orphans[..], &mut repairs);
|
|
||||||
Ok(repairs)
|
Ok(repairs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,8 +239,11 @@ impl RepairService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_repairs_for_orphans(orphans: &[u64], repairs: &mut Vec<RepairType>) {
|
fn generate_repairs_for_orphans(
|
||||||
repairs.extend(orphans.iter().map(|h| RepairType::Orphan(*h)));
|
orphans: impl Iterator<Item = u64>,
|
||||||
|
repairs: &mut Vec<RepairType>,
|
||||||
|
) {
|
||||||
|
repairs.extend(orphans.take(MAX_ORPHANS).map(RepairType::Orphan));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Repairs any fork starting at the input slot
|
/// Repairs any fork starting at the input slot
|
||||||
@@ -402,6 +404,20 @@ impl RepairService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn find_incomplete_slots(blockstore: &Blockstore, root: Slot) -> HashSet<Slot> {
|
||||||
|
blockstore
|
||||||
|
.live_slots_iterator(root)
|
||||||
|
.filter_map(|(slot, slot_meta)| {
|
||||||
|
if !slot_meta.is_full() {
|
||||||
|
Some(slot)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn join(self) -> thread::Result<()> {
|
pub fn join(self) -> thread::Result<()> {
|
||||||
self.t_repair.join()
|
self.t_repair.join()
|
||||||
}
|
}
|
||||||
@@ -914,4 +930,63 @@ mod test {
|
|||||||
);
|
);
|
||||||
assert_eq!(stash.len(), 2);
|
assert_eq!(stash.len(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_find_incomplete_slots() {
|
||||||
|
let blockstore_path = get_tmp_ledger_path!();
|
||||||
|
{
|
||||||
|
let blockstore = Blockstore::open(&blockstore_path).unwrap();
|
||||||
|
let num_entries_per_slot = 100;
|
||||||
|
let (mut shreds, _) = make_slot_entries(0, 0, num_entries_per_slot);
|
||||||
|
assert!(shreds.len() > 1);
|
||||||
|
let (shreds4, _) = make_slot_entries(4, 0, num_entries_per_slot);
|
||||||
|
shreds.extend(shreds4);
|
||||||
|
blockstore.insert_shreds(shreds, None, false).unwrap();
|
||||||
|
|
||||||
|
// Nothing is incomplete
|
||||||
|
assert!(RepairService::find_incomplete_slots(&blockstore, 0).is_empty());
|
||||||
|
|
||||||
|
// Insert a slot 5 that chains to an incomplete orphan slot 3
|
||||||
|
let (shreds5, _) = make_slot_entries(5, 3, num_entries_per_slot);
|
||||||
|
blockstore.insert_shreds(shreds5, None, false).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
RepairService::find_incomplete_slots(&blockstore, 0),
|
||||||
|
vec![3].into_iter().collect()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Insert another incomplete orphan slot 2 that is the parent of slot 3.
|
||||||
|
// Both should be incomplete
|
||||||
|
let (shreds3, _) = make_slot_entries(3, 2, num_entries_per_slot);
|
||||||
|
blockstore
|
||||||
|
.insert_shreds(shreds3[1..].to_vec(), None, false)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
RepairService::find_incomplete_slots(&blockstore, 0),
|
||||||
|
vec![2, 3].into_iter().collect()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Insert a incomplete slot 6 that chains to the root 0,
|
||||||
|
// should also be incomplete
|
||||||
|
let (shreds6, _) = make_slot_entries(6, 0, num_entries_per_slot);
|
||||||
|
blockstore
|
||||||
|
.insert_shreds(shreds6[1..].to_vec(), None, false)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
RepairService::find_incomplete_slots(&blockstore, 0),
|
||||||
|
vec![2, 3, 6].into_iter().collect()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Complete slot 3, should no longer be marked incomplete
|
||||||
|
blockstore
|
||||||
|
.insert_shreds(shreds3[..].to_vec(), None, false)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
RepairService::find_incomplete_slots(&blockstore, 0),
|
||||||
|
vec![2, 6].into_iter().collect()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Blockstore::destroy(&blockstore_path).expect("Expected successful database destruction");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -824,6 +824,7 @@ impl ReplayStage {
|
|||||||
stats.stake_lockouts = stake_lockouts;
|
stats.stake_lockouts = stake_lockouts;
|
||||||
stats.block_height = bank.block_height();
|
stats.block_height = bank.block_height();
|
||||||
stats.computed = true;
|
stats.computed = true;
|
||||||
|
new_stats.push(stats.slot);
|
||||||
}
|
}
|
||||||
stats.vote_threshold = tower.check_vote_stake_threshold(
|
stats.vote_threshold = tower.check_vote_stake_threshold(
|
||||||
bank.slot(),
|
bank.slot(),
|
||||||
@@ -833,7 +834,6 @@ impl ReplayStage {
|
|||||||
stats.is_locked_out = tower.is_locked_out(bank.slot(), &ancestors);
|
stats.is_locked_out = tower.is_locked_out(bank.slot(), &ancestors);
|
||||||
stats.has_voted = tower.has_voted(bank.slot());
|
stats.has_voted = tower.has_voted(bank.slot());
|
||||||
stats.is_recent = tower.is_recent(bank.slot());
|
stats.is_recent = tower.is_recent(bank.slot());
|
||||||
new_stats.push(stats.slot);
|
|
||||||
}
|
}
|
||||||
new_stats
|
new_stats
|
||||||
}
|
}
|
||||||
@@ -964,6 +964,7 @@ impl ReplayStage {
|
|||||||
bank: Arc<Bank>,
|
bank: Arc<Bank>,
|
||||||
slot_full_senders: &[Sender<(u64, Pubkey)>],
|
slot_full_senders: &[Sender<(u64, Pubkey)>],
|
||||||
) {
|
) {
|
||||||
|
info!("bank frozen: {}", bank.slot());
|
||||||
bank.freeze();
|
bank.freeze();
|
||||||
slot_full_senders.iter().for_each(|sender| {
|
slot_full_senders.iter().for_each(|sender| {
|
||||||
if let Err(e) = sender.send((bank.slot(), *bank.collector_id())) {
|
if let Err(e) = sender.send((bank.slot(), *bank.collector_id())) {
|
||||||
@@ -1080,7 +1081,10 @@ pub(crate) mod tests {
|
|||||||
transaction::TransactionError,
|
transaction::TransactionError,
|
||||||
};
|
};
|
||||||
use solana_stake_program::stake_state;
|
use solana_stake_program::stake_state;
|
||||||
use solana_vote_program::vote_state::{self, Vote, VoteState, VoteStateVersions};
|
use solana_vote_program::{
|
||||||
|
vote_state::{self, Vote, VoteState, VoteStateVersions},
|
||||||
|
vote_transaction,
|
||||||
|
};
|
||||||
use std::{
|
use std::{
|
||||||
fs::remove_dir_all,
|
fs::remove_dir_all,
|
||||||
iter,
|
iter,
|
||||||
@@ -1909,6 +1913,119 @@ pub(crate) mod tests {
|
|||||||
Blockstore::destroy(&ledger_path).unwrap();
|
Blockstore::destroy(&ledger_path).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_compute_bank_stats_confirmed() {
|
||||||
|
let node_keypair = Keypair::new();
|
||||||
|
let vote_keypair = Keypair::new();
|
||||||
|
let stake_keypair = Keypair::new();
|
||||||
|
let node_pubkey = node_keypair.pubkey();
|
||||||
|
let mut keypairs = HashMap::new();
|
||||||
|
keypairs.insert(
|
||||||
|
node_pubkey,
|
||||||
|
ValidatorVoteKeypairs::new(node_keypair, vote_keypair, stake_keypair),
|
||||||
|
);
|
||||||
|
|
||||||
|
let (bank_forks, mut progress) = initialize_state(&keypairs);
|
||||||
|
let bank0 = bank_forks.get(0).unwrap().clone();
|
||||||
|
let my_keypairs = keypairs.get(&node_pubkey).unwrap();
|
||||||
|
let vote_tx = vote_transaction::new_vote_transaction(
|
||||||
|
vec![0],
|
||||||
|
bank0.hash(),
|
||||||
|
bank0.last_blockhash(),
|
||||||
|
&my_keypairs.node_keypair,
|
||||||
|
&my_keypairs.vote_keypair,
|
||||||
|
&my_keypairs.vote_keypair,
|
||||||
|
);
|
||||||
|
|
||||||
|
let bank_forks = RwLock::new(bank_forks);
|
||||||
|
let bank1 = Bank::new_from_parent(&bank0, &node_pubkey, 1);
|
||||||
|
bank1.process_transaction(&vote_tx).unwrap();
|
||||||
|
bank1.freeze();
|
||||||
|
|
||||||
|
// Test confirmations
|
||||||
|
let ancestors = bank_forks.read().unwrap().ancestors();
|
||||||
|
let mut frozen_banks: Vec<_> = bank_forks
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.frozen_banks()
|
||||||
|
.values()
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
let tower = Tower::new_for_tests(0, 0.67);
|
||||||
|
let newly_computed = ReplayStage::compute_bank_stats(
|
||||||
|
&node_pubkey,
|
||||||
|
&ancestors,
|
||||||
|
&mut frozen_banks,
|
||||||
|
&tower,
|
||||||
|
&mut progress,
|
||||||
|
);
|
||||||
|
assert_eq!(newly_computed, vec![0]);
|
||||||
|
// The only vote is in bank 1, and bank_forks does not currently contain
|
||||||
|
// bank 1, so no slot should be confirmed.
|
||||||
|
{
|
||||||
|
let fork_progress = progress.get(&0).unwrap();
|
||||||
|
let confirmed_forks = ReplayStage::confirm_forks(
|
||||||
|
&tower,
|
||||||
|
&fork_progress.fork_stats.stake_lockouts,
|
||||||
|
fork_progress.fork_stats.total_staked,
|
||||||
|
&progress,
|
||||||
|
&bank_forks,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(confirmed_forks.is_empty())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert the bank that contains a vote for slot 0, which confirms slot 0
|
||||||
|
bank_forks.write().unwrap().insert(bank1);
|
||||||
|
progress.insert(1, ForkProgress::new(bank0.last_blockhash()));
|
||||||
|
let ancestors = bank_forks.read().unwrap().ancestors();
|
||||||
|
let mut frozen_banks: Vec<_> = bank_forks
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.frozen_banks()
|
||||||
|
.values()
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
let newly_computed = ReplayStage::compute_bank_stats(
|
||||||
|
&node_pubkey,
|
||||||
|
&ancestors,
|
||||||
|
&mut frozen_banks,
|
||||||
|
&tower,
|
||||||
|
&mut progress,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(newly_computed, vec![1]);
|
||||||
|
{
|
||||||
|
let fork_progress = progress.get(&1).unwrap();
|
||||||
|
let confirmed_forks = ReplayStage::confirm_forks(
|
||||||
|
&tower,
|
||||||
|
&fork_progress.fork_stats.stake_lockouts,
|
||||||
|
fork_progress.fork_stats.total_staked,
|
||||||
|
&progress,
|
||||||
|
&bank_forks,
|
||||||
|
);
|
||||||
|
assert_eq!(confirmed_forks, vec![0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ancestors = bank_forks.read().unwrap().ancestors();
|
||||||
|
let mut frozen_banks: Vec<_> = bank_forks
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.frozen_banks()
|
||||||
|
.values()
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
let newly_computed = ReplayStage::compute_bank_stats(
|
||||||
|
&node_pubkey,
|
||||||
|
&ancestors,
|
||||||
|
&mut frozen_banks,
|
||||||
|
&tower,
|
||||||
|
&mut progress,
|
||||||
|
);
|
||||||
|
// No new stats should have been computed
|
||||||
|
assert!(newly_computed.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_child_bank_heavier() {
|
fn test_child_bank_heavier() {
|
||||||
let node_keypair = Keypair::new();
|
let node_keypair = Keypair::new();
|
||||||
|
@@ -284,6 +284,7 @@ mod tests {
|
|||||||
use solana_ledger::create_new_tmp_ledger;
|
use solana_ledger::create_new_tmp_ledger;
|
||||||
use solana_net_utils::find_available_port_in_range;
|
use solana_net_utils::find_available_port_in_range;
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey::Pubkey;
|
||||||
|
use std::net::{IpAddr, Ipv4Addr};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_skip_repair() {
|
fn test_skip_repair() {
|
||||||
@@ -300,11 +301,12 @@ mod tests {
|
|||||||
let bank_forks = Arc::new(RwLock::new(bank_forks));
|
let bank_forks = Arc::new(RwLock::new(bank_forks));
|
||||||
|
|
||||||
let mut me = ContactInfo::new_localhost(&Pubkey::new_rand(), 0);
|
let mut me = ContactInfo::new_localhost(&Pubkey::new_rand(), 0);
|
||||||
let port = find_available_port_in_range((8000, 10000)).unwrap();
|
let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
|
||||||
|
let port = find_available_port_in_range(ip_addr, (8000, 10000)).unwrap();
|
||||||
let me_retransmit = UdpSocket::bind(format!("127.0.0.1:{}", port)).unwrap();
|
let me_retransmit = UdpSocket::bind(format!("127.0.0.1:{}", port)).unwrap();
|
||||||
// need to make sure tvu and tpu are valid addresses
|
// need to make sure tvu and tpu are valid addresses
|
||||||
me.tvu_forwards = me_retransmit.local_addr().unwrap();
|
me.tvu_forwards = me_retransmit.local_addr().unwrap();
|
||||||
let port = find_available_port_in_range((8000, 10000)).unwrap();
|
let port = find_available_port_in_range(ip_addr, (8000, 10000)).unwrap();
|
||||||
me.tvu = UdpSocket::bind(format!("127.0.0.1:{}", port))
|
me.tvu = UdpSocket::bind(format!("127.0.0.1:{}", port))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.local_addr()
|
.local_addr()
|
||||||
|
191
core/src/rpc.rs
191
core/src/rpc.rs
@@ -9,9 +9,10 @@ use jsonrpc_core::{Error, Metadata, Result};
|
|||||||
use jsonrpc_derive::rpc;
|
use jsonrpc_derive::rpc;
|
||||||
use solana_client::rpc_response::{
|
use solana_client::rpc_response::{
|
||||||
Response, RpcAccount, RpcBlockCommitment, RpcBlockhashFeeCalculator, RpcConfirmedBlock,
|
Response, RpcAccount, RpcBlockCommitment, RpcBlockhashFeeCalculator, RpcConfirmedBlock,
|
||||||
RpcContactInfo, RpcEpochInfo, RpcKeyedAccount, RpcLeaderSchedule, RpcResponseContext,
|
RpcContactInfo, RpcEpochInfo, RpcFeeCalculator, RpcFeeRateGovernor, RpcIdentity,
|
||||||
RpcSignatureConfirmation, RpcStorageTurn, RpcTransactionEncoding, RpcVersionInfo,
|
RpcKeyedAccount, RpcLeaderSchedule, RpcResponseContext, RpcSignatureConfirmation,
|
||||||
RpcVoteAccountInfo, RpcVoteAccountStatus,
|
RpcStorageTurn, RpcTransactionEncoding, RpcVersionInfo, RpcVoteAccountInfo,
|
||||||
|
RpcVoteAccountStatus,
|
||||||
};
|
};
|
||||||
use solana_faucet::faucet::request_airdrop_transaction;
|
use solana_faucet::faucet::request_airdrop_transaction;
|
||||||
use solana_ledger::{
|
use solana_ledger::{
|
||||||
@@ -33,6 +34,7 @@ use solana_vote_program::vote_state::{VoteState, MAX_LOCKOUT_HISTORY};
|
|||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
net::{SocketAddr, UdpSocket},
|
net::{SocketAddr, UdpSocket},
|
||||||
|
str::FromStr,
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock},
|
||||||
thread::sleep,
|
thread::sleep,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
@@ -48,7 +50,9 @@ fn new_response<T>(bank: &Bank, value: T) -> RpcResponse<T> {
|
|||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct JsonRpcConfig {
|
pub struct JsonRpcConfig {
|
||||||
pub enable_validator_exit: bool,
|
pub enable_validator_exit: bool,
|
||||||
|
pub enable_set_log_filter: bool,
|
||||||
pub enable_get_confirmed_block: bool,
|
pub enable_get_confirmed_block: bool,
|
||||||
|
pub identity_pubkey: Pubkey,
|
||||||
pub faucet_addr: Option<SocketAddr>,
|
pub faucet_addr: Option<SocketAddr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,6 +168,29 @@ impl JsonRpcRequestProcessor {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_fee_calculator_for_blockhash(
|
||||||
|
&self,
|
||||||
|
blockhash: &Hash,
|
||||||
|
) -> RpcResponse<Option<RpcFeeCalculator>> {
|
||||||
|
let bank = &*self.bank(None);
|
||||||
|
let fee_calculator = bank.get_fee_calculator(blockhash);
|
||||||
|
new_response(
|
||||||
|
bank,
|
||||||
|
fee_calculator.map(|fee_calculator| RpcFeeCalculator { fee_calculator }),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_fee_rate_governor(&self) -> RpcResponse<RpcFeeRateGovernor> {
|
||||||
|
let bank = &*self.bank(None);
|
||||||
|
let fee_rate_governor = bank.get_fee_rate_governor();
|
||||||
|
new_response(
|
||||||
|
bank,
|
||||||
|
RpcFeeRateGovernor {
|
||||||
|
fee_rate_governor: fee_rate_governor.clone(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn confirm_transaction(
|
pub fn confirm_transaction(
|
||||||
&self,
|
&self,
|
||||||
signature: Result<Signature>,
|
signature: Result<Signature>,
|
||||||
@@ -313,6 +340,13 @@ impl JsonRpcRequestProcessor {
|
|||||||
Ok(pubkeys)
|
Ok(pubkeys)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_log_filter(&self, filter: String) -> Result<()> {
|
||||||
|
if self.config.enable_set_log_filter {
|
||||||
|
solana_logger::setup_with(&filter);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn validator_exit(&self) -> Result<bool> {
|
pub fn validator_exit(&self) -> Result<bool> {
|
||||||
if self.config.enable_validator_exit {
|
if self.config.enable_validator_exit {
|
||||||
warn!("validator_exit request...");
|
warn!("validator_exit request...");
|
||||||
@@ -491,6 +525,16 @@ pub trait RpcSol {
|
|||||||
commitment: Option<CommitmentConfig>,
|
commitment: Option<CommitmentConfig>,
|
||||||
) -> RpcResponse<RpcBlockhashFeeCalculator>;
|
) -> RpcResponse<RpcBlockhashFeeCalculator>;
|
||||||
|
|
||||||
|
#[rpc(meta, name = "getFeeCalculatorForBlockhash")]
|
||||||
|
fn get_fee_calculator_for_blockhash(
|
||||||
|
&self,
|
||||||
|
meta: Self::Metadata,
|
||||||
|
blockhash: String,
|
||||||
|
) -> RpcResponse<Option<RpcFeeCalculator>>;
|
||||||
|
|
||||||
|
#[rpc(meta, name = "getFeeRateGovernor")]
|
||||||
|
fn get_fee_rate_governor(&self, meta: Self::Metadata) -> RpcResponse<RpcFeeRateGovernor>;
|
||||||
|
|
||||||
#[rpc(meta, name = "getSignatureStatus")]
|
#[rpc(meta, name = "getSignatureStatus")]
|
||||||
fn get_signature_status(
|
fn get_signature_status(
|
||||||
&self,
|
&self,
|
||||||
@@ -580,6 +624,9 @@ pub trait RpcSol {
|
|||||||
commitment: Option<CommitmentConfig>,
|
commitment: Option<CommitmentConfig>,
|
||||||
) -> Result<Option<RpcSignatureConfirmation>>;
|
) -> Result<Option<RpcSignatureConfirmation>>;
|
||||||
|
|
||||||
|
#[rpc(meta, name = "getIdentity")]
|
||||||
|
fn get_identity(&self, meta: Self::Metadata) -> Result<RpcIdentity>;
|
||||||
|
|
||||||
#[rpc(meta, name = "getVersion")]
|
#[rpc(meta, name = "getVersion")]
|
||||||
fn get_version(&self, meta: Self::Metadata) -> Result<RpcVersionInfo>;
|
fn get_version(&self, meta: Self::Metadata) -> Result<RpcVersionInfo>;
|
||||||
|
|
||||||
@@ -813,6 +860,28 @@ impl RpcSol for RpcSolImpl {
|
|||||||
.get_recent_blockhash(commitment)
|
.get_recent_blockhash(commitment)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_fee_calculator_for_blockhash(
|
||||||
|
&self,
|
||||||
|
meta: Self::Metadata,
|
||||||
|
blockhash: String,
|
||||||
|
) -> RpcResponse<Option<RpcFeeCalculator>> {
|
||||||
|
debug!("get_fee_calculator_for_blockhash rpc request received");
|
||||||
|
let blockhash =
|
||||||
|
Hash::from_str(&blockhash).map_err(|e| Error::invalid_params(format!("{:?}", e)))?;
|
||||||
|
meta.request_processor
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get_fee_calculator_for_blockhash(&blockhash)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_fee_rate_governor(&self, meta: Self::Metadata) -> RpcResponse<RpcFeeRateGovernor> {
|
||||||
|
debug!("get_fee_rate_governor rpc request received");
|
||||||
|
meta.request_processor
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get_fee_rate_governor()
|
||||||
|
}
|
||||||
|
|
||||||
fn get_signature_status(
|
fn get_signature_status(
|
||||||
&self,
|
&self,
|
||||||
meta: Self::Metadata,
|
meta: Self::Metadata,
|
||||||
@@ -1054,15 +1123,29 @@ impl RpcSol for RpcSolImpl {
|
|||||||
meta.request_processor.read().unwrap().validator_exit()
|
meta.request_processor.read().unwrap().validator_exit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_identity(&self, meta: Self::Metadata) -> Result<RpcIdentity> {
|
||||||
|
Ok(RpcIdentity {
|
||||||
|
identity: meta
|
||||||
|
.request_processor
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.config
|
||||||
|
.identity_pubkey
|
||||||
|
.to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn get_version(&self, _: Self::Metadata) -> Result<RpcVersionInfo> {
|
fn get_version(&self, _: Self::Metadata) -> Result<RpcVersionInfo> {
|
||||||
Ok(RpcVersionInfo {
|
Ok(RpcVersionInfo {
|
||||||
solana_core: solana_clap_utils::version!().to_string(),
|
solana_core: solana_clap_utils::version!().to_string(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_log_filter(&self, _meta: Self::Metadata, filter: String) -> Result<()> {
|
fn set_log_filter(&self, meta: Self::Metadata, filter: String) -> Result<()> {
|
||||||
solana_logger::setup_with(&filter);
|
meta.request_processor
|
||||||
Ok(())
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.set_log_filter(filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_confirmed_block(
|
fn get_confirmed_block(
|
||||||
@@ -1247,6 +1330,7 @@ pub mod tests {
|
|||||||
let request_processor = Arc::new(RwLock::new(JsonRpcRequestProcessor::new(
|
let request_processor = Arc::new(RwLock::new(JsonRpcRequestProcessor::new(
|
||||||
JsonRpcConfig {
|
JsonRpcConfig {
|
||||||
enable_get_confirmed_block: true,
|
enable_get_confirmed_block: true,
|
||||||
|
identity_pubkey: *pubkey,
|
||||||
..JsonRpcConfig::default()
|
..JsonRpcConfig::default()
|
||||||
},
|
},
|
||||||
bank_forks.clone(),
|
bank_forks.clone(),
|
||||||
@@ -1770,8 +1854,80 @@ pub mod tests {
|
|||||||
"value":{
|
"value":{
|
||||||
"blockhash": blockhash.to_string(),
|
"blockhash": blockhash.to_string(),
|
||||||
"feeCalculator": {
|
"feeCalculator": {
|
||||||
"burnPercent": DEFAULT_BURN_PERCENT,
|
|
||||||
"lamportsPerSignature": 0,
|
"lamportsPerSignature": 0,
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
"id": 1
|
||||||
|
});
|
||||||
|
let expected: Response =
|
||||||
|
serde_json::from_value(expected).expect("expected response deserialization");
|
||||||
|
let result: Response = serde_json::from_str(&res.expect("actual response"))
|
||||||
|
.expect("actual response deserialization");
|
||||||
|
assert_eq!(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rpc_get_fee_calculator_for_blockhash() {
|
||||||
|
let bob_pubkey = Pubkey::new_rand();
|
||||||
|
let RpcHandler { io, meta, bank, .. } = start_rpc_handler_with_tx(&bob_pubkey);
|
||||||
|
|
||||||
|
let (blockhash, fee_calculator) = bank.last_blockhash_with_fee_calculator();
|
||||||
|
let fee_calculator = RpcFeeCalculator { fee_calculator };
|
||||||
|
|
||||||
|
let req = format!(
|
||||||
|
r#"{{"jsonrpc":"2.0","id":1,"method":"getFeeCalculatorForBlockhash","params":["{:?}"]}}"#,
|
||||||
|
blockhash
|
||||||
|
);
|
||||||
|
let res = io.handle_request_sync(&req, meta.clone());
|
||||||
|
let expected = json!({
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"result": {
|
||||||
|
"context":{"slot":0},
|
||||||
|
"value":fee_calculator,
|
||||||
|
},
|
||||||
|
"id": 1
|
||||||
|
});
|
||||||
|
let expected: Response =
|
||||||
|
serde_json::from_value(expected).expect("expected response deserialization");
|
||||||
|
let result: Response = serde_json::from_str(&res.expect("actual response"))
|
||||||
|
.expect("actual response deserialization");
|
||||||
|
assert_eq!(expected, result);
|
||||||
|
|
||||||
|
// Expired (non-existent) blockhash
|
||||||
|
let req = format!(
|
||||||
|
r#"{{"jsonrpc":"2.0","id":1,"method":"getFeeCalculatorForBlockhash","params":["{:?}"]}}"#,
|
||||||
|
Hash::default()
|
||||||
|
);
|
||||||
|
let res = io.handle_request_sync(&req, meta.clone());
|
||||||
|
let expected = json!({
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"result": {
|
||||||
|
"context":{"slot":0},
|
||||||
|
"value":Value::Null,
|
||||||
|
},
|
||||||
|
"id": 1
|
||||||
|
});
|
||||||
|
let expected: Response =
|
||||||
|
serde_json::from_value(expected).expect("expected response deserialization");
|
||||||
|
let result: Response = serde_json::from_str(&res.expect("actual response"))
|
||||||
|
.expect("actual response deserialization");
|
||||||
|
assert_eq!(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rpc_get_fee_rate_governor() {
|
||||||
|
let bob_pubkey = Pubkey::new_rand();
|
||||||
|
let RpcHandler { io, meta, .. } = start_rpc_handler_with_tx(&bob_pubkey);
|
||||||
|
|
||||||
|
let req = format!(r#"{{"jsonrpc":"2.0","id":1,"method":"getFeeRateGovernor"}}"#);
|
||||||
|
let res = io.handle_request_sync(&req, meta);
|
||||||
|
let expected = json!({
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"result": {
|
||||||
|
"context":{"slot":0},
|
||||||
|
"value":{
|
||||||
|
"feeRateGovernor": {
|
||||||
|
"burnPercent": DEFAULT_BURN_PERCENT,
|
||||||
"maxLamportsPerSignature": 0,
|
"maxLamportsPerSignature": 0,
|
||||||
"minLamportsPerSignature": 0,
|
"minLamportsPerSignature": 0,
|
||||||
"targetLamportsPerSignature": 0,
|
"targetLamportsPerSignature": 0,
|
||||||
@@ -1950,6 +2106,27 @@ pub mod tests {
|
|||||||
assert_eq!(exit.load(Ordering::Relaxed), true);
|
assert_eq!(exit.load(Ordering::Relaxed), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rpc_get_identity() {
|
||||||
|
let bob_pubkey = Pubkey::new_rand();
|
||||||
|
let RpcHandler { io, meta, .. } = start_rpc_handler_with_tx(&bob_pubkey);
|
||||||
|
|
||||||
|
let req = format!(r#"{{"jsonrpc":"2.0","id":1,"method":"getIdentity"}}"#);
|
||||||
|
let res = io.handle_request_sync(&req, meta);
|
||||||
|
let expected = json!({
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"result": {
|
||||||
|
"identity": bob_pubkey.to_string()
|
||||||
|
},
|
||||||
|
"id": 1
|
||||||
|
});
|
||||||
|
let expected: Response =
|
||||||
|
serde_json::from_value(expected).expect("expected response deserialization");
|
||||||
|
let result: Response = serde_json::from_str(&res.expect("actual response"))
|
||||||
|
.expect("actual response deserialization");
|
||||||
|
assert_eq!(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rpc_get_version() {
|
fn test_rpc_get_version() {
|
||||||
let bob_pubkey = Pubkey::new_rand();
|
let bob_pubkey = Pubkey::new_rand();
|
||||||
|
@@ -262,9 +262,10 @@ mod tests {
|
|||||||
let cluster_info = Arc::new(RwLock::new(ClusterInfo::new_with_invalid_keypair(
|
let cluster_info = Arc::new(RwLock::new(ClusterInfo::new_with_invalid_keypair(
|
||||||
ContactInfo::default(),
|
ContactInfo::default(),
|
||||||
)));
|
)));
|
||||||
|
let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
|
||||||
let rpc_addr = SocketAddr::new(
|
let rpc_addr = SocketAddr::new(
|
||||||
IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
|
ip_addr,
|
||||||
solana_net_utils::find_available_port_in_range((10000, 65535)).unwrap(),
|
solana_net_utils::find_available_port_in_range(ip_addr, (10000, 65535)).unwrap(),
|
||||||
);
|
);
|
||||||
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank.slot(), bank)));
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank.slot(), bank)));
|
||||||
let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::default()));
|
let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::default()));
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
use crate::cluster_info::{ClusterInfo, MAX_SNAPSHOT_HASHES};
|
use crate::cluster_info::{ClusterInfo, MAX_SNAPSHOT_HASHES};
|
||||||
use solana_ledger::{
|
use solana_ledger::{snapshot_package::SnapshotPackageReceiver, snapshot_utils};
|
||||||
snapshot_package::SnapshotPackageReceiver, snapshot_utils::archive_snapshot_package,
|
use solana_sdk::{clock::Slot, hash::Hash};
|
||||||
};
|
|
||||||
use std::{
|
use std::{
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicBool, Ordering},
|
atomic::{AtomicBool, Ordering},
|
||||||
@@ -19,15 +18,24 @@ pub struct SnapshotPackagerService {
|
|||||||
impl SnapshotPackagerService {
|
impl SnapshotPackagerService {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
snapshot_package_receiver: SnapshotPackageReceiver,
|
snapshot_package_receiver: SnapshotPackageReceiver,
|
||||||
|
starting_snapshot_hash: Option<(Slot, Hash)>,
|
||||||
exit: &Arc<AtomicBool>,
|
exit: &Arc<AtomicBool>,
|
||||||
cluster_info: &Arc<RwLock<ClusterInfo>>,
|
cluster_info: &Arc<RwLock<ClusterInfo>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let exit = exit.clone();
|
let exit = exit.clone();
|
||||||
let cluster_info = cluster_info.clone();
|
let cluster_info = cluster_info.clone();
|
||||||
|
|
||||||
let t_snapshot_packager = Builder::new()
|
let t_snapshot_packager = Builder::new()
|
||||||
.name("solana-snapshot-packager".to_string())
|
.name("solana-snapshot-packager".to_string())
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
let mut hashes = vec![];
|
let mut hashes = vec![];
|
||||||
|
if let Some(starting_snapshot_hash) = starting_snapshot_hash {
|
||||||
|
hashes.push(starting_snapshot_hash);
|
||||||
|
}
|
||||||
|
cluster_info
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.push_snapshot_hashes(hashes.clone());
|
||||||
loop {
|
loop {
|
||||||
if exit.load(Ordering::Relaxed) {
|
if exit.load(Ordering::Relaxed) {
|
||||||
break;
|
break;
|
||||||
@@ -35,17 +43,18 @@ impl SnapshotPackagerService {
|
|||||||
|
|
||||||
match snapshot_package_receiver.recv_timeout(Duration::from_secs(1)) {
|
match snapshot_package_receiver.recv_timeout(Duration::from_secs(1)) {
|
||||||
Ok(mut snapshot_package) => {
|
Ok(mut snapshot_package) => {
|
||||||
hashes.push((snapshot_package.root, snapshot_package.hash));
|
|
||||||
// Only package the latest
|
// Only package the latest
|
||||||
while let Ok(new_snapshot_package) =
|
while let Ok(new_snapshot_package) =
|
||||||
snapshot_package_receiver.try_recv()
|
snapshot_package_receiver.try_recv()
|
||||||
{
|
{
|
||||||
snapshot_package = new_snapshot_package;
|
snapshot_package = new_snapshot_package;
|
||||||
hashes.push((snapshot_package.root, snapshot_package.hash));
|
|
||||||
}
|
}
|
||||||
if let Err(err) = archive_snapshot_package(&snapshot_package) {
|
if let Err(err) =
|
||||||
|
snapshot_utils::archive_snapshot_package(&snapshot_package)
|
||||||
|
{
|
||||||
warn!("Failed to create snapshot archive: {}", err);
|
warn!("Failed to create snapshot archive: {}", err);
|
||||||
}
|
} else {
|
||||||
|
hashes.push((snapshot_package.root, snapshot_package.hash));
|
||||||
while hashes.len() > MAX_SNAPSHOT_HASHES {
|
while hashes.len() > MAX_SNAPSHOT_HASHES {
|
||||||
hashes.remove(0);
|
hashes.remove(0);
|
||||||
}
|
}
|
||||||
@@ -54,6 +63,7 @@ impl SnapshotPackagerService {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.push_snapshot_hashes(hashes.clone());
|
.push_snapshot_hashes(hashes.clone());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Err(RecvTimeoutError::Disconnected) => break,
|
Err(RecvTimeoutError::Disconnected) => break,
|
||||||
Err(RecvTimeoutError::Timeout) => (),
|
Err(RecvTimeoutError::Timeout) => (),
|
||||||
}
|
}
|
||||||
|
@@ -133,7 +133,7 @@ pub fn responder(name: &'static str, sock: Arc<UdpSocket>, r: PacketReceiver) ->
|
|||||||
match e {
|
match e {
|
||||||
StreamerError::RecvTimeoutError(RecvTimeoutError::Disconnected) => break,
|
StreamerError::RecvTimeoutError(RecvTimeoutError::Disconnected) => break,
|
||||||
StreamerError::RecvTimeoutError(RecvTimeoutError::Timeout) => (),
|
StreamerError::RecvTimeoutError(RecvTimeoutError::Timeout) => (),
|
||||||
_ => warn!("{} responder error: {:?}", name, e),
|
_ => info!("{} responder error: {:?}", name, e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@@ -1,7 +1,10 @@
|
|||||||
use crossbeam_channel::{Receiver, RecvTimeoutError};
|
use crossbeam_channel::{Receiver, RecvTimeoutError};
|
||||||
use solana_client::rpc_response::RpcTransactionStatus;
|
use solana_client::rpc_response::RpcTransactionStatus;
|
||||||
use solana_ledger::{blockstore::Blockstore, blockstore_processor::TransactionStatusBatch};
|
use solana_ledger::{blockstore::Blockstore, blockstore_processor::TransactionStatusBatch};
|
||||||
use solana_runtime::bank::{Bank, HashAgeKind};
|
use solana_runtime::{
|
||||||
|
bank::{Bank, HashAgeKind},
|
||||||
|
nonce_utils,
|
||||||
|
};
|
||||||
use std::{
|
use std::{
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicBool, Ordering},
|
atomic::{AtomicBool, Ordering},
|
||||||
@@ -59,13 +62,12 @@ impl TransactionStatusService {
|
|||||||
.zip(balances.post_balances)
|
.zip(balances.post_balances)
|
||||||
{
|
{
|
||||||
if Bank::can_commit(&status) && !transaction.signatures.is_empty() {
|
if Bank::can_commit(&status) && !transaction.signatures.is_empty() {
|
||||||
let fee_hash = if let Some(HashAgeKind::DurableNonce(_, _)) = hash_age_kind {
|
let fee_calculator = match hash_age_kind {
|
||||||
bank.last_blockhash()
|
Some(HashAgeKind::DurableNonce(_, account)) => {
|
||||||
} else {
|
nonce_utils::fee_calculator_of(&account)
|
||||||
transaction.message().recent_blockhash
|
}
|
||||||
};
|
_ => bank.get_fee_calculator(&transaction.message().recent_blockhash),
|
||||||
let fee_calculator = bank
|
}
|
||||||
.get_fee_calculator(&fee_hash)
|
|
||||||
.expect("FeeCalculator must exist");
|
.expect("FeeCalculator must exist");
|
||||||
let fee = fee_calculator.calculate_fee(transaction.message());
|
let fee = fee_calculator.calculate_fee(transaction.message());
|
||||||
blockstore
|
blockstore
|
||||||
|
@@ -14,7 +14,6 @@ use crate::{
|
|||||||
shred_fetch_stage::ShredFetchStage,
|
shred_fetch_stage::ShredFetchStage,
|
||||||
sigverify_shreds::ShredSigVerifier,
|
sigverify_shreds::ShredSigVerifier,
|
||||||
sigverify_stage::{DisabledSigVerifier, SigVerifyStage},
|
sigverify_stage::{DisabledSigVerifier, SigVerifyStage},
|
||||||
snapshot_packager_service::SnapshotPackagerService,
|
|
||||||
storage_stage::{StorageStage, StorageState},
|
storage_stage::{StorageStage, StorageState},
|
||||||
};
|
};
|
||||||
use crossbeam_channel::unbounded;
|
use crossbeam_channel::unbounded;
|
||||||
@@ -23,6 +22,7 @@ use solana_ledger::{
|
|||||||
bank_forks::BankForks,
|
bank_forks::BankForks,
|
||||||
blockstore::{Blockstore, CompletedSlotsReceiver},
|
blockstore::{Blockstore, CompletedSlotsReceiver},
|
||||||
blockstore_processor::TransactionStatusSender,
|
blockstore_processor::TransactionStatusSender,
|
||||||
|
snapshot_package::SnapshotPackageSender,
|
||||||
};
|
};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
@@ -47,7 +47,6 @@ pub struct Tvu {
|
|||||||
blockstream_service: Option<BlockstreamService>,
|
blockstream_service: Option<BlockstreamService>,
|
||||||
ledger_cleanup_service: Option<LedgerCleanupService>,
|
ledger_cleanup_service: Option<LedgerCleanupService>,
|
||||||
storage_stage: StorageStage,
|
storage_stage: StorageStage,
|
||||||
snapshot_packager_service: Option<SnapshotPackagerService>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Sockets {
|
pub struct Sockets {
|
||||||
@@ -88,6 +87,7 @@ impl Tvu {
|
|||||||
shred_version: u16,
|
shred_version: u16,
|
||||||
transaction_status_sender: Option<TransactionStatusSender>,
|
transaction_status_sender: Option<TransactionStatusSender>,
|
||||||
rewards_recorder_sender: Option<RewardsRecorderSender>,
|
rewards_recorder_sender: Option<RewardsRecorderSender>,
|
||||||
|
snapshot_package_sender: Option<SnapshotPackageSender>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let keypair: Arc<Keypair> = cluster_info
|
let keypair: Arc<Keypair> = cluster_info
|
||||||
.read()
|
.read()
|
||||||
@@ -148,18 +148,6 @@ impl Tvu {
|
|||||||
|
|
||||||
let (blockstream_slot_sender, blockstream_slot_receiver) = channel();
|
let (blockstream_slot_sender, blockstream_slot_receiver) = channel();
|
||||||
let (ledger_cleanup_slot_sender, ledger_cleanup_slot_receiver) = channel();
|
let (ledger_cleanup_slot_sender, ledger_cleanup_slot_receiver) = channel();
|
||||||
let (snapshot_packager_service, snapshot_package_sender) = {
|
|
||||||
let snapshot_config = { bank_forks.read().unwrap().snapshot_config().clone() };
|
|
||||||
if snapshot_config.is_some() {
|
|
||||||
// Start a snapshot packaging service
|
|
||||||
let (sender, receiver) = channel();
|
|
||||||
let snapshot_packager_service =
|
|
||||||
SnapshotPackagerService::new(receiver, exit, &cluster_info.clone());
|
|
||||||
(Some(snapshot_packager_service), Some(sender))
|
|
||||||
} else {
|
|
||||||
(None, None)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let replay_stage_config = ReplayStageConfig {
|
let replay_stage_config = ReplayStageConfig {
|
||||||
my_pubkey: keypair.pubkey(),
|
my_pubkey: keypair.pubkey(),
|
||||||
@@ -225,7 +213,6 @@ impl Tvu {
|
|||||||
blockstream_service,
|
blockstream_service,
|
||||||
ledger_cleanup_service,
|
ledger_cleanup_service,
|
||||||
storage_stage,
|
storage_stage,
|
||||||
snapshot_packager_service,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,9 +228,6 @@ impl Tvu {
|
|||||||
self.ledger_cleanup_service.unwrap().join()?;
|
self.ledger_cleanup_service.unwrap().join()?;
|
||||||
}
|
}
|
||||||
self.replay_stage.join()?;
|
self.replay_stage.join()?;
|
||||||
if let Some(s) = self.snapshot_packager_service {
|
|
||||||
s.join()?;
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -317,6 +301,7 @@ pub mod tests {
|
|||||||
0,
|
0,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
exit.store(true, Ordering::Relaxed);
|
exit.store(true, Ordering::Relaxed);
|
||||||
tvu.join().unwrap();
|
tvu.join().unwrap();
|
||||||
|
@@ -16,6 +16,7 @@ use crate::{
|
|||||||
serve_repair::ServeRepair,
|
serve_repair::ServeRepair,
|
||||||
serve_repair_service::ServeRepairService,
|
serve_repair_service::ServeRepairService,
|
||||||
sigverify,
|
sigverify,
|
||||||
|
snapshot_packager_service::SnapshotPackagerService,
|
||||||
storage_stage::StorageState,
|
storage_stage::StorageState,
|
||||||
tpu::Tpu,
|
tpu::Tpu,
|
||||||
transaction_status_service::TransactionStatusService,
|
transaction_status_service::TransactionStatusService,
|
||||||
@@ -50,7 +51,7 @@ use std::{
|
|||||||
process,
|
process,
|
||||||
sync::atomic::{AtomicBool, Ordering},
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
sync::mpsc::Receiver,
|
sync::mpsc::Receiver,
|
||||||
sync::{Arc, Mutex, RwLock},
|
sync::{mpsc::channel, Arc, Mutex, RwLock},
|
||||||
thread::{sleep, Result},
|
thread::{sleep, Result},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
@@ -72,7 +73,7 @@ pub struct ValidatorConfig {
|
|||||||
pub broadcast_stage_type: BroadcastStageType,
|
pub broadcast_stage_type: BroadcastStageType,
|
||||||
pub enable_partition: Option<Arc<AtomicBool>>,
|
pub enable_partition: Option<Arc<AtomicBool>>,
|
||||||
pub fixed_leader_schedule: Option<FixedSchedule>,
|
pub fixed_leader_schedule: Option<FixedSchedule>,
|
||||||
pub wait_for_supermajority: bool,
|
pub wait_for_supermajority: Option<Slot>,
|
||||||
pub new_hard_forks: Option<Vec<Slot>>,
|
pub new_hard_forks: Option<Vec<Slot>>,
|
||||||
pub trusted_validators: Option<HashSet<Pubkey>>, // None = trust all
|
pub trusted_validators: Option<HashSet<Pubkey>>, // None = trust all
|
||||||
}
|
}
|
||||||
@@ -95,7 +96,7 @@ impl Default for ValidatorConfig {
|
|||||||
broadcast_stage_type: BroadcastStageType::Standard,
|
broadcast_stage_type: BroadcastStageType::Standard,
|
||||||
enable_partition: None,
|
enable_partition: None,
|
||||||
fixed_leader_schedule: None,
|
fixed_leader_schedule: None,
|
||||||
wait_for_supermajority: false,
|
wait_for_supermajority: None,
|
||||||
new_hard_forks: None,
|
new_hard_forks: None,
|
||||||
trusted_validators: None,
|
trusted_validators: None,
|
||||||
}
|
}
|
||||||
@@ -127,6 +128,7 @@ pub struct Validator {
|
|||||||
rewards_recorder_service: Option<RewardsRecorderService>,
|
rewards_recorder_service: Option<RewardsRecorderService>,
|
||||||
gossip_service: GossipService,
|
gossip_service: GossipService,
|
||||||
serve_repair_service: ServeRepairService,
|
serve_repair_service: ServeRepairService,
|
||||||
|
snapshot_packager_service: Option<SnapshotPackagerService>,
|
||||||
poh_recorder: Arc<Mutex<PohRecorder>>,
|
poh_recorder: Arc<Mutex<PohRecorder>>,
|
||||||
poh_service: PohService,
|
poh_service: PohService,
|
||||||
tpu: Tpu,
|
tpu: Tpu,
|
||||||
@@ -355,39 +357,16 @@ impl Validator {
|
|||||||
.set_entrypoint(entrypoint_info.clone());
|
.set_entrypoint(entrypoint_info.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(snapshot_hash) = snapshot_hash {
|
let (snapshot_packager_service, snapshot_package_sender) =
|
||||||
if let Some(ref trusted_validators) = config.trusted_validators {
|
if config.snapshot_config.is_some() {
|
||||||
let mut trusted = false;
|
// Start a snapshot packaging service
|
||||||
for _ in 0..10 {
|
let (sender, receiver) = channel();
|
||||||
trusted = cluster_info
|
let snapshot_packager_service =
|
||||||
.read()
|
SnapshotPackagerService::new(receiver, snapshot_hash, &exit, &cluster_info);
|
||||||
.unwrap()
|
(Some(snapshot_packager_service), Some(sender))
|
||||||
.get_snapshot_hash(snapshot_hash.0)
|
} else {
|
||||||
.iter()
|
(None, None)
|
||||||
.any(|(pubkey, hash)| {
|
};
|
||||||
trusted_validators.contains(pubkey) && snapshot_hash.1 == *hash
|
|
||||||
});
|
|
||||||
if trusted {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
sleep(Duration::from_secs(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
if !trusted {
|
|
||||||
error!(
|
|
||||||
"The snapshot hash for slot {} is not published by your trusted validators: {:?}",
|
|
||||||
snapshot_hash.0, trusted_validators
|
|
||||||
);
|
|
||||||
process::exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the node was loaded from a snapshot, advertise it in gossip
|
|
||||||
cluster_info
|
|
||||||
.write()
|
|
||||||
.unwrap()
|
|
||||||
.push_snapshot_hashes(vec![snapshot_hash]);
|
|
||||||
}
|
|
||||||
|
|
||||||
wait_for_supermajority(config, &bank, &cluster_info);
|
wait_for_supermajority(config, &bank, &cluster_info);
|
||||||
|
|
||||||
@@ -451,6 +430,7 @@ impl Validator {
|
|||||||
node.info.shred_version,
|
node.info.shred_version,
|
||||||
transaction_status_sender.clone(),
|
transaction_status_sender.clone(),
|
||||||
rewards_recorder_sender,
|
rewards_recorder_sender,
|
||||||
|
snapshot_package_sender,
|
||||||
);
|
);
|
||||||
|
|
||||||
if config.dev_sigverify_disabled {
|
if config.dev_sigverify_disabled {
|
||||||
@@ -480,6 +460,7 @@ impl Validator {
|
|||||||
rpc_service,
|
rpc_service,
|
||||||
transaction_status_service,
|
transaction_status_service,
|
||||||
rewards_recorder_service,
|
rewards_recorder_service,
|
||||||
|
snapshot_packager_service,
|
||||||
tpu,
|
tpu,
|
||||||
tvu,
|
tvu,
|
||||||
poh_service,
|
poh_service,
|
||||||
@@ -541,6 +522,10 @@ impl Validator {
|
|||||||
rewards_recorder_service.join()?;
|
rewards_recorder_service.join()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(s) = self.snapshot_packager_service {
|
||||||
|
s.join()?;
|
||||||
|
}
|
||||||
|
|
||||||
self.gossip_service.join()?;
|
self.gossip_service.join()?;
|
||||||
self.serve_repair_service.join()?;
|
self.serve_repair_service.join()?;
|
||||||
self.tpu.join()?;
|
self.tpu.join()?;
|
||||||
@@ -637,19 +622,19 @@ fn wait_for_supermajority(
|
|||||||
bank: &Arc<Bank>,
|
bank: &Arc<Bank>,
|
||||||
cluster_info: &Arc<RwLock<ClusterInfo>>,
|
cluster_info: &Arc<RwLock<ClusterInfo>>,
|
||||||
) {
|
) {
|
||||||
if !config.wait_for_supermajority {
|
if config.wait_for_supermajority != Some(bank.slot()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"Waiting for more than 75% of activated stake at slot {} to be in gossip...",
|
"Waiting for 80% of activated stake at slot {} to be in gossip...",
|
||||||
bank.slot()
|
bank.slot()
|
||||||
);
|
);
|
||||||
loop {
|
loop {
|
||||||
let gossip_stake_percent = get_stake_percent_in_gossip(&bank, &cluster_info);
|
let gossip_stake_percent = get_stake_percent_in_gossip(&bank, &cluster_info);
|
||||||
|
|
||||||
info!("{}% of activated stake in gossip", gossip_stake_percent,);
|
info!("{}% of activated stake in gossip", gossip_stake_percent,);
|
||||||
if gossip_stake_percent > 75 {
|
if gossip_stake_percent >= 80 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
sleep(Duration::new(1, 0));
|
sleep(Duration::new(1, 0));
|
||||||
@@ -687,7 +672,7 @@ impl TestValidator {
|
|||||||
|
|
||||||
pub fn run_with_options(options: TestValidatorOptions) -> Self {
|
pub fn run_with_options(options: TestValidatorOptions) -> Self {
|
||||||
use crate::genesis_utils::{create_genesis_config_with_leader_ex, GenesisConfigInfo};
|
use crate::genesis_utils::{create_genesis_config_with_leader_ex, GenesisConfigInfo};
|
||||||
use solana_sdk::fee_calculator::FeeCalculator;
|
use solana_sdk::fee_calculator::FeeRateGovernor;
|
||||||
|
|
||||||
let TestValidatorOptions {
|
let TestValidatorOptions {
|
||||||
fees,
|
fees,
|
||||||
@@ -713,7 +698,7 @@ impl TestValidator {
|
|||||||
|
|
||||||
genesis_config.rent.lamports_per_byte_year = 1;
|
genesis_config.rent.lamports_per_byte_year = 1;
|
||||||
genesis_config.rent.exemption_threshold = 1.0;
|
genesis_config.rent.exemption_threshold = 1.0;
|
||||||
genesis_config.fee_calculator = FeeCalculator::new(fees, 0);
|
genesis_config.fee_rate_governor = FeeRateGovernor::new(fees, 0);
|
||||||
|
|
||||||
let (ledger_path, blockhash) = create_new_tmp_ledger!(&genesis_config);
|
let (ledger_path, blockhash) = create_new_tmp_ledger!(&genesis_config);
|
||||||
|
|
||||||
|
@@ -317,7 +317,7 @@ mod tests {
|
|||||||
)));
|
)));
|
||||||
|
|
||||||
let snapshot_packager_service =
|
let snapshot_packager_service =
|
||||||
SnapshotPackagerService::new(receiver, &exit, &cluster_info);
|
SnapshotPackagerService::new(receiver, None, &exit, &cluster_info);
|
||||||
|
|
||||||
// Close the channel so that the package service will exit after reading all the
|
// Close the channel so that the package service will exit after reading all the
|
||||||
// packages off the channel
|
// packages off the channel
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-crate-features"
|
name = "solana-crate-features"
|
||||||
version = "1.0.1"
|
version = "1.0.5"
|
||||||
description = "Solana Crate Features"
|
description = "Solana Crate Features"
|
||||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
|
11
docs/offline-cmd-md-links.sh
Executable file
11
docs/offline-cmd-md-links.sh
Executable file
@@ -0,0 +1,11 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
CLI_USAGE_RELPATH="../cli/usage.md"
|
||||||
|
|
||||||
|
SED_OMIT_NONMATCHING=$'\nt\nd'
|
||||||
|
SED_CMD="s:^#### solana-(.*):* [\`\\1\`](${CLI_USAGE_RELPATH}#solana-\\1):${SED_OMIT_NONMATCHING}"
|
||||||
|
|
||||||
|
OFFLINE_CMDS=$(grep -E '#### solana-|--signer ' src/cli/usage.md | grep -B1 -- --signer | sed -Ee "$SED_CMD")
|
||||||
|
|
||||||
|
# Omit deprecated
|
||||||
|
grep -vE '\b(pay)\b' <<<"$OFFLINE_CMDS"
|
@@ -1,12 +1,12 @@
|
|||||||
# Table of contents
|
# Table of contents
|
||||||
|
|
||||||
* [Introduction](introduction.md)
|
* [Introduction](introduction.md)
|
||||||
|
* [Installing Solana](install-solana.md)
|
||||||
* [Using Solana from the Command-line](cli/README.md)
|
* [Using Solana from the Command-line](cli/README.md)
|
||||||
* [Command-line Usage](cli/usage.md)
|
* [Command-line Usage](cli/usage.md)
|
||||||
* [Remote Wallet](remote-wallet/README.md)
|
* [Remote Wallet](remote-wallet/README.md)
|
||||||
* [Ledger Hardware Wallet](remote-wallet/ledger.md)
|
* [Ledger Hardware Wallet](remote-wallet/ledger.md)
|
||||||
* [Paper Wallet](paper-wallet/README.md)
|
* [Paper Wallet](paper-wallet/README.md)
|
||||||
* [Installation](paper-wallet/installation.md)
|
|
||||||
* [Paper Wallet Usage](paper-wallet/usage.md)
|
* [Paper Wallet Usage](paper-wallet/usage.md)
|
||||||
* [Offline Signing](offline-signing/README.md)
|
* [Offline Signing](offline-signing/README.md)
|
||||||
* [Durable Transaction Nonces](offline-signing/durable-nonce.md)
|
* [Durable Transaction Nonces](offline-signing/durable-nonce.md)
|
||||||
@@ -41,13 +41,11 @@
|
|||||||
* [Running a Validator](running-validator/README.md)
|
* [Running a Validator](running-validator/README.md)
|
||||||
* [Validator Requirements](running-validator/validator-reqs.md)
|
* [Validator Requirements](running-validator/validator-reqs.md)
|
||||||
* [Choosing a Testnet](running-validator/validator-testnet.md)
|
* [Choosing a Testnet](running-validator/validator-testnet.md)
|
||||||
* [Installing the Validator Software](running-validator/validator-software.md)
|
|
||||||
* [Starting a Validator](running-validator/validator-start.md)
|
* [Starting a Validator](running-validator/validator-start.md)
|
||||||
* [Staking](running-validator/validator-stake.md)
|
* [Staking](running-validator/validator-stake.md)
|
||||||
* [Monitoring a Validator](running-validator/validator-monitor.md)
|
* [Monitoring a Validator](running-validator/validator-monitor.md)
|
||||||
* [Publishing Validator Info](running-validator/validator-info.md)
|
* [Publishing Validator Info](running-validator/validator-info.md)
|
||||||
* [Troubleshooting](running-validator/validator-troubleshoot.md)
|
* [Troubleshooting](running-validator/validator-troubleshoot.md)
|
||||||
* [Running an Archiver](running-archiver.md)
|
|
||||||
* [Understanding Solana's Architecture](cluster/README.md)
|
* [Understanding Solana's Architecture](cluster/README.md)
|
||||||
* [Synchronization](cluster/synchronization.md)
|
* [Synchronization](cluster/synchronization.md)
|
||||||
* [Leader Rotation](cluster/leader-rotation.md)
|
* [Leader Rotation](cluster/leader-rotation.md)
|
||||||
|
@@ -24,7 +24,10 @@ To interact with a Solana node inside a JavaScript application, use the [solana-
|
|||||||
* [getConfirmedBlocks](jsonrpc-api.md#getconfirmedblocks)
|
* [getConfirmedBlocks](jsonrpc-api.md#getconfirmedblocks)
|
||||||
* [getEpochInfo](jsonrpc-api.md#getepochinfo)
|
* [getEpochInfo](jsonrpc-api.md#getepochinfo)
|
||||||
* [getEpochSchedule](jsonrpc-api.md#getepochschedule)
|
* [getEpochSchedule](jsonrpc-api.md#getepochschedule)
|
||||||
|
* [getFeeCalculatorForBlockhash](jsonrpc-api.md#getfeecalculatorforblockhash)
|
||||||
|
* [getFeeRateGovernor](jsonrpc-api.md#getfeerategovernor)
|
||||||
* [getGenesisHash](jsonrpc-api.md#getgenesishash)
|
* [getGenesisHash](jsonrpc-api.md#getgenesishash)
|
||||||
|
* [getIdentity](jsonrpc-api.md#getidentity)
|
||||||
* [getInflation](jsonrpc-api.md#getinflation)
|
* [getInflation](jsonrpc-api.md#getinflation)
|
||||||
* [getLeaderSchedule](jsonrpc-api.md#getleaderschedule)
|
* [getLeaderSchedule](jsonrpc-api.md#getleaderschedule)
|
||||||
* [getMinimumBalanceForRentExemption](jsonrpc-api.md#getminimumbalanceforrentexemption)
|
* [getMinimumBalanceForRentExemption](jsonrpc-api.md#getminimumbalanceforrentexemption)
|
||||||
@@ -314,13 +317,13 @@ The result field will be an object with the following fields:
|
|||||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedBlock","params":[430, "json"]}' localhost:8899
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedBlock","params":[430, "json"]}' localhost:8899
|
||||||
|
|
||||||
// Result
|
// Result
|
||||||
{"jsonrpc":"2.0","result":{"blockhash":"Gp3t5bfDsJv1ovP8cB1SuRhXVuoTqDv7p3tymyubYg5","parentSlot":429,"previousBlockhash":"EFejToxii1L5aUF2NrK9dsbAEmZSNyN5nsipmZHQR1eA","transactions":[{"transaction":{"message":{"accountKeys":["6H94zdiaYfRfPfKjYLjyr2VFBg6JHXygy84r3qhc3NsC","39UAy8hsoYPywGPGdmun747omSr79zLSjqvPJN3zetoH","SysvarS1otHashes111111111111111111111111111","SysvarC1ock11111111111111111111111111111111","Vote111111111111111111111111111111111111111"],"header":{"numReadonlySignedAccounts":0,"numReadonlyUnsignedAccounts":3,"numRequiredSignatures":2},"instructions":[{"accounts":[1,2,3],"data":"29z5mr1JoRmJYQ6ynmk3pf31cGFRziAF1M3mT3L6sFXf5cKLdkEaMXMT8AqLpD4CpcupHmuMEmtZHpomrwfdZetSomNy3d","programIdIndex":4}],"recentBlockhash":"EFejToxii1L5aUF2NrK9dsbAEmZSNyN5nsipmZHQR1eA"},"signatures":["35YGay1Lwjwgxe9zaH6APSHbt9gYQUCtBWTNL3aVwVGn9xTFw2fgds7qK5AL29mP63A9j3rh8KpN1TgSR62XCaby","4vANMjSKiwEchGSXwVrQkwHnmsbKQmy9vdrsYxWdCup1bLsFzX8gKrFTSVDCZCae2dbxJB9mPNhqB2sD1vvr4sAD"]},"meta":{"fee":1.0.1,"postBalances":[499999972500,15298080,1,1,1],"preBalances":[499999990500,15298080,1,1,1],"status":{"Ok":null}}}]},"id":1}
|
{"jsonrpc":"2.0","result":{"blockhash":"Gp3t5bfDsJv1ovP8cB1SuRhXVuoTqDv7p3tymyubYg5","parentSlot":429,"previousBlockhash":"EFejToxii1L5aUF2NrK9dsbAEmZSNyN5nsipmZHQR1eA","transactions":[{"transaction":{"message":{"accountKeys":["6H94zdiaYfRfPfKjYLjyr2VFBg6JHXygy84r3qhc3NsC","39UAy8hsoYPywGPGdmun747omSr79zLSjqvPJN3zetoH","SysvarS1otHashes111111111111111111111111111","SysvarC1ock11111111111111111111111111111111","Vote111111111111111111111111111111111111111"],"header":{"numReadonlySignedAccounts":0,"numReadonlyUnsignedAccounts":3,"numRequiredSignatures":2},"instructions":[{"accounts":[1,2,3],"data":"29z5mr1JoRmJYQ6ynmk3pf31cGFRziAF1M3mT3L6sFXf5cKLdkEaMXMT8AqLpD4CpcupHmuMEmtZHpomrwfdZetSomNy3d","programIdIndex":4}],"recentBlockhash":"EFejToxii1L5aUF2NrK9dsbAEmZSNyN5nsipmZHQR1eA"},"signatures":["35YGay1Lwjwgxe9zaH6APSHbt9gYQUCtBWTNL3aVwVGn9xTFw2fgds7qK5AL29mP63A9j3rh8KpN1TgSR62XCaby","4vANMjSKiwEchGSXwVrQkwHnmsbKQmy9vdrsYxWdCup1bLsFzX8gKrFTSVDCZCae2dbxJB9mPNhqB2sD1vvr4sAD"]},"meta":{"fee":1.0.5,"postBalances":[499999972500,15298080,1,1,1],"preBalances":[499999990500,15298080,1,1,1],"status":{"Ok":null}}}]},"id":1}
|
||||||
|
|
||||||
// Request
|
// Request
|
||||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedBlock","params":[430, "binary"]}' localhost:8899
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedBlock","params":[430, "binary"]}' localhost:8899
|
||||||
|
|
||||||
// Result
|
// Result
|
||||||
{"jsonrpc":"2.0","result":{"blockhash":"Gp3t5bfDsJv1ovP8cB1SuRhXVuoTqDv7p3tymyubYg5","parentSlot":429,"previousBlockhash":"EFejToxii1L5aUF2NrK9dsbAEmZSNyN5nsipmZHQR1eA","transactions":[{"transaction":"81UZJt4dh4Do66jDhrgkQudS8J2N6iG3jaVav7gJrqJSFY4Ug53iA9JFJZh2gxKWcaFdLJwhHx9mRdg9JwDAWB4ywiu5154CRwXV4FMdnPLg7bhxRLwhhYaLsVgMF5AyNRcTzjCVoBvqFgDU7P8VEKDEiMvD3qxzm1pLZVxDG1LTQpT3Dz4Uviv4KQbFQNuC22KupBoyHFB7Zh6KFdMqux4M9PvhoqcoJsJKwXjWpKu7xmEKnnrSbfLadkgjBmmjhW3fdTrFvnhQdTkhtdJxUL1xS9GMuJQer8YgSKNtUXB1eXZQwXU8bU2BjYkZE6Q5Xww8hu9Z4E4Mo4QsooVtHoP6BM3NKw8zjVbWfoCQqxTrwuSzrNCWCWt58C24LHecH67CTt2uXbYSviixvrYkK7A3t68BxTJcF1dXJitEPTFe2ceTkauLJqrJgnER4iUrsjr26T8YgWvpY9wkkWFSviQW6wV5RASTCUasVEcrDiaKj8EQMkgyDoe9HyKitSVg67vMWJFpUXpQobseWJUs5FTWWzmfHmFp8FZ","meta":{"fee":1.0.1,"postBalances":[499999972500,15298080,1,1,1],"preBalances":[499999990500,15298080,1,1,1],"status":{"Ok":null}}}]},"id":1}
|
{"jsonrpc":"2.0","result":{"blockhash":"Gp3t5bfDsJv1ovP8cB1SuRhXVuoTqDv7p3tymyubYg5","parentSlot":429,"previousBlockhash":"EFejToxii1L5aUF2NrK9dsbAEmZSNyN5nsipmZHQR1eA","transactions":[{"transaction":"81UZJt4dh4Do66jDhrgkQudS8J2N6iG3jaVav7gJrqJSFY4Ug53iA9JFJZh2gxKWcaFdLJwhHx9mRdg9JwDAWB4ywiu5154CRwXV4FMdnPLg7bhxRLwhhYaLsVgMF5AyNRcTzjCVoBvqFgDU7P8VEKDEiMvD3qxzm1pLZVxDG1LTQpT3Dz4Uviv4KQbFQNuC22KupBoyHFB7Zh6KFdMqux4M9PvhoqcoJsJKwXjWpKu7xmEKnnrSbfLadkgjBmmjhW3fdTrFvnhQdTkhtdJxUL1xS9GMuJQer8YgSKNtUXB1eXZQwXU8bU2BjYkZE6Q5Xww8hu9Z4E4Mo4QsooVtHoP6BM3NKw8zjVbWfoCQqxTrwuSzrNCWCWt58C24LHecH67CTt2uXbYSviixvrYkK7A3t68BxTJcF1dXJitEPTFe2ceTkauLJqrJgnER4iUrsjr26T8YgWvpY9wkkWFSviQW6wV5RASTCUasVEcrDiaKj8EQMkgyDoe9HyKitSVg67vMWJFpUXpQobseWJUs5FTWWzmfHmFp8FZ","meta":{"fee":1.0.5,"postBalances":[499999972500,15298080,1,1,1],"preBalances":[499999990500,15298080,1,1,1],"status":{"Ok":null}}}]},"id":1}
|
||||||
```
|
```
|
||||||
|
|
||||||
### getConfirmedBlocks
|
### getConfirmedBlocks
|
||||||
@@ -403,6 +406,58 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "m
|
|||||||
{"jsonrpc":"2.0","result":{"firstNormalEpoch":8,"firstNormalSlot":8160,"leaderScheduleSlotOffset":8192,"slotsPerEpoch":8192,"warmup":true},"id":1}
|
{"jsonrpc":"2.0","result":{"firstNormalEpoch":8,"firstNormalSlot":8160,"leaderScheduleSlotOffset":8192,"slotsPerEpoch":8192,"warmup":true},"id":1}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### getFeeCalculatorForBlockhash
|
||||||
|
|
||||||
|
Returns the fee calculator associated with the query blockhash, or `null` if the blockhash has expired
|
||||||
|
|
||||||
|
#### Parameters:
|
||||||
|
|
||||||
|
* `blockhash: <string>`, query blockhash as a Base58 encoded string
|
||||||
|
|
||||||
|
#### Results:
|
||||||
|
|
||||||
|
The `result` field will be `null` if the query blockhash has expired, otherwise an `object` with the following fields:
|
||||||
|
|
||||||
|
* `feeCalculator: <object>`, `FeeCalculator` object describing the cluster fee rate at the queried blockhash
|
||||||
|
|
||||||
|
#### Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
// Request
|
||||||
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getFeeCalculatorForBlockhash", "params":["GJxqhuxcgfn5Tcj6y3f8X4FeCDd2RQ6SnEMo1AAxrPRZ"]}' http://localhost:8899
|
||||||
|
|
||||||
|
// Result
|
||||||
|
{"jsonrpc":"2.0","result":{"context":{"slot":221},"value":{"feeCalculator":{"lamportsPerSignature":5000}}},"id":1}
|
||||||
|
```
|
||||||
|
|
||||||
|
### getFeeRateGovernor
|
||||||
|
|
||||||
|
Returns the fee rate governor information from the root bank
|
||||||
|
|
||||||
|
#### Parameters:
|
||||||
|
|
||||||
|
None
|
||||||
|
|
||||||
|
#### Results:
|
||||||
|
|
||||||
|
The `result` field will be an `object` with the following fields:
|
||||||
|
|
||||||
|
* `burnPercent: <u8>`, Percentage of fees collected to be destroyed
|
||||||
|
* `maxLamportsPerSignature: <u64>`, Largest value `lamportsPerSignature` can attain for the next slot
|
||||||
|
* `minLamportsPerSignature: <u64>`, Smallest value `lamportsPerSignature` can attain for the next slot
|
||||||
|
* `targetLamportsPerSignature: <u64>`, Desired fee rate for the cluster
|
||||||
|
* `targetSignaturesPerSlot: <u64>`, Desired signature rate for the cluster
|
||||||
|
|
||||||
|
#### Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
// Request
|
||||||
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getFeeRateGovernor"}' http://localhost:8899
|
||||||
|
|
||||||
|
// Result
|
||||||
|
{"jsonrpc":"2.0","result":{"context":{"slot":54},"value":{"feeRateGovernor":{"burnPercent":50,"maxLamportsPerSignature":100000,"minLamportsPerSignature":5000,"targetLamportsPerSignature":10000,"targetSignaturesPerSlot":20000}}},"id":1}
|
||||||
|
```
|
||||||
|
|
||||||
### getGenesisHash
|
### getGenesisHash
|
||||||
|
|
||||||
Returns the genesis hash
|
Returns the genesis hash
|
||||||
@@ -425,6 +480,29 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "m
|
|||||||
{"jsonrpc":"2.0","result":"GH7ome3EiwEr7tu9JuTh2dpYWBJK3z69Xm1ZE3MEE6JC","id":1}
|
{"jsonrpc":"2.0","result":"GH7ome3EiwEr7tu9JuTh2dpYWBJK3z69Xm1ZE3MEE6JC","id":1}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### getIdentity
|
||||||
|
|
||||||
|
Returns the identity pubkey for the current node
|
||||||
|
|
||||||
|
#### Parameters:
|
||||||
|
|
||||||
|
None
|
||||||
|
|
||||||
|
#### Results:
|
||||||
|
|
||||||
|
The result field will be a JSON object with the following fields:
|
||||||
|
|
||||||
|
* `identity`, the identity pubkey of the current node \(as a base-58 encoded string\)
|
||||||
|
|
||||||
|
#### Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
// Request
|
||||||
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getIdentity"}' http://localhost:8899
|
||||||
|
// Result
|
||||||
|
{"jsonrpc":"2.0","result":{"identity": "2r1F4iWqVcb8M1DbAjQuFpebkQHY9hcVU4WuW2DJBppN"},"id":1}
|
||||||
|
```
|
||||||
|
|
||||||
### getInflation
|
### getInflation
|
||||||
|
|
||||||
Returns the inflation configuration of the cluster
|
Returns the inflation configuration of the cluster
|
||||||
@@ -579,7 +657,7 @@ An RpcResponse containing a JSON object consisting of a string blockhash and Fee
|
|||||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getRecentBlockhash"}' http://localhost:8899
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getRecentBlockhash"}' http://localhost:8899
|
||||||
|
|
||||||
// Result
|
// Result
|
||||||
{"jsonrpc":"2.0","result":{"context":{"slot":1},"value":{"blockhash":"CSymwgTNX1j3E4qhKfJAUE41nBWEwXufoYryPbkde5RR","feeCalculator":{"burnPercent":50,"lamportsPerSignature":5000,"maxLamportsPerSignature":1.0.10,"minLamportsPerSignature":5000,"targetLamportsPerSignature":1.0.1,"targetSignaturesPerSlot":20000}}},"id":1}
|
{"jsonrpc":"2.0","result":{"context":{"slot":1},"value":{"blockhash":"CSymwgTNX1j3E4qhKfJAUE41nBWEwXufoYryPbkde5RR","feeCalculator":{"burnPercent":50,"lamportsPerSignature":5000,"maxLamportsPerSignature":1.0.50,"minLamportsPerSignature":5000,"targetLamportsPerSignature":1.0.5,"targetSignaturesPerSlot":20000}}},"id":1}
|
||||||
```
|
```
|
||||||
|
|
||||||
### getSignatureConfirmation
|
### getSignatureConfirmation
|
||||||
@@ -830,7 +908,7 @@ The result field will be a JSON object with the following fields:
|
|||||||
// Request
|
// Request
|
||||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getVersion"}' http://localhost:8899
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getVersion"}' http://localhost:8899
|
||||||
// Result
|
// Result
|
||||||
{"jsonrpc":"2.0","result":{"solana-core": "1.0.1"},"id":1}
|
{"jsonrpc":"2.0","result":{"solana-core": "1.0.5"},"id":1}
|
||||||
```
|
```
|
||||||
|
|
||||||
### getVoteAccounts
|
### getVoteAccounts
|
||||||
@@ -916,7 +994,7 @@ Creates new transaction
|
|||||||
|
|
||||||
#### Parameters:
|
#### Parameters:
|
||||||
|
|
||||||
* `<array>` - array of octets containing a fully-signed Transaction
|
* `<string>` - fully-signed Transaction, as base-58 encoded string
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
|
@@ -52,7 +52,7 @@ $ NDEBUG=1 ./multinode-demo/faucet.sh
|
|||||||
|
|
||||||
### Singlenode Testnet
|
### Singlenode Testnet
|
||||||
|
|
||||||
Before you start a validator, make sure you know the IP address of the machine you want to be the bootstrap validator for the demo, and make sure that udp ports 8000-1.0.1 are open on all the machines you want to test with.
|
Before you start a validator, make sure you know the IP address of the machine you want to be the bootstrap validator for the demo, and make sure that udp ports 8000-1.0.5 are open on all the machines you want to test with.
|
||||||
|
|
||||||
Now start the bootstrap validator in a separate shell:
|
Now start the bootstrap validator in a separate shell:
|
||||||
|
|
||||||
@@ -151,7 +151,7 @@ The stream will output a series of JSON objects:
|
|||||||
|
|
||||||
## Public Testnet
|
## Public Testnet
|
||||||
|
|
||||||
In this example the client connects to our public testnet. To run validators on the testnet you would need to open udp ports `8000-1.0.1`.
|
In this example the client connects to our public testnet. To run validators on the testnet you would need to open udp ports `8000-1.0.5`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ NDEBUG=1 ./multinode-demo/bench-tps.sh --entrypoint devnet.solana.com:8001 --faucet devnet.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
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,7 @@ Without a partition lasting longer than an epoch, the cluster will work as follo
|
|||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
The epoch duration is 100 slots. The root fork is updated from fork computed at slot height 99 to a fork computed at slot height 102. Forks with slots at height 100,101 were skipped because of failures. The new leader schedule is computed using fork at slot height 102. It is active from slot 200 until it is updated again.
|
The epoch duration is 100 slots. The root fork is updated from fork computed at slot height 99 to a fork computed at slot height 102. Forks with slots at height 1.0.501 were skipped because of failures. The new leader schedule is computed using fork at slot height 102. It is active from slot 200 until it is updated again.
|
||||||
|
|
||||||
No inconsistency can exist because every validator that is voting with the cluster has skipped 100 and 101 when its root passes 102. All validators, regardless of voting pattern, would be committing to a root that is either 102, or a descendant of 102.
|
No inconsistency can exist because every validator that is voting with the cluster has skipped 100 and 101 when its root passes 102. All validators, regardless of voting pattern, would be committing to a root that is either 102, or a descendant of 102.
|
||||||
|
|
||||||
|
@@ -166,14 +166,14 @@ Rewards are paid against the "effective" portion of the stake for that epoch.
|
|||||||
|
|
||||||
#### Warmup example
|
#### Warmup example
|
||||||
|
|
||||||
Consider the situation of a single stake of 1.0.1 activated at epoch N, with network warmup rate of 20%, and a quiescent total network stake at epoch N of 2,000.
|
Consider the situation of a single stake of 1.0.5 activated at epoch N, with network warmup rate of 20%, and a quiescent total network stake at epoch N of 2,000.
|
||||||
|
|
||||||
At epoch N+1, the amount available to be activated for the network is 400 \(20% of 200\), and at epoch N, this example stake is the only stake activating, and so is entitled to all of the warmup room available.
|
At epoch N+1, the amount available to be activated for the network is 400 \(20% of 200\), and at epoch N, this example stake is the only stake activating, and so is entitled to all of the warmup room available.
|
||||||
|
|
||||||
| epoch | effective | activating | total effective | total activating |
|
| epoch | effective | activating | total effective | total activating |
|
||||||
| :--- | ---: | ---: | ---: | ---: |
|
| :--- | ---: | ---: | ---: | ---: |
|
||||||
| N-1 | | | 2,000 | 0 |
|
| N-1 | | | 2,000 | 0 |
|
||||||
| N | 0 | 1.0.1 | 2,000 | 1.0.1 |
|
| N | 0 | 1.0.5 | 2,000 | 1.0.5 |
|
||||||
| N+1 | 400 | 600 | 2,400 | 600 |
|
| N+1 | 400 | 600 | 2,400 | 600 |
|
||||||
| N+2 | 880 | 120 | 2,880 | 120 |
|
| N+2 | 880 | 120 | 2,880 | 120 |
|
||||||
| N+3 | 1000 | 0 | 3,000 | 0 |
|
| N+3 | 1000 | 0 | 3,000 | 0 |
|
||||||
@@ -183,7 +183,7 @@ Were 2 stakes \(X and Y\) to activate at epoch N, they would be awarded a portio
|
|||||||
| epoch | X eff | X act | Y eff | Y act | total effective | total activating |
|
| epoch | X eff | X act | Y eff | Y act | total effective | total activating |
|
||||||
| :--- | ---: | ---: | ---: | ---: | ---: | ---: |
|
| :--- | ---: | ---: | ---: | ---: | ---: | ---: |
|
||||||
| N-1 | | | | | 2,000 | 0 |
|
| N-1 | | | | | 2,000 | 0 |
|
||||||
| N | 0 | 1.0.1 | 0 | 200 | 2,000 | 1,200 |
|
| N | 0 | 1.0.5 | 0 | 200 | 2,000 | 1,200 |
|
||||||
| N+1 | 333 | 667 | 67 | 133 | 2,400 | 800 |
|
| N+1 | 333 | 667 | 67 | 133 | 2,400 | 800 |
|
||||||
| N+2 | 733 | 267 | 146 | 54 | 2,880 | 321 |
|
| N+2 | 733 | 267 | 146 | 54 | 2,880 | 321 |
|
||||||
| N+3 | 1000 | 0 | 200 | 0 | 3,200 | 0 |
|
| N+3 | 1000 | 0 | 200 | 0 | 3,200 | 0 |
|
||||||
|
@@ -1,14 +1,14 @@
|
|||||||
# Installing the Validator Software
|
# Installing Solana
|
||||||
|
|
||||||
Install the Solana release
|
Install the Solana release
|
||||||
[v1.0.0](https://github.com/solana-labs/solana/releases/tag/v1.0.0) on your
|
[v1.0.5](https://github.com/solana-labs/solana/releases/tag/v1.0.5) on your
|
||||||
machine by running:
|
machine by running:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -sSf https://raw.githubusercontent.com/solana-labs/solana/v1.0.0/install/solana-install-init.sh | sh -s - 1.0.0
|
curl -sSf https://raw.githubusercontent.com/solana-labs/solana/v1.0.5/install/solana-install-init.sh | sh -s - 1.0.5
|
||||||
```
|
```
|
||||||
|
|
||||||
If you are connecting to a different testnet, you can replace `1.0.0` with the
|
If you are connecting to a different testnet, you can replace `1.0.5` with the
|
||||||
release tag matching the software version of your desired testnet, or replace it
|
release tag matching the software version of your desired testnet, or replace it
|
||||||
with the named channel `stable`, `beta`, or `edge`.
|
with the named channel `stable`, `beta`, or `edge`.
|
||||||
|
|
||||||
@@ -16,11 +16,11 @@ The following output indicates a successful update:
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
looking for latest release
|
looking for latest release
|
||||||
downloading v1.0.0 installer
|
downloading v1.0.5 installer
|
||||||
Configuration: /home/solana/.config/solana/install/config.yml
|
Configuration: /home/solana/.config/solana/install/config.yml
|
||||||
Active release directory: /home/solana/.local/share/solana/install/active_release
|
Active release directory: /home/solana/.local/share/solana/install/active_release
|
||||||
* Release version: 1.0.0
|
* Release version: 1.0.5
|
||||||
* Release URL: https://github.com/solana-labs/solana/releases/download/v1.0.0/solana-release-x86_64-unknown-linux-gnu.tar.bz2
|
* Release URL: https://github.com/solana-labs/solana/releases/download/v1.0.5/solana-release-x86_64-unknown-linux-gnu.tar.bz2
|
||||||
Update successful
|
Update successful
|
||||||
```
|
```
|
||||||
|
|
@@ -18,7 +18,7 @@ All claims, content, designs, algorithms, estimates, roadmaps, specifications, a
|
|||||||
|
|
||||||
In November of 2017, Anatoly Yakovenko published a whitepaper describing Proof of History, a technique for keeping time between computers that do not trust one another. From Anatoly's previous experience designing distributed systems at Qualcomm, Mesosphere and Dropbox, he knew that a reliable clock makes network synchronization very simple. When synchronization is simple the resulting network can be blazing fast, bound only by network bandwidth.
|
In November of 2017, Anatoly Yakovenko published a whitepaper describing Proof of History, a technique for keeping time between computers that do not trust one another. From Anatoly's previous experience designing distributed systems at Qualcomm, Mesosphere and Dropbox, he knew that a reliable clock makes network synchronization very simple. When synchronization is simple the resulting network can be blazing fast, bound only by network bandwidth.
|
||||||
|
|
||||||
Anatoly watched as blockchain systems without clocks, such as Bitcoin and Ethereum, struggled to scale beyond 15 transactions per second worldwide when centralized payment systems such as Visa required peaks of 65,000 tps. Without a clock, it was clear they'd never graduate to being the global payment system or global supercomputer most had dreamed them to be. When Anatoly solved the problem of getting computers that don’t trust each other to agree on time, he knew he had the key to bring 40 years of distributed systems research to the world of blockchain. The resulting cluster wouldn't be just 10 times faster, or a 100 times, or a 1.0.1 times, but 10,000 times faster, right out of the gate!
|
Anatoly watched as blockchain systems without clocks, such as Bitcoin and Ethereum, struggled to scale beyond 15 transactions per second worldwide when centralized payment systems such as Visa required peaks of 65,000 tps. Without a clock, it was clear they'd never graduate to being the global payment system or global supercomputer most had dreamed them to be. When Anatoly solved the problem of getting computers that don’t trust each other to agree on time, he knew he had the key to bring 40 years of distributed systems research to the world of blockchain. The resulting cluster wouldn't be just 10 times faster, or a 100 times, or a 1.0.5 times, but 10,000 times faster, right out of the gate!
|
||||||
|
|
||||||
Anatoly's implementation began in a private codebase and was implemented in the C programming language. Greg Fitzgerald, who had previously worked with Anatoly at semiconductor giant Qualcomm Incorporated, encouraged him to reimplement the project in the Rust programming language. Greg had worked on the LLVM compiler infrastructure, which underlies both the Clang C/C++ compiler as well as the Rust compiler. Greg claimed that the language's safety guarantees would improve software productivity and that its lack of a garbage collector would allow programs to perform as well as those written in C. Anatoly gave it a shot and just two weeks later, had migrated his entire codebase to Rust. Sold. With plans to weave all the world's transactions together on a single, scalable blockchain, Anatoly called the project Loom.
|
Anatoly's implementation began in a private codebase and was implemented in the C programming language. Greg Fitzgerald, who had previously worked with Anatoly at semiconductor giant Qualcomm Incorporated, encouraged him to reimplement the project in the Rust programming language. Greg had worked on the LLVM compiler infrastructure, which underlies both the Clang C/C++ compiler as well as the Rust compiler. Greg claimed that the language's safety guarantees would improve software productivity and that its lack of a garbage collector would allow programs to perform as well as those written in C. Anatoly gave it a shot and just two weeks later, had migrated his entire codebase to Rust. Sold. With plans to weave all the world's transactions together on a single, scalable blockchain, Anatoly called the project Loom.
|
||||||
|
|
||||||
|
@@ -14,9 +14,15 @@ transaction.
|
|||||||
## Commands Supporting Offline Signing
|
## Commands Supporting Offline Signing
|
||||||
|
|
||||||
At present, the following commands support offline signing:
|
At present, the following commands support offline signing:
|
||||||
* [`delegate-stake`](../api-reference/cli.md#solana-delegate-stake)
|
* [`create-stake-account`](../cli/usage.md#solana-create-stake-account)
|
||||||
* [`deactivate-stake`](../api-reference/cli.md#solana-deactivate-stake)
|
* [`deactivate-stake`](../cli/usage.md#solana-deactivate-stake)
|
||||||
* [`pay`](../api-reference/cli.md#solana-pay)
|
* [`delegate-stake`](../cli/usage.md#solana-delegate-stake)
|
||||||
|
* [`split-stake`](../cli/usage.md#solana-split-stake)
|
||||||
|
* [`stake-authorize-staker`](../cli/usage.md#solana-stake-authorize-staker)
|
||||||
|
* [`stake-authorize-withdrawer`](../cli/usage.md#solana-stake-authorize-withdrawer)
|
||||||
|
* [`stake-set-lockup`](../cli/usage.md#solana-stake-set-lockup)
|
||||||
|
* [`transfer`](../cli/usage.md#solana-transfer)
|
||||||
|
* [`withdraw-stake`](../cli/usage.md#solana-withdraw-stake)
|
||||||
|
|
||||||
## Signing Transactions Offline
|
## Signing Transactions Offline
|
||||||
|
|
||||||
|
@@ -222,7 +222,7 @@ expires and the transaction fails
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ solana pay -k alice.json --blockhash expiredDTaxfagttWjQweib42b6ZHADSx94Tw8gHx3W7 bob.json 1
|
$ solana pay -k alice.json --blockhash expiredDTaxfagttWjQweib42b6ZHADSx94Tw8gHx3W7 bob.json 1
|
||||||
[2020-01-02T18:48:28.46291.0.1Z ERROR solana_cli::cli] Io(Custom { kind: Other, error: "Transaction \"33gQQaoPc9jWePMvDAeyJpcnSPiGUAdtVg8zREWv4GiKjkcGNufgpcbFyRKRrA25NkgjZySEeKue5rawyeH5TzsV\" failed: None" })
|
[2020-01-02T18:48:28.46291.0.5Z ERROR solana_cli::cli] Io(Custom { kind: Other, error: "Transaction \"33gQQaoPc9jWePMvDAeyJpcnSPiGUAdtVg8zREWv4GiKjkcGNufgpcbFyRKRrA25NkgjZySEeKue5rawyeH5TzsV\" failed: None" })
|
||||||
Error: Io(Custom { kind: Other, error: "Transaction \"33gQQaoPc9jWePMvDAeyJpcnSPiGUAdtVg8zREWv4GiKjkcGNufgpcbFyRKRrA25NkgjZySEeKue5rawyeH5TzsV\" failed: None" })
|
Error: Io(Custom { kind: Other, error: "Transaction \"33gQQaoPc9jWePMvDAeyJpcnSPiGUAdtVg8zREWv4GiKjkcGNufgpcbFyRKRrA25NkgjZySEeKue5rawyeH5TzsV\" failed: None" })
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@@ -1,51 +0,0 @@
|
|||||||
# Installation Guide
|
|
||||||
Follow this guide to setup Solana's key generation tool called `solana-keygen`
|
|
||||||
|
|
||||||
{% hint style="warn" %}
|
|
||||||
After installation, ensure your version is `1.0.0` or higher by running `solana-keygen -V`
|
|
||||||
{% endhint %}
|
|
||||||
|
|
||||||
## Download
|
|
||||||
First, download the latest release tarball from GitHub.
|
|
||||||
|
|
||||||
1. Setup download url
|
|
||||||
|
|
||||||
```bash
|
|
||||||
solana_downloads=https://github.com/solana-labs/solana/releases/latest/download
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Specify the download file based on your machine
|
|
||||||
|
|
||||||
**MacOS**
|
|
||||||
```bash
|
|
||||||
solana_release=solana-release-x86_64-apple-darwin.tar.bz2
|
|
||||||
```
|
|
||||||
|
|
||||||
**Linux**
|
|
||||||
```bash
|
|
||||||
solana_release=solana-release-x86_64-unknown-linux-gnu.tar.bz2
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Download
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -L -sSf -o solana-release.tar.bz2 $solana_downloads/$solana_release
|
|
||||||
```
|
|
||||||
|
|
||||||
## Extract
|
|
||||||
Next, extract the tarball
|
|
||||||
```bash
|
|
||||||
tar xf solana-release.tar.bz2
|
|
||||||
```
|
|
||||||
|
|
||||||
## Add to "PATH"
|
|
||||||
Now add the tool to your PATH environment variable with the following command
|
|
||||||
```bash
|
|
||||||
export PATH="$(pwd)/solana-release/bin:${PATH}"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Check
|
|
||||||
Finally, check that `solana-keygen` can be run by running
|
|
||||||
```bash
|
|
||||||
solana-keygen -V
|
|
||||||
```
|
|
@@ -10,6 +10,18 @@ written to disk by unencrypted memory swaps. It is the user's responsibility to
|
|||||||
protect against this scenario.
|
protect against this scenario.
|
||||||
{% endhint %}
|
{% endhint %}
|
||||||
|
|
||||||
|
## Before You Begin
|
||||||
|
|
||||||
|
- [Install the Solana command-line tools](../install-solana.md)
|
||||||
|
|
||||||
|
### Check your installation
|
||||||
|
|
||||||
|
Check that `solana-keygen` is installed correctly by running:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
solana-keygen --version
|
||||||
|
```
|
||||||
|
|
||||||
## Creating a Paper Wallet
|
## Creating a Paper Wallet
|
||||||
|
|
||||||
Using the `solana-keygen` tool, it is possible to generate new seed phrases as
|
Using the `solana-keygen` tool, it is possible to generate new seed phrases as
|
||||||
@@ -221,21 +233,20 @@ done < public_keys.txt
|
|||||||
|
|
||||||
In order to run a validator, you will need to specify an "identity keypair"
|
In order to run a validator, you will need to specify an "identity keypair"
|
||||||
which will be used to fund all of the vote transactions signed by your validator.
|
which will be used to fund all of the vote transactions signed by your validator.
|
||||||
Rather than specifying a path with `--identity-keypair <PATH>` you can use the
|
Rather than specifying a path with `--identity-keypair <PATH>` you can pass
|
||||||
`--ask-seed-phrase` option.
|
`ASK` to securely input the funding keypair.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana-validator --ask-seed-phrase identity-keypair --ledger ...
|
solana-validator --identity-keypair ASK --ledger ...
|
||||||
|
|
||||||
[identity-keypair] seed phrase: 🔒
|
[identity-keypair] seed phrase: 🔒
|
||||||
[identity-keypair] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue:
|
[identity-keypair] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue:
|
||||||
```
|
```
|
||||||
|
|
||||||
The `--ask-seed-phrase` option accepts multiple keypairs. If you wish to use this
|
You can use this input method for your voting keypair as well:
|
||||||
input method for your voting keypair as well you can do the following:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
solana-validator --ask-seed-phrase identity-keypair voting-keypair --ledger ...
|
solana-validator --identity-keypair ASK --voting-keypair ASK --ledger ...
|
||||||
|
|
||||||
[identity-keypair] seed phrase: 🔒
|
[identity-keypair] seed phrase: 🔒
|
||||||
[identity-keypair] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue:
|
[identity-keypair] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue:
|
||||||
|
@@ -6,15 +6,60 @@ secure transaction signing.
|
|||||||
|
|
||||||
## Before You Begin
|
## Before You Begin
|
||||||
|
|
||||||
|
- [Install the Solana command-line tools](../install-solana.md)
|
||||||
- [Initialize your Ledger Nano S](https://support.ledger.com/hc/en-us/articles/360000613793)
|
- [Initialize your Ledger Nano S](https://support.ledger.com/hc/en-us/articles/360000613793)
|
||||||
- [Install the latest device firmware](https://support.ledgerwallet.com/hc/en-us/articles/360002731113-Update-Ledger-Nano-S-firmware)
|
- [Install the latest device firmware](https://support.ledgerwallet.com/hc/en-us/articles/360002731113-Update-Ledger-Nano-S-firmware)
|
||||||
- [Install Ledger Live](https://support.ledger.com/hc/en-us/articles/360006395553/) software on your computer
|
|
||||||
|
|
||||||
## Install the Solana App on Ledger Nano S
|
## Install the Solana App on Ledger Nano S
|
||||||
|
|
||||||
|
The Solana Ledger app is not yet available on Ledger Live. Until it is, you
|
||||||
|
can install a development version of the app from the command-line. Note that
|
||||||
|
because the app is not installed via Ledger Live, you will need to approve
|
||||||
|
installation from an "unsafe" manager, as well as see the message, "This app
|
||||||
|
is not genuine" each time you open the app. Once the app is available on
|
||||||
|
Ledger Live, you can reinstall the app from there, and the message will no
|
||||||
|
longer be displayed.
|
||||||
|
|
||||||
|
1. Connect your Ledger device via USB and enter your pin to unlock it
|
||||||
|
2. Download and run the Solana Ledger app installer:
|
||||||
|
```text
|
||||||
|
curl -sSLf https://github.com/solana-labs/ledger-app-solana/releases/download/v0.1.1/install.sh | sh
|
||||||
|
```
|
||||||
|
3. When prompted, approve the "unsafe" manager on your device
|
||||||
|
4. When prompted, approve the installation on your device
|
||||||
|
5. An installation window appears and your device will display Processing…
|
||||||
|
6. The app installation is confirmed
|
||||||
|
|
||||||
|
### Troubleshooting
|
||||||
|
|
||||||
|
If you encounter the following error:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/runpy.py", line 193, in _run_module_as_main
|
||||||
|
"__main__", mod_spec)
|
||||||
|
File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/runpy.py", line 85, in _run_code
|
||||||
|
exec(code, run_globals)
|
||||||
|
File "ledger-env/lib/python3.7/site-packages/ledgerblue/loadApp.py", line 197, in <module>
|
||||||
|
dongle = getDongle(args.apdu)
|
||||||
|
File "ledger-env/lib/python3.7/site-packages/ledgerblue/comm.py", line 216, in getDongle
|
||||||
|
dev.open_path(hidDevicePath)
|
||||||
|
File "hid.pyx", line 72, in hid.device.open_path
|
||||||
|
OSError: open failed
|
||||||
|
```
|
||||||
|
|
||||||
|
To fix, check the following:
|
||||||
|
1. Ensure your Ledger device is connected to USB
|
||||||
|
2. Ensure your Ledger device is unlocked and not waiting for you to enter your pin
|
||||||
|
3. Ensure the Ledger Live application is not open
|
||||||
|
|
||||||
|
### Future: Installation once the Solana app is on Ledger Live
|
||||||
|
|
||||||
|
- [Install Ledger Live](https://support.ledger.com/hc/en-us/articles/360006395553/) software on your computer
|
||||||
|
|
||||||
1. Open the Manager in Ledger Live
|
1. Open the Manager in Ledger Live
|
||||||
2. Connect and unlock your Ledger Nano S
|
2. Connect your Ledger device via USB and enter your pin to unlock it
|
||||||
3. If asked, allow the manager on your device by pressing the right button
|
3. When prompted, approve the manager on your device
|
||||||
4. Find Solana in the app catalog and click Install
|
4. Find Solana in the app catalog and click Install
|
||||||
5. An installation window appears and your device will display Processing…
|
5. An installation window appears and your device will display Processing…
|
||||||
6. The app installation is confirmed
|
6. The app installation is confirmed
|
||||||
@@ -78,6 +123,12 @@ usb://ledger/nano-s/BsNsvfXqQTtJnagwFWdBS7FBXgnsK8VZ5CmuznN85swK?key=0/0
|
|||||||
|
|
||||||
### Set CLI Configuration
|
### Set CLI Configuration
|
||||||
|
|
||||||
|
Configure the `solana` CLI tool to connect to a particular cluster:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
solana config set --url <CLUSTER_URL> # (i.e. http://devnet.solana.com:8899)
|
||||||
|
```
|
||||||
|
|
||||||
If you want to set a Ledger key as the default signer for CLI commands, use the
|
If you want to set a Ledger key as the default signer for CLI commands, use the
|
||||||
[CLI configuration settings](../cli/usage.md#solana-config):
|
[CLI configuration settings](../cli/usage.md#solana-config):
|
||||||
|
|
||||||
|
@@ -1,156 +0,0 @@
|
|||||||
# Running an Archiver
|
|
||||||
|
|
||||||
This document describes how to setup an archiver in the testnet
|
|
||||||
|
|
||||||
Please note some of the information and instructions described here may change in future releases.
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
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 devnet.solana.com, which serves as the entrypoint to the cluster for your archiver node.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
## Machine Requirements
|
|
||||||
|
|
||||||
Archivers don't need specialized hardware. Anything with more than 128GB of disk space will be able to participate in the cluster as an archiver node.
|
|
||||||
|
|
||||||
Currently the disk space requirements are very low but we expect them to change in the future.
|
|
||||||
|
|
||||||
Prebuilt binaries are available for Linux x86\_64 \(Ubuntu 18.04 recommended\), macOS, and Windows.
|
|
||||||
|
|
||||||
### Confirm The Testnet Is Reachable
|
|
||||||
|
|
||||||
Before starting an archiver node, sanity check that the cluster is accessible to your machine by running some simple commands. If any of the commands fail, please retry 5-10 minutes later to confirm the testnet is not just restarting itself before debugging further.
|
|
||||||
|
|
||||||
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://devnet.solana.com:8899
|
|
||||||
```
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
## Archiver Setup
|
|
||||||
|
|
||||||
#### Obtaining The Software
|
|
||||||
|
|
||||||
#### Bootstrap with `solana-install`
|
|
||||||
|
|
||||||
The `solana-install` tool can be used to easily install and upgrade the cluster software.
|
|
||||||
|
|
||||||
#### Linux and mac OS
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -sSf https://raw.githubusercontent.com/solana-labs/solana/v1.0.0/install/solana-install-init.sh | sh -s
|
|
||||||
```
|
|
||||||
|
|
||||||
Alternatively build the `solana-install` program from source and run the following command to obtain the same result:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
solana-install init
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Windows
|
|
||||||
|
|
||||||
Download and install **solana-install-init** from [https://github.com/solana-labs/solana/releases/latest](https://github.com/solana-labs/solana/releases/latest)
|
|
||||||
|
|
||||||
After a successful install, `solana-install update` may be used to easily update the software to a newer version at any time.
|
|
||||||
|
|
||||||
#### Download Prebuilt Binaries
|
|
||||||
|
|
||||||
If you would rather not use `solana-install` to manage the install, you can manually download and install the binaries.
|
|
||||||
|
|
||||||
#### Linux
|
|
||||||
|
|
||||||
Download the binaries by navigating to [https://github.com/solana-labs/solana/releases/latest](https://github.com/solana-labs/solana/releases/latest), download **solana-release-x86\_64-unknown-linux-gnu.tar.bz2**, then extract the archive:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
tar jxf solana-release-x86_64-unknown-linux-gnu.tar.bz2
|
|
||||||
cd solana-release/
|
|
||||||
export PATH=$PWD/bin:$PATH
|
|
||||||
```
|
|
||||||
|
|
||||||
#### mac OS
|
|
||||||
|
|
||||||
Download the binaries by navigating to [https://github.com/solana-labs/solana/releases/latest](https://github.com/solana-labs/solana/releases/latest), download **solana-release-x86\_64-apple-darwin.tar.bz2**, then extract the archive:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
tar jxf solana-release-x86_64-apple-darwin.tar.bz2
|
|
||||||
cd solana-release/
|
|
||||||
export PATH=$PWD/bin:$PATH
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Windows
|
|
||||||
|
|
||||||
Download the binaries by navigating to [https://github.com/solana-labs/solana/releases/latest](https://github.com/solana-labs/solana/releases/latest), download **solana-release-x86\_64-pc-windows-msvc.tar.bz2**, then extract it into a folder. It is a good idea to add this extracted folder to your windows PATH.
|
|
||||||
|
|
||||||
## Starting The Archiver
|
|
||||||
|
|
||||||
Try running following command to join the gossip network and view all the other nodes in the cluster:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
solana-gossip spy --entrypoint devnet.solana.com:8001
|
|
||||||
# Press ^C to exit
|
|
||||||
```
|
|
||||||
|
|
||||||
Now configure the keypairs for your archiver by running:
|
|
||||||
|
|
||||||
Navigate to the solana install location and open a cmd prompt
|
|
||||||
|
|
||||||
```bash
|
|
||||||
solana-keygen new -o archiver-keypair.json
|
|
||||||
solana-keygen new -o storage-keypair.json
|
|
||||||
```
|
|
||||||
|
|
||||||
Use solana-keygen to show the public keys for each of the keypairs, they will be needed in the next step:
|
|
||||||
|
|
||||||
* Windows
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# The archiver's identity
|
|
||||||
solana-keygen pubkey archiver-keypair.json
|
|
||||||
solana-keygen pubkey storage-keypair.json
|
|
||||||
```
|
|
||||||
|
|
||||||
* Linux and mac OS
|
|
||||||
|
|
||||||
\`\`\`bash
|
|
||||||
|
|
||||||
export ARCHIVER\_IDENTITY=$\(solana-keygen pubkey archiver-keypair.json\)
|
|
||||||
|
|
||||||
export STORAGE\_IDENTITY=$\(solana-keygen pubkey storage-keypair.json\)
|
|
||||||
|
|
||||||
```text
|
|
||||||
Then set up the storage accounts for your archiver by running:
|
|
||||||
```bash
|
|
||||||
solana --keypair archiver-keypair.json airdrop .0001
|
|
||||||
solana --keypair archiver-keypair.json create-archiver-storage-account $ARCHIVER_IDENTITY $STORAGE_IDENTITY
|
|
||||||
```
|
|
||||||
|
|
||||||
Note: Every time the testnet restarts, run the steps to setup the archiver accounts again.
|
|
||||||
|
|
||||||
To start the archiver:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
solana-archiver --entrypoint devnet.solana.com:8001 --identity-keypair archiver-keypair.json --storage-keypair storage-keypair.json --ledger archiver-ledger
|
|
||||||
```
|
|
||||||
|
|
||||||
## Verify Archiver Setup
|
|
||||||
|
|
||||||
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 devnet.solana.com:8001
|
|
||||||
```
|
|
||||||
|
|
||||||
Provide the **storage account pubkey** to the `solana storage-account` command to view the recent mining activity from your archiver:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
solana --keypair storage-keypair.json storage-account $STORAGE_IDENTITY
|
|
||||||
```
|
|
@@ -33,10 +33,10 @@ Here are our recommendations for low, medium, and high end machine specification
|
|||||||
## Software
|
## Software
|
||||||
|
|
||||||
* We build and run on Ubuntu 18.04. Some users have had trouble when running on Ubuntu 16.04
|
* We build and run on Ubuntu 18.04. Some users have had trouble when running on Ubuntu 16.04
|
||||||
* See [Validator Software](validator-software.md) for the current Solana software release.
|
* See [Installing Solana](../install-solana.md) for the current Solana software release.
|
||||||
|
|
||||||
Be sure to ensure that the machine used is not behind a residential NAT to avoid
|
Be sure to ensure that the machine used is not behind a residential NAT to avoid
|
||||||
NAT traversal issues. A cloud-hosted machine works best. **Ensure that IP ports 8000 through 1.0.1 are not blocked for Internet inbound and outbound traffic.**
|
NAT traversal issues. A cloud-hosted machine works best. **Ensure that IP ports 8000 through 1.0.5 are not blocked for Internet inbound and outbound traffic.**
|
||||||
For more information on port forwarding with regards to residential networks,
|
For more information on port forwarding with regards to residential networks,
|
||||||
see [this document](http://www.mcs.sdsmt.edu/lpyeatt/courses/314/PortForwardingSetup.pdf).
|
see [this document](http://www.mcs.sdsmt.edu/lpyeatt/courses/314/PortForwardingSetup.pdf).
|
||||||
|
|
||||||
|
@@ -174,9 +174,9 @@ If your validator is connected, its public key and IP address will appear in the
|
|||||||
### Controlling local network port allocation
|
### Controlling local network port allocation
|
||||||
|
|
||||||
By default the validator will dynamically select available network ports in the
|
By default the validator will dynamically select available network ports in the
|
||||||
8000-1.0.1 range, and may be overridden with `--dynamic-port-range`. For
|
8000-1.0.5 range, and may be overridden with `--dynamic-port-range`. For
|
||||||
example, `solana-validator --dynamic-port-range 1.0.1-1.0.1 ...` will restrict
|
example, `solana-validator --dynamic-port-range 1.0.5-1.0.5 ...` will restrict
|
||||||
the validator to ports 1.0.1-11011.
|
the validator to ports 1.0.5-1.0.5.
|
||||||
|
|
||||||
### Limiting ledger size to conserve disk space
|
### Limiting ledger size to conserve disk space
|
||||||
|
|
||||||
|
@@ -24,7 +24,7 @@ stability, all that can be said is that CI automation was successful.
|
|||||||
### Get Testnet Version
|
### Get Testnet Version
|
||||||
|
|
||||||
You can submit a JSON-RPC request to see the specific software version of the
|
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).
|
cluster. Use this to specify [the software version to install](../install-solana.md).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1, "method":"getVersion"}' devnet.solana.com:8899
|
curl -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1, "method":"getVersion"}' devnet.solana.com:8899
|
||||||
|
@@ -3,4 +3,4 @@
|
|||||||
Please see the official [TOUR DE SOL PARTICIPATION TERMS](https://drive.google.com/a/solana.com/file/d/15ueLG6VJoQ5Hx4rnpjFeuL3pG5DbrBbE/view?usp=sharing) for complete details.
|
Please see the official [TOUR DE SOL PARTICIPATION TERMS](https://drive.google.com/a/solana.com/file/d/15ueLG6VJoQ5Hx4rnpjFeuL3pG5DbrBbE/view?usp=sharing) for complete details.
|
||||||
Download below:
|
Download below:
|
||||||
|
|
||||||
{% file src="../../.gitbook/assets/solana-tour-de-sol-participation-terms-20190723.pdf" caption="Tour de SOL Participation Terms" %}
|
{% file src="../../.gitbook/assets/solana-tour-de-sol-participation-terms-201.0.53.pdf" caption="Tour de SOL Participation Terms" %}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-faucet"
|
name = "solana-faucet"
|
||||||
version = "1.0.1"
|
version = "1.0.5"
|
||||||
description = "Solana Faucet"
|
description = "Solana Faucet"
|
||||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
@@ -19,10 +19,10 @@ clap = "2.33"
|
|||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
serde = "1.0.104"
|
serde = "1.0.104"
|
||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "1.0.1" }
|
solana-clap-utils = { path = "../clap-utils", version = "1.0.5" }
|
||||||
solana-logger = { path = "../logger", version = "1.0.1" }
|
solana-logger = { path = "../logger", version = "1.0.5" }
|
||||||
solana-metrics = { path = "../metrics", version = "1.0.1" }
|
solana-metrics = { path = "../metrics", version = "1.0.5" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.0.1" }
|
solana-sdk = { path = "../sdk", version = "1.0.5" }
|
||||||
tokio = "0.1"
|
tokio = "0.1"
|
||||||
tokio-codec = "0.1"
|
tokio-codec = "0.1"
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-genesis-programs"
|
name = "solana-genesis-programs"
|
||||||
version = "1.0.1"
|
version = "1.0.5"
|
||||||
description = "Solana genesis programs"
|
description = "Solana genesis programs"
|
||||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
@@ -10,16 +10,16 @@ edition = "2018"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = { version = "0.4.8" }
|
log = { version = "0.4.8" }
|
||||||
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "1.0.1" }
|
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "1.0.5" }
|
||||||
solana-budget-program = { path = "../programs/budget", version = "1.0.1" }
|
solana-budget-program = { path = "../programs/budget", version = "1.0.5" }
|
||||||
solana-config-program = { path = "../programs/config", version = "1.0.1" }
|
solana-config-program = { path = "../programs/config", version = "1.0.5" }
|
||||||
solana-exchange-program = { path = "../programs/exchange", version = "1.0.1" }
|
solana-exchange-program = { path = "../programs/exchange", version = "1.0.5" }
|
||||||
solana-runtime = { path = "../runtime", version = "1.0.1" }
|
solana-runtime = { path = "../runtime", version = "1.0.5" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.0.1" }
|
solana-sdk = { path = "../sdk", version = "1.0.5" }
|
||||||
solana-stake-program = { path = "../programs/stake", version = "1.0.1" }
|
solana-stake-program = { path = "../programs/stake", version = "1.0.5" }
|
||||||
solana-storage-program = { path = "../programs/storage", version = "1.0.1" }
|
solana-storage-program = { path = "../programs/storage", version = "1.0.5" }
|
||||||
solana-vest-program = { path = "../programs/vest", version = "1.0.1" }
|
solana-vest-program = { path = "../programs/vest", version = "1.0.5" }
|
||||||
solana-vote-program = { path = "../programs/vote", version = "1.0.1" }
|
solana-vote-program = { path = "../programs/vote", version = "1.0.5" }
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["lib"]
|
crate-type = ["lib"]
|
||||||
|
@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.com>"]
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "solana-genesis"
|
name = "solana-genesis"
|
||||||
description = "Blockchain, Rebuilt for Scale"
|
description = "Blockchain, Rebuilt for Scale"
|
||||||
version = "1.0.1"
|
version = "1.0.5"
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
homepage = "https://solana.com/"
|
homepage = "https://solana.com/"
|
||||||
@@ -15,13 +15,13 @@ chrono = "0.4"
|
|||||||
serde = "1.0.104"
|
serde = "1.0.104"
|
||||||
serde_json = "1.0.46"
|
serde_json = "1.0.46"
|
||||||
serde_yaml = "0.8.11"
|
serde_yaml = "0.8.11"
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "1.0.1" }
|
solana-clap-utils = { path = "../clap-utils", version = "1.0.5" }
|
||||||
solana-genesis-programs = { path = "../genesis-programs", version = "1.0.1" }
|
solana-genesis-programs = { path = "../genesis-programs", version = "1.0.5" }
|
||||||
solana-ledger = { path = "../ledger", version = "1.0.1" }
|
solana-ledger = { path = "../ledger", version = "1.0.5" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.0.1" }
|
solana-sdk = { path = "../sdk", version = "1.0.5" }
|
||||||
solana-stake-program = { path = "../programs/stake", version = "1.0.1" }
|
solana-stake-program = { path = "../programs/stake", version = "1.0.5" }
|
||||||
solana-storage-program = { path = "../programs/storage", version = "1.0.1" }
|
solana-storage-program = { path = "../programs/storage", version = "1.0.5" }
|
||||||
solana-vote-program = { path = "../programs/vote", version = "1.0.1" }
|
solana-vote-program = { path = "../programs/vote", version = "1.0.5" }
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
@@ -10,7 +10,16 @@ const UNLOCKS_ALL_AT_9_MONTHS: UnlockInfo = UnlockInfo {
|
|||||||
cliff_years: 0.75,
|
cliff_years: 0.75,
|
||||||
unlocks: 0,
|
unlocks: 0,
|
||||||
unlock_years: 0.0,
|
unlock_years: 0.0,
|
||||||
custodian: "6LnFgiECFQKUcxNYDvUBMxgjeGQzzy4kgxGhantoxfUe",
|
custodian: "Mc5XB47H3DKJHym5RLa9mPzWv5snERsF3KNv5AauXK8",
|
||||||
|
};
|
||||||
|
|
||||||
|
// 9 month schedule is 50% after 9 months, then monthly for 2 years
|
||||||
|
const UNLOCKS_HALF_AT_9_MONTHS: UnlockInfo = UnlockInfo {
|
||||||
|
cliff_fraction: 0.5,
|
||||||
|
cliff_years: 0.75,
|
||||||
|
unlocks: 24,
|
||||||
|
unlock_years: 2.0,
|
||||||
|
custodian: "Mc5XB47H3DKJHym5RLa9mPzWv5snERsF3KNv5AauXK8",
|
||||||
};
|
};
|
||||||
|
|
||||||
// no lockups
|
// no lockups
|
||||||
@@ -19,114 +28,138 @@ const UNLOCKS_ALL_DAY_ZERO: UnlockInfo = UnlockInfo {
|
|||||||
cliff_years: 0.0,
|
cliff_years: 0.0,
|
||||||
unlocks: 0,
|
unlocks: 0,
|
||||||
unlock_years: 0.0,
|
unlock_years: 0.0,
|
||||||
custodian: "6LnFgiECFQKUcxNYDvUBMxgjeGQzzy4kgxGhantoxfUe",
|
custodian: "Mc5XB47H3DKJHym5RLa9mPzWv5snERsF3KNv5AauXK8",
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const BATCH_FOUR_STAKER_INFOS: &[StakerInfo] = &[
|
pub const CREATOR_STAKER_INFOS: &[StakerInfo] = &[
|
||||||
StakerInfo {
|
StakerInfo {
|
||||||
name: "impossible pizza",
|
name: "impossible pizza",
|
||||||
staker: "CDtJpwRSiPRDGeKrvymWQKM7JY9M3hU7iimEKBDxZyoP",
|
staker: "CDtJpwRSiPRDGeKrvymWQKM7JY9M3hU7iimEKBDxZyoP",
|
||||||
lamports: 5_000_000 * LAMPORTS_PER_SOL,
|
lamports: 5_000_000 * LAMPORTS_PER_SOL,
|
||||||
},
|
withdrawer: None,
|
||||||
StakerInfo {
|
|
||||||
name: "wretched texture",
|
|
||||||
staker: "HbENu65qjWLEB5TrMouSSWLq9mbtGx2bvfhPjk2FpYek",
|
|
||||||
lamports: 225_000 * LAMPORTS_PER_SOL,
|
|
||||||
},
|
},
|
||||||
StakerInfo {
|
StakerInfo {
|
||||||
name: "nutritious examination",
|
name: "nutritious examination",
|
||||||
staker: "C9CfFpmLDsQsz6wt7MrrZquNB5oS4QkpJkmDAiboVEZZ",
|
staker: "C9CfFpmLDsQsz6wt7MrrZquNB5oS4QkpJkmDAiboVEZZ",
|
||||||
lamports: 5_000_000 * LAMPORTS_PER_SOL,
|
lamports: 5_000_000 * LAMPORTS_PER_SOL,
|
||||||
|
withdrawer: None,
|
||||||
},
|
},
|
||||||
StakerInfo {
|
StakerInfo {
|
||||||
name: "tidy impression",
|
name: "tidy impression",
|
||||||
staker: "6ne6Rbag4FAnop1KNgVdM1SEHnJEysHSWyqvRpFrzaig",
|
staker: "6ne6Rbag4FAnop1KNgVdM1SEHnJEysHSWyqvRpFrzaig",
|
||||||
lamports: 5_000_000 * LAMPORTS_PER_SOL,
|
lamports: 5_000_000 * LAMPORTS_PER_SOL,
|
||||||
},
|
withdrawer: None,
|
||||||
StakerInfo {
|
|
||||||
name: "unbecoming silver",
|
|
||||||
staker: "4AcoZa1P8fF5XK21RJsiuMRZPEScbbWNc75oakRFHiBz",
|
|
||||||
lamports: 28_800 * LAMPORTS_PER_SOL,
|
|
||||||
},
|
},
|
||||||
StakerInfo {
|
StakerInfo {
|
||||||
name: "dramatic treatment",
|
name: "dramatic treatment",
|
||||||
staker: "GTyawCMwt3kMb51AgDtfdp97mDot7jNwc8ifuS9qqANg",
|
staker: "GTyawCMwt3kMb51AgDtfdp97mDot7jNwc8ifuS9qqANg",
|
||||||
lamports: 1_205_602 * LAMPORTS_PER_SOL,
|
lamports: 1_205_602 * LAMPORTS_PER_SOL,
|
||||||
|
withdrawer: None,
|
||||||
},
|
},
|
||||||
StakerInfo {
|
StakerInfo {
|
||||||
name: "angry noise",
|
name: "angry noise",
|
||||||
staker: "Fqxs9MhqjKuMq6YwjBG4ktEapuZQ3kj19mpuHLZKtkg9",
|
staker: "Fqxs9MhqjKuMq6YwjBG4ktEapuZQ3kj19mpuHLZKtkg9",
|
||||||
lamports: 5_000_000 * LAMPORTS_PER_SOL,
|
lamports: 5_000_000 * LAMPORTS_PER_SOL,
|
||||||
|
withdrawer: None,
|
||||||
},
|
},
|
||||||
StakerInfo {
|
StakerInfo {
|
||||||
name: "hard cousin",
|
name: "hard cousin",
|
||||||
staker: "9MYDzj7QuAX9QAK7da1GhzPB4gA3qbPNWsW3MMSZobru",
|
staker: "9MYDzj7QuAX9QAK7da1GhzPB4gA3qbPNWsW3MMSZobru",
|
||||||
lamports: 5_000_000 * LAMPORTS_PER_SOL,
|
lamports: 5_000_000 * LAMPORTS_PER_SOL,
|
||||||
},
|
withdrawer: None,
|
||||||
StakerInfo {
|
|
||||||
name: "inexpensive uncle",
|
|
||||||
staker: "E4DLNkmdL34ejA48ApfPDoFVuD9XWAFqi8bXzBGRhKst",
|
|
||||||
lamports: 300_000 * LAMPORTS_PER_SOL,
|
|
||||||
},
|
},
|
||||||
StakerInfo {
|
StakerInfo {
|
||||||
name: "lopsided skill",
|
name: "lopsided skill",
|
||||||
staker: "8cV7zCTF5UMrZakZXiL2Jw5uY3ms2Wz4twzFXEY9Kge2",
|
staker: "8cV7zCTF5UMrZakZXiL2Jw5uY3ms2Wz4twzFXEY9Kge2",
|
||||||
lamports: 5_000_000 * LAMPORTS_PER_SOL,
|
lamports: 5_000_000 * LAMPORTS_PER_SOL,
|
||||||
|
withdrawer: None,
|
||||||
},
|
},
|
||||||
StakerInfo {
|
StakerInfo {
|
||||||
name: "red snake",
|
name: "red snake",
|
||||||
staker: "JBGnGdLyo7V2z9hz51mnnbyDp9sBACtw5WYH9YRG8n7e",
|
staker: "JBGnGdLyo7V2z9hz51mnnbyDp9sBACtw5WYH9YRG8n7e",
|
||||||
lamports: 3_655_292 * LAMPORTS_PER_SOL,
|
lamports: 3_655_292 * LAMPORTS_PER_SOL,
|
||||||
},
|
withdrawer: None,
|
||||||
StakerInfo {
|
|
||||||
name: "hellish money",
|
|
||||||
staker: "CqKdQ57mBj2mKcAbpjWc28Ls7yXzBXboxSTCRWocmUVj",
|
|
||||||
lamports: 200_000 * LAMPORTS_PER_SOL,
|
|
||||||
},
|
|
||||||
StakerInfo {
|
|
||||||
name: "full grape",
|
|
||||||
staker: "2SCJKvh7wWo32PtfUZdVZQ84WnMWoUpF4WTm6ZxcCJ15",
|
|
||||||
lamports: 450_000 * LAMPORTS_PER_SOL,
|
|
||||||
},
|
|
||||||
StakerInfo {
|
|
||||||
name: "nice ghost",
|
|
||||||
staker: "FeumxB3gfzrVQzABBiha8AacKPY3Rf4BTFSh2aZWHqR8",
|
|
||||||
lamports: 650_000 * LAMPORTS_PER_SOL,
|
|
||||||
},
|
},
|
||||||
StakerInfo {
|
StakerInfo {
|
||||||
name: "jolly year",
|
name: "jolly year",
|
||||||
staker: "HBwFWNGPVZgkf3yqUKxuAds5aANGWX62LzUFvZVCWLdJ",
|
staker: "HBwFWNGPVZgkf3yqUKxuAds5aANGWX62LzUFvZVCWLdJ",
|
||||||
lamports: 5_000_000 * LAMPORTS_PER_SOL,
|
lamports: 5_000_000 * LAMPORTS_PER_SOL,
|
||||||
|
withdrawer: None,
|
||||||
},
|
},
|
||||||
StakerInfo {
|
StakerInfo {
|
||||||
name: "typical initiative",
|
name: "typical initiative",
|
||||||
staker: "3JMz3kaDUZEVK2JVjRqwERGMp7LbWbgUjAFBb42qxoHb",
|
staker: "3JMz3kaDUZEVK2JVjRqwERGMp7LbWbgUjAFBb42qxoHb",
|
||||||
lamports: 5_000_000 * LAMPORTS_PER_SOL,
|
lamports: 5_000_000 * LAMPORTS_PER_SOL,
|
||||||
|
withdrawer: None,
|
||||||
},
|
},
|
||||||
StakerInfo {
|
StakerInfo {
|
||||||
name: "deserted window",
|
name: "deserted window",
|
||||||
staker: "XTeBBZextvHkoRqDF8yb4hihjcraKQDwTEXhzjd8fip",
|
staker: "XTeBBZextvHkoRqDF8yb4hihjcraKQDwTEXhzjd8fip",
|
||||||
lamports: 3_655_292 * LAMPORTS_PER_SOL,
|
lamports: 3_655_292 * LAMPORTS_PER_SOL,
|
||||||
|
withdrawer: None,
|
||||||
},
|
},
|
||||||
StakerInfo {
|
StakerInfo {
|
||||||
name: "eight nation",
|
name: "eight nation",
|
||||||
staker: "E5bSU5ywqPiz3ije89ef5gaEC7jy81BAc72Zeb9MqeHY",
|
staker: "E5bSU5ywqPiz3ije89ef5gaEC7jy81BAc72Zeb9MqeHY",
|
||||||
lamports: 103_519 * LAMPORTS_PER_SOL,
|
lamports: 103_519 * LAMPORTS_PER_SOL,
|
||||||
|
withdrawer: None,
|
||||||
},
|
},
|
||||||
StakerInfo {
|
StakerInfo {
|
||||||
name: "earsplitting meaning",
|
name: "earsplitting meaning",
|
||||||
staker: "4ZemkSoE75RFE1SVLnnmHcaNWT4qN8KFrKP2wAYfv8CB",
|
staker: "4ZemkSoE75RFE1SVLnnmHcaNWT4qN8KFrKP2wAYfv8CB",
|
||||||
lamports: 5_000_000 * LAMPORTS_PER_SOL,
|
lamports: 5_000_000 * LAMPORTS_PER_SOL,
|
||||||
|
withdrawer: None,
|
||||||
},
|
},
|
||||||
StakerInfo {
|
StakerInfo {
|
||||||
name: "alike cheese",
|
name: "alike cheese",
|
||||||
staker: "72BGEwYee5txFonmpEarTEKCZVN2UxcSUgdphdhcx3V",
|
staker: "72BGEwYee5txFonmpEarTEKCZVN2UxcSUgdphdhcx3V",
|
||||||
lamports: 3_880_295 * LAMPORTS_PER_SOL,
|
lamports: 3_880_295 * LAMPORTS_PER_SOL,
|
||||||
|
withdrawer: None,
|
||||||
},
|
},
|
||||||
StakerInfo {
|
StakerInfo {
|
||||||
name: "noisy honey",
|
name: "noisy honey",
|
||||||
staker: "DRp1Scyn4yJZQfMAdQew2x8RtvRmsNELN37JTK5Xvzgn",
|
staker: "DRp1Scyn4yJZQfMAdQew2x8RtvRmsNELN37JTK5Xvzgn",
|
||||||
lamports: 5_000_000 * LAMPORTS_PER_SOL,
|
lamports: 5_000_000 * LAMPORTS_PER_SOL,
|
||||||
|
withdrawer: None,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
pub const SERVICE_STAKER_INFOS: &[StakerInfo] = &[
|
||||||
|
StakerInfo {
|
||||||
|
name: "wretched texture",
|
||||||
|
staker: "HbENu65qjWLEB5TrMouSSWLq9mbtGx2bvfhPjk2FpYek",
|
||||||
|
lamports: 225_000 * LAMPORTS_PER_SOL,
|
||||||
|
withdrawer: None,
|
||||||
|
},
|
||||||
|
StakerInfo {
|
||||||
|
name: "unbecoming silver",
|
||||||
|
staker: "4AcoZa1P8fF5XK21RJsiuMRZPEScbbWNc75oakRFHiBz",
|
||||||
|
lamports: 28_800 * LAMPORTS_PER_SOL,
|
||||||
|
withdrawer: None,
|
||||||
|
},
|
||||||
|
StakerInfo {
|
||||||
|
name: "inexpensive uncle",
|
||||||
|
staker: "E4DLNkmdL34ejA48ApfPDoFVuD9XWAFqi8bXzBGRhKst",
|
||||||
|
lamports: 300_000 * LAMPORTS_PER_SOL,
|
||||||
|
withdrawer: None,
|
||||||
|
},
|
||||||
|
StakerInfo {
|
||||||
|
name: "hellish money",
|
||||||
|
staker: "CqKdQ57mBj2mKcAbpjWc28Ls7yXzBXboxSTCRWocmUVj",
|
||||||
|
lamports: 200_000 * LAMPORTS_PER_SOL,
|
||||||
|
withdrawer: None,
|
||||||
|
},
|
||||||
|
StakerInfo {
|
||||||
|
name: "full grape",
|
||||||
|
staker: "2SCJKvh7wWo32PtfUZdVZQ84WnMWoUpF4WTm6ZxcCJ15",
|
||||||
|
lamports: 450_000 * LAMPORTS_PER_SOL,
|
||||||
|
withdrawer: None,
|
||||||
|
},
|
||||||
|
StakerInfo {
|
||||||
|
name: "nice ghost",
|
||||||
|
staker: "FeumxB3gfzrVQzABBiha8AacKPY3Rf4BTFSh2aZWHqR8",
|
||||||
|
lamports: 650_000 * LAMPORTS_PER_SOL,
|
||||||
|
withdrawer: None,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -135,11 +168,13 @@ pub const FOUNDATION_STAKER_INFOS: &[StakerInfo] = &[
|
|||||||
name: "lyrical supermarket",
|
name: "lyrical supermarket",
|
||||||
staker: "GRZwoJGisLTszcxtWpeREJ98EGg8pZewhbtcrikoU7b3",
|
staker: "GRZwoJGisLTszcxtWpeREJ98EGg8pZewhbtcrikoU7b3",
|
||||||
lamports: 5_000_000 * LAMPORTS_PER_SOL,
|
lamports: 5_000_000 * LAMPORTS_PER_SOL,
|
||||||
|
withdrawer: None,
|
||||||
},
|
},
|
||||||
StakerInfo {
|
StakerInfo {
|
||||||
name: "frequent description",
|
name: "frequent description",
|
||||||
staker: "J51tinoLdmEdUR27LUVymrb2LB3xQo1aSHSgmbSGdj58",
|
staker: "J51tinoLdmEdUR27LUVymrb2LB3xQo1aSHSgmbSGdj58",
|
||||||
lamports: 57_500_000 * LAMPORTS_PER_SOL,
|
lamports: 57_500_000 * LAMPORTS_PER_SOL,
|
||||||
|
withdrawer: None,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -148,29 +183,34 @@ pub const GRANTS_STAKER_INFOS: &[StakerInfo] = &[
|
|||||||
name: "rightful agreement",
|
name: "rightful agreement",
|
||||||
staker: "DNaKiBwwbbqk1wVoC5AQxWQbuDhvaDVbAtXzsVos9mrc",
|
staker: "DNaKiBwwbbqk1wVoC5AQxWQbuDhvaDVbAtXzsVos9mrc",
|
||||||
lamports: 5_000_000 * LAMPORTS_PER_SOL,
|
lamports: 5_000_000 * LAMPORTS_PER_SOL,
|
||||||
|
withdrawer: None,
|
||||||
},
|
},
|
||||||
StakerInfo {
|
StakerInfo {
|
||||||
name: "tasty location",
|
name: "tasty location",
|
||||||
staker: "HvXQPXAijjG1vnQs6HXVtUUtFVzi5HNgXV9LGnHvYF85",
|
staker: "HvXQPXAijjG1vnQs6HXVtUUtFVzi5HNgXV9LGnHvYF85",
|
||||||
lamports: 15_000_000 * LAMPORTS_PER_SOL,
|
lamports: 15_000_000 * LAMPORTS_PER_SOL,
|
||||||
|
withdrawer: None,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
pub const COMMUNITY_STAKER_INFOS: &[StakerInfo] = &[
|
pub const COMMUNITY_STAKER_INFOS: &[StakerInfo] = &[
|
||||||
StakerInfo {
|
StakerInfo {
|
||||||
name: "shrill charity",
|
name: "shrill charity",
|
||||||
staker: "BzuqQFnu7oNUeok9ZoJezpqu2vZJU7XR1PxVLkk6wwUD",
|
staker: "Eo1iDtrZZiAkQFA8u431hedChaSUnPbU8MWg849MFvEZ",
|
||||||
lamports: 5_000_000 * LAMPORTS_PER_SOL,
|
lamports: 5_000_000 * LAMPORTS_PER_SOL,
|
||||||
|
withdrawer: Some("8CUUMKYNGxdgYio5CLHRHyzMEhhVRMcqefgE6dLqnVRK"),
|
||||||
},
|
},
|
||||||
StakerInfo {
|
StakerInfo {
|
||||||
name: "legal gate",
|
name: "legal gate",
|
||||||
staker: "FwMbkDZUb78aiMWhZY4BEroAcqmnrXZV77nwrg71C57d",
|
staker: "7KCzZCbZz6V1U1YXUpBNaqQzQCg2DKo8JsNhKASKtYxe",
|
||||||
lamports: 16_086_641 * LAMPORTS_PER_SOL,
|
lamports: 16_086_641 * LAMPORTS_PER_SOL,
|
||||||
|
withdrawer: Some("92viKFftk1dJjqJwreFqT2qHXxjSUuEE9VyHvTdY1mpY"),
|
||||||
},
|
},
|
||||||
StakerInfo {
|
StakerInfo {
|
||||||
name: "cluttered complaint",
|
name: "cluttered complaint",
|
||||||
staker: "4h1rt2ic4AXwG7p3Qqhw57EMDD4c3tLYb5J3QstGA2p5",
|
staker: "2J8mJU6tWg78DdQVEqMfpN3rMeNbcRT9qGL3yLbmSXYL",
|
||||||
lamports: 153_333_633 * LAMPORTS_PER_SOL + 41 * LAMPORTS_PER_SOL / 100,
|
lamports: 153_333_633 * LAMPORTS_PER_SOL + 41 * LAMPORTS_PER_SOL / 100,
|
||||||
|
withdrawer: Some("7kgfDmgbEfypBujqn4tyApjf8H7ZWuaL3F6Ah9vQHzgR"),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -178,13 +218,10 @@ fn add_stakes(
|
|||||||
genesis_config: &mut GenesisConfig,
|
genesis_config: &mut GenesisConfig,
|
||||||
staker_infos: &[StakerInfo],
|
staker_infos: &[StakerInfo],
|
||||||
unlock_info: &UnlockInfo,
|
unlock_info: &UnlockInfo,
|
||||||
granularity: u64,
|
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
staker_infos
|
staker_infos
|
||||||
.iter()
|
.iter()
|
||||||
.map(|staker_info| {
|
.map(|staker_info| create_and_add_stakes(genesis_config, staker_info, unlock_info, None))
|
||||||
create_and_add_stakes(genesis_config, staker_info, unlock_info, granularity)
|
|
||||||
})
|
|
||||||
.sum::<u64>()
|
.sum::<u64>()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,24 +231,21 @@ pub fn add_genesis_accounts(genesis_config: &mut GenesisConfig, mut issued_lampo
|
|||||||
|
|
||||||
issued_lamports += add_stakes(
|
issued_lamports += add_stakes(
|
||||||
genesis_config,
|
genesis_config,
|
||||||
&BATCH_FOUR_STAKER_INFOS,
|
&CREATOR_STAKER_INFOS,
|
||||||
|
&UNLOCKS_HALF_AT_9_MONTHS,
|
||||||
|
) + add_stakes(
|
||||||
|
genesis_config,
|
||||||
|
&SERVICE_STAKER_INFOS,
|
||||||
&UNLOCKS_ALL_AT_9_MONTHS,
|
&UNLOCKS_ALL_AT_9_MONTHS,
|
||||||
1_000_000 * LAMPORTS_PER_SOL,
|
|
||||||
) + add_stakes(
|
) + add_stakes(
|
||||||
genesis_config,
|
genesis_config,
|
||||||
&FOUNDATION_STAKER_INFOS,
|
&FOUNDATION_STAKER_INFOS,
|
||||||
&UNLOCKS_ALL_DAY_ZERO,
|
&UNLOCKS_ALL_DAY_ZERO,
|
||||||
1_000_000 * LAMPORTS_PER_SOL,
|
) + add_stakes(genesis_config, &GRANTS_STAKER_INFOS, &UNLOCKS_ALL_DAY_ZERO)
|
||||||
) + add_stakes(
|
+ add_stakes(
|
||||||
genesis_config,
|
|
||||||
&GRANTS_STAKER_INFOS,
|
|
||||||
&UNLOCKS_ALL_DAY_ZERO,
|
|
||||||
1_000_000 * LAMPORTS_PER_SOL,
|
|
||||||
) + add_stakes(
|
|
||||||
genesis_config,
|
genesis_config,
|
||||||
&COMMUNITY_STAKER_INFOS,
|
&COMMUNITY_STAKER_INFOS,
|
||||||
&UNLOCKS_ALL_DAY_ZERO,
|
&UNLOCKS_ALL_DAY_ZERO,
|
||||||
1_000_000 * LAMPORTS_PER_SOL,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// "one thanks" (community pool) gets 500_000_000SOL (total) - above distributions
|
// "one thanks" (community pool) gets 500_000_000SOL (total) - above distributions
|
||||||
@@ -219,11 +253,12 @@ pub fn add_genesis_accounts(genesis_config: &mut GenesisConfig, mut issued_lampo
|
|||||||
genesis_config,
|
genesis_config,
|
||||||
&StakerInfo {
|
&StakerInfo {
|
||||||
name: "one thanks",
|
name: "one thanks",
|
||||||
staker: "3b7akieYUyCgz3Cwt5sTSErMWjg8NEygD6mbGjhGkduB",
|
staker: "7vEAL3nS9CWmy1q6njUUyHE7Cf5RmyQpND6CsoHjzPiR",
|
||||||
lamports: 500_000_000 * LAMPORTS_PER_SOL - issued_lamports,
|
lamports: 500_000_000 * LAMPORTS_PER_SOL - issued_lamports,
|
||||||
|
withdrawer: Some("3FFaheyqtyAXZSYxDzsr5CVKvJuvZD1WE1VEsBtDbRqB"),
|
||||||
},
|
},
|
||||||
&UNLOCKS_ALL_DAY_ZERO,
|
&UNLOCKS_ALL_DAY_ZERO,
|
||||||
1_000_000 * LAMPORTS_PER_SOL,
|
None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
use clap::{crate_description, crate_name, value_t, value_t_or_exit, App, Arg, ArgMatches};
|
use clap::{crate_description, crate_name, value_t, value_t_or_exit, App, Arg, ArgMatches};
|
||||||
use solana_clap_utils::{
|
use solana_clap_utils::{
|
||||||
input_parsers::{pubkey_of, unix_timestamp_from_rfc3339_datetime},
|
input_parsers::{pubkey_of, pubkeys_of, unix_timestamp_from_rfc3339_datetime},
|
||||||
input_validators::{is_rfc3339_datetime, is_valid_percentage},
|
input_validators::{is_pubkey_or_keypair, is_rfc3339_datetime, is_valid_percentage},
|
||||||
};
|
};
|
||||||
use solana_genesis::{genesis_accounts::add_genesis_accounts, Base64Account};
|
use solana_genesis::{genesis_accounts::add_genesis_accounts, Base64Account};
|
||||||
use solana_ledger::{blockstore::create_new_ledger, poh::compute_hashes_per_tick};
|
use solana_ledger::{blockstore::create_new_ledger, poh::compute_hashes_per_tick};
|
||||||
@@ -11,7 +11,7 @@ use solana_sdk::{
|
|||||||
account::Account,
|
account::Account,
|
||||||
clock,
|
clock,
|
||||||
epoch_schedule::EpochSchedule,
|
epoch_schedule::EpochSchedule,
|
||||||
fee_calculator::FeeCalculator,
|
fee_calculator::FeeRateGovernor,
|
||||||
genesis_config::{GenesisConfig, OperatingMode},
|
genesis_config::{GenesisConfig, OperatingMode},
|
||||||
native_token::sol_to_lamports,
|
native_token::sol_to_lamports,
|
||||||
poh_config::PohConfig,
|
poh_config::PohConfig,
|
||||||
@@ -21,16 +21,9 @@ use solana_sdk::{
|
|||||||
system_program, timing,
|
system_program, timing,
|
||||||
};
|
};
|
||||||
use solana_stake_program::stake_state::{self, StakeState};
|
use solana_stake_program::stake_state::{self, StakeState};
|
||||||
use solana_storage_program::storage_contract;
|
|
||||||
use solana_vote_program::vote_state::{self, VoteState};
|
use solana_vote_program::vote_state::{self, VoteState};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, HashMap},
|
collections::HashMap, error, fs::File, io, path::PathBuf, process, str::FromStr, time::Duration,
|
||||||
error,
|
|
||||||
fs::File,
|
|
||||||
io,
|
|
||||||
path::PathBuf,
|
|
||||||
str::FromStr,
|
|
||||||
time::Duration,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub enum AccountFileFormat {
|
pub enum AccountFileFormat {
|
||||||
@@ -38,16 +31,6 @@ pub enum AccountFileFormat {
|
|||||||
Keypair,
|
Keypair,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn required_pubkey(matches: &ArgMatches<'_>, name: &str) -> Result<Pubkey, Box<dyn error::Error>> {
|
|
||||||
pubkey_of(matches, name).ok_or_else(|| {
|
|
||||||
format!(
|
|
||||||
"Invalid pubkey or file: {}",
|
|
||||||
matches.value_of(name).unwrap()
|
|
||||||
)
|
|
||||||
.into()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pubkey_from_str(key_str: &str) -> Result<Pubkey, Box<dyn error::Error>> {
|
fn pubkey_from_str(key_str: &str) -> Result<Pubkey, Box<dyn error::Error>> {
|
||||||
Pubkey::from_str(key_str).or_else(|_| {
|
Pubkey::from_str(key_str).or_else(|_| {
|
||||||
let bytes: Vec<u8> = serde_json::from_str(key_str)?;
|
let bytes: Vec<u8> = serde_json::from_str(key_str)?;
|
||||||
@@ -99,16 +82,16 @@ pub fn load_genesis_accounts(file: &str, genesis_config: &mut GenesisConfig) ->
|
|||||||
|
|
||||||
#[allow(clippy::cognitive_complexity)]
|
#[allow(clippy::cognitive_complexity)]
|
||||||
fn main() -> Result<(), Box<dyn error::Error>> {
|
fn main() -> Result<(), Box<dyn error::Error>> {
|
||||||
let fee_calculator = FeeCalculator::default();
|
let fee_rate_governor = FeeRateGovernor::default();
|
||||||
let (
|
let (
|
||||||
default_target_lamports_per_signature,
|
default_target_lamports_per_signature,
|
||||||
default_target_signatures_per_slot,
|
default_target_signatures_per_slot,
|
||||||
default_fee_burn_percentage,
|
default_fee_burn_percentage,
|
||||||
) = {
|
) = {
|
||||||
(
|
(
|
||||||
&fee_calculator.target_lamports_per_signature.to_string(),
|
&fee_rate_governor.target_lamports_per_signature.to_string(),
|
||||||
&fee_calculator.target_signatures_per_slot.to_string(),
|
&fee_rate_governor.target_signatures_per_slot.to_string(),
|
||||||
&fee_calculator.burn_percent.to_string(),
|
&fee_rate_governor.burn_percent.to_string(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -151,13 +134,16 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||||||
.help("Time when the bootstrap validator will start the cluster [default: current system time]"),
|
.help("Time when the bootstrap validator will start the cluster [default: current system time]"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("bootstrap_validator_pubkey_file")
|
Arg::with_name("bootstrap_validator")
|
||||||
.short("b")
|
.short("b")
|
||||||
.long("bootstrap-validator-pubkey")
|
.long("bootstrap-validator")
|
||||||
.value_name("BOOTSTRAP VALIDATOR PUBKEY")
|
.value_name("IDENTITY_PUBKEY VOTE_PUBKEY STAKE_PUBKEY")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
|
.validator(is_pubkey_or_keypair)
|
||||||
|
.number_of_values(3)
|
||||||
|
.multiple(true)
|
||||||
.required(true)
|
.required(true)
|
||||||
.help("Path to file containing the bootstrap validator's pubkey"),
|
.help("The bootstrap validator's identity, vote and stake pubkeys"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("ledger_path")
|
Arg::with_name("ledger_path")
|
||||||
@@ -174,58 +160,36 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||||||
.long("faucet-lamports")
|
.long("faucet-lamports")
|
||||||
.value_name("LAMPORTS")
|
.value_name("LAMPORTS")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.requires("faucet_pubkey_file")
|
.requires("faucet_pubkey")
|
||||||
.help("Number of lamports to assign to the faucet"),
|
.help("Number of lamports to assign to the faucet"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("faucet_pubkey_file")
|
Arg::with_name("faucet_pubkey")
|
||||||
.short("m")
|
.short("m")
|
||||||
.long("faucet-pubkey")
|
.long("faucet-pubkey")
|
||||||
.value_name("PUBKEY")
|
.value_name("PUBKEY")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
|
.validator(is_pubkey_or_keypair)
|
||||||
.requires("faucet_lamports")
|
.requires("faucet_lamports")
|
||||||
.help("Path to file containing the faucet's pubkey"),
|
.help("Path to file containing the faucet's pubkey"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("bootstrap_vote_pubkey_file")
|
Arg::with_name("bootstrap_stake_authorized_pubkey")
|
||||||
.long("bootstrap-vote-pubkey")
|
|
||||||
.value_name("BOOTSTRAP VOTE PUBKEY")
|
|
||||||
.takes_value(true)
|
|
||||||
.required(true)
|
|
||||||
.help("Path to file containing the bootstrap validator's voting pubkey"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("bootstrap_stake_pubkey_file")
|
|
||||||
.long("bootstrap-stake-pubkey")
|
|
||||||
.value_name("BOOTSTRAP STAKE PUBKEY")
|
|
||||||
.takes_value(true)
|
|
||||||
.required(true)
|
|
||||||
.help("Path to file containing the bootstrap validator's staking pubkey"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("bootstrap_stake_authorized_pubkey_file")
|
|
||||||
.long("bootstrap-stake-authorized-pubkey")
|
.long("bootstrap-stake-authorized-pubkey")
|
||||||
.value_name("BOOTSTRAP STAKE AUTHORIZED PUBKEY")
|
.value_name("BOOTSTRAP STAKE AUTHORIZED PUBKEY")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
|
.validator(is_pubkey_or_keypair)
|
||||||
.help(
|
.help(
|
||||||
"Path to file containing the pubkey authorized to manage the bootstrap \
|
"Path to file containing the pubkey authorized to manage the bootstrap \
|
||||||
validator's stake [default: --bootstrap-validator-pubkey]",
|
validator's stake [default: --bootstrap-validator-pubkey]",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.arg(
|
|
||||||
Arg::with_name("bootstrap_storage_pubkey_file")
|
|
||||||
.long("bootstrap-storage-pubkey")
|
|
||||||
.value_name("BOOTSTRAP STORAGE PUBKEY")
|
|
||||||
.takes_value(true)
|
|
||||||
.help("Path to file containing the bootstrap validator's storage pubkey"),
|
|
||||||
)
|
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("bootstrap_validator_lamports")
|
Arg::with_name("bootstrap_validator_lamports")
|
||||||
.long("bootstrap-validator-lamports")
|
.long("bootstrap-validator-lamports")
|
||||||
.value_name("LAMPORTS")
|
.value_name("LAMPORTS")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.default_value(default_bootstrap_validator_lamports)
|
.default_value(default_bootstrap_validator_lamports)
|
||||||
.required(true)
|
|
||||||
.help("Number of lamports to assign to the bootstrap validator"),
|
.help("Number of lamports to assign to the bootstrap validator"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
@@ -234,7 +198,6 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||||||
.value_name("LAMPORTS")
|
.value_name("LAMPORTS")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.default_value(default_bootstrap_validator_stake_lamports)
|
.default_value(default_bootstrap_validator_stake_lamports)
|
||||||
.required(true)
|
|
||||||
.help("Number of lamports to assign to the bootstrap validator's stake account"),
|
.help("Number of lamports to assign to the bootstrap validator's stake account"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
@@ -391,6 +354,20 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let bootstrap_validator_pubkeys = pubkeys_of(&matches, "bootstrap_validator").unwrap();
|
||||||
|
assert_eq!(bootstrap_validator_pubkeys.len() % 3, 0);
|
||||||
|
|
||||||
|
// Ensure there are no duplicated pubkeys in the --bootstrap-validator list
|
||||||
|
{
|
||||||
|
let mut v = bootstrap_validator_pubkeys.clone();
|
||||||
|
v.sort();
|
||||||
|
v.dedup();
|
||||||
|
if v.len() != bootstrap_validator_pubkeys.len() {
|
||||||
|
eprintln!("Error: --bootstrap-validator pubkeys cannot be duplicated");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let bootstrap_validator_lamports =
|
let bootstrap_validator_lamports =
|
||||||
value_t_or_exit!(matches, "bootstrap_validator_lamports", u64);
|
value_t_or_exit!(matches, "bootstrap_validator_lamports", u64);
|
||||||
|
|
||||||
@@ -400,60 +377,17 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||||||
StakeState::get_rent_exempt_reserve(&rent),
|
StakeState::get_rent_exempt_reserve(&rent),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let bootstrap_validator_pubkey = required_pubkey(&matches, "bootstrap_validator_pubkey_file")?;
|
|
||||||
let bootstrap_vote_pubkey = required_pubkey(&matches, "bootstrap_vote_pubkey_file")?;
|
|
||||||
let bootstrap_stake_pubkey = required_pubkey(&matches, "bootstrap_stake_pubkey_file")?;
|
|
||||||
let bootstrap_stake_authorized_pubkey =
|
let bootstrap_stake_authorized_pubkey =
|
||||||
pubkey_of(&matches, "bootstrap_stake_authorized_pubkey_file");
|
pubkey_of(&matches, "bootstrap_stake_authorized_pubkey");
|
||||||
let bootstrap_storage_pubkey = pubkey_of(&matches, "bootstrap_storage_pubkey_file");
|
let faucet_pubkey = pubkey_of(&matches, "faucet_pubkey");
|
||||||
let faucet_pubkey = pubkey_of(&matches, "faucet_pubkey_file");
|
|
||||||
|
|
||||||
let bootstrap_validator_vote_account = vote_state::create_account(
|
|
||||||
&bootstrap_vote_pubkey,
|
|
||||||
&bootstrap_validator_pubkey,
|
|
||||||
100,
|
|
||||||
VoteState::get_rent_exempt_reserve(&rent).max(1),
|
|
||||||
);
|
|
||||||
|
|
||||||
let bootstrap_validator_stake_account = stake_state::create_account(
|
|
||||||
bootstrap_stake_authorized_pubkey
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&bootstrap_validator_pubkey),
|
|
||||||
&bootstrap_vote_pubkey,
|
|
||||||
&bootstrap_validator_vote_account,
|
|
||||||
&rent,
|
|
||||||
bootstrap_validator_stake_lamports,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut accounts: BTreeMap<Pubkey, Account> = [
|
|
||||||
// node needs an account to issue votes from
|
|
||||||
(
|
|
||||||
bootstrap_validator_pubkey,
|
|
||||||
Account::new(bootstrap_validator_lamports, 0, &system_program::id()),
|
|
||||||
),
|
|
||||||
// where votes go to
|
|
||||||
(bootstrap_vote_pubkey, bootstrap_validator_vote_account),
|
|
||||||
// bootstrap validator stake
|
|
||||||
(bootstrap_stake_pubkey, bootstrap_validator_stake_account),
|
|
||||||
]
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if let Some(bootstrap_storage_pubkey) = bootstrap_storage_pubkey {
|
|
||||||
accounts.insert(
|
|
||||||
bootstrap_storage_pubkey,
|
|
||||||
storage_contract::create_validator_storage_account(bootstrap_validator_pubkey, 1),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let ticks_per_slot = value_t_or_exit!(matches, "ticks_per_slot", u64);
|
let ticks_per_slot = value_t_or_exit!(matches, "ticks_per_slot", u64);
|
||||||
|
|
||||||
let mut fee_calculator = FeeCalculator::new(
|
let mut fee_rate_governor = FeeRateGovernor::new(
|
||||||
value_t_or_exit!(matches, "target_lamports_per_signature", u64),
|
value_t_or_exit!(matches, "target_lamports_per_signature", u64),
|
||||||
value_t_or_exit!(matches, "target_signatures_per_slot", usize),
|
value_t_or_exit!(matches, "target_signatures_per_slot", u64),
|
||||||
);
|
);
|
||||||
fee_calculator.burn_percent = value_t_or_exit!(matches, "fee_burn_percentage", u8);
|
fee_rate_governor.burn_percent = value_t_or_exit!(matches, "fee_burn_percentage", u8);
|
||||||
|
|
||||||
let mut poh_config = PohConfig::default();
|
let mut poh_config = PohConfig::default();
|
||||||
poh_config.target_tick_duration = if matches.is_present("target_tick_duration") {
|
poh_config.target_tick_duration = if matches.is_present("target_tick_duration") {
|
||||||
@@ -508,18 +442,54 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||||||
let inflation = solana_genesis_programs::get_inflation(operating_mode, 0).unwrap();
|
let inflation = solana_genesis_programs::get_inflation(operating_mode, 0).unwrap();
|
||||||
|
|
||||||
let mut genesis_config = GenesisConfig {
|
let mut genesis_config = GenesisConfig {
|
||||||
accounts,
|
|
||||||
native_instruction_processors,
|
native_instruction_processors,
|
||||||
ticks_per_slot,
|
ticks_per_slot,
|
||||||
epoch_schedule,
|
epoch_schedule,
|
||||||
inflation,
|
inflation,
|
||||||
fee_calculator,
|
fee_rate_governor,
|
||||||
rent,
|
rent,
|
||||||
poh_config,
|
poh_config,
|
||||||
operating_mode,
|
operating_mode,
|
||||||
..GenesisConfig::default()
|
..GenesisConfig::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut bootstrap_validator_pubkeys_iter = bootstrap_validator_pubkeys.iter();
|
||||||
|
loop {
|
||||||
|
let identity_pubkey = match bootstrap_validator_pubkeys_iter.next() {
|
||||||
|
None => break,
|
||||||
|
Some(identity_pubkey) => identity_pubkey,
|
||||||
|
};
|
||||||
|
let vote_pubkey = bootstrap_validator_pubkeys_iter.next().unwrap();
|
||||||
|
let stake_pubkey = bootstrap_validator_pubkeys_iter.next().unwrap();
|
||||||
|
|
||||||
|
genesis_config.add_account(
|
||||||
|
*identity_pubkey,
|
||||||
|
Account::new(bootstrap_validator_lamports, 0, &system_program::id()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let vote_account = vote_state::create_account(
|
||||||
|
&vote_pubkey,
|
||||||
|
&identity_pubkey,
|
||||||
|
100,
|
||||||
|
VoteState::get_rent_exempt_reserve(&rent).max(1),
|
||||||
|
);
|
||||||
|
|
||||||
|
genesis_config.add_account(
|
||||||
|
*stake_pubkey,
|
||||||
|
stake_state::create_account(
|
||||||
|
bootstrap_stake_authorized_pubkey
|
||||||
|
.as_ref()
|
||||||
|
.unwrap_or(&identity_pubkey),
|
||||||
|
&vote_pubkey,
|
||||||
|
&vote_account,
|
||||||
|
&rent,
|
||||||
|
bootstrap_validator_stake_lamports,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
genesis_config.add_account(*vote_pubkey, vote_account);
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(creation_time) = unix_timestamp_from_rfc3339_datetime(&matches, "creation_time") {
|
if let Some(creation_time) = unix_timestamp_from_rfc3339_datetime(&matches, "creation_time") {
|
||||||
genesis_config.creation_time = creation_time;
|
genesis_config.creation_time = creation_time;
|
||||||
}
|
}
|
||||||
|
@@ -16,6 +16,7 @@ use solana_stake_program::{
|
|||||||
pub struct StakerInfo {
|
pub struct StakerInfo {
|
||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
pub staker: &'static str,
|
pub staker: &'static str,
|
||||||
|
pub withdrawer: Option<&'static str>,
|
||||||
pub lamports: u64,
|
pub lamports: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,7 +25,7 @@ pub struct StakerInfo {
|
|||||||
// lamports to cover TX fees (delegation) for one year,
|
// lamports to cover TX fees (delegation) for one year,
|
||||||
// and we support one delegation per epoch
|
// and we support one delegation per epoch
|
||||||
fn calculate_staker_fees(genesis_config: &GenesisConfig, years: f64) -> u64 {
|
fn calculate_staker_fees(genesis_config: &GenesisConfig, years: f64) -> u64 {
|
||||||
genesis_config.fee_calculator.max_lamports_per_signature
|
genesis_config.fee_rate_governor.max_lamports_per_signature
|
||||||
* genesis_config.epoch_schedule.get_epoch(years_as_slots(
|
* genesis_config.epoch_schedule.get_epoch(years_as_slots(
|
||||||
years,
|
years,
|
||||||
&genesis_config.poh_config.target_tick_duration,
|
&genesis_config.poh_config.target_tick_duration,
|
||||||
@@ -41,14 +42,22 @@ pub fn create_and_add_stakes(
|
|||||||
// description of how the stakes' lockups will expire
|
// description of how the stakes' lockups will expire
|
||||||
unlock_info: &UnlockInfo,
|
unlock_info: &UnlockInfo,
|
||||||
// the largest each stake account should be, in lamports
|
// the largest each stake account should be, in lamports
|
||||||
granularity: u64,
|
granularity: Option<u64>,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let authorized = Authorized::auto(
|
let granularity = granularity.unwrap_or(std::u64::MAX);
|
||||||
&staker_info
|
let staker = &staker_info
|
||||||
.staker
|
.staker
|
||||||
.parse::<Pubkey>()
|
.parse::<Pubkey>()
|
||||||
.expect("invalid staker"),
|
.expect("invalid staker");
|
||||||
);
|
let withdrawer = &staker_info
|
||||||
|
.withdrawer
|
||||||
|
.unwrap_or(staker_info.staker)
|
||||||
|
.parse::<Pubkey>()
|
||||||
|
.expect("invalid staker");
|
||||||
|
let authorized = Authorized {
|
||||||
|
staker: *staker,
|
||||||
|
withdrawer: *withdrawer,
|
||||||
|
};
|
||||||
let custodian = unlock_info
|
let custodian = unlock_info
|
||||||
.custodian
|
.custodian
|
||||||
.parse::<Pubkey>()
|
.parse::<Pubkey>()
|
||||||
@@ -163,7 +172,7 @@ mod tests {
|
|||||||
) {
|
) {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
total_lamports,
|
total_lamports,
|
||||||
create_and_add_stakes(genesis_config, staker_info, unlock_info, granularity)
|
create_and_add_stakes(genesis_config, staker_info, unlock_info, Some(granularity))
|
||||||
);
|
);
|
||||||
assert_eq!(genesis_config.accounts.len(), len);
|
assert_eq!(genesis_config.accounts.len(), len);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -239,6 +248,7 @@ mod tests {
|
|||||||
name: "fun",
|
name: "fun",
|
||||||
staker: "P1aceHo1derPubkey11111111111111111111111111",
|
staker: "P1aceHo1derPubkey11111111111111111111111111",
|
||||||
lamports: total_lamports,
|
lamports: total_lamports,
|
||||||
|
withdrawer: None,
|
||||||
},
|
},
|
||||||
&UnlockInfo {
|
&UnlockInfo {
|
||||||
cliff_fraction: 0.5,
|
cliff_fraction: 0.5,
|
||||||
@@ -264,6 +274,7 @@ mod tests {
|
|||||||
name: "fun",
|
name: "fun",
|
||||||
staker: "P1aceHo1derPubkey11111111111111111111111111",
|
staker: "P1aceHo1derPubkey11111111111111111111111111",
|
||||||
lamports: total_lamports,
|
lamports: total_lamports,
|
||||||
|
withdrawer: None,
|
||||||
},
|
},
|
||||||
&UnlockInfo {
|
&UnlockInfo {
|
||||||
cliff_fraction: 0.5,
|
cliff_fraction: 0.5,
|
||||||
@@ -289,6 +300,7 @@ mod tests {
|
|||||||
name: "fun",
|
name: "fun",
|
||||||
staker: "P1aceHo1derPubkey11111111111111111111111111",
|
staker: "P1aceHo1derPubkey11111111111111111111111111",
|
||||||
lamports: total_lamports,
|
lamports: total_lamports,
|
||||||
|
withdrawer: None,
|
||||||
},
|
},
|
||||||
&UnlockInfo {
|
&UnlockInfo {
|
||||||
cliff_fraction: 0.5,
|
cliff_fraction: 0.5,
|
||||||
@@ -313,6 +325,7 @@ mod tests {
|
|||||||
name: "fun",
|
name: "fun",
|
||||||
staker: "P1aceHo1derPubkey11111111111111111111111111",
|
staker: "P1aceHo1derPubkey11111111111111111111111111",
|
||||||
lamports: total_lamports,
|
lamports: total_lamports,
|
||||||
|
withdrawer: None,
|
||||||
},
|
},
|
||||||
&UnlockInfo {
|
&UnlockInfo {
|
||||||
cliff_fraction: 0.5,
|
cliff_fraction: 0.5,
|
||||||
|
@@ -3,19 +3,19 @@ authors = ["Solana Maintainers <maintainers@solana.com>"]
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "solana-gossip"
|
name = "solana-gossip"
|
||||||
description = "Blockchain, Rebuilt for Scale"
|
description = "Blockchain, Rebuilt for Scale"
|
||||||
version = "1.0.1"
|
version = "1.0.5"
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
homepage = "https://solana.com/"
|
homepage = "https://solana.com/"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33.0"
|
clap = "2.33.0"
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "1.0.1" }
|
solana-clap-utils = { path = "../clap-utils", version = "1.0.5" }
|
||||||
solana-core = { path = "../core", version = "1.0.1" }
|
solana-core = { path = "../core", version = "1.0.5" }
|
||||||
solana-client = { path = "../client", version = "1.0.1" }
|
solana-client = { path = "../client", version = "1.0.5" }
|
||||||
solana-logger = { path = "../logger", version = "1.0.1" }
|
solana-logger = { path = "../logger", version = "1.0.5" }
|
||||||
solana-net-utils = { path = "../net-utils", version = "1.0.1" }
|
solana-net-utils = { path = "../net-utils", version = "1.0.5" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.0.1" }
|
solana-sdk = { path = "../sdk", version = "1.0.5" }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@@ -197,7 +197,10 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||||||
let gossip_addr = SocketAddr::new(
|
let gossip_addr = SocketAddr::new(
|
||||||
gossip_host,
|
gossip_host,
|
||||||
value_t!(matches, "gossip_port", u16).unwrap_or_else(|_| {
|
value_t!(matches, "gossip_port", u16).unwrap_or_else(|_| {
|
||||||
solana_net_utils::find_available_port_in_range((0, 1))
|
solana_net_utils::find_available_port_in_range(
|
||||||
|
IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
|
||||||
|
(0, 1),
|
||||||
|
)
|
||||||
.expect("unable to find an available gossip port")
|
.expect("unable to find an available gossip port")
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.com>"]
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "solana-install"
|
name = "solana-install"
|
||||||
description = "The solana cluster software installer"
|
description = "The solana cluster software installer"
|
||||||
version = "1.0.1"
|
version = "1.0.5"
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
homepage = "https://solana.com/"
|
homepage = "https://solana.com/"
|
||||||
@@ -24,11 +24,11 @@ reqwest = { version = "0.10.1", default-features = false, features = ["blocking"
|
|||||||
serde = "1.0.104"
|
serde = "1.0.104"
|
||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
serde_yaml = "0.8.11"
|
serde_yaml = "0.8.11"
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "1.0.1" }
|
solana-clap-utils = { path = "../clap-utils", version = "1.0.5" }
|
||||||
solana-client = { path = "../client", version = "1.0.1" }
|
solana-client = { path = "../client", version = "1.0.5" }
|
||||||
solana-config-program = { path = "../programs/config", version = "1.0.1" }
|
solana-config-program = { path = "../programs/config", version = "1.0.5" }
|
||||||
solana-logger = { path = "../logger", version = "1.0.1" }
|
solana-logger = { path = "../logger", version = "1.0.5" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.0.1" }
|
solana-sdk = { path = "../sdk", version = "1.0.5" }
|
||||||
semver = "0.9.0"
|
semver = "0.9.0"
|
||||||
tar = "0.4.26"
|
tar = "0.4.26"
|
||||||
tempdir = "0.3.7"
|
tempdir = "0.3.7"
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-keygen"
|
name = "solana-keygen"
|
||||||
version = "1.0.1"
|
version = "1.0.5"
|
||||||
description = "Solana key generation utility"
|
description = "Solana key generation utility"
|
||||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
@@ -13,10 +13,10 @@ bs58 = "0.3.0"
|
|||||||
clap = "2.33"
|
clap = "2.33"
|
||||||
dirs = "2.0.2"
|
dirs = "2.0.2"
|
||||||
num_cpus = "1.12.0"
|
num_cpus = "1.12.0"
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "1.0.1" }
|
solana-clap-utils = { path = "../clap-utils", version = "1.0.5" }
|
||||||
solana-cli-config = { path = "../cli-config", version = "1.0.1" }
|
solana-cli-config = { path = "../cli-config", version = "1.0.5" }
|
||||||
solana-remote-wallet = { path = "../remote-wallet", version = "1.0.1" }
|
solana-remote-wallet = { path = "../remote-wallet", version = "1.0.5" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.0.1" }
|
solana-sdk = { path = "../sdk", version = "1.0.5" }
|
||||||
tiny-bip39 = "0.7.0"
|
tiny-bip39 = "0.7.0"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
@@ -12,7 +12,7 @@ use solana_clap_utils::{
|
|||||||
SKIP_SEED_PHRASE_VALIDATION_ARG,
|
SKIP_SEED_PHRASE_VALIDATION_ARG,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use solana_cli_config::config::{Config, CONFIG_FILE};
|
use solana_cli_config::{Config, CONFIG_FILE};
|
||||||
use solana_remote_wallet::remote_wallet::{maybe_wallet_manager, RemoteWalletManager};
|
use solana_remote_wallet::remote_wallet::{maybe_wallet_manager, RemoteWalletManager};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
instruction::{AccountMeta, Instruction},
|
instruction::{AccountMeta, Instruction},
|
||||||
|
@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.com>"]
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "solana-ledger-tool"
|
name = "solana-ledger-tool"
|
||||||
description = "Blockchain, Rebuilt for Scale"
|
description = "Blockchain, Rebuilt for Scale"
|
||||||
version = "1.0.1"
|
version = "1.0.5"
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
homepage = "https://solana.com/"
|
homepage = "https://solana.com/"
|
||||||
@@ -14,12 +14,12 @@ clap = "2.33.0"
|
|||||||
histogram = "*"
|
histogram = "*"
|
||||||
serde_json = "1.0.46"
|
serde_json = "1.0.46"
|
||||||
serde_yaml = "0.8.11"
|
serde_yaml = "0.8.11"
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "1.0.1" }
|
solana-clap-utils = { path = "../clap-utils", version = "1.0.5" }
|
||||||
solana-ledger = { path = "../ledger", version = "1.0.1" }
|
solana-ledger = { path = "../ledger", version = "1.0.5" }
|
||||||
solana-logger = { path = "../logger", version = "1.0.1" }
|
solana-logger = { path = "../logger", version = "1.0.5" }
|
||||||
solana-runtime = { path = "../runtime", version = "1.0.1" }
|
solana-runtime = { path = "../runtime", version = "1.0.5" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.0.1" }
|
solana-sdk = { path = "../sdk", version = "1.0.5" }
|
||||||
solana-vote-program = { path = "../programs/vote", version = "1.0.1" }
|
solana-vote-program = { path = "../programs/vote", version = "1.0.5" }
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
@@ -4,6 +4,7 @@ use clap::{
|
|||||||
};
|
};
|
||||||
use histogram;
|
use histogram;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
use solana_clap_utils::input_validators::is_slot;
|
||||||
use solana_ledger::{
|
use solana_ledger::{
|
||||||
bank_forks::{BankForks, SnapshotConfig},
|
bank_forks::{BankForks, SnapshotConfig},
|
||||||
bank_forks_utils,
|
bank_forks_utils,
|
||||||
@@ -576,11 +577,13 @@ fn main() {
|
|||||||
let halt_at_slot_arg = Arg::with_name("halt_at_slot")
|
let halt_at_slot_arg = Arg::with_name("halt_at_slot")
|
||||||
.long("halt-at-slot")
|
.long("halt-at-slot")
|
||||||
.value_name("SLOT")
|
.value_name("SLOT")
|
||||||
|
.validator(is_slot)
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.help("Halt processing at the given slot");
|
.help("Halt processing at the given slot");
|
||||||
let hard_forks_arg = Arg::with_name("hard_forks")
|
let hard_forks_arg = Arg::with_name("hard_forks")
|
||||||
.long("hard-fork")
|
.long("hard-fork")
|
||||||
.value_name("SLOT")
|
.value_name("SLOT")
|
||||||
|
.validator(is_slot)
|
||||||
.multiple(true)
|
.multiple(true)
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.help("Add a hard fork at this slot");
|
.help("Add a hard fork at this slot");
|
||||||
@@ -609,6 +612,7 @@ fn main() {
|
|||||||
Arg::with_name("slots")
|
Arg::with_name("slots")
|
||||||
.index(1)
|
.index(1)
|
||||||
.value_name("SLOTS")
|
.value_name("SLOTS")
|
||||||
|
.validator(is_slot)
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.multiple(true)
|
.multiple(true)
|
||||||
.required(true)
|
.required(true)
|
||||||
@@ -685,6 +689,7 @@ fn main() {
|
|||||||
Arg::with_name("snapshot_slot")
|
Arg::with_name("snapshot_slot")
|
||||||
.index(1)
|
.index(1)
|
||||||
.value_name("SLOT")
|
.value_name("SLOT")
|
||||||
|
.validator(is_slot)
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.help("Slot at which to create the snapshot"),
|
.help("Slot at which to create the snapshot"),
|
||||||
)
|
)
|
||||||
@@ -713,6 +718,24 @@ fn main() {
|
|||||||
.required(true)
|
.required(true)
|
||||||
.help("The location of the YAML file with a list of rollback slot heights and hashes"),
|
.help("The location of the YAML file with a list of rollback slot heights and hashes"),
|
||||||
)
|
)
|
||||||
|
).subcommand(
|
||||||
|
SubCommand::with_name("purge")
|
||||||
|
.about("Purge the ledger at the block height")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("start_slot")
|
||||||
|
.index(1)
|
||||||
|
.value_name("SLOT")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.help("Start slot to purge from."),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("end_slot")
|
||||||
|
.index(2)
|
||||||
|
.value_name("SLOT")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Optional ending slot to stop purging."),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("list-roots")
|
SubCommand::with_name("list-roots")
|
||||||
@@ -993,6 +1016,13 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
("purge", Some(arg_matches)) => {
|
||||||
|
let start_slot = value_t_or_exit!(arg_matches, "start_slot", Slot);
|
||||||
|
let end_slot = value_t!(arg_matches, "end_slot", Slot);
|
||||||
|
let end_slot = end_slot.map_or(None, Some);
|
||||||
|
let blockstore = open_blockstore(&ledger_path);
|
||||||
|
blockstore.purge_slots(start_slot, end_slot);
|
||||||
|
}
|
||||||
("prune", Some(arg_matches)) => {
|
("prune", Some(arg_matches)) => {
|
||||||
if let Some(prune_file_path) = arg_matches.value_of("slot_list") {
|
if let Some(prune_file_path) = arg_matches.value_of("slot_list") {
|
||||||
let blockstore = open_blockstore(&ledger_path);
|
let blockstore = open_blockstore(&ledger_path);
|
||||||
@@ -1088,20 +1118,19 @@ fn main() {
|
|||||||
Ok(metas) => {
|
Ok(metas) => {
|
||||||
let all = arg_matches.is_present("all");
|
let all = arg_matches.is_present("all");
|
||||||
|
|
||||||
println!("Collecting Ledger information...");
|
|
||||||
let slots: Vec<_> = metas.map(|(slot, _)| slot).collect();
|
let slots: Vec<_> = metas.map(|(slot, _)| slot).collect();
|
||||||
if slots.is_empty() {
|
if slots.is_empty() {
|
||||||
println!("Ledger is empty. No slots found.");
|
println!("Ledger is empty");
|
||||||
} else {
|
} else {
|
||||||
let first = slots.first().unwrap();
|
let first = slots.first().unwrap();
|
||||||
let last = slots.last().unwrap_or_else(|| first);
|
let last = slots.last().unwrap_or_else(|| first);
|
||||||
if first != last {
|
if first != last {
|
||||||
println!("Ledger contains data from slots {:?} to {:?}", first, last);
|
println!("Ledger has data for slots {:?} to {:?}", first, last);
|
||||||
if all {
|
if all {
|
||||||
println!("Non-empty slots: {:?}", slots);
|
println!("Non-empty slots: {:?}", slots);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
println!("Ledger only contains some data for slot {:?}", first);
|
println!("Ledger has data for slot {:?}", first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-ledger"
|
name = "solana-ledger"
|
||||||
version = "1.0.1"
|
version = "1.0.5"
|
||||||
description = "Solana ledger"
|
description = "Solana ledger"
|
||||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
@@ -27,19 +27,19 @@ reed-solomon-erasure = { package = "solana-reed-solomon-erasure", version = "4.0
|
|||||||
regex = "1.3.4"
|
regex = "1.3.4"
|
||||||
serde = "1.0.104"
|
serde = "1.0.104"
|
||||||
serde_bytes = "0.11.3"
|
serde_bytes = "0.11.3"
|
||||||
solana-client = { path = "../client", version = "1.0.1" }
|
solana-client = { path = "../client", version = "1.0.5" }
|
||||||
solana-genesis-programs = { path = "../genesis-programs", version = "1.0.1" }
|
solana-genesis-programs = { path = "../genesis-programs", version = "1.0.5" }
|
||||||
solana-logger = { path = "../logger", version = "1.0.1" }
|
solana-logger = { path = "../logger", version = "1.0.5" }
|
||||||
solana-measure = { path = "../measure", version = "1.0.1" }
|
solana-measure = { path = "../measure", version = "1.0.5" }
|
||||||
solana-merkle-tree = { path = "../merkle-tree", version = "1.0.1" }
|
solana-merkle-tree = { path = "../merkle-tree", version = "1.0.5" }
|
||||||
solana-metrics = { path = "../metrics", version = "1.0.1" }
|
solana-metrics = { path = "../metrics", version = "1.0.5" }
|
||||||
solana-perf = { path = "../perf", version = "1.0.1" }
|
solana-perf = { path = "../perf", version = "1.0.5" }
|
||||||
ed25519-dalek = "1.0.0-pre.1"
|
ed25519-dalek = "1.0.0-pre.1"
|
||||||
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.0.1" }
|
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.0.5" }
|
||||||
solana-runtime = { path = "../runtime", version = "1.0.1" }
|
solana-runtime = { path = "../runtime", version = "1.0.5" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.0.1" }
|
solana-sdk = { path = "../sdk", version = "1.0.5" }
|
||||||
solana-stake-program = { path = "../programs/stake", version = "1.0.1" }
|
solana-stake-program = { path = "../programs/stake", version = "1.0.5" }
|
||||||
solana-vote-program = { path = "../programs/vote", version = "1.0.1" }
|
solana-vote-program = { path = "../programs/vote", version = "1.0.5" }
|
||||||
sys-info = "0.5.9"
|
sys-info = "0.5.9"
|
||||||
symlink = "0.1.0"
|
symlink = "0.1.0"
|
||||||
tar = "0.4.26"
|
tar = "0.4.26"
|
||||||
@@ -57,7 +57,7 @@ features = ["lz4"]
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
assert_matches = "1.3.0"
|
assert_matches = "1.3.0"
|
||||||
matches = "0.1.6"
|
matches = "0.1.6"
|
||||||
solana-budget-program = { path = "../programs/budget", version = "1.0.1" }
|
solana-budget-program = { path = "../programs/budget", version = "1.0.5" }
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["lib"]
|
crate-type = ["lib"]
|
||||||
|
@@ -11,6 +11,7 @@ use crate::{
|
|||||||
entry::{create_ticks, Entry},
|
entry::{create_ticks, Entry},
|
||||||
erasure::ErasureConfig,
|
erasure::ErasureConfig,
|
||||||
leader_schedule_cache::LeaderScheduleCache,
|
leader_schedule_cache::LeaderScheduleCache,
|
||||||
|
next_slots_iterator::NextSlotsIterator,
|
||||||
rooted_slot_iterator::RootedSlotIterator,
|
rooted_slot_iterator::RootedSlotIterator,
|
||||||
shred::{Shred, Shredder},
|
shred::{Shred, Shredder},
|
||||||
};
|
};
|
||||||
@@ -437,6 +438,17 @@ impl Blockstore {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn live_slots_iterator<'a>(
|
||||||
|
&'a self,
|
||||||
|
root: Slot,
|
||||||
|
) -> impl Iterator<Item = (Slot, SlotMeta)> + 'a {
|
||||||
|
let root_forks = NextSlotsIterator::new(root, self);
|
||||||
|
|
||||||
|
let orphans_iter = self.orphans_iterator(root + 1).unwrap();
|
||||||
|
root_forks.chain(orphans_iter.flat_map(move |orphan| NextSlotsIterator::new(orphan, self)))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn slot_data_iterator<'a>(
|
pub fn slot_data_iterator<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
slot: Slot,
|
slot: Slot,
|
||||||
@@ -1749,24 +1761,11 @@ impl Blockstore {
|
|||||||
.is_some()
|
.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_orphans(&self, max: Option<usize>) -> Vec<u64> {
|
pub fn orphans_iterator<'a>(&'a self, slot: Slot) -> Result<impl Iterator<Item = u64> + 'a> {
|
||||||
let mut results = vec![];
|
let orphans_iter = self
|
||||||
|
|
||||||
let mut iter = self
|
|
||||||
.db
|
.db
|
||||||
.raw_iterator_cf(self.db.cf_handle::<cf::Orphans>())
|
.iter::<cf::Orphans>(IteratorMode::From(slot, IteratorDirection::Forward))?;
|
||||||
.unwrap();
|
Ok(orphans_iter.map(|(slot, _)| slot))
|
||||||
iter.seek_to_first();
|
|
||||||
while iter.valid() {
|
|
||||||
if let Some(max) = max {
|
|
||||||
if results.len() > max {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
results.push(<cf::Orphans as Column>::index(&iter.key().unwrap()));
|
|
||||||
iter.next();
|
|
||||||
}
|
|
||||||
results
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prune blockstore such that slots higher than `target_slot` are deleted and all references to
|
/// Prune blockstore such that slots higher than `target_slot` are deleted and all references to
|
||||||
@@ -3765,7 +3764,10 @@ pub mod tests {
|
|||||||
.expect("Expect database get to succeed")
|
.expect("Expect database get to succeed")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(is_orphan(&meta));
|
assert!(is_orphan(&meta));
|
||||||
assert_eq!(blockstore.get_orphans(None), vec![1]);
|
assert_eq!(
|
||||||
|
blockstore.orphans_iterator(0).unwrap().collect::<Vec<_>>(),
|
||||||
|
vec![1]
|
||||||
|
);
|
||||||
|
|
||||||
// Write slot 1 which chains to slot 0, so now slot 0 is the
|
// Write slot 1 which chains to slot 0, so now slot 0 is the
|
||||||
// orphan, and slot 1 is no longer the orphan.
|
// orphan, and slot 1 is no longer the orphan.
|
||||||
@@ -3783,7 +3785,10 @@ pub mod tests {
|
|||||||
.expect("Expect database get to succeed")
|
.expect("Expect database get to succeed")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(is_orphan(&meta));
|
assert!(is_orphan(&meta));
|
||||||
assert_eq!(blockstore.get_orphans(None), vec![0]);
|
assert_eq!(
|
||||||
|
blockstore.orphans_iterator(0).unwrap().collect::<Vec<_>>(),
|
||||||
|
vec![0]
|
||||||
|
);
|
||||||
|
|
||||||
// Write some slot that also chains to existing slots and orphan,
|
// Write some slot that also chains to existing slots and orphan,
|
||||||
// nothing should change
|
// nothing should change
|
||||||
@@ -3791,7 +3796,10 @@ pub mod tests {
|
|||||||
let (shred5, _) = make_slot_entries(5, 1, 1);
|
let (shred5, _) = make_slot_entries(5, 1, 1);
|
||||||
blockstore.insert_shreds(shred4, None, false).unwrap();
|
blockstore.insert_shreds(shred4, None, false).unwrap();
|
||||||
blockstore.insert_shreds(shred5, None, false).unwrap();
|
blockstore.insert_shreds(shred5, None, false).unwrap();
|
||||||
assert_eq!(blockstore.get_orphans(None), vec![0]);
|
assert_eq!(
|
||||||
|
blockstore.orphans_iterator(0).unwrap().collect::<Vec<_>>(),
|
||||||
|
vec![0]
|
||||||
|
);
|
||||||
|
|
||||||
// Write zeroth slot, no more orphans
|
// Write zeroth slot, no more orphans
|
||||||
blockstore.insert_shreds(shreds, None, false).unwrap();
|
blockstore.insert_shreds(shreds, None, false).unwrap();
|
||||||
|
@@ -258,6 +258,7 @@ pub trait EntrySlice {
|
|||||||
fn verify_tick_hash_count(&self, tick_hash_count: &mut u64, hashes_per_tick: u64) -> bool;
|
fn verify_tick_hash_count(&self, tick_hash_count: &mut u64, hashes_per_tick: u64) -> bool;
|
||||||
/// Counts tick entries
|
/// Counts tick entries
|
||||||
fn tick_count(&self) -> u64;
|
fn tick_count(&self) -> u64;
|
||||||
|
fn verify_transaction_signatures(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EntrySlice for [Entry] {
|
impl EntrySlice for [Entry] {
|
||||||
@@ -304,11 +305,35 @@ impl EntrySlice for [Entry] {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn verify_transaction_signatures(&self) -> bool {
|
||||||
|
PAR_THREAD_POOL.with(|thread_pool| {
|
||||||
|
thread_pool.borrow().install(|| {
|
||||||
|
self.par_iter().all(|e| {
|
||||||
|
e.transactions
|
||||||
|
.par_iter()
|
||||||
|
.all(|transaction| transaction.verify().is_ok())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn start_verify(
|
fn start_verify(
|
||||||
&self,
|
&self,
|
||||||
start_hash: &Hash,
|
start_hash: &Hash,
|
||||||
recyclers: VerifyRecyclers,
|
recyclers: VerifyRecyclers,
|
||||||
) -> EntryVerificationState {
|
) -> EntryVerificationState {
|
||||||
|
let start = Instant::now();
|
||||||
|
let res = self.verify_transaction_signatures();
|
||||||
|
if !res {
|
||||||
|
return EntryVerificationState::CPU(VerificationData {
|
||||||
|
thread_h: None,
|
||||||
|
verification_status: EntryVerificationStatus::Failure,
|
||||||
|
duration_ms: timing::duration_as_ms(&start.elapsed()),
|
||||||
|
hashes: None,
|
||||||
|
tx_hashes: vec![],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let api = perf_libs::api();
|
let api = perf_libs::api();
|
||||||
if api.is_none() {
|
if api.is_none() {
|
||||||
return self.verify_cpu(start_hash);
|
return self.verify_cpu(start_hash);
|
||||||
@@ -316,8 +341,6 @@ impl EntrySlice for [Entry] {
|
|||||||
let api = api.unwrap();
|
let api = api.unwrap();
|
||||||
inc_new_counter_warn!("entry_verify-num_entries", self.len() as usize);
|
inc_new_counter_warn!("entry_verify-num_entries", self.len() as usize);
|
||||||
|
|
||||||
let start = Instant::now();
|
|
||||||
|
|
||||||
let genesis = [Entry {
|
let genesis = [Entry {
|
||||||
num_hashes: 0,
|
num_hashes: 0,
|
||||||
hash: *start_hash,
|
hash: *start_hash,
|
||||||
@@ -509,6 +532,40 @@ mod tests {
|
|||||||
assert!(!e0.verify(&zero));
|
assert!(!e0.verify(&zero));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_transaction_signing() {
|
||||||
|
use solana_sdk::signature::Signature;
|
||||||
|
let zero = Hash::default();
|
||||||
|
|
||||||
|
let keypair = Keypair::new();
|
||||||
|
let tx0 = system_transaction::transfer(&keypair, &keypair.pubkey(), 0, zero);
|
||||||
|
let tx1 = system_transaction::transfer(&keypair, &keypair.pubkey(), 1, zero);
|
||||||
|
|
||||||
|
// Verify entry with 2 transctions
|
||||||
|
let mut e0 = vec![Entry::new(&zero, 0, vec![tx0.clone(), tx1.clone()])];
|
||||||
|
assert!(e0.verify(&zero));
|
||||||
|
|
||||||
|
// Clear signature of the first transaction, see that it does not verify
|
||||||
|
let orig_sig = e0[0].transactions[0].signatures[0];
|
||||||
|
e0[0].transactions[0].signatures[0] = Signature::default();
|
||||||
|
assert!(!e0.verify(&zero));
|
||||||
|
|
||||||
|
// restore original signature
|
||||||
|
e0[0].transactions[0].signatures[0] = orig_sig;
|
||||||
|
assert!(e0.verify(&zero));
|
||||||
|
|
||||||
|
// Resize signatures and see verification fails.
|
||||||
|
let len = e0[0].transactions[0].signatures.len();
|
||||||
|
e0[0].transactions[0]
|
||||||
|
.signatures
|
||||||
|
.resize(len - 1, Signature::default());
|
||||||
|
assert!(!e0.verify(&zero));
|
||||||
|
|
||||||
|
// Pass an entry with no transactions
|
||||||
|
let e0 = vec![Entry::new(&zero, 0, vec![])];
|
||||||
|
assert!(e0.verify(&zero));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_witness_reorder_attack() {
|
fn test_witness_reorder_attack() {
|
||||||
let zero = Hash::default();
|
let zero = Hash::default();
|
||||||
@@ -597,7 +654,7 @@ mod tests {
|
|||||||
let zero = Hash::default();
|
let zero = Hash::default();
|
||||||
let one = hash(&zero.as_ref());
|
let one = hash(&zero.as_ref());
|
||||||
let two = hash(&one.as_ref());
|
let two = hash(&one.as_ref());
|
||||||
let alice_pubkey = Keypair::default();
|
let alice_pubkey = Keypair::new();
|
||||||
let tx0 = create_sample_payment(&alice_pubkey, one);
|
let tx0 = create_sample_payment(&alice_pubkey, one);
|
||||||
let tx1 = create_sample_timestamp(&alice_pubkey, one);
|
let tx1 = create_sample_timestamp(&alice_pubkey, one);
|
||||||
assert_eq!(vec![][..].verify(&one), true); // base case
|
assert_eq!(vec![][..].verify(&one), true); // base case
|
||||||
|
@@ -12,6 +12,7 @@ pub mod genesis_utils;
|
|||||||
pub mod leader_schedule;
|
pub mod leader_schedule;
|
||||||
pub mod leader_schedule_cache;
|
pub mod leader_schedule_cache;
|
||||||
pub mod leader_schedule_utils;
|
pub mod leader_schedule_utils;
|
||||||
|
pub mod next_slots_iterator;
|
||||||
pub mod poh;
|
pub mod poh;
|
||||||
pub mod rooted_slot_iterator;
|
pub mod rooted_slot_iterator;
|
||||||
pub mod shred;
|
pub mod shred;
|
||||||
|
117
ledger/src/next_slots_iterator.rs
Normal file
117
ledger/src/next_slots_iterator.rs
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
use crate::{blockstore::*, blockstore_meta::SlotMeta};
|
||||||
|
use solana_sdk::clock::Slot;
|
||||||
|
|
||||||
|
pub struct NextSlotsIterator<'a> {
|
||||||
|
pending_slots: Vec<Slot>,
|
||||||
|
blockstore: &'a Blockstore,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> NextSlotsIterator<'a> {
|
||||||
|
pub fn new(start_slot: Slot, blockstore: &'a Blockstore) -> Self {
|
||||||
|
Self {
|
||||||
|
pending_slots: vec![start_slot],
|
||||||
|
blockstore,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for NextSlotsIterator<'a> {
|
||||||
|
type Item = (Slot, SlotMeta);
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.pending_slots.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let slot = self.pending_slots.pop().unwrap();
|
||||||
|
if let Some(slot_meta) = self.blockstore.meta(slot).unwrap() {
|
||||||
|
self.pending_slots.extend(slot_meta.next_slots.iter());
|
||||||
|
Some((slot, slot_meta))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::blockstore_processor::fill_blockstore_slot_with_ticks;
|
||||||
|
use solana_sdk::hash::Hash;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_next_slots_iterator() {
|
||||||
|
let blockstore_path = get_tmp_ledger_path!();
|
||||||
|
let blockstore = Blockstore::open(&blockstore_path).unwrap();
|
||||||
|
blockstore.set_roots(&[0]).unwrap();
|
||||||
|
let ticks_per_slot = 5;
|
||||||
|
/*
|
||||||
|
Build a blockstore in the ledger with the following fork structure:
|
||||||
|
|
||||||
|
slot 0
|
||||||
|
|
|
||||||
|
slot 1 <-- set_root
|
||||||
|
/ \
|
||||||
|
slot 2 |
|
||||||
|
/ |
|
||||||
|
slot 3 |
|
||||||
|
|
|
||||||
|
slot 4
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Fork 1, ending at slot 3
|
||||||
|
let last_entry_hash = Hash::default();
|
||||||
|
let fork_point = 1;
|
||||||
|
let mut fork_hash = Hash::default();
|
||||||
|
for slot in 0..=3 {
|
||||||
|
let parent = {
|
||||||
|
if slot == 0 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
slot - 1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let last_entry_hash = fill_blockstore_slot_with_ticks(
|
||||||
|
&blockstore,
|
||||||
|
ticks_per_slot,
|
||||||
|
slot,
|
||||||
|
parent,
|
||||||
|
last_entry_hash,
|
||||||
|
);
|
||||||
|
|
||||||
|
if slot == fork_point {
|
||||||
|
fork_hash = last_entry_hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fork 2, ending at slot 4
|
||||||
|
let _ =
|
||||||
|
fill_blockstore_slot_with_ticks(&blockstore, ticks_per_slot, 4, fork_point, fork_hash);
|
||||||
|
|
||||||
|
// Trying to get an iterator on any slot on the root fork should succeed
|
||||||
|
let result: HashSet<_> = NextSlotsIterator::new(0, &blockstore)
|
||||||
|
.into_iter()
|
||||||
|
.map(|(slot, _)| slot)
|
||||||
|
.collect();
|
||||||
|
let expected = vec![0, 1, 2, 3, 4].into_iter().collect();
|
||||||
|
assert_eq!(result, expected);
|
||||||
|
|
||||||
|
let result: HashSet<_> = NextSlotsIterator::new(2, &blockstore)
|
||||||
|
.into_iter()
|
||||||
|
.map(|(slot, _)| slot)
|
||||||
|
.collect();
|
||||||
|
let expected = vec![2, 3].into_iter().collect();
|
||||||
|
assert_eq!(result, expected);
|
||||||
|
|
||||||
|
let result: HashSet<_> = NextSlotsIterator::new(4, &blockstore)
|
||||||
|
.into_iter()
|
||||||
|
.map(|(slot, _)| slot)
|
||||||
|
.collect();
|
||||||
|
let expected = vec![4].into_iter().collect();
|
||||||
|
assert_eq!(result, expected);
|
||||||
|
|
||||||
|
drop(blockstore);
|
||||||
|
Blockstore::destroy(&blockstore_path).expect("Expected successful database destruction");
|
||||||
|
}
|
||||||
|
}
|
@@ -337,7 +337,7 @@ pub fn add_snapshot<P: AsRef<Path>>(
|
|||||||
bank: &Bank,
|
bank: &Bank,
|
||||||
snapshot_storages: &[SnapshotStorage],
|
snapshot_storages: &[SnapshotStorage],
|
||||||
) -> Result<SlotSnapshotPaths> {
|
) -> Result<SlotSnapshotPaths> {
|
||||||
bank.purge_zero_lamport_accounts();
|
bank.clean_accounts();
|
||||||
bank.update_accounts_hash();
|
bank.update_accounts_hash();
|
||||||
let slot = bank.slot();
|
let slot = bank.slot();
|
||||||
// snapshot_path/slot
|
// snapshot_path/slot
|
||||||
|
@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.com>"]
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "solana-local-cluster"
|
name = "solana-local-cluster"
|
||||||
description = "Blockchain, Rebuilt for Scale"
|
description = "Blockchain, Rebuilt for Scale"
|
||||||
version = "1.0.1"
|
version = "1.0.5"
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
homepage = "https://solana.com/"
|
homepage = "https://solana.com/"
|
||||||
@@ -12,23 +12,23 @@ homepage = "https://solana.com/"
|
|||||||
itertools = "0.8.1"
|
itertools = "0.8.1"
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
rand = "0.6.5"
|
rand = "0.6.5"
|
||||||
solana-archiver-lib = { path = "../archiver-lib", version = "1.0.1" }
|
solana-archiver-lib = { path = "../archiver-lib", version = "1.0.5" }
|
||||||
solana-config-program = { path = "../programs/config", version = "1.0.1" }
|
solana-config-program = { path = "../programs/config", version = "1.0.5" }
|
||||||
solana-core = { path = "../core", version = "1.0.1" }
|
solana-core = { path = "../core", version = "1.0.5" }
|
||||||
solana-client = { path = "../client", version = "1.0.1" }
|
solana-client = { path = "../client", version = "1.0.5" }
|
||||||
solana-faucet = { path = "../faucet", version = "1.0.1" }
|
solana-faucet = { path = "../faucet", version = "1.0.5" }
|
||||||
solana-exchange-program = { path = "../programs/exchange", version = "1.0.1" }
|
solana-exchange-program = { path = "../programs/exchange", version = "1.0.5" }
|
||||||
solana-genesis-programs = { path = "../genesis-programs", version = "1.0.1" }
|
solana-genesis-programs = { path = "../genesis-programs", version = "1.0.5" }
|
||||||
solana-ledger = { path = "../ledger", version = "1.0.1" }
|
solana-ledger = { path = "../ledger", version = "1.0.5" }
|
||||||
solana-logger = { path = "../logger", version = "1.0.1" }
|
solana-logger = { path = "../logger", version = "1.0.5" }
|
||||||
solana-runtime = { path = "../runtime", version = "1.0.1" }
|
solana-runtime = { path = "../runtime", version = "1.0.5" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.0.1" }
|
solana-sdk = { path = "../sdk", version = "1.0.5" }
|
||||||
solana-stake-program = { path = "../programs/stake", version = "1.0.1" }
|
solana-stake-program = { path = "../programs/stake", version = "1.0.5" }
|
||||||
solana-storage-program = { path = "../programs/storage", version = "1.0.1" }
|
solana-storage-program = { path = "../programs/storage", version = "1.0.5" }
|
||||||
solana-vest-program = { path = "../programs/vest", version = "1.0.1" }
|
solana-vest-program = { path = "../programs/vest", version = "1.0.5" }
|
||||||
solana-vote-program = { path = "../programs/vote", version = "1.0.1" }
|
solana-vote-program = { path = "../programs/vote", version = "1.0.5" }
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.0.1" }
|
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.0.5" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
assert_matches = "1.3.0"
|
assert_matches = "1.3.0"
|
||||||
|
@@ -158,7 +158,7 @@ fn test_validator_exit_2() {
|
|||||||
let num_nodes = 2;
|
let num_nodes = 2;
|
||||||
let mut validator_config = ValidatorConfig::default();
|
let mut validator_config = ValidatorConfig::default();
|
||||||
validator_config.rpc_config.enable_validator_exit = true;
|
validator_config.rpc_config.enable_validator_exit = true;
|
||||||
validator_config.wait_for_supermajority = true;
|
validator_config.wait_for_supermajority = Some(0);
|
||||||
|
|
||||||
let config = ClusterConfig {
|
let config = ClusterConfig {
|
||||||
cluster_lamports: 10_000,
|
cluster_lamports: 10_000,
|
||||||
@@ -217,7 +217,9 @@ fn run_cluster_partition(
|
|||||||
assert_eq!(node_stakes.len(), num_nodes);
|
assert_eq!(node_stakes.len(), num_nodes);
|
||||||
let cluster_lamports = node_stakes.iter().sum::<u64>() * 2;
|
let cluster_lamports = node_stakes.iter().sum::<u64>() * 2;
|
||||||
let partition_start_epoch = 2;
|
let partition_start_epoch = 2;
|
||||||
|
let enable_partition = Arc::new(AtomicBool::new(true));
|
||||||
let mut validator_config = ValidatorConfig::default();
|
let mut validator_config = ValidatorConfig::default();
|
||||||
|
validator_config.enable_partition = Some(enable_partition.clone());
|
||||||
|
|
||||||
// Returns:
|
// Returns:
|
||||||
// 1) The keys for the validiators
|
// 1) The keys for the validiators
|
||||||
@@ -255,7 +257,6 @@ fn run_cluster_partition(
|
|||||||
..ClusterConfig::default()
|
..ClusterConfig::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let enable_partition = Some(Arc::new(AtomicBool::new(true)));
|
|
||||||
info!(
|
info!(
|
||||||
"PARTITION_TEST starting cluster with {:?} partitions slots_per_epoch: {}",
|
"PARTITION_TEST starting cluster with {:?} partitions slots_per_epoch: {}",
|
||||||
partitions, config.slots_per_epoch,
|
partitions, config.slots_per_epoch,
|
||||||
@@ -282,10 +283,7 @@ fn run_cluster_partition(
|
|||||||
|
|
||||||
if reached_epoch {
|
if reached_epoch {
|
||||||
info!("PARTITION_TEST start partition");
|
info!("PARTITION_TEST start partition");
|
||||||
enable_partition
|
enable_partition.clone().store(false, Ordering::Relaxed);
|
||||||
.clone()
|
|
||||||
.unwrap()
|
|
||||||
.store(false, Ordering::Relaxed);
|
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
sleep(Duration::from_millis(100));
|
sleep(Duration::from_millis(100));
|
||||||
@@ -294,7 +292,7 @@ fn run_cluster_partition(
|
|||||||
sleep(Duration::from_millis(leader_schedule_time));
|
sleep(Duration::from_millis(leader_schedule_time));
|
||||||
|
|
||||||
info!("PARTITION_TEST remove partition");
|
info!("PARTITION_TEST remove partition");
|
||||||
enable_partition.unwrap().store(true, Ordering::Relaxed);
|
enable_partition.store(true, Ordering::Relaxed);
|
||||||
|
|
||||||
let mut dead_nodes = HashSet::new();
|
let mut dead_nodes = HashSet::new();
|
||||||
let mut alive_node_contact_infos = vec![];
|
let mut alive_node_contact_infos = vec![];
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user