Compare commits

...

62 Commits

Author SHA1 Message Date
mergify[bot]
f26f18d29d Don't unwrap on session new (#9531)
automerge
2020-04-16 10:05:41 -07:00
mergify[bot]
9b58d72f52 Rpc: Speed up getBlockTime (#9510) (#9513)
automerge
2020-04-16 00:19:50 -07:00
Michael Vines
0c885d6a04 Default to RUST_BACKTRACE=1 for more informative validator logs
(cherry picked from commit 4ac15e68cf)
2020-04-15 22:46:24 -07:00
Michael Vines
70b51c0a5a Pacify shellcheck 2020-04-15 17:53:23 -07:00
Michael Vines
560660ae11 Always run shellcheck 2020-04-15 17:53:03 -07:00
Michael Vines
5436855b67 Update build-cli-usage.sh 2020-04-15 17:49:15 -07:00
Michael Vines
7351e5ed2c Use $rust_stable
(cherry picked from commit d567799d43)

# Conflicts:
#	docs/build-cli-usage.sh
2020-04-15 17:49:15 -07:00
mergify[bot]
7ee993fadf RPC: Add health check URI (bp #9499) (#9504)
automerge
2020-04-15 12:31:08 -07:00
sakridge
9bf459e82f Fix race in multi_bind_in_range (#9493)
(cherry picked from commit ee72714c08)
2020-04-14 17:59:15 -07:00
sakridge
545090ff17 limit test jobs to 16 to prevent OOM (#9500)
(cherry picked from commit 2b2b2cac1f)
2020-04-14 17:52:05 -07:00
Michael Vines
bd8074507f Bump version to v1.0.18 2020-04-14 09:58:33 -07:00
Ryo Onodera
cfc7b22c4c Use type alias 2020-04-13 21:12:44 -07:00
Ryo Onodera
5f1c637508 Conditionally change max_age 2020-04-13 21:12:44 -07:00
Ryo Onodera
d888e0a6d7 Use same max_age regardless of leader/not-leader 2020-04-13 21:12:44 -07:00
Michael Vines
10e808e1bd Fail coverage faster in CI 2020-04-13 21:09:26 -07:00
Michael Vines
d9b03ca38b Assume json_rpc_url can be upgrade to a websocket if no port is supplied
(cherry picked from commit bcfadd6085)
2020-04-13 20:32:26 -07:00
Michael Vines
608d75b348 Unfold coverage test failures
(cherry picked from commit d4ea1ec6ad)
2020-04-13 18:08:25 -07:00
Michael Vines
cee3cee4ef Reorder CI jobs to allow for more concurrent PRs
(cherry picked from commit ce027da236)
2020-04-13 13:00:39 -07:00
Dan Albert
09e51323f0 Update buildkite-tests.yml
(cherry picked from commit 92a5a51632)
2020-04-13 11:01:00 -07:00
Michael Vines
da6f702129 Bump version to 1.0.17 2020-04-11 19:32:42 -07:00
mergify[bot]
02a83e7c6e Allow lower shred count (#9410) (#9451)
automerge
2020-04-11 14:49:32 -07:00
sakridge
83263e1737 Calculate account refs fix (#9448) 2020-04-11 12:56:20 -07:00
mergify[bot]
1f7ac22b60 Don't subject authorizing a new stake authority to lockup (#9434) (#9441)
automerge
2020-04-10 17:25:15 -07:00
Michael Vines
747debae56 Cache downloads to speed up CI
(cherry picked from commit b4e00275b2)
2020-04-10 12:25:49 -07:00
mergify[bot]
00b4186469 Improve coverage.sh usability when used locally (#9054) (#9424)
automerge
2020-04-10 05:59:35 -07:00
mergify[bot]
b087dabf4f Rpc: Add getConfirmedSignaturesForAddress (#9407) (#9417)
automerge
2020-04-09 21:20:28 -07:00
mergify[bot]
e00eb0a069 Remove Trust Wallet Beta install instructions (#9396) (#9397)
automerge
2020-04-09 08:52:04 -07:00
mergify[bot]
d4e49ffd06 Rpc: Add getConfirmedTransaction (#9381) (#9392)
automerge
2020-04-09 01:00:34 -07:00
Michael Vines
0f34a190ea Bump version to 1.0.16 2020-04-09 00:05:16 -07:00
mergify[bot]
df2fb8f5b3 Add --no-wait arg to transfer (#9388) (#9390)
automerge
2020-04-08 23:49:40 -07:00
mergify[bot]
80d2a6046b Moar vm.max_map_count (#9389)
automerge
2020-04-08 23:18:30 -07:00
mergify[bot]
24273b826f Add blockstore address-to-signature index (#9367) (#9378)
automerge
2020-04-08 13:55:53 -07:00
mergify[bot]
68d2616e35 stake-monitor: Add 1 SOL grace, to allow for a complaint system account to fund a reasonable number of transactions. (bp #9359) (#9363)
automerge
2020-04-08 11:56:01 -07:00
mergify[bot]
f506d39339 Improve ledger-tool/accounts for easier debuging (#9370) (#9371)
automerge
2020-04-08 11:31:41 -07:00
Michael Vines
bc58c9ec7e Cache solana-perf.tgz to speed up CI (#9360)
automerge

(cherry picked from commit dc91698b3a)
2020-04-07 13:32:13 -07:00
Michael Vines
b57a52cd85 Bump version to 1.0.15 2020-04-07 09:36:47 -07:00
Michael Vines
8631be42ac Add support for monitoring system account balances (#9345)
automerge

(cherry picked from commit 03978ac5a5)
2020-04-06 22:57:47 -07:00
Tyera Eulberg
09367369ef Reinstate commitment param to support old clients (#9324) (#9329)
automerge
2020-04-06 12:21:19 -07:00
mergify[bot]
23b8c95cc4 Update getSignatureStatuses to return historical statuses (#9314) (#9321)
automerge
2020-04-06 04:24:19 -07:00
Tyera Eulberg
e61392b057 Rework TransactionStatus index in blockstore (#9281) (#9304) (#9312)
automerge

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2020-04-05 12:52:15 -06:00
mergify[bot]
7deba20395 Deprecate confirmTransaction, getSignatureStatus, and getSignatureConfirmation (bp #9298) (#9308)
automerge
2020-04-05 02:41:10 -07:00
Michael Vines
274c097f84 RPC: add err field to TransactionStatus, alongside the now deprecated status field (#9296) (#9307)
automerge
2020-04-05 00:21:15 -07:00
Michael Vines
1c7cea1af4 Add stake-monitor 2020-04-03 20:37:28 -07:00
Michael Vines
4406496d2f Add log before opening database
(cherry picked from commit b557b3170e)
2020-04-03 15:10:57 -07:00
sakridge
a15fa4840c Fix repair dos (#9056) 2020-04-03 13:20:39 -07:00
Michael Vines
c9030660d6 Bump version to 1.0.14 2020-04-02 22:42:28 -07:00
Dan Albert
fdeda769d0 Backport wallet doc changes to v1.0 (#9268)
* Add ledger live screenshots and reduce duplicate instructions (#9258)

automerge

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

automerge
2020-04-02 15:37:26 -06:00
mergify[bot]
53edd26578 Add instructions for Trust Wallet Beta for Android (#9261) (#9264)
automerge
2020-04-02 12:25:32 -07:00
Dan Albert
2433cdd6d6 Set checks timeout to 20 minutes 2020-04-02 13:11:03 -06:00
Dan Albert
77b34c278e Do not trigger tests if only docs were modified (#9240) (#9257) 2020-04-02 10:44:51 -06:00
mergify[bot]
90e993fd9a Add epoch subcommand (#9249) (#9254)
automerge
2020-04-01 22:29:18 -07:00
Tyera Eulberg
251054d8c9 Backport confirmations fix. (#9252)
automerge
2020-04-01 19:42:04 -07:00
Tyera Eulberg
d4bb7cec69 Undo breaking rpc removal of getSignatureConfirmation (#9247) 2020-04-01 18:04:11 -06:00
mergify[bot]
a3f6b04345 Add fee-payer option to docs (bp #9230) (#9236)
automerge
2020-04-01 15:11:47 -07:00
Justin Starry
ba4a5053dd Undo getSignatureStatus breaking change, add getSignatureStatuses (#9231)
automerge
2020-04-01 12:21:56 -07:00
mergify[bot]
6b47a259c3 Add a support page for wallet docs (#9229) (#9234)
automerge
2020-04-01 11:22:50 -07:00
mergify[bot]
c9ec13cf1f Tune udp buffers and vmmap immediately (#9194) (#9216)
automerge
2020-04-01 00:52:50 -07:00
mergify[bot]
906a6ab837 Fix error with account hash list getting too big for gossip (#9197) (#9214)
automerge
2020-03-31 23:27:18 -07:00
mergify[bot]
d0e478a9f8 Fix panic (#9195) (#9208)
automerge
2020-03-31 22:03:46 -07:00
mergify[bot]
b560b64d33 Remove unecessary security exception and add a new one (bp #9200) (#9205)
* Remove unecessary exception and add a new one (#9200)

(cherry picked from commit 62e12e3af5)

# Conflicts:
#	ci/test-checks.sh

* Update test-checks.sh

Co-authored-by: sakridge <sakridge@gmail.com>
Co-authored-by: Michael Vines <mvines@gmail.com>
2020-03-31 19:50:04 -07:00
mergify[bot]
057af41716 Fix links (#9184) (#9187)
automerge
2020-03-31 09:57:23 -07:00
Michael Vines
a44b8abd48 Bump version to v1.0.13 2020-03-30 23:05:41 -07:00
153 changed files with 4825 additions and 1654 deletions

733
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -48,6 +48,7 @@ members = [
"sdk",
"sdk-c",
"scripts",
"stake-monitor",
"sys-tuner",
"transaction-status",
"upload-perf",

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-archiver-lib"
version = "1.0.12"
version = "1.0.18"
description = "Solana Archiver Library"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@@ -15,22 +15,22 @@ ed25519-dalek = "=1.0.0-pre.1"
log = "0.4.8"
rand = "0.6.5"
rand_chacha = "0.1.1"
solana-client = { path = "../client", version = "1.0.12" }
solana-storage-program = { path = "../programs/storage", version = "1.0.12" }
solana-client = { path = "../client", version = "1.0.18" }
solana-storage-program = { path = "../programs/storage", version = "1.0.18" }
thiserror = "1.0"
serde = "1.0.104"
serde_json = "1.0.46"
serde_derive = "1.0.103"
solana-net-utils = { path = "../net-utils", version = "1.0.12" }
solana-chacha = { path = "../chacha", version = "1.0.12" }
solana-chacha-sys = { path = "../chacha-sys", version = "1.0.12" }
solana-ledger = { path = "../ledger", version = "1.0.12" }
solana-logger = { path = "../logger", version = "1.0.12" }
solana-perf = { path = "../perf", version = "1.0.12" }
solana-sdk = { path = "../sdk", version = "1.0.12" }
solana-core = { path = "../core", version = "1.0.12" }
solana-archiver-utils = { path = "../archiver-utils", version = "1.0.12" }
solana-metrics = { path = "../metrics", version = "1.0.12" }
solana-net-utils = { path = "../net-utils", version = "1.0.18" }
solana-chacha = { path = "../chacha", version = "1.0.18" }
solana-chacha-sys = { path = "../chacha-sys", version = "1.0.18" }
solana-ledger = { path = "../ledger", version = "1.0.18" }
solana-logger = { path = "../logger", version = "1.0.18" }
solana-perf = { path = "../perf", version = "1.0.18" }
solana-sdk = { path = "../sdk", version = "1.0.18" }
solana-core = { path = "../core", version = "1.0.18" }
solana-archiver-utils = { path = "../archiver-utils", version = "1.0.18" }
solana-metrics = { path = "../metrics", version = "1.0.18" }
[dev-dependencies]
hex = "0.4.0"

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-archiver-utils"
version = "1.0.12"
version = "1.0.18"
description = "Solana Archiver Utils"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@@ -11,12 +11,12 @@ edition = "2018"
[dependencies]
log = "0.4.8"
rand = "0.6.5"
solana-chacha = { path = "../chacha", version = "1.0.12" }
solana-chacha-sys = { path = "../chacha-sys", version = "1.0.12" }
solana-ledger = { path = "../ledger", version = "1.0.12" }
solana-logger = { path = "../logger", version = "1.0.12" }
solana-perf = { path = "../perf", version = "1.0.12" }
solana-sdk = { path = "../sdk", version = "1.0.12" }
solana-chacha = { path = "../chacha", version = "1.0.18" }
solana-chacha-sys = { path = "../chacha-sys", version = "1.0.18" }
solana-ledger = { path = "../ledger", version = "1.0.18" }
solana-logger = { path = "../logger", version = "1.0.18" }
solana-perf = { path = "../perf", version = "1.0.18" }
solana-sdk = { path = "../sdk", version = "1.0.18" }
[dev-dependencies]
hex = "0.4.0"

View File

@@ -2,7 +2,7 @@
authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-archiver"
version = "1.0.12"
version = "1.0.18"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@@ -10,11 +10,11 @@ homepage = "https://solana.com/"
[dependencies]
clap = "2.33.0"
console = "0.9.2"
solana-clap-utils = { path = "../clap-utils", version = "1.0.12" }
solana-core = { path = "../core", version = "1.0.12" }
solana-logger = { path = "../logger", version = "1.0.12" }
solana-metrics = { path = "../metrics", version = "1.0.12" }
solana-archiver-lib = { path = "../archiver-lib", version = "1.0.12" }
solana-net-utils = { path = "../net-utils", version = "1.0.12" }
solana-sdk = { path = "../sdk", version = "1.0.12" }
solana-clap-utils = { path = "../clap-utils", version = "1.0.18" }
solana-core = { path = "../core", version = "1.0.18" }
solana-logger = { path = "../logger", version = "1.0.18" }
solana-metrics = { path = "../metrics", version = "1.0.18" }
solana-archiver-lib = { path = "../archiver-lib", version = "1.0.18" }
solana-net-utils = { path = "../net-utils", version = "1.0.18" }
solana-sdk = { path = "../sdk", version = "1.0.18" }

View File

@@ -2,7 +2,7 @@
authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-banking-bench"
version = "1.0.12"
version = "1.0.18"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@@ -10,11 +10,11 @@ homepage = "https://solana.com/"
[dependencies]
log = "0.4.6"
rayon = "1.2.0"
solana-core = { path = "../core", version = "1.0.12" }
solana-ledger = { path = "../ledger", version = "1.0.12" }
solana-logger = { path = "../logger", version = "1.0.12" }
solana-runtime = { path = "../runtime", version = "1.0.12" }
solana-measure = { path = "../measure", version = "1.0.12" }
solana-sdk = { path = "../sdk", version = "1.0.12" }
solana-core = { path = "../core", version = "1.0.18" }
solana-ledger = { path = "../ledger", version = "1.0.18" }
solana-logger = { path = "../logger", version = "1.0.18" }
solana-runtime = { path = "../runtime", version = "1.0.18" }
solana-measure = { path = "../measure", version = "1.0.18" }
solana-sdk = { path = "../sdk", version = "1.0.18" }
rand = "0.6.5"
crossbeam-channel = "0.3"

View File

@@ -2,7 +2,7 @@
authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-bench-exchange"
version = "1.0.12"
version = "1.0.18"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@@ -18,17 +18,17 @@ rand = "0.6.5"
rayon = "1.2.0"
serde_json = "1.0.46"
serde_yaml = "0.8.11"
solana-clap-utils = { path = "../clap-utils", version = "1.0.12" }
solana-core = { path = "../core", version = "1.0.12" }
solana-genesis = { path = "../genesis", version = "1.0.12" }
solana-client = { path = "../client", version = "1.0.12" }
solana-faucet = { path = "../faucet", version = "1.0.12" }
solana-exchange-program = { path = "../programs/exchange", version = "1.0.12" }
solana-logger = { path = "../logger", version = "1.0.12" }
solana-metrics = { path = "../metrics", version = "1.0.12" }
solana-net-utils = { path = "../net-utils", version = "1.0.12" }
solana-runtime = { path = "../runtime", version = "1.0.12" }
solana-sdk = { path = "../sdk", version = "1.0.12" }
solana-clap-utils = { path = "../clap-utils", version = "1.0.18" }
solana-core = { path = "../core", version = "1.0.18" }
solana-genesis = { path = "../genesis", version = "1.0.18" }
solana-client = { path = "../client", version = "1.0.18" }
solana-faucet = { path = "../faucet", version = "1.0.18" }
solana-exchange-program = { path = "../programs/exchange", version = "1.0.18" }
solana-logger = { path = "../logger", version = "1.0.18" }
solana-metrics = { path = "../metrics", version = "1.0.18" }
solana-net-utils = { path = "../net-utils", version = "1.0.18" }
solana-runtime = { path = "../runtime", version = "1.0.18" }
solana-sdk = { path = "../sdk", version = "1.0.18" }
[dev-dependencies]
solana-local-cluster = { path = "../local-cluster", version = "1.0.12" }
solana-local-cluster = { path = "../local-cluster", version = "1.0.18" }

View File

@@ -2,14 +2,14 @@
authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-bench-streamer"
version = "1.0.12"
version = "1.0.18"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
[dependencies]
clap = "2.33.0"
solana-clap-utils = { path = "../clap-utils", version = "1.0.12" }
solana-core = { path = "../core", version = "1.0.12" }
solana-logger = { path = "../logger", version = "1.0.12" }
solana-net-utils = { path = "../net-utils", version = "1.0.12" }
solana-clap-utils = { path = "../clap-utils", version = "1.0.18" }
solana-core = { path = "../core", version = "1.0.18" }
solana-logger = { path = "../logger", version = "1.0.18" }
solana-net-utils = { path = "../net-utils", version = "1.0.18" }

View File

@@ -2,7 +2,7 @@
authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-bench-tps"
version = "1.0.12"
version = "1.0.18"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@@ -14,24 +14,24 @@ log = "0.4.8"
rayon = "1.2.0"
serde_json = "1.0.46"
serde_yaml = "0.8.11"
solana-clap-utils = { path = "../clap-utils", version = "1.0.12" }
solana-core = { path = "../core", version = "1.0.12" }
solana-genesis = { path = "../genesis", version = "1.0.12" }
solana-client = { path = "../client", version = "1.0.12" }
solana-faucet = { path = "../faucet", version = "1.0.12" }
solana-librapay = { path = "../programs/librapay", version = "1.0.12", optional = true }
solana-logger = { path = "../logger", version = "1.0.12" }
solana-metrics = { path = "../metrics", version = "1.0.12" }
solana-measure = { path = "../measure", version = "1.0.12" }
solana-net-utils = { path = "../net-utils", version = "1.0.12" }
solana-runtime = { path = "../runtime", version = "1.0.12" }
solana-sdk = { path = "../sdk", version = "1.0.12" }
solana-move-loader-program = { path = "../programs/move_loader", version = "1.0.12", optional = true }
solana-clap-utils = { path = "../clap-utils", version = "1.0.18" }
solana-core = { path = "../core", version = "1.0.18" }
solana-genesis = { path = "../genesis", version = "1.0.18" }
solana-client = { path = "../client", version = "1.0.18" }
solana-faucet = { path = "../faucet", version = "1.0.18" }
solana-librapay = { path = "../programs/librapay", version = "1.0.18", optional = true }
solana-logger = { path = "../logger", version = "1.0.18" }
solana-metrics = { path = "../metrics", version = "1.0.18" }
solana-measure = { path = "../measure", version = "1.0.18" }
solana-net-utils = { path = "../net-utils", version = "1.0.18" }
solana-runtime = { path = "../runtime", version = "1.0.18" }
solana-sdk = { path = "../sdk", version = "1.0.18" }
solana-move-loader-program = { path = "../programs/move_loader", version = "1.0.18", optional = true }
[dev-dependencies]
serial_test = "0.3.2"
serial_test_derive = "0.4.0"
solana-local-cluster = { path = "../local-cluster", version = "1.0.12" }
solana-local-cluster = { path = "../local-cluster", version = "1.0.18" }
[features]
move = ["solana-librapay", "solana-move-loader-program"]

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-chacha-cuda"
version = "1.0.12"
version = "1.0.18"
description = "Solana Chacha Cuda APIs"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@@ -10,12 +10,12 @@ edition = "2018"
[dependencies]
log = "0.4.8"
solana-archiver-utils = { path = "../archiver-utils", version = "1.0.12" }
solana-chacha = { path = "../chacha", version = "1.0.12" }
solana-ledger = { path = "../ledger", version = "1.0.12" }
solana-logger = { path = "../logger", version = "1.0.12" }
solana-perf = { path = "../perf", version = "1.0.12" }
solana-sdk = { path = "../sdk", version = "1.0.12" }
solana-archiver-utils = { path = "../archiver-utils", version = "1.0.18" }
solana-chacha = { path = "../chacha", version = "1.0.18" }
solana-ledger = { path = "../ledger", version = "1.0.18" }
solana-logger = { path = "../logger", version = "1.0.18" }
solana-perf = { path = "../perf", version = "1.0.18" }
solana-sdk = { path = "../sdk", version = "1.0.18" }
[dev-dependencies]
hex-literal = "0.2.1"

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-chacha-sys"
version = "1.0.12"
version = "1.0.18"
description = "Solana chacha-sys"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-chacha"
version = "1.0.12"
version = "1.0.18"
description = "Solana Chacha APIs"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@@ -12,11 +12,11 @@ edition = "2018"
log = "0.4.8"
rand = "0.6.5"
rand_chacha = "0.1.1"
solana-chacha-sys = { path = "../chacha-sys", version = "1.0.12" }
solana-ledger = { path = "../ledger", version = "1.0.12" }
solana-logger = { path = "../logger", version = "1.0.12" }
solana-perf = { path = "../perf", version = "1.0.12" }
solana-sdk = { path = "../sdk", version = "1.0.12" }
solana-chacha-sys = { path = "../chacha-sys", version = "1.0.18" }
solana-ledger = { path = "../ledger", version = "1.0.18" }
solana-logger = { path = "../logger", version = "1.0.18" }
solana-perf = { path = "../perf", version = "1.0.18" }
solana-sdk = { path = "../sdk", version = "1.0.18" }
[dev-dependencies]
hex-literal = "0.2.1"

View File

@@ -18,3 +18,6 @@ steps:
- command: "ci/publish-docs.sh"
timeout_in_minutes: 15
name: "publish docs"
- command: ". ci/rust-version.sh; ci/docker-run.sh $$rust_stable_docker_image ci/test-move.sh"
name: "move"
timeout_in_minutes: 20

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

@@ -0,0 +1,26 @@
# These steps are conditionally triggered by ci/buildkite.yml when files
# other than those in docs/ are modified
steps:
- command: ". ci/rust-version.sh; ci/docker-run.sh $$rust_nightly_docker_image ci/test-coverage.sh"
name: "coverage"
timeout_in_minutes: 30
- wait
- 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"
- 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-local-cluster.sh"
name: "local-cluster"
timeout_in_minutes: 45
artifact_paths: "log-*.txt"

View File

@@ -1,42 +1,25 @@
# 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
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"
name: "checks"
timeout_in_minutes: 20
- 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"
- command: "ci/maybe-trigger-tests.sh"
name: "maybe-trigger-tests"
timeout_in_minutes: 2
- wait
- trigger: "solana-secondary"
branches: "!pull/*"
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" audit --version
_ cargo +"$rust_stable" audit --ignore RUSTSEC-2020-0002
_ cargo +"$rust_stable" audit --ignore RUSTSEC-2020-0002 --ignore RUSTSEC-2020-0008
_ ci/nits.sh
_ ci/order-crates-for-publishing.py
_ docs/build.sh

View File

@@ -38,10 +38,15 @@ test -d target/release/bpf && find target/release/bpf -name '*.d' -delete
# Clear the BPF sysroot files, they are not automatically rebuilt
rm -rf target/xargo # Issue #3105
# Limit compiler jobs to reduce memory usage
# on machines with 1gb/thread of memory
NPROC=$(nproc)
NPROC=$((NPROC>16 ? 16 : NPROC))
echo "Executing $testName"
case $testName in
test-stable)
_ cargo +"$rust_stable" test --all --exclude solana-local-cluster ${V:+--verbose} -- --nocapture
_ cargo +"$rust_stable" test --jobs "$NPROC" --all --exclude solana-local-cluster ${V:+--verbose} -- --nocapture
_ cargo +"$rust_stable" test --manifest-path bench-tps/Cargo.toml --features=move ${V:+--verbose} test_bench_tps_local_cluster_move -- --nocapture
;;
test-stable-perf)

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-clap-utils"
version = "1.0.12"
version = "1.0.18"
description = "Solana utilities for the clap"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@@ -11,8 +11,8 @@ edition = "2018"
[dependencies]
clap = "2.33.0"
rpassword = "4.0"
solana-remote-wallet = { path = "../remote-wallet", version = "1.0.12" }
solana-sdk = { path = "../sdk", version = "1.0.12" }
solana-remote-wallet = { path = "../remote-wallet", version = "1.0.18" }
solana-sdk = { path = "../sdk", version = "1.0.18" }
thiserror = "1.0.11"
tiny-bip39 = "0.7.0"
url = "2.1.0"

View File

@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-cli-config"
description = "Blockchain, Rebuilt for Scale"
version = "1.0.12"
version = "1.0.18"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"

View File

@@ -1,10 +1,6 @@
// Wallet settings that can be configured for long-term use
use serde_derive::{Deserialize, Serialize};
use std::{
fs::{create_dir_all, File},
io::{self, Write},
path::Path,
};
use std::io;
use url::Url;
lazy_static! {
@@ -46,23 +42,11 @@ impl Default for Config {
impl Config {
pub fn load(config_file: &str) -> Result<Self, io::Error> {
let file = File::open(config_file.to_string())?;
let config = serde_yaml::from_reader(file)
.map_err(|err| io::Error::new(io::ErrorKind::Other, format!("{:?}", err)))?;
Ok(config)
crate::load_config_file(config_file)
}
pub fn save(&self, config_file: &str) -> Result<(), io::Error> {
let serialized = serde_yaml::to_string(self)
.map_err(|err| io::Error::new(io::ErrorKind::Other, format!("{:?}", err)))?;
if let Some(outdir) = Path::new(&config_file).parent() {
create_dir_all(outdir)?;
}
let mut file = File::create(config_file)?;
file.write_all(&serialized.into_bytes())?;
Ok(())
crate::save_config_file(self, config_file)
}
pub fn compute_websocket_url(json_rpc_url: &str) -> String {
@@ -76,17 +60,38 @@ impl Config {
ws_url
.set_scheme(if is_secure { "wss" } else { "ws" })
.expect("unable to set scheme");
let ws_port = match json_rpc_url.port() {
Some(port) => port + 1,
None => {
if is_secure {
8901
} else {
8900
}
}
};
ws_url.set_port(Some(ws_port)).expect("unable to set port");
if let Some(port) = json_rpc_url.port() {
ws_url.set_port(Some(port + 1)).expect("unable to set port");
}
ws_url.to_string()
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn compute_websocket_url() {
assert_eq!(
Config::compute_websocket_url(&"http://devnet.solana.com"),
"ws://devnet.solana.com/".to_string()
);
assert_eq!(
Config::compute_websocket_url(&"https://devnet.solana.com"),
"wss://devnet.solana.com/".to_string()
);
assert_eq!(
Config::compute_websocket_url(&"http://example.com:8899"),
"ws://example.com:8900/".to_string()
);
assert_eq!(
Config::compute_websocket_url(&"https://example.com:1234"),
"wss://example.com:1235/".to_string()
);
assert_eq!(Config::compute_websocket_url(&"garbage"), String::new());
}
}

View File

@@ -3,3 +3,37 @@ extern crate lazy_static;
mod config;
pub use config::{Config, CONFIG_FILE};
use std::{
fs::{create_dir_all, File},
io::{self, Write},
path::Path,
};
pub fn load_config_file<T, P>(config_file: P) -> Result<T, io::Error>
where
T: serde::de::DeserializeOwned,
P: AsRef<Path>,
{
let file = File::open(config_file)?;
let config = serde_yaml::from_reader(file)
.map_err(|err| io::Error::new(io::ErrorKind::Other, format!("{:?}", err)))?;
Ok(config)
}
pub fn save_config_file<T, P>(config: &T, config_file: P) -> Result<(), io::Error>
where
T: serde::ser::Serialize,
P: AsRef<Path>,
{
let serialized = serde_yaml::to_string(config)
.map_err(|err| io::Error::new(io::ErrorKind::Other, format!("{:?}", err)))?;
if let Some(outdir) = config_file.as_ref().parent() {
create_dir_all(outdir)?;
}
let mut file = File::create(config_file)?;
file.write_all(&serialized.into_bytes())?;
Ok(())
}

View File

@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-cli"
description = "Blockchain, Rebuilt for Scale"
version = "1.0.12"
version = "1.0.18"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@@ -26,28 +26,28 @@ reqwest = { version = "0.10.1", default-features = false, features = ["blocking"
serde = "1.0.104"
serde_derive = "1.0.103"
serde_json = "1.0.46"
solana-budget-program = { path = "../programs/budget", version = "1.0.12" }
solana-clap-utils = { path = "../clap-utils", version = "1.0.12" }
solana-cli-config = { path = "../cli-config", version = "1.0.12" }
solana-client = { path = "../client", version = "1.0.12" }
solana-config-program = { path = "../programs/config", version = "1.0.12" }
solana-faucet = { path = "../faucet", version = "1.0.12" }
solana-logger = { path = "../logger", version = "1.0.12" }
solana-net-utils = { path = "../net-utils", version = "1.0.12" }
solana-remote-wallet = { path = "../remote-wallet", version = "1.0.12" }
solana-runtime = { path = "../runtime", version = "1.0.12" }
solana-sdk = { path = "../sdk", version = "1.0.12" }
solana-stake-program = { path = "../programs/stake", version = "1.0.12" }
solana-storage-program = { path = "../programs/storage", version = "1.0.12" }
solana-vote-program = { path = "../programs/vote", version = "1.0.12" }
solana-vote-signer = { path = "../vote-signer", version = "1.0.12" }
solana-budget-program = { path = "../programs/budget", version = "1.0.18" }
solana-clap-utils = { path = "../clap-utils", version = "1.0.18" }
solana-cli-config = { path = "../cli-config", version = "1.0.18" }
solana-client = { path = "../client", version = "1.0.18" }
solana-config-program = { path = "../programs/config", version = "1.0.18" }
solana-faucet = { path = "../faucet", version = "1.0.18" }
solana-logger = { path = "../logger", version = "1.0.18" }
solana-net-utils = { path = "../net-utils", version = "1.0.18" }
solana-remote-wallet = { path = "../remote-wallet", version = "1.0.18" }
solana-runtime = { path = "../runtime", version = "1.0.18" }
solana-sdk = { path = "../sdk", version = "1.0.18" }
solana-stake-program = { path = "../programs/stake", version = "1.0.18" }
solana-storage-program = { path = "../programs/storage", version = "1.0.18" }
solana-vote-program = { path = "../programs/vote", version = "1.0.18" }
solana-vote-signer = { path = "../vote-signer", version = "1.0.18" }
titlecase = "1.1.0"
thiserror = "1.0.11"
url = "2.1.1"
[dev-dependencies]
solana-core = { path = "../core", version = "1.0.12" }
solana-budget-program = { path = "../programs/budget", version = "1.0.12" }
solana-core = { path = "../core", version = "1.0.18" }
solana-budget-program = { path = "../programs/budget", version = "1.0.18" }
tempfile = "3.1.0"
[[bin]]

View File

@@ -188,6 +188,9 @@ pub enum CliCommand {
commitment_config: CommitmentConfig,
},
GetGenesisHash,
GetEpoch {
commitment_config: CommitmentConfig,
},
GetSlot {
commitment_config: CommitmentConfig,
},
@@ -402,6 +405,7 @@ pub enum CliCommand {
to: Pubkey,
from: SignerIndex,
sign_only: bool,
no_wait: bool,
blockhash_query: BlockhashQuery,
nonce_account: Option<Pubkey>,
nonce_authority: SignerIndex,
@@ -583,6 +587,7 @@ pub fn parse_command(
command: CliCommand::GetGenesisHash,
signers: vec![],
}),
("epoch", Some(matches)) => parse_get_epoch(matches),
("slot", Some(matches)) => parse_get_slot(matches),
("transaction-count", Some(matches)) => parse_get_transaction_count(matches),
("leader-schedule", Some(_matches)) => Ok(CliCommandInfo {
@@ -898,6 +903,7 @@ pub fn parse_command(
let lamports = lamports_of_sol(matches, "amount").unwrap();
let to = pubkey_of_signer(matches, "to", wallet_manager)?.unwrap();
let sign_only = matches.is_present(SIGN_ONLY_ARG.name);
let no_wait = matches.is_present("no_wait");
let blockhash_query = BlockhashQuery::new_from_matches(matches);
let nonce_account = pubkey_of_signer(matches, NONCE_ARG.name, wallet_manager)?;
let (nonce_authority, nonce_authority_pubkey) =
@@ -923,6 +929,7 @@ pub fn parse_command(
lamports,
to,
sign_only,
no_wait,
blockhash_query,
nonce_account,
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
@@ -1149,7 +1156,7 @@ fn process_balance(
}
fn process_confirm(rpc_client: &RpcClient, signature: &Signature) -> ProcessResult {
match rpc_client.get_signature_status(&signature.to_string()) {
match rpc_client.get_signature_status(&signature) {
Ok(status) => {
if let Some(result) = status {
match result {
@@ -1484,6 +1491,7 @@ fn process_transfer(
to: &Pubkey,
from: SignerIndex,
sign_only: bool,
no_wait: bool,
blockhash_query: &BlockhashQuery,
nonce_account: Option<&Pubkey>,
nonce_authority: SignerIndex,
@@ -1530,7 +1538,11 @@ fn process_transfer(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers);
let result = if no_wait {
rpc_client.send_transaction(&tx)
} else {
rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers)
};
log_instruction_custom_error::<SystemError>(result)
}
}
@@ -1598,6 +1610,9 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
CliCommand::GetEpochInfo { 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 } => {
process_get_slot(&rpc_client, *commitment_config)
}
@@ -2073,6 +2088,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
to,
from,
sign_only,
no_wait,
ref blockhash_query,
ref nonce_account,
nonce_authority,
@@ -2084,6 +2100,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
to,
*from,
*sign_only,
*no_wait,
blockhash_query,
nonce_account.as_ref(),
*nonce_authority,
@@ -2158,7 +2175,7 @@ pub fn request_and_confirm_airdrop(
log_instruction_custom_error::<SystemError>(result)
}
pub fn log_instruction_custom_error<E>(result: ClientResult<String>) -> ProcessResult
pub fn log_instruction_custom_error<E>(result: ClientResult<Signature>) -> ProcessResult
where
E: 'static + std::error::Error + DecodeError<E> + FromPrimitive,
{
@@ -2175,7 +2192,7 @@ where
}
Err(err.into())
}
Ok(sig) => Ok(sig),
Ok(sig) => Ok(sig.to_string()),
}
}
@@ -2490,6 +2507,12 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
.validator(is_valid_signer)
.help("Source account of funds (if different from client local account)"),
)
.arg(
Arg::with_name("no_wait")
.long("no-wait")
.takes_value(false)
.help("Return signature immediately after submitting the transaction, instead of waiting for confirmations"),
)
.offline_args()
.arg(nonce_arg())
.arg(nonce_authority_arg())
@@ -3550,6 +3573,33 @@ mod tests {
to: to_pubkey,
from: 0,
sign_only: false,
no_wait: false,
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
nonce_account: None,
nonce_authority: 0,
fee_payer: 0,
},
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
}
);
// Test Transfer no-wait
let test_transfer = test_commands.clone().get_matches_from(vec![
"test",
"transfer",
"--no-wait",
&to_string,
"42",
]);
assert_eq!(
parse_command(&test_transfer, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::Transfer {
lamports: 42_000_000_000,
to: to_pubkey,
from: 0,
sign_only: false,
no_wait: true,
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
nonce_account: None,
nonce_authority: 0,
@@ -3579,6 +3629,7 @@ mod tests {
to: to_pubkey,
from: 0,
sign_only: true,
no_wait: false,
blockhash_query: BlockhashQuery::None(blockhash),
nonce_account: None,
nonce_authority: 0,
@@ -3613,6 +3664,7 @@ mod tests {
to: to_pubkey,
from: 0,
sign_only: false,
no_wait: false,
blockhash_query: BlockhashQuery::FeeCalculator(
blockhash_query::Source::Cluster,
blockhash
@@ -3651,6 +3703,7 @@ mod tests {
to: to_pubkey,
from: 0,
sign_only: false,
no_wait: false,
blockhash_query: BlockhashQuery::FeeCalculator(
blockhash_query::Source::NonceAccount(nonce_address),
blockhash

View File

@@ -117,6 +117,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::with_name("transaction-count").about("Get current transaction count")
.alias("get-transaction-count")
@@ -325,6 +336,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_get_transaction_count(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
let commitment_config = if matches.is_present("confirmed") {
CommitmentConfig::default()
@@ -575,6 +598,14 @@ pub fn process_get_slot(
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> {
let epoch = value_t!(matches, "epoch", Epoch).ok();
let slot_limit = value_t!(matches, "slot_limit", u64).ok();
@@ -1280,6 +1311,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_transaction_count = test_commands
.clone()
.get_matches_from(vec!["test", "transaction-count"]);

View File

@@ -266,8 +266,8 @@ pub fn process_claim_storage_reward(
&fee_calculator,
&tx.message,
)?;
let signature_str = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &signers)?;
Ok(signature_str)
let signature = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &signers)?;
Ok(signature.to_string())
}
pub fn process_show_storage_account(

View File

@@ -338,6 +338,7 @@ fn test_create_account_with_seed() {
to: to_address,
from: 0,
sign_only: true,
no_wait: false,
blockhash_query: BlockhashQuery::None(nonce_hash),
nonce_account: Some(nonce_address),
nonce_authority: 0,
@@ -358,6 +359,7 @@ fn test_create_account_with_seed() {
to: to_address,
from: 0,
sign_only: false,
no_wait: false,
blockhash_query: BlockhashQuery::FeeCalculator(
blockhash_query::Source::NonceAccount(nonce_address),
sign_only.blockhash,

View File

@@ -68,6 +68,7 @@ fn test_transfer() {
to: recipient_pubkey,
from: 0,
sign_only: false,
no_wait: false,
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
nonce_account: None,
nonce_authority: 0,
@@ -95,6 +96,7 @@ fn test_transfer() {
to: recipient_pubkey,
from: 0,
sign_only: true,
no_wait: false,
blockhash_query: BlockhashQuery::None(blockhash),
nonce_account: None,
nonce_authority: 0,
@@ -110,6 +112,7 @@ fn test_transfer() {
to: recipient_pubkey,
from: 0,
sign_only: false,
no_wait: false,
blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
nonce_account: None,
nonce_authority: 0,
@@ -147,6 +150,7 @@ fn test_transfer() {
to: recipient_pubkey,
from: 0,
sign_only: false,
no_wait: false,
blockhash_query: BlockhashQuery::FeeCalculator(
blockhash_query::Source::NonceAccount(nonce_account.pubkey()),
nonce_hash,
@@ -187,6 +191,7 @@ fn test_transfer() {
to: recipient_pubkey,
from: 0,
sign_only: true,
no_wait: false,
blockhash_query: BlockhashQuery::None(nonce_hash),
nonce_account: Some(nonce_account.pubkey()),
nonce_authority: 0,
@@ -202,6 +207,7 @@ fn test_transfer() {
to: recipient_pubkey,
from: 0,
sign_only: false,
no_wait: false,
blockhash_query: BlockhashQuery::FeeCalculator(
blockhash_query::Source::NonceAccount(nonce_account.pubkey()),
sign_only.blockhash,
@@ -269,6 +275,7 @@ fn test_transfer_multisession_signing() {
to: to_pubkey,
from: 1,
sign_only: true,
no_wait: false,
blockhash_query: BlockhashQuery::None(blockhash),
nonce_account: None,
nonce_authority: 0,
@@ -293,6 +300,7 @@ fn test_transfer_multisession_signing() {
to: to_pubkey,
from: 1,
sign_only: true,
no_wait: false,
blockhash_query: BlockhashQuery::None(blockhash),
nonce_account: None,
nonce_authority: 0,
@@ -314,6 +322,7 @@ fn test_transfer_multisession_signing() {
to: to_pubkey,
from: 1,
sign_only: false,
no_wait: false,
blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
nonce_account: None,
nonce_authority: 0,

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-client"
version = "1.0.12"
version = "1.0.18"
description = "Solana Client"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@@ -19,10 +19,10 @@ reqwest = { version = "0.10.1", default-features = false, features = ["blocking"
serde = "1.0.104"
serde_derive = "1.0.103"
serde_json = "1.0.46"
solana-net-utils = { path = "../net-utils", version = "1.0.12" }
solana-sdk = { path = "../sdk", version = "1.0.12" }
solana-transaction-status = { path = "../transaction-status", version = "1.0.12" }
solana-vote-program = { path = "../programs/vote", version = "1.0.12" }
solana-net-utils = { path = "../net-utils", version = "1.0.18" }
solana-sdk = { path = "../sdk", version = "1.0.18" }
solana-transaction-status = { path = "../transaction-status", version = "1.0.18" }
solana-vote-program = { path = "../programs/vote", version = "1.0.18" }
thiserror = "1.0"
tungstenite = "0.10.1"
url = "2.1.1"
@@ -31,4 +31,4 @@ url = "2.1.1"
assert_matches = "1.3.0"
jsonrpc-core = "14.0.5"
jsonrpc-http-server = "14.0.6"
solana-logger = { path = "../logger", version = "1.0.12" }
solana-logger = { path = "../logger", version = "1.0.18" }

View File

@@ -40,7 +40,7 @@ impl GenericRpcClientRequest for MockRpcClientRequest {
fn send(
&self,
request: &RpcRequest,
params: serde_json::Value,
_params: serde_json::Value,
_retries: usize,
) -> Result<serde_json::Value> {
if let Some(value) = self.mocks.write().unwrap().remove(request) {
@@ -50,17 +50,6 @@ impl GenericRpcClientRequest for MockRpcClientRequest {
return Ok(Value::Null);
}
let val = match request {
RpcRequest::ConfirmTransaction => {
if let Some(params_array) = params.as_array() {
if let Value::String(param_string) = &params_array[0] {
Value::Bool(param_string == SIGNATURE)
} else {
Value::Null
}
} else {
Value::Null
}
}
RpcRequest::GetBalance => serde_json::to_value(Response {
context: RpcResponseContext { slot: 1 },
value: Value::Number(Number::from(50)),
@@ -87,7 +76,7 @@ impl GenericRpcClientRequest for MockRpcClientRequest {
context: RpcResponseContext { slot: 1 },
value: serde_json::to_value(FeeRateGovernor::default()).unwrap(),
})?,
RpcRequest::GetSignatureStatus => {
RpcRequest::GetSignatureStatuses => {
let status: transaction::Result<()> = if self.url == "account_in_use" {
Err(TransactionError::AccountInUse)
} else if self.url == "instruction_error" {
@@ -101,10 +90,12 @@ impl GenericRpcClientRequest for MockRpcClientRequest {
let status = if self.url == "sig_not_found" {
None
} else {
let err = status.clone().err();
Some(TransactionStatus {
status,
slot: 1,
confirmations: Some(0),
confirmations: None,
err,
})
};
serde_json::to_value(Response {

View File

@@ -27,12 +27,11 @@ use solana_sdk::{
signers::Signers,
transaction::{self, Transaction, TransactionError},
};
use solana_transaction_status::{ConfirmedBlock, TransactionStatus};
use solana_transaction_status::{ConfirmedBlock, TransactionEncoding, TransactionStatus};
use solana_vote_program::vote_state::MAX_LOCKOUT_HISTORY;
use std::{
error,
net::SocketAddr,
str::FromStr,
thread::sleep,
time::{Duration, Instant},
};
@@ -71,7 +70,7 @@ impl RpcClient {
}
}
pub fn confirm_transaction(&self, signature: &str) -> ClientResult<bool> {
pub fn confirm_transaction(&self, signature: &Signature) -> ClientResult<bool> {
Ok(self
.confirm_transaction_with_commitment(signature, CommitmentConfig::default())?
.value)
@@ -79,55 +78,72 @@ impl RpcClient {
pub fn confirm_transaction_with_commitment(
&self,
signature: &str,
signature: &Signature,
commitment_config: CommitmentConfig,
) -> RpcResult<bool> {
let response = self
.client
.send(
&RpcRequest::ConfirmTransaction,
json!([signature, commitment_config]),
0,
)
.map_err(|err| err.into_with_command("ConfirmTransaction"))?;
let Response { context, value } = self.get_signature_statuses(&[*signature])?;
serde_json::from_value::<Response<bool>>(response)
.map_err(|err| ClientError::new_with_command(err.into(), "ConfirmTransaction"))
Ok(Response {
context,
value: value[0]
.as_ref()
.filter(|result| result.satisfies_commitment(commitment_config))
.map(|result| result.status.is_ok())
.unwrap_or_default(),
})
}
pub fn send_transaction(&self, transaction: &Transaction) -> ClientResult<String> {
pub fn send_transaction(&self, transaction: &Transaction) -> ClientResult<Signature> {
let serialized_encoded = bs58::encode(serialize(transaction).unwrap()).into_string();
let signature =
let response =
self.client
.send(&RpcRequest::SendTransaction, json!([serialized_encoded]), 5)?;
if signature.as_str().is_none() {
Err(RpcError::ForUser("Received result of an unexpected type".to_string()).into())
} else {
Ok(signature.as_str().unwrap().to_string())
match response.as_str() {
None => {
Err(RpcError::ForUser("Received result of an unexpected type".to_string()).into())
}
Some(signature_base58_str) => signature_base58_str
.parse::<Signature>()
.map_err(|err| RpcError::ParseError(err.to_string()).into()),
}
}
pub fn get_signature_status(
&self,
signature: &str,
signature: &Signature,
) -> ClientResult<Option<transaction::Result<()>>> {
self.get_signature_status_with_commitment(signature, CommitmentConfig::default())
}
pub fn get_signature_statuses(
&self,
signatures: &[Signature],
) -> RpcResult<Vec<Option<TransactionStatus>>> {
let signatures: Vec<_> = signatures.iter().map(|s| s.to_string()).collect();
let signature_status =
self.client
.send(&RpcRequest::GetSignatureStatuses, json!([signatures]), 5)?;
Ok(serde_json::from_value(signature_status)
.map_err(|err| ClientError::new_with_command(err.into(), "GetSignatureStatuses"))?)
}
pub fn get_signature_status_with_commitment(
&self,
signature: &str,
signature: &Signature,
commitment_config: CommitmentConfig,
) -> ClientResult<Option<transaction::Result<()>>> {
let signature_status = self.client.send(
&RpcRequest::GetSignatureStatus,
json!([[signature.to_string()], commitment_config]),
&RpcRequest::GetSignatureStatuses,
json!([[signature.to_string()]]),
5,
)?;
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]
.clone()
.filter(|result| result.satisfies_commitment(commitment_config))
.map(|status_meta| status_meta.status))
}
@@ -176,9 +192,17 @@ impl RpcClient {
}
pub fn get_confirmed_block(&self, slot: Slot) -> ClientResult<ConfirmedBlock> {
self.get_confirmed_block_with_encoding(slot, TransactionEncoding::Json)
}
pub fn get_confirmed_block_with_encoding(
&self,
slot: Slot,
encoding: TransactionEncoding,
) -> ClientResult<ConfirmedBlock> {
let response = self
.client
.send(&RpcRequest::GetConfirmedBlock, json!([slot]), 0)
.send(&RpcRequest::GetConfirmedBlock, json!([slot, encoding]), 0)
.map_err(|err| err.into_with_command("GetConfirmedBlock"))?;
serde_json::from_value(response)
@@ -325,13 +349,13 @@ impl RpcClient {
&self,
transaction: &mut Transaction,
signer_keys: &T,
) -> ClientResult<String> {
) -> ClientResult<Signature> {
let mut send_retries = 20;
loop {
let mut status_retries = 15;
let signature_str = self.send_transaction(transaction)?;
let signature = self.send_transaction(transaction)?;
let status = loop {
let status = self.get_signature_status(&signature_str)?;
let status = self.get_signature_status(&signature)?;
if status.is_none() {
status_retries -= 1;
if status_retries == 0 {
@@ -347,7 +371,7 @@ impl RpcClient {
};
send_retries = if let Some(result) = status.clone() {
match result {
Ok(_) => return Ok(signature_str),
Ok(_) => return Ok(signature),
Err(TransactionError::AccountInUse) => {
// Fetch a new blockhash and re-sign the transaction before sending it again
self.resign_transaction(transaction, signer_keys)?;
@@ -809,10 +833,9 @@ impl RpcClient {
) -> ClientResult<()> {
let now = Instant::now();
loop {
if let Ok(Some(_)) = self.get_signature_status_with_commitment(
&signature.to_string(),
commitment_config.clone(),
) {
if let Ok(Some(_)) =
self.get_signature_status_with_commitment(&signature, commitment_config.clone())
{
break;
}
if now.elapsed().as_secs() > 15 {
@@ -832,14 +855,13 @@ impl RpcClient {
trace!("check_signature: {:?}", signature);
for _ in 0..30 {
let response = self.client.send(
&RpcRequest::ConfirmTransaction,
json!([signature.to_string(), CommitmentConfig::recent()]),
0,
);
let response =
self.confirm_transaction_with_commitment(signature, CommitmentConfig::recent());
match response {
Ok(Value::Bool(signature_status)) => {
Ok(Response {
value: signature_status,
..
}) => {
if signature_status {
trace!("Response found signature");
} else {
@@ -848,12 +870,6 @@ impl RpcClient {
return signature_status;
}
Ok(other) => {
debug!(
"check_signature request failed, expected bool, got: {:?}",
other
);
}
Err(err) => {
debug!("check_signature request failed: {:?}", err);
}
@@ -925,20 +941,20 @@ impl RpcClient {
let response = self
.client
.send(
&RpcRequest::GetSignatureStatus,
json!([[signature.to_string()], CommitmentConfig::recent().ok()]),
&RpcRequest::GetSignatureStatuses,
json!([[signature.to_string()]]),
1,
)
.map_err(|err| err.into_with_command("GetSignatureStatus"))?;
let result: Response<Vec<Option<TransactionStatus>>> =
serde_json::from_value(response).unwrap();
.map_err(|err| err.into_with_command("GetSignatureStatuses"))?;
let result: Response<Vec<Option<TransactionStatus>>> = serde_json::from_value(response)
.map_err(|err| ClientError::new_with_command(err.into(), "GetSignatureStatuses"))?;
let confirmations = result.value[0]
.clone()
.ok_or_else(|| {
ClientError::new_with_command(
ClientErrorKind::Custom("signature not found".to_string()),
"GetSignatureStatus",
"GetSignatureStatuses",
)
})?
.confirmations
@@ -950,7 +966,7 @@ impl RpcClient {
&self,
transaction: &mut Transaction,
signer_keys: &T,
) -> ClientResult<String> {
) -> ClientResult<Signature> {
let mut confirmations = 0;
let progress_bar = new_spinner_progress_bar();
@@ -961,23 +977,21 @@ impl RpcClient {
));
let mut send_retries = 20;
let signature_str = loop {
let signature = loop {
let mut status_retries = 15;
let (signature_str, status) = loop {
let signature_str = self.send_transaction(transaction)?;
let (signature, status) = loop {
let signature = self.send_transaction(transaction)?;
// Get recent commitment in order to count confirmations for successful transactions
let status = self.get_signature_status_with_commitment(
&signature_str,
CommitmentConfig::recent(),
)?;
let status = self
.get_signature_status_with_commitment(&signature, CommitmentConfig::recent())?;
if status.is_none() {
status_retries -= 1;
if status_retries == 0 {
break (signature_str, status);
break (signature, status);
}
} else {
break (signature_str, status);
break (signature, status);
}
if cfg!(not(test)) {
@@ -1002,7 +1016,7 @@ impl RpcClient {
if let Some(result) = status {
match result {
Ok(_) => {
break signature_str;
break signature;
}
Err(err) => {
return Err(err.into());
@@ -1015,19 +1029,13 @@ impl RpcClient {
}
}
};
let signature = Signature::from_str(&signature_str).map_err(|_| {
ClientError::from(ClientErrorKind::Custom(format!(
"Returned string {} cannot be parsed as a signature",
signature_str
)))
})?;
loop {
// Return when default (max) commitment is reached
// Failed transactions have already been eliminated, `is_some` check is sufficient
if self.get_signature_status(&signature_str)?.is_some() {
if self.get_signature_status(&signature)?.is_some() {
progress_bar.set_message("Transaction confirmed");
progress_bar.finish_and_clear();
return Ok(signature_str);
return Ok(signature);
}
progress_bar.set_message(&format!(
"[{}/{}] Waiting for confirmations",
@@ -1189,7 +1197,7 @@ mod tests {
let tx = system_transaction::transfer(&key, &to, 50, blockhash);
let signature = rpc_client.send_transaction(&tx);
assert_eq!(signature.unwrap(), SIGNATURE.to_string());
assert_eq!(signature.unwrap(), SIGNATURE.parse().unwrap());
let rpc_client = RpcClient::new_mock("fails".to_string());
@@ -1212,18 +1220,17 @@ mod tests {
#[test]
fn test_get_signature_status() {
let signature = Signature::default();
let rpc_client = RpcClient::new_mock("succeeds".to_string());
let signature = "good_signature";
let status = rpc_client.get_signature_status(&signature).unwrap();
assert_eq!(status, Some(Ok(())));
let rpc_client = RpcClient::new_mock("sig_not_found".to_string());
let signature = "sig_not_found";
let status = rpc_client.get_signature_status(&signature).unwrap();
assert_eq!(status, None);
let rpc_client = RpcClient::new_mock("account_in_use".to_string());
let signature = "account_in_use";
let status = rpc_client.get_signature_status(&signature).unwrap();
assert_eq!(status, Some(Err(TransactionError::AccountInUse)));
}

View File

@@ -3,7 +3,6 @@ use thiserror::Error;
#[derive(Debug, PartialEq, Eq, Hash)]
pub enum RpcRequest {
ConfirmTransaction,
DeregisterNode,
ValidatorExit,
GetAccountInfo,
@@ -22,7 +21,7 @@ pub enum RpcRequest {
GetRecentBlockhash,
GetFeeCalculatorForBlockhash,
GetFeeRateGovernor,
GetSignatureStatus,
GetSignatureStatuses,
GetSlot,
GetSlotLeader,
GetStorageTurn,
@@ -44,7 +43,6 @@ impl RpcRequest {
pub(crate) fn build_request_json(&self, id: u64, params: Value) -> Value {
let jsonrpc = "2.0";
let method = match self {
RpcRequest::ConfirmTransaction => "confirmTransaction",
RpcRequest::DeregisterNode => "deregisterNode",
RpcRequest::ValidatorExit => "validatorExit",
RpcRequest::GetAccountInfo => "getAccountInfo",
@@ -63,7 +61,7 @@ impl RpcRequest {
RpcRequest::GetRecentBlockhash => "getRecentBlockhash",
RpcRequest::GetFeeCalculatorForBlockhash => "getFeeCalculatorForBlockhash",
RpcRequest::GetFeeRateGovernor => "getFeeRateGovernor",
RpcRequest::GetSignatureStatus => "getSignatureStatus",
RpcRequest::GetSignatureStatuses => "getSignatureStatuses",
RpcRequest::GetSlot => "getSlot",
RpcRequest::GetSlotLeader => "getSlotLeader",
RpcRequest::GetStorageTurn => "getStorageTurn",

View File

@@ -4,7 +4,7 @@ use solana_sdk::{
clock::{Epoch, Slot},
fee_calculator::{FeeCalculator, FeeRateGovernor},
pubkey::Pubkey,
transaction::Result,
transaction::{Result, TransactionError},
};
use std::{collections::HashMap, net::SocketAddr, str::FromStr};
@@ -54,6 +54,12 @@ pub struct RpcKeyedAccount {
pub account: RpcAccount,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct RpcSignatureResult {
pub err: Option<TransactionError>,
}
/// A duplicate representation of a Message for pretty JSON serialization
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]

View File

@@ -471,7 +471,7 @@ impl SyncClient for ThinClient {
) -> TransportResult<Option<transaction::Result<()>>> {
let status = self
.rpc_client()
.get_signature_status(&signature.to_string())
.get_signature_status(&signature)
.map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
@@ -488,7 +488,7 @@ impl SyncClient for ThinClient {
) -> TransportResult<Option<transaction::Result<()>>> {
let status = self
.rpc_client()
.get_signature_status_with_commitment(&signature.to_string(), commitment_config)
.get_signature_status_with_commitment(&signature, commitment_config)
.map_err(|err| {
io::Error::new(
io::ErrorKind::Other,

View File

@@ -1,7 +1,7 @@
[package]
name = "solana-core"
description = "Blockchain, Rebuilt for Scale"
version = "1.0.12"
version = "1.0.18"
documentation = "https://docs.rs/solana"
homepage = "https://solana.com/"
readme = "../README.md"
@@ -43,34 +43,34 @@ regex = "1.3.4"
serde = "1.0.104"
serde_derive = "1.0.103"
serde_json = "1.0.46"
solana-budget-program = { path = "../programs/budget", version = "1.0.12" }
solana-clap-utils = { path = "../clap-utils", version = "1.0.12" }
solana-client = { path = "../client", version = "1.0.12" }
solana-transaction-status = { path = "../transaction-status", version = "1.0.12" }
solana-faucet = { path = "../faucet", version = "1.0.12" }
solana-budget-program = { path = "../programs/budget", version = "1.0.18" }
solana-clap-utils = { path = "../clap-utils", version = "1.0.18" }
solana-client = { path = "../client", version = "1.0.18" }
solana-transaction-status = { path = "../transaction-status", version = "1.0.18" }
solana-faucet = { path = "../faucet", version = "1.0.18" }
ed25519-dalek = "=1.0.0-pre.1"
solana-ledger = { path = "../ledger", version = "1.0.12" }
solana-logger = { path = "../logger", version = "1.0.12" }
solana-merkle-tree = { path = "../merkle-tree", version = "1.0.12" }
solana-metrics = { path = "../metrics", version = "1.0.12" }
solana-measure = { path = "../measure", version = "1.0.12" }
solana-net-utils = { path = "../net-utils", version = "1.0.12" }
solana-chacha-cuda = { path = "../chacha-cuda", version = "1.0.12" }
solana-perf = { path = "../perf", version = "1.0.12" }
solana-runtime = { path = "../runtime", version = "1.0.12" }
solana-sdk = { path = "../sdk", version = "1.0.12" }
solana-stake-program = { path = "../programs/stake", version = "1.0.12" }
solana-storage-program = { path = "../programs/storage", version = "1.0.12" }
solana-vote-program = { path = "../programs/vote", version = "1.0.12" }
solana-vote-signer = { path = "../vote-signer", version = "1.0.12" }
solana-sys-tuner = { path = "../sys-tuner", version = "1.0.12" }
solana-ledger = { path = "../ledger", version = "1.0.18" }
solana-logger = { path = "../logger", version = "1.0.18" }
solana-merkle-tree = { path = "../merkle-tree", version = "1.0.18" }
solana-metrics = { path = "../metrics", version = "1.0.18" }
solana-measure = { path = "../measure", version = "1.0.18" }
solana-net-utils = { path = "../net-utils", version = "1.0.18" }
solana-chacha-cuda = { path = "../chacha-cuda", version = "1.0.18" }
solana-perf = { path = "../perf", version = "1.0.18" }
solana-runtime = { path = "../runtime", version = "1.0.18" }
solana-sdk = { path = "../sdk", version = "1.0.18" }
solana-stake-program = { path = "../programs/stake", version = "1.0.18" }
solana-storage-program = { path = "../programs/storage", version = "1.0.18" }
solana-vote-program = { path = "../programs/vote", version = "1.0.18" }
solana-vote-signer = { path = "../vote-signer", version = "1.0.18" }
solana-sys-tuner = { path = "../sys-tuner", version = "1.0.18" }
tempfile = "3.1.0"
thiserror = "1.0"
tokio = "0.1"
tokio-codec = "0.1"
tokio-fs = "0.1"
tokio-io = "0.1"
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.0.12" }
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.0.18" }
trees = "0.2.1"
[dev-dependencies]

View File

@@ -4,7 +4,7 @@
// hash on gossip. Monitor gossip for messages from validators in the --trusted-validators
// 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::{
snapshot_package::SnapshotPackage, snapshot_package::SnapshotPackageReceiver,
snapshot_package::SnapshotPackageSender,
@@ -94,6 +94,10 @@ impl AccountsHashVerifier {
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 {
let mut slot_to_hash = HashMap::new();
for (slot, hash) in hashes.iter() {
@@ -119,6 +123,7 @@ impl AccountsHashVerifier {
slot_to_hash: &mut HashMap<Slot, Hash>,
) -> bool {
let mut verified_count = 0;
let mut highest_slot = 0;
if let Some(trusted_validators) = trusted_validators.as_ref() {
for trusted_validator in trusted_validators {
let cluster_info_r = cluster_info.read().unwrap();
@@ -140,6 +145,7 @@ impl AccountsHashVerifier {
verified_count += 1;
}
} else {
highest_slot = std::cmp::max(*slot, highest_slot);
slot_to_hash.insert(*slot, *hash);
}
}
@@ -147,6 +153,10 @@ impl AccountsHashVerifier {
}
}
inc_new_counter_info!("accounts_hash_verifier-hashes_verified", verified_count);
datapoint_info!(
"accounts_hash_verifier",
("highest_slot_verified", highest_slot, i64),
);
false
}
@@ -197,4 +207,57 @@ mod tests {
&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

@@ -1980,10 +1980,20 @@ mod tests {
{
if let EncodedTransaction::Json(transaction) = transaction {
if transaction.signatures[0] == success_signature.to_string() {
assert_eq!(meta.unwrap().status, Ok(()));
let meta = meta.unwrap();
assert_eq!(meta.err, None);
assert_eq!(meta.status, Ok(()));
} else if transaction.signatures[0] == ix_error_signature.to_string() {
let meta = meta.unwrap();
assert_eq!(
meta.unwrap().status,
meta.err,
Some(TransactionError::InstructionError(
0,
InstructionError::CustomError(1)
))
);
assert_eq!(
meta.status,
Err(TransactionError::InstructionError(
0,
InstructionError::CustomError(1)

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_sdk::clock::Slot;
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 {
block_commitment: HashMap<Slot, BlockCommitment>,
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 {
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 {
block_commitment,
total_stake,
bank,
root,
}
}
@@ -53,38 +79,62 @@ impl BlockCommitmentCache {
self.total_stake
}
pub fn get_block_with_depth_commitment(
&self,
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 bank(&self) -> Arc<Bank> {
self.bank.clone()
}
pub fn get_rooted_block_with_commitment(&self, minimum_stake_percentage: f64) -> Option<u64> {
self.get_block_with_depth_commitment(MAX_LOCKOUT_HISTORY - 1, minimum_stake_percentage)
pub fn slot(&self) -> Slot {
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 {
bank: Arc<Bank>,
root: Slot,
total_staked: u64,
}
impl CommitmentAggregationData {
pub fn new(bank: Arc<Bank>, total_staked: u64) -> Self {
Self { bank, total_staked }
pub fn new(bank: Arc<Bank>, root: Slot, total_staked: u64) -> Self {
Self {
bank,
root,
total_staked,
}
}
}
@@ -144,14 +194,24 @@ impl AggregateCommitmentService {
continue;
}
let mut aggregate_commitment_time = Measure::start("aggregate-commitment-ms");
let block_commitment = Self::aggregate_commitment(&ancestors, &aggregation_data.bank);
let mut new_block_commitment =
BlockCommitmentCache::new(block_commitment, aggregation_data.total_staked);
let mut new_block_commitment = BlockCommitmentCache::new(
block_commitment,
aggregation_data.total_staked,
aggregation_data.bank,
aggregation_data.root,
);
let mut w_block_commitment_cache = block_commitment_cache.write().unwrap();
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]
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
let mut cache0 = BlockCommitment::default();
cache0.increase_confirmation_stake(1, 15);
cache0.increase_confirmation_stake(2, 25);
cache0.increase_confirmation_stake(1, 5);
cache0.increase_confirmation_stake(2, 40);
let mut cache1 = BlockCommitment::default();
cache1.increase_confirmation_stake(1, 10);
cache1.increase_confirmation_stake(2, 20);
cache1.increase_confirmation_stake(1, 40);
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();
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);
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_rooted_block_with_commitment(0.1),
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
);
assert_eq!(block_commitment_cache.get_confirmation_count(0), Some(2));
assert_eq!(block_commitment_cache.get_confirmation_count(1), Some(1));
assert_eq!(block_commitment_cache.get_confirmation_count(2), Some(0),);
assert_eq!(block_commitment_cache.get_confirmation_count(3), None,);
}
#[test]

View File

@@ -13,15 +13,18 @@ use std::thread;
use std::thread::{Builder, JoinHandle};
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).
// - To try and keep the RocksDB size under 400GB:
// Seeing about 1600b/shred, using 2000b/shred for margin, so 200m shreds can be stored in 400gb.
// at 5k shreds/slot at 50k tps, this is 500k slots (~5 hours).
// At idle, 60 shreds/slot this is about 4m slots (18 days)
// This is chosen to allow enough time for
// - A validator to download a snapshot from a peer and boot from it
// - To make sure that if a validator needs to reboot from its own snapshot, it has enough slots locally
// to catch back up to where it was when it stopped
pub const DEFAULT_MAX_LEDGER_SHREDS: u64 = 250_000_000;
pub const DEFAULT_MAX_LEDGER_SHREDS: u64 = 200_000_000;
// Allow down to 50m, or 3.5 days at idle, 1hr at 50k load, around ~100GB
pub const DEFAULT_MIN_MAX_LEDGER_SHREDS: u64 = 50_000_000;
// Check for removing slots at this interval so we don't purge too often
// and starve other blockstore users.

View File

@@ -26,6 +26,7 @@ use solana_metrics::inc_new_counter_info;
use solana_runtime::bank::Bank;
use solana_sdk::{
clock::Slot,
genesis_config::GenesisConfig,
hash::Hash,
pubkey::Pubkey,
signature::{Keypair, Signer},
@@ -80,6 +81,7 @@ pub struct ReplayStageConfig {
pub block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
pub transaction_status_sender: Option<TransactionStatusSender>,
pub rewards_recorder_sender: Option<RewardsRecorderSender>,
pub genesis_config: GenesisConfig,
}
pub struct ReplayStage {
@@ -183,6 +185,7 @@ impl ReplayStage {
block_commitment_cache,
transaction_status_sender,
rewards_recorder_sender,
genesis_config,
} = config;
let (root_bank_sender, root_bank_receiver) = channel();
@@ -192,7 +195,7 @@ impl ReplayStage {
// Start the replay stage loop
let (lockouts_sender, commitment_service) =
AggregateCommitmentService::new(&exit, block_commitment_cache);
AggregateCommitmentService::new(&exit, block_commitment_cache.clone());
#[allow(clippy::cognitive_complexity)]
let t_replay = Builder::new()
@@ -246,6 +249,7 @@ impl ReplayStage {
&slot_full_senders,
transaction_status_sender.clone(),
&verify_recyclers,
&genesis_config,
);
datapoint_debug!(
"replay_stage-memory",
@@ -309,7 +313,10 @@ impl ReplayStage {
let start = allocated.get();
if !is_locked_out && vote_threshold {
info!("voting: {} {}", bank.slot(), fork_weight);
subscriptions.notify_subscribers(bank.slot(), &bank_forks);
subscriptions.notify_subscribers(
block_commitment_cache.read().unwrap().slot(),
&bank_forks,
);
if let Some(votable_leader) =
leader_schedule_cache.slot_leader_at(bank.slot(), Some(&bank))
{
@@ -548,6 +555,7 @@ impl ReplayStage {
bank_progress: &mut ForkProgress,
transaction_status_sender: Option<TransactionStatusSender>,
verify_recyclers: &VerifyRecyclers,
genesis_config: &GenesisConfig,
) -> result::Result<usize, BlockstoreProcessorError> {
let tx_count_before = bank_progress.replay_progress.num_txs;
let confirm_result = blockstore_processor::confirm_slot(
@@ -559,6 +567,7 @@ impl ReplayStage {
transaction_status_sender,
None,
verify_recyclers,
Some(genesis_config),
);
let tx_count_after = bank_progress.replay_progress.num_txs;
let tx_count = tx_count_after - tx_count_before;
@@ -647,7 +656,13 @@ impl ReplayStage {
return Err(e.into());
}
}
Self::update_commitment_cache(bank.clone(), total_staked, lockouts_sender);
Self::update_commitment_cache(
bank.clone(),
bank_forks.read().unwrap().root(),
total_staked,
lockouts_sender,
);
if let Some(ref voting_keypair) = voting_keypair {
let node_keypair = cluster_info.read().unwrap().keypair.clone();
@@ -675,10 +690,13 @@ impl ReplayStage {
fn update_commitment_cache(
bank: Arc<Bank>,
root: Slot,
total_staked: u64,
lockouts_sender: &Sender<CommitmentAggregationData>,
) {
if let Err(e) = lockouts_sender.send(CommitmentAggregationData::new(bank, total_staked)) {
if let Err(e) =
lockouts_sender.send(CommitmentAggregationData::new(bank, root, total_staked))
{
trace!("lockouts_sender failed: {:?}", e);
}
}
@@ -725,6 +743,7 @@ impl ReplayStage {
slot_full_senders: &[Sender<(u64, Pubkey)>],
transaction_status_sender: Option<TransactionStatusSender>,
verify_recyclers: &VerifyRecyclers,
genesis_config: &GenesisConfig,
) -> bool {
let mut did_complete_bank = false;
let mut tx_count = 0;
@@ -753,6 +772,7 @@ impl ReplayStage {
bank_progress,
transaction_status_sender.clone(),
verify_recyclers,
genesis_config,
);
match replay_result {
Ok(replay_tx_count) => tx_count += replay_tx_count,
@@ -1397,7 +1417,10 @@ pub(crate) mod tests {
let bank0 = Bank::new(&genesis_config);
let leader_schedule_cache = Arc::new(LeaderScheduleCache::new_from_bank(&bank0));
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 bank_forks = BankForks::new(0, bank0);
bank_forks.working_bank().freeze();
@@ -1694,6 +1717,7 @@ pub(crate) mod tests {
&mut bank0_progress,
None,
&VerifyRecyclers::default(),
&genesis_config,
);
// Check that the erroring bank was marked as dead in the progress map
@@ -1766,7 +1790,12 @@ pub(crate) mod tests {
bank_forks.write().unwrap().insert(bank1);
let arc_bank1 = bank_forks.read().unwrap().get(1).unwrap().clone();
leader_vote(&arc_bank1, &leader_voting_pubkey);
ReplayStage::update_commitment_cache(arc_bank1.clone(), leader_lamports, &lockouts_sender);
ReplayStage::update_commitment_cache(
arc_bank1.clone(),
0,
leader_lamports,
&lockouts_sender,
);
let bank2 = Bank::new_from_parent(&arc_bank1, &Pubkey::default(), arc_bank1.slot() + 1);
let _res = bank2.transfer(10, &genesis_config_info.mint_keypair, &Pubkey::new_rand());
@@ -1777,7 +1806,12 @@ pub(crate) mod tests {
bank_forks.write().unwrap().insert(bank2);
let arc_bank2 = bank_forks.read().unwrap().get(2).unwrap().clone();
leader_vote(&arc_bank2, &leader_voting_pubkey);
ReplayStage::update_commitment_cache(arc_bank2.clone(), leader_lamports, &lockouts_sender);
ReplayStage::update_commitment_cache(
arc_bank2.clone(),
0,
leader_lamports,
&lockouts_sender,
);
thread::sleep(Duration::from_millis(200));
let mut expected0 = BlockCommitment::default();
@@ -1907,10 +1941,20 @@ pub(crate) mod tests {
{
if let EncodedTransaction::Json(transaction) = transaction {
if transaction.signatures[0] == signatures[0].to_string() {
assert_eq!(meta.unwrap().status, Ok(()));
let meta = meta.unwrap();
assert_eq!(meta.err, None);
assert_eq!(meta.status, Ok(()));
} else if transaction.signatures[0] == signatures[1].to_string() {
let meta = meta.unwrap();
assert_eq!(
meta.unwrap().status,
meta.err,
Some(TransactionError::InstructionError(
0,
InstructionError::CustomError(1)
))
);
assert_eq!(
meta.status,
Err(TransactionError::InstructionError(
0,
InstructionError::CustomError(1)

View File

@@ -12,7 +12,7 @@ use solana_faucet::faucet::request_airdrop_transaction;
use solana_ledger::{
bank_forks::BankForks, blockstore::Blockstore, rooted_slot_iterator::RootedSlotIterator,
};
use solana_runtime::{bank::Bank, status_cache::SignatureConfirmationStatus};
use solana_runtime::bank::Bank;
use solana_sdk::{
clock::{Slot, UnixTimestamp},
commitment_config::{CommitmentConfig, CommitmentLevel},
@@ -22,9 +22,11 @@ use solana_sdk::{
pubkey::Pubkey,
signature::Signature,
timing::slot_duration_from_slots_per_year,
transaction::Transaction,
transaction::{self, Transaction},
};
use solana_transaction_status::{
ConfirmedBlock, ConfirmedTransaction, TransactionEncoding, TransactionStatus,
};
use solana_transaction_status::{ConfirmedBlock, TransactionEncoding, TransactionStatus};
use solana_vote_program::vote_state::{VoteState, MAX_LOCKOUT_HISTORY};
use std::{
collections::HashMap,
@@ -35,6 +37,9 @@ use std::{
time::{Duration, Instant},
};
const MAX_QUERY_ITEMS: usize = 256;
const MAX_SLOT_RANGE: u64 = 10_000;
type RpcResponse<T> = Result<Response<T>>;
fn new_response<T>(bank: &Bank, value: T) -> RpcResponse<T> {
@@ -51,6 +56,15 @@ pub struct JsonRpcConfig {
pub faucet_addr: Option<SocketAddr>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcSignatureStatusConfig {
pub search_transaction_history: Option<bool>,
// DEPRECATED
#[serde(flatten)]
pub commitment: Option<CommitmentConfig>,
}
#[derive(Clone)]
pub struct JsonRpcRequestProcessor {
bank_forks: Arc<RwLock<BankForks>>,
@@ -195,11 +209,9 @@ impl JsonRpcRequestProcessor {
match signature {
Err(e) => Err(e),
Ok(sig) => {
let status = bank.get_signature_confirmation_status(&sig);
let status = bank.get_signature_status(&sig);
match status {
Some(SignatureConfirmationStatus { status, .. }) => {
new_response(bank, status.is_ok())
}
Some(status) => new_response(bank, status.is_ok()),
None => new_response(bank, false),
}
}
@@ -398,31 +410,69 @@ impl JsonRpcRequestProcessor {
.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(
&self,
signatures: Vec<Signature>,
signature: Signature,
commitment: Option<CommitmentConfig>,
) -> Option<transaction::Result<()>> {
self.bank(commitment).get_signature_status(&signature)
}
pub fn get_signature_statuses(
&self,
signatures: Vec<Signature>,
config: Option<RpcSignatureStatusConfig>,
) -> RpcResponse<Vec<Option<TransactionStatus>>> {
let mut statuses: Vec<Option<TransactionStatus>> = vec![];
// DEPRECATED
let commitment = config
.clone()
.and_then(|x| x.commitment)
.or_else(|| Some(CommitmentConfig::recent()));
let search_transaction_history = config
.and_then(|x| x.search_transaction_history)
.unwrap_or(false);
let bank = self.bank(commitment);
for signature in signatures {
let status = bank.get_signature_confirmation_status(&signature).map(
|SignatureConfirmationStatus {
slot,
status,
confirmations,
}| TransactionStatus {
slot,
status,
confirmations: if confirmations <= MAX_LOCKOUT_HISTORY {
Some(confirmations)
} else {
None
},
},
);
let status = if let Some(status) = self.get_transaction_status(signature, &bank) {
Some(status)
} else if self.config.enable_rpc_transaction_history && search_transaction_history {
self.blockstore
.get_transaction_status(signature)
.map_err(|_| Error::internal_error())?
.map(|(slot, status_meta)| {
let err = status_meta.status.clone().err();
TransactionStatus {
slot,
status: status_meta.status,
confirmations: None,
err,
}
})
} else {
None
};
statuses.push(status);
}
Ok(Response {
@@ -430,6 +480,63 @@ impl JsonRpcRequestProcessor {
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))
};
let err = status.clone().err();
TransactionStatus {
slot,
status,
confirmations,
err,
}
})
}
pub fn get_confirmed_transaction(
&self,
signature: Signature,
encoding: Option<TransactionEncoding>,
) -> Result<Option<ConfirmedTransaction>> {
if self.config.enable_rpc_transaction_history {
Ok(self
.blockstore
.get_confirmed_transaction(signature, encoding)
.unwrap_or(None))
} else {
Ok(None)
}
}
pub fn get_confirmed_signatures_for_address(
&self,
pubkey: Pubkey,
start_slot: Slot,
end_slot: Slot,
) -> Result<Vec<Signature>> {
if self.config.enable_rpc_transaction_history {
Ok(self
.blockstore
.get_confirmed_signatures_for_address(pubkey, start_slot, end_slot)
.unwrap_or_else(|_| vec![]))
} else {
Ok(vec![])
}
}
}
fn get_tpu_addr(cluster_info: &Arc<RwLock<ClusterInfo>>) -> Result<SocketAddr> {
@@ -457,6 +564,7 @@ impl Metadata for Meta {}
pub trait RpcSol {
type Metadata;
// DEPRECATED
#[rpc(meta, name = "confirmTransaction")]
fn confirm_transaction(
&self,
@@ -465,6 +573,24 @@ pub trait RpcSol {
commitment: Option<CommitmentConfig>,
) -> RpcResponse<bool>;
// DEPRECATED
#[rpc(meta, name = "getSignatureStatus")]
fn get_signature_status(
&self,
meta: Self::Metadata,
signature_str: String,
commitment: Option<CommitmentConfig>,
) -> Result<Option<transaction::Result<()>>>;
// DEPRECATED (used by Trust Wallet)
#[rpc(meta, name = "getSignatureConfirmation")]
fn get_signature_confirmation(
&self,
meta: Self::Metadata,
signature_str: String,
commitment: Option<CommitmentConfig>,
) -> Result<Option<RpcSignatureConfirmation>>;
#[rpc(meta, name = "getAccountInfo")]
fn get_account_info(
&self,
@@ -552,12 +678,12 @@ pub trait RpcSol {
#[rpc(meta, name = "getFeeRateGovernor")]
fn get_fee_rate_governor(&self, meta: Self::Metadata) -> RpcResponse<RpcFeeRateGovernor>;
#[rpc(meta, name = "getSignatureStatus")]
fn get_signature_status(
#[rpc(meta, name = "getSignatureStatuses")]
fn get_signature_statuses(
&self,
meta: Self::Metadata,
signature_strs: Vec<String>,
commitment: Option<CommitmentConfig>,
config: Option<RpcSignatureStatusConfig>,
) -> RpcResponse<Vec<Option<TransactionStatus>>>;
#[rpc(meta, name = "getSlot")]
@@ -652,6 +778,23 @@ pub trait RpcSol {
start_slot: Slot,
end_slot: Option<Slot>,
) -> Result<Vec<Slot>>;
#[rpc(meta, name = "getConfirmedTransaction")]
fn get_confirmed_transaction(
&self,
meta: Self::Metadata,
signature_str: String,
encoding: Option<TransactionEncoding>,
) -> Result<Option<ConfirmedTransaction>>;
#[rpc(meta, name = "getConfirmedSignaturesForAddress")]
fn get_confirmed_signatures_for_address(
&self,
meta: Self::Metadata,
pubkey_str: String,
start_slot: Slot,
end_slot: Slot,
) -> Result<Vec<String>>;
}
pub struct RpcSolImpl;
@@ -883,12 +1026,50 @@ impl RpcSol for RpcSolImpl {
.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(
&self,
meta: Self::Metadata,
signature_strs: Vec<String>,
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,
meta: Self::Metadata,
signature_strs: Vec<String>,
config: Option<RpcSignatureStatusConfig>,
) -> RpcResponse<Vec<Option<TransactionStatus>>> {
if signature_strs.len() > MAX_QUERY_ITEMS {
return Err(Error::invalid_params(format!(
"Too many inputs provided; max {}",
MAX_QUERY_ITEMS
)));
}
let mut signatures: Vec<Signature> = vec![];
for signature_str in signature_strs {
signatures.push(verify_signature(&signature_str)?);
@@ -896,7 +1077,7 @@ impl RpcSol for RpcSolImpl {
meta.request_processor
.read()
.unwrap()
.get_signature_status(signatures, commitment)
.get_signature_statuses(signatures, config)
}
fn get_slot(&self, meta: Self::Metadata, commitment: Option<CommitmentConfig>) -> Result<u64> {
@@ -989,9 +1170,10 @@ impl RpcSol for RpcSolImpl {
.request_processor
.read()
.unwrap()
.get_signature_status(vec![signature], commitment.clone())?
.get_signature_statuses(vec![signature], None)?
.value[0]
.clone()
.filter(|result| result.satisfies_commitment(commitment.unwrap_or_default()))
.map(|x| x.status);
if signature_status == Some(Ok(())) {
@@ -1156,6 +1338,51 @@ impl RpcSol for RpcSolImpl {
fn get_block_time(&self, meta: Self::Metadata, slot: Slot) -> Result<Option<UnixTimestamp>> {
meta.request_processor.read().unwrap().get_block_time(slot)
}
fn get_confirmed_transaction(
&self,
meta: Self::Metadata,
signature_str: String,
encoding: Option<TransactionEncoding>,
) -> Result<Option<ConfirmedTransaction>> {
let signature = verify_signature(&signature_str)?;
meta.request_processor
.read()
.unwrap()
.get_confirmed_transaction(signature, encoding)
}
fn get_confirmed_signatures_for_address(
&self,
meta: Self::Metadata,
pubkey_str: String,
start_slot: Slot,
end_slot: Slot,
) -> Result<Vec<String>> {
let pubkey = verify_pubkey(pubkey_str)?;
if end_slot <= start_slot {
return Err(Error::invalid_params(format!(
"start_slot {} must be smaller than end_slot {}",
start_slot, end_slot
)));
}
if end_slot - start_slot > MAX_SLOT_RANGE {
return Err(Error::invalid_params(format!(
"Slot range too large; max {}",
MAX_SLOT_RANGE
)));
}
meta.request_processor
.read()
.unwrap()
.get_confirmed_signatures_for_address(pubkey, start_slot, end_slot)
.map(|signatures| {
signatures
.iter()
.map(|signature| signature.to_string())
.collect()
})
}
}
#[cfg(test)]
@@ -1220,18 +1447,6 @@ pub mod tests {
) -> RpcHandler {
let (bank_forks, alice, leader_vote_keypair) = new_bank_forks();
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 blockstore = Blockstore::open(&ledger_path).unwrap();
let blockstore = Arc::new(blockstore);
@@ -1247,6 +1462,24 @@ pub mod tests {
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
let vote = Vote {
slots: vec![1],
@@ -1748,6 +1981,76 @@ pub mod tests {
#[test]
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 RpcHandler {
io,
@@ -1759,7 +2062,7 @@ pub mod tests {
} = start_rpc_handler_with_tx(&bob_pubkey);
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]
);
let res = io.handle_request_sync(&req, meta.clone());
@@ -1768,12 +2071,14 @@ pub mod tests {
let result: Option<TransactionStatus> =
serde_json::from_value(json["result"]["value"][0].clone())
.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
let tx = system_transaction::transfer(&alice, &bob_pubkey, 10, blockhash);
let req = format!(
r#"{{"jsonrpc":"2.0","id":1,"method":"getSignatureStatus","params":[["{}"]]}}"#,
r#"{{"jsonrpc":"2.0","id":1,"method":"getSignatureStatuses","params":[["{}"]]}}"#,
tx.signatures[0]
);
let res = io.handle_request_sync(&req, meta.clone());
@@ -1785,7 +2090,7 @@ pub mod tests {
// Test getSignatureStatus request on a TransactionError
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]
);
let res = io.handle_request_sync(&req, meta.clone());
@@ -2117,6 +2422,8 @@ pub mod tests {
fn test_rpc_processor_get_block_commitment() {
let exit = Arc::new(AtomicBool::new(false));
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_slot1 = BlockCommitment::new([9; MAX_LOCKOUT_HISTORY]);
let mut block_commitment: HashMap<u64, BlockCommitment> = HashMap::new();
@@ -2126,8 +2433,12 @@ pub mod tests {
block_commitment
.entry(1)
.or_insert(commitment_slot1.clone());
let block_commitment_cache =
Arc::new(RwLock::new(BlockCommitmentCache::new(block_commitment, 42)));
let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::new(
block_commitment,
42,
bank_forks.read().unwrap().working_bank(),
0,
)));
let ledger_path = get_tmp_ledger_path!();
let blockstore = Blockstore::open(&ledger_path).unwrap();
@@ -2135,7 +2446,7 @@ pub mod tests {
config.enable_validator_exit = true;
let request_processor = JsonRpcRequestProcessor::new(
config,
new_bank_forks().0,
bank_forks,
block_commitment_cache,
Arc::new(blockstore),
StorageState::default(),
@@ -2199,7 +2510,7 @@ pub mod tests {
.get_block_commitment(0)
.map(|block_commitment| block_commitment.commitment)
);
assert_eq!(total_stake, 42);
assert_eq!(total_stake, 10);
let req =
format!(r#"{{"jsonrpc":"2.0","id":1,"method":"getBlockCommitment","params":[2]}}"#);
@@ -2217,7 +2528,7 @@ pub mod tests {
panic!("Expected single response");
};
assert_eq!(commitment_response.commitment, None);
assert_eq!(commitment_response.total_stake, 42);
assert_eq!(commitment_response.total_stake, 10);
}
#[test]
@@ -2246,11 +2557,21 @@ pub mod tests {
{
if let EncodedTransaction::Json(transaction) = transaction {
if transaction.signatures[0] == confirmed_block_signatures[0].to_string() {
let meta = meta.unwrap();
assert_eq!(transaction.message.recent_blockhash, blockhash.to_string());
assert_eq!(meta.unwrap().status, Ok(()));
assert_eq!(meta.status, Ok(()));
assert_eq!(meta.err, None);
} else if transaction.signatures[0] == confirmed_block_signatures[1].to_string() {
let meta = meta.unwrap();
assert_eq!(
meta.unwrap().status,
meta.err,
Some(TransactionError::InstructionError(
0,
InstructionError::CustomError(1)
))
);
assert_eq!(
meta.status,
Err(TransactionError::InstructionError(
0,
InstructionError::CustomError(1)
@@ -2280,11 +2601,21 @@ pub mod tests {
let decoded_transaction: Transaction =
deserialize(&bs58::decode(&transaction).into_vec().unwrap()).unwrap();
if decoded_transaction.signatures[0] == confirmed_block_signatures[0] {
let meta = meta.unwrap();
assert_eq!(decoded_transaction.message.recent_blockhash, blockhash);
assert_eq!(meta.unwrap().status, Ok(()));
assert_eq!(meta.status, Ok(()));
assert_eq!(meta.err, None);
} else if decoded_transaction.signatures[0] == confirmed_block_signatures[1] {
let meta = meta.unwrap();
assert_eq!(
meta.unwrap().status,
meta.err,
Some(TransactionError::InstructionError(
0,
InstructionError::CustomError(1)
))
);
assert_eq!(
meta.status,
Err(TransactionError::InstructionError(
0,
InstructionError::CustomError(1)

View File

@@ -4,8 +4,10 @@ use crate::rpc_subscriptions::{Confirmations, RpcSubscriptions, SlotInfo};
use jsonrpc_core::{Error, ErrorCode, Result};
use jsonrpc_derive::rpc;
use jsonrpc_pubsub::{typed::Subscriber, Session, SubscriptionId};
use solana_client::rpc_response::{Response as RpcResponse, RpcAccount, RpcKeyedAccount};
use solana_sdk::{clock::Slot, pubkey::Pubkey, signature::Signature, transaction};
use solana_client::rpc_response::{
Response as RpcResponse, RpcAccount, RpcKeyedAccount, RpcSignatureResult,
};
use solana_sdk::{clock::Slot, pubkey::Pubkey, signature::Signature};
use std::sync::{atomic, Arc};
// Suppress needless_return due to
@@ -74,7 +76,7 @@ pub trait RpcSolPubSub {
fn signature_subscribe(
&self,
meta: Self::Metadata,
subscriber: Subscriber<RpcResponse<transaction::Result<()>>>,
subscriber: Subscriber<RpcResponse<RpcSignatureResult>>,
signature_str: String,
confirmations: Option<Confirmations>,
);
@@ -225,7 +227,7 @@ impl RpcSolPubSub for RpcSolPubSubImpl {
fn signature_subscribe(
&self,
_meta: Self::Metadata,
subscriber: Subscriber<RpcResponse<transaction::Result<()>>>,
subscriber: Subscriber<RpcResponse<RpcSignatureResult>>,
signature_str: String,
confirmations: Option<Confirmations>,
) {
@@ -312,8 +314,11 @@ impl RpcSolPubSub for RpcSolPubSubImpl {
#[cfg(test)]
mod tests {
use super::*;
use crate::genesis_utils::{create_genesis_config, GenesisConfigInfo};
use crate::rpc_subscriptions::tests::robust_poll_or_panic;
use crate::{
commitment::{BlockCommitment, BlockCommitmentCache},
genesis_utils::{create_genesis_config, GenesisConfigInfo},
rpc_subscriptions::tests::robust_poll_or_panic,
};
use jsonrpc_core::{futures::sync::mpsc, Response};
use jsonrpc_pubsub::{PubSubHandler, Session};
use solana_budget_program::{self, budget_instruction};
@@ -325,7 +330,12 @@ mod tests {
system_program, system_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(
bank_forks: &Arc<RwLock<BankForks>>,
@@ -358,8 +368,13 @@ mod tests {
let bank = Bank::new(&genesis_config);
let blockhash = bank.last_blockhash();
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()
};
// Test signature subscriptions
let tx = system_transaction::transfer(&alice, &bob_pubkey, 20, blockhash);
@@ -372,7 +387,7 @@ mod tests {
// Test signature confirmation notification
let (response, _) = robust_poll_or_panic(receiver);
let expected_res: Option<transaction::Result<()>> = Some(Ok(()));
let expected_res = RpcSignatureResult { err: None };
let expected = json!({
"jsonrpc": "2.0",
"method": "signatureNotification",
@@ -457,7 +472,13 @@ mod tests {
let blockhash = bank.last_blockhash();
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 (subscriber, _id_receiver, receiver) = Subscriber::new_test("accountNotification");
rpc.account_subscribe(
@@ -591,7 +612,13 @@ mod tests {
let bank_forks = Arc::new(RwLock::new(BankForks::new(0, bank)));
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 (subscriber, _id_receiver, receiver) = Subscriber::new_test("accountNotification");
rpc.account_subscribe(session, subscriber, bob.pubkey().to_string(), Some(2));
@@ -622,7 +649,12 @@ mod tests {
let bank_forks = Arc::new(RwLock::new(BankForks::new(0, bank)));
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 (subscriber, _id_receiver, receiver) = Subscriber::new_test("accountNotification");
rpc.account_subscribe(session, subscriber, bob.pubkey().to_string(), Some(2));
@@ -640,10 +672,32 @@ mod tests {
let bank0 = bank_forks.read().unwrap()[0].clone();
let bank1 = Bank::new_from_parent(&bank0, &Pubkey::default(), 1);
bank_forks.write().unwrap().insert(bank1);
rpc.subscriptions.notify_subscribers(1, &bank_forks);
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);
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);
let expected = json!({
"jsonrpc": "2.0",

View File

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

View File

@@ -15,8 +15,9 @@ use solana_ledger::{
blockstore::Blockstore,
snapshot_utils,
};
use solana_sdk::hash::Hash;
use solana_sdk::{hash::Hash, pubkey::Pubkey};
use std::{
collections::HashSet,
net::SocketAddr,
path::{Path, PathBuf},
sync::{mpsc::channel, Arc, RwLock},
@@ -24,6 +25,10 @@ use std::{
};
use tokio::prelude::Future;
// If trusted validators are specified, consider this validator healthy if its latest account hash
// is no further behind than this distance from the latest trusted validator account hash
const HEALTH_CHECK_SLOT_DISTANCE: u64 = 150;
pub struct JsonRpcService {
thread_hdl: JoinHandle<()>,
@@ -37,15 +42,24 @@ struct RpcRequestMiddleware {
ledger_path: PathBuf,
snapshot_archive_path_regex: Regex,
snapshot_config: Option<SnapshotConfig>,
cluster_info: Arc<RwLock<ClusterInfo>>,
trusted_validators: Option<HashSet<Pubkey>>,
}
impl RpcRequestMiddleware {
pub fn new(ledger_path: PathBuf, snapshot_config: Option<SnapshotConfig>) -> Self {
pub fn new(
ledger_path: PathBuf,
snapshot_config: Option<SnapshotConfig>,
cluster_info: Arc<RwLock<ClusterInfo>>,
trusted_validators: Option<HashSet<Pubkey>>,
) -> Self {
Self {
ledger_path,
snapshot_archive_path_regex: Regex::new(r"/snapshot-\d+-[[:alnum:]]+\.tar\.bz2$")
.unwrap(),
snapshot_config,
cluster_info,
trusted_validators,
}
}
@@ -104,6 +118,58 @@ impl RpcRequestMiddleware {
),
}
}
fn health_check(&self) -> &'static str {
let response = if let Some(trusted_validators) = &self.trusted_validators {
let (latest_account_hash_slot, latest_trusted_validator_account_hash_slot) = {
let cluster_info = self.cluster_info.read().unwrap();
(
cluster_info
.get_accounts_hash_for_node(&cluster_info.id())
.map(|hashes| hashes.iter().max_by(|a, b| a.0.cmp(&b.0)))
.flatten()
.map(|slot_hash| slot_hash.0)
.unwrap_or(0),
trusted_validators
.iter()
.map(|trusted_validator| {
cluster_info
.get_accounts_hash_for_node(&trusted_validator)
.map(|hashes| hashes.iter().max_by(|a, b| a.0.cmp(&b.0)))
.flatten()
.map(|slot_hash| slot_hash.0)
.unwrap_or(0)
})
.max()
.unwrap_or(0),
)
};
// This validator is considered healthy if its latest account hash slot is within
// `HEALTH_CHECK_SLOT_DISTANCE` of the latest trusted validator's account hash slot
if latest_account_hash_slot > 0
&& latest_trusted_validator_account_hash_slot > 0
&& latest_account_hash_slot
> latest_trusted_validator_account_hash_slot
.saturating_sub(HEALTH_CHECK_SLOT_DISTANCE)
{
"ok"
} else {
warn!(
"health check: me={}, latest trusted_validator={}",
latest_account_hash_slot, latest_trusted_validator_account_hash_slot
);
"behind"
}
} else {
// No trusted validator point of reference available, so this validator is healthy
// because it's running
"ok"
};
info!("health check: {}", response);
response
}
}
impl RequestMiddleware for RpcRequestMiddleware {
@@ -138,6 +204,16 @@ impl RequestMiddleware for RpcRequestMiddleware {
}
if self.is_get_path(request.uri().path()) {
self.get(request.uri().path())
} else if request.uri().path() == "/health" {
RequestMiddlewareAction::Respond {
should_validate_hosts: true,
response: Box::new(jsonrpc_core::futures::future::ok(
hyper::Response::builder()
.status(hyper::StatusCode::OK)
.body(hyper::Body::from(self.health_check()))
.unwrap(),
)),
}
} else {
RequestMiddlewareAction::Proceed {
should_continue_on_invalid_cors: false,
@@ -161,6 +237,7 @@ impl JsonRpcService {
ledger_path: &Path,
storage_state: StorageState,
validator_exit: Arc<RwLock<Option<ValidatorExit>>>,
trusted_validators: Option<HashSet<Pubkey>>,
) -> Self {
info!("rpc bound to {:?}", rpc_addr);
info!("rpc configuration: {:?}", config);
@@ -186,20 +263,35 @@ impl JsonRpcService {
let rpc = RpcSolImpl;
io.extend_with(rpc.to_delegate());
let server =
ServerBuilder::with_meta_extractor(io, move |_req: &hyper::Request<hyper::Body>| Meta {
let request_middleware = RpcRequestMiddleware::new(
ledger_path,
snapshot_config,
cluster_info.clone(),
trusted_validators,
);
let server = ServerBuilder::with_meta_extractor(
io,
move |_req: &hyper::Request<hyper::Body>| Meta {
request_processor: request_processor.clone(),
cluster_info: cluster_info.clone(),
genesis_hash
}).threads(4)
.cors(DomainsValidation::AllowOnly(vec![
AccessControlAllowOrigin::Any,
]))
.cors_max_age(86400)
.request_middleware(RpcRequestMiddleware::new(ledger_path, snapshot_config))
.start_http(&rpc_addr);
genesis_hash,
},
)
.threads(4)
.cors(DomainsValidation::AllowOnly(vec![
AccessControlAllowOrigin::Any,
]))
.cors_max_age(86400)
.request_middleware(request_middleware)
.start_http(&rpc_addr);
if let Err(e) = server {
warn!("JSON RPC service unavailable error: {:?}. \nAlso, check that port {} is not already in use by another application", e, rpc_addr.port());
warn!(
"JSON RPC service unavailable error: {:?}. \n\
Also, check that port {} is not already in use by another application",
e,
rpc_addr.port()
);
return;
}
@@ -240,6 +332,7 @@ mod tests {
use super::*;
use crate::{
contact_info::ContactInfo,
crds_value::{CrdsData, CrdsValue, SnapshotHash},
genesis_utils::{create_genesis_config, GenesisConfigInfo},
rpc::tests::create_validator_exit,
};
@@ -283,6 +376,7 @@ mod tests {
&PathBuf::from("farf"),
StorageState::default(),
validator_exit,
None,
);
let thread = rpc_service.thread_hdl.thread();
assert_eq!(thread.name().unwrap(), "solana-jsonrpc");
@@ -303,7 +397,11 @@ mod tests {
#[test]
fn test_is_get_path() {
let rrm = RpcRequestMiddleware::new(PathBuf::from("/"), None);
let cluster_info = Arc::new(RwLock::new(ClusterInfo::new_with_invalid_keypair(
ContactInfo::default(),
)));
let rrm = RpcRequestMiddleware::new(PathBuf::from("/"), None, cluster_info.clone(), None);
let rrm_with_snapshot_config = RpcRequestMiddleware::new(
PathBuf::from("/"),
Some(SnapshotConfig {
@@ -311,6 +409,8 @@ mod tests {
snapshot_package_output_path: PathBuf::from("/"),
snapshot_path: PathBuf::from("/"),
}),
cluster_info,
None,
);
assert!(rrm.is_get_path("/genesis.tar.bz2"));
@@ -332,4 +432,95 @@ mod tests {
assert!(!rrm.is_get_path(".."));
assert!(!rrm.is_get_path("🎣"));
}
#[test]
fn test_health_check_with_no_trusted_validators() {
let cluster_info = Arc::new(RwLock::new(ClusterInfo::new_with_invalid_keypair(
ContactInfo::default(),
)));
let rm = RpcRequestMiddleware::new(PathBuf::from("/"), None, cluster_info.clone(), None);
assert_eq!(rm.health_check(), "ok");
}
#[test]
fn test_health_check_with_trusted_validators() {
let cluster_info = Arc::new(RwLock::new(ClusterInfo::new_with_invalid_keypair(
ContactInfo::default(),
)));
let trusted_validators = vec![Pubkey::new_rand(), Pubkey::new_rand(), Pubkey::new_rand()];
let rm = RpcRequestMiddleware::new(
PathBuf::from("/"),
None,
cluster_info.clone(),
Some(trusted_validators.clone().into_iter().collect()),
);
// No account hashes for this node or any trusted validators == "behind"
assert_eq!(rm.health_check(), "behind");
// No account hashes for any trusted validators == "behind"
{
let mut cluster_info = cluster_info.write().unwrap();
cluster_info
.push_accounts_hashes(vec![(1000, Hash::default()), (900, Hash::default())]);
}
assert_eq!(rm.health_check(), "behind");
// This node is ahead of the trusted validators == "ok"
{
let mut cluster_info = cluster_info.write().unwrap();
cluster_info
.gossip
.crds
.insert(
CrdsValue::new_unsigned(CrdsData::AccountsHashes(SnapshotHash::new(
trusted_validators[0].clone(),
vec![
(1, Hash::default()),
(1001, Hash::default()),
(2, Hash::default()),
],
))),
1,
)
.unwrap();
}
assert_eq!(rm.health_check(), "ok");
// Node is slightly behind the trusted validators == "ok"
{
let mut cluster_info = cluster_info.write().unwrap();
cluster_info
.gossip
.crds
.insert(
CrdsValue::new_unsigned(CrdsData::AccountsHashes(SnapshotHash::new(
trusted_validators[1].clone(),
vec![(1000 + HEALTH_CHECK_SLOT_DISTANCE - 1, Hash::default())],
))),
1,
)
.unwrap();
}
assert_eq!(rm.health_check(), "ok");
// Node is far behind the trusted validators == "behind"
{
let mut cluster_info = cluster_info.write().unwrap();
cluster_info
.gossip
.crds
.insert(
CrdsValue::new_unsigned(CrdsData::AccountsHashes(SnapshotHash::new(
trusted_validators[2].clone(),
vec![(1000 + HEALTH_CHECK_SLOT_DISTANCE, Hash::default())],
))),
1,
)
.unwrap();
}
assert_eq!(rm.health_check(), "behind");
}
}

View File

@@ -1,5 +1,6 @@
//! The `pubsub` module implements a threaded subscription service on client RPC request
use crate::commitment::BlockCommitmentCache;
use core::hash::Hash;
use jsonrpc_core::futures::Future;
use jsonrpc_pubsub::{
@@ -7,18 +8,23 @@ use jsonrpc_pubsub::{
SubscriptionId,
};
use serde::Serialize;
use solana_client::rpc_response::{Response, RpcAccount, RpcKeyedAccount, RpcResponseContext};
use solana_client::rpc_response::{
Response, RpcAccount, RpcKeyedAccount, RpcResponseContext, RpcSignatureResult,
};
use solana_ledger::bank_forks::BankForks;
use solana_runtime::bank::Bank;
use solana_sdk::{
account::Account, clock::Slot, pubkey::Pubkey, signature::Signature, transaction,
};
use solana_vote_program::vote_state::MAX_LOCKOUT_HISTORY;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{Receiver, RecvTimeoutError, SendError, Sender};
use std::sync::{
atomic::{AtomicBool, Ordering},
mpsc::{Receiver, RecvTimeoutError, SendError, Sender},
};
use std::thread::{Builder, JoinHandle};
use std::time::Duration;
use std::{
cmp::min,
collections::{HashMap, HashSet},
iter,
sync::{Arc, Mutex, RwLock},
@@ -62,7 +68,7 @@ type RpcProgramSubscriptions = RwLock<
type RpcSignatureSubscriptions = RwLock<
HashMap<
Signature,
HashMap<SubscriptionId, (Sink<Response<transaction::Result<()>>>, Confirmations)>,
HashMap<SubscriptionId, (Sink<Response<RpcSignatureResult>>, Confirmations)>,
>,
>;
type RpcSlotSubscriptions = RwLock<HashMap<SubscriptionId, Sink<SlotInfo>>>;
@@ -80,11 +86,7 @@ fn add_subscription<K, S>(
{
let sink = subscriber.assign_id(sub_id.clone()).unwrap();
let confirmations = confirmations.unwrap_or(0);
let confirmations = if confirmations > MAX_LOCKOUT_HISTORY {
MAX_LOCKOUT_HISTORY
} else {
confirmations
};
let confirmations = min(confirmations, MAX_LOCKOUT_HISTORY + 1);
if let Some(current_hashmap) = subscriptions.get_mut(&hashmap_key) {
current_hashmap.insert(sub_id, (sink, confirmations));
return;
@@ -120,8 +122,8 @@ where
fn check_confirmations_and_notify<K, S, B, F, X>(
subscriptions: &HashMap<K, HashMap<SubscriptionId, (Sink<Response<S>>, Confirmations)>>,
hashmap_key: &K,
current_slot: Slot,
bank_forks: &Arc<RwLock<BankForks>>,
block_commitment_cache: &Arc<RwLock<BlockCommitmentCache>>,
bank_method: B,
filter_results: F,
notifier: &RpcNotifier,
@@ -133,6 +135,10 @@ where
F: Fn(X, u64) -> Box<dyn Iterator<Item = S>>,
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
.read()
.unwrap()
@@ -140,27 +146,29 @@ where
.unwrap()
.ancestors
.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();
if let Some(hashmap) = subscriptions.get(hashmap_key) {
for (sub_id, (sink, confirmations)) in hashmap.iter() {
let desired_slot: Vec<u64> = current_ancestors
.iter()
.filter(|(_, &v)| v == *confirmations)
.map(|(k, _)| k)
.cloned()
.collect();
let root: Vec<u64> = current_ancestors
.iter()
.filter(|(_, &v)| v == 32)
.map(|(k, _)| k)
.cloned()
.collect();
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);
let desired_slot = if *confirmations == 0 {
Some(&current_slot)
} else if *confirmations == MAX_LOCKOUT_HISTORY + 1 {
Some(&root)
} else {
confirmation_slots.get(confirmations)
};
if let Some(&slot) = desired_slot {
let results = {
let bank_forks = bank_forks.read().unwrap();
let desired_bank = bank_forks.get(slot).unwrap();
bank_method(&desired_bank, hashmap_key)
};
for result in filter_results(results, root) {
notifier.notify(
Response {
@@ -201,11 +209,15 @@ fn filter_account_result(
Box::new(iter::empty())
}
fn filter_signature_result<S>(result: Option<S>, _root: Slot) -> Box<dyn Iterator<Item = S>>
where
S: 'static + Clone + Serialize,
{
Box::new(result.into_iter())
fn filter_signature_result(
result: Option<transaction::Result<()>>,
_root: Slot,
) -> Box<dyn Iterator<Item = RpcSignatureResult>> {
Box::new(
result
.into_iter()
.map(|result| RpcSignatureResult { err: result.err() }),
)
}
fn filter_program_results(
@@ -236,7 +248,10 @@ pub struct RpcSubscriptions {
impl Default for RpcSubscriptions {
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 +264,10 @@ impl Drop for 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): (
Sender<NotificationEntry>,
Receiver<NotificationEntry>,
@@ -288,6 +306,7 @@ impl RpcSubscriptions {
signature_subscriptions_clone,
slot_subscriptions_clone,
root_subscriptions_clone,
block_commitment_cache,
);
})
.unwrap();
@@ -307,8 +326,8 @@ impl RpcSubscriptions {
fn check_account(
pubkey: &Pubkey,
current_slot: Slot,
bank_forks: &Arc<RwLock<BankForks>>,
block_commitment_cache: &Arc<RwLock<BlockCommitmentCache>>,
account_subscriptions: Arc<RpcAccountSubscriptions>,
notifier: &RpcNotifier,
) {
@@ -316,8 +335,8 @@ impl RpcSubscriptions {
check_confirmations_and_notify(
&subscriptions,
pubkey,
current_slot,
bank_forks,
block_commitment_cache,
Bank::get_account_modified_since_parent,
filter_account_result,
notifier,
@@ -326,8 +345,8 @@ impl RpcSubscriptions {
fn check_program(
program_id: &Pubkey,
current_slot: Slot,
bank_forks: &Arc<RwLock<BankForks>>,
block_commitment_cache: &Arc<RwLock<BlockCommitmentCache>>,
program_subscriptions: Arc<RpcProgramSubscriptions>,
notifier: &RpcNotifier,
) {
@@ -335,8 +354,8 @@ impl RpcSubscriptions {
check_confirmations_and_notify(
&subscriptions,
program_id,
current_slot,
bank_forks,
block_commitment_cache,
Bank::get_program_accounts_modified_since_parent,
filter_program_results,
notifier,
@@ -345,8 +364,8 @@ impl RpcSubscriptions {
fn check_signature(
signature: &Signature,
current_slot: Slot,
bank_forks: &Arc<RwLock<BankForks>>,
block_commitment_cache: &Arc<RwLock<BlockCommitmentCache>>,
signature_subscriptions: Arc<RpcSignatureSubscriptions>,
notifier: &RpcNotifier,
) {
@@ -354,8 +373,8 @@ impl RpcSubscriptions {
let notified_ids = check_confirmations_and_notify(
&subscriptions,
signature,
current_slot,
bank_forks,
block_commitment_cache,
Bank::get_signature_status_processed_since_parent,
filter_signature_result,
notifier,
@@ -417,7 +436,7 @@ impl RpcSubscriptions {
signature: Signature,
confirmations: Option<Confirmations>,
sub_id: SubscriptionId,
subscriber: Subscriber<Response<transaction::Result<()>>>,
subscriber: Subscriber<Response<RpcSignatureResult>>,
) {
let mut subscriptions = self.signature_subscriptions.write().unwrap();
add_subscription(
@@ -499,6 +518,7 @@ impl RpcSubscriptions {
signature_subscriptions: Arc<RpcSignatureSubscriptions>,
slot_subscriptions: Arc<RpcSlotSubscriptions>,
root_subscriptions: Arc<RpcRootSubscriptions>,
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
) {
loop {
if exit.load(Ordering::Relaxed) {
@@ -518,7 +538,7 @@ impl RpcSubscriptions {
notifier.notify(root, sink);
}
}
NotificationEntry::Bank((current_slot, bank_forks)) => {
NotificationEntry::Bank((_current_slot, bank_forks)) => {
let pubkeys: Vec<_> = {
let subs = account_subscriptions.read().unwrap();
subs.keys().cloned().collect()
@@ -526,8 +546,8 @@ impl RpcSubscriptions {
for pubkey in &pubkeys {
Self::check_account(
pubkey,
current_slot,
&bank_forks,
&block_commitment_cache,
account_subscriptions.clone(),
&notifier,
);
@@ -540,8 +560,8 @@ impl RpcSubscriptions {
for program_id in &programs {
Self::check_program(
program_id,
current_slot,
&bank_forks,
&block_commitment_cache,
program_subscriptions.clone(),
&notifier,
);
@@ -554,8 +574,8 @@ impl RpcSubscriptions {
for signature in &signatures {
Self::check_signature(
signature,
current_slot,
&bank_forks,
&block_commitment_cache,
signature_subscriptions.clone(),
&notifier,
);
@@ -596,7 +616,10 @@ impl RpcSubscriptions {
#[cfg(test)]
pub(crate) mod tests {
use super::*;
use crate::genesis_utils::{create_genesis_config, GenesisConfigInfo};
use crate::{
commitment::BlockCommitment,
genesis_utils::{create_genesis_config, GenesisConfigInfo},
};
use jsonrpc_core::futures::{self, stream::Stream};
use jsonrpc_pubsub::typed::Subscriber;
use solana_budget_program;
@@ -663,7 +686,10 @@ pub(crate) mod tests {
Subscriber::new_test("accountNotification");
let sub_id = SubscriptionId::Number(0 as u64);
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);
assert!(subscriptions
@@ -732,7 +758,10 @@ pub(crate) mod tests {
Subscriber::new_test("programNotification");
let sub_id = SubscriptionId::Number(0 as u64);
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(
solana_budget_program::id(),
None,
@@ -812,27 +841,41 @@ pub(crate) mod tests {
.unwrap()
.process_transaction(&processed_tx)
.unwrap();
let bank1 = bank_forks[1].clone();
let bank_forks = Arc::new(RwLock::new(bank_forks));
let exit = Arc::new(AtomicBool::new(false));
let subscriptions = RpcSubscriptions::new(&exit);
let mut cache0 = BlockCommitment::default();
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");
let (processed_sub, _id_receiver, processed_recv) =
Subscriber::new_test("signatureNotification");
subscriptions.add_signature_subscription(
past_bank_tx.signatures[0],
Some(0),
SubscriptionId::Number(1 as u64),
Subscriber::new_test("signatureNotification").0,
past_bank_sub1,
);
subscriptions.add_signature_subscription(
past_bank_tx.signatures[0],
Some(1),
SubscriptionId::Number(2 as u64),
past_bank_sub,
past_bank_sub2,
);
subscriptions.add_signature_subscription(
processed_tx.signatures[0],
@@ -855,43 +898,48 @@ pub(crate) mod tests {
}
subscriptions.notify_subscribers(1, &bank_forks);
let expected_res: Option<transaction::Result<()>> = Some(Ok(()));
let expected_res = RpcSignatureResult { err: None };
let expected = json!({
"jsonrpc": "2.0",
"method": "signatureNotification",
"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);
struct Notification {
slot: Slot,
id: u64,
}
let expected = json!({
"jsonrpc": "2.0",
"method": "signatureNotification",
"params": {
"result": {
"context": { "slot": 1 },
"value": expected_res,
},
"subscription": 3,
}
});
let expected_notification = |exp: Notification| -> String {
let json = json!({
"jsonrpc": "2.0",
"method": "signatureNotification",
"params": {
"result": {
"context": { "slot": exp.slot },
"value": &expected_res,
},
"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);
assert_eq!(serde_json::to_string(&expected).unwrap(), response);
let sig_subs = subscriptions.signature_subscriptions.read().unwrap();
assert_eq!(expected, response);
// Subscription should be automatically removed after notification
let sig_subs = subscriptions.signature_subscriptions.read().unwrap();
assert!(!sig_subs.contains_key(&processed_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);
assert!(!sig_subs.contains_key(&past_bank_tx.signatures[0]));
// Unprocessed signature subscription should not be removed
assert_eq!(
@@ -906,7 +954,10 @@ pub(crate) mod tests {
Subscriber::new_test("slotNotification");
let sub_id = SubscriptionId::Number(0 as u64);
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);
assert!(subscriptions
@@ -944,7 +995,10 @@ pub(crate) mod tests {
Subscriber::new_test("rootNotification");
let sub_id = SubscriptionId::Number(0 as u64);
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);
assert!(subscriptions

View File

@@ -9,6 +9,7 @@ use crate::{
use bincode::serialize;
use rand::{thread_rng, Rng};
use solana_ledger::blockstore::Blockstore;
use solana_measure::measure::Measure;
use solana_measure::thread_mem_usage;
use solana_metrics::{datapoint_debug, inc_new_counter_debug};
use solana_perf::packet::{Packets, PacketsRecycler};
@@ -184,12 +185,33 @@ impl ServeRepair {
blockstore: Option<&Arc<Blockstore>>,
requests_receiver: &PacketReceiver,
response_sender: &PacketSender,
max_packets: &mut usize,
) -> Result<()> {
//TODO cache connections
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);
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);
}
}
let mut time = Measure::start("repair::handle_packets");
for reqs in reqs_v {
Self::handle_packets(obj, &recycler, blockstore, reqs, response_sender);
}
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(())
}
@@ -204,22 +226,26 @@ impl ServeRepair {
let recycler = PacketsRecycler::default();
Builder::new()
.name("solana-repair-listen".to_string())
.spawn(move || loop {
let result = Self::run_listen(
&me,
&recycler,
blockstore.as_ref(),
&requests_receiver,
&response_sender,
);
match result {
Err(Error::RecvTimeoutError(_)) | Ok(_) => {}
Err(err) => info!("repair listener error: {:?}", err),
};
if exit.load(Ordering::Relaxed) {
return;
.spawn(move || {
let mut max_packets = 1024;
loop {
let result = Self::run_listen(
&me,
&recycler,
blockstore.as_ref(),
&requests_receiver,
&response_sender,
&mut max_packets,
);
match result {
Err(Error::RecvTimeoutError(_)) | Ok(_) => {}
Err(err) => info!("repair listener error: {:?}", err),
};
if exit.load(Ordering::Relaxed) {
return;
}
thread_mem_usage::datapoint("solana-repair-listen");
}
thread_mem_usage::datapoint("solana-repair-listen");
})
.unwrap()
}

View File

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

View File

@@ -70,9 +70,14 @@ impl TransactionStatusService {
}
.expect("FeeCalculator must exist");
let fee = fee_calculator.calculate_fee(transaction.message());
let (writable_keys, readonly_keys) =
transaction.message.get_account_keys_by_lock_type();
blockstore
.write_transaction_status(
(slot, transaction.signatures[0]),
slot,
transaction.signatures[0],
writable_keys,
readonly_keys,
&TransactionStatusMeta {
status,
fee,

View File

@@ -26,6 +26,7 @@ use solana_ledger::{
snapshot_package::SnapshotPackageSender,
};
use solana_sdk::{
genesis_config::GenesisConfig,
pubkey::Pubkey,
signature::{Keypair, Signer},
};
@@ -67,6 +68,7 @@ pub struct TvuConfig {
pub halt_on_trusted_validators_accounts_hash_mismatch: bool,
pub trusted_validators: Option<HashSet<Pubkey>>,
pub accounts_hash_fault_injection_slots: u64,
pub genesis_config: GenesisConfig,
}
impl Tvu {
@@ -182,9 +184,10 @@ impl Tvu {
slot_full_senders: vec![blockstream_slot_sender],
latest_root_senders: vec![ledger_cleanup_slot_sender],
accounts_hash_sender: Some(accounts_hash_sender),
block_commitment_cache,
block_commitment_cache: block_commitment_cache.clone(),
transaction_status_sender,
rewards_recorder_sender,
genesis_config: tvu_config.genesis_config,
};
let (replay_stage, root_bank_receiver) = ReplayStage::new(
@@ -226,6 +229,7 @@ impl Tvu {
&exit,
&bank_forks,
&cluster_info,
block_commitment_cache,
);
Tvu {
@@ -314,7 +318,10 @@ pub mod tests {
&StorageState::default(),
None,
l_receiver,
&Arc::new(RpcSubscriptions::new(&exit)),
&Arc::new(RpcSubscriptions::new(
&exit,
Arc::new(RwLock::new(BlockCommitmentCache::default())),
)),
&poh_recorder,
&leader_schedule_cache,
&exit,

View File

@@ -239,7 +239,7 @@ impl Validator {
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)| {
if ContactInfo::is_valid_address(&node.info.rpc) {
@@ -262,6 +262,7 @@ impl Validator {
ledger_path,
storage_state.clone(),
validator_exit.clone(),
config.trusted_validators.clone(),
),
PubSubService::new(
&subscriptions,
@@ -316,7 +317,7 @@ impl Validator {
std::thread::park();
}
let poh_config = Arc::new(genesis_config.poh_config);
let poh_config = Arc::new(genesis_config.poh_config.clone());
let (mut poh_recorder, entry_receiver) = PohRecorder::new_with_clear_signal(
bank.tick_height(),
bank.last_blockhash(),
@@ -443,6 +444,7 @@ impl Validator {
shred_version: node.info.shred_version,
trusted_validators: config.trusted_validators.clone(),
accounts_hash_fault_injection_slots: config.accounts_hash_fault_injection_slots,
genesis_config,
},
);

View File

@@ -3,8 +3,8 @@ use solana_client::{
rpc_client::RpcClient,
};
use solana_core::{
rpc_pubsub_service::PubSubService, rpc_subscriptions::RpcSubscriptions,
validator::TestValidator,
commitment::BlockCommitmentCache, rpc_pubsub_service::PubSubService,
rpc_subscriptions::RpcSubscriptions, validator::TestValidator,
};
use solana_sdk::{
commitment_config::CommitmentConfig, pubkey::Pubkey, rpc_port, signature::Signer,
@@ -15,7 +15,7 @@ use std::{
net::{IpAddr, SocketAddr},
sync::{
atomic::{AtomicBool, Ordering},
Arc,
Arc, RwLock,
},
thread::sleep,
time::{Duration, Instant},
@@ -58,7 +58,7 @@ fn test_rpc_client() {
let now = Instant::now();
while now.elapsed().as_secs() <= 20 {
let response = client
.confirm_transaction_with_commitment(signature.as_str(), CommitmentConfig::default())
.confirm_transaction_with_commitment(&signature, CommitmentConfig::default())
.unwrap();
if response.value {
@@ -85,7 +85,10 @@ fn test_slot_subscription() {
rpc_port::DEFAULT_RPC_PUBSUB_PORT,
);
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);
std::thread::sleep(Duration::from_millis(400));

View File

@@ -9,15 +9,12 @@ use reqwest::{self, header::CONTENT_TYPE};
use serde_json::{json, Value};
use solana_client::{
rpc_client::{get_rpc_request_str, RpcClient},
rpc_response::Response,
rpc_response::{Response, RpcSignatureResult},
};
use solana_core::{rpc_pubsub::gen_client::Client as PubsubClient, validator::TestValidator};
use solana_sdk::{
commitment_config::CommitmentConfig,
hash::Hash,
pubkey::Pubkey,
system_transaction,
transaction::{self, Transaction},
commitment_config::CommitmentConfig, hash::Hash, pubkey::Pubkey, system_transaction,
transaction::Transaction,
};
use std::{
collections::HashSet,
@@ -224,7 +221,7 @@ fn test_rpc_subscriptions() {
let mut rt = Runtime::new().unwrap();
let rpc_pubsub_url = format!("ws://{}/", leader_data.rpc_pubsub);
let (status_sender, status_receiver) = channel::<(String, Response<transaction::Result<()>>)>();
let (status_sender, status_receiver) = channel::<(String, Response<RpcSignatureResult>)>();
let status_sender = Arc::new(Mutex::new(status_sender));
let (sent_sender, sent_receiver) = channel::<()>();
let sent_sender = Arc::new(Mutex::new(sent_sender));
@@ -296,7 +293,7 @@ fn test_rpc_subscriptions() {
let timeout = deadline.saturating_duration_since(Instant::now());
match status_receiver.recv_timeout(timeout) {
Ok((sig, result)) => {
assert!(result.value.is_ok());
assert!(result.value.err.is_none());
assert!(signature_set.remove(&sig));
}
Err(_err) => {

View File

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

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-crate-features"
version = "1.0.12"
version = "1.0.18"
description = "Solana Crate Features"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"

View File

@@ -3,7 +3,9 @@ set -e
cd "$(dirname "$0")"
usage=$(cargo -q run -p solana-cli -- -C ~/.foo --help | sed 's|'"$HOME"'|~|g')
: "${rust_stable:=}" # Pacify shellcheck
usage=$(cargo +"$rust_stable" -q run -p solana-cli -- -C ~/.foo --help | sed -e 's|'"$HOME"'|~|g' -e 's/[[:space:]]\+$//')
out=${1:-src/cli/usage.md}
@@ -29,6 +31,6 @@ in_subcommands=0
while read -r subcommand rest; do
[[ $subcommand == "SUBCOMMANDS:" ]] && in_subcommands=1 && continue
if ((in_subcommands)); then
section "$(cargo -q run -p solana-cli -- help "$subcommand" | sed 's|'"$HOME"'|~|g')" "####" >> "$out"
section "$(cargo +"$rust_stable" -q run -p solana-cli -- help "$subcommand" | sed 's|'"$HOME"'|~|g')" "####" >> "$out"
fi
done <<<"$usage">>"$out"

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

View File

@@ -6,10 +6,11 @@
* [Trust Wallet](wallet/trust-wallet.md)
* [Ledger Live](wallet/ledger-live.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 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)
* [Install the Solana Command Line Tool Suite](cli/install-solana-cli-tools.md)
* [Generate Keys](cli/generate-keys.md)

View File

@@ -14,7 +14,6 @@ To interact with a Solana node inside a JavaScript application, use the [solana-
## Methods
* [confirmTransaction](jsonrpc-api.md#confirmtransaction)
* [getAccountInfo](jsonrpc-api.md#getaccountinfo)
* [getBalance](jsonrpc-api.md#getbalance)
* [getBlockCommitment](jsonrpc-api.md#getblockcommitment)
@@ -22,6 +21,8 @@ To interact with a Solana node inside a JavaScript application, use the [solana-
* [getClusterNodes](jsonrpc-api.md#getclusternodes)
* [getConfirmedBlock](jsonrpc-api.md#getconfirmedblock)
* [getConfirmedBlocks](jsonrpc-api.md#getconfirmedblocks)
* [getConfirmedSignaturesForAddress](jsonrpc-api.md#getconfirmedsignaturesforaddress)
* [getConfirmedTransaction](jsonrpc-api.md#getconfirmedtransaction)
* [getEpochInfo](jsonrpc-api.md#getepochinfo)
* [getEpochSchedule](jsonrpc-api.md#getepochschedule)
* [getFeeCalculatorForBlockhash](jsonrpc-api.md#getfeecalculatorforblockhash)
@@ -33,7 +34,7 @@ To interact with a Solana node inside a JavaScript application, use the [solana-
* [getMinimumBalanceForRentExemption](jsonrpc-api.md#getminimumbalanceforrentexemption)
* [getProgramAccounts](jsonrpc-api.md#getprogramaccounts)
* [getRecentBlockhash](jsonrpc-api.md#getrecentblockhash)
* [getSignatureStatus](jsonrpc-api.md#getsignaturestatus)
* [getSignatureStatuses](jsonrpc-api.md#getsignaturestatuses)
* [getSlot](jsonrpc-api.md#getslot)
* [getSlotLeader](jsonrpc-api.md#getslotleader)
* [getSlotsPerSegment](jsonrpc-api.md#getslotspersegment)
@@ -114,31 +115,18 @@ Many methods that take a commitment parameter return an RpcResponse JSON object
* `value` : The value returned by the operation itself.
## Health Check
Although not a JSON RPC API, a `GET /heath` at the RPC HTTP Endpoint provides a
health-check mechanism for use by load balancers or other network
infrastructure. This request will always return a HTTP 200 OK response with a body of
"ok" or "behind" based on the following conditions:
1. If one or more `--trusted-validator` arguments are provided to `solana-validator`, "ok" is returned
when the node has within `HEALTH_CHECK_SLOT_DISTANCE` slots of the highest trusted validator,
otherwise "behind" is returned.
2. "ok" is always returned if no trusted validators are provided.
## JSON RPC API Reference
### confirmTransaction
Returns a transaction receipt
#### Parameters:
* `<string>` - Signature of Transaction to confirm, as base-58 encoded string
* `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
#### Results:
* `RpcResponse<bool>` - RpcResponse JSON object with `value` field set to Transaction status, boolean true if Transaction is confirmed
#### Example:
```bash
// Request
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"confirmTransaction", "params":["5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"]}' http://localhost:8899
// Result
{"jsonrpc":"2.0","result":{"context":{"slot":1},"value":true},"id":1}
```
### getAccountInfo
Returns all information associated with the account of provided Pubkey
@@ -298,12 +286,13 @@ The result field will be an object with the following fields:
* `transactions: <array>` - an array of JSON objects containing:
* `transaction: <object|string>` - [Transaction](#transaction-structure) object, either in JSON format or base-58 encoded binary data, depending on encoding parameter
* `meta: <object>` - transaction status metadata object, containing `null` or:
* `status: <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#L18)
* `fee: <u64>` - fee this transaction was charged, as u64 integer
* `preBalances: <array>` - array of u64 account balances from before the transaction was processed
* `postBalances: <array>` - array of u64 account balances after the transaction was processed
* `err: <object | null>` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L14)
* `fee: <u64>` - fee this transaction was charged, as u64 integer
* `preBalances: <array>` - array of u64 account balances from before the transaction was processed
* `postBalances: <array>` - array of u64 account balances after the transaction was processed
* DEPRECATED: `status: <object>` - Transaction status
* `"Ok": <null>` - Transaction was successful
* `"Err": <ERR>` - Transaction failed with TransactionError
* `rewards: <array>` - an array of JSON objects containing:
* `pubkey: <string>` - The public key, as base-58 encoded string, of the account that received the reward
* `lamports: <i64>`- number of reward lamports credited or debited by the account, as a i64
@@ -315,13 +304,13 @@ The result field will be an object with the following fields:
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedBlock","params":[430, "json"]}' localhost:8899
// Result
{"jsonrpc":"2.0","result":{"blockhash":"Gp3t5bfDsJv1ovP8cB1SuRhXVuoTqDv7p3tymyubYg5","parentSlot":429,"previousBlockhash":"EFejToxii1L5aUF2NrK9dsbAEmZSNyN5nsipmZHQR1eA","transactions":[{"transaction":{"message":{"accountKeys":["6H94zdiaYfRfPfKjYLjyr2VFBg6JHXygy84r3qhc3NsC","39UAy8hsoYPywGPGdmun747omSr79zLSjqvPJN3zetoH","SysvarS1otHashes111111111111111111111111111","SysvarC1ock11111111111111111111111111111111","Vote111111111111111111111111111111111111111"],"header":{"numReadonlySignedAccounts":0,"numReadonlyUnsignedAccounts":3,"numRequiredSignatures":2},"instructions":[{"accounts":[1,2,3],"data":"29z5mr1JoRmJYQ6ynmk3pf31cGFRziAF1M3mT3L6sFXf5cKLdkEaMXMT8AqLpD4CpcupHmuMEmtZHpomrwfdZetSomNy3d","programIdIndex":4}],"recentBlockhash":"EFejToxii1L5aUF2NrK9dsbAEmZSNyN5nsipmZHQR1eA"},"signatures":["35YGay1Lwjwgxe9zaH6APSHbt9gYQUCtBWTNL3aVwVGn9xTFw2fgds7qK5AL29mP63A9j3rh8KpN1TgSR62XCaby","4vANMjSKiwEchGSXwVrQkwHnmsbKQmy9vdrsYxWdCup1bLsFzX8gKrFTSVDCZCae2dbxJB9mPNhqB2sD1vvr4sAD"]},"meta":{"fee":1000,"postBalances":[499999972500,15298080,1,1,1],"preBalances":[499999990500,15298080,1,1,1],"status":{"Ok":null}}}]},"id":1}
{"jsonrpc":"2.0","result":{"blockhash":"Gp3t5bfDsJv1ovP8cB1SuRhXVuoTqDv7p3tymyubYg5","parentSlot":429,"previousBlockhash":"EFejToxii1L5aUF2NrK9dsbAEmZSNyN5nsipmZHQR1eA","transactions":[{"transaction":{"message":{"accountKeys":["6H94zdiaYfRfPfKjYLjyr2VFBg6JHXygy84r3qhc3NsC","39UAy8hsoYPywGPGdmun747omSr79zLSjqvPJN3zetoH","SysvarS1otHashes111111111111111111111111111","SysvarC1ock11111111111111111111111111111111","Vote111111111111111111111111111111111111111"],"header":{"numReadonlySignedAccounts":0,"numReadonlyUnsignedAccounts":3,"numRequiredSignatures":2},"instructions":[{"accounts":[1,2,3],"data":"29z5mr1JoRmJYQ6ynmk3pf31cGFRziAF1M3mT3L6sFXf5cKLdkEaMXMT8AqLpD4CpcupHmuMEmtZHpomrwfdZetSomNy3d","programIdIndex":4}],"recentBlockhash":"EFejToxii1L5aUF2NrK9dsbAEmZSNyN5nsipmZHQR1eA"},"signatures":["35YGay1Lwjwgxe9zaH6APSHbt9gYQUCtBWTNL3aVwVGn9xTFw2fgds7qK5AL29mP63A9j3rh8KpN1TgSR62XCaby","4vANMjSKiwEchGSXwVrQkwHnmsbKQmy9vdrsYxWdCup1bLsFzX8gKrFTSVDCZCae2dbxJB9mPNhqB2sD1vvr4sAD"]},"meta":{"err":null,"fee":18000,"postBalances":[499999972500,15298080,1,1,1],"preBalances":[499999990500,15298080,1,1,1],"status":{"Ok":null}}}]},"id":1}
// Request
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedBlock","params":[430, "binary"]}' localhost:8899
// Result
{"jsonrpc":"2.0","result":{"blockhash":"Gp3t5bfDsJv1ovP8cB1SuRhXVuoTqDv7p3tymyubYg5","parentSlot":429,"previousBlockhash":"EFejToxii1L5aUF2NrK9dsbAEmZSNyN5nsipmZHQR1eA","transactions":[{"transaction":"81UZJt4dh4Do66jDhrgkQudS8J2N6iG3jaVav7gJrqJSFY4Ug53iA9JFJZh2gxKWcaFdLJwhHx9mRdg9JwDAWB4ywiu5154CRwXV4FMdnPLg7bhxRLwhhYaLsVgMF5AyNRcTzjCVoBvqFgDU7P8VEKDEiMvD3qxzm1pLZVxDG1LTQpT3Dz4Uviv4KQbFQNuC22KupBoyHFB7Zh6KFdMqux4M9PvhoqcoJsJKwXjWpKu7xmEKnnrSbfLadkgjBmmjhW3fdTrFvnhQdTkhtdJxUL1xS9GMuJQer8YgSKNtUXB1eXZQwXU8bU2BjYkZE6Q5Xww8hu9Z4E4Mo4QsooVtHoP6BM3NKw8zjVbWfoCQqxTrwuSzrNCWCWt58C24LHecH67CTt2uXbYSviixvrYkK7A3t68BxTJcF1dXJitEPTFe2ceTkauLJqrJgnER4iUrsjr26T8YgWvpY9wkkWFSviQW6wV5RASTCUasVEcrDiaKj8EQMkgyDoe9HyKitSVg67vMWJFpUXpQobseWJUs5FTWWzmfHmFp8FZ","meta":{"fee":1000,"postBalances":[499999972500,15298080,1,1,1],"preBalances":[499999990500,15298080,1,1,1],"status":{"Ok":null}}}]},"id":1}
{"jsonrpc":"2.0","result":{"blockhash":"Gp3t5bfDsJv1ovP8cB1SuRhXVuoTqDv7p3tymyubYg5","parentSlot":429,"previousBlockhash":"EFejToxii1L5aUF2NrK9dsbAEmZSNyN5nsipmZHQR1eA","transactions":[{"transaction":"81UZJt4dh4Do66jDhrgkQudS8J2N6iG3jaVav7gJrqJSFY4Ug53iA9JFJZh2gxKWcaFdLJwhHx9mRdg9JwDAWB4ywiu5154CRwXV4FMdnPLg7bhxRLwhhYaLsVgMF5AyNRcTzjCVoBvqFgDU7P8VEKDEiMvD3qxzm1pLZVxDG1LTQpT3Dz4Uviv4KQbFQNuC22KupBoyHFB7Zh6KFdMqux4M9PvhoqcoJsJKwXjWpKu7xmEKnnrSbfLadkgjBmmjhW3fdTrFvnhQdTkhtdJxUL1xS9GMuJQer8YgSKNtUXB1eXZQwXU8bU2BjYkZE6Q5Xww8hu9Z4E4Mo4QsooVtHoP6BM3NKw8zjVbWfoCQqxTrwuSzrNCWCWt58C24LHecH67CTt2uXbYSviixvrYkK7A3t68BxTJcF1dXJitEPTFe2ceTkauLJqrJgnER4iUrsjr26T8YgWvpY9wkkWFSviQW6wV5RASTCUasVEcrDiaKj8EQMkgyDoe9HyKitSVg67vMWJFpUXpQobseWJUs5FTWWzmfHmFp8FZ","meta":{"err":null,"fee":18000,"postBalances":[499999972500,15298080,1,1,1],"preBalances":[499999990500,15298080,1,1,1],"status":{"Ok":null}}}]},"id":1}
```
#### Transaction Structure
@@ -368,6 +357,72 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"m
{"jsonrpc":"2.0","result":[5,6,7,8,9,10],"id":1}
```
### getConfirmedSignaturesForAddress
Returns a list of all the confirmed signatures for transactions involving an address, within a specified Slot range. Max range allowed is 10_000 Slots.
#### Parameters:
* `<string>` - account address as base-58 encoded string
* `<u64>` - start slot, inclusive
* `<u64>` - end slot, inclusive
#### Results:
The result field will be an array of:
* `<string>` - transaction signature as base-58 encoded string
The signatures will be ordered based on the Slot in which they were confirmed in, from lowest to highest Slot
#### Example:
```bash
// Request
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedSignaturesForAddress","params":["6H94zdiaYfRfPfKjYLjyr2VFBg6JHXygy84r3qhc3NsC", 0, 100]}' localhost:8899
// Result
{"jsonrpc":"2.0","result":{["35YGay1Lwjwgxe9zaH6APSHbt9gYQUCtBWTNL3aVwVGn9xTFw2fgds7qK5AL29mP63A9j3rh8KpN1TgSR62XCaby","4bJdGN8Tt2kLWZ3Fa1dpwPSEkXWWTSszPSf1rRVsCwNjxbbUdwTeiWtmi8soA26YmwnKD4aAxNp8ci1Gjpdv4gsr","4LQ14a7BYY27578Uj8LPCaVhSdJGLn9DJqnUJHpy95FMqdKf9acAhUhecPQNjNUy6VoNFUbvwYkPociFSf87cWbG"]},"id":1}
```
### getConfirmedTransaction
Returns transaction details for a confirmed transaction
#### Parameters:
* `<string>` - transaction signature as base-58 encoded string
* `<string>` - (optional) encoding for the returned Transaction, either "json" or "binary". If not provided, the default encoding is JSON.
#### Results:
The result field will be an object with the following fields:
* `slot: <u64>` - the slot this transaction was processed in
* `transaction: <object|string>` - [Transaction](#transaction-structure) object, either in JSON format or base-58 encoded binary data, depending on encoding parameter
* `meta: <object>` - transaction status metadata object, containing `null` or:
* `err: <object | null>` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L14)
* `fee: <u64>` - fee this transaction was charged, as u64 integer
* `preBalances: <array>` - array of u64 account balances from before the transaction was processed
* `postBalances: <array>` - array of u64 account balances after the transaction was processed
* DEPRECATED: `status: <object>` - Transaction status
* `"Ok": <null>` - Transaction was successful
* `"Err": <ERR>` - Transaction failed with TransactionError
#### Example:
```bash
// Request
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedTransaction","params":["35YGay1Lwjwgxe9zaH6APSHbt9gYQUCtBWTNL3aVwVGn9xTFw2fgds7qK5AL29mP63A9j3rh8KpN1TgSR62XCaby", "json"]}' localhost:8899
// Result
{"jsonrpc":"2.0","result":{"slot":430,"transaction":{"message":{"accountKeys":["6H94zdiaYfRfPfKjYLjyr2VFBg6JHXygy84r3qhc3NsC","39UAy8hsoYPywGPGdmun747omSr79zLSjqvPJN3zetoH","SysvarS1otHashes111111111111111111111111111","SysvarC1ock11111111111111111111111111111111","Vote111111111111111111111111111111111111111"],"header":{"numReadonlySignedAccounts":0,"numReadonlyUnsignedAccounts":3,"numRequiredSignatures":2},"instructions":[{"accounts":[1,2,3],"data":"29z5mr1JoRmJYQ6ynmk3pf31cGFRziAF1M3mT3L6sFXf5cKLdkEaMXMT8AqLpD4CpcupHmuMEmtZHpomrwfdZetSomNy3d","programIdIndex":4}],"recentBlockhash":"EFejToxii1L5aUF2NrK9dsbAEmZSNyN5nsipmZHQR1eA"},"signatures":["35YGay1Lwjwgxe9zaH6APSHbt9gYQUCtBWTNL3aVwVGn9xTFw2fgds7qK5AL29mP63A9j3rh8KpN1TgSR62XCaby","4vANMjSKiwEchGSXwVrQkwHnmsbKQmy9vdrsYxWdCup1bLsFzX8gKrFTSVDCZCae2dbxJB9mPNhqB2sD1vvr4sAD"]},"meta":{"err":null,"fee":18000,"postBalances":[499999972500,15298080,1,1,1],"preBalances":[499999990500,15298080,1,1,1],"status":{"Ok":null}}},"id":1}
// Request
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedTransaction","params":["35YGay1Lwjwgxe9zaH6APSHbt9gYQUCtBWTNL3aVwVGn9xTFw2fgds7qK5AL29mP63A9j3rh8KpN1TgSR62XCaby", "binary"]}' localhost:8899
// Result
{"jsonrpc":"2.0","result":{"slot":430,"transaction":"81UZJt4dh4Do66jDhrgkQudS8J2N6iG3jaVav7gJrqJSFY4Ug53iA9JFJZh2gxKWcaFdLJwhHx9mRdg9JwDAWB4ywiu5154CRwXV4FMdnPLg7bhxRLwhhYaLsVgMF5AyNRcTzjCVoBvqFgDU7P8VEKDEiMvD3qxzm1pLZVxDG1LTQpT3Dz4Uviv4KQbFQNuC22KupBoyHFB7Zh6KFdMqux4M9PvhoqcoJsJKwXjWpKu7xmEKnnrSbfLadkgjBmmjhW3fdTrFvnhQdTkhtdJxUL1xS9GMuJQer8YgSKNtUXB1eXZQwXU8bU2BjYkZE6Q5Xww8hu9Z4E4Mo4QsooVtHoP6BM3NKw8zjVbWfoCQqxTrwuSzrNCWCWt58C24LHecH67CTt2uXbYSviixvrYkK7A3t68BxTJcF1dXJitEPTFe2ceTkauLJqrJgnER4iUrsjr26T8YgWvpY9wkkWFSviQW6wV5RASTCUasVEcrDiaKj8EQMkgyDoe9HyKitSVg67vMWJFpUXpQobseWJUs5FTWWzmfHmFp8FZ","meta":{"err":null,"fee":18000,"postBalances":[499999972500,15298080,1,1,1],"preBalances":[499999990500,15298080,1,1,1],"status":{"Ok":null}}},"id":1}
```
### getEpochInfo
Returns information about the current epoch
@@ -654,16 +709,18 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "m
{"jsonrpc":"2.0","result":{"context":{"slot":1},"value":{"blockhash":"CSymwgTNX1j3E4qhKfJAUE41nBWEwXufoYryPbkde5RR","feeCalculator":{"burnPercent":50,"lamportsPerSignature":5000,"maxLamportsPerSignature":10000,"minLamportsPerSignature":5000,"targetLamportsPerSignature":1000,"targetSignaturesPerSlot":20000}}},"id":1}
```
### getSignatureStatus
### getSignatureStatuses
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 statuses of a list of signatures. Unless the
`searchTransactionHistory` configuration parameter is included, this method only
searches the recent status cache of signatures, which retains statuses for all
active slots plus `MAX_RECENT_BLOCKHASHES` rooted slots.
#### Parameters:
* `<array>` - An array of transaction signatures to confirm, as base-58 encoded strings
* `<object>` - (optional) Extended Rpc configuration, containing the following optional fields:
* `commitment: <string>` - [Commitment](jsonrpc-api.md#configuring-state-commitment)
* `searchTransactionHistory: <bool>` - whether to search the ledger transaction status cache, which may be expensive
* `<object>` - (optional) Configuration object containing the following field:
* `searchTransactionHistory: <bool>` - if true, a Solana node will search its ledger cache for any signatures not found in the recent status cache
#### Results:
@@ -677,21 +734,25 @@ An array of:
* `<object>`
* `slot: <u64>` - The slot the transaction was processed
* `confirmations: <usize | null>` - Number of blocks since signature confirmation, null if rooted
* `status: <object>` - Transaction status
* `err: <object | null>` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L14)
* DEPRECATED: `status: <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)
* `"Err": <ERR>` - Transaction failed with TransactionError
#### Example:
```bash
// Request
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getSignatureStatus", "params":[["5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW", "5j7s6NiJS3JAkvgkoc18WVAsiSaci2pxB2A6ueCJP4tprA2TFg9wSyTLeYouxPBJEMzJinENTkpA52YStRW5Dia7"]]]}' http://localhost:8899
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getSignatureStatuses", "params":[["5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW", "5j7s6NiJS3JAkvgkoc18WVAsiSaci2pxB2A6ueCJP4tprA2TFg9wSyTLeYouxPBJEMzJinENTkpA52YStRW5Dia7"]]}' http://localhost:8899
// Request with configuration
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getSignatureStatuses", "params":[["5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"], {"searchTransactionHistory": true}]}' http://localhost:8899
// Result
{"jsonrpc":"2.0","result":{"context":{"slot":82},"value":[{"slot": 72, "confirmations": 10, "status": {"Ok": null}}, null]},"id":1}
{"jsonrpc":"2.0","result":{"context":{"slot":82},"value":[{"slot": 72, "confirmations": 10, "err": null, "status": {"Ok": null}}, null]},"id":1}
// Result, first transaction rooted
{"jsonrpc":"2.0","result":{"context":{"slot":82},"value":[{"slot": 48, "confirmations": null, "status": {"Ok": null}}, null]},"id":1}
{"jsonrpc":"2.0","result":{"context":{"slot":82},"value":[{"slot": 48, "confirmations": null, "err": null, "status": {"Ok": null}}, null]},"id":1}
```
### getSlot
@@ -889,7 +950,7 @@ The result field will be a JSON object with the following fields:
// Request
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getVersion"}' http://localhost:8899
// Result
{"jsonrpc":"2.0","result":{"solana-core": "1.0.12"},"id":1}
{"jsonrpc":"2.0","result":{"solana-core": "1.0.18"},"id":1}
```
### getVoteAccounts
@@ -1200,7 +1261,7 @@ Subscribe to a transaction signature to receive notification when the transactio
#### Notification Format:
```bash
{"jsonrpc": "2.0","method": "signatureNotification", "params": {"result": "Confirmed","subscription":0}}
{"jsonrpc": "2.0","method": "signatureNotification", "params": {"result": {"err": null}, "subscription":0}}
```
### signatureUnsubscribe

View File

@@ -33,11 +33,13 @@ want to perform an action on the stake account you create next.
Now, create a stake account:
```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
stake account at the public key of stake-account.json.
`<AMOUNT>` tokens are transferred from the account at the "from" `<KEYPAIR>` to
a new stake account at the public key of stake-account.json.
The stake-account.json file can now be discarded. To authorize additional
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:
```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
@@ -87,7 +91,8 @@ addresses can be cumbersome. Fortunately, you can derive stake addresses using
the `--seed` option:
```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
@@ -122,12 +127,13 @@ is the vote account address. Choose a validator and use its vote account
address in `solana delegate-stake`:
```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
`<STAKE_ACCOUNT_ADDRESS>`. The stake is delegated to the vote account with
address `<VOTE_ACCOUNT_ADDRESS>`.
The stake authority `<KEYPAIR>` authorizes the operation on the account with
address `<STAKE_ACCOUNT_ADDRESS>`. The stake is delegated to the vote account
with address `<VOTE_ACCOUNT_ADDRESS>`.
After delegating stake, use `solana stake-account` to observe the changes
to the stake account:
@@ -155,11 +161,12 @@ Once delegated, you can undelegate stake with the `solana deactivate-stake`
command:
```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
`<STAKE_ACCOUNT_ADDRESS>`.
The stake authority `<KEYPAIR>` authorizes the operation on the account
with address `<STAKE_ACCOUNT_ADDRESS>`.
Note that stake takes several epochs to "cool down". Attempts to delegate stake
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:
```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
withdraw authority, and `<AMOUNT>` is the number of tokens to transfer to
`<RECIPIENT_ADDRESS>`.
`<STAKE_ACCOUNT_ADDRESS>` is the existing stake account, the stake authority
`<KEYPAIR>` is the withdraw authority, and `<AMOUNT>` is the number of tokens
to transfer to `<RECIPIENT_ADDRESS>`.
## 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:
```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 authority, `<NEW_STAKE_ACCOUNT_KEYPAIR>` is the keypair for the new account,
and `<AMOUNT>` is the number of tokens to transfer to the new account.
`<STAKE_ACCOUNT_ADDRESS>` is the existing stake account, the stake authority
`<KEYPAIR>` is the stake authority, `<NEW_STAKE_ACCOUNT_KEYPAIR>` is the
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`
option. See

View File

@@ -84,10 +84,10 @@ pubkey: GKvqsuNcnwWqPzzuhLmGi4rzzh55FhJtGizkhHaEJqiV
```
```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.
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:
```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`:

View File

@@ -171,7 +171,7 @@ $ solana send-timestamp <PUBKEY> <PROCESS_ID> --date 2018-12-24T23:59:00
## Usage
### solana-cli
```text
solana-cli 1.0.12 [channel=unknown commit=unknown]
solana-cli 1.0.18 [channel=unknown commit=unknown]
Blockchain, Rebuilt for Scale
USAGE:

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" %}
## Support
Check out our [Wallet Support Page](../wallet/support.md) for ways to get help.

View File

@@ -6,64 +6,8 @@ secure transaction signing.
## 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)
- [Initialize your Ledger Nano S](https://support.ledger.com/hc/en-us/articles/360000613793)
- [Install the latest device firmware](https://support.ledgerwallet.com/hc/en-us/articles/360002731113-Update-Ledger-Nano-S-firmware)
## Install the Solana App on Ledger Nano S
The Solana Ledger app is not yet available on Ledger Live. Until it is, you
can install a development version of the app from the command-line. Note that
because the app is not installed via Ledger Live, you will need to approve
installation from an "unsafe" manager, as well as see the message, "This app
is not genuine" each time you open the app. Once the app is available on
Ledger Live, you can reinstall the app from there, and the message will no
longer be displayed.
1. Connect your Ledger device via USB and enter your pin to unlock it
2. Download and run the Solana Ledger app installer:
```text
curl -sSLf https://github.com/solana-labs/ledger-app-solana/releases/download/v0.1.1/install.sh | sh
```
3. When prompted, approve the "unsafe" manager on your device
4. When prompted, approve the installation on your device
5. An installation window appears and your device will display Processing…
6. The app installation is confirmed
### Troubleshooting
If you encounter the following error:
```text
Traceback (most recent call last):
File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/runpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "ledger-env/lib/python3.7/site-packages/ledgerblue/loadApp.py", line 197, in <module>
dongle = getDongle(args.apdu)
File "ledger-env/lib/python3.7/site-packages/ledgerblue/comm.py", line 216, in getDongle
dev.open_path(hidDevicePath)
File "hid.pyx", line 72, in hid.device.open_path
OSError: open failed
```
To fix, check the following:
1. Ensure your Ledger device is connected to USB
2. Ensure your Ledger device is unlocked and not waiting for you to enter your pin
3. Ensure the Ledger Live application is not open
### Future: Installation once the Solana app is on Ledger Live
- [Install Ledger Live](https://support.ledger.com/hc/en-us/articles/360006395553/) software on your computer
1. Open the Manager in Ledger Live
2. Connect your Ledger device via USB and enter your pin to unlock it
3. When prompted, approve the manager on your device
4. Find Solana in the app catalog and click Install
5. An installation window appears and your device will display Processing…
6. The app installation is confirmed
## Use Ledger Device with Solana CLI
@@ -98,4 +42,4 @@ anywhere you see an option or argument that accepts a `<KEYPAIR>`.
## 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)
- Follow the instructions for device setup included in the package,
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 desktop software](https://www.ledger.com/ledger-live/),
@@ -30,13 +31,20 @@ line tools](../remote-wallet/ledger.md).
- Open Ledger Live
- Currently Ledger Live needs to be in "Developer Mode"
(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
click Install
- 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
- "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
- 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
line tools](../remote-wallet/ledger.md).
instructions on how to
[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,24 @@
#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
app. Support for Android is coming very very soon!**
## Install Trust Wallet
##Set Up Trust Wallet
- Open the App Store or Play Store
#### iOS
- Open the App Store
- Download “Trust: Crypto & Bitcoin Wallet” from Six Days LLC
- Requires iOS 13.0 or higher
- Open Trust Wallet and follow the app prompts to get started
#### Android
- Open the Play Store
- Download “Trust Crypto Wallet” from Six Days LLC
- Requires Android 6.0 or higher
- Open Trust Wallet and follow the app prompts to get started
##Add Solana (SOL) tokens to your wallet
## Add Solana (SOL) tokens to your wallet
- From the main page, go to the “Tokens” tab at the top of the screen
- Tap the “+” icon at the top right corner
- Search for “Solana” in the search page, and when the “Solana SOL” token is
@@ -19,7 +27,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)
##Receiving SOL tokens
## Receiving SOL tokens
- To receive SOL tokens that youve purchased or earned, you need to send your
Receive Address to whoever is sending you tokens.
- Tap “Receive” to view a QR code and your text address, which is a long string
@@ -32,6 +40,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)
##Troubleshooting
## Troubleshooting
If you are having trouble setting up your Trust Wallet app, check out their
[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

@@ -1,6 +1,6 @@
[package]
name = "solana-faucet"
version = "1.0.12"
version = "1.0.18"
description = "Solana Faucet"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@@ -19,10 +19,10 @@ clap = "2.33"
log = "0.4.8"
serde = "1.0.104"
serde_derive = "1.0.103"
solana-clap-utils = { path = "../clap-utils", version = "1.0.12" }
solana-logger = { path = "../logger", version = "1.0.12" }
solana-metrics = { path = "../metrics", version = "1.0.12" }
solana-sdk = { path = "../sdk", version = "1.0.12" }
solana-clap-utils = { path = "../clap-utils", version = "1.0.18" }
solana-logger = { path = "../logger", version = "1.0.18" }
solana-metrics = { path = "../metrics", version = "1.0.18" }
solana-sdk = { path = "../sdk", version = "1.0.18" }
tokio = "0.1"
tokio-codec = "0.1"

View File

@@ -21,10 +21,20 @@ if [[ ! -f target/perf-libs/.$VERSION ]]; then
(
set -x
cd target/perf-libs
curl -L --retry 5 --retry-delay 2 --retry-connrefused -o solana-perf.tgz \
https://github.com/solana-labs/solana-perf-libs/releases/download/$PERF_LIBS_VERSION/solana-perf.tgz
if [[ -r ~/.cache/solana-perf-$PERF_LIBS_VERSION.tgz ]]; then
cp ~/.cache/solana-perf-$PERF_LIBS_VERSION.tgz solana-perf.tgz
else
curl -L --retry 5 --retry-delay 2 --retry-connrefused -o solana-perf.tgz \
https://github.com/solana-labs/solana-perf-libs/releases/download/$PERF_LIBS_VERSION/solana-perf.tgz
fi
tar zxvf solana-perf.tgz
rm -f solana-perf.tgz
if [[ ! -r ~/.cache/solana-perf-$PERF_LIBS_VERSION.tgz ]]; then
# Save it for next time
mkdir -p ~/.cache
mv solana-perf.tgz ~/.cache/solana-perf-$PERF_LIBS_VERSION.tgz
fi
touch .$VERSION
)

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-genesis-programs"
version = "1.0.12"
version = "1.0.18"
description = "Solana genesis programs"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@@ -10,16 +10,16 @@ edition = "2018"
[dependencies]
log = { version = "0.4.8" }
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "1.0.12" }
solana-budget-program = { path = "../programs/budget", version = "1.0.12" }
solana-config-program = { path = "../programs/config", version = "1.0.12" }
solana-exchange-program = { path = "../programs/exchange", version = "1.0.12" }
solana-runtime = { path = "../runtime", version = "1.0.12" }
solana-sdk = { path = "../sdk", version = "1.0.12" }
solana-stake-program = { path = "../programs/stake", version = "1.0.12" }
solana-storage-program = { path = "../programs/storage", version = "1.0.12" }
solana-vest-program = { path = "../programs/vest", version = "1.0.12" }
solana-vote-program = { path = "../programs/vote", version = "1.0.12" }
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "1.0.18" }
solana-budget-program = { path = "../programs/budget", version = "1.0.18" }
solana-config-program = { path = "../programs/config", version = "1.0.18" }
solana-exchange-program = { path = "../programs/exchange", version = "1.0.18" }
solana-runtime = { path = "../runtime", version = "1.0.18" }
solana-sdk = { path = "../sdk", version = "1.0.18" }
solana-stake-program = { path = "../programs/stake", version = "1.0.18" }
solana-storage-program = { path = "../programs/storage", version = "1.0.18" }
solana-vest-program = { path = "../programs/vest", version = "1.0.18" }
solana-vote-program = { path = "../programs/vote", version = "1.0.18" }
[lib]
crate-type = ["lib"]

View File

@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-genesis"
description = "Blockchain, Rebuilt for Scale"
version = "1.0.12"
version = "1.0.18"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@@ -15,13 +15,13 @@ chrono = "0.4"
serde = "1.0.104"
serde_json = "1.0.46"
serde_yaml = "0.8.11"
solana-clap-utils = { path = "../clap-utils", version = "1.0.12" }
solana-genesis-programs = { path = "../genesis-programs", version = "1.0.12" }
solana-ledger = { path = "../ledger", version = "1.0.12" }
solana-sdk = { path = "../sdk", version = "1.0.12" }
solana-stake-program = { path = "../programs/stake", version = "1.0.12" }
solana-storage-program = { path = "../programs/storage", version = "1.0.12" }
solana-vote-program = { path = "../programs/vote", version = "1.0.12" }
solana-clap-utils = { path = "../clap-utils", version = "1.0.18" }
solana-genesis-programs = { path = "../genesis-programs", version = "1.0.18" }
solana-ledger = { path = "../ledger", version = "1.0.18" }
solana-sdk = { path = "../sdk", version = "1.0.18" }
solana-stake-program = { path = "../programs/stake", version = "1.0.18" }
solana-storage-program = { path = "../programs/storage", version = "1.0.18" }
solana-vote-program = { path = "../programs/vote", version = "1.0.18" }
tempfile = "3.1.0"
[[bin]]

View File

@@ -3,19 +3,19 @@ authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-gossip"
description = "Blockchain, Rebuilt for Scale"
version = "1.0.12"
version = "1.0.18"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
[dependencies]
clap = "2.33.0"
solana-clap-utils = { path = "../clap-utils", version = "1.0.12" }
solana-core = { path = "../core", version = "1.0.12" }
solana-client = { path = "../client", version = "1.0.12" }
solana-logger = { path = "../logger", version = "1.0.12" }
solana-net-utils = { path = "../net-utils", version = "1.0.12" }
solana-sdk = { path = "../sdk", version = "1.0.12" }
solana-clap-utils = { path = "../clap-utils", version = "1.0.18" }
solana-core = { path = "../core", version = "1.0.18" }
solana-client = { path = "../client", version = "1.0.18" }
solana-logger = { path = "../logger", version = "1.0.18" }
solana-net-utils = { path = "../net-utils", version = "1.0.18" }
solana-sdk = { path = "../sdk", version = "1.0.18" }

View File

@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-install"
description = "The solana cluster software installer"
version = "1.0.12"
version = "1.0.18"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@@ -24,11 +24,11 @@ reqwest = { version = "0.10.1", default-features = false, features = ["blocking"
serde = "1.0.104"
serde_derive = "1.0.103"
serde_yaml = "0.8.11"
solana-clap-utils = { path = "../clap-utils", version = "1.0.12" }
solana-client = { path = "../client", version = "1.0.12" }
solana-config-program = { path = "../programs/config", version = "1.0.12" }
solana-logger = { path = "../logger", version = "1.0.12" }
solana-sdk = { path = "../sdk", version = "1.0.12" }
solana-clap-utils = { path = "../clap-utils", version = "1.0.18" }
solana-client = { path = "../client", version = "1.0.18" }
solana-config-program = { path = "../programs/config", version = "1.0.18" }
solana-logger = { path = "../logger", version = "1.0.18" }
solana-sdk = { path = "../sdk", version = "1.0.18" }
semver = "0.9.0"
tar = "0.4.26"
tempdir = "0.3.7"

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-keygen"
version = "1.0.12"
version = "1.0.18"
description = "Solana key generation utility"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@@ -13,10 +13,10 @@ bs58 = "0.3.0"
clap = "2.33"
dirs = "2.0.2"
num_cpus = "1.12.0"
solana-clap-utils = { path = "../clap-utils", version = "1.0.12" }
solana-cli-config = { path = "../cli-config", version = "1.0.12" }
solana-remote-wallet = { path = "../remote-wallet", version = "1.0.12" }
solana-sdk = { path = "../sdk", version = "1.0.12" }
solana-clap-utils = { path = "../clap-utils", version = "1.0.18" }
solana-cli-config = { path = "../cli-config", version = "1.0.18" }
solana-remote-wallet = { path = "../remote-wallet", version = "1.0.18" }
solana-sdk = { path = "../sdk", version = "1.0.18" }
tiny-bip39 = "0.7.0"
[[bin]]

View File

@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-ledger-tool"
description = "Blockchain, Rebuilt for Scale"
version = "1.0.12"
version = "1.0.18"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@@ -14,13 +14,13 @@ clap = "2.33.0"
histogram = "*"
serde_json = "1.0.46"
serde_yaml = "0.8.11"
solana-clap-utils = { path = "../clap-utils", version = "1.0.12" }
solana-ledger = { path = "../ledger", version = "1.0.12" }
solana-logger = { path = "../logger", version = "1.0.12" }
solana-runtime = { path = "../runtime", version = "1.0.12" }
solana-sdk = { path = "../sdk", version = "1.0.12" }
solana-vote-program = { path = "../programs/vote", version = "1.0.12" }
solana-stake-program = { path = "../programs/stake", version = "1.0.12" }
solana-clap-utils = { path = "../clap-utils", version = "1.0.18" }
solana-ledger = { path = "../ledger", version = "1.0.18" }
solana-logger = { path = "../logger", version = "1.0.18" }
solana-runtime = { path = "../runtime", version = "1.0.18" }
solana-sdk = { path = "../sdk", version = "1.0.18" }
solana-vote-program = { path = "../programs/vote", version = "1.0.18" }
solana-stake-program = { path = "../programs/stake", version = "1.0.18" }
tempfile = "3.1.0"
[dev-dependencies]

View File

@@ -144,7 +144,7 @@ fn output_slot(
println!(" Data: {:?}", instruction.data);
}
}
match blockstore.read_transaction_status((slot, transaction.signatures[0])) {
match blockstore.read_transaction_status((transaction.signatures[0], slot)) {
Ok(transaction_status) => {
if let Some(transaction_status) = transaction_status {
println!(
@@ -808,6 +808,12 @@ fn main() {
.arg(&account_paths_arg)
.arg(&halt_at_slot_arg)
.arg(&hard_forks_arg)
.arg(
Arg::with_name("include_sysvars")
.long("include-sysvars")
.takes_value(false)
.help("Include sysvars too"),
)
).subcommand(
SubCommand::with_name("prune")
.about("Prune the ledger at the block height")
@@ -1090,6 +1096,7 @@ fn main() {
..ProcessOptions::default()
};
let genesis_config = open_genesis_config(&ledger_path);
let include_sysvars = arg_matches.is_present("include_sysvars");
match load_bank_forks(arg_matches, &ledger_path, &genesis_config, process_options) {
Ok((bank_forks, bank_forks_info, _leader_schedule_cache, _snapshot_hash)) => {
let slot = dev_halt_at_slot.unwrap_or_else(|| {
@@ -1105,19 +1112,23 @@ fn main() {
exit(1);
});
let accounts: Vec<_> = bank
let accounts: BTreeMap<_, _> = bank
.get_program_accounts(None)
.into_iter()
.filter(|(pubkey, _account)| !solana_sdk::sysvar::is_sysvar_id(pubkey))
.filter(|(pubkey, _account)| {
include_sysvars || !solana_sdk::sysvar::is_sysvar_id(pubkey)
})
.collect();
println!("---");
for (pubkey, account) in accounts.into_iter() {
let data_len = account.data.len();
println!("{}:", pubkey);
println!(" - lamports: {}", account.lamports);
println!(" - owner: '{}'", account.owner);
println!(" - executable: {}", account.executable);
println!(" - data: '{}'", bs58::encode(account.data).into_string());
println!(" - data_len: {}", data_len);
}
}
Err(err) => {

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-ledger"
version = "1.0.12"
version = "1.0.18"
description = "Solana ledger"
authors = ["Solana Maintainers <maintainers@solana.com>"]
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.4"
serde = "1.0.104"
serde_bytes = "0.11.3"
solana-transaction-status = { path = "../transaction-status", version = "1.0.12" }
solana-genesis-programs = { path = "../genesis-programs", version = "1.0.12" }
solana-logger = { path = "../logger", version = "1.0.12" }
solana-measure = { path = "../measure", version = "1.0.12" }
solana-merkle-tree = { path = "../merkle-tree", version = "1.0.12" }
solana-metrics = { path = "../metrics", version = "1.0.12" }
solana-perf = { path = "../perf", version = "1.0.12" }
solana-transaction-status = { path = "../transaction-status", version = "1.0.18" }
solana-genesis-programs = { path = "../genesis-programs", version = "1.0.18" }
solana-logger = { path = "../logger", version = "1.0.18" }
solana-measure = { path = "../measure", version = "1.0.18" }
solana-merkle-tree = { path = "../merkle-tree", version = "1.0.18" }
solana-metrics = { path = "../metrics", version = "1.0.18" }
solana-perf = { path = "../perf", version = "1.0.18" }
ed25519-dalek = "1.0.0-pre.1"
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.0.12" }
solana-runtime = { path = "../runtime", version = "1.0.12" }
solana-sdk = { path = "../sdk", version = "1.0.12" }
solana-stake-program = { path = "../programs/stake", version = "1.0.12" }
solana-vote-program = { path = "../programs/vote", version = "1.0.12" }
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.0.18" }
solana-runtime = { path = "../runtime", version = "1.0.18" }
solana-sdk = { path = "../sdk", version = "1.0.18" }
solana-stake-program = { path = "../programs/stake", version = "1.0.18" }
solana-vote-program = { path = "../programs/vote", version = "1.0.18" }
symlink = "0.1.0"
tar = "0.4.26"
thiserror = "1.0"
@@ -57,7 +57,7 @@ features = ["lz4"]
[dev-dependencies]
assert_matches = "1.3.0"
matches = "0.1.6"
solana-budget-program = { path = "../programs/budget", version = "1.0.12" }
solana-budget-program = { path = "../programs/budget", version = "1.0.18" }
[lib]
crate-type = ["lib"]

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,7 @@ use rocksdb::{
};
use serde::de::DeserializeOwned;
use serde::Serialize;
use solana_sdk::{clock::Slot, signature::Signature};
use solana_sdk::{clock::Slot, pubkey::Pubkey, signature::Signature};
use solana_transaction_status::{Rewards, TransactionStatusMeta};
use std::{collections::HashMap, fs, marker::PhantomData, path::Path, sync::Arc};
use thiserror::Error;
@@ -38,6 +38,10 @@ const DATA_SHRED_CF: &str = "data_shred";
const CODE_SHRED_CF: &str = "code_shred";
/// Column family for Transaction Status
const TRANSACTION_STATUS_CF: &str = "transaction_status";
/// Column family for Address Signatures
const ADDRESS_SIGNATURES_CF: &str = "address_signatures";
/// Column family for Transaction Status Index
const TRANSACTION_STATUS_INDEX_CF: &str = "transaction_status_index";
/// Column family for Rewards
const REWARDS_CF: &str = "rewards";
@@ -108,6 +112,14 @@ pub mod columns {
/// The transaction status column
pub struct TransactionStatus;
#[derive(Debug)]
/// The address signatures column
pub struct AddressSignatures;
#[derive(Debug)]
/// The transaction status index column
pub struct TransactionStatusIndex;
#[derive(Debug)]
/// The rewards column
pub struct Rewards;
@@ -119,8 +131,8 @@ struct Rocks(rocksdb::DB);
impl Rocks {
fn open(path: &Path) -> Result<Rocks> {
use columns::{
DeadSlots, DuplicateSlots, ErasureMeta, Index, Orphans, Rewards, Root, ShredCode,
ShredData, SlotMeta, TransactionStatus,
AddressSignatures, DeadSlots, DuplicateSlots, ErasureMeta, Index, Orphans, Rewards,
Root, ShredCode, ShredData, SlotMeta, TransactionStatus, TransactionStatusIndex,
};
fs::create_dir_all(&path)?;
@@ -145,6 +157,10 @@ impl Rocks {
ColumnFamilyDescriptor::new(ShredCode::NAME, get_cf_options());
let transaction_status_cf_descriptor =
ColumnFamilyDescriptor::new(TransactionStatus::NAME, get_cf_options());
let address_signatures_cf_descriptor =
ColumnFamilyDescriptor::new(AddressSignatures::NAME, get_cf_options());
let transaction_status_index_cf_descriptor =
ColumnFamilyDescriptor::new(TransactionStatusIndex::NAME, get_cf_options());
let rewards_cf_descriptor = ColumnFamilyDescriptor::new(Rewards::NAME, get_cf_options());
let cfs = vec![
@@ -158,6 +174,8 @@ impl Rocks {
shred_data_cf_descriptor,
shred_code_cf_descriptor,
transaction_status_cf_descriptor,
address_signatures_cf_descriptor,
transaction_status_index_cf_descriptor,
rewards_cf_descriptor,
];
@@ -169,8 +187,8 @@ impl Rocks {
fn columns(&self) -> Vec<&'static str> {
use columns::{
DeadSlots, DuplicateSlots, ErasureMeta, Index, Orphans, Rewards, Root, ShredCode,
ShredData, SlotMeta, TransactionStatus,
AddressSignatures, DeadSlots, DuplicateSlots, ErasureMeta, Index, Orphans, Rewards,
Root, ShredCode, ShredData, SlotMeta, TransactionStatus, TransactionStatusIndex,
};
vec![
@@ -184,6 +202,8 @@ impl Rocks {
ShredData::NAME,
ShredCode::NAME,
TransactionStatus::NAME,
AddressSignatures::NAME,
TransactionStatusIndex::NAME,
Rewards::NAME,
]
}
@@ -256,7 +276,7 @@ pub trait Column {
fn key(index: Self::Index) -> Vec<u8>;
fn index(key: &[u8]) -> Self::Index;
fn slot(index: Self::Index) -> Slot;
fn primary_index(index: Self::Index) -> Slot;
fn as_index(slot: Slot) -> Self::Index;
}
@@ -272,6 +292,14 @@ impl TypedColumn for columns::TransactionStatus {
type Type = TransactionStatusMeta;
}
impl TypedColumn for columns::AddressSignatures {
type Type = blockstore_meta::AddressSignatureMeta;
}
impl TypedColumn for columns::TransactionStatusIndex {
type Type = blockstore_meta::TransactionStatusIndexMeta;
}
pub trait SlotColumn<Index = u64> {}
impl<T: SlotColumn> Column for T {
@@ -287,7 +315,7 @@ impl<T: SlotColumn> Column for T {
BigEndian::read_u64(&key[..8])
}
fn slot(index: u64) -> Slot {
fn primary_index(index: u64) -> Slot {
index
}
@@ -297,27 +325,29 @@ impl<T: SlotColumn> Column for T {
}
impl Column for columns::TransactionStatus {
type Index = (Slot, Signature);
type Index = (u64, Signature, Slot);
fn key((slot, index): (Slot, Signature)) -> Vec<u8> {
let mut key = vec![0; 8 + 64];
BigEndian::write_u64(&mut key[..8], slot);
key[8..72].clone_from_slice(&index.as_ref()[0..64]);
fn key((index, signature, slot): (u64, Signature, Slot)) -> Vec<u8> {
let mut key = vec![0; 8 + 64 + 8]; // size_of u64 + size_of Signature + size_of Slot
BigEndian::write_u64(&mut key[0..8], index);
key[8..72].clone_from_slice(&signature.as_ref()[0..64]);
BigEndian::write_u64(&mut key[72..80], slot);
key
}
fn index(key: &[u8]) -> (Slot, Signature) {
let slot = BigEndian::read_u64(&key[..8]);
let index = Signature::new(&key[8..72]);
(slot, index)
fn index(key: &[u8]) -> (u64, Signature, Slot) {
let index = BigEndian::read_u64(&key[0..8]);
let signature = Signature::new(&key[8..72]);
let slot = BigEndian::read_u64(&key[72..80]);
(index, signature, slot)
}
fn slot(index: Self::Index) -> Slot {
fn primary_index(index: Self::Index) -> u64 {
index.0
}
fn as_index(slot: Slot) -> Self::Index {
(slot, Signature::default())
fn as_index(index: u64) -> Self::Index {
(index, Signature::default(), 0)
}
}
@@ -325,6 +355,65 @@ impl ColumnName for columns::TransactionStatus {
const NAME: &'static str = TRANSACTION_STATUS_CF;
}
impl Column for columns::AddressSignatures {
type Index = (u64, Pubkey, Slot, Signature);
fn key((index, pubkey, slot, signature): (u64, Pubkey, Slot, Signature)) -> Vec<u8> {
let mut key = vec![0; 8 + 32 + 8 + 64]; // size_of u64 + size_of Pubkey + size_of Slot + size_of Signature
BigEndian::write_u64(&mut key[0..8], index);
key[8..40].clone_from_slice(&pubkey.as_ref()[0..32]);
BigEndian::write_u64(&mut key[40..48], slot);
key[48..112].clone_from_slice(&signature.as_ref()[0..64]);
key
}
fn index(key: &[u8]) -> (u64, Pubkey, Slot, Signature) {
let index = BigEndian::read_u64(&key[0..8]);
let pubkey = Pubkey::new(&key[8..40]);
let slot = BigEndian::read_u64(&key[40..48]);
let signature = Signature::new(&key[48..112]);
(index, pubkey, slot, signature)
}
fn primary_index(index: Self::Index) -> u64 {
index.0
}
fn as_index(index: u64) -> Self::Index {
(index, Pubkey::default(), 0, Signature::default())
}
}
impl ColumnName for columns::AddressSignatures {
const NAME: &'static str = ADDRESS_SIGNATURES_CF;
}
impl Column for columns::TransactionStatusIndex {
type Index = u64;
fn key(index: u64) -> Vec<u8> {
let mut key = vec![0; 8];
BigEndian::write_u64(&mut key[..], index);
key
}
fn index(key: &[u8]) -> u64 {
BigEndian::read_u64(&key[..8])
}
fn primary_index(index: u64) -> u64 {
index
}
fn as_index(slot: u64) -> u64 {
slot
}
}
impl ColumnName for columns::TransactionStatusIndex {
const NAME: &'static str = TRANSACTION_STATUS_INDEX_CF;
}
impl SlotColumn for columns::Rewards {}
impl ColumnName for columns::Rewards {
const NAME: &'static str = REWARDS_CF;
@@ -344,7 +433,7 @@ impl Column for columns::ShredCode {
columns::ShredData::index(key)
}
fn slot(index: Self::Index) -> Slot {
fn primary_index(index: Self::Index) -> Slot {
index.0
}
@@ -373,7 +462,7 @@ impl Column for columns::ShredData {
(slot, index)
}
fn slot(index: Self::Index) -> Slot {
fn primary_index(index: Self::Index) -> Slot {
index.0
}
@@ -451,7 +540,7 @@ impl Column for columns::ErasureMeta {
key
}
fn slot(index: Self::Index) -> Slot {
fn primary_index(index: Self::Index) -> Slot {
index.0
}
@@ -583,7 +672,7 @@ impl Database {
let max_slot = self
.iter::<C>(IteratorMode::End)?
.next()
.map(|(i, _)| C::slot(i))
.map(|(i, _)| C::primary_index(i))
.unwrap_or(0);
let end = max_slot <= to;
result.map(|_| end)
@@ -624,7 +713,7 @@ where
let iter = self.iter(iter_config)?;
for (index, _) in iter {
if let Some(to) = to {
if C::slot(index) > to {
if C::primary_index(index) > to {
end = false;
break;
}

View File

@@ -222,6 +222,17 @@ impl DuplicateSlotProof {
}
}
#[derive(Debug, Default, Deserialize, Serialize, PartialEq)]
pub struct TransactionStatusIndexMeta {
pub max_slot: Slot,
pub frozen: bool,
}
#[derive(Debug, Default, Deserialize, Serialize, PartialEq)]
pub struct AddressSignatureMeta {
pub writeable: bool,
}
#[cfg(test)]
mod test {
use super::*;

View File

@@ -20,8 +20,8 @@ use solana_runtime::{
transaction_batch::TransactionBatch,
};
use solana_sdk::{
clock::{Slot, MAX_RECENT_BLOCKHASHES},
genesis_config::GenesisConfig,
clock::{Epoch, Slot, MAX_PROCESSING_AGE, MAX_RECENT_BLOCKHASHES},
genesis_config::{GenesisConfig, OperatingMode},
hash::Hash,
pubkey::Pubkey,
signature::Keypair,
@@ -57,11 +57,22 @@ fn first_err(results: &[Result<()>]) -> Result<()> {
Ok(())
}
const MAX_AGE_CORRECTION_EPOCH: Epoch = 14;
fn execute_batch(
batch: &TransactionBatch,
bank: &Arc<Bank>,
transaction_status_sender: Option<TransactionStatusSender>,
genesis_config: Option<&GenesisConfig>,
) -> Result<()> {
// See https://github.com/solana-labs/solana/pull/9423
let max_age_reduced = if let Some(genesis_config) = genesis_config {
genesis_config.operating_mode == OperatingMode::Stable
&& bank.epoch() >= MAX_AGE_CORRECTION_EPOCH
} else {
false
};
let (
TransactionResults {
fee_collection_results,
@@ -70,7 +81,11 @@ fn execute_batch(
balances,
) = batch.bank().load_execute_and_commit_transactions(
batch,
MAX_RECENT_BLOCKHASHES,
if max_age_reduced {
MAX_PROCESSING_AGE
} else {
MAX_RECENT_BLOCKHASHES
},
transaction_status_sender.is_some(),
);
@@ -112,6 +127,7 @@ fn execute_batches(
batches: &[TransactionBatch],
entry_callback: Option<&ProcessCallback>,
transaction_status_sender: Option<TransactionStatusSender>,
genesis_config: Option<&GenesisConfig>,
) -> Result<()> {
inc_new_counter_debug!("bank-par_execute_entries-count", batches.len());
let results: Vec<Result<()>> = PAR_THREAD_POOL.with(|thread_pool| {
@@ -119,7 +135,7 @@ fn execute_batches(
batches
.into_par_iter()
.map_with(transaction_status_sender, |sender, batch| {
let result = execute_batch(batch, bank, sender.clone());
let result = execute_batch(batch, bank, sender.clone(), genesis_config);
if let Some(entry_callback) = entry_callback {
entry_callback(bank);
}
@@ -143,7 +159,14 @@ pub fn process_entries(
randomize: bool,
transaction_status_sender: Option<TransactionStatusSender>,
) -> Result<()> {
process_entries_with_callback(bank, entries, randomize, None, transaction_status_sender)
process_entries_with_callback(
bank,
entries,
randomize,
None,
transaction_status_sender,
Some(&GenesisConfig::default()),
)
}
fn process_entries_with_callback(
@@ -152,6 +175,7 @@ fn process_entries_with_callback(
randomize: bool,
entry_callback: Option<&ProcessCallback>,
transaction_status_sender: Option<TransactionStatusSender>,
genesis_config: Option<&GenesisConfig>,
) -> Result<()> {
// accumulator for entries that can be processed in parallel
let mut batches = vec![];
@@ -168,6 +192,7 @@ fn process_entries_with_callback(
&batches,
entry_callback,
transaction_status_sender.clone(),
genesis_config,
)?;
batches.clear();
for hash in &tick_hashes {
@@ -223,12 +248,19 @@ fn process_entries_with_callback(
&batches,
entry_callback,
transaction_status_sender.clone(),
genesis_config,
)?;
batches.clear();
}
}
}
execute_batches(bank, &batches, entry_callback, transaction_status_sender)?;
execute_batches(
bank,
&batches,
entry_callback,
transaction_status_sender,
genesis_config,
)?;
for hash in tick_hashes {
bank.register_tick(&hash);
}
@@ -363,6 +395,7 @@ pub fn process_blockstore_from_root(
&mut rooted_path,
opts,
recyclers,
genesis_config,
)?;
let (banks, bank_forks_info): (Vec<_>, Vec<_>) =
fork_info.into_iter().map(|(_, v)| v).unzip();
@@ -456,6 +489,7 @@ fn confirm_full_slot(
last_entry_hash: &Hash,
opts: &ProcessOptions,
recyclers: &VerifyRecyclers,
genesis_config: Option<&GenesisConfig>,
) -> result::Result<(), BlockstoreProcessorError> {
let mut timing = ConfirmationTiming::default();
let mut progress = ConfirmationProgress::new(*last_entry_hash);
@@ -469,6 +503,7 @@ fn confirm_full_slot(
None,
opts.entry_callback.as_ref(),
recyclers,
genesis_config,
)?;
if !bank.is_complete() {
@@ -527,6 +562,7 @@ pub fn confirm_slot(
transaction_status_sender: Option<TransactionStatusSender>,
entry_callback: Option<&ProcessCallback>,
recyclers: &VerifyRecyclers,
genesis_config: Option<&GenesisConfig>,
) -> result::Result<(), BlockstoreProcessorError> {
let slot = bank.slot();
@@ -592,6 +628,7 @@ pub fn confirm_slot(
true,
entry_callback,
transaction_status_sender,
genesis_config,
)
.map_err(BlockstoreProcessorError::from);
replay_elapsed.stop();
@@ -625,8 +662,15 @@ fn process_bank_0(
recyclers: &VerifyRecyclers,
) -> result::Result<(), BlockstoreProcessorError> {
assert_eq!(bank0.slot(), 0);
confirm_full_slot(blockstore, bank0, &bank0.last_blockhash(), opts, recyclers)
.expect("processing for bank 0 must succceed");
confirm_full_slot(
blockstore,
bank0,
&bank0.last_blockhash(),
opts,
recyclers,
None,
)
.expect("processing for bank 0 must succceed");
bank0.freeze();
Ok(())
}
@@ -701,6 +745,7 @@ fn process_pending_slots(
rooted_path: &mut Vec<u64>,
opts: &ProcessOptions,
recyclers: &VerifyRecyclers,
genesis_config: &GenesisConfig,
) -> result::Result<HashMap<u64, (Arc<Bank>, BankForksInfo)>, BlockstoreProcessorError> {
let mut fork_info = HashMap::new();
let mut last_status_report = Instant::now();
@@ -730,7 +775,16 @@ fn process_pending_slots(
let allocated = thread_mem_usage::Allocatedp::default();
let initial_allocation = allocated.get();
if process_single_slot(blockstore, &bank, &last_entry_hash, opts, recyclers).is_err() {
if process_single_slot(
blockstore,
&bank,
&last_entry_hash,
opts,
recyclers,
genesis_config,
)
.is_err()
{
continue;
}
@@ -778,10 +832,19 @@ fn process_single_slot(
last_entry_hash: &Hash,
opts: &ProcessOptions,
recyclers: &VerifyRecyclers,
genesis_config: &GenesisConfig,
) -> result::Result<(), BlockstoreProcessorError> {
// Mark corrupt slots as dead so validators don't replay this slot and
// see DuplicateSignature errors later in ReplayStage
confirm_full_slot(blockstore, bank, last_entry_hash, opts, recyclers).map_err(|err| {
confirm_full_slot(
blockstore,
bank,
last_entry_hash,
opts,
recyclers,
Some(genesis_config),
)
.map_err(|err| {
let slot = bank.slot();
blockstore
.set_dead_slot(slot)
@@ -2444,6 +2507,7 @@ pub mod tests {
&bank0.last_blockhash(),
&opts,
&recyclers,
None,
)
.unwrap();
bank1.squash();
@@ -2609,7 +2673,7 @@ pub mod tests {
let entry = next_entry(&new_blockhash, 1, vec![tx]);
entries.push(entry);
process_entries_with_callback(&bank0, &entries, true, None, None).unwrap();
process_entries_with_callback(&bank0, &entries, true, None, None, None).unwrap();
assert_eq!(bank0.get_balance(&keypair.pubkey()), 1)
}

View File

@@ -764,7 +764,7 @@ impl Shredder {
return Err(reed_solomon_erasure::Error::TooFewShardsPresent);
}
let session = Session::new(num_data, num_coding).unwrap();
let session = Session::new(num_data, num_coding)?;
let valid_data_len = PACKET_DATA_SIZE - SIZE_OF_DATA_SHRED_IGNORED_TAIL;
let coding_block_offset = SIZE_OF_CODING_SHRED_HEADER + SIZE_OF_COMMON_SHRED_HEADER;

View File

@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-local-cluster"
description = "Blockchain, Rebuilt for Scale"
version = "1.0.12"
version = "1.0.18"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@@ -12,23 +12,23 @@ homepage = "https://solana.com/"
itertools = "0.8.1"
log = "0.4.8"
rand = "0.6.5"
solana-archiver-lib = { path = "../archiver-lib", version = "1.0.12" }
solana-config-program = { path = "../programs/config", version = "1.0.12" }
solana-core = { path = "../core", version = "1.0.12" }
solana-client = { path = "../client", version = "1.0.12" }
solana-faucet = { path = "../faucet", version = "1.0.12" }
solana-exchange-program = { path = "../programs/exchange", version = "1.0.12" }
solana-genesis-programs = { path = "../genesis-programs", version = "1.0.12" }
solana-ledger = { path = "../ledger", version = "1.0.12" }
solana-logger = { path = "../logger", version = "1.0.12" }
solana-runtime = { path = "../runtime", version = "1.0.12" }
solana-sdk = { path = "../sdk", version = "1.0.12" }
solana-stake-program = { path = "../programs/stake", version = "1.0.12" }
solana-storage-program = { path = "../programs/storage", version = "1.0.12" }
solana-vest-program = { path = "../programs/vest", version = "1.0.12" }
solana-vote-program = { path = "../programs/vote", version = "1.0.12" }
solana-archiver-lib = { path = "../archiver-lib", version = "1.0.18" }
solana-config-program = { path = "../programs/config", version = "1.0.18" }
solana-core = { path = "../core", version = "1.0.18" }
solana-client = { path = "../client", version = "1.0.18" }
solana-faucet = { path = "../faucet", version = "1.0.18" }
solana-exchange-program = { path = "../programs/exchange", version = "1.0.18" }
solana-genesis-programs = { path = "../genesis-programs", version = "1.0.18" }
solana-ledger = { path = "../ledger", version = "1.0.18" }
solana-logger = { path = "../logger", version = "1.0.18" }
solana-runtime = { path = "../runtime", version = "1.0.18" }
solana-sdk = { path = "../sdk", version = "1.0.18" }
solana-stake-program = { path = "../programs/stake", version = "1.0.18" }
solana-storage-program = { path = "../programs/storage", version = "1.0.18" }
solana-vest-program = { path = "../programs/vest", version = "1.0.18" }
solana-vote-program = { path = "../programs/vote", version = "1.0.18" }
tempfile = "3.1.0"
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.0.12" }
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.0.18" }
[dev-dependencies]
assert_matches = "1.3.0"

View File

@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-log-analyzer"
description = "The solana cluster network analysis tool"
version = "1.0.12"
version = "1.0.18"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@@ -14,8 +14,8 @@ byte-unit = "3.0.3"
clap = "2.33.0"
serde = "1.0.104"
serde_json = "1.0.46"
solana-clap-utils = { path = "../clap-utils", version = "1.0.12" }
solana-logger = { path = "../logger", version = "1.0.12" }
solana-clap-utils = { path = "../clap-utils", version = "1.0.18" }
solana-logger = { path = "../logger", version = "1.0.18" }
[[bin]]
name = "solana-log-analyzer"

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-logger"
version = "1.0.12"
version = "1.0.18"
description = "Solana Logger"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"

View File

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

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-merkle-tree"
version = "1.0.12"
version = "1.0.18"
description = "Solana Merkle Tree"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@@ -9,7 +9,7 @@ homepage = "https://solana.com/"
edition = "2018"
[dependencies]
solana-sdk = { path = "../sdk", version = "1.0.12" }
solana-sdk = { path = "../sdk", version = "1.0.18" }
[dev-dependencies]
hex = "0.4.0"

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-metrics"
version = "1.0.12"
version = "1.0.18"
description = "Solana Metrics"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@@ -14,7 +14,7 @@ gethostname = "0.2.1"
lazy_static = "1.4.0"
log = "0.4.8"
reqwest = { version = "0.10.1", default-features = false, features = ["blocking", "rustls-tls"] }
solana-sdk = { path = "../sdk", version = "1.0.12" }
solana-sdk = { path = "../sdk", version = "1.0.18" }
[dev-dependencies]
rand = "0.6.5"

View File

@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-net-shaper"
description = "The solana cluster network shaping tool"
version = "1.0.12"
version = "1.0.18"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@@ -13,8 +13,8 @@ publish = false
clap = "2.33.0"
serde = "1.0.104"
serde_json = "1.0.46"
solana-clap-utils = { path = "../clap-utils", version = "1.0.12" }
solana-logger = { path = "../logger", version = "1.0.12" }
solana-clap-utils = { path = "../clap-utils", version = "1.0.18" }
solana-logger = { path = "../logger", version = "1.0.18" }
rand = "0.6.5"
[[bin]]

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-net-utils"
version = "1.0.12"
version = "1.0.18"
description = "Solana Network Utilities"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@@ -18,8 +18,8 @@ rand = "0.6.1"
serde = "1.0.104"
serde_derive = "1.0.103"
socket2 = "0.3.11"
solana-clap-utils = { path = "../clap-utils", version = "1.0.12" }
solana-logger = { path = "../logger", version = "1.0.12" }
solana-clap-utils = { path = "../clap-utils", version = "1.0.18" }
solana-logger = { path = "../logger", version = "1.0.18" }
tokio = "0.1"
tokio-codec = "0.1"

View File

@@ -327,13 +327,32 @@ pub fn multi_bind_in_range(
}
let mut sockets = Vec::with_capacity(num);
let port = {
let (port, _) = bind_in_range(ip_addr, range)?;
port
}; // drop the probe, port should be available... briefly.
const NUM_TRIES: usize = 100;
let mut port = 0;
let mut error = None;
for _ in 0..NUM_TRIES {
port = {
let (port, _) = bind_in_range(ip_addr, range)?;
port
}; // drop the probe, port should be available... briefly.
for _ in 0..num {
sockets.push(bind_to(ip_addr, port, true)?);
for _ in 0..num {
let sock = bind_to(ip_addr, port, true);
if let Ok(sock) = sock {
sockets.push(sock);
} else {
error = Some(sock);
break;
}
}
if sockets.len() == num {
break;
} else {
sockets.clear();
}
}
if sockets.len() != num {
error.unwrap()?;
}
Ok((port, sockets))
}

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-perf"
version = "1.0.12"
version = "1.0.18"
description = "Solana Performance APIs"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@@ -17,11 +17,11 @@ serde = "1.0.104"
dlopen_derive = "0.1.4"
lazy_static = "1.4.0"
log = "0.4.8"
solana-sdk = { path = "../sdk", version = "1.0.12" }
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.0.12" }
solana-budget-program = { path = "../programs/budget", version = "1.0.12" }
solana-logger = { path = "../logger", version = "1.0.12" }
solana-metrics = { path = "../metrics", version = "1.0.12" }
solana-sdk = { path = "../sdk", version = "1.0.18" }
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.0.18" }
solana-budget-program = { path = "../programs/budget", version = "1.0.18" }
solana-logger = { path = "../logger", version = "1.0.18" }
solana-metrics = { path = "../metrics", version = "1.0.18" }
[lib]
name = "solana_perf"

View File

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

View File

@@ -3,7 +3,7 @@
[package]
name = "solana-bpf-rust-128bit"
version = "1.0.12"
version = "1.0.18"
description = "Solana BPF test program written in Rust"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@@ -12,11 +12,11 @@ homepage = "https://solana.com/"
edition = "2018"
[dependencies]
solana-sdk = { path = "../../../../sdk/", version = "1.0.12", default-features = false }
solana-bpf-rust-128bit-dep = { path = "../128bit_dep", version = "1.0.12" }
solana-sdk = { path = "../../../../sdk/", version = "1.0.18", default-features = false }
solana-bpf-rust-128bit-dep = { path = "../128bit_dep", version = "1.0.18" }
[dev_dependencies]
solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.0.12" }
solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.0.18" }
[features]
program = ["solana-sdk/program"]

View File

@@ -3,7 +3,7 @@
[package]
name = "solana-bpf-rust-128bit-dep"
version = "1.0.12"
version = "1.0.18"
description = "Solana BPF test program written in Rust"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@@ -12,10 +12,10 @@ homepage = "https://solana.com/"
edition = "2018"
[dependencies]
solana-sdk = { path = "../../../../sdk/", version = "1.0.12", default-features = false }
solana-sdk = { path = "../../../../sdk/", version = "1.0.18", default-features = false }
[dev_dependencies]
solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.0.12" }
solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.0.18" }
[features]
program = ["solana-sdk/program"]

View File

@@ -3,7 +3,7 @@
[package]
name = "solana-bpf-rust-alloc"
version = "1.0.12"
version = "1.0.18"
description = "Solana BPF test program written in Rust"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@@ -12,10 +12,10 @@ homepage = "https://solana.com/"
edition = "2018"
[dependencies]
solana-sdk = { path = "../../../../sdk/", version = "1.0.12", default-features = false }
solana-sdk = { path = "../../../../sdk/", version = "1.0.18", default-features = false }
[dev_dependencies]
solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.0.12" }
solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.0.18" }
[features]
program = ["solana-sdk/program"]

View File

@@ -3,7 +3,7 @@
[package]
name = "solana-bpf-rust-dep-crate"
version = "1.0.12"
version = "1.0.18"
description = "Solana BPF test program written in Rust"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@@ -13,10 +13,10 @@ edition = "2018"
[dependencies]
byteorder = { version = "1", default-features = false }
solana-sdk = { path = "../../../../sdk/", version = "1.0.12", default-features = false }
solana-sdk = { path = "../../../../sdk/", version = "1.0.18", default-features = false }
[dev_dependencies]
solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.0.12" }
solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.0.18" }
[features]
program = ["solana-sdk/program"]

View File

@@ -3,7 +3,7 @@
[package]
name = "solana-bpf-rust-dup-accounts"
version = "1.0.12"
version = "1.0.18"
description = "Solana BPF test program written in Rust"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@@ -12,10 +12,10 @@ homepage = "https://solana.com/"
edition = "2018"
[dependencies]
solana-sdk = { path = "../../../../sdk/", version = "1.0.12", default-features = false }
solana-sdk = { path = "../../../../sdk/", version = "1.0.18", default-features = false }
[dev_dependencies]
solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.0.12" }
solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.0.18" }
[features]
program = ["solana-sdk/program"]

View File

@@ -3,7 +3,7 @@
[package]
name = "solana-bpf-rust-error-handling"
version = "1.0.12"
version = "1.0.18"
description = "Solana BPF test program written in Rust"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@@ -14,11 +14,11 @@ edition = "2018"
[dependencies]
num-derive = "0.2"
num-traits = "0.2"
solana-sdk = { path = "../../../../sdk/", version = "1.0.12", default-features = false }
solana-sdk = { path = "../../../../sdk/", version = "1.0.18", default-features = false }
thiserror = "1.0"
[dev_dependencies]
solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.0.12" }
solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.0.18" }
[features]
program = ["solana-sdk/program"]

View File

@@ -3,7 +3,7 @@
[package]
name = "solana-bpf-rust-external-spend"
version = "1.0.12"
version = "1.0.18"
description = "Solana BPF test program written in Rust"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@@ -12,10 +12,10 @@ homepage = "https://solana.com/"
edition = "2018"
[dependencies]
solana-sdk = { path = "../../../../sdk/", version = "1.0.12", default-features = false }
solana-sdk = { path = "../../../../sdk/", version = "1.0.18", default-features = false }
[dev_dependencies]
solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.0.12" }
solana-sdk-bpf-test = { path = "../../../../sdk/bpf/rust/test", version = "1.0.18" }
[features]
program = ["solana-sdk/program"]

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