Compare commits

...

37 Commits

Author SHA1 Message Date
47ddb84078 cli: Add --follow option to catchup command to allow for easy ongoing monitoring between two nodes (bp #9260) (#9278)
automerge
2020-04-02 20:43:19 -07:00
4649378f95 ReplayStage fixes (#9271)
Co-authored-by: Carl <carl@solana.com>
2020-04-02 18:12:59 -07:00
3f6027055c Tame overeager wallet manager (#9262) (#9272)
automerge
2020-04-02 16:54:14 -07:00
d61a46476a Backport wallet doc changes to v1.1 (#9266)
* Add ledger live screenshots and reduce duplicate instructions (#9258)

automerge

* Add instructions for Trust Wallet Beta for Android (#9261)

automerge
2020-04-02 15:36:50 -06:00
c112f51f97 Add instructions for Trust Wallet Beta for Android (#9261) (#9265)
automerge
2020-04-02 12:25:46 -07:00
c1351d6b12 Set checks timeout to 20 minutes 2020-04-02 13:11:22 -06:00
c1acfe4843 Add epoch subcommand (#9249) (#9255)
automerge
2020-04-01 21:41:22 -07:00
68a4288078 Place AccountsHashes in same enum ordinal position as the v1.0 version (#9251) (#9253)
automerge

(cherry picked from commit 8b14eb9020)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-04-01 19:29:55 -07:00
c4c96e1460 Undo breaking rpc removal of getSignatureConfirmation (#9245) (#9250)
automerge
2020-04-01 17:57:41 -07:00
32ab57fa83 Do not trigger tests if only docs were modified (#9240) (#9242)
automerge
2020-04-01 14:30:36 -07:00
a33e8cc164 Do not trigger tests if only docs were modified (#9240) (#9243) 2020-04-01 14:46:38 -06:00
c8b4f616b0 Undo getSignatureStatus breaking change, add getSignatureStatuses (#9232)
automerge
2020-04-01 11:53:55 -07:00
380c3b0080 Add fee-payer option to docs (#9230) (#9237)
automerge
2020-04-01 11:29:39 -07:00
2d6847c27b Add a support page for wallet docs (#9229) (#9235)
automerge
2020-04-01 11:26:36 -07:00
d5b9899ac9 Clean up solana-stake-accounts (#9211) (#9213)
automerge
2020-04-01 10:37:27 -07:00
9817cd769a Fix solana-stake-accounts rebase/move (#9199) (#9210)
automerge
2020-04-01 09:11:54 -07:00
ec3d2fdbdc Fix repair dos (#9056) (#9221)
automerge
2020-04-01 07:47:15 -07:00
1f794fb1da Tune udp buffers and vmmap immediately (#9194) (#9217)
automerge
2020-04-01 01:19:19 -07:00
89e1d7300d Fix error with account hash list getting too big for gossip (#9197) (#9215)
automerge
2020-03-31 23:44:58 -07:00
d239550e68 Fix panic (#9195) (#9209)
automerge
2020-03-31 21:35:12 -07:00
3dc336e1f1 Remove unecessary exception and add a new one (#9200) (#9206)
(cherry picked from commit 62e12e3af5)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-03-31 19:38:37 -07:00
220a369efa Enforce an executable's rent exemption in the runtime (#9134) (#9191)
(cherry picked from commit 130c0b484d)

Co-authored-by: Jack May <jack@solana.com>
2020-03-31 11:57:19 -07:00
b079564a13 Add more Ledger wallet documentation (#9182) (#9190)
automerge
2020-03-31 10:08:02 -07:00
e8935aa99e Fix links (#9184) (#9188)
automerge
2020-03-31 09:57:43 -07:00
016a342de0 solana-validator now supports multiple --authorized-voter arguments (#9174) (#9181)
automerge
2020-03-31 09:21:47 -07:00
47c6dfe1aa Bump version to v1.1.1 2020-03-30 23:15:07 -07:00
c66d528e85 Check ClusterSlots for confirmation of block propagation (#9115) (#9178)
(cherry picked from commit 66946a4680)

Co-authored-by: carllin <wumu727@gmail.com>
2020-03-30 23:09:00 -07:00
8ba8deb933 Ledger cleanup fixes (#9131) (#9176)
automerge
2020-03-30 20:41:48 -07:00
587342d5e3 Install solana-stake-accounts (#9169) (#9173)
automerge
2020-03-30 19:53:39 -07:00
f31d2d9cc4 Use cluster confirmations in rpc and pubsub (#9138) (#9170)
automerge
2020-03-30 18:11:45 -07:00
bc761c2c02 Add solana-stake-accounts CLI tool (bp #9164) (#9168)
automerge
2020-03-30 17:25:07 -07:00
6f4bc3aaff Store BlockCommitmentCache slot and root metadata (#9154) (#9162)
automerge
2020-03-30 11:40:11 -07:00
070664ff94 Make repair metrics less chatty (#9094) (#9156)
automerge
2020-03-29 16:18:48 -07:00
61c2883de6 Calculate ref counts earlier to prevent bad clean (#9147) (#9155)
automerge
2020-03-29 15:53:56 -07:00
e32f7dbe49 catchup now retries when the desired node is not yet online (#9148) (#9152)
automerge
2020-03-29 10:39:56 -07:00
c0b178db45 Sanitize zero lamport accounts in append vecs (#9083) (#9149)
automerge
2020-03-29 00:39:28 -07:00
1027b0681b Fix race in RPC subscriptions test (#9142) (#9145)
automerge
2020-03-28 12:00:20 -07:00
158 changed files with 4523 additions and 1917 deletions

768
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -52,6 +52,7 @@ members = [
"sdk", "sdk",
"sdk-c", "sdk-c",
"scripts", "scripts",
"stake-accounts",
"stake-monitor", "stake-monitor",
"sys-tuner", "sys-tuner",
"transaction-status", "transaction-status",

View File

@ -2,7 +2,7 @@
authors = ["Solana Maintainers <maintainers@solana.com>"] authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018" edition = "2018"
name = "solana-accounts-bench" name = "solana-accounts-bench"
version = "1.1.0" version = "1.1.1"
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,10 +10,10 @@ homepage = "https://solana.com/"
[dependencies] [dependencies]
log = "0.4.6" log = "0.4.6"
rayon = "1.3.0" rayon = "1.3.0"
solana-logger = { path = "../logger", version = "1.1.0" } solana-logger = { path = "../logger", version = "1.1.1" }
solana-runtime = { path = "../runtime", version = "1.1.0" } solana-runtime = { path = "../runtime", version = "1.1.1" }
solana-measure = { path = "../measure", version = "1.1.0" } solana-measure = { path = "../measure", version = "1.1.1" }
solana-sdk = { path = "../sdk", version = "1.1.0" } solana-sdk = { path = "../sdk", version = "1.1.1" }
rand = "0.6.5" rand = "0.6.5"
clap = "2.33.0" clap = "2.33.0"
crossbeam-channel = "0.4" crossbeam-channel = "0.4"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "solana-archiver-lib" name = "solana-archiver-lib"
version = "1.1.0" version = "1.1.1"
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,23 +15,23 @@ 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.1.0" } solana-client = { path = "../client", version = "1.1.1" }
solana-storage-program = { path = "../programs/storage", version = "1.1.0" } solana-storage-program = { path = "../programs/storage", version = "1.1.1" }
thiserror = "1.0" thiserror = "1.0"
serde = "1.0.105" serde = "1.0.105"
serde_json = "1.0.48" serde_json = "1.0.48"
serde_derive = "1.0.103" serde_derive = "1.0.103"
solana-net-utils = { path = "../net-utils", version = "1.1.0" } solana-net-utils = { path = "../net-utils", version = "1.1.1" }
solana-chacha = { path = "../chacha", version = "1.1.0" } solana-chacha = { path = "../chacha", version = "1.1.1" }
solana-chacha-sys = { path = "../chacha-sys", version = "1.1.0" } solana-chacha-sys = { path = "../chacha-sys", version = "1.1.1" }
solana-ledger = { path = "../ledger", version = "1.1.0" } solana-ledger = { path = "../ledger", version = "1.1.1" }
solana-logger = { path = "../logger", version = "1.1.0" } solana-logger = { path = "../logger", version = "1.1.1" }
solana-perf = { path = "../perf", version = "1.1.0" } solana-perf = { path = "../perf", version = "1.1.1" }
solana-sdk = { path = "../sdk", version = "1.1.0" } solana-sdk = { path = "../sdk", version = "1.1.1" }
solana-core = { path = "../core", version = "1.1.0" } solana-core = { path = "../core", version = "1.1.1" }
solana-streamer = { path = "../streamer", version = "1.1.0" } solana-streamer = { path = "../streamer", version = "1.1.1" }
solana-archiver-utils = { path = "../archiver-utils", version = "1.1.0" } solana-archiver-utils = { path = "../archiver-utils", version = "1.1.1" }
solana-metrics = { path = "../metrics", version = "1.1.0" } solana-metrics = { path = "../metrics", version = "1.1.1" }
[dev-dependencies] [dev-dependencies]
hex = "0.4.2" hex = "0.4.2"

View File

@ -14,7 +14,7 @@ use solana_core::{
contact_info::ContactInfo, contact_info::ContactInfo,
gossip_service::GossipService, gossip_service::GossipService,
repair_service, repair_service,
repair_service::{RepairService, RepairSlotRange, RepairStrategy}, repair_service::{RepairService, RepairSlotRange, RepairStats, RepairStrategy},
serve_repair::ServeRepair, serve_repair::ServeRepair,
shred_fetch_stage::ShredFetchStage, shred_fetch_stage::ShredFetchStage,
sigverify_stage::{DisabledSigVerifier, SigVerifyStage}, sigverify_stage::{DisabledSigVerifier, SigVerifyStage},
@ -844,13 +844,14 @@ impl Archiver {
repair_service::MAX_REPAIR_LENGTH, repair_service::MAX_REPAIR_LENGTH,
&repair_slot_range, &repair_slot_range,
); );
let mut repair_stats = RepairStats::default();
//iter over the repairs and send them //iter over the repairs and send them
if let Ok(repairs) = repairs { if let Ok(repairs) = repairs {
let reqs: Vec<_> = repairs let reqs: Vec<_> = repairs
.into_iter() .into_iter()
.filter_map(|repair_request| { .filter_map(|repair_request| {
serve_repair serve_repair
.map_repair_request(&repair_request) .map_repair_request(&repair_request, &mut repair_stats)
.map(|result| ((archiver_info.gossip, result), repair_request)) .map(|result| ((archiver_info.gossip, result), repair_request))
.ok() .ok()
}) })

View File

@ -1,6 +1,6 @@
[package] [package]
name = "solana-archiver-utils" name = "solana-archiver-utils"
version = "1.1.0" version = "1.1.1"
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.1.0" } solana-chacha = { path = "../chacha", version = "1.1.1" }
solana-chacha-sys = { path = "../chacha-sys", version = "1.1.0" } solana-chacha-sys = { path = "../chacha-sys", version = "1.1.1" }
solana-ledger = { path = "../ledger", version = "1.1.0" } solana-ledger = { path = "../ledger", version = "1.1.1" }
solana-logger = { path = "../logger", version = "1.1.0" } solana-logger = { path = "../logger", version = "1.1.1" }
solana-perf = { path = "../perf", version = "1.1.0" } solana-perf = { path = "../perf", version = "1.1.1" }
solana-sdk = { path = "../sdk", version = "1.1.0" } solana-sdk = { path = "../sdk", version = "1.1.1" }
[dev-dependencies] [dev-dependencies]
hex = "0.4.2" hex = "0.4.2"

View File

@ -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.1.0" version = "1.1.1"
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.10.0" console = "0.10.0"
solana-clap-utils = { path = "../clap-utils", version = "1.1.0" } solana-clap-utils = { path = "../clap-utils", version = "1.1.1" }
solana-core = { path = "../core", version = "1.1.0" } solana-core = { path = "../core", version = "1.1.1" }
solana-logger = { path = "../logger", version = "1.1.0" } solana-logger = { path = "../logger", version = "1.1.1" }
solana-metrics = { path = "../metrics", version = "1.1.0" } solana-metrics = { path = "../metrics", version = "1.1.1" }
solana-archiver-lib = { path = "../archiver-lib", version = "1.1.0" } solana-archiver-lib = { path = "../archiver-lib", version = "1.1.1" }
solana-net-utils = { path = "../net-utils", version = "1.1.0" } solana-net-utils = { path = "../net-utils", version = "1.1.1" }
solana-sdk = { path = "../sdk", version = "1.1.0" } solana-sdk = { path = "../sdk", version = "1.1.1" }

View File

@ -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.1.0" version = "1.1.1"
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,13 +10,13 @@ homepage = "https://solana.com/"
[dependencies] [dependencies]
log = "0.4.6" log = "0.4.6"
rayon = "1.3.0" rayon = "1.3.0"
solana-core = { path = "../core", version = "1.1.0" } solana-core = { path = "../core", version = "1.1.1" }
solana-streamer = { path = "../streamer", version = "1.1.0" } solana-streamer = { path = "../streamer", version = "1.1.1" }
solana-perf = { path = "../perf", version = "1.1.0" } solana-perf = { path = "../perf", version = "1.1.1" }
solana-ledger = { path = "../ledger", version = "1.1.0" } solana-ledger = { path = "../ledger", version = "1.1.1" }
solana-logger = { path = "../logger", version = "1.1.0" } solana-logger = { path = "../logger", version = "1.1.1" }
solana-runtime = { path = "../runtime", version = "1.1.0" } solana-runtime = { path = "../runtime", version = "1.1.1" }
solana-measure = { path = "../measure", version = "1.1.0" } solana-measure = { path = "../measure", version = "1.1.1" }
solana-sdk = { path = "../sdk", version = "1.1.0" } solana-sdk = { path = "../sdk", version = "1.1.1" }
rand = "0.6.5" rand = "0.6.5"
crossbeam-channel = "0.4" crossbeam-channel = "0.4"

View File

@ -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.1.0" version = "1.1.1"
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.3.0" rayon = "1.3.0"
serde_json = "1.0.48" serde_json = "1.0.48"
serde_yaml = "0.8.11" serde_yaml = "0.8.11"
solana-clap-utils = { path = "../clap-utils", version = "1.1.0" } solana-clap-utils = { path = "../clap-utils", version = "1.1.1" }
solana-core = { path = "../core", version = "1.1.0" } solana-core = { path = "../core", version = "1.1.1" }
solana-genesis = { path = "../genesis", version = "1.1.0" } solana-genesis = { path = "../genesis", version = "1.1.1" }
solana-client = { path = "../client", version = "1.1.0" } solana-client = { path = "../client", version = "1.1.1" }
solana-faucet = { path = "../faucet", version = "1.1.0" } solana-faucet = { path = "../faucet", version = "1.1.1" }
solana-exchange-program = { path = "../programs/exchange", version = "1.1.0" } solana-exchange-program = { path = "../programs/exchange", version = "1.1.1" }
solana-logger = { path = "../logger", version = "1.1.0" } solana-logger = { path = "../logger", version = "1.1.1" }
solana-metrics = { path = "../metrics", version = "1.1.0" } solana-metrics = { path = "../metrics", version = "1.1.1" }
solana-net-utils = { path = "../net-utils", version = "1.1.0" } solana-net-utils = { path = "../net-utils", version = "1.1.1" }
solana-runtime = { path = "../runtime", version = "1.1.0" } solana-runtime = { path = "../runtime", version = "1.1.1" }
solana-sdk = { path = "../sdk", version = "1.1.0" } solana-sdk = { path = "../sdk", version = "1.1.1" }
[dev-dependencies] [dev-dependencies]
solana-local-cluster = { path = "../local-cluster", version = "1.1.0" } solana-local-cluster = { path = "../local-cluster", version = "1.1.1" }

View File

@ -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.1.0" version = "1.1.1"
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.1.0" } solana-clap-utils = { path = "../clap-utils", version = "1.1.1" }
solana-streamer = { path = "../streamer", version = "1.1.0" } solana-streamer = { path = "../streamer", version = "1.1.1" }
solana-logger = { path = "../logger", version = "1.1.0" } solana-logger = { path = "../logger", version = "1.1.1" }
solana-net-utils = { path = "../net-utils", version = "1.1.0" } solana-net-utils = { path = "../net-utils", version = "1.1.1" }

View File

@ -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.1.0" version = "1.1.1"
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.3.0" rayon = "1.3.0"
serde_json = "1.0.48" serde_json = "1.0.48"
serde_yaml = "0.8.11" serde_yaml = "0.8.11"
solana-clap-utils = { path = "../clap-utils", version = "1.1.0" } solana-clap-utils = { path = "../clap-utils", version = "1.1.1" }
solana-core = { path = "../core", version = "1.1.0" } solana-core = { path = "../core", version = "1.1.1" }
solana-genesis = { path = "../genesis", version = "1.1.0" } solana-genesis = { path = "../genesis", version = "1.1.1" }
solana-client = { path = "../client", version = "1.1.0" } solana-client = { path = "../client", version = "1.1.1" }
solana-faucet = { path = "../faucet", version = "1.1.0" } solana-faucet = { path = "../faucet", version = "1.1.1" }
solana-librapay = { path = "../programs/librapay", version = "1.1.0", optional = true } solana-librapay = { path = "../programs/librapay", version = "1.1.1", optional = true }
solana-logger = { path = "../logger", version = "1.1.0" } solana-logger = { path = "../logger", version = "1.1.1" }
solana-metrics = { path = "../metrics", version = "1.1.0" } solana-metrics = { path = "../metrics", version = "1.1.1" }
solana-measure = { path = "../measure", version = "1.1.0" } solana-measure = { path = "../measure", version = "1.1.1" }
solana-net-utils = { path = "../net-utils", version = "1.1.0" } solana-net-utils = { path = "../net-utils", version = "1.1.1" }
solana-runtime = { path = "../runtime", version = "1.1.0" } solana-runtime = { path = "../runtime", version = "1.1.1" }
solana-sdk = { path = "../sdk", version = "1.1.0" } solana-sdk = { path = "../sdk", version = "1.1.1" }
solana-move-loader-program = { path = "../programs/move_loader", version = "1.1.0", optional = true } solana-move-loader-program = { path = "../programs/move_loader", version = "1.1.1", optional = true }
[dev-dependencies] [dev-dependencies]
serial_test = "0.4.0" serial_test = "0.4.0"
serial_test_derive = "0.4.0" serial_test_derive = "0.4.0"
solana-local-cluster = { path = "../local-cluster", version = "1.1.0" } solana-local-cluster = { path = "../local-cluster", version = "1.1.1" }
[features] [features]
move = ["solana-librapay", "solana-move-loader-program"] move = ["solana-librapay", "solana-move-loader-program"]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "solana-chacha-cuda" name = "solana-chacha-cuda"
version = "1.1.0" version = "1.1.1"
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.1.0" } solana-archiver-utils = { path = "../archiver-utils", version = "1.1.1" }
solana-chacha = { path = "../chacha", version = "1.1.0" } solana-chacha = { path = "../chacha", version = "1.1.1" }
solana-ledger = { path = "../ledger", version = "1.1.0" } solana-ledger = { path = "../ledger", version = "1.1.1" }
solana-logger = { path = "../logger", version = "1.1.0" } solana-logger = { path = "../logger", version = "1.1.1" }
solana-perf = { path = "../perf", version = "1.1.0" } solana-perf = { path = "../perf", version = "1.1.1" }
solana-sdk = { path = "../sdk", version = "1.1.0" } solana-sdk = { path = "../sdk", version = "1.1.1" }
[dev-dependencies] [dev-dependencies]
hex-literal = "0.2.1" hex-literal = "0.2.1"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "solana-chacha-sys" name = "solana-chacha-sys"
version = "1.1.0" version = "1.1.1"
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"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "solana-chacha" name = "solana-chacha"
version = "1.1.0" version = "1.1.1"
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.1.0" } solana-chacha-sys = { path = "../chacha-sys", version = "1.1.1" }
solana-ledger = { path = "../ledger", version = "1.1.0" } solana-ledger = { path = "../ledger", version = "1.1.1" }
solana-logger = { path = "../logger", version = "1.1.0" } solana-logger = { path = "../logger", version = "1.1.1" }
solana-perf = { path = "../perf", version = "1.1.0" } solana-perf = { path = "../perf", version = "1.1.1" }
solana-sdk = { path = "../sdk", version = "1.1.0" } solana-sdk = { path = "../sdk", version = "1.1.1" }
[dev-dependencies] [dev-dependencies]
hex-literal = "0.2.1" hex-literal = "0.2.1"

37
ci/buildkite-tests.yml Normal file
View File

@ -0,0 +1,37 @@
# These steps are conditionally triggered by ci/buildkite.yml when files
# other than those in docs/ are modified
steps:
- command: "ci/shellcheck.sh"
name: "shellcheck"
timeout_in_minutes: 5
- wait
- command: "ci/test-stable-perf.sh"
name: "stable-perf"
timeout_in_minutes: 40
artifact_paths: "log-*.txt"
agents:
- "queue=cuda"
- command: "ci/test-bench.sh"
name: "bench"
timeout_in_minutes: 30
- command: ". ci/rust-version.sh; ci/docker-run.sh $$rust_stable_docker_image ci/test-stable.sh"
name: "stable"
timeout_in_minutes: 60
artifact_paths: "log-*.txt"
agents:
- "queue=rpc-test-capable"
- command: ". ci/rust-version.sh; ci/docker-run.sh $$rust_stable_docker_image ci/test-move.sh"
name: "move"
timeout_in_minutes: 20
- command: ". ci/rust-version.sh; ci/docker-run.sh $$rust_stable_docker_image ci/test-local-cluster.sh"
name: "local-cluster"
timeout_in_minutes: 45
artifact_paths: "log-*.txt"
- command: ". ci/rust-version.sh; ci/docker-run.sh $$rust_nightly_docker_image ci/test-coverage.sh"
name: "coverage"
timeout_in_minutes: 30
agents:
- "queue=rpc-test-capable"

View File

@ -1,42 +1,22 @@
# Build steps that run on pushes and pull requests. # Build steps that run on pushes and pull requests.
# If files other than those in docs/ were modified, this will be followed up by
# ci/buildkite-tests.yml
# #
# Release tags use buildkite-release.yml instead # Release tags use buildkite-release.yml instead
steps: steps:
- command: "ci/shellcheck.sh"
name: "shellcheck"
timeout_in_minutes: 5
- command: ". ci/rust-version.sh; ci/docker-run.sh $$rust_nightly_docker_image ci/test-checks.sh" - command: ". ci/rust-version.sh; ci/docker-run.sh $$rust_nightly_docker_image ci/test-checks.sh"
name: "checks" name: "checks"
timeout_in_minutes: 20 timeout_in_minutes: 20
- wait - wait
- command: "ci/test-stable-perf.sh"
name: "stable-perf" - command: "ci/maybe-trigger-tests.sh"
timeout_in_minutes: 40 name: "maybe-trigger-tests"
artifact_paths: "log-*.txt" timeout_in_minutes: 2
agents:
- "queue=cuda"
- command: "ci/test-bench.sh"
name: "bench"
timeout_in_minutes: 30
- command: ". ci/rust-version.sh; ci/docker-run.sh $$rust_stable_docker_image ci/test-stable.sh"
name: "stable"
timeout_in_minutes: 60
artifact_paths: "log-*.txt"
agents:
- "queue=rpc-test-capable"
- command: ". ci/rust-version.sh; ci/docker-run.sh $$rust_stable_docker_image ci/test-move.sh"
name: "move"
timeout_in_minutes: 20
- command: ". ci/rust-version.sh; ci/docker-run.sh $$rust_stable_docker_image ci/test-local-cluster.sh"
name: "local-cluster"
timeout_in_minutes: 45
artifact_paths: "log-*.txt"
- command: ". ci/rust-version.sh; ci/docker-run.sh $$rust_nightly_docker_image ci/test-coverage.sh"
name: "coverage"
timeout_in_minutes: 30
agents:
- "queue=rpc-test-capable"
- wait - wait
- trigger: "solana-secondary" - trigger: "solana-secondary"
branches: "!pull/*" branches: "!pull/*"
async: true async: true

21
ci/maybe-trigger-tests.sh Executable file
View File

@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -e
cd "$(dirname "$0")/.."
annotate() {
${BUILDKITE:-false} && {
buildkite-agent annotate "$@"
}
}
# Skip if only the docs have been modified
ci/affects-files.sh \
\!^docs/ \
|| {
annotate --style info \
"Skipping all further tests as only docs/ files were modified"
exit 0
}
annotate --style info "Triggering tests"
buildkite-agent pipeline upload ci/buildkite-tests.yml

View File

@ -22,7 +22,7 @@ _ cargo +"$rust_stable" clippy --all --exclude solana-sdk-c -- --deny=warnings
_ cargo +"$rust_stable" clippy --manifest-path sdk-c/Cargo.toml -- --deny=warnings _ cargo +"$rust_stable" clippy --manifest-path sdk-c/Cargo.toml -- --deny=warnings
_ cargo +"$rust_stable" audit --version _ cargo +"$rust_stable" audit --version
_ cargo +"$rust_stable" audit --ignore RUSTSEC-2020-0002 --ignore RUSTSEC-2020-0006 _ cargo +"$rust_stable" audit --ignore RUSTSEC-2020-0002 --ignore RUSTSEC-2020-0008
_ ci/nits.sh _ ci/nits.sh
_ ci/order-crates-for-publishing.py _ ci/order-crates-for-publishing.py
_ docs/build.sh _ docs/build.sh

View File

@ -1,6 +1,6 @@
[package] [package]
name = "solana-clap-utils" name = "solana-clap-utils"
version = "1.1.0" version = "1.1.1"
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.1.0" } solana-remote-wallet = { path = "../remote-wallet", version = "1.1.1" }
solana-sdk = { path = "../sdk", version = "1.1.0" } solana-sdk = { path = "../sdk", version = "1.1.1" }
thiserror = "1.0.11" thiserror = "1.0.11"
tiny-bip39 = "0.7.0" tiny-bip39 = "0.7.0"
url = "2.1.0" url = "2.1.0"

View File

@ -62,6 +62,21 @@ pub fn keypair_of(matches: &ArgMatches<'_>, name: &str) -> Option<Keypair> {
} }
} }
pub fn keypairs_of(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<Keypair>> {
matches.values_of(name).map(|values| {
values
.filter_map(|value| {
if value == ASK_KEYWORD {
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
keypair_from_seed_phrase(name, skip_validation, true).ok()
} else {
read_keypair_file(value).ok()
}
})
.collect()
})
}
// Return a pubkey for an argument that can itself be parsed into a pubkey, // Return a pubkey for an argument that can itself be parsed into a pubkey,
// or is a filename that can be read as a keypair // or is a filename that can be read as a keypair
pub fn pubkey_of(matches: &ArgMatches<'_>, name: &str) -> Option<Pubkey> { pub fn pubkey_of(matches: &ArgMatches<'_>, name: &str) -> Option<Pubkey> {

View File

@ -47,6 +47,13 @@ pub fn parse_keypair_path(path: &str) -> KeypairUrl {
} }
} }
pub fn check_for_usb<S>(mut items: impl Iterator<Item = S>) -> bool
where
S: Into<String>,
{
items.any(|arg| matches!(parse_keypair_path(&arg.into()), KeypairUrl::Usb(_)))
}
pub fn presigner_from_pubkey_sigs( pub fn presigner_from_pubkey_sigs(
pubkey: &Pubkey, pubkey: &Pubkey,
signers: &[(Pubkey, Signature)], signers: &[(Pubkey, Signature)],
@ -256,4 +263,20 @@ mod tests {
sanitize_seed_phrase(seed_phrase) sanitize_seed_phrase(seed_phrase)
); );
} }
#[test]
fn test_check_for_usb() {
let args: Vec<&str> = vec![];
assert_eq!(check_for_usb(args.into_iter()), false);
let args = vec!["usb://"];
assert_eq!(check_for_usb(args.into_iter()), true);
let args = vec!["other"];
assert_eq!(check_for_usb(args.into_iter()), false);
let args = vec!["other", "usb://", "another"];
assert_eq!(check_for_usb(args.into_iter()), true);
let args = vec!["other", "another"];
assert_eq!(check_for_usb(args.into_iter()), false);
let args = vec!["usb://", "usb://"];
assert_eq!(check_for_usb(args.into_iter()), true);
}
} }

View File

@ -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.1.0" version = "1.1.1"
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/"

View 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.1.0" version = "1.1.1"
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,28 +26,28 @@ reqwest = { version = "0.10.4", default-features = false, features = ["blocking"
serde = "1.0.105" serde = "1.0.105"
serde_derive = "1.0.103" serde_derive = "1.0.103"
serde_json = "1.0.48" serde_json = "1.0.48"
solana-budget-program = { path = "../programs/budget", version = "1.1.0" } solana-budget-program = { path = "../programs/budget", version = "1.1.1" }
solana-clap-utils = { path = "../clap-utils", version = "1.1.0" } solana-clap-utils = { path = "../clap-utils", version = "1.1.1" }
solana-cli-config = { path = "../cli-config", version = "1.1.0" } solana-cli-config = { path = "../cli-config", version = "1.1.1" }
solana-client = { path = "../client", version = "1.1.0" } solana-client = { path = "../client", version = "1.1.1" }
solana-config-program = { path = "../programs/config", version = "1.1.0" } solana-config-program = { path = "../programs/config", version = "1.1.1" }
solana-faucet = { path = "../faucet", version = "1.1.0" } solana-faucet = { path = "../faucet", version = "1.1.1" }
solana-logger = { path = "../logger", version = "1.1.0" } solana-logger = { path = "../logger", version = "1.1.1" }
solana-net-utils = { path = "../net-utils", version = "1.1.0" } solana-net-utils = { path = "../net-utils", version = "1.1.1" }
solana-remote-wallet = { path = "../remote-wallet", version = "1.1.0" } solana-remote-wallet = { path = "../remote-wallet", version = "1.1.1" }
solana-runtime = { path = "../runtime", version = "1.1.0" } solana-runtime = { path = "../runtime", version = "1.1.1" }
solana-sdk = { path = "../sdk", version = "1.1.0" } solana-sdk = { path = "../sdk", version = "1.1.1" }
solana-stake-program = { path = "../programs/stake", version = "1.1.0" } solana-stake-program = { path = "../programs/stake", version = "1.1.1" }
solana-storage-program = { path = "../programs/storage", version = "1.1.0" } solana-storage-program = { path = "../programs/storage", version = "1.1.1" }
solana-vote-program = { path = "../programs/vote", version = "1.1.0" } solana-vote-program = { path = "../programs/vote", version = "1.1.1" }
solana-vote-signer = { path = "../vote-signer", version = "1.1.0" } solana-vote-signer = { path = "../vote-signer", version = "1.1.1" }
titlecase = "1.1.0" titlecase = "1.1.0"
thiserror = "1.0.13" thiserror = "1.0.13"
url = "2.1.1" url = "2.1.1"
[dev-dependencies] [dev-dependencies]
solana-core = { path = "../core", version = "1.1.0" } solana-core = { path = "../core", version = "1.1.1" }
solana-budget-program = { path = "../programs/budget", version = "1.1.0" } solana-budget-program = { path = "../programs/budget", version = "1.1.1" }
tempfile = "3.1.0" tempfile = "3.1.0"
[[bin]] [[bin]]

View File

@ -173,6 +173,8 @@ pub enum CliCommand {
Catchup { Catchup {
node_pubkey: Pubkey, node_pubkey: Pubkey,
node_json_rpc_url: Option<String>, node_json_rpc_url: Option<String>,
commitment_config: CommitmentConfig,
follow: bool,
}, },
ClusterVersion, ClusterVersion,
CreateAddressWithSeed { CreateAddressWithSeed {
@ -188,6 +190,9 @@ pub enum CliCommand {
commitment_config: CommitmentConfig, commitment_config: CommitmentConfig,
}, },
GetGenesisHash, GetGenesisHash,
GetEpoch {
commitment_config: CommitmentConfig,
},
GetSlot { GetSlot {
commitment_config: CommitmentConfig, commitment_config: CommitmentConfig,
}, },
@ -586,6 +591,7 @@ pub fn parse_command(
command: CliCommand::GetGenesisHash, command: CliCommand::GetGenesisHash,
signers: vec![], signers: vec![],
}), }),
("epoch", Some(matches)) => parse_get_epoch(matches),
("slot", Some(matches)) => parse_get_slot(matches), ("slot", Some(matches)) => parse_get_slot(matches),
("total-supply", Some(matches)) => parse_total_supply(matches), ("total-supply", Some(matches)) => parse_total_supply(matches),
("transaction-count", Some(matches)) => parse_get_transaction_count(matches), ("transaction-count", Some(matches)) => parse_get_transaction_count(matches),
@ -1589,7 +1595,15 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
CliCommand::Catchup { CliCommand::Catchup {
node_pubkey, node_pubkey,
node_json_rpc_url, node_json_rpc_url,
} => process_catchup(&rpc_client, node_pubkey, node_json_rpc_url), commitment_config,
follow,
} => process_catchup(
&rpc_client,
node_pubkey,
node_json_rpc_url,
*commitment_config,
*follow,
),
CliCommand::ClusterVersion => process_cluster_version(&rpc_client), CliCommand::ClusterVersion => process_cluster_version(&rpc_client),
CliCommand::CreateAddressWithSeed { CliCommand::CreateAddressWithSeed {
from_pubkey, from_pubkey,
@ -1602,6 +1616,9 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
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::GetEpoch { commitment_config } => {
process_get_epoch(&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)
} }

View File

@ -69,6 +69,20 @@ impl ClusterQuerySubCommands for App<'_, '_> {
.takes_value(true) .takes_value(true)
.validator(is_url) .validator(is_url)
.help("JSON RPC URL for validator, which is useful for validators with a private RPC service") .help("JSON RPC URL for validator, which is useful for validators with a private RPC service")
)
.arg(
Arg::with_name("confirmed")
.long("confirmed")
.takes_value(false)
.help(
"Return information at maximum-lockout commitment level",
),
)
.arg(
Arg::with_name("follow")
.long("follow")
.takes_value(false)
.help("Continue reporting progress even after the validator has caught up"),
), ),
) )
.subcommand( .subcommand(
@ -119,6 +133,17 @@ impl ClusterQuerySubCommands for App<'_, '_> {
), ),
), ),
) )
.subcommand(
SubCommand::with_name("epoch").about("Get current epoch")
.arg(
Arg::with_name("confirmed")
.long("confirmed")
.takes_value(false)
.help(
"Return epoch at maximum-lockout commitment level",
),
),
)
.subcommand( .subcommand(
SubCommand::with_name("total-supply").about("Get total number of SOL") SubCommand::with_name("total-supply").about("Get total number of SOL")
.arg( .arg(
@ -262,10 +287,18 @@ pub fn parse_catchup(
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let node_pubkey = pubkey_of_signer(matches, "node_pubkey", wallet_manager)?.unwrap(); let node_pubkey = pubkey_of_signer(matches, "node_pubkey", wallet_manager)?.unwrap();
let node_json_rpc_url = value_t!(matches, "node_json_rpc_url", String).ok(); let node_json_rpc_url = value_t!(matches, "node_json_rpc_url", String).ok();
let commitment_config = if matches.is_present("confirmed") {
CommitmentConfig::default()
} else {
CommitmentConfig::recent()
};
let follow = matches.is_present("follow");
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::Catchup { command: CliCommand::Catchup {
node_pubkey, node_pubkey,
node_json_rpc_url, node_json_rpc_url,
commitment_config,
follow,
}, },
signers: vec![], signers: vec![],
}) })
@ -338,6 +371,18 @@ pub fn parse_get_slot(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliErr
}) })
} }
pub fn parse_get_epoch(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
let commitment_config = if matches.is_present("confirmed") {
CommitmentConfig::default()
} else {
CommitmentConfig::recent()
};
Ok(CliCommandInfo {
command: CliCommand::GetEpoch { commitment_config },
signers: vec![],
})
}
pub fn parse_total_supply(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> { pub fn parse_total_supply(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
let commitment_config = if matches.is_present("confirmed") { let commitment_config = if matches.is_present("confirmed") {
CommitmentConfig::default() CommitmentConfig::default()
@ -409,20 +454,37 @@ pub fn process_catchup(
rpc_client: &RpcClient, rpc_client: &RpcClient,
node_pubkey: &Pubkey, node_pubkey: &Pubkey,
node_json_rpc_url: &Option<String>, node_json_rpc_url: &Option<String>,
commitment_config: CommitmentConfig,
follow: bool,
) -> ProcessResult { ) -> ProcessResult {
let cluster_nodes = rpc_client.get_cluster_nodes()?; let sleep_interval = 5;
let progress_bar = new_spinner_progress_bar();
progress_bar.set_message("Connecting...");
let node_client = if let Some(node_json_rpc_url) = node_json_rpc_url { let node_client = if let Some(node_json_rpc_url) = node_json_rpc_url {
RpcClient::new(node_json_rpc_url.to_string()) RpcClient::new(node_json_rpc_url.to_string())
} else { } else {
RpcClient::new_socket( let rpc_addr = loop {
cluster_nodes let cluster_nodes = rpc_client.get_cluster_nodes()?;
if let Some(contact_info) = 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))? {
.rpc if let Some(rpc_addr) = contact_info.rpc {
.ok_or_else(|| format!("RPC service not found for {}", node_pubkey))?, break rpc_addr;
) }
progress_bar.set_message(&format!("RPC service not found for {}", node_pubkey));
} else {
progress_bar.set_message(&format!(
"Contact information not found for {}",
node_pubkey
));
}
sleep(Duration::from_secs(sleep_interval as u64));
};
RpcClient::new_socket(rpc_addr)
}; };
let reported_node_pubkey = node_client.get_identity()?; let reported_node_pubkey = node_client.get_identity()?;
@ -438,16 +500,12 @@ pub fn process_catchup(
return Err("Both RPC URLs reference the same node, unable to monitor for catchup. Try a different --url".into()); 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();
progress_bar.set_message("Connecting...");
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;
loop { loop {
let rpc_slot = rpc_client.get_slot_with_commitment(CommitmentConfig::recent())?; let rpc_slot = rpc_client.get_slot_with_commitment(commitment_config)?;
let node_slot = node_client.get_slot_with_commitment(CommitmentConfig::recent())?; let node_slot = node_client.get_slot_with_commitment(commitment_config)?;
if node_slot > std::cmp::min(previous_rpc_slot, rpc_slot) { if !follow && node_slot > std::cmp::min(previous_rpc_slot, rpc_slot) {
progress_bar.finish_and_clear(); progress_bar.finish_and_clear();
return Ok(format!( return Ok(format!(
"{} has caught up (us:{} them:{})", "{} has caught up (us:{} them:{})",
@ -461,7 +519,7 @@ pub fn process_catchup(
slot_distance, slot_distance,
node_slot, node_slot,
rpc_slot, rpc_slot,
if previous_rpc_slot == std::u64::MAX { if slot_distance == 0 || previous_rpc_slot == std::u64::MAX {
"".to_string() "".to_string()
} else { } else {
let slots_per_second = let slots_per_second =
@ -606,6 +664,14 @@ pub fn process_get_slot(
Ok(slot.to_string()) Ok(slot.to_string())
} }
pub fn process_get_epoch(
rpc_client: &RpcClient,
commitment_config: CommitmentConfig,
) -> ProcessResult {
let epoch_info = rpc_client.get_epoch_info_with_commitment(commitment_config.clone())?;
Ok(epoch_info.epoch.to_string())
}
pub fn parse_show_block_production(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> { pub fn parse_show_block_production(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
let epoch = value_t!(matches, "epoch", Epoch).ok(); let epoch = value_t!(matches, "epoch", Epoch).ok();
let slot_limit = value_t!(matches, "slot_limit", u64).ok(); let slot_limit = value_t!(matches, "slot_limit", u64).ok();
@ -1319,6 +1385,19 @@ mod tests {
} }
); );
let test_get_epoch = test_commands
.clone()
.get_matches_from(vec!["test", "epoch"]);
assert_eq!(
parse_command(&test_get_epoch, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::GetEpoch {
commitment_config: CommitmentConfig::recent(),
},
signers: vec![],
}
);
let test_total_supply = test_commands let test_total_supply = test_commands
.clone() .clone()
.get_matches_from(vec!["test", "total-supply"]); .get_matches_from(vec!["test", "total-supply"]);

View File

@ -2,7 +2,9 @@ use clap::{crate_description, crate_name, AppSettings, Arg, ArgGroup, ArgMatches
use console::style; use console::style;
use solana_clap_utils::{ use solana_clap_utils::{
input_validators::is_url, keypair::SKIP_SEED_PHRASE_VALIDATION_ARG, offline::SIGN_ONLY_ARG, input_validators::is_url,
keypair::{check_for_usb, SKIP_SEED_PHRASE_VALIDATION_ARG},
offline::SIGN_ONLY_ARG,
DisplayError, DisplayError,
}; };
use solana_cli::{ use solana_cli::{
@ -233,12 +235,20 @@ fn main() -> Result<(), Box<dyn error::Error>> {
) )
.get_matches(); .get_matches();
do_main(&matches).map_err(|err| DisplayError::new_as_boxed(err).into()) do_main(&matches, check_for_usb(std::env::args()))
.map_err(|err| DisplayError::new_as_boxed(err).into())
} }
fn do_main(matches: &ArgMatches<'_>) -> Result<(), Box<dyn error::Error>> { fn do_main(
matches: &ArgMatches<'_>,
need_wallet_manager: bool,
) -> Result<(), Box<dyn error::Error>> {
if parse_settings(&matches)? { if parse_settings(&matches)? {
let wallet_manager = maybe_wallet_manager()?; let wallet_manager = if need_wallet_manager {
maybe_wallet_manager()?
} else {
None
};
let (mut config, signers) = parse_args(&matches, wallet_manager)?; let (mut config, signers) = parse_args(&matches, wallet_manager)?;
config.signers = signers.iter().map(|s| s.as_ref()).collect(); config.signers = signers.iter().map(|s| s.as_ref()).collect();

View File

@ -1,6 +1,6 @@
[package] [package]
name = "solana-client" name = "solana-client"
version = "1.1.0" version = "1.1.1"
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"
@ -19,10 +19,10 @@ reqwest = { version = "0.10.4", default-features = false, features = ["blocking"
serde = "1.0.105" serde = "1.0.105"
serde_derive = "1.0.103" serde_derive = "1.0.103"
serde_json = "1.0.48" serde_json = "1.0.48"
solana-transaction-status = { path = "../transaction-status", version = "1.1.0" } solana-transaction-status = { path = "../transaction-status", version = "1.1.1" }
solana-net-utils = { path = "../net-utils", version = "1.1.0" } solana-net-utils = { path = "../net-utils", version = "1.1.1" }
solana-sdk = { path = "../sdk", version = "1.1.0" } solana-sdk = { path = "../sdk", version = "1.1.1" }
solana-vote-program = { path = "../programs/vote", version = "1.1.0" } solana-vote-program = { path = "../programs/vote", version = "1.1.1" }
thiserror = "1.0" thiserror = "1.0"
tungstenite = "0.10.1" tungstenite = "0.10.1"
url = "2.1.1" url = "2.1.1"
@ -31,4 +31,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.1.0" } solana-logger = { path = "../logger", version = "1.1.1" }

View File

@ -88,6 +88,21 @@ impl GenericRpcClientRequest for MockRpcClientRequest {
value: serde_json::to_value(FeeRateGovernor::default()).unwrap(), value: serde_json::to_value(FeeRateGovernor::default()).unwrap(),
})?, })?,
RpcRequest::GetSignatureStatus => { RpcRequest::GetSignatureStatus => {
let response: Option<transaction::Result<()>> = if self.url == "account_in_use" {
Some(Err(TransactionError::AccountInUse))
} else if self.url == "instruction_error" {
Some(Err(TransactionError::InstructionError(
0,
InstructionError::UninitializedAccount,
)))
} else if self.url == "sig_not_found" {
None
} else {
Some(Ok(()))
};
serde_json::to_value(response).unwrap()
}
RpcRequest::GetSignatureStatuses => {
let status: transaction::Result<()> = if self.url == "account_in_use" { let status: transaction::Result<()> = if self.url == "account_in_use" {
Err(TransactionError::AccountInUse) Err(TransactionError::AccountInUse)
} else if self.url == "instruction_error" { } else if self.url == "instruction_error" {

View File

@ -119,12 +119,13 @@ impl RpcClient {
commitment_config: CommitmentConfig, commitment_config: CommitmentConfig,
) -> ClientResult<Option<transaction::Result<()>>> { ) -> ClientResult<Option<transaction::Result<()>>> {
let signature_status = self.client.send( let signature_status = self.client.send(
&RpcRequest::GetSignatureStatus, &RpcRequest::GetSignatureStatuses,
json!([[signature.to_string()], commitment_config]), json!([[signature.to_string()], commitment_config]),
5, 5,
)?; )?;
let result: Response<Vec<Option<TransactionStatus>>> = let result: Response<Vec<Option<TransactionStatus>>> =
serde_json::from_value(signature_status).unwrap(); serde_json::from_value(signature_status)
.map_err(|err| ClientError::new_with_command(err.into(), "GetSignatureStatuses"))?;
Ok(result.value[0] Ok(result.value[0]
.clone() .clone()
.map(|status_meta| status_meta.status)) .map(|status_meta| status_meta.status))
@ -948,20 +949,20 @@ impl RpcClient {
let response = self let response = self
.client .client
.send( .send(
&RpcRequest::GetSignatureStatus, &RpcRequest::GetSignatureStatuses,
json!([[signature.to_string()], CommitmentConfig::recent().ok()]), json!([[signature.to_string()], CommitmentConfig::recent().ok()]),
1, 1,
) )
.map_err(|err| err.into_with_command("GetSignatureStatus"))?; .map_err(|err| err.into_with_command("GetSignatureStatuses"))?;
let result: Response<Vec<Option<TransactionStatus>>> = let result: Response<Vec<Option<TransactionStatus>>> = serde_json::from_value(response)
serde_json::from_value(response).unwrap(); .map_err(|err| ClientError::new_with_command(err.into(), "GetSignatureStatuses"))?;
let confirmations = result.value[0] let confirmations = result.value[0]
.clone() .clone()
.ok_or_else(|| { .ok_or_else(|| {
ClientError::new_with_command( ClientError::new_with_command(
ClientErrorKind::Custom("signature not found".to_string()), ClientErrorKind::Custom("signature not found".to_string()),
"GetSignatureStatus", "GetSignatureStatuses",
) )
})? })?
.confirmations .confirmations

View File

@ -23,6 +23,7 @@ pub enum RpcRequest {
GetFeeCalculatorForBlockhash, GetFeeCalculatorForBlockhash,
GetFeeRateGovernor, GetFeeRateGovernor,
GetSignatureStatus, GetSignatureStatus,
GetSignatureStatuses,
GetSlot, GetSlot,
GetSlotLeader, GetSlotLeader,
GetStorageTurn, GetStorageTurn,
@ -65,6 +66,7 @@ impl RpcRequest {
RpcRequest::GetFeeCalculatorForBlockhash => "getFeeCalculatorForBlockhash", RpcRequest::GetFeeCalculatorForBlockhash => "getFeeCalculatorForBlockhash",
RpcRequest::GetFeeRateGovernor => "getFeeRateGovernor", RpcRequest::GetFeeRateGovernor => "getFeeRateGovernor",
RpcRequest::GetSignatureStatus => "getSignatureStatus", RpcRequest::GetSignatureStatus => "getSignatureStatus",
RpcRequest::GetSignatureStatuses => "getSignatureStatuses",
RpcRequest::GetSlot => "getSlot", RpcRequest::GetSlot => "getSlot",
RpcRequest::GetSlotLeader => "getSlotLeader", RpcRequest::GetSlotLeader => "getSlotLeader",
RpcRequest::GetStorageTurn => "getStorageTurn", RpcRequest::GetStorageTurn => "getStorageTurn",

View File

@ -1,7 +1,7 @@
[package] [package]
name = "solana-core" name = "solana-core"
description = "Blockchain, Rebuilt for Scale" description = "Blockchain, Rebuilt for Scale"
version = "1.1.0" version = "1.1.1"
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,35 +41,35 @@ regex = "1.3.6"
serde = "1.0.105" serde = "1.0.105"
serde_derive = "1.0.103" serde_derive = "1.0.103"
serde_json = "1.0.48" serde_json = "1.0.48"
solana-budget-program = { path = "../programs/budget", version = "1.1.0" } solana-budget-program = { path = "../programs/budget", version = "1.1.1" }
solana-clap-utils = { path = "../clap-utils", version = "1.1.0" } solana-clap-utils = { path = "../clap-utils", version = "1.1.1" }
solana-client = { path = "../client", version = "1.1.0" } solana-client = { path = "../client", version = "1.1.1" }
solana-transaction-status = { path = "../transaction-status", version = "1.1.0" } solana-transaction-status = { path = "../transaction-status", version = "1.1.1" }
solana-faucet = { path = "../faucet", version = "1.1.0" } solana-faucet = { path = "../faucet", version = "1.1.1" }
ed25519-dalek = "=1.0.0-pre.1" ed25519-dalek = "=1.0.0-pre.1"
solana-ledger = { path = "../ledger", version = "1.1.0" } solana-ledger = { path = "../ledger", version = "1.1.1" }
solana-logger = { path = "../logger", version = "1.1.0" } solana-logger = { path = "../logger", version = "1.1.1" }
solana-merkle-tree = { path = "../merkle-tree", version = "1.1.0" } solana-merkle-tree = { path = "../merkle-tree", version = "1.1.1" }
solana-metrics = { path = "../metrics", version = "1.1.0" } solana-metrics = { path = "../metrics", version = "1.1.1" }
solana-measure = { path = "../measure", version = "1.1.0" } solana-measure = { path = "../measure", version = "1.1.1" }
solana-net-utils = { path = "../net-utils", version = "1.1.0" } solana-net-utils = { path = "../net-utils", version = "1.1.1" }
solana-chacha-cuda = { path = "../chacha-cuda", version = "1.1.0" } solana-chacha-cuda = { path = "../chacha-cuda", version = "1.1.1" }
solana-perf = { path = "../perf", version = "1.1.0" } solana-perf = { path = "../perf", version = "1.1.1" }
solana-runtime = { path = "../runtime", version = "1.1.0" } solana-runtime = { path = "../runtime", version = "1.1.1" }
solana-sdk = { path = "../sdk", version = "1.1.0" } solana-sdk = { path = "../sdk", version = "1.1.1" }
solana-stake-program = { path = "../programs/stake", version = "1.1.0" } solana-stake-program = { path = "../programs/stake", version = "1.1.1" }
solana-storage-program = { path = "../programs/storage", version = "1.1.0" } solana-storage-program = { path = "../programs/storage", version = "1.1.1" }
solana-streamer = { path = "../streamer", version = "1.1.0" } solana-streamer = { path = "../streamer", version = "1.1.1" }
solana-vote-program = { path = "../programs/vote", version = "1.1.0" } solana-vote-program = { path = "../programs/vote", version = "1.1.1" }
solana-vote-signer = { path = "../vote-signer", version = "1.1.0" } solana-vote-signer = { path = "../vote-signer", version = "1.1.1" }
solana-sys-tuner = { path = "../sys-tuner", version = "1.1.0" } solana-sys-tuner = { path = "../sys-tuner", version = "1.1.1" }
tempfile = "3.1.0" tempfile = "3.1.0"
thiserror = "1.0" thiserror = "1.0"
tokio = "0.1" 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.1.0" } solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.1.1" }
trees = "0.2.1" trees = "0.2.1"
[dev-dependencies] [dev-dependencies]

View File

@ -4,7 +4,7 @@
// hash on gossip. Monitor gossip for messages from validators in the --trusted-validators // hash on gossip. Monitor gossip for messages from validators in the --trusted-validators
// set and halt the node if a mismatch is detected. // set and halt the node if a mismatch is detected.
use crate::cluster_info::ClusterInfo; use crate::cluster_info::{ClusterInfo, MAX_SNAPSHOT_HASHES};
use solana_ledger::{ use solana_ledger::{
snapshot_package::SnapshotPackage, snapshot_package::SnapshotPackageReceiver, snapshot_package::SnapshotPackage, snapshot_package::SnapshotPackageReceiver,
snapshot_package::SnapshotPackageSender, snapshot_package::SnapshotPackageSender,
@ -94,6 +94,10 @@ impl AccountsHashVerifier {
hashes.push((snapshot_package.root, snapshot_package.hash)); hashes.push((snapshot_package.root, snapshot_package.hash));
} }
while hashes.len() > MAX_SNAPSHOT_HASHES {
hashes.remove(0);
}
if halt_on_trusted_validator_accounts_hash_mismatch { if halt_on_trusted_validator_accounts_hash_mismatch {
let mut slot_to_hash = HashMap::new(); let mut slot_to_hash = HashMap::new();
for (slot, hash) in hashes.iter() { for (slot, hash) in hashes.iter() {
@ -119,6 +123,7 @@ impl AccountsHashVerifier {
slot_to_hash: &mut HashMap<Slot, Hash>, slot_to_hash: &mut HashMap<Slot, Hash>,
) -> bool { ) -> bool {
let mut verified_count = 0; let mut verified_count = 0;
let mut highest_slot = 0;
if let Some(trusted_validators) = trusted_validators.as_ref() { if let Some(trusted_validators) = trusted_validators.as_ref() {
for trusted_validator in trusted_validators { for trusted_validator in trusted_validators {
let cluster_info_r = cluster_info.read().unwrap(); let cluster_info_r = cluster_info.read().unwrap();
@ -140,6 +145,7 @@ impl AccountsHashVerifier {
verified_count += 1; verified_count += 1;
} }
} else { } else {
highest_slot = std::cmp::max(*slot, highest_slot);
slot_to_hash.insert(*slot, *hash); slot_to_hash.insert(*slot, *hash);
} }
} }
@ -147,6 +153,10 @@ impl AccountsHashVerifier {
} }
} }
inc_new_counter_info!("accounts_hash_verifier-hashes_verified", verified_count); inc_new_counter_info!("accounts_hash_verifier-hashes_verified", verified_count);
datapoint_info!(
"accounts_hash_verifier",
("highest_slot_verified", highest_slot, i64),
);
false false
} }
@ -197,4 +207,57 @@ mod tests {
&mut slot_to_hash, &mut slot_to_hash,
)); ));
} }
#[test]
fn test_max_hashes() {
solana_logger::setup();
use std::path::PathBuf;
use tempfile::TempDir;
let keypair = Keypair::new();
let contact_info = ContactInfo::new_localhost(&keypair.pubkey(), 0);
let cluster_info = ClusterInfo::new_with_invalid_keypair(contact_info);
let cluster_info = Arc::new(RwLock::new(cluster_info));
let trusted_validators = HashSet::new();
let exit = Arc::new(AtomicBool::new(false));
let mut hashes = vec![];
for i in 0..MAX_SNAPSHOT_HASHES + 1 {
let snapshot_links = TempDir::new().unwrap();
let snapshot_package = SnapshotPackage {
hash: hash(&[i as u8]),
root: 100 + i as u64,
slot_deltas: vec![],
snapshot_links,
tar_output_file: PathBuf::from("."),
storages: vec![],
};
AccountsHashVerifier::process_snapshot(
snapshot_package,
&cluster_info,
&Some(trusted_validators.clone()),
false,
&None,
&mut hashes,
&exit,
0,
);
}
let cluster_info_r = cluster_info.read().unwrap();
let cluster_hashes = cluster_info_r
.get_accounts_hash_for_node(&keypair.pubkey())
.unwrap();
info!("{:?}", cluster_hashes);
assert_eq!(hashes.len(), MAX_SNAPSHOT_HASHES);
assert_eq!(cluster_hashes.len(), MAX_SNAPSHOT_HASHES);
assert_eq!(cluster_hashes[0], (101, hash(&[1])));
assert_eq!(
cluster_hashes[MAX_SNAPSHOT_HASHES - 1],
(
100 + MAX_SNAPSHOT_HASHES as u64,
hash(&[MAX_SNAPSHOT_HASHES as u8])
)
);
}
} }

View File

@ -1,3 +1,6 @@
use crate::consensus::VOTE_THRESHOLD_SIZE;
use solana_measure::measure::Measure;
use solana_metrics::inc_new_counter_info;
use solana_runtime::bank::Bank; use solana_runtime::bank::Bank;
use solana_sdk::clock::Slot; use solana_sdk::clock::Slot;
use solana_vote_program::{vote_state::VoteState, vote_state::MAX_LOCKOUT_HISTORY}; use solana_vote_program::{vote_state::VoteState, vote_state::MAX_LOCKOUT_HISTORY};
@ -31,17 +34,40 @@ impl BlockCommitment {
} }
} }
#[derive(Debug, Default)] #[derive(Default)]
pub struct BlockCommitmentCache { pub struct BlockCommitmentCache {
block_commitment: HashMap<Slot, BlockCommitment>, block_commitment: HashMap<Slot, BlockCommitment>,
total_stake: u64, total_stake: u64,
bank: Arc<Bank>,
root: Slot,
}
impl std::fmt::Debug for BlockCommitmentCache {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("BlockCommitmentCache")
.field("block_commitment", &self.block_commitment)
.field("total_stake", &self.total_stake)
.field(
"bank",
&format_args!("Bank({{current_slot: {:?}}})", self.bank.slot()),
)
.field("root", &self.root)
.finish()
}
} }
impl BlockCommitmentCache { impl BlockCommitmentCache {
pub fn new(block_commitment: HashMap<Slot, BlockCommitment>, total_stake: u64) -> Self { pub fn new(
block_commitment: HashMap<Slot, BlockCommitment>,
total_stake: u64,
bank: Arc<Bank>,
root: Slot,
) -> Self {
Self { Self {
block_commitment, block_commitment,
total_stake, total_stake,
bank,
root,
} }
} }
@ -53,38 +79,62 @@ impl BlockCommitmentCache {
self.total_stake self.total_stake
} }
pub fn get_block_with_depth_commitment( pub fn bank(&self) -> Arc<Bank> {
&self, self.bank.clone()
minimum_depth: usize,
minimum_stake_percentage: f64,
) -> Option<Slot> {
self.block_commitment
.iter()
.filter(|&(_, block_commitment)| {
let fork_stake_minimum_depth: u64 = block_commitment.commitment[minimum_depth..]
.iter()
.cloned()
.sum();
fork_stake_minimum_depth as f64 / self.total_stake as f64
>= minimum_stake_percentage
})
.map(|(slot, _)| *slot)
.max()
} }
pub fn get_rooted_block_with_commitment(&self, minimum_stake_percentage: f64) -> Option<u64> { pub fn slot(&self) -> Slot {
self.get_block_with_depth_commitment(MAX_LOCKOUT_HISTORY - 1, minimum_stake_percentage) self.bank.slot()
}
pub fn root(&self) -> Slot {
self.root
}
pub fn get_confirmation_count(&self, slot: Slot) -> Option<usize> {
self.get_lockout_count(slot, VOTE_THRESHOLD_SIZE)
}
// Returns the lowest level at which at least `minimum_stake_percentage` of the total epoch
// stake is locked out
fn get_lockout_count(&self, slot: Slot, minimum_stake_percentage: f64) -> Option<usize> {
self.get_block_commitment(slot).map(|block_commitment| {
let iterator = block_commitment.commitment.iter().enumerate().rev();
let mut sum = 0;
for (i, stake) in iterator {
sum += stake;
if (sum as f64 / self.total_stake as f64) > minimum_stake_percentage {
return i + 1;
}
}
0
})
}
#[cfg(test)]
pub fn new_for_tests() -> Self {
let mut block_commitment: HashMap<Slot, BlockCommitment> = HashMap::new();
block_commitment.insert(0, BlockCommitment::default());
Self {
block_commitment,
total_stake: 42,
..Self::default()
}
} }
} }
pub struct CommitmentAggregationData { pub struct CommitmentAggregationData {
bank: Arc<Bank>, bank: Arc<Bank>,
root: Slot,
total_staked: u64, total_staked: u64,
} }
impl CommitmentAggregationData { impl CommitmentAggregationData {
pub fn new(bank: Arc<Bank>, total_staked: u64) -> Self { pub fn new(bank: Arc<Bank>, root: Slot, total_staked: u64) -> Self {
Self { bank, total_staked } Self {
bank,
root,
total_staked,
}
} }
} }
@ -144,14 +194,24 @@ impl AggregateCommitmentService {
continue; continue;
} }
let mut aggregate_commitment_time = Measure::start("aggregate-commitment-ms");
let block_commitment = Self::aggregate_commitment(&ancestors, &aggregation_data.bank); let block_commitment = Self::aggregate_commitment(&ancestors, &aggregation_data.bank);
let mut new_block_commitment = let mut new_block_commitment = BlockCommitmentCache::new(
BlockCommitmentCache::new(block_commitment, aggregation_data.total_staked); block_commitment,
aggregation_data.total_staked,
aggregation_data.bank,
aggregation_data.root,
);
let mut w_block_commitment_cache = block_commitment_cache.write().unwrap(); let mut w_block_commitment_cache = block_commitment_cache.write().unwrap();
std::mem::swap(&mut *w_block_commitment_cache, &mut new_block_commitment); std::mem::swap(&mut *w_block_commitment_cache, &mut new_block_commitment);
aggregate_commitment_time.stop();
inc_new_counter_info!(
"aggregate-commitment-ms",
aggregate_commitment_time.as_ms() as usize
);
} }
} }
@ -246,84 +306,31 @@ mod tests {
} }
#[test] #[test]
fn test_get_block_with_depth_commitment() { fn test_get_confirmations() {
let bank = Arc::new(Bank::default());
// Build BlockCommitmentCache with votes at depths 0 and 1 for 2 slots // Build BlockCommitmentCache with votes at depths 0 and 1 for 2 slots
let mut cache0 = BlockCommitment::default(); let mut cache0 = BlockCommitment::default();
cache0.increase_confirmation_stake(1, 15); cache0.increase_confirmation_stake(1, 5);
cache0.increase_confirmation_stake(2, 25); cache0.increase_confirmation_stake(2, 40);
let mut cache1 = BlockCommitment::default(); let mut cache1 = BlockCommitment::default();
cache1.increase_confirmation_stake(1, 10); cache1.increase_confirmation_stake(1, 40);
cache1.increase_confirmation_stake(2, 20); cache1.increase_confirmation_stake(2, 5);
let mut cache2 = BlockCommitment::default();
cache2.increase_confirmation_stake(1, 20);
cache2.increase_confirmation_stake(2, 5);
let mut block_commitment = HashMap::new(); let mut block_commitment = HashMap::new();
block_commitment.entry(0).or_insert(cache0.clone()); block_commitment.entry(0).or_insert(cache0.clone());
block_commitment.entry(1).or_insert(cache1.clone()); block_commitment.entry(1).or_insert(cache1.clone());
let block_commitment_cache = BlockCommitmentCache::new(block_commitment, 50); block_commitment.entry(2).or_insert(cache2.clone());
let block_commitment_cache = BlockCommitmentCache::new(block_commitment, 50, bank, 0);
// Neither slot has rooted votes assert_eq!(block_commitment_cache.get_confirmation_count(0), Some(2));
assert_eq!( assert_eq!(block_commitment_cache.get_confirmation_count(1), Some(1));
block_commitment_cache.get_rooted_block_with_commitment(0.1), assert_eq!(block_commitment_cache.get_confirmation_count(2), Some(0),);
None assert_eq!(block_commitment_cache.get_confirmation_count(3), None,);
);
// Neither slot meets the minimum level of commitment 0.6 at depth 1
assert_eq!(
block_commitment_cache.get_block_with_depth_commitment(1, 0.6),
None
);
// Only slot 0 meets the minimum level of commitment 0.5 at depth 1
assert_eq!(
block_commitment_cache.get_block_with_depth_commitment(1, 0.5),
Some(0)
);
// If multiple slots meet the minimum level of commitment, method should return the most recent
assert_eq!(
block_commitment_cache.get_block_with_depth_commitment(1, 0.4),
Some(1)
);
// If multiple slots meet the minimum level of commitment, method should return the most recent
assert_eq!(
block_commitment_cache.get_block_with_depth_commitment(0, 0.6),
Some(1)
);
// Neither slot meets the minimum level of commitment 0.9 at depth 0
assert_eq!(
block_commitment_cache.get_block_with_depth_commitment(0, 0.9),
None
);
}
#[test]
fn test_get_rooted_block_with_commitment() {
// Build BlockCommitmentCache with rooted votes
let mut cache0 = BlockCommitment::new([0; MAX_LOCKOUT_HISTORY]);
cache0.increase_confirmation_stake(MAX_LOCKOUT_HISTORY, 40);
cache0.increase_confirmation_stake(MAX_LOCKOUT_HISTORY - 1, 10);
let mut cache1 = BlockCommitment::new([0; MAX_LOCKOUT_HISTORY]);
cache1.increase_confirmation_stake(MAX_LOCKOUT_HISTORY, 30);
cache1.increase_confirmation_stake(MAX_LOCKOUT_HISTORY - 1, 10);
cache1.increase_confirmation_stake(MAX_LOCKOUT_HISTORY - 2, 10);
let mut block_commitment = HashMap::new();
block_commitment.entry(0).or_insert(cache0.clone());
block_commitment.entry(1).or_insert(cache1.clone());
let block_commitment_cache = BlockCommitmentCache::new(block_commitment, 50);
// Only slot 0 meets the minimum level of commitment 0.66 at root
assert_eq!(
block_commitment_cache.get_rooted_block_with_commitment(0.66),
Some(0)
);
// If multiple slots meet the minimum level of commitment, method should return the most recent
assert_eq!(
block_commitment_cache.get_rooted_block_with_commitment(0.6),
Some(1)
);
// Neither slot meets the minimum level of commitment 0.9 at root
assert_eq!(
block_commitment_cache.get_rooted_block_with_commitment(0.9),
None
);
} }
#[test] #[test]

View File

@ -358,11 +358,12 @@ impl Tower {
pub(crate) fn check_switch_threshold( pub(crate) fn check_switch_threshold(
&self, &self,
_slot: u64, _slot: Slot,
_ancestors: &HashMap<Slot, HashSet<u64>>, _ancestors: &HashMap<Slot, HashSet<u64>>,
_descendants: &HashMap<Slot, HashSet<u64>>, _descendants: &HashMap<Slot, HashSet<u64>>,
_progress: &ProgressMap, _progress: &ProgressMap,
_total_stake: u64, _total_epoch_stake: u64,
_epoch_vote_accounts: &HashMap<Pubkey, (u64, Account)>,
) -> bool { ) -> bool {
true true
} }
@ -482,6 +483,7 @@ pub mod test {
use super::*; use super::*;
use crate::{ use crate::{
cluster_info_vote_listener::VoteTracker, cluster_info_vote_listener::VoteTracker,
cluster_slots::ClusterSlots,
progress_map::ForkProgress, progress_map::ForkProgress,
replay_stage::{HeaviestForkFailures, ReplayStage}, replay_stage::{HeaviestForkFailures, ReplayStage},
}; };
@ -612,6 +614,7 @@ pub mod test {
tower, tower,
progress, progress,
&VoteTracker::default(), &VoteTracker::default(),
&ClusterSlots::default(),
bank_forks, bank_forks,
&mut HashSet::new(), &mut HashSet::new(),
); );
@ -645,7 +648,6 @@ pub mod test {
bank_forks, bank_forks,
progress, progress,
&None, &None,
&mut 0,
&mut HashSet::new(), &mut HashSet::new(),
); );
} }

View File

@ -69,8 +69,8 @@ pub enum CrdsData {
Vote(VoteIndex, Vote), Vote(VoteIndex, Vote),
LowestSlot(u8, LowestSlot), LowestSlot(u8, LowestSlot),
SnapshotHashes(SnapshotHash), SnapshotHashes(SnapshotHash),
EpochSlots(EpochSlotsIndex, EpochSlots),
AccountsHashes(SnapshotHash), AccountsHashes(SnapshotHash),
EpochSlots(EpochSlotsIndex, EpochSlots),
} }
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
@ -189,8 +189,8 @@ impl CrdsValue {
CrdsData::Vote(_, vote) => vote.wallclock, CrdsData::Vote(_, vote) => vote.wallclock,
CrdsData::LowestSlot(_, obj) => obj.wallclock, CrdsData::LowestSlot(_, obj) => obj.wallclock,
CrdsData::SnapshotHashes(hash) => hash.wallclock, CrdsData::SnapshotHashes(hash) => hash.wallclock,
CrdsData::EpochSlots(_, p) => p.wallclock,
CrdsData::AccountsHashes(hash) => hash.wallclock, CrdsData::AccountsHashes(hash) => hash.wallclock,
CrdsData::EpochSlots(_, p) => p.wallclock,
} }
} }
pub fn pubkey(&self) -> Pubkey { pub fn pubkey(&self) -> Pubkey {
@ -199,8 +199,8 @@ impl CrdsValue {
CrdsData::Vote(_, vote) => vote.from, CrdsData::Vote(_, vote) => vote.from,
CrdsData::LowestSlot(_, slots) => slots.from, CrdsData::LowestSlot(_, slots) => slots.from,
CrdsData::SnapshotHashes(hash) => hash.from, CrdsData::SnapshotHashes(hash) => hash.from,
CrdsData::EpochSlots(_, p) => p.from,
CrdsData::AccountsHashes(hash) => hash.from, CrdsData::AccountsHashes(hash) => hash.from,
CrdsData::EpochSlots(_, p) => p.from,
} }
} }
pub fn label(&self) -> CrdsValueLabel { pub fn label(&self) -> CrdsValueLabel {
@ -209,8 +209,8 @@ impl CrdsValue {
CrdsData::Vote(ix, _) => CrdsValueLabel::Vote(*ix, self.pubkey()), CrdsData::Vote(ix, _) => CrdsValueLabel::Vote(*ix, self.pubkey()),
CrdsData::LowestSlot(_, _) => CrdsValueLabel::LowestSlot(self.pubkey()), CrdsData::LowestSlot(_, _) => CrdsValueLabel::LowestSlot(self.pubkey()),
CrdsData::SnapshotHashes(_) => CrdsValueLabel::SnapshotHashes(self.pubkey()), CrdsData::SnapshotHashes(_) => CrdsValueLabel::SnapshotHashes(self.pubkey()),
CrdsData::EpochSlots(ix, _) => CrdsValueLabel::EpochSlots(*ix, self.pubkey()),
CrdsData::AccountsHashes(_) => CrdsValueLabel::AccountsHashes(self.pubkey()), CrdsData::AccountsHashes(_) => CrdsValueLabel::AccountsHashes(self.pubkey()),
CrdsData::EpochSlots(ix, _) => CrdsValueLabel::EpochSlots(*ix, self.pubkey()),
} }
} }
pub fn contact_info(&self) -> Option<&ContactInfo> { pub fn contact_info(&self) -> Option<&ContactInfo> {

View File

@ -1,6 +1,8 @@
//! The `ledger_cleanup_service` drops older ledger data to limit disk space usage //! The `ledger_cleanup_service` drops older ledger data to limit disk space usage
use solana_ledger::blockstore::Blockstore; use solana_ledger::blockstore::Blockstore;
use solana_ledger::blockstore_db::Result as BlockstoreResult;
use solana_measure::measure::Measure;
use solana_metrics::datapoint_debug; use solana_metrics::datapoint_debug;
use solana_sdk::clock::Slot; use solana_sdk::clock::Slot;
use std::string::ToString; use std::string::ToString;
@ -11,13 +13,22 @@ use std::thread;
use std::thread::{Builder, JoinHandle}; use std::thread::{Builder, JoinHandle};
use std::time::Duration; use std::time::Duration;
// - To try and keep the RocksDB size under 512GB:
// Seeing about 1600b/shred, using 2000b/shred for margin, so 250m shreds can be stored in 512gb.
// at 5k shreds/slot at 50k tps, this is 500k slots (~5.5 hours).
// At idle, 60 shreds/slot this is about 4m slots (18 days)
// This is chosen to allow enough time for // This is chosen to allow enough time for
// - To try and keep the RocksDB size under 512GB at 50k tps (100 slots take ~2GB).
// - A validator to download a snapshot from a peer and boot from it // - A validator to download a snapshot from a peer and boot from it
// - To make sure that if a validator needs to reboot from its own snapshot, it has enough slots locally // - To make sure that if a validator needs to reboot from its own snapshot, it has enough slots locally
// to catch back up to where it was when it stopped // to catch back up to where it was when it stopped
pub const DEFAULT_MAX_LEDGER_SLOTS: u64 = 270_000; pub const DEFAULT_MAX_LEDGER_SHREDS: u64 = 250_000_000;
// Remove a fixed number of slots at a time, it's more efficient than doing it one-by-one
// Check for removing slots at this interval so we don't purge too often
// and starve other blockstore users.
pub const DEFAULT_PURGE_SLOT_INTERVAL: u64 = 512;
// Remove a limited number of slots at a time, so the operation
// does not take too long and block other blockstore users.
pub const DEFAULT_PURGE_BATCH_SIZE: u64 = 256; pub const DEFAULT_PURGE_BATCH_SIZE: u64 = 256;
pub struct LedgerCleanupService { pub struct LedgerCleanupService {
@ -36,7 +47,7 @@ impl LedgerCleanupService {
max_ledger_slots max_ledger_slots
); );
let exit = exit.clone(); let exit = exit.clone();
let mut next_purge_batch = max_ledger_slots; let mut last_purge_slot = 0;
let t_cleanup = Builder::new() let t_cleanup = Builder::new()
.name("solana-ledger-cleanup".to_string()) .name("solana-ledger-cleanup".to_string())
.spawn(move || loop { .spawn(move || loop {
@ -47,7 +58,8 @@ impl LedgerCleanupService {
&new_root_receiver, &new_root_receiver,
&blockstore, &blockstore,
max_ledger_slots, max_ledger_slots,
&mut next_purge_batch, &mut last_purge_slot,
DEFAULT_PURGE_SLOT_INTERVAL,
) { ) {
match e { match e {
RecvTimeoutError::Disconnected => break, RecvTimeoutError::Disconnected => break,
@ -59,45 +71,123 @@ impl LedgerCleanupService {
Self { t_cleanup } Self { t_cleanup }
} }
fn find_slots_to_clean(
blockstore: &Arc<Blockstore>,
root: Slot,
max_ledger_shreds: u64,
) -> (u64, Slot, Slot) {
let mut shreds = Vec::new();
let mut iterate_time = Measure::start("iterate_time");
let mut total_shreds = 0;
let mut first_slot = 0;
for (i, (slot, meta)) in blockstore.slot_meta_iterator(0).unwrap().enumerate() {
if i == 0 {
first_slot = slot;
debug!("purge: searching from slot: {}", slot);
}
// Not exact since non-full slots will have holes
total_shreds += meta.received;
shreds.push((slot, meta.received));
if slot > root {
break;
}
}
iterate_time.stop();
info!(
"checking for ledger purge: max_shreds: {} slots: {} total_shreds: {} {}",
max_ledger_shreds,
shreds.len(),
total_shreds,
iterate_time
);
if (total_shreds as u64) < max_ledger_shreds {
return (0, 0, 0);
}
let mut cur_shreds = 0;
let mut lowest_slot_to_clean = shreds[0].0;
for (slot, num_shreds) in shreds.iter().rev() {
cur_shreds += *num_shreds as u64;
if cur_shreds > max_ledger_shreds {
lowest_slot_to_clean = *slot;
break;
}
}
(cur_shreds, lowest_slot_to_clean, first_slot)
}
fn cleanup_ledger( fn cleanup_ledger(
new_root_receiver: &Receiver<Slot>, new_root_receiver: &Receiver<Slot>,
blockstore: &Arc<Blockstore>, blockstore: &Arc<Blockstore>,
max_ledger_slots: u64, max_ledger_shreds: u64,
next_purge_batch: &mut u64, last_purge_slot: &mut u64,
purge_interval: u64,
) -> Result<(), RecvTimeoutError> { ) -> Result<(), RecvTimeoutError> {
let disk_utilization_pre = blockstore.storage_size(); let mut root = new_root_receiver.recv_timeout(Duration::from_secs(1))?;
// Get the newest root
let root = new_root_receiver.recv_timeout(Duration::from_secs(1))?; while let Ok(new_root) = new_root_receiver.try_recv() {
root = new_root;
// Notify blockstore of impending purge
if root > *next_purge_batch {
//cleanup
let lowest_slot = root - max_ledger_slots;
*blockstore.lowest_cleanup_slot.write().unwrap() = lowest_slot;
blockstore.purge_slots(0, Some(lowest_slot));
*next_purge_batch += DEFAULT_PURGE_BATCH_SIZE;
} }
let disk_utilization_post = blockstore.storage_size(); if root - *last_purge_slot > purge_interval {
let disk_utilization_pre = blockstore.storage_size();
if let (Ok(disk_utilization_pre), Ok(disk_utilization_post)) = info!(
(disk_utilization_pre, disk_utilization_post) "purge: new root: {} last_purge: {} purge_interval: {} disk: {:?}",
{ root, last_purge_slot, purge_interval, disk_utilization_pre
datapoint_debug!(
"ledger_disk_utilization",
("disk_utilization_pre", disk_utilization_pre as i64, i64),
("disk_utilization_post", disk_utilization_post as i64, i64),
(
"disk_utilization_delta",
(disk_utilization_pre as i64 - disk_utilization_post as i64),
i64
)
); );
*last_purge_slot = root;
let (num_shreds_to_clean, lowest_slot_to_clean, mut first_slot) =
Self::find_slots_to_clean(blockstore, root, max_ledger_shreds);
if num_shreds_to_clean > 0 {
debug!(
"cleaning up to: {} shreds: {} first: {}",
lowest_slot_to_clean, num_shreds_to_clean, first_slot
);
loop {
let current_lowest =
std::cmp::min(lowest_slot_to_clean, first_slot + DEFAULT_PURGE_BATCH_SIZE);
let mut slot_update_time = Measure::start("slot_update");
*blockstore.lowest_cleanup_slot.write().unwrap() = current_lowest;
slot_update_time.stop();
let mut clean_time = Measure::start("ledger_clean");
blockstore.purge_slots(first_slot, Some(current_lowest));
clean_time.stop();
debug!(
"ledger purge {} -> {}: {} {}",
first_slot, current_lowest, slot_update_time, clean_time
);
first_slot += DEFAULT_PURGE_BATCH_SIZE;
if current_lowest == lowest_slot_to_clean {
break;
}
thread::sleep(Duration::from_millis(500));
}
}
let disk_utilization_post = blockstore.storage_size();
Self::report_disk_metrics(disk_utilization_pre, disk_utilization_post);
} }
Ok(()) Ok(())
} }
fn report_disk_metrics(pre: BlockstoreResult<u64>, post: BlockstoreResult<u64>) {
if let (Ok(pre), Ok(post)) = (pre, post) {
datapoint_debug!(
"ledger_disk_utilization",
("disk_utilization_pre", pre as i64, i64),
("disk_utilization_post", post as i64, i64),
("disk_utilization_delta", (pre as i64 - post as i64), i64)
);
}
}
pub fn join(self) -> thread::Result<()> { pub fn join(self) -> thread::Result<()> {
self.t_cleanup.join() self.t_cleanup.join()
} }
@ -111,6 +201,7 @@ mod tests {
#[test] #[test]
fn test_cleanup() { fn test_cleanup() {
solana_logger::setup();
let blockstore_path = get_tmp_ledger_path!(); let blockstore_path = get_tmp_ledger_path!();
let blockstore = Blockstore::open(&blockstore_path).unwrap(); let blockstore = Blockstore::open(&blockstore_path).unwrap();
let (shreds, _) = make_many_slot_entries(0, 50, 5); let (shreds, _) = make_many_slot_entries(0, 50, 5);
@ -118,10 +209,10 @@ mod tests {
let blockstore = Arc::new(blockstore); let blockstore = Arc::new(blockstore);
let (sender, receiver) = channel(); let (sender, receiver) = channel();
//send a signal to kill slots 0-40 //send a signal to kill all but 5 shreds, which will be in the newest slots
let mut next_purge_slot = 0; let mut last_purge_slot = 0;
sender.send(50).unwrap(); sender.send(50).unwrap();
LedgerCleanupService::cleanup_ledger(&receiver, &blockstore, 10, &mut next_purge_slot) LedgerCleanupService::cleanup_ledger(&receiver, &blockstore, 5, &mut last_purge_slot, 10)
.unwrap(); .unwrap();
//check that 0-40 don't exist //check that 0-40 don't exist
@ -134,6 +225,62 @@ mod tests {
Blockstore::destroy(&blockstore_path).expect("Expected successful database destruction"); Blockstore::destroy(&blockstore_path).expect("Expected successful database destruction");
} }
#[test]
fn test_cleanup_speed() {
solana_logger::setup();
let blockstore_path = get_tmp_ledger_path!();
let mut blockstore = Blockstore::open(&blockstore_path).unwrap();
blockstore.set_no_compaction(true);
let blockstore = Arc::new(blockstore);
let (sender, receiver) = channel();
let mut first_insert = Measure::start("first_insert");
let initial_slots = 50;
let initial_entries = 5;
let (shreds, _) = make_many_slot_entries(0, initial_slots, initial_entries);
blockstore.insert_shreds(shreds, None, false).unwrap();
first_insert.stop();
info!("{}", first_insert);
let mut last_purge_slot = 0;
let mut slot = initial_slots;
let mut num_slots = 6;
for _ in 0..5 {
let mut insert_time = Measure::start("insert time");
let batch_size = 2;
let batches = num_slots / batch_size;
for i in 0..batches {
let (shreds, _) = make_many_slot_entries(slot + i * batch_size, batch_size, 5);
blockstore.insert_shreds(shreds, None, false).unwrap();
if i % 100 == 0 {
info!("inserting..{} of {}", i, batches);
}
}
insert_time.stop();
let mut time = Measure::start("purge time");
sender.send(slot + num_slots).unwrap();
LedgerCleanupService::cleanup_ledger(
&receiver,
&blockstore,
initial_slots,
&mut last_purge_slot,
10,
)
.unwrap();
time.stop();
info!(
"slot: {} size: {} {} {}",
slot, num_slots, insert_time, time
);
slot += num_slots;
num_slots *= 2;
}
drop(blockstore);
Blockstore::destroy(&blockstore_path).expect("Expected successful database destruction");
}
#[test] #[test]
fn test_compaction() { fn test_compaction() {
let blockstore_path = get_tmp_ledger_path!(); let blockstore_path = get_tmp_ledger_path!();
@ -142,7 +289,7 @@ mod tests {
let n = 10_000; let n = 10_000;
let batch_size = 100; let batch_size = 100;
let batches = n / batch_size; let batches = n / batch_size;
let max_ledger_slots = 100; let max_ledger_shreds = 100;
for i in 0..batches { for i in 0..batches {
let (shreds, _) = make_many_slot_entries(i * batch_size, batch_size, 1); let (shreds, _) = make_many_slot_entries(i * batch_size, batch_size, 1);
@ -158,8 +305,9 @@ mod tests {
LedgerCleanupService::cleanup_ledger( LedgerCleanupService::cleanup_ledger(
&receiver, &receiver,
&blockstore, &blockstore,
max_ledger_slots, max_ledger_shreds,
&mut next_purge_batch, &mut next_purge_batch,
10,
) )
.unwrap(); .unwrap();
@ -170,7 +318,7 @@ mod tests {
assert!(u2 < u1, "insufficient compaction! pre={},post={}", u1, u2,); assert!(u2 < u1, "insufficient compaction! pre={},post={}", u1, u2,);
// check that early slots don't exist // check that early slots don't exist
let max_slot = n - max_ledger_slots; let max_slot = n - max_ledger_shreds - 1;
blockstore blockstore
.slot_meta_iterator(0) .slot_meta_iterator(0)
.unwrap() .unwrap()

View File

@ -1,13 +1,13 @@
use crate::{ use crate::{
cluster_info_vote_listener::SlotVoteTracker, consensus::StakeLockout, cluster_info_vote_listener::SlotVoteTracker, cluster_slots::SlotPubkeys,
replay_stage::SUPERMINORITY_THRESHOLD, consensus::StakeLockout, replay_stage::SUPERMINORITY_THRESHOLD,
}; };
use solana_ledger::{ use solana_ledger::{
bank_forks::BankForks, bank_forks::BankForks,
blockstore_processor::{ConfirmationProgress, ConfirmationTiming}, blockstore_processor::{ConfirmationProgress, ConfirmationTiming},
}; };
use solana_runtime::bank::Bank; use solana_runtime::bank::Bank;
use solana_sdk::{clock::Slot, hash::Hash, pubkey::Pubkey}; use solana_sdk::{account::Account, clock::Slot, hash::Hash, pubkey::Pubkey};
use std::{ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
rc::Rc, rc::Rc,
@ -179,14 +179,84 @@ pub(crate) struct ForkStats {
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub(crate) struct PropagatedStats { pub(crate) struct PropagatedStats {
pub(crate) propagated_validators: HashSet<Rc<Pubkey>>, pub(crate) propagated_validators: HashSet<Rc<Pubkey>>,
pub(crate) propagated_node_ids: HashSet<Rc<Pubkey>>,
pub(crate) propagated_validators_stake: u64, pub(crate) propagated_validators_stake: u64,
pub(crate) is_propagated: bool, pub(crate) is_propagated: bool,
pub(crate) is_leader_slot: bool, pub(crate) is_leader_slot: bool,
pub(crate) prev_leader_slot: Option<Slot>, pub(crate) prev_leader_slot: Option<Slot>,
pub(crate) slot_vote_tracker: Option<Arc<RwLock<SlotVoteTracker>>>, pub(crate) slot_vote_tracker: Option<Arc<RwLock<SlotVoteTracker>>>,
pub(crate) cluster_slot_pubkeys: Option<Arc<RwLock<SlotPubkeys>>>,
pub(crate) total_epoch_stake: u64, pub(crate) total_epoch_stake: u64,
} }
impl PropagatedStats {
pub fn add_vote_pubkey(
&mut self,
vote_pubkey: &Pubkey,
all_pubkeys: &mut HashSet<Rc<Pubkey>>,
stake: u64,
) {
if !self.propagated_validators.contains(vote_pubkey) {
let mut cached_pubkey: Option<Rc<Pubkey>> = all_pubkeys.get(vote_pubkey).cloned();
if cached_pubkey.is_none() {
let new_pubkey = Rc::new(*vote_pubkey);
all_pubkeys.insert(new_pubkey.clone());
cached_pubkey = Some(new_pubkey);
}
let vote_pubkey = cached_pubkey.unwrap();
self.propagated_validators.insert(vote_pubkey);
self.propagated_validators_stake += stake;
}
}
pub fn add_node_pubkey(
&mut self,
node_pubkey: &Pubkey,
all_pubkeys: &mut HashSet<Rc<Pubkey>>,
bank: &Bank,
) {
if !self.propagated_node_ids.contains(node_pubkey) {
let node_vote_accounts = bank
.epoch_vote_accounts_for_node_id(&node_pubkey)
.map(|v| &v.vote_accounts);
if let Some(node_vote_accounts) = node_vote_accounts {
self.add_node_pubkey_internal(
node_pubkey,
all_pubkeys,
node_vote_accounts,
bank.epoch_vote_accounts(bank.epoch())
.expect("Epoch stakes for bank's own epoch must exist"),
);
}
}
}
fn add_node_pubkey_internal(
&mut self,
node_pubkey: &Pubkey,
all_pubkeys: &mut HashSet<Rc<Pubkey>>,
vote_account_pubkeys: &[Pubkey],
epoch_vote_accounts: &HashMap<Pubkey, (u64, Account)>,
) {
let mut cached_pubkey: Option<Rc<Pubkey>> = all_pubkeys.get(node_pubkey).cloned();
if cached_pubkey.is_none() {
let new_pubkey = Rc::new(*node_pubkey);
all_pubkeys.insert(new_pubkey.clone());
cached_pubkey = Some(new_pubkey);
}
let node_pubkey = cached_pubkey.unwrap();
self.propagated_node_ids.insert(node_pubkey);
for vote_account_pubkey in vote_account_pubkeys.iter() {
let stake = epoch_vote_accounts
.get(vote_account_pubkey)
.map(|(stake, _)| *stake)
.unwrap_or(0);
self.add_vote_pubkey(vote_account_pubkey, all_pubkeys, stake);
}
}
}
#[derive(Default)] #[derive(Default)]
pub(crate) struct ProgressMap { pub(crate) struct ProgressMap {
progress_map: HashMap<Slot, ForkProgress>, progress_map: HashMap<Slot, ForkProgress>,
@ -288,6 +358,120 @@ impl ProgressMap {
mod test { mod test {
use super::*; use super::*;
#[test]
fn test_add_vote_pubkey() {
let mut stats = PropagatedStats::default();
let mut all_pubkeys = HashSet::new();
let mut vote_pubkey = Pubkey::new_rand();
all_pubkeys.insert(Rc::new(vote_pubkey.clone()));
// Add a vote pubkey, the number of references in all_pubkeys
// should be 2
stats.add_vote_pubkey(&vote_pubkey, &mut all_pubkeys, 1);
assert!(stats.propagated_validators.contains(&vote_pubkey));
assert_eq!(stats.propagated_validators_stake, 1);
assert_eq!(Rc::strong_count(all_pubkeys.get(&vote_pubkey).unwrap()), 2);
// Adding it again should change no state since the key already existed
stats.add_vote_pubkey(&vote_pubkey, &mut all_pubkeys, 1);
assert!(stats.propagated_validators.contains(&vote_pubkey));
assert_eq!(stats.propagated_validators_stake, 1);
// Addding another pubkey should succeed
vote_pubkey = Pubkey::new_rand();
stats.add_vote_pubkey(&vote_pubkey, &mut all_pubkeys, 2);
assert!(stats.propagated_validators.contains(&vote_pubkey));
assert_eq!(stats.propagated_validators_stake, 3);
assert_eq!(Rc::strong_count(all_pubkeys.get(&vote_pubkey).unwrap()), 2);
}
#[test]
fn test_add_node_pubkey_internal() {
let num_vote_accounts = 10;
let staked_vote_accounts = 5;
let vote_account_pubkeys: Vec<_> = std::iter::repeat_with(|| Pubkey::new_rand())
.take(num_vote_accounts)
.collect();
let epoch_vote_accounts: HashMap<_, _> = vote_account_pubkeys
.iter()
.skip(num_vote_accounts - staked_vote_accounts)
.map(|pubkey| (*pubkey, (1, Account::default())))
.collect();
let mut stats = PropagatedStats::default();
let mut all_pubkeys = HashSet::new();
let mut node_pubkey = Pubkey::new_rand();
all_pubkeys.insert(Rc::new(node_pubkey.clone()));
// Add a vote pubkey, the number of references in all_pubkeys
// should be 2
stats.add_node_pubkey_internal(
&node_pubkey,
&mut all_pubkeys,
&vote_account_pubkeys,
&epoch_vote_accounts,
);
assert!(stats.propagated_node_ids.contains(&node_pubkey));
assert_eq!(
stats.propagated_validators_stake,
staked_vote_accounts as u64
);
assert_eq!(Rc::strong_count(all_pubkeys.get(&node_pubkey).unwrap()), 2);
// Adding it again should not change any state
stats.add_node_pubkey_internal(
&node_pubkey,
&mut all_pubkeys,
&vote_account_pubkeys,
&epoch_vote_accounts,
);
assert!(stats.propagated_node_ids.contains(&node_pubkey));
assert_eq!(
stats.propagated_validators_stake,
staked_vote_accounts as u64
);
// Addding another pubkey with same vote accounts should succeed, but stake
// shouldn't increase
node_pubkey = Pubkey::new_rand();
stats.add_node_pubkey_internal(
&node_pubkey,
&mut all_pubkeys,
&vote_account_pubkeys,
&epoch_vote_accounts,
);
assert!(stats.propagated_node_ids.contains(&node_pubkey));
assert_eq!(
stats.propagated_validators_stake,
staked_vote_accounts as u64
);
assert_eq!(Rc::strong_count(all_pubkeys.get(&node_pubkey).unwrap()), 2);
// Addding another pubkey with different vote accounts should succeed
// and increase stake
node_pubkey = Pubkey::new_rand();
let vote_account_pubkeys: Vec<_> = std::iter::repeat_with(|| Pubkey::new_rand())
.take(num_vote_accounts)
.collect();
let epoch_vote_accounts: HashMap<_, _> = vote_account_pubkeys
.iter()
.skip(num_vote_accounts - staked_vote_accounts)
.map(|pubkey| (*pubkey, (1, Account::default())))
.collect();
stats.add_node_pubkey_internal(
&node_pubkey,
&mut all_pubkeys,
&vote_account_pubkeys,
&epoch_vote_accounts,
);
assert!(stats.propagated_node_ids.contains(&node_pubkey));
assert_eq!(
stats.propagated_validators_stake,
2 * staked_vote_accounts as u64
);
assert_eq!(Rc::strong_count(all_pubkeys.get(&node_pubkey).unwrap()), 2);
}
#[test] #[test]
fn test_is_propagated_status_on_construction() { fn test_is_propagated_status_on_construction() {
// If the given ValidatorStakeInfo == None, then this is not // If the given ValidatorStakeInfo == None, then this is not

View File

@ -20,9 +20,31 @@ use std::{
sync::{Arc, RwLock}, sync::{Arc, RwLock},
thread::sleep, thread::sleep,
thread::{self, Builder, JoinHandle}, thread::{self, Builder, JoinHandle},
time::Duration, time::{Duration, Instant},
}; };
#[derive(Default)]
pub struct RepairStatsGroup {
pub count: u64,
pub min: u64,
pub max: u64,
}
impl RepairStatsGroup {
pub fn update(&mut self, slot: u64) {
self.count += 1;
self.min = std::cmp::min(self.min, slot);
self.max = std::cmp::max(self.max, slot);
}
}
#[derive(Default)]
pub struct RepairStats {
pub shred: RepairStatsGroup,
pub highest_shred: RepairStatsGroup,
pub orphan: RepairStatsGroup,
}
pub const MAX_REPAIR_LENGTH: usize = 512; pub const MAX_REPAIR_LENGTH: usize = 512;
pub const REPAIR_MS: u64 = 100; pub const REPAIR_MS: u64 = 100;
pub const MAX_ORPHANS: usize = 5; pub const MAX_ORPHANS: usize = 5;
@ -93,6 +115,8 @@ impl RepairService {
if let RepairStrategy::RepairAll { .. } = repair_strategy { if let RepairStrategy::RepairAll { .. } = repair_strategy {
Self::initialize_lowest_slot(id, blockstore, cluster_info); Self::initialize_lowest_slot(id, blockstore, cluster_info);
} }
let mut repair_stats = RepairStats::default();
let mut last_stats = Instant::now();
loop { loop {
if exit.load(Ordering::Relaxed) { if exit.load(Ordering::Relaxed) {
break; break;
@ -137,7 +161,12 @@ impl RepairService {
.into_iter() .into_iter()
.filter_map(|repair_request| { .filter_map(|repair_request| {
serve_repair serve_repair
.repair_request(&cluster_slots, &repair_request, &mut cache) .repair_request(
&cluster_slots,
&repair_request,
&mut cache,
&mut repair_stats,
)
.map(|result| (result, repair_request)) .map(|result| (result, repair_request))
.ok() .ok()
}) })
@ -150,6 +179,24 @@ impl RepairService {
}); });
} }
} }
if last_stats.elapsed().as_secs() > 1 {
let repair_total = repair_stats.shred.count
+ repair_stats.highest_shred.count
+ repair_stats.orphan.count;
if repair_total > 0 {
datapoint_info!(
"serve_repair-repair",
("repair-total", repair_total, i64),
("shred-count", repair_stats.shred.count, i64),
("highest-shred-count", repair_stats.highest_shred.count, i64),
("orphan-count", repair_stats.orphan.count, i64),
("repair-highest-slot", repair_stats.highest_shred.max, i64),
("repair-orphan", repair_stats.orphan.max, i64),
);
}
repair_stats = RepairStats::default();
last_stats = Instant::now();
}
sleep(Duration::from_millis(REPAIR_MS)); sleep(Duration::from_millis(REPAIR_MS));
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,7 @@ use solana_ledger::{
bank_forks::BankForks, blockstore::Blockstore, rooted_slot_iterator::RootedSlotIterator, bank_forks::BankForks, blockstore::Blockstore, rooted_slot_iterator::RootedSlotIterator,
}; };
use solana_perf::packet::PACKET_DATA_SIZE; use solana_perf::packet::PACKET_DATA_SIZE;
use solana_runtime::{bank::Bank, status_cache::SignatureConfirmationStatus}; use solana_runtime::bank::Bank;
use solana_sdk::{ use solana_sdk::{
clock::{Slot, UnixTimestamp}, clock::{Slot, UnixTimestamp},
commitment_config::{CommitmentConfig, CommitmentLevel}, commitment_config::{CommitmentConfig, CommitmentLevel},
@ -23,7 +23,7 @@ use solana_sdk::{
pubkey::Pubkey, pubkey::Pubkey,
signature::Signature, signature::Signature,
timing::slot_duration_from_slots_per_year, timing::slot_duration_from_slots_per_year,
transaction::Transaction, transaction::{self, Transaction},
}; };
use solana_transaction_status::{ConfirmedBlock, TransactionEncoding, TransactionStatus}; use solana_transaction_status::{ConfirmedBlock, TransactionEncoding, TransactionStatus};
use solana_vote_program::vote_state::{VoteState, MAX_LOCKOUT_HISTORY}; use solana_vote_program::vote_state::{VoteState, MAX_LOCKOUT_HISTORY};
@ -196,11 +196,9 @@ impl JsonRpcRequestProcessor {
match signature { match signature {
Err(e) => Err(e), Err(e) => Err(e),
Ok(sig) => { Ok(sig) => {
let status = bank.get_signature_confirmation_status(&sig); let status = bank.get_signature_status(&sig);
match status { match status {
Some(SignatureConfirmationStatus { status, .. }) => { Some(status) => new_response(bank, status.is_ok()),
new_response(bank, status.is_ok())
}
None => new_response(bank, false), None => new_response(bank, false),
} }
} }
@ -399,7 +397,35 @@ impl JsonRpcRequestProcessor {
.unwrap_or(None)) .unwrap_or(None))
} }
pub fn get_signature_confirmation_status(
&self,
signature: Signature,
commitment: Option<CommitmentConfig>,
) -> Option<RpcSignatureConfirmation> {
self.get_transaction_status(signature, &self.bank(commitment))
.map(
|TransactionStatus {
status,
confirmations,
..
}| RpcSignatureConfirmation {
confirmations: confirmations.unwrap_or(MAX_LOCKOUT_HISTORY + 1),
status,
},
)
}
pub fn get_signature_status( pub fn get_signature_status(
&self,
signature: Signature,
commitment: Option<CommitmentConfig>,
) -> Option<transaction::Result<()>> {
self.bank(commitment)
.get_signature_status_slot(&signature)
.map(|(_, status)| status)
}
pub fn get_signature_statuses(
&self, &self,
signatures: Vec<Signature>, signatures: Vec<Signature>,
commitment: Option<CommitmentConfig>, commitment: Option<CommitmentConfig>,
@ -409,21 +435,7 @@ impl JsonRpcRequestProcessor {
let bank = self.bank(commitment); let bank = self.bank(commitment);
for signature in signatures { for signature in signatures {
let status = bank.get_signature_confirmation_status(&signature).map( let status = self.get_transaction_status(signature, &bank);
|SignatureConfirmationStatus {
slot,
status,
confirmations,
}| TransactionStatus {
slot,
status,
confirmations: if confirmations <= MAX_LOCKOUT_HISTORY {
Some(confirmations)
} else {
None
},
},
);
statuses.push(status); statuses.push(status);
} }
Ok(Response { Ok(Response {
@ -431,6 +443,30 @@ impl JsonRpcRequestProcessor {
value: statuses, value: statuses,
}) })
} }
fn get_transaction_status(
&self,
signature: Signature,
bank: &Arc<Bank>,
) -> Option<TransactionStatus> {
bank.get_signature_status_slot(&signature)
.map(|(slot, status)| {
let r_block_commitment_cache = self.block_commitment_cache.read().unwrap();
let confirmations = if r_block_commitment_cache.root() >= slot {
None
} else {
r_block_commitment_cache
.get_confirmation_count(slot)
.or(Some(0))
};
TransactionStatus {
slot,
status,
confirmations,
}
})
}
} }
fn get_tpu_addr(cluster_info: &Arc<RwLock<ClusterInfo>>) -> Result<SocketAddr> { fn get_tpu_addr(cluster_info: &Arc<RwLock<ClusterInfo>>) -> Result<SocketAddr> {
@ -553,8 +589,24 @@ pub trait RpcSol {
#[rpc(meta, name = "getFeeRateGovernor")] #[rpc(meta, name = "getFeeRateGovernor")]
fn get_fee_rate_governor(&self, meta: Self::Metadata) -> RpcResponse<RpcFeeRateGovernor>; fn get_fee_rate_governor(&self, meta: Self::Metadata) -> RpcResponse<RpcFeeRateGovernor>;
#[rpc(meta, name = "getSignatureConfirmation")]
fn get_signature_confirmation(
&self,
meta: Self::Metadata,
signature_str: String,
commitment: Option<CommitmentConfig>,
) -> Result<Option<RpcSignatureConfirmation>>;
#[rpc(meta, name = "getSignatureStatus")] #[rpc(meta, name = "getSignatureStatus")]
fn get_signature_status( fn get_signature_status(
&self,
meta: Self::Metadata,
signature_str: String,
commitment: Option<CommitmentConfig>,
) -> Result<Option<transaction::Result<()>>>;
#[rpc(meta, name = "getSignatureStatuses")]
fn get_signature_statuses(
&self, &self,
meta: Self::Metadata, meta: Self::Metadata,
signature_strs: Vec<String>, signature_strs: Vec<String>,
@ -884,7 +936,39 @@ impl RpcSol for RpcSolImpl {
.get_fee_rate_governor() .get_fee_rate_governor()
} }
fn get_signature_confirmation(
&self,
meta: Self::Metadata,
signature_str: String,
commitment: Option<CommitmentConfig>,
) -> Result<Option<RpcSignatureConfirmation>> {
debug!(
"get_signature_confirmation rpc request received: {:?}",
signature_str
);
let signature = verify_signature(&signature_str)?;
Ok(meta
.request_processor
.read()
.unwrap()
.get_signature_confirmation_status(signature, commitment))
}
fn get_signature_status( fn get_signature_status(
&self,
meta: Self::Metadata,
signature_str: String,
commitment: Option<CommitmentConfig>,
) -> Result<Option<transaction::Result<()>>> {
let signature = verify_signature(&signature_str)?;
Ok(meta
.request_processor
.read()
.unwrap()
.get_signature_status(signature, commitment))
}
fn get_signature_statuses(
&self, &self,
meta: Self::Metadata, meta: Self::Metadata,
signature_strs: Vec<String>, signature_strs: Vec<String>,
@ -897,7 +981,7 @@ impl RpcSol for RpcSolImpl {
meta.request_processor meta.request_processor
.read() .read()
.unwrap() .unwrap()
.get_signature_status(signatures, commitment) .get_signature_statuses(signatures, commitment)
} }
fn get_slot(&self, meta: Self::Metadata, commitment: Option<CommitmentConfig>) -> Result<u64> { fn get_slot(&self, meta: Self::Metadata, commitment: Option<CommitmentConfig>) -> Result<u64> {
@ -990,7 +1074,7 @@ impl RpcSol for RpcSolImpl {
.request_processor .request_processor
.read() .read()
.unwrap() .unwrap()
.get_signature_status(vec![signature], commitment.clone())? .get_signature_statuses(vec![signature], commitment.clone())?
.value[0] .value[0]
.clone() .clone()
.map(|x| x.status); .map(|x| x.status);
@ -1222,18 +1306,6 @@ pub mod tests {
) -> RpcHandler { ) -> RpcHandler {
let (bank_forks, alice, leader_vote_keypair) = new_bank_forks(); let (bank_forks, alice, leader_vote_keypair) = new_bank_forks();
let bank = bank_forks.read().unwrap().working_bank(); let bank = bank_forks.read().unwrap().working_bank();
let commitment_slot0 = BlockCommitment::new([8; MAX_LOCKOUT_HISTORY]);
let commitment_slot1 = BlockCommitment::new([9; MAX_LOCKOUT_HISTORY]);
let mut block_commitment: HashMap<u64, BlockCommitment> = HashMap::new();
block_commitment
.entry(0)
.or_insert(commitment_slot0.clone());
block_commitment
.entry(1)
.or_insert(commitment_slot1.clone());
let block_commitment_cache =
Arc::new(RwLock::new(BlockCommitmentCache::new(block_commitment, 42)));
let ledger_path = get_tmp_ledger_path!(); let ledger_path = get_tmp_ledger_path!();
let blockstore = Blockstore::open(&ledger_path).unwrap(); let blockstore = Blockstore::open(&ledger_path).unwrap();
let blockstore = Arc::new(blockstore); let blockstore = Arc::new(blockstore);
@ -1249,6 +1321,24 @@ pub mod tests {
blockstore.clone(), blockstore.clone(),
); );
let mut commitment_slot0 = BlockCommitment::default();
commitment_slot0.increase_confirmation_stake(2, 9);
let mut commitment_slot1 = BlockCommitment::default();
commitment_slot1.increase_confirmation_stake(1, 9);
let mut block_commitment: HashMap<u64, BlockCommitment> = HashMap::new();
block_commitment
.entry(0)
.or_insert(commitment_slot0.clone());
block_commitment
.entry(1)
.or_insert(commitment_slot1.clone());
let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::new(
block_commitment,
10,
bank.clone(),
0,
)));
// Add timestamp vote to blockstore // Add timestamp vote to blockstore
let vote = Vote { let vote = Vote {
slots: vec![1], slots: vec![1],
@ -1750,6 +1840,76 @@ pub mod tests {
#[test] #[test]
fn test_rpc_get_signature_status() { fn test_rpc_get_signature_status() {
let bob_pubkey = Pubkey::new_rand();
let RpcHandler {
io,
meta,
blockhash,
alice,
..
} = start_rpc_handler_with_tx(&bob_pubkey);
let tx = system_transaction::transfer(&alice, &bob_pubkey, 20, blockhash);
let req = format!(
r#"{{"jsonrpc":"2.0","id":1,"method":"getSignatureStatus","params":["{}"]}}"#,
tx.signatures[0]
);
let res = io.handle_request_sync(&req, meta.clone());
let expected_res: Option<transaction::Result<()>> = Some(Ok(()));
let expected = json!({
"jsonrpc": "2.0",
"result": expected_res,
"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 getSignatureStatus request on unprocessed tx
let tx = system_transaction::transfer(&alice, &bob_pubkey, 10, blockhash);
let req = format!(
r#"{{"jsonrpc":"2.0","id":1,"method":"getSignatureStatus","params":["{}"]}}"#,
tx.signatures[0]
);
let res = io.handle_request_sync(&req, meta.clone());
let expected_res: Option<String> = None;
let expected = json!({
"jsonrpc": "2.0",
"result": expected_res,
"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 getSignatureStatus request on a TransactionError
let tx = system_transaction::transfer(&alice, &bob_pubkey, std::u64::MAX, blockhash);
let req = format!(
r#"{{"jsonrpc":"2.0","id":1,"method":"getSignatureStatus","params":["{}"]}}"#,
tx.signatures[0]
);
let res = io.handle_request_sync(&req, meta);
let expected_res: Option<transaction::Result<()>> = Some(Err(
TransactionError::InstructionError(0, InstructionError::CustomError(1)),
));
let expected = json!({
"jsonrpc": "2.0",
"result": expected_res,
"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_signature_statuses() {
let bob_pubkey = Pubkey::new_rand(); let bob_pubkey = Pubkey::new_rand();
let RpcHandler { let RpcHandler {
io, io,
@ -1761,7 +1921,7 @@ pub mod tests {
} = start_rpc_handler_with_tx(&bob_pubkey); } = start_rpc_handler_with_tx(&bob_pubkey);
let req = format!( let req = format!(
r#"{{"jsonrpc":"2.0","id":1,"method":"getSignatureStatus","params":[["{}"]]}}"#, r#"{{"jsonrpc":"2.0","id":1,"method":"getSignatureStatuses","params":[["{}"]]}}"#,
confirmed_block_signatures[0] confirmed_block_signatures[0]
); );
let res = io.handle_request_sync(&req, meta.clone()); let res = io.handle_request_sync(&req, meta.clone());
@ -1770,12 +1930,14 @@ pub mod tests {
let result: Option<TransactionStatus> = let result: Option<TransactionStatus> =
serde_json::from_value(json["result"]["value"][0].clone()) serde_json::from_value(json["result"]["value"][0].clone())
.expect("actual response deserialization"); .expect("actual response deserialization");
assert_eq!(expected_res, result.as_ref().unwrap().status); let result = result.as_ref().unwrap();
assert_eq!(expected_res, result.status);
assert_eq!(None, result.confirmations);
// Test getSignatureStatus request on unprocessed tx // Test getSignatureStatus request on unprocessed tx
let tx = system_transaction::transfer(&alice, &bob_pubkey, 10, blockhash); let tx = system_transaction::transfer(&alice, &bob_pubkey, 10, blockhash);
let req = format!( let req = format!(
r#"{{"jsonrpc":"2.0","id":1,"method":"getSignatureStatus","params":[["{}"]]}}"#, r#"{{"jsonrpc":"2.0","id":1,"method":"getSignatureStatuses","params":[["{}"]]}}"#,
tx.signatures[0] tx.signatures[0]
); );
let res = io.handle_request_sync(&req, meta.clone()); let res = io.handle_request_sync(&req, meta.clone());
@ -1787,7 +1949,7 @@ pub mod tests {
// Test getSignatureStatus request on a TransactionError // Test getSignatureStatus request on a TransactionError
let req = format!( let req = format!(
r#"{{"jsonrpc":"2.0","id":1,"method":"getSignatureStatus","params":[["{}"]]}}"#, r#"{{"jsonrpc":"2.0","id":1,"method":"getSignatureStatuses","params":[["{}"]]}}"#,
confirmed_block_signatures[1] confirmed_block_signatures[1]
); );
let res = io.handle_request_sync(&req, meta.clone()); let res = io.handle_request_sync(&req, meta.clone());
@ -2119,6 +2281,8 @@ pub mod tests {
fn test_rpc_processor_get_block_commitment() { fn test_rpc_processor_get_block_commitment() {
let exit = Arc::new(AtomicBool::new(false)); let exit = Arc::new(AtomicBool::new(false));
let validator_exit = create_validator_exit(&exit); let validator_exit = create_validator_exit(&exit);
let bank_forks = new_bank_forks().0;
let commitment_slot0 = BlockCommitment::new([8; MAX_LOCKOUT_HISTORY]); let commitment_slot0 = BlockCommitment::new([8; MAX_LOCKOUT_HISTORY]);
let commitment_slot1 = BlockCommitment::new([9; MAX_LOCKOUT_HISTORY]); let commitment_slot1 = BlockCommitment::new([9; MAX_LOCKOUT_HISTORY]);
let mut block_commitment: HashMap<u64, BlockCommitment> = HashMap::new(); let mut block_commitment: HashMap<u64, BlockCommitment> = HashMap::new();
@ -2128,8 +2292,12 @@ pub mod tests {
block_commitment block_commitment
.entry(1) .entry(1)
.or_insert(commitment_slot1.clone()); .or_insert(commitment_slot1.clone());
let block_commitment_cache = let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::new(
Arc::new(RwLock::new(BlockCommitmentCache::new(block_commitment, 42))); block_commitment,
42,
bank_forks.read().unwrap().working_bank(),
0,
)));
let ledger_path = get_tmp_ledger_path!(); let ledger_path = get_tmp_ledger_path!();
let blockstore = Blockstore::open(&ledger_path).unwrap(); let blockstore = Blockstore::open(&ledger_path).unwrap();
@ -2137,7 +2305,7 @@ pub mod tests {
config.enable_validator_exit = true; config.enable_validator_exit = true;
let request_processor = JsonRpcRequestProcessor::new( let request_processor = JsonRpcRequestProcessor::new(
config, config,
new_bank_forks().0, bank_forks,
block_commitment_cache, block_commitment_cache,
Arc::new(blockstore), Arc::new(blockstore),
StorageState::default(), StorageState::default(),
@ -2201,7 +2369,7 @@ pub mod tests {
.get_block_commitment(0) .get_block_commitment(0)
.map(|block_commitment| block_commitment.commitment) .map(|block_commitment| block_commitment.commitment)
); );
assert_eq!(total_stake, 42); assert_eq!(total_stake, 10);
let req = let req =
format!(r#"{{"jsonrpc":"2.0","id":1,"method":"getBlockCommitment","params":[2]}}"#); format!(r#"{{"jsonrpc":"2.0","id":1,"method":"getBlockCommitment","params":[2]}}"#);
@ -2219,7 +2387,7 @@ pub mod tests {
panic!("Expected single response"); panic!("Expected single response");
}; };
assert_eq!(commitment_response.commitment, None); assert_eq!(commitment_response.commitment, None);
assert_eq!(commitment_response.total_stake, 42); assert_eq!(commitment_response.total_stake, 10);
} }
#[test] #[test]

View File

@ -312,7 +312,10 @@ impl RpcSolPubSub for RpcSolPubSubImpl {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::rpc_subscriptions::tests::robust_poll_or_panic; use crate::{
commitment::{BlockCommitment, BlockCommitmentCache},
rpc_subscriptions::tests::robust_poll_or_panic,
};
use jsonrpc_core::{futures::sync::mpsc, Response}; use jsonrpc_core::{futures::sync::mpsc, Response};
use jsonrpc_pubsub::{PubSubHandler, Session}; use jsonrpc_pubsub::{PubSubHandler, Session};
use solana_budget_program::{self, budget_instruction}; use solana_budget_program::{self, budget_instruction};
@ -325,7 +328,12 @@ mod tests {
system_program, system_transaction, system_program, system_transaction,
transaction::{self, Transaction}, transaction::{self, Transaction},
}; };
use std::{sync::RwLock, thread::sleep, time::Duration}; use std::{
collections::HashMap,
sync::{atomic::AtomicBool, RwLock},
thread::sleep,
time::Duration,
};
fn process_transaction_and_notify( fn process_transaction_and_notify(
bank_forks: &Arc<RwLock<BankForks>>, bank_forks: &Arc<RwLock<BankForks>>,
@ -358,8 +366,13 @@ mod tests {
let bank = Bank::new(&genesis_config); let bank = Bank::new(&genesis_config);
let blockhash = bank.last_blockhash(); let blockhash = bank.last_blockhash();
let bank_forks = Arc::new(RwLock::new(BankForks::new(0, bank))); let bank_forks = Arc::new(RwLock::new(BankForks::new(0, bank)));
let rpc = RpcSolPubSubImpl {
let rpc = RpcSolPubSubImpl::default(); subscriptions: Arc::new(RpcSubscriptions::new(
&Arc::new(AtomicBool::new(false)),
Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests())),
)),
..RpcSolPubSubImpl::default()
};
// Test signature subscriptions // Test signature subscriptions
let tx = system_transaction::transfer(&alice, &bob_pubkey, 20, blockhash); let tx = system_transaction::transfer(&alice, &bob_pubkey, 20, blockhash);
@ -457,7 +470,13 @@ mod tests {
let blockhash = bank.last_blockhash(); let blockhash = bank.last_blockhash();
let bank_forks = Arc::new(RwLock::new(BankForks::new(0, bank))); let bank_forks = Arc::new(RwLock::new(BankForks::new(0, bank)));
let rpc = RpcSolPubSubImpl::default(); let rpc = RpcSolPubSubImpl {
subscriptions: Arc::new(RpcSubscriptions::new(
&Arc::new(AtomicBool::new(false)),
Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests())),
)),
..RpcSolPubSubImpl::default()
};
let session = create_session(); let session = create_session();
let (subscriber, _id_receiver, receiver) = Subscriber::new_test("accountNotification"); let (subscriber, _id_receiver, receiver) = Subscriber::new_test("accountNotification");
rpc.account_subscribe( rpc.account_subscribe(
@ -591,7 +610,13 @@ mod tests {
let bank_forks = Arc::new(RwLock::new(BankForks::new(0, bank))); let bank_forks = Arc::new(RwLock::new(BankForks::new(0, bank)));
let bob = Keypair::new(); let bob = Keypair::new();
let rpc = RpcSolPubSubImpl::default(); let mut rpc = RpcSolPubSubImpl::default();
let exit = Arc::new(AtomicBool::new(false));
let subscriptions = RpcSubscriptions::new(
&exit,
Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests())),
);
rpc.subscriptions = Arc::new(subscriptions);
let session = create_session(); let session = create_session();
let (subscriber, _id_receiver, receiver) = Subscriber::new_test("accountNotification"); let (subscriber, _id_receiver, receiver) = Subscriber::new_test("accountNotification");
rpc.account_subscribe(session, subscriber, bob.pubkey().to_string(), Some(2)); rpc.account_subscribe(session, subscriber, bob.pubkey().to_string(), Some(2));
@ -622,7 +647,12 @@ mod tests {
let bank_forks = Arc::new(RwLock::new(BankForks::new(0, bank))); let bank_forks = Arc::new(RwLock::new(BankForks::new(0, bank)));
let bob = Keypair::new(); let bob = Keypair::new();
let rpc = RpcSolPubSubImpl::default(); let mut rpc = RpcSolPubSubImpl::default();
let exit = Arc::new(AtomicBool::new(false));
let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests()));
let subscriptions = RpcSubscriptions::new(&exit, block_commitment_cache.clone());
rpc.subscriptions = Arc::new(subscriptions);
let session = create_session(); let session = create_session();
let (subscriber, _id_receiver, receiver) = Subscriber::new_test("accountNotification"); let (subscriber, _id_receiver, receiver) = Subscriber::new_test("accountNotification");
rpc.account_subscribe(session, subscriber, bob.pubkey().to_string(), Some(2)); rpc.account_subscribe(session, subscriber, bob.pubkey().to_string(), Some(2));
@ -640,10 +670,32 @@ mod tests {
let bank0 = bank_forks.read().unwrap()[0].clone(); let bank0 = bank_forks.read().unwrap()[0].clone();
let bank1 = Bank::new_from_parent(&bank0, &Pubkey::default(), 1); let bank1 = Bank::new_from_parent(&bank0, &Pubkey::default(), 1);
bank_forks.write().unwrap().insert(bank1); bank_forks.write().unwrap().insert(bank1);
rpc.subscriptions.notify_subscribers(1, &bank_forks);
let bank1 = bank_forks.read().unwrap()[1].clone(); let bank1 = bank_forks.read().unwrap()[1].clone();
let mut cache0 = BlockCommitment::default();
cache0.increase_confirmation_stake(1, 10);
let mut block_commitment = HashMap::new();
block_commitment.entry(0).or_insert(cache0.clone());
let mut new_block_commitment =
BlockCommitmentCache::new(block_commitment, 10, bank1.clone(), 0);
let mut w_block_commitment_cache = block_commitment_cache.write().unwrap();
std::mem::swap(&mut *w_block_commitment_cache, &mut new_block_commitment);
drop(w_block_commitment_cache);
rpc.subscriptions.notify_subscribers(1, &bank_forks);
let bank2 = Bank::new_from_parent(&bank1, &Pubkey::default(), 2); let bank2 = Bank::new_from_parent(&bank1, &Pubkey::default(), 2);
bank_forks.write().unwrap().insert(bank2); bank_forks.write().unwrap().insert(bank2);
let bank2 = bank_forks.read().unwrap()[2].clone();
let mut cache0 = BlockCommitment::default();
cache0.increase_confirmation_stake(2, 10);
let mut block_commitment = HashMap::new();
block_commitment.entry(0).or_insert(cache0.clone());
let mut new_block_commitment = BlockCommitmentCache::new(block_commitment, 10, bank2, 0);
let mut w_block_commitment_cache = block_commitment_cache.write().unwrap();
std::mem::swap(&mut *w_block_commitment_cache, &mut new_block_commitment);
drop(w_block_commitment_cache);
rpc.subscriptions.notify_subscribers(2, &bank_forks); rpc.subscriptions.notify_subscribers(2, &bank_forks);
let expected = json!({ let expected = json!({
"jsonrpc": "2.0", "jsonrpc": "2.0",

View File

@ -1,14 +1,20 @@
//! The `pubsub` module implements a threaded subscription service on client RPC request //! The `pubsub` module implements a threaded subscription service on client RPC request
use crate::rpc_pubsub::{RpcSolPubSub, RpcSolPubSubImpl}; use crate::{
use crate::rpc_subscriptions::RpcSubscriptions; rpc_pubsub::{RpcSolPubSub, RpcSolPubSubImpl},
rpc_subscriptions::RpcSubscriptions,
};
use jsonrpc_pubsub::{PubSubHandler, Session}; use jsonrpc_pubsub::{PubSubHandler, Session};
use jsonrpc_ws_server::{RequestContext, ServerBuilder}; use jsonrpc_ws_server::{RequestContext, ServerBuilder};
use std::net::SocketAddr; use std::{
use std::sync::atomic::{AtomicBool, Ordering}; net::SocketAddr,
use std::sync::Arc; sync::{
use std::thread::{self, sleep, Builder, JoinHandle}; atomic::{AtomicBool, Ordering},
use std::time::Duration; Arc,
},
thread::{self, sleep, Builder, JoinHandle},
time::Duration,
};
pub struct PubSubService { pub struct PubSubService {
thread_hdl: JoinHandle<()>, thread_hdl: JoinHandle<()>,
@ -66,13 +72,20 @@ impl PubSubService {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use std::net::{IpAddr, Ipv4Addr}; use crate::commitment::BlockCommitmentCache;
use std::{
net::{IpAddr, Ipv4Addr},
sync::RwLock,
};
#[test] #[test]
fn test_pubsub_new() { fn test_pubsub_new() {
let pubsub_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0); let pubsub_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0);
let exit = Arc::new(AtomicBool::new(false)); let exit = Arc::new(AtomicBool::new(false));
let subscriptions = Arc::new(RpcSubscriptions::new(&exit)); let subscriptions = Arc::new(RpcSubscriptions::new(
&exit,
Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests())),
));
let pubsub_service = PubSubService::new(&subscriptions, pubsub_addr, &exit); let pubsub_service = PubSubService::new(&subscriptions, pubsub_addr, &exit);
let thread = pubsub_service.thread_hdl.thread(); let thread = pubsub_service.thread_hdl.thread();
assert_eq!(thread.name().unwrap(), "solana-pubsub"); assert_eq!(thread.name().unwrap(), "solana-pubsub");

View File

@ -1,5 +1,6 @@
//! The `pubsub` module implements a threaded subscription service on client RPC request //! The `pubsub` module implements a threaded subscription service on client RPC request
use crate::commitment::BlockCommitmentCache;
use core::hash::Hash; use core::hash::Hash;
use jsonrpc_core::futures::Future; use jsonrpc_core::futures::Future;
use jsonrpc_pubsub::{ use jsonrpc_pubsub::{
@ -14,11 +15,14 @@ use solana_sdk::{
account::Account, clock::Slot, pubkey::Pubkey, signature::Signature, transaction, account::Account, clock::Slot, pubkey::Pubkey, signature::Signature, transaction,
}; };
use solana_vote_program::vote_state::MAX_LOCKOUT_HISTORY; use solana_vote_program::vote_state::MAX_LOCKOUT_HISTORY;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{
use std::sync::mpsc::{Receiver, RecvTimeoutError, SendError, Sender}; atomic::{AtomicBool, Ordering},
mpsc::{Receiver, RecvTimeoutError, SendError, Sender},
};
use std::thread::{Builder, JoinHandle}; use std::thread::{Builder, JoinHandle};
use std::time::Duration; use std::time::Duration;
use std::{ use std::{
cmp::min,
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
iter, iter,
sync::{Arc, Mutex, RwLock}, sync::{Arc, Mutex, RwLock},
@ -80,11 +84,7 @@ fn add_subscription<K, S>(
{ {
let sink = subscriber.assign_id(sub_id.clone()).unwrap(); let sink = subscriber.assign_id(sub_id.clone()).unwrap();
let confirmations = confirmations.unwrap_or(0); let confirmations = confirmations.unwrap_or(0);
let confirmations = if confirmations > MAX_LOCKOUT_HISTORY { let confirmations = min(confirmations, MAX_LOCKOUT_HISTORY + 1);
MAX_LOCKOUT_HISTORY
} else {
confirmations
};
if let Some(current_hashmap) = subscriptions.get_mut(&hashmap_key) { if let Some(current_hashmap) = subscriptions.get_mut(&hashmap_key) {
current_hashmap.insert(sub_id, (sink, confirmations)); current_hashmap.insert(sub_id, (sink, confirmations));
return; return;
@ -120,8 +120,8 @@ where
fn check_confirmations_and_notify<K, S, B, F, X>( fn check_confirmations_and_notify<K, S, B, F, X>(
subscriptions: &HashMap<K, HashMap<SubscriptionId, (Sink<Response<S>>, Confirmations)>>, subscriptions: &HashMap<K, HashMap<SubscriptionId, (Sink<Response<S>>, Confirmations)>>,
hashmap_key: &K, hashmap_key: &K,
current_slot: Slot,
bank_forks: &Arc<RwLock<BankForks>>, bank_forks: &Arc<RwLock<BankForks>>,
block_commitment_cache: &Arc<RwLock<BlockCommitmentCache>>,
bank_method: B, bank_method: B,
filter_results: F, filter_results: F,
notifier: &RpcNotifier, notifier: &RpcNotifier,
@ -133,6 +133,10 @@ where
F: Fn(X, u64) -> Box<dyn Iterator<Item = S>>, F: Fn(X, u64) -> Box<dyn Iterator<Item = S>>,
X: Clone + Serialize, X: Clone + Serialize,
{ {
let mut confirmation_slots: HashMap<usize, Slot> = HashMap::new();
let r_block_commitment_cache = block_commitment_cache.read().unwrap();
let current_slot = r_block_commitment_cache.slot();
let root = r_block_commitment_cache.root();
let current_ancestors = bank_forks let current_ancestors = bank_forks
.read() .read()
.unwrap() .unwrap()
@ -140,27 +144,29 @@ where
.unwrap() .unwrap()
.ancestors .ancestors
.clone(); .clone();
for (slot, _) in current_ancestors.iter() {
if let Some(confirmations) = r_block_commitment_cache.get_confirmation_count(*slot) {
confirmation_slots.entry(confirmations).or_insert(*slot);
}
}
drop(r_block_commitment_cache);
let mut notified_set: HashSet<SubscriptionId> = HashSet::new(); let mut notified_set: HashSet<SubscriptionId> = HashSet::new();
if let Some(hashmap) = subscriptions.get(hashmap_key) { if let Some(hashmap) = subscriptions.get(hashmap_key) {
for (sub_id, (sink, confirmations)) in hashmap.iter() { for (sub_id, (sink, confirmations)) in hashmap.iter() {
let desired_slot: Vec<u64> = current_ancestors let desired_slot = if *confirmations == 0 {
.iter() Some(&current_slot)
.filter(|(_, &v)| v == *confirmations) } else if *confirmations == MAX_LOCKOUT_HISTORY + 1 {
.map(|(k, _)| k) Some(&root)
.cloned() } else {
.collect(); confirmation_slots.get(confirmations)
let root: Vec<u64> = current_ancestors };
.iter() if let Some(&slot) = desired_slot {
.filter(|(_, &v)| v == 32) let results = {
.map(|(k, _)| k) let bank_forks = bank_forks.read().unwrap();
.cloned() let desired_bank = bank_forks.get(slot).unwrap();
.collect(); bank_method(&desired_bank, hashmap_key)
let root = if root.len() == 1 { root[0] } else { 0 }; };
if desired_slot.len() == 1 {
let slot = desired_slot[0];
let desired_bank = bank_forks.read().unwrap().get(slot).unwrap().clone();
let results = bank_method(&desired_bank, hashmap_key);
for result in filter_results(results, root) { for result in filter_results(results, root) {
notifier.notify( notifier.notify(
Response { Response {
@ -236,7 +242,10 @@ pub struct RpcSubscriptions {
impl Default for RpcSubscriptions { impl Default for RpcSubscriptions {
fn default() -> Self { fn default() -> Self {
Self::new(&Arc::new(AtomicBool::new(false))) Self::new(
&Arc::new(AtomicBool::new(false)),
Arc::new(RwLock::new(BlockCommitmentCache::default())),
)
} }
} }
@ -249,7 +258,10 @@ impl Drop for RpcSubscriptions {
} }
impl RpcSubscriptions { impl RpcSubscriptions {
pub fn new(exit: &Arc<AtomicBool>) -> Self { pub fn new(
exit: &Arc<AtomicBool>,
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
) -> Self {
let (notification_sender, notification_receiver): ( let (notification_sender, notification_receiver): (
Sender<NotificationEntry>, Sender<NotificationEntry>,
Receiver<NotificationEntry>, Receiver<NotificationEntry>,
@ -288,6 +300,7 @@ impl RpcSubscriptions {
signature_subscriptions_clone, signature_subscriptions_clone,
slot_subscriptions_clone, slot_subscriptions_clone,
root_subscriptions_clone, root_subscriptions_clone,
block_commitment_cache,
); );
}) })
.unwrap(); .unwrap();
@ -307,8 +320,8 @@ impl RpcSubscriptions {
fn check_account( fn check_account(
pubkey: &Pubkey, pubkey: &Pubkey,
current_slot: Slot,
bank_forks: &Arc<RwLock<BankForks>>, bank_forks: &Arc<RwLock<BankForks>>,
block_commitment_cache: &Arc<RwLock<BlockCommitmentCache>>,
account_subscriptions: Arc<RpcAccountSubscriptions>, account_subscriptions: Arc<RpcAccountSubscriptions>,
notifier: &RpcNotifier, notifier: &RpcNotifier,
) { ) {
@ -316,8 +329,8 @@ impl RpcSubscriptions {
check_confirmations_and_notify( check_confirmations_and_notify(
&subscriptions, &subscriptions,
pubkey, pubkey,
current_slot,
bank_forks, bank_forks,
block_commitment_cache,
Bank::get_account_modified_since_parent, Bank::get_account_modified_since_parent,
filter_account_result, filter_account_result,
notifier, notifier,
@ -326,8 +339,8 @@ impl RpcSubscriptions {
fn check_program( fn check_program(
program_id: &Pubkey, program_id: &Pubkey,
current_slot: Slot,
bank_forks: &Arc<RwLock<BankForks>>, bank_forks: &Arc<RwLock<BankForks>>,
block_commitment_cache: &Arc<RwLock<BlockCommitmentCache>>,
program_subscriptions: Arc<RpcProgramSubscriptions>, program_subscriptions: Arc<RpcProgramSubscriptions>,
notifier: &RpcNotifier, notifier: &RpcNotifier,
) { ) {
@ -335,8 +348,8 @@ impl RpcSubscriptions {
check_confirmations_and_notify( check_confirmations_and_notify(
&subscriptions, &subscriptions,
program_id, program_id,
current_slot,
bank_forks, bank_forks,
block_commitment_cache,
Bank::get_program_accounts_modified_since_parent, Bank::get_program_accounts_modified_since_parent,
filter_program_results, filter_program_results,
notifier, notifier,
@ -345,8 +358,8 @@ impl RpcSubscriptions {
fn check_signature( fn check_signature(
signature: &Signature, signature: &Signature,
current_slot: Slot,
bank_forks: &Arc<RwLock<BankForks>>, bank_forks: &Arc<RwLock<BankForks>>,
block_commitment_cache: &Arc<RwLock<BlockCommitmentCache>>,
signature_subscriptions: Arc<RpcSignatureSubscriptions>, signature_subscriptions: Arc<RpcSignatureSubscriptions>,
notifier: &RpcNotifier, notifier: &RpcNotifier,
) { ) {
@ -354,8 +367,8 @@ impl RpcSubscriptions {
let notified_ids = check_confirmations_and_notify( let notified_ids = check_confirmations_and_notify(
&subscriptions, &subscriptions,
signature, signature,
current_slot,
bank_forks, bank_forks,
block_commitment_cache,
Bank::get_signature_status_processed_since_parent, Bank::get_signature_status_processed_since_parent,
filter_signature_result, filter_signature_result,
notifier, notifier,
@ -499,6 +512,7 @@ impl RpcSubscriptions {
signature_subscriptions: Arc<RpcSignatureSubscriptions>, signature_subscriptions: Arc<RpcSignatureSubscriptions>,
slot_subscriptions: Arc<RpcSlotSubscriptions>, slot_subscriptions: Arc<RpcSlotSubscriptions>,
root_subscriptions: Arc<RpcRootSubscriptions>, root_subscriptions: Arc<RpcRootSubscriptions>,
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
) { ) {
loop { loop {
if exit.load(Ordering::Relaxed) { if exit.load(Ordering::Relaxed) {
@ -518,7 +532,7 @@ impl RpcSubscriptions {
notifier.notify(root, sink); notifier.notify(root, sink);
} }
} }
NotificationEntry::Bank((current_slot, bank_forks)) => { NotificationEntry::Bank((_current_slot, bank_forks)) => {
let pubkeys: Vec<_> = { let pubkeys: Vec<_> = {
let subs = account_subscriptions.read().unwrap(); let subs = account_subscriptions.read().unwrap();
subs.keys().cloned().collect() subs.keys().cloned().collect()
@ -526,8 +540,8 @@ impl RpcSubscriptions {
for pubkey in &pubkeys { for pubkey in &pubkeys {
Self::check_account( Self::check_account(
pubkey, pubkey,
current_slot,
&bank_forks, &bank_forks,
&block_commitment_cache,
account_subscriptions.clone(), account_subscriptions.clone(),
&notifier, &notifier,
); );
@ -540,8 +554,8 @@ impl RpcSubscriptions {
for program_id in &programs { for program_id in &programs {
Self::check_program( Self::check_program(
program_id, program_id,
current_slot,
&bank_forks, &bank_forks,
&block_commitment_cache,
program_subscriptions.clone(), program_subscriptions.clone(),
&notifier, &notifier,
); );
@ -554,8 +568,8 @@ impl RpcSubscriptions {
for signature in &signatures { for signature in &signatures {
Self::check_signature( Self::check_signature(
signature, signature,
current_slot,
&bank_forks, &bank_forks,
&block_commitment_cache,
signature_subscriptions.clone(), signature_subscriptions.clone(),
&notifier, &notifier,
); );
@ -596,6 +610,7 @@ impl RpcSubscriptions {
#[cfg(test)] #[cfg(test)]
pub(crate) mod tests { pub(crate) mod tests {
use super::*; use super::*;
use crate::commitment::BlockCommitment;
use jsonrpc_core::futures::{self, stream::Stream}; use jsonrpc_core::futures::{self, stream::Stream};
use jsonrpc_pubsub::typed::Subscriber; use jsonrpc_pubsub::typed::Subscriber;
use solana_budget_program; use solana_budget_program;
@ -663,7 +678,10 @@ pub(crate) mod tests {
Subscriber::new_test("accountNotification"); Subscriber::new_test("accountNotification");
let sub_id = SubscriptionId::Number(0 as u64); let sub_id = SubscriptionId::Number(0 as u64);
let exit = Arc::new(AtomicBool::new(false)); let exit = Arc::new(AtomicBool::new(false));
let subscriptions = RpcSubscriptions::new(&exit); let subscriptions = RpcSubscriptions::new(
&exit,
Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests())),
);
subscriptions.add_account_subscription(alice.pubkey(), None, sub_id.clone(), subscriber); subscriptions.add_account_subscription(alice.pubkey(), None, sub_id.clone(), subscriber);
assert!(subscriptions assert!(subscriptions
@ -732,7 +750,10 @@ pub(crate) mod tests {
Subscriber::new_test("programNotification"); Subscriber::new_test("programNotification");
let sub_id = SubscriptionId::Number(0 as u64); let sub_id = SubscriptionId::Number(0 as u64);
let exit = Arc::new(AtomicBool::new(false)); let exit = Arc::new(AtomicBool::new(false));
let subscriptions = RpcSubscriptions::new(&exit); let subscriptions = RpcSubscriptions::new(
&exit,
Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests())),
);
subscriptions.add_program_subscription( subscriptions.add_program_subscription(
solana_budget_program::id(), solana_budget_program::id(),
None, None,
@ -812,27 +833,41 @@ pub(crate) mod tests {
.unwrap() .unwrap()
.process_transaction(&processed_tx) .process_transaction(&processed_tx)
.unwrap(); .unwrap();
let bank1 = bank_forks[1].clone();
let bank_forks = Arc::new(RwLock::new(bank_forks)); let bank_forks = Arc::new(RwLock::new(bank_forks));
let exit = Arc::new(AtomicBool::new(false)); let mut cache0 = BlockCommitment::default();
let subscriptions = RpcSubscriptions::new(&exit); cache0.increase_confirmation_stake(1, 10);
let cache1 = BlockCommitment::default();
let (past_bank_sub, _id_receiver, past_bank_recv) = let mut block_commitment = HashMap::new();
block_commitment.entry(0).or_insert(cache0.clone());
block_commitment.entry(1).or_insert(cache1.clone());
let block_commitment_cache = BlockCommitmentCache::new(block_commitment, 10, bank1, 0);
let exit = Arc::new(AtomicBool::new(false));
let subscriptions =
RpcSubscriptions::new(&exit, Arc::new(RwLock::new(block_commitment_cache)));
let (past_bank_sub1, _id_receiver, past_bank_recv1) =
Subscriber::new_test("signatureNotification");
let (past_bank_sub2, _id_receiver, past_bank_recv2) =
Subscriber::new_test("signatureNotification"); Subscriber::new_test("signatureNotification");
let (processed_sub, _id_receiver, processed_recv) = let (processed_sub, _id_receiver, processed_recv) =
Subscriber::new_test("signatureNotification"); Subscriber::new_test("signatureNotification");
subscriptions.add_signature_subscription( subscriptions.add_signature_subscription(
past_bank_tx.signatures[0], past_bank_tx.signatures[0],
Some(0), Some(0),
SubscriptionId::Number(1 as u64), SubscriptionId::Number(1 as u64),
Subscriber::new_test("signatureNotification").0, past_bank_sub1,
); );
subscriptions.add_signature_subscription( subscriptions.add_signature_subscription(
past_bank_tx.signatures[0], past_bank_tx.signatures[0],
Some(1), Some(1),
SubscriptionId::Number(2 as u64), SubscriptionId::Number(2 as u64),
past_bank_sub, past_bank_sub2,
); );
subscriptions.add_signature_subscription( subscriptions.add_signature_subscription(
processed_tx.signatures[0], processed_tx.signatures[0],
@ -857,41 +892,46 @@ pub(crate) mod tests {
subscriptions.notify_subscribers(1, &bank_forks); subscriptions.notify_subscribers(1, &bank_forks);
let expected_res: Option<transaction::Result<()>> = Some(Ok(())); let expected_res: Option<transaction::Result<()>> = Some(Ok(()));
let expected = json!({ struct Notification {
"jsonrpc": "2.0", slot: Slot,
"method": "signatureNotification", id: u64,
"params": { }
"result": {
"context": { "slot": 0 },
"value": expected_res,
},
"subscription": 2,
}
});
let (response, _) = robust_poll_or_panic(past_bank_recv);
assert_eq!(serde_json::to_string(&expected).unwrap(), response);
let expected = json!({ let expected_notification = |exp: Notification| -> String {
"jsonrpc": "2.0", let json = json!({
"method": "signatureNotification", "jsonrpc": "2.0",
"params": { "method": "signatureNotification",
"result": { "params": {
"context": { "slot": 1 }, "result": {
"value": expected_res, "context": { "slot": exp.slot },
}, "value": &expected_res,
"subscription": 3, },
} "subscription": exp.id,
}); }
});
serde_json::to_string(&json).unwrap()
};
// Expect to receive a notification from bank 1 because this subscription is
// looking for 0 confirmations and so checks the current bank
let expected = expected_notification(Notification { slot: 1, id: 1 });
let (response, _) = robust_poll_or_panic(past_bank_recv1);
assert_eq!(expected, response);
// Expect to receive a notification from bank 0 because this subscription is
// looking for 1 confirmation and so checks the past bank
let expected = expected_notification(Notification { slot: 0, id: 2 });
let (response, _) = robust_poll_or_panic(past_bank_recv2);
assert_eq!(expected, response);
let expected = expected_notification(Notification { slot: 1, id: 3 });
let (response, _) = robust_poll_or_panic(processed_recv); let (response, _) = robust_poll_or_panic(processed_recv);
assert_eq!(serde_json::to_string(&expected).unwrap(), response); assert_eq!(expected, response);
let sig_subs = subscriptions.signature_subscriptions.read().unwrap();
// Subscription should be automatically removed after notification // Subscription should be automatically removed after notification
let sig_subs = subscriptions.signature_subscriptions.read().unwrap();
assert!(!sig_subs.contains_key(&processed_tx.signatures[0])); assert!(!sig_subs.contains_key(&processed_tx.signatures[0]));
assert!(!sig_subs.contains_key(&past_bank_tx.signatures[0]));
// Only one notification is expected for signature processed in previous bank
assert_eq!(sig_subs.get(&past_bank_tx.signatures[0]).unwrap().len(), 1);
// Unprocessed signature subscription should not be removed // Unprocessed signature subscription should not be removed
assert_eq!( assert_eq!(
@ -906,7 +946,10 @@ pub(crate) mod tests {
Subscriber::new_test("slotNotification"); Subscriber::new_test("slotNotification");
let sub_id = SubscriptionId::Number(0 as u64); let sub_id = SubscriptionId::Number(0 as u64);
let exit = Arc::new(AtomicBool::new(false)); let exit = Arc::new(AtomicBool::new(false));
let subscriptions = RpcSubscriptions::new(&exit); let subscriptions = RpcSubscriptions::new(
&exit,
Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests())),
);
subscriptions.add_slot_subscription(sub_id.clone(), subscriber); subscriptions.add_slot_subscription(sub_id.clone(), subscriber);
assert!(subscriptions assert!(subscriptions
@ -944,7 +987,10 @@ pub(crate) mod tests {
Subscriber::new_test("rootNotification"); Subscriber::new_test("rootNotification");
let sub_id = SubscriptionId::Number(0 as u64); let sub_id = SubscriptionId::Number(0 as u64);
let exit = Arc::new(AtomicBool::new(false)); let exit = Arc::new(AtomicBool::new(false));
let subscriptions = RpcSubscriptions::new(&exit); let subscriptions = RpcSubscriptions::new(
&exit,
Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests())),
);
subscriptions.add_root_subscription(sub_id.clone(), subscriber); subscriptions.add_root_subscription(sub_id.clone(), subscriber);
assert!(subscriptions assert!(subscriptions

View File

@ -2,11 +2,13 @@ use crate::{
cluster_info::{ClusterInfo, ClusterInfoError}, cluster_info::{ClusterInfo, ClusterInfoError},
cluster_slots::ClusterSlots, cluster_slots::ClusterSlots,
contact_info::ContactInfo, contact_info::ContactInfo,
repair_service::RepairStats,
result::{Error, Result}, result::{Error, Result},
weighted_shuffle::weighted_best, weighted_shuffle::weighted_best,
}; };
use bincode::serialize; use bincode::serialize;
use solana_ledger::blockstore::Blockstore; use solana_ledger::blockstore::Blockstore;
use solana_measure::measure::Measure;
use solana_measure::thread_mem_usage; use solana_measure::thread_mem_usage;
use solana_metrics::{datapoint_debug, inc_new_counter_debug}; use solana_metrics::{datapoint_debug, inc_new_counter_debug};
use solana_perf::packet::{limited_deserialize, Packet, Packets, PacketsRecycler}; use solana_perf::packet::{limited_deserialize, Packet, Packets, PacketsRecycler};
@ -46,6 +48,17 @@ impl RepairType {
} }
} }
#[derive(Default)]
pub struct ServeRepairStats {
pub total_packets: usize,
pub dropped_packets: usize,
pub processed: usize,
pub self_repair: usize,
pub window_index: usize,
pub highest_window_index: usize,
pub orphan: usize,
}
/// Window protocol messages /// Window protocol messages
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
enum RepairProtocol { enum RepairProtocol {
@ -106,6 +119,7 @@ impl ServeRepair {
from_addr: &SocketAddr, from_addr: &SocketAddr,
blockstore: Option<&Arc<Blockstore>>, blockstore: Option<&Arc<Blockstore>>,
request: RepairProtocol, request: RepairProtocol,
stats: &mut ServeRepairStats,
) -> Option<Packets> { ) -> Option<Packets> {
let now = Instant::now(); let now = Instant::now();
@ -113,18 +127,14 @@ impl ServeRepair {
let my_id = me.read().unwrap().keypair.pubkey(); let my_id = me.read().unwrap().keypair.pubkey();
let from = Self::get_repair_sender(&request); let from = Self::get_repair_sender(&request);
if from.id == my_id { if from.id == my_id {
warn!( stats.self_repair += 1;
"{}: Ignored received repair request from ME {}",
my_id, from.id,
);
inc_new_counter_debug!("serve_repair-handle-repair--eq", 1);
return None; return None;
} }
let (res, label) = { let (res, label) = {
match &request { match &request {
RepairProtocol::WindowIndex(from, slot, shred_index) => { RepairProtocol::WindowIndex(from, slot, shred_index) => {
inc_new_counter_debug!("serve_repair-request-window-index", 1); stats.window_index += 1;
( (
Self::run_window_request( Self::run_window_request(
recycler, recycler,
@ -140,7 +150,7 @@ impl ServeRepair {
} }
RepairProtocol::HighestWindowIndex(_, slot, highest_index) => { RepairProtocol::HighestWindowIndex(_, slot, highest_index) => {
inc_new_counter_debug!("serve_repair-request-highest-window-index", 1); stats.highest_window_index += 1;
( (
Self::run_highest_window_request( Self::run_highest_window_request(
recycler, recycler,
@ -153,7 +163,7 @@ impl ServeRepair {
) )
} }
RepairProtocol::Orphan(_, slot) => { RepairProtocol::Orphan(_, slot) => {
inc_new_counter_debug!("serve_repair-request-orphan", 1); stats.orphan += 1;
( (
Self::run_orphan( Self::run_orphan(
recycler, recycler,
@ -187,15 +197,71 @@ impl ServeRepair {
blockstore: Option<&Arc<Blockstore>>, blockstore: Option<&Arc<Blockstore>>,
requests_receiver: &PacketReceiver, requests_receiver: &PacketReceiver,
response_sender: &PacketSender, response_sender: &PacketSender,
stats: &mut ServeRepairStats,
max_packets: &mut usize,
) -> Result<()> { ) -> Result<()> {
//TODO cache connections //TODO cache connections
let timeout = Duration::new(1, 0); let timeout = Duration::new(1, 0);
let reqs = requests_receiver.recv_timeout(timeout)?; let mut reqs_v = vec![requests_receiver.recv_timeout(timeout)?];
let mut total_packets = reqs_v[0].packets.len();
Self::handle_packets(obj, &recycler, blockstore, reqs, response_sender); let mut dropped_packets = 0;
while let Ok(more) = requests_receiver.try_recv() {
total_packets += more.packets.len();
if total_packets < *max_packets {
// Drop the rest in the channel in case of dos
reqs_v.push(more);
} else {
dropped_packets += more.packets.len();
}
}
stats.dropped_packets += dropped_packets;
stats.total_packets += total_packets;
let mut time = Measure::start("repair::handle_packets");
for reqs in reqs_v {
Self::handle_packets(obj, &recycler, blockstore, reqs, response_sender, stats);
}
time.stop();
if total_packets >= *max_packets {
if time.as_ms() > 1000 {
*max_packets = (*max_packets * 9) / 10;
} else {
*max_packets = (*max_packets * 10) / 9;
}
}
Ok(()) Ok(())
} }
fn report_reset_stats(me: &Arc<RwLock<Self>>, stats: &mut ServeRepairStats) {
if stats.self_repair > 0 {
let my_id = me.read().unwrap().keypair.pubkey();
warn!(
"{}: Ignored received repair requests from ME: {}",
my_id, stats.self_repair,
);
inc_new_counter_debug!("serve_repair-handle-repair--eq", stats.self_repair);
}
inc_new_counter_info!("serve_repair-total_packets", stats.total_packets);
inc_new_counter_info!("serve_repair-dropped_packets", stats.dropped_packets);
debug!(
"repair_listener: total_packets: {} passed: {}",
stats.total_packets, stats.processed
);
inc_new_counter_debug!("serve_repair-request-window-index", stats.window_index);
inc_new_counter_debug!(
"serve_repair-request-highest-window-index",
stats.highest_window_index
);
inc_new_counter_debug!("serve_repair-request-orphan", stats.orphan);
*stats = ServeRepairStats::default();
}
pub fn listen( pub fn listen(
me: Arc<RwLock<Self>>, me: Arc<RwLock<Self>>,
blockstore: Option<Arc<Blockstore>>, blockstore: Option<Arc<Blockstore>>,
@ -207,22 +273,33 @@ impl ServeRepair {
let recycler = PacketsRecycler::default(); let recycler = PacketsRecycler::default();
Builder::new() Builder::new()
.name("solana-repair-listen".to_string()) .name("solana-repair-listen".to_string())
.spawn(move || loop { .spawn(move || {
let result = Self::run_listen( let mut last_print = Instant::now();
&me, let mut stats = ServeRepairStats::default();
&recycler, let mut max_packets = 1024;
blockstore.as_ref(), loop {
&requests_receiver, let result = Self::run_listen(
&response_sender, &me,
); &recycler,
match result { blockstore.as_ref(),
Err(Error::RecvTimeoutError(_)) | Ok(_) => {} &requests_receiver,
Err(err) => info!("repair listener error: {:?}", err), &response_sender,
}; &mut stats,
if exit.load(Ordering::Relaxed) { &mut max_packets,
return; );
match result {
Err(Error::RecvTimeoutError(_)) | Ok(_) => {}
Err(err) => info!("repair listener error: {:?}", err),
};
if exit.load(Ordering::Relaxed) {
return;
}
if last_print.elapsed().as_secs() > 2 {
Self::report_reset_stats(&me, &mut stats);
last_print = Instant::now();
}
thread_mem_usage::datapoint("solana-repair-listen");
} }
thread_mem_usage::datapoint("solana-repair-listen");
}) })
.unwrap() .unwrap()
} }
@ -233,6 +310,7 @@ impl ServeRepair {
blockstore: Option<&Arc<Blockstore>>, blockstore: Option<&Arc<Blockstore>>,
packets: Packets, packets: Packets,
response_sender: &PacketSender, response_sender: &PacketSender,
stats: &mut ServeRepairStats,
) { ) {
// iter over the packets, collect pulls separately and process everything else // iter over the packets, collect pulls separately and process everything else
let allocated = thread_mem_usage::Allocatedp::default(); let allocated = thread_mem_usage::Allocatedp::default();
@ -242,7 +320,9 @@ impl ServeRepair {
limited_deserialize(&packet.data[..packet.meta.size]) limited_deserialize(&packet.data[..packet.meta.size])
.into_iter() .into_iter()
.for_each(|request| { .for_each(|request| {
let rsp = Self::handle_repair(me, recycler, &from_addr, blockstore, request); stats.processed += 1;
let rsp =
Self::handle_repair(me, recycler, &from_addr, blockstore, request, stats);
if let Some(rsp) = rsp { if let Some(rsp) = rsp {
let _ignore_disconnect = response_sender.send(rsp); let _ignore_disconnect = response_sender.send(rsp);
} }
@ -277,6 +357,7 @@ impl ServeRepair {
cluster_slots: &ClusterSlots, cluster_slots: &ClusterSlots,
repair_request: &RepairType, repair_request: &RepairType,
cache: &mut RepairCache, cache: &mut RepairCache,
repair_stats: &mut RepairStats,
) -> Result<(SocketAddr, Vec<u8>)> { ) -> Result<(SocketAddr, Vec<u8>)> {
// find a peer that appears to be accepting replication and has the desired slot, as indicated // find a peer that appears to be accepting replication and has the desired slot, as indicated
// by a valid tvu port location // by a valid tvu port location
@ -295,30 +376,26 @@ impl ServeRepair {
let (repair_peers, weights) = cache.get(&repair_request.slot()).unwrap(); let (repair_peers, weights) = cache.get(&repair_request.slot()).unwrap();
let n = weighted_best(&weights, Pubkey::new_rand().to_bytes()); let n = weighted_best(&weights, Pubkey::new_rand().to_bytes());
let addr = repair_peers[n].serve_repair; // send the request to the peer's serve_repair port let addr = repair_peers[n].serve_repair; // send the request to the peer's serve_repair port
let out = self.map_repair_request(repair_request)?; let out = self.map_repair_request(repair_request, repair_stats)?;
Ok((addr, out)) Ok((addr, out))
} }
pub fn map_repair_request(&self, repair_request: &RepairType) -> Result<Vec<u8>> { pub fn map_repair_request(
&self,
repair_request: &RepairType,
repair_stats: &mut RepairStats,
) -> Result<Vec<u8>> {
match repair_request { match repair_request {
RepairType::Shred(slot, shred_index) => { RepairType::Shred(slot, shred_index) => {
datapoint_debug!( repair_stats.shred.update(*slot);
"serve_repair-repair",
("repair-slot", *slot, i64),
("repair-ix", *shred_index, i64)
);
Ok(self.window_index_request_bytes(*slot, *shred_index)?) Ok(self.window_index_request_bytes(*slot, *shred_index)?)
} }
RepairType::HighestShred(slot, shred_index) => { RepairType::HighestShred(slot, shred_index) => {
datapoint_info!( repair_stats.highest_shred.update(*slot);
"serve_repair-repair_highest",
("repair-highest-slot", *slot, i64),
("repair-highest-ix", *shred_index, i64)
);
Ok(self.window_highest_index_request_bytes(*slot, *shred_index)?) Ok(self.window_highest_index_request_bytes(*slot, *shred_index)?)
} }
RepairType::Orphan(slot) => { RepairType::Orphan(slot) => {
datapoint_info!("serve_repair-repair_orphan", ("repair-orphan", *slot, i64)); repair_stats.orphan.update(*slot);
Ok(self.orphan_bytes(*slot)?) Ok(self.orphan_bytes(*slot)?)
} }
} }
@ -583,6 +660,7 @@ mod tests {
&cluster_slots, &cluster_slots,
&RepairType::Shred(0, 0), &RepairType::Shred(0, 0),
&mut HashMap::new(), &mut HashMap::new(),
&mut RepairStats::default(),
); );
assert_matches!(rv, Err(Error::ClusterInfoError(ClusterInfoError::NoPeers))); assert_matches!(rv, Err(Error::ClusterInfoError(ClusterInfoError::NoPeers)));
@ -608,6 +686,7 @@ mod tests {
&cluster_slots, &cluster_slots,
&RepairType::Shred(0, 0), &RepairType::Shred(0, 0),
&mut HashMap::new(), &mut HashMap::new(),
&mut RepairStats::default(),
) )
.unwrap(); .unwrap();
assert_eq!(nxt.serve_repair, serve_repair_addr); assert_eq!(nxt.serve_repair, serve_repair_addr);
@ -639,6 +718,7 @@ mod tests {
&cluster_slots, &cluster_slots,
&RepairType::Shred(0, 0), &RepairType::Shred(0, 0),
&mut HashMap::new(), &mut HashMap::new(),
&mut RepairStats::default(),
) )
.unwrap(); .unwrap();
if rv.0 == serve_repair_addr { if rv.0 == serve_repair_addr {

View File

@ -4,6 +4,7 @@
use crate::{ use crate::{
cluster_info::ClusterInfo, cluster_info::ClusterInfo,
commitment::BlockCommitmentCache,
contact_info::ContactInfo, contact_info::ContactInfo,
result::{Error, Result}, result::{Error, Result},
}; };
@ -11,9 +12,7 @@ use rand::{Rng, SeedableRng};
use rand_chacha::ChaChaRng; use rand_chacha::ChaChaRng;
use solana_chacha_cuda::chacha_cuda::chacha_cbc_encrypt_file_many_keys; use solana_chacha_cuda::chacha_cuda::chacha_cbc_encrypt_file_many_keys;
use solana_ledger::{bank_forks::BankForks, blockstore::Blockstore}; use solana_ledger::{bank_forks::BankForks, blockstore::Blockstore};
use solana_runtime::{ use solana_runtime::{bank::Bank, storage_utils::archiver_accounts};
bank::Bank, status_cache::SignatureConfirmationStatus, storage_utils::archiver_accounts,
};
use solana_sdk::{ use solana_sdk::{
account::Account, account::Account,
account_utils::StateMut, account_utils::StateMut,
@ -30,6 +29,7 @@ use solana_storage_program::{
storage_instruction, storage_instruction,
storage_instruction::proof_validation, storage_instruction::proof_validation,
}; };
use solana_vote_program::vote_state::MAX_LOCKOUT_HISTORY;
use std::{ use std::{
cmp, cmp,
collections::HashMap, collections::HashMap,
@ -185,6 +185,7 @@ impl StorageStage {
exit: &Arc<AtomicBool>, exit: &Arc<AtomicBool>,
bank_forks: &Arc<RwLock<BankForks>>, bank_forks: &Arc<RwLock<BankForks>>,
cluster_info: &Arc<RwLock<ClusterInfo>>, cluster_info: &Arc<RwLock<ClusterInfo>>,
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
) -> Self { ) -> Self {
let (instruction_sender, instruction_receiver) = channel(); let (instruction_sender, instruction_receiver) = channel();
@ -256,6 +257,7 @@ impl StorageStage {
&keypair, &keypair,
&storage_keypair, &storage_keypair,
&transactions_socket, &transactions_socket,
&block_commitment_cache,
) )
.unwrap_or_else(|err| { .unwrap_or_else(|err| {
info!("failed to send storage transaction: {:?}", err) info!("failed to send storage transaction: {:?}", err)
@ -289,6 +291,7 @@ impl StorageStage {
keypair: &Arc<Keypair>, keypair: &Arc<Keypair>,
storage_keypair: &Arc<Keypair>, storage_keypair: &Arc<Keypair>,
transactions_socket: &UdpSocket, transactions_socket: &UdpSocket,
block_commitment_cache: &Arc<RwLock<BlockCommitmentCache>>,
) -> io::Result<()> { ) -> io::Result<()> {
let working_bank = bank_forks.read().unwrap().working_bank(); let working_bank = bank_forks.read().unwrap().working_bank();
let blockhash = working_bank.confirmed_last_blockhash().0; let blockhash = working_bank.confirmed_last_blockhash().0;
@ -323,8 +326,13 @@ impl StorageStage {
cluster_info.read().unwrap().my_data().tpu, cluster_info.read().unwrap().my_data().tpu,
)?; )?;
sleep(Duration::from_millis(100)); sleep(Duration::from_millis(100));
if Self::poll_for_signature_confirmation(bank_forks, &transaction.signatures[0], 0) if Self::poll_for_signature_confirmation(
.is_ok() bank_forks,
block_commitment_cache,
&transaction.signatures[0],
0,
)
.is_ok()
{ {
break; break;
}; };
@ -334,23 +342,24 @@ impl StorageStage {
fn poll_for_signature_confirmation( fn poll_for_signature_confirmation(
bank_forks: &Arc<RwLock<BankForks>>, bank_forks: &Arc<RwLock<BankForks>>,
block_commitment_cache: &Arc<RwLock<BlockCommitmentCache>>,
signature: &Signature, signature: &Signature,
min_confirmed_blocks: usize, min_confirmed_blocks: usize,
) -> Result<()> { ) -> Result<()> {
let mut now = Instant::now(); let mut now = Instant::now();
let mut confirmed_blocks = 0; let mut confirmed_blocks = 0;
loop { loop {
let response = bank_forks let working_bank = bank_forks.read().unwrap().working_bank();
.read() let response = working_bank.get_signature_status_slot(signature);
.unwrap() if let Some((slot, status)) = response {
.working_bank() let confirmations = if working_bank.src.roots().contains(&slot) {
.get_signature_confirmation_status(signature); MAX_LOCKOUT_HISTORY + 1
if let Some(SignatureConfirmationStatus { } else {
confirmations, let r_block_commitment_cache = block_commitment_cache.read().unwrap();
status, r_block_commitment_cache
.. .get_confirmation_count(slot)
}) = response .unwrap_or(0)
{ };
if status.is_ok() { if status.is_ok() {
if confirmed_blocks != confirmations { if confirmed_blocks != confirmations {
now = Instant::now(); now = Instant::now();
@ -655,12 +664,18 @@ mod tests {
use rayon::prelude::*; use rayon::prelude::*;
use solana_ledger::genesis_utils::{create_genesis_config, GenesisConfigInfo}; use solana_ledger::genesis_utils::{create_genesis_config, GenesisConfigInfo};
use solana_runtime::bank::Bank; use solana_runtime::bank::Bank;
use solana_sdk::hash::Hasher; use solana_sdk::{
use solana_sdk::signature::{Keypair, Signer}; hash::Hasher,
use std::cmp::{max, min}; signature::{Keypair, Signer},
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; };
use std::sync::mpsc::channel; use std::{
use std::sync::{Arc, RwLock}; cmp::{max, min},
sync::{
atomic::{AtomicBool, AtomicUsize, Ordering},
mpsc::channel,
Arc, RwLock,
},
};
#[test] #[test]
fn test_storage_stage_none_ledger() { fn test_storage_stage_none_ledger() {
@ -675,6 +690,7 @@ mod tests {
&[bank.clone()], &[bank.clone()],
vec![0], vec![0],
))); )));
let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::default()));
let (_slot_sender, slot_receiver) = channel(); let (_slot_sender, slot_receiver) = channel();
let storage_state = StorageState::new( let storage_state = StorageState::new(
&bank.last_blockhash(), &bank.last_blockhash(),
@ -690,6 +706,7 @@ mod tests {
&exit.clone(), &exit.clone(),
&bank_forks, &bank_forks,
&cluster_info, &cluster_info,
block_commitment_cache,
); );
exit.store(true, Ordering::Relaxed); exit.store(true, Ordering::Relaxed);
storage_stage.join().unwrap(); storage_stage.join().unwrap();

View File

@ -81,7 +81,7 @@ impl Tvu {
#[allow(clippy::new_ret_no_self, clippy::too_many_arguments)] #[allow(clippy::new_ret_no_self, clippy::too_many_arguments)]
pub fn new( pub fn new(
vote_account: &Pubkey, vote_account: &Pubkey,
voting_keypair: Option<Arc<Keypair>>, authorized_voter_keypairs: Vec<Arc<Keypair>>,
storage_keypair: &Arc<Keypair>, storage_keypair: &Arc<Keypair>,
bank_forks: &Arc<RwLock<BankForks>>, bank_forks: &Arc<RwLock<BankForks>>,
cluster_info: &Arc<RwLock<ClusterInfo>>, cluster_info: &Arc<RwLock<ClusterInfo>>,
@ -160,7 +160,7 @@ impl Tvu {
*bank_forks.read().unwrap().working_bank().epoch_schedule(), *bank_forks.read().unwrap().working_bank().epoch_schedule(),
cfg, cfg,
tvu_config.shred_version, tvu_config.shred_version,
cluster_slots, cluster_slots.clone(),
); );
let (ledger_cleanup_slot_sender, ledger_cleanup_slot_receiver) = channel(); let (ledger_cleanup_slot_sender, ledger_cleanup_slot_receiver) = channel();
@ -179,13 +179,13 @@ impl Tvu {
let replay_stage_config = ReplayStageConfig { let replay_stage_config = ReplayStageConfig {
my_pubkey: keypair.pubkey(), my_pubkey: keypair.pubkey(),
vote_account: *vote_account, vote_account: *vote_account,
voting_keypair, authorized_voter_keypairs,
exit: exit.clone(), exit: exit.clone(),
subscriptions: subscriptions.clone(), subscriptions: subscriptions.clone(),
leader_schedule_cache: leader_schedule_cache.clone(), leader_schedule_cache: leader_schedule_cache.clone(),
latest_root_senders: vec![ledger_cleanup_slot_sender], latest_root_senders: vec![ledger_cleanup_slot_sender],
accounts_hash_sender: Some(accounts_hash_sender), accounts_hash_sender: Some(accounts_hash_sender),
block_commitment_cache, block_commitment_cache: block_commitment_cache.clone(),
transaction_status_sender, transaction_status_sender,
rewards_recorder_sender, rewards_recorder_sender,
}; };
@ -198,6 +198,7 @@ impl Tvu {
ledger_signal_receiver, ledger_signal_receiver,
poh_recorder.clone(), poh_recorder.clone(),
vote_tracker, vote_tracker,
cluster_slots,
retransmit_slots_sender, retransmit_slots_sender,
); );
@ -221,6 +222,7 @@ impl Tvu {
&exit, &exit,
&bank_forks, &bank_forks,
&cluster_info, &cluster_info,
block_commitment_cache,
); );
Tvu { Tvu {
@ -285,14 +287,14 @@ pub mod tests {
let bank = bank_forks.working_bank(); let bank = bank_forks.working_bank();
let (exit, poh_recorder, poh_service, _entry_receiver) = let (exit, poh_recorder, poh_service, _entry_receiver) =
create_test_recorder(&bank, &blockstore, None); create_test_recorder(&bank, &blockstore, None);
let voting_keypair = Keypair::new(); let vote_keypair = Keypair::new();
let storage_keypair = Arc::new(Keypair::new()); let storage_keypair = Arc::new(Keypair::new());
let leader_schedule_cache = Arc::new(LeaderScheduleCache::new_from_bank(&bank)); let leader_schedule_cache = Arc::new(LeaderScheduleCache::new_from_bank(&bank));
let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::default())); let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::default()));
let (retransmit_slots_sender, _retransmit_slots_receiver) = unbounded(); let (retransmit_slots_sender, _retransmit_slots_receiver) = unbounded();
let tvu = Tvu::new( let tvu = Tvu::new(
&voting_keypair.pubkey(), &vote_keypair.pubkey(),
Some(Arc::new(voting_keypair)), vec![Arc::new(vote_keypair)],
&storage_keypair, &storage_keypair,
&Arc::new(RwLock::new(bank_forks)), &Arc::new(RwLock::new(bank_forks)),
&cref1, &cref1,
@ -307,7 +309,10 @@ pub mod tests {
blockstore, blockstore,
&StorageState::default(), &StorageState::default(),
l_receiver, l_receiver,
&Arc::new(RpcSubscriptions::new(&exit)), &Arc::new(RpcSubscriptions::new(
&exit,
Arc::new(RwLock::new(BlockCommitmentCache::default())),
)),
&poh_recorder, &poh_recorder,
&leader_schedule_cache, &leader_schedule_cache,
&exit, &exit,

View File

@ -151,7 +151,7 @@ impl Validator {
keypair: &Arc<Keypair>, keypair: &Arc<Keypair>,
ledger_path: &Path, ledger_path: &Path,
vote_account: &Pubkey, vote_account: &Pubkey,
authorized_voter: &Arc<Keypair>, mut authorized_voter_keypairs: Vec<Arc<Keypair>>,
storage_keypair: &Arc<Keypair>, storage_keypair: &Arc<Keypair>,
entrypoint_info_option: Option<&ContactInfo>, entrypoint_info_option: Option<&ContactInfo>,
poh_verify: bool, poh_verify: bool,
@ -162,7 +162,15 @@ impl Validator {
warn!("identity: {}", id); warn!("identity: {}", id);
warn!("vote account: {}", vote_account); warn!("vote account: {}", vote_account);
warn!("authorized voter: {}", authorized_voter.pubkey());
if config.voting_disabled {
warn!("voting disabled");
authorized_voter_keypairs.clear();
} else {
for authorized_voter_keypair in &authorized_voter_keypairs {
warn!("authorized voter: {}", authorized_voter_keypair.pubkey());
}
}
report_target_features(); report_target_features();
info!("entrypoint: {:?}", entrypoint_info_option); info!("entrypoint: {:?}", entrypoint_info_option);
@ -234,7 +242,7 @@ impl Validator {
let blockstore = Arc::new(blockstore); let blockstore = Arc::new(blockstore);
let subscriptions = Arc::new(RpcSubscriptions::new(&exit)); let subscriptions = Arc::new(RpcSubscriptions::new(&exit, block_commitment_cache.clone()));
let rpc_service = config.rpc_ports.map(|(rpc_port, rpc_pubsub_port)| { let rpc_service = config.rpc_ports.map(|(rpc_port, rpc_pubsub_port)| {
if ContactInfo::is_valid_address(&node.info.rpc) { if ContactInfo::is_valid_address(&node.info.rpc) {
@ -386,11 +394,7 @@ impl Validator {
let (retransmit_slots_sender, retransmit_slots_receiver) = unbounded(); let (retransmit_slots_sender, retransmit_slots_receiver) = unbounded();
let tvu = Tvu::new( let tvu = Tvu::new(
vote_account, vote_account,
if config.voting_disabled { authorized_voter_keypairs,
None
} else {
Some(authorized_voter.clone())
},
storage_keypair, storage_keypair,
&bank_forks, &bank_forks,
&cluster_info, &cluster_info,
@ -728,7 +732,7 @@ impl TestValidator {
&node_keypair, &node_keypair,
&ledger_path, &ledger_path,
&leader_voting_keypair.pubkey(), &leader_voting_keypair.pubkey(),
&leader_voting_keypair, vec![leader_voting_keypair.clone()],
&storage_keypair, &storage_keypair,
None, None,
true, true,
@ -836,7 +840,7 @@ mod tests {
&Arc::new(validator_keypair), &Arc::new(validator_keypair),
&validator_ledger_path, &validator_ledger_path,
&voting_keypair.pubkey(), &voting_keypair.pubkey(),
&voting_keypair, vec![voting_keypair.clone()],
&storage_keypair, &storage_keypair,
Some(&leader_node.info), Some(&leader_node.info),
true, true,
@ -861,7 +865,7 @@ mod tests {
.genesis_config; .genesis_config;
let (validator_ledger_path, _blockhash) = create_new_tmp_ledger!(&genesis_config); let (validator_ledger_path, _blockhash) = create_new_tmp_ledger!(&genesis_config);
ledger_paths.push(validator_ledger_path.clone()); ledger_paths.push(validator_ledger_path.clone());
let voting_keypair = Arc::new(Keypair::new()); let vote_account_keypair = Arc::new(Keypair::new());
let storage_keypair = Arc::new(Keypair::new()); let storage_keypair = Arc::new(Keypair::new());
let config = ValidatorConfig { let config = ValidatorConfig {
rpc_ports: Some(( rpc_ports: Some((
@ -874,8 +878,8 @@ mod tests {
validator_node, validator_node,
&Arc::new(validator_keypair), &Arc::new(validator_keypair),
&validator_ledger_path, &validator_ledger_path,
&voting_keypair.pubkey(), &vote_account_keypair.pubkey(),
&voting_keypair, vec![vote_account_keypair.clone()],
&storage_keypair, &storage_keypair,
Some(&leader_node.info), Some(&leader_node.info),
true, true,

View File

@ -3,8 +3,8 @@ use solana_client::{
rpc_client::RpcClient, rpc_client::RpcClient,
}; };
use solana_core::{ use solana_core::{
rpc_pubsub_service::PubSubService, rpc_subscriptions::RpcSubscriptions, commitment::BlockCommitmentCache, rpc_pubsub_service::PubSubService,
validator::TestValidator, rpc_subscriptions::RpcSubscriptions, validator::TestValidator,
}; };
use solana_sdk::{ use solana_sdk::{
commitment_config::CommitmentConfig, pubkey::Pubkey, rpc_port, signature::Signer, commitment_config::CommitmentConfig, pubkey::Pubkey, rpc_port, signature::Signer,
@ -15,7 +15,7 @@ use std::{
net::{IpAddr, SocketAddr}, net::{IpAddr, SocketAddr},
sync::{ sync::{
atomic::{AtomicBool, Ordering}, atomic::{AtomicBool, Ordering},
Arc, Arc, RwLock,
}, },
thread::sleep, thread::sleep,
time::{Duration, Instant}, time::{Duration, Instant},
@ -85,7 +85,10 @@ fn test_slot_subscription() {
rpc_port::DEFAULT_RPC_PUBSUB_PORT, rpc_port::DEFAULT_RPC_PUBSUB_PORT,
); );
let exit = Arc::new(AtomicBool::new(false)); let exit = Arc::new(AtomicBool::new(false));
let subscriptions = Arc::new(RpcSubscriptions::new(&exit)); let subscriptions = Arc::new(RpcSubscriptions::new(
&exit,
Arc::new(RwLock::new(BlockCommitmentCache::default())),
));
let pubsub_service = PubSubService::new(&subscriptions, pubsub_addr, &exit); let pubsub_service = PubSubService::new(&subscriptions, pubsub_addr, &exit);
std::thread::sleep(Duration::from_millis(400)); std::thread::sleep(Duration::from_millis(400));

View File

@ -16,6 +16,7 @@ use solana_sdk::{
commitment_config::CommitmentConfig, commitment_config::CommitmentConfig,
hash::Hash, hash::Hash,
pubkey::Pubkey, pubkey::Pubkey,
signature::Signer,
system_transaction, system_transaction,
transaction::{self, Transaction}, transaction::{self, Transaction},
}; };
@ -24,7 +25,6 @@ use std::{
fs::remove_dir_all, fs::remove_dir_all,
net::UdpSocket, net::UdpSocket,
sync::mpsc::channel, sync::mpsc::channel,
sync::{Arc, Mutex},
thread::sleep, thread::sleep,
time::{Duration, Instant}, time::{Duration, Instant},
}; };
@ -210,9 +210,11 @@ fn test_rpc_subscriptions() {
.. ..
} = TestValidator::run(); } = TestValidator::run();
// Create transaction signatures to subscribe to
let transactions_socket = UdpSocket::bind("0.0.0.0:0").unwrap(); let transactions_socket = UdpSocket::bind("0.0.0.0:0").unwrap();
let transactions: Vec<Transaction> = (0..100) transactions_socket.connect(leader_data.tpu).unwrap();
// Create transaction signatures to subscribe to
let transactions: Vec<Transaction> = (0..1000)
.map(|_| system_transaction::transfer(&alice, &Pubkey::new_rand(), 1, genesis_hash)) .map(|_| system_transaction::transfer(&alice, &Pubkey::new_rand(), 1, genesis_hash))
.collect(); .collect();
let mut signature_set: HashSet<String> = transactions let mut signature_set: HashSet<String> = transactions
@ -220,15 +222,15 @@ fn test_rpc_subscriptions() {
.map(|tx| tx.signatures[0].to_string()) .map(|tx| tx.signatures[0].to_string())
.collect(); .collect();
// Track when subscriptions are ready
let (ready_sender, ready_receiver) = channel::<()>();
// Track when status notifications are received
let (status_sender, status_receiver) = channel::<(String, Response<transaction::Result<()>>)>();
// Create the pub sub runtime // Create the pub sub runtime
let mut rt = Runtime::new().unwrap(); let mut rt = Runtime::new().unwrap();
let rpc_pubsub_url = format!("ws://{}/", leader_data.rpc_pubsub); let rpc_pubsub_url = format!("ws://{}/", leader_data.rpc_pubsub);
let (status_sender, status_receiver) = channel::<(String, Response<transaction::Result<()>>)>();
let status_sender = Arc::new(Mutex::new(status_sender));
let (sent_sender, sent_receiver) = channel::<()>();
let sent_sender = Arc::new(Mutex::new(sent_sender));
// Subscribe to all signatures // Subscribe to all signatures
rt.spawn({ rt.spawn({
let connect = ws::try_connect::<PubsubClient>(&rpc_pubsub_url).unwrap(); let connect = ws::try_connect::<PubsubClient>(&rpc_pubsub_url).unwrap();
@ -237,18 +239,12 @@ fn test_rpc_subscriptions() {
.and_then(move |client| { .and_then(move |client| {
for sig in signature_set { for sig in signature_set {
let status_sender = status_sender.clone(); let status_sender = status_sender.clone();
let sent_sender = sent_sender.clone();
tokio::spawn( tokio::spawn(
client client
.signature_subscribe(sig.clone(), None) .signature_subscribe(sig.clone(), None)
.and_then(move |sig_stream| { .and_then(move |sig_stream| {
sent_sender.lock().unwrap().send(()).unwrap();
sig_stream.for_each(move |result| { sig_stream.for_each(move |result| {
status_sender status_sender.send((sig.clone(), result)).unwrap();
.lock()
.unwrap()
.send((sig.clone(), result))
.unwrap();
future::ok(()) future::ok(())
}) })
}) })
@ -257,37 +253,50 @@ fn test_rpc_subscriptions() {
}), }),
); );
} }
tokio::spawn(
client
.slot_subscribe()
.and_then(move |slot_stream| {
slot_stream.for_each(move |_| {
ready_sender.send(()).unwrap();
future::ok(())
})
})
.map_err(|err| {
eprintln!("slot sub err: {:#?}", err);
}),
);
future::ok(()) future::ok(())
}) })
.map_err(|_| ()) .map_err(|_| ())
}); });
// Wait for signature subscriptions // Wait for signature subscriptions
let deadline = Instant::now() + Duration::from_secs(2); ready_receiver.recv_timeout(Duration::from_secs(2)).unwrap();
(0..transactions.len()).for_each(|_| {
sent_receiver
.recv_timeout(deadline.saturating_duration_since(Instant::now()))
.unwrap();
});
let rpc_client = RpcClient::new_socket(leader_data.rpc); let rpc_client = RpcClient::new_socket(leader_data.rpc);
let mut transaction_count = rpc_client let mut mint_balance = rpc_client
.get_transaction_count_with_commitment(CommitmentConfig::recent()) .get_balance_with_commitment(&alice.pubkey(), CommitmentConfig::recent())
.unwrap(); .unwrap()
.value;
assert!(mint_balance >= transactions.len() as u64);
// Send all transactions to tpu socket for processing // Send all transactions to tpu socket for processing
transactions.iter().for_each(|tx| { transactions.iter().for_each(|tx| {
transactions_socket transactions_socket
.send_to(&bincode::serialize(&tx).unwrap(), leader_data.tpu) .send(&bincode::serialize(&tx).unwrap())
.unwrap(); .unwrap();
}); });
// Track mint balance to know when transactions have completed
let now = Instant::now(); let now = Instant::now();
let expected_transaction_count = transaction_count + transactions.len() as u64; let expected_mint_balance = mint_balance - transactions.len() as u64;
while transaction_count < expected_transaction_count && now.elapsed() < Duration::from_secs(5) { while mint_balance != expected_mint_balance && now.elapsed() < Duration::from_secs(5) {
transaction_count = rpc_client mint_balance = rpc_client
.get_transaction_count_with_commitment(CommitmentConfig::recent()) .get_balance_with_commitment(&alice.pubkey(), CommitmentConfig::recent())
.unwrap(); .unwrap()
sleep(Duration::from_millis(200)); .value;
sleep(Duration::from_millis(100));
} }
// Wait for all signature subscriptions // Wait for all signature subscriptions
@ -300,12 +309,12 @@ fn test_rpc_subscriptions() {
assert!(signature_set.remove(&sig)); assert!(signature_set.remove(&sig));
} }
Err(_err) => { Err(_err) => {
eprintln!( assert!(
false,
"recv_timeout, {}/{} signatures remaining", "recv_timeout, {}/{} signatures remaining",
signature_set.len(), signature_set.len(),
transactions.len() transactions.len()
); );
assert!(false)
} }
} }
} }

View File

@ -3,28 +3,36 @@
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use log::*; use log::*;
use solana_core::storage_stage::{test_cluster_info, SLOTS_PER_TURN_TEST}; use solana_core::{
use solana_core::storage_stage::{StorageStage, StorageState}; commitment::BlockCommitmentCache,
use solana_ledger::bank_forks::BankForks; storage_stage::{test_cluster_info, StorageStage, StorageState, SLOTS_PER_TURN_TEST},
use solana_ledger::blockstore_processor; };
use solana_ledger::entry; use solana_ledger::{
use solana_ledger::genesis_utils::{create_genesis_config, GenesisConfigInfo}; bank_forks::BankForks,
use solana_ledger::{blockstore::Blockstore, create_new_tmp_ledger}; blockstore::Blockstore,
blockstore_processor, create_new_tmp_ledger, entry,
genesis_utils::{create_genesis_config, GenesisConfigInfo},
};
use solana_runtime::bank::Bank; use solana_runtime::bank::Bank;
use solana_sdk::clock::DEFAULT_TICKS_PER_SLOT; use solana_sdk::{
use solana_sdk::hash::Hash; clock::DEFAULT_TICKS_PER_SLOT,
use solana_sdk::message::Message; hash::Hash,
use solana_sdk::pubkey::Pubkey; message::Message,
use solana_sdk::signature::{Keypair, Signer}; pubkey::Pubkey,
use solana_sdk::transaction::Transaction; signature::{Keypair, Signer},
use solana_storage_program::storage_instruction; transaction::Transaction,
use solana_storage_program::storage_instruction::StorageAccountType; };
use std::fs::remove_dir_all; use solana_storage_program::storage_instruction::{self, StorageAccountType};
use std::sync::atomic::{AtomicBool, Ordering}; use std::{
use std::sync::mpsc::channel; fs::remove_dir_all,
use std::sync::{Arc, RwLock}; sync::{
use std::thread::sleep; atomic::{AtomicBool, Ordering},
use std::time::Duration; mpsc::channel,
Arc, RwLock,
},
thread::sleep,
time::Duration,
};
#[test] #[test]
fn test_storage_stage_process_account_proofs() { fn test_storage_stage_process_account_proofs() {
@ -52,6 +60,7 @@ mod tests {
&[bank.clone()], &[bank.clone()],
vec![0], vec![0],
))); )));
let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::default()));
let cluster_info = test_cluster_info(&keypair.pubkey()); let cluster_info = test_cluster_info(&keypair.pubkey());
let (bank_sender, bank_receiver) = channel(); let (bank_sender, bank_receiver) = channel();
@ -69,6 +78,7 @@ mod tests {
&exit.clone(), &exit.clone(),
&bank_forks, &bank_forks,
&cluster_info, &cluster_info,
block_commitment_cache,
); );
bank_sender.send(vec![bank.clone()]).unwrap(); bank_sender.send(vec![bank.clone()]).unwrap();
@ -171,6 +181,7 @@ mod tests {
&[bank.clone()], &[bank.clone()],
vec![0], vec![0],
))); )));
let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::default()));
let cluster_info = test_cluster_info(&keypair.pubkey()); let cluster_info = test_cluster_info(&keypair.pubkey());
let (bank_sender, bank_receiver) = channel(); let (bank_sender, bank_receiver) = channel();
@ -188,6 +199,7 @@ mod tests {
&exit.clone(), &exit.clone(),
&bank_forks, &bank_forks,
&cluster_info, &cluster_info,
block_commitment_cache,
); );
bank_sender.send(vec![bank.clone()]).unwrap(); bank_sender.send(vec![bank.clone()]).unwrap();

View File

@ -1,6 +1,6 @@
[package] [package]
name = "solana-crate-features" name = "solana-crate-features"
version = "1.1.0" version = "1.1.1"
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"

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

View File

@ -6,15 +6,17 @@
* [Trust Wallet](wallet/trust-wallet.md) * [Trust Wallet](wallet/trust-wallet.md)
* [Ledger Live](wallet/ledger-live.md) * [Ledger Live](wallet/ledger-live.md)
* [Command-line Wallets](wallet/cli-wallets.md) * [Command-line Wallets](wallet/cli-wallets.md)
* [Hardware Wallets](remote-wallet/README.md)
* [Ledger Hardware Wallet](remote-wallet/ledger.md)
* [Paper Wallet](paper-wallet/README.md) * [Paper Wallet](paper-wallet/README.md)
* [Paper Wallet Usage](paper-wallet/paper-wallet-usage.md) * [Paper Wallet Usage](paper-wallet/paper-wallet-usage.md)
* [Hardware Wallets](remote-wallet/README.md)
* [Ledger Hardware Wallet](remote-wallet/ledger.md)
* [Support / Troubleshooting](wallet/support.md)
* [Command Line Guide](cli/README.md) * [Command Line Guide](cli/README.md)
* [Install the Solana Command Line Tool Suite](cli/install-solana-cli-tools.md) * [Install the Solana Command Line Tool Suite](cli/install-solana-cli-tools.md)
* [Generate Keys](cli/generate-keys.md) * [Generate Keys](cli/generate-keys.md)
* [Send and Receive Tokens](cli/transfer-tokens.md) * [Send and Receive Tokens](cli/transfer-tokens.md)
* [Delegate Stake](cli/delegate-stake.md) * [Delegate Stake](cli/delegate-stake.md)
* [Manage Stake Accounts](cli/manage-stake-accounts.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)
* [Command-line Reference](cli/usage.md) * [Command-line Reference](cli/usage.md)

View File

@ -34,6 +34,7 @@ To interact with a Solana node inside a JavaScript application, use the [solana-
* [getProgramAccounts](jsonrpc-api.md#getprogramaccounts) * [getProgramAccounts](jsonrpc-api.md#getprogramaccounts)
* [getRecentBlockhash](jsonrpc-api.md#getrecentblockhash) * [getRecentBlockhash](jsonrpc-api.md#getrecentblockhash)
* [getSignatureStatus](jsonrpc-api.md#getsignaturestatus) * [getSignatureStatus](jsonrpc-api.md#getsignaturestatus)
* [getSignatureStatuses](jsonrpc-api.md#getsignaturestatuses)
* [getSlot](jsonrpc-api.md#getslot) * [getSlot](jsonrpc-api.md#getslot)
* [getSlotLeader](jsonrpc-api.md#getslotleader) * [getSlotLeader](jsonrpc-api.md#getslotleader)
* [getSlotsPerSegment](jsonrpc-api.md#getslotspersegment) * [getSlotsPerSegment](jsonrpc-api.md#getslotspersegment)
@ -118,7 +119,7 @@ Many methods that take a commitment parameter return an RpcResponse JSON object
### confirmTransaction ### confirmTransaction
Returns a transaction receipt Returns a transaction receipt. This method only searches the recent status cache of signatures, which retains all active slots plus `MAX_RECENT_BLOCKHASHES` rooted slots.
#### Parameters: #### Parameters:
@ -656,14 +657,39 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "m
### getSignatureStatus ### getSignatureStatus
Returns the status of a given signature. This method is similar to [confirmTransaction](jsonrpc-api.md#confirmtransaction) but provides more resolution for error events. Returns the status of a given signature. This method is similar to [confirmTransaction](jsonrpc-api.md#confirmtransaction) but provides more resolution for error events. This method only searches the recent status cache of signatures, which retains all active slots plus `MAX_RECENT_BLOCKHASHES` rooted slots.
#### Parameters:
* `<string>` - Signature of Transaction to confirm, as base-58 encoded string
* `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
#### Results:
* `<null>` - Unknown transaction
* `<object>` - Transaction status:
* `"Ok": <null>` - Transaction was successful
* `"Err": <ERR>` - Transaction failed with TransactionError [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L14)
#### Example:
```bash
// Request
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getSignatureStatus", "params":["5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"]}' http://localhost:8899
// Result
{"jsonrpc":"2.0","result":{"Ok": null},"id":1}
```
### getSignatureStatuses
Returns the statuses of a list of signatures. This method is similar to [confirmTransaction](jsonrpc-api.md#confirmtransaction) but provides more resolution for error events. This method only searches the recent status cache of signatures, which retains all active slots plus `MAX_RECENT_BLOCKHASHES` rooted slots.
#### Parameters: #### Parameters:
* `<array>` - An array of transaction signatures to confirm, as base-58 encoded strings * `<array>` - An array of transaction signatures to confirm, as base-58 encoded strings
* `<object>` - (optional) Extended Rpc configuration, containing the following optional fields: * `<object>` - (optional) Extended Rpc configuration, containing the following optional fields:
* `commitment: <string>` - [Commitment](jsonrpc-api.md#configuring-state-commitment) * `commitment: <string>` - [Commitment](jsonrpc-api.md#configuring-state-commitment)
* `searchTransactionHistory: <bool>` - whether to search the ledger transaction status cache, which may be expensive
#### Results: #### Results:
@ -889,7 +915,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.1.0"},"id":1} {"jsonrpc":"2.0","result":{"solana-core": "1.1.1"},"id":1}
``` ```
### getVoteAccounts ### getVoteAccounts

View File

@ -33,11 +33,13 @@ want to perform an action on the stake account you create next.
Now, create a stake account: Now, create a stake account:
```bash ```bash
solana create-stake-account --from <KEYPAIR> stake-account.json <AMOUNT> --stake-authority <KEYPAIR> --withdraw-authority <KEYPAIR> solana create-stake-account --from <KEYPAIR> stake-account.json <AMOUNT> \
--stake-authority <KEYPAIR> --withdraw-authority <KEYPAIR> \
--fee-payer <KEYPAIR>
``` ```
`<AMOUNT>` tokens are transferred from the account at `<KEYPAIR>` to a new `<AMOUNT>` tokens are transferred from the account at the "from" `<KEYPAIR>` to
stake account at the public key of stake-account.json. a new stake account at the public key of stake-account.json.
The stake-account.json file can now be discarded. To authorize additional The stake-account.json file can now be discarded. To authorize additional
actions, you will use the `--stake-authority` or `withdraw-authority` keypair, actions, you will use the `--stake-authority` or `withdraw-authority` keypair,
@ -72,7 +74,9 @@ Stake and withdraw authorities can be set when creating an account via the
run: run:
```bash ```bash
solana stake-authorize <STAKE_ACCOUNT_ADDRESS> --stake-authority <KEYPAIR> --new-stake-authority <PUBKEY> solana stake-authorize <STAKE_ACCOUNT_ADDRESS> \
--stake-authority <KEYPAIR> --new-stake-authority <PUBKEY> \
--fee-payer <KEYPAIR>
``` ```
This will use the existing stake authority `<KEYPAIR>` to authorize a new stake This will use the existing stake authority `<KEYPAIR>` to authorize a new stake
@ -87,7 +91,8 @@ addresses can be cumbersome. Fortunately, you can derive stake addresses using
the `--seed` option: the `--seed` option:
```bash ```bash
solana create-stake-account --from <KEYPAIR> <STAKE_ACCOUNT_KEYPAIR> --seed <STRING> <AMOUNT> --stake-authority <PUBKEY> --withdraw-authority <PUBKEY> solana create-stake-account --from <KEYPAIR> <STAKE_ACCOUNT_KEYPAIR> --seed <STRING> <AMOUNT> \
--stake-authority <PUBKEY> --withdraw-authority <PUBKEY> --fee-payer <KEYPAIR>
``` ```
`<STRING>` is an arbitrary string up to 32 bytes, but will typically be a `<STRING>` is an arbitrary string up to 32 bytes, but will typically be a
@ -122,12 +127,13 @@ is the vote account address. Choose a validator and use its vote account
address in `solana delegate-stake`: address in `solana delegate-stake`:
```bash ```bash
solana delegate-stake --stake-authority <KEYPAIR> <STAKE_ACCOUNT_ADDRESS> <VOTE_ACCOUNT_ADDRESS> solana delegate-stake --stake-authority <KEYPAIR> <STAKE_ACCOUNT_ADDRESS> <VOTE_ACCOUNT_ADDRESS> \
--fee-payer <KEYPAIR>
``` ```
`<KEYPAIR>` authorizes the operation on the account with address The stake authority `<KEYPAIR>` authorizes the operation on the account with
`<STAKE_ACCOUNT_ADDRESS>`. The stake is delegated to the vote account with address `<STAKE_ACCOUNT_ADDRESS>`. The stake is delegated to the vote account
address `<VOTE_ACCOUNT_ADDRESS>`. with address `<VOTE_ACCOUNT_ADDRESS>`.
After delegating stake, use `solana stake-account` to observe the changes After delegating stake, use `solana stake-account` to observe the changes
to the stake account: to the stake account:
@ -155,11 +161,12 @@ Once delegated, you can undelegate stake with the `solana deactivate-stake`
command: command:
```bash ```bash
solana deactivate-stake --stake-authority <KEYPAIR> <STAKE_ACCOUNT_ADDRESS> solana deactivate-stake --stake-authority <KEYPAIR> <STAKE_ACCOUNT_ADDRESS> \
--fee-payer <KEYPAIR>
``` ```
`<KEYPAIR>` authorizes the operation on the account with address The stake authority `<KEYPAIR>` authorizes the operation on the account
`<STAKE_ACCOUNT_ADDRESS>`. with address `<STAKE_ACCOUNT_ADDRESS>`.
Note that stake takes several epochs to "cool down". Attempts to delegate stake Note that stake takes several epochs to "cool down". Attempts to delegate stake
in the cool down period will fail. in the cool down period will fail.
@ -169,12 +176,13 @@ in the cool down period will fail.
Transfer tokens out of a stake account with the `solana withdraw-stake` command: Transfer tokens out of a stake account with the `solana withdraw-stake` command:
```bash ```bash
solana withdraw-stake --withdraw-authority <KEYPAIR> <STAKE_ACCOUNT_ADDRESS> <RECIPIENT_ADDRESS> <AMOUNT> solana withdraw-stake --withdraw-authority <KEYPAIR> <STAKE_ACCOUNT_ADDRESS> <RECIPIENT_ADDRESS> <AMOUNT> \
--fee-payer <KEYPAIR>
``` ```
`<STAKE_ACCOUNT_ADDRESS>` is the existing stake account, `<KEYPAIR>` is the `<STAKE_ACCOUNT_ADDRESS>` is the existing stake account, the stake authority
withdraw authority, and `<AMOUNT>` is the number of tokens to transfer to `<KEYPAIR>` is the withdraw authority, and `<AMOUNT>` is the number of tokens
`<RECIPIENT_ADDRESS>`. to transfer to `<RECIPIENT_ADDRESS>`.
## Split Stake ## Split Stake
@ -184,12 +192,14 @@ currently staked, cooling down, or locked up. To transfer tokens from an
existing stake account to a new one, use the `solana split-stake` command: existing stake account to a new one, use the `solana split-stake` command:
```bash ```bash
solana split-stake --stake-authority <KEYPAIR> <STAKE_ACCOUNT_ADDRESS> <NEW_STAKE_ACCOUNT_KEYPAIR> <AMOUNT> solana split-stake --stake-authority <KEYPAIR> <STAKE_ACCOUNT_ADDRESS> <NEW_STAKE_ACCOUNT_KEYPAIR> <AMOUNT> \
--fee-payer <KEYPAIR>
``` ```
`<STAKE_ACCOUNT_ADDRESS>` is the existing stake account, `<KEYPAIR>` is the `<STAKE_ACCOUNT_ADDRESS>` is the existing stake account, the stake authority
stake authority, `<NEW_STAKE_ACCOUNT_KEYPAIR>` is the keypair for the new account, `<KEYPAIR>` is the stake authority, `<NEW_STAKE_ACCOUNT_KEYPAIR>` is the
and `<AMOUNT>` is the number of tokens to transfer to the new account. keypair for the new account, and `<AMOUNT>` is the number of tokens to transfer
to the new account.
To split a stake account into a derived account address, use the `--seed` To split a stake account into a derived account address, use the `--seed`
option. See option. See

View File

@ -0,0 +1,76 @@
# Manage Stake Accounts
If you want to delegate stake to many different validators, you will need
to create a separate stake account for each. If you follow the convention
of creating the first stake account at seed "0", the second at "1", the
third at "2", and so on, then the `solana-stake-accounts` tool will allow
you to operate on all accounts with single invocations. You can use it to
sum up the balances of all accounts, move accounts to a new wallet, or set
new authorities.
## Usage
### Create a stake account
Create and fund a derived stake account at the stake authority public key:
```bash
solana-stake-accounts new <FUNDING_KEYPAIR> <BASE_KEYPAIR> <AMOUNT> \
--stake-authority <PUBKEY> --withdraw-authority <PUBKEY> \
--fee-payer <KEYPAIR>
```
### Count accounts
Count the number of derived accounts:
```bash
solana-stake-accounts count <BASE_PUBKEY>
```
### Get stake account balances
Sum the balance of derived stake accounts:
```bash
solana-stake-accounts balance <BASE_PUBKEY> --num-accounts <NUMBER>
```
### Get stake account addresses
List the address of each stake account derived from the given public key:
```bash
solana-stake-accounts addresses <BASE_PUBKEY> --num-accounts <NUMBER>
```
### Set new authorities
Set new authorities on each derived stake account:
```bash
solana-stake-accounts authorize <BASE_PUBKEY> \
--stake-authority <KEYPAIR> --withdraw-authority <KEYPAIR> \
--new-stake-authority <PUBKEY> --new-withdraw-authority <PUBKEY> \
--num-accounts <NUMBER> --fee-payer <KEYPAIR>
```
### Relocate stake accounts
Relocate stake accounts:
```bash
solana-stake-accounts rebase <BASE_PUBKEY> <NEW_BASE_KEYPAIR> \
--stake-authority <KEYPAIR> --num-accounts <NUMBER> \
--fee-payer <KEYPAIR>
```
To atomically rebase and authorize each stake account, use the 'move'
command:
```bash
solana-stake-accounts move <BASE_PUBKEY> <NEW_BASE_KEYPAIR> \
--stake-authority <KEYPAIR> --withdraw-authority <KEYPAIR> \
--new-stake-authority <PUBKEY> --new-withdraw-authority <PUBKEY> \
--num-accounts <NUMBER> --fee-payer <KEYPAIR>
```

View File

@ -84,10 +84,10 @@ pubkey: GKvqsuNcnwWqPzzuhLmGi4rzzh55FhJtGizkhHaEJqiV
``` ```
```bash ```bash
solana transfer --from <SENDER_KEYPAIR> <RECIPIENT_ACCOUNT_ADDRESS> 5 --url http://devnet.solana.com solana transfer --from <KEYPAIR> <RECIPIENT_ACCOUNT_ADDRESS> 5--url http://devnet.solana.com --fee-payer <KEYPAIR>
``` ```
where you replace `<SENDER_KEYPAIR>` with the path to a keypair in your wallet, where you replace `<KEYPAIR>` with the path to a keypair in your wallet,
and replace `<RECIPIENT_ACCOUNT_ADDRESS>` with the output of `solana-keygen new` above. and replace `<RECIPIENT_ACCOUNT_ADDRESS>` with the output of `solana-keygen new` above.
Confirm the updated balances with `solana balance`: Confirm the updated balances with `solana balance`:
@ -107,7 +107,7 @@ tokens to transfer. Once you have that collected, you can transfer tokens
with the `solana transfer` command: with the `solana transfer` command:
```bash ```bash
solana transfer --from <SENDER_KEYPAIR> <RECIPIENT_ACCOUNT_ADDRESS> <AMOUNT> solana transfer --from <KEYPAIR> <RECIPIENT_ACCOUNT_ADDRESS> <AMOUNT> --fee-payer <KEYPAIR>
``` ```
Confirm the updated balances with `solana balance`: Confirm the updated balances with `solana balance`:

View File

@ -289,3 +289,7 @@ Refer to the following page for a comprehensive guide on delegating stake:
--- ---
{% page-ref page="../api-reference/cli.md" %} {% page-ref page="../api-reference/cli.md" %}
## Support
Check out our [Wallet Support Page](../wallet/support.md) for ways to get help.

View File

@ -76,3 +76,29 @@ but where `BsNsvfXqQTtJnagwFWdBS7FBXgnsK8VZ5CmuznN85swK` is your `WALLET_ID`.
With your fully qualified URL, you can connect multiple hardware wallets to With your fully qualified URL, you can connect multiple hardware wallets to
the same computer and uniquely identify a keypair from any of them. the same computer and uniquely identify a keypair from any of them.
## Troubleshooting
### Keypair URL parameters are ignored in zsh
The question mark character is a special character in zsh. If that's not a
feature you use, add the following line to your `~/.zshrc` to treat it as a
normal character:
```bash
unsetopt nomatch
```
Then either restart your shell window or run `~/.zshrc`:
```bash
source ~/.zshrc
```
If you would prefer not to disable zsh's special handling of the question mark
character, you can disable it explictly with a backslash in your keypair URLs.
For example:
```bash
solana-keygen pubkey usb://ledger\?key=0
```

View File

@ -6,31 +6,19 @@ secure transaction signing.
## Before You Begin ## Before You Begin
- [Set up a Ledger Nano S with the Solana App](../wallet/ledger-live.md)
- [Install the Solana command-line tools](../cli/install-solana-cli-tools.md) - [Install the Solana command-line tools](../cli/install-solana-cli-tools.md)
- [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 Ledger Live](https://support.ledger.com/hc/en-us/articles/360006395553/) software on your computer
## Install the Solana App on Ledger Nano S
1. Open Ledger Live
2. Click Experimental Features and enable Developer Mode
3. Open the Manager in Ledger Live
4. Connect your Ledger device via USB and enter your pin to unlock it
5. When prompted, approve the manager on your device
6. Find Solana in the app catalog and click Install
7. An installation window appears and your device will display Processing…
8. The app installation is confirmed
9. Close Ledger Live
## Use Ledger Device with Solana CLI ## Use Ledger Device with Solana CLI
1. Ensure the Ledger Live application is closed 1. Ensure the Ledger Live application is closed
2. Plug your Ledger device into your computer's USB port 2. Plug your Ledger device into your computer's USB port
3. Enter your pin and start the Solana app on the Ledger device 3. Enter your pin and start the Solana app on the Ledger device
4. On your computer, run: 4. Press both buttons to advance past the "Pending Ledger review" screen
5. Ensure the screen reads "Application is ready"
6. On your computer, run:
```text ```bash
solana-keygen pubkey usb://ledger solana-keygen pubkey usb://ledger
``` ```
@ -41,7 +29,7 @@ computer, you can use your wallet key to specify which Ledger hardware wallet
you want to use. Run the same command again, but this time, with its fully you want to use. Run the same command again, but this time, with its fully
qualified URL: qualified URL:
```text ```bash
solana-keygen pubkey usb://ledger/<WALLET_ID> solana-keygen pubkey usb://ledger/<WALLET_ID>
``` ```
@ -57,4 +45,4 @@ anywhere you see an option or argument that accepts a `<KEYPAIR>`.
## Support ## Support
Email maintainers@solana.com Check out our [Wallet Support Page](../wallet/support.md) for ways to get help.

View File

@ -16,6 +16,7 @@ line tools](../remote-wallet/ledger.md).
- Order a [Nano S from Ledger](https://shop.ledger.com/products/ledger-nano-s) - Order a [Nano S from Ledger](https://shop.ledger.com/products/ledger-nano-s)
- Follow the instructions for device setup included in the package, - Follow the instructions for device setup included in the package,
or [Ledger's Start page](https://www.ledger.com/start/) or [Ledger's Start page](https://www.ledger.com/start/)
- [Install the latest device firmware](https://support.ledgerwallet.com/hc/en-us/articles/360002731113-Update-Ledger-Nano-S-firmware)
##Install Ledger Live ##Install Ledger Live
- Install [Ledger Live desktop software](https://www.ledger.com/ledger-live/), - Install [Ledger Live desktop software](https://www.ledger.com/ledger-live/),
@ -30,13 +31,20 @@ line tools](../remote-wallet/ledger.md).
- Open Ledger Live - Open Ledger Live
- Currently Ledger Live needs to be in "Developer Mode" - Currently Ledger Live needs to be in "Developer Mode"
(Settings > Experimental Features > Developer Mode) to see our app. (Settings > Experimental Features > Developer Mode) to see our app.
![Enabling Developer Mode](../.gitbook/assets/ledger-live-enable-developer-mode.png)
- Go to Manager in the app and find "Solana" in the App Catalog and - Go to Manager in the app and find "Solana" in the App Catalog and
click Install click Install
- Make sure your device is plugged in via USB and is unlocked with its PIN - Make sure your device is plugged in via USB and is unlocked with its PIN
- You may be prompted on the Nano S to confirm the install of Solana App - You may be prompted on the Nano S to confirm the install of Solana App
- "Solana" should now show as "Installed" in the Ledger Live Manager - "Solana" should now show as "Installed" in the Ledger Live Manager
![Installed Solana App in Manager](../.gitbook/assets/ledger-live-install-solana-app.png)
##Interact with Solana network ##Interact with Solana network
- To interact with your Ledger wallet on our live network, please see our - To interact with your Ledger wallet on our live network, please see our
instructions on how to [use a Ledger Nano S with the Solana command instructions on how to
line tools](../remote-wallet/ledger.md). [use a Ledger Nano S with the Solana command line tools](../remote-wallet/ledger.md).
## Support
Check out our [Wallet Support Page](../wallet/support.md) for ways to get help.

View File

@ -0,0 +1,13 @@
# Support / Troubleshooting
If you have questions or are having trouble setting up or using your wallet
of choice, please make sure you've read through all the relevant pages in our
[Wallet Guide](README.md). The Solana team is working hard to support new
features on popular wallets, and we do our best to keep our documents up to date
with the latest available features.
If you have questions after reading the docs, feel free to reach out to us on
our [Telegram](https://t.me/solanaio).
For **technical support**, reach out to us on
[Discord](https://discordapp.com/invite/pquxPsq), using the #wallet-support
channel in our Community section.

View File

@ -1,16 +1,56 @@
#Trust Wallet # Trust Wallet
Trust Wallet is an app for your smartphone or tablet and is the fastest and
simplest way for most users to get started with a Solana wallet.
**NOTE: Trust Wallet currently only supports Solana on the iOS version of its ## Install Trust Wallet
app. Support for Android is coming very very soon!**
##Set Up Trust Wallet #### iOS
- Open the App Store or Play Store - Open the App Store
- Download “Trust: Crypto & Bitcoin Wallet” from Six Days LLC - Download “Trust: Crypto & Bitcoin Wallet” from Six Days LLC
- Requires iOS 13.0 or higher - Requires iOS 13.0 or higher
- Requires Android 6.0 or higher
- Open Trust Wallet and follow the app prompts to get started - Open Trust Wallet and follow the app prompts to get started
##Add Solana (SOL) tokens to your wallet ***
#### Android
**NOTE: At this time, Solana's SOL tokens are only supported in the Beta version
of Trust Wallet for Android. The following steps explain how to install this
Beta version to start using your Solana wallet. Check back here or check the
latest official Trust Wallet release notes for when support is added to their
official Android release.**
- Open the Play Store
- Download the official version of Trust Wallet
- “Trust: Crypto & Bitcoin Wallet” from Six Days LLC
- Requires Android 6.0 or higher
![Install the Official Version of Trust Wallet](../.gitbook/assets/install-official-trust-wallet.png)
##### Enable Beta version of Trust Wallet
- Make sure you already have the official version installed
- Open Play Store and view Trust Wallet's app page
- Scroll down to the bottom to the "Beta" section and tap "Join"
- It may take a few minutes for your device to get access to the Beta version
![Join the Beta program](../.gitbook/assets/join-beta-trust-wallet.png)
##### Upgrade to the Beta version
- Open Play Store .
- Tap Menu --> My apps and games --> Beta.
- Tap Trust Wallet
- Tap Upgrade when brought back to the Trust Wallet (Beta) install page
![Find Beta app you've joined](../.gitbook/assets/find-beta-apps.png)
***
![Upgrade to Trust Wallet Beta](../.gitbook/assets/update-trust-wallet-to-beta.png)
##### Beta Install Support for Android
- [Google's Official Help for Installing Beta Versions of Apps](https://support.google.com/googleplay/answer/7003180?hl=en)
## Add Solana (SOL) tokens to your wallet
- From the main page, go to the “Tokens” tab at the top of the screen - From the main page, go to the “Tokens” tab at the top of the screen
- Tap the “+” icon at the top right corner - Tap the “+” icon at the top right corner
- Search for “Solana” in the search page, and when the “Solana SOL” token is - Search for “Solana” in the search page, and when the “Solana SOL” token is
@ -19,7 +59,7 @@ shown, slide the slider to enable this token.
[Trust Wallet Official Docs: How to Add or Remove a Coin](https://community.trustwallet.com/t/how-to-add-or-remove-a-coin/896) [Trust Wallet Official Docs: How to Add or Remove a Coin](https://community.trustwallet.com/t/how-to-add-or-remove-a-coin/896)
##Receiving SOL tokens ## Receiving SOL tokens
- To receive SOL tokens that youve purchased or earned, you need to send your - To receive SOL tokens that youve purchased or earned, you need to send your
Receive Address to whoever is sending you tokens. Receive Address to whoever is sending you tokens.
- Tap “Receive” to view a QR code and your text address, which is a long string - Tap “Receive” to view a QR code and your text address, which is a long string
@ -32,6 +72,10 @@ to that address, **those tokens will be lost forever**.
[Trust Wallet Official Docs: How to Find my Receiving Address](https://community.trustwallet.com/t/how-to-find-my-receiving-address/2006) [Trust Wallet Official Docs: How to Find my Receiving Address](https://community.trustwallet.com/t/how-to-find-my-receiving-address/2006)
##Troubleshooting ## Troubleshooting
If you are having trouble setting up your Trust Wallet app, check out their If you are having trouble setting up your Trust Wallet app, check out their
[Community Help Center](https://community.trustwallet.com/c/helpcenter) [Community Help Center](https://community.trustwallet.com/c/helpcenter)
## Support
Check out our [Wallet Support Page](../wallet/support.md) for ways to get help.

View File

@ -2,7 +2,7 @@
authors = ["Solana Maintainers <maintainers@solana.com>"] authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018" edition = "2018"
name = "solana-dos" name = "solana-dos"
version = "1.1.0" version = "1.1.1"
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/"
@ -13,7 +13,7 @@ clap = "2.33.0"
log = "0.4.8" log = "0.4.8"
rand = "0.6.5" rand = "0.6.5"
rayon = "1.3.0" rayon = "1.3.0"
solana-core = { path = "../core", version = "1.1.0" } solana-core = { path = "../core", version = "1.1.1" }
solana-logger = { path = "../logger", version = "1.1.0" } solana-logger = { path = "../logger", version = "1.1.1" }
solana-net-utils = { path = "../net-utils", version = "1.1.0" } solana-net-utils = { path = "../net-utils", version = "1.1.1" }
solana-runtime = { path = "../runtime", version = "1.1.0" } solana-runtime = { path = "../runtime", version = "1.1.1" }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "solana-download-utils" name = "solana-download-utils"
version = "1.1.0" version = "1.1.1"
description = "Solana Download Utils" description = "Solana Download 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"
@ -14,8 +14,8 @@ console = "0.10.0"
indicatif = "0.14.0" indicatif = "0.14.0"
log = "0.4.8" log = "0.4.8"
reqwest = { version = "0.10.4", default-features = false, features = ["blocking", "rustls-tls", "json"] } reqwest = { version = "0.10.4", default-features = false, features = ["blocking", "rustls-tls", "json"] }
solana-sdk = { path = "../sdk", version = "1.1.0" } solana-sdk = { path = "../sdk", version = "1.1.1" }
solana-ledger = { path = "../ledger", version = "1.1.0" } solana-ledger = { path = "../ledger", version = "1.1.1" }
tar = "0.4.26" tar = "0.4.26"
[lib] [lib]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "solana-faucet" name = "solana-faucet"
version = "1.1.0" version = "1.1.1"
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.105" serde = "1.0.105"
serde_derive = "1.0.103" serde_derive = "1.0.103"
solana-clap-utils = { path = "../clap-utils", version = "1.1.0" } solana-clap-utils = { path = "../clap-utils", version = "1.1.1" }
solana-logger = { path = "../logger", version = "1.1.0" } solana-logger = { path = "../logger", version = "1.1.1" }
solana-metrics = { path = "../metrics", version = "1.1.0" } solana-metrics = { path = "../metrics", version = "1.1.1" }
solana-sdk = { path = "../sdk", version = "1.1.0" } solana-sdk = { path = "../sdk", version = "1.1.1" }
tokio = "0.1" tokio = "0.1"
tokio-codec = "0.1" tokio-codec = "0.1"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "solana-genesis-programs" name = "solana-genesis-programs"
version = "1.1.0" version = "1.1.1"
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.1.0" } solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "1.1.1" }
solana-budget-program = { path = "../programs/budget", version = "1.1.0" } solana-budget-program = { path = "../programs/budget", version = "1.1.1" }
solana-config-program = { path = "../programs/config", version = "1.1.0" } solana-config-program = { path = "../programs/config", version = "1.1.1" }
solana-exchange-program = { path = "../programs/exchange", version = "1.1.0" } solana-exchange-program = { path = "../programs/exchange", version = "1.1.1" }
solana-runtime = { path = "../runtime", version = "1.1.0" } solana-runtime = { path = "../runtime", version = "1.1.1" }
solana-sdk = { path = "../sdk", version = "1.1.0" } solana-sdk = { path = "../sdk", version = "1.1.1" }
solana-stake-program = { path = "../programs/stake", version = "1.1.0" } solana-stake-program = { path = "../programs/stake", version = "1.1.1" }
solana-storage-program = { path = "../programs/storage", version = "1.1.0" } solana-storage-program = { path = "../programs/storage", version = "1.1.1" }
solana-vest-program = { path = "../programs/vest", version = "1.1.0" } solana-vest-program = { path = "../programs/vest", version = "1.1.1" }
solana-vote-program = { path = "../programs/vote", version = "1.1.0" } solana-vote-program = { path = "../programs/vote", version = "1.1.1" }
[lib] [lib]
crate-type = ["lib"] crate-type = ["lib"]

View File

@ -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.1.0" version = "1.1.1"
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.105" serde = "1.0.105"
serde_json = "1.0.48" serde_json = "1.0.48"
serde_yaml = "0.8.11" serde_yaml = "0.8.11"
solana-clap-utils = { path = "../clap-utils", version = "1.1.0" } solana-clap-utils = { path = "../clap-utils", version = "1.1.1" }
solana-genesis-programs = { path = "../genesis-programs", version = "1.1.0" } solana-genesis-programs = { path = "../genesis-programs", version = "1.1.1" }
solana-ledger = { path = "../ledger", version = "1.1.0" } solana-ledger = { path = "../ledger", version = "1.1.1" }
solana-sdk = { path = "../sdk", version = "1.1.0" } solana-sdk = { path = "../sdk", version = "1.1.1" }
solana-stake-program = { path = "../programs/stake", version = "1.1.0" } solana-stake-program = { path = "../programs/stake", version = "1.1.1" }
solana-storage-program = { path = "../programs/storage", version = "1.1.0" } solana-storage-program = { path = "../programs/storage", version = "1.1.1" }
solana-vote-program = { path = "../programs/vote", version = "1.1.0" } solana-vote-program = { path = "../programs/vote", version = "1.1.1" }
tempfile = "3.1.0" tempfile = "3.1.0"
[[bin]] [[bin]]

View File

@ -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.1.0" version = "1.1.1"
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.1.0" } solana-clap-utils = { path = "../clap-utils", version = "1.1.1" }
solana-core = { path = "../core", version = "1.1.0" } solana-core = { path = "../core", version = "1.1.1" }
solana-client = { path = "../client", version = "1.1.0" } solana-client = { path = "../client", version = "1.1.1" }
solana-logger = { path = "../logger", version = "1.1.0" } solana-logger = { path = "../logger", version = "1.1.1" }
solana-net-utils = { path = "../net-utils", version = "1.1.0" } solana-net-utils = { path = "../net-utils", version = "1.1.1" }
solana-sdk = { path = "../sdk", version = "1.1.0" } solana-sdk = { path = "../sdk", version = "1.1.1" }

View File

@ -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.1.0" version = "1.1.1"
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.4", default-features = false, features = ["blocking"
serde = "1.0.105" serde = "1.0.105"
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.1.0" } solana-clap-utils = { path = "../clap-utils", version = "1.1.1" }
solana-client = { path = "../client", version = "1.1.0" } solana-client = { path = "../client", version = "1.1.1" }
solana-config-program = { path = "../programs/config", version = "1.1.0" } solana-config-program = { path = "../programs/config", version = "1.1.1" }
solana-logger = { path = "../logger", version = "1.1.0" } solana-logger = { path = "../logger", version = "1.1.1" }
solana-sdk = { path = "../sdk", version = "1.1.0" } solana-sdk = { path = "../sdk", version = "1.1.1" }
semver = "0.9.0" semver = "0.9.0"
tar = "0.4.26" tar = "0.4.26"
tempdir = "0.3.7" tempdir = "0.3.7"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "solana-keygen" name = "solana-keygen"
version = "1.1.0" version = "1.1.1"
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.1.0" } solana-clap-utils = { path = "../clap-utils", version = "1.1.1" }
solana-cli-config = { path = "../cli-config", version = "1.1.0" } solana-cli-config = { path = "../cli-config", version = "1.1.1" }
solana-remote-wallet = { path = "../remote-wallet", version = "1.1.0" } solana-remote-wallet = { path = "../remote-wallet", version = "1.1.1" }
solana-sdk = { path = "../sdk", version = "1.1.0" } solana-sdk = { path = "../sdk", version = "1.1.1" }
tiny-bip39 = "0.7.0" tiny-bip39 = "0.7.0"
[[bin]] [[bin]]

View File

@ -8,7 +8,7 @@ use num_cpus;
use solana_clap_utils::{ use solana_clap_utils::{
input_validators::is_derivation, input_validators::is_derivation,
keypair::{ keypair::{
keypair_from_seed_phrase, prompt_passphrase, signer_from_path, check_for_usb, keypair_from_seed_phrase, prompt_passphrase, signer_from_path,
SKIP_SEED_PHRASE_VALIDATION_ARG, SKIP_SEED_PHRASE_VALIDATION_ARG,
}, },
DisplayError, DisplayError,
@ -407,7 +407,11 @@ fn do_main(matches: &ArgMatches<'_>) -> Result<(), Box<dyn error::Error>> {
Config::default() Config::default()
}; };
let wallet_manager = maybe_wallet_manager()?; let wallet_manager = if check_for_usb(std::env::args()) {
maybe_wallet_manager()?
} else {
None
};
match matches.subcommand() { match matches.subcommand() {
("pubkey", Some(matches)) => { ("pubkey", Some(matches)) => {

View File

@ -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.1.0" version = "1.1.1"
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,13 +14,13 @@ clap = "2.33.0"
histogram = "*" histogram = "*"
serde_json = "1.0.48" serde_json = "1.0.48"
serde_yaml = "0.8.11" serde_yaml = "0.8.11"
solana-clap-utils = { path = "../clap-utils", version = "1.1.0" } solana-clap-utils = { path = "../clap-utils", version = "1.1.1" }
solana-ledger = { path = "../ledger", version = "1.1.0" } solana-ledger = { path = "../ledger", version = "1.1.1" }
solana-logger = { path = "../logger", version = "1.1.0" } solana-logger = { path = "../logger", version = "1.1.1" }
solana-runtime = { path = "../runtime", version = "1.1.0" } solana-runtime = { path = "../runtime", version = "1.1.1" }
solana-sdk = { path = "../sdk", version = "1.1.0" } solana-sdk = { path = "../sdk", version = "1.1.1" }
solana-vote-program = { path = "../programs/vote", version = "1.1.0" } solana-vote-program = { path = "../programs/vote", version = "1.1.1" }
solana-stake-program = { path = "../programs/stake", version = "1.1.0" } solana-stake-program = { path = "../programs/stake", version = "1.1.1" }
tempfile = "3.1.0" tempfile = "3.1.0"
[dev-dependencies] [dev-dependencies]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "solana-ledger" name = "solana-ledger"
version = "1.1.0" version = "1.1.1"
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"
@ -28,19 +28,19 @@ reed-solomon-erasure = { package = "solana-reed-solomon-erasure", version = "4.0
regex = "1.3.6" regex = "1.3.6"
serde = "1.0.105" serde = "1.0.105"
serde_bytes = "0.11.3" serde_bytes = "0.11.3"
solana-transaction-status = { path = "../transaction-status", version = "1.1.0" } solana-transaction-status = { path = "../transaction-status", version = "1.1.1" }
solana-genesis-programs = { path = "../genesis-programs", version = "1.1.0" } solana-genesis-programs = { path = "../genesis-programs", version = "1.1.1" }
solana-logger = { path = "../logger", version = "1.1.0" } solana-logger = { path = "../logger", version = "1.1.1" }
solana-measure = { path = "../measure", version = "1.1.0" } solana-measure = { path = "../measure", version = "1.1.1" }
solana-merkle-tree = { path = "../merkle-tree", version = "1.1.0" } solana-merkle-tree = { path = "../merkle-tree", version = "1.1.1" }
solana-metrics = { path = "../metrics", version = "1.1.0" } solana-metrics = { path = "../metrics", version = "1.1.1" }
solana-perf = { path = "../perf", version = "1.1.0" } solana-perf = { path = "../perf", version = "1.1.1" }
ed25519-dalek = "1.0.0-pre.1" ed25519-dalek = "1.0.0-pre.1"
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.1.0" } solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.1.1" }
solana-runtime = { path = "../runtime", version = "1.1.0" } solana-runtime = { path = "../runtime", version = "1.1.1" }
solana-sdk = { path = "../sdk", version = "1.1.0" } solana-sdk = { path = "../sdk", version = "1.1.1" }
solana-stake-program = { path = "../programs/stake", version = "1.1.0" } solana-stake-program = { path = "../programs/stake", version = "1.1.1" }
solana-vote-program = { path = "../programs/vote", version = "1.1.0" } solana-vote-program = { path = "../programs/vote", version = "1.1.1" }
symlink = "0.1.0" symlink = "0.1.0"
tar = "0.4.26" tar = "0.4.26"
thiserror = "1.0" thiserror = "1.0"
@ -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.1.0" } solana-budget-program = { path = "../programs/budget", version = "1.1.1" }
[lib] [lib]
crate-type = ["lib"] crate-type = ["lib"]

View File

@ -53,7 +53,7 @@ pub enum BlockstoreError {
FsExtraError(#[from] fs_extra::error::Error), FsExtraError(#[from] fs_extra::error::Error),
SlotCleanedUp, SlotCleanedUp,
} }
pub(crate) type Result<T> = std::result::Result<T, BlockstoreError>; pub type Result<T> = std::result::Result<T, BlockstoreError>;
impl std::fmt::Display for BlockstoreError { impl std::fmt::Display for BlockstoreError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {

View File

@ -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.1.0" version = "1.1.1"
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,24 +12,24 @@ homepage = "https://solana.com/"
itertools = "0.9.0" itertools = "0.9.0"
log = "0.4.8" log = "0.4.8"
rand = "0.6.5" rand = "0.6.5"
solana-archiver-lib = { path = "../archiver-lib", version = "1.1.0" } solana-archiver-lib = { path = "../archiver-lib", version = "1.1.1" }
solana-config-program = { path = "../programs/config", version = "1.1.0" } solana-config-program = { path = "../programs/config", version = "1.1.1" }
solana-core = { path = "../core", version = "1.1.0" } solana-core = { path = "../core", version = "1.1.1" }
solana-client = { path = "../client", version = "1.1.0" } solana-client = { path = "../client", version = "1.1.1" }
solana-download-utils = { path = "../download-utils", version = "1.1.0" } solana-download-utils = { path = "../download-utils", version = "1.1.1" }
solana-faucet = { path = "../faucet", version = "1.1.0" } solana-faucet = { path = "../faucet", version = "1.1.1" }
solana-exchange-program = { path = "../programs/exchange", version = "1.1.0" } solana-exchange-program = { path = "../programs/exchange", version = "1.1.1" }
solana-genesis-programs = { path = "../genesis-programs", version = "1.1.0" } solana-genesis-programs = { path = "../genesis-programs", version = "1.1.1" }
solana-ledger = { path = "../ledger", version = "1.1.0" } solana-ledger = { path = "../ledger", version = "1.1.1" }
solana-logger = { path = "../logger", version = "1.1.0" } solana-logger = { path = "../logger", version = "1.1.1" }
solana-runtime = { path = "../runtime", version = "1.1.0" } solana-runtime = { path = "../runtime", version = "1.1.1" }
solana-sdk = { path = "../sdk", version = "1.1.0" } solana-sdk = { path = "../sdk", version = "1.1.1" }
solana-stake-program = { path = "../programs/stake", version = "1.1.0" } solana-stake-program = { path = "../programs/stake", version = "1.1.1" }
solana-storage-program = { path = "../programs/storage", version = "1.1.0" } solana-storage-program = { path = "../programs/storage", version = "1.1.1" }
solana-vest-program = { path = "../programs/vest", version = "1.1.0" } solana-vest-program = { path = "../programs/vest", version = "1.1.1" }
solana-vote-program = { path = "../programs/vote", version = "1.1.0" } solana-vote-program = { path = "../programs/vote", version = "1.1.1" }
tempfile = "3.1.0" tempfile = "3.1.0"
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.1.0" } solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.1.1" }
[dev-dependencies] [dev-dependencies]
assert_matches = "1.3.0" assert_matches = "1.3.0"

View File

@ -219,7 +219,7 @@ impl LocalCluster {
&leader_keypair, &leader_keypair,
&leader_ledger_path, &leader_ledger_path,
&leader_voting_keypair.pubkey(), &leader_voting_keypair.pubkey(),
&leader_voting_keypair, vec![leader_voting_keypair.clone()],
&leader_storage_keypair, &leader_storage_keypair,
None, None,
true, true,
@ -367,7 +367,7 @@ impl LocalCluster {
&validator_keypair, &validator_keypair,
&ledger_path, &ledger_path,
&voting_keypair.pubkey(), &voting_keypair.pubkey(),
&voting_keypair, vec![voting_keypair.clone()],
&storage_keypair, &storage_keypair,
Some(&self.entry_point_info), Some(&self.entry_point_info),
true, true,
@ -687,7 +687,7 @@ impl Cluster for LocalCluster {
&validator_info.keypair, &validator_info.keypair,
&validator_info.ledger_path, &validator_info.ledger_path,
&validator_info.voting_keypair.pubkey(), &validator_info.voting_keypair.pubkey(),
&validator_info.voting_keypair, vec![validator_info.voting_keypair.clone()],
&validator_info.storage_keypair, &validator_info.storage_keypair,
entry_point_info, entry_point_info,
true, true,

View File

@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018" edition = "2018"
name = "solana-log-analyzer" name = "solana-log-analyzer"
description = "The solana cluster network analysis tool" description = "The solana cluster network analysis tool"
version = "1.1.0" version = "1.1.1"
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,8 +14,8 @@ byte-unit = "3.0.3"
clap = "2.33.0" clap = "2.33.0"
serde = "1.0.105" serde = "1.0.105"
serde_json = "1.0.48" serde_json = "1.0.48"
solana-clap-utils = { path = "../clap-utils", version = "1.1.0" } solana-clap-utils = { path = "../clap-utils", version = "1.1.1" }
solana-logger = { path = "../logger", version = "1.1.0" } solana-logger = { path = "../logger", version = "1.1.1" }
[[bin]] [[bin]]
name = "solana-log-analyzer" name = "solana-log-analyzer"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "solana-logger" name = "solana-logger"
version = "1.1.0" version = "1.1.1"
description = "Solana Logger" description = "Solana Logger"
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"

View File

@ -1,7 +1,7 @@
[package] [package]
name = "solana-measure" name = "solana-measure"
description = "Blockchain, Rebuilt for Scale" description = "Blockchain, Rebuilt for Scale"
version = "1.1.0" version = "1.1.1"
documentation = "https://docs.rs/solana" documentation = "https://docs.rs/solana"
homepage = "https://solana.com/" homepage = "https://solana.com/"
readme = "../README.md" readme = "../README.md"
@ -12,8 +12,8 @@ edition = "2018"
[dependencies] [dependencies]
log = "0.4.8" log = "0.4.8"
solana-sdk = { path = "../sdk", version = "1.1.0" } solana-sdk = { path = "../sdk", version = "1.1.1" }
solana-metrics = { path = "../metrics", version = "1.1.0" } solana-metrics = { path = "../metrics", version = "1.1.1" }
[target."cfg(unix)".dependencies] [target."cfg(unix)".dependencies]
jemallocator = "0.3.2" jemallocator = "0.3.2"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "solana-merkle-tree" name = "solana-merkle-tree"
version = "1.1.0" version = "1.1.1"
description = "Solana Merkle Tree" description = "Solana Merkle Tree"
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"
@ -9,7 +9,7 @@ homepage = "https://solana.com/"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
solana-sdk = { path = "../sdk", version = "1.1.0" } solana-sdk = { path = "../sdk", version = "1.1.1" }
fast-math = "0.1" fast-math = "0.1"
[dev-dependencies] [dev-dependencies]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "solana-metrics" name = "solana-metrics"
version = "1.1.0" version = "1.1.1"
description = "Solana Metrics" description = "Solana Metrics"
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"
@ -14,7 +14,7 @@ gethostname = "0.2.1"
lazy_static = "1.4.0" lazy_static = "1.4.0"
log = "0.4.8" log = "0.4.8"
reqwest = { version = "0.10.4", default-features = false, features = ["blocking", "rustls-tls", "json"] } reqwest = { version = "0.10.4", default-features = false, features = ["blocking", "rustls-tls", "json"] }
solana-sdk = { path = "../sdk", version = "1.1.0" } solana-sdk = { path = "../sdk", version = "1.1.1" }
[dev-dependencies] [dev-dependencies]
rand = "0.6.5" rand = "0.6.5"

View File

@ -6928,7 +6928,7 @@
], ],
"orderByTime": "ASC", "orderByTime": "ASC",
"policy": "default", "policy": "default",
"query": "SELECT last(\"repair-highest-slot\") AS \"slot\" FROM \"$testnet\".\"autogen\".\"serve_repair-repair_highest\" WHERE host_id::tag =~ /$hostid/ AND $timeFilter GROUP BY time($__interval)", "query": "SELECT last(\"repair-highest-slot\") AS \"slot\" FROM \"$testnet\".\"autogen\".\"serve_repair-repair\" WHERE host_id::tag =~ /$hostid/ AND $timeFilter GROUP BY time($__interval)",
"rawQuery": true, "rawQuery": true,
"refId": "C", "refId": "C",
"resultFormat": "time_series", "resultFormat": "time_series",
@ -6965,7 +6965,7 @@
], ],
"orderByTime": "ASC", "orderByTime": "ASC",
"policy": "default", "policy": "default",
"query": "SELECT last(\"repair-highest-ix\") AS \"ix\" FROM \"$testnet\".\"autogen\".\"serve_repair-repair_highest\" WHERE host_id::tag =~ /$hostid/ AND $timeFilter GROUP BY time($__interval)", "query": "SELECT last(\"repair-highest-ix\") AS \"ix\" FROM \"$testnet\".\"autogen\".\"serve_repair-repair\" WHERE host_id::tag =~ /$hostid/ AND $timeFilter GROUP BY time($__interval)",
"rawQuery": true, "rawQuery": true,
"refId": "A", "refId": "A",
"resultFormat": "time_series", "resultFormat": "time_series",
@ -7245,7 +7245,7 @@
], ],
"orderByTime": "ASC", "orderByTime": "ASC",
"policy": "default", "policy": "default",
"query": "SELECT last(\"repair-orphan\") AS \"slot\" FROM \"$testnet\".\"autogen\".\"serve_repair-repair_orphan\" WHERE host_id::tag =~ /$hostid/ AND $timeFilter GROUP BY time($__interval)", "query": "SELECT last(\"repair-orphan\") AS \"slot\" FROM \"$testnet\".\"autogen\".\"serve_repair-repair\" WHERE host_id::tag =~ /$hostid/ AND $timeFilter GROUP BY time($__interval)",
"rawQuery": true, "rawQuery": true,
"refId": "C", "refId": "C",
"resultFormat": "time_series", "resultFormat": "time_series",
@ -10270,4 +10270,4 @@
"title": "Cluster Telemetry (edge)", "title": "Cluster Telemetry (edge)",
"uid": "monitor-edge", "uid": "monitor-edge",
"version": 2 "version": 2
} }

View File

@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018" edition = "2018"
name = "solana-net-shaper" name = "solana-net-shaper"
description = "The solana cluster network shaping tool" description = "The solana cluster network shaping tool"
version = "1.1.0" version = "1.1.1"
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/"
@ -13,8 +13,8 @@ publish = false
clap = "2.33.0" clap = "2.33.0"
serde = "1.0.105" serde = "1.0.105"
serde_json = "1.0.48" serde_json = "1.0.48"
solana-clap-utils = { path = "../clap-utils", version = "1.1.0" } solana-clap-utils = { path = "../clap-utils", version = "1.1.1" }
solana-logger = { path = "../logger", version = "1.1.0" } solana-logger = { path = "../logger", version = "1.1.1" }
rand = "0.6.5" rand = "0.6.5"
[[bin]] [[bin]]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "solana-net-utils" name = "solana-net-utils"
version = "1.1.0" version = "1.1.1"
description = "Solana Network Utilities" description = "Solana Network Utilities"
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 @@ rand = "0.6.1"
serde = "1.0.105" serde = "1.0.105"
serde_derive = "1.0.103" serde_derive = "1.0.103"
socket2 = "0.3.11" socket2 = "0.3.11"
solana-clap-utils = { path = "../clap-utils", version = "1.1.0" } solana-clap-utils = { path = "../clap-utils", version = "1.1.1" }
solana-logger = { path = "../logger", version = "1.1.0" } solana-logger = { path = "../logger", version = "1.1.1" }
tokio = "0.1" tokio = "0.1"
tokio-codec = "0.1" tokio-codec = "0.1"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "solana-perf" name = "solana-perf"
version = "1.1.0" version = "1.1.1"
description = "Solana Performance APIs" description = "Solana Performance 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"
@ -17,11 +17,11 @@ serde = "1.0.105"
dlopen_derive = "0.1.4" dlopen_derive = "0.1.4"
lazy_static = "1.4.0" lazy_static = "1.4.0"
log = "0.4.8" log = "0.4.8"
solana-sdk = { path = "../sdk", version = "1.1.0" } solana-sdk = { path = "../sdk", version = "1.1.1" }
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.1.0" } solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.1.1" }
solana-budget-program = { path = "../programs/budget", version = "1.1.0" } solana-budget-program = { path = "../programs/budget", version = "1.1.1" }
solana-logger = { path = "../logger", version = "1.1.0" } solana-logger = { path = "../logger", version = "1.1.1" }
solana-metrics = { path = "../metrics", version = "1.1.0" } solana-metrics = { path = "../metrics", version = "1.1.1" }
[lib] [lib]
name = "solana_perf" name = "solana_perf"

View File

@ -1,7 +1,7 @@
[package] [package]
name = "solana-bpf-programs" name = "solana-bpf-programs"
description = "Blockchain, Rebuilt for Scale" description = "Blockchain, Rebuilt for Scale"
version = "1.1.0" version = "1.1.1"
documentation = "https://docs.rs/solana" documentation = "https://docs.rs/solana"
homepage = "https://solana.com/" homepage = "https://solana.com/"
readme = "README.md" readme = "README.md"
@ -22,10 +22,10 @@ walkdir = "2"
bincode = "1.1.4" bincode = "1.1.4"
byteorder = "1.3.2" byteorder = "1.3.2"
elf = "0.0.10" elf = "0.0.10"
solana-bpf-loader-program = { path = "../bpf_loader", version = "1.1.0" } solana-bpf-loader-program = { path = "../bpf_loader", version = "1.1.1" }
solana-logger = { path = "../../logger", version = "1.1.0" } solana-logger = { path = "../../logger", version = "1.1.1" }
solana-runtime = { path = "../../runtime", version = "1.1.0" } solana-runtime = { path = "../../runtime", version = "1.1.1" }
solana-sdk = { path = "../../sdk", version = "1.1.0" } solana-sdk = { path = "../../sdk", version = "1.1.1" }
solana_rbpf = "=0.1.25" solana_rbpf = "=0.1.25"
[[bench]] [[bench]]

View File

@ -3,7 +3,7 @@
[package] [package]
name = "solana-bpf-rust-128bit" name = "solana-bpf-rust-128bit"
version = "1.1.0" version = "1.1.1"
description = "Solana BPF test program written in Rust" description = "Solana BPF test program written in Rust"
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 @@ homepage = "https://solana.com/"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
solana-sdk = { path = "../../../../sdk/", version = "1.1.0", default-features = false } solana-sdk = { path = "../../../../sdk/", version = "1.1.1", default-features = false }
solana-bpf-rust-128bit-dep = { path = "../128bit_dep", version = "1.1.0" } solana-bpf-rust-128bit-dep = { path = "../128bit_dep", version = "1.1.1" }
[dev_dependencies] [dev_dependencies]
solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.1.0" } solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.1.1" }
[features] [features]
program = ["solana-sdk/program"] program = ["solana-sdk/program"]

View File

@ -3,7 +3,7 @@
[package] [package]
name = "solana-bpf-rust-128bit-dep" name = "solana-bpf-rust-128bit-dep"
version = "1.1.0" version = "1.1.1"
description = "Solana BPF test program written in Rust" description = "Solana BPF test program written in Rust"
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,10 +12,10 @@ homepage = "https://solana.com/"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
solana-sdk = { path = "../../../../sdk/", version = "1.1.0", default-features = false } solana-sdk = { path = "../../../../sdk/", version = "1.1.1", default-features = false }
[dev_dependencies] [dev_dependencies]
solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.1.0" } solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.1.1" }
[features] [features]
program = ["solana-sdk/program"] program = ["solana-sdk/program"]

View File

@ -3,7 +3,7 @@
[package] [package]
name = "solana-bpf-rust-alloc" name = "solana-bpf-rust-alloc"
version = "1.1.0" version = "1.1.1"
description = "Solana BPF test program written in Rust" description = "Solana BPF test program written in Rust"
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,10 +12,10 @@ homepage = "https://solana.com/"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
solana-sdk = { path = "../../../../sdk/", version = "1.1.0", default-features = false } solana-sdk = { path = "../../../../sdk/", version = "1.1.1", default-features = false }
[dev_dependencies] [dev_dependencies]
solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.1.0" } solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.1.1" }
[features] [features]
program = ["solana-sdk/program"] program = ["solana-sdk/program"]

View File

@ -3,7 +3,7 @@
[package] [package]
name = "solana-bpf-rust-dep-crate" name = "solana-bpf-rust-dep-crate"
version = "1.1.0" version = "1.1.1"
description = "Solana BPF test program written in Rust" description = "Solana BPF test program written in Rust"
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 @@ edition = "2018"
[dependencies] [dependencies]
byteorder = { version = "1", default-features = false } byteorder = { version = "1", default-features = false }
solana-sdk = { path = "../../../../sdk/", version = "1.1.0", default-features = false } solana-sdk = { path = "../../../../sdk/", version = "1.1.1", default-features = false }
[dev_dependencies] [dev_dependencies]
solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.1.0" } solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.1.1" }
[features] [features]
program = ["solana-sdk/program"] program = ["solana-sdk/program"]

View File

@ -3,7 +3,7 @@
[package] [package]
name = "solana-bpf-rust-dup-accounts" name = "solana-bpf-rust-dup-accounts"
version = "1.1.0" version = "1.1.1"
description = "Solana BPF test program written in Rust" description = "Solana BPF test program written in Rust"
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,10 +12,10 @@ homepage = "https://solana.com/"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
solana-sdk = { path = "../../../../sdk/", version = "1.1.0", default-features = false } solana-sdk = { path = "../../../../sdk/", version = "1.1.1", default-features = false }
[dev_dependencies] [dev_dependencies]
solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.1.0" } solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.1.1" }
[features] [features]
program = ["solana-sdk/program"] program = ["solana-sdk/program"]

View File

@ -3,7 +3,7 @@
[package] [package]
name = "solana-bpf-rust-error-handling" name = "solana-bpf-rust-error-handling"
version = "1.1.0" version = "1.1.1"
description = "Solana BPF test program written in Rust" description = "Solana BPF test program written in Rust"
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"
@ -14,11 +14,11 @@ edition = "2018"
[dependencies] [dependencies]
num-derive = "0.2" num-derive = "0.2"
num-traits = "0.2" num-traits = "0.2"
solana-sdk = { path = "../../../../sdk/", version = "1.1.0", default-features = false } solana-sdk = { path = "../../../../sdk/", version = "1.1.1", default-features = false }
thiserror = "1.0" thiserror = "1.0"
[dev_dependencies] [dev_dependencies]
solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.1.0" } solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.1.1" }
[features] [features]
program = ["solana-sdk/program"] program = ["solana-sdk/program"]

View File

@ -3,7 +3,7 @@
[package] [package]
name = "solana-bpf-rust-external-spend" name = "solana-bpf-rust-external-spend"
version = "1.1.0" version = "1.1.1"
description = "Solana BPF test program written in Rust" description = "Solana BPF test program written in Rust"
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,10 +12,10 @@ homepage = "https://solana.com/"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
solana-sdk = { path = "../../../../sdk/", version = "1.1.0", default-features = false } solana-sdk = { path = "../../../../sdk/", version = "1.1.1", default-features = false }
[dev_dependencies] [dev_dependencies]
solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.1.0" } solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.1.1" }
[features] [features]
program = ["solana-sdk/program"] program = ["solana-sdk/program"]

View File

@ -3,7 +3,7 @@
[package] [package]
name = "solana-bpf-rust-iter" name = "solana-bpf-rust-iter"
version = "1.1.0" version = "1.1.1"
description = "Solana BPF test program written in Rust" description = "Solana BPF test program written in Rust"
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,10 +12,10 @@ homepage = "https://solana.com/"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
solana-sdk = { path = "../../../../sdk/", version = "1.1.0", default-features = false } solana-sdk = { path = "../../../../sdk/", version = "1.1.1", default-features = false }
[dev_dependencies] [dev_dependencies]
solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.1.0" } solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.1.1" }
[features] [features]
program = ["solana-sdk/program"] program = ["solana-sdk/program"]

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