Compare commits
403 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
0b5beac561 | ||
|
83801f2b05 | ||
|
13527fc937 | ||
|
bfc9932b17 | ||
|
1ea1a70d2b | ||
|
0039861984 | ||
|
fb3e88bb7e | ||
|
c01bff61f8 | ||
|
6a0ba2587d | ||
|
3520990271 | ||
|
f7d2ba24f0 | ||
|
90c6f8ae1a | ||
|
24bd147b1a | ||
|
07191dc224 | ||
|
c20e74a248 | ||
|
f52feddfcd | ||
|
a46d14a2d7 | ||
|
fb9f35d2f0 | ||
|
3687dccda6 | ||
|
8d96797e68 | ||
|
820aa7e0ea | ||
|
eea3c4e4f1 | ||
|
c66ee3c623 | ||
|
871066fb8d | ||
|
aa084e1d3c | ||
|
be0f9d4837 | ||
|
38485489c9 | ||
|
abbc146508 | ||
|
d58e2f1c68 | ||
|
fa32b4a848 | ||
|
aeb6ef3969 | ||
|
a0c1e4a6e2 | ||
|
c1047b48fe | ||
|
add6989289 | ||
|
363b9d7209 | ||
|
9846a8089e | ||
|
d96bcc9ead | ||
|
de445ec83e | ||
|
bb4943d8a7 | ||
|
0f3705340b | ||
|
d972a21aab | ||
|
f705340971 | ||
|
e313cf8332 | ||
|
38d7d7df50 | ||
|
eb0a448b99 | ||
|
e6f54ffd56 | ||
|
44315895cf | ||
|
9899e6fb39 | ||
|
da1796f97a | ||
|
501fea7a3c | ||
|
2583174cbd | ||
|
72aa24db2c | ||
|
bbf53f9050 | ||
|
e7a95b1189 | ||
|
311a1a38dc | ||
|
a1828a0e45 | ||
|
01d9062f7b | ||
|
446e02bad1 | ||
|
3955778cb6 | ||
|
4f4a0b9dc8 | ||
|
97c1c944fb | ||
|
7890957250 | ||
|
535c7a44db | ||
|
fbbc640958 | ||
|
3559062c00 | ||
|
1a7e5669bf | ||
|
5511d52c62 | ||
|
50d5978b00 | ||
|
cea722ac09 | ||
|
82b087e51e | ||
|
da682eaef6 | ||
|
688562c049 | ||
|
50e6fad0e6 | ||
|
d5cce435ce | ||
|
8f75cfe247 | ||
|
686b775e35 | ||
|
a3be6bacc6 | ||
|
b493d554ad | ||
|
0fc3bcabfd | ||
|
7cf36c488d | ||
|
89c39d0be1 | ||
|
f5eb5a3ba6 | ||
|
64c2e759ab | ||
|
a09ee672a6 | ||
|
057b5d7e24 | ||
|
fe4c59e38d | ||
|
305d5d97d8 | ||
|
05f464798f | ||
|
96d8ee9e07 | ||
|
471a3f991b | ||
|
fd9e003ae1 | ||
|
e26ff09df7 | ||
|
fdcf589f7c | ||
|
f44dfc8d04 | ||
|
c736c4633e | ||
|
7099d07fd3 | ||
|
e3b47d22d7 | ||
|
f789da1e20 | ||
|
b74bb12ebe | ||
|
ddf019c1a4 | ||
|
74d57b1c2f | ||
|
086e653a0b | ||
|
225d15bde8 | ||
|
6d8d5d1379 | ||
|
fcd2a78d73 | ||
|
5cf52c3c20 | ||
|
bc334427e3 | ||
|
d0cac2a2ea | ||
|
3743e44fb3 | ||
|
f5ef999b31 | ||
|
55d5339daa | ||
|
594b69395a | ||
|
6dc62bfb77 | ||
|
08b53c0963 | ||
|
b5baa966ac | ||
|
ff38a46af6 | ||
|
841f596b26 | ||
|
5e1497856b | ||
|
e085b580b5 | ||
|
d967ff0138 | ||
|
71e3a99742 | ||
|
640f4a1ec7 | ||
|
8d904877ef | ||
|
fbe4e95e6a | ||
|
e7e7cbe632 | ||
|
7e5b75fa7e | ||
|
60beb509f7 | ||
|
15f6b6ccd6 | ||
|
f56f3d81b5 | ||
|
098c94352d | ||
|
c929e8e02b | ||
|
80f2c485ba | ||
|
f855f4d1c0 | ||
|
81a26aa4fc | ||
|
855cf9a362 | ||
|
96ba314281 | ||
|
e52e6dfbaa | ||
|
181ff3d13e | ||
|
195ce0ed79 | ||
|
b24e301201 | ||
|
fb492efda8 | ||
|
c40216350c | ||
|
d031bbcf2e | ||
|
c183c3a5ec | ||
|
f04d4af4f2 | ||
|
ea0a3521ed | ||
|
a75898a415 | ||
|
a77fce465a | ||
|
719f162229 | ||
|
a39cc8d21f | ||
|
87767b181d | ||
|
88b19e10cb | ||
|
b42cda32ff | ||
|
2344391c48 | ||
|
207d13e429 | ||
|
360f166f5a | ||
|
b4deeb8e36 | ||
|
b3e1fde8b2 | ||
|
b838aba840 | ||
|
c8b3d0ba07 | ||
|
e7106278e9 | ||
|
63cf168fef | ||
|
71ea198a07 | ||
|
f6b65b033e | ||
|
0311ad5ddf | ||
|
f5454e62a1 | ||
|
89ea4dfa8b | ||
|
fefcfdba80 | ||
|
1072bd7640 | ||
|
a7280f117a | ||
|
d46a19098a | ||
|
44fffcbb1c | ||
|
e14c2f94f4 | ||
|
437c356626 | ||
|
fd68f8ba2e | ||
|
2374664e95 | ||
|
2cb9ca5966 | ||
|
4f247a232f | ||
|
15a2c73826 | ||
|
d23f1436c5 | ||
|
70c87d1a23 | ||
|
053ce10ce5 | ||
|
055eb360c2 | ||
|
25cd1ceeeb | ||
|
52ee3b1cee | ||
|
bbadcca414 | ||
|
e9eba97299 | ||
|
920b63944e | ||
|
8104895a07 | ||
|
c9e646b86b | ||
|
7c47db1e3d | ||
|
c619e9b560 | ||
|
ccd48923a0 | ||
|
4e797cc867 | ||
|
9627bfced3 | ||
|
f823b10597 | ||
|
c9e56c9749 | ||
|
da7482d631 | ||
|
97650c7f37 | ||
|
e738bf1c9a | ||
|
afebb2a8a5 | ||
|
4e4fd03b65 | ||
|
049ca18dc5 | ||
|
495c64556e | ||
|
747e91d434 | ||
|
6d4f6e79b0 | ||
|
98e9f34704 | ||
|
70f74174e8 | ||
|
70985f82f1 | ||
|
3b2bdd9f8a | ||
|
d33ae59fbf | ||
|
9ead7ca11a | ||
|
dbcef35f7d | ||
|
9e733d7d9b | ||
|
39f1240ec2 | ||
|
fa249721fa | ||
|
137793cd4c | ||
|
47d8608aee | ||
|
ed410aea10 | ||
|
957dfa8f73 | ||
|
98095b6f8d | ||
|
a2c32d7d0e | ||
|
b15d826476 | ||
|
ed97a2578d | ||
|
89f61f0b41 | ||
|
04cc9c1148 | ||
|
8314ab4508 | ||
|
3a98042753 | ||
|
60d316c9fd | ||
|
e324c221a6 | ||
|
61246999ac | ||
|
e476dc4eaa | ||
|
ee18e7668b | ||
|
62db7f6562 | ||
|
2e9b501355 | ||
|
089a99f1e3 | ||
|
57961b1d17 | ||
|
fe8b2b7850 | ||
|
0bf45cbab6 | ||
|
5877427389 | ||
|
25141288f4 | ||
|
b28d10d46f | ||
|
b6dc48da75 | ||
|
f2d929c12d | ||
|
c49b89091a | ||
|
23fe3a86d9 | ||
|
2f778725d6 | ||
|
93a119a51e | ||
|
65a7b536c9 | ||
|
1281483a8c | ||
|
4312841433 | ||
|
b859acbfea | ||
|
40a3885d3b | ||
|
36b7c2ea97 | ||
|
24bd4ff6d4 | ||
|
69b3f10207 | ||
|
9922f09a1d | ||
|
38a99c0c25 | ||
|
7031235714 | ||
|
dfb2356a9a | ||
|
010794806a | ||
|
6f95d5f72a | ||
|
2720b939fd | ||
|
a25c3fcf7d | ||
|
7cc4810174 | ||
|
c1a55bf249 | ||
|
f19778b7d9 | ||
|
eecdacac42 | ||
|
429f130532 | ||
|
19b9839dfc | ||
|
ad2bf3afa6 | ||
|
5c739ba236 | ||
|
9fac507606 | ||
|
d5a37cb06e | ||
|
86eb0157c0 | ||
|
072dab0948 | ||
|
e20e79f412 | ||
|
f118db81ce | ||
|
4ecb78d303 | ||
|
0a28e40606 | ||
|
4d7a5a9daf | ||
|
64cf6b4388 | ||
|
f334c3b895 | ||
|
15a7bcd4fe | ||
|
8d6636d02a | ||
|
cf896dbeee | ||
|
e5b60b75f8 | ||
|
0e155fdbd9 | ||
|
b79a337ddd | ||
|
c4050f541d | ||
|
f0b74a4ecf | ||
|
f7979378fd | ||
|
d7c5607982 | ||
|
91ab5ae990 | ||
|
605e767259 | ||
|
597618846b | ||
|
712267bf51 | ||
|
eb9cef0cd4 | ||
|
62e0e19961 | ||
|
9aee9cb867 | ||
|
2b11558b36 | ||
|
18c4e1b023 | ||
|
6bac44ed92 | ||
|
8cb622084f | ||
|
38f7e9a979 | ||
|
a536f779ee | ||
|
84a5e5ec97 | ||
|
dd33aae3cf | ||
|
be2ace47e3 | ||
|
53b074aa35 | ||
|
a4ad2925a2 | ||
|
edfbd8d65a | ||
|
e0ae54fd7e | ||
|
60297951ec | ||
|
e0f9f72a2c | ||
|
5236acf4b0 | ||
|
5dd61b5db2 | ||
|
8752bf0826 | ||
|
b1712e80ec | ||
|
2fe1a4677c | ||
|
f76c128f4f | ||
|
b143b9c3c2 | ||
|
b4178b75e7 | ||
|
c54b751df7 | ||
|
0fde9e893f | ||
|
d24abbdac9 | ||
|
3b03985f28 | ||
|
d05bfa08c7 | ||
|
9da2ac7a44 | ||
|
9e95d0fb58 | ||
|
94cad9873c | ||
|
f33171b32f | ||
|
aa6406f263 | ||
|
77864a6bee | ||
|
b51715d33c | ||
|
7d395177d4 | ||
|
77ba6d6784 | ||
|
4bf0a54ed7 | ||
|
8a526f2f53 | ||
|
43f99bdb31 | ||
|
0008dc62e4 | ||
|
7e8174fb79 | ||
|
4ad2ebcde9 | ||
|
da183d655a | ||
|
2e449276be | ||
|
8cac6835c0 | ||
|
677c184e47 | ||
|
f36cfb92f7 | ||
|
e7062de05f | ||
|
a443e2e773 | ||
|
3a6db787e2 | ||
|
f3c986385f | ||
|
3df811348f | ||
|
e8c86ed3e5 | ||
|
489a7bb576 | ||
|
688dd85e61 | ||
|
fe54a30084 | ||
|
80942841a2 | ||
|
d2808a8e29 | ||
|
f8413a28b5 | ||
|
bc96332899 | ||
|
ceeeb3c9dd | ||
|
bd058ec8f1 | ||
|
4b5ac44fc8 | ||
|
fef979f0e5 | ||
|
cca2cdf39b | ||
|
6e91996606 | ||
|
99be00d61f | ||
|
68f808026e | ||
|
0c7ab0a1bb | ||
|
3d8ccbc079 | ||
|
275d096a46 | ||
|
6d70a06b23 | ||
|
7e68b2e1bd | ||
|
f0d761630e | ||
|
1986927eb6 | ||
|
9a0ea61007 | ||
|
51a70e52f2 | ||
|
9797c93db3 | ||
|
9598114658 | ||
|
d3ef061044 | ||
|
1f102d2617 | ||
|
5e97bd3d8a | ||
|
ed06e8b85d | ||
|
10b9225edb | ||
|
b1b5ddd2b9 | ||
|
6b9b107ead | ||
|
3fef98fd1e | ||
|
e999823b4b | ||
|
1e46a5b147 | ||
|
567a1cb944 | ||
|
2996cebfaa | ||
|
7a1889aaf9 | ||
|
9188153b7d | ||
|
4b9f2e987a | ||
|
bb5c76483a | ||
|
aafbb251b9 | ||
|
dd32540ceb | ||
|
e1a9cbaf3c | ||
|
83740246fc | ||
|
7a53ca18a6 | ||
|
c1a8637cb5 | ||
|
d6831309cd |
@@ -31,4 +31,9 @@ export CARGO_TARGET_CACHE=$HOME/cargo-target-cache/"$CHANNEL"-"$BUILDKITE_LABEL"
|
|||||||
|
|
||||||
mkdir -p "$CARGO_TARGET_CACHE"/target
|
mkdir -p "$CARGO_TARGET_CACHE"/target
|
||||||
rsync -a --delete --link-dest="$CARGO_TARGET_CACHE" "$CARGO_TARGET_CACHE"/target .
|
rsync -a --delete --link-dest="$CARGO_TARGET_CACHE" "$CARGO_TARGET_CACHE"/target .
|
||||||
|
|
||||||
|
# Don't reuse BPF target build artifacts due to incremental build issues with
|
||||||
|
# `std:
|
||||||
|
# "found possibly newer version of crate `std` which `xyz` depends on
|
||||||
|
rm -rf target/bpfel-unknown-unknown
|
||||||
)
|
)
|
||||||
|
Binary file not shown.
4
.cache/fontconfig/CACHEDIR.TAG
Normal file
4
.cache/fontconfig/CACHEDIR.TAG
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Signature: 8a477f597d28d172789f06886806bc55
|
||||||
|
# This file is a cache directory tag created by fontconfig.
|
||||||
|
# For information about cache directory tags, see:
|
||||||
|
# http://www.brynosaurus.com/cachedir/
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -34,6 +34,8 @@ jobs:
|
|||||||
- stable
|
- stable
|
||||||
install:
|
install:
|
||||||
- source ci/rust-version.sh
|
- source ci/rust-version.sh
|
||||||
|
- PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH"
|
||||||
|
- readlink -f .
|
||||||
script:
|
script:
|
||||||
- source ci/env.sh
|
- source ci/env.sh
|
||||||
- ci/publish-tarball.sh
|
- ci/publish-tarball.sh
|
||||||
|
1447
Cargo.lock
generated
1447
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,7 @@ members = [
|
|||||||
"dos",
|
"dos",
|
||||||
"download-utils",
|
"download-utils",
|
||||||
"faucet",
|
"faucet",
|
||||||
|
"frozen-abi",
|
||||||
"perf",
|
"perf",
|
||||||
"validator",
|
"validator",
|
||||||
"genesis",
|
"genesis",
|
||||||
@@ -30,12 +31,14 @@ members = [
|
|||||||
"merkle-tree",
|
"merkle-tree",
|
||||||
"stake-o-matic",
|
"stake-o-matic",
|
||||||
"storage-bigtable",
|
"storage-bigtable",
|
||||||
|
"storage-proto",
|
||||||
"streamer",
|
"streamer",
|
||||||
"measure",
|
"measure",
|
||||||
"metrics",
|
"metrics",
|
||||||
"net-shaper",
|
"net-shaper",
|
||||||
"notifier",
|
"notifier",
|
||||||
"poh-bench",
|
"poh-bench",
|
||||||
|
"program-test",
|
||||||
"programs/secp256k1",
|
"programs/secp256k1",
|
||||||
"programs/bpf_loader",
|
"programs/bpf_loader",
|
||||||
"programs/budget",
|
"programs/budget",
|
||||||
@@ -51,6 +54,8 @@ members = [
|
|||||||
"ramp-tps",
|
"ramp-tps",
|
||||||
"runtime",
|
"runtime",
|
||||||
"sdk",
|
"sdk",
|
||||||
|
"sdk/cargo-build-bpf",
|
||||||
|
"sdk/cargo-test-bpf",
|
||||||
"scripts",
|
"scripts",
|
||||||
"stake-accounts",
|
"stake-accounts",
|
||||||
"stake-monitor",
|
"stake-monitor",
|
||||||
|
@@ -61,8 +61,9 @@ $ cargo test
|
|||||||
### Starting a local testnet
|
### Starting a local testnet
|
||||||
Start your own testnet locally, instructions are in the [online docs](https://docs.solana.com/cluster/bench-tps).
|
Start your own testnet locally, instructions are in the [online docs](https://docs.solana.com/cluster/bench-tps).
|
||||||
|
|
||||||
### Accessing the remote testnet
|
### Accessing the remote development cluster
|
||||||
* `testnet` - public stable testnet accessible via devnet.solana.com. Runs 24/7
|
* `devnet` - stable public cluster for development accessible via
|
||||||
|
devnet.solana.com. Runs 24/7. Learn more about the [public clusters](https://docs.solana.com/clusters)
|
||||||
|
|
||||||
# Benchmarking
|
# Benchmarking
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-account-decoder"
|
name = "solana-account-decoder"
|
||||||
version = "1.4.0"
|
version = "1.4.16"
|
||||||
description = "Solana account decoder"
|
description = "Solana account decoder"
|
||||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
@@ -18,12 +18,13 @@ lazy_static = "1.4.0"
|
|||||||
serde = "1.0.112"
|
serde = "1.0.112"
|
||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
serde_json = "1.0.56"
|
serde_json = "1.0.56"
|
||||||
solana-config-program = { path = "../programs/config", version = "1.4.0" }
|
solana-config-program = { path = "../programs/config", version = "1.4.16" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.4.0" }
|
solana-sdk = { path = "../sdk", version = "1.4.16" }
|
||||||
solana-stake-program = { path = "../programs/stake", version = "1.4.0" }
|
solana-stake-program = { path = "../programs/stake", version = "1.4.16" }
|
||||||
solana-vote-program = { path = "../programs/vote", version = "1.4.0" }
|
solana-vote-program = { path = "../programs/vote", version = "1.4.16" }
|
||||||
spl-token-v2-0 = { package = "spl-token", version = "=2.0.6", features = ["skip-no-mangle"] }
|
spl-token-v2-0 = { package = "spl-token", version = "=3.0.0", features = ["no-entrypoint"] }
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
|
zstd = "0.5.1"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
targets = ["x86_64-unknown-linux-gnu"]
|
targets = ["x86_64-unknown-linux-gnu"]
|
||||||
|
@@ -12,9 +12,14 @@ pub mod parse_token;
|
|||||||
pub mod parse_vote;
|
pub mod parse_vote;
|
||||||
pub mod validator_info;
|
pub mod validator_info;
|
||||||
|
|
||||||
use crate::parse_account_data::{parse_account_data, AccountAdditionalData, ParsedAccount};
|
use {
|
||||||
use solana_sdk::{account::Account, clock::Epoch, fee_calculator::FeeCalculator, pubkey::Pubkey};
|
crate::parse_account_data::{parse_account_data, AccountAdditionalData, ParsedAccount},
|
||||||
use std::str::FromStr;
|
solana_sdk::{account::Account, clock::Epoch, fee_calculator::FeeCalculator, pubkey::Pubkey},
|
||||||
|
std::{
|
||||||
|
io::{Read, Write},
|
||||||
|
str::FromStr,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
pub type StringAmount = String;
|
pub type StringAmount = String;
|
||||||
|
|
||||||
@@ -44,6 +49,8 @@ pub enum UiAccountEncoding {
|
|||||||
Base58,
|
Base58,
|
||||||
Base64,
|
Base64,
|
||||||
JsonParsed,
|
JsonParsed,
|
||||||
|
#[serde(rename = "base64+zstd")]
|
||||||
|
Base64Zstd,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UiAccount {
|
impl UiAccount {
|
||||||
@@ -66,6 +73,19 @@ impl UiAccount {
|
|||||||
base64::encode(slice_data(&account.data, data_slice_config)),
|
base64::encode(slice_data(&account.data, data_slice_config)),
|
||||||
encoding,
|
encoding,
|
||||||
),
|
),
|
||||||
|
UiAccountEncoding::Base64Zstd => {
|
||||||
|
let mut encoder = zstd::stream::write::Encoder::new(Vec::new(), 0).unwrap();
|
||||||
|
match encoder
|
||||||
|
.write_all(slice_data(&account.data, data_slice_config))
|
||||||
|
.and_then(|()| encoder.finish())
|
||||||
|
{
|
||||||
|
Ok(zstd_data) => UiAccountData::Binary(base64::encode(zstd_data), encoding),
|
||||||
|
Err(_) => UiAccountData::Binary(
|
||||||
|
base64::encode(slice_data(&account.data, data_slice_config)),
|
||||||
|
UiAccountEncoding::Base64,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
UiAccountEncoding::JsonParsed => {
|
UiAccountEncoding::JsonParsed => {
|
||||||
if let Ok(parsed_data) =
|
if let Ok(parsed_data) =
|
||||||
parse_account_data(pubkey, &account.owner, &account.data, additional_data)
|
parse_account_data(pubkey, &account.owner, &account.data, additional_data)
|
||||||
@@ -92,6 +112,16 @@ impl UiAccount {
|
|||||||
UiAccountData::Binary(blob, encoding) => match encoding {
|
UiAccountData::Binary(blob, encoding) => match encoding {
|
||||||
UiAccountEncoding::Base58 => bs58::decode(blob).into_vec().ok(),
|
UiAccountEncoding::Base58 => bs58::decode(blob).into_vec().ok(),
|
||||||
UiAccountEncoding::Base64 => base64::decode(blob).ok(),
|
UiAccountEncoding::Base64 => base64::decode(blob).ok(),
|
||||||
|
UiAccountEncoding::Base64Zstd => base64::decode(blob)
|
||||||
|
.ok()
|
||||||
|
.map(|zstd_data| {
|
||||||
|
let mut data = vec![];
|
||||||
|
zstd::stream::read::Decoder::new(zstd_data.as_slice())
|
||||||
|
.and_then(|mut reader| reader.read_to_end(&mut data))
|
||||||
|
.map(|_| data)
|
||||||
|
.ok()
|
||||||
|
})
|
||||||
|
.flatten(),
|
||||||
UiAccountEncoding::Binary | UiAccountEncoding::JsonParsed => None,
|
UiAccountEncoding::Binary | UiAccountEncoding::JsonParsed => None,
|
||||||
},
|
},
|
||||||
}?;
|
}?;
|
||||||
@@ -179,4 +209,25 @@ mod test {
|
|||||||
});
|
});
|
||||||
assert_eq!(slice_data(&data, slice_config), &[] as &[u8]);
|
assert_eq!(slice_data(&data, slice_config), &[] as &[u8]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_base64_zstd() {
|
||||||
|
let encoded_account = UiAccount::encode(
|
||||||
|
&Pubkey::default(),
|
||||||
|
Account {
|
||||||
|
data: vec![0; 1024],
|
||||||
|
..Account::default()
|
||||||
|
},
|
||||||
|
UiAccountEncoding::Base64Zstd,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
assert!(matches!(
|
||||||
|
encoded_account.data,
|
||||||
|
UiAccountData::Binary(_, UiAccountEncoding::Base64Zstd)
|
||||||
|
));
|
||||||
|
|
||||||
|
let decoded_account = encoded_account.decode().unwrap();
|
||||||
|
assert_eq!(decoded_account.data, vec![0; 1024]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -111,8 +111,8 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_account_data() {
|
fn test_parse_account_data() {
|
||||||
let account_pubkey = Pubkey::new_rand();
|
let account_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let other_program = Pubkey::new_rand();
|
let other_program = solana_sdk::pubkey::new_rand();
|
||||||
let data = vec![0; 4];
|
let data = vec![0; 4];
|
||||||
assert!(parse_account_data(&account_pubkey, &other_program, &data, None).is_err());
|
assert!(parse_account_data(&account_pubkey, &other_program, &data, None).is_err());
|
||||||
|
|
||||||
|
@@ -117,7 +117,7 @@ mod test {
|
|||||||
}))
|
}))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
};
|
};
|
||||||
let info_pubkey = Pubkey::new_rand();
|
let info_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let validator_info_config_account = create_config_account(
|
let validator_info_config_account = create_config_account(
|
||||||
vec![(validator_info::id(), false), (info_pubkey, true)],
|
vec![(validator_info::id(), false), (info_pubkey, true)],
|
||||||
&validator_info,
|
&validator_info,
|
||||||
|
@@ -134,7 +134,6 @@ impl From<Delegation> for UiDelegation {
|
|||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use bincode::serialize;
|
use bincode::serialize;
|
||||||
use solana_sdk::pubkey::Pubkey;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_stake() {
|
fn test_parse_stake() {
|
||||||
@@ -145,8 +144,8 @@ mod test {
|
|||||||
StakeAccountType::Uninitialized
|
StakeAccountType::Uninitialized
|
||||||
);
|
);
|
||||||
|
|
||||||
let pubkey = Pubkey::new_rand();
|
let pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let custodian = Pubkey::new_rand();
|
let custodian = solana_sdk::pubkey::new_rand();
|
||||||
let authorized = Authorized::auto(&pubkey);
|
let authorized = Authorized::auto(&pubkey);
|
||||||
let lockup = Lockup {
|
let lockup = Lockup {
|
||||||
unix_timestamp: 0,
|
unix_timestamp: 0,
|
||||||
@@ -180,7 +179,7 @@ mod test {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
let voter_pubkey = Pubkey::new_rand();
|
let voter_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let stake = Stake {
|
let stake = Stake {
|
||||||
delegation: Delegation {
|
delegation: Delegation {
|
||||||
voter_pubkey,
|
voter_pubkey,
|
||||||
|
@@ -105,6 +105,7 @@ pub enum SysvarAccountType {
|
|||||||
pub struct UiClock {
|
pub struct UiClock {
|
||||||
pub slot: Slot,
|
pub slot: Slot,
|
||||||
pub epoch: Epoch,
|
pub epoch: Epoch,
|
||||||
|
pub epoch_start_timestamp: UnixTimestamp,
|
||||||
pub leader_schedule_epoch: Epoch,
|
pub leader_schedule_epoch: Epoch,
|
||||||
pub unix_timestamp: UnixTimestamp,
|
pub unix_timestamp: UnixTimestamp,
|
||||||
}
|
}
|
||||||
@@ -114,6 +115,7 @@ impl From<Clock> for UiClock {
|
|||||||
Self {
|
Self {
|
||||||
slot: clock.slot,
|
slot: clock.slot,
|
||||||
epoch: clock.epoch,
|
epoch: clock.epoch,
|
||||||
|
epoch_start_timestamp: clock.epoch_start_timestamp,
|
||||||
leader_schedule_epoch: clock.leader_schedule_epoch,
|
leader_schedule_epoch: clock.leader_schedule_epoch,
|
||||||
unix_timestamp: clock.unix_timestamp,
|
unix_timestamp: clock.unix_timestamp,
|
||||||
}
|
}
|
||||||
@@ -212,15 +214,14 @@ pub struct UiStakeHistoryEntry {
|
|||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
fee_calculator::FeeCalculator,
|
account::create_account, fee_calculator::FeeCalculator, hash::Hash,
|
||||||
hash::Hash,
|
sysvar::recent_blockhashes::IterItem,
|
||||||
sysvar::{recent_blockhashes::IterItem, Sysvar},
|
|
||||||
};
|
};
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_sysvars() {
|
fn test_parse_sysvars() {
|
||||||
let clock_sysvar = Clock::default().create_account(1);
|
let clock_sysvar = create_account(&Clock::default(), 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_sysvar(&clock_sysvar.data, &sysvar::clock::id()).unwrap(),
|
parse_sysvar(&clock_sysvar.data, &sysvar::clock::id()).unwrap(),
|
||||||
SysvarAccountType::Clock(UiClock::default()),
|
SysvarAccountType::Clock(UiClock::default()),
|
||||||
@@ -233,13 +234,13 @@ mod test {
|
|||||||
first_normal_epoch: 1,
|
first_normal_epoch: 1,
|
||||||
first_normal_slot: 12,
|
first_normal_slot: 12,
|
||||||
};
|
};
|
||||||
let epoch_schedule_sysvar = epoch_schedule.create_account(1);
|
let epoch_schedule_sysvar = create_account(&epoch_schedule, 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_sysvar(&epoch_schedule_sysvar.data, &sysvar::epoch_schedule::id()).unwrap(),
|
parse_sysvar(&epoch_schedule_sysvar.data, &sysvar::epoch_schedule::id()).unwrap(),
|
||||||
SysvarAccountType::EpochSchedule(epoch_schedule),
|
SysvarAccountType::EpochSchedule(epoch_schedule),
|
||||||
);
|
);
|
||||||
|
|
||||||
let fees_sysvar = Fees::default().create_account(1);
|
let fees_sysvar = create_account(&Fees::default(), 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_sysvar(&fees_sysvar.data, &sysvar::fees::id()).unwrap(),
|
parse_sysvar(&fees_sysvar.data, &sysvar::fees::id()).unwrap(),
|
||||||
SysvarAccountType::Fees(UiFees::default()),
|
SysvarAccountType::Fees(UiFees::default()),
|
||||||
@@ -251,7 +252,7 @@ mod test {
|
|||||||
};
|
};
|
||||||
let recent_blockhashes =
|
let recent_blockhashes =
|
||||||
RecentBlockhashes::from_iter(vec![IterItem(0, &hash, &fee_calculator)].into_iter());
|
RecentBlockhashes::from_iter(vec![IterItem(0, &hash, &fee_calculator)].into_iter());
|
||||||
let recent_blockhashes_sysvar = recent_blockhashes.create_account(1);
|
let recent_blockhashes_sysvar = create_account(&recent_blockhashes, 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_sysvar(
|
parse_sysvar(
|
||||||
&recent_blockhashes_sysvar.data,
|
&recent_blockhashes_sysvar.data,
|
||||||
@@ -269,13 +270,13 @@ mod test {
|
|||||||
exemption_threshold: 2.0,
|
exemption_threshold: 2.0,
|
||||||
burn_percent: 5,
|
burn_percent: 5,
|
||||||
};
|
};
|
||||||
let rent_sysvar = rent.create_account(1);
|
let rent_sysvar = create_account(&rent, 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_sysvar(&rent_sysvar.data, &sysvar::rent::id()).unwrap(),
|
parse_sysvar(&rent_sysvar.data, &sysvar::rent::id()).unwrap(),
|
||||||
SysvarAccountType::Rent(rent.into()),
|
SysvarAccountType::Rent(rent.into()),
|
||||||
);
|
);
|
||||||
|
|
||||||
let rewards_sysvar = Rewards::default().create_account(1);
|
let rewards_sysvar = create_account(&Rewards::default(), 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_sysvar(&rewards_sysvar.data, &sysvar::rewards::id()).unwrap(),
|
parse_sysvar(&rewards_sysvar.data, &sysvar::rewards::id()).unwrap(),
|
||||||
SysvarAccountType::Rewards(UiRewards::default()),
|
SysvarAccountType::Rewards(UiRewards::default()),
|
||||||
@@ -283,7 +284,7 @@ mod test {
|
|||||||
|
|
||||||
let mut slot_hashes = SlotHashes::default();
|
let mut slot_hashes = SlotHashes::default();
|
||||||
slot_hashes.add(1, hash);
|
slot_hashes.add(1, hash);
|
||||||
let slot_hashes_sysvar = slot_hashes.create_account(1);
|
let slot_hashes_sysvar = create_account(&slot_hashes, 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_sysvar(&slot_hashes_sysvar.data, &sysvar::slot_hashes::id()).unwrap(),
|
parse_sysvar(&slot_hashes_sysvar.data, &sysvar::slot_hashes::id()).unwrap(),
|
||||||
SysvarAccountType::SlotHashes(vec![UiSlotHashEntry {
|
SysvarAccountType::SlotHashes(vec![UiSlotHashEntry {
|
||||||
@@ -294,7 +295,7 @@ mod test {
|
|||||||
|
|
||||||
let mut slot_history = SlotHistory::default();
|
let mut slot_history = SlotHistory::default();
|
||||||
slot_history.add(42);
|
slot_history.add(42);
|
||||||
let slot_history_sysvar = slot_history.create_account(1);
|
let slot_history_sysvar = create_account(&slot_history, 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_sysvar(&slot_history_sysvar.data, &sysvar::slot_history::id()).unwrap(),
|
parse_sysvar(&slot_history_sysvar.data, &sysvar::slot_history::id()).unwrap(),
|
||||||
SysvarAccountType::SlotHistory(UiSlotHistory {
|
SysvarAccountType::SlotHistory(UiSlotHistory {
|
||||||
@@ -310,7 +311,7 @@ mod test {
|
|||||||
deactivating: 3,
|
deactivating: 3,
|
||||||
};
|
};
|
||||||
stake_history.add(1, stake_history_entry.clone());
|
stake_history.add(1, stake_history_entry.clone());
|
||||||
let stake_history_sysvar = stake_history.create_account(1);
|
let stake_history_sysvar = create_account(&stake_history, 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_sysvar(&stake_history_sysvar.data, &sysvar::stake_history::id()).unwrap(),
|
parse_sysvar(&stake_history_sysvar.data, &sysvar::stake_history::id()).unwrap(),
|
||||||
SysvarAccountType::StakeHistory(vec![UiStakeHistoryEntry {
|
SysvarAccountType::StakeHistory(vec![UiStakeHistoryEntry {
|
||||||
@@ -319,7 +320,7 @@ mod test {
|
|||||||
}]),
|
}]),
|
||||||
);
|
);
|
||||||
|
|
||||||
let bad_pubkey = Pubkey::new_rand();
|
let bad_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
assert!(parse_sysvar(&stake_history_sysvar.data, &bad_pubkey).is_err());
|
assert!(parse_sysvar(&stake_history_sysvar.data, &bad_pubkey).is_err());
|
||||||
|
|
||||||
let bad_data = vec![0; 4];
|
let bad_data = vec![0; 4];
|
||||||
|
@@ -4,7 +4,9 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey::Pubkey;
|
||||||
use spl_token_v2_0::{
|
use spl_token_v2_0::{
|
||||||
solana_sdk::{program_option::COption, program_pack::Pack, pubkey::Pubkey as SplTokenPubkey},
|
solana_program::{
|
||||||
|
program_option::COption, program_pack::Pack, pubkey::Pubkey as SplTokenPubkey,
|
||||||
|
},
|
||||||
state::{Account, AccountState, Mint, Multisig},
|
state::{Account, AccountState, Mint, Multisig},
|
||||||
};
|
};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
@@ -21,6 +23,16 @@ pub fn spl_token_v2_0_native_mint() -> Pubkey {
|
|||||||
Pubkey::from_str(&spl_token_v2_0::native_mint::id().to_string()).unwrap()
|
Pubkey::from_str(&spl_token_v2_0::native_mint::id().to_string()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A helper function to convert a solana_sdk::pubkey::Pubkey to spl_sdk::pubkey::Pubkey
|
||||||
|
pub fn spl_token_v2_0_pubkey(pubkey: &Pubkey) -> SplTokenPubkey {
|
||||||
|
SplTokenPubkey::from_str(&pubkey.to_string()).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
// A helper function to convert a spl_sdk::pubkey::Pubkey to solana_sdk::pubkey::Pubkey
|
||||||
|
pub fn pubkey_from_spl_token_v2_0(pubkey: &SplTokenPubkey) -> Pubkey {
|
||||||
|
Pubkey::from_str(&pubkey.to_string()).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_token(
|
pub fn parse_token(
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
mint_decimals: Option<u8>,
|
mint_decimals: Option<u8>,
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "solana-accounts-bench"
|
name = "solana-accounts-bench"
|
||||||
version = "1.4.0"
|
version = "1.4.16"
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
homepage = "https://solana.com/"
|
homepage = "https://solana.com/"
|
||||||
@@ -11,11 +11,11 @@ publish = false
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.4.6"
|
log = "0.4.6"
|
||||||
rayon = "1.4.0"
|
rayon = "1.4.0"
|
||||||
solana-logger = { path = "../logger", version = "1.4.0" }
|
solana-logger = { path = "../logger", version = "1.4.16" }
|
||||||
solana-runtime = { path = "../runtime", version = "1.4.0" }
|
solana-runtime = { path = "../runtime", version = "1.4.16" }
|
||||||
solana-measure = { path = "../measure", version = "1.4.0" }
|
solana-measure = { path = "../measure", version = "1.4.16" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.4.0" }
|
solana-sdk = { path = "../sdk", version = "1.4.16" }
|
||||||
solana-version = { path = "../version", version = "1.4.0" }
|
solana-version = { path = "../version", version = "1.4.16" }
|
||||||
rand = "0.7.0"
|
rand = "0.7.0"
|
||||||
clap = "2.33.1"
|
clap = "2.33.1"
|
||||||
crossbeam-channel = "0.4"
|
crossbeam-channel = "0.4"
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "solana-banking-bench"
|
name = "solana-banking-bench"
|
||||||
version = "1.4.0"
|
version = "1.4.16"
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
homepage = "https://solana.com/"
|
homepage = "https://solana.com/"
|
||||||
@@ -14,16 +14,16 @@ crossbeam-channel = "0.4"
|
|||||||
log = "0.4.6"
|
log = "0.4.6"
|
||||||
rand = "0.7.0"
|
rand = "0.7.0"
|
||||||
rayon = "1.4.0"
|
rayon = "1.4.0"
|
||||||
solana-core = { path = "../core", version = "1.4.0" }
|
solana-core = { path = "../core", version = "1.4.16" }
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "1.4.0" }
|
solana-clap-utils = { path = "../clap-utils", version = "1.4.16" }
|
||||||
solana-streamer = { path = "../streamer", version = "1.4.0" }
|
solana-streamer = { path = "../streamer", version = "1.4.16" }
|
||||||
solana-perf = { path = "../perf", version = "1.4.0" }
|
solana-perf = { path = "../perf", version = "1.4.16" }
|
||||||
solana-ledger = { path = "../ledger", version = "1.4.0" }
|
solana-ledger = { path = "../ledger", version = "1.4.16" }
|
||||||
solana-logger = { path = "../logger", version = "1.4.0" }
|
solana-logger = { path = "../logger", version = "1.4.16" }
|
||||||
solana-runtime = { path = "../runtime", version = "1.4.0" }
|
solana-runtime = { path = "../runtime", version = "1.4.16" }
|
||||||
solana-measure = { path = "../measure", version = "1.4.0" }
|
solana-measure = { path = "../measure", version = "1.4.16" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.4.0" }
|
solana-sdk = { path = "../sdk", version = "1.4.16" }
|
||||||
solana-version = { path = "../version", version = "1.4.0" }
|
solana-version = { path = "../version", version = "1.4.16" }
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
targets = ["x86_64-unknown-linux-gnu"]
|
targets = ["x86_64-unknown-linux-gnu"]
|
||||||
|
@@ -20,7 +20,6 @@ use solana_perf::packet::to_packets_chunked;
|
|||||||
use solana_runtime::{bank::Bank, bank_forks::BankForks};
|
use solana_runtime::{bank::Bank, bank_forks::BankForks};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
pubkey::Pubkey,
|
|
||||||
signature::Keypair,
|
signature::Keypair,
|
||||||
signature::Signature,
|
signature::Signature,
|
||||||
system_transaction,
|
system_transaction,
|
||||||
@@ -69,7 +68,7 @@ fn make_accounts_txs(
|
|||||||
hash: Hash,
|
hash: Hash,
|
||||||
same_payer: bool,
|
same_payer: bool,
|
||||||
) -> Vec<Transaction> {
|
) -> Vec<Transaction> {
|
||||||
let to_pubkey = Pubkey::new_rand();
|
let to_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let payer_key = Keypair::new();
|
let payer_key = Keypair::new();
|
||||||
let dummy = system_transaction::transfer(&payer_key, &to_pubkey, 1, hash);
|
let dummy = system_transaction::transfer(&payer_key, &to_pubkey, 1, hash);
|
||||||
(0..total_num_transactions)
|
(0..total_num_transactions)
|
||||||
@@ -78,9 +77,9 @@ fn make_accounts_txs(
|
|||||||
let mut new = dummy.clone();
|
let mut new = dummy.clone();
|
||||||
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
|
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
|
||||||
if !same_payer {
|
if !same_payer {
|
||||||
new.message.account_keys[0] = Pubkey::new_rand();
|
new.message.account_keys[0] = solana_sdk::pubkey::new_rand();
|
||||||
}
|
}
|
||||||
new.message.account_keys[1] = Pubkey::new_rand();
|
new.message.account_keys[1] = solana_sdk::pubkey::new_rand();
|
||||||
new.signatures = vec![Signature::new(&sig[0..64])];
|
new.signatures = vec![Signature::new(&sig[0..64])];
|
||||||
new
|
new
|
||||||
})
|
})
|
||||||
@@ -241,7 +240,7 @@ fn main() {
|
|||||||
let base_tx_count = bank.transaction_count();
|
let base_tx_count = bank.transaction_count();
|
||||||
let mut txs_processed = 0;
|
let mut txs_processed = 0;
|
||||||
let mut root = 1;
|
let mut root = 1;
|
||||||
let collector = Pubkey::new_rand();
|
let collector = solana_sdk::pubkey::new_rand();
|
||||||
let config = Config {
|
let config = Config {
|
||||||
packets_per_batch: packets_per_chunk,
|
packets_per_batch: packets_per_chunk,
|
||||||
chunk_len,
|
chunk_len,
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-banks-client"
|
name = "solana-banks-client"
|
||||||
version = "1.4.0"
|
version = "1.4.16"
|
||||||
description = "Solana banks client"
|
description = "Solana banks client"
|
||||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
@@ -12,15 +12,15 @@ edition = "2018"
|
|||||||
async-trait = "0.1.36"
|
async-trait = "0.1.36"
|
||||||
bincode = "1.3.1"
|
bincode = "1.3.1"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
solana-banks-interface = { path = "../banks-interface", version = "1.4.0" }
|
solana-banks-interface = { path = "../banks-interface", version = "1.4.16" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.4.0" }
|
solana-sdk = { path = "../sdk", version = "1.4.16" }
|
||||||
tarpc = { version = "0.22.0", features = ["full"] }
|
tarpc = { version = "0.23.0", features = ["full"] }
|
||||||
tokio = "0.2"
|
tokio = { version = "0.3", features = ["full"] }
|
||||||
tokio-serde = { version = "0.6", features = ["bincode"] }
|
tokio-serde = { version = "0.6", features = ["bincode"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
solana-runtime = { path = "../runtime", version = "1.4.0" }
|
solana-runtime = { path = "../runtime", version = "1.4.16" }
|
||||||
solana-banks-server = { path = "../banks-server", version = "1.4.0" }
|
solana-banks-server = { path = "../banks-server", version = "1.4.16" }
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["lib"]
|
crate-type = ["lib"]
|
||||||
|
@@ -10,9 +10,17 @@ use futures::future::join_all;
|
|||||||
pub use solana_banks_interface::{BanksClient, TransactionStatus};
|
pub use solana_banks_interface::{BanksClient, TransactionStatus};
|
||||||
use solana_banks_interface::{BanksRequest, BanksResponse};
|
use solana_banks_interface::{BanksRequest, BanksResponse};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::Account, clock::Slot, commitment_config::CommitmentLevel,
|
account::{from_account, Account},
|
||||||
fee_calculator::FeeCalculator, hash::Hash, pubkey::Pubkey, signature::Signature,
|
clock::Slot,
|
||||||
transaction::Transaction, transport,
|
commitment_config::CommitmentLevel,
|
||||||
|
fee_calculator::FeeCalculator,
|
||||||
|
hash::Hash,
|
||||||
|
pubkey::Pubkey,
|
||||||
|
rent::Rent,
|
||||||
|
signature::Signature,
|
||||||
|
sysvar,
|
||||||
|
transaction::Transaction,
|
||||||
|
transport,
|
||||||
};
|
};
|
||||||
use std::io::{self, Error, ErrorKind};
|
use std::io::{self, Error, ErrorKind};
|
||||||
use tarpc::{
|
use tarpc::{
|
||||||
@@ -40,6 +48,9 @@ pub trait BanksClientExt {
|
|||||||
/// use them to calculate the transaction fee.
|
/// use them to calculate the transaction fee.
|
||||||
async fn get_fees(&mut self) -> io::Result<(FeeCalculator, Hash, Slot)>;
|
async fn get_fees(&mut self) -> io::Result<(FeeCalculator, Hash, Slot)>;
|
||||||
|
|
||||||
|
/// Return the cluster rent
|
||||||
|
async fn get_rent(&mut self) -> io::Result<Rent>;
|
||||||
|
|
||||||
/// Send a transaction and return after the transaction has been rejected or
|
/// Send a transaction and return after the transaction has been rejected or
|
||||||
/// reached the given level of commitment.
|
/// reached the given level of commitment.
|
||||||
async fn process_transaction_with_commitment(
|
async fn process_transaction_with_commitment(
|
||||||
@@ -108,6 +119,17 @@ impl BanksClientExt for BanksClient {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_rent(&mut self) -> io::Result<Rent> {
|
||||||
|
let rent_sysvar = self
|
||||||
|
.get_account(sysvar::rent::id())
|
||||||
|
.await?
|
||||||
|
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Rent sysvar not present"))?;
|
||||||
|
|
||||||
|
from_account::<Rent>(&rent_sysvar).ok_or_else(|| {
|
||||||
|
io::Error::new(io::ErrorKind::Other, "Failed to deserialize Rent sysvar")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_recent_blockhash(&mut self) -> io::Result<Hash> {
|
async fn get_recent_blockhash(&mut self) -> io::Result<Hash> {
|
||||||
Ok(self.get_fees().await?.1)
|
Ok(self.get_fees().await?.1)
|
||||||
}
|
}
|
||||||
@@ -213,10 +235,10 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use solana_banks_server::banks_server::start_local_server;
|
use solana_banks_server::banks_server::start_local_server;
|
||||||
use solana_runtime::{bank::Bank, bank_forks::BankForks, genesis_utils::create_genesis_config};
|
use solana_runtime::{bank::Bank, bank_forks::BankForks, genesis_utils::create_genesis_config};
|
||||||
use solana_sdk::{message::Message, pubkey::Pubkey, signature::Signer, system_instruction};
|
use solana_sdk::{message::Message, signature::Signer, system_instruction};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use tarpc::transport;
|
use tarpc::transport;
|
||||||
use tokio::{runtime::Runtime, time::delay_for};
|
use tokio::{runtime::Runtime, time::sleep};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_banks_client_new() {
|
fn test_banks_client_new() {
|
||||||
@@ -235,7 +257,7 @@ mod tests {
|
|||||||
&genesis.genesis_config,
|
&genesis.genesis_config,
|
||||||
))));
|
))));
|
||||||
|
|
||||||
let bob_pubkey = Pubkey::new_rand();
|
let bob_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let mint_pubkey = genesis.mint_keypair.pubkey();
|
let mint_pubkey = genesis.mint_keypair.pubkey();
|
||||||
let instruction = system_instruction::transfer(&mint_pubkey, &bob_pubkey, 1);
|
let instruction = system_instruction::transfer(&mint_pubkey, &bob_pubkey, 1);
|
||||||
let message = Message::new(&[instruction], Some(&mint_pubkey));
|
let message = Message::new(&[instruction], Some(&mint_pubkey));
|
||||||
@@ -265,7 +287,7 @@ mod tests {
|
|||||||
))));
|
))));
|
||||||
|
|
||||||
let mint_pubkey = &genesis.mint_keypair.pubkey();
|
let mint_pubkey = &genesis.mint_keypair.pubkey();
|
||||||
let bob_pubkey = Pubkey::new_rand();
|
let bob_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let instruction = system_instruction::transfer(&mint_pubkey, &bob_pubkey, 1);
|
let instruction = system_instruction::transfer(&mint_pubkey, &bob_pubkey, 1);
|
||||||
let message = Message::new(&[instruction], Some(&mint_pubkey));
|
let message = Message::new(&[instruction], Some(&mint_pubkey));
|
||||||
|
|
||||||
@@ -285,7 +307,7 @@ mod tests {
|
|||||||
if root_slot > last_valid_slot {
|
if root_slot > last_valid_slot {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
delay_for(Duration::from_millis(100)).await;
|
sleep(Duration::from_millis(100)).await;
|
||||||
status = banks_client.get_transaction_status(signature).await?;
|
status = banks_client.get_transaction_status(signature).await?;
|
||||||
}
|
}
|
||||||
assert!(status.unwrap().err.is_none());
|
assert!(status.unwrap().err.is_none());
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-banks-interface"
|
name = "solana-banks-interface"
|
||||||
version = "1.4.0"
|
version = "1.4.16"
|
||||||
description = "Solana banks RPC interface"
|
description = "Solana banks RPC interface"
|
||||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
@@ -10,8 +10,8 @@ edition = "2018"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version = "1.0.112", features = ["derive"] }
|
serde = { version = "1.0.112", features = ["derive"] }
|
||||||
solana-sdk = { path = "../sdk", version = "1.4.0" }
|
solana-sdk = { path = "../sdk", version = "1.4.16" }
|
||||||
tarpc = { version = "0.22.0", features = ["full"] }
|
tarpc = { version = "0.23.0", features = ["full"] }
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["lib"]
|
crate-type = ["lib"]
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-banks-server"
|
name = "solana-banks-server"
|
||||||
version = "1.4.0"
|
version = "1.4.16"
|
||||||
description = "Solana banks server"
|
description = "Solana banks server"
|
||||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
@@ -12,12 +12,12 @@ edition = "2018"
|
|||||||
bincode = "1.3.1"
|
bincode = "1.3.1"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
solana-banks-interface = { path = "../banks-interface", version = "1.4.0" }
|
solana-banks-interface = { path = "../banks-interface", version = "1.4.16" }
|
||||||
solana-runtime = { path = "../runtime", version = "1.4.0" }
|
solana-runtime = { path = "../runtime", version = "1.4.16" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.4.0" }
|
solana-sdk = { path = "../sdk", version = "1.4.16" }
|
||||||
solana-metrics = { path = "../metrics", version = "1.4.0" }
|
solana-metrics = { path = "../metrics", version = "1.4.16" }
|
||||||
tarpc = { version = "0.22.0", features = ["full"] }
|
tarpc = { version = "0.23.0", features = ["full"] }
|
||||||
tokio = "0.2"
|
tokio = { version = "0.3", features = ["full"] }
|
||||||
tokio-serde = { version = "0.6", features = ["bincode"] }
|
tokio-serde = { version = "0.6", features = ["bincode"] }
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
@@ -5,11 +5,7 @@ use futures::{
|
|||||||
prelude::stream::{self, StreamExt},
|
prelude::stream::{self, StreamExt},
|
||||||
};
|
};
|
||||||
use solana_banks_interface::{Banks, BanksRequest, BanksResponse, TransactionStatus};
|
use solana_banks_interface::{Banks, BanksRequest, BanksResponse, TransactionStatus};
|
||||||
use solana_runtime::{
|
use solana_runtime::{bank::Bank, bank_forks::BankForks, commitment::BlockCommitmentCache};
|
||||||
bank::Bank,
|
|
||||||
bank_forks::BankForks,
|
|
||||||
commitment::{BlockCommitmentCache, CommitmentSlots},
|
|
||||||
};
|
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::Account,
|
account::Account,
|
||||||
clock::Slot,
|
clock::Slot,
|
||||||
@@ -21,7 +17,6 @@ use solana_sdk::{
|
|||||||
transaction::{self, Transaction},
|
transaction::{self, Transaction},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
|
||||||
io,
|
io,
|
||||||
net::{Ipv4Addr, SocketAddr},
|
net::{Ipv4Addr, SocketAddr},
|
||||||
sync::{
|
sync::{
|
||||||
@@ -38,7 +33,7 @@ use tarpc::{
|
|||||||
server::{self, Channel, Handler},
|
server::{self, Channel, Handler},
|
||||||
transport,
|
transport,
|
||||||
};
|
};
|
||||||
use tokio::time::delay_for;
|
use tokio::time::sleep;
|
||||||
use tokio_serde::formats::Bincode;
|
use tokio_serde::formats::Bincode;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -84,11 +79,9 @@ impl BanksServer {
|
|||||||
let (transaction_sender, transaction_receiver) = channel();
|
let (transaction_sender, transaction_receiver) = channel();
|
||||||
let bank = bank_forks.read().unwrap().working_bank();
|
let bank = bank_forks.read().unwrap().working_bank();
|
||||||
let slot = bank.slot();
|
let slot = bank.slot();
|
||||||
let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::new(
|
let block_commitment_cache = Arc::new(RwLock::new(
|
||||||
HashMap::default(),
|
BlockCommitmentCache::new_for_tests_with_slots(slot, slot),
|
||||||
0,
|
));
|
||||||
CommitmentSlots::new_from_slot(slot),
|
|
||||||
)));
|
|
||||||
Builder::new()
|
Builder::new()
|
||||||
.name("solana-bank-forks-client".to_string())
|
.name("solana-bank-forks-client".to_string())
|
||||||
.spawn(move || Self::run(&bank, transaction_receiver))
|
.spawn(move || Self::run(&bank, transaction_receiver))
|
||||||
@@ -109,23 +102,36 @@ impl BanksServer {
|
|||||||
|
|
||||||
async fn poll_signature_status(
|
async fn poll_signature_status(
|
||||||
self,
|
self,
|
||||||
signature: Signature,
|
signature: &Signature,
|
||||||
|
blockhash: &Hash,
|
||||||
last_valid_slot: Slot,
|
last_valid_slot: Slot,
|
||||||
commitment: CommitmentLevel,
|
commitment: CommitmentLevel,
|
||||||
) -> Option<transaction::Result<()>> {
|
) -> Option<transaction::Result<()>> {
|
||||||
let mut status = self.bank(commitment).get_signature_status(&signature);
|
let mut status = self
|
||||||
|
.bank(commitment)
|
||||||
|
.get_signature_status_with_blockhash(signature, blockhash);
|
||||||
while status.is_none() {
|
while status.is_none() {
|
||||||
delay_for(Duration::from_millis(200)).await;
|
sleep(Duration::from_millis(200)).await;
|
||||||
let bank = self.bank(commitment);
|
let bank = self.bank(commitment);
|
||||||
if bank.slot() > last_valid_slot {
|
if bank.slot() > last_valid_slot {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
status = bank.get_signature_status(&signature);
|
status = bank.get_signature_status_with_blockhash(signature, blockhash);
|
||||||
}
|
}
|
||||||
status
|
status
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn verify_transaction(transaction: &Transaction) -> transaction::Result<()> {
|
||||||
|
if let Err(err) = transaction.verify() {
|
||||||
|
Err(err)
|
||||||
|
} else if let Err(err) = transaction.verify_precompiles() {
|
||||||
|
Err(err)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tarpc::server]
|
#[tarpc::server]
|
||||||
impl Banks for BanksServer {
|
impl Banks for BanksServer {
|
||||||
async fn send_transaction_with_context(self, _: Context, transaction: Transaction) {
|
async fn send_transaction_with_context(self, _: Context, transaction: Transaction) {
|
||||||
@@ -187,19 +193,23 @@ impl Banks for BanksServer {
|
|||||||
transaction: Transaction,
|
transaction: Transaction,
|
||||||
commitment: CommitmentLevel,
|
commitment: CommitmentLevel,
|
||||||
) -> Option<transaction::Result<()>> {
|
) -> Option<transaction::Result<()>> {
|
||||||
|
if let Err(err) = verify_transaction(&transaction) {
|
||||||
|
return Some(Err(err));
|
||||||
|
}
|
||||||
|
|
||||||
let blockhash = &transaction.message.recent_blockhash;
|
let blockhash = &transaction.message.recent_blockhash;
|
||||||
let last_valid_slot = self
|
let last_valid_slot = self
|
||||||
.bank_forks
|
.bank_forks
|
||||||
.read()
|
.read()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.root_bank()
|
.root_bank()
|
||||||
.get_blockhash_last_valid_slot(&blockhash)
|
.get_blockhash_last_valid_slot(blockhash)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let signature = transaction.signatures.get(0).cloned().unwrap_or_default();
|
let signature = transaction.signatures.get(0).cloned().unwrap_or_default();
|
||||||
let info =
|
let info =
|
||||||
TransactionInfo::new(signature, serialize(&transaction).unwrap(), last_valid_slot);
|
TransactionInfo::new(signature, serialize(&transaction).unwrap(), last_valid_slot);
|
||||||
self.transaction_sender.send(info).unwrap();
|
self.transaction_sender.send(info).unwrap();
|
||||||
self.poll_signature_status(signature, last_valid_slot, commitment)
|
self.poll_signature_status(&signature, blockhash, last_valid_slot, commitment)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "solana-bench-exchange"
|
name = "solana-bench-exchange"
|
||||||
version = "1.4.0"
|
version = "1.4.16"
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
homepage = "https://solana.com/"
|
homepage = "https://solana.com/"
|
||||||
@@ -18,21 +18,21 @@ rand = "0.7.0"
|
|||||||
rayon = "1.4.0"
|
rayon = "1.4.0"
|
||||||
serde_json = "1.0.56"
|
serde_json = "1.0.56"
|
||||||
serde_yaml = "0.8.13"
|
serde_yaml = "0.8.13"
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "1.4.0" }
|
solana-clap-utils = { path = "../clap-utils", version = "1.4.16" }
|
||||||
solana-core = { path = "../core", version = "1.4.0" }
|
solana-core = { path = "../core", version = "1.4.16" }
|
||||||
solana-genesis = { path = "../genesis", version = "1.4.0" }
|
solana-genesis = { path = "../genesis", version = "1.4.16" }
|
||||||
solana-client = { path = "../client", version = "1.4.0" }
|
solana-client = { path = "../client", version = "1.4.16" }
|
||||||
solana-faucet = { path = "../faucet", version = "1.4.0" }
|
solana-faucet = { path = "../faucet", version = "1.4.16" }
|
||||||
solana-exchange-program = { path = "../programs/exchange", version = "1.4.0" }
|
solana-exchange-program = { path = "../programs/exchange", version = "1.4.16" }
|
||||||
solana-logger = { path = "../logger", version = "1.4.0" }
|
solana-logger = { path = "../logger", version = "1.4.16" }
|
||||||
solana-metrics = { path = "../metrics", version = "1.4.0" }
|
solana-metrics = { path = "../metrics", version = "1.4.16" }
|
||||||
solana-net-utils = { path = "../net-utils", version = "1.4.0" }
|
solana-net-utils = { path = "../net-utils", version = "1.4.16" }
|
||||||
solana-runtime = { path = "../runtime", version = "1.4.0" }
|
solana-runtime = { path = "../runtime", version = "1.4.16" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.4.0" }
|
solana-sdk = { path = "../sdk", version = "1.4.16" }
|
||||||
solana-version = { path = "../version", version = "1.4.0" }
|
solana-version = { path = "../version", version = "1.4.16" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
solana-local-cluster = { path = "../local-cluster", version = "1.4.0" }
|
solana-local-cluster = { path = "../local-cluster", version = "1.4.16" }
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
targets = ["x86_64-unknown-linux-gnu"]
|
targets = ["x86_64-unknown-linux-gnu"]
|
||||||
|
@@ -39,7 +39,7 @@ fn test_exchange_local_cluster() {
|
|||||||
} = config;
|
} = config;
|
||||||
let accounts_in_groups = batch_size * account_groups;
|
let accounts_in_groups = batch_size * account_groups;
|
||||||
|
|
||||||
let cluster = LocalCluster::new(&ClusterConfig {
|
let cluster = LocalCluster::new(&mut ClusterConfig {
|
||||||
node_stakes: vec![100_000; NUM_NODES],
|
node_stakes: vec![100_000; NUM_NODES],
|
||||||
cluster_lamports: 100_000_000_000_000,
|
cluster_lamports: 100_000_000_000_000,
|
||||||
validator_configs: vec![ValidatorConfig::default(); NUM_NODES],
|
validator_configs: vec![ValidatorConfig::default(); NUM_NODES],
|
||||||
@@ -86,7 +86,7 @@ fn test_exchange_bank_client() {
|
|||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
let (genesis_config, identity) = create_genesis_config(100_000_000_000_000);
|
let (genesis_config, identity) = create_genesis_config(100_000_000_000_000);
|
||||||
let mut bank = Bank::new(&genesis_config);
|
let mut bank = Bank::new(&genesis_config);
|
||||||
bank.add_builtin_program("exchange_program", id(), process_instruction);
|
bank.add_builtin("exchange_program", id(), process_instruction);
|
||||||
let clients = vec![BankClient::new(bank)];
|
let clients = vec![BankClient::new(bank)];
|
||||||
|
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "solana-bench-streamer"
|
name = "solana-bench-streamer"
|
||||||
version = "1.4.0"
|
version = "1.4.16"
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
homepage = "https://solana.com/"
|
homepage = "https://solana.com/"
|
||||||
@@ -10,11 +10,11 @@ publish = false
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33.1"
|
clap = "2.33.1"
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "1.4.0" }
|
solana-clap-utils = { path = "../clap-utils", version = "1.4.16" }
|
||||||
solana-streamer = { path = "../streamer", version = "1.4.0" }
|
solana-streamer = { path = "../streamer", version = "1.4.16" }
|
||||||
solana-logger = { path = "../logger", version = "1.4.0" }
|
solana-logger = { path = "../logger", version = "1.4.16" }
|
||||||
solana-net-utils = { path = "../net-utils", version = "1.4.0" }
|
solana-net-utils = { path = "../net-utils", version = "1.4.16" }
|
||||||
solana-version = { path = "../version", version = "1.4.0" }
|
solana-version = { path = "../version", version = "1.4.16" }
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
targets = ["x86_64-unknown-linux-gnu"]
|
targets = ["x86_64-unknown-linux-gnu"]
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "solana-bench-tps"
|
name = "solana-bench-tps"
|
||||||
version = "1.4.0"
|
version = "1.4.16"
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
homepage = "https://solana.com/"
|
homepage = "https://solana.com/"
|
||||||
@@ -15,23 +15,23 @@ log = "0.4.8"
|
|||||||
rayon = "1.4.0"
|
rayon = "1.4.0"
|
||||||
serde_json = "1.0.56"
|
serde_json = "1.0.56"
|
||||||
serde_yaml = "0.8.13"
|
serde_yaml = "0.8.13"
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "1.4.0" }
|
solana-clap-utils = { path = "../clap-utils", version = "1.4.16" }
|
||||||
solana-core = { path = "../core", version = "1.4.0" }
|
solana-core = { path = "../core", version = "1.4.16" }
|
||||||
solana-genesis = { path = "../genesis", version = "1.4.0" }
|
solana-genesis = { path = "../genesis", version = "1.4.16" }
|
||||||
solana-client = { path = "../client", version = "1.4.0" }
|
solana-client = { path = "../client", version = "1.4.16" }
|
||||||
solana-faucet = { path = "../faucet", version = "1.4.0" }
|
solana-faucet = { path = "../faucet", version = "1.4.16" }
|
||||||
solana-logger = { path = "../logger", version = "1.4.0" }
|
solana-logger = { path = "../logger", version = "1.4.16" }
|
||||||
solana-metrics = { path = "../metrics", version = "1.4.0" }
|
solana-metrics = { path = "../metrics", version = "1.4.16" }
|
||||||
solana-measure = { path = "../measure", version = "1.4.0" }
|
solana-measure = { path = "../measure", version = "1.4.16" }
|
||||||
solana-net-utils = { path = "../net-utils", version = "1.4.0" }
|
solana-net-utils = { path = "../net-utils", version = "1.4.16" }
|
||||||
solana-runtime = { path = "../runtime", version = "1.4.0" }
|
solana-runtime = { path = "../runtime", version = "1.4.16" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.4.0" }
|
solana-sdk = { path = "../sdk", version = "1.4.16" }
|
||||||
solana-version = { path = "../version", version = "1.4.0" }
|
solana-version = { path = "../version", version = "1.4.16" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serial_test = "0.4.0"
|
serial_test = "0.4.0"
|
||||||
serial_test_derive = "0.4.0"
|
serial_test_derive = "0.4.0"
|
||||||
solana-local-cluster = { path = "../local-cluster", version = "1.4.0" }
|
solana-local-cluster = { path = "../local-cluster", version = "1.4.16" }
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
targets = ["x86_64-unknown-linux-gnu"]
|
targets = ["x86_64-unknown-linux-gnu"]
|
||||||
|
@@ -15,7 +15,7 @@ fn test_bench_tps_local_cluster(config: Config) {
|
|||||||
|
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
const NUM_NODES: usize = 1;
|
const NUM_NODES: usize = 1;
|
||||||
let cluster = LocalCluster::new(&ClusterConfig {
|
let cluster = LocalCluster::new(&mut ClusterConfig {
|
||||||
node_stakes: vec![999_990; NUM_NODES],
|
node_stakes: vec![999_990; NUM_NODES],
|
||||||
cluster_lamports: 200_000_000,
|
cluster_lamports: 200_000_000,
|
||||||
validator_configs: vec![ValidatorConfig::default(); NUM_NODES],
|
validator_configs: vec![ValidatorConfig::default(); NUM_NODES],
|
||||||
|
31
cargo
Executable file
31
cargo
Executable file
@@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# shellcheck source=ci/rust-version.sh
|
||||||
|
here=$(dirname "$0")
|
||||||
|
|
||||||
|
source "${here}"/ci/rust-version.sh all
|
||||||
|
|
||||||
|
toolchain=
|
||||||
|
case "$1" in
|
||||||
|
stable)
|
||||||
|
# shellcheck disable=SC2054 # rust_stable is sourced from rust-version.sh
|
||||||
|
toolchain="$rust_stable"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
nightly)
|
||||||
|
# shellcheck disable=SC2054 # rust_nightly is sourced from rust-version.sh
|
||||||
|
toolchain="$rust_nightly"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
+*)
|
||||||
|
toolchain="${1#+}"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# shellcheck disable=SC2054 # rust_stable is sourced from rust-version.sh
|
||||||
|
toolchain="$rust_stable"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
set -x
|
||||||
|
exec cargo "+${toolchain}" "${@}"
|
13
cargo-build-bpf
Executable file
13
cargo-build-bpf
Executable file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
here=$(dirname "$0")
|
||||||
|
|
||||||
|
maybe_bpf_sdk="--bpf-sdk $here/sdk/bpf"
|
||||||
|
for a in "$@"; do
|
||||||
|
if [[ $a = --bpf-sdk ]]; then
|
||||||
|
maybe_bpf_sdk=
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
set -x
|
||||||
|
exec "$here"/cargo run --manifest-path "$here"/sdk/cargo-build-bpf/Cargo.toml -- $maybe_bpf_sdk "$@"
|
14
cargo-test-bpf
Executable file
14
cargo-test-bpf
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
here=$(dirname "$0")
|
||||||
|
|
||||||
|
maybe_bpf_sdk="--bpf-sdk $here/sdk/bpf"
|
||||||
|
for a in "$@"; do
|
||||||
|
if [[ $a = --bpf-sdk ]]; then
|
||||||
|
maybe_bpf_sdk=
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
export CARGO_BUILD_BPF="$here"/cargo-build-bpf
|
||||||
|
set -x
|
||||||
|
exec "$here"/cargo run --manifest-path "$here"/sdk/cargo-test-bpf/Cargo.toml -- $maybe_bpf_sdk "$@"
|
@@ -175,6 +175,30 @@ EOF
|
|||||||
"Stable-perf skipped as no relevant files were modified"
|
"Stable-perf skipped as no relevant files were modified"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Downstream backwards compatibility
|
||||||
|
if affects \
|
||||||
|
.rs$ \
|
||||||
|
Cargo.lock$ \
|
||||||
|
Cargo.toml$ \
|
||||||
|
^ci/rust-version.sh \
|
||||||
|
^ci/test-stable-perf.sh \
|
||||||
|
^ci/test-stable.sh \
|
||||||
|
^ci/test-local-cluster.sh \
|
||||||
|
^core/build.rs \
|
||||||
|
^fetch-perf-libs.sh \
|
||||||
|
^programs/ \
|
||||||
|
^sdk/ \
|
||||||
|
^scripts/build-downstream-projects.sh \
|
||||||
|
; then
|
||||||
|
cat >> "$output_file" <<"EOF"
|
||||||
|
- command: "scripts/build-downstream-projects.sh"
|
||||||
|
name: "downstream-projects"
|
||||||
|
timeout_in_minutes: 30
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
annotate --style info \
|
||||||
|
"downstream-projects skipped as no relevant files were modified"
|
||||||
|
fi
|
||||||
# Benches...
|
# Benches...
|
||||||
if affects \
|
if affects \
|
||||||
.rs$ \
|
.rs$ \
|
||||||
|
@@ -26,8 +26,9 @@ declare print_free_tree=(
|
|||||||
':runtime/src/**.rs'
|
':runtime/src/**.rs'
|
||||||
':sdk/bpf/rust/rust-utils/**.rs'
|
':sdk/bpf/rust/rust-utils/**.rs'
|
||||||
':sdk/**.rs'
|
':sdk/**.rs'
|
||||||
':^sdk/src/program_option.rs'
|
':^sdk/cargo-build-bpf/**.rs'
|
||||||
':^sdk/src/program_stubs.rs'
|
':^sdk/program/src/program_option.rs'
|
||||||
|
':^sdk/program/src/program_stubs.rs'
|
||||||
':programs/**.rs'
|
':programs/**.rs'
|
||||||
':^**bin**.rs'
|
':^**bin**.rs'
|
||||||
':^**bench**.rs'
|
':^**bench**.rs'
|
||||||
|
@@ -42,10 +42,10 @@ def get_packages():
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Order dependencies
|
# Order dependencies
|
||||||
deleted_dependencies = []
|
|
||||||
sorted_dependency_graph = []
|
sorted_dependency_graph = []
|
||||||
max_iterations = pow(len(dependency_graph),2)
|
max_iterations = pow(len(dependency_graph),2)
|
||||||
while len(deleted_dependencies) < len(dependency_graph):
|
while dependency_graph:
|
||||||
|
deleted_packages = []
|
||||||
if max_iterations == 0:
|
if max_iterations == 0:
|
||||||
# One day be more helpful and find the actual cycle for the user...
|
# One day be more helpful and find the actual cycle for the user...
|
||||||
sys.exit('Error: Circular dependency suspected between these packages: \n {}\n'.format('\n '.join(dependency_graph.keys())))
|
sys.exit('Error: Circular dependency suspected between these packages: \n {}\n'.format('\n '.join(dependency_graph.keys())))
|
||||||
@@ -53,13 +53,17 @@ def get_packages():
|
|||||||
max_iterations -= 1
|
max_iterations -= 1
|
||||||
|
|
||||||
for package, dependencies in dependency_graph.items():
|
for package, dependencies in dependency_graph.items():
|
||||||
|
if package in deleted_packages:
|
||||||
|
continue
|
||||||
for dependency in dependencies:
|
for dependency in dependencies:
|
||||||
if dependency in dependency_graph:
|
if dependency in dependency_graph:
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
deleted_dependencies.append(package)
|
deleted_packages.append(package)
|
||||||
sorted_dependency_graph.append((package, manifest_path[package]))
|
sorted_dependency_graph.append((package, manifest_path[package]))
|
||||||
|
|
||||||
|
dependency_graph = {p: d for p, d in dependency_graph.items() if not p in deleted_packages }
|
||||||
|
|
||||||
|
|
||||||
return sorted_dependency_graph
|
return sorted_dependency_graph
|
||||||
|
|
||||||
|
@@ -4,6 +4,8 @@ cd "$(dirname "$0")/.."
|
|||||||
source ci/semver_bash/semver.sh
|
source ci/semver_bash/semver.sh
|
||||||
source ci/rust-version.sh stable
|
source ci/rust-version.sh stable
|
||||||
|
|
||||||
|
cargo="$(readlink -f ./cargo)"
|
||||||
|
|
||||||
# shellcheck disable=SC2086
|
# shellcheck disable=SC2086
|
||||||
is_crate_version_uploaded() {
|
is_crate_version_uploaded() {
|
||||||
name=$1
|
name=$1
|
||||||
@@ -66,11 +68,11 @@ for Cargo_toml in $Cargo_tomls; do
|
|||||||
(
|
(
|
||||||
set -x
|
set -x
|
||||||
rm -rf crate-test
|
rm -rf crate-test
|
||||||
cargo +"$rust_stable" init crate-test
|
"$cargo" stable init crate-test
|
||||||
cd crate-test/
|
cd crate-test/
|
||||||
echo "${crate_name} = \"${expectedCrateVersion}\"" >> Cargo.toml
|
echo "${crate_name} = \"${expectedCrateVersion}\"" >> Cargo.toml
|
||||||
echo "[workspace]" >> Cargo.toml
|
echo "[workspace]" >> Cargo.toml
|
||||||
cargo +"$rust_stable" check
|
"$cargo" stable check
|
||||||
) && really_uploaded=1
|
) && really_uploaded=1
|
||||||
if ((really_uploaded)); then
|
if ((really_uploaded)); then
|
||||||
break;
|
break;
|
||||||
|
@@ -91,17 +91,15 @@ echo --- Creating release tarball
|
|||||||
cp "${RELEASE_BASENAME}"/version.yml "${TARBALL_BASENAME}"-$TARGET.yml
|
cp "${RELEASE_BASENAME}"/version.yml "${TARBALL_BASENAME}"-$TARGET.yml
|
||||||
)
|
)
|
||||||
|
|
||||||
# Metrics tarball is platform agnostic, only publish it from Linux
|
# Maybe tarballs are platform agnostic, only publish them from the Linux build
|
||||||
MAYBE_TARBALLS=
|
MAYBE_TARBALLS=
|
||||||
if [[ "$CI_OS_NAME" = linux ]]; then
|
if [[ "$CI_OS_NAME" = linux ]]; then
|
||||||
metrics/create-metrics-tarball.sh
|
|
||||||
(
|
(
|
||||||
set -x
|
set -x
|
||||||
sdk/bpf/scripts/package.sh
|
sdk/bpf/scripts/package.sh
|
||||||
[[ -f bpf-sdk.tar.bz2 ]]
|
[[ -f bpf-sdk.tar.bz2 ]]
|
||||||
|
|
||||||
)
|
)
|
||||||
MAYBE_TARBALLS="bpf-sdk.tar.bz2 solana-metrics.tar.bz2"
|
MAYBE_TARBALLS="bpf-sdk.tar.bz2"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
source ci/upload-ci-artifact.sh
|
source ci/upload-ci-artifact.sh
|
||||||
@@ -126,7 +124,7 @@ for file in "${TARBALL_BASENAME}"-$TARGET.tar.bz2 "${TARBALL_BASENAME}"-$TARGET.
|
|||||||
/usr/bin/s3cmd --acl-public put /solana/"$file" s3://release.solana.com/"$CHANNEL_OR_TAG"/"$file"
|
/usr/bin/s3cmd --acl-public put /solana/"$file" s3://release.solana.com/"$CHANNEL_OR_TAG"/"$file"
|
||||||
|
|
||||||
echo Published to:
|
echo Published to:
|
||||||
$DRYRUN ci/format-url.sh http://release.solana.com/"$CHANNEL_OR_TAG"/"$file"
|
$DRYRUN ci/format-url.sh https://release.solana.com/"$CHANNEL_OR_TAG"/"$file"
|
||||||
)
|
)
|
||||||
|
|
||||||
if [[ -n $TAG ]]; then
|
if [[ -n $TAG ]]; then
|
||||||
@@ -149,4 +147,30 @@ for file in "${TARBALL_BASENAME}"-$TARGET.tar.bz2 "${TARBALL_BASENAME}"-$TARGET.
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
||||||
|
# Create install wrapper for release.solana.com
|
||||||
|
if [[ -n $BUILDKITE ]]; then
|
||||||
|
cat > release.solana.com-install <<EOF
|
||||||
|
SOLANA_RELEASE=$CHANNEL_OR_TAG
|
||||||
|
SOLANA_INSTALL_INIT_ARGS=$CHANNEL_OR_TAG
|
||||||
|
SOLANA_DOWNLOAD_ROOT=http://release.solana.com
|
||||||
|
EOF
|
||||||
|
cat install/solana-install-init.sh >> release.solana.com-install
|
||||||
|
|
||||||
|
echo --- AWS S3 Store: "install"
|
||||||
|
(
|
||||||
|
set -x
|
||||||
|
$DRYRUN docker run \
|
||||||
|
--rm \
|
||||||
|
--env AWS_ACCESS_KEY_ID \
|
||||||
|
--env AWS_SECRET_ACCESS_KEY \
|
||||||
|
--volume "$PWD:/solana" \
|
||||||
|
eremite/aws-cli:2018.12.18 \
|
||||||
|
/usr/bin/s3cmd --acl-public put /solana/release.solana.com-install s3://release.solana.com/"$CHANNEL_OR_TAG"/install
|
||||||
|
|
||||||
|
echo Published to:
|
||||||
|
$DRYRUN ci/format-url.sh https://release.solana.com/"$CHANNEL_OR_TAG"/install
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
echo --- ok
|
echo --- ok
|
||||||
|
@@ -6,7 +6,8 @@ source ci/_
|
|||||||
source ci/upload-ci-artifact.sh
|
source ci/upload-ci-artifact.sh
|
||||||
|
|
||||||
eval "$(ci/channel-info.sh)"
|
eval "$(ci/channel-info.sh)"
|
||||||
source ci/rust-version.sh all
|
|
||||||
|
cargo="$(readlink -f "./cargo")"
|
||||||
|
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
export RUST_BACKTRACE=1
|
export RUST_BACKTRACE=1
|
||||||
@@ -27,35 +28,35 @@ test -d target/debug/bpf && find target/debug/bpf -name '*.d' -delete
|
|||||||
test -d target/release/bpf && find target/release/bpf -name '*.d' -delete
|
test -d target/release/bpf && find target/release/bpf -name '*.d' -delete
|
||||||
|
|
||||||
# Ensure all dependencies are built
|
# Ensure all dependencies are built
|
||||||
_ cargo +$rust_nightly build --release
|
_ "$cargo" nightly build --release
|
||||||
|
|
||||||
# Remove "BENCH_FILE", if it exists so that the following commands can append
|
# Remove "BENCH_FILE", if it exists so that the following commands can append
|
||||||
rm -f "$BENCH_FILE"
|
rm -f "$BENCH_FILE"
|
||||||
|
|
||||||
# Run sdk benches
|
# Run sdk benches
|
||||||
_ cargo +$rust_nightly bench --manifest-path sdk/Cargo.toml ${V:+--verbose} \
|
_ "$cargo" nightly bench --manifest-path sdk/Cargo.toml ${V:+--verbose} \
|
||||||
-- -Z unstable-options --format=json | tee -a "$BENCH_FILE"
|
-- -Z unstable-options --format=json | tee -a "$BENCH_FILE"
|
||||||
|
|
||||||
# Run runtime benches
|
# Run runtime benches
|
||||||
_ cargo +$rust_nightly bench --manifest-path runtime/Cargo.toml ${V:+--verbose} \
|
_ "$cargo" nightly bench --manifest-path runtime/Cargo.toml ${V:+--verbose} \
|
||||||
-- -Z unstable-options --format=json | tee -a "$BENCH_FILE"
|
-- -Z unstable-options --format=json | tee -a "$BENCH_FILE"
|
||||||
|
|
||||||
# Run core benches
|
# Run core benches
|
||||||
_ cargo +$rust_nightly bench --manifest-path core/Cargo.toml ${V:+--verbose} \
|
_ "$cargo" nightly bench --manifest-path core/Cargo.toml ${V:+--verbose} \
|
||||||
-- -Z unstable-options --format=json | tee -a "$BENCH_FILE"
|
-- -Z unstable-options --format=json | tee -a "$BENCH_FILE"
|
||||||
|
|
||||||
# Run bpf benches
|
# Run bpf benches
|
||||||
_ cargo +$rust_nightly bench --manifest-path programs/bpf/Cargo.toml ${V:+--verbose} --features=bpf_c \
|
_ "$cargo" nightly bench --manifest-path programs/bpf/Cargo.toml ${V:+--verbose} --features=bpf_c \
|
||||||
-- -Z unstable-options --format=json --nocapture | tee -a "$BENCH_FILE"
|
-- -Z unstable-options --format=json --nocapture | tee -a "$BENCH_FILE"
|
||||||
|
|
||||||
# Run banking/accounts bench. Doesn't require nightly, but use since it is already built.
|
# Run banking/accounts bench. Doesn't require nightly, but use since it is already built.
|
||||||
_ cargo +$rust_nightly run --release --manifest-path banking-bench/Cargo.toml ${V:+--verbose} | tee -a "$BENCH_FILE"
|
_ "$cargo" nightly run --release --manifest-path banking-bench/Cargo.toml ${V:+--verbose} | tee -a "$BENCH_FILE"
|
||||||
_ cargo +$rust_nightly run --release --manifest-path accounts-bench/Cargo.toml ${V:+--verbose} -- --num_accounts 10000 --num_slots 4 | tee -a "$BENCH_FILE"
|
_ "$cargo" nightly run --release --manifest-path accounts-bench/Cargo.toml ${V:+--verbose} -- --num_accounts 10000 --num_slots 4 | tee -a "$BENCH_FILE"
|
||||||
|
|
||||||
# `solana-upload-perf` disabled as it can take over 30 minutes to complete for some
|
# `solana-upload-perf` disabled as it can take over 30 minutes to complete for some
|
||||||
# reason
|
# reason
|
||||||
exit 0
|
exit 0
|
||||||
_ cargo +$rust_nightly run --release --package solana-upload-perf \
|
_ "$cargo" nightly run --release --package solana-upload-perf \
|
||||||
-- "$BENCH_FILE" "$TARGET_BRANCH" "$UPLOAD_METRICS" | tee "$BENCH_ARTIFACT"
|
-- "$BENCH_FILE" "$TARGET_BRANCH" "$UPLOAD_METRICS" | tee "$BENCH_ARTIFACT"
|
||||||
|
|
||||||
upload-ci-artifact "$BENCH_FILE"
|
upload-ci-artifact "$BENCH_FILE"
|
||||||
|
@@ -8,6 +8,9 @@ source ci/_
|
|||||||
source ci/rust-version.sh stable
|
source ci/rust-version.sh stable
|
||||||
source ci/rust-version.sh nightly
|
source ci/rust-version.sh nightly
|
||||||
eval "$(ci/channel-info.sh)"
|
eval "$(ci/channel-info.sh)"
|
||||||
|
cargo="$(readlink -f "./cargo")"
|
||||||
|
|
||||||
|
scripts/increment-cargo-version.sh check
|
||||||
|
|
||||||
echo --- build environment
|
echo --- build environment
|
||||||
(
|
(
|
||||||
@@ -16,14 +19,14 @@ echo --- build environment
|
|||||||
rustup run "$rust_stable" rustc --version --verbose
|
rustup run "$rust_stable" rustc --version --verbose
|
||||||
rustup run "$rust_nightly" rustc --version --verbose
|
rustup run "$rust_nightly" rustc --version --verbose
|
||||||
|
|
||||||
cargo +"$rust_stable" --version --verbose
|
"$cargo" stable --version --verbose
|
||||||
cargo +"$rust_nightly" --version --verbose
|
"$cargo" nightly --version --verbose
|
||||||
|
|
||||||
cargo +"$rust_stable" clippy --version --verbose
|
"$cargo" stable clippy --version --verbose
|
||||||
cargo +"$rust_nightly" clippy --version --verbose
|
"$cargo" nightly clippy --version --verbose
|
||||||
|
|
||||||
# audit is done only with stable
|
# audit is done only with stable
|
||||||
cargo +"$rust_stable" audit --version
|
"$cargo" stable audit --version
|
||||||
)
|
)
|
||||||
|
|
||||||
export RUST_BACKTRACE=1
|
export RUST_BACKTRACE=1
|
||||||
@@ -41,7 +44,7 @@ if [[ $CI_BASE_BRANCH = "$EDGE_CHANNEL" ]]; then
|
|||||||
echo "$0: [tree (for outdated Cargo.lock sync)|check (for compilation error)|update -p foo --precise x.y.z (for your Cargo.toml update)] ..." >&2
|
echo "$0: [tree (for outdated Cargo.lock sync)|check (for compilation error)|update -p foo --precise x.y.z (for your Cargo.toml update)] ..." >&2
|
||||||
exit "$check_status"
|
exit "$check_status"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Ensure nightly and --benches
|
# Ensure nightly and --benches
|
||||||
_ scripts/cargo-for-all-lock-files.sh +"$rust_nightly" check --locked --all-targets
|
_ scripts/cargo-for-all-lock-files.sh +"$rust_nightly" check --locked --all-targets
|
||||||
else
|
else
|
||||||
@@ -49,26 +52,49 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
_ ci/order-crates-for-publishing.py
|
_ ci/order-crates-for-publishing.py
|
||||||
_ cargo +"$rust_stable" fmt --all -- --check
|
_ "$cargo" stable fmt --all -- --check
|
||||||
|
|
||||||
# -Z... is needed because of clippy bug: https://github.com/rust-lang/rust-clippy/issues/4612
|
# -Z... is needed because of clippy bug: https://github.com/rust-lang/rust-clippy/issues/4612
|
||||||
# run nightly clippy for `sdk/` as there's a moderate amount of nightly-only code there
|
# run nightly clippy for `sdk/` as there's a moderate amount of nightly-only code there
|
||||||
_ cargo +"$rust_nightly" clippy \
|
_ "$cargo" nightly clippy \
|
||||||
-Zunstable-options --workspace --all-targets \
|
-Zunstable-options --workspace --all-targets \
|
||||||
-- --deny=warnings --allow=clippy::stable_sort_primitive
|
-- --deny=warnings --allow=clippy::stable_sort_primitive
|
||||||
|
|
||||||
_ scripts/cargo-for-all-lock-files.sh +"$rust_stable" audit --ignore RUSTSEC-2020-0002 --ignore RUSTSEC-2020-0008
|
cargo_audit_ignores=(
|
||||||
|
# failure is officially deprecated/unmaintained
|
||||||
|
#
|
||||||
|
# Blocked on multiple upstream crates removing their `failure` dependency.
|
||||||
|
--ignore RUSTSEC-2020-0036
|
||||||
|
|
||||||
|
# `net2` crate has been deprecated; use `socket2` instead
|
||||||
|
#
|
||||||
|
# Blocked on https://github.com/paritytech/jsonrpc/issues/575
|
||||||
|
--ignore RUSTSEC-2020-0016
|
||||||
|
|
||||||
|
# stdweb is unmaintained
|
||||||
|
#
|
||||||
|
# Blocked on multiple upstream crates removing their `stdweb` dependency.
|
||||||
|
--ignore RUSTSEC-2020-0056
|
||||||
|
|
||||||
|
# Potential segfault in the time crate
|
||||||
|
#
|
||||||
|
# Blocked on multiple crates updating `time` to >= 0.2.23
|
||||||
|
--ignore RUSTSEC-2020-0071
|
||||||
|
)
|
||||||
|
_ scripts/cargo-for-all-lock-files.sh +"$rust_stable" audit "${cargo_audit_ignores[@]}"
|
||||||
|
|
||||||
{
|
{
|
||||||
cd programs/bpf
|
cd programs/bpf
|
||||||
_ cargo +"$rust_stable" audit
|
_ "$cargo" stable audit
|
||||||
for project in rust/*/ ; do
|
for project in rust/*/ ; do
|
||||||
echo "+++ do_bpf_checks $project"
|
echo "+++ do_bpf_checks $project"
|
||||||
(
|
(
|
||||||
cd "$project"
|
cd "$project"
|
||||||
_ cargo +"$rust_stable" fmt -- --check
|
_ "$cargo" stable fmt -- --check
|
||||||
_ cargo +"$rust_nightly" test
|
_ "$cargo" nightly test
|
||||||
_ cargo +"$rust_nightly" clippy -- --deny=warnings --allow=clippy::missing_safety_doc
|
_ "$cargo" nightly clippy -- --deny=warnings \
|
||||||
|
--allow=clippy::missing_safety_doc \
|
||||||
|
--allow=clippy::stable_sort_primitive
|
||||||
)
|
)
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,8 @@
|
|||||||
set -e
|
set -e
|
||||||
cd "$(dirname "$0")/.."
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
cargo="$(readlink -f "./cargo")"
|
||||||
|
|
||||||
source ci/_
|
source ci/_
|
||||||
|
|
||||||
annotate() {
|
annotate() {
|
||||||
@@ -37,12 +39,15 @@ NPROC=$((NPROC>14 ? 14 : NPROC))
|
|||||||
echo "Executing $testName"
|
echo "Executing $testName"
|
||||||
case $testName in
|
case $testName in
|
||||||
test-stable)
|
test-stable)
|
||||||
_ cargo +"$rust_stable" test --jobs "$NPROC" --all --exclude solana-local-cluster ${V:+--verbose} -- --nocapture
|
_ "$cargo" stable test --jobs "$NPROC" --all --exclude solana-local-cluster ${V:+--verbose} -- --nocapture
|
||||||
;;
|
;;
|
||||||
test-stable-perf)
|
test-stable-perf)
|
||||||
|
# BPF solana-sdk legacy compile test
|
||||||
|
./cargo-build-bpf --manifest-path sdk/Cargo.toml
|
||||||
|
|
||||||
# BPF program tests
|
# BPF program tests
|
||||||
_ make -C programs/bpf/c tests
|
_ make -C programs/bpf/c tests
|
||||||
_ cargo +"$rust_stable" test \
|
_ "$cargo" stable test \
|
||||||
--manifest-path programs/bpf/Cargo.toml \
|
--manifest-path programs/bpf/Cargo.toml \
|
||||||
--no-default-features --features=bpf_c,bpf_rust -- --nocapture
|
--no-default-features --features=bpf_c,bpf_rust -- --nocapture
|
||||||
|
|
||||||
@@ -62,13 +67,13 @@ test-stable-perf)
|
|||||||
export SOLANA_CUDA=1
|
export SOLANA_CUDA=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_ cargo +"$rust_stable" build --bins ${V:+--verbose}
|
_ "$cargo" stable build --bins ${V:+--verbose}
|
||||||
_ cargo +"$rust_stable" test --package solana-perf --package solana-ledger --package solana-core --lib ${V:+--verbose} -- --nocapture
|
_ "$cargo" stable test --package solana-perf --package solana-ledger --package solana-core --lib ${V:+--verbose} -- --nocapture
|
||||||
_ cargo +"$rust_stable" run --manifest-path poh-bench/Cargo.toml ${V:+--verbose} -- --hashes-per-tick 10
|
_ "$cargo" stable run --manifest-path poh-bench/Cargo.toml ${V:+--verbose} -- --hashes-per-tick 10
|
||||||
;;
|
;;
|
||||||
test-local-cluster)
|
test-local-cluster)
|
||||||
_ cargo +"$rust_stable" build --release --bins ${V:+--verbose}
|
_ "$cargo" stable build --release --bins ${V:+--verbose}
|
||||||
_ cargo +"$rust_stable" test --release --package solana-local-cluster ${V:+--verbose} -- --nocapture --test-threads=1
|
_ "$cargo" stable test --release --package solana-local-cluster ${V:+--verbose} -- --nocapture --test-threads=1
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-clap-utils"
|
name = "solana-clap-utils"
|
||||||
version = "1.4.0"
|
version = "1.4.16"
|
||||||
description = "Solana utilities for the clap"
|
description = "Solana utilities for the clap"
|
||||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
@@ -11,8 +11,8 @@ edition = "2018"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33.0"
|
clap = "2.33.0"
|
||||||
rpassword = "4.0"
|
rpassword = "4.0"
|
||||||
solana-remote-wallet = { path = "../remote-wallet", version = "1.4.0" }
|
solana-remote-wallet = { path = "../remote-wallet", version = "1.4.16" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.4.0" }
|
solana-sdk = { path = "../sdk", version = "1.4.16" }
|
||||||
thiserror = "1.0.20"
|
thiserror = "1.0.20"
|
||||||
tiny-bip39 = "0.7.0"
|
tiny-bip39 = "0.7.0"
|
||||||
url = "2.1.0"
|
url = "2.1.0"
|
||||||
|
@@ -189,6 +189,7 @@ pub fn commitment_of(matches: &ArgMatches<'_>, name: &str) -> Option<CommitmentC
|
|||||||
"recent" => CommitmentConfig::recent(),
|
"recent" => CommitmentConfig::recent(),
|
||||||
"root" => CommitmentConfig::root(),
|
"root" => CommitmentConfig::root(),
|
||||||
"single" => CommitmentConfig::single(),
|
"single" => CommitmentConfig::single(),
|
||||||
|
"singleGossip" => CommitmentConfig::single_gossip(),
|
||||||
_ => CommitmentConfig::default(),
|
_ => CommitmentConfig::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -228,8 +229,8 @@ mod tests {
|
|||||||
assert_eq!(values_of(&matches, "multiple"), Some(vec![50, 39]));
|
assert_eq!(values_of(&matches, "multiple"), Some(vec![50, 39]));
|
||||||
assert_eq!(values_of::<u64>(&matches, "single"), None);
|
assert_eq!(values_of::<u64>(&matches, "single"), None);
|
||||||
|
|
||||||
let pubkey0 = Pubkey::new_rand();
|
let pubkey0 = solana_sdk::pubkey::new_rand();
|
||||||
let pubkey1 = Pubkey::new_rand();
|
let pubkey1 = solana_sdk::pubkey::new_rand();
|
||||||
let matches = app().clone().get_matches_from(vec![
|
let matches = app().clone().get_matches_from(vec![
|
||||||
"test",
|
"test",
|
||||||
"--multiple",
|
"--multiple",
|
||||||
@@ -251,7 +252,7 @@ mod tests {
|
|||||||
assert_eq!(value_of(&matches, "single"), Some(50));
|
assert_eq!(value_of(&matches, "single"), Some(50));
|
||||||
assert_eq!(value_of::<u64>(&matches, "multiple"), None);
|
assert_eq!(value_of::<u64>(&matches, "multiple"), None);
|
||||||
|
|
||||||
let pubkey = Pubkey::new_rand();
|
let pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let matches = app()
|
let matches = app()
|
||||||
.clone()
|
.clone()
|
||||||
.get_matches_from(vec!["test", "--single", &pubkey.to_string()]);
|
.get_matches_from(vec!["test", "--single", &pubkey.to_string()]);
|
||||||
@@ -331,8 +332,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pubkeys_sigs_of() {
|
fn test_pubkeys_sigs_of() {
|
||||||
let key1 = Pubkey::new_rand();
|
let key1 = solana_sdk::pubkey::new_rand();
|
||||||
let key2 = Pubkey::new_rand();
|
let key2 = solana_sdk::pubkey::new_rand();
|
||||||
let sig1 = Keypair::new().sign_message(&[0u8]);
|
let sig1 = Keypair::new().sign_message(&[0u8]);
|
||||||
let sig2 = Keypair::new().sign_message(&[1u8]);
|
let sig2 = Keypair::new().sign_message(&[1u8]);
|
||||||
let signer1 = format!("{}={}", key1, sig1);
|
let signer1 = format!("{}={}", key1, sig1);
|
||||||
|
@@ -298,7 +298,24 @@ pub fn keypair_from_seed_phrase(
|
|||||||
keypair_from_seed_phrase_and_passphrase(&seed_phrase, &passphrase)?
|
keypair_from_seed_phrase_and_passphrase(&seed_phrase, &passphrase)?
|
||||||
} else {
|
} else {
|
||||||
let sanitized = sanitize_seed_phrase(seed_phrase);
|
let sanitized = sanitize_seed_phrase(seed_phrase);
|
||||||
let mnemonic = Mnemonic::from_phrase(&sanitized, Language::English)?;
|
let parse_language_fn = || {
|
||||||
|
for language in &[
|
||||||
|
Language::English,
|
||||||
|
Language::ChineseSimplified,
|
||||||
|
Language::ChineseTraditional,
|
||||||
|
Language::Japanese,
|
||||||
|
Language::Spanish,
|
||||||
|
Language::Korean,
|
||||||
|
Language::French,
|
||||||
|
Language::Italian,
|
||||||
|
] {
|
||||||
|
if let Ok(mnemonic) = Mnemonic::from_phrase(&sanitized, *language) {
|
||||||
|
return Ok(mnemonic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err("Can't get mnemonic from seed phrases")
|
||||||
|
};
|
||||||
|
let mnemonic = parse_language_fn()?;
|
||||||
let passphrase = prompt_passphrase(&passphrase_prompt)?;
|
let passphrase = prompt_passphrase(&passphrase_prompt)?;
|
||||||
let seed = Seed::new(&mnemonic, &passphrase);
|
let seed = Seed::new(&mnemonic, &passphrase);
|
||||||
keypair_from_seed(seed.as_bytes())?
|
keypair_from_seed(seed.as_bytes())?
|
||||||
|
@@ -3,13 +3,13 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "solana-cli-config"
|
name = "solana-cli-config"
|
||||||
description = "Blockchain, Rebuilt for Scale"
|
description = "Blockchain, Rebuilt for Scale"
|
||||||
version = "1.4.0"
|
version = "1.4.16"
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
homepage = "https://solana.com/"
|
homepage = "https://solana.com/"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
dirs = "2.0.2"
|
dirs-next = "2.0.0"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
serde = "1.0.112"
|
serde = "1.0.112"
|
||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
|
@@ -5,7 +5,7 @@ use url::Url;
|
|||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref CONFIG_FILE: Option<String> = {
|
pub static ref CONFIG_FILE: Option<String> = {
|
||||||
dirs::home_dir().map(|mut path| {
|
dirs_next::home_dir().map(|mut path| {
|
||||||
path.extend(&[".config", "solana", "cli", "config.yml"]);
|
path.extend(&[".config", "solana", "cli", "config.yml"]);
|
||||||
path.to_str().unwrap().to_string()
|
path.to_str().unwrap().to_string()
|
||||||
})
|
})
|
||||||
@@ -25,7 +25,7 @@ pub struct Config {
|
|||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let keypair_path = {
|
let keypair_path = {
|
||||||
let mut keypair_path = dirs::home_dir().expect("home directory");
|
let mut keypair_path = dirs_next::home_dir().expect("home directory");
|
||||||
keypair_path.extend(&[".config", "solana", "id.json"]);
|
keypair_path.extend(&[".config", "solana", "id.json"]);
|
||||||
keypair_path.to_str().unwrap().to_string()
|
keypair_path.to_str().unwrap().to_string()
|
||||||
};
|
};
|
||||||
@@ -76,17 +76,6 @@ impl Config {
|
|||||||
ws_url.to_string()
|
ws_url.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compute_rpc_banks_url(json_rpc_url: &str) -> String {
|
|
||||||
let json_rpc_url: Option<Url> = json_rpc_url.parse().ok();
|
|
||||||
if json_rpc_url.is_none() {
|
|
||||||
return "".to_string();
|
|
||||||
}
|
|
||||||
let mut url = json_rpc_url.unwrap();
|
|
||||||
let port = url.port().unwrap_or(8899);
|
|
||||||
url.set_port(Some(port + 3)).expect("unable to set port");
|
|
||||||
url.to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn import_address_labels<P>(&mut self, filename: P) -> Result<(), io::Error>
|
pub fn import_address_labels<P>(&mut self, filename: P) -> Result<(), io::Error>
|
||||||
where
|
where
|
||||||
P: AsRef<Path>,
|
P: AsRef<Path>,
|
||||||
@@ -133,28 +122,4 @@ mod test {
|
|||||||
|
|
||||||
assert_eq!(Config::compute_websocket_url(&"garbage"), String::new());
|
assert_eq!(Config::compute_websocket_url(&"garbage"), String::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn compute_rpc_banks_url() {
|
|
||||||
assert_eq!(
|
|
||||||
Config::compute_rpc_banks_url(&"http://devnet.solana.com"),
|
|
||||||
"http://devnet.solana.com:8902/".to_string()
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
Config::compute_rpc_banks_url(&"https://devnet.solana.com"),
|
|
||||||
"https://devnet.solana.com:8902/".to_string()
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
Config::compute_rpc_banks_url(&"http://example.com:8899"),
|
|
||||||
"http://example.com:8902/".to_string()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Config::compute_rpc_banks_url(&"https://example.com:1234"),
|
|
||||||
"https://example.com:1237/".to_string()
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(Config::compute_rpc_banks_url(&"garbage"), String::new());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "solana-cli-output"
|
name = "solana-cli-output"
|
||||||
description = "Blockchain, Rebuilt for Scale"
|
description = "Blockchain, Rebuilt for Scale"
|
||||||
version = "1.4.0"
|
version = "1.4.16"
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
homepage = "https://solana.com/"
|
homepage = "https://solana.com/"
|
||||||
@@ -17,13 +17,13 @@ indicatif = "0.15.0"
|
|||||||
serde = "1.0.112"
|
serde = "1.0.112"
|
||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
serde_json = "1.0.56"
|
serde_json = "1.0.56"
|
||||||
solana-account-decoder = { path = "../account-decoder", version = "1.4.0" }
|
solana-account-decoder = { path = "../account-decoder", version = "1.4.16" }
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "1.4.0" }
|
solana-clap-utils = { path = "../clap-utils", version = "1.4.16" }
|
||||||
solana-client = { path = "../client", version = "1.4.0" }
|
solana-client = { path = "../client", version = "1.4.16" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.4.0" }
|
solana-sdk = { path = "../sdk", version = "1.4.16" }
|
||||||
solana-stake-program = { path = "../programs/stake", version = "1.4.0" }
|
solana-stake-program = { path = "../programs/stake", version = "1.4.16" }
|
||||||
solana-transaction-status = { path = "../transaction-status", version = "1.4.0" }
|
solana-transaction-status = { path = "../transaction-status", version = "1.4.16" }
|
||||||
solana-vote-program = { path = "../programs/vote", version = "1.4.0" }
|
solana-vote-program = { path = "../programs/vote", version = "1.4.16" }
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
targets = ["x86_64-unknown-linux-gnu"]
|
targets = ["x86_64-unknown-linux-gnu"]
|
||||||
|
@@ -132,7 +132,7 @@ impl fmt::Display for CliBlockProduction {
|
|||||||
"{}",
|
"{}",
|
||||||
style(format!(
|
style(format!(
|
||||||
" {:<44} {:>15} {:>15} {:>15} {:>23}",
|
" {:<44} {:>15} {:>15} {:>15} {:>23}",
|
||||||
"Identity Pubkey",
|
"Identity",
|
||||||
"Leader Slots",
|
"Leader Slots",
|
||||||
"Blocks Produced",
|
"Blocks Produced",
|
||||||
"Skipped Slots",
|
"Skipped Slots",
|
||||||
@@ -301,7 +301,7 @@ pub struct CliValidatorsStakeByVersion {
|
|||||||
pub struct CliValidators {
|
pub struct CliValidators {
|
||||||
pub total_active_stake: u64,
|
pub total_active_stake: u64,
|
||||||
pub total_current_stake: u64,
|
pub total_current_stake: u64,
|
||||||
pub total_deliquent_stake: u64,
|
pub total_delinquent_stake: u64,
|
||||||
pub current_validators: Vec<CliValidator>,
|
pub current_validators: Vec<CliValidator>,
|
||||||
pub delinquent_validators: Vec<CliValidator>,
|
pub delinquent_validators: Vec<CliValidator>,
|
||||||
pub stake_by_version: BTreeMap<String, CliValidatorsStakeByVersion>,
|
pub stake_by_version: BTreeMap<String, CliValidatorsStakeByVersion>,
|
||||||
@@ -360,7 +360,7 @@ impl fmt::Display for CliValidators {
|
|||||||
"Active Stake:",
|
"Active Stake:",
|
||||||
&build_balance_message(self.total_active_stake, self.use_lamports_unit, true),
|
&build_balance_message(self.total_active_stake, self.use_lamports_unit, true),
|
||||||
)?;
|
)?;
|
||||||
if self.total_deliquent_stake > 0 {
|
if self.total_delinquent_stake > 0 {
|
||||||
writeln_name_value(
|
writeln_name_value(
|
||||||
f,
|
f,
|
||||||
"Current Stake:",
|
"Current Stake:",
|
||||||
@@ -376,11 +376,11 @@ impl fmt::Display for CliValidators {
|
|||||||
&format!(
|
&format!(
|
||||||
"{} ({:0.2}%)",
|
"{} ({:0.2}%)",
|
||||||
&build_balance_message(
|
&build_balance_message(
|
||||||
self.total_deliquent_stake,
|
self.total_delinquent_stake,
|
||||||
self.use_lamports_unit,
|
self.use_lamports_unit,
|
||||||
true
|
true
|
||||||
),
|
),
|
||||||
100. * self.total_deliquent_stake as f64 / self.total_active_stake as f64
|
100. * self.total_delinquent_stake as f64 / self.total_active_stake as f64
|
||||||
),
|
),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
@@ -412,8 +412,8 @@ impl fmt::Display for CliValidators {
|
|||||||
"{}",
|
"{}",
|
||||||
style(format!(
|
style(format!(
|
||||||
" {:<44} {:<38} {} {} {} {:>10} {:^8} {}",
|
" {:<44} {:<38} {} {} {} {:>10} {:^8} {}",
|
||||||
"Identity Pubkey",
|
"Identity",
|
||||||
"Vote Account Pubkey",
|
"Vote Account",
|
||||||
"Commission",
|
"Commission",
|
||||||
"Last Vote",
|
"Last Vote",
|
||||||
"Root Block",
|
"Root Block",
|
||||||
@@ -520,7 +520,7 @@ impl fmt::Display for CliNonceAccount {
|
|||||||
)
|
)
|
||||||
)?;
|
)?;
|
||||||
let nonce = self.nonce.as_deref().unwrap_or("uninitialized");
|
let nonce = self.nonce.as_deref().unwrap_or("uninitialized");
|
||||||
writeln!(f, "Nonce: {}", nonce)?;
|
writeln!(f, "Nonce blockhash: {}", nonce)?;
|
||||||
if let Some(fees) = self.lamports_per_signature {
|
if let Some(fees) = self.lamports_per_signature {
|
||||||
writeln!(f, "Fee: {} lamports per signature", fees)?;
|
writeln!(f, "Fee: {} lamports per signature", fees)?;
|
||||||
} else {
|
} else {
|
||||||
@@ -541,7 +541,15 @@ impl CliStakeVec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl QuietDisplay for CliStakeVec {}
|
impl QuietDisplay for CliStakeVec {}
|
||||||
impl VerboseDisplay for CliStakeVec {}
|
impl VerboseDisplay for CliStakeVec {
|
||||||
|
fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result {
|
||||||
|
for state in &self.0 {
|
||||||
|
writeln!(w)?;
|
||||||
|
VerboseDisplay::write_str(state, w)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for CliStakeVec {
|
impl fmt::Display for CliStakeVec {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
@@ -562,7 +570,12 @@ pub struct CliKeyedStakeState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl QuietDisplay for CliKeyedStakeState {}
|
impl QuietDisplay for CliKeyedStakeState {}
|
||||||
impl VerboseDisplay for CliKeyedStakeState {}
|
impl VerboseDisplay for CliKeyedStakeState {
|
||||||
|
fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result {
|
||||||
|
writeln!(w, "Stake Pubkey: {}", self.stake_pubkey)?;
|
||||||
|
VerboseDisplay::write_str(&self.stake_state, w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for CliKeyedStakeState {
|
impl fmt::Display for CliKeyedStakeState {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
@@ -619,6 +632,8 @@ pub struct CliStakeState {
|
|||||||
pub stake_type: CliStakeType,
|
pub stake_type: CliStakeType,
|
||||||
pub account_balance: u64,
|
pub account_balance: u64,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub credits_observed: Option<u64>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub delegated_stake: Option<u64>,
|
pub delegated_stake: Option<u64>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub delegated_vote_account_address: Option<String>,
|
pub delegated_vote_account_address: Option<String>,
|
||||||
@@ -647,7 +662,15 @@ pub struct CliStakeState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl QuietDisplay for CliStakeState {}
|
impl QuietDisplay for CliStakeState {}
|
||||||
impl VerboseDisplay for CliStakeState {}
|
impl VerboseDisplay for CliStakeState {
|
||||||
|
fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result {
|
||||||
|
write!(w, "{}", self)?;
|
||||||
|
if let Some(credits) = self.credits_observed {
|
||||||
|
writeln!(w, "Credits Observed: {}", credits)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for CliStakeState {
|
impl fmt::Display for CliStakeState {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
@@ -661,13 +684,8 @@ impl fmt::Display for CliStakeState {
|
|||||||
if lockup.unix_timestamp != UnixTimestamp::default() {
|
if lockup.unix_timestamp != UnixTimestamp::default() {
|
||||||
writeln!(
|
writeln!(
|
||||||
f,
|
f,
|
||||||
"Lockup Timestamp: {} (UnixTimestamp: {})",
|
"Lockup Timestamp: {}",
|
||||||
DateTime::<Utc>::from_utc(
|
unix_timestamp_to_string(lockup.unix_timestamp)
|
||||||
NaiveDateTime::from_timestamp(lockup.unix_timestamp, 0),
|
|
||||||
Utc
|
|
||||||
)
|
|
||||||
.to_rfc3339_opts(SecondsFormat::Secs, true),
|
|
||||||
lockup.unix_timestamp
|
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
if lockup.epoch != Epoch::default() {
|
if lockup.epoch != Epoch::default() {
|
||||||
@@ -952,8 +970,8 @@ impl VerboseDisplay for CliValidatorInfo {}
|
|||||||
|
|
||||||
impl fmt::Display for CliValidatorInfo {
|
impl fmt::Display for CliValidatorInfo {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
writeln_name_value(f, "Validator Identity Pubkey:", &self.identity_pubkey)?;
|
writeln_name_value(f, "Validator Identity:", &self.identity_pubkey)?;
|
||||||
writeln_name_value(f, " Info Pubkey:", &self.info_pubkey)?;
|
writeln_name_value(f, " Info Address:", &self.info_pubkey)?;
|
||||||
for (key, value) in self.info.iter() {
|
for (key, value) in self.info.iter() {
|
||||||
writeln_name_value(
|
writeln_name_value(
|
||||||
f,
|
f,
|
||||||
@@ -1008,7 +1026,12 @@ impl fmt::Display for CliVoteAccount {
|
|||||||
None => "~".to_string(),
|
None => "~".to_string(),
|
||||||
}
|
}
|
||||||
)?;
|
)?;
|
||||||
writeln!(f, "Recent Timestamp: {:?}", self.recent_timestamp)?;
|
writeln!(
|
||||||
|
f,
|
||||||
|
"Recent Timestamp: {} from slot {}",
|
||||||
|
unix_timestamp_to_string(self.recent_timestamp.timestamp),
|
||||||
|
self.recent_timestamp.slot
|
||||||
|
)?;
|
||||||
if !self.votes.is_empty() {
|
if !self.votes.is_empty() {
|
||||||
writeln!(f, "Recent Votes:")?;
|
writeln!(f, "Recent Votes:")?;
|
||||||
for vote in &self.votes {
|
for vote in &self.votes {
|
||||||
@@ -1093,19 +1116,22 @@ pub struct CliBlockTime {
|
|||||||
impl QuietDisplay for CliBlockTime {}
|
impl QuietDisplay for CliBlockTime {}
|
||||||
impl VerboseDisplay for CliBlockTime {}
|
impl VerboseDisplay for CliBlockTime {}
|
||||||
|
|
||||||
|
fn unix_timestamp_to_string(unix_timestamp: UnixTimestamp) -> String {
|
||||||
|
format!(
|
||||||
|
"{} (UnixTimestamp: {})",
|
||||||
|
match NaiveDateTime::from_timestamp_opt(unix_timestamp, 0) {
|
||||||
|
Some(ndt) =>
|
||||||
|
DateTime::<Utc>::from_utc(ndt, Utc).to_rfc3339_opts(SecondsFormat::Secs, true),
|
||||||
|
None => "unknown".to_string(),
|
||||||
|
},
|
||||||
|
unix_timestamp,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for CliBlockTime {
|
impl fmt::Display for CliBlockTime {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
writeln_name_value(f, "Block:", &self.slot.to_string())?;
|
writeln_name_value(f, "Block:", &self.slot.to_string())?;
|
||||||
writeln_name_value(
|
writeln_name_value(f, "Date:", &unix_timestamp_to_string(self.timestamp))
|
||||||
f,
|
|
||||||
"Date:",
|
|
||||||
&format!(
|
|
||||||
"{} (UnixTimestamp: {})",
|
|
||||||
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(self.timestamp, 0), Utc)
|
|
||||||
.to_rfc3339_opts(SecondsFormat::Secs, true),
|
|
||||||
self.timestamp
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -197,6 +197,15 @@ pub fn write_transaction<W: io::Write>(
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(log_messages) = &transaction_status.log_messages {
|
||||||
|
if !log_messages.is_empty() {
|
||||||
|
writeln!(w, "{}Log Messages:", prefix,)?;
|
||||||
|
for log_message in log_messages {
|
||||||
|
writeln!(w, "{} {}", prefix, log_message,)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
writeln!(w, "{}Status: Unavailable", prefix)?;
|
writeln!(w, "{}Status: Unavailable", prefix)?;
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "solana-cli"
|
name = "solana-cli"
|
||||||
description = "Blockchain, Rebuilt for Scale"
|
description = "Blockchain, Rebuilt for Scale"
|
||||||
version = "1.4.0"
|
version = "1.4.16"
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
homepage = "https://solana.com/"
|
homepage = "https://solana.com/"
|
||||||
@@ -16,39 +16,41 @@ clap = "2.33.1"
|
|||||||
criterion-stats = "0.3.0"
|
criterion-stats = "0.3.0"
|
||||||
ctrlc = { version = "3.1.5", features = ["termination"] }
|
ctrlc = { version = "3.1.5", features = ["termination"] }
|
||||||
console = "0.11.3"
|
console = "0.11.3"
|
||||||
dirs = "2.0.2"
|
dirs-next = "2.0.0"
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
Inflector = "0.11.4"
|
Inflector = "0.11.4"
|
||||||
indicatif = "0.15.0"
|
indicatif = "0.15.0"
|
||||||
humantime = "2.0.1"
|
humantime = "2.0.1"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
pretty-hex = "0.1.1"
|
pretty-hex = "0.1.1"
|
||||||
reqwest = { version = "0.10.6", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
reqwest = { version = "0.10.8", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||||
serde = "1.0.112"
|
serde = "1.0.112"
|
||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
serde_json = "1.0.56"
|
serde_json = "1.0.56"
|
||||||
solana-account-decoder = { path = "../account-decoder", version = "1.4.0" }
|
solana-account-decoder = { path = "../account-decoder", version = "1.4.16" }
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "1.4.0" }
|
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "1.4.16" }
|
||||||
solana-cli-config = { path = "../cli-config", version = "1.4.0" }
|
solana-clap-utils = { path = "../clap-utils", version = "1.4.16" }
|
||||||
solana-cli-output = { path = "../cli-output", version = "1.4.0" }
|
solana-cli-config = { path = "../cli-config", version = "1.4.16" }
|
||||||
solana-client = { path = "../client", version = "1.4.0" }
|
solana-cli-output = { path = "../cli-output", version = "1.4.16" }
|
||||||
solana-config-program = { path = "../programs/config", version = "1.4.0" }
|
solana-client = { path = "../client", version = "1.4.16" }
|
||||||
solana-faucet = { path = "../faucet", version = "1.4.0" }
|
solana-config-program = { path = "../programs/config", version = "1.4.16" }
|
||||||
solana-logger = { path = "../logger", version = "1.4.0" }
|
solana-faucet = { path = "../faucet", version = "1.4.16" }
|
||||||
solana-net-utils = { path = "../net-utils", version = "1.4.0" }
|
solana-logger = { path = "../logger", version = "1.4.16" }
|
||||||
solana-remote-wallet = { path = "../remote-wallet", version = "1.4.0" }
|
solana-net-utils = { path = "../net-utils", version = "1.4.16" }
|
||||||
solana-runtime = { path = "../runtime", version = "1.4.0" }
|
solana_rbpf = "=0.1.34"
|
||||||
solana-sdk = { path = "../sdk", version = "1.4.0" }
|
solana-remote-wallet = { path = "../remote-wallet", version = "1.4.16" }
|
||||||
solana-stake-program = { path = "../programs/stake", version = "1.4.0" }
|
solana-sdk = { path = "../sdk", version = "1.4.16" }
|
||||||
solana-transaction-status = { path = "../transaction-status", version = "1.4.0" }
|
solana-stake-program = { path = "../programs/stake", version = "1.4.16" }
|
||||||
solana-version = { path = "../version", version = "1.4.0" }
|
solana-transaction-status = { path = "../transaction-status", version = "1.4.16" }
|
||||||
solana-vote-program = { path = "../programs/vote", version = "1.4.0" }
|
solana-version = { path = "../version", version = "1.4.16" }
|
||||||
solana-vote-signer = { path = "../vote-signer", version = "1.4.0" }
|
solana-vote-program = { path = "../programs/vote", version = "1.4.16" }
|
||||||
|
solana-vote-signer = { path = "../vote-signer", version = "1.4.16" }
|
||||||
thiserror = "1.0.20"
|
thiserror = "1.0.20"
|
||||||
|
tiny-bip39 = "0.7.0"
|
||||||
url = "2.1.1"
|
url = "2.1.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
solana-core = { path = "../core", version = "1.4.0" }
|
solana-core = { path = "../core", version = "1.4.16" }
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
@@ -54,12 +54,42 @@ pub fn check_account_for_multiple_fees_with_commitment(
|
|||||||
fee_calculator: &FeeCalculator,
|
fee_calculator: &FeeCalculator,
|
||||||
messages: &[&Message],
|
messages: &[&Message],
|
||||||
commitment: CommitmentConfig,
|
commitment: CommitmentConfig,
|
||||||
|
) -> Result<(), CliError> {
|
||||||
|
check_account_for_spend_multiple_fees_with_commitment(
|
||||||
|
rpc_client,
|
||||||
|
account_pubkey,
|
||||||
|
0,
|
||||||
|
fee_calculator,
|
||||||
|
messages,
|
||||||
|
commitment,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_account_for_spend_multiple_fees_with_commitment(
|
||||||
|
rpc_client: &RpcClient,
|
||||||
|
account_pubkey: &Pubkey,
|
||||||
|
balance: u64,
|
||||||
|
fee_calculator: &FeeCalculator,
|
||||||
|
messages: &[&Message],
|
||||||
|
commitment: CommitmentConfig,
|
||||||
) -> Result<(), CliError> {
|
) -> Result<(), CliError> {
|
||||||
let fee = calculate_fee(fee_calculator, messages);
|
let fee = calculate_fee(fee_calculator, messages);
|
||||||
if !check_account_for_balance_with_commitment(rpc_client, account_pubkey, fee, commitment)
|
if !check_account_for_balance_with_commitment(
|
||||||
.map_err(Into::<ClientError>::into)?
|
rpc_client,
|
||||||
|
account_pubkey,
|
||||||
|
balance + fee,
|
||||||
|
commitment,
|
||||||
|
)
|
||||||
|
.map_err(Into::<ClientError>::into)?
|
||||||
{
|
{
|
||||||
return Err(CliError::InsufficientFundsForFee(lamports_to_sol(fee)));
|
if balance > 0 {
|
||||||
|
return Err(CliError::InsufficientFundsForSpendAndFee(
|
||||||
|
lamports_to_sol(balance),
|
||||||
|
lamports_to_sol(fee),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
return Err(CliError::InsufficientFundsForFee(lamports_to_sol(fee)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -131,7 +161,7 @@ mod tests {
|
|||||||
context: RpcResponseContext { slot: 1 },
|
context: RpcResponseContext { slot: 1 },
|
||||||
value: json!(account_balance),
|
value: json!(account_balance),
|
||||||
});
|
});
|
||||||
let pubkey = Pubkey::new_rand();
|
let pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let fee_calculator = FeeCalculator::new(1);
|
let fee_calculator = FeeCalculator::new(1);
|
||||||
|
|
||||||
let pubkey0 = Pubkey::new(&[0; 32]);
|
let pubkey0 = Pubkey::new(&[0; 32]);
|
||||||
@@ -191,7 +221,7 @@ mod tests {
|
|||||||
context: RpcResponseContext { slot: 1 },
|
context: RpcResponseContext { slot: 1 },
|
||||||
value: json!(account_balance),
|
value: json!(account_balance),
|
||||||
});
|
});
|
||||||
let pubkey = Pubkey::new_rand();
|
let pubkey = solana_sdk::pubkey::new_rand();
|
||||||
|
|
||||||
let mut mocks = HashMap::new();
|
let mut mocks = HashMap::new();
|
||||||
mocks.insert(RpcRequest::GetBalance, account_balance_response);
|
mocks.insert(RpcRequest::GetBalance, account_balance_response);
|
||||||
@@ -237,9 +267,9 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_unique_pubkeys() {
|
fn test_check_unique_pubkeys() {
|
||||||
let pubkey0 = Pubkey::new_rand();
|
let pubkey0 = solana_sdk::pubkey::new_rand();
|
||||||
let pubkey_clone = pubkey0;
|
let pubkey_clone = pubkey0;
|
||||||
let pubkey1 = Pubkey::new_rand();
|
let pubkey1 = solana_sdk::pubkey::new_rand();
|
||||||
|
|
||||||
check_unique_pubkeys((&pubkey0, "foo".to_string()), (&pubkey1, "bar".to_string()))
|
check_unique_pubkeys((&pubkey0, "foo".to_string()), (&pubkey1, "bar".to_string()))
|
||||||
.expect("unexpected result");
|
.expect("unexpected result");
|
||||||
|
239
cli/src/cli.rs
239
cli/src/cli.rs
@@ -1,12 +1,15 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
checks::*, cluster_query::*, feature::*, inflation::*, nonce::*, spend_utils::*, stake::*,
|
checks::*, cluster_query::*, feature::*, inflation::*, nonce::*, send_tpu::*, spend_utils::*,
|
||||||
validator_info::*, vote::*,
|
stake::*, validator_info::*, vote::*,
|
||||||
};
|
};
|
||||||
|
use bincode::serialize;
|
||||||
|
use bip39::{Language, Mnemonic, MnemonicType, Seed};
|
||||||
use clap::{value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand};
|
use clap::{value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand};
|
||||||
use log::*;
|
use log::*;
|
||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
use serde_json::{self, json, Value};
|
use serde_json::{self, json, Value};
|
||||||
use solana_account_decoder::{UiAccount, UiAccountEncoding};
|
use solana_account_decoder::{UiAccount, UiAccountEncoding};
|
||||||
|
use solana_bpf_loader_program::bpf_verifier;
|
||||||
use solana_clap_utils::{
|
use solana_clap_utils::{
|
||||||
self,
|
self,
|
||||||
commitment::commitment_arg_with_default,
|
commitment::commitment_arg_with_default,
|
||||||
@@ -28,26 +31,28 @@ use solana_client::{
|
|||||||
client_error::{ClientError, ClientErrorKind, Result as ClientResult},
|
client_error::{ClientError, ClientErrorKind, Result as ClientResult},
|
||||||
nonce_utils,
|
nonce_utils,
|
||||||
rpc_client::RpcClient,
|
rpc_client::RpcClient,
|
||||||
rpc_config::{RpcLargestAccountsFilter, RpcSendTransactionConfig},
|
rpc_config::{RpcLargestAccountsFilter, RpcSendTransactionConfig, RpcTransactionLogsFilter},
|
||||||
rpc_request::MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS,
|
rpc_request::MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS,
|
||||||
rpc_response::RpcKeyedAccount,
|
rpc_response::{RpcKeyedAccount, RpcLeaderSchedule},
|
||||||
};
|
};
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
use solana_faucet::faucet::request_airdrop_transaction;
|
use solana_faucet::faucet::request_airdrop_transaction;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use solana_faucet::faucet_mock::request_airdrop_transaction;
|
use solana_faucet::faucet_mock::request_airdrop_transaction;
|
||||||
|
use solana_rbpf::vm::EbpfVm;
|
||||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
bpf_loader, bpf_loader_deprecated,
|
bpf_loader, bpf_loader_deprecated,
|
||||||
clock::{Epoch, Slot, DEFAULT_TICKS_PER_SECOND},
|
clock::{Epoch, Slot},
|
||||||
commitment_config::CommitmentConfig,
|
commitment_config::CommitmentConfig,
|
||||||
decode_error::DecodeError,
|
decode_error::DecodeError,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
instruction::{Instruction, InstructionError},
|
instruction::{Instruction, InstructionError},
|
||||||
loader_instruction,
|
loader_instruction,
|
||||||
message::Message,
|
message::Message,
|
||||||
|
native_token::Sol,
|
||||||
pubkey::{Pubkey, MAX_SEED_LEN},
|
pubkey::{Pubkey, MAX_SEED_LEN},
|
||||||
signature::{Keypair, Signature, Signer, SignerError},
|
signature::{keypair_from_seed, Keypair, Signature, Signer, SignerError},
|
||||||
signers::Signers,
|
signers::Signers,
|
||||||
system_instruction::{self, SystemError},
|
system_instruction::{self, SystemError},
|
||||||
system_program,
|
system_program,
|
||||||
@@ -60,12 +65,13 @@ use solana_stake_program::{
|
|||||||
use solana_transaction_status::{EncodedTransaction, UiTransactionEncoding};
|
use solana_transaction_status::{EncodedTransaction, UiTransactionEncoding};
|
||||||
use solana_vote_program::vote_state::VoteAuthorize;
|
use solana_vote_program::vote_state::VoteAuthorize;
|
||||||
use std::{
|
use std::{
|
||||||
|
cmp::min,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
error,
|
error,
|
||||||
fmt::Write as FmtWrite,
|
fmt::Write as FmtWrite,
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{Read, Write},
|
io::{Read, Write},
|
||||||
net::{IpAddr, SocketAddr},
|
net::{IpAddr, SocketAddr, UdpSocket},
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
thread::sleep,
|
thread::sleep,
|
||||||
@@ -98,7 +104,7 @@ pub enum CliCommand {
|
|||||||
Fees,
|
Fees,
|
||||||
FirstAvailableBlock,
|
FirstAvailableBlock,
|
||||||
GetBlock {
|
GetBlock {
|
||||||
slot: Slot,
|
slot: Option<Slot>,
|
||||||
},
|
},
|
||||||
GetBlockTime {
|
GetBlockTime {
|
||||||
slot: Option<Slot>,
|
slot: Option<Slot>,
|
||||||
@@ -114,11 +120,16 @@ pub enum CliCommand {
|
|||||||
},
|
},
|
||||||
LeaderSchedule,
|
LeaderSchedule,
|
||||||
LiveSlots,
|
LiveSlots,
|
||||||
|
Logs {
|
||||||
|
filter: RpcTransactionLogsFilter,
|
||||||
|
},
|
||||||
Ping {
|
Ping {
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
interval: Duration,
|
interval: Duration,
|
||||||
count: Option<u64>,
|
count: Option<u64>,
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
|
blockhash: Option<Hash>,
|
||||||
|
print_timestamp: bool,
|
||||||
},
|
},
|
||||||
ShowBlockProduction {
|
ShowBlockProduction {
|
||||||
epoch: Option<Epoch>,
|
epoch: Option<Epoch>,
|
||||||
@@ -175,6 +186,7 @@ pub enum CliCommand {
|
|||||||
program_location: String,
|
program_location: String,
|
||||||
address: Option<SignerIndex>,
|
address: Option<SignerIndex>,
|
||||||
use_deprecated_loader: bool,
|
use_deprecated_loader: bool,
|
||||||
|
allow_excessive_balance: bool,
|
||||||
},
|
},
|
||||||
// Stake Commands
|
// Stake Commands
|
||||||
CreateStakeAccount {
|
CreateStakeAccount {
|
||||||
@@ -575,6 +587,7 @@ pub fn parse_command(
|
|||||||
command: CliCommand::LiveSlots,
|
command: CliCommand::LiveSlots,
|
||||||
signers: vec![],
|
signers: vec![],
|
||||||
}),
|
}),
|
||||||
|
("logs", Some(matches)) => parse_logs(matches, wallet_manager),
|
||||||
("block-production", Some(matches)) => parse_show_block_production(matches),
|
("block-production", Some(matches)) => parse_show_block_production(matches),
|
||||||
("gossip", Some(_matches)) => Ok(CliCommandInfo {
|
("gossip", Some(_matches)) => Ok(CliCommandInfo {
|
||||||
command: CliCommand::ShowGossip,
|
command: CliCommand::ShowGossip,
|
||||||
@@ -606,13 +619,13 @@ pub fn parse_command(
|
|||||||
signers.push(signer);
|
signers.push(signer);
|
||||||
1
|
1
|
||||||
});
|
});
|
||||||
let use_deprecated_loader = matches.is_present("use_deprecated_loader");
|
|
||||||
|
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::Deploy {
|
command: CliCommand::Deploy {
|
||||||
program_location: matches.value_of("program_location").unwrap().to_string(),
|
program_location: matches.value_of("program_location").unwrap().to_string(),
|
||||||
address,
|
address,
|
||||||
use_deprecated_loader,
|
use_deprecated_loader: matches.is_present("use_deprecated_loader"),
|
||||||
|
allow_excessive_balance: matches.is_present("allow_excessive_balance"),
|
||||||
},
|
},
|
||||||
signers,
|
signers,
|
||||||
})
|
})
|
||||||
@@ -1026,33 +1039,50 @@ fn send_and_confirm_transactions_with_spinner<T: Signers>(
|
|||||||
) -> Result<(), Box<dyn error::Error>> {
|
) -> Result<(), Box<dyn error::Error>> {
|
||||||
let progress_bar = new_spinner_progress_bar();
|
let progress_bar = new_spinner_progress_bar();
|
||||||
let mut send_retries = 5;
|
let mut send_retries = 5;
|
||||||
|
let mut leader_schedule: Option<RpcLeaderSchedule> = None;
|
||||||
|
let mut leader_schedule_epoch = 0;
|
||||||
|
let send_socket = UdpSocket::bind("0.0.0.0:0").unwrap();
|
||||||
|
let cluster_nodes = rpc_client.get_cluster_nodes().ok();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut status_retries = 15;
|
let mut status_retries = 15;
|
||||||
|
|
||||||
|
progress_bar.set_message("Finding leader node...");
|
||||||
|
let epoch_info = rpc_client.get_epoch_info_with_commitment(commitment)?;
|
||||||
|
if epoch_info.epoch > leader_schedule_epoch || leader_schedule.is_none() {
|
||||||
|
leader_schedule = rpc_client
|
||||||
|
.get_leader_schedule_with_commitment(Some(epoch_info.absolute_slot), commitment)?;
|
||||||
|
leader_schedule_epoch = epoch_info.epoch;
|
||||||
|
}
|
||||||
|
let tpu_address = get_leader_tpu(
|
||||||
|
min(epoch_info.slot_index + 1, epoch_info.slots_in_epoch),
|
||||||
|
leader_schedule.as_ref(),
|
||||||
|
cluster_nodes.as_ref(),
|
||||||
|
);
|
||||||
|
|
||||||
// Send all transactions
|
// Send all transactions
|
||||||
let mut pending_transactions = HashMap::new();
|
let mut pending_transactions = HashMap::new();
|
||||||
let num_transactions = transactions.len();
|
let num_transactions = transactions.len();
|
||||||
for transaction in transactions {
|
for transaction in transactions {
|
||||||
if cfg!(not(test)) {
|
if let Some(tpu_address) = tpu_address {
|
||||||
// Delay ~1 tick between write transactions in an attempt to reduce AccountInUse errors
|
let wire_transaction =
|
||||||
// when all the write transactions modify the same program account (eg, deploying a
|
serialize(&transaction).expect("serialization should succeed");
|
||||||
// new program)
|
send_transaction_tpu(&send_socket, &tpu_address, &wire_transaction);
|
||||||
sleep(Duration::from_millis(1000 / DEFAULT_TICKS_PER_SECOND));
|
} else {
|
||||||
|
let _result = rpc_client
|
||||||
|
.send_transaction_with_config(
|
||||||
|
&transaction,
|
||||||
|
RpcSendTransactionConfig {
|
||||||
|
preflight_commitment: Some(commitment.commitment),
|
||||||
|
..RpcSendTransactionConfig::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
let _result = rpc_client
|
|
||||||
.send_transaction_with_config(
|
|
||||||
&transaction,
|
|
||||||
RpcSendTransactionConfig {
|
|
||||||
preflight_commitment: Some(commitment.commitment),
|
|
||||||
..RpcSendTransactionConfig::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
pending_transactions.insert(transaction.signatures[0], transaction);
|
pending_transactions.insert(transaction.signatures[0], transaction);
|
||||||
|
|
||||||
progress_bar.set_message(&format!(
|
progress_bar.set_message(&format!(
|
||||||
"[{}/{}] Transactions sent",
|
"[{}/{}] Total Transactions sent",
|
||||||
pending_transactions.len(),
|
pending_transactions.len(),
|
||||||
num_transactions
|
num_transactions
|
||||||
));
|
));
|
||||||
@@ -1088,6 +1118,11 @@ fn send_and_confirm_transactions_with_spinner<T: Signers>(
|
|||||||
let _ = pending_transactions.remove(&signature);
|
let _ = pending_transactions.remove(&signature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
progress_bar.set_message(&format!(
|
||||||
|
"[{}/{}] Transactions confirmed",
|
||||||
|
num_transactions - pending_transactions.len(),
|
||||||
|
num_transactions
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if pending_transactions.is_empty() {
|
if pending_transactions.is_empty() {
|
||||||
@@ -1129,8 +1164,52 @@ fn process_deploy(
|
|||||||
program_location: &str,
|
program_location: &str,
|
||||||
address: Option<SignerIndex>,
|
address: Option<SignerIndex>,
|
||||||
use_deprecated_loader: bool,
|
use_deprecated_loader: bool,
|
||||||
|
allow_excessive_balance: bool,
|
||||||
|
) -> ProcessResult {
|
||||||
|
const WORDS: usize = 12;
|
||||||
|
// Create ephemeral keypair to use for program address, if not provided
|
||||||
|
let mnemonic = Mnemonic::new(MnemonicType::for_word_count(WORDS)?, Language::English);
|
||||||
|
let seed = Seed::new(&mnemonic, "");
|
||||||
|
let new_keypair = keypair_from_seed(seed.as_bytes())?;
|
||||||
|
|
||||||
|
let result = do_process_deploy(
|
||||||
|
rpc_client,
|
||||||
|
config,
|
||||||
|
program_location,
|
||||||
|
address,
|
||||||
|
use_deprecated_loader,
|
||||||
|
allow_excessive_balance,
|
||||||
|
new_keypair,
|
||||||
|
);
|
||||||
|
|
||||||
|
if result.is_err() && address.is_none() {
|
||||||
|
let phrase: &str = mnemonic.phrase();
|
||||||
|
let divider = String::from_utf8(vec![b'='; phrase.len()]).unwrap();
|
||||||
|
eprintln!(
|
||||||
|
"{}\nTo reuse this address, recover the ephemeral keypair file with",
|
||||||
|
divider
|
||||||
|
);
|
||||||
|
eprintln!(
|
||||||
|
"`solana-keygen recover` and the following {}-word seed phrase,",
|
||||||
|
WORDS
|
||||||
|
);
|
||||||
|
eprintln!(
|
||||||
|
"then pass it as the [PROGRAM_ADDRESS_SIGNER] argument to `solana deploy ...`\n{}\n{}\n{}",
|
||||||
|
divider, phrase, divider
|
||||||
|
);
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_process_deploy(
|
||||||
|
rpc_client: &RpcClient,
|
||||||
|
config: &CliConfig,
|
||||||
|
program_location: &str,
|
||||||
|
address: Option<SignerIndex>,
|
||||||
|
use_deprecated_loader: bool,
|
||||||
|
allow_excessive_balance: bool,
|
||||||
|
new_keypair: Keypair,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let new_keypair = Keypair::new(); // Create ephemeral keypair to use for program address, if not provided
|
|
||||||
let program_id = if let Some(i) = address {
|
let program_id = if let Some(i) = address {
|
||||||
config.signers[i]
|
config.signers[i]
|
||||||
} else {
|
} else {
|
||||||
@@ -1144,6 +1223,9 @@ fn process_deploy(
|
|||||||
CliError::DynamicProgramError(format!("Unable to read program file: {}", err))
|
CliError::DynamicProgramError(format!("Unable to read program file: {}", err))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
EbpfVm::create_executable_from_elf(&program_data, Some(|x| bpf_verifier::check(x, false)))
|
||||||
|
.map_err(|err| CliError::DynamicProgramError(format!("ELF error: {}", err)))?;
|
||||||
|
|
||||||
let loader_id = if use_deprecated_loader {
|
let loader_id = if use_deprecated_loader {
|
||||||
bpf_loader_deprecated::id()
|
bpf_loader_deprecated::id()
|
||||||
} else {
|
} else {
|
||||||
@@ -1154,11 +1236,12 @@ fn process_deploy(
|
|||||||
let signers = [config.signers[0], program_id];
|
let signers = [config.signers[0], program_id];
|
||||||
|
|
||||||
// Check program account to see if partial initialization has occurred
|
// Check program account to see if partial initialization has occurred
|
||||||
let initial_instructions = if let Some(account) = rpc_client
|
let (initial_instructions, balance_needed) = if let Some(account) = rpc_client
|
||||||
.get_account_with_commitment(&program_id.pubkey(), config.commitment)?
|
.get_account_with_commitment(&program_id.pubkey(), config.commitment)?
|
||||||
.value
|
.value
|
||||||
{
|
{
|
||||||
let mut instructions: Vec<Instruction> = vec![];
|
let mut instructions: Vec<Instruction> = vec![];
|
||||||
|
let mut balance_needed = 0;
|
||||||
if account.executable {
|
if account.executable {
|
||||||
return Err(CliError::DynamicProgramError(
|
return Err(CliError::DynamicProgramError(
|
||||||
"Program account is already executable".to_string(),
|
"Program account is already executable".to_string(),
|
||||||
@@ -1182,21 +1265,35 @@ fn process_deploy(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if account.lamports < minimum_balance {
|
if account.lamports < minimum_balance {
|
||||||
|
let balance = minimum_balance - account.lamports;
|
||||||
instructions.push(system_instruction::transfer(
|
instructions.push(system_instruction::transfer(
|
||||||
&config.signers[0].pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
&program_id.pubkey(),
|
&program_id.pubkey(),
|
||||||
minimum_balance - account.lamports,
|
balance,
|
||||||
));
|
));
|
||||||
|
balance_needed = balance;
|
||||||
|
} else if account.lamports > minimum_balance
|
||||||
|
&& system_program::check_id(&account.owner)
|
||||||
|
&& !allow_excessive_balance
|
||||||
|
{
|
||||||
|
return Err(CliError::DynamicProgramError(format!(
|
||||||
|
"Program account has a balance: {:?}; it may already be in use",
|
||||||
|
Sol(account.lamports)
|
||||||
|
))
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
instructions
|
(instructions, balance_needed)
|
||||||
} else {
|
} else {
|
||||||
vec![system_instruction::create_account(
|
(
|
||||||
&config.signers[0].pubkey(),
|
vec![system_instruction::create_account(
|
||||||
&program_id.pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
|
&program_id.pubkey(),
|
||||||
|
minimum_balance,
|
||||||
|
program_data.len() as u64,
|
||||||
|
&loader_id,
|
||||||
|
)],
|
||||||
minimum_balance,
|
minimum_balance,
|
||||||
program_data.len() as u64,
|
)
|
||||||
&loader_id,
|
|
||||||
)]
|
|
||||||
};
|
};
|
||||||
let initial_message = if !initial_instructions.is_empty() {
|
let initial_message = if !initial_instructions.is_empty() {
|
||||||
Some(Message::new(
|
Some(Message::new(
|
||||||
@@ -1239,9 +1336,10 @@ fn process_deploy(
|
|||||||
.get_recent_blockhash_with_commitment(config.commitment)?
|
.get_recent_blockhash_with_commitment(config.commitment)?
|
||||||
.value;
|
.value;
|
||||||
|
|
||||||
check_account_for_multiple_fees_with_commitment(
|
check_account_for_spend_multiple_fees_with_commitment(
|
||||||
rpc_client,
|
rpc_client,
|
||||||
&config.signers[0].pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
|
balance_needed,
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
&messages,
|
&messages,
|
||||||
config.commitment,
|
config.commitment,
|
||||||
@@ -1266,8 +1364,8 @@ fn process_deploy(
|
|||||||
config.commitment,
|
config.commitment,
|
||||||
config.send_transaction_config,
|
config.send_transaction_config,
|
||||||
);
|
);
|
||||||
log_instruction_custom_error::<SystemError>(result, &config).map_err(|_| {
|
log_instruction_custom_error::<SystemError>(result, &config).map_err(|err| {
|
||||||
CliError::DynamicProgramError("Program account allocation failed".to_string())
|
CliError::DynamicProgramError(format!("Program account allocation failed: {}", err))
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1290,8 +1388,8 @@ fn process_deploy(
|
|||||||
config.commitment,
|
config.commitment,
|
||||||
last_valid_slot,
|
last_valid_slot,
|
||||||
)
|
)
|
||||||
.map_err(|_| {
|
.map_err(|err| {
|
||||||
CliError::DynamicProgramError("Data writes to program account failed".to_string())
|
CliError::DynamicProgramError(format!("Data writes to program account failed: {}", err))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let (blockhash, _, _) = rpc_client
|
let (blockhash, _, _) = rpc_client
|
||||||
@@ -1453,13 +1551,25 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
|||||||
process_inflation_subcommand(&rpc_client, config, inflation_subcommand)
|
process_inflation_subcommand(&rpc_client, config, inflation_subcommand)
|
||||||
}
|
}
|
||||||
CliCommand::LeaderSchedule => process_leader_schedule(&rpc_client),
|
CliCommand::LeaderSchedule => process_leader_schedule(&rpc_client),
|
||||||
CliCommand::LiveSlots => process_live_slots(&config.websocket_url),
|
CliCommand::LiveSlots => process_live_slots(&config),
|
||||||
|
CliCommand::Logs { filter } => process_logs(&config, filter),
|
||||||
CliCommand::Ping {
|
CliCommand::Ping {
|
||||||
lamports,
|
lamports,
|
||||||
interval,
|
interval,
|
||||||
count,
|
count,
|
||||||
timeout,
|
timeout,
|
||||||
} => process_ping(&rpc_client, config, *lamports, interval, count, timeout),
|
blockhash,
|
||||||
|
print_timestamp,
|
||||||
|
} => process_ping(
|
||||||
|
&rpc_client,
|
||||||
|
config,
|
||||||
|
*lamports,
|
||||||
|
interval,
|
||||||
|
count,
|
||||||
|
timeout,
|
||||||
|
blockhash,
|
||||||
|
*print_timestamp,
|
||||||
|
),
|
||||||
CliCommand::ShowBlockProduction { epoch, slot_limit } => {
|
CliCommand::ShowBlockProduction { epoch, slot_limit } => {
|
||||||
process_show_block_production(&rpc_client, config, *epoch, *slot_limit)
|
process_show_block_production(&rpc_client, config, *epoch, *slot_limit)
|
||||||
}
|
}
|
||||||
@@ -1565,12 +1675,14 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
|||||||
program_location,
|
program_location,
|
||||||
address,
|
address,
|
||||||
use_deprecated_loader,
|
use_deprecated_loader,
|
||||||
|
allow_excessive_balance,
|
||||||
} => process_deploy(
|
} => process_deploy(
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
config,
|
config,
|
||||||
program_location,
|
program_location,
|
||||||
*address,
|
*address,
|
||||||
*use_deprecated_loader,
|
*use_deprecated_loader,
|
||||||
|
*allow_excessive_balance,
|
||||||
),
|
),
|
||||||
|
|
||||||
// Stake Commands
|
// Stake Commands
|
||||||
@@ -2187,7 +2299,7 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
|
|||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("address_signer")
|
Arg::with_name("address_signer")
|
||||||
.index(2)
|
.index(2)
|
||||||
.value_name("SIGNER_KEYPAIR")
|
.value_name("PROGRAM_ADDRESS_SIGNER")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.validator(is_valid_signer)
|
.validator(is_valid_signer)
|
||||||
.help("The signer for the desired address of the program [default: new random address]")
|
.help("The signer for the desired address of the program [default: new random address]")
|
||||||
@@ -2199,6 +2311,12 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
|
|||||||
.hidden(true) // Don't document this argument to discourage its use
|
.hidden(true) // Don't document this argument to discourage its use
|
||||||
.help("Use the deprecated BPF loader")
|
.help("Use the deprecated BPF loader")
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("allow_excessive_balance")
|
||||||
|
.long("allow-excessive-deploy-account-balance")
|
||||||
|
.takes_value(false)
|
||||||
|
.help("Use the designated program id, even if the account already holds a large balance of SOL")
|
||||||
|
)
|
||||||
.arg(commitment_arg_with_default("max")),
|
.arg(commitment_arg_with_default("max")),
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
@@ -2355,7 +2473,10 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(signer_info.signers.len(), 1);
|
assert_eq!(signer_info.signers.len(), 1);
|
||||||
assert_eq!(signer_info.index_of(None), Some(0));
|
assert_eq!(signer_info.index_of(None), Some(0));
|
||||||
assert_eq!(signer_info.index_of(Some(Pubkey::new_rand())), None);
|
assert_eq!(
|
||||||
|
signer_info.index_of(Some(solana_sdk::pubkey::new_rand())),
|
||||||
|
None
|
||||||
|
);
|
||||||
|
|
||||||
let keypair0 = keypair_from_seed(&[1u8; 32]).unwrap();
|
let keypair0 = keypair_from_seed(&[1u8; 32]).unwrap();
|
||||||
let keypair0_pubkey = keypair0.pubkey();
|
let keypair0_pubkey = keypair0.pubkey();
|
||||||
@@ -2411,7 +2532,7 @@ mod tests {
|
|||||||
fn test_cli_parse_command() {
|
fn test_cli_parse_command() {
|
||||||
let test_commands = app("test", "desc", "version");
|
let test_commands = app("test", "desc", "version");
|
||||||
|
|
||||||
let pubkey = Pubkey::new_rand();
|
let pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let pubkey_string = format!("{}", pubkey);
|
let pubkey_string = format!("{}", pubkey);
|
||||||
|
|
||||||
let default_keypair = Keypair::new();
|
let default_keypair = Keypair::new();
|
||||||
@@ -2507,7 +2628,7 @@ mod tests {
|
|||||||
assert!(parse_command(&test_bad_signature, &default_signer, &mut None).is_err());
|
assert!(parse_command(&test_bad_signature, &default_signer, &mut None).is_err());
|
||||||
|
|
||||||
// Test CreateAddressWithSeed
|
// Test CreateAddressWithSeed
|
||||||
let from_pubkey = Some(Pubkey::new_rand());
|
let from_pubkey = Some(solana_sdk::pubkey::new_rand());
|
||||||
let from_str = from_pubkey.unwrap().to_string();
|
let from_str = from_pubkey.unwrap().to_string();
|
||||||
for (name, program_id) in &[
|
for (name, program_id) in &[
|
||||||
("STAKE", solana_stake_program::id()),
|
("STAKE", solana_stake_program::id()),
|
||||||
@@ -2564,6 +2685,7 @@ mod tests {
|
|||||||
program_location: "/Users/test/program.o".to_string(),
|
program_location: "/Users/test/program.o".to_string(),
|
||||||
address: None,
|
address: None,
|
||||||
use_deprecated_loader: false,
|
use_deprecated_loader: false,
|
||||||
|
allow_excessive_balance: false,
|
||||||
},
|
},
|
||||||
signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
|
signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
|
||||||
}
|
}
|
||||||
@@ -2585,6 +2707,7 @@ mod tests {
|
|||||||
program_location: "/Users/test/program.o".to_string(),
|
program_location: "/Users/test/program.o".to_string(),
|
||||||
address: Some(1),
|
address: Some(1),
|
||||||
use_deprecated_loader: false,
|
use_deprecated_loader: false,
|
||||||
|
allow_excessive_balance: false,
|
||||||
},
|
},
|
||||||
signers: vec![
|
signers: vec![
|
||||||
read_keypair_file(&keypair_file).unwrap().into(),
|
read_keypair_file(&keypair_file).unwrap().into(),
|
||||||
@@ -2664,7 +2787,7 @@ mod tests {
|
|||||||
let result = process_command(&config);
|
let result = process_command(&config);
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
|
|
||||||
let new_authorized_pubkey = Pubkey::new_rand();
|
let new_authorized_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
config.signers = vec![&bob_keypair];
|
config.signers = vec![&bob_keypair];
|
||||||
config.command = CliCommand::VoteAuthorize {
|
config.command = CliCommand::VoteAuthorize {
|
||||||
vote_account_pubkey: bob_pubkey,
|
vote_account_pubkey: bob_pubkey,
|
||||||
@@ -2686,7 +2809,7 @@ mod tests {
|
|||||||
|
|
||||||
let bob_keypair = Keypair::new();
|
let bob_keypair = Keypair::new();
|
||||||
let bob_pubkey = bob_keypair.pubkey();
|
let bob_pubkey = bob_keypair.pubkey();
|
||||||
let custodian = Pubkey::new_rand();
|
let custodian = solana_sdk::pubkey::new_rand();
|
||||||
config.command = CliCommand::CreateStakeAccount {
|
config.command = CliCommand::CreateStakeAccount {
|
||||||
stake_account: 1,
|
stake_account: 1,
|
||||||
seed: None,
|
seed: None,
|
||||||
@@ -2709,8 +2832,8 @@ mod tests {
|
|||||||
let result = process_command(&config);
|
let result = process_command(&config);
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
|
|
||||||
let stake_account_pubkey = Pubkey::new_rand();
|
let stake_account_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let to_pubkey = Pubkey::new_rand();
|
let to_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
config.command = CliCommand::WithdrawStake {
|
config.command = CliCommand::WithdrawStake {
|
||||||
stake_account_pubkey,
|
stake_account_pubkey,
|
||||||
destination_account_pubkey: to_pubkey,
|
destination_account_pubkey: to_pubkey,
|
||||||
@@ -2727,7 +2850,7 @@ mod tests {
|
|||||||
let result = process_command(&config);
|
let result = process_command(&config);
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
|
|
||||||
let stake_account_pubkey = Pubkey::new_rand();
|
let stake_account_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
config.command = CliCommand::DeactivateStake {
|
config.command = CliCommand::DeactivateStake {
|
||||||
stake_account_pubkey,
|
stake_account_pubkey,
|
||||||
stake_authority: 0,
|
stake_authority: 0,
|
||||||
@@ -2740,7 +2863,7 @@ mod tests {
|
|||||||
let result = process_command(&config);
|
let result = process_command(&config);
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
|
|
||||||
let stake_account_pubkey = Pubkey::new_rand();
|
let stake_account_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let split_stake_account = Keypair::new();
|
let split_stake_account = Keypair::new();
|
||||||
config.command = CliCommand::SplitStake {
|
config.command = CliCommand::SplitStake {
|
||||||
stake_account_pubkey,
|
stake_account_pubkey,
|
||||||
@@ -2758,8 +2881,8 @@ mod tests {
|
|||||||
let result = process_command(&config);
|
let result = process_command(&config);
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
|
|
||||||
let stake_account_pubkey = Pubkey::new_rand();
|
let stake_account_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let source_stake_account_pubkey = Pubkey::new_rand();
|
let source_stake_account_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let merge_stake_account = Keypair::new();
|
let merge_stake_account = Keypair::new();
|
||||||
config.command = CliCommand::MergeStake {
|
config.command = CliCommand::MergeStake {
|
||||||
stake_account_pubkey,
|
stake_account_pubkey,
|
||||||
@@ -2782,7 +2905,7 @@ mod tests {
|
|||||||
assert_eq!(process_command(&config).unwrap(), "1234");
|
assert_eq!(process_command(&config).unwrap(), "1234");
|
||||||
|
|
||||||
// CreateAddressWithSeed
|
// CreateAddressWithSeed
|
||||||
let from_pubkey = Pubkey::new_rand();
|
let from_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
config.signers = vec![];
|
config.signers = vec![];
|
||||||
config.command = CliCommand::CreateAddressWithSeed {
|
config.command = CliCommand::CreateAddressWithSeed {
|
||||||
from_pubkey: Some(from_pubkey),
|
from_pubkey: Some(from_pubkey),
|
||||||
@@ -2795,7 +2918,7 @@ mod tests {
|
|||||||
assert_eq!(address.unwrap(), expected_address.to_string());
|
assert_eq!(address.unwrap(), expected_address.to_string());
|
||||||
|
|
||||||
// Need airdrop cases
|
// Need airdrop cases
|
||||||
let to = Pubkey::new_rand();
|
let to = solana_sdk::pubkey::new_rand();
|
||||||
config.signers = vec![&keypair];
|
config.signers = vec![&keypair];
|
||||||
config.command = CliCommand::Airdrop {
|
config.command = CliCommand::Airdrop {
|
||||||
faucet_host: None,
|
faucet_host: None,
|
||||||
@@ -2898,6 +3021,7 @@ mod tests {
|
|||||||
program_location: pathbuf.to_str().unwrap().to_string(),
|
program_location: pathbuf.to_str().unwrap().to_string(),
|
||||||
address: None,
|
address: None,
|
||||||
use_deprecated_loader: false,
|
use_deprecated_loader: false,
|
||||||
|
allow_excessive_balance: false,
|
||||||
};
|
};
|
||||||
let result = process_command(&config);
|
let result = process_command(&config);
|
||||||
let json: Value = serde_json::from_str(&result.unwrap()).unwrap();
|
let json: Value = serde_json::from_str(&result.unwrap()).unwrap();
|
||||||
@@ -2916,6 +3040,7 @@ mod tests {
|
|||||||
program_location: "bad/file/location.so".to_string(),
|
program_location: "bad/file/location.so".to_string(),
|
||||||
address: None,
|
address: None,
|
||||||
use_deprecated_loader: false,
|
use_deprecated_loader: false,
|
||||||
|
allow_excessive_balance: false,
|
||||||
};
|
};
|
||||||
assert!(process_command(&config).is_err());
|
assert!(process_command(&config).is_err());
|
||||||
}
|
}
|
||||||
|
@@ -1,12 +1,17 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult},
|
cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult},
|
||||||
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
|
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
|
||||||
|
stake::is_stake_program_v2_enabled,
|
||||||
};
|
};
|
||||||
use chrono::{Local, TimeZone};
|
use chrono::{Local, TimeZone};
|
||||||
use clap::{value_t, value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand};
|
use clap::{value_t, value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand};
|
||||||
use console::{style, Emoji};
|
use console::{style, Emoji};
|
||||||
use solana_clap_utils::{
|
use solana_clap_utils::{
|
||||||
commitment::commitment_arg, input_parsers::*, input_validators::*, keypair::DefaultSigner,
|
commitment::{commitment_arg, commitment_arg_with_default},
|
||||||
|
input_parsers::*,
|
||||||
|
input_validators::*,
|
||||||
|
keypair::DefaultSigner,
|
||||||
|
offline::{blockhash_arg, BLOCKHASH_ARG},
|
||||||
};
|
};
|
||||||
use solana_cli_output::{
|
use solana_cli_output::{
|
||||||
display::{
|
display::{
|
||||||
@@ -20,17 +25,19 @@ use solana_client::{
|
|||||||
rpc_client::{GetConfirmedSignaturesForAddress2Config, RpcClient},
|
rpc_client::{GetConfirmedSignaturesForAddress2Config, RpcClient},
|
||||||
rpc_config::{
|
rpc_config::{
|
||||||
RpcAccountInfoConfig, RpcLargestAccountsConfig, RpcLargestAccountsFilter,
|
RpcAccountInfoConfig, RpcLargestAccountsConfig, RpcLargestAccountsFilter,
|
||||||
RpcProgramAccountsConfig,
|
RpcProgramAccountsConfig, RpcTransactionLogsConfig, RpcTransactionLogsFilter,
|
||||||
},
|
},
|
||||||
rpc_filter,
|
rpc_filter,
|
||||||
rpc_response::SlotInfo,
|
rpc_response::SlotInfo,
|
||||||
};
|
};
|
||||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
|
account::from_account,
|
||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
clock::{self, Clock, Slot},
|
clock::{self, Clock, Slot},
|
||||||
commitment_config::CommitmentConfig,
|
commitment_config::CommitmentConfig,
|
||||||
epoch_schedule::Epoch,
|
epoch_schedule::Epoch,
|
||||||
|
hash::Hash,
|
||||||
message::Message,
|
message::Message,
|
||||||
native_token::lamports_to_sol,
|
native_token::lamports_to_sol,
|
||||||
pubkey::{self, Pubkey},
|
pubkey::{self, Pubkey},
|
||||||
@@ -38,8 +45,7 @@ use solana_sdk::{
|
|||||||
system_instruction, system_program,
|
system_instruction, system_program,
|
||||||
sysvar::{
|
sysvar::{
|
||||||
self,
|
self,
|
||||||
stake_history::{self, StakeHistory},
|
stake_history::{self},
|
||||||
Sysvar,
|
|
||||||
},
|
},
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
};
|
};
|
||||||
@@ -52,7 +58,7 @@ use std::{
|
|||||||
Arc,
|
Arc,
|
||||||
},
|
},
|
||||||
thread::sleep,
|
thread::sleep,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant, SystemTime, UNIX_EPOCH},
|
||||||
};
|
};
|
||||||
|
|
||||||
static CHECK_MARK: Emoji = Emoji("✅ ", "");
|
static CHECK_MARK: Emoji = Emoji("✅ ", "");
|
||||||
@@ -73,8 +79,7 @@ impl ClusterQuerySubCommands for App<'_, '_> {
|
|||||||
.validator(is_slot)
|
.validator(is_slot)
|
||||||
.value_name("SLOT")
|
.value_name("SLOT")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.index(1)
|
.index(1),
|
||||||
.required(true),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
@@ -209,6 +214,13 @@ impl ClusterQuerySubCommands for App<'_, '_> {
|
|||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.help("Stop after submitting count transactions"),
|
.help("Stop after submitting count transactions"),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("print_timestamp")
|
||||||
|
.short("D")
|
||||||
|
.long("print-timestamp")
|
||||||
|
.takes_value(false)
|
||||||
|
.help("Print timestamp (unix time + microseconds as in gettimeofday) before each line"),
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("lamports")
|
Arg::with_name("lamports")
|
||||||
.long("lamports")
|
.long("lamports")
|
||||||
@@ -227,12 +239,33 @@ impl ClusterQuerySubCommands for App<'_, '_> {
|
|||||||
.default_value("15")
|
.default_value("15")
|
||||||
.help("Wait up to timeout seconds for transaction confirmation"),
|
.help("Wait up to timeout seconds for transaction confirmation"),
|
||||||
)
|
)
|
||||||
|
.arg(blockhash_arg())
|
||||||
.arg(commitment_arg()),
|
.arg(commitment_arg()),
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("live-slots")
|
SubCommand::with_name("live-slots")
|
||||||
.about("Show information about the current slot progression"),
|
.about("Show information about the current slot progression"),
|
||||||
)
|
)
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("logs")
|
||||||
|
.about("Stream transaction logs")
|
||||||
|
.arg(
|
||||||
|
pubkey!(Arg::with_name("address")
|
||||||
|
.index(1)
|
||||||
|
.value_name("ADDRESS"),
|
||||||
|
"Account address to monitor \
|
||||||
|
[default: monitor all transactions except for votes] \
|
||||||
|
")
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("include_votes")
|
||||||
|
.long("include-votes")
|
||||||
|
.takes_value(false)
|
||||||
|
.conflicts_with("address")
|
||||||
|
.help("Include vote transactions when monitoring all transactions")
|
||||||
|
)
|
||||||
|
.arg(commitment_arg_with_default("singleGossip")),
|
||||||
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("block-production")
|
SubCommand::with_name("block-production")
|
||||||
.about("Show information about block production")
|
.about("Show information about block production")
|
||||||
@@ -351,19 +384,23 @@ pub fn parse_cluster_ping(
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
let timeout = Duration::from_secs(value_t_or_exit!(matches, "timeout", u64));
|
let timeout = Duration::from_secs(value_t_or_exit!(matches, "timeout", u64));
|
||||||
|
let blockhash = value_of(matches, BLOCKHASH_ARG.name);
|
||||||
|
let print_timestamp = matches.is_present("print_timestamp");
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::Ping {
|
command: CliCommand::Ping {
|
||||||
lamports,
|
lamports,
|
||||||
interval,
|
interval,
|
||||||
count,
|
count,
|
||||||
timeout,
|
timeout,
|
||||||
|
blockhash,
|
||||||
|
print_timestamp,
|
||||||
},
|
},
|
||||||
signers: vec![default_signer.signer_from_path(matches, wallet_manager)?],
|
signers: vec![default_signer.signer_from_path(matches, wallet_manager)?],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_get_block(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
pub fn parse_get_block(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||||
let slot = value_t_or_exit!(matches, "slot", Slot);
|
let slot = value_of(matches, "slot");
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::GetBlock { slot },
|
command: CliCommand::GetBlock { slot },
|
||||||
signers: vec![],
|
signers: vec![],
|
||||||
@@ -625,7 +662,7 @@ pub fn process_cluster_date(rpc_client: &RpcClient, config: &CliConfig) -> Proce
|
|||||||
let result = rpc_client
|
let result = rpc_client
|
||||||
.get_account_with_commitment(&sysvar::clock::id(), CommitmentConfig::default())?;
|
.get_account_with_commitment(&sysvar::clock::id(), CommitmentConfig::default())?;
|
||||||
if let Some(clock_account) = result.value {
|
if let Some(clock_account) = result.value {
|
||||||
let clock: Clock = Sysvar::from_account(&clock_account).ok_or_else(|| {
|
let clock: Clock = from_account(&clock_account).ok_or_else(|| {
|
||||||
CliError::RpcRequestError("Failed to deserialize clock sysvar".to_string())
|
CliError::RpcRequestError("Failed to deserialize clock sysvar".to_string())
|
||||||
})?;
|
})?;
|
||||||
let block_time = CliBlockTime {
|
let block_time = CliBlockTime {
|
||||||
@@ -700,7 +737,17 @@ pub fn process_leader_schedule(rpc_client: &RpcClient) -> ProcessResult {
|
|||||||
Ok("".to_string())
|
Ok("".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_get_block(rpc_client: &RpcClient, _config: &CliConfig, slot: Slot) -> ProcessResult {
|
pub fn process_get_block(
|
||||||
|
rpc_client: &RpcClient,
|
||||||
|
_config: &CliConfig,
|
||||||
|
slot: Option<Slot>,
|
||||||
|
) -> ProcessResult {
|
||||||
|
let slot = if let Some(slot) = slot {
|
||||||
|
slot
|
||||||
|
} else {
|
||||||
|
rpc_client.get_slot()?
|
||||||
|
};
|
||||||
|
|
||||||
let mut block =
|
let mut block =
|
||||||
rpc_client.get_confirmed_block_with_encoding(slot, UiTransactionEncoding::Base64)?;
|
rpc_client.get_confirmed_block_with_encoding(slot, UiTransactionEncoding::Base64)?;
|
||||||
|
|
||||||
@@ -716,18 +763,23 @@ pub fn process_get_block(rpc_client: &RpcClient, _config: &CliConfig, slot: Slot
|
|||||||
let mut total_rewards = 0;
|
let mut total_rewards = 0;
|
||||||
println!("Rewards:",);
|
println!("Rewards:",);
|
||||||
println!(
|
println!(
|
||||||
" {:<44} {:<15} {:<13} {:>14}",
|
" {:<44} {:^15} {:<15} {:<20} {:>14}",
|
||||||
"Address", "Amount", "New Balance", "Percent Change"
|
"Address", "Type", "Amount", "New Balance", "Percent Change"
|
||||||
);
|
);
|
||||||
for reward in block.rewards {
|
for reward in block.rewards {
|
||||||
let sign = if reward.lamports < 0 { "-" } else { "" };
|
let sign = if reward.lamports < 0 { "-" } else { "" };
|
||||||
|
|
||||||
total_rewards += reward.lamports;
|
total_rewards += reward.lamports;
|
||||||
println!(
|
println!(
|
||||||
" {:<44} {:>15} {}",
|
" {:<44} {:^15} {:>15} {}",
|
||||||
reward.pubkey,
|
reward.pubkey,
|
||||||
|
if let Some(reward_type) = reward.reward_type {
|
||||||
|
format!("{}", reward_type)
|
||||||
|
} else {
|
||||||
|
"-".to_string()
|
||||||
|
},
|
||||||
format!(
|
format!(
|
||||||
"{}◎{:<14.4}",
|
"{}◎{:<14.9}",
|
||||||
sign,
|
sign,
|
||||||
lamports_to_sol(reward.lamports.abs() as u64)
|
lamports_to_sol(reward.lamports.abs() as u64)
|
||||||
),
|
),
|
||||||
@@ -735,7 +787,7 @@ pub fn process_get_block(rpc_client: &RpcClient, _config: &CliConfig, slot: Slot
|
|||||||
" - -".to_string()
|
" - -".to_string()
|
||||||
} else {
|
} else {
|
||||||
format!(
|
format!(
|
||||||
"◎{:<12.4} {:>13.4}%",
|
"◎{:<19.9} {:>13.9}%",
|
||||||
lamports_to_sol(reward.post_balance),
|
lamports_to_sol(reward.post_balance),
|
||||||
reward.lamports.abs() as f64
|
reward.lamports.abs() as f64
|
||||||
/ (reward.post_balance as f64 - reward.lamports as f64)
|
/ (reward.post_balance as f64 - reward.lamports as f64)
|
||||||
@@ -746,7 +798,7 @@ pub fn process_get_block(rpc_client: &RpcClient, _config: &CliConfig, slot: Slot
|
|||||||
|
|
||||||
let sign = if total_rewards < 0 { "-" } else { "" };
|
let sign = if total_rewards < 0 { "-" } else { "" };
|
||||||
println!(
|
println!(
|
||||||
"Total Rewards: {}◎{:12.9}",
|
"Total Rewards: {}◎{:<12.9}",
|
||||||
sign,
|
sign,
|
||||||
lamports_to_sol(total_rewards.abs() as u64)
|
lamports_to_sol(total_rewards.abs() as u64)
|
||||||
);
|
);
|
||||||
@@ -1011,6 +1063,8 @@ pub fn process_ping(
|
|||||||
interval: &Duration,
|
interval: &Duration,
|
||||||
count: &Option<u64>,
|
count: &Option<u64>,
|
||||||
timeout: &Duration,
|
timeout: &Duration,
|
||||||
|
fixed_blockhash: &Option<Hash>,
|
||||||
|
print_timestamp: bool,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
println_name_value("Source Account:", &config.signers[0].pubkey().to_string());
|
println_name_value("Source Account:", &config.signers[0].pubkey().to_string());
|
||||||
println!();
|
println!();
|
||||||
@@ -1028,9 +1082,21 @@ pub fn process_ping(
|
|||||||
let (mut blockhash, mut fee_calculator) = rpc_client.get_recent_blockhash()?;
|
let (mut blockhash, mut fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||||
let mut blockhash_transaction_count = 0;
|
let mut blockhash_transaction_count = 0;
|
||||||
let mut blockhash_acquired = Instant::now();
|
let mut blockhash_acquired = Instant::now();
|
||||||
|
if let Some(fixed_blockhash) = fixed_blockhash {
|
||||||
|
let blockhash_origin = if *fixed_blockhash != Hash::default() {
|
||||||
|
blockhash = *fixed_blockhash;
|
||||||
|
"supplied from cli arguments"
|
||||||
|
} else {
|
||||||
|
"fetched from cluster"
|
||||||
|
};
|
||||||
|
println!(
|
||||||
|
"Fixed blockhash is used: {} ({})",
|
||||||
|
blockhash, blockhash_origin
|
||||||
|
);
|
||||||
|
}
|
||||||
'mainloop: for seq in 0..count.unwrap_or(std::u64::MAX) {
|
'mainloop: for seq in 0..count.unwrap_or(std::u64::MAX) {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
if now.duration_since(blockhash_acquired).as_secs() > 60 {
|
if fixed_blockhash.is_none() && now.duration_since(blockhash_acquired).as_secs() > 60 {
|
||||||
// Fetch a new blockhash every minute
|
// Fetch a new blockhash every minute
|
||||||
let (new_blockhash, new_fee_calculator) = rpc_client.get_new_blockhash(&blockhash)?;
|
let (new_blockhash, new_fee_calculator) = rpc_client.get_new_blockhash(&blockhash)?;
|
||||||
blockhash = new_blockhash;
|
blockhash = new_blockhash;
|
||||||
@@ -1061,6 +1127,18 @@ pub fn process_ping(
|
|||||||
let mut tx = Transaction::new_unsigned(message);
|
let mut tx = Transaction::new_unsigned(message);
|
||||||
tx.try_sign(&config.signers, blockhash)?;
|
tx.try_sign(&config.signers, blockhash)?;
|
||||||
|
|
||||||
|
let timestamp = || {
|
||||||
|
let micros = SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_micros();
|
||||||
|
if print_timestamp {
|
||||||
|
format!("[{}.{:06}] ", micros / 1_000_000, micros % 1_000_000)
|
||||||
|
} else {
|
||||||
|
format!("")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
match rpc_client.send_transaction(&tx) {
|
match rpc_client.send_transaction(&tx) {
|
||||||
Ok(signature) => {
|
Ok(signature) => {
|
||||||
let transaction_sent = Instant::now();
|
let transaction_sent = Instant::now();
|
||||||
@@ -1074,15 +1152,20 @@ pub fn process_ping(
|
|||||||
let elapsed_time_millis = elapsed_time.as_millis() as u64;
|
let elapsed_time_millis = elapsed_time.as_millis() as u64;
|
||||||
confirmation_time.push_back(elapsed_time_millis);
|
confirmation_time.push_back(elapsed_time_millis);
|
||||||
println!(
|
println!(
|
||||||
"{}{} lamport(s) transferred: seq={:<3} time={:>4}ms signature={}",
|
"{}{}{} lamport(s) transferred: seq={:<3} time={:>4}ms signature={}",
|
||||||
|
timestamp(),
|
||||||
CHECK_MARK, lamports, seq, elapsed_time_millis, signature
|
CHECK_MARK, lamports, seq, elapsed_time_millis, signature
|
||||||
);
|
);
|
||||||
confirmed_count += 1;
|
confirmed_count += 1;
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
println!(
|
println!(
|
||||||
"{}Transaction failed: seq={:<3} error={:?} signature={}",
|
"{}{}Transaction failed: seq={:<3} error={:?} signature={}",
|
||||||
CROSS_MARK, seq, err, signature
|
timestamp(),
|
||||||
|
CROSS_MARK,
|
||||||
|
seq,
|
||||||
|
err,
|
||||||
|
signature
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1091,8 +1174,11 @@ pub fn process_ping(
|
|||||||
|
|
||||||
if elapsed_time >= *timeout {
|
if elapsed_time >= *timeout {
|
||||||
println!(
|
println!(
|
||||||
"{}Confirmation timeout: seq={:<3} signature={}",
|
"{}{}Confirmation timeout: seq={:<3} signature={}",
|
||||||
CROSS_MARK, seq, signature
|
timestamp(),
|
||||||
|
CROSS_MARK,
|
||||||
|
seq,
|
||||||
|
signature
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1110,8 +1196,11 @@ pub fn process_ping(
|
|||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
println!(
|
println!(
|
||||||
"{}Submit failed: seq={:<3} error={:?}",
|
"{}{}Submit failed: seq={:<3} error={:?}",
|
||||||
CROSS_MARK, seq, err
|
timestamp(),
|
||||||
|
CROSS_MARK,
|
||||||
|
seq,
|
||||||
|
err
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1146,24 +1235,83 @@ pub fn process_ping(
|
|||||||
Ok("".to_string())
|
Ok("".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_live_slots(url: &str) -> ProcessResult {
|
pub fn parse_logs(
|
||||||
let exit = Arc::new(AtomicBool::new(false));
|
matches: &ArgMatches<'_>,
|
||||||
|
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||||
|
) -> Result<CliCommandInfo, CliError> {
|
||||||
|
let address = pubkey_of_signer(matches, "address", wallet_manager)?;
|
||||||
|
let include_votes = matches.is_present("include_votes");
|
||||||
|
|
||||||
// Disable Ctrl+C handler as sometimes the PubsubClient shutdown can stall. Also it doesn't
|
let filter = match address {
|
||||||
// really matter that the shutdown is clean because the process is terminating.
|
None => {
|
||||||
/*
|
if include_votes {
|
||||||
let exit_clone = exit.clone();
|
RpcTransactionLogsFilter::AllWithVotes
|
||||||
ctrlc::set_handler(move || {
|
} else {
|
||||||
exit_clone.store(true, Ordering::Relaxed);
|
RpcTransactionLogsFilter::All
|
||||||
})?;
|
}
|
||||||
*/
|
}
|
||||||
|
Some(address) => RpcTransactionLogsFilter::Mentions(vec![address.to_string()]),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(CliCommandInfo {
|
||||||
|
command: CliCommand::Logs { filter },
|
||||||
|
signers: vec![],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_logs(config: &CliConfig, filter: &RpcTransactionLogsFilter) -> ProcessResult {
|
||||||
|
println!(
|
||||||
|
"Streaming transaction logs{}. {:?} commitment",
|
||||||
|
match filter {
|
||||||
|
RpcTransactionLogsFilter::All => "".into(),
|
||||||
|
RpcTransactionLogsFilter::AllWithVotes => " (including votes)".into(),
|
||||||
|
RpcTransactionLogsFilter::Mentions(addresses) =>
|
||||||
|
format!(" mentioning {}", addresses.join(",")),
|
||||||
|
},
|
||||||
|
config.commitment.commitment
|
||||||
|
);
|
||||||
|
|
||||||
|
let (_client, receiver) = PubsubClient::logs_subscribe(
|
||||||
|
&config.websocket_url,
|
||||||
|
filter.clone(),
|
||||||
|
RpcTransactionLogsConfig {
|
||||||
|
commitment: Some(config.commitment),
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match receiver.recv() {
|
||||||
|
Ok(logs) => {
|
||||||
|
println!("Transaction executed in slot {}:", logs.context.slot);
|
||||||
|
println!(" Signature: {}", logs.value.signature);
|
||||||
|
println!(
|
||||||
|
" Status: {}",
|
||||||
|
logs.value
|
||||||
|
.err
|
||||||
|
.map(|err| err.to_string())
|
||||||
|
.unwrap_or_else(|| "Ok".to_string())
|
||||||
|
);
|
||||||
|
println!(" Log Messages:");
|
||||||
|
for log in logs.value.logs {
|
||||||
|
println!(" {}", log);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
return Ok(format!("Disconnected: {}", err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_live_slots(config: &CliConfig) -> ProcessResult {
|
||||||
|
let exit = Arc::new(AtomicBool::new(false));
|
||||||
|
|
||||||
let mut current: Option<SlotInfo> = None;
|
let mut current: Option<SlotInfo> = None;
|
||||||
let mut message = "".to_string();
|
let mut message = "".to_string();
|
||||||
|
|
||||||
let slot_progress = new_spinner_progress_bar();
|
let slot_progress = new_spinner_progress_bar();
|
||||||
slot_progress.set_message("Connecting...");
|
slot_progress.set_message("Connecting...");
|
||||||
let (mut client, receiver) = PubsubClient::slot_subscribe(url)?;
|
let (mut client, receiver) = PubsubClient::slot_subscribe(&config.websocket_url)?;
|
||||||
slot_progress.set_message("Connected.");
|
slot_progress.set_message("Connected.");
|
||||||
|
|
||||||
let spacer = "|";
|
let spacer = "|";
|
||||||
@@ -1253,14 +1401,16 @@ pub fn process_show_gossip(rpc_client: &RpcClient, config: &CliConfig) -> Proces
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|node| {
|
.map(|node| {
|
||||||
format!(
|
format!(
|
||||||
"{:15} | {:44} | {:6} | {:5} | {:5} | {}",
|
"{:15} | {:44} | {:6} | {:5} | {:21} | {}",
|
||||||
node.gossip
|
node.gossip
|
||||||
.map(|addr| addr.ip().to_string())
|
.map(|addr| addr.ip().to_string())
|
||||||
.unwrap_or_else(|| "none".to_string()),
|
.unwrap_or_else(|| "none".to_string()),
|
||||||
format_labeled_address(&node.pubkey, &config.address_labels),
|
format_labeled_address(&node.pubkey, &config.address_labels),
|
||||||
format_port(node.gossip),
|
format_port(node.gossip),
|
||||||
format_port(node.tpu),
|
format_port(node.tpu),
|
||||||
format_port(node.rpc),
|
node.rpc
|
||||||
|
.map(|addr| addr.to_string())
|
||||||
|
.unwrap_or_else(|| "none".to_string()),
|
||||||
node.version.unwrap_or_else(|| "unknown".to_string()),
|
node.version.unwrap_or_else(|| "unknown".to_string()),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -1268,9 +1418,9 @@ pub fn process_show_gossip(rpc_client: &RpcClient, config: &CliConfig) -> Proces
|
|||||||
|
|
||||||
Ok(format!(
|
Ok(format!(
|
||||||
"IP Address | Node identifier \
|
"IP Address | Node identifier \
|
||||||
| Gossip | TPU | RPC | Version\n\
|
| Gossip | TPU | RPC Address | Version\n\
|
||||||
----------------+----------------------------------------------+\
|
----------------+----------------------------------------------+\
|
||||||
--------+-------+-------+----------------\n\
|
--------+-------+-----------------------+----------------\n\
|
||||||
{}\n\
|
{}\n\
|
||||||
Nodes: {}",
|
Nodes: {}",
|
||||||
s.join("\n"),
|
s.join("\n"),
|
||||||
@@ -1325,14 +1475,16 @@ pub fn process_show_stakes(
|
|||||||
.get_program_accounts_with_config(&solana_stake_program::id(), program_accounts_config)?;
|
.get_program_accounts_with_config(&solana_stake_program::id(), program_accounts_config)?;
|
||||||
let stake_history_account = rpc_client.get_account(&stake_history::id())?;
|
let stake_history_account = rpc_client.get_account(&stake_history::id())?;
|
||||||
let clock_account = rpc_client.get_account(&sysvar::clock::id())?;
|
let clock_account = rpc_client.get_account(&sysvar::clock::id())?;
|
||||||
let clock: Clock = Sysvar::from_account(&clock_account).ok_or_else(|| {
|
let clock: Clock = from_account(&clock_account).ok_or_else(|| {
|
||||||
CliError::RpcRequestError("Failed to deserialize clock sysvar".to_string())
|
CliError::RpcRequestError("Failed to deserialize clock sysvar".to_string())
|
||||||
})?;
|
})?;
|
||||||
progress_bar.finish_and_clear();
|
progress_bar.finish_and_clear();
|
||||||
|
|
||||||
let stake_history = StakeHistory::from_account(&stake_history_account).ok_or_else(|| {
|
let stake_history = from_account(&stake_history_account).ok_or_else(|| {
|
||||||
CliError::RpcRequestError("Failed to deserialize stake history".to_string())
|
CliError::RpcRequestError("Failed to deserialize stake history".to_string())
|
||||||
})?;
|
})?;
|
||||||
|
// At v1.6, this check can be removed and simply passed as `true`
|
||||||
|
let stake_program_v2_enabled = is_stake_program_v2_enabled(rpc_client);
|
||||||
|
|
||||||
let mut stake_accounts: Vec<CliKeyedStakeState> = vec![];
|
let mut stake_accounts: Vec<CliKeyedStakeState> = vec![];
|
||||||
for (stake_pubkey, stake_account) in all_stake_accounts {
|
for (stake_pubkey, stake_account) in all_stake_accounts {
|
||||||
@@ -1348,6 +1500,7 @@ pub fn process_show_stakes(
|
|||||||
use_lamports_unit,
|
use_lamports_unit,
|
||||||
&stake_history,
|
&stake_history,
|
||||||
&clock,
|
&clock,
|
||||||
|
stake_program_v2_enabled,
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1366,6 +1519,7 @@ pub fn process_show_stakes(
|
|||||||
use_lamports_unit,
|
use_lamports_unit,
|
||||||
&stake_history,
|
&stake_history,
|
||||||
&clock,
|
&clock,
|
||||||
|
stake_program_v2_enabled,
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1405,12 +1559,12 @@ pub fn process_show_validators(
|
|||||||
.map(|vote_account| vote_account.activated_stake)
|
.map(|vote_account| vote_account.activated_stake)
|
||||||
.sum();
|
.sum();
|
||||||
|
|
||||||
let total_deliquent_stake = vote_accounts
|
let total_delinquent_stake = vote_accounts
|
||||||
.delinquent
|
.delinquent
|
||||||
.iter()
|
.iter()
|
||||||
.map(|vote_account| vote_account.activated_stake)
|
.map(|vote_account| vote_account.activated_stake)
|
||||||
.sum();
|
.sum();
|
||||||
let total_current_stake = total_active_stake - total_deliquent_stake;
|
let total_current_stake = total_active_stake - total_delinquent_stake;
|
||||||
|
|
||||||
let mut current = vote_accounts.current;
|
let mut current = vote_accounts.current;
|
||||||
current.sort_by(|a, b| b.activated_stake.cmp(&a.activated_stake));
|
current.sort_by(|a, b| b.activated_stake.cmp(&a.activated_stake));
|
||||||
@@ -1464,7 +1618,7 @@ pub fn process_show_validators(
|
|||||||
let cli_validators = CliValidators {
|
let cli_validators = CliValidators {
|
||||||
total_active_stake,
|
total_active_stake,
|
||||||
total_current_stake,
|
total_current_stake,
|
||||||
total_deliquent_stake,
|
total_delinquent_stake,
|
||||||
current_validators,
|
current_validators,
|
||||||
delinquent_validators,
|
delinquent_validators,
|
||||||
stake_by_version,
|
stake_by_version,
|
||||||
@@ -1539,6 +1693,7 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::cli::{app, parse_command};
|
use crate::cli::{app, parse_command};
|
||||||
use solana_sdk::signature::{write_keypair, Keypair};
|
use solana_sdk::signature::{write_keypair, Keypair};
|
||||||
|
use std::str::FromStr;
|
||||||
use tempfile::NamedTempFile;
|
use tempfile::NamedTempFile;
|
||||||
|
|
||||||
fn make_tmp_file() -> (String, NamedTempFile) {
|
fn make_tmp_file() -> (String, NamedTempFile) {
|
||||||
@@ -1674,8 +1829,11 @@ mod tests {
|
|||||||
"2",
|
"2",
|
||||||
"-t",
|
"-t",
|
||||||
"3",
|
"3",
|
||||||
|
"-D",
|
||||||
"--commitment",
|
"--commitment",
|
||||||
"max",
|
"max",
|
||||||
|
"--blockhash",
|
||||||
|
"4CCNp28j6AhGq7PkjPDP4wbQWBS8LLbQin2xV5n8frKX",
|
||||||
]);
|
]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_ping, &default_signer, &mut None).unwrap(),
|
parse_command(&test_ping, &default_signer, &mut None).unwrap(),
|
||||||
@@ -1685,6 +1843,10 @@ mod tests {
|
|||||||
interval: Duration::from_secs(1),
|
interval: Duration::from_secs(1),
|
||||||
count: Some(2),
|
count: Some(2),
|
||||||
timeout: Duration::from_secs(3),
|
timeout: Duration::from_secs(3),
|
||||||
|
blockhash: Some(
|
||||||
|
Hash::from_str("4CCNp28j6AhGq7PkjPDP4wbQWBS8LLbQin2xV5n8frKX").unwrap()
|
||||||
|
),
|
||||||
|
print_timestamp: true,
|
||||||
},
|
},
|
||||||
signers: vec![default_keypair.into()],
|
signers: vec![default_keypair.into()],
|
||||||
}
|
}
|
||||||
|
@@ -9,12 +9,13 @@ use solana_clap_utils::{input_parsers::*, input_validators::*, keypair::*};
|
|||||||
use solana_cli_output::{QuietDisplay, VerboseDisplay};
|
use solana_cli_output::{QuietDisplay, VerboseDisplay};
|
||||||
use solana_client::{client_error::ClientError, rpc_client::RpcClient};
|
use solana_client::{client_error::ClientError, rpc_client::RpcClient};
|
||||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||||
use solana_runtime::{
|
use solana_sdk::{
|
||||||
|
clock::Slot,
|
||||||
feature::{self, Feature},
|
feature::{self, Feature},
|
||||||
feature_set::FEATURE_NAMES,
|
feature_set::FEATURE_NAMES,
|
||||||
};
|
message::Message,
|
||||||
use solana_sdk::{
|
pubkey::Pubkey,
|
||||||
clock::Slot, message::Message, pubkey::Pubkey, system_instruction, transaction::Transaction,
|
transaction::Transaction,
|
||||||
};
|
};
|
||||||
use std::{collections::HashMap, fmt, sync::Arc};
|
use std::{collections::HashMap, fmt, sync::Arc};
|
||||||
|
|
||||||
@@ -230,7 +231,7 @@ fn active_stake_by_feature_set(rpc_client: &RpcClient) -> Result<HashMap<u32, u6
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Feature activation is only allowed when 95% of the active stake is on the current feature set
|
// Feature activation is only allowed when 95% of the active stake is on the current feature set
|
||||||
fn feature_activation_allowed(rpc_client: &RpcClient) -> Result<bool, ClientError> {
|
fn feature_activation_allowed(rpc_client: &RpcClient, quiet: bool) -> Result<bool, ClientError> {
|
||||||
let my_feature_set = solana_version::Version::default().feature_set;
|
let my_feature_set = solana_version::Version::default().feature_set;
|
||||||
|
|
||||||
let active_stake_by_feature_set = active_stake_by_feature_set(rpc_client)?;
|
let active_stake_by_feature_set = active_stake_by_feature_set(rpc_client)?;
|
||||||
@@ -240,8 +241,23 @@ fn feature_activation_allowed(rpc_client: &RpcClient) -> Result<bool, ClientErro
|
|||||||
.map(|percentage| *percentage >= 95)
|
.map(|percentage| *percentage >= 95)
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
|
|
||||||
if !feature_activation_allowed {
|
if !feature_activation_allowed && !quiet {
|
||||||
println!("\n{}", style("Stake By Feature Set:").bold());
|
if active_stake_by_feature_set.get(&my_feature_set).is_none() {
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
style("To activate features the tool and cluster feature sets must match, select a tool version that matches the cluster")
|
||||||
|
.bold());
|
||||||
|
} else {
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
style("To activate features the stake must be >= 95%").bold()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
style(format!("Tool Feture Set: {}", my_feature_set)).bold()
|
||||||
|
);
|
||||||
|
println!("{}", style("Cluster Feature Sets and Stakes:").bold());
|
||||||
for (feature_set, percentage) in active_stake_by_feature_set.iter() {
|
for (feature_set, percentage) in active_stake_by_feature_set.iter() {
|
||||||
if *feature_set == 0 {
|
if *feature_set == 0 {
|
||||||
println!("unknown - {}%", percentage);
|
println!("unknown - {}%", percentage);
|
||||||
@@ -258,6 +274,7 @@ fn feature_activation_allowed(rpc_client: &RpcClient) -> Result<bool, ClientErro
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
println!();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(feature_activation_allowed)
|
Ok(feature_activation_allowed)
|
||||||
@@ -278,7 +295,7 @@ fn process_status(
|
|||||||
let feature_id = &feature_ids[i];
|
let feature_id = &feature_ids[i];
|
||||||
let feature_name = FEATURE_NAMES.get(feature_id).unwrap();
|
let feature_name = FEATURE_NAMES.get(feature_id).unwrap();
|
||||||
if let Some(account) = account {
|
if let Some(account) = account {
|
||||||
if let Some(feature) = Feature::from_account(&account) {
|
if let Some(feature) = feature::from_account(&account) {
|
||||||
let feature_status = match feature.activated_at {
|
let feature_status = match feature.activated_at {
|
||||||
None => CliFeatureStatus::Pending,
|
None => CliFeatureStatus::Pending,
|
||||||
Some(activation_slot) => CliFeatureStatus::Active(activation_slot),
|
Some(activation_slot) => CliFeatureStatus::Active(activation_slot),
|
||||||
@@ -299,9 +316,10 @@ fn process_status(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let feature_activation_allowed = feature_activation_allowed(rpc_client, features.len() <= 1)?;
|
||||||
let feature_set = CliFeatures {
|
let feature_set = CliFeatures {
|
||||||
features,
|
features,
|
||||||
feature_activation_allowed: feature_activation_allowed(rpc_client)?,
|
feature_activation_allowed,
|
||||||
inactive,
|
inactive,
|
||||||
};
|
};
|
||||||
Ok(config.output_format.formatted_string(&feature_set))
|
Ok(config.output_format.formatted_string(&feature_set))
|
||||||
@@ -318,12 +336,12 @@ fn process_activate(
|
|||||||
.next()
|
.next()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
if let Some(account) = account {
|
if let Some(account) = account {
|
||||||
if Feature::from_account(&account).is_some() {
|
if feature::from_account(&account).is_some() {
|
||||||
return Err(format!("{} has already been activated", feature_id).into());
|
return Err(format!("{} has already been activated", feature_id).into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !feature_activation_allowed(rpc_client)? {
|
if !feature_activation_allowed(rpc_client, false)? {
|
||||||
return Err("Feature activation is not allowed at this time".into());
|
return Err("Feature activation is not allowed at this time".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,15 +356,11 @@ fn process_activate(
|
|||||||
&config.signers[0].pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
|lamports| {
|
|lamports| {
|
||||||
Message::new(
|
Message::new(
|
||||||
&[
|
&feature::activate_with_lamports(
|
||||||
system_instruction::transfer(
|
&feature_id,
|
||||||
&config.signers[0].pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
&feature_id,
|
lamports,
|
||||||
lamports,
|
),
|
||||||
),
|
|
||||||
system_instruction::allocate(&feature_id, Feature::size_of() as u64),
|
|
||||||
system_instruction::assign(&feature_id, &feature::id()),
|
|
||||||
],
|
|
||||||
Some(&config.signers[0].pubkey()),
|
Some(&config.signers[0].pubkey()),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@@ -26,6 +26,7 @@ pub mod cluster_query;
|
|||||||
pub mod feature;
|
pub mod feature;
|
||||||
pub mod inflation;
|
pub mod inflation;
|
||||||
pub mod nonce;
|
pub mod nonce;
|
||||||
|
pub mod send_tpu;
|
||||||
pub mod spend_utils;
|
pub mod spend_utils;
|
||||||
pub mod stake;
|
pub mod stake;
|
||||||
pub mod test_utils;
|
pub mod test_utils;
|
||||||
|
@@ -168,6 +168,7 @@ pub fn parse_args<'a>(
|
|||||||
let CliCommandInfo { command, signers } =
|
let CliCommandInfo { command, signers } =
|
||||||
parse_command(&matches, &default_signer, &mut wallet_manager)?;
|
parse_command(&matches, &default_signer, &mut wallet_manager)?;
|
||||||
|
|
||||||
|
let verbose = matches.is_present("verbose");
|
||||||
let output_format = matches
|
let output_format = matches
|
||||||
.value_of("output_format")
|
.value_of("output_format")
|
||||||
.map(|value| match value {
|
.map(|value| match value {
|
||||||
@@ -175,7 +176,11 @@ pub fn parse_args<'a>(
|
|||||||
"json-compact" => OutputFormat::JsonCompact,
|
"json-compact" => OutputFormat::JsonCompact,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
})
|
})
|
||||||
.unwrap_or(OutputFormat::Display);
|
.unwrap_or(if verbose {
|
||||||
|
OutputFormat::DisplayVerbose
|
||||||
|
} else {
|
||||||
|
OutputFormat::Display
|
||||||
|
});
|
||||||
|
|
||||||
let commitment = matches
|
let commitment = matches
|
||||||
.subcommand_name()
|
.subcommand_name()
|
||||||
@@ -198,7 +203,7 @@ pub fn parse_args<'a>(
|
|||||||
keypair_path: default_signer_path,
|
keypair_path: default_signer_path,
|
||||||
rpc_client: None,
|
rpc_client: None,
|
||||||
rpc_timeout,
|
rpc_timeout,
|
||||||
verbose: matches.is_present("verbose"),
|
verbose,
|
||||||
output_format,
|
output_format,
|
||||||
commitment,
|
commitment,
|
||||||
send_transaction_config: RpcSendTransactionConfig::default(),
|
send_transaction_config: RpcSendTransactionConfig::default(),
|
||||||
|
@@ -580,6 +580,7 @@ mod tests {
|
|||||||
fee_calculator::FeeCalculator,
|
fee_calculator::FeeCalculator,
|
||||||
hash::hash,
|
hash::hash,
|
||||||
nonce::{self, state::Versions, State},
|
nonce::{self, state::Versions, State},
|
||||||
|
nonce_account,
|
||||||
signature::{read_keypair_file, write_keypair, Keypair, Signer},
|
signature::{read_keypair_file, write_keypair, Keypair, Signer},
|
||||||
system_program,
|
system_program,
|
||||||
};
|
};
|
||||||
@@ -833,7 +834,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_check_nonce_account() {
|
fn test_check_nonce_account() {
|
||||||
let blockhash = Hash::default();
|
let blockhash = Hash::default();
|
||||||
let nonce_pubkey = Pubkey::new_rand();
|
let nonce_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let data = Versions::new_current(State::Initialized(nonce::state::Data {
|
let data = Versions::new_current(State::Initialized(nonce::state::Data {
|
||||||
authority: nonce_pubkey,
|
authority: nonce_pubkey,
|
||||||
blockhash,
|
blockhash,
|
||||||
@@ -869,7 +870,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let data = Versions::new_current(State::Initialized(nonce::state::Data {
|
let data = Versions::new_current(State::Initialized(nonce::state::Data {
|
||||||
authority: Pubkey::new_rand(),
|
authority: solana_sdk::pubkey::new_rand(),
|
||||||
blockhash,
|
blockhash,
|
||||||
fee_calculator: FeeCalculator::default(),
|
fee_calculator: FeeCalculator::default(),
|
||||||
}));
|
}));
|
||||||
@@ -891,7 +892,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_account_identity_ok() {
|
fn test_account_identity_ok() {
|
||||||
let nonce_account = nonce::create_account(1).into_inner();
|
let nonce_account = nonce_account::create_account(1).into_inner();
|
||||||
assert_eq!(account_identity_ok(&nonce_account), Ok(()));
|
assert_eq!(account_identity_ok(&nonce_account), Ok(()));
|
||||||
|
|
||||||
let system_account = Account::new(1, 0, &system_program::id());
|
let system_account = Account::new(1, 0, &system_program::id());
|
||||||
@@ -910,7 +911,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_state_from_account() {
|
fn test_state_from_account() {
|
||||||
let mut nonce_account = nonce::create_account(1).into_inner();
|
let mut nonce_account = nonce_account::create_account(1).into_inner();
|
||||||
assert_eq!(state_from_account(&nonce_account), Ok(State::Uninitialized));
|
assert_eq!(state_from_account(&nonce_account), Ok(State::Uninitialized));
|
||||||
|
|
||||||
let data = nonce::state::Data {
|
let data = nonce::state::Data {
|
||||||
@@ -935,7 +936,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_data_from_helpers() {
|
fn test_data_from_helpers() {
|
||||||
let mut nonce_account = nonce::create_account(1).into_inner();
|
let mut nonce_account = nonce_account::create_account(1).into_inner();
|
||||||
let state = state_from_account(&nonce_account).unwrap();
|
let state = state_from_account(&nonce_account).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
data_from_state(&state),
|
data_from_state(&state),
|
||||||
|
29
cli/src/send_tpu.rs
Normal file
29
cli/src/send_tpu.rs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
use log::*;
|
||||||
|
use solana_client::rpc_response::{RpcContactInfo, RpcLeaderSchedule};
|
||||||
|
use std::net::{SocketAddr, UdpSocket};
|
||||||
|
|
||||||
|
pub fn get_leader_tpu(
|
||||||
|
slot_index: u64,
|
||||||
|
leader_schedule: Option<&RpcLeaderSchedule>,
|
||||||
|
cluster_nodes: Option<&Vec<RpcContactInfo>>,
|
||||||
|
) -> Option<SocketAddr> {
|
||||||
|
leader_schedule?
|
||||||
|
.iter()
|
||||||
|
.find(|(_pubkey, slots)| slots.iter().any(|slot| *slot as u64 == slot_index))
|
||||||
|
.and_then(|(pubkey, _)| {
|
||||||
|
cluster_nodes?
|
||||||
|
.iter()
|
||||||
|
.find(|contact_info| contact_info.pubkey == *pubkey)
|
||||||
|
.and_then(|contact_info| contact_info.tpu)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_transaction_tpu(
|
||||||
|
send_socket: &UdpSocket,
|
||||||
|
tpu_address: &SocketAddr,
|
||||||
|
wire_transaction: &[u8],
|
||||||
|
) {
|
||||||
|
if let Err(err) = send_socket.send_to(wire_transaction, tpu_address) {
|
||||||
|
warn!("Failed to send transaction to {}: {:?}", tpu_address, err);
|
||||||
|
}
|
||||||
|
}
|
103
cli/src/stake.rs
103
cli/src/stake.rs
@@ -23,20 +23,25 @@ use solana_cli_output::{
|
|||||||
CliStakeType,
|
CliStakeType,
|
||||||
};
|
};
|
||||||
use solana_client::{
|
use solana_client::{
|
||||||
blockhash_query::BlockhashQuery, nonce_utils, rpc_client::RpcClient,
|
blockhash_query::BlockhashQuery,
|
||||||
rpc_request::DELINQUENT_VALIDATOR_SLOT_DISTANCE,
|
client_error::{ClientError, ClientErrorKind},
|
||||||
|
nonce_utils,
|
||||||
|
rpc_client::RpcClient,
|
||||||
|
rpc_custom_error,
|
||||||
|
rpc_request::{self, DELINQUENT_VALIDATOR_SLOT_DISTANCE},
|
||||||
};
|
};
|
||||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
|
account::from_account,
|
||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
clock::{Clock, Epoch, Slot, UnixTimestamp},
|
clock::{Clock, Epoch, Slot, UnixTimestamp, SECONDS_PER_DAY},
|
||||||
|
feature, feature_set,
|
||||||
message::Message,
|
message::Message,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
system_instruction::SystemError,
|
system_instruction::SystemError,
|
||||||
sysvar::{
|
sysvar::{
|
||||||
clock,
|
clock,
|
||||||
stake_history::{self, StakeHistory},
|
stake_history::{self, StakeHistory},
|
||||||
Sysvar,
|
|
||||||
},
|
},
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
};
|
};
|
||||||
@@ -1497,6 +1502,7 @@ pub fn build_stake_state(
|
|||||||
use_lamports_unit: bool,
|
use_lamports_unit: bool,
|
||||||
stake_history: &StakeHistory,
|
stake_history: &StakeHistory,
|
||||||
clock: &Clock,
|
clock: &Clock,
|
||||||
|
stake_program_v2_enabled: bool,
|
||||||
) -> CliStakeState {
|
) -> CliStakeState {
|
||||||
match stake_state {
|
match stake_state {
|
||||||
StakeState::Stake(
|
StakeState::Stake(
|
||||||
@@ -1508,9 +1514,12 @@ pub fn build_stake_state(
|
|||||||
stake,
|
stake,
|
||||||
) => {
|
) => {
|
||||||
let current_epoch = clock.epoch;
|
let current_epoch = clock.epoch;
|
||||||
let (active_stake, activating_stake, deactivating_stake) = stake
|
let (active_stake, activating_stake, deactivating_stake) =
|
||||||
.delegation
|
stake.delegation.stake_activating_and_deactivating(
|
||||||
.stake_activating_and_deactivating(current_epoch, Some(stake_history));
|
current_epoch,
|
||||||
|
Some(stake_history),
|
||||||
|
stake_program_v2_enabled,
|
||||||
|
);
|
||||||
let lockup = if lockup.is_in_force(clock, None) {
|
let lockup = if lockup.is_in_force(clock, None) {
|
||||||
Some(lockup.into())
|
Some(lockup.into())
|
||||||
} else {
|
} else {
|
||||||
@@ -1519,6 +1528,7 @@ pub fn build_stake_state(
|
|||||||
CliStakeState {
|
CliStakeState {
|
||||||
stake_type: CliStakeType::Stake,
|
stake_type: CliStakeType::Stake,
|
||||||
account_balance,
|
account_balance,
|
||||||
|
credits_observed: Some(stake.credits_observed),
|
||||||
delegated_stake: Some(stake.delegation.stake),
|
delegated_stake: Some(stake.delegation.stake),
|
||||||
delegated_vote_account_address: if stake.delegation.voter_pubkey
|
delegated_vote_account_address: if stake.delegation.voter_pubkey
|
||||||
!= Pubkey::default()
|
!= Pubkey::default()
|
||||||
@@ -1570,6 +1580,7 @@ pub fn build_stake_state(
|
|||||||
CliStakeState {
|
CliStakeState {
|
||||||
stake_type: CliStakeType::Initialized,
|
stake_type: CliStakeType::Initialized,
|
||||||
account_balance,
|
account_balance,
|
||||||
|
credits_observed: Some(0),
|
||||||
authorized: Some(authorized.into()),
|
authorized: Some(authorized.into()),
|
||||||
lockup,
|
lockup,
|
||||||
use_lamports_unit,
|
use_lamports_unit,
|
||||||
@@ -1605,10 +1616,26 @@ pub(crate) fn fetch_epoch_rewards(
|
|||||||
.get(0)
|
.get(0)
|
||||||
.ok_or_else(|| format!("Unable to fetch first confirmed block for epoch {}", epoch))?;
|
.ok_or_else(|| format!("Unable to fetch first confirmed block for epoch {}", epoch))?;
|
||||||
|
|
||||||
let first_confirmed_block = rpc_client.get_confirmed_block_with_encoding(
|
let first_confirmed_block = match rpc_client.get_confirmed_block_with_encoding(
|
||||||
first_confirmed_block_in_epoch,
|
first_confirmed_block_in_epoch,
|
||||||
solana_transaction_status::UiTransactionEncoding::Base64,
|
solana_transaction_status::UiTransactionEncoding::Base64,
|
||||||
)?;
|
) {
|
||||||
|
Ok(first_confirmed_block) => first_confirmed_block,
|
||||||
|
Err(ClientError {
|
||||||
|
kind:
|
||||||
|
ClientErrorKind::RpcError(rpc_request::RpcError::RpcResponseError {
|
||||||
|
code: rpc_custom_error::JSON_RPC_SERVER_ERROR_BLOCK_NOT_AVAILABLE,
|
||||||
|
..
|
||||||
|
}),
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
// RPC node doesn't have this block
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
return Err(err.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let epoch_start_time = if let Some(block_time) = first_confirmed_block.block_time {
|
let epoch_start_time = if let Some(block_time) = first_confirmed_block.block_time {
|
||||||
block_time
|
block_time
|
||||||
@@ -1620,13 +1647,13 @@ pub(crate) fn fetch_epoch_rewards(
|
|||||||
let previous_epoch_rewards = first_confirmed_block.rewards;
|
let previous_epoch_rewards = first_confirmed_block.rewards;
|
||||||
|
|
||||||
if let Some((effective_slot, epoch_end_time, epoch_rewards)) = epoch_info {
|
if let Some((effective_slot, epoch_end_time, epoch_rewards)) = epoch_info {
|
||||||
let wall_clock_epoch_duration =
|
let wallclock_epoch_duration =
|
||||||
{ Local.timestamp(epoch_end_time, 0) - Local.timestamp(epoch_start_time, 0) }
|
{ Local.timestamp(epoch_end_time, 0) - Local.timestamp(epoch_start_time, 0) }
|
||||||
.to_std()?
|
.to_std()?
|
||||||
.as_secs_f64();
|
.as_secs_f64();
|
||||||
|
|
||||||
const SECONDS_PER_YEAR: f64 = (24 * 60 * 60 * 356) as f64;
|
let wallclock_epochs_per_year =
|
||||||
let percent_of_year = SECONDS_PER_YEAR / wall_clock_epoch_duration;
|
(SECONDS_PER_DAY * 356) as f64 / wallclock_epoch_duration;
|
||||||
|
|
||||||
if let Some(reward) = epoch_rewards
|
if let Some(reward) = epoch_rewards
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -1642,7 +1669,7 @@ pub(crate) fn fetch_epoch_rewards(
|
|||||||
amount: reward.lamports.abs() as u64,
|
amount: reward.lamports.abs() as u64,
|
||||||
post_balance: reward.post_balance,
|
post_balance: reward.post_balance,
|
||||||
percent_change: balance_increase_percent,
|
percent_change: balance_increase_percent,
|
||||||
apr: balance_increase_percent * percent_of_year,
|
apr: balance_increase_percent * wallclock_epochs_per_year,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1676,12 +1703,11 @@ pub fn process_show_stake_account(
|
|||||||
match stake_account.state() {
|
match stake_account.state() {
|
||||||
Ok(stake_state) => {
|
Ok(stake_state) => {
|
||||||
let stake_history_account = rpc_client.get_account(&stake_history::id())?;
|
let stake_history_account = rpc_client.get_account(&stake_history::id())?;
|
||||||
let stake_history =
|
let stake_history = from_account(&stake_history_account).ok_or_else(|| {
|
||||||
StakeHistory::from_account(&stake_history_account).ok_or_else(|| {
|
CliError::RpcRequestError("Failed to deserialize stake history".to_string())
|
||||||
CliError::RpcRequestError("Failed to deserialize stake history".to_string())
|
})?;
|
||||||
})?;
|
|
||||||
let clock_account = rpc_client.get_account(&clock::id())?;
|
let clock_account = rpc_client.get_account(&clock::id())?;
|
||||||
let clock: Clock = Sysvar::from_account(&clock_account).ok_or_else(|| {
|
let clock: Clock = from_account(&clock_account).ok_or_else(|| {
|
||||||
CliError::RpcRequestError("Failed to deserialize clock sysvar".to_string())
|
CliError::RpcRequestError("Failed to deserialize clock sysvar".to_string())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@@ -1691,15 +1717,17 @@ pub fn process_show_stake_account(
|
|||||||
use_lamports_unit,
|
use_lamports_unit,
|
||||||
&stake_history,
|
&stake_history,
|
||||||
&clock,
|
&clock,
|
||||||
|
is_stake_program_v2_enabled(rpc_client), // At v1.6, this check can be removed and simply passed as `true`
|
||||||
);
|
);
|
||||||
|
|
||||||
if state.stake_type == CliStakeType::Stake {
|
if state.stake_type == CliStakeType::Stake {
|
||||||
if let Some(activation_epoch) = state.activation_epoch {
|
if let Some(activation_epoch) = state.activation_epoch {
|
||||||
state.epoch_rewards = Some(fetch_epoch_rewards(
|
let rewards =
|
||||||
rpc_client,
|
fetch_epoch_rewards(rpc_client, stake_account_address, activation_epoch);
|
||||||
stake_account_address,
|
match rewards {
|
||||||
activation_epoch,
|
Ok(rewards) => state.epoch_rewards = Some(rewards),
|
||||||
)?);
|
Err(error) => eprintln!("Failed to fetch epoch rewards: {:?}", error),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(config.output_format.formatted_string(&state))
|
Ok(config.output_format.formatted_string(&state))
|
||||||
@@ -1718,7 +1746,7 @@ pub fn process_show_stake_history(
|
|||||||
use_lamports_unit: bool,
|
use_lamports_unit: bool,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let stake_history_account = rpc_client.get_account(&stake_history::id())?;
|
let stake_history_account = rpc_client.get_account(&stake_history::id())?;
|
||||||
let stake_history = StakeHistory::from_account(&stake_history_account).ok_or_else(|| {
|
let stake_history = from_account::<StakeHistory>(&stake_history_account).ok_or_else(|| {
|
||||||
CliError::RpcRequestError("Failed to deserialize stake history".to_string())
|
CliError::RpcRequestError("Failed to deserialize stake history".to_string())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@@ -1861,6 +1889,15 @@ pub fn process_delegate_stake(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_stake_program_v2_enabled(rpc_client: &RpcClient) -> bool {
|
||||||
|
rpc_client
|
||||||
|
.get_account(&feature_set::stake_program_v2::id())
|
||||||
|
.ok()
|
||||||
|
.and_then(|account| feature::from_account(&account))
|
||||||
|
.and_then(|feature| feature.activated_at)
|
||||||
|
.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -2406,9 +2443,9 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Test CreateStakeAccount SubCommand
|
// Test CreateStakeAccount SubCommand
|
||||||
let custodian = Pubkey::new_rand();
|
let custodian = solana_sdk::pubkey::new_rand();
|
||||||
let custodian_string = format!("{}", custodian);
|
let custodian_string = format!("{}", custodian);
|
||||||
let authorized = Pubkey::new_rand();
|
let authorized = solana_sdk::pubkey::new_rand();
|
||||||
let authorized_string = format!("{}", authorized);
|
let authorized_string = format!("{}", authorized);
|
||||||
let test_create_stake_account = test_commands.clone().get_matches_from(vec![
|
let test_create_stake_account = test_commands.clone().get_matches_from(vec![
|
||||||
"test",
|
"test",
|
||||||
@@ -2546,7 +2583,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Test DelegateStake Subcommand
|
// Test DelegateStake Subcommand
|
||||||
let vote_account_pubkey = Pubkey::new_rand();
|
let vote_account_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let vote_account_string = vote_account_pubkey.to_string();
|
let vote_account_string = vote_account_pubkey.to_string();
|
||||||
let test_delegate_stake = test_commands.clone().get_matches_from(vec![
|
let test_delegate_stake = test_commands.clone().get_matches_from(vec![
|
||||||
"test",
|
"test",
|
||||||
@@ -2573,7 +2610,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Test DelegateStake Subcommand w/ authority
|
// Test DelegateStake Subcommand w/ authority
|
||||||
let vote_account_pubkey = Pubkey::new_rand();
|
let vote_account_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let vote_account_string = vote_account_pubkey.to_string();
|
let vote_account_string = vote_account_pubkey.to_string();
|
||||||
let test_delegate_stake = test_commands.clone().get_matches_from(vec![
|
let test_delegate_stake = test_commands.clone().get_matches_from(vec![
|
||||||
"test",
|
"test",
|
||||||
@@ -2692,7 +2729,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Test Delegate Subcommand w/ absent fee payer
|
// Test Delegate Subcommand w/ absent fee payer
|
||||||
let key1 = Pubkey::new_rand();
|
let key1 = solana_sdk::pubkey::new_rand();
|
||||||
let sig1 = Keypair::new().sign_message(&[0u8]);
|
let sig1 = Keypair::new().sign_message(&[0u8]);
|
||||||
let signer1 = format!("{}={}", key1, sig1);
|
let signer1 = format!("{}={}", key1, sig1);
|
||||||
let test_delegate_stake = test_commands.clone().get_matches_from(vec![
|
let test_delegate_stake = test_commands.clone().get_matches_from(vec![
|
||||||
@@ -2732,7 +2769,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Test Delegate Subcommand w/ absent fee payer and absent nonce authority
|
// Test Delegate Subcommand w/ absent fee payer and absent nonce authority
|
||||||
let key2 = Pubkey::new_rand();
|
let key2 = solana_sdk::pubkey::new_rand();
|
||||||
let sig2 = Keypair::new().sign_message(&[0u8]);
|
let sig2 = Keypair::new().sign_message(&[0u8]);
|
||||||
let signer2 = format!("{}={}", key2, sig2);
|
let signer2 = format!("{}={}", key2, sig2);
|
||||||
let test_delegate_stake = test_commands.clone().get_matches_from(vec![
|
let test_delegate_stake = test_commands.clone().get_matches_from(vec![
|
||||||
@@ -3060,7 +3097,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Test Deactivate Subcommand w/ absent fee payer
|
// Test Deactivate Subcommand w/ absent fee payer
|
||||||
let key1 = Pubkey::new_rand();
|
let key1 = solana_sdk::pubkey::new_rand();
|
||||||
let sig1 = Keypair::new().sign_message(&[0u8]);
|
let sig1 = Keypair::new().sign_message(&[0u8]);
|
||||||
let signer1 = format!("{}={}", key1, sig1);
|
let signer1 = format!("{}={}", key1, sig1);
|
||||||
let test_deactivate_stake = test_commands.clone().get_matches_from(vec![
|
let test_deactivate_stake = test_commands.clone().get_matches_from(vec![
|
||||||
@@ -3097,7 +3134,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Test Deactivate Subcommand w/ absent fee payer and nonce authority
|
// Test Deactivate Subcommand w/ absent fee payer and nonce authority
|
||||||
let key2 = Pubkey::new_rand();
|
let key2 = solana_sdk::pubkey::new_rand();
|
||||||
let sig2 = Keypair::new().sign_message(&[0u8]);
|
let sig2 = Keypair::new().sign_message(&[0u8]);
|
||||||
let signer2 = format!("{}={}", key2, sig2);
|
let signer2 = format!("{}={}", key2, sig2);
|
||||||
let test_deactivate_stake = test_commands.clone().get_matches_from(vec![
|
let test_deactivate_stake = test_commands.clone().get_matches_from(vec![
|
||||||
@@ -3276,7 +3313,7 @@ mod tests {
|
|||||||
let stake_account_keypair = Keypair::new();
|
let stake_account_keypair = Keypair::new();
|
||||||
write_keypair(&stake_account_keypair, tmp_file.as_file_mut()).unwrap();
|
write_keypair(&stake_account_keypair, tmp_file.as_file_mut()).unwrap();
|
||||||
|
|
||||||
let source_stake_account_pubkey = Pubkey::new_rand();
|
let source_stake_account_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let test_merge_stake_account = test_commands.clone().get_matches_from(vec![
|
let test_merge_stake_account = test_commands.clone().get_matches_from(vec![
|
||||||
"test",
|
"test",
|
||||||
"merge-stake",
|
"merge-stake",
|
||||||
|
@@ -486,7 +486,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_validator_info() {
|
fn test_parse_validator_info() {
|
||||||
let pubkey = Pubkey::new_rand();
|
let pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let keys = vec![(validator_info::id(), false), (pubkey, true)];
|
let keys = vec![(validator_info::id(), false), (pubkey, true)];
|
||||||
let config = ConfigKeys { keys };
|
let config = ConfigKeys { keys };
|
||||||
|
|
||||||
|
@@ -696,11 +696,14 @@ pub fn process_show_vote_account(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let epoch_rewards = Some(crate::stake::fetch_epoch_rewards(
|
let epoch_rewards = match crate::stake::fetch_epoch_rewards(rpc_client, vote_account_address, 1)
|
||||||
rpc_client,
|
{
|
||||||
vote_account_address,
|
Ok(rewards) => Some(rewards),
|
||||||
1,
|
Err(error) => {
|
||||||
)?);
|
eprintln!("Failed to fetch epoch rewards: {:?}", error);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let vote_account_data = CliVoteAccount {
|
let vote_account_data = CliVoteAccount {
|
||||||
account_balance: vote_account.lamports,
|
account_balance: vote_account.lamports,
|
||||||
@@ -915,7 +918,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// test init with an authed voter
|
// test init with an authed voter
|
||||||
let authed = Pubkey::new_rand();
|
let authed = solana_sdk::pubkey::new_rand();
|
||||||
let (keypair_file, mut tmp_file) = make_tmp_file();
|
let (keypair_file, mut tmp_file) = make_tmp_file();
|
||||||
let keypair = Keypair::new();
|
let keypair = Keypair::new();
|
||||||
write_keypair(&keypair, tmp_file.as_file_mut()).unwrap();
|
write_keypair(&keypair, tmp_file.as_file_mut()).unwrap();
|
||||||
|
@@ -55,7 +55,7 @@ fn test_cli_deploy_program() {
|
|||||||
faucet_host: None,
|
faucet_host: None,
|
||||||
faucet_port: faucet_addr.port(),
|
faucet_port: faucet_addr.port(),
|
||||||
pubkey: None,
|
pubkey: None,
|
||||||
lamports: 3 * minimum_balance_for_rent_exemption, // min balance for rent exemption for two programs + leftover for tx processing
|
lamports: 4 * minimum_balance_for_rent_exemption, // min balance for rent exemption for three programs + leftover for tx processing
|
||||||
};
|
};
|
||||||
config.signers = vec![&keypair];
|
config.signers = vec![&keypair];
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
@@ -64,6 +64,7 @@ fn test_cli_deploy_program() {
|
|||||||
program_location: pathbuf.to_str().unwrap().to_string(),
|
program_location: pathbuf.to_str().unwrap().to_string(),
|
||||||
address: None,
|
address: None,
|
||||||
use_deprecated_loader: false,
|
use_deprecated_loader: false,
|
||||||
|
allow_excessive_balance: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let response = process_command(&config);
|
let response = process_command(&config);
|
||||||
@@ -98,6 +99,7 @@ fn test_cli_deploy_program() {
|
|||||||
program_location: pathbuf.to_str().unwrap().to_string(),
|
program_location: pathbuf.to_str().unwrap().to_string(),
|
||||||
address: Some(1),
|
address: Some(1),
|
||||||
use_deprecated_loader: false,
|
use_deprecated_loader: false,
|
||||||
|
allow_excessive_balance: false,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
let account1 = rpc_client
|
let account1 = rpc_client
|
||||||
@@ -113,6 +115,44 @@ fn test_cli_deploy_program() {
|
|||||||
// Attempt to redeploy to the same address
|
// Attempt to redeploy to the same address
|
||||||
process_command(&config).unwrap_err();
|
process_command(&config).unwrap_err();
|
||||||
|
|
||||||
|
// Attempt to deploy to account with excess balance
|
||||||
|
let custom_address_keypair = Keypair::new();
|
||||||
|
config.command = CliCommand::Airdrop {
|
||||||
|
faucet_host: None,
|
||||||
|
faucet_port: faucet_addr.port(),
|
||||||
|
pubkey: None,
|
||||||
|
lamports: 2 * minimum_balance_for_rent_exemption, // Anything over minimum_balance_for_rent_exemption should trigger err
|
||||||
|
};
|
||||||
|
config.signers = vec![&custom_address_keypair];
|
||||||
|
process_command(&config).unwrap();
|
||||||
|
|
||||||
|
config.signers = vec![&keypair, &custom_address_keypair];
|
||||||
|
config.command = CliCommand::Deploy {
|
||||||
|
program_location: pathbuf.to_str().unwrap().to_string(),
|
||||||
|
address: Some(1),
|
||||||
|
use_deprecated_loader: false,
|
||||||
|
allow_excessive_balance: false,
|
||||||
|
};
|
||||||
|
process_command(&config).unwrap_err();
|
||||||
|
|
||||||
|
// Use forcing parameter to deploy to account with excess balance
|
||||||
|
config.command = CliCommand::Deploy {
|
||||||
|
program_location: pathbuf.to_str().unwrap().to_string(),
|
||||||
|
address: Some(1),
|
||||||
|
use_deprecated_loader: false,
|
||||||
|
allow_excessive_balance: true,
|
||||||
|
};
|
||||||
|
process_command(&config).unwrap();
|
||||||
|
let account2 = rpc_client
|
||||||
|
.get_account_with_commitment(&custom_address_keypair.pubkey(), CommitmentConfig::recent())
|
||||||
|
.unwrap()
|
||||||
|
.value
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(account2.lamports, 2 * minimum_balance_for_rent_exemption);
|
||||||
|
assert_eq!(account2.owner, bpf_loader::id());
|
||||||
|
assert_eq!(account2.executable, true);
|
||||||
|
assert_eq!(account0.data, account2.data);
|
||||||
|
|
||||||
server.close().unwrap();
|
server.close().unwrap();
|
||||||
remove_dir_all(ledger_path).unwrap();
|
remove_dir_all(ledger_path).unwrap();
|
||||||
}
|
}
|
||||||
|
@@ -10,7 +10,7 @@ use solana_client::{
|
|||||||
rpc_client::RpcClient,
|
rpc_client::RpcClient,
|
||||||
};
|
};
|
||||||
use solana_core::contact_info::ContactInfo;
|
use solana_core::contact_info::ContactInfo;
|
||||||
use solana_core::test_validator::{TestValidator, TestValidatorOptions};
|
use solana_core::test_validator::TestValidator;
|
||||||
use solana_faucet::faucet::run_local_faucet;
|
use solana_faucet::faucet::run_local_faucet;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
commitment_config::CommitmentConfig,
|
commitment_config::CommitmentConfig,
|
||||||
@@ -172,7 +172,7 @@ fn full_battery_tests(
|
|||||||
assert_ne!(first_nonce, third_nonce);
|
assert_ne!(first_nonce, third_nonce);
|
||||||
|
|
||||||
// Withdraw from nonce account
|
// Withdraw from nonce account
|
||||||
let payee_pubkey = Pubkey::new_rand();
|
let payee_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
config_payer.signers = authorized_signers;
|
config_payer.signers = authorized_signers;
|
||||||
config_payer.command = CliCommand::WithdrawFromNonceAccount {
|
config_payer.command = CliCommand::WithdrawFromNonceAccount {
|
||||||
nonce_account,
|
nonce_account,
|
||||||
@@ -231,17 +231,14 @@ fn full_battery_tests(
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_account_with_seed() {
|
fn test_create_account_with_seed() {
|
||||||
|
solana_logger::setup();
|
||||||
let TestValidator {
|
let TestValidator {
|
||||||
server,
|
server,
|
||||||
leader_data,
|
leader_data,
|
||||||
alice: mint_keypair,
|
alice: mint_keypair,
|
||||||
ledger_path,
|
ledger_path,
|
||||||
..
|
..
|
||||||
} = TestValidator::run_with_options(TestValidatorOptions {
|
} = TestValidator::run_with_fees(1);
|
||||||
fees: 1,
|
|
||||||
bootstrap_validator_lamports: 42_000,
|
|
||||||
..TestValidatorOptions::default()
|
|
||||||
});
|
|
||||||
|
|
||||||
let (sender, receiver) = channel();
|
let (sender, receiver) = channel();
|
||||||
run_local_faucet(mint_keypair, sender, None);
|
run_local_faucet(mint_keypair, sender, None);
|
||||||
|
@@ -9,7 +9,7 @@ use solana_client::{
|
|||||||
nonce_utils,
|
nonce_utils,
|
||||||
rpc_client::RpcClient,
|
rpc_client::RpcClient,
|
||||||
};
|
};
|
||||||
use solana_core::test_validator::{TestValidator, TestValidatorOptions};
|
use solana_core::test_validator::TestValidator;
|
||||||
use solana_faucet::faucet::run_local_faucet;
|
use solana_faucet::faucet::run_local_faucet;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
@@ -848,11 +848,7 @@ fn test_stake_authorize_with_fee_payer() {
|
|||||||
alice,
|
alice,
|
||||||
ledger_path,
|
ledger_path,
|
||||||
..
|
..
|
||||||
} = TestValidator::run_with_options(TestValidatorOptions {
|
} = TestValidator::run_with_fees(SIG_FEE);
|
||||||
fees: SIG_FEE,
|
|
||||||
bootstrap_validator_lamports: 42_000,
|
|
||||||
..TestValidatorOptions::default()
|
|
||||||
});
|
|
||||||
let (sender, receiver) = channel();
|
let (sender, receiver) = channel();
|
||||||
run_local_faucet(alice, sender, None);
|
run_local_faucet(alice, sender, None);
|
||||||
let faucet_addr = receiver.recv().unwrap();
|
let faucet_addr = receiver.recv().unwrap();
|
||||||
@@ -985,11 +981,7 @@ fn test_stake_split() {
|
|||||||
alice,
|
alice,
|
||||||
ledger_path,
|
ledger_path,
|
||||||
..
|
..
|
||||||
} = TestValidator::run_with_options(TestValidatorOptions {
|
} = TestValidator::run_with_fees(1);
|
||||||
fees: 1,
|
|
||||||
bootstrap_validator_lamports: 42_000,
|
|
||||||
..TestValidatorOptions::default()
|
|
||||||
});
|
|
||||||
let (sender, receiver) = channel();
|
let (sender, receiver) = channel();
|
||||||
run_local_faucet(alice, sender, None);
|
run_local_faucet(alice, sender, None);
|
||||||
let faucet_addr = receiver.recv().unwrap();
|
let faucet_addr = receiver.recv().unwrap();
|
||||||
@@ -1140,11 +1132,7 @@ fn test_stake_set_lockup() {
|
|||||||
alice,
|
alice,
|
||||||
ledger_path,
|
ledger_path,
|
||||||
..
|
..
|
||||||
} = TestValidator::run_with_options(TestValidatorOptions {
|
} = TestValidator::run_with_fees(1);
|
||||||
fees: 1,
|
|
||||||
bootstrap_validator_lamports: 42_000,
|
|
||||||
..TestValidatorOptions::default()
|
|
||||||
});
|
|
||||||
let (sender, receiver) = channel();
|
let (sender, receiver) = channel();
|
||||||
run_local_faucet(alice, sender, None);
|
run_local_faucet(alice, sender, None);
|
||||||
let faucet_addr = receiver.recv().unwrap();
|
let faucet_addr = receiver.recv().unwrap();
|
||||||
|
@@ -9,7 +9,7 @@ use solana_client::{
|
|||||||
nonce_utils,
|
nonce_utils,
|
||||||
rpc_client::RpcClient,
|
rpc_client::RpcClient,
|
||||||
};
|
};
|
||||||
use solana_core::test_validator::{TestValidator, TestValidatorOptions};
|
use solana_core::test_validator::TestValidator;
|
||||||
use solana_faucet::faucet::run_local_faucet;
|
use solana_faucet::faucet::run_local_faucet;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
commitment_config::CommitmentConfig,
|
commitment_config::CommitmentConfig,
|
||||||
@@ -21,17 +21,14 @@ use std::{fs::remove_dir_all, sync::mpsc::channel};
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_transfer() {
|
fn test_transfer() {
|
||||||
|
solana_logger::setup();
|
||||||
let TestValidator {
|
let TestValidator {
|
||||||
server,
|
server,
|
||||||
leader_data,
|
leader_data,
|
||||||
alice: mint_keypair,
|
alice: mint_keypair,
|
||||||
ledger_path,
|
ledger_path,
|
||||||
..
|
..
|
||||||
} = TestValidator::run_with_options(TestValidatorOptions {
|
} = TestValidator::run_with_fees(1);
|
||||||
fees: 1,
|
|
||||||
bootstrap_validator_lamports: 42_000,
|
|
||||||
..TestValidatorOptions::default()
|
|
||||||
});
|
|
||||||
|
|
||||||
let (sender, receiver) = channel();
|
let (sender, receiver) = channel();
|
||||||
run_local_faucet(mint_keypair, sender, None);
|
run_local_faucet(mint_keypair, sender, None);
|
||||||
@@ -252,17 +249,14 @@ fn test_transfer() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_transfer_multisession_signing() {
|
fn test_transfer_multisession_signing() {
|
||||||
|
solana_logger::setup();
|
||||||
let TestValidator {
|
let TestValidator {
|
||||||
server,
|
server,
|
||||||
leader_data,
|
leader_data,
|
||||||
alice: mint_keypair,
|
alice: mint_keypair,
|
||||||
ledger_path,
|
ledger_path,
|
||||||
..
|
..
|
||||||
} = TestValidator::run_with_options(TestValidatorOptions {
|
} = TestValidator::run_with_fees(1);
|
||||||
fees: 1,
|
|
||||||
bootstrap_validator_lamports: 42_000,
|
|
||||||
..TestValidatorOptions::default()
|
|
||||||
});
|
|
||||||
|
|
||||||
let (sender, receiver) = channel();
|
let (sender, receiver) = channel();
|
||||||
run_local_faucet(mint_keypair, sender, None);
|
run_local_faucet(mint_keypair, sender, None);
|
||||||
@@ -382,17 +376,14 @@ fn test_transfer_multisession_signing() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_transfer_all() {
|
fn test_transfer_all() {
|
||||||
|
solana_logger::setup();
|
||||||
let TestValidator {
|
let TestValidator {
|
||||||
server,
|
server,
|
||||||
leader_data,
|
leader_data,
|
||||||
alice: mint_keypair,
|
alice: mint_keypair,
|
||||||
ledger_path,
|
ledger_path,
|
||||||
..
|
..
|
||||||
} = TestValidator::run_with_options(TestValidatorOptions {
|
} = TestValidator::run_with_fees(1);
|
||||||
fees: 1,
|
|
||||||
bootstrap_validator_lamports: 42_000,
|
|
||||||
..TestValidatorOptions::default()
|
|
||||||
});
|
|
||||||
|
|
||||||
let (sender, receiver) = channel();
|
let (sender, receiver) = channel();
|
||||||
run_local_faucet(mint_keypair, sender, None);
|
run_local_faucet(mint_keypair, sender, None);
|
||||||
|
@@ -12,7 +12,6 @@ use solana_faucet::faucet::run_local_faucet;
|
|||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
commitment_config::CommitmentConfig,
|
commitment_config::CommitmentConfig,
|
||||||
pubkey::Pubkey,
|
|
||||||
signature::{Keypair, Signer},
|
signature::{Keypair, Signer},
|
||||||
};
|
};
|
||||||
use solana_vote_program::vote_state::{VoteAuthorize, VoteState, VoteStateVersions};
|
use solana_vote_program::vote_state::{VoteAuthorize, VoteState, VoteStateVersions};
|
||||||
@@ -110,7 +109,7 @@ fn test_vote_authorize_and_withdraw() {
|
|||||||
assert_eq!(authorized_withdrawer, withdraw_authority.pubkey());
|
assert_eq!(authorized_withdrawer, withdraw_authority.pubkey());
|
||||||
|
|
||||||
// Withdraw from vote account
|
// Withdraw from vote account
|
||||||
let destination_account = Pubkey::new_rand(); // Send withdrawal to new account to make balance check easy
|
let destination_account = solana_sdk::pubkey::new_rand(); // Send withdrawal to new account to make balance check easy
|
||||||
config.signers = vec![&default_signer, &withdraw_authority];
|
config.signers = vec![&default_signer, &withdraw_authority];
|
||||||
config.command = CliCommand::WithdrawFromVoteAccount {
|
config.command = CliCommand::WithdrawFromVoteAccount {
|
||||||
vote_account_pubkey,
|
vote_account_pubkey,
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-client"
|
name = "solana-client"
|
||||||
version = "1.4.0"
|
version = "1.4.16"
|
||||||
description = "Solana Client"
|
description = "Solana Client"
|
||||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
@@ -17,16 +17,18 @@ indicatif = "0.15.0"
|
|||||||
jsonrpc-core = "15.0.0"
|
jsonrpc-core = "15.0.0"
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
rayon = "1.4.0"
|
rayon = "1.4.0"
|
||||||
reqwest = { version = "0.10.6", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
reqwest = { version = "0.10.8", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||||
|
semver = "0.11.0"
|
||||||
serde = "1.0.112"
|
serde = "1.0.112"
|
||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
serde_json = "1.0.56"
|
serde_json = "1.0.56"
|
||||||
solana-account-decoder = { path = "../account-decoder", version = "1.4.0" }
|
solana-account-decoder = { path = "../account-decoder", version = "1.4.16" }
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "1.4.0" }
|
solana-clap-utils = { path = "../clap-utils", version = "1.4.16" }
|
||||||
solana-net-utils = { path = "../net-utils", version = "1.4.0" }
|
solana-net-utils = { path = "../net-utils", version = "1.4.16" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.4.0" }
|
solana-sdk = { path = "../sdk", version = "1.4.16" }
|
||||||
solana-transaction-status = { path = "../transaction-status", version = "1.4.0" }
|
solana-transaction-status = { path = "../transaction-status", version = "1.4.16" }
|
||||||
solana-vote-program = { path = "../programs/vote", version = "1.4.0" }
|
solana-version = { path = "../version", version = "1.4.16" }
|
||||||
|
solana-vote-program = { path = "../programs/vote", version = "1.4.16" }
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
tungstenite = "0.10.1"
|
tungstenite = "0.10.1"
|
||||||
url = "2.1.1"
|
url = "2.1.1"
|
||||||
@@ -35,7 +37,7 @@ url = "2.1.1"
|
|||||||
assert_matches = "1.3.0"
|
assert_matches = "1.3.0"
|
||||||
jsonrpc-core = "15.0.0"
|
jsonrpc-core = "15.0.0"
|
||||||
jsonrpc-http-server = "15.0.0"
|
jsonrpc-http-server = "15.0.0"
|
||||||
solana-logger = { path = "../logger", version = "1.4.0" }
|
solana-logger = { path = "../logger", version = "1.4.16" }
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
targets = ["x86_64-unknown-linux-gnu"]
|
targets = ["x86_64-unknown-linux-gnu"]
|
||||||
|
@@ -50,10 +50,10 @@ impl Into<TransportError> for ClientErrorKind {
|
|||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
#[error("{kind}")]
|
#[error("{kind}")]
|
||||||
pub struct ClientError {
|
pub struct ClientError {
|
||||||
request: Option<rpc_request::RpcRequest>,
|
pub request: Option<rpc_request::RpcRequest>,
|
||||||
|
|
||||||
#[source]
|
#[source]
|
||||||
kind: ClientErrorKind,
|
pub kind: ClientErrorKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientError {
|
impl ClientError {
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
client_error::Result,
|
client_error::Result,
|
||||||
rpc_request::{RpcError, RpcRequest},
|
rpc_custom_error,
|
||||||
|
rpc_request::{RpcError, RpcRequest, RpcResponseErrorData},
|
||||||
|
rpc_response::RpcSimulateTransactionResult,
|
||||||
rpc_sender::RpcSender,
|
rpc_sender::RpcSender,
|
||||||
};
|
};
|
||||||
use log::*;
|
use log::*;
|
||||||
@@ -27,6 +29,13 @@ impl HttpSender {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct RpcErrorObject {
|
||||||
|
code: i64,
|
||||||
|
message: String,
|
||||||
|
data: serde_json::Value,
|
||||||
|
}
|
||||||
|
|
||||||
impl RpcSender for HttpSender {
|
impl RpcSender for HttpSender {
|
||||||
fn send(&self, request: RpcRequest, params: serde_json::Value) -> Result<serde_json::Value> {
|
fn send(&self, request: RpcRequest, params: serde_json::Value) -> Result<serde_json::Value> {
|
||||||
// Concurrent requests are not supported so reuse the same request id for all requests
|
// Concurrent requests are not supported so reuse the same request id for all requests
|
||||||
@@ -63,11 +72,36 @@ impl RpcSender for HttpSender {
|
|||||||
|
|
||||||
let json: serde_json::Value = serde_json::from_str(&response.text()?)?;
|
let json: serde_json::Value = serde_json::from_str(&response.text()?)?;
|
||||||
if json["error"].is_object() {
|
if json["error"].is_object() {
|
||||||
return Err(RpcError::RpcRequestError(format!(
|
return match serde_json::from_value::<RpcErrorObject>(json["error"].clone())
|
||||||
"RPC Error response: {}",
|
{
|
||||||
serde_json::to_string(&json["error"]).unwrap()
|
Ok(rpc_error_object) => {
|
||||||
))
|
let data = match rpc_error_object.code {
|
||||||
.into());
|
rpc_custom_error::JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE => {
|
||||||
|
match serde_json::from_value::<RpcSimulateTransactionResult>(json["error"]["data"].clone()) {
|
||||||
|
Ok(data) => RpcResponseErrorData::SendTransactionPreflightFailure(data),
|
||||||
|
Err(err) => {
|
||||||
|
debug!("Failed to deserialize RpcSimulateTransactionResult: {:?}", err);
|
||||||
|
RpcResponseErrorData::Empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => RpcResponseErrorData::Empty
|
||||||
|
};
|
||||||
|
|
||||||
|
Err(RpcError::RpcResponseError {
|
||||||
|
code: rpc_error_object.code,
|
||||||
|
message: rpc_error_object.message,
|
||||||
|
data,
|
||||||
|
}
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
Err(err) => Err(RpcError::RpcRequestError(format!(
|
||||||
|
"Failed to deserialize RPC error response: {} [{}]",
|
||||||
|
serde_json::to_string(&json["error"]).unwrap(),
|
||||||
|
err
|
||||||
|
))
|
||||||
|
.into()),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return Ok(json["result"].clone());
|
return Ok(json["result"].clone());
|
||||||
}
|
}
|
||||||
|
@@ -10,6 +10,7 @@ pub mod perf_utils;
|
|||||||
pub mod pubsub_client;
|
pub mod pubsub_client;
|
||||||
pub mod rpc_client;
|
pub mod rpc_client;
|
||||||
pub mod rpc_config;
|
pub mod rpc_config;
|
||||||
|
pub mod rpc_custom_error;
|
||||||
pub mod rpc_filter;
|
pub mod rpc_filter;
|
||||||
pub mod rpc_request;
|
pub mod rpc_request;
|
||||||
pub mod rpc_response;
|
pub mod rpc_response;
|
||||||
|
@@ -1,17 +1,19 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
client_error::Result,
|
client_error::Result,
|
||||||
rpc_request::RpcRequest,
|
rpc_request::RpcRequest,
|
||||||
rpc_response::{Response, RpcResponseContext},
|
rpc_response::{Response, RpcResponseContext, RpcVersionInfo},
|
||||||
rpc_sender::RpcSender,
|
rpc_sender::RpcSender,
|
||||||
};
|
};
|
||||||
use serde_json::{Number, Value};
|
use serde_json::{json, Number, Value};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
|
epoch_info::EpochInfo,
|
||||||
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
||||||
instruction::InstructionError,
|
instruction::InstructionError,
|
||||||
signature::Signature,
|
signature::Signature,
|
||||||
transaction::{self, Transaction, TransactionError},
|
transaction::{self, Transaction, TransactionError},
|
||||||
};
|
};
|
||||||
use solana_transaction_status::TransactionStatus;
|
use solana_transaction_status::TransactionStatus;
|
||||||
|
use solana_version::Version;
|
||||||
use std::{collections::HashMap, sync::RwLock};
|
use std::{collections::HashMap, sync::RwLock};
|
||||||
|
|
||||||
pub const PUBKEY: &str = "7RoSF9fUmdphVCpabEoefH81WwrW7orsWonXWqTXkKV8";
|
pub const PUBKEY: &str = "7RoSF9fUmdphVCpabEoefH81WwrW7orsWonXWqTXkKV8";
|
||||||
@@ -57,6 +59,13 @@ impl RpcSender for MockSender {
|
|||||||
serde_json::to_value(FeeCalculator::default()).unwrap(),
|
serde_json::to_value(FeeCalculator::default()).unwrap(),
|
||||||
),
|
),
|
||||||
})?,
|
})?,
|
||||||
|
RpcRequest::GetEpochInfo => serde_json::to_value(EpochInfo {
|
||||||
|
epoch: 1,
|
||||||
|
slot_index: 2,
|
||||||
|
slots_in_epoch: 32,
|
||||||
|
absolute_slot: 34,
|
||||||
|
block_height: 34,
|
||||||
|
})?,
|
||||||
RpcRequest::GetFeeCalculatorForBlockhash => {
|
RpcRequest::GetFeeCalculatorForBlockhash => {
|
||||||
let value = if self.url == "blockhash_expired" {
|
let value = if self.url == "blockhash_expired" {
|
||||||
Value::Null
|
Value::Null
|
||||||
@@ -112,13 +121,20 @@ impl RpcSender for MockSender {
|
|||||||
Signature::new(&[8; 64]).to_string()
|
Signature::new(&[8; 64]).to_string()
|
||||||
} else {
|
} else {
|
||||||
let tx_str = params.as_array().unwrap()[0].as_str().unwrap().to_string();
|
let tx_str = params.as_array().unwrap()[0].as_str().unwrap().to_string();
|
||||||
let data = bs58::decode(tx_str).into_vec().unwrap();
|
let data = base64::decode(tx_str).unwrap();
|
||||||
let tx: Transaction = bincode::deserialize(&data).unwrap();
|
let tx: Transaction = bincode::deserialize(&data).unwrap();
|
||||||
tx.signatures[0].to_string()
|
tx.signatures[0].to_string()
|
||||||
};
|
};
|
||||||
Value::String(signature)
|
Value::String(signature)
|
||||||
}
|
}
|
||||||
RpcRequest::GetMinimumBalanceForRentExemption => Value::Number(Number::from(20)),
|
RpcRequest::GetMinimumBalanceForRentExemption => Value::Number(Number::from(20)),
|
||||||
|
RpcRequest::GetVersion => {
|
||||||
|
let version = Version::default();
|
||||||
|
json!(RpcVersionInfo {
|
||||||
|
solana_core: version.to_string(),
|
||||||
|
feature_set: Some(version.feature_set),
|
||||||
|
})
|
||||||
|
}
|
||||||
_ => Value::Null,
|
_ => Value::Null,
|
||||||
};
|
};
|
||||||
Ok(val)
|
Ok(val)
|
||||||
|
@@ -1,4 +1,7 @@
|
|||||||
use crate::rpc_response::{Response as RpcResponse, RpcSignatureResult, SlotInfo};
|
use crate::{
|
||||||
|
rpc_config::{RpcSignatureSubscribeConfig, RpcTransactionLogsConfig, RpcTransactionLogsFilter},
|
||||||
|
rpc_response::{Response as RpcResponse, RpcLogsResponse, RpcSignatureResult, SlotInfo},
|
||||||
|
};
|
||||||
use log::*;
|
use log::*;
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use serde_json::{
|
use serde_json::{
|
||||||
@@ -20,8 +23,6 @@ use thiserror::Error;
|
|||||||
use tungstenite::{client::AutoStream, connect, Message, WebSocket};
|
use tungstenite::{client::AutoStream, connect, Message, WebSocket};
|
||||||
use url::{ParseError, Url};
|
use url::{ParseError, Url};
|
||||||
|
|
||||||
type PubsubSignatureResponse = PubsubClientSubscription<RpcResponse<RpcSignatureResult>>;
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum PubsubClientError {
|
pub enum PubsubClientError {
|
||||||
#[error("url parse error")]
|
#[error("url parse error")]
|
||||||
@@ -33,8 +34,8 @@ pub enum PubsubClientError {
|
|||||||
#[error("json parse error")]
|
#[error("json parse error")]
|
||||||
JsonParseError(#[from] serde_json::error::Error),
|
JsonParseError(#[from] serde_json::error::Error),
|
||||||
|
|
||||||
#[error("unexpected message format")]
|
#[error("unexpected message format: {0}")]
|
||||||
UnexpectedMessageError,
|
UnexpectedMessageError(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PubsubClientSubscription<T>
|
pub struct PubsubClientSubscription<T>
|
||||||
@@ -89,8 +90,11 @@ where
|
|||||||
return Ok(x);
|
return Ok(x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// TODO: Add proper JSON RPC response/error handling...
|
||||||
Err(PubsubClientError::UnexpectedMessageError)
|
Err(PubsubClientError::UnexpectedMessageError(format!(
|
||||||
|
"{:?}",
|
||||||
|
json_msg
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_unsubscribe(&self) -> Result<(), PubsubClientError> {
|
pub fn send_unsubscribe(&self) -> Result<(), PubsubClientError> {
|
||||||
@@ -114,14 +118,18 @@ where
|
|||||||
let message_text = &message.into_text().unwrap();
|
let message_text = &message.into_text().unwrap();
|
||||||
let json_msg: Map<String, Value> = serde_json::from_str(message_text)?;
|
let json_msg: Map<String, Value> = serde_json::from_str(message_text)?;
|
||||||
|
|
||||||
if let Some(Object(value_1)) = json_msg.get("params") {
|
if let Some(Object(params)) = json_msg.get("params") {
|
||||||
if let Some(value_2) = value_1.get("result") {
|
if let Some(result) = params.get("result") {
|
||||||
let x: T = serde_json::from_value::<T>(value_2.clone()).unwrap();
|
let x: T = serde_json::from_value::<T>(result.clone()).unwrap();
|
||||||
return Ok(x);
|
return Ok(x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(PubsubClientError::UnexpectedMessageError)
|
// TODO: Add proper JSON RPC response/error handling...
|
||||||
|
Err(PubsubClientError::UnexpectedMessageError(format!(
|
||||||
|
"{:?}",
|
||||||
|
json_msg
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn shutdown(&mut self) -> std::thread::Result<()> {
|
pub fn shutdown(&mut self) -> std::thread::Result<()> {
|
||||||
@@ -138,15 +146,79 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const SLOT_OPERATION: &str = "slot";
|
pub type LogsSubscription = (
|
||||||
const SIGNATURE_OPERATION: &str = "signature";
|
PubsubClientSubscription<RpcResponse<RpcLogsResponse>>,
|
||||||
|
Receiver<RpcResponse<RpcLogsResponse>>,
|
||||||
|
);
|
||||||
|
pub type SlotsSubscription = (PubsubClientSubscription<SlotInfo>, Receiver<SlotInfo>);
|
||||||
|
pub type SignatureSubscription = (
|
||||||
|
PubsubClientSubscription<RpcResponse<RpcSignatureResult>>,
|
||||||
|
Receiver<RpcResponse<RpcSignatureResult>>,
|
||||||
|
);
|
||||||
|
|
||||||
pub struct PubsubClient {}
|
pub struct PubsubClient {}
|
||||||
|
|
||||||
impl PubsubClient {
|
impl PubsubClient {
|
||||||
pub fn slot_subscribe(
|
pub fn logs_subscribe(
|
||||||
url: &str,
|
url: &str,
|
||||||
) -> Result<(PubsubClientSubscription<SlotInfo>, Receiver<SlotInfo>), PubsubClientError> {
|
filter: RpcTransactionLogsFilter,
|
||||||
|
config: RpcTransactionLogsConfig,
|
||||||
|
) -> Result<LogsSubscription, PubsubClientError> {
|
||||||
|
let url = Url::parse(url)?;
|
||||||
|
let (socket, _response) = connect(url)?;
|
||||||
|
let (sender, receiver) = channel();
|
||||||
|
|
||||||
|
let socket = Arc::new(RwLock::new(socket));
|
||||||
|
let socket_clone = socket.clone();
|
||||||
|
let exit = Arc::new(AtomicBool::new(false));
|
||||||
|
let exit_clone = exit.clone();
|
||||||
|
|
||||||
|
let subscription_id =
|
||||||
|
PubsubClientSubscription::<RpcResponse<RpcLogsResponse>>::send_subscribe(
|
||||||
|
&socket_clone,
|
||||||
|
json!({
|
||||||
|
"jsonrpc":"2.0","id":1,"method":"logsSubscribe","params":[filter, config]
|
||||||
|
})
|
||||||
|
.to_string(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let t_cleanup = std::thread::spawn(move || {
|
||||||
|
loop {
|
||||||
|
if exit_clone.load(Ordering::Relaxed) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
match PubsubClientSubscription::read_message(&socket_clone) {
|
||||||
|
Ok(message) => match sender.send(message) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(err) => {
|
||||||
|
info!("receive error: {:?}", err);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
info!("receive error: {:?}", err);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("websocket - exited receive loop");
|
||||||
|
});
|
||||||
|
|
||||||
|
let result = PubsubClientSubscription {
|
||||||
|
message_type: PhantomData,
|
||||||
|
operation: "logs",
|
||||||
|
socket,
|
||||||
|
subscription_id,
|
||||||
|
t_cleanup: Some(t_cleanup),
|
||||||
|
exit,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((result, receiver))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn slot_subscribe(url: &str) -> Result<SlotsSubscription, PubsubClientError> {
|
||||||
let url = Url::parse(url)?;
|
let url = Url::parse(url)?;
|
||||||
let (socket, _response) = connect(url)?;
|
let (socket, _response) = connect(url)?;
|
||||||
let (sender, receiver) = channel::<SlotInfo>();
|
let (sender, receiver) = channel::<SlotInfo>();
|
||||||
@@ -158,41 +230,37 @@ impl PubsubClient {
|
|||||||
let subscription_id = PubsubClientSubscription::<SlotInfo>::send_subscribe(
|
let subscription_id = PubsubClientSubscription::<SlotInfo>::send_subscribe(
|
||||||
&socket_clone,
|
&socket_clone,
|
||||||
json!({
|
json!({
|
||||||
"jsonrpc":"2.0","id":1,"method":format!("{}Subscribe", SLOT_OPERATION),"params":[]
|
"jsonrpc":"2.0","id":1,"method":"slotSubscribe","params":[]
|
||||||
})
|
})
|
||||||
.to_string(),
|
.to_string(),
|
||||||
)
|
)?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let t_cleanup = std::thread::spawn(move || {
|
let t_cleanup = std::thread::spawn(move || {
|
||||||
loop {
|
loop {
|
||||||
if exit_clone.load(Ordering::Relaxed) {
|
if exit_clone.load(Ordering::Relaxed) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
match PubsubClientSubscription::read_message(&socket_clone) {
|
||||||
let message: Result<SlotInfo, PubsubClientError> =
|
Ok(message) => match sender.send(message) {
|
||||||
PubsubClientSubscription::read_message(&socket_clone);
|
|
||||||
|
|
||||||
if let Ok(msg) = message {
|
|
||||||
match sender.send(msg) {
|
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
info!("receive error: {:?}", err);
|
info!("receive error: {:?}", err);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
info!("receive error: {:?}", err);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
info!("receive error: {:?}", message);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("websocket - exited receive loop");
|
info!("websocket - exited receive loop");
|
||||||
});
|
});
|
||||||
|
|
||||||
let result: PubsubClientSubscription<SlotInfo> = PubsubClientSubscription {
|
let result = PubsubClientSubscription {
|
||||||
message_type: PhantomData,
|
message_type: PhantomData,
|
||||||
operation: SLOT_OPERATION,
|
operation: "slot",
|
||||||
socket,
|
socket,
|
||||||
subscription_id,
|
subscription_id,
|
||||||
t_cleanup: Some(t_cleanup),
|
t_cleanup: Some(t_cleanup),
|
||||||
@@ -205,16 +273,11 @@ impl PubsubClient {
|
|||||||
pub fn signature_subscribe(
|
pub fn signature_subscribe(
|
||||||
url: &str,
|
url: &str,
|
||||||
signature: &Signature,
|
signature: &Signature,
|
||||||
) -> Result<
|
config: Option<RpcSignatureSubscribeConfig>,
|
||||||
(
|
) -> Result<SignatureSubscription, PubsubClientError> {
|
||||||
PubsubSignatureResponse,
|
|
||||||
Receiver<RpcResponse<RpcSignatureResult>>,
|
|
||||||
),
|
|
||||||
PubsubClientError,
|
|
||||||
> {
|
|
||||||
let url = Url::parse(url)?;
|
let url = Url::parse(url)?;
|
||||||
let (socket, _response) = connect(url)?;
|
let (socket, _response) = connect(url)?;
|
||||||
let (sender, receiver) = channel::<RpcResponse<RpcSignatureResult>>();
|
let (sender, receiver) = channel();
|
||||||
|
|
||||||
let socket = Arc::new(RwLock::new(socket));
|
let socket = Arc::new(RwLock::new(socket));
|
||||||
let socket_clone = socket.clone();
|
let socket_clone = socket.clone();
|
||||||
@@ -223,10 +286,10 @@ impl PubsubClient {
|
|||||||
let body = json!({
|
let body = json!({
|
||||||
"jsonrpc":"2.0",
|
"jsonrpc":"2.0",
|
||||||
"id":1,
|
"id":1,
|
||||||
"method":format!("{}Subscribe", SIGNATURE_OPERATION),
|
"method":"signatureSubscribe",
|
||||||
"params":[
|
"params":[
|
||||||
signature.to_string(),
|
signature.to_string(),
|
||||||
{"enableReceivedNotification": true }
|
config
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
.to_string();
|
.to_string();
|
||||||
@@ -234,8 +297,7 @@ impl PubsubClient {
|
|||||||
PubsubClientSubscription::<RpcResponse<RpcSignatureResult>>::send_subscribe(
|
PubsubClientSubscription::<RpcResponse<RpcSignatureResult>>::send_subscribe(
|
||||||
&socket_clone,
|
&socket_clone,
|
||||||
body,
|
body,
|
||||||
)
|
)?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let t_cleanup = std::thread::spawn(move || {
|
let t_cleanup = std::thread::spawn(move || {
|
||||||
loop {
|
loop {
|
||||||
@@ -263,15 +325,14 @@ impl PubsubClient {
|
|||||||
info!("websocket - exited receive loop");
|
info!("websocket - exited receive loop");
|
||||||
});
|
});
|
||||||
|
|
||||||
let result: PubsubClientSubscription<RpcResponse<RpcSignatureResult>> =
|
let result = PubsubClientSubscription {
|
||||||
PubsubClientSubscription {
|
message_type: PhantomData,
|
||||||
message_type: PhantomData,
|
operation: "signature",
|
||||||
operation: SIGNATURE_OPERATION,
|
socket,
|
||||||
socket,
|
subscription_id,
|
||||||
subscription_id,
|
t_cleanup: Some(t_cleanup),
|
||||||
t_cleanup: Some(t_cleanup),
|
exit,
|
||||||
exit,
|
};
|
||||||
};
|
|
||||||
|
|
||||||
Ok((result, receiver))
|
Ok((result, receiver))
|
||||||
}
|
}
|
||||||
|
@@ -8,7 +8,7 @@ use crate::{
|
|||||||
RpcProgramAccountsConfig, RpcSendTransactionConfig, RpcSimulateTransactionConfig,
|
RpcProgramAccountsConfig, RpcSendTransactionConfig, RpcSimulateTransactionConfig,
|
||||||
RpcTokenAccountsFilter,
|
RpcTokenAccountsFilter,
|
||||||
},
|
},
|
||||||
rpc_request::{RpcError, RpcRequest, TokenAccountsFilter},
|
rpc_request::{RpcError, RpcRequest, RpcResponseErrorData, TokenAccountsFilter},
|
||||||
rpc_response::*,
|
rpc_response::*,
|
||||||
rpc_sender::RpcSender,
|
rpc_sender::RpcSender,
|
||||||
};
|
};
|
||||||
@@ -41,12 +41,15 @@ use solana_transaction_status::{
|
|||||||
use solana_vote_program::vote_state::MAX_LOCKOUT_HISTORY;
|
use solana_vote_program::vote_state::MAX_LOCKOUT_HISTORY;
|
||||||
use std::{
|
use std::{
|
||||||
net::SocketAddr,
|
net::SocketAddr,
|
||||||
|
sync::RwLock,
|
||||||
thread::sleep,
|
thread::sleep,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct RpcClient {
|
pub struct RpcClient {
|
||||||
sender: Box<dyn RpcSender + Send + Sync + 'static>,
|
sender: Box<dyn RpcSender + Send + Sync + 'static>,
|
||||||
|
commitment_config: CommitmentConfig,
|
||||||
|
default_cluster_transaction_encoding: RwLock<Option<UiTransactionEncoding>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_encode_transaction(
|
fn serialize_encode_transaction(
|
||||||
@@ -70,26 +73,41 @@ fn serialize_encode_transaction(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RpcClient {
|
impl RpcClient {
|
||||||
pub fn new_sender<T: RpcSender + Send + Sync + 'static>(sender: T) -> Self {
|
fn new_sender<T: RpcSender + Send + Sync + 'static>(
|
||||||
|
sender: T,
|
||||||
|
commitment_config: CommitmentConfig,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
sender: Box::new(sender),
|
sender: Box::new(sender),
|
||||||
|
default_cluster_transaction_encoding: RwLock::new(None),
|
||||||
|
commitment_config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(url: String) -> Self {
|
pub fn new(url: String) -> Self {
|
||||||
Self::new_sender(HttpSender::new(url))
|
Self::new_with_commitment(url, CommitmentConfig::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_commitment(url: String, commitment_config: CommitmentConfig) -> Self {
|
||||||
|
Self::new_sender(HttpSender::new(url), commitment_config)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_timeout(url: String, timeout: Duration) -> Self {
|
pub fn new_with_timeout(url: String, timeout: Duration) -> Self {
|
||||||
Self::new_sender(HttpSender::new_with_timeout(url, timeout))
|
Self::new_sender(
|
||||||
|
HttpSender::new_with_timeout(url, timeout),
|
||||||
|
CommitmentConfig::default(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_mock(url: String) -> Self {
|
pub fn new_mock(url: String) -> Self {
|
||||||
Self::new_sender(MockSender::new(url))
|
Self::new_sender(MockSender::new(url), CommitmentConfig::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_mock_with_mocks(url: String, mocks: Mocks) -> Self {
|
pub fn new_mock_with_mocks(url: String, mocks: Mocks) -> Self {
|
||||||
Self::new_sender(MockSender::new_with_mocks(url, mocks))
|
Self::new_sender(
|
||||||
|
MockSender::new_with_mocks(url, mocks),
|
||||||
|
CommitmentConfig::default(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_socket(addr: SocketAddr) -> Self {
|
pub fn new_socket(addr: SocketAddr) -> Self {
|
||||||
@@ -103,10 +121,14 @@ impl RpcClient {
|
|||||||
|
|
||||||
pub fn confirm_transaction(&self, signature: &Signature) -> ClientResult<bool> {
|
pub fn confirm_transaction(&self, signature: &Signature) -> ClientResult<bool> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.confirm_transaction_with_commitment(signature, CommitmentConfig::default())?
|
.confirm_transaction_with_commitment(signature, self.commitment_config)?
|
||||||
.value)
|
.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn commitment(&self) -> CommitmentConfig {
|
||||||
|
self.commitment_config
|
||||||
|
}
|
||||||
|
|
||||||
pub fn confirm_transaction_with_commitment(
|
pub fn confirm_transaction_with_commitment(
|
||||||
&self,
|
&self,
|
||||||
signature: &Signature,
|
signature: &Signature,
|
||||||
@@ -125,7 +147,38 @@ impl RpcClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_transaction(&self, transaction: &Transaction) -> ClientResult<Signature> {
|
pub fn send_transaction(&self, transaction: &Transaction) -> ClientResult<Signature> {
|
||||||
self.send_transaction_with_config(transaction, RpcSendTransactionConfig::default())
|
self.send_transaction_with_config(
|
||||||
|
transaction,
|
||||||
|
RpcSendTransactionConfig {
|
||||||
|
preflight_commitment: Some(self.commitment_config.commitment),
|
||||||
|
..RpcSendTransactionConfig::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_cluster_transaction_encoding(&self) -> Result<UiTransactionEncoding, RpcError> {
|
||||||
|
let default_cluster_transaction_encoding =
|
||||||
|
self.default_cluster_transaction_encoding.read().unwrap();
|
||||||
|
if let Some(encoding) = *default_cluster_transaction_encoding {
|
||||||
|
Ok(encoding)
|
||||||
|
} else {
|
||||||
|
drop(default_cluster_transaction_encoding);
|
||||||
|
let cluster_version = self.get_version().map_err(|e| {
|
||||||
|
RpcError::RpcRequestError(format!("cluster version query failed: {}", e))
|
||||||
|
})?;
|
||||||
|
let cluster_version =
|
||||||
|
semver::Version::parse(&cluster_version.solana_core).map_err(|e| {
|
||||||
|
RpcError::RpcRequestError(format!("failed to parse cluster version: {}", e))
|
||||||
|
})?;
|
||||||
|
// Prefer base64 since 1.3.16
|
||||||
|
let encoding = if cluster_version < semver::Version::new(1, 3, 16) {
|
||||||
|
UiTransactionEncoding::Base58
|
||||||
|
} else {
|
||||||
|
UiTransactionEncoding::Base64
|
||||||
|
};
|
||||||
|
*self.default_cluster_transaction_encoding.write().unwrap() = Some(encoding);
|
||||||
|
Ok(encoding)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_transaction_with_config(
|
pub fn send_transaction_with_config(
|
||||||
@@ -133,12 +186,43 @@ impl RpcClient {
|
|||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
config: RpcSendTransactionConfig,
|
config: RpcSendTransactionConfig,
|
||||||
) -> ClientResult<Signature> {
|
) -> ClientResult<Signature> {
|
||||||
let encoding = config.encoding.unwrap_or(UiTransactionEncoding::Base58);
|
let encoding = if let Some(encoding) = config.encoding {
|
||||||
|
encoding
|
||||||
|
} else {
|
||||||
|
self.default_cluster_transaction_encoding()?
|
||||||
|
};
|
||||||
|
let config = RpcSendTransactionConfig {
|
||||||
|
encoding: Some(encoding),
|
||||||
|
..config
|
||||||
|
};
|
||||||
let serialized_encoded = serialize_encode_transaction(transaction, encoding)?;
|
let serialized_encoded = serialize_encode_transaction(transaction, encoding)?;
|
||||||
let signature_base58_str: String = self.send(
|
let signature_base58_str: String = match self.send(
|
||||||
RpcRequest::SendTransaction,
|
RpcRequest::SendTransaction,
|
||||||
json!([serialized_encoded, config]),
|
json!([serialized_encoded, config]),
|
||||||
)?;
|
) {
|
||||||
|
Ok(signature_base58_str) => signature_base58_str,
|
||||||
|
Err(err) => {
|
||||||
|
if let ClientErrorKind::RpcError(RpcError::RpcResponseError {
|
||||||
|
code,
|
||||||
|
message,
|
||||||
|
data,
|
||||||
|
}) = &err.kind
|
||||||
|
{
|
||||||
|
debug!("{} {}", code, message);
|
||||||
|
if let RpcResponseErrorData::SendTransactionPreflightFailure(
|
||||||
|
RpcSimulateTransactionResult {
|
||||||
|
logs: Some(logs), ..
|
||||||
|
},
|
||||||
|
) = data
|
||||||
|
{
|
||||||
|
for (i, log) in logs.iter().enumerate() {
|
||||||
|
debug!("{:>3}: {}", i + 1, log);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let signature = signature_base58_str
|
let signature = signature_base58_str
|
||||||
.parse::<Signature>()
|
.parse::<Signature>()
|
||||||
@@ -161,26 +245,34 @@ impl RpcClient {
|
|||||||
pub fn simulate_transaction(
|
pub fn simulate_transaction(
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
sig_verify: bool,
|
|
||||||
) -> RpcResult<RpcSimulateTransactionResult> {
|
) -> RpcResult<RpcSimulateTransactionResult> {
|
||||||
self.simulate_transaction_with_config(
|
self.simulate_transaction_with_config(
|
||||||
transaction,
|
transaction,
|
||||||
sig_verify,
|
RpcSimulateTransactionConfig {
|
||||||
RpcSimulateTransactionConfig::default(),
|
commitment: Some(self.commitment_config),
|
||||||
|
..RpcSimulateTransactionConfig::default()
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn simulate_transaction_with_config(
|
pub fn simulate_transaction_with_config(
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
sig_verify: bool,
|
|
||||||
config: RpcSimulateTransactionConfig,
|
config: RpcSimulateTransactionConfig,
|
||||||
) -> RpcResult<RpcSimulateTransactionResult> {
|
) -> RpcResult<RpcSimulateTransactionResult> {
|
||||||
let encoding = config.encoding.unwrap_or(UiTransactionEncoding::Base58);
|
let encoding = if let Some(encoding) = config.encoding {
|
||||||
|
encoding
|
||||||
|
} else {
|
||||||
|
self.default_cluster_transaction_encoding()?
|
||||||
|
};
|
||||||
|
let config = RpcSimulateTransactionConfig {
|
||||||
|
encoding: Some(encoding),
|
||||||
|
..config
|
||||||
|
};
|
||||||
let serialized_encoded = serialize_encode_transaction(transaction, encoding)?;
|
let serialized_encoded = serialize_encode_transaction(transaction, encoding)?;
|
||||||
self.send(
|
self.send(
|
||||||
RpcRequest::SimulateTransaction,
|
RpcRequest::SimulateTransaction,
|
||||||
json!([serialized_encoded, { "sigVerify": sig_verify }]),
|
json!([serialized_encoded, config]),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,7 +280,7 @@ impl RpcClient {
|
|||||||
&self,
|
&self,
|
||||||
signature: &Signature,
|
signature: &Signature,
|
||||||
) -> ClientResult<Option<transaction::Result<()>>> {
|
) -> ClientResult<Option<transaction::Result<()>>> {
|
||||||
self.get_signature_status_with_commitment(signature, CommitmentConfig::default())
|
self.get_signature_status_with_commitment(signature, self.commitment_config)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_signature_statuses(
|
pub fn get_signature_statuses(
|
||||||
@@ -246,7 +338,7 @@ impl RpcClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_slot(&self) -> ClientResult<Slot> {
|
pub fn get_slot(&self) -> ClientResult<Slot> {
|
||||||
self.get_slot_with_commitment(CommitmentConfig::default())
|
self.get_slot_with_commitment(self.commitment_config)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_slot_with_commitment(
|
pub fn get_slot_with_commitment(
|
||||||
@@ -264,7 +356,7 @@ impl RpcClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn total_supply(&self) -> ClientResult<u64> {
|
pub fn total_supply(&self) -> ClientResult<u64> {
|
||||||
self.total_supply_with_commitment(CommitmentConfig::default())
|
self.total_supply_with_commitment(self.commitment_config)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn total_supply_with_commitment(
|
pub fn total_supply_with_commitment(
|
||||||
@@ -282,7 +374,7 @@ impl RpcClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_vote_accounts(&self) -> ClientResult<RpcVoteAccountStatus> {
|
pub fn get_vote_accounts(&self) -> ClientResult<RpcVoteAccountStatus> {
|
||||||
self.get_vote_accounts_with_commitment(CommitmentConfig::default())
|
self.get_vote_accounts_with_commitment(self.commitment_config)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_vote_accounts_with_commitment(
|
pub fn get_vote_accounts_with_commitment(
|
||||||
@@ -410,7 +502,7 @@ impl RpcClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_epoch_info(&self) -> ClientResult<EpochInfo> {
|
pub fn get_epoch_info(&self) -> ClientResult<EpochInfo> {
|
||||||
self.get_epoch_info_with_commitment(CommitmentConfig::default())
|
self.get_epoch_info_with_commitment(self.commitment_config)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_epoch_info_with_commitment(
|
pub fn get_epoch_info_with_commitment(
|
||||||
@@ -424,7 +516,7 @@ impl RpcClient {
|
|||||||
&self,
|
&self,
|
||||||
slot: Option<Slot>,
|
slot: Option<Slot>,
|
||||||
) -> ClientResult<Option<RpcLeaderSchedule>> {
|
) -> ClientResult<Option<RpcLeaderSchedule>> {
|
||||||
self.get_leader_schedule_with_commitment(slot, CommitmentConfig::default())
|
self.get_leader_schedule_with_commitment(slot, self.commitment_config)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_leader_schedule_with_commitment(
|
pub fn get_leader_schedule_with_commitment(
|
||||||
@@ -518,8 +610,10 @@ impl RpcClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Note that `get_account` returns `Err(..)` if the account does not exist whereas
|
||||||
|
/// `get_account_with_commitment` returns `Ok(None)` if the account does not exist.
|
||||||
pub fn get_account(&self, pubkey: &Pubkey) -> ClientResult<Account> {
|
pub fn get_account(&self, pubkey: &Pubkey) -> ClientResult<Account> {
|
||||||
self.get_account_with_commitment(pubkey, CommitmentConfig::default())?
|
self.get_account_with_commitment(pubkey, self.commitment_config)?
|
||||||
.value
|
.value
|
||||||
.ok_or_else(|| RpcError::ForUser(format!("AccountNotFound: pubkey={}", pubkey)).into())
|
.ok_or_else(|| RpcError::ForUser(format!("AccountNotFound: pubkey={}", pubkey)).into())
|
||||||
}
|
}
|
||||||
@@ -567,7 +661,7 @@ impl RpcClient {
|
|||||||
|
|
||||||
pub fn get_multiple_accounts(&self, pubkeys: &[Pubkey]) -> ClientResult<Vec<Option<Account>>> {
|
pub fn get_multiple_accounts(&self, pubkeys: &[Pubkey]) -> ClientResult<Vec<Option<Account>>> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.get_multiple_accounts_with_commitment(pubkeys, CommitmentConfig::default())?
|
.get_multiple_accounts_with_commitment(pubkeys, self.commitment_config)?
|
||||||
.value)
|
.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -621,7 +715,7 @@ impl RpcClient {
|
|||||||
/// Request the balance of the account `pubkey`.
|
/// Request the balance of the account `pubkey`.
|
||||||
pub fn get_balance(&self, pubkey: &Pubkey) -> ClientResult<u64> {
|
pub fn get_balance(&self, pubkey: &Pubkey) -> ClientResult<u64> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.get_balance_with_commitment(pubkey, CommitmentConfig::default())?
|
.get_balance_with_commitment(pubkey, self.commitment_config)?
|
||||||
.value)
|
.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -663,7 +757,7 @@ impl RpcClient {
|
|||||||
|
|
||||||
/// Request the transaction count.
|
/// Request the transaction count.
|
||||||
pub fn get_transaction_count(&self) -> ClientResult<u64> {
|
pub fn get_transaction_count(&self) -> ClientResult<u64> {
|
||||||
self.get_transaction_count_with_commitment(CommitmentConfig::default())
|
self.get_transaction_count_with_commitment(self.commitment_config)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_transaction_count_with_commitment(
|
pub fn get_transaction_count_with_commitment(
|
||||||
@@ -675,7 +769,7 @@ impl RpcClient {
|
|||||||
|
|
||||||
pub fn get_recent_blockhash(&self) -> ClientResult<(Hash, FeeCalculator)> {
|
pub fn get_recent_blockhash(&self) -> ClientResult<(Hash, FeeCalculator)> {
|
||||||
let (blockhash, fee_calculator, _last_valid_slot) = self
|
let (blockhash, fee_calculator, _last_valid_slot) = self
|
||||||
.get_recent_blockhash_with_commitment(CommitmentConfig::default())?
|
.get_recent_blockhash_with_commitment(self.commitment_config)?
|
||||||
.value;
|
.value;
|
||||||
Ok((blockhash, fee_calculator))
|
Ok((blockhash, fee_calculator))
|
||||||
}
|
}
|
||||||
@@ -732,10 +826,7 @@ impl RpcClient {
|
|||||||
blockhash: &Hash,
|
blockhash: &Hash,
|
||||||
) -> ClientResult<Option<FeeCalculator>> {
|
) -> ClientResult<Option<FeeCalculator>> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.get_fee_calculator_for_blockhash_with_commitment(
|
.get_fee_calculator_for_blockhash_with_commitment(blockhash, self.commitment_config)?
|
||||||
blockhash,
|
|
||||||
CommitmentConfig::default(),
|
|
||||||
)?
|
|
||||||
.value)
|
.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -811,7 +902,7 @@ impl RpcClient {
|
|||||||
|
|
||||||
pub fn get_token_account(&self, pubkey: &Pubkey) -> ClientResult<Option<UiTokenAccount>> {
|
pub fn get_token_account(&self, pubkey: &Pubkey) -> ClientResult<Option<UiTokenAccount>> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.get_token_account_with_commitment(pubkey, CommitmentConfig::default())?
|
.get_token_account_with_commitment(pubkey, self.commitment_config)?
|
||||||
.value)
|
.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -872,7 +963,7 @@ impl RpcClient {
|
|||||||
|
|
||||||
pub fn get_token_account_balance(&self, pubkey: &Pubkey) -> ClientResult<UiTokenAmount> {
|
pub fn get_token_account_balance(&self, pubkey: &Pubkey) -> ClientResult<UiTokenAmount> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.get_token_account_balance_with_commitment(pubkey, CommitmentConfig::default())?
|
.get_token_account_balance_with_commitment(pubkey, self.commitment_config)?
|
||||||
.value)
|
.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -896,7 +987,7 @@ impl RpcClient {
|
|||||||
.get_token_accounts_by_delegate_with_commitment(
|
.get_token_accounts_by_delegate_with_commitment(
|
||||||
delegate,
|
delegate,
|
||||||
token_account_filter,
|
token_account_filter,
|
||||||
CommitmentConfig::default(),
|
self.commitment_config,
|
||||||
)?
|
)?
|
||||||
.value)
|
.value)
|
||||||
}
|
}
|
||||||
@@ -935,7 +1026,7 @@ impl RpcClient {
|
|||||||
.get_token_accounts_by_owner_with_commitment(
|
.get_token_accounts_by_owner_with_commitment(
|
||||||
owner,
|
owner,
|
||||||
token_account_filter,
|
token_account_filter,
|
||||||
CommitmentConfig::default(),
|
self.commitment_config,
|
||||||
)?
|
)?
|
||||||
.value)
|
.value)
|
||||||
}
|
}
|
||||||
@@ -967,7 +1058,7 @@ impl RpcClient {
|
|||||||
|
|
||||||
pub fn get_token_supply(&self, mint: &Pubkey) -> ClientResult<UiTokenAmount> {
|
pub fn get_token_supply(&self, mint: &Pubkey) -> ClientResult<UiTokenAmount> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.get_token_supply_with_commitment(mint, CommitmentConfig::default())?
|
.get_token_supply_with_commitment(mint, self.commitment_config)?
|
||||||
.value)
|
.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1048,7 +1139,7 @@ impl RpcClient {
|
|||||||
|
|
||||||
/// Poll the server to confirm a transaction.
|
/// Poll the server to confirm a transaction.
|
||||||
pub fn poll_for_signature(&self, signature: &Signature) -> ClientResult<()> {
|
pub fn poll_for_signature(&self, signature: &Signature) -> ClientResult<()> {
|
||||||
self.poll_for_signature_with_commitment(signature, CommitmentConfig::default())
|
self.poll_for_signature_with_commitment(signature, self.commitment_config)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Poll the server to confirm a transaction.
|
/// Poll the server to confirm a transaction.
|
||||||
@@ -1156,10 +1247,9 @@ impl RpcClient {
|
|||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
) -> ClientResult<Signature> {
|
) -> ClientResult<Signature> {
|
||||||
self.send_and_confirm_transaction_with_spinner_and_config(
|
self.send_and_confirm_transaction_with_spinner_and_commitment(
|
||||||
transaction,
|
transaction,
|
||||||
CommitmentConfig::default(),
|
self.commitment_config,
|
||||||
RpcSendTransactionConfig::default(),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1171,7 +1261,10 @@ impl RpcClient {
|
|||||||
self.send_and_confirm_transaction_with_spinner_and_config(
|
self.send_and_confirm_transaction_with_spinner_and_config(
|
||||||
transaction,
|
transaction,
|
||||||
commitment,
|
commitment,
|
||||||
RpcSendTransactionConfig::default(),
|
RpcSendTransactionConfig {
|
||||||
|
preflight_commitment: Some(commitment.commitment),
|
||||||
|
..RpcSendTransactionConfig::default()
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1418,7 +1511,7 @@ mod tests {
|
|||||||
let rpc_client = RpcClient::new_mock("succeeds".to_string());
|
let rpc_client = RpcClient::new_mock("succeeds".to_string());
|
||||||
|
|
||||||
let key = Keypair::new();
|
let key = Keypair::new();
|
||||||
let to = Pubkey::new_rand();
|
let to = solana_sdk::pubkey::new_rand();
|
||||||
let blockhash = Hash::default();
|
let blockhash = Hash::default();
|
||||||
let tx = system_transaction::transfer(&key, &to, 50, blockhash);
|
let tx = system_transaction::transfer(&key, &to, 50, blockhash);
|
||||||
|
|
||||||
@@ -1471,7 +1564,7 @@ mod tests {
|
|||||||
let rpc_client = RpcClient::new_mock("succeeds".to_string());
|
let rpc_client = RpcClient::new_mock("succeeds".to_string());
|
||||||
|
|
||||||
let key = Keypair::new();
|
let key = Keypair::new();
|
||||||
let to = Pubkey::new_rand();
|
let to = solana_sdk::pubkey::new_rand();
|
||||||
let blockhash = Hash::default();
|
let blockhash = Hash::default();
|
||||||
let tx = system_transaction::transfer(&key, &to, 50, blockhash);
|
let tx = system_transaction::transfer(&key, &to, 50, blockhash);
|
||||||
let result = rpc_client.send_and_confirm_transaction(&tx);
|
let result = rpc_client.send_and_confirm_transaction(&tx);
|
||||||
|
@@ -71,6 +71,21 @@ pub struct RpcProgramAccountsConfig {
|
|||||||
pub account_config: RpcAccountInfoConfig,
|
pub account_config: RpcAccountInfoConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub enum RpcTransactionLogsFilter {
|
||||||
|
All,
|
||||||
|
AllWithVotes,
|
||||||
|
Mentions(Vec<String>), // base58-encoded list of addresses
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct RpcTransactionLogsConfig {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub commitment: Option<CommitmentConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub enum RpcTokenAccountsFilter {
|
pub enum RpcTokenAccountsFilter {
|
||||||
|
@@ -1,13 +1,15 @@
|
|||||||
|
//! Implementation defined RPC server errors
|
||||||
|
|
||||||
|
use crate::rpc_response::RpcSimulateTransactionResult;
|
||||||
use jsonrpc_core::{Error, ErrorCode};
|
use jsonrpc_core::{Error, ErrorCode};
|
||||||
use solana_client::rpc_response::RpcSimulateTransactionResult;
|
|
||||||
use solana_sdk::clock::Slot;
|
use solana_sdk::clock::Slot;
|
||||||
|
|
||||||
const JSON_RPC_SERVER_ERROR_1: i64 = -32001;
|
pub const JSON_RPC_SERVER_ERROR_BLOCK_CLEANED_UP: i64 = -32001;
|
||||||
const JSON_RPC_SERVER_ERROR_2: i64 = -32002;
|
pub const JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE: i64 = -32002;
|
||||||
const JSON_RPC_SERVER_ERROR_3: i64 = -32003;
|
pub const JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE: i64 = -32003;
|
||||||
const JSON_RPC_SERVER_ERROR_4: i64 = -32004;
|
pub const JSON_RPC_SERVER_ERROR_BLOCK_NOT_AVAILABLE: i64 = -32004;
|
||||||
const JSON_RPC_SERVER_ERROR_5: i64 = -32005;
|
pub const JSON_RPC_SERVER_ERROR_NODE_UNHEALTHLY: i64 = -32005;
|
||||||
const JSON_RPC_SERVER_ERROR_6: i64 = -32006;
|
pub const JSON_RPC_SERVER_ERROR_TRANSACTION_PRECOMPILE_VERIFICATION_FAILURE: i64 = -32006;
|
||||||
|
|
||||||
pub enum RpcCustomError {
|
pub enum RpcCustomError {
|
||||||
BlockCleanedUp {
|
BlockCleanedUp {
|
||||||
@@ -33,7 +35,7 @@ impl From<RpcCustomError> for Error {
|
|||||||
slot,
|
slot,
|
||||||
first_available_block,
|
first_available_block,
|
||||||
} => Self {
|
} => Self {
|
||||||
code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_1),
|
code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_BLOCK_CLEANED_UP),
|
||||||
message: format!(
|
message: format!(
|
||||||
"Block {} cleaned up, does not exist on node. First available block: {}",
|
"Block {} cleaned up, does not exist on node. First available block: {}",
|
||||||
slot, first_available_block,
|
slot, first_available_block,
|
||||||
@@ -41,27 +43,33 @@ impl From<RpcCustomError> for Error {
|
|||||||
data: None,
|
data: None,
|
||||||
},
|
},
|
||||||
RpcCustomError::SendTransactionPreflightFailure { message, result } => Self {
|
RpcCustomError::SendTransactionPreflightFailure { message, result } => Self {
|
||||||
code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_2),
|
code: ErrorCode::ServerError(
|
||||||
|
JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE,
|
||||||
|
),
|
||||||
message,
|
message,
|
||||||
data: Some(serde_json::json!(result)),
|
data: Some(serde_json::json!(result)),
|
||||||
},
|
},
|
||||||
RpcCustomError::TransactionSignatureVerificationFailure => Self {
|
RpcCustomError::TransactionSignatureVerificationFailure => Self {
|
||||||
code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_3),
|
code: ErrorCode::ServerError(
|
||||||
|
JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE,
|
||||||
|
),
|
||||||
message: "Transaction signature verification failure".to_string(),
|
message: "Transaction signature verification failure".to_string(),
|
||||||
data: None,
|
data: None,
|
||||||
},
|
},
|
||||||
RpcCustomError::BlockNotAvailable { slot } => Self {
|
RpcCustomError::BlockNotAvailable { slot } => Self {
|
||||||
code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_4),
|
code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_BLOCK_NOT_AVAILABLE),
|
||||||
message: format!("Block not available for slot {}", slot),
|
message: format!("Block not available for slot {}", slot),
|
||||||
data: None,
|
data: None,
|
||||||
},
|
},
|
||||||
RpcCustomError::RpcNodeUnhealthy => Self {
|
RpcCustomError::RpcNodeUnhealthy => Self {
|
||||||
code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_5),
|
code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_NODE_UNHEALTHLY),
|
||||||
message: "RPC node is unhealthy".to_string(),
|
message: "RPC node is unhealthy".to_string(),
|
||||||
data: None,
|
data: None,
|
||||||
},
|
},
|
||||||
RpcCustomError::TransactionPrecompileVerificationFailure(e) => Self {
|
RpcCustomError::TransactionPrecompileVerificationFailure(e) => Self {
|
||||||
code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_6),
|
code: ErrorCode::ServerError(
|
||||||
|
JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE,
|
||||||
|
),
|
||||||
message: format!("Transaction precompile verification failure {:?}", e),
|
message: format!("Transaction precompile verification failure {:?}", e),
|
||||||
data: None,
|
data: None,
|
||||||
},
|
},
|
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::rpc_response::RpcSimulateTransactionResult;
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey::Pubkey;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
@@ -138,10 +139,42 @@ impl RpcRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum RpcResponseErrorData {
|
||||||
|
Empty,
|
||||||
|
SendTransactionPreflightFailure(RpcSimulateTransactionResult),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for RpcResponseErrorData {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
RpcResponseErrorData::SendTransactionPreflightFailure(
|
||||||
|
RpcSimulateTransactionResult {
|
||||||
|
logs: Some(logs), ..
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
if logs.is_empty() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
// Give the user a hint that there is more useful logging information available...
|
||||||
|
write!(f, "[{} log messages]", logs.len())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum RpcError {
|
pub enum RpcError {
|
||||||
#[error("rpc request error: {0}")]
|
#[error("RPC request error: {0}")]
|
||||||
RpcRequestError(String),
|
RpcRequestError(String),
|
||||||
|
#[error("RPC response error {code}: {message} {data}")]
|
||||||
|
RpcResponseError {
|
||||||
|
code: i64,
|
||||||
|
message: String,
|
||||||
|
data: RpcResponseErrorData,
|
||||||
|
},
|
||||||
#[error("parse error: expected {0}")]
|
#[error("parse error: expected {0}")]
|
||||||
ParseError(String), /* "expected" */
|
ParseError(String), /* "expected" */
|
||||||
// Anything in a `ForUser` needs to die. The caller should be
|
// Anything in a `ForUser` needs to die. The caller should be
|
||||||
@@ -226,7 +259,7 @@ mod tests {
|
|||||||
|
|
||||||
// Test request with CommitmentConfig and params
|
// Test request with CommitmentConfig and params
|
||||||
let test_request = RpcRequest::GetTokenAccountsByOwner;
|
let test_request = RpcRequest::GetTokenAccountsByOwner;
|
||||||
let mint = Pubkey::new_rand();
|
let mint = solana_sdk::pubkey::new_rand();
|
||||||
let token_account_filter = RpcTokenAccountsFilter::Mint(mint.to_string());
|
let token_account_filter = RpcTokenAccountsFilter::Mint(mint.to_string());
|
||||||
let request = test_request
|
let request = test_request
|
||||||
.build_request_json(1, json!([addr, token_account_filter, commitment_config]));
|
.build_request_json(1, json!([addr, token_account_filter, commitment_config]));
|
||||||
|
@@ -108,6 +108,14 @@ pub enum RpcSignatureResult {
|
|||||||
ReceivedSignature(ReceivedSignatureResult),
|
ReceivedSignature(ReceivedSignatureResult),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct RpcLogsResponse {
|
||||||
|
pub signature: String, // Signature as base58 string
|
||||||
|
pub err: Option<TransactionError>,
|
||||||
|
pub logs: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct ProcessedSignatureResult {
|
pub struct ProcessedSignatureResult {
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
//! messages to the network directly. The binary encoding of its messages are
|
//! messages to the network directly. The binary encoding of its messages are
|
||||||
//! unstable and may change in future releases.
|
//! unstable and may change in future releases.
|
||||||
|
|
||||||
use crate::{rpc_client::RpcClient, rpc_response::Response};
|
use crate::{rpc_client::RpcClient, rpc_config::RpcProgramAccountsConfig, rpc_response::Response};
|
||||||
use bincode::{serialize_into, serialized_size};
|
use bincode::{serialize_into, serialized_size};
|
||||||
use log::*;
|
use log::*;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
@@ -276,6 +276,16 @@ impl ThinClient {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_program_accounts_with_config(
|
||||||
|
&self,
|
||||||
|
pubkey: &Pubkey,
|
||||||
|
config: RpcProgramAccountsConfig,
|
||||||
|
) -> TransportResult<Vec<(Pubkey, Account)>> {
|
||||||
|
self.rpc_client()
|
||||||
|
.get_program_accounts_with_config(pubkey, config)
|
||||||
|
.map_err(|e| e.into())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn wait_for_balance_with_commitment(
|
pub fn wait_for_balance_with_commitment(
|
||||||
&self,
|
&self,
|
||||||
pubkey: &Pubkey,
|
pubkey: &Pubkey,
|
||||||
@@ -389,6 +399,12 @@ impl SyncClient for ThinClient {
|
|||||||
.map(|r| r.value)
|
.map(|r| r.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_minimum_balance_for_rent_exemption(&self, data_len: usize) -> TransportResult<u64> {
|
||||||
|
self.rpc_client()
|
||||||
|
.get_minimum_balance_for_rent_exemption(data_len)
|
||||||
|
.map_err(|e| e.into())
|
||||||
|
}
|
||||||
|
|
||||||
fn get_recent_blockhash(&self) -> TransportResult<(Hash, FeeCalculator)> {
|
fn get_recent_blockhash(&self) -> TransportResult<(Hash, FeeCalculator)> {
|
||||||
let (blockhash, fee_calculator, _last_valid_slot) =
|
let (blockhash, fee_calculator, _last_valid_slot) =
|
||||||
self.get_recent_blockhash_with_commitment(CommitmentConfig::default())?;
|
self.get_recent_blockhash_with_commitment(CommitmentConfig::default())?;
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-core"
|
name = "solana-core"
|
||||||
description = "Blockchain, Rebuilt for Scale"
|
description = "Blockchain, Rebuilt for Scale"
|
||||||
version = "1.4.0"
|
version = "1.4.16"
|
||||||
documentation = "https://docs.rs/solana"
|
documentation = "https://docs.rs/solana"
|
||||||
homepage = "https://solana.com/"
|
homepage = "https://solana.com/"
|
||||||
readme = "../README.md"
|
readme = "../README.md"
|
||||||
@@ -14,6 +14,7 @@ edition = "2018"
|
|||||||
codecov = { repository = "solana-labs/solana", branch = "master", service = "github" }
|
codecov = { repository = "solana-labs/solana", branch = "master", service = "github" }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
ahash = "0.6.1"
|
||||||
base64 = "0.12.3"
|
base64 = "0.12.3"
|
||||||
bincode = "1.3.1"
|
bincode = "1.3.1"
|
||||||
bv = { version = "0.11.1", features = ["serde"] }
|
bv = { version = "0.11.1", features = ["serde"] }
|
||||||
@@ -34,53 +35,54 @@ jsonrpc-http-server = "15.0.0"
|
|||||||
jsonrpc-pubsub = "15.0.0"
|
jsonrpc-pubsub = "15.0.0"
|
||||||
jsonrpc-ws-server = "15.0.0"
|
jsonrpc-ws-server = "15.0.0"
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
|
lru = "0.6.0"
|
||||||
num_cpus = "1.13.0"
|
num_cpus = "1.13.0"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
rand = "0.7.0"
|
rand = "0.7.0"
|
||||||
rand_chacha = "0.2.2"
|
rand_chacha = "0.2.2"
|
||||||
raptorq = "1.4.2"
|
raptorq = "1.4.2"
|
||||||
rayon = "1.4.0"
|
rayon = "1.4.1"
|
||||||
regex = "1.3.9"
|
regex = "1.3.9"
|
||||||
serde = "1.0.112"
|
serde = "1.0.112"
|
||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
serde_json = "1.0.56"
|
serde_json = "1.0.56"
|
||||||
solana-account-decoder = { path = "../account-decoder", version = "1.4.0" }
|
solana-account-decoder = { path = "../account-decoder", version = "1.4.16" }
|
||||||
solana-banks-server = { path = "../banks-server", version = "1.4.0" }
|
solana-banks-server = { path = "../banks-server", version = "1.4.16" }
|
||||||
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "1.4.0" }
|
solana-clap-utils = { path = "../clap-utils", version = "1.4.16" }
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "1.4.0" }
|
solana-client = { path = "../client", version = "1.4.16" }
|
||||||
solana-client = { path = "../client", version = "1.4.0" }
|
solana-faucet = { path = "../faucet", version = "1.4.16" }
|
||||||
solana-faucet = { path = "../faucet", version = "1.4.0" }
|
solana-frozen-abi = { path = "../frozen-abi", version = "1.4.16" }
|
||||||
solana-ledger = { path = "../ledger", version = "1.4.0" }
|
solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "1.4.16" }
|
||||||
solana-logger = { path = "../logger", version = "1.4.0" }
|
solana-ledger = { path = "../ledger", version = "1.4.16" }
|
||||||
solana-merkle-tree = { path = "../merkle-tree", version = "1.4.0" }
|
solana-logger = { path = "../logger", version = "1.4.16" }
|
||||||
solana-metrics = { path = "../metrics", version = "1.4.0" }
|
solana-merkle-tree = { path = "../merkle-tree", version = "1.4.16" }
|
||||||
solana-measure = { path = "../measure", version = "1.4.0" }
|
solana-metrics = { path = "../metrics", version = "1.4.16" }
|
||||||
solana-net-utils = { path = "../net-utils", version = "1.4.0" }
|
solana-measure = { path = "../measure", version = "1.4.16" }
|
||||||
solana-perf = { path = "../perf", version = "1.4.0" }
|
solana-net-utils = { path = "../net-utils", version = "1.4.16" }
|
||||||
solana-runtime = { path = "../runtime", version = "1.4.0" }
|
solana-perf = { path = "../perf", version = "1.4.16" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.4.0" }
|
solana-runtime = { path = "../runtime", version = "1.4.16" }
|
||||||
solana-sdk-macro-frozen-abi = { path = "../sdk/macro-frozen-abi", version = "1.4.0" }
|
solana-sdk = { path = "../sdk", version = "1.4.16" }
|
||||||
solana-stake-program = { path = "../programs/stake", version = "1.4.0" }
|
solana-stake-program = { path = "../programs/stake", version = "1.4.16" }
|
||||||
solana-storage-bigtable = { path = "../storage-bigtable", version = "1.4.0" }
|
solana-storage-bigtable = { path = "../storage-bigtable", version = "1.4.16" }
|
||||||
solana-streamer = { path = "../streamer", version = "1.4.0" }
|
solana-streamer = { path = "../streamer", version = "1.4.16" }
|
||||||
solana-sys-tuner = { path = "../sys-tuner", version = "1.4.0" }
|
solana-sys-tuner = { path = "../sys-tuner", version = "1.4.16" }
|
||||||
solana-transaction-status = { path = "../transaction-status", version = "1.4.0" }
|
solana-transaction-status = { path = "../transaction-status", version = "1.4.16" }
|
||||||
solana-version = { path = "../version", version = "1.4.0" }
|
solana-version = { path = "../version", version = "1.4.16" }
|
||||||
solana-vote-program = { path = "../programs/vote", version = "1.4.0" }
|
solana-vote-program = { path = "../programs/vote", version = "1.4.16" }
|
||||||
solana-vote-signer = { path = "../vote-signer", version = "1.4.0" }
|
solana-vote-signer = { path = "../vote-signer", version = "1.4.16" }
|
||||||
spl-token-v2-0 = { package = "spl-token", version = "=2.0.6", features = ["skip-no-mangle"] }
|
spl-token-v2-0 = { package = "spl-token", version = "=3.0.0", features = ["no-entrypoint"] }
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
tokio = { version = "0.2.22", features = ["full"] }
|
tokio = { version = "0.2", features = ["full"] }
|
||||||
tokio_01 = { version = "0.1", package = "tokio" }
|
tokio_01 = { version = "0.1", package = "tokio" }
|
||||||
tokio_fs_01 = { version = "0.1", package = "tokio-fs" }
|
tokio_fs_01 = { version = "0.1", package = "tokio-fs" }
|
||||||
tokio_io_01 = { version = "0.1", package = "tokio-io" }
|
tokio_io_01 = { version = "0.1", package = "tokio-io" }
|
||||||
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.4.0" }
|
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.4.16" }
|
||||||
trees = "0.2.1"
|
trees = "0.2.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
matches = "0.1.6"
|
matches = "0.1.6"
|
||||||
reqwest = { version = "0.10.6", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
reqwest = { version = "0.10.8", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||||
serial_test = "0.4.0"
|
serial_test = "0.4.0"
|
||||||
serial_test_derive = "0.4.0"
|
serial_test_derive = "0.4.0"
|
||||||
systemstat = "0.1.5"
|
systemstat = "0.1.5"
|
||||||
@@ -94,6 +96,9 @@ name = "banking_stage"
|
|||||||
[[bench]]
|
[[bench]]
|
||||||
name = "blockstore"
|
name = "blockstore"
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "crds"
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "crds_gossip_pull"
|
name = "crds_gossip_pull"
|
||||||
|
|
||||||
|
@@ -20,7 +20,7 @@ use solana_runtime::bank::Bank;
|
|||||||
use solana_sdk::genesis_config::GenesisConfig;
|
use solana_sdk::genesis_config::GenesisConfig;
|
||||||
use solana_sdk::hash::Hash;
|
use solana_sdk::hash::Hash;
|
||||||
use solana_sdk::message::Message;
|
use solana_sdk::message::Message;
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey;
|
||||||
use solana_sdk::signature::Keypair;
|
use solana_sdk::signature::Keypair;
|
||||||
use solana_sdk::signature::Signature;
|
use solana_sdk::signature::Signature;
|
||||||
use solana_sdk::signature::Signer;
|
use solana_sdk::signature::Signer;
|
||||||
@@ -56,7 +56,7 @@ fn bench_consume_buffered(bencher: &mut Bencher) {
|
|||||||
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(100_000);
|
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(100_000);
|
||||||
let bank = Arc::new(Bank::new(&genesis_config));
|
let bank = Arc::new(Bank::new(&genesis_config));
|
||||||
let ledger_path = get_tmp_ledger_path!();
|
let ledger_path = get_tmp_ledger_path!();
|
||||||
let my_pubkey = Pubkey::new_rand();
|
let my_pubkey = pubkey::new_rand();
|
||||||
{
|
{
|
||||||
let blockstore = Arc::new(
|
let blockstore = Arc::new(
|
||||||
Blockstore::open(&ledger_path).expect("Expected to be able to open database ledger"),
|
Blockstore::open(&ledger_path).expect("Expected to be able to open database ledger"),
|
||||||
@@ -94,15 +94,15 @@ fn bench_consume_buffered(bencher: &mut Bencher) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn make_accounts_txs(txes: usize, mint_keypair: &Keypair, hash: Hash) -> Vec<Transaction> {
|
fn make_accounts_txs(txes: usize, mint_keypair: &Keypair, hash: Hash) -> Vec<Transaction> {
|
||||||
let to_pubkey = Pubkey::new_rand();
|
let to_pubkey = pubkey::new_rand();
|
||||||
let dummy = system_transaction::transfer(mint_keypair, &to_pubkey, 1, hash);
|
let dummy = system_transaction::transfer(mint_keypair, &to_pubkey, 1, hash);
|
||||||
(0..txes)
|
(0..txes)
|
||||||
.into_par_iter()
|
.into_par_iter()
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
let mut new = dummy.clone();
|
let mut new = dummy.clone();
|
||||||
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
|
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
|
||||||
new.message.account_keys[0] = Pubkey::new_rand();
|
new.message.account_keys[0] = pubkey::new_rand();
|
||||||
new.message.account_keys[1] = Pubkey::new_rand();
|
new.message.account_keys[1] = pubkey::new_rand();
|
||||||
new.signatures = vec![Signature::new(&sig[0..64])];
|
new.signatures = vec![Signature::new(&sig[0..64])];
|
||||||
new
|
new
|
||||||
})
|
})
|
||||||
@@ -117,7 +117,7 @@ fn make_programs_txs(txes: usize, hash: Hash) -> Vec<Transaction> {
|
|||||||
let mut instructions = vec![];
|
let mut instructions = vec![];
|
||||||
let from_key = Keypair::new();
|
let from_key = Keypair::new();
|
||||||
for _ in 1..progs {
|
for _ in 1..progs {
|
||||||
let to_key = Pubkey::new_rand();
|
let to_key = pubkey::new_rand();
|
||||||
instructions.push(system_instruction::transfer(&from_key.pubkey(), &to_key, 1));
|
instructions.push(system_instruction::transfer(&from_key.pubkey(), &to_key, 1));
|
||||||
}
|
}
|
||||||
let message = Message::new(&instructions, Some(&from_key.pubkey()));
|
let message = Message::new(&instructions, Some(&from_key.pubkey()));
|
||||||
|
@@ -8,7 +8,7 @@ use solana_core::broadcast_stage::{broadcast_shreds, get_broadcast_peers};
|
|||||||
use solana_core::cluster_info::{ClusterInfo, Node};
|
use solana_core::cluster_info::{ClusterInfo, Node};
|
||||||
use solana_core::contact_info::ContactInfo;
|
use solana_core::contact_info::ContactInfo;
|
||||||
use solana_ledger::shred::Shred;
|
use solana_ledger::shred::Shred;
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey;
|
||||||
use solana_sdk::timing::timestamp;
|
use solana_sdk::timing::timestamp;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
@@ -20,7 +20,7 @@ use test::Bencher;
|
|||||||
#[bench]
|
#[bench]
|
||||||
fn broadcast_shreds_bench(bencher: &mut Bencher) {
|
fn broadcast_shreds_bench(bencher: &mut Bencher) {
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
let leader_pubkey = Pubkey::new_rand();
|
let leader_pubkey = pubkey::new_rand();
|
||||||
let leader_info = Node::new_localhost_with_pubkey(&leader_pubkey);
|
let leader_info = Node::new_localhost_with_pubkey(&leader_pubkey);
|
||||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(leader_info.info);
|
let cluster_info = ClusterInfo::new_with_invalid_keypair(leader_info.info);
|
||||||
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
|
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
|
||||||
@@ -30,7 +30,7 @@ fn broadcast_shreds_bench(bencher: &mut Bencher) {
|
|||||||
let mut stakes = HashMap::new();
|
let mut stakes = HashMap::new();
|
||||||
const NUM_PEERS: usize = 200;
|
const NUM_PEERS: usize = 200;
|
||||||
for _ in 0..NUM_PEERS {
|
for _ in 0..NUM_PEERS {
|
||||||
let id = Pubkey::new_rand();
|
let id = pubkey::new_rand();
|
||||||
let contact_info = ContactInfo::new_localhost(&id, timestamp());
|
let contact_info = ContactInfo::new_localhost(&id, timestamp());
|
||||||
cluster_info.insert_info(contact_info);
|
cluster_info.insert_info(contact_info);
|
||||||
stakes.insert(id, thread_rng().gen_range(1, NUM_PEERS) as u64);
|
stakes.insert(id, thread_rng().gen_range(1, NUM_PEERS) as u64);
|
||||||
|
31
core/benches/crds.rs
Normal file
31
core/benches/crds.rs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#![feature(test)]
|
||||||
|
|
||||||
|
extern crate test;
|
||||||
|
|
||||||
|
use rand::{thread_rng, Rng};
|
||||||
|
use rayon::ThreadPoolBuilder;
|
||||||
|
use solana_core::crds::Crds;
|
||||||
|
use solana_core::crds_gossip_pull::CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS;
|
||||||
|
use solana_core::crds_value::CrdsValue;
|
||||||
|
use solana_sdk::pubkey::Pubkey;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use test::Bencher;
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_find_old_labels(bencher: &mut Bencher) {
|
||||||
|
let thread_pool = ThreadPoolBuilder::new().build().unwrap();
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
let mut crds = Crds::default();
|
||||||
|
let now = CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS + CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS / 1000;
|
||||||
|
std::iter::repeat_with(|| (CrdsValue::new_rand(&mut rng, None), rng.gen_range(0, now)))
|
||||||
|
.take(50_000)
|
||||||
|
.for_each(|(v, ts)| assert!(crds.insert(v, ts).is_ok()));
|
||||||
|
let mut timeouts = HashMap::new();
|
||||||
|
timeouts.insert(Pubkey::default(), CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS);
|
||||||
|
bencher.iter(|| {
|
||||||
|
let out = crds.find_old_labels(&thread_pool, now, &timeouts);
|
||||||
|
assert!(out.len() > 10);
|
||||||
|
assert!(out.len() < 250);
|
||||||
|
out
|
||||||
|
});
|
||||||
|
}
|
@@ -8,13 +8,13 @@ use solana_core::cluster_info::MAX_BLOOM_SIZE;
|
|||||||
use solana_core::crds::Crds;
|
use solana_core::crds::Crds;
|
||||||
use solana_core::crds_gossip_pull::{CrdsFilter, CrdsGossipPull};
|
use solana_core::crds_gossip_pull::{CrdsFilter, CrdsGossipPull};
|
||||||
use solana_core::crds_value::CrdsValue;
|
use solana_core::crds_value::CrdsValue;
|
||||||
use solana_sdk::hash::Hash;
|
use solana_sdk::hash;
|
||||||
use test::Bencher;
|
use test::Bencher;
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn bench_hash_as_u64(bencher: &mut Bencher) {
|
fn bench_hash_as_u64(bencher: &mut Bencher) {
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
let hashes: Vec<_> = std::iter::repeat_with(|| Hash::new_rand(&mut rng))
|
let hashes: Vec<_> = std::iter::repeat_with(|| hash::new_rand(&mut rng))
|
||||||
.take(1000)
|
.take(1000)
|
||||||
.collect();
|
.collect();
|
||||||
bencher.iter(|| {
|
bencher.iter(|| {
|
||||||
@@ -34,12 +34,12 @@ fn bench_build_crds_filters(bencher: &mut Bencher) {
|
|||||||
for _ in 0..50_000 {
|
for _ in 0..50_000 {
|
||||||
crds_gossip_pull
|
crds_gossip_pull
|
||||||
.purged_values
|
.purged_values
|
||||||
.push_back((Hash::new_rand(&mut rng), rng.gen()));
|
.push_back((solana_sdk::hash::new_rand(&mut rng), rng.gen()));
|
||||||
}
|
}
|
||||||
let mut num_inserts = 0;
|
let mut num_inserts = 0;
|
||||||
for _ in 0..90_000 {
|
for _ in 0..90_000 {
|
||||||
if crds
|
if crds
|
||||||
.insert(CrdsValue::new_rand(&mut rng), rng.gen())
|
.insert(CrdsValue::new_rand(&mut rng, None), rng.gen())
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
num_inserts += 1;
|
num_inserts += 1;
|
||||||
|
@@ -7,14 +7,14 @@ use solana_core::contact_info::ContactInfo;
|
|||||||
use solana_core::crds::VersionedCrdsValue;
|
use solana_core::crds::VersionedCrdsValue;
|
||||||
use solana_core::crds_shards::CrdsShards;
|
use solana_core::crds_shards::CrdsShards;
|
||||||
use solana_core::crds_value::{CrdsData, CrdsValue};
|
use solana_core::crds_value::{CrdsData, CrdsValue};
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey;
|
||||||
use solana_sdk::timing::timestamp;
|
use solana_sdk::timing::timestamp;
|
||||||
use test::Bencher;
|
use test::Bencher;
|
||||||
|
|
||||||
const CRDS_SHARDS_BITS: u32 = 8;
|
const CRDS_SHARDS_BITS: u32 = 8;
|
||||||
|
|
||||||
fn new_test_crds_value() -> VersionedCrdsValue {
|
fn new_test_crds_value() -> VersionedCrdsValue {
|
||||||
let data = CrdsData::ContactInfo(ContactInfo::new_localhost(&Pubkey::new_rand(), timestamp()));
|
let data = CrdsData::ContactInfo(ContactInfo::new_localhost(&pubkey::new_rand(), timestamp()));
|
||||||
VersionedCrdsValue::new(timestamp(), CrdsValue::new_unsigned(data))
|
VersionedCrdsValue::new(timestamp(), CrdsValue::new_unsigned(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -7,14 +7,18 @@ use log::*;
|
|||||||
use solana_core::cluster_info::{ClusterInfo, Node};
|
use solana_core::cluster_info::{ClusterInfo, Node};
|
||||||
use solana_core::contact_info::ContactInfo;
|
use solana_core::contact_info::ContactInfo;
|
||||||
use solana_core::retransmit_stage::retransmitter;
|
use solana_core::retransmit_stage::retransmitter;
|
||||||
|
use solana_ledger::entry::Entry;
|
||||||
use solana_ledger::genesis_utils::{create_genesis_config, GenesisConfigInfo};
|
use solana_ledger::genesis_utils::{create_genesis_config, GenesisConfigInfo};
|
||||||
use solana_ledger::leader_schedule_cache::LeaderScheduleCache;
|
use solana_ledger::leader_schedule_cache::LeaderScheduleCache;
|
||||||
|
use solana_ledger::shred::Shredder;
|
||||||
use solana_measure::measure::Measure;
|
use solana_measure::measure::Measure;
|
||||||
use solana_perf::packet::to_packets_chunked;
|
use solana_perf::packet::{Packet, Packets};
|
||||||
use solana_perf::test_tx::test_tx;
|
|
||||||
use solana_runtime::bank::Bank;
|
use solana_runtime::bank::Bank;
|
||||||
use solana_runtime::bank_forks::BankForks;
|
use solana_runtime::bank_forks::BankForks;
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::hash::Hash;
|
||||||
|
use solana_sdk::pubkey;
|
||||||
|
use solana_sdk::signature::{Keypair, Signer};
|
||||||
|
use solana_sdk::system_transaction;
|
||||||
use solana_sdk::timing::timestamp;
|
use solana_sdk::timing::timestamp;
|
||||||
use std::net::UdpSocket;
|
use std::net::UdpSocket;
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
@@ -34,7 +38,7 @@ fn bench_retransmitter(bencher: &mut Bencher) {
|
|||||||
const NUM_PEERS: usize = 4;
|
const NUM_PEERS: usize = 4;
|
||||||
let mut peer_sockets = Vec::new();
|
let mut peer_sockets = Vec::new();
|
||||||
for _ in 0..NUM_PEERS {
|
for _ in 0..NUM_PEERS {
|
||||||
let id = Pubkey::new_rand();
|
let id = pubkey::new_rand();
|
||||||
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
|
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
|
||||||
let mut contact_info = ContactInfo::new_localhost(&id, timestamp());
|
let mut contact_info = ContactInfo::new_localhost(&id, timestamp());
|
||||||
contact_info.tvu = socket.local_addr().unwrap();
|
contact_info.tvu = socket.local_addr().unwrap();
|
||||||
@@ -63,14 +67,24 @@ fn bench_retransmitter(bencher: &mut Bencher) {
|
|||||||
let leader_schedule_cache = Arc::new(LeaderScheduleCache::new_from_bank(&bank));
|
let leader_schedule_cache = Arc::new(LeaderScheduleCache::new_from_bank(&bank));
|
||||||
|
|
||||||
// To work reliably with higher values, this needs larger udp rmem size
|
// To work reliably with higher values, this needs larger udp rmem size
|
||||||
let tx = test_tx();
|
let entries: Vec<_> = (0..5)
|
||||||
const NUM_PACKETS: usize = 50;
|
.map(|_| {
|
||||||
let chunk_size = NUM_PACKETS / (4 * NUM_THREADS);
|
let keypair0 = Keypair::new();
|
||||||
let batches = to_packets_chunked(
|
let keypair1 = Keypair::new();
|
||||||
&std::iter::repeat(tx).take(NUM_PACKETS).collect::<Vec<_>>(),
|
let tx0 =
|
||||||
chunk_size,
|
system_transaction::transfer(&keypair0, &keypair1.pubkey(), 1, Hash::default());
|
||||||
);
|
Entry::new(&Hash::default(), 1, vec![tx0])
|
||||||
info!("batches: {}", batches.len());
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let keypair = Arc::new(Keypair::new());
|
||||||
|
let slot = 0;
|
||||||
|
let parent = 0;
|
||||||
|
let shredder =
|
||||||
|
Shredder::new(slot, parent, 0.0, keypair, 0, 0).expect("Failed to create entry shredder");
|
||||||
|
let mut data_shreds = shredder.entries_to_shreds(&entries, true, 0).0;
|
||||||
|
|
||||||
|
let num_packets = data_shreds.len();
|
||||||
|
|
||||||
let retransmitter_handles = retransmitter(
|
let retransmitter_handles = retransmitter(
|
||||||
Arc::new(sockets),
|
Arc::new(sockets),
|
||||||
@@ -80,6 +94,8 @@ fn bench_retransmitter(bencher: &mut Bencher) {
|
|||||||
packet_receiver,
|
packet_receiver,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let mut index = 0;
|
||||||
|
let mut slot = 0;
|
||||||
let total = Arc::new(AtomicUsize::new(0));
|
let total = Arc::new(AtomicUsize::new(0));
|
||||||
bencher.iter(move || {
|
bencher.iter(move || {
|
||||||
let peer_sockets1 = peer_sockets.clone();
|
let peer_sockets1 = peer_sockets.clone();
|
||||||
@@ -96,7 +112,7 @@ fn bench_retransmitter(bencher: &mut Bencher) {
|
|||||||
while peer_sockets2[p].recv(&mut buf).is_ok() {
|
while peer_sockets2[p].recv(&mut buf).is_ok() {
|
||||||
total2.fetch_add(1, Ordering::Relaxed);
|
total2.fetch_add(1, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
if total2.load(Ordering::Relaxed) >= NUM_PACKETS {
|
if total2.load(Ordering::Relaxed) >= num_packets {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
info!("{} recv", total2.load(Ordering::Relaxed));
|
info!("{} recv", total2.load(Ordering::Relaxed));
|
||||||
@@ -107,9 +123,17 @@ fn bench_retransmitter(bencher: &mut Bencher) {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
for packets in batches.clone() {
|
for shred in data_shreds.iter_mut() {
|
||||||
packet_sender.send(packets).unwrap();
|
shred.set_slot(slot);
|
||||||
|
shred.set_index(index);
|
||||||
|
index += 1;
|
||||||
|
index %= 200;
|
||||||
|
let mut p = Packet::default();
|
||||||
|
shred.copy_to_packet(&mut p);
|
||||||
|
let _ = packet_sender.send(Packets::new(vec![p]));
|
||||||
}
|
}
|
||||||
|
slot += 1;
|
||||||
|
|
||||||
info!("sent...");
|
info!("sent...");
|
||||||
|
|
||||||
let mut join_time = Measure::start("join");
|
let mut join_time = Measure::start("join");
|
||||||
|
@@ -123,8 +123,14 @@ fn bench_shredder_coding(bencher: &mut Bencher) {
|
|||||||
let symbol_count = MAX_DATA_SHREDS_PER_FEC_BLOCK as usize;
|
let symbol_count = MAX_DATA_SHREDS_PER_FEC_BLOCK as usize;
|
||||||
let data_shreds = make_shreds(symbol_count);
|
let data_shreds = make_shreds(symbol_count);
|
||||||
bencher.iter(|| {
|
bencher.iter(|| {
|
||||||
Shredder::generate_coding_shreds(0, RECOMMENDED_FEC_RATE, &data_shreds[..symbol_count], 0)
|
Shredder::generate_coding_shreds(
|
||||||
.len();
|
0,
|
||||||
|
RECOMMENDED_FEC_RATE,
|
||||||
|
&data_shreds[..symbol_count],
|
||||||
|
0,
|
||||||
|
symbol_count,
|
||||||
|
)
|
||||||
|
.len();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,8 +138,13 @@ fn bench_shredder_coding(bencher: &mut Bencher) {
|
|||||||
fn bench_shredder_decoding(bencher: &mut Bencher) {
|
fn bench_shredder_decoding(bencher: &mut Bencher) {
|
||||||
let symbol_count = MAX_DATA_SHREDS_PER_FEC_BLOCK as usize;
|
let symbol_count = MAX_DATA_SHREDS_PER_FEC_BLOCK as usize;
|
||||||
let data_shreds = make_shreds(symbol_count);
|
let data_shreds = make_shreds(symbol_count);
|
||||||
let coding_shreds =
|
let coding_shreds = Shredder::generate_coding_shreds(
|
||||||
Shredder::generate_coding_shreds(0, RECOMMENDED_FEC_RATE, &data_shreds[..symbol_count], 0);
|
0,
|
||||||
|
RECOMMENDED_FEC_RATE,
|
||||||
|
&data_shreds[..symbol_count],
|
||||||
|
0,
|
||||||
|
symbol_count,
|
||||||
|
);
|
||||||
bencher.iter(|| {
|
bencher.iter(|| {
|
||||||
Shredder::try_recovery(
|
Shredder::try_recovery(
|
||||||
coding_shreds[..].to_vec(),
|
coding_shreds[..].to_vec(),
|
||||||
|
@@ -1 +1 @@
|
|||||||
../sdk/build.rs
|
../frozen-abi/build.rs
|
@@ -204,6 +204,7 @@ mod tests {
|
|||||||
{
|
{
|
||||||
let message = make_accounts_hashes_message(&validator1, vec![(0, hash1)]).unwrap();
|
let message = make_accounts_hashes_message(&validator1, vec![(0, hash1)]).unwrap();
|
||||||
cluster_info.push_message(message);
|
cluster_info.push_message(message);
|
||||||
|
cluster_info.flush_push_queue();
|
||||||
}
|
}
|
||||||
slot_to_hash.insert(0, hash2);
|
slot_to_hash.insert(0, hash2);
|
||||||
trusted_validators.insert(validator1.pubkey());
|
trusted_validators.insert(validator1.pubkey());
|
||||||
@@ -254,6 +255,7 @@ mod tests {
|
|||||||
100,
|
100,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
cluster_info.flush_push_queue();
|
||||||
let cluster_hashes = cluster_info
|
let cluster_hashes = cluster_info
|
||||||
.get_accounts_hash_for_node(&keypair.pubkey(), |c| c.clone())
|
.get_accounts_hash_for_node(&keypair.pubkey(), |c| c.clone())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@@ -1,151 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
consensus::{ComputedBankState, Tower},
|
|
||||||
fork_choice::ForkChoice,
|
|
||||||
progress_map::{ForkStats, ProgressMap},
|
|
||||||
};
|
|
||||||
use solana_runtime::{bank::Bank, bank_forks::BankForks};
|
|
||||||
use solana_sdk::timing;
|
|
||||||
use std::time::Instant;
|
|
||||||
use std::{
|
|
||||||
collections::{HashMap, HashSet},
|
|
||||||
sync::{Arc, RwLock},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct BankWeightForkChoice {}
|
|
||||||
|
|
||||||
impl ForkChoice for BankWeightForkChoice {
|
|
||||||
fn compute_bank_stats(
|
|
||||||
&mut self,
|
|
||||||
bank: &Bank,
|
|
||||||
_tower: &Tower,
|
|
||||||
progress: &mut ProgressMap,
|
|
||||||
computed_bank_state: &ComputedBankState,
|
|
||||||
) {
|
|
||||||
let bank_slot = bank.slot();
|
|
||||||
// Only time progress map should be missing a bank slot
|
|
||||||
// is if this node was the leader for this slot as those banks
|
|
||||||
// are not replayed in replay_active_banks()
|
|
||||||
let parent_weight = bank
|
|
||||||
.parent()
|
|
||||||
.and_then(|b| progress.get(&b.slot()))
|
|
||||||
.map(|x| x.fork_stats.fork_weight)
|
|
||||||
.unwrap_or(0);
|
|
||||||
|
|
||||||
let stats = progress
|
|
||||||
.get_fork_stats_mut(bank_slot)
|
|
||||||
.expect("All frozen banks must exist in the Progress map");
|
|
||||||
|
|
||||||
let ComputedBankState { bank_weight, .. } = computed_bank_state;
|
|
||||||
stats.weight = *bank_weight;
|
|
||||||
stats.fork_weight = stats.weight + parent_weight;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns:
|
|
||||||
// 1) The heaviest overall bank
|
|
||||||
// 2) The heaviest bank on the same fork as the last vote (doesn't require a
|
|
||||||
// switching proof to vote for)
|
|
||||||
fn select_forks(
|
|
||||||
&self,
|
|
||||||
frozen_banks: &[Arc<Bank>],
|
|
||||||
tower: &Tower,
|
|
||||||
progress: &ProgressMap,
|
|
||||||
ancestors: &HashMap<u64, HashSet<u64>>,
|
|
||||||
_bank_forks: &RwLock<BankForks>,
|
|
||||||
) -> (Arc<Bank>, Option<Arc<Bank>>) {
|
|
||||||
let tower_start = Instant::now();
|
|
||||||
assert!(!frozen_banks.is_empty());
|
|
||||||
let num_frozen_banks = frozen_banks.len();
|
|
||||||
|
|
||||||
trace!("frozen_banks {}", frozen_banks.len());
|
|
||||||
let num_old_banks = frozen_banks
|
|
||||||
.iter()
|
|
||||||
.filter(|b| b.slot() < tower.root().unwrap_or(0))
|
|
||||||
.count();
|
|
||||||
|
|
||||||
let last_voted_slot = tower.last_voted_slot();
|
|
||||||
let mut heaviest_bank_on_same_fork = None;
|
|
||||||
let mut heaviest_same_fork_weight = 0;
|
|
||||||
let stats: Vec<&ForkStats> = frozen_banks
|
|
||||||
.iter()
|
|
||||||
.map(|bank| {
|
|
||||||
// Only time progress map should be missing a bank slot
|
|
||||||
// is if this node was the leader for this slot as those banks
|
|
||||||
// are not replayed in replay_active_banks()
|
|
||||||
let stats = progress
|
|
||||||
.get_fork_stats(bank.slot())
|
|
||||||
.expect("All frozen banks must exist in the Progress map");
|
|
||||||
|
|
||||||
if let Some(last_voted_slot) = last_voted_slot {
|
|
||||||
if ancestors
|
|
||||||
.get(&bank.slot())
|
|
||||||
.expect("Entry in frozen banks must exist in ancestors")
|
|
||||||
.contains(&last_voted_slot)
|
|
||||||
{
|
|
||||||
// Descendant of last vote cannot be locked out
|
|
||||||
assert!(!stats.is_locked_out);
|
|
||||||
|
|
||||||
// ancestors(slot) should not contain the slot itself,
|
|
||||||
// so we should never get the same bank as the last vote
|
|
||||||
assert_ne!(bank.slot(), last_voted_slot);
|
|
||||||
// highest weight, lowest slot first. frozen_banks is sorted
|
|
||||||
// from least slot to greatest slot, so if two banks have
|
|
||||||
// the same fork weight, the lower slot will be picked
|
|
||||||
if stats.fork_weight > heaviest_same_fork_weight {
|
|
||||||
heaviest_bank_on_same_fork = Some(bank.clone());
|
|
||||||
heaviest_same_fork_weight = stats.fork_weight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stats
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let num_not_recent = stats.iter().filter(|s| !s.is_recent).count();
|
|
||||||
let num_has_voted = stats.iter().filter(|s| s.has_voted).count();
|
|
||||||
let num_empty = stats.iter().filter(|s| s.is_empty).count();
|
|
||||||
let num_threshold_failure = stats.iter().filter(|s| !s.vote_threshold).count();
|
|
||||||
let num_votable_threshold_failure = stats
|
|
||||||
.iter()
|
|
||||||
.filter(|s| s.is_recent && !s.has_voted && !s.vote_threshold)
|
|
||||||
.count();
|
|
||||||
|
|
||||||
let mut candidates: Vec<_> = frozen_banks.iter().zip(stats.iter()).collect();
|
|
||||||
|
|
||||||
//highest weight, lowest slot first
|
|
||||||
candidates.sort_by_key(|b| (b.1.fork_weight, 0i64 - b.0.slot() as i64));
|
|
||||||
let rv = candidates
|
|
||||||
.last()
|
|
||||||
.expect("frozen banks was nonempty so candidates must also be nonempty");
|
|
||||||
let ms = timing::duration_as_ms(&tower_start.elapsed());
|
|
||||||
let weights: Vec<(u128, u64, u64)> = candidates
|
|
||||||
.iter()
|
|
||||||
.map(|x| (x.1.weight, x.0.slot(), x.1.block_height))
|
|
||||||
.collect();
|
|
||||||
debug!(
|
|
||||||
"@{:?} tower duration: {:?} len: {}/{} weights: {:?}",
|
|
||||||
timing::timestamp(),
|
|
||||||
ms,
|
|
||||||
candidates.len(),
|
|
||||||
stats.iter().filter(|s| !s.has_voted).count(),
|
|
||||||
weights,
|
|
||||||
);
|
|
||||||
datapoint_debug!(
|
|
||||||
"replay_stage-select_forks",
|
|
||||||
("frozen_banks", num_frozen_banks as i64, i64),
|
|
||||||
("not_recent", num_not_recent as i64, i64),
|
|
||||||
("has_voted", num_has_voted as i64, i64),
|
|
||||||
("old_banks", num_old_banks as i64, i64),
|
|
||||||
("empty_banks", num_empty as i64, i64),
|
|
||||||
("threshold_failure", num_threshold_failure as i64, i64),
|
|
||||||
(
|
|
||||||
"votable_threshold_failure",
|
|
||||||
num_votable_threshold_failure as i64,
|
|
||||||
i64
|
|
||||||
),
|
|
||||||
("tower_duration", ms as i64, i64),
|
|
||||||
);
|
|
||||||
|
|
||||||
(rv.0.clone(), heaviest_bank_on_same_fork)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -23,7 +23,7 @@ use solana_perf::{
|
|||||||
};
|
};
|
||||||
use solana_runtime::{
|
use solana_runtime::{
|
||||||
accounts_db::ErrorCounters,
|
accounts_db::ErrorCounters,
|
||||||
bank::{Bank, TransactionBalancesSet, TransactionProcessResult},
|
bank::{Bank, TransactionBalancesSet, TransactionCheckResult, TransactionExecutionResult},
|
||||||
bank_utils,
|
bank_utils,
|
||||||
transaction_batch::TransactionBatch,
|
transaction_batch::TransactionBatch,
|
||||||
vote_sender_types::ReplayVoteSender,
|
vote_sender_types::ReplayVoteSender,
|
||||||
@@ -460,7 +460,7 @@ impl BankingStage {
|
|||||||
fn record_transactions(
|
fn record_transactions(
|
||||||
bank_slot: Slot,
|
bank_slot: Slot,
|
||||||
txs: &[Transaction],
|
txs: &[Transaction],
|
||||||
results: &[TransactionProcessResult],
|
results: &[TransactionExecutionResult],
|
||||||
poh: &Arc<Mutex<PohRecorder>>,
|
poh: &Arc<Mutex<PohRecorder>>,
|
||||||
) -> (Result<usize, PohRecorderError>, Vec<usize>) {
|
) -> (Result<usize, PohRecorderError>, Vec<usize>) {
|
||||||
let mut processed_generation = Measure::start("record::process_generation");
|
let mut processed_generation = Measure::start("record::process_generation");
|
||||||
@@ -534,13 +534,14 @@ impl BankingStage {
|
|||||||
mut loaded_accounts,
|
mut loaded_accounts,
|
||||||
results,
|
results,
|
||||||
inner_instructions,
|
inner_instructions,
|
||||||
|
transaction_logs,
|
||||||
mut retryable_txs,
|
mut retryable_txs,
|
||||||
tx_count,
|
tx_count,
|
||||||
signature_count,
|
signature_count,
|
||||||
) = bank.load_and_execute_transactions(
|
) = bank.load_and_execute_transactions(
|
||||||
batch,
|
batch,
|
||||||
MAX_PROCESSING_AGE,
|
MAX_PROCESSING_AGE,
|
||||||
None,
|
transaction_status_sender.is_some(),
|
||||||
transaction_status_sender.is_some(),
|
transaction_status_sender.is_some(),
|
||||||
);
|
);
|
||||||
load_execute_time.stop();
|
load_execute_time.stop();
|
||||||
@@ -577,9 +578,10 @@ impl BankingStage {
|
|||||||
bank.clone(),
|
bank.clone(),
|
||||||
batch.transactions(),
|
batch.transactions(),
|
||||||
batch.iteration_order_vec(),
|
batch.iteration_order_vec(),
|
||||||
tx_results.processing_results,
|
tx_results.execution_results,
|
||||||
TransactionBalancesSet::new(pre_balances, post_balances),
|
TransactionBalancesSet::new(pre_balances, post_balances),
|
||||||
inner_instructions,
|
inner_instructions,
|
||||||
|
transaction_logs,
|
||||||
sender,
|
sender,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -717,7 +719,7 @@ impl BankingStage {
|
|||||||
// This function returns a vector containing index of all valid transactions. A valid
|
// This function returns a vector containing index of all valid transactions. A valid
|
||||||
// transaction has result Ok() as the value
|
// transaction has result Ok() as the value
|
||||||
fn filter_valid_transaction_indexes(
|
fn filter_valid_transaction_indexes(
|
||||||
valid_txs: &[TransactionProcessResult],
|
valid_txs: &[TransactionCheckResult],
|
||||||
transaction_indexes: &[usize],
|
transaction_indexes: &[usize],
|
||||||
) -> Vec<usize> {
|
) -> Vec<usize> {
|
||||||
let valid_transactions = valid_txs
|
let valid_transactions = valid_txs
|
||||||
@@ -1091,7 +1093,6 @@ mod tests {
|
|||||||
get_tmp_ledger_path,
|
get_tmp_ledger_path,
|
||||||
};
|
};
|
||||||
use solana_perf::packet::to_packets;
|
use solana_perf::packet::to_packets;
|
||||||
use solana_runtime::bank::HashAgeKind;
|
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
instruction::InstructionError,
|
instruction::InstructionError,
|
||||||
signature::{Keypair, Signer},
|
signature::{Keypair, Signer},
|
||||||
@@ -1244,16 +1245,16 @@ mod tests {
|
|||||||
bank.process_transaction(&fund_tx).unwrap();
|
bank.process_transaction(&fund_tx).unwrap();
|
||||||
|
|
||||||
// good tx
|
// good tx
|
||||||
let to = Pubkey::new_rand();
|
let to = solana_sdk::pubkey::new_rand();
|
||||||
let tx = system_transaction::transfer(&mint_keypair, &to, 1, start_hash);
|
let tx = system_transaction::transfer(&mint_keypair, &to, 1, start_hash);
|
||||||
|
|
||||||
// good tx, but no verify
|
// good tx, but no verify
|
||||||
let to2 = Pubkey::new_rand();
|
let to2 = solana_sdk::pubkey::new_rand();
|
||||||
let tx_no_ver = system_transaction::transfer(&keypair, &to2, 2, start_hash);
|
let tx_no_ver = system_transaction::transfer(&keypair, &to2, 2, start_hash);
|
||||||
|
|
||||||
// bad tx, AccountNotFound
|
// bad tx, AccountNotFound
|
||||||
let keypair = Keypair::new();
|
let keypair = Keypair::new();
|
||||||
let to3 = Pubkey::new_rand();
|
let to3 = solana_sdk::pubkey::new_rand();
|
||||||
let tx_anf = system_transaction::transfer(&keypair, &to3, 1, start_hash);
|
let tx_anf = system_transaction::transfer(&keypair, &to3, 1, start_hash);
|
||||||
|
|
||||||
// send 'em over
|
// send 'em over
|
||||||
@@ -1446,19 +1447,16 @@ mod tests {
|
|||||||
let poh_recorder = Arc::new(Mutex::new(poh_recorder));
|
let poh_recorder = Arc::new(Mutex::new(poh_recorder));
|
||||||
|
|
||||||
poh_recorder.lock().unwrap().set_working_bank(working_bank);
|
poh_recorder.lock().unwrap().set_working_bank(working_bank);
|
||||||
let pubkey = Pubkey::new_rand();
|
let pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let keypair2 = Keypair::new();
|
let keypair2 = Keypair::new();
|
||||||
let pubkey2 = Pubkey::new_rand();
|
let pubkey2 = solana_sdk::pubkey::new_rand();
|
||||||
|
|
||||||
let transactions = vec![
|
let transactions = vec![
|
||||||
system_transaction::transfer(&mint_keypair, &pubkey, 1, genesis_config.hash()),
|
system_transaction::transfer(&mint_keypair, &pubkey, 1, genesis_config.hash()),
|
||||||
system_transaction::transfer(&keypair2, &pubkey2, 1, genesis_config.hash()),
|
system_transaction::transfer(&keypair2, &pubkey2, 1, genesis_config.hash()),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut results = vec![
|
let mut results = vec![(Ok(()), None), (Ok(()), None)];
|
||||||
(Ok(()), Some(HashAgeKind::Extant)),
|
|
||||||
(Ok(()), Some(HashAgeKind::Extant)),
|
|
||||||
];
|
|
||||||
let _ = BankingStage::record_transactions(
|
let _ = BankingStage::record_transactions(
|
||||||
bank.slot(),
|
bank.slot(),
|
||||||
&transactions,
|
&transactions,
|
||||||
@@ -1474,7 +1472,7 @@ mod tests {
|
|||||||
1,
|
1,
|
||||||
SystemError::ResultWithNegativeLamports.into(),
|
SystemError::ResultWithNegativeLamports.into(),
|
||||||
)),
|
)),
|
||||||
Some(HashAgeKind::Extant),
|
None,
|
||||||
);
|
);
|
||||||
let (res, retryable) = BankingStage::record_transactions(
|
let (res, retryable) = BankingStage::record_transactions(
|
||||||
bank.slot(),
|
bank.slot(),
|
||||||
@@ -1526,7 +1524,7 @@ mod tests {
|
|||||||
mint_keypair,
|
mint_keypair,
|
||||||
..
|
..
|
||||||
} = create_genesis_config(10_000);
|
} = create_genesis_config(10_000);
|
||||||
let pubkey = Pubkey::new_rand();
|
let pubkey = solana_sdk::pubkey::new_rand();
|
||||||
|
|
||||||
let transactions = vec![
|
let transactions = vec![
|
||||||
None,
|
None,
|
||||||
@@ -1607,7 +1605,7 @@ mod tests {
|
|||||||
mint_keypair,
|
mint_keypair,
|
||||||
..
|
..
|
||||||
} = create_genesis_config(10_000);
|
} = create_genesis_config(10_000);
|
||||||
let pubkey = Pubkey::new_rand();
|
let pubkey = solana_sdk::pubkey::new_rand();
|
||||||
|
|
||||||
let transactions = vec![
|
let transactions = vec![
|
||||||
system_transaction::transfer(&mint_keypair, &pubkey, 1, genesis_config.hash()),
|
system_transaction::transfer(&mint_keypair, &pubkey, 1, genesis_config.hash()),
|
||||||
@@ -1650,10 +1648,10 @@ mod tests {
|
|||||||
&[
|
&[
|
||||||
(Err(TransactionError::BlockhashNotFound), None),
|
(Err(TransactionError::BlockhashNotFound), None),
|
||||||
(Err(TransactionError::BlockhashNotFound), None),
|
(Err(TransactionError::BlockhashNotFound), None),
|
||||||
(Ok(()), Some(HashAgeKind::Extant)),
|
(Ok(()), None),
|
||||||
(Err(TransactionError::BlockhashNotFound), None),
|
(Err(TransactionError::BlockhashNotFound), None),
|
||||||
(Ok(()), Some(HashAgeKind::Extant)),
|
(Ok(()), None),
|
||||||
(Ok(()), Some(HashAgeKind::Extant)),
|
(Ok(()), None),
|
||||||
],
|
],
|
||||||
&[2, 4, 5, 9, 11, 13]
|
&[2, 4, 5, 9, 11, 13]
|
||||||
),
|
),
|
||||||
@@ -1663,12 +1661,12 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
BankingStage::filter_valid_transaction_indexes(
|
BankingStage::filter_valid_transaction_indexes(
|
||||||
&[
|
&[
|
||||||
(Ok(()), Some(HashAgeKind::Extant)),
|
(Ok(()), None),
|
||||||
(Err(TransactionError::BlockhashNotFound), None),
|
(Err(TransactionError::BlockhashNotFound), None),
|
||||||
(Err(TransactionError::BlockhashNotFound), None),
|
(Err(TransactionError::BlockhashNotFound), None),
|
||||||
(Ok(()), Some(HashAgeKind::Extant)),
|
(Ok(()), None),
|
||||||
(Ok(()), Some(HashAgeKind::Extant)),
|
(Ok(()), None),
|
||||||
(Ok(()), Some(HashAgeKind::Extant)),
|
(Ok(()), None),
|
||||||
],
|
],
|
||||||
&[1, 6, 7, 9, 31, 43]
|
&[1, 6, 7, 9, 31, 43]
|
||||||
),
|
),
|
||||||
@@ -1678,8 +1676,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_should_process_or_forward_packets() {
|
fn test_should_process_or_forward_packets() {
|
||||||
let my_pubkey = Pubkey::new_rand();
|
let my_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let my_pubkey1 = Pubkey::new_rand();
|
let my_pubkey1 = solana_sdk::pubkey::new_rand();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
BankingStage::consume_or_forward_packets(&my_pubkey, None, true, false,),
|
BankingStage::consume_or_forward_packets(&my_pubkey, None, true, false,),
|
||||||
@@ -1725,7 +1723,7 @@ mod tests {
|
|||||||
..
|
..
|
||||||
} = create_genesis_config(10_000);
|
} = create_genesis_config(10_000);
|
||||||
let bank = Arc::new(Bank::new(&genesis_config));
|
let bank = Arc::new(Bank::new(&genesis_config));
|
||||||
let pubkey = Pubkey::new_rand();
|
let pubkey = solana_sdk::pubkey::new_rand();
|
||||||
|
|
||||||
let transactions = vec![system_transaction::transfer(
|
let transactions = vec![system_transaction::transfer(
|
||||||
&mint_keypair,
|
&mint_keypair,
|
||||||
@@ -1822,8 +1820,8 @@ mod tests {
|
|||||||
..
|
..
|
||||||
} = create_genesis_config(10_000);
|
} = create_genesis_config(10_000);
|
||||||
let bank = Arc::new(Bank::new(&genesis_config));
|
let bank = Arc::new(Bank::new(&genesis_config));
|
||||||
let pubkey = Pubkey::new_rand();
|
let pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let pubkey1 = Pubkey::new_rand();
|
let pubkey1 = solana_sdk::pubkey::new_rand();
|
||||||
|
|
||||||
let transactions = vec![
|
let transactions = vec![
|
||||||
system_transaction::transfer(&mint_keypair, &pubkey, 1, genesis_config.hash()),
|
system_transaction::transfer(&mint_keypair, &pubkey, 1, genesis_config.hash()),
|
||||||
@@ -1918,7 +1916,7 @@ mod tests {
|
|||||||
} = create_genesis_config(10_000);
|
} = create_genesis_config(10_000);
|
||||||
let bank = Arc::new(Bank::new(&genesis_config));
|
let bank = Arc::new(Bank::new(&genesis_config));
|
||||||
|
|
||||||
let pubkey = Pubkey::new_rand();
|
let pubkey = solana_sdk::pubkey::new_rand();
|
||||||
|
|
||||||
let transactions =
|
let transactions =
|
||||||
vec![
|
vec![
|
||||||
@@ -1936,7 +1934,7 @@ mod tests {
|
|||||||
bank.slot(),
|
bank.slot(),
|
||||||
Some((4, 4)),
|
Some((4, 4)),
|
||||||
bank.ticks_per_slot(),
|
bank.ticks_per_slot(),
|
||||||
&Pubkey::new_rand(),
|
&solana_sdk::pubkey::new_rand(),
|
||||||
&Arc::new(blockstore),
|
&Arc::new(blockstore),
|
||||||
&Arc::new(LeaderScheduleCache::new_from_bank(&bank)),
|
&Arc::new(LeaderScheduleCache::new_from_bank(&bank)),
|
||||||
&Arc::new(PohConfig::default()),
|
&Arc::new(PohConfig::default()),
|
||||||
@@ -1976,8 +1974,8 @@ mod tests {
|
|||||||
..
|
..
|
||||||
} = create_genesis_config(10_000);
|
} = create_genesis_config(10_000);
|
||||||
let bank = Arc::new(Bank::new(&genesis_config));
|
let bank = Arc::new(Bank::new(&genesis_config));
|
||||||
let pubkey = Pubkey::new_rand();
|
let pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let pubkey1 = Pubkey::new_rand();
|
let pubkey1 = solana_sdk::pubkey::new_rand();
|
||||||
let keypair1 = Keypair::new();
|
let keypair1 = Keypair::new();
|
||||||
|
|
||||||
let success_tx =
|
let success_tx =
|
||||||
|
@@ -140,13 +140,12 @@ impl BroadcastRun for BroadcastFakeShredsRun {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::contact_info::ContactInfo;
|
use crate::contact_info::ContactInfo;
|
||||||
use solana_sdk::pubkey::Pubkey;
|
|
||||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tvu_peers_ordering() {
|
fn test_tvu_peers_ordering() {
|
||||||
let cluster = ClusterInfo::new_with_invalid_keypair(ContactInfo::new_localhost(
|
let cluster = ClusterInfo::new_with_invalid_keypair(ContactInfo::new_localhost(
|
||||||
&Pubkey::new_rand(),
|
&solana_sdk::pubkey::new_rand(),
|
||||||
0,
|
0,
|
||||||
));
|
));
|
||||||
cluster.insert_info(ContactInfo::new_with_socketaddr(&SocketAddr::new(
|
cluster.insert_info(ContactInfo::new_with_socketaddr(&SocketAddr::new(
|
||||||
|
@@ -92,7 +92,7 @@ mod tests {
|
|||||||
let bank0 = Arc::new(Bank::new(&genesis_config));
|
let bank0 = Arc::new(Bank::new(&genesis_config));
|
||||||
let tx = system_transaction::transfer(
|
let tx = system_transaction::transfer(
|
||||||
&mint_keypair,
|
&mint_keypair,
|
||||||
&Pubkey::new_rand(),
|
&solana_sdk::pubkey::new_rand(),
|
||||||
1,
|
1,
|
||||||
genesis_config.hash(),
|
genesis_config.hash(),
|
||||||
);
|
);
|
||||||
|
@@ -2,7 +2,7 @@ use crossbeam_channel::{Receiver, RecvTimeoutError, Sender};
|
|||||||
use solana_ledger::blockstore::Blockstore;
|
use solana_ledger::blockstore::Blockstore;
|
||||||
use solana_measure::measure::Measure;
|
use solana_measure::measure::Measure;
|
||||||
use solana_runtime::bank::Bank;
|
use solana_runtime::bank::Bank;
|
||||||
use solana_sdk::timing::slot_duration_from_slots_per_year;
|
use solana_sdk::{feature_set, timing::slot_duration_from_slots_per_year};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
sync::{
|
sync::{
|
||||||
@@ -60,13 +60,24 @@ impl CacheBlockTimeService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn cache_block_time(bank: Arc<Bank>, blockstore: &Arc<Blockstore>) {
|
fn cache_block_time(bank: Arc<Bank>, blockstore: &Arc<Blockstore>) {
|
||||||
let slot_duration = slot_duration_from_slots_per_year(bank.slots_per_year());
|
if bank
|
||||||
let epoch = bank.epoch_schedule().get_epoch(bank.slot());
|
.feature_set
|
||||||
let stakes = HashMap::new();
|
.is_active(&feature_set::timestamp_correction::id())
|
||||||
let stakes = bank.epoch_vote_accounts(epoch).unwrap_or(&stakes);
|
{
|
||||||
|
if let Err(e) = blockstore.cache_block_time(bank.slot(), bank.clock().unix_timestamp) {
|
||||||
|
error!("cache_block_time failed: slot {:?} {:?}", bank.slot(), e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let slot_duration = slot_duration_from_slots_per_year(bank.slots_per_year());
|
||||||
|
let epoch = bank.epoch_schedule().get_epoch(bank.slot());
|
||||||
|
let stakes = HashMap::new();
|
||||||
|
let stakes = bank.epoch_vote_accounts(epoch).unwrap_or(&stakes);
|
||||||
|
|
||||||
if let Err(e) = blockstore.cache_block_time(bank.slot(), slot_duration, stakes) {
|
if let Err(e) =
|
||||||
error!("cache_block_time failed: slot {:?} {:?}", bank.slot(), e);
|
blockstore.cache_block_time_from_slot_entries(bank.slot(), slot_duration, stakes)
|
||||||
|
{
|
||||||
|
error!("cache_block_time failed: slot {:?} {:?}", bank.slot(), e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -27,7 +27,7 @@ use solana_runtime::{
|
|||||||
vote_sender_types::{ReplayVoteReceiver, ReplayedVote},
|
vote_sender_types::{ReplayVoteReceiver, ReplayedVote},
|
||||||
};
|
};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
clock::{Epoch, Slot},
|
clock::{Epoch, Slot, DEFAULT_MS_PER_SLOT},
|
||||||
epoch_schedule::EpochSchedule,
|
epoch_schedule::EpochSchedule,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
@@ -98,7 +98,7 @@ impl VoteTracker {
|
|||||||
epoch_schedule: *root_bank.epoch_schedule(),
|
epoch_schedule: *root_bank.epoch_schedule(),
|
||||||
..VoteTracker::default()
|
..VoteTracker::default()
|
||||||
};
|
};
|
||||||
vote_tracker.process_new_root_bank(&root_bank);
|
vote_tracker.progress_with_new_root_bank(&root_bank);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*vote_tracker.leader_schedule_epoch.read().unwrap(),
|
*vote_tracker.leader_schedule_epoch.read().unwrap(),
|
||||||
root_bank.get_leader_schedule_epoch(root_bank.slot())
|
root_bank.get_leader_schedule_epoch(root_bank.slot())
|
||||||
@@ -174,7 +174,7 @@ impl VoteTracker {
|
|||||||
self.keys.get_or_insert(&pubkey);
|
self.keys.get_or_insert(&pubkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_leader_schedule_epoch(&self, root_bank: &Bank) {
|
fn progress_leader_schedule_epoch(&self, root_bank: &Bank) {
|
||||||
// Update with any newly calculated epoch state about future epochs
|
// Update with any newly calculated epoch state about future epochs
|
||||||
let start_leader_schedule_epoch = *self.leader_schedule_epoch.read().unwrap();
|
let start_leader_schedule_epoch = *self.leader_schedule_epoch.read().unwrap();
|
||||||
let mut greatest_leader_schedule_epoch = start_leader_schedule_epoch;
|
let mut greatest_leader_schedule_epoch = start_leader_schedule_epoch;
|
||||||
@@ -205,7 +205,7 @@ impl VoteTracker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_new_root(&self, root_bank: &Bank) {
|
fn purge_stale_state(&self, root_bank: &Bank) {
|
||||||
// Purge any outdated slot data
|
// Purge any outdated slot data
|
||||||
let new_root = root_bank.slot();
|
let new_root = root_bank.slot();
|
||||||
let root_epoch = root_bank.epoch();
|
let root_epoch = root_bank.epoch();
|
||||||
@@ -220,15 +220,15 @@ impl VoteTracker {
|
|||||||
self.epoch_authorized_voters
|
self.epoch_authorized_voters
|
||||||
.write()
|
.write()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.retain(|epoch, _| epoch >= &root_epoch);
|
.retain(|epoch, _| *epoch >= root_epoch);
|
||||||
self.keys.purge();
|
self.keys.purge();
|
||||||
*self.current_epoch.write().unwrap() = root_epoch;
|
*self.current_epoch.write().unwrap() = root_epoch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_new_root_bank(&self, root_bank: &Bank) {
|
fn progress_with_new_root_bank(&self, root_bank: &Bank) {
|
||||||
self.update_leader_schedule_epoch(root_bank);
|
self.progress_leader_schedule_epoch(root_bank);
|
||||||
self.update_new_root(root_bank);
|
self.purge_stale_state(root_bank);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,7 +425,7 @@ impl ClusterInfoVoteListener {
|
|||||||
blockstore: Arc<Blockstore>,
|
blockstore: Arc<Blockstore>,
|
||||||
bank_notification_sender: Option<BankNotificationSender>,
|
bank_notification_sender: Option<BankNotificationSender>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut optimistic_confirmation_verifier =
|
let mut confirmation_verifier =
|
||||||
OptimisticConfirmationVerifier::new(bank_forks.read().unwrap().root());
|
OptimisticConfirmationVerifier::new(bank_forks.read().unwrap().root());
|
||||||
let mut last_process_root = Instant::now();
|
let mut last_process_root = Instant::now();
|
||||||
loop {
|
loop {
|
||||||
@@ -434,21 +434,21 @@ impl ClusterInfoVoteListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let root_bank = bank_forks.read().unwrap().root_bank().clone();
|
let root_bank = bank_forks.read().unwrap().root_bank().clone();
|
||||||
if last_process_root.elapsed().as_millis() > 400 {
|
if last_process_root.elapsed().as_millis() > DEFAULT_MS_PER_SLOT as u128 {
|
||||||
let unrooted_optimistic_slots = optimistic_confirmation_verifier
|
let unrooted_optimistic_slots = confirmation_verifier
|
||||||
.get_unrooted_optimistic_slots(&root_bank, &blockstore);
|
.verify_for_unrooted_optimistic_slots(&root_bank, &blockstore);
|
||||||
// SlotVoteTracker's for all `slots` in `unrooted_optimistic_slots`
|
// SlotVoteTracker's for all `slots` in `unrooted_optimistic_slots`
|
||||||
// should still be available because we haven't purged in
|
// should still be available because we haven't purged in
|
||||||
// `process_new_root_bank()` yet, which is called below
|
// `progress_with_new_root_bank()` yet, which is called below
|
||||||
OptimisticConfirmationVerifier::log_unrooted_optimistic_slots(
|
OptimisticConfirmationVerifier::log_unrooted_optimistic_slots(
|
||||||
&root_bank,
|
&root_bank,
|
||||||
&vote_tracker,
|
&vote_tracker,
|
||||||
&unrooted_optimistic_slots,
|
&unrooted_optimistic_slots,
|
||||||
);
|
);
|
||||||
vote_tracker.process_new_root_bank(&root_bank);
|
vote_tracker.progress_with_new_root_bank(&root_bank);
|
||||||
last_process_root = Instant::now();
|
last_process_root = Instant::now();
|
||||||
}
|
}
|
||||||
let optimistic_confirmed_slots = Self::get_and_process_votes(
|
let confirmed_slots = Self::listen_and_confirm_votes(
|
||||||
&gossip_vote_txs_receiver,
|
&gossip_vote_txs_receiver,
|
||||||
&vote_tracker,
|
&vote_tracker,
|
||||||
&root_bank,
|
&root_bank,
|
||||||
@@ -457,19 +457,17 @@ impl ClusterInfoVoteListener {
|
|||||||
&replay_votes_receiver,
|
&replay_votes_receiver,
|
||||||
&bank_notification_sender,
|
&bank_notification_sender,
|
||||||
);
|
);
|
||||||
|
match confirmed_slots {
|
||||||
if let Err(e) = optimistic_confirmed_slots {
|
Ok(confirmed_slots) => {
|
||||||
match e {
|
confirmation_verifier.add_new_optimistic_confirmed_slots(confirmed_slots);
|
||||||
|
}
|
||||||
|
Err(e) => match e {
|
||||||
Error::CrossbeamRecvTimeoutError(RecvTimeoutError::Timeout)
|
Error::CrossbeamRecvTimeoutError(RecvTimeoutError::Timeout)
|
||||||
| Error::ReadyTimeoutError => (),
|
| Error::ReadyTimeoutError => (),
|
||||||
_ => {
|
_ => {
|
||||||
error!("thread {:?} error {:?}", thread::current().name(), e);
|
error!("thread {:?} error {:?}", thread::current().name(), e);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
} else {
|
|
||||||
let optimistic_confirmed_slots = optimistic_confirmed_slots.unwrap();
|
|
||||||
optimistic_confirmation_verifier
|
|
||||||
.add_new_optimistic_confirmed_slots(optimistic_confirmed_slots);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -483,7 +481,7 @@ impl ClusterInfoVoteListener {
|
|||||||
verified_vote_sender: &VerifiedVoteSender,
|
verified_vote_sender: &VerifiedVoteSender,
|
||||||
replay_votes_receiver: &ReplayVoteReceiver,
|
replay_votes_receiver: &ReplayVoteReceiver,
|
||||||
) -> Result<Vec<(Slot, Hash)>> {
|
) -> Result<Vec<(Slot, Hash)>> {
|
||||||
Self::get_and_process_votes(
|
Self::listen_and_confirm_votes(
|
||||||
gossip_vote_txs_receiver,
|
gossip_vote_txs_receiver,
|
||||||
vote_tracker,
|
vote_tracker,
|
||||||
root_bank,
|
root_bank,
|
||||||
@@ -494,7 +492,7 @@ impl ClusterInfoVoteListener {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_and_process_votes(
|
fn listen_and_confirm_votes(
|
||||||
gossip_vote_txs_receiver: &VerifiedVoteTransactionsReceiver,
|
gossip_vote_txs_receiver: &VerifiedVoteTransactionsReceiver,
|
||||||
vote_tracker: &VoteTracker,
|
vote_tracker: &VoteTracker,
|
||||||
root_bank: &Bank,
|
root_bank: &Bank,
|
||||||
@@ -523,7 +521,7 @@ impl ClusterInfoVoteListener {
|
|||||||
let gossip_vote_txs: Vec<_> = gossip_vote_txs_receiver.try_iter().flatten().collect();
|
let gossip_vote_txs: Vec<_> = gossip_vote_txs_receiver.try_iter().flatten().collect();
|
||||||
let replay_votes: Vec<_> = replay_votes_receiver.try_iter().collect();
|
let replay_votes: Vec<_> = replay_votes_receiver.try_iter().collect();
|
||||||
if !gossip_vote_txs.is_empty() || !replay_votes.is_empty() {
|
if !gossip_vote_txs.is_empty() || !replay_votes.is_empty() {
|
||||||
return Ok(Self::process_votes(
|
return Ok(Self::filter_and_confirm_with_new_votes(
|
||||||
vote_tracker,
|
vote_tracker,
|
||||||
gossip_vote_txs,
|
gossip_vote_txs,
|
||||||
replay_votes,
|
replay_votes,
|
||||||
@@ -541,7 +539,7 @@ impl ClusterInfoVoteListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn update_new_votes(
|
fn track_new_votes_and_notify_confirmations(
|
||||||
vote: Vote,
|
vote: Vote,
|
||||||
vote_pubkey: &Pubkey,
|
vote_pubkey: &Pubkey,
|
||||||
vote_tracker: &VoteTracker,
|
vote_tracker: &VoteTracker,
|
||||||
@@ -557,56 +555,52 @@ impl ClusterInfoVoteListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let last_vote_slot = vote.slots.last().unwrap();
|
let last_vote_slot = *vote.slots.last().unwrap();
|
||||||
|
let last_vote_hash = vote.hash;
|
||||||
|
|
||||||
let root = root_bank.slot();
|
let root = root_bank.slot();
|
||||||
let last_vote_hash = vote.hash;
|
|
||||||
let mut is_new_vote = false;
|
let mut is_new_vote = false;
|
||||||
for slot in vote.slots.iter().rev() {
|
// If slot is before the root, ignore it
|
||||||
// If slot is before the root, or so far ahead we don't have
|
for slot in vote.slots.iter().filter(|slot| **slot > root).rev() {
|
||||||
// stake information, then ignore it
|
let slot = *slot;
|
||||||
let epoch = root_bank.epoch_schedule().get_epoch(*slot);
|
|
||||||
|
// if we don't have stake information, ignore it
|
||||||
|
let epoch = root_bank.epoch_schedule().get_epoch(slot);
|
||||||
let epoch_stakes = root_bank.epoch_stakes(epoch);
|
let epoch_stakes = root_bank.epoch_stakes(epoch);
|
||||||
if *slot <= root || epoch_stakes.is_none() {
|
if epoch_stakes.is_none() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let epoch_stakes = epoch_stakes.unwrap();
|
let epoch_stakes = epoch_stakes.unwrap();
|
||||||
let epoch_vote_accounts = Stakes::vote_accounts(epoch_stakes.stakes());
|
|
||||||
let total_epoch_stake = epoch_stakes.total_stake();
|
|
||||||
let unduplicated_pubkey = vote_tracker.keys.get_or_insert(&vote_pubkey);
|
let unduplicated_pubkey = vote_tracker.keys.get_or_insert(&vote_pubkey);
|
||||||
|
|
||||||
// The last vote slot, which is the greatest slot in the stack
|
// The last vote slot, which is the greatest slot in the stack
|
||||||
// of votes in a vote transaction, qualifies for optimistic confirmation.
|
// of votes in a vote transaction, qualifies for optimistic confirmation.
|
||||||
let update_optimistic_confirmation_info = if slot == last_vote_slot {
|
if slot == last_vote_slot {
|
||||||
let stake = epoch_vote_accounts
|
let vote_accounts = Stakes::vote_accounts(epoch_stakes.stakes());
|
||||||
|
let stake = vote_accounts
|
||||||
.get(&vote_pubkey)
|
.get(&vote_pubkey)
|
||||||
.map(|(stake, _)| *stake)
|
.map(|(stake, _)| *stake)
|
||||||
.unwrap_or(0);
|
.unwrap_or_default();
|
||||||
Some((stake, last_vote_hash))
|
let total_stake = epoch_stakes.total_stake();
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
// If this vote for this slot qualifies for optimistic confirmation
|
|
||||||
if let Some((stake, hash)) = update_optimistic_confirmation_info {
|
|
||||||
// Fast track processing of the last slot in a vote transactions
|
// Fast track processing of the last slot in a vote transactions
|
||||||
// so that notifications for optimistic confirmation can be sent
|
// so that notifications for optimistic confirmation can be sent
|
||||||
// as soon as possible.
|
// as soon as possible.
|
||||||
let (is_confirmed, is_new) = Self::add_optimistic_confirmation_vote(
|
let (is_confirmed, is_new) = Self::track_optimistic_confirmation_vote(
|
||||||
vote_tracker,
|
vote_tracker,
|
||||||
*slot,
|
last_vote_slot,
|
||||||
hash,
|
last_vote_hash,
|
||||||
unduplicated_pubkey.clone(),
|
unduplicated_pubkey.clone(),
|
||||||
stake,
|
stake,
|
||||||
total_epoch_stake,
|
total_stake,
|
||||||
);
|
);
|
||||||
|
|
||||||
if is_confirmed {
|
if is_confirmed {
|
||||||
new_optimistic_confirmed_slots.push((*slot, last_vote_hash));
|
new_optimistic_confirmed_slots.push((last_vote_slot, last_vote_hash));
|
||||||
// Notify subscribers about new optimistic confirmation
|
// Notify subscribers about new optimistic confirmation
|
||||||
if let Some(sender) = bank_notification_sender {
|
if let Some(sender) = bank_notification_sender {
|
||||||
sender
|
sender
|
||||||
.send(BankNotification::OptimisticallyConfirmed(*slot))
|
.send(BankNotification::OptimisticallyConfirmed(last_vote_slot))
|
||||||
.unwrap_or_else(|err| {
|
.unwrap_or_else(|err| {
|
||||||
warn!("bank_notification_sender failed: {:?}", err)
|
warn!("bank_notification_sender failed: {:?}", err)
|
||||||
});
|
});
|
||||||
@@ -617,7 +611,7 @@ impl ClusterInfoVoteListener {
|
|||||||
// By now:
|
// By now:
|
||||||
// 1) The vote must have come from ReplayStage,
|
// 1) The vote must have come from ReplayStage,
|
||||||
// 2) We've seen this vote from replay for this hash before
|
// 2) We've seen this vote from replay for this hash before
|
||||||
// (`add_optimistic_confirmation_vote()` will not set `is_new == true`
|
// (`track_optimistic_confirmation_vote()` will not set `is_new == true`
|
||||||
// for same slot different hash), so short circuit because this vote
|
// for same slot different hash), so short circuit because this vote
|
||||||
// has no new information
|
// has no new information
|
||||||
|
|
||||||
@@ -629,7 +623,7 @@ impl ClusterInfoVoteListener {
|
|||||||
is_new_vote = is_new;
|
is_new_vote = is_new;
|
||||||
}
|
}
|
||||||
|
|
||||||
diff.entry(*slot)
|
diff.entry(slot)
|
||||||
.or_default()
|
.or_default()
|
||||||
.entry(unduplicated_pubkey)
|
.entry(unduplicated_pubkey)
|
||||||
.and_modify(|seen_in_gossip_previously| {
|
.and_modify(|seen_in_gossip_previously| {
|
||||||
@@ -644,7 +638,40 @@ impl ClusterInfoVoteListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_votes(
|
fn filter_gossip_votes(
|
||||||
|
vote_tracker: &VoteTracker,
|
||||||
|
vote_pubkey: &Pubkey,
|
||||||
|
vote: &Vote,
|
||||||
|
gossip_tx: &Transaction,
|
||||||
|
) -> bool {
|
||||||
|
if vote.slots.is_empty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let last_vote_slot = vote.slots.last().unwrap();
|
||||||
|
// Votes from gossip need to be verified as they have not been
|
||||||
|
// verified by the replay pipeline. Determine the authorized voter
|
||||||
|
// based on the last vote slot. This will drop votes from authorized
|
||||||
|
// voters trying to make votes for slots earlier than the epoch for
|
||||||
|
// which they are authorized
|
||||||
|
let actual_authorized_voter =
|
||||||
|
vote_tracker.get_authorized_voter(&vote_pubkey, *last_vote_slot);
|
||||||
|
|
||||||
|
if actual_authorized_voter.is_none() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Voting without the correct authorized pubkey, dump the vote
|
||||||
|
if !VoteTracker::vote_contains_authorized_voter(
|
||||||
|
&gossip_tx,
|
||||||
|
&actual_authorized_voter.unwrap(),
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filter_and_confirm_with_new_votes(
|
||||||
vote_tracker: &VoteTracker,
|
vote_tracker: &VoteTracker,
|
||||||
gossip_vote_txs: Vec<Transaction>,
|
gossip_vote_txs: Vec<Transaction>,
|
||||||
replayed_votes: Vec<ReplayedVote>,
|
replayed_votes: Vec<ReplayedVote>,
|
||||||
@@ -662,37 +689,13 @@ impl ClusterInfoVoteListener {
|
|||||||
.filter_map(|gossip_tx| {
|
.filter_map(|gossip_tx| {
|
||||||
vote_transaction::parse_vote_transaction(gossip_tx)
|
vote_transaction::parse_vote_transaction(gossip_tx)
|
||||||
.filter(|(vote_pubkey, vote, _)| {
|
.filter(|(vote_pubkey, vote, _)| {
|
||||||
if vote.slots.is_empty() {
|
Self::filter_gossip_votes(vote_tracker, vote_pubkey, vote, gossip_tx)
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let last_vote_slot = vote.slots.last().unwrap();
|
|
||||||
// Votes from gossip need to be verified as they have not been
|
|
||||||
// verified by the replay pipeline. Determine the authorized voter
|
|
||||||
// based on the last vote slot. This will drop votes from authorized
|
|
||||||
// voters trying to make votes for slots earlier than the epoch for
|
|
||||||
// which they are authorized
|
|
||||||
let actual_authorized_voter =
|
|
||||||
vote_tracker.get_authorized_voter(&vote_pubkey, *last_vote_slot);
|
|
||||||
|
|
||||||
if actual_authorized_voter.is_none() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Voting without the correct authorized pubkey, dump the vote
|
|
||||||
if !VoteTracker::vote_contains_authorized_voter(
|
|
||||||
&gossip_tx,
|
|
||||||
&actual_authorized_voter.unwrap(),
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
})
|
})
|
||||||
.map(|v| (true, v))
|
.map(|v| (true, v))
|
||||||
})
|
})
|
||||||
.chain(replayed_votes.into_iter().map(|v| (false, v)))
|
.chain(replayed_votes.into_iter().map(|v| (false, v)))
|
||||||
{
|
{
|
||||||
Self::update_new_votes(
|
Self::track_new_votes_and_notify_confirmations(
|
||||||
vote,
|
vote,
|
||||||
&vote_pubkey,
|
&vote_pubkey,
|
||||||
&vote_tracker,
|
&vote_tracker,
|
||||||
@@ -757,7 +760,7 @@ impl ClusterInfoVoteListener {
|
|||||||
|
|
||||||
// Returns if the slot was optimistically confirmed, and whether
|
// Returns if the slot was optimistically confirmed, and whether
|
||||||
// the slot was new
|
// the slot was new
|
||||||
fn add_optimistic_confirmation_vote(
|
fn track_optimistic_confirmation_vote(
|
||||||
vote_tracker: &VoteTracker,
|
vote_tracker: &VoteTracker,
|
||||||
slot: Slot,
|
slot: Slot,
|
||||||
hash: Hash,
|
hash: Hash,
|
||||||
@@ -898,7 +901,7 @@ mod tests {
|
|||||||
let (vote_tracker, bank, _, _) = setup();
|
let (vote_tracker, bank, _, _) = setup();
|
||||||
|
|
||||||
// Check outdated slots are purged with new root
|
// Check outdated slots are purged with new root
|
||||||
let new_voter = Arc::new(Pubkey::new_rand());
|
let new_voter = Arc::new(solana_sdk::pubkey::new_rand());
|
||||||
// Make separate copy so the original doesn't count toward
|
// Make separate copy so the original doesn't count toward
|
||||||
// the ref count, which would prevent cleanup
|
// the ref count, which would prevent cleanup
|
||||||
let new_voter_ = Arc::new(*new_voter);
|
let new_voter_ = Arc::new(*new_voter);
|
||||||
@@ -909,7 +912,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.contains_key(&bank.slot()));
|
.contains_key(&bank.slot()));
|
||||||
let bank1 = Bank::new_from_parent(&bank, &Pubkey::default(), bank.slot() + 1);
|
let bank1 = Bank::new_from_parent(&bank, &Pubkey::default(), bank.slot() + 1);
|
||||||
vote_tracker.process_new_root_bank(&bank1);
|
vote_tracker.progress_with_new_root_bank(&bank1);
|
||||||
assert!(!vote_tracker
|
assert!(!vote_tracker
|
||||||
.slot_vote_trackers
|
.slot_vote_trackers
|
||||||
.read()
|
.read()
|
||||||
@@ -926,7 +929,7 @@ mod tests {
|
|||||||
bank.epoch_schedule()
|
bank.epoch_schedule()
|
||||||
.get_first_slot_in_epoch(current_epoch + 1),
|
.get_first_slot_in_epoch(current_epoch + 1),
|
||||||
);
|
);
|
||||||
vote_tracker.process_new_root_bank(&new_epoch_bank);
|
vote_tracker.progress_with_new_root_bank(&new_epoch_bank);
|
||||||
assert!(!vote_tracker.keys.0.read().unwrap().contains(&new_voter));
|
assert!(!vote_tracker.keys.0.read().unwrap().contains(&new_voter));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*vote_tracker.current_epoch.read().unwrap(),
|
*vote_tracker.current_epoch.read().unwrap(),
|
||||||
@@ -956,7 +959,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
let next_leader_schedule_bank =
|
let next_leader_schedule_bank =
|
||||||
Bank::new_from_parent(&bank, &Pubkey::default(), next_leader_schedule_computed);
|
Bank::new_from_parent(&bank, &Pubkey::default(), next_leader_schedule_computed);
|
||||||
vote_tracker.update_leader_schedule_epoch(&next_leader_schedule_bank);
|
vote_tracker.progress_leader_schedule_epoch(&next_leader_schedule_bank);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*vote_tracker.leader_schedule_epoch.read().unwrap(),
|
*vote_tracker.leader_schedule_epoch.read().unwrap(),
|
||||||
next_leader_schedule_epoch
|
next_leader_schedule_epoch
|
||||||
@@ -1007,7 +1010,7 @@ mod tests {
|
|||||||
&votes_sender,
|
&votes_sender,
|
||||||
&replay_votes_sender,
|
&replay_votes_sender,
|
||||||
);
|
);
|
||||||
ClusterInfoVoteListener::get_and_process_votes(
|
ClusterInfoVoteListener::listen_and_confirm_votes(
|
||||||
&votes_receiver,
|
&votes_receiver,
|
||||||
&vote_tracker,
|
&vote_tracker,
|
||||||
&bank3,
|
&bank3,
|
||||||
@@ -1036,7 +1039,7 @@ mod tests {
|
|||||||
&votes_sender,
|
&votes_sender,
|
||||||
&replay_votes_sender,
|
&replay_votes_sender,
|
||||||
);
|
);
|
||||||
ClusterInfoVoteListener::get_and_process_votes(
|
ClusterInfoVoteListener::listen_and_confirm_votes(
|
||||||
&votes_receiver,
|
&votes_receiver,
|
||||||
&vote_tracker,
|
&vote_tracker,
|
||||||
&bank3,
|
&bank3,
|
||||||
@@ -1114,7 +1117,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Check that all the votes were registered for each validator correctly
|
// Check that all the votes were registered for each validator correctly
|
||||||
ClusterInfoVoteListener::get_and_process_votes(
|
ClusterInfoVoteListener::listen_and_confirm_votes(
|
||||||
&votes_txs_receiver,
|
&votes_txs_receiver,
|
||||||
&vote_tracker,
|
&vote_tracker,
|
||||||
&bank0,
|
&bank0,
|
||||||
@@ -1233,7 +1236,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read and process votes from channel `votes_receiver`
|
// Read and process votes from channel `votes_receiver`
|
||||||
ClusterInfoVoteListener::get_and_process_votes(
|
ClusterInfoVoteListener::listen_and_confirm_votes(
|
||||||
&votes_txs_receiver,
|
&votes_txs_receiver,
|
||||||
&vote_tracker,
|
&vote_tracker,
|
||||||
&bank0,
|
&bank0,
|
||||||
@@ -1328,7 +1331,7 @@ mod tests {
|
|||||||
))
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
let _ = ClusterInfoVoteListener::get_and_process_votes(
|
let _ = ClusterInfoVoteListener::listen_and_confirm_votes(
|
||||||
&votes_receiver,
|
&votes_receiver,
|
||||||
&vote_tracker,
|
&vote_tracker,
|
||||||
&bank,
|
&bank,
|
||||||
@@ -1474,7 +1477,7 @@ mod tests {
|
|||||||
)];
|
)];
|
||||||
|
|
||||||
let (verified_vote_sender, _verified_vote_receiver) = unbounded();
|
let (verified_vote_sender, _verified_vote_receiver) = unbounded();
|
||||||
ClusterInfoVoteListener::process_votes(
|
ClusterInfoVoteListener::filter_and_confirm_with_new_votes(
|
||||||
&vote_tracker,
|
&vote_tracker,
|
||||||
vote_tx,
|
vote_tx,
|
||||||
// Add gossip vote for same slot, should not affect outcome
|
// Add gossip vote for same slot, should not affect outcome
|
||||||
@@ -1545,7 +1548,7 @@ mod tests {
|
|||||||
|
|
||||||
let new_root_bank =
|
let new_root_bank =
|
||||||
Bank::new_from_parent(&bank, &Pubkey::default(), first_slot_in_new_epoch - 2);
|
Bank::new_from_parent(&bank, &Pubkey::default(), first_slot_in_new_epoch - 2);
|
||||||
ClusterInfoVoteListener::process_votes(
|
ClusterInfoVoteListener::filter_and_confirm_with_new_votes(
|
||||||
&vote_tracker,
|
&vote_tracker,
|
||||||
vote_txs,
|
vote_txs,
|
||||||
vec![(
|
vec![(
|
||||||
@@ -1681,7 +1684,7 @@ mod tests {
|
|||||||
fn run_test_verify_votes_1_pass(hash: Option<Hash>) {
|
fn run_test_verify_votes_1_pass(hash: Option<Hash>) {
|
||||||
let vote_tx = test_vote_tx(hash);
|
let vote_tx = test_vote_tx(hash);
|
||||||
let votes = vec![vote_tx];
|
let votes = vec![vote_tx];
|
||||||
let labels = vec![CrdsValueLabel::Vote(0, Pubkey::new_rand())];
|
let labels = vec![CrdsValueLabel::Vote(0, solana_sdk::pubkey::new_rand())];
|
||||||
let (vote_txs, packets) = ClusterInfoVoteListener::verify_votes(votes, labels);
|
let (vote_txs, packets) = ClusterInfoVoteListener::verify_votes(votes, labels);
|
||||||
assert_eq!(vote_txs.len(), 1);
|
assert_eq!(vote_txs.len(), 1);
|
||||||
verify_packets_len(&packets, 1);
|
verify_packets_len(&packets, 1);
|
||||||
@@ -1698,7 +1701,7 @@ mod tests {
|
|||||||
let mut bad_vote = vote_tx.clone();
|
let mut bad_vote = vote_tx.clone();
|
||||||
bad_vote.signatures[0] = Signature::default();
|
bad_vote.signatures[0] = Signature::default();
|
||||||
let votes = vec![vote_tx.clone(), bad_vote, vote_tx];
|
let votes = vec![vote_tx.clone(), bad_vote, vote_tx];
|
||||||
let label = CrdsValueLabel::Vote(0, Pubkey::new_rand());
|
let label = CrdsValueLabel::Vote(0, solana_sdk::pubkey::new_rand());
|
||||||
let labels: Vec<_> = (0..votes.len()).map(|_| label.clone()).collect();
|
let labels: Vec<_> = (0..votes.len()).map(|_| label.clone()).collect();
|
||||||
let (vote_txs, packets) = ClusterInfoVoteListener::verify_votes(votes, labels);
|
let (vote_txs, packets) = ClusterInfoVoteListener::verify_votes(votes, labels);
|
||||||
assert_eq!(vote_txs.len(), 2);
|
assert_eq!(vote_txs.len(), 2);
|
||||||
|
@@ -106,28 +106,30 @@ impl ClusterSlots {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compute_weights(&self, slot: Slot, repair_peers: &[ContactInfo]) -> Vec<(u64, usize)> {
|
pub fn compute_weights(&self, slot: Slot, repair_peers: &[ContactInfo]) -> Vec<u64> {
|
||||||
let slot_peers = self.lookup(slot);
|
let stakes = {
|
||||||
|
let validator_stakes = self.validator_stakes.read().unwrap();
|
||||||
|
repair_peers
|
||||||
|
.iter()
|
||||||
|
.map(|peer| {
|
||||||
|
validator_stakes
|
||||||
|
.get(&peer.id)
|
||||||
|
.map(|node| node.total_stake)
|
||||||
|
.unwrap_or(0)
|
||||||
|
+ 1
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
let slot_peers = match self.lookup(slot) {
|
||||||
|
None => return stakes,
|
||||||
|
Some(slot_peers) => slot_peers,
|
||||||
|
};
|
||||||
|
let slot_peers = slot_peers.read().unwrap();
|
||||||
repair_peers
|
repair_peers
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.map(|peer| slot_peers.get(&peer.id).cloned().unwrap_or(0))
|
||||||
.map(|(i, x)| {
|
.zip(stakes)
|
||||||
let peer_stake = slot_peers
|
.map(|(a, b)| a + b)
|
||||||
.as_ref()
|
|
||||||
.and_then(|v| v.read().unwrap().get(&x.id).cloned())
|
|
||||||
.unwrap_or(0);
|
|
||||||
(
|
|
||||||
1 + peer_stake
|
|
||||||
+ self
|
|
||||||
.validator_stakes
|
|
||||||
.read()
|
|
||||||
.unwrap()
|
|
||||||
.get(&x.id)
|
|
||||||
.map(|v| v.total_stake)
|
|
||||||
.unwrap_or(0),
|
|
||||||
i,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,7 +230,7 @@ mod tests {
|
|||||||
fn test_compute_weights() {
|
fn test_compute_weights() {
|
||||||
let cs = ClusterSlots::default();
|
let cs = ClusterSlots::default();
|
||||||
let ci = ContactInfo::default();
|
let ci = ContactInfo::default();
|
||||||
assert_eq!(cs.compute_weights(0, &[ci]), vec![(1, 0)]);
|
assert_eq!(cs.compute_weights(0, &[ci]), vec![1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -237,8 +239,8 @@ mod tests {
|
|||||||
let mut c1 = ContactInfo::default();
|
let mut c1 = ContactInfo::default();
|
||||||
let mut c2 = ContactInfo::default();
|
let mut c2 = ContactInfo::default();
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
let k1 = Pubkey::new_rand();
|
let k1 = solana_sdk::pubkey::new_rand();
|
||||||
let k2 = Pubkey::new_rand();
|
let k2 = solana_sdk::pubkey::new_rand();
|
||||||
map.insert(Arc::new(k1), std::u64::MAX / 2);
|
map.insert(Arc::new(k1), std::u64::MAX / 2);
|
||||||
map.insert(Arc::new(k2), 0);
|
map.insert(Arc::new(k2), 0);
|
||||||
cs.cluster_slots
|
cs.cluster_slots
|
||||||
@@ -249,7 +251,7 @@ mod tests {
|
|||||||
c2.id = k2;
|
c2.id = k2;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cs.compute_weights(0, &[c1, c2]),
|
cs.compute_weights(0, &[c1, c2]),
|
||||||
vec![(std::u64::MAX / 2 + 1, 0), (1, 1)]
|
vec![std::u64::MAX / 2 + 1, 1]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,8 +261,8 @@ mod tests {
|
|||||||
let mut c1 = ContactInfo::default();
|
let mut c1 = ContactInfo::default();
|
||||||
let mut c2 = ContactInfo::default();
|
let mut c2 = ContactInfo::default();
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
let k1 = Pubkey::new_rand();
|
let k1 = solana_sdk::pubkey::new_rand();
|
||||||
let k2 = Pubkey::new_rand();
|
let k2 = solana_sdk::pubkey::new_rand();
|
||||||
map.insert(Arc::new(k2), 0);
|
map.insert(Arc::new(k2), 0);
|
||||||
cs.cluster_slots
|
cs.cluster_slots
|
||||||
.write()
|
.write()
|
||||||
@@ -281,7 +283,7 @@ mod tests {
|
|||||||
c2.id = k2;
|
c2.id = k2;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cs.compute_weights(0, &[c1, c2]),
|
cs.compute_weights(0, &[c1, c2]),
|
||||||
vec![(std::u64::MAX / 2 + 1, 0), (1, 1)]
|
vec![std::u64::MAX / 2 + 1, 1]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,7 +292,7 @@ mod tests {
|
|||||||
let cs = ClusterSlots::default();
|
let cs = ClusterSlots::default();
|
||||||
let mut contact_infos = vec![ContactInfo::default(); 2];
|
let mut contact_infos = vec![ContactInfo::default(); 2];
|
||||||
for ci in contact_infos.iter_mut() {
|
for ci in contact_infos.iter_mut() {
|
||||||
ci.id = Pubkey::new_rand();
|
ci.id = solana_sdk::pubkey::new_rand();
|
||||||
}
|
}
|
||||||
let slot = 9;
|
let slot = 9;
|
||||||
|
|
||||||
@@ -359,7 +361,7 @@ mod tests {
|
|||||||
let mut epoch_slot = EpochSlots::default();
|
let mut epoch_slot = EpochSlots::default();
|
||||||
epoch_slot.fill(&[1], 0);
|
epoch_slot.fill(&[1], 0);
|
||||||
cs.update_internal(0, (vec![epoch_slot], None));
|
cs.update_internal(0, (vec![epoch_slot], None));
|
||||||
let self_id = Pubkey::new_rand();
|
let self_id = solana_sdk::pubkey::new_rand();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cs.generate_repairs_for_missing_slots(&self_id, 0),
|
cs.generate_repairs_for_missing_slots(&self_id, 0),
|
||||||
vec![RepairType::HighestShred(1, 0)]
|
vec![RepairType::HighestShred(1, 0)]
|
||||||
|
@@ -181,6 +181,7 @@ mod test {
|
|||||||
let node_info = Node::new_localhost_with_pubkey(&Pubkey::default());
|
let node_info = Node::new_localhost_with_pubkey(&Pubkey::default());
|
||||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(node_info.info);
|
let cluster_info = ClusterInfo::new_with_invalid_keypair(node_info.info);
|
||||||
ClusterSlotsService::update_lowest_slot(&Pubkey::default(), 5, &cluster_info);
|
ClusterSlotsService::update_lowest_slot(&Pubkey::default(), 5, &cluster_info);
|
||||||
|
cluster_info.flush_push_queue();
|
||||||
let lowest = cluster_info
|
let lowest = cluster_info
|
||||||
.get_lowest_slot_for_node(&Pubkey::default(), None, |lowest_slot, _| {
|
.get_lowest_slot_for_node(&Pubkey::default(), None, |lowest_slot, _| {
|
||||||
lowest_slot.clone()
|
lowest_slot.clone()
|
||||||
|
@@ -113,7 +113,17 @@ impl AggregateCommitmentService {
|
|||||||
"aggregate-commitment-ms",
|
"aggregate-commitment-ms",
|
||||||
aggregate_commitment_time.as_ms() as i64,
|
aggregate_commitment_time.as_ms() as i64,
|
||||||
i64
|
i64
|
||||||
)
|
),
|
||||||
|
(
|
||||||
|
"highest-confirmed-root",
|
||||||
|
update_commitment_slots.highest_confirmed_root as i64,
|
||||||
|
i64
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"highest-confirmed-slot",
|
||||||
|
update_commitment_slots.highest_confirmed_slot as i64,
|
||||||
|
i64
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Triggers rpc_subscription notifications as soon as new commitment data is available,
|
// Triggers rpc_subscription notifications as soon as new commitment data is available,
|
||||||
@@ -176,19 +186,15 @@ impl AggregateCommitmentService {
|
|||||||
if lamports == 0 {
|
if lamports == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let vote_state = VoteState::from(&account);
|
if let Ok(vote_state) = account.vote_state().as_ref() {
|
||||||
if vote_state.is_none() {
|
Self::aggregate_commitment_for_vote_account(
|
||||||
continue;
|
&mut commitment,
|
||||||
|
&mut rooted_stake,
|
||||||
|
vote_state,
|
||||||
|
ancestors,
|
||||||
|
lamports,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let vote_state = vote_state.unwrap();
|
|
||||||
Self::aggregate_commitment_for_vote_account(
|
|
||||||
&mut commitment,
|
|
||||||
&mut rooted_stake,
|
|
||||||
&vote_state,
|
|
||||||
ancestors,
|
|
||||||
lamports,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
(commitment, rooted_stake)
|
(commitment, rooted_stake)
|
||||||
@@ -375,19 +381,22 @@ mod tests {
|
|||||||
|
|
||||||
let rooted_stake_amount = 40;
|
let rooted_stake_amount = 40;
|
||||||
|
|
||||||
let sk1 = Pubkey::new_rand();
|
let sk1 = solana_sdk::pubkey::new_rand();
|
||||||
let pk1 = Pubkey::new_rand();
|
let pk1 = solana_sdk::pubkey::new_rand();
|
||||||
let mut vote_account1 = vote_state::create_account(&pk1, &Pubkey::new_rand(), 0, 100);
|
let mut vote_account1 =
|
||||||
|
vote_state::create_account(&pk1, &solana_sdk::pubkey::new_rand(), 0, 100);
|
||||||
let stake_account1 =
|
let stake_account1 =
|
||||||
stake_state::create_account(&sk1, &pk1, &vote_account1, &genesis_config.rent, 100);
|
stake_state::create_account(&sk1, &pk1, &vote_account1, &genesis_config.rent, 100);
|
||||||
let sk2 = Pubkey::new_rand();
|
let sk2 = solana_sdk::pubkey::new_rand();
|
||||||
let pk2 = Pubkey::new_rand();
|
let pk2 = solana_sdk::pubkey::new_rand();
|
||||||
let mut vote_account2 = vote_state::create_account(&pk2, &Pubkey::new_rand(), 0, 50);
|
let mut vote_account2 =
|
||||||
|
vote_state::create_account(&pk2, &solana_sdk::pubkey::new_rand(), 0, 50);
|
||||||
let stake_account2 =
|
let stake_account2 =
|
||||||
stake_state::create_account(&sk2, &pk2, &vote_account2, &genesis_config.rent, 50);
|
stake_state::create_account(&sk2, &pk2, &vote_account2, &genesis_config.rent, 50);
|
||||||
let sk3 = Pubkey::new_rand();
|
let sk3 = solana_sdk::pubkey::new_rand();
|
||||||
let pk3 = Pubkey::new_rand();
|
let pk3 = solana_sdk::pubkey::new_rand();
|
||||||
let mut vote_account3 = vote_state::create_account(&pk3, &Pubkey::new_rand(), 0, 1);
|
let mut vote_account3 =
|
||||||
|
vote_state::create_account(&pk3, &solana_sdk::pubkey::new_rand(), 0, 1);
|
||||||
let stake_account3 = stake_state::create_account(
|
let stake_account3 = stake_state::create_account(
|
||||||
&sk3,
|
&sk3,
|
||||||
&pk3,
|
&pk3,
|
||||||
@@ -395,9 +404,10 @@ mod tests {
|
|||||||
&genesis_config.rent,
|
&genesis_config.rent,
|
||||||
rooted_stake_amount,
|
rooted_stake_amount,
|
||||||
);
|
);
|
||||||
let sk4 = Pubkey::new_rand();
|
let sk4 = solana_sdk::pubkey::new_rand();
|
||||||
let pk4 = Pubkey::new_rand();
|
let pk4 = solana_sdk::pubkey::new_rand();
|
||||||
let mut vote_account4 = vote_state::create_account(&pk4, &Pubkey::new_rand(), 0, 1);
|
let mut vote_account4 =
|
||||||
|
vote_state::create_account(&pk4, &solana_sdk::pubkey::new_rand(), 0, 1);
|
||||||
let stake_account4 = stake_state::create_account(
|
let stake_account4 = stake_state::create_account(
|
||||||
&sk4,
|
&sk4,
|
||||||
&pk4,
|
&pk4,
|
||||||
@@ -478,9 +488,14 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_highest_confirmed_root_advance() {
|
fn test_highest_confirmed_root_advance() {
|
||||||
fn get_vote_account_root_slot(vote_pubkey: Pubkey, bank: &Arc<Bank>) -> Slot {
|
fn get_vote_account_root_slot(vote_pubkey: Pubkey, bank: &Arc<Bank>) -> Slot {
|
||||||
let account = &bank.vote_accounts()[&vote_pubkey].1;
|
let (_stake, vote_account) = bank.get_vote_account(&vote_pubkey).unwrap();
|
||||||
let vote_state = VoteState::from(account).unwrap();
|
let slot = vote_account
|
||||||
vote_state.root_slot.unwrap()
|
.vote_state()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.root_slot
|
||||||
|
.unwrap();
|
||||||
|
slot
|
||||||
}
|
}
|
||||||
|
|
||||||
let block_commitment_cache = RwLock::new(BlockCommitmentCache::new_for_tests());
|
let block_commitment_cache = RwLock::new(BlockCommitmentCache::new_for_tests());
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
use crate::rpc_subscriptions::RpcSubscriptions;
|
use crate::rpc_subscriptions::RpcSubscriptions;
|
||||||
use crossbeam_channel::{Receiver, RecvTimeoutError, Sender};
|
use crossbeam_channel::{Receiver, RecvTimeoutError, Sender};
|
||||||
use solana_ledger::blockstore::{Blockstore, CompletedDataSetInfo};
|
use solana_ledger::blockstore::{Blockstore, CompletedDataSetInfo};
|
||||||
|
use solana_ledger::entry::Entry;
|
||||||
use solana_sdk::signature::Signature;
|
use solana_sdk::signature::Signature;
|
||||||
use std::{
|
use std::{
|
||||||
sync::{
|
sync::{
|
||||||
@@ -61,10 +62,7 @@ impl CompletedDataSetsService {
|
|||||||
} = completed_set_info;
|
} = completed_set_info;
|
||||||
match blockstore.get_entries_in_data_block(slot, start_index, end_index, None) {
|
match blockstore.get_entries_in_data_block(slot, start_index, end_index, None) {
|
||||||
Ok(entries) => {
|
Ok(entries) => {
|
||||||
let transactions = entries
|
let transactions = Self::get_transaction_signatures(entries);
|
||||||
.into_iter()
|
|
||||||
.flat_map(|e| e.transactions.into_iter().map(|t| t.signatures[0]))
|
|
||||||
.collect::<Vec<Signature>>();
|
|
||||||
if !transactions.is_empty() {
|
if !transactions.is_empty() {
|
||||||
rpc_subscriptions.notify_signatures_received((slot, transactions));
|
rpc_subscriptions.notify_signatures_received((slot, transactions));
|
||||||
}
|
}
|
||||||
@@ -76,7 +74,51 @@ impl CompletedDataSetsService {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_transaction_signatures(entries: Vec<Entry>) -> Vec<Signature> {
|
||||||
|
entries
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|e| {
|
||||||
|
e.transactions
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|mut t| t.signatures.drain(..).next())
|
||||||
|
})
|
||||||
|
.collect::<Vec<Signature>>()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn join(self) -> thread::Result<()> {
|
pub fn join(self) -> thread::Result<()> {
|
||||||
self.thread_hdl.join()
|
self.thread_hdl.join()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod test {
|
||||||
|
use super::*;
|
||||||
|
use solana_sdk::hash::Hash;
|
||||||
|
use solana_sdk::signature::{Keypair, Signer};
|
||||||
|
use solana_sdk::transaction::Transaction;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_zero_signatures() {
|
||||||
|
let tx = Transaction::new_with_payer(&[], None);
|
||||||
|
let entries = vec![Entry::new(&Hash::default(), 1, vec![tx])];
|
||||||
|
let signatures = CompletedDataSetsService::get_transaction_signatures(entries);
|
||||||
|
assert!(signatures.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_multi_signatures() {
|
||||||
|
let kp = Keypair::new();
|
||||||
|
let tx =
|
||||||
|
Transaction::new_signed_with_payer(&[], Some(&kp.pubkey()), &[&kp], Hash::default());
|
||||||
|
let entries = vec![Entry::new(&Hash::default(), 1, vec![tx.clone()])];
|
||||||
|
let signatures = CompletedDataSetsService::get_transaction_signatures(entries);
|
||||||
|
assert_eq!(signatures.len(), 1);
|
||||||
|
|
||||||
|
let entries = vec![
|
||||||
|
Entry::new(&Hash::default(), 1, vec![tx.clone(), tx.clone()]),
|
||||||
|
Entry::new(&Hash::default(), 1, vec![tx]),
|
||||||
|
];
|
||||||
|
let signatures = CompletedDataSetsService::get_transaction_signatures(entries);
|
||||||
|
assert_eq!(signatures.len(), 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user