Compare commits
88 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
03b21f2e9d | ||
|
cc5565b17e | ||
|
50beef0b15 | ||
|
06a54e1423 | ||
|
4d731ecd08 | ||
|
ee06789a66 | ||
|
2dabe1d706 | ||
|
3b1279a005 | ||
|
5c9f85f28d | ||
|
e12dd46ef3 | ||
|
c4fa03b478 | ||
|
9fb749deb7 | ||
|
bd48344de2 | ||
|
78e54f1d2c | ||
|
76a6576976 | ||
|
92ec1ae255 | ||
|
0d203728cc | ||
|
625773e5b8 | ||
|
a4cb1e45ae | ||
|
8aded2778e | ||
|
d940c5b1a3 | ||
|
1be045df94 | ||
|
86191911c7 | ||
|
8f852d8a6b | ||
|
68a439f8da | ||
|
e021832708 | ||
|
87b11aa187 | ||
|
7475a6f444 | ||
|
86ce650661 | ||
|
4dc5a53014 | ||
|
5e35cf3536 | ||
|
e8a8d1efb3 | ||
|
defd9238fa | ||
|
5f061dcea1 | ||
|
e6ee27a738 | ||
|
dd2d25d698 | ||
|
9096c3df02 | ||
|
9f94c2a9a0 | ||
|
34213da9f4 | ||
|
c3c4991c44 | ||
|
9d37a33dcd | ||
|
a04ca03fee | ||
|
64ce4a6203 | ||
|
7ac3c9ec76 | ||
|
7d91515e8d | ||
|
4e3f2c3d2d | ||
|
8b67ba6d3d | ||
|
c2ce68ab90 | ||
|
fe87cb1cd1 | ||
|
1c8f6a836a | ||
|
3d5ff7968e | ||
|
d6160f7744 | ||
|
5e9ce99abf | ||
|
ebd6fe7acb | ||
|
9e91a2c2fd | ||
|
899f57962a | ||
|
3176b00e57 | ||
|
08b9da8397 | ||
|
2bc21ecba2 | ||
|
5b2a65fab3 | ||
|
f5d56eabf3 | ||
|
af45efb62c | ||
|
f528cda832 | ||
|
eeef9f4e59 | ||
|
32124b59e9 | ||
|
aa9772f9c0 | ||
|
5f183bd773 | ||
|
2238e5001b | ||
|
79fa7ef55c | ||
|
07df827411 | ||
|
a259ff0e72 | ||
|
d7d3e767e7 | ||
|
6e8aa9af17 | ||
|
0236de7bc8 | ||
|
899bd1572a | ||
|
97ec4cd44e | ||
|
5500970a7e | ||
|
caea04d8d5 | ||
|
b1a90c3580 | ||
|
5bd4e38345 | ||
|
fddba08571 | ||
|
87963764fa | ||
|
b691a159dd | ||
|
5af1d48be8 | ||
|
3b3ec3313f | ||
|
be00246fb5 | ||
|
1d80ba9edf | ||
|
4bcf976ecd |
2
.buildkite/env/secrets.ejson
vendored
2
.buildkite/env/secrets.ejson
vendored
@@ -2,6 +2,6 @@
|
||||
"_public_key": "ae29f4f7ad2fc92de70d470e411c8426d5d48db8817c9e3dae574b122192335f",
|
||||
"_comment": "These credentials are encrypted and pose no risk",
|
||||
"environment": {
|
||||
"CODECOV_TOKEN": "EJ[1:KToenD1Sr3w82lHGxz1n+j3hwNlLk/1pYrjZHlvY6kE=:hN1Q25omtJ+4yYVn+qzIsPLKT3O6J9XN:DMLNLXi/pkWgvwF6gNIcNF222sgsRR9LnwLZYj0P0wGj7q6w8YQnd1Rskj+sRroI/z5pQg==]"
|
||||
"CODECOV_TOKEN": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:3K68mE38LJ2RB98VWmjuNLFBNn1XTGR4:cR4r05/TOZQKmEZp1v4CSgUJtC6QJiOaL85QjXW0qZ061fMnsBA8AtAPMDoDq4WCGOZM1A==]"
|
||||
}
|
||||
}
|
||||
|
@@ -36,7 +36,4 @@ export CARGO_TARGET_CACHE=$HOME/cargo-target-cache/"$CHANNEL"-"$BUILDKITE_LABEL"
|
||||
# `std:
|
||||
# "found possibly newer version of crate `std` which `xyz` depends on
|
||||
rm -rf target/bpfel-unknown-unknown
|
||||
if [[ $BUILDKITE_LABEL = "stable-perf" ]]; then
|
||||
rm -rf target/release
|
||||
fi
|
||||
)
|
||||
|
@@ -73,7 +73,7 @@ jobs:
|
||||
|
||||
language: node_js
|
||||
node_js:
|
||||
- "lts/*"
|
||||
- "node"
|
||||
|
||||
cache:
|
||||
directories:
|
||||
|
946
Cargo.lock
generated
946
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
10
Cargo.toml
10
Cargo.toml
@@ -21,7 +21,6 @@ members = [
|
||||
"perf",
|
||||
"validator",
|
||||
"genesis",
|
||||
"genesis-utils",
|
||||
"gossip",
|
||||
"install",
|
||||
"keygen",
|
||||
@@ -32,6 +31,7 @@ members = [
|
||||
"log-analyzer",
|
||||
"merkle-root-bench",
|
||||
"merkle-tree",
|
||||
"stake-o-matic",
|
||||
"storage-bigtable",
|
||||
"storage-proto",
|
||||
"streamer",
|
||||
@@ -39,20 +39,21 @@ members = [
|
||||
"metrics",
|
||||
"net-shaper",
|
||||
"notifier",
|
||||
"poh",
|
||||
"poh-bench",
|
||||
"program-test",
|
||||
"programs/secp256k1",
|
||||
"programs/bpf_loader",
|
||||
"programs/budget",
|
||||
"programs/config",
|
||||
"programs/exchange",
|
||||
"programs/failure",
|
||||
"programs/noop",
|
||||
"programs/ownable",
|
||||
"programs/stake",
|
||||
"programs/vest",
|
||||
"programs/vote",
|
||||
"remote-wallet",
|
||||
"rpc",
|
||||
"ramp-tps",
|
||||
"runtime",
|
||||
"runtime/store-tool",
|
||||
"sdk",
|
||||
@@ -76,6 +77,3 @@ members = [
|
||||
exclude = [
|
||||
"programs/bpf",
|
||||
]
|
||||
|
||||
[profile.dev]
|
||||
split-debuginfo = "unpacked"
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<p align="center">
|
||||
<a href="https://solana.com">
|
||||
<img alt="Solana" src="https://i.imgur.com/uBVzyX3.png" width="250" />
|
||||
<img alt="Solana" src="https://i.imgur.com/OMnvVEz.png" width="250" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@@ -32,12 +32,6 @@ $ sudo apt-get update
|
||||
$ sudo apt-get install libssl-dev libudev-dev pkg-config zlib1g-dev llvm clang make
|
||||
```
|
||||
|
||||
On Mac M1s, make sure you set up your terminal & homebrew [to use](https://5balloons.info/correct-way-to-install-and-use-homebrew-on-m1-macs/) Rosetta. You can install it with:
|
||||
|
||||
```bash
|
||||
$ softwareupdate --install-rosetta
|
||||
```
|
||||
|
||||
## **2. Download the source code.**
|
||||
|
||||
```bash
|
||||
|
28
SECURITY.md
28
SECURITY.md
@@ -42,36 +42,14 @@ RPC DoS/Crashes:
|
||||
$5,000 USD in locked SOL tokens (locked for 12 months)
|
||||
* RPC attacks
|
||||
|
||||
Out of Scope:
|
||||
The following components are out of scope for the bounty program
|
||||
* Metrics: `/metrics` in the monorepo as well as https://metrics.solana.com
|
||||
* Explorer: `/explorer` in the monorepo as well as https://explorer.solana.com
|
||||
* Any encrypted credentials, auth tokens, etc. checked into the repo
|
||||
* Bugs in dependencies. Please take them upstream!
|
||||
* Attacks that require social engineering
|
||||
|
||||
Eligibility:
|
||||
* The participant submitting the bug report shall follow the process outlined within this document
|
||||
* The participant submitting the bug bounty shall follow the process outlined within this document
|
||||
* Valid exploits can be eligible even if they are not successfully executed on the cluster
|
||||
* Multiple submissions for the same class of exploit are still eligible for compensation, though may be compensated at a lower rate, however these will be assessed on a case-by-case basis
|
||||
* Participants must complete KYC and sign the participation agreement here when the registrations are open https://solana.com/validator-registration. Security exploits will still be assessed and open for submission at all times. This needs only be done prior to distribution of tokens.
|
||||
|
||||
Payment of Bug Bounties:
|
||||
* Payments for eligible bug reports are distributed monthly.
|
||||
* Bounties for all bug reports submitted in a given month are paid out in the middle of the
|
||||
following month.
|
||||
* The SOL/USD conversion rate used for payments is the market price at the end of
|
||||
the last day of the month for the month in which the bug was submitted.
|
||||
* The reference for this price is the Closing Price given by Coingecko.com on
|
||||
that date given here:
|
||||
https://www.coingecko.com/en/coins/solana/historical_data/usd#panel
|
||||
* For example, for all bugs submitted in March 2021, the SOL/USD price for bug
|
||||
payouts is the Close price on 2021-03-31 of $19.49. This applies to all bugs
|
||||
submitted in March 2021, to be paid in mid-April 2021.
|
||||
* Bug bounties are paid out in
|
||||
[stake accounts](https://solana.com/staking) with a
|
||||
[lockup](https://docs.solana.com/staking/stake-accounts#lockups)
|
||||
expiring 12 months from the last day of the month in which the bug was submitted.
|
||||
Notes:
|
||||
* All locked tokens can be staked during the lockup period
|
||||
|
||||
<a name="process"></a>
|
||||
## Incident Response Process
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-account-decoder"
|
||||
version = "1.7.1"
|
||||
version = "1.6.2"
|
||||
description = "Solana account decoder"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -19,10 +19,10 @@ lazy_static = "1.4.0"
|
||||
serde = "1.0.122"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.56"
|
||||
solana-config-program = { path = "../programs/config", version = "=1.7.1" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.1" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "=1.7.1" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.7.1" }
|
||||
solana-config-program = { path = "../programs/config", version = "=1.6.2" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.2" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "=1.6.2" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.6.2" }
|
||||
spl-token-v2-0 = { package = "spl-token", version = "=3.1.0", features = ["no-entrypoint"] }
|
||||
thiserror = "1.0"
|
||||
zstd = "0.5.1"
|
||||
|
@@ -48,7 +48,7 @@ pub enum UiAccountData {
|
||||
Binary(String, UiAccountEncoding),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum UiAccountEncoding {
|
||||
Binary, // Legacy. Retained for RPC backwards compatibility
|
||||
@@ -62,7 +62,7 @@ pub enum UiAccountEncoding {
|
||||
impl UiAccount {
|
||||
pub fn encode<T: ReadableAccount>(
|
||||
pubkey: &Pubkey,
|
||||
account: &T,
|
||||
account: T,
|
||||
encoding: UiAccountEncoding,
|
||||
additional_data: Option<AccountAdditionalData>,
|
||||
data_slice_config: Option<UiDataSliceConfig>,
|
||||
@@ -224,7 +224,7 @@ mod test {
|
||||
fn test_base64_zstd() {
|
||||
let encoded_account = UiAccount::encode(
|
||||
&Pubkey::default(),
|
||||
&AccountSharedData::from(Account {
|
||||
AccountSharedData::from(Account {
|
||||
data: vec![0; 1024],
|
||||
..Account::default()
|
||||
}),
|
||||
|
@@ -9,13 +9,7 @@ pub fn parse_nonce(data: &[u8]) -> Result<UiNonceState, ParseAccountError> {
|
||||
.map_err(|_| ParseAccountError::from(InstructionError::InvalidAccountData))?;
|
||||
let nonce_state = nonce_state.convert_to_current();
|
||||
match nonce_state {
|
||||
// This prevents parsing an allocated System-owned account with empty data of any non-zero
|
||||
// length as `uninitialized` nonce. An empty account of the wrong length can never be
|
||||
// initialized as a nonce account, and an empty account of the correct length may not be an
|
||||
// uninitialized nonce account, since it can be assigned to another program.
|
||||
State::Uninitialized => Err(ParseAccountError::from(
|
||||
InstructionError::InvalidAccountData,
|
||||
)),
|
||||
State::Uninitialized => Ok(UiNonceState::Uninitialized),
|
||||
State::Initialized(data) => Ok(UiNonceState::Initialized(UiNonceData {
|
||||
authority: data.authority.to_string(),
|
||||
blockhash: data.blockhash.to_string(),
|
||||
|
@@ -14,23 +14,23 @@ use std::str::FromStr;
|
||||
// A helper function to convert spl_token_v2_0::id() as spl_sdk::pubkey::Pubkey to
|
||||
// solana_sdk::pubkey::Pubkey
|
||||
pub fn spl_token_id_v2_0() -> Pubkey {
|
||||
Pubkey::new_from_array(spl_token_v2_0::id().to_bytes())
|
||||
Pubkey::from_str(&spl_token_v2_0::id().to_string()).unwrap()
|
||||
}
|
||||
|
||||
// A helper function to convert spl_token_v2_0::native_mint::id() as spl_sdk::pubkey::Pubkey to
|
||||
// solana_sdk::pubkey::Pubkey
|
||||
pub fn spl_token_v2_0_native_mint() -> Pubkey {
|
||||
Pubkey::new_from_array(spl_token_v2_0::native_mint::id().to_bytes())
|
||||
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::new_from_array(pubkey.to_bytes())
|
||||
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::new_from_array(pubkey.to_bytes())
|
||||
Pubkey::from_str(&pubkey.to_string()).unwrap()
|
||||
}
|
||||
|
||||
pub fn parse_token(
|
||||
|
@@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-accounts-bench"
|
||||
version = "1.7.1"
|
||||
version = "1.6.2"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -11,11 +11,11 @@ publish = false
|
||||
[dependencies]
|
||||
log = "0.4.11"
|
||||
rayon = "1.5.0"
|
||||
solana-logger = { path = "../logger", version = "=1.7.1" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.1" }
|
||||
solana-measure = { path = "../measure", version = "=1.7.1" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.1" }
|
||||
solana-version = { path = "../version", version = "=1.7.1" }
|
||||
solana-logger = { path = "../logger", version = "=1.6.2" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.6.2" }
|
||||
solana-measure = { path = "../measure", version = "=1.6.2" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.2" }
|
||||
solana-version = { path = "../version", version = "=1.6.2" }
|
||||
rand = "0.7.0"
|
||||
clap = "2.33.1"
|
||||
crossbeam-channel = "0.4"
|
||||
|
@@ -6,11 +6,10 @@ use rayon::prelude::*;
|
||||
use solana_measure::measure::Measure;
|
||||
use solana_runtime::{
|
||||
accounts::{create_test_accounts, update_accounts_bench, Accounts},
|
||||
accounts_index::AccountSecondaryIndexes,
|
||||
ancestors::Ancestors,
|
||||
accounts_index::Ancestors,
|
||||
};
|
||||
use solana_sdk::{genesis_config::ClusterType, pubkey::Pubkey};
|
||||
use std::{env, fs, path::PathBuf};
|
||||
use std::{collections::HashSet, env, fs, path::PathBuf};
|
||||
|
||||
fn main() {
|
||||
solana_logger::setup();
|
||||
@@ -59,12 +58,8 @@ fn main() {
|
||||
if fs::remove_dir_all(path.clone()).is_err() {
|
||||
println!("Warning: Couldn't remove {:?}", path);
|
||||
}
|
||||
let accounts = Accounts::new_with_config(
|
||||
vec![path],
|
||||
&ClusterType::Testnet,
|
||||
AccountSecondaryIndexes::default(),
|
||||
false,
|
||||
);
|
||||
let accounts =
|
||||
Accounts::new_with_config(vec![path], &ClusterType::Testnet, HashSet::new(), false);
|
||||
println!("Creating {} accounts", num_accounts);
|
||||
let mut create_time = Measure::start("create accounts");
|
||||
let pubkeys: Vec<_> = (0..num_slots)
|
||||
@@ -88,19 +83,17 @@ fn main() {
|
||||
num_slots,
|
||||
create_time
|
||||
);
|
||||
let mut ancestors = Vec::with_capacity(num_slots);
|
||||
ancestors.push(0);
|
||||
let mut ancestors: Ancestors = vec![(0, 0)].into_iter().collect();
|
||||
for i in 1..num_slots {
|
||||
ancestors.push(i as u64);
|
||||
ancestors.insert(i as u64, i - 1);
|
||||
accounts.add_root(i as u64);
|
||||
}
|
||||
let ancestors = Ancestors::from(ancestors);
|
||||
let mut elapsed = vec![0; iterations];
|
||||
let mut elapsed_store = vec![0; iterations];
|
||||
for x in 0..iterations {
|
||||
if clean {
|
||||
let mut time = Measure::start("clean");
|
||||
accounts.accounts_db.clean_accounts(None, false);
|
||||
accounts.accounts_db.clean_accounts(None);
|
||||
time.stop();
|
||||
println!("{}", time);
|
||||
for slot in 0..num_slots {
|
||||
|
@@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-accounts-cluster-bench"
|
||||
version = "1.7.1"
|
||||
version = "1.6.2"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -13,23 +13,22 @@ clap = "2.33.1"
|
||||
log = "0.4.11"
|
||||
rand = "0.7.0"
|
||||
rayon = "1.4.1"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.7.1" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.1" }
|
||||
solana-client = { path = "../client", version = "=1.7.1" }
|
||||
solana-core = { path = "../core", version = "=1.7.1" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.7.1" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.7.1" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.1" }
|
||||
solana-measure = { path = "../measure", version = "=1.7.1" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.1" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.1" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.1" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.7.1" }
|
||||
solana-version = { path = "../version", version = "=1.7.1" }
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.6.2" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.6.2" }
|
||||
solana-client = { path = "../client", version = "=1.6.2" }
|
||||
solana-core = { path = "../core", version = "=1.6.2" }
|
||||
solana-measure = { path = "../measure", version = "=1.6.2" }
|
||||
solana-logger = { path = "../logger", version = "=1.6.2" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.6.2" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.6.2" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.6.2" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.2" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.6.2" }
|
||||
solana-version = { path = "../version", version = "=1.6.2" }
|
||||
spl-token-v2-0 = { package = "spl-token", version = "=3.1.0", features = ["no-entrypoint"] }
|
||||
|
||||
[dev-dependencies]
|
||||
solana-local-cluster = { path = "../local-cluster", version = "=1.7.1" }
|
||||
solana-local-cluster = { path = "../local-cluster", version = "=1.6.2" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -1,13 +1,13 @@
|
||||
#![allow(clippy::integer_arithmetic)]
|
||||
use clap::{crate_description, crate_name, value_t, values_t_or_exit, App, Arg};
|
||||
use clap::{crate_description, crate_name, value_t, value_t_or_exit, App, Arg};
|
||||
use log::*;
|
||||
use rand::{thread_rng, Rng};
|
||||
use rayon::prelude::*;
|
||||
use solana_account_decoder::parse_token::spl_token_v2_0_pubkey;
|
||||
use solana_clap_utils::input_parsers::pubkey_of;
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_core::gossip_service::discover;
|
||||
use solana_faucet::faucet::{request_airdrop_transaction, FAUCET_PORT};
|
||||
use solana_gossip::gossip_service::discover;
|
||||
use solana_measure::measure::Measure;
|
||||
use solana_runtime::inline_spl_token_v2_0;
|
||||
use solana_sdk::{
|
||||
@@ -32,10 +32,6 @@ use std::{
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
// Create and close messages both require 2 signatures; if transaction construction changes, update
|
||||
// this magic number
|
||||
const NUM_SIGNATURES: u64 = 2;
|
||||
|
||||
pub fn airdrop_lamports(
|
||||
client: &RpcClient,
|
||||
faucet_addr: &SocketAddr,
|
||||
@@ -189,13 +185,14 @@ impl TransactionExecutor {
|
||||
let mut start = Measure::start("sig_status");
|
||||
let statuses: Vec<_> = sigs_w
|
||||
.chunks(200)
|
||||
.flat_map(|sig_chunk| {
|
||||
.map(|sig_chunk| {
|
||||
let only_sigs: Vec<_> = sig_chunk.iter().map(|s| s.0).collect();
|
||||
client
|
||||
.get_signature_statuses(&only_sigs)
|
||||
.expect("status fail")
|
||||
.value
|
||||
})
|
||||
.flatten()
|
||||
.collect();
|
||||
let mut num_cleared = 0;
|
||||
let start_len = sigs_w.len();
|
||||
@@ -359,7 +356,7 @@ fn make_close_message(
|
||||
fn run_accounts_bench(
|
||||
entrypoint_addr: SocketAddr,
|
||||
faucet_addr: SocketAddr,
|
||||
payer_keypairs: &[&Keypair],
|
||||
keypair: &Keypair,
|
||||
iterations: usize,
|
||||
maybe_space: Option<u64>,
|
||||
batch_size: usize,
|
||||
@@ -372,7 +369,7 @@ fn run_accounts_bench(
|
||||
let client =
|
||||
RpcClient::new_socket_with_commitment(entrypoint_addr, CommitmentConfig::confirmed());
|
||||
|
||||
info!("Targeting {}", entrypoint_addr);
|
||||
info!("Targetting {}", entrypoint_addr);
|
||||
|
||||
let mut last_blockhash = Instant::now();
|
||||
let mut last_log = Instant::now();
|
||||
@@ -381,10 +378,7 @@ fn run_accounts_bench(
|
||||
let mut tx_sent_count = 0;
|
||||
let mut total_accounts_created = 0;
|
||||
let mut total_accounts_closed = 0;
|
||||
let mut balances: Vec<_> = payer_keypairs
|
||||
.iter()
|
||||
.map(|keypair| client.get_balance(&keypair.pubkey()).unwrap_or(0))
|
||||
.collect();
|
||||
let mut balance = client.get_balance(&keypair.pubkey()).unwrap_or(0);
|
||||
let mut last_balance = Instant::now();
|
||||
|
||||
let default_max_lamports = 1000;
|
||||
@@ -401,7 +395,7 @@ fn run_accounts_bench(
|
||||
max_closed: Arc::new(AtomicU64::default()),
|
||||
};
|
||||
|
||||
info!("Starting balance(s): {:?}", balances);
|
||||
info!("Starting balance: {}", balance);
|
||||
|
||||
let executor = TransactionExecutor::new(entrypoint_addr);
|
||||
|
||||
@@ -411,32 +405,31 @@ fn run_accounts_bench(
|
||||
last_blockhash = Instant::now();
|
||||
}
|
||||
|
||||
let fee = recent_blockhash
|
||||
.1
|
||||
.lamports_per_signature
|
||||
.saturating_mul(NUM_SIGNATURES);
|
||||
let message = make_create_message(
|
||||
keypair,
|
||||
&base_keypair,
|
||||
seed_tracker.max_created.clone(),
|
||||
num_instructions,
|
||||
min_balance,
|
||||
maybe_space,
|
||||
mint,
|
||||
);
|
||||
let fee = recent_blockhash.1.calculate_fee(&message);
|
||||
let lamports = min_balance + fee;
|
||||
|
||||
for (i, balance) in balances.iter_mut().enumerate() {
|
||||
if *balance < lamports || last_balance.elapsed().as_millis() > 2000 {
|
||||
if let Ok(b) = client.get_balance(&payer_keypairs[i].pubkey()) {
|
||||
*balance = b;
|
||||
}
|
||||
last_balance = Instant::now();
|
||||
if *balance < lamports * 2 {
|
||||
info!(
|
||||
"Balance {} is less than needed: {}, doing aidrop...",
|
||||
balance, lamports
|
||||
);
|
||||
if !airdrop_lamports(
|
||||
&client,
|
||||
&faucet_addr,
|
||||
&payer_keypairs[i],
|
||||
lamports * 100_000,
|
||||
) {
|
||||
warn!("failed airdrop, exiting");
|
||||
return;
|
||||
}
|
||||
if balance < lamports || last_balance.elapsed().as_millis() > 2000 {
|
||||
if let Ok(b) = client.get_balance(&keypair.pubkey()) {
|
||||
balance = b;
|
||||
}
|
||||
last_balance = Instant::now();
|
||||
if balance < lamports {
|
||||
info!(
|
||||
"Balance {} is less than needed: {}, doing aidrop...",
|
||||
balance, lamports
|
||||
);
|
||||
if !airdrop_lamports(&client, &faucet_addr, keypair, lamports * 100_000) {
|
||||
warn!("failed airdrop, exiting");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -444,36 +437,29 @@ fn run_accounts_bench(
|
||||
let sigs_len = executor.num_outstanding();
|
||||
if sigs_len < batch_size {
|
||||
let num_to_create = batch_size - sigs_len;
|
||||
if num_to_create >= payer_keypairs.len() {
|
||||
info!("creating {} new", num_to_create);
|
||||
let chunk_size = num_to_create / payer_keypairs.len();
|
||||
if chunk_size > 0 {
|
||||
for (i, keypair) in payer_keypairs.iter().enumerate() {
|
||||
let txs: Vec<_> = (0..chunk_size)
|
||||
.into_par_iter()
|
||||
.map(|_| {
|
||||
let message = make_create_message(
|
||||
keypair,
|
||||
&base_keypair,
|
||||
seed_tracker.max_created.clone(),
|
||||
num_instructions,
|
||||
min_balance,
|
||||
maybe_space,
|
||||
mint,
|
||||
);
|
||||
let signers: Vec<&Keypair> = vec![keypair, &base_keypair];
|
||||
Transaction::new(&signers, message, recent_blockhash.0)
|
||||
})
|
||||
.collect();
|
||||
balances[i] = balances[i].saturating_sub(lamports * txs.len() as u64);
|
||||
info!("txs: {}", txs.len());
|
||||
let new_ids = executor.push_transactions(txs);
|
||||
info!("ids: {}", new_ids.len());
|
||||
tx_sent_count += new_ids.len();
|
||||
total_accounts_created += num_instructions * new_ids.len();
|
||||
}
|
||||
}
|
||||
}
|
||||
info!("creating {} new", num_to_create);
|
||||
let txs: Vec<_> = (0..num_to_create)
|
||||
.into_par_iter()
|
||||
.map(|_| {
|
||||
let message = make_create_message(
|
||||
keypair,
|
||||
&base_keypair,
|
||||
seed_tracker.max_created.clone(),
|
||||
num_instructions,
|
||||
min_balance,
|
||||
maybe_space,
|
||||
mint,
|
||||
);
|
||||
let signers: Vec<&Keypair> = vec![keypair, &base_keypair];
|
||||
Transaction::new(&signers, message, recent_blockhash.0)
|
||||
})
|
||||
.collect();
|
||||
balance = balance.saturating_sub(lamports * txs.len() as u64);
|
||||
info!("txs: {}", txs.len());
|
||||
let new_ids = executor.push_transactions(txs);
|
||||
info!("ids: {}", new_ids.len());
|
||||
tx_sent_count += new_ids.len();
|
||||
total_accounts_created += num_instructions * new_ids.len();
|
||||
|
||||
if close_nth > 0 {
|
||||
let expected_closed = total_accounts_created as u64 / close_nth;
|
||||
@@ -482,18 +468,18 @@ fn run_accounts_bench(
|
||||
.into_par_iter()
|
||||
.map(|_| {
|
||||
let message = make_close_message(
|
||||
&payer_keypairs[0],
|
||||
keypair,
|
||||
&base_keypair,
|
||||
seed_tracker.max_closed.clone(),
|
||||
1,
|
||||
min_balance,
|
||||
mint.is_some(),
|
||||
);
|
||||
let signers: Vec<&Keypair> = vec![&payer_keypairs[0], &base_keypair];
|
||||
let signers: Vec<&Keypair> = vec![keypair, &base_keypair];
|
||||
Transaction::new(&signers, message, recent_blockhash.0)
|
||||
})
|
||||
.collect();
|
||||
balances[0] = balances[0].saturating_sub(fee * txs.len() as u64);
|
||||
balance = balance.saturating_sub(fee * txs.len() as u64);
|
||||
info!("close txs: {}", txs.len());
|
||||
let new_ids = executor.push_transactions(txs);
|
||||
info!("close ids: {}", new_ids.len());
|
||||
@@ -508,8 +494,8 @@ fn run_accounts_bench(
|
||||
count += 1;
|
||||
if last_log.elapsed().as_millis() > 3000 {
|
||||
info!(
|
||||
"total_accounts_created: {} total_accounts_closed: {} tx_sent_count: {} loop_count: {} balance(s): {:?}",
|
||||
total_accounts_created, total_accounts_closed, tx_sent_count, count, balances
|
||||
"total_accounts_created: {} total_accounts_closed: {} tx_sent_count: {} loop_count: {} balance: {}",
|
||||
total_accounts_created, total_accounts_closed, tx_sent_count, count, balance
|
||||
);
|
||||
last_log = Instant::now();
|
||||
}
|
||||
@@ -560,7 +546,6 @@ fn main() {
|
||||
Arg::with_name("identity")
|
||||
.long("identity")
|
||||
.takes_value(true)
|
||||
.multiple(true)
|
||||
.value_name("FILE")
|
||||
.help("keypair file"),
|
||||
)
|
||||
@@ -576,13 +561,7 @@ fn main() {
|
||||
.long("close-frequency")
|
||||
.takes_value(true)
|
||||
.value_name("BYTES")
|
||||
.help(
|
||||
"Send close transactions after this many accounts created. \
|
||||
Note: a `close-frequency` value near or below `batch-size` \
|
||||
may result in transaction-simulation errors, as the close \
|
||||
transactions will be submitted before the corresponding \
|
||||
create transactions have been confirmed",
|
||||
),
|
||||
.help("Send close transactions after this many accounts created"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("num_instructions")
|
||||
@@ -642,29 +621,20 @@ fn main() {
|
||||
|
||||
let mint = pubkey_of(&matches, "mint");
|
||||
|
||||
let payer_keypairs: Vec<_> = values_t_or_exit!(matches, "identity", String)
|
||||
.iter()
|
||||
.map(|keypair_string| {
|
||||
read_keypair_file(keypair_string)
|
||||
.unwrap_or_else(|_| panic!("bad keypair {:?}", keypair_string))
|
||||
})
|
||||
.collect();
|
||||
let mut payer_keypair_refs: Vec<&Keypair> = vec![];
|
||||
for keypair in payer_keypairs.iter() {
|
||||
payer_keypair_refs.push(keypair);
|
||||
}
|
||||
let keypair =
|
||||
read_keypair_file(&value_t_or_exit!(matches, "identity", String)).expect("bad keypair");
|
||||
|
||||
let rpc_addr = if !skip_gossip {
|
||||
info!("Finding cluster entry: {:?}", entrypoint_addr);
|
||||
let (gossip_nodes, _validators) = discover(
|
||||
None, // keypair
|
||||
None,
|
||||
Some(&entrypoint_addr),
|
||||
None, // num_nodes
|
||||
Duration::from_secs(60), // timeout
|
||||
None, // find_node_by_pubkey
|
||||
Some(&entrypoint_addr), // find_node_by_gossip_addr
|
||||
None, // my_gossip_addr
|
||||
0, // my_shred_version
|
||||
None,
|
||||
Some(60),
|
||||
None,
|
||||
Some(&entrypoint_addr),
|
||||
None,
|
||||
0,
|
||||
)
|
||||
.unwrap_or_else(|err| {
|
||||
eprintln!("Failed to discover {} node: {:?}", entrypoint_addr, err);
|
||||
@@ -681,7 +651,7 @@ fn main() {
|
||||
run_accounts_bench(
|
||||
rpc_addr,
|
||||
faucet_addr,
|
||||
&payer_keypair_refs,
|
||||
&keypair,
|
||||
iterations,
|
||||
space,
|
||||
batch_size,
|
||||
@@ -727,7 +697,7 @@ pub mod test {
|
||||
run_accounts_bench(
|
||||
cluster.entry_point_info.rpc,
|
||||
faucet_addr,
|
||||
&[&cluster.funding_keypair],
|
||||
&cluster.funding_keypair,
|
||||
iterations,
|
||||
maybe_space,
|
||||
batch_size,
|
||||
|
@@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-banking-bench"
|
||||
version = "1.7.1"
|
||||
version = "1.6.2"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -14,18 +14,16 @@ crossbeam-channel = "0.4"
|
||||
log = "0.4.11"
|
||||
rand = "0.7.0"
|
||||
rayon = "1.5.0"
|
||||
solana-core = { path = "../core", version = "=1.7.1" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.1" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.7.1" }
|
||||
solana-ledger = { path = "../ledger", version = "=1.7.1" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.1" }
|
||||
solana-measure = { path = "../measure", version = "=1.7.1" }
|
||||
solana-perf = { path = "../perf", version = "=1.7.1" }
|
||||
solana-poh = { path = "../poh", version = "=1.7.1" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.1" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.7.1" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.1" }
|
||||
solana-version = { path = "../version", version = "=1.7.1" }
|
||||
solana-core = { path = "../core", version = "=1.6.2" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.6.2" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.6.2" }
|
||||
solana-perf = { path = "../perf", version = "=1.6.2" }
|
||||
solana-ledger = { path = "../ledger", version = "=1.6.2" }
|
||||
solana-logger = { path = "../logger", version = "=1.6.2" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.6.2" }
|
||||
solana-measure = { path = "../measure", version = "=1.6.2" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.2" }
|
||||
solana-version = { path = "../version", version = "=1.6.2" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -4,8 +4,13 @@ use crossbeam_channel::unbounded;
|
||||
use log::*;
|
||||
use rand::{thread_rng, Rng};
|
||||
use rayon::prelude::*;
|
||||
use solana_core::banking_stage::BankingStage;
|
||||
use solana_gossip::{cluster_info::ClusterInfo, cluster_info::Node};
|
||||
use solana_core::{
|
||||
banking_stage::{create_test_recorder, BankingStage},
|
||||
cluster_info::ClusterInfo,
|
||||
cluster_info::Node,
|
||||
poh_recorder::PohRecorder,
|
||||
poh_recorder::WorkingBankEntry,
|
||||
};
|
||||
use solana_ledger::{
|
||||
blockstore::Blockstore,
|
||||
genesis_utils::{create_genesis_config, GenesisConfigInfo},
|
||||
@@ -13,7 +18,6 @@ use solana_ledger::{
|
||||
};
|
||||
use solana_measure::measure::Measure;
|
||||
use solana_perf::packet::to_packets_chunked;
|
||||
use solana_poh::poh_recorder::{create_test_recorder, PohRecorder, WorkingBankEntry};
|
||||
use solana_runtime::{
|
||||
accounts_background_service::AbsRequestSender, bank::Bank, bank_forks::BankForks,
|
||||
};
|
||||
@@ -74,7 +78,7 @@ fn make_accounts_txs(
|
||||
.into_par_iter()
|
||||
.map(|_| {
|
||||
let mut new = dummy.clone();
|
||||
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen::<u8>()).collect();
|
||||
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
|
||||
if !same_payer {
|
||||
new.message.account_keys[0] = solana_sdk::pubkey::new_rand();
|
||||
}
|
||||
@@ -185,7 +189,7 @@ fn main() {
|
||||
genesis_config.hash(),
|
||||
);
|
||||
// Ignore any pesky duplicate signature errors in the case we are using single-payer
|
||||
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen::<u8>()).collect();
|
||||
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
|
||||
fund.signatures = vec![Signature::new(&sig[0..64])];
|
||||
let x = bank.process_transaction(&fund);
|
||||
x.unwrap();
|
||||
@@ -351,7 +355,7 @@ fn main() {
|
||||
if bank.slot() > 0 && bank.slot() % 16 == 0 {
|
||||
for tx in transactions.iter_mut() {
|
||||
tx.message.recent_blockhash = bank.last_blockhash();
|
||||
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen::<u8>()).collect();
|
||||
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
|
||||
tx.signatures[0] = Signature::new(&sig[0..64]);
|
||||
}
|
||||
verified = to_packets_chunked(&transactions.clone(), packets_per_chunk);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-banks-client"
|
||||
version = "1.7.1"
|
||||
version = "1.6.2"
|
||||
description = "Solana banks client"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -15,16 +15,16 @@ borsh = "0.8.1"
|
||||
borsh-derive = "0.8.1"
|
||||
futures = "0.3"
|
||||
mio = "0.7.6"
|
||||
solana-banks-interface = { path = "../banks-interface", version = "=1.7.1" }
|
||||
solana-program = { path = "../sdk/program", version = "=1.7.1" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.1" }
|
||||
solana-banks-interface = { path = "../banks-interface", version = "=1.6.2" }
|
||||
solana-program = { path = "../sdk/program", version = "=1.6.2" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.2" }
|
||||
tarpc = { version = "0.24.1", features = ["full"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio = { version = "1.1", features = ["full"] }
|
||||
tokio-serde = { version = "0.8", features = ["bincode"] }
|
||||
|
||||
[dev-dependencies]
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.1" }
|
||||
solana-banks-server = { path = "../banks-server", version = "=1.7.1" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.6.2" }
|
||||
solana-banks-server = { path = "../banks-server", version = "=1.6.2" }
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib"]
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-banks-interface"
|
||||
version = "1.7.1"
|
||||
version = "1.6.2"
|
||||
description = "Solana banks RPC interface"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -12,11 +12,11 @@ edition = "2018"
|
||||
[dependencies]
|
||||
mio = "0.7.6"
|
||||
serde = { version = "1.0.122", features = ["derive"] }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.1" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.2" }
|
||||
tarpc = { version = "0.24.1", features = ["full"] }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio = { version = "1.1", features = ["full"] }
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib"]
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-banks-server"
|
||||
version = "1.7.1"
|
||||
version = "1.6.2"
|
||||
description = "Solana banks server"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -14,12 +14,12 @@ bincode = "1.3.1"
|
||||
futures = "0.3"
|
||||
log = "0.4.11"
|
||||
mio = "0.7.6"
|
||||
solana-banks-interface = { path = "../banks-interface", version = "=1.7.1" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.1" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.1" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.7.1" }
|
||||
solana-banks-interface = { path = "../banks-interface", version = "=1.6.2" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.6.2" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.2" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.6.2" }
|
||||
tarpc = { version = "0.24.1", features = ["full"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio = { version = "1.1", features = ["full"] }
|
||||
tokio-serde = { version = "0.8", features = ["bincode"] }
|
||||
tokio-stream = "0.1"
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-bench-exchange"
|
||||
version = "1.7.1"
|
||||
version = "1.6.2"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -18,22 +18,21 @@ rand = "0.7.0"
|
||||
rayon = "1.5.0"
|
||||
serde_json = "1.0.56"
|
||||
serde_yaml = "0.8.13"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.1" }
|
||||
solana-core = { path = "../core", version = "=1.7.1" }
|
||||
solana-genesis = { path = "../genesis", version = "=1.7.1" }
|
||||
solana-client = { path = "../client", version = "=1.7.1" }
|
||||
solana-exchange-program = { path = "../programs/exchange", version = "=1.7.1" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.7.1" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.7.1" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.1" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.7.1" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.1" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.1" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.1" }
|
||||
solana-version = { path = "../version", version = "=1.7.1" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.6.2" }
|
||||
solana-core = { path = "../core", version = "=1.6.2" }
|
||||
solana-genesis = { path = "../genesis", version = "=1.6.2" }
|
||||
solana-client = { path = "../client", version = "=1.6.2" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.6.2" }
|
||||
solana-exchange-program = { path = "../programs/exchange", version = "=1.6.2" }
|
||||
solana-logger = { path = "../logger", version = "=1.6.2" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.6.2" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.6.2" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.6.2" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.2" }
|
||||
solana-version = { path = "../version", version = "=1.6.2" }
|
||||
|
||||
[dev-dependencies]
|
||||
solana-local-cluster = { path = "../local-cluster", version = "=1.7.1" }
|
||||
solana-local-cluster = { path = "../local-cluster", version = "=1.6.2" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -5,7 +5,7 @@ pub mod order_book;
|
||||
|
||||
use crate::bench::{airdrop_lamports, create_client_accounts_file, do_bench_exchange, Config};
|
||||
use log::*;
|
||||
use solana_gossip::gossip_service::{discover_cluster, get_multi_client};
|
||||
use solana_core::gossip_service::{discover_cluster, get_multi_client};
|
||||
use solana_sdk::signature::Signer;
|
||||
|
||||
fn main() {
|
||||
|
@@ -1,11 +1,13 @@
|
||||
use log::*;
|
||||
use solana_bench_exchange::bench::{airdrop_lamports, do_bench_exchange, Config};
|
||||
use solana_core::validator::ValidatorConfig;
|
||||
use solana_core::{
|
||||
gossip_service::{discover_cluster, get_multi_client},
|
||||
validator::ValidatorConfig,
|
||||
};
|
||||
use solana_exchange_program::{
|
||||
exchange_processor::process_instruction, id, solana_exchange_program,
|
||||
};
|
||||
use solana_faucet::faucet::run_local_faucet_with_port;
|
||||
use solana_gossip::gossip_service::{discover_cluster, get_multi_client};
|
||||
use solana_local_cluster::{
|
||||
local_cluster::{ClusterConfig, LocalCluster},
|
||||
validator_configs::make_identical_validator_configs,
|
||||
|
@@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-bench-streamer"
|
||||
version = "1.7.1"
|
||||
version = "1.6.2"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -10,11 +10,11 @@ publish = false
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33.1"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.1" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.7.1" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.1" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.1" }
|
||||
solana-version = { path = "../version", version = "=1.7.1" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.6.2" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.6.2" }
|
||||
solana-logger = { path = "../logger", version = "=1.6.2" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.6.2" }
|
||||
solana-version = { path = "../version", version = "=1.6.2" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -75,7 +75,7 @@ fn main() -> Result<()> {
|
||||
|
||||
let mut read_channels = Vec::new();
|
||||
let mut read_threads = Vec::new();
|
||||
let recycler = PacketsRecycler::default();
|
||||
let recycler = PacketsRecycler::new_without_limit("bench-streamer-recycler-shrink-stats");
|
||||
for _ in 0..num_sockets {
|
||||
let read = solana_net_utils::bind_to(ip_addr, port, false).unwrap();
|
||||
read.set_read_timeout(Some(Duration::new(1, 0))).unwrap();
|
||||
|
@@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-bench-tps"
|
||||
version = "1.7.1"
|
||||
version = "1.6.2"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -15,23 +15,22 @@ log = "0.4.11"
|
||||
rayon = "1.5.0"
|
||||
serde_json = "1.0.56"
|
||||
serde_yaml = "0.8.13"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.1" }
|
||||
solana-core = { path = "../core", version = "=1.7.1" }
|
||||
solana-genesis = { path = "../genesis", version = "=1.7.1" }
|
||||
solana-client = { path = "../client", version = "=1.7.1" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.7.1" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.7.1" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.1" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.7.1" }
|
||||
solana-measure = { path = "../measure", version = "=1.7.1" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.1" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.1" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.1" }
|
||||
solana-version = { path = "../version", version = "=1.7.1" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.6.2" }
|
||||
solana-core = { path = "../core", version = "=1.6.2" }
|
||||
solana-genesis = { path = "../genesis", version = "=1.6.2" }
|
||||
solana-client = { path = "../client", version = "=1.6.2" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.6.2" }
|
||||
solana-logger = { path = "../logger", version = "=1.6.2" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.6.2" }
|
||||
solana-measure = { path = "../measure", version = "=1.6.2" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.6.2" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.6.2" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.2" }
|
||||
solana-version = { path = "../version", version = "=1.6.2" }
|
||||
|
||||
[dev-dependencies]
|
||||
serial_test = "0.4.0"
|
||||
solana-local-cluster = { path = "../local-cluster", version = "=1.7.1" }
|
||||
solana-local-cluster = { path = "../local-cluster", version = "=1.6.2" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -2,8 +2,8 @@
|
||||
use log::*;
|
||||
use solana_bench_tps::bench::{do_bench_tps, generate_and_fund_keypairs, generate_keypairs};
|
||||
use solana_bench_tps::cli;
|
||||
use solana_core::gossip_service::{discover_cluster, get_client, get_multi_client};
|
||||
use solana_genesis::Base64Account;
|
||||
use solana_gossip::gossip_service::{discover_cluster, get_client, get_multi_client};
|
||||
use solana_sdk::fee_calculator::FeeRateGovernor;
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use solana_sdk::system_program;
|
||||
|
@@ -5,9 +5,8 @@ use solana_bench_tps::{
|
||||
cli::Config,
|
||||
};
|
||||
use solana_client::thin_client::create_client;
|
||||
use solana_core::validator::ValidatorConfig;
|
||||
use solana_core::{cluster_info::VALIDATOR_PORT_RANGE, validator::ValidatorConfig};
|
||||
use solana_faucet::faucet::run_local_faucet_with_port;
|
||||
use solana_gossip::cluster_info::VALIDATOR_PORT_RANGE;
|
||||
use solana_local_cluster::{
|
||||
local_cluster::{ClusterConfig, LocalCluster},
|
||||
validator_configs::make_identical_validator_configs,
|
||||
|
9
cargo
9
cargo
@@ -3,22 +3,25 @@
|
||||
# shellcheck source=ci/rust-version.sh
|
||||
here=$(dirname "$0")
|
||||
|
||||
source "${here}"/ci/rust-version.sh all
|
||||
|
||||
toolchain=
|
||||
case "$1" in
|
||||
stable)
|
||||
source "${here}"/ci/rust-version.sh stable
|
||||
# shellcheck disable=SC2054 # rust_stable is sourced from rust-version.sh
|
||||
toolchain="$rust_stable"
|
||||
shift
|
||||
;;
|
||||
nightly)
|
||||
source "${here}"/ci/rust-version.sh nightly
|
||||
# shellcheck disable=SC2054 # rust_nightly is sourced from rust-version.sh
|
||||
toolchain="$rust_nightly"
|
||||
shift
|
||||
;;
|
||||
+*)
|
||||
toolchain="${1#+}"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
source "${here}"/ci/rust-version.sh stable
|
||||
# shellcheck disable=SC2054 # rust_stable is sourced from rust-version.sh
|
||||
toolchain="$rust_stable"
|
||||
;;
|
||||
|
@@ -5,6 +5,9 @@ steps:
|
||||
- command: "ci/publish-tarball.sh"
|
||||
timeout_in_minutes: 60
|
||||
name: "publish tarball"
|
||||
- command: "ci/publish-bpf-sdk.sh"
|
||||
timeout_in_minutes: 5
|
||||
name: "publish bpf sdk"
|
||||
- wait
|
||||
- command: "sdk/docker-solana/build.sh"
|
||||
timeout_in_minutes: 60
|
||||
|
@@ -105,18 +105,11 @@ if [[ -z "$CHANNEL" ]]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $CHANNEL = beta ]]; then
|
||||
CHANNEL_LATEST_TAG="$BETA_CHANNEL_LATEST_TAG"
|
||||
elif [[ $CHANNEL = stable ]]; then
|
||||
CHANNEL_LATEST_TAG="$STABLE_CHANNEL_LATEST_TAG"
|
||||
fi
|
||||
|
||||
echo EDGE_CHANNEL="$EDGE_CHANNEL"
|
||||
echo BETA_CHANNEL="$BETA_CHANNEL"
|
||||
echo BETA_CHANNEL_LATEST_TAG="$BETA_CHANNEL_LATEST_TAG"
|
||||
echo STABLE_CHANNEL="$STABLE_CHANNEL"
|
||||
echo STABLE_CHANNEL_LATEST_TAG="$STABLE_CHANNEL_LATEST_TAG"
|
||||
echo CHANNEL="$CHANNEL"
|
||||
echo CHANNEL_LATEST_TAG="$CHANNEL_LATEST_TAG"
|
||||
|
||||
exit 0
|
||||
|
@@ -7,6 +7,8 @@ src_root="$(readlink -f "${here}/..")"
|
||||
|
||||
cd "${src_root}"
|
||||
|
||||
source ci/rust-version.sh stable
|
||||
|
||||
cargo_audit_ignores=(
|
||||
# failure is officially deprecated/unmaintained
|
||||
#
|
||||
@@ -40,4 +42,4 @@ cargo_audit_ignores=(
|
||||
--ignore RUSTSEC-2020-0146
|
||||
|
||||
)
|
||||
scripts/cargo-for-all-lock-files.sh stable audit "${cargo_audit_ignores[@]}"
|
||||
scripts/cargo-for-all-lock-files.sh +"$rust_stable" audit "${cargo_audit_ignores[@]}"
|
||||
|
@@ -1,4 +1,4 @@
|
||||
FROM solanalabs/rust:1.52.1
|
||||
FROM solanalabs/rust:1.50.0
|
||||
ARG date
|
||||
|
||||
RUN set -x \
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# Note: when the rust version is changed also modify
|
||||
# ci/rust-version.sh to pick up the new image tag
|
||||
FROM rust:1.52.1
|
||||
FROM rust:1.50.0
|
||||
|
||||
# Add Google Protocol Buffers for Libra's metrics library.
|
||||
ENV PROTOC_VERSION 3.8.0
|
||||
|
@@ -70,7 +70,7 @@ done
|
||||
|
||||
source ci/upload-ci-artifact.sh
|
||||
source scripts/configure-metrics.sh
|
||||
source multinode-demo/common.sh --prebuild
|
||||
source multinode-demo/common.sh
|
||||
|
||||
nodes=(
|
||||
"multinode-demo/bootstrap-validator.sh \
|
||||
|
27
ci/publish-bpf-sdk.sh
Executable file
27
ci/publish-bpf-sdk.sh
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
eval "$(ci/channel-info.sh)"
|
||||
|
||||
if [[ -n "$CI_TAG" ]]; then
|
||||
CHANNEL_OR_TAG=$CI_TAG
|
||||
else
|
||||
CHANNEL_OR_TAG=$CHANNEL
|
||||
fi
|
||||
|
||||
(
|
||||
set -x
|
||||
sdk/bpf/scripts/package.sh
|
||||
[[ -f bpf-sdk.tar.bz2 ]]
|
||||
)
|
||||
|
||||
source ci/upload-ci-artifact.sh
|
||||
echo --- AWS S3 Store
|
||||
if [[ -z $CHANNEL_OR_TAG ]]; then
|
||||
echo Skipped
|
||||
else
|
||||
upload-s3-artifact "/solana/bpf-sdk.tar.bz2" "s3://solana-sdk/$CHANNEL_OR_TAG/bpf-sdk.tar.bz2"
|
||||
fi
|
||||
|
||||
exit 0
|
@@ -83,7 +83,7 @@ echo --- Creating release tarball
|
||||
export CHANNEL
|
||||
|
||||
source ci/rust-version.sh stable
|
||||
scripts/cargo-install-all.sh stable "${RELEASE_BASENAME}"
|
||||
scripts/cargo-install-all.sh +"$rust_stable" "${RELEASE_BASENAME}"
|
||||
|
||||
tar cvf "${TARBALL_BASENAME}"-$TARGET.tar "${RELEASE_BASENAME}"
|
||||
bzip2 "${TARBALL_BASENAME}"-$TARGET.tar
|
||||
|
@@ -7,7 +7,7 @@ source multinode-demo/common.sh
|
||||
|
||||
rm -rf config/run/init-completed config/ledger config/snapshot-ledger
|
||||
|
||||
SOLANA_RUN_SH_VALIDATOR_ARGS="--snapshot-interval-slots 200" timeout 120 ./run.sh &
|
||||
timeout 120 ./run.sh &
|
||||
pid=$!
|
||||
|
||||
attempts=20
|
||||
@@ -16,17 +16,14 @@ while [[ ! -f config/run/init-completed ]]; do
|
||||
if ((--attempts == 0)); then
|
||||
echo "Error: validator failed to boot"
|
||||
exit 1
|
||||
else
|
||||
echo "Checking init"
|
||||
fi
|
||||
done
|
||||
|
||||
snapshot_slot=1
|
||||
|
||||
# wait a bit longer than snapshot_slot
|
||||
while [[ $($solana_cli --url http://localhost:8899 slot --commitment processed) -le $((snapshot_slot + 1)) ]]; do
|
||||
while [[ $($solana_cli --url http://localhost:8899 slot --commitment recent) -le $((snapshot_slot + 1)) ]]; do
|
||||
sleep 1
|
||||
echo "Checking slot"
|
||||
done
|
||||
|
||||
$solana_validator --ledger config/ledger exit --force || true
|
||||
|
@@ -18,13 +18,13 @@
|
||||
if [[ -n $RUST_STABLE_VERSION ]]; then
|
||||
stable_version="$RUST_STABLE_VERSION"
|
||||
else
|
||||
stable_version=1.52.1
|
||||
stable_version=1.50.0
|
||||
fi
|
||||
|
||||
if [[ -n $RUST_NIGHTLY_VERSION ]]; then
|
||||
nightly_version="$RUST_NIGHTLY_VERSION"
|
||||
else
|
||||
nightly_version=2021-05-18
|
||||
nightly_version=2021-02-18
|
||||
fi
|
||||
|
||||
|
||||
|
@@ -27,7 +27,7 @@ BENCH_ARTIFACT=current_bench_results.log
|
||||
_ "$cargo" build --manifest-path=keygen/Cargo.toml
|
||||
export PATH="$PWD/target/debug":$PATH
|
||||
|
||||
# Clear the C dependency files, if dependency moves these files are not regenerated
|
||||
# Clear the C dependency files, if dependeny moves these files are not regenerated
|
||||
test -d target/debug/bpf && find target/debug/bpf -name '*.d' -delete
|
||||
test -d target/release/bpf && find target/release/bpf -name '*.d' -delete
|
||||
|
||||
@@ -45,14 +45,6 @@ _ "$cargo" nightly bench --manifest-path sdk/Cargo.toml ${V:+--verbose} \
|
||||
_ "$cargo" nightly bench --manifest-path runtime/Cargo.toml ${V:+--verbose} \
|
||||
-- -Z unstable-options --format=json | tee -a "$BENCH_FILE"
|
||||
|
||||
# Run gossip benches
|
||||
_ "$cargo" nightly bench --manifest-path gossip/Cargo.toml ${V:+--verbose} \
|
||||
-- -Z unstable-options --format=json | tee -a "$BENCH_FILE"
|
||||
|
||||
# Run poh benches
|
||||
_ "$cargo" nightly bench --manifest-path poh/Cargo.toml ${V:+--verbose} \
|
||||
-- -Z unstable-options --format=json | tee -a "$BENCH_FILE"
|
||||
|
||||
# Run core benches
|
||||
_ "$cargo" nightly bench --manifest-path core/Cargo.toml ${V:+--verbose} \
|
||||
-- -Z unstable-options --format=json | tee -a "$BENCH_FILE"
|
||||
|
@@ -45,7 +45,7 @@ export RUSTFLAGS="-D warnings -A incomplete_features"
|
||||
# Only force up-to-date lock files on edge
|
||||
if [[ $CI_BASE_BRANCH = "$EDGE_CHANNEL" ]]; then
|
||||
# Exclude --benches as it's not available in rust stable yet
|
||||
if _ scripts/cargo-for-all-lock-files.sh stable check --locked --tests --bins --examples; then
|
||||
if _ scripts/cargo-for-all-lock-files.sh +"$rust_stable" check --locked --tests --bins --examples; then
|
||||
true
|
||||
else
|
||||
check_status=$?
|
||||
@@ -56,7 +56,7 @@ if [[ $CI_BASE_BRANCH = "$EDGE_CHANNEL" ]]; then
|
||||
fi
|
||||
|
||||
# Ensure nightly and --benches
|
||||
_ scripts/cargo-for-all-lock-files.sh nightly check --locked --all-targets
|
||||
_ scripts/cargo-for-all-lock-files.sh +"$rust_nightly" check --locked --all-targets
|
||||
else
|
||||
echo "Note: cargo-for-all-lock-files.sh skipped because $CI_BASE_BRANCH != $EDGE_CHANNEL"
|
||||
fi
|
||||
@@ -79,6 +79,7 @@ _ ci/do-audit.sh
|
||||
cd "$project"
|
||||
_ "$cargo" nightly clippy -- --deny=warnings --allow=clippy::missing_safety_doc
|
||||
_ "$cargo" stable fmt -- --check
|
||||
_ "$cargo" nightly test
|
||||
)
|
||||
done
|
||||
}
|
||||
|
@@ -25,29 +25,4 @@ echo
|
||||
_ ci/nits.sh
|
||||
_ ci/check-ssh-keys.sh
|
||||
|
||||
|
||||
# Ensure the current channel version is not equal ("greater") than
|
||||
# the version of the latest tag
|
||||
if [[ -z $CI_TAG ]]; then
|
||||
echo "--- channel version check"
|
||||
(
|
||||
eval "$(ci/channel-info.sh)"
|
||||
|
||||
if [[ -n $CHANNEL_LATEST_TAG ]]; then
|
||||
source scripts/read-cargo-variable.sh
|
||||
|
||||
version=$(readCargoVariable version "version/Cargo.toml")
|
||||
echo "version: v$version"
|
||||
echo "latest channel tag: $CHANNEL_LATEST_TAG"
|
||||
|
||||
if [[ $CHANNEL_LATEST_TAG = v$version ]]; then
|
||||
echo "Error: please run ./scripts/increment-cargo-version.sh"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "Skipped. CHANNEL_LATEST_TAG (CHANNEL=$CHANNEL) unset"
|
||||
fi
|
||||
)
|
||||
fi
|
||||
|
||||
echo --- ok
|
||||
|
@@ -43,11 +43,7 @@ test-stable-perf)
|
||||
# BPF solana-sdk legacy compile test
|
||||
./cargo-build-bpf --manifest-path sdk/Cargo.toml
|
||||
|
||||
# BPF Program unit tests
|
||||
"$cargo" test --manifest-path programs/bpf/Cargo.toml
|
||||
cargo-build-bpf --manifest-path programs/bpf/Cargo.toml --bpf-sdk sdk/bpf
|
||||
|
||||
# BPF program system tests
|
||||
# BPF program tests
|
||||
_ make -C programs/bpf/c tests
|
||||
_ "$cargo" stable test \
|
||||
--manifest-path programs/bpf/Cargo.toml \
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-clap-utils"
|
||||
version = "1.7.1"
|
||||
version = "1.6.2"
|
||||
description = "Solana utilities for the clap"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -12,17 +12,13 @@ edition = "2018"
|
||||
[dependencies]
|
||||
clap = "2.33.0"
|
||||
rpassword = "4.0"
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "=1.7.1" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.1" }
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "=1.6.2" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.2" }
|
||||
thiserror = "1.0.21"
|
||||
tiny-bip39 = "0.8.0"
|
||||
uriparse = "0.6.3"
|
||||
url = "2.1.0"
|
||||
chrono = "0.4"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.1.0"
|
||||
|
||||
[lib]
|
||||
name = "solana_clap_utils"
|
||||
|
||||
|
@@ -1,7 +1,5 @@
|
||||
use {
|
||||
crate::{input_validators, ArgConstant},
|
||||
clap::Arg,
|
||||
};
|
||||
use crate::{input_validators, ArgConstant};
|
||||
use clap::Arg;
|
||||
|
||||
pub const FEE_PAYER_ARG: ArgConstant<'static> = ArgConstant {
|
||||
name: "fee_payer",
|
||||
|
@@ -1,24 +1,19 @@
|
||||
use {
|
||||
crate::keypair::{
|
||||
keypair_from_seed_phrase, pubkey_from_path, resolve_signer_from_path, signer_from_path,
|
||||
ASK_KEYWORD, SKIP_SEED_PHRASE_VALIDATION_ARG,
|
||||
},
|
||||
chrono::DateTime,
|
||||
clap::ArgMatches,
|
||||
solana_remote_wallet::remote_wallet::RemoteWalletManager,
|
||||
solana_sdk::{
|
||||
clock::UnixTimestamp,
|
||||
commitment_config::CommitmentConfig,
|
||||
genesis_config::ClusterType,
|
||||
native_token::sol_to_lamports,
|
||||
pubkey::Pubkey,
|
||||
signature::{read_keypair_file, Keypair, Signature, Signer},
|
||||
},
|
||||
std::{str::FromStr, sync::Arc},
|
||||
use crate::keypair::{
|
||||
keypair_from_seed_phrase, pubkey_from_path, resolve_signer_from_path, signer_from_path,
|
||||
ASK_KEYWORD, SKIP_SEED_PHRASE_VALIDATION_ARG,
|
||||
};
|
||||
|
||||
// Sentinel value used to indicate to write to screen instead of file
|
||||
pub const STDOUT_OUTFILE_TOKEN: &str = "-";
|
||||
use chrono::DateTime;
|
||||
use clap::ArgMatches;
|
||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||
use solana_sdk::{
|
||||
clock::UnixTimestamp,
|
||||
commitment_config::CommitmentConfig,
|
||||
genesis_config::ClusterType,
|
||||
native_token::sol_to_lamports,
|
||||
pubkey::Pubkey,
|
||||
signature::{read_keypair_file, Keypair, Signature, Signer},
|
||||
};
|
||||
use std::{str::FromStr, sync::Arc};
|
||||
|
||||
// Return parsed values from matches at `name`
|
||||
pub fn values_of<T>(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<T>>
|
||||
@@ -60,7 +55,7 @@ pub fn keypair_of(matches: &ArgMatches<'_>, name: &str) -> Option<Keypair> {
|
||||
if let Some(value) = matches.value_of(name) {
|
||||
if value == ASK_KEYWORD {
|
||||
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
||||
keypair_from_seed_phrase(name, skip_validation, true, None, true).ok()
|
||||
keypair_from_seed_phrase(name, skip_validation, true).ok()
|
||||
} else {
|
||||
read_keypair_file(value).ok()
|
||||
}
|
||||
@@ -75,7 +70,7 @@ pub fn keypairs_of(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<Keypair>>
|
||||
.filter_map(|value| {
|
||||
if value == ASK_KEYWORD {
|
||||
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
||||
keypair_from_seed_phrase(name, skip_validation, true, None, true).ok()
|
||||
keypair_from_seed_phrase(name, skip_validation, true).ok()
|
||||
} else {
|
||||
read_keypair_file(value).ok()
|
||||
}
|
||||
|
@@ -1,15 +1,13 @@
|
||||
use {
|
||||
crate::keypair::{parse_signer_source, SignerSourceKind, ASK_KEYWORD},
|
||||
chrono::DateTime,
|
||||
solana_sdk::{
|
||||
clock::{Epoch, Slot},
|
||||
hash::Hash,
|
||||
pubkey::{Pubkey, MAX_SEED_LEN},
|
||||
signature::{read_keypair_file, Signature},
|
||||
},
|
||||
std::fmt::Display,
|
||||
std::str::FromStr,
|
||||
use crate::keypair::{parse_keypair_path, KeypairUrl, ASK_KEYWORD};
|
||||
use chrono::DateTime;
|
||||
use solana_sdk::{
|
||||
clock::{Epoch, Slot},
|
||||
hash::Hash,
|
||||
pubkey::{Pubkey, MAX_SEED_LEN},
|
||||
signature::{read_keypair_file, Signature},
|
||||
};
|
||||
use std::fmt::Display;
|
||||
use std::str::FromStr;
|
||||
|
||||
fn is_parsable_generic<U, T>(string: T) -> Result<(), String>
|
||||
where
|
||||
@@ -34,29 +32,6 @@ where
|
||||
is_parsable_generic::<T, String>(string)
|
||||
}
|
||||
|
||||
// Return an error if string cannot be parsed as numeric type T, and value not within specified
|
||||
// range
|
||||
pub fn is_within_range<T>(string: String, range_min: T, range_max: T) -> Result<(), String>
|
||||
where
|
||||
T: FromStr + Copy + std::fmt::Debug + PartialOrd + std::ops::Add<Output = T> + From<usize>,
|
||||
T::Err: Display,
|
||||
{
|
||||
match string.parse::<T>() {
|
||||
Ok(input) => {
|
||||
let range = range_min..range_max + 1.into();
|
||||
if !range.contains(&input) {
|
||||
Err(format!(
|
||||
"input '{:?}' out of range ({:?}..{:?}]",
|
||||
input, range_min, range_max
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Err(err) => Err(format!("error parsing '{}': {}", string, err)),
|
||||
}
|
||||
}
|
||||
|
||||
// Return an error if a pubkey cannot be parsed.
|
||||
pub fn is_pubkey<T>(string: T) -> Result<(), String>
|
||||
where
|
||||
@@ -96,26 +71,6 @@ where
|
||||
.map_err(|err| format!("{}", err))
|
||||
}
|
||||
|
||||
// Return an error if a `SignerSourceKind::Prompt` cannot be parsed
|
||||
pub fn is_prompt_signer_source<T>(string: T) -> Result<(), String>
|
||||
where
|
||||
T: AsRef<str> + Display,
|
||||
{
|
||||
if string.as_ref() == ASK_KEYWORD {
|
||||
return Ok(());
|
||||
}
|
||||
match parse_signer_source(string.as_ref())
|
||||
.map_err(|err| format!("{}", err))?
|
||||
.kind
|
||||
{
|
||||
SignerSourceKind::Prompt => Ok(()),
|
||||
_ => Err(format!(
|
||||
"Unable to parse input as `prompt:` URI scheme or `ASK` keyword: {}",
|
||||
string
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
// Return an error if string cannot be parsed as pubkey string or keypair file location
|
||||
pub fn is_pubkey_or_keypair<T>(string: T) -> Result<(), String>
|
||||
where
|
||||
@@ -130,11 +85,8 @@ pub fn is_valid_pubkey<T>(string: T) -> Result<(), String>
|
||||
where
|
||||
T: AsRef<str> + Display,
|
||||
{
|
||||
match parse_signer_source(string.as_ref())
|
||||
.map_err(|err| format!("{}", err))?
|
||||
.kind
|
||||
{
|
||||
SignerSourceKind::Filepath(path) => is_keypair(path),
|
||||
match parse_keypair_path(string.as_ref()) {
|
||||
KeypairUrl::Filepath(path) => is_keypair(path),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
@@ -215,8 +167,8 @@ where
|
||||
pub fn normalize_to_url_if_moniker<T: AsRef<str>>(url_or_moniker: T) -> String {
|
||||
match url_or_moniker.as_ref() {
|
||||
"m" | "mainnet-beta" => "https://api.mainnet-beta.solana.com",
|
||||
"t" | "testnet" => "https://api.testnet.solana.com",
|
||||
"d" | "devnet" => "https://api.devnet.solana.com",
|
||||
"t" | "testnet" => "https://testnet.solana.com",
|
||||
"d" | "devnet" => "https://devnet.solana.com",
|
||||
"l" | "localhost" => "http://localhost:8899",
|
||||
url => url,
|
||||
}
|
||||
|
@@ -1,39 +1,30 @@
|
||||
use {
|
||||
crate::{
|
||||
input_parsers::{pubkeys_sigs_of, STDOUT_OUTFILE_TOKEN},
|
||||
offline::{SIGNER_ARG, SIGN_ONLY_ARG},
|
||||
ArgConstant,
|
||||
use crate::{
|
||||
input_parsers::pubkeys_sigs_of,
|
||||
offline::{SIGNER_ARG, SIGN_ONLY_ARG},
|
||||
ArgConstant,
|
||||
};
|
||||
use bip39::{Language, Mnemonic, Seed};
|
||||
use clap::ArgMatches;
|
||||
use rpassword::prompt_password_stderr;
|
||||
use solana_remote_wallet::{
|
||||
remote_keypair::generate_remote_keypair,
|
||||
remote_wallet::{maybe_wallet_manager, RemoteWalletError, RemoteWalletManager},
|
||||
};
|
||||
use solana_sdk::{
|
||||
hash::Hash,
|
||||
message::Message,
|
||||
pubkey::Pubkey,
|
||||
signature::{
|
||||
keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair,
|
||||
read_keypair_file, Keypair, NullSigner, Presigner, Signature, Signer,
|
||||
},
|
||||
bip39::{Language, Mnemonic, Seed},
|
||||
clap::ArgMatches,
|
||||
rpassword::prompt_password_stderr,
|
||||
solana_remote_wallet::{
|
||||
locator::{Locator as RemoteWalletLocator, LocatorError as RemoteWalletLocatorError},
|
||||
remote_keypair::generate_remote_keypair,
|
||||
remote_wallet::{maybe_wallet_manager, RemoteWalletError, RemoteWalletManager},
|
||||
},
|
||||
solana_sdk::{
|
||||
derivation_path::{DerivationPath, DerivationPathError},
|
||||
hash::Hash,
|
||||
message::Message,
|
||||
pubkey::Pubkey,
|
||||
signature::{
|
||||
generate_seed_from_seed_phrase_and_passphrase, keypair_from_seed,
|
||||
keypair_from_seed_and_derivation_path, keypair_from_seed_phrase_and_passphrase,
|
||||
read_keypair, read_keypair_file, Keypair, NullSigner, Presigner, Signature, Signer,
|
||||
},
|
||||
},
|
||||
std::{
|
||||
cell::RefCell,
|
||||
convert::TryFrom,
|
||||
error,
|
||||
io::{stdin, stdout, Write},
|
||||
ops::Deref,
|
||||
process::exit,
|
||||
str::FromStr,
|
||||
sync::Arc,
|
||||
},
|
||||
thiserror::Error,
|
||||
};
|
||||
use std::{
|
||||
error,
|
||||
io::{stdin, stdout, Write},
|
||||
process::exit,
|
||||
str::FromStr,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
pub struct SignOnly {
|
||||
@@ -92,48 +83,12 @@ impl CliSignerInfo {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct DefaultSigner {
|
||||
pub arg_name: String,
|
||||
pub path: String,
|
||||
is_path_checked: RefCell<bool>,
|
||||
}
|
||||
|
||||
impl DefaultSigner {
|
||||
pub fn new<AN: AsRef<str>, P: AsRef<str>>(arg_name: AN, path: P) -> Self {
|
||||
let arg_name = arg_name.as_ref().to_string();
|
||||
let path = path.as_ref().to_string();
|
||||
Self {
|
||||
arg_name,
|
||||
path,
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn path(&self) -> Result<&str, Box<dyn std::error::Error>> {
|
||||
if !self.is_path_checked.borrow().deref() {
|
||||
parse_signer_source(&self.path)
|
||||
.and_then(|s| {
|
||||
if let SignerSourceKind::Filepath(path) = &s.kind {
|
||||
std::fs::metadata(path).map(|_| ()).map_err(|e| e.into())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
.map_err(|_| {
|
||||
std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!(
|
||||
"No default signer found, run \"solana-keygen new -o {}\" to create a new one",
|
||||
self.path
|
||||
),
|
||||
)
|
||||
})?;
|
||||
*self.is_path_checked.borrow_mut() = true;
|
||||
}
|
||||
Ok(&self.path)
|
||||
}
|
||||
|
||||
pub fn generate_unique_signers(
|
||||
&self,
|
||||
bulk_signers: Vec<Option<Box<dyn Signer>>>,
|
||||
@@ -148,9 +103,11 @@ impl DefaultSigner {
|
||||
unique_signers.push(default_signer);
|
||||
}
|
||||
|
||||
for signer in bulk_signers.into_iter().flatten() {
|
||||
if !unique_signers.iter().any(|s| s == &signer) {
|
||||
unique_signers.push(signer);
|
||||
for signer in bulk_signers.into_iter() {
|
||||
if let Some(signer) = signer {
|
||||
if !unique_signers.iter().any(|s| s == &signer) {
|
||||
unique_signers.push(signer);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(CliSignerInfo {
|
||||
@@ -163,7 +120,7 @@ impl DefaultSigner {
|
||||
matches: &ArgMatches,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<Box<dyn Signer>, Box<dyn std::error::Error>> {
|
||||
signer_from_path(matches, self.path()?, &self.arg_name, wallet_manager)
|
||||
signer_from_path(matches, &self.path, &self.arg_name, wallet_manager)
|
||||
}
|
||||
|
||||
pub fn signer_from_path_with_config(
|
||||
@@ -172,142 +129,29 @@ impl DefaultSigner {
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
config: &SignerFromPathConfig,
|
||||
) -> Result<Box<dyn Signer>, Box<dyn std::error::Error>> {
|
||||
signer_from_path_with_config(
|
||||
matches,
|
||||
self.path()?,
|
||||
&self.arg_name,
|
||||
wallet_manager,
|
||||
config,
|
||||
)
|
||||
signer_from_path_with_config(matches, &self.path, &self.arg_name, wallet_manager, config)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct SignerSource {
|
||||
pub kind: SignerSourceKind,
|
||||
pub derivation_path: Option<DerivationPath>,
|
||||
pub legacy: bool,
|
||||
}
|
||||
|
||||
impl SignerSource {
|
||||
fn new(kind: SignerSourceKind) -> Self {
|
||||
Self {
|
||||
kind,
|
||||
derivation_path: None,
|
||||
legacy: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn new_legacy(kind: SignerSourceKind) -> Self {
|
||||
Self {
|
||||
kind,
|
||||
derivation_path: None,
|
||||
legacy: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const SIGNER_SOURCE_PROMPT: &str = "prompt";
|
||||
const SIGNER_SOURCE_FILEPATH: &str = "file";
|
||||
const SIGNER_SOURCE_USB: &str = "usb";
|
||||
const SIGNER_SOURCE_STDIN: &str = "stdin";
|
||||
const SIGNER_SOURCE_PUBKEY: &str = "pubkey";
|
||||
|
||||
pub(crate) enum SignerSourceKind {
|
||||
Prompt,
|
||||
pub enum KeypairUrl {
|
||||
Ask,
|
||||
Filepath(String),
|
||||
Usb(RemoteWalletLocator),
|
||||
Usb(String),
|
||||
Stdin,
|
||||
Pubkey(Pubkey),
|
||||
}
|
||||
|
||||
impl AsRef<str> for SignerSourceKind {
|
||||
fn as_ref(&self) -> &str {
|
||||
match self {
|
||||
Self::Prompt => SIGNER_SOURCE_PROMPT,
|
||||
Self::Filepath(_) => SIGNER_SOURCE_FILEPATH,
|
||||
Self::Usb(_) => SIGNER_SOURCE_USB,
|
||||
Self::Stdin => SIGNER_SOURCE_STDIN,
|
||||
Self::Pubkey(_) => SIGNER_SOURCE_PUBKEY,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for SignerSourceKind {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let s: &str = self.as_ref();
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub(crate) enum SignerSourceError {
|
||||
#[error("unrecognized signer source")]
|
||||
UnrecognizedSource,
|
||||
#[error(transparent)]
|
||||
RemoteWalletLocatorError(#[from] RemoteWalletLocatorError),
|
||||
#[error(transparent)]
|
||||
DerivationPathError(#[from] DerivationPathError),
|
||||
#[error(transparent)]
|
||||
IoError(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
pub(crate) fn parse_signer_source<S: AsRef<str>>(
|
||||
source: S,
|
||||
) -> Result<SignerSource, SignerSourceError> {
|
||||
let source = source.as_ref();
|
||||
let source = {
|
||||
#[cfg(target_family = "windows")]
|
||||
{
|
||||
source.replace("\\", "/")
|
||||
}
|
||||
#[cfg(not(target_family = "windows"))]
|
||||
{
|
||||
source.to_string()
|
||||
}
|
||||
};
|
||||
match uriparse::URIReference::try_from(source.as_str()) {
|
||||
Err(_) => Err(SignerSourceError::UnrecognizedSource),
|
||||
Ok(uri) => {
|
||||
if let Some(scheme) = uri.scheme() {
|
||||
let scheme = scheme.as_str().to_ascii_lowercase();
|
||||
match scheme.as_str() {
|
||||
SIGNER_SOURCE_PROMPT => Ok(SignerSource {
|
||||
kind: SignerSourceKind::Prompt,
|
||||
derivation_path: DerivationPath::from_uri_any_query(&uri)?,
|
||||
legacy: false,
|
||||
}),
|
||||
SIGNER_SOURCE_FILEPATH => Ok(SignerSource::new(SignerSourceKind::Filepath(
|
||||
uri.path().to_string(),
|
||||
))),
|
||||
SIGNER_SOURCE_USB => Ok(SignerSource {
|
||||
kind: SignerSourceKind::Usb(RemoteWalletLocator::new_from_uri(&uri)?),
|
||||
derivation_path: DerivationPath::from_uri_key_query(&uri)?,
|
||||
legacy: false,
|
||||
}),
|
||||
SIGNER_SOURCE_STDIN => Ok(SignerSource::new(SignerSourceKind::Stdin)),
|
||||
_ => {
|
||||
#[cfg(target_family = "windows")]
|
||||
// On Windows, an absolute path's drive letter will be parsed as the URI
|
||||
// scheme. Assume a filepath source in case of a single character shceme.
|
||||
if scheme.len() == 1 {
|
||||
return Ok(SignerSource::new(SignerSourceKind::Filepath(source)));
|
||||
}
|
||||
Err(SignerSourceError::UnrecognizedSource)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match source.as_str() {
|
||||
STDOUT_OUTFILE_TOKEN => Ok(SignerSource::new(SignerSourceKind::Stdin)),
|
||||
ASK_KEYWORD => Ok(SignerSource::new_legacy(SignerSourceKind::Prompt)),
|
||||
_ => match Pubkey::from_str(source.as_str()) {
|
||||
Ok(pubkey) => Ok(SignerSource::new(SignerSourceKind::Pubkey(pubkey))),
|
||||
Err(_) => std::fs::metadata(source.as_str())
|
||||
.map(|_| SignerSource::new(SignerSourceKind::Filepath(source)))
|
||||
.map_err(|err| err.into()),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn parse_keypair_path(path: &str) -> KeypairUrl {
|
||||
if path == "-" {
|
||||
KeypairUrl::Stdin
|
||||
} else if path == ASK_KEYWORD {
|
||||
KeypairUrl::Ask
|
||||
} else if path.starts_with("usb://") {
|
||||
KeypairUrl::Usb(path.to_string())
|
||||
} else if let Ok(pubkey) = Pubkey::from_str(path) {
|
||||
KeypairUrl::Pubkey(pubkey)
|
||||
} else {
|
||||
KeypairUrl::Filepath(path.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,23 +198,16 @@ pub fn signer_from_path_with_config(
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
config: &SignerFromPathConfig,
|
||||
) -> Result<Box<dyn Signer>, Box<dyn error::Error>> {
|
||||
let SignerSource {
|
||||
kind,
|
||||
derivation_path,
|
||||
legacy,
|
||||
} = parse_signer_source(path)?;
|
||||
match kind {
|
||||
SignerSourceKind::Prompt => {
|
||||
match parse_keypair_path(path) {
|
||||
KeypairUrl::Ask => {
|
||||
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
||||
Ok(Box::new(keypair_from_seed_phrase(
|
||||
keypair_name,
|
||||
skip_validation,
|
||||
false,
|
||||
derivation_path,
|
||||
legacy,
|
||||
)?))
|
||||
}
|
||||
SignerSourceKind::Filepath(path) => match read_keypair_file(&path) {
|
||||
KeypairUrl::Filepath(path) => match read_keypair_file(&path) {
|
||||
Err(e) => Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("could not read keypair file \"{}\". Run \"solana-keygen new\" to create a keypair file: {}", path, e),
|
||||
@@ -378,18 +215,17 @@ pub fn signer_from_path_with_config(
|
||||
.into()),
|
||||
Ok(file) => Ok(Box::new(file)),
|
||||
},
|
||||
SignerSourceKind::Stdin => {
|
||||
KeypairUrl::Stdin => {
|
||||
let mut stdin = std::io::stdin();
|
||||
Ok(Box::new(read_keypair(&mut stdin)?))
|
||||
}
|
||||
SignerSourceKind::Usb(locator) => {
|
||||
KeypairUrl::Usb(path) => {
|
||||
if wallet_manager.is_none() {
|
||||
*wallet_manager = maybe_wallet_manager()?;
|
||||
}
|
||||
if let Some(wallet_manager) = wallet_manager {
|
||||
Ok(Box::new(generate_remote_keypair(
|
||||
locator,
|
||||
derivation_path.unwrap_or_default(),
|
||||
path,
|
||||
wallet_manager,
|
||||
matches.is_present("confirm_key"),
|
||||
keypair_name,
|
||||
@@ -398,7 +234,7 @@ pub fn signer_from_path_with_config(
|
||||
Err(RemoteWalletError::NoDeviceFound.into())
|
||||
}
|
||||
}
|
||||
SignerSourceKind::Pubkey(pubkey) => {
|
||||
KeypairUrl::Pubkey(pubkey) => {
|
||||
let presigner = pubkeys_sigs_of(matches, SIGNER_ARG.name)
|
||||
.as_ref()
|
||||
.and_then(|presigners| presigner_from_pubkey_sigs(&pubkey, presigners));
|
||||
@@ -423,9 +259,8 @@ pub fn pubkey_from_path(
|
||||
keypair_name: &str,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<Pubkey, Box<dyn error::Error>> {
|
||||
let SignerSource { kind, .. } = parse_signer_source(path)?;
|
||||
match kind {
|
||||
SignerSourceKind::Pubkey(pubkey) => Ok(pubkey),
|
||||
match parse_keypair_path(path) {
|
||||
KeypairUrl::Pubkey(pubkey) => Ok(pubkey),
|
||||
_ => Ok(signer_from_path(matches, path, keypair_name, wallet_manager)?.pubkey()),
|
||||
}
|
||||
}
|
||||
@@ -436,51 +271,34 @@ pub fn resolve_signer_from_path(
|
||||
keypair_name: &str,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<Option<String>, Box<dyn error::Error>> {
|
||||
let SignerSource {
|
||||
kind,
|
||||
derivation_path,
|
||||
legacy,
|
||||
} = parse_signer_source(path)?;
|
||||
match kind {
|
||||
SignerSourceKind::Prompt => {
|
||||
match parse_keypair_path(path) {
|
||||
KeypairUrl::Ask => {
|
||||
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
||||
// This method validates the seed phrase, but returns `None` because there is no path
|
||||
// on disk or to a device
|
||||
keypair_from_seed_phrase(
|
||||
keypair_name,
|
||||
skip_validation,
|
||||
false,
|
||||
derivation_path,
|
||||
legacy,
|
||||
)
|
||||
.map(|_| None)
|
||||
keypair_from_seed_phrase(keypair_name, skip_validation, false).map(|_| None)
|
||||
}
|
||||
SignerSourceKind::Filepath(path) => match read_keypair_file(&path) {
|
||||
KeypairUrl::Filepath(path) => match read_keypair_file(&path) {
|
||||
Err(e) => Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!(
|
||||
"could not read keypair file \"{}\". \
|
||||
Run \"solana-keygen new\" to create a keypair file: {}",
|
||||
path, e
|
||||
),
|
||||
format!("could not read keypair file \"{}\". Run \"solana-keygen new\" to create a keypair file: {}", path, e),
|
||||
)
|
||||
.into()),
|
||||
Ok(_) => Ok(Some(path.to_string())),
|
||||
},
|
||||
SignerSourceKind::Stdin => {
|
||||
KeypairUrl::Stdin => {
|
||||
let mut stdin = std::io::stdin();
|
||||
// This method validates the keypair from stdin, but returns `None` because there is no
|
||||
// path on disk or to a device
|
||||
read_keypair(&mut stdin).map(|_| None)
|
||||
}
|
||||
SignerSourceKind::Usb(locator) => {
|
||||
KeypairUrl::Usb(path) => {
|
||||
if wallet_manager.is_none() {
|
||||
*wallet_manager = maybe_wallet_manager()?;
|
||||
}
|
||||
if let Some(wallet_manager) = wallet_manager {
|
||||
let path = generate_remote_keypair(
|
||||
locator,
|
||||
derivation_path.unwrap_or_default(),
|
||||
path,
|
||||
wallet_manager,
|
||||
matches.is_present("confirm_key"),
|
||||
keypair_name,
|
||||
@@ -495,7 +313,7 @@ pub fn resolve_signer_from_path(
|
||||
}
|
||||
}
|
||||
|
||||
// Keyword used to indicate that the user should be prompted for a keypair seed phrase
|
||||
// Keyword used to indicate that the user should be asked for a keypair seed phrase
|
||||
pub const ASK_KEYWORD: &str = "ASK";
|
||||
|
||||
pub const SKIP_SEED_PHRASE_VALIDATION_ARG: ArgConstant<'static> = ArgConstant {
|
||||
@@ -516,56 +334,6 @@ pub fn prompt_passphrase(prompt: &str) -> Result<String, Box<dyn error::Error>>
|
||||
Ok(passphrase)
|
||||
}
|
||||
|
||||
/// Parses a path into a SignerSource and returns a Keypair for supporting SignerSourceKinds
|
||||
pub fn keypair_from_path(
|
||||
matches: &ArgMatches,
|
||||
path: &str,
|
||||
keypair_name: &str,
|
||||
confirm_pubkey: bool,
|
||||
) -> Result<Keypair, Box<dyn error::Error>> {
|
||||
let SignerSource {
|
||||
kind,
|
||||
derivation_path,
|
||||
legacy,
|
||||
} = parse_signer_source(path)?;
|
||||
match kind {
|
||||
SignerSourceKind::Prompt => {
|
||||
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
||||
Ok(keypair_from_seed_phrase(
|
||||
keypair_name,
|
||||
skip_validation,
|
||||
confirm_pubkey,
|
||||
derivation_path,
|
||||
legacy,
|
||||
)?)
|
||||
}
|
||||
SignerSourceKind::Filepath(path) => match read_keypair_file(&path) {
|
||||
Err(e) => Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!(
|
||||
"could not read keypair file \"{}\". \
|
||||
Run \"solana-keygen new\" to create a keypair file: {}",
|
||||
path, e
|
||||
),
|
||||
)
|
||||
.into()),
|
||||
Ok(file) => Ok(file),
|
||||
},
|
||||
SignerSourceKind::Stdin => {
|
||||
let mut stdin = std::io::stdin();
|
||||
Ok(read_keypair(&mut stdin)?)
|
||||
}
|
||||
_ => Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!(
|
||||
"signer of type `{:?}` does not support Keypair output",
|
||||
kind
|
||||
),
|
||||
)
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads user input from stdin to retrieve a seed phrase and passphrase for keypair derivation
|
||||
/// Optionally skips validation of seed phrase
|
||||
/// Optionally confirms recovered public key
|
||||
@@ -573,8 +341,6 @@ pub fn keypair_from_seed_phrase(
|
||||
keypair_name: &str,
|
||||
skip_validation: bool,
|
||||
confirm_pubkey: bool,
|
||||
derivation_path: Option<DerivationPath>,
|
||||
legacy: bool,
|
||||
) -> Result<Keypair, Box<dyn error::Error>> {
|
||||
let seed_phrase = prompt_password_stderr(&format!("[{}] seed phrase: ", keypair_name))?;
|
||||
let seed_phrase = seed_phrase.trim();
|
||||
@@ -585,12 +351,7 @@ pub fn keypair_from_seed_phrase(
|
||||
|
||||
let keypair = if skip_validation {
|
||||
let passphrase = prompt_passphrase(&passphrase_prompt)?;
|
||||
if legacy {
|
||||
keypair_from_seed_phrase_and_passphrase(&seed_phrase, &passphrase)?
|
||||
} else {
|
||||
let seed = generate_seed_from_seed_phrase_and_passphrase(&seed_phrase, &passphrase);
|
||||
keypair_from_seed_and_derivation_path(&seed, derivation_path)?
|
||||
}
|
||||
keypair_from_seed_phrase_and_passphrase(&seed_phrase, &passphrase)?
|
||||
} else {
|
||||
let sanitized = sanitize_seed_phrase(seed_phrase);
|
||||
let parse_language_fn = || {
|
||||
@@ -613,11 +374,7 @@ pub fn keypair_from_seed_phrase(
|
||||
let mnemonic = parse_language_fn()?;
|
||||
let passphrase = prompt_passphrase(&passphrase_prompt)?;
|
||||
let seed = Seed::new(&mnemonic, &passphrase);
|
||||
if legacy {
|
||||
keypair_from_seed(seed.as_bytes())?
|
||||
} else {
|
||||
keypair_from_seed_and_derivation_path(&seed.as_bytes(), derivation_path)?
|
||||
}
|
||||
keypair_from_seed(seed.as_bytes())?
|
||||
};
|
||||
|
||||
if confirm_pubkey {
|
||||
@@ -645,9 +402,7 @@ fn sanitize_seed_phrase(seed_phrase: &str) -> String {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use solana_remote_wallet::locator::Manufacturer;
|
||||
use solana_sdk::system_instruction;
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
#[test]
|
||||
fn test_sanitize_seed_phrase() {
|
||||
@@ -688,122 +443,4 @@ mod tests {
|
||||
];
|
||||
assert_eq!(signer_pubkeys, expect);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_signer_source() {
|
||||
assert!(matches!(
|
||||
parse_signer_source(STDOUT_OUTFILE_TOKEN).unwrap(),
|
||||
SignerSource {
|
||||
kind: SignerSourceKind::Stdin,
|
||||
derivation_path: None,
|
||||
legacy: false,
|
||||
}
|
||||
));
|
||||
let stdin = "stdin:".to_string();
|
||||
assert!(matches!(
|
||||
parse_signer_source(&stdin).unwrap(),
|
||||
SignerSource {
|
||||
kind: SignerSourceKind::Stdin,
|
||||
derivation_path: None,
|
||||
legacy: false,
|
||||
}
|
||||
));
|
||||
assert!(matches!(
|
||||
parse_signer_source(ASK_KEYWORD).unwrap(),
|
||||
SignerSource {
|
||||
kind: SignerSourceKind::Prompt,
|
||||
derivation_path: None,
|
||||
legacy: true,
|
||||
}
|
||||
));
|
||||
let pubkey = Pubkey::new_unique();
|
||||
assert!(
|
||||
matches!(parse_signer_source(&pubkey.to_string()).unwrap(), SignerSource {
|
||||
kind: SignerSourceKind::Pubkey(p),
|
||||
derivation_path: None,
|
||||
legacy: false,
|
||||
}
|
||||
if p == pubkey)
|
||||
);
|
||||
|
||||
// Set up absolute and relative path strs
|
||||
let file0 = NamedTempFile::new().unwrap();
|
||||
let path = file0.path();
|
||||
assert!(path.is_absolute());
|
||||
let absolute_path_str = path.to_str().unwrap();
|
||||
|
||||
let file1 = NamedTempFile::new_in(std::env::current_dir().unwrap()).unwrap();
|
||||
let path = file1.path().file_name().unwrap().to_str().unwrap();
|
||||
let path = std::path::Path::new(path);
|
||||
assert!(path.is_relative());
|
||||
let relative_path_str = path.to_str().unwrap();
|
||||
|
||||
assert!(
|
||||
matches!(parse_signer_source(absolute_path_str).unwrap(), SignerSource {
|
||||
kind: SignerSourceKind::Filepath(p),
|
||||
derivation_path: None,
|
||||
legacy: false,
|
||||
} if p == absolute_path_str)
|
||||
);
|
||||
assert!(
|
||||
matches!(parse_signer_source(&relative_path_str).unwrap(), SignerSource {
|
||||
kind: SignerSourceKind::Filepath(p),
|
||||
derivation_path: None,
|
||||
legacy: false,
|
||||
} if p == relative_path_str)
|
||||
);
|
||||
|
||||
let usb = "usb://ledger".to_string();
|
||||
let expected_locator = RemoteWalletLocator {
|
||||
manufacturer: Manufacturer::Ledger,
|
||||
pubkey: None,
|
||||
};
|
||||
assert!(matches!(parse_signer_source(&usb).unwrap(), SignerSource {
|
||||
kind: SignerSourceKind::Usb(u),
|
||||
derivation_path: None,
|
||||
legacy: false,
|
||||
} if u == expected_locator));
|
||||
let usb = "usb://ledger?key=0/0".to_string();
|
||||
let expected_locator = RemoteWalletLocator {
|
||||
manufacturer: Manufacturer::Ledger,
|
||||
pubkey: None,
|
||||
};
|
||||
let expected_derivation_path = Some(DerivationPath::new_bip44(Some(0), Some(0)));
|
||||
assert!(matches!(parse_signer_source(&usb).unwrap(), SignerSource {
|
||||
kind: SignerSourceKind::Usb(u),
|
||||
derivation_path: d,
|
||||
legacy: false,
|
||||
} if u == expected_locator && d == expected_derivation_path));
|
||||
// Catchall into SignerSource::Filepath fails
|
||||
let junk = "sometextthatisnotapubkeyorfile".to_string();
|
||||
assert!(Pubkey::from_str(&junk).is_err());
|
||||
assert!(matches!(
|
||||
parse_signer_source(&junk),
|
||||
Err(SignerSourceError::IoError(_))
|
||||
));
|
||||
|
||||
let prompt = "prompt:".to_string();
|
||||
assert!(matches!(
|
||||
parse_signer_source(&prompt).unwrap(),
|
||||
SignerSource {
|
||||
kind: SignerSourceKind::Prompt,
|
||||
derivation_path: None,
|
||||
legacy: false,
|
||||
}
|
||||
));
|
||||
assert!(
|
||||
matches!(parse_signer_source(&format!("file:{}", absolute_path_str)).unwrap(), SignerSource {
|
||||
kind: SignerSourceKind::Filepath(p),
|
||||
derivation_path: None,
|
||||
legacy: false,
|
||||
} if p == absolute_path_str)
|
||||
);
|
||||
assert!(
|
||||
matches!(parse_signer_source(&format!("file:{}", relative_path_str)).unwrap(), SignerSource {
|
||||
kind: SignerSourceKind::Filepath(p),
|
||||
derivation_path: None,
|
||||
legacy: false,
|
||||
} if p == relative_path_str)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -27,6 +27,5 @@ pub mod fee_payer;
|
||||
pub mod input_parsers;
|
||||
pub mod input_validators;
|
||||
pub mod keypair;
|
||||
pub mod memo;
|
||||
pub mod nonce;
|
||||
pub mod offline;
|
||||
|
@@ -1,15 +0,0 @@
|
||||
use {crate::ArgConstant, clap::Arg};
|
||||
|
||||
pub const MEMO_ARG: ArgConstant<'static> = ArgConstant {
|
||||
name: "memo",
|
||||
long: "--with-memo",
|
||||
help: "Specify a memo string to include in the transaction.",
|
||||
};
|
||||
|
||||
pub fn memo_arg<'a, 'b>() -> Arg<'a, 'b> {
|
||||
Arg::with_name(MEMO_ARG.name)
|
||||
.long(MEMO_ARG.long)
|
||||
.takes_value(true)
|
||||
.value_name("MEMO")
|
||||
.help(MEMO_ARG.help)
|
||||
}
|
@@ -1,7 +1,5 @@
|
||||
use {
|
||||
crate::{input_validators::*, offline::BLOCKHASH_ARG, ArgConstant},
|
||||
clap::{App, Arg},
|
||||
};
|
||||
use crate::{input_validators::*, offline::BLOCKHASH_ARG, ArgConstant};
|
||||
use clap::{App, Arg};
|
||||
|
||||
pub const NONCE_ARG: ArgConstant<'static> = ArgConstant {
|
||||
name: "nonce",
|
||||
|
@@ -1,7 +1,5 @@
|
||||
use {
|
||||
crate::{input_validators::*, ArgConstant},
|
||||
clap::{App, Arg},
|
||||
};
|
||||
use crate::{input_validators::*, ArgConstant};
|
||||
use clap::{App, Arg};
|
||||
|
||||
pub const BLOCKHASH_ARG: ArgConstant<'static> = ArgConstant {
|
||||
name: "blockhash",
|
||||
|
@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-cli-config"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.7.1"
|
||||
version = "1.6.2"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
|
@@ -107,13 +107,13 @@ mod test {
|
||||
#[test]
|
||||
fn compute_websocket_url() {
|
||||
assert_eq!(
|
||||
Config::compute_websocket_url(&"http://api.devnet.solana.com"),
|
||||
"ws://api.devnet.solana.com/".to_string()
|
||||
Config::compute_websocket_url(&"http://devnet.solana.com"),
|
||||
"ws://devnet.solana.com/".to_string()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Config::compute_websocket_url(&"https://api.devnet.solana.com"),
|
||||
"wss://api.devnet.solana.com/".to_string()
|
||||
Config::compute_websocket_url(&"https://devnet.solana.com"),
|
||||
"wss://devnet.solana.com/".to_string()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
|
@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-cli-output"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.7.1"
|
||||
version = "1.6.2"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -12,21 +12,20 @@ documentation = "https://docs.rs/solana-cli-output"
|
||||
[dependencies]
|
||||
base64 = "0.13.0"
|
||||
chrono = { version = "0.4.11", features = ["serde"] }
|
||||
console = "0.14.1"
|
||||
console = "0.11.3"
|
||||
humantime = "2.0.1"
|
||||
Inflector = "0.11.4"
|
||||
indicatif = "0.15.0"
|
||||
serde = "1.0.122"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.56"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.7.1" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.1" }
|
||||
solana-client = { path = "../client", version = "=1.7.1" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.1" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "=1.7.1" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.7.1" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.7.1" }
|
||||
spl-memo = { version = "=3.0.1", features = ["no-entrypoint"] }
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.6.2" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.6.2" }
|
||||
solana-client = { path = "../client", version = "=1.6.2" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.2" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "=1.6.2" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.6.2" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.6.2" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -15,8 +15,8 @@ use {
|
||||
solana_account_decoder::parse_token::UiTokenAccount,
|
||||
solana_clap_utils::keypair::SignOnly,
|
||||
solana_client::rpc_response::{
|
||||
RpcAccountBalance, RpcContactInfo, RpcInflationGovernor, RpcInflationRate, RpcKeyedAccount,
|
||||
RpcSupply, RpcVoteAccountInfo,
|
||||
RpcAccountBalance, RpcInflationGovernor, RpcInflationRate, RpcKeyedAccount, RpcSupply,
|
||||
RpcVoteAccountInfo,
|
||||
},
|
||||
solana_sdk::{
|
||||
clock::{Epoch, Slot, UnixTimestamp},
|
||||
@@ -303,32 +303,14 @@ pub struct CliValidatorsStakeByVersion {
|
||||
pub delinquent_active_stake: u64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
|
||||
pub enum CliValidatorsSortOrder {
|
||||
Delinquent,
|
||||
Commission,
|
||||
EpochCredits,
|
||||
Identity,
|
||||
LastVote,
|
||||
Root,
|
||||
SkipRate,
|
||||
Stake,
|
||||
VoteAccount,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliValidators {
|
||||
pub total_active_stake: u64,
|
||||
pub total_current_stake: u64,
|
||||
pub total_delinquent_stake: u64,
|
||||
pub validators: Vec<CliValidator>,
|
||||
#[serde(skip_serializing)]
|
||||
pub validators_sort_order: CliValidatorsSortOrder,
|
||||
#[serde(skip_serializing)]
|
||||
pub validators_reverse_sort: bool,
|
||||
#[serde(skip_serializing)]
|
||||
pub number_validators: bool,
|
||||
pub current_validators: Vec<CliValidator>,
|
||||
pub delinquent_validators: Vec<CliValidator>,
|
||||
pub stake_by_version: BTreeMap<String, CliValidatorsStakeByVersion>,
|
||||
#[serde(skip_serializing)]
|
||||
pub use_lamports_unit: bool,
|
||||
@@ -344,40 +326,30 @@ impl fmt::Display for CliValidators {
|
||||
validator: &CliValidator,
|
||||
total_active_stake: u64,
|
||||
use_lamports_unit: bool,
|
||||
highest_last_vote: u64,
|
||||
highest_root: u64,
|
||||
delinquent: bool,
|
||||
) -> fmt::Result {
|
||||
fn non_zero_or_dash(v: u64, max_v: u64) -> String {
|
||||
fn non_zero_or_dash(v: u64) -> String {
|
||||
if v == 0 {
|
||||
"- ".into()
|
||||
} else if v == max_v {
|
||||
format!("{:>8} ( 0)", v)
|
||||
} else if v > max_v.saturating_sub(100) {
|
||||
format!("{:>8} ({:>3})", v, -(max_v.saturating_sub(v) as isize))
|
||||
"-".into()
|
||||
} else {
|
||||
format!("{:>8} ", v)
|
||||
format!("{}", v)
|
||||
}
|
||||
}
|
||||
|
||||
writeln!(
|
||||
f,
|
||||
"{} {:<44} {:<44} {:>3}% {:>14} {:>14} {:>7} {:>8} {:>7} {}",
|
||||
if validator.delinquent {
|
||||
"{} {:<44} {:<44} {:>3}% {:>8} {:>10} {:>10} {:>8} {}",
|
||||
if delinquent {
|
||||
WARNING.to_string()
|
||||
} else {
|
||||
"\u{a0}".to_string()
|
||||
" ".to_string()
|
||||
},
|
||||
validator.identity_pubkey,
|
||||
validator.vote_account_pubkey,
|
||||
validator.commission,
|
||||
non_zero_or_dash(validator.last_vote, highest_last_vote),
|
||||
non_zero_or_dash(validator.root_slot, highest_root),
|
||||
if let Some(skip_rate) = validator.skip_rate {
|
||||
format!("{:.2}%", skip_rate)
|
||||
} else {
|
||||
"- ".to_string()
|
||||
},
|
||||
validator.epoch_credits,
|
||||
non_zero_or_dash(validator.last_vote),
|
||||
non_zero_or_dash(validator.root_slot),
|
||||
validator.credits,
|
||||
validator.version,
|
||||
if validator.activated_stake > 0 {
|
||||
format!(
|
||||
@@ -390,100 +362,39 @@ impl fmt::Display for CliValidators {
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
let padding = if self.number_validators {
|
||||
((self.validators.len() + 1) as f64).log10().floor() as usize + 1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let header = style(format!(
|
||||
"{:padding$} {:<44} {:<38} {} {} {} {} {} {} {}",
|
||||
" ",
|
||||
"Identity",
|
||||
"Vote Account",
|
||||
"Commission",
|
||||
"Last Vote ",
|
||||
"Root Slot ",
|
||||
"Skip Rate",
|
||||
"Credits",
|
||||
"Version",
|
||||
"Active Stake",
|
||||
padding = padding + 1
|
||||
))
|
||||
.bold();
|
||||
writeln!(f, "{}", header)?;
|
||||
|
||||
let mut sorted_validators = self.validators.clone();
|
||||
match self.validators_sort_order {
|
||||
CliValidatorsSortOrder::Delinquent => {
|
||||
sorted_validators.sort_by_key(|a| a.delinquent);
|
||||
}
|
||||
CliValidatorsSortOrder::Commission => {
|
||||
sorted_validators.sort_by_key(|a| a.commission);
|
||||
}
|
||||
CliValidatorsSortOrder::EpochCredits => {
|
||||
sorted_validators.sort_by_key(|a| a.epoch_credits);
|
||||
}
|
||||
CliValidatorsSortOrder::Identity => {
|
||||
sorted_validators.sort_by(|a, b| a.identity_pubkey.cmp(&b.identity_pubkey));
|
||||
}
|
||||
CliValidatorsSortOrder::LastVote => {
|
||||
sorted_validators.sort_by_key(|a| a.last_vote);
|
||||
}
|
||||
CliValidatorsSortOrder::Root => {
|
||||
sorted_validators.sort_by_key(|a| a.root_slot);
|
||||
}
|
||||
CliValidatorsSortOrder::VoteAccount => {
|
||||
sorted_validators.sort_by(|a, b| a.vote_account_pubkey.cmp(&b.vote_account_pubkey));
|
||||
}
|
||||
CliValidatorsSortOrder::SkipRate => {
|
||||
sorted_validators.sort_by(|a, b| {
|
||||
use std::cmp::Ordering;
|
||||
match (a.skip_rate, b.skip_rate) {
|
||||
(None, None) => Ordering::Equal,
|
||||
(None, Some(_)) => Ordering::Greater,
|
||||
(Some(_), None) => Ordering::Less,
|
||||
(Some(a), Some(b)) => a.partial_cmp(&b).unwrap_or(Ordering::Equal),
|
||||
}
|
||||
});
|
||||
}
|
||||
CliValidatorsSortOrder::Stake => {
|
||||
sorted_validators.sort_by_key(|a| a.activated_stake);
|
||||
}
|
||||
}
|
||||
|
||||
if self.validators_reverse_sort {
|
||||
sorted_validators.reverse();
|
||||
}
|
||||
|
||||
let highest_root = sorted_validators
|
||||
.iter()
|
||||
.map(|v| v.root_slot)
|
||||
.max()
|
||||
.unwrap_or_default();
|
||||
let highest_last_vote = sorted_validators
|
||||
.iter()
|
||||
.map(|v| v.last_vote)
|
||||
.max()
|
||||
.unwrap_or_default();
|
||||
|
||||
for (i, validator) in sorted_validators.iter().enumerate() {
|
||||
if padding > 0 {
|
||||
write!(f, "{:padding$}", i + 1, padding = padding)?;
|
||||
}
|
||||
writeln!(
|
||||
f,
|
||||
"{}",
|
||||
style(format!(
|
||||
" {:<44} {:<38} {} {} {} {:>10} {:^8} {}",
|
||||
"Identity",
|
||||
"Vote Account",
|
||||
"Commission",
|
||||
"Last Vote",
|
||||
"Root Block",
|
||||
"Credits",
|
||||
"Version",
|
||||
"Active Stake",
|
||||
))
|
||||
.bold()
|
||||
)?;
|
||||
for validator in &self.current_validators {
|
||||
write_vote_account(
|
||||
f,
|
||||
validator,
|
||||
self.total_active_stake,
|
||||
self.use_lamports_unit,
|
||||
highest_last_vote,
|
||||
highest_root,
|
||||
false,
|
||||
)?;
|
||||
}
|
||||
|
||||
// The actual header has long scrolled away. Print the header once more as a footer
|
||||
if self.validators.len() > 100 {
|
||||
writeln!(f, "{}", header)?;
|
||||
for validator in &self.delinquent_validators {
|
||||
write_vote_account(
|
||||
f,
|
||||
validator,
|
||||
self.total_active_stake,
|
||||
self.use_lamports_unit,
|
||||
true,
|
||||
)?;
|
||||
}
|
||||
|
||||
writeln!(f)?;
|
||||
@@ -542,7 +453,7 @@ impl fmt::Display for CliValidators {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliValidator {
|
||||
pub identity_pubkey: String,
|
||||
@@ -550,12 +461,9 @@ pub struct CliValidator {
|
||||
pub commission: u8,
|
||||
pub last_vote: u64,
|
||||
pub root_slot: u64,
|
||||
pub credits: u64, // lifetime credits
|
||||
pub epoch_credits: u64, // credits earned in the current epoch
|
||||
pub credits: u64,
|
||||
pub activated_stake: u64,
|
||||
pub version: String,
|
||||
pub delinquent: bool,
|
||||
pub skip_rate: Option<f64>,
|
||||
}
|
||||
|
||||
impl CliValidator {
|
||||
@@ -563,67 +471,27 @@ impl CliValidator {
|
||||
vote_account: &RpcVoteAccountInfo,
|
||||
current_epoch: Epoch,
|
||||
version: String,
|
||||
skip_rate: Option<f64>,
|
||||
address_labels: &HashMap<String, String>,
|
||||
) -> Self {
|
||||
Self::_new(
|
||||
vote_account,
|
||||
current_epoch,
|
||||
version,
|
||||
skip_rate,
|
||||
address_labels,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_delinquent(
|
||||
vote_account: &RpcVoteAccountInfo,
|
||||
current_epoch: Epoch,
|
||||
version: String,
|
||||
skip_rate: Option<f64>,
|
||||
address_labels: &HashMap<String, String>,
|
||||
) -> Self {
|
||||
Self::_new(
|
||||
vote_account,
|
||||
current_epoch,
|
||||
version,
|
||||
skip_rate,
|
||||
address_labels,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
fn _new(
|
||||
vote_account: &RpcVoteAccountInfo,
|
||||
current_epoch: Epoch,
|
||||
version: String,
|
||||
skip_rate: Option<f64>,
|
||||
address_labels: &HashMap<String, String>,
|
||||
delinquent: bool,
|
||||
) -> Self {
|
||||
let (credits, epoch_credits) = vote_account
|
||||
.epoch_credits
|
||||
.iter()
|
||||
.find_map(|(epoch, credits, pre_credits)| {
|
||||
if *epoch == current_epoch {
|
||||
Some((*credits, credits.saturating_sub(*pre_credits)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap_or((0, 0));
|
||||
Self {
|
||||
identity_pubkey: format_labeled_address(&vote_account.node_pubkey, address_labels),
|
||||
vote_account_pubkey: format_labeled_address(&vote_account.vote_pubkey, address_labels),
|
||||
commission: vote_account.commission,
|
||||
last_vote: vote_account.last_vote,
|
||||
root_slot: vote_account.root_slot,
|
||||
credits,
|
||||
epoch_credits,
|
||||
credits: vote_account
|
||||
.epoch_credits
|
||||
.iter()
|
||||
.find_map(|(epoch, credits, _)| {
|
||||
if *epoch == current_epoch {
|
||||
Some(*credits)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap_or(0),
|
||||
activated_stake: vote_account.activated_stake,
|
||||
version,
|
||||
delinquent,
|
||||
skip_rate,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -735,76 +603,6 @@ pub struct CliEpochReward {
|
||||
pub apr: Option<f64>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliKeyedEpochReward {
|
||||
pub address: String,
|
||||
pub reward: Option<CliEpochReward>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliEpochRewardshMetadata {
|
||||
pub epoch: Epoch,
|
||||
pub effective_slot: Slot,
|
||||
pub block_time: UnixTimestamp,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliKeyedEpochRewards {
|
||||
#[serde(flatten, skip_serializing_if = "Option::is_none")]
|
||||
pub epoch_metadata: Option<CliEpochRewardshMetadata>,
|
||||
pub rewards: Vec<CliKeyedEpochReward>,
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliKeyedEpochRewards {}
|
||||
impl VerboseDisplay for CliKeyedEpochRewards {}
|
||||
|
||||
impl fmt::Display for CliKeyedEpochRewards {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.rewards.is_empty() {
|
||||
writeln!(f, "No rewards found in epoch")?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(metadata) = &self.epoch_metadata {
|
||||
writeln!(f, "Epoch: {}", metadata.epoch)?;
|
||||
writeln!(f, "Reward Slot: {}", metadata.effective_slot)?;
|
||||
let timestamp = metadata.block_time;
|
||||
writeln!(f, "Block Time: {}", unix_timestamp_to_string(timestamp))?;
|
||||
}
|
||||
writeln!(f, "Epoch Rewards:")?;
|
||||
writeln!(
|
||||
f,
|
||||
" {:<44} {:<18} {:<18} {:>14} {:>14}",
|
||||
"Address", "Amount", "New Balance", "Percent Change", "APR"
|
||||
)?;
|
||||
for keyed_reward in &self.rewards {
|
||||
match &keyed_reward.reward {
|
||||
Some(reward) => {
|
||||
writeln!(
|
||||
f,
|
||||
" {:<44} ◎{:<17.9} ◎{:<17.9} {:>13.2}% {}",
|
||||
keyed_reward.address,
|
||||
lamports_to_sol(reward.amount),
|
||||
lamports_to_sol(reward.post_balance),
|
||||
reward.percent_change,
|
||||
reward
|
||||
.apr
|
||||
.map(|apr| format!("{:>13.2}%", apr))
|
||||
.unwrap_or_default(),
|
||||
)?;
|
||||
}
|
||||
None => {
|
||||
writeln!(f, " {:<44} No rewards in epoch", keyed_reward.address,)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn show_votes_and_credits(
|
||||
f: &mut fmt::Formatter,
|
||||
votes: &[CliLockout],
|
||||
@@ -910,13 +708,13 @@ fn show_epoch_rewards(
|
||||
writeln!(f, "Epoch Rewards:")?;
|
||||
writeln!(
|
||||
f,
|
||||
" {:<6} {:<11} {:<18} {:<18} {:>14} {:>14}",
|
||||
" {:<6} {:<11} {:<16} {:<16} {:>14} {:>14}",
|
||||
"Epoch", "Reward Slot", "Amount", "New Balance", "Percent Change", "APR"
|
||||
)?;
|
||||
for reward in epoch_rewards {
|
||||
writeln!(
|
||||
f,
|
||||
" {:<6} {:<11} ◎{:<17.9} ◎{:<17.9} {:>13.2}% {}",
|
||||
" {:<6} {:<11} ◎{:<16.9} ◎{:<14.9} {:>13.2}% {}",
|
||||
reward.epoch,
|
||||
reward.effective_slot,
|
||||
lamports_to_sol(reward.amount),
|
||||
@@ -1461,18 +1259,18 @@ impl fmt::Display for CliInflation {
|
||||
if (self.governor.initial - self.governor.terminal).abs() < f64::EPSILON {
|
||||
writeln!(
|
||||
f,
|
||||
"Fixed rate: {:>5.2}%",
|
||||
"Fixed APR: {:>5.2}%",
|
||||
self.governor.terminal * 100.
|
||||
)?;
|
||||
} else {
|
||||
writeln!(
|
||||
f,
|
||||
"Initial rate: {:>5.2}%",
|
||||
"Initial APR: {:>5.2}%",
|
||||
self.governor.initial * 100.
|
||||
)?;
|
||||
writeln!(
|
||||
f,
|
||||
"Terminal rate: {:>5.2}%",
|
||||
"Terminal APR: {:>5.2}%",
|
||||
self.governor.terminal * 100.
|
||||
)?;
|
||||
writeln!(
|
||||
@@ -1480,10 +1278,6 @@ impl fmt::Display for CliInflation {
|
||||
"Rate reduction per year: {:>5.2}%",
|
||||
self.governor.taper * 100.
|
||||
)?;
|
||||
writeln!(
|
||||
f,
|
||||
"* Rate reduction is derived using the target slot time in genesis config"
|
||||
)?;
|
||||
}
|
||||
if self.governor.foundation_term > 0. {
|
||||
writeln!(
|
||||
@@ -1505,17 +1299,17 @@ impl fmt::Display for CliInflation {
|
||||
)?;
|
||||
writeln!(
|
||||
f,
|
||||
"Total rate: {:>5.2}%",
|
||||
"Total APR: {:>5.2}%",
|
||||
self.current_rate.total * 100.
|
||||
)?;
|
||||
writeln!(
|
||||
f,
|
||||
"Staking rate: {:>5.2}%",
|
||||
"Staking APR: {:>5.2}%",
|
||||
self.current_rate.validator * 100.
|
||||
)?;
|
||||
writeln!(
|
||||
f,
|
||||
"Foundation rate: {:>5.2}%",
|
||||
"Foundation APR: {:>5.2}%",
|
||||
self.current_rate.foundation * 100.
|
||||
)
|
||||
}
|
||||
@@ -2126,9 +1920,6 @@ impl fmt::Display for CliBlock {
|
||||
if let Some(block_time) = self.encoded_confirmed_block.block_time {
|
||||
writeln!(f, "Block Time: {:?}", Local.timestamp(block_time, 0))?;
|
||||
}
|
||||
if let Some(block_height) = self.encoded_confirmed_block.block_height {
|
||||
writeln!(f, "Block Height: {:?}", block_height)?;
|
||||
}
|
||||
if !self.encoded_confirmed_block.rewards.is_empty() {
|
||||
let mut rewards = self.encoded_confirmed_block.rewards.clone();
|
||||
rewards.sort_by(|a, b| a.pubkey.cmp(&b.pubkey));
|
||||
@@ -2286,97 +2077,6 @@ impl fmt::Display for CliTransactionConfirmation {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliGossipNode {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub ip_address: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub identity_label: Option<String>,
|
||||
pub identity_pubkey: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub gossip_port: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub tpu_port: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub rpc_host: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub version: Option<String>,
|
||||
}
|
||||
|
||||
impl CliGossipNode {
|
||||
pub fn new(info: RpcContactInfo, labels: &HashMap<String, String>) -> Self {
|
||||
Self {
|
||||
ip_address: info.gossip.map(|addr| addr.ip().to_string()),
|
||||
identity_label: labels.get(&info.pubkey).cloned(),
|
||||
identity_pubkey: info.pubkey,
|
||||
gossip_port: info.gossip.map(|addr| addr.port()),
|
||||
tpu_port: info.tpu.map(|addr| addr.port()),
|
||||
rpc_host: info.rpc.map(|addr| addr.to_string()),
|
||||
version: info.version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unwrap_to_string_or_none<T>(option: Option<T>) -> String
|
||||
where
|
||||
T: std::string::ToString,
|
||||
{
|
||||
unwrap_to_string_or_default(option, "none")
|
||||
}
|
||||
|
||||
fn unwrap_to_string_or_default<T>(option: Option<T>, default: &str) -> String
|
||||
where
|
||||
T: std::string::ToString,
|
||||
{
|
||||
option
|
||||
.as_ref()
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_else(|| default.to_string())
|
||||
}
|
||||
|
||||
impl fmt::Display for CliGossipNode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{:15} | {:44} | {:6} | {:5} | {:21} | {}",
|
||||
unwrap_to_string_or_none(self.ip_address.as_ref()),
|
||||
self.identity_label
|
||||
.as_ref()
|
||||
.unwrap_or(&self.identity_pubkey),
|
||||
unwrap_to_string_or_none(self.gossip_port.as_ref()),
|
||||
unwrap_to_string_or_none(self.tpu_port.as_ref()),
|
||||
unwrap_to_string_or_none(self.rpc_host.as_ref()),
|
||||
unwrap_to_string_or_default(self.version.as_ref(), "unknown"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliGossipNode {}
|
||||
impl VerboseDisplay for CliGossipNode {}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct CliGossipNodes(pub Vec<CliGossipNode>);
|
||||
|
||||
impl fmt::Display for CliGossipNodes {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(
|
||||
f,
|
||||
"IP Address | Node identifier \
|
||||
| Gossip | TPU | RPC Address | Version\n\
|
||||
----------------+----------------------------------------------+\
|
||||
--------+-------+-----------------------+----------------",
|
||||
)?;
|
||||
for node in self.0.iter() {
|
||||
writeln!(f, "{}", node)?;
|
||||
}
|
||||
writeln!(f, "Nodes: {}", self.0.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliGossipNodes {}
|
||||
impl VerboseDisplay for CliGossipNodes {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@@ -5,11 +5,9 @@ use {
|
||||
indicatif::{ProgressBar, ProgressStyle},
|
||||
solana_sdk::{
|
||||
clock::UnixTimestamp, hash::Hash, message::Message, native_token::lamports_to_sol,
|
||||
program_utils::limited_deserialize, pubkey::Pubkey, transaction::Transaction,
|
||||
program_utils::limited_deserialize, transaction::Transaction,
|
||||
},
|
||||
solana_transaction_status::UiTransactionStatusMeta,
|
||||
spl_memo::id as spl_memo_id,
|
||||
spl_memo::v1::id as spl_memo_v1_id,
|
||||
std::{collections::HashMap, fmt, io},
|
||||
};
|
||||
|
||||
@@ -30,11 +28,6 @@ impl Default for BuildBalanceMessageConfig {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_memo_program(k: &Pubkey) -> bool {
|
||||
let k_str = k.to_string();
|
||||
(k_str == spl_memo_v1_id().to_string()) || (k_str == spl_memo_id().to_string())
|
||||
}
|
||||
|
||||
pub fn build_balance_message_with_config(
|
||||
lamports: u64,
|
||||
config: &BuildBalanceMessageConfig,
|
||||
@@ -140,7 +133,7 @@ fn format_account_mode(message: &Message, index: usize) -> String {
|
||||
} else {
|
||||
"-"
|
||||
},
|
||||
if message.is_writable(index, /*demote_sysvar_write_locks=*/ true) {
|
||||
if message.is_writable(index) {
|
||||
"w" // comment for consistent rust fmt (no joking; lol)
|
||||
} else {
|
||||
"-"
|
||||
@@ -260,11 +253,6 @@ pub fn write_transaction<W: io::Write>(
|
||||
writeln!(w, "{} {:?}", prefix, system_instruction)?;
|
||||
raw = false;
|
||||
}
|
||||
} else if is_memo_program(&program_pubkey) {
|
||||
if let Ok(s) = std::str::from_utf8(&instruction.data) {
|
||||
writeln!(w, "{} Data: \"{}\"", prefix, s)?;
|
||||
raw = false;
|
||||
}
|
||||
}
|
||||
|
||||
if raw {
|
||||
@@ -322,38 +310,7 @@ pub fn write_transaction<W: io::Write>(
|
||||
if !log_messages.is_empty() {
|
||||
writeln!(w, "{}Log Messages:", prefix,)?;
|
||||
for log_message in log_messages {
|
||||
writeln!(w, "{} {}", prefix, log_message)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(rewards) = &transaction_status.rewards {
|
||||
if !rewards.is_empty() {
|
||||
writeln!(w, "{}Rewards:", prefix,)?;
|
||||
writeln!(
|
||||
w,
|
||||
"{} {:<44} {:^15} {:<15} {:<20}",
|
||||
prefix, "Address", "Type", "Amount", "New Balance"
|
||||
)?;
|
||||
for reward in rewards {
|
||||
let sign = if reward.lamports < 0 { "-" } else { "" };
|
||||
writeln!(
|
||||
w,
|
||||
"{} {:<44} {:^15} {:<15} {}",
|
||||
prefix,
|
||||
reward.pubkey,
|
||||
if let Some(reward_type) = reward.reward_type {
|
||||
format!("{}", reward_type)
|
||||
} else {
|
||||
"-".to_string()
|
||||
},
|
||||
format!(
|
||||
"{}◎{:<14.9}",
|
||||
sign,
|
||||
lamports_to_sol(reward.lamports.abs() as u64)
|
||||
),
|
||||
format!("◎{:<18.9}", lamports_to_sol(reward.post_balance),)
|
||||
)?;
|
||||
writeln!(w, "{} {}", prefix, log_message,)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-cli"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.7.1"
|
||||
version = "1.6.2"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -16,7 +16,7 @@ chrono = { version = "0.4.11", features = ["serde"] }
|
||||
clap = "2.33.1"
|
||||
criterion-stats = "0.3.0"
|
||||
ctrlc = { version = "3.1.5", features = ["termination"] }
|
||||
console = "0.14.1"
|
||||
console = "0.11.3"
|
||||
dirs-next = "2.0.0"
|
||||
log = "0.4.11"
|
||||
Inflector = "0.11.4"
|
||||
@@ -28,30 +28,29 @@ reqwest = { version = "0.11.2", default-features = false, features = ["blocking"
|
||||
serde = "1.0.122"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.56"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.7.1" }
|
||||
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.7.1" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.1" }
|
||||
solana-cli-config = { path = "../cli-config", version = "=1.7.1" }
|
||||
solana-cli-output = { path = "../cli-output", version = "=1.7.1" }
|
||||
solana-client = { path = "../client", version = "=1.7.1" }
|
||||
solana-config-program = { path = "../programs/config", version = "=1.7.1" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.7.1" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.1" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.1" }
|
||||
solana_rbpf = "=0.2.11"
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "=1.7.1" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.1" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "=1.7.1" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.7.1" }
|
||||
solana-version = { path = "../version", version = "=1.7.1" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.7.1" }
|
||||
spl-memo = { version = "=3.0.1", features = ["no-entrypoint"] }
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.6.2" }
|
||||
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.6.2" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.6.2" }
|
||||
solana-cli-config = { path = "../cli-config", version = "=1.6.2" }
|
||||
solana-cli-output = { path = "../cli-output", version = "=1.6.2" }
|
||||
solana-client = { path = "../client", version = "=1.6.2" }
|
||||
solana-config-program = { path = "../programs/config", version = "=1.6.2" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.6.2" }
|
||||
solana-logger = { path = "../logger", version = "=1.6.2" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.6.2" }
|
||||
solana_rbpf = "=0.2.5"
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "=1.6.2" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.2" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "=1.6.2" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.6.2" }
|
||||
solana-version = { path = "../version", version = "=1.6.2" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.6.2" }
|
||||
thiserror = "1.0.21"
|
||||
tiny-bip39 = "0.7.0"
|
||||
url = "2.1.1"
|
||||
|
||||
[dev-dependencies]
|
||||
solana-core = { path = "../core", version = "=1.7.1" }
|
||||
solana-core = { path = "../core", version = "=1.6.2" }
|
||||
tempfile = "3.1.0"
|
||||
|
||||
[[bin]]
|
||||
|
@@ -231,9 +231,18 @@ mod tests {
|
||||
mocks.insert(RpcRequest::GetBalance, account_balance_response);
|
||||
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
|
||||
|
||||
assert!(check_account_for_balance(&rpc_client, &pubkey, 1).unwrap());
|
||||
assert!(check_account_for_balance(&rpc_client, &pubkey, account_balance).unwrap());
|
||||
assert!(!check_account_for_balance(&rpc_client, &pubkey, account_balance + 1).unwrap());
|
||||
assert_eq!(
|
||||
check_account_for_balance(&rpc_client, &pubkey, 1).unwrap(),
|
||||
true
|
||||
);
|
||||
assert_eq!(
|
||||
check_account_for_balance(&rpc_client, &pubkey, account_balance).unwrap(),
|
||||
true
|
||||
);
|
||||
assert_eq!(
|
||||
check_account_for_balance(&rpc_client, &pubkey, account_balance + 1).unwrap(),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
426
cli/src/cli.rs
426
cli/src/cli.rs
File diff suppressed because it is too large
Load Diff
@@ -24,8 +24,8 @@ use solana_client::{
|
||||
pubsub_client::PubsubClient,
|
||||
rpc_client::{GetConfirmedSignaturesForAddress2Config, RpcClient},
|
||||
rpc_config::{
|
||||
RpcAccountInfoConfig, RpcBlockConfig, RpcLargestAccountsConfig, RpcLargestAccountsFilter,
|
||||
RpcProgramAccountsConfig, RpcTransactionConfig, RpcTransactionLogsConfig,
|
||||
RpcAccountInfoConfig, RpcConfirmedBlockConfig, RpcLargestAccountsConfig,
|
||||
RpcLargestAccountsFilter, RpcProgramAccountsConfig, RpcTransactionLogsConfig,
|
||||
RpcTransactionLogsFilter,
|
||||
},
|
||||
rpc_filter,
|
||||
@@ -41,27 +41,23 @@ use solana_sdk::{
|
||||
hash::Hash,
|
||||
message::Message,
|
||||
native_token::lamports_to_sol,
|
||||
nonce::State as NonceState,
|
||||
pubkey::{self, Pubkey},
|
||||
rent::Rent,
|
||||
rpc_port::DEFAULT_RPC_PORT_STR,
|
||||
signature::Signature,
|
||||
slot_history, system_instruction, system_program,
|
||||
system_instruction, system_program,
|
||||
sysvar::{
|
||||
self,
|
||||
slot_history::SlotHistory,
|
||||
stake_history::{self},
|
||||
},
|
||||
timing,
|
||||
transaction::Transaction,
|
||||
};
|
||||
use solana_stake_program::stake_state::StakeState;
|
||||
use solana_transaction_status::UiTransactionEncoding;
|
||||
use solana_vote_program::vote_state::VoteState;
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap, VecDeque},
|
||||
fmt,
|
||||
str::FromStr,
|
||||
net::SocketAddr,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
@@ -69,7 +65,6 @@ use std::{
|
||||
thread::sleep,
|
||||
time::{Duration, Instant, SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
static CHECK_MARK: Emoji = Emoji("✅ ", "");
|
||||
static CROSS_MARK: Emoji = Emoji("❌ ", "");
|
||||
@@ -349,38 +344,6 @@ impl ClusterQuerySubCommands for App<'_, '_> {
|
||||
.long("lamports")
|
||||
.takes_value(false)
|
||||
.help("Display balance in lamports instead of SOL"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("number")
|
||||
.long("number")
|
||||
.short("n")
|
||||
.takes_value(false)
|
||||
.help("Number the validators"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("reverse")
|
||||
.long("reverse")
|
||||
.short("r")
|
||||
.takes_value(false)
|
||||
.help("Reverse order while sorting"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("sort")
|
||||
.long("sort")
|
||||
.takes_value(true)
|
||||
.possible_values(&[
|
||||
"delinquent",
|
||||
"commission",
|
||||
"credits",
|
||||
"identity",
|
||||
"last-vote",
|
||||
"root",
|
||||
"skip-rate",
|
||||
"stake",
|
||||
"vote-account",
|
||||
])
|
||||
.default_value("stake")
|
||||
.help("Sort order (does not affect JSON output)"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
@@ -434,14 +397,9 @@ impl ClusterQuerySubCommands for App<'_, '_> {
|
||||
.arg(
|
||||
Arg::with_name("data_length")
|
||||
.index(1)
|
||||
.value_name("DATA_LENGTH_OR_MONIKER")
|
||||
.value_name("DATA_LENGTH")
|
||||
.required(true)
|
||||
.validator(|s| {
|
||||
RentLengthValue::from_str(&s)
|
||||
.map(|_| ())
|
||||
.map_err(|e| e.to_string())
|
||||
})
|
||||
.help("Length of data in the account to calculate rent for, or moniker: [nonce, stake, system, vote]"),
|
||||
.help("Length of data in the account to calculate rent for"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("lamports")
|
||||
@@ -614,29 +572,9 @@ pub fn parse_show_stakes(
|
||||
|
||||
pub fn parse_show_validators(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||
let use_lamports_unit = matches.is_present("lamports");
|
||||
let number_validators = matches.is_present("number");
|
||||
let reverse_sort = matches.is_present("reverse");
|
||||
|
||||
let sort_order = match value_t_or_exit!(matches, "sort", String).as_str() {
|
||||
"delinquent" => CliValidatorsSortOrder::Delinquent,
|
||||
"commission" => CliValidatorsSortOrder::Commission,
|
||||
"credits" => CliValidatorsSortOrder::EpochCredits,
|
||||
"identity" => CliValidatorsSortOrder::Identity,
|
||||
"last-vote" => CliValidatorsSortOrder::LastVote,
|
||||
"root" => CliValidatorsSortOrder::Root,
|
||||
"skip-rate" => CliValidatorsSortOrder::SkipRate,
|
||||
"stake" => CliValidatorsSortOrder::Stake,
|
||||
"vote-account" => CliValidatorsSortOrder::VoteAccount,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::ShowValidators {
|
||||
use_lamports_unit,
|
||||
sort_order,
|
||||
reverse_sort,
|
||||
number_validators,
|
||||
},
|
||||
command: CliCommand::ShowValidators { use_lamports_unit },
|
||||
signers: vec![],
|
||||
})
|
||||
}
|
||||
@@ -1027,12 +965,12 @@ pub fn process_get_block(
|
||||
};
|
||||
|
||||
let encoded_confirmed_block = rpc_client
|
||||
.get_block_with_config(
|
||||
.get_confirmed_block_with_config(
|
||||
slot,
|
||||
RpcBlockConfig {
|
||||
RpcConfirmedBlockConfig {
|
||||
encoding: Some(UiTransactionEncoding::Base64),
|
||||
commitment: Some(CommitmentConfig::confirmed()),
|
||||
..RpcBlockConfig::default()
|
||||
..RpcConfirmedBlockConfig::default()
|
||||
},
|
||||
)?
|
||||
.into();
|
||||
@@ -1093,8 +1031,8 @@ pub fn process_get_slot(rpc_client: &RpcClient, _config: &CliConfig) -> ProcessR
|
||||
}
|
||||
|
||||
pub fn process_get_block_height(rpc_client: &RpcClient, _config: &CliConfig) -> ProcessResult {
|
||||
let block_height = rpc_client.get_block_height()?;
|
||||
Ok(block_height.to_string())
|
||||
let epoch_info = rpc_client.get_epoch_info()?;
|
||||
Ok(epoch_info.block_height.to_string())
|
||||
}
|
||||
|
||||
pub fn parse_show_block_production(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||
@@ -1121,6 +1059,8 @@ pub fn process_show_block_production(
|
||||
return Err(format!("Epoch {} is in the future", epoch).into());
|
||||
}
|
||||
|
||||
let minimum_ledger_slot = rpc_client.minimum_ledger_slot()?;
|
||||
|
||||
let first_slot_in_epoch = epoch_schedule.get_first_slot_in_epoch(epoch);
|
||||
let end_slot = std::cmp::min(
|
||||
epoch_info.absolute_slot,
|
||||
@@ -1133,60 +1073,32 @@ pub fn process_show_block_production(
|
||||
first_slot_in_epoch
|
||||
};
|
||||
|
||||
if minimum_ledger_slot > end_slot {
|
||||
return Err(format!(
|
||||
"Ledger data not available for slots {} to {} (minimum ledger slot is {})",
|
||||
start_slot, end_slot, minimum_ledger_slot
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
if minimum_ledger_slot > start_slot {
|
||||
println!(
|
||||
"\n{}",
|
||||
style(format!(
|
||||
"Note: Requested start slot was {} but minimum ledger slot is {}",
|
||||
start_slot, minimum_ledger_slot
|
||||
))
|
||||
.italic(),
|
||||
);
|
||||
start_slot = minimum_ledger_slot;
|
||||
}
|
||||
|
||||
let progress_bar = new_spinner_progress_bar();
|
||||
progress_bar.set_message(&format!(
|
||||
"Fetching confirmed blocks between slots {} and {}...",
|
||||
start_slot, end_slot
|
||||
));
|
||||
|
||||
let slot_history_account = rpc_client
|
||||
.get_account_with_commitment(&sysvar::slot_history::id(), CommitmentConfig::finalized())?
|
||||
.value
|
||||
.unwrap();
|
||||
|
||||
let slot_history: SlotHistory = from_account(&slot_history_account).ok_or_else(|| {
|
||||
CliError::RpcRequestError("Failed to deserialize slot history".to_string())
|
||||
})?;
|
||||
|
||||
let (confirmed_blocks, start_slot) =
|
||||
if start_slot >= slot_history.oldest() && end_slot <= slot_history.newest() {
|
||||
// Fast, more reliable path using the SlotHistory sysvar
|
||||
|
||||
let confirmed_blocks: Vec<_> = (start_slot..=end_slot)
|
||||
.filter(|slot| slot_history.check(*slot) == slot_history::Check::Found)
|
||||
.collect();
|
||||
(confirmed_blocks, start_slot)
|
||||
} else {
|
||||
// Slow, less reliable path using `getBlocks`.
|
||||
//
|
||||
// "less reliable" because if the RPC node has holds in its ledger then the block production data will be
|
||||
// incorrect. This condition currently can't be detected over RPC
|
||||
//
|
||||
|
||||
let minimum_ledger_slot = rpc_client.minimum_ledger_slot()?;
|
||||
if minimum_ledger_slot > end_slot {
|
||||
return Err(format!(
|
||||
"Ledger data not available for slots {} to {} (minimum ledger slot is {})",
|
||||
start_slot, end_slot, minimum_ledger_slot
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
if minimum_ledger_slot > start_slot {
|
||||
progress_bar.println(format!(
|
||||
"{}",
|
||||
style(format!(
|
||||
"Note: Requested start slot was {} but minimum ledger slot is {}",
|
||||
start_slot, minimum_ledger_slot
|
||||
))
|
||||
.italic(),
|
||||
));
|
||||
start_slot = minimum_ledger_slot;
|
||||
}
|
||||
|
||||
let confirmed_blocks = rpc_client.get_blocks(start_slot, Some(end_slot))?;
|
||||
(confirmed_blocks, start_slot)
|
||||
};
|
||||
let confirmed_blocks = rpc_client.get_confirmed_blocks(start_slot, Some(end_slot))?;
|
||||
|
||||
let start_slot_index = (start_slot - first_slot_in_epoch) as usize;
|
||||
let end_slot_index = (end_slot - first_slot_in_epoch) as usize;
|
||||
@@ -1312,8 +1224,8 @@ pub fn process_supply(
|
||||
}
|
||||
|
||||
pub fn process_total_supply(rpc_client: &RpcClient, _config: &CliConfig) -> ProcessResult {
|
||||
let supply = rpc_client.supply()?.value;
|
||||
Ok(format!("{} SOL", lamports_to_sol(supply.total)))
|
||||
let total_supply = rpc_client.total_supply()?;
|
||||
Ok(format!("{} SOL", lamports_to_sol(total_supply)))
|
||||
}
|
||||
|
||||
pub fn process_get_transaction_count(rpc_client: &RpcClient, _config: &CliConfig) -> ProcessResult {
|
||||
@@ -1654,14 +1566,40 @@ pub fn process_live_slots(config: &CliConfig) -> ProcessResult {
|
||||
pub fn process_show_gossip(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
|
||||
let cluster_nodes = rpc_client.get_cluster_nodes()?;
|
||||
|
||||
let nodes: Vec<_> = cluster_nodes
|
||||
fn format_port(addr: Option<SocketAddr>) -> String {
|
||||
addr.map(|addr| addr.port().to_string())
|
||||
.unwrap_or_else(|| "none".to_string())
|
||||
}
|
||||
|
||||
let s: Vec<_> = cluster_nodes
|
||||
.into_iter()
|
||||
.map(|node| CliGossipNode::new(node, &config.address_labels))
|
||||
.map(|node| {
|
||||
format!(
|
||||
"{:15} | {:44} | {:6} | {:5} | {:21} | {}",
|
||||
node.gossip
|
||||
.map(|addr| addr.ip().to_string())
|
||||
.unwrap_or_else(|| "none".to_string()),
|
||||
format_labeled_address(&node.pubkey, &config.address_labels),
|
||||
format_port(node.gossip),
|
||||
format_port(node.tpu),
|
||||
node.rpc
|
||||
.map(|addr| addr.to_string())
|
||||
.unwrap_or_else(|| "none".to_string()),
|
||||
node.version.unwrap_or_else(|| "unknown".to_string()),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(config
|
||||
.output_format
|
||||
.formatted_string(&CliGossipNodes(nodes)))
|
||||
Ok(format!(
|
||||
"IP Address | Node identifier \
|
||||
| Gossip | TPU | RPC Address | Version\n\
|
||||
----------------+----------------------------------------------+\
|
||||
--------+-------+-----------------------+----------------\n\
|
||||
{}\n\
|
||||
Nodes: {}",
|
||||
s.join("\n"),
|
||||
s.len(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn process_show_stakes(
|
||||
@@ -1671,16 +1609,17 @@ pub fn process_show_stakes(
|
||||
vote_account_pubkeys: Option<&[Pubkey]>,
|
||||
) -> ProcessResult {
|
||||
use crate::stake::build_stake_state;
|
||||
use solana_stake_program::stake_state::StakeState;
|
||||
|
||||
let progress_bar = new_spinner_progress_bar();
|
||||
progress_bar.set_message("Fetching stake accounts...");
|
||||
|
||||
let mut program_accounts_config = RpcProgramAccountsConfig {
|
||||
filters: None,
|
||||
account_config: RpcAccountInfoConfig {
|
||||
encoding: Some(solana_account_decoder::UiAccountEncoding::Base64),
|
||||
..RpcAccountInfoConfig::default()
|
||||
},
|
||||
..RpcProgramAccountsConfig::default()
|
||||
};
|
||||
|
||||
if let Some(vote_account_pubkeys) = vote_account_pubkeys {
|
||||
@@ -1782,36 +1721,10 @@ pub fn process_show_validators(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
use_lamports_unit: bool,
|
||||
validators_sort_order: CliValidatorsSortOrder,
|
||||
validators_reverse_sort: bool,
|
||||
number_validators: bool,
|
||||
) -> ProcessResult {
|
||||
let progress_bar = new_spinner_progress_bar();
|
||||
progress_bar.set_message("Fetching vote accounts...");
|
||||
let epoch_info = rpc_client.get_epoch_info()?;
|
||||
let vote_accounts = rpc_client.get_vote_accounts()?;
|
||||
|
||||
progress_bar.set_message("Fetching block production...");
|
||||
let skip_rate: HashMap<_, _> = rpc_client
|
||||
.get_block_production()
|
||||
.ok()
|
||||
.map(|result| {
|
||||
result
|
||||
.value
|
||||
.by_identity
|
||||
.into_iter()
|
||||
.map(|(identity, (leader_slots, blocks_produced))| {
|
||||
(
|
||||
identity,
|
||||
100. * (leader_slots.saturating_sub(blocks_produced)) as f64
|
||||
/ leader_slots as f64,
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
progress_bar.set_message("Fetching version information...");
|
||||
let mut node_version = HashMap::new();
|
||||
let unknown_version = "unknown".to_string();
|
||||
for contact_info in rpc_client.get_cluster_nodes()? {
|
||||
@@ -1823,8 +1736,6 @@ pub fn process_show_validators(
|
||||
);
|
||||
}
|
||||
|
||||
progress_bar.finish_and_clear();
|
||||
|
||||
let total_active_stake = vote_accounts
|
||||
.current
|
||||
.iter()
|
||||
@@ -1839,8 +1750,9 @@ pub fn process_show_validators(
|
||||
.sum();
|
||||
let total_current_stake = total_active_stake - total_delinquent_stake;
|
||||
|
||||
let current_validators: Vec<CliValidator> = vote_accounts
|
||||
.current
|
||||
let mut current = vote_accounts.current;
|
||||
current.sort_by(|a, b| b.activated_stake.cmp(&a.activated_stake));
|
||||
let current_validators: Vec<CliValidator> = current
|
||||
.iter()
|
||||
.map(|vote_account| {
|
||||
CliValidator::new(
|
||||
@@ -1850,23 +1762,22 @@ pub fn process_show_validators(
|
||||
.get(&vote_account.node_pubkey)
|
||||
.unwrap_or(&unknown_version)
|
||||
.clone(),
|
||||
skip_rate.get(&vote_account.node_pubkey).cloned(),
|
||||
&config.address_labels,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let delinquent_validators: Vec<CliValidator> = vote_accounts
|
||||
.delinquent
|
||||
let mut delinquent = vote_accounts.delinquent;
|
||||
delinquent.sort_by(|a, b| b.activated_stake.cmp(&a.activated_stake));
|
||||
let delinquent_validators: Vec<CliValidator> = delinquent
|
||||
.iter()
|
||||
.map(|vote_account| {
|
||||
CliValidator::new_delinquent(
|
||||
CliValidator::new(
|
||||
vote_account,
|
||||
epoch_info.epoch,
|
||||
node_version
|
||||
.get(&vote_account.node_pubkey)
|
||||
.unwrap_or(&unknown_version)
|
||||
.clone(),
|
||||
skip_rate.get(&vote_account.node_pubkey).cloned(),
|
||||
&config.address_labels,
|
||||
)
|
||||
})
|
||||
@@ -1892,13 +1803,8 @@ pub fn process_show_validators(
|
||||
total_active_stake,
|
||||
total_current_stake,
|
||||
total_delinquent_stake,
|
||||
validators: current_validators
|
||||
.into_iter()
|
||||
.chain(delinquent_validators.into_iter())
|
||||
.collect(),
|
||||
validators_sort_order,
|
||||
validators_reverse_sort,
|
||||
number_validators,
|
||||
current_validators,
|
||||
delinquent_validators,
|
||||
stake_by_version,
|
||||
use_lamports_unit,
|
||||
};
|
||||
@@ -1914,13 +1820,12 @@ pub fn process_transaction_history(
|
||||
limit: usize,
|
||||
show_transactions: bool,
|
||||
) -> ProcessResult {
|
||||
let results = rpc_client.get_signatures_for_address_with_config(
|
||||
let results = rpc_client.get_confirmed_signatures_for_address2_with_config(
|
||||
address,
|
||||
GetConfirmedSignaturesForAddress2Config {
|
||||
before,
|
||||
until,
|
||||
limit: Some(limit),
|
||||
commitment: Some(CommitmentConfig::confirmed()),
|
||||
},
|
||||
)?;
|
||||
|
||||
@@ -1937,13 +1842,9 @@ pub fn process_transaction_history(
|
||||
Some(block_time) =>
|
||||
format!("timestamp={} ", unix_timestamp_to_string(block_time)),
|
||||
},
|
||||
if let Some(err) = result.err {
|
||||
format!("Failed: {:?}", err)
|
||||
} else {
|
||||
match result.confirmation_status {
|
||||
None => "Finalized".to_string(),
|
||||
Some(status) => format!("{:?}", status),
|
||||
}
|
||||
match result.err {
|
||||
None => "Confirmed".to_string(),
|
||||
Some(err) => format!("Failed: {:?}", err),
|
||||
},
|
||||
result.memo.unwrap_or_else(|| "".to_string()),
|
||||
);
|
||||
@@ -1953,13 +1854,9 @@ pub fn process_transaction_history(
|
||||
|
||||
if show_transactions {
|
||||
if let Ok(signature) = result.signature.parse::<Signature>() {
|
||||
match rpc_client.get_transaction_with_config(
|
||||
&signature,
|
||||
RpcTransactionConfig {
|
||||
encoding: Some(UiTransactionEncoding::Base64),
|
||||
commitment: Some(CommitmentConfig::confirmed()),
|
||||
},
|
||||
) {
|
||||
match rpc_client
|
||||
.get_confirmed_transaction(&signature, UiTransactionEncoding::Base64)
|
||||
{
|
||||
Ok(confirmed_transaction) => {
|
||||
println_transaction(
|
||||
&confirmed_transaction
|
||||
@@ -2012,47 +1909,6 @@ impl fmt::Display for CliRentCalculation {
|
||||
impl QuietDisplay for CliRentCalculation {}
|
||||
impl VerboseDisplay for CliRentCalculation {}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum RentLengthValue {
|
||||
Nonce,
|
||||
Stake,
|
||||
System,
|
||||
Vote,
|
||||
Bytes(usize),
|
||||
}
|
||||
|
||||
impl RentLengthValue {
|
||||
pub fn length(&self) -> usize {
|
||||
match self {
|
||||
Self::Nonce => NonceState::size(),
|
||||
Self::Stake => std::mem::size_of::<StakeState>(),
|
||||
Self::System => 0,
|
||||
Self::Vote => VoteState::size_of(),
|
||||
Self::Bytes(l) => *l,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[error("expected number or moniker, got \"{0}\"")]
|
||||
pub struct RentLengthValueError(pub String);
|
||||
|
||||
impl FromStr for RentLengthValue {
|
||||
type Err = RentLengthValueError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let s = s.to_ascii_lowercase();
|
||||
match s.as_str() {
|
||||
"nonce" => Ok(Self::Nonce),
|
||||
"stake" => Ok(Self::Stake),
|
||||
"system" => Ok(Self::System),
|
||||
"vote" => Ok(Self::Vote),
|
||||
_ => usize::from_str(&s)
|
||||
.map(Self::Bytes)
|
||||
.map_err(|_| RentLengthValueError(s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_calculate_rent(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
@@ -2098,7 +1954,10 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let (default_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
let default_signer = DefaultSigner::new("", &default_keypair_file);
|
||||
let default_signer = DefaultSigner {
|
||||
path: default_keypair_file,
|
||||
arg_name: String::new(),
|
||||
};
|
||||
|
||||
let test_cluster_version = test_commands
|
||||
.clone()
|
||||
|
@@ -1,22 +1,14 @@
|
||||
use crate::cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult};
|
||||
use clap::{App, Arg, ArgMatches, SubCommand};
|
||||
use solana_clap_utils::{
|
||||
input_parsers::{pubkeys_of, value_of},
|
||||
input_validators::is_valid_pubkey,
|
||||
keypair::*,
|
||||
};
|
||||
use solana_cli_output::{
|
||||
CliEpochRewardshMetadata, CliInflation, CliKeyedEpochReward, CliKeyedEpochRewards,
|
||||
};
|
||||
use clap::{App, ArgMatches, SubCommand};
|
||||
use solana_clap_utils::keypair::*;
|
||||
use solana_cli_output::CliInflation;
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||
use solana_sdk::{clock::Epoch, pubkey::Pubkey};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum InflationCliCommand {
|
||||
Show,
|
||||
Rewards(Vec<Pubkey>, Option<Epoch>),
|
||||
}
|
||||
|
||||
pub trait InflationSubCommands {
|
||||
@@ -25,47 +17,17 @@ pub trait InflationSubCommands {
|
||||
|
||||
impl InflationSubCommands for App<'_, '_> {
|
||||
fn inflation_subcommands(self) -> Self {
|
||||
self.subcommand(
|
||||
SubCommand::with_name("inflation")
|
||||
.about("Show inflation information")
|
||||
.subcommand(
|
||||
SubCommand::with_name("rewards")
|
||||
.about("Show inflation rewards for a set of addresses")
|
||||
.arg(pubkey!(
|
||||
Arg::with_name("addresses")
|
||||
.value_name("ADDRESS")
|
||||
.index(1)
|
||||
.multiple(true)
|
||||
.required(true),
|
||||
"Address of account to query for rewards. "
|
||||
))
|
||||
.arg(
|
||||
Arg::with_name("rewards_epoch")
|
||||
.long("rewards-epoch")
|
||||
.takes_value(true)
|
||||
.value_name("EPOCH")
|
||||
.help("Display rewards for specific epoch [default: latest epoch]"),
|
||||
),
|
||||
),
|
||||
)
|
||||
self.subcommand(SubCommand::with_name("inflation").about("Show inflation information"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_inflation_subcommand(
|
||||
matches: &ArgMatches<'_>,
|
||||
_matches: &ArgMatches<'_>,
|
||||
_default_signer: &DefaultSigner,
|
||||
_wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let command = match matches.subcommand() {
|
||||
("rewards", Some(matches)) => {
|
||||
let addresses = pubkeys_of(matches, "addresses").unwrap();
|
||||
let rewards_epoch = value_of(matches, "rewards_epoch");
|
||||
InflationCliCommand::Rewards(addresses, rewards_epoch)
|
||||
}
|
||||
_ => InflationCliCommand::Show,
|
||||
};
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::Inflation(command),
|
||||
command: CliCommand::Inflation(InflationCliCommand::Show),
|
||||
signers: vec![],
|
||||
})
|
||||
}
|
||||
@@ -75,15 +37,8 @@ pub fn process_inflation_subcommand(
|
||||
config: &CliConfig,
|
||||
inflation_subcommand: &InflationCliCommand,
|
||||
) -> ProcessResult {
|
||||
match inflation_subcommand {
|
||||
InflationCliCommand::Show => process_show(rpc_client, config),
|
||||
InflationCliCommand::Rewards(ref addresses, rewards_epoch) => {
|
||||
process_rewards(rpc_client, config, addresses, *rewards_epoch)
|
||||
}
|
||||
}
|
||||
}
|
||||
assert_eq!(*inflation_subcommand, InflationCliCommand::Show);
|
||||
|
||||
fn process_show(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
|
||||
let governor = rpc_client.get_inflation_governor()?;
|
||||
let current_rate = rpc_client.get_inflation_rate()?;
|
||||
|
||||
@@ -94,49 +49,3 @@ fn process_show(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
|
||||
|
||||
Ok(config.output_format.formatted_string(&inflation))
|
||||
}
|
||||
|
||||
fn process_rewards(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
addresses: &[Pubkey],
|
||||
rewards_epoch: Option<Epoch>,
|
||||
) -> ProcessResult {
|
||||
let rewards = rpc_client
|
||||
.get_inflation_reward(&addresses, rewards_epoch)
|
||||
.map_err(|err| {
|
||||
if let Some(epoch) = rewards_epoch {
|
||||
format!("Rewards not available for epoch {}", epoch)
|
||||
} else {
|
||||
format!("Rewards not available {}", err)
|
||||
}
|
||||
})?;
|
||||
let epoch_schedule = rpc_client.get_epoch_schedule()?;
|
||||
|
||||
let mut epoch_rewards: Vec<CliKeyedEpochReward> = vec![];
|
||||
let epoch_metadata = if let Some(Some(first_reward)) = rewards.iter().find(|&v| v.is_some()) {
|
||||
let (epoch_start_time, epoch_end_time) =
|
||||
crate::stake::get_epoch_boundary_timestamps(rpc_client, first_reward, &epoch_schedule)?;
|
||||
for (reward, address) in rewards.iter().zip(addresses) {
|
||||
let cli_reward = reward.as_ref().and_then(|reward| {
|
||||
crate::stake::make_cli_reward(reward, epoch_start_time, epoch_end_time)
|
||||
});
|
||||
epoch_rewards.push(CliKeyedEpochReward {
|
||||
address: address.to_string(),
|
||||
reward: cli_reward,
|
||||
});
|
||||
}
|
||||
let block_time = rpc_client.get_block_time(first_reward.effective_slot)?;
|
||||
Some(CliEpochRewardshMetadata {
|
||||
epoch: first_reward.epoch,
|
||||
effective_slot: first_reward.effective_slot,
|
||||
block_time,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let cli_rewards = CliKeyedEpochRewards {
|
||||
epoch_metadata,
|
||||
rewards: epoch_rewards,
|
||||
};
|
||||
Ok(config.output_format.formatted_string(&cli_rewards))
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@ macro_rules! ACCOUNT_STRING {
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_use]
|
||||
macro_rules! pubkey {
|
||||
($arg:expr, $help:expr) => {
|
||||
$arg.takes_value(true)
|
||||
@@ -25,9 +26,9 @@ pub mod cli;
|
||||
pub mod cluster_query;
|
||||
pub mod feature;
|
||||
pub mod inflation;
|
||||
pub mod memo;
|
||||
pub mod nonce;
|
||||
pub mod program;
|
||||
pub mod send_tpu;
|
||||
pub mod spend_utils;
|
||||
pub mod stake;
|
||||
pub mod test_utils;
|
||||
|
@@ -179,7 +179,10 @@ pub fn parse_args<'a>(
|
||||
&config.keypair_path,
|
||||
);
|
||||
|
||||
let default_signer = DefaultSigner::new(default_signer_arg_name, &default_signer_path);
|
||||
let default_signer = DefaultSigner {
|
||||
arg_name: default_signer_arg_name,
|
||||
path: default_signer_path.clone(),
|
||||
};
|
||||
|
||||
let CliCommandInfo {
|
||||
command,
|
||||
|
@@ -1,22 +0,0 @@
|
||||
use solana_sdk::instruction::Instruction;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use spl_memo::id;
|
||||
|
||||
pub trait WithMemo {
|
||||
fn with_memo<T: AsRef<str>>(self, memo: Option<T>) -> Self;
|
||||
}
|
||||
|
||||
impl WithMemo for Vec<Instruction> {
|
||||
fn with_memo<T: AsRef<str>>(mut self, memo: Option<T>) -> Self {
|
||||
if let Some(memo) = &memo {
|
||||
let memo = memo.as_ref();
|
||||
let memo_ix = Instruction {
|
||||
program_id: Pubkey::new(&id().to_bytes()),
|
||||
accounts: vec![],
|
||||
data: memo.as_bytes().to_vec(),
|
||||
};
|
||||
self.push(memo_ix);
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
@@ -4,7 +4,6 @@ use crate::{
|
||||
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
|
||||
ProcessResult,
|
||||
},
|
||||
memo::WithMemo,
|
||||
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
|
||||
};
|
||||
use clap::{App, Arg, ArgMatches, SubCommand};
|
||||
@@ -12,7 +11,6 @@ use solana_clap_utils::{
|
||||
input_parsers::*,
|
||||
input_validators::*,
|
||||
keypair::{DefaultSigner, SignerIndex},
|
||||
memo::MEMO_ARG,
|
||||
nonce::*,
|
||||
};
|
||||
use solana_cli_output::CliNonceAccount;
|
||||
@@ -173,7 +171,6 @@ pub fn parse_authorize_nonce_account(
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap();
|
||||
let new_authority = pubkey_of_signer(matches, "new_authority", wallet_manager)?.unwrap();
|
||||
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
|
||||
let (nonce_authority, nonce_authority_pubkey) =
|
||||
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
|
||||
|
||||
@@ -188,7 +185,6 @@ pub fn parse_authorize_nonce_account(
|
||||
command: CliCommand::AuthorizeNonceAccount {
|
||||
nonce_account,
|
||||
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
|
||||
memo,
|
||||
new_authority,
|
||||
},
|
||||
signers: signer_info.signers,
|
||||
@@ -205,7 +201,6 @@ pub fn parse_nonce_create_account(
|
||||
let seed = matches.value_of("seed").map(|s| s.to_string());
|
||||
let amount = SpendAmount::new_from_matches(matches, "amount");
|
||||
let nonce_authority = pubkey_of_signer(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
|
||||
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
|
||||
|
||||
let payer_provided = None;
|
||||
let signer_info = default_signer.generate_unique_signers(
|
||||
@@ -219,7 +214,6 @@ pub fn parse_nonce_create_account(
|
||||
nonce_account: signer_info.index_of(nonce_account_pubkey).unwrap(),
|
||||
seed,
|
||||
nonce_authority,
|
||||
memo,
|
||||
amount,
|
||||
},
|
||||
signers: signer_info.signers,
|
||||
@@ -245,7 +239,6 @@ pub fn parse_new_nonce(
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap();
|
||||
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
|
||||
let (nonce_authority, nonce_authority_pubkey) =
|
||||
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
|
||||
|
||||
@@ -260,7 +253,6 @@ pub fn parse_new_nonce(
|
||||
command: CliCommand::NewNonce {
|
||||
nonce_account,
|
||||
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
|
||||
memo,
|
||||
},
|
||||
signers: signer_info.signers,
|
||||
})
|
||||
@@ -292,7 +284,6 @@ pub fn parse_withdraw_from_nonce_account(
|
||||
let destination_account_pubkey =
|
||||
pubkey_of_signer(matches, "destination_account_pubkey", wallet_manager)?.unwrap();
|
||||
let lamports = lamports_of_sol(matches, "amount").unwrap();
|
||||
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
|
||||
let (nonce_authority, nonce_authority_pubkey) =
|
||||
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
|
||||
|
||||
@@ -307,7 +298,6 @@ pub fn parse_withdraw_from_nonce_account(
|
||||
command: CliCommand::WithdrawFromNonceAccount {
|
||||
nonce_account,
|
||||
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
|
||||
memo,
|
||||
destination_account_pubkey,
|
||||
lamports,
|
||||
},
|
||||
@@ -340,19 +330,13 @@ pub fn process_authorize_nonce_account(
|
||||
config: &CliConfig,
|
||||
nonce_account: &Pubkey,
|
||||
nonce_authority: SignerIndex,
|
||||
memo: Option<&String>,
|
||||
new_authority: &Pubkey,
|
||||
) -> ProcessResult {
|
||||
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||
|
||||
let nonce_authority = config.signers[nonce_authority];
|
||||
let ixs = vec![authorize_nonce_account(
|
||||
nonce_account,
|
||||
&nonce_authority.pubkey(),
|
||||
new_authority,
|
||||
)]
|
||||
.with_memo(memo);
|
||||
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
|
||||
let ix = authorize_nonce_account(nonce_account, &nonce_authority.pubkey(), new_authority);
|
||||
let message = Message::new(&[ix], Some(&config.signers[0].pubkey()));
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||
|
||||
@@ -373,7 +357,6 @@ pub fn process_create_nonce_account(
|
||||
nonce_account: SignerIndex,
|
||||
seed: Option<String>,
|
||||
nonce_authority: Option<Pubkey>,
|
||||
memo: Option<&String>,
|
||||
amount: SpendAmount,
|
||||
) -> ProcessResult {
|
||||
let nonce_account_pubkey = config.signers[nonce_account].pubkey();
|
||||
@@ -400,7 +383,6 @@ pub fn process_create_nonce_account(
|
||||
&nonce_authority,
|
||||
lamports,
|
||||
)
|
||||
.with_memo(memo)
|
||||
} else {
|
||||
create_nonce_account(
|
||||
&config.signers[0].pubkey(),
|
||||
@@ -408,7 +390,6 @@ pub fn process_create_nonce_account(
|
||||
&nonce_authority,
|
||||
lamports,
|
||||
)
|
||||
.with_memo(memo)
|
||||
};
|
||||
Message::new(&ixs, Some(&config.signers[0].pubkey()))
|
||||
};
|
||||
@@ -470,7 +451,6 @@ pub fn process_new_nonce(
|
||||
config: &CliConfig,
|
||||
nonce_account: &Pubkey,
|
||||
nonce_authority: SignerIndex,
|
||||
memo: Option<&String>,
|
||||
) -> ProcessResult {
|
||||
check_unique_pubkeys(
|
||||
(&config.signers[0].pubkey(), "cli keypair".to_string()),
|
||||
@@ -486,13 +466,9 @@ pub fn process_new_nonce(
|
||||
}
|
||||
|
||||
let nonce_authority = config.signers[nonce_authority];
|
||||
let ixs = vec![advance_nonce_account(
|
||||
&nonce_account,
|
||||
&nonce_authority.pubkey(),
|
||||
)]
|
||||
.with_memo(memo);
|
||||
let ix = advance_nonce_account(&nonce_account, &nonce_authority.pubkey());
|
||||
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
|
||||
let message = Message::new(&[ix], Some(&config.signers[0].pubkey()));
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||
check_account_for_fee_with_commitment(
|
||||
@@ -541,21 +517,19 @@ pub fn process_withdraw_from_nonce_account(
|
||||
config: &CliConfig,
|
||||
nonce_account: &Pubkey,
|
||||
nonce_authority: SignerIndex,
|
||||
memo: Option<&String>,
|
||||
destination_account_pubkey: &Pubkey,
|
||||
lamports: u64,
|
||||
) -> ProcessResult {
|
||||
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||
|
||||
let nonce_authority = config.signers[nonce_authority];
|
||||
let ixs = vec![withdraw_nonce_account(
|
||||
let ix = withdraw_nonce_account(
|
||||
nonce_account,
|
||||
&nonce_authority.pubkey(),
|
||||
destination_account_pubkey,
|
||||
lamports,
|
||||
)]
|
||||
.with_memo(memo);
|
||||
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
|
||||
);
|
||||
let message = Message::new(&[ix], Some(&config.signers[0].pubkey()));
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||
check_account_for_fee_with_commitment(
|
||||
@@ -596,7 +570,10 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let (default_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
let default_signer = DefaultSigner::new("", &default_keypair_file);
|
||||
let default_signer = DefaultSigner {
|
||||
path: default_keypair_file.clone(),
|
||||
arg_name: String::new(),
|
||||
};
|
||||
let (keypair_file, mut tmp_file) = make_tmp_file();
|
||||
let nonce_account_keypair = Keypair::new();
|
||||
write_keypair(&nonce_account_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
@@ -620,7 +597,6 @@ mod tests {
|
||||
command: CliCommand::AuthorizeNonceAccount {
|
||||
nonce_account: nonce_account_pubkey,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
new_authority: Pubkey::default(),
|
||||
},
|
||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
||||
@@ -642,7 +618,6 @@ mod tests {
|
||||
command: CliCommand::AuthorizeNonceAccount {
|
||||
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
|
||||
nonce_authority: 1,
|
||||
memo: None,
|
||||
new_authority: Pubkey::default(),
|
||||
},
|
||||
signers: vec![
|
||||
@@ -666,7 +641,6 @@ mod tests {
|
||||
nonce_account: 1,
|
||||
seed: None,
|
||||
nonce_authority: None,
|
||||
memo: None,
|
||||
amount: SpendAmount::Some(50_000_000_000),
|
||||
},
|
||||
signers: vec![
|
||||
@@ -692,7 +666,6 @@ mod tests {
|
||||
nonce_account: 1,
|
||||
seed: None,
|
||||
nonce_authority: Some(nonce_authority_keypair.pubkey()),
|
||||
memo: None,
|
||||
amount: SpendAmount::Some(50_000_000_000),
|
||||
},
|
||||
signers: vec![
|
||||
@@ -728,7 +701,6 @@ mod tests {
|
||||
command: CliCommand::NewNonce {
|
||||
nonce_account: nonce_account.pubkey(),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
},
|
||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
||||
}
|
||||
@@ -749,7 +721,6 @@ mod tests {
|
||||
command: CliCommand::NewNonce {
|
||||
nonce_account: nonce_account.pubkey(),
|
||||
nonce_authority: 1,
|
||||
memo: None,
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
@@ -794,7 +765,6 @@ mod tests {
|
||||
command: CliCommand::WithdrawFromNonceAccount {
|
||||
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
destination_account_pubkey: nonce_account_pubkey,
|
||||
lamports: 42_000_000_000
|
||||
},
|
||||
@@ -823,7 +793,6 @@ mod tests {
|
||||
command: CliCommand::WithdrawFromNonceAccount {
|
||||
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
|
||||
nonce_authority: 1,
|
||||
memo: None,
|
||||
destination_account_pubkey: nonce_account_pubkey,
|
||||
lamports: 42_000_000_000
|
||||
},
|
||||
|
@@ -1,3 +1,4 @@
|
||||
use crate::send_tpu::{get_leader_tpus, send_transaction_tpu};
|
||||
use crate::{
|
||||
checks::*,
|
||||
cli::{
|
||||
@@ -5,6 +6,7 @@ use crate::{
|
||||
ProcessResult,
|
||||
},
|
||||
};
|
||||
use bincode::serialize;
|
||||
use bip39::{Language, Mnemonic, MnemonicType, Seed};
|
||||
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
|
||||
use log::*;
|
||||
@@ -23,7 +25,7 @@ use solana_client::{
|
||||
rpc_config::{RpcAccountInfoConfig, RpcProgramAccountsConfig},
|
||||
rpc_filter::{Memcmp, MemcmpEncodedBytes, RpcFilterType},
|
||||
rpc_request::MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS,
|
||||
tpu_client::{TpuClient, TpuClientConfig},
|
||||
rpc_response::RpcLeaderSchedule,
|
||||
};
|
||||
use solana_rbpf::vm::{Config, Executable};
|
||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||
@@ -49,17 +51,20 @@ use solana_sdk::{
|
||||
};
|
||||
use solana_transaction_status::TransactionConfirmationStatus;
|
||||
use std::{
|
||||
cmp::min,
|
||||
collections::HashMap,
|
||||
error,
|
||||
fs::File,
|
||||
io::{Read, Write},
|
||||
net::UdpSocket,
|
||||
path::PathBuf,
|
||||
sync::Arc,
|
||||
thread::sleep,
|
||||
time::Duration,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
const DATA_CHUNK_SIZE: usize = 229; // Keep program chunks under PACKET_DATA_SIZE
|
||||
const NUM_TPU_LEADERS: u64 = 2;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ProgramCliCommand {
|
||||
@@ -381,17 +386,22 @@ pub fn parse_program_subcommand(
|
||||
default_signer.signer_from_path(matches, wallet_manager)?,
|
||||
)];
|
||||
|
||||
let program_location = matches
|
||||
.value_of("program_location")
|
||||
.map(|location| location.to_string());
|
||||
let program_location = if let Some(location) = matches.value_of("program_location") {
|
||||
Some(location.to_string())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let buffer_pubkey = if let Ok((buffer_signer, Some(buffer_pubkey))) =
|
||||
signer_of(matches, "buffer", wallet_manager)
|
||||
{
|
||||
bulk_signers.push(buffer_signer);
|
||||
Some(buffer_pubkey)
|
||||
} else if let Some(buffer_pubkey) = pubkey_of_signer(matches, "buffer", wallet_manager)?
|
||||
{
|
||||
Some(buffer_pubkey)
|
||||
} else {
|
||||
pubkey_of_signer(matches, "buffer", wallet_manager)?
|
||||
None
|
||||
};
|
||||
|
||||
let program_pubkey = if let Ok((program_signer, Some(program_pubkey))) =
|
||||
@@ -399,8 +409,12 @@ pub fn parse_program_subcommand(
|
||||
{
|
||||
bulk_signers.push(program_signer);
|
||||
Some(program_pubkey)
|
||||
} else {
|
||||
} else if let Some(program_pubkey) =
|
||||
pubkey_of_signer(matches, "program_id", wallet_manager)?
|
||||
{
|
||||
Some(program_pubkey)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let upgrade_authority_pubkey =
|
||||
@@ -449,8 +463,11 @@ pub fn parse_program_subcommand(
|
||||
{
|
||||
bulk_signers.push(buffer_signer);
|
||||
Some(buffer_pubkey)
|
||||
} else if let Some(buffer_pubkey) = pubkey_of_signer(matches, "buffer", wallet_manager)?
|
||||
{
|
||||
Some(buffer_pubkey)
|
||||
} else {
|
||||
pubkey_of_signer(matches, "buffer", wallet_manager)?
|
||||
None
|
||||
};
|
||||
|
||||
let buffer_authority_pubkey =
|
||||
@@ -617,7 +634,7 @@ pub fn parse_program_subcommand(
|
||||
}
|
||||
|
||||
pub fn process_program_subcommand(
|
||||
rpc_client: Arc<RpcClient>,
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
program_subcommand: &ProgramCliCommand,
|
||||
) -> ProcessResult {
|
||||
@@ -633,7 +650,7 @@ pub fn process_program_subcommand(
|
||||
max_len,
|
||||
allow_excessive_balance,
|
||||
} => process_program_deploy(
|
||||
rpc_client,
|
||||
&rpc_client,
|
||||
config,
|
||||
program_location,
|
||||
*program_signer_index,
|
||||
@@ -652,7 +669,7 @@ pub fn process_program_subcommand(
|
||||
buffer_authority_signer_index,
|
||||
max_len,
|
||||
} => process_write_buffer(
|
||||
rpc_client,
|
||||
&rpc_client,
|
||||
config,
|
||||
program_location,
|
||||
*buffer_signer_index,
|
||||
@@ -741,7 +758,7 @@ fn get_default_program_keypair(program_location: &Option<String>) -> Keypair {
|
||||
/// Deploy using upgradeable loader
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn process_program_deploy(
|
||||
rpc_client: Arc<RpcClient>,
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
program_location: &Option<String>,
|
||||
program_signer_index: Option<SignerIndex>,
|
||||
@@ -887,7 +904,7 @@ fn process_program_deploy(
|
||||
|
||||
let result = if do_deploy {
|
||||
do_process_program_write_and_deploy(
|
||||
rpc_client.clone(),
|
||||
rpc_client,
|
||||
config,
|
||||
&program_data,
|
||||
buffer_data_len,
|
||||
@@ -902,7 +919,7 @@ fn process_program_deploy(
|
||||
)
|
||||
} else {
|
||||
do_process_program_upgrade(
|
||||
rpc_client.clone(),
|
||||
rpc_client,
|
||||
config,
|
||||
&program_data,
|
||||
&program_pubkey,
|
||||
@@ -913,7 +930,7 @@ fn process_program_deploy(
|
||||
};
|
||||
if result.is_ok() && is_final {
|
||||
process_set_authority(
|
||||
&rpc_client,
|
||||
rpc_client,
|
||||
config,
|
||||
Some(program_pubkey),
|
||||
None,
|
||||
@@ -928,7 +945,7 @@ fn process_program_deploy(
|
||||
}
|
||||
|
||||
fn process_write_buffer(
|
||||
rpc_client: Arc<RpcClient>,
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
program_location: &str,
|
||||
buffer_signer_index: Option<SignerIndex>,
|
||||
@@ -1102,7 +1119,6 @@ fn get_buffers(
|
||||
data_slice: Some(UiDataSliceConfig { offset: 0, length }),
|
||||
..RpcAccountInfoConfig::default()
|
||||
},
|
||||
..RpcProgramAccountsConfig::default()
|
||||
},
|
||||
)?;
|
||||
Ok(results)
|
||||
@@ -1407,7 +1423,6 @@ fn process_close(
|
||||
data_slice: Some(UiDataSliceConfig { offset: 0, length }),
|
||||
..RpcAccountInfoConfig::default()
|
||||
},
|
||||
..RpcProgramAccountsConfig::default()
|
||||
},
|
||||
)?;
|
||||
|
||||
@@ -1447,7 +1462,7 @@ fn process_close(
|
||||
|
||||
/// Deploy using non-upgradeable loader
|
||||
pub fn process_deploy(
|
||||
rpc_client: Arc<RpcClient>,
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
program_location: &str,
|
||||
buffer_signer_index: Option<SignerIndex>,
|
||||
@@ -1492,7 +1507,7 @@ pub fn process_deploy(
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn do_process_program_write_and_deploy(
|
||||
rpc_client: Arc<RpcClient>,
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
program_data: &[u8],
|
||||
buffer_data_len: usize,
|
||||
@@ -1630,7 +1645,7 @@ fn do_process_program_write_and_deploy(
|
||||
messages.push(message);
|
||||
}
|
||||
|
||||
check_payer(&rpc_client, config, balance_needed, &messages)?;
|
||||
check_payer(rpc_client, config, balance_needed, &messages)?;
|
||||
|
||||
send_deploy_messages(
|
||||
rpc_client,
|
||||
@@ -1657,7 +1672,7 @@ fn do_process_program_write_and_deploy(
|
||||
}
|
||||
|
||||
fn do_process_program_upgrade(
|
||||
rpc_client: Arc<RpcClient>,
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
program_data: &[u8],
|
||||
program_id: &Pubkey,
|
||||
@@ -1753,7 +1768,7 @@ fn do_process_program_upgrade(
|
||||
);
|
||||
messages.push(&final_message);
|
||||
|
||||
check_payer(&rpc_client, config, balance_needed, &messages)?;
|
||||
check_payer(rpc_client, config, balance_needed, &messages)?;
|
||||
send_deploy_messages(
|
||||
rpc_client,
|
||||
config,
|
||||
@@ -1779,7 +1794,7 @@ fn read_and_verify_elf(program_location: &str) -> Result<Vec<u8>, Box<dyn std::e
|
||||
.map_err(|err| format!("Unable to read program file: {}", err))?;
|
||||
|
||||
// Verify the program
|
||||
<dyn Executable<BpfError, ThisInstructionMeter>>::from_elf(
|
||||
Executable::<BpfError, ThisInstructionMeter>::from_elf(
|
||||
&program_data,
|
||||
Some(|x| bpf_verifier::check(x)),
|
||||
Config::default(),
|
||||
@@ -1858,7 +1873,7 @@ fn check_payer(
|
||||
}
|
||||
|
||||
fn send_deploy_messages(
|
||||
rpc_client: Arc<RpcClient>,
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
initial_message: &Option<Message>,
|
||||
write_messages: &Option<Vec<Message>>,
|
||||
@@ -1906,8 +1921,7 @@ fn send_deploy_messages(
|
||||
}
|
||||
|
||||
send_and_confirm_transactions_with_spinner(
|
||||
rpc_client.clone(),
|
||||
&config.websocket_url,
|
||||
&rpc_client,
|
||||
write_transactions,
|
||||
&[payer_signer, write_signer],
|
||||
config.commitment,
|
||||
@@ -1976,8 +1990,7 @@ fn report_ephemeral_mnemonic(words: usize, mnemonic: bip39::Mnemonic) {
|
||||
}
|
||||
|
||||
fn send_and_confirm_transactions_with_spinner<T: Signers>(
|
||||
rpc_client: Arc<RpcClient>,
|
||||
websocket_url: &str,
|
||||
rpc_client: &RpcClient,
|
||||
mut transactions: Vec<Transaction>,
|
||||
signer_keys: &T,
|
||||
commitment: CommitmentConfig,
|
||||
@@ -1985,19 +1998,39 @@ fn send_and_confirm_transactions_with_spinner<T: Signers>(
|
||||
) -> Result<(), Box<dyn error::Error>> {
|
||||
let progress_bar = new_spinner_progress_bar();
|
||||
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();
|
||||
|
||||
progress_bar.set_message("Finding leader nodes...");
|
||||
let tpu_client = TpuClient::new(
|
||||
rpc_client.clone(),
|
||||
websocket_url,
|
||||
TpuClientConfig::default(),
|
||||
)?;
|
||||
loop {
|
||||
progress_bar.set_message("Finding leader nodes...");
|
||||
let epoch_info = rpc_client.get_epoch_info()?;
|
||||
let mut slot = epoch_info.absolute_slot;
|
||||
let mut last_epoch_fetch = Instant::now();
|
||||
if epoch_info.epoch > leader_schedule_epoch || leader_schedule.is_none() {
|
||||
leader_schedule = rpc_client.get_leader_schedule(Some(epoch_info.absolute_slot))?;
|
||||
leader_schedule_epoch = epoch_info.epoch;
|
||||
}
|
||||
|
||||
let mut tpu_addresses = get_leader_tpus(
|
||||
min(epoch_info.slot_index + 1, epoch_info.slots_in_epoch),
|
||||
NUM_TPU_LEADERS,
|
||||
leader_schedule.as_ref(),
|
||||
cluster_nodes.as_ref(),
|
||||
);
|
||||
|
||||
// Send all transactions
|
||||
let mut pending_transactions = HashMap::new();
|
||||
let num_transactions = transactions.len();
|
||||
for transaction in transactions {
|
||||
if !tpu_client.send_transaction(&transaction) {
|
||||
if !tpu_addresses.is_empty() {
|
||||
let wire_transaction =
|
||||
serialize(&transaction).expect("serialization should succeed");
|
||||
for tpu_address in &tpu_addresses {
|
||||
send_transaction_tpu(&send_socket, &tpu_address, &wire_transaction);
|
||||
}
|
||||
} else {
|
||||
let _result = rpc_client
|
||||
.send_transaction_with_config(
|
||||
&transaction,
|
||||
@@ -2017,11 +2050,22 @@ fn send_and_confirm_transactions_with_spinner<T: Signers>(
|
||||
|
||||
// Throttle transactions to about 100 TPS
|
||||
sleep(Duration::from_millis(10));
|
||||
|
||||
// Update leader periodically
|
||||
if last_epoch_fetch.elapsed() > Duration::from_millis(400) {
|
||||
let epoch_info = rpc_client.get_epoch_info()?;
|
||||
last_epoch_fetch = Instant::now();
|
||||
tpu_addresses = get_leader_tpus(
|
||||
min(epoch_info.slot_index + 1, epoch_info.slots_in_epoch),
|
||||
NUM_TPU_LEADERS,
|
||||
leader_schedule.as_ref(),
|
||||
cluster_nodes.as_ref(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Collect statuses for all the transactions, drop those that are confirmed
|
||||
loop {
|
||||
let mut slot = 0;
|
||||
let pending_signatures = pending_transactions.keys().cloned().collect::<Vec<_>>();
|
||||
for pending_signatures_chunk in
|
||||
pending_signatures.chunks(MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS)
|
||||
@@ -2063,8 +2107,22 @@ fn send_and_confirm_transactions_with_spinner<T: Signers>(
|
||||
break;
|
||||
}
|
||||
|
||||
let epoch_info = rpc_client.get_epoch_info()?;
|
||||
tpu_addresses = get_leader_tpus(
|
||||
min(epoch_info.slot_index + 1, epoch_info.slots_in_epoch),
|
||||
NUM_TPU_LEADERS,
|
||||
leader_schedule.as_ref(),
|
||||
cluster_nodes.as_ref(),
|
||||
);
|
||||
|
||||
for transaction in pending_transactions.values() {
|
||||
if !tpu_client.send_transaction(transaction) {
|
||||
if !tpu_addresses.is_empty() {
|
||||
let wire_transaction =
|
||||
serialize(&transaction).expect("serialization should succeed");
|
||||
for tpu_address in &tpu_addresses {
|
||||
send_transaction_tpu(&send_socket, &tpu_address, &wire_transaction);
|
||||
}
|
||||
} else {
|
||||
let _result = rpc_client
|
||||
.send_transaction_with_config(
|
||||
transaction,
|
||||
@@ -2131,16 +2189,19 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let keypair_file = make_tmp_path("keypair_file");
|
||||
write_keypair_file(&default_keypair, &keypair_file).unwrap();
|
||||
let default_signer = DefaultSigner::new("", &keypair_file);
|
||||
let default_signer = DefaultSigner {
|
||||
path: keypair_file.clone(),
|
||||
arg_name: "".to_string(),
|
||||
};
|
||||
|
||||
let test_command = test_commands.clone().get_matches_from(vec![
|
||||
let test_deploy = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
"program",
|
||||
"deploy",
|
||||
"/Users/test/program.so",
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_command, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_deploy, &default_signer, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Program(ProgramCliCommand::Deploy {
|
||||
program_location: Some("/Users/test/program.so".to_string()),
|
||||
@@ -2157,7 +2218,7 @@ mod tests {
|
||||
}
|
||||
);
|
||||
|
||||
let test_command = test_commands.clone().get_matches_from(vec![
|
||||
let test_deploy = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
"program",
|
||||
"deploy",
|
||||
@@ -2166,7 +2227,7 @@ mod tests {
|
||||
"42",
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_command, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_deploy, &default_signer, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Program(ProgramCliCommand::Deploy {
|
||||
program_location: Some("/Users/test/program.so".to_string()),
|
||||
@@ -2186,7 +2247,7 @@ mod tests {
|
||||
let buffer_keypair = Keypair::new();
|
||||
let buffer_keypair_file = make_tmp_path("buffer_keypair_file");
|
||||
write_keypair_file(&buffer_keypair, &buffer_keypair_file).unwrap();
|
||||
let test_command = test_commands.clone().get_matches_from(vec![
|
||||
let test_deploy = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
"program",
|
||||
"deploy",
|
||||
@@ -2194,7 +2255,7 @@ mod tests {
|
||||
&buffer_keypair_file,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_command, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_deploy, &default_signer, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Program(ProgramCliCommand::Deploy {
|
||||
program_location: None,
|
||||
@@ -2276,7 +2337,7 @@ mod tests {
|
||||
let authority_keypair = Keypair::new();
|
||||
let authority_keypair_file = make_tmp_path("authority_keypair_file");
|
||||
write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
|
||||
let test_command = test_commands.clone().get_matches_from(vec![
|
||||
let test_deploy = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
"program",
|
||||
"deploy",
|
||||
@@ -2285,7 +2346,7 @@ mod tests {
|
||||
&authority_keypair_file,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_command, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_deploy, &default_signer, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Program(ProgramCliCommand::Deploy {
|
||||
program_location: Some("/Users/test/program.so".to_string()),
|
||||
@@ -2305,7 +2366,7 @@ mod tests {
|
||||
}
|
||||
);
|
||||
|
||||
let test_command = test_commands.clone().get_matches_from(vec![
|
||||
let test_deploy = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
"program",
|
||||
"deploy",
|
||||
@@ -2313,7 +2374,7 @@ mod tests {
|
||||
"--final",
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_command, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_deploy, &default_signer, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Program(ProgramCliCommand::Deploy {
|
||||
program_location: Some("/Users/test/program.so".to_string()),
|
||||
@@ -2339,17 +2400,20 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let keypair_file = make_tmp_path("keypair_file");
|
||||
write_keypair_file(&default_keypair, &keypair_file).unwrap();
|
||||
let default_signer = DefaultSigner::new("", &keypair_file);
|
||||
let default_signer = DefaultSigner {
|
||||
path: keypair_file.clone(),
|
||||
arg_name: "".to_string(),
|
||||
};
|
||||
|
||||
// defaults
|
||||
let test_command = test_commands.clone().get_matches_from(vec![
|
||||
let test_deploy = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
"program",
|
||||
"write-buffer",
|
||||
"/Users/test/program.so",
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_command, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_deploy, &default_signer, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
|
||||
program_location: "/Users/test/program.so".to_string(),
|
||||
@@ -2363,7 +2427,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// specify max len
|
||||
let test_command = test_commands.clone().get_matches_from(vec![
|
||||
let test_deploy = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
"program",
|
||||
"write-buffer",
|
||||
@@ -2372,7 +2436,7 @@ mod tests {
|
||||
"42",
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_command, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_deploy, &default_signer, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
|
||||
program_location: "/Users/test/program.so".to_string(),
|
||||
@@ -2389,7 +2453,7 @@ mod tests {
|
||||
let buffer_keypair = Keypair::new();
|
||||
let buffer_keypair_file = make_tmp_path("buffer_keypair_file");
|
||||
write_keypair_file(&buffer_keypair, &buffer_keypair_file).unwrap();
|
||||
let test_command = test_commands.clone().get_matches_from(vec![
|
||||
let test_deploy = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
"program",
|
||||
"write-buffer",
|
||||
@@ -2398,7 +2462,7 @@ mod tests {
|
||||
&buffer_keypair_file,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_command, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_deploy, &default_signer, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
|
||||
program_location: "/Users/test/program.so".to_string(),
|
||||
@@ -2418,7 +2482,7 @@ mod tests {
|
||||
let authority_keypair = Keypair::new();
|
||||
let authority_keypair_file = make_tmp_path("authority_keypair_file");
|
||||
write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
|
||||
let test_command = test_commands.clone().get_matches_from(vec![
|
||||
let test_deploy = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
"program",
|
||||
"write-buffer",
|
||||
@@ -2427,7 +2491,7 @@ mod tests {
|
||||
&authority_keypair_file,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_command, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_deploy, &default_signer, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
|
||||
program_location: "/Users/test/program.so".to_string(),
|
||||
@@ -2450,7 +2514,7 @@ mod tests {
|
||||
let authority_keypair = Keypair::new();
|
||||
let authority_keypair_file = make_tmp_path("authority_keypair_file");
|
||||
write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
|
||||
let test_command = test_commands.clone().get_matches_from(vec![
|
||||
let test_deploy = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
"program",
|
||||
"write-buffer",
|
||||
@@ -2461,7 +2525,7 @@ mod tests {
|
||||
&authority_keypair_file,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_command, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_deploy, &default_signer, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
|
||||
program_location: "/Users/test/program.so".to_string(),
|
||||
@@ -2487,11 +2551,14 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let keypair_file = make_tmp_path("keypair_file");
|
||||
write_keypair_file(&default_keypair, &keypair_file).unwrap();
|
||||
let default_signer = DefaultSigner::new("", &keypair_file);
|
||||
let default_signer = DefaultSigner {
|
||||
path: keypair_file.clone(),
|
||||
arg_name: "".to_string(),
|
||||
};
|
||||
|
||||
let program_pubkey = Pubkey::new_unique();
|
||||
let new_authority_pubkey = Pubkey::new_unique();
|
||||
let test_command = test_commands.clone().get_matches_from(vec![
|
||||
let test_deploy = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
"program",
|
||||
"set-upgrade-authority",
|
||||
@@ -2500,7 +2567,7 @@ mod tests {
|
||||
&new_authority_pubkey.to_string(),
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_command, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_deploy, &default_signer, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthority {
|
||||
program_pubkey,
|
||||
@@ -2515,7 +2582,7 @@ mod tests {
|
||||
let new_authority_pubkey = Keypair::new();
|
||||
let new_authority_pubkey_file = make_tmp_path("authority_keypair_file");
|
||||
write_keypair_file(&new_authority_pubkey, &new_authority_pubkey_file).unwrap();
|
||||
let test_command = test_commands.clone().get_matches_from(vec![
|
||||
let test_deploy = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
"program",
|
||||
"set-upgrade-authority",
|
||||
@@ -2524,7 +2591,7 @@ mod tests {
|
||||
&new_authority_pubkey_file,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_command, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_deploy, &default_signer, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthority {
|
||||
program_pubkey,
|
||||
@@ -2539,7 +2606,7 @@ mod tests {
|
||||
let new_authority_pubkey = Keypair::new();
|
||||
let new_authority_pubkey_file = make_tmp_path("authority_keypair_file");
|
||||
write_keypair_file(&new_authority_pubkey, &new_authority_pubkey_file).unwrap();
|
||||
let test_command = test_commands.clone().get_matches_from(vec![
|
||||
let test_deploy = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
"program",
|
||||
"set-upgrade-authority",
|
||||
@@ -2547,7 +2614,7 @@ mod tests {
|
||||
"--final",
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_command, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_deploy, &default_signer, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthority {
|
||||
program_pubkey,
|
||||
@@ -2562,7 +2629,7 @@ mod tests {
|
||||
let authority = Keypair::new();
|
||||
let authority_keypair_file = make_tmp_path("authority_keypair_file");
|
||||
write_keypair_file(&authority, &authority_keypair_file).unwrap();
|
||||
let test_command = test_commands.clone().get_matches_from(vec![
|
||||
let test_deploy = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
"program",
|
||||
"set-upgrade-authority",
|
||||
@@ -2572,7 +2639,7 @@ mod tests {
|
||||
"--final",
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_command, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_deploy, &default_signer, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthority {
|
||||
program_pubkey,
|
||||
@@ -2595,11 +2662,14 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let keypair_file = make_tmp_path("keypair_file");
|
||||
write_keypair_file(&default_keypair, &keypair_file).unwrap();
|
||||
let default_signer = DefaultSigner::new("", &keypair_file);
|
||||
let default_signer = DefaultSigner {
|
||||
path: keypair_file.clone(),
|
||||
arg_name: "".to_string(),
|
||||
};
|
||||
|
||||
let buffer_pubkey = Pubkey::new_unique();
|
||||
let new_authority_pubkey = Pubkey::new_unique();
|
||||
let test_command = test_commands.clone().get_matches_from(vec![
|
||||
let test_deploy = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
"program",
|
||||
"set-buffer-authority",
|
||||
@@ -2608,7 +2678,7 @@ mod tests {
|
||||
&new_authority_pubkey.to_string(),
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_command, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_deploy, &default_signer, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Program(ProgramCliCommand::SetBufferAuthority {
|
||||
buffer_pubkey,
|
||||
@@ -2623,7 +2693,7 @@ mod tests {
|
||||
let new_authority_keypair = Keypair::new();
|
||||
let new_authority_keypair_file = make_tmp_path("authority_keypair_file");
|
||||
write_keypair_file(&new_authority_keypair, &new_authority_keypair_file).unwrap();
|
||||
let test_command = test_commands.clone().get_matches_from(vec![
|
||||
let test_deploy = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
"program",
|
||||
"set-buffer-authority",
|
||||
@@ -2632,7 +2702,7 @@ mod tests {
|
||||
&new_authority_keypair_file,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_command, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_deploy, &default_signer, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Program(ProgramCliCommand::SetBufferAuthority {
|
||||
buffer_pubkey,
|
||||
@@ -2652,7 +2722,10 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let keypair_file = make_tmp_path("keypair_file");
|
||||
write_keypair_file(&default_keypair, &keypair_file).unwrap();
|
||||
let default_signer = DefaultSigner::new("", &keypair_file);
|
||||
let default_signer = DefaultSigner {
|
||||
path: keypair_file,
|
||||
arg_name: "".to_string(),
|
||||
};
|
||||
|
||||
// defaults
|
||||
let buffer_pubkey = Pubkey::new_unique();
|
||||
@@ -2751,7 +2824,10 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let keypair_file = make_tmp_path("keypair_file");
|
||||
write_keypair_file(&default_keypair, &keypair_file).unwrap();
|
||||
let default_signer = DefaultSigner::new("", &keypair_file);
|
||||
let default_signer = DefaultSigner {
|
||||
path: keypair_file.clone(),
|
||||
arg_name: "".to_string(),
|
||||
};
|
||||
|
||||
// defaults
|
||||
let buffer_pubkey = Pubkey::new_unique();
|
||||
@@ -2869,7 +2945,7 @@ mod tests {
|
||||
write_keypair_file(&program_pubkey, &program_keypair_location).unwrap();
|
||||
|
||||
let config = CliConfig {
|
||||
rpc_client: Some(Arc::new(RpcClient::new_mock("".to_string()))),
|
||||
rpc_client: Some(RpcClient::new_mock("".to_string())),
|
||||
command: CliCommand::Program(ProgramCliCommand::Deploy {
|
||||
program_location: Some(program_location.to_str().unwrap().to_string()),
|
||||
buffer_signer_index: None,
|
||||
|
46
cli/src/send_tpu.rs
Normal file
46
cli/src/send_tpu.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use log::*;
|
||||
use solana_client::rpc_response::{RpcContactInfo, RpcLeaderSchedule};
|
||||
use solana_sdk::clock::NUM_CONSECUTIVE_LEADER_SLOTS;
|
||||
use std::net::{SocketAddr, UdpSocket};
|
||||
|
||||
pub fn get_leader_tpus(
|
||||
slot_index: u64,
|
||||
num_leaders: u64,
|
||||
leader_schedule: Option<&RpcLeaderSchedule>,
|
||||
cluster_nodes: Option<&Vec<RpcContactInfo>>,
|
||||
) -> Vec<SocketAddr> {
|
||||
let leaders: Vec<_> = (0..num_leaders)
|
||||
.filter_map(|i| {
|
||||
leader_schedule?
|
||||
.iter()
|
||||
.find(|(_pubkey, slots)| {
|
||||
slots.iter().any(|slot| {
|
||||
*slot as u64 == (slot_index + (i * NUM_CONSECUTIVE_LEADER_SLOTS))
|
||||
})
|
||||
})
|
||||
.and_then(|(pubkey, _)| {
|
||||
cluster_nodes?
|
||||
.iter()
|
||||
.find(|contact_info| contact_info.pubkey == *pubkey)
|
||||
.and_then(|contact_info| contact_info.tpu)
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
let mut unique_leaders = vec![];
|
||||
for leader in leaders.into_iter() {
|
||||
if !unique_leaders.contains(&leader) {
|
||||
unique_leaders.push(leader);
|
||||
}
|
||||
}
|
||||
unique_leaders
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
535
cli/src/stake.rs
535
cli/src/stake.rs
File diff suppressed because it is too large
Load Diff
112
cli/src/vote.rs
112
cli/src/vote.rs
@@ -4,7 +4,6 @@ use crate::{
|
||||
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
|
||||
ProcessResult,
|
||||
},
|
||||
memo::WithMemo,
|
||||
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
|
||||
};
|
||||
use clap::{value_t_or_exit, App, Arg, ArgMatches, SubCommand};
|
||||
@@ -12,7 +11,6 @@ use solana_clap_utils::{
|
||||
input_parsers::*,
|
||||
input_validators::*,
|
||||
keypair::{DefaultSigner, SignerIndex},
|
||||
memo::{memo_arg, MEMO_ARG},
|
||||
};
|
||||
use solana_cli_output::{CliEpochVotingHistory, CliLockout, CliVoteAccount};
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
@@ -81,8 +79,7 @@ impl VoteSubCommands for App<'_, '_> {
|
||||
.value_name("STRING")
|
||||
.takes_value(true)
|
||||
.help("Seed for address generation; if specified, the resulting account will be at a derived address of the VOTE ACCOUNT pubkey")
|
||||
)
|
||||
.arg(memo_arg())
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("vote-authorize-voter")
|
||||
@@ -108,8 +105,7 @@ impl VoteSubCommands for App<'_, '_> {
|
||||
.value_name("NEW_AUTHORIZED_PUBKEY")
|
||||
.required(true),
|
||||
"New authorized vote signer. "),
|
||||
)
|
||||
.arg(memo_arg())
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("vote-authorize-withdrawer")
|
||||
@@ -135,8 +131,7 @@ impl VoteSubCommands for App<'_, '_> {
|
||||
.value_name("AUTHORIZED_PUBKEY")
|
||||
.required(true),
|
||||
"New authorized withdrawer. "),
|
||||
)
|
||||
.arg(memo_arg())
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("vote-update-validator")
|
||||
@@ -166,7 +161,6 @@ impl VoteSubCommands for App<'_, '_> {
|
||||
.validator(is_valid_signer)
|
||||
.help("Authorized withdrawer keypair"),
|
||||
)
|
||||
.arg(memo_arg())
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("vote-update-commission")
|
||||
@@ -196,7 +190,6 @@ impl VoteSubCommands for App<'_, '_> {
|
||||
.validator(is_valid_signer)
|
||||
.help("Authorized withdrawer keypair"),
|
||||
)
|
||||
.arg(memo_arg())
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("vote-account")
|
||||
@@ -214,22 +207,6 @@ impl VoteSubCommands for App<'_, '_> {
|
||||
.long("lamports")
|
||||
.takes_value(false)
|
||||
.help("Display balance in lamports instead of SOL"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("with_rewards")
|
||||
.long("with-rewards")
|
||||
.takes_value(false)
|
||||
.help("Display inflation rewards"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("num_rewards_epochs")
|
||||
.long("num-rewards-epochs")
|
||||
.takes_value(true)
|
||||
.value_name("NUM")
|
||||
.validator(|s| is_within_range(s, 1, 10))
|
||||
.default_value_if("with_rewards", None, "1")
|
||||
.requires("with_rewards")
|
||||
.help("Display rewards for NUM recent epochs, max 10 [default: latest epoch only]"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
@@ -266,7 +243,6 @@ impl VoteSubCommands for App<'_, '_> {
|
||||
.validator(is_valid_signer)
|
||||
.help("Authorized withdrawer [default: cli config keypair]"),
|
||||
)
|
||||
.arg(memo_arg())
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -283,7 +259,6 @@ pub fn parse_create_vote_account(
|
||||
let commission = value_t_or_exit!(matches, "commission", u8);
|
||||
let authorized_voter = pubkey_of_signer(matches, "authorized_voter", wallet_manager)?;
|
||||
let authorized_withdrawer = pubkey_of_signer(matches, "authorized_withdrawer", wallet_manager)?;
|
||||
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
|
||||
|
||||
let payer_provided = None;
|
||||
let signer_info = default_signer.generate_unique_signers(
|
||||
@@ -300,7 +275,6 @@ pub fn parse_create_vote_account(
|
||||
authorized_voter,
|
||||
authorized_withdrawer,
|
||||
commission,
|
||||
memo,
|
||||
},
|
||||
signers: signer_info.signers,
|
||||
})
|
||||
@@ -324,14 +298,12 @@ pub fn parse_vote_authorize(
|
||||
matches,
|
||||
wallet_manager,
|
||||
)?;
|
||||
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::VoteAuthorize {
|
||||
vote_account_pubkey,
|
||||
new_authorized_pubkey,
|
||||
vote_authorize,
|
||||
memo,
|
||||
},
|
||||
signers: signer_info.signers,
|
||||
})
|
||||
@@ -355,14 +327,12 @@ pub fn parse_vote_update_validator(
|
||||
matches,
|
||||
wallet_manager,
|
||||
)?;
|
||||
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::VoteUpdateValidator {
|
||||
vote_account_pubkey,
|
||||
new_identity_account: signer_info.index_of(new_identity_pubkey).unwrap(),
|
||||
withdraw_authority: signer_info.index_of(authorized_withdrawer_pubkey).unwrap(),
|
||||
memo,
|
||||
},
|
||||
signers: signer_info.signers,
|
||||
})
|
||||
@@ -385,14 +355,12 @@ pub fn parse_vote_update_commission(
|
||||
matches,
|
||||
wallet_manager,
|
||||
)?;
|
||||
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::VoteUpdateCommission {
|
||||
vote_account_pubkey,
|
||||
commission,
|
||||
withdraw_authority: signer_info.index_of(authorized_withdrawer_pubkey).unwrap(),
|
||||
memo,
|
||||
},
|
||||
signers: signer_info.signers,
|
||||
})
|
||||
@@ -405,16 +373,10 @@ pub fn parse_vote_get_account_command(
|
||||
let vote_account_pubkey =
|
||||
pubkey_of_signer(matches, "vote_account_pubkey", wallet_manager)?.unwrap();
|
||||
let use_lamports_unit = matches.is_present("lamports");
|
||||
let with_rewards = if matches.is_present("with_rewards") {
|
||||
Some(value_of(matches, "num_rewards_epochs").unwrap())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::ShowVoteAccount {
|
||||
pubkey: vote_account_pubkey,
|
||||
use_lamports_unit,
|
||||
with_rewards,
|
||||
},
|
||||
signers: vec![],
|
||||
})
|
||||
@@ -440,7 +402,6 @@ pub fn parse_withdraw_from_vote_account(
|
||||
matches,
|
||||
wallet_manager,
|
||||
)?;
|
||||
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::WithdrawFromVoteAccount {
|
||||
@@ -448,7 +409,6 @@ pub fn parse_withdraw_from_vote_account(
|
||||
destination_account_pubkey,
|
||||
withdraw_authority: signer_info.index_of(withdraw_authority_pubkey).unwrap(),
|
||||
withdraw_amount,
|
||||
memo,
|
||||
},
|
||||
signers: signer_info.signers,
|
||||
})
|
||||
@@ -463,7 +423,6 @@ pub fn process_create_vote_account(
|
||||
authorized_voter: &Option<Pubkey>,
|
||||
authorized_withdrawer: &Option<Pubkey>,
|
||||
commission: u8,
|
||||
memo: Option<&String>,
|
||||
) -> ProcessResult {
|
||||
let vote_account = config.signers[vote_account];
|
||||
let vote_account_pubkey = vote_account.pubkey();
|
||||
@@ -506,7 +465,6 @@ pub fn process_create_vote_account(
|
||||
&vote_init,
|
||||
lamports,
|
||||
)
|
||||
.with_memo(memo)
|
||||
} else {
|
||||
vote_instruction::create_account(
|
||||
&config.signers[0].pubkey(),
|
||||
@@ -514,7 +472,6 @@ pub fn process_create_vote_account(
|
||||
&vote_init,
|
||||
lamports,
|
||||
)
|
||||
.with_memo(memo)
|
||||
};
|
||||
Message::new(&ixs, Some(&config.signers[0].pubkey()))
|
||||
};
|
||||
@@ -558,7 +515,6 @@ pub fn process_vote_authorize(
|
||||
vote_account_pubkey: &Pubkey,
|
||||
new_authorized_pubkey: &Pubkey,
|
||||
vote_authorize: VoteAuthorize,
|
||||
memo: Option<&String>,
|
||||
) -> ProcessResult {
|
||||
// If the `authorized_account` is also the fee payer, `config.signers` will only have one
|
||||
// keypair in it
|
||||
@@ -578,8 +534,7 @@ pub fn process_vote_authorize(
|
||||
&authorized.pubkey(), // current authorized
|
||||
new_authorized_pubkey, // new vote signer/withdrawer
|
||||
vote_authorize, // vote or withdraw
|
||||
)]
|
||||
.with_memo(memo);
|
||||
)];
|
||||
|
||||
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
@@ -601,7 +556,6 @@ pub fn process_vote_update_validator(
|
||||
vote_account_pubkey: &Pubkey,
|
||||
new_identity_account: SignerIndex,
|
||||
withdraw_authority: SignerIndex,
|
||||
memo: Option<&String>,
|
||||
) -> ProcessResult {
|
||||
let authorized_withdrawer = config.signers[withdraw_authority];
|
||||
let new_identity_account = config.signers[new_identity_account];
|
||||
@@ -615,8 +569,7 @@ pub fn process_vote_update_validator(
|
||||
vote_account_pubkey,
|
||||
&authorized_withdrawer.pubkey(),
|
||||
&new_identity_pubkey,
|
||||
)]
|
||||
.with_memo(memo);
|
||||
)];
|
||||
|
||||
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
@@ -638,7 +591,6 @@ pub fn process_vote_update_commission(
|
||||
vote_account_pubkey: &Pubkey,
|
||||
commission: u8,
|
||||
withdraw_authority: SignerIndex,
|
||||
memo: Option<&String>,
|
||||
) -> ProcessResult {
|
||||
let authorized_withdrawer = config.signers[withdraw_authority];
|
||||
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||
@@ -646,8 +598,7 @@ pub fn process_vote_update_commission(
|
||||
vote_account_pubkey,
|
||||
&authorized_withdrawer.pubkey(),
|
||||
commission,
|
||||
)]
|
||||
.with_memo(memo);
|
||||
)];
|
||||
|
||||
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
@@ -696,7 +647,6 @@ pub fn process_show_vote_account(
|
||||
config: &CliConfig,
|
||||
vote_account_address: &Pubkey,
|
||||
use_lamports_unit: bool,
|
||||
with_rewards: Option<usize>,
|
||||
) -> ProcessResult {
|
||||
let (vote_account, vote_state) =
|
||||
get_vote_account(rpc_client, vote_account_address, config.commitment)?;
|
||||
@@ -722,16 +672,14 @@ pub fn process_show_vote_account(
|
||||
}
|
||||
}
|
||||
|
||||
let epoch_rewards =
|
||||
with_rewards.and_then(|num_epochs| {
|
||||
match crate::stake::fetch_epoch_rewards(rpc_client, vote_account_address, num_epochs) {
|
||||
Ok(rewards) => Some(rewards),
|
||||
Err(error) => {
|
||||
eprintln!("Failed to fetch epoch rewards: {:?}", error);
|
||||
None
|
||||
}
|
||||
}
|
||||
});
|
||||
let epoch_rewards = match crate::stake::fetch_epoch_rewards(rpc_client, vote_account_address, 1)
|
||||
{
|
||||
Ok(rewards) => Some(rewards),
|
||||
Err(error) => {
|
||||
eprintln!("Failed to fetch epoch rewards: {:?}", error);
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let vote_account_data = CliVoteAccount {
|
||||
account_balance: vote_account.lamports,
|
||||
@@ -758,7 +706,6 @@ pub fn process_withdraw_from_vote_account(
|
||||
withdraw_authority: SignerIndex,
|
||||
withdraw_amount: SpendAmount,
|
||||
destination_account_pubkey: &Pubkey,
|
||||
memo: Option<&String>,
|
||||
) -> ProcessResult {
|
||||
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||
let withdraw_authority = config.signers[withdraw_authority];
|
||||
@@ -779,15 +726,14 @@ pub fn process_withdraw_from_vote_account(
|
||||
}
|
||||
};
|
||||
|
||||
let ixs = vec![withdraw(
|
||||
let ix = withdraw(
|
||||
vote_account_pubkey,
|
||||
&withdraw_authority.pubkey(),
|
||||
lamports,
|
||||
destination_account_pubkey,
|
||||
)]
|
||||
.with_memo(memo);
|
||||
);
|
||||
|
||||
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
|
||||
let message = Message::new(&[ix], Some(&config.signers[0].pubkey()));
|
||||
let mut transaction = Transaction::new_unsigned(message);
|
||||
transaction.try_sign(&config.signers, recent_blockhash)?;
|
||||
check_account_for_fee_with_commitment(
|
||||
@@ -826,7 +772,10 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let (default_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
let default_signer = DefaultSigner::new("", &default_keypair_file);
|
||||
let default_signer = DefaultSigner {
|
||||
path: default_keypair_file.clone(),
|
||||
arg_name: String::new(),
|
||||
};
|
||||
|
||||
let test_authorize_voter = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
@@ -841,8 +790,7 @@ mod tests {
|
||||
command: CliCommand::VoteAuthorize {
|
||||
vote_account_pubkey: pubkey,
|
||||
new_authorized_pubkey: pubkey2,
|
||||
vote_authorize: VoteAuthorize::Voter,
|
||||
memo: None,
|
||||
vote_authorize: VoteAuthorize::Voter
|
||||
},
|
||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
||||
}
|
||||
@@ -865,8 +813,7 @@ mod tests {
|
||||
command: CliCommand::VoteAuthorize {
|
||||
vote_account_pubkey: pubkey,
|
||||
new_authorized_pubkey: pubkey2,
|
||||
vote_authorize: VoteAuthorize::Voter,
|
||||
memo: None,
|
||||
vote_authorize: VoteAuthorize::Voter
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
@@ -900,7 +847,6 @@ mod tests {
|
||||
authorized_voter: None,
|
||||
authorized_withdrawer: None,
|
||||
commission: 10,
|
||||
memo: None,
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
@@ -930,7 +876,6 @@ mod tests {
|
||||
authorized_voter: None,
|
||||
authorized_withdrawer: None,
|
||||
commission: 100,
|
||||
memo: None,
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
@@ -963,8 +908,7 @@ mod tests {
|
||||
identity_account: 2,
|
||||
authorized_voter: Some(authed),
|
||||
authorized_withdrawer: None,
|
||||
commission: 100,
|
||||
memo: None,
|
||||
commission: 100
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
@@ -995,8 +939,7 @@ mod tests {
|
||||
identity_account: 2,
|
||||
authorized_voter: None,
|
||||
authorized_withdrawer: Some(authed),
|
||||
commission: 100,
|
||||
memo: None,
|
||||
commission: 100
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
@@ -1020,7 +963,6 @@ mod tests {
|
||||
vote_account_pubkey: pubkey,
|
||||
new_identity_account: 2,
|
||||
withdraw_authority: 1,
|
||||
memo: None,
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
@@ -1044,7 +986,6 @@ mod tests {
|
||||
vote_account_pubkey: pubkey,
|
||||
commission: 42,
|
||||
withdraw_authority: 1,
|
||||
memo: None,
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
@@ -1069,7 +1010,6 @@ mod tests {
|
||||
destination_account_pubkey: pubkey,
|
||||
withdraw_authority: 0,
|
||||
withdraw_amount: SpendAmount::Some(42_000_000_000),
|
||||
memo: None,
|
||||
},
|
||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
||||
}
|
||||
@@ -1091,7 +1031,6 @@ mod tests {
|
||||
destination_account_pubkey: pubkey,
|
||||
withdraw_authority: 0,
|
||||
withdraw_amount: SpendAmount::All,
|
||||
memo: None,
|
||||
},
|
||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
||||
}
|
||||
@@ -1118,7 +1057,6 @@ mod tests {
|
||||
destination_account_pubkey: pubkey,
|
||||
withdraw_authority: 1,
|
||||
withdraw_amount: SpendAmount::Some(42_000_000_000),
|
||||
memo: None,
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
|
@@ -22,38 +22,44 @@ use solana_sdk::{
|
||||
#[test]
|
||||
fn test_nonce() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
full_battery_tests(test_validator, None, false);
|
||||
full_battery_tests(
|
||||
TestValidator::with_no_fees(mint_keypair.pubkey()),
|
||||
mint_keypair,
|
||||
None,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nonce_with_seed() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
full_battery_tests(test_validator, Some(String::from("seed")), false);
|
||||
full_battery_tests(
|
||||
TestValidator::with_no_fees(mint_keypair.pubkey()),
|
||||
mint_keypair,
|
||||
Some(String::from("seed")),
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nonce_with_authority() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
full_battery_tests(test_validator, None, true);
|
||||
full_battery_tests(
|
||||
TestValidator::with_no_fees(mint_keypair.pubkey()),
|
||||
mint_keypair,
|
||||
None,
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
fn full_battery_tests(
|
||||
test_validator: TestValidator,
|
||||
mint_keypair: Keypair,
|
||||
seed: Option<String>,
|
||||
use_nonce_authority: bool,
|
||||
) {
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
let json_rpc_url = test_validator.rpc_url();
|
||||
@@ -65,9 +71,10 @@ fn full_battery_tests(
|
||||
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&config_payer,
|
||||
&faucet_addr,
|
||||
&config_payer.signers[0].pubkey(),
|
||||
2000,
|
||||
&config_payer,
|
||||
)
|
||||
.unwrap();
|
||||
check_recent_balance(2000, &rpc_client, &config_payer.signers[0].pubkey());
|
||||
@@ -101,7 +108,6 @@ fn full_battery_tests(
|
||||
nonce_account: 1,
|
||||
seed,
|
||||
nonce_authority: optional_authority,
|
||||
memo: None,
|
||||
amount: SpendAmount::Some(1000),
|
||||
};
|
||||
|
||||
@@ -135,7 +141,6 @@ fn full_battery_tests(
|
||||
config_payer.command = CliCommand::NewNonce {
|
||||
nonce_account,
|
||||
nonce_authority: index,
|
||||
memo: None,
|
||||
};
|
||||
process_command(&config_payer).unwrap();
|
||||
|
||||
@@ -153,7 +158,6 @@ fn full_battery_tests(
|
||||
config_payer.command = CliCommand::WithdrawFromNonceAccount {
|
||||
nonce_account,
|
||||
nonce_authority: index,
|
||||
memo: None,
|
||||
destination_account_pubkey: payee_pubkey,
|
||||
lamports: 100,
|
||||
};
|
||||
@@ -174,7 +178,6 @@ fn full_battery_tests(
|
||||
config_payer.command = CliCommand::AuthorizeNonceAccount {
|
||||
nonce_account,
|
||||
nonce_authority: index,
|
||||
memo: None,
|
||||
new_authority: new_authority.pubkey(),
|
||||
};
|
||||
process_command(&config_payer).unwrap();
|
||||
@@ -183,7 +186,6 @@ fn full_battery_tests(
|
||||
config_payer.command = CliCommand::NewNonce {
|
||||
nonce_account,
|
||||
nonce_authority: index,
|
||||
memo: None,
|
||||
};
|
||||
process_command(&config_payer).unwrap_err();
|
||||
|
||||
@@ -192,7 +194,6 @@ fn full_battery_tests(
|
||||
config_payer.command = CliCommand::NewNonce {
|
||||
nonce_account,
|
||||
nonce_authority: 1,
|
||||
memo: None,
|
||||
};
|
||||
process_command(&config_payer).unwrap();
|
||||
|
||||
@@ -200,7 +201,6 @@ fn full_battery_tests(
|
||||
config_payer.command = CliCommand::WithdrawFromNonceAccount {
|
||||
nonce_account,
|
||||
nonce_authority: 1,
|
||||
memo: None,
|
||||
destination_account_pubkey: payee_pubkey,
|
||||
lamports: 100,
|
||||
};
|
||||
@@ -214,29 +214,32 @@ fn full_battery_tests(
|
||||
fn test_create_account_with_seed() {
|
||||
solana_logger::setup();
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
|
||||
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
|
||||
|
||||
let offline_nonce_authority_signer = keypair_from_seed(&[1u8; 32]).unwrap();
|
||||
let online_nonce_creator_signer = keypair_from_seed(&[2u8; 32]).unwrap();
|
||||
let to_address = Pubkey::new(&[3u8; 32]);
|
||||
let config = CliConfig::recent_for_tests();
|
||||
|
||||
// Setup accounts
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&CliConfig::recent_for_tests(),
|
||||
&faucet_addr,
|
||||
&offline_nonce_authority_signer.pubkey(),
|
||||
42,
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&CliConfig::recent_for_tests(),
|
||||
&faucet_addr,
|
||||
&online_nonce_creator_signer.pubkey(),
|
||||
4242,
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
check_recent_balance(42, &rpc_client, &offline_nonce_authority_signer.pubkey());
|
||||
@@ -260,7 +263,6 @@ fn test_create_account_with_seed() {
|
||||
nonce_account: 0,
|
||||
seed: Some(seed),
|
||||
nonce_authority: Some(authority_pubkey),
|
||||
memo: None,
|
||||
amount: SpendAmount::Some(241),
|
||||
};
|
||||
process_command(&creator_config).unwrap();
|
||||
@@ -297,7 +299,6 @@ fn test_create_account_with_seed() {
|
||||
blockhash_query: BlockhashQuery::None(nonce_hash),
|
||||
nonce_account: Some(nonce_address),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
@@ -326,7 +327,6 @@ fn test_create_account_with_seed() {
|
||||
),
|
||||
nonce_account: Some(nonce_address),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
|
@@ -28,9 +28,8 @@ fn test_cli_program_deploy_non_upgradeable() {
|
||||
pathbuf.set_extension("so");
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -47,6 +46,8 @@ fn test_cli_program_deploy_non_upgradeable() {
|
||||
config.json_rpc_url = test_validator.rpc_url();
|
||||
config.signers = vec![&keypair];
|
||||
config.command = CliCommand::Airdrop {
|
||||
faucet_host: None,
|
||||
faucet_port: faucet_addr.port(),
|
||||
pubkey: None,
|
||||
lamports: 4 * minimum_balance_for_rent_exemption, // min balance for rent exemption for three programs + leftover for tx processing
|
||||
};
|
||||
@@ -72,7 +73,7 @@ fn test_cli_program_deploy_non_upgradeable() {
|
||||
let account0 = rpc_client.get_account(&program_id).unwrap();
|
||||
assert_eq!(account0.lamports, minimum_balance_for_rent_exemption);
|
||||
assert_eq!(account0.owner, bpf_loader::id());
|
||||
assert!(account0.executable);
|
||||
assert_eq!(account0.executable, true);
|
||||
let mut file = File::open(pathbuf.to_str().unwrap().to_string()).unwrap();
|
||||
let mut elf = Vec::new();
|
||||
file.read_to_end(&mut elf).unwrap();
|
||||
@@ -93,7 +94,7 @@ fn test_cli_program_deploy_non_upgradeable() {
|
||||
.unwrap();
|
||||
assert_eq!(account1.lamports, minimum_balance_for_rent_exemption);
|
||||
assert_eq!(account1.owner, bpf_loader::id());
|
||||
assert!(account1.executable);
|
||||
assert_eq!(account1.executable, true);
|
||||
assert_eq!(account1.data, account0.data);
|
||||
|
||||
// Attempt to redeploy to the same address
|
||||
@@ -103,6 +104,8 @@ fn test_cli_program_deploy_non_upgradeable() {
|
||||
let custom_address_keypair = Keypair::new();
|
||||
config.signers = vec![&custom_address_keypair];
|
||||
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
|
||||
};
|
||||
@@ -129,7 +132,7 @@ fn test_cli_program_deploy_non_upgradeable() {
|
||||
.unwrap();
|
||||
assert_eq!(account2.lamports, 2 * minimum_balance_for_rent_exemption);
|
||||
assert_eq!(account2.owner, bpf_loader::id());
|
||||
assert!(account2.executable);
|
||||
assert_eq!(account2.executable, true);
|
||||
assert_eq!(account2.data, account0.data);
|
||||
}
|
||||
|
||||
@@ -144,9 +147,8 @@ fn test_cli_program_deploy_no_authority() {
|
||||
pathbuf.set_extension("so");
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -169,6 +171,8 @@ fn test_cli_program_deploy_no_authority() {
|
||||
let keypair = Keypair::new();
|
||||
config.json_rpc_url = test_validator.rpc_url();
|
||||
config.command = CliCommand::Airdrop {
|
||||
faucet_host: None,
|
||||
faucet_port: faucet_addr.port(),
|
||||
pubkey: None,
|
||||
lamports: 100 * minimum_balance_for_programdata + minimum_balance_for_program,
|
||||
};
|
||||
@@ -227,9 +231,8 @@ fn test_cli_program_deploy_with_authority() {
|
||||
pathbuf.set_extension("so");
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -253,6 +256,8 @@ fn test_cli_program_deploy_with_authority() {
|
||||
config.json_rpc_url = test_validator.rpc_url();
|
||||
config.signers = vec![&keypair];
|
||||
config.command = CliCommand::Airdrop {
|
||||
faucet_host: None,
|
||||
faucet_port: faucet_addr.port(),
|
||||
pubkey: None,
|
||||
lamports: 100 * minimum_balance_for_programdata + minimum_balance_for_program,
|
||||
};
|
||||
@@ -289,7 +294,7 @@ fn test_cli_program_deploy_with_authority() {
|
||||
let program_account = rpc_client.get_account(&program_keypair.pubkey()).unwrap();
|
||||
assert_eq!(program_account.lamports, minimum_balance_for_program);
|
||||
assert_eq!(program_account.owner, bpf_loader_upgradeable::id());
|
||||
assert!(program_account.executable);
|
||||
assert_eq!(program_account.executable, true);
|
||||
let (programdata_pubkey, _) = Pubkey::find_program_address(
|
||||
&[program_keypair.pubkey().as_ref()],
|
||||
&bpf_loader_upgradeable::id(),
|
||||
@@ -300,7 +305,7 @@ fn test_cli_program_deploy_with_authority() {
|
||||
minimum_balance_for_programdata
|
||||
);
|
||||
assert_eq!(programdata_account.owner, bpf_loader_upgradeable::id());
|
||||
assert!(!programdata_account.executable);
|
||||
assert_eq!(programdata_account.executable, false);
|
||||
assert_eq!(
|
||||
programdata_account.data[UpgradeableLoaderState::programdata_data_offset().unwrap()..],
|
||||
program_data[..]
|
||||
@@ -332,7 +337,7 @@ fn test_cli_program_deploy_with_authority() {
|
||||
let program_account = rpc_client.get_account(&program_pubkey).unwrap();
|
||||
assert_eq!(program_account.lamports, minimum_balance_for_program);
|
||||
assert_eq!(program_account.owner, bpf_loader_upgradeable::id());
|
||||
assert!(program_account.executable);
|
||||
assert_eq!(program_account.executable, true);
|
||||
let (programdata_pubkey, _) =
|
||||
Pubkey::find_program_address(&[program_pubkey.as_ref()], &bpf_loader_upgradeable::id());
|
||||
let programdata_account = rpc_client.get_account(&programdata_pubkey).unwrap();
|
||||
@@ -341,7 +346,7 @@ fn test_cli_program_deploy_with_authority() {
|
||||
minimum_balance_for_programdata
|
||||
);
|
||||
assert_eq!(programdata_account.owner, bpf_loader_upgradeable::id());
|
||||
assert!(!programdata_account.executable);
|
||||
assert_eq!(programdata_account.executable, false);
|
||||
assert_eq!(
|
||||
programdata_account.data[UpgradeableLoaderState::programdata_data_offset().unwrap()..],
|
||||
program_data[..]
|
||||
@@ -364,7 +369,7 @@ fn test_cli_program_deploy_with_authority() {
|
||||
let program_account = rpc_client.get_account(&program_pubkey).unwrap();
|
||||
assert_eq!(program_account.lamports, minimum_balance_for_program);
|
||||
assert_eq!(program_account.owner, bpf_loader_upgradeable::id());
|
||||
assert!(program_account.executable);
|
||||
assert_eq!(program_account.executable, true);
|
||||
let (programdata_pubkey, _) =
|
||||
Pubkey::find_program_address(&[program_pubkey.as_ref()], &bpf_loader_upgradeable::id());
|
||||
let programdata_account = rpc_client.get_account(&programdata_pubkey).unwrap();
|
||||
@@ -373,7 +378,7 @@ fn test_cli_program_deploy_with_authority() {
|
||||
minimum_balance_for_programdata
|
||||
);
|
||||
assert_eq!(programdata_account.owner, bpf_loader_upgradeable::id());
|
||||
assert!(!programdata_account.executable);
|
||||
assert_eq!(programdata_account.executable, false);
|
||||
assert_eq!(
|
||||
programdata_account.data[UpgradeableLoaderState::programdata_data_offset().unwrap()..],
|
||||
program_data[..]
|
||||
@@ -418,7 +423,7 @@ fn test_cli_program_deploy_with_authority() {
|
||||
let program_account = rpc_client.get_account(&program_pubkey).unwrap();
|
||||
assert_eq!(program_account.lamports, minimum_balance_for_program);
|
||||
assert_eq!(program_account.owner, bpf_loader_upgradeable::id());
|
||||
assert!(program_account.executable);
|
||||
assert_eq!(program_account.executable, true);
|
||||
let (programdata_pubkey, _) =
|
||||
Pubkey::find_program_address(&[program_pubkey.as_ref()], &bpf_loader_upgradeable::id());
|
||||
let programdata_account = rpc_client.get_account(&programdata_pubkey).unwrap();
|
||||
@@ -427,7 +432,7 @@ fn test_cli_program_deploy_with_authority() {
|
||||
minimum_balance_for_programdata
|
||||
);
|
||||
assert_eq!(programdata_account.owner, bpf_loader_upgradeable::id());
|
||||
assert!(!programdata_account.executable);
|
||||
assert_eq!(programdata_account.executable, false);
|
||||
assert_eq!(
|
||||
programdata_account.data[UpgradeableLoaderState::programdata_data_offset().unwrap()..],
|
||||
program_data[..]
|
||||
@@ -555,9 +560,8 @@ fn test_cli_program_write_buffer() {
|
||||
pathbuf.set_extension("so");
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -582,6 +586,8 @@ fn test_cli_program_write_buffer() {
|
||||
config.json_rpc_url = test_validator.rpc_url();
|
||||
config.signers = vec![&keypair];
|
||||
config.command = CliCommand::Airdrop {
|
||||
faucet_host: None,
|
||||
faucet_port: faucet_addr.port(),
|
||||
pubkey: None,
|
||||
lamports: 100 * minimum_balance_for_buffer,
|
||||
};
|
||||
@@ -837,9 +843,8 @@ fn test_cli_program_set_buffer_authority() {
|
||||
pathbuf.set_extension("so");
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -859,6 +864,8 @@ fn test_cli_program_set_buffer_authority() {
|
||||
config.json_rpc_url = test_validator.rpc_url();
|
||||
config.signers = vec![&keypair];
|
||||
config.command = CliCommand::Airdrop {
|
||||
faucet_host: None,
|
||||
faucet_port: faucet_addr.port(),
|
||||
pubkey: None,
|
||||
lamports: 100 * minimum_balance_for_buffer,
|
||||
};
|
||||
@@ -950,9 +957,8 @@ fn test_cli_program_mismatch_buffer_authority() {
|
||||
pathbuf.set_extension("so");
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -972,6 +978,8 @@ fn test_cli_program_mismatch_buffer_authority() {
|
||||
config.json_rpc_url = test_validator.rpc_url();
|
||||
config.signers = vec![&keypair];
|
||||
config.command = CliCommand::Airdrop {
|
||||
faucet_host: None,
|
||||
faucet_port: faucet_addr.port(),
|
||||
pubkey: None,
|
||||
lamports: 100 * minimum_balance_for_buffer,
|
||||
};
|
||||
@@ -1039,9 +1047,8 @@ fn test_cli_program_show() {
|
||||
pathbuf.set_extension("so");
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -1064,6 +1071,8 @@ fn test_cli_program_show() {
|
||||
// Airdrop
|
||||
config.signers = vec![&keypair];
|
||||
config.command = CliCommand::Airdrop {
|
||||
faucet_host: None,
|
||||
faucet_port: faucet_addr.port(),
|
||||
pubkey: None,
|
||||
lamports: 100 * minimum_balance_for_buffer,
|
||||
};
|
||||
@@ -1219,9 +1228,8 @@ fn test_cli_program_dump() {
|
||||
pathbuf.set_extension("so");
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -1244,6 +1252,8 @@ fn test_cli_program_dump() {
|
||||
// Airdrop
|
||||
config.signers = vec![&keypair];
|
||||
config.command = CliCommand::Airdrop {
|
||||
faucet_host: None,
|
||||
faucet_port: faucet_addr.port(),
|
||||
pubkey: None,
|
||||
lamports: 100 * minimum_balance_for_buffer,
|
||||
};
|
||||
|
@@ -10,13 +10,15 @@ use solana_sdk::{
|
||||
#[test]
|
||||
fn test_cli_request_airdrop() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let mut bob_config = CliConfig::recent_for_tests();
|
||||
bob_config.json_rpc_url = test_validator.rpc_url();
|
||||
bob_config.command = CliCommand::Airdrop {
|
||||
faucet_host: None,
|
||||
faucet_port: faucet_addr.port(),
|
||||
pubkey: None,
|
||||
lamports: 50,
|
||||
};
|
||||
|
@@ -26,9 +26,8 @@ use solana_stake_program::{
|
||||
#[test]
|
||||
fn test_stake_delegation_force() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -38,8 +37,14 @@ fn test_stake_delegation_force() {
|
||||
config.json_rpc_url = test_validator.rpc_url();
|
||||
config.signers = vec![&default_signer];
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &config.signers[0].pubkey(), 100_000)
|
||||
.unwrap();
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&faucet_addr,
|
||||
&config.signers[0].pubkey(),
|
||||
100_000,
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Create vote account
|
||||
let vote_keypair = Keypair::new();
|
||||
@@ -51,7 +56,6 @@ fn test_stake_delegation_force() {
|
||||
authorized_voter: None,
|
||||
authorized_withdrawer: None,
|
||||
commission: 0,
|
||||
memo: None,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
|
||||
@@ -70,7 +74,6 @@ fn test_stake_delegation_force() {
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
from: 0,
|
||||
};
|
||||
@@ -88,7 +91,6 @@ fn test_stake_delegation_force() {
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap_err();
|
||||
@@ -104,7 +106,6 @@ fn test_stake_delegation_force() {
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -115,9 +116,8 @@ fn test_seed_stake_delegation_and_deactivation() {
|
||||
solana_logger::setup();
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -129,9 +129,10 @@ fn test_seed_stake_delegation_and_deactivation() {
|
||||
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&config_validator,
|
||||
&faucet_addr,
|
||||
&config_validator.signers[0].pubkey(),
|
||||
100_000,
|
||||
&config_validator,
|
||||
)
|
||||
.unwrap();
|
||||
check_recent_balance(100_000, &rpc_client, &config_validator.signers[0].pubkey());
|
||||
@@ -157,7 +158,6 @@ fn test_seed_stake_delegation_and_deactivation() {
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
from: 0,
|
||||
};
|
||||
@@ -174,7 +174,6 @@ fn test_seed_stake_delegation_and_deactivation() {
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config_validator).unwrap();
|
||||
@@ -188,8 +187,6 @@ fn test_seed_stake_delegation_and_deactivation() {
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
seed: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config_validator).unwrap();
|
||||
@@ -200,9 +197,8 @@ fn test_stake_delegation_and_deactivation() {
|
||||
solana_logger::setup();
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -216,9 +212,10 @@ fn test_stake_delegation_and_deactivation() {
|
||||
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&config_validator,
|
||||
&faucet_addr,
|
||||
&config_validator.signers[0].pubkey(),
|
||||
100_000,
|
||||
&config_validator,
|
||||
)
|
||||
.unwrap();
|
||||
check_recent_balance(100_000, &rpc_client, &config_validator.signers[0].pubkey());
|
||||
@@ -237,7 +234,6 @@ fn test_stake_delegation_and_deactivation() {
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
from: 0,
|
||||
};
|
||||
@@ -255,7 +251,6 @@ fn test_stake_delegation_and_deactivation() {
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config_validator).unwrap();
|
||||
@@ -269,8 +264,6 @@ fn test_stake_delegation_and_deactivation() {
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
seed: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config_validator).unwrap();
|
||||
@@ -281,9 +274,8 @@ fn test_offline_stake_delegation_and_deactivation() {
|
||||
solana_logger::setup();
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -308,18 +300,20 @@ fn test_offline_stake_delegation_and_deactivation() {
|
||||
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&config_validator,
|
||||
&faucet_addr,
|
||||
&config_validator.signers[0].pubkey(),
|
||||
100_000,
|
||||
&config_offline,
|
||||
)
|
||||
.unwrap();
|
||||
check_recent_balance(100_000, &rpc_client, &config_validator.signers[0].pubkey());
|
||||
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&config_offline,
|
||||
&faucet_addr,
|
||||
&config_offline.signers[0].pubkey(),
|
||||
100_000,
|
||||
&config_validator,
|
||||
)
|
||||
.unwrap();
|
||||
check_recent_balance(100_000, &rpc_client, &config_offline.signers[0].pubkey());
|
||||
@@ -338,7 +332,6 @@ fn test_offline_stake_delegation_and_deactivation() {
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
from: 0,
|
||||
};
|
||||
@@ -356,7 +349,6 @@ fn test_offline_stake_delegation_and_deactivation() {
|
||||
blockhash_query: BlockhashQuery::None(blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
config_offline.output_format = OutputFormat::JsonCompact;
|
||||
@@ -377,7 +369,6 @@ fn test_offline_stake_delegation_and_deactivation() {
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config_payer).unwrap();
|
||||
@@ -392,8 +383,6 @@ fn test_offline_stake_delegation_and_deactivation() {
|
||||
blockhash_query: BlockhashQuery::None(blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
seed: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
let sig_response = process_command(&config_offline).unwrap();
|
||||
@@ -411,8 +400,6 @@ fn test_offline_stake_delegation_and_deactivation() {
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
seed: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config_payer).unwrap();
|
||||
@@ -423,9 +410,8 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
||||
solana_logger::setup();
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -439,8 +425,14 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
||||
.get_minimum_balance_for_rent_exemption(NonceState::size())
|
||||
.unwrap();
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &config.signers[0].pubkey(), 100_000)
|
||||
.unwrap();
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&faucet_addr,
|
||||
&config.signers[0].pubkey(),
|
||||
100_000,
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Create stake account
|
||||
let stake_keypair = Keypair::new();
|
||||
@@ -457,7 +449,6 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
from: 0,
|
||||
};
|
||||
@@ -470,7 +461,6 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
||||
nonce_account: 1,
|
||||
seed: None,
|
||||
nonce_authority: Some(config.signers[0].pubkey()),
|
||||
memo: None,
|
||||
amount: SpendAmount::Some(minimum_nonce_balance),
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -500,7 +490,6 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
||||
),
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -527,8 +516,6 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
||||
),
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
seed: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -539,9 +526,8 @@ fn test_stake_authorize() {
|
||||
solana_logger::setup();
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -551,8 +537,14 @@ fn test_stake_authorize() {
|
||||
config.json_rpc_url = test_validator.rpc_url();
|
||||
config.signers = vec![&default_signer];
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &config.signers[0].pubkey(), 100_000)
|
||||
.unwrap();
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&faucet_addr,
|
||||
&config.signers[0].pubkey(),
|
||||
100_000,
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let offline_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
||||
let mut config_offline = CliConfig::recent_for_tests();
|
||||
@@ -565,9 +557,10 @@ fn test_stake_authorize() {
|
||||
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&config_offline,
|
||||
&faucet_addr,
|
||||
&config_offline.signers[0].pubkey(),
|
||||
100_000,
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -587,7 +580,6 @@ fn test_stake_authorize() {
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
from: 0,
|
||||
};
|
||||
@@ -605,10 +597,8 @@ fn test_stake_authorize() {
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||
@@ -636,10 +626,8 @@ fn test_stake_authorize() {
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||
@@ -662,10 +650,8 @@ fn test_stake_authorize() {
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||
@@ -688,10 +674,8 @@ fn test_stake_authorize() {
|
||||
blockhash_query: BlockhashQuery::None(blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
};
|
||||
config_offline.output_format = OutputFormat::JsonCompact;
|
||||
let sign_reply = process_command(&config_offline).unwrap();
|
||||
@@ -707,10 +691,8 @@ fn test_stake_authorize() {
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||
@@ -731,7 +713,6 @@ fn test_stake_authorize() {
|
||||
nonce_account: 1,
|
||||
seed: None,
|
||||
nonce_authority: Some(offline_authority_pubkey),
|
||||
memo: None,
|
||||
amount: SpendAmount::Some(minimum_nonce_balance),
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -758,10 +739,8 @@ fn test_stake_authorize() {
|
||||
blockhash_query: BlockhashQuery::None(nonce_hash),
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
};
|
||||
let sign_reply = process_command(&config_offline).unwrap();
|
||||
let sign_only = parse_sign_only_reply_string(&sign_reply);
|
||||
@@ -781,10 +760,8 @@ fn test_stake_authorize() {
|
||||
),
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||
@@ -812,9 +789,8 @@ fn test_stake_authorize_with_fee_payer() {
|
||||
const SIG_FEE: u64 = 42;
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), SIG_FEE);
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(mint_pubkey, SIG_FEE, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -840,13 +816,16 @@ fn test_stake_authorize_with_fee_payer() {
|
||||
config_offline.command = CliCommand::ClusterVersion;
|
||||
process_command(&config_offline).unwrap_err();
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &default_pubkey, 100_000).unwrap();
|
||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &default_pubkey, 100_000, &config)
|
||||
.unwrap();
|
||||
check_recent_balance(100_000, &rpc_client, &config.signers[0].pubkey());
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config_payer, &payer_pubkey, 100_000).unwrap();
|
||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &payer_pubkey, 100_000, &config)
|
||||
.unwrap();
|
||||
check_recent_balance(100_000, &rpc_client, &payer_pubkey);
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config_offline, &offline_pubkey, 100_000).unwrap();
|
||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 100_000, &config)
|
||||
.unwrap();
|
||||
check_recent_balance(100_000, &rpc_client, &offline_pubkey);
|
||||
|
||||
check_ready(&rpc_client);
|
||||
@@ -867,7 +846,6 @@ fn test_stake_authorize_with_fee_payer() {
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
from: 0,
|
||||
};
|
||||
@@ -885,10 +863,8 @@ fn test_stake_authorize_with_fee_payer() {
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 1,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
// `config` balance has not changed, despite submitting the TX
|
||||
@@ -907,10 +883,8 @@ fn test_stake_authorize_with_fee_payer() {
|
||||
blockhash_query: BlockhashQuery::None(blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
};
|
||||
config_offline.output_format = OutputFormat::JsonCompact;
|
||||
let sign_reply = process_command(&config_offline).unwrap();
|
||||
@@ -926,10 +900,8 @@ fn test_stake_authorize_with_fee_payer() {
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
no_wait: false,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
// `config`'s balance again has not changed
|
||||
@@ -944,9 +916,8 @@ fn test_stake_split() {
|
||||
solana_logger::setup();
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -965,11 +936,18 @@ fn test_stake_split() {
|
||||
config_offline.command = CliCommand::ClusterVersion;
|
||||
process_command(&config_offline).unwrap_err();
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &config.signers[0].pubkey(), 500_000)
|
||||
.unwrap();
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&faucet_addr,
|
||||
&config.signers[0].pubkey(),
|
||||
500_000,
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
check_recent_balance(500_000, &rpc_client, &config.signers[0].pubkey());
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config_offline, &offline_pubkey, 100_000).unwrap();
|
||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 100_000, &config)
|
||||
.unwrap();
|
||||
check_recent_balance(100_000, &rpc_client, &offline_pubkey);
|
||||
|
||||
// Create stake account, identity is authority
|
||||
@@ -991,7 +969,6 @@ fn test_stake_split() {
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
from: 0,
|
||||
};
|
||||
@@ -1012,7 +989,6 @@ fn test_stake_split() {
|
||||
nonce_account: 1,
|
||||
seed: None,
|
||||
nonce_authority: Some(offline_pubkey),
|
||||
memo: None,
|
||||
amount: SpendAmount::Some(minimum_nonce_balance),
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -1040,7 +1016,6 @@ fn test_stake_split() {
|
||||
blockhash_query: BlockhashQuery::None(nonce_hash),
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
split_stake_account: 1,
|
||||
seed: None,
|
||||
lamports: 2 * minimum_stake_balance,
|
||||
@@ -1063,7 +1038,6 @@ fn test_stake_split() {
|
||||
),
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
split_stake_account: 1,
|
||||
seed: None,
|
||||
lamports: 2 * minimum_stake_balance,
|
||||
@@ -1087,9 +1061,8 @@ fn test_stake_set_lockup() {
|
||||
solana_logger::setup();
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -1108,11 +1081,18 @@ fn test_stake_set_lockup() {
|
||||
config_offline.command = CliCommand::ClusterVersion;
|
||||
process_command(&config_offline).unwrap_err();
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &config.signers[0].pubkey(), 500_000)
|
||||
.unwrap();
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&faucet_addr,
|
||||
&config.signers[0].pubkey(),
|
||||
500_000,
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
check_recent_balance(500_000, &rpc_client, &config.signers[0].pubkey());
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config_offline, &offline_pubkey, 100_000).unwrap();
|
||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 100_000, &config)
|
||||
.unwrap();
|
||||
check_recent_balance(100_000, &rpc_client, &offline_pubkey);
|
||||
|
||||
// Create stake account, identity is authority
|
||||
@@ -1133,7 +1113,7 @@ fn test_stake_set_lockup() {
|
||||
stake_account: 1,
|
||||
seed: None,
|
||||
staker: Some(offline_pubkey),
|
||||
withdrawer: Some(config.signers[0].pubkey()),
|
||||
withdrawer: Some(offline_pubkey),
|
||||
lockup,
|
||||
amount: SpendAmount::Some(10 * minimum_stake_balance),
|
||||
sign_only: false,
|
||||
@@ -1141,7 +1121,6 @@ fn test_stake_set_lockup() {
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
from: 0,
|
||||
};
|
||||
@@ -1168,7 +1147,6 @@ fn test_stake_set_lockup() {
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -1203,7 +1181,6 @@ fn test_stake_set_lockup() {
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -1223,7 +1200,6 @@ fn test_stake_set_lockup() {
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -1255,7 +1231,6 @@ fn test_stake_set_lockup() {
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -1271,7 +1246,6 @@ fn test_stake_set_lockup() {
|
||||
nonce_account: 1,
|
||||
seed: None,
|
||||
nonce_authority: Some(offline_pubkey),
|
||||
memo: None,
|
||||
amount: SpendAmount::Some(minimum_nonce_balance),
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -1302,7 +1276,6 @@ fn test_stake_set_lockup() {
|
||||
blockhash_query: BlockhashQuery::None(nonce_hash),
|
||||
nonce_account: Some(nonce_account_pubkey),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
config_offline.output_format = OutputFormat::JsonCompact;
|
||||
@@ -1323,7 +1296,6 @@ fn test_stake_set_lockup() {
|
||||
),
|
||||
nonce_account: Some(nonce_account_pubkey),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -1346,9 +1318,8 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
solana_logger::setup();
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -1366,11 +1337,18 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
// Verify that we cannot reach the cluster
|
||||
process_command(&config_offline).unwrap_err();
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &config.signers[0].pubkey(), 200_000)
|
||||
.unwrap();
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&faucet_addr,
|
||||
&config.signers[0].pubkey(),
|
||||
200_000,
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
check_recent_balance(200_000, &rpc_client, &config.signers[0].pubkey());
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config_offline, &offline_pubkey, 100_000).unwrap();
|
||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 100_000, &config)
|
||||
.unwrap();
|
||||
check_recent_balance(100_000, &rpc_client, &offline_pubkey);
|
||||
|
||||
// Create nonce account
|
||||
@@ -1384,7 +1362,6 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
nonce_account: 1,
|
||||
seed: None,
|
||||
nonce_authority: Some(offline_pubkey),
|
||||
memo: None,
|
||||
amount: SpendAmount::Some(minimum_nonce_balance),
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -1415,7 +1392,6 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
blockhash_query: BlockhashQuery::None(nonce_hash),
|
||||
nonce_account: Some(nonce_pubkey),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
from: 0,
|
||||
};
|
||||
@@ -1441,7 +1417,6 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
),
|
||||
nonce_account: Some(nonce_pubkey),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
from: 0,
|
||||
};
|
||||
@@ -1465,7 +1440,7 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
config_offline.command = CliCommand::WithdrawStake {
|
||||
stake_account_pubkey: stake_pubkey,
|
||||
destination_account_pubkey: recipient_pubkey,
|
||||
amount: SpendAmount::Some(42),
|
||||
lamports: 42,
|
||||
withdraw_authority: 0,
|
||||
custodian: None,
|
||||
sign_only: true,
|
||||
@@ -1473,8 +1448,6 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
blockhash_query: BlockhashQuery::None(nonce_hash),
|
||||
nonce_account: Some(nonce_pubkey),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
seed: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
let sig_response = process_command(&config_offline).unwrap();
|
||||
@@ -1484,7 +1457,7 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
config.command = CliCommand::WithdrawStake {
|
||||
stake_account_pubkey: stake_pubkey,
|
||||
destination_account_pubkey: recipient_pubkey,
|
||||
amount: SpendAmount::Some(42),
|
||||
lamports: 42,
|
||||
withdraw_authority: 0,
|
||||
custodian: None,
|
||||
sign_only: false,
|
||||
@@ -1495,8 +1468,6 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
),
|
||||
nonce_account: Some(nonce_pubkey),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
seed: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -1527,7 +1498,6 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
blockhash_query: BlockhashQuery::None(nonce_hash),
|
||||
nonce_account: Some(nonce_pubkey),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
from: 0,
|
||||
};
|
||||
@@ -1551,7 +1521,6 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
),
|
||||
nonce_account: Some(nonce_pubkey),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
from: 0,
|
||||
};
|
||||
|
@@ -22,9 +22,8 @@ use solana_sdk::{
|
||||
fn test_transfer() {
|
||||
solana_logger::setup();
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -39,7 +38,8 @@ fn test_transfer() {
|
||||
let sender_pubkey = config.signers[0].pubkey();
|
||||
let recipient_pubkey = Pubkey::new(&[1u8; 32]);
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 50_000).unwrap();
|
||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &sender_pubkey, 50_000, &config)
|
||||
.unwrap();
|
||||
check_recent_balance(50_000, &rpc_client, &sender_pubkey);
|
||||
check_recent_balance(0, &rpc_client, &recipient_pubkey);
|
||||
|
||||
@@ -57,7 +57,6 @@ fn test_transfer() {
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
@@ -78,7 +77,6 @@ fn test_transfer() {
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
@@ -95,7 +93,7 @@ fn test_transfer() {
|
||||
process_command(&offline).unwrap_err();
|
||||
|
||||
let offline_pubkey = offline.signers[0].pubkey();
|
||||
request_and_confirm_airdrop(&rpc_client, &offline, &offline_pubkey, 50).unwrap();
|
||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 50, &config).unwrap();
|
||||
check_recent_balance(50, &rpc_client, &offline_pubkey);
|
||||
|
||||
// Offline transfer
|
||||
@@ -111,7 +109,6 @@ fn test_transfer() {
|
||||
blockhash_query: BlockhashQuery::None(blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
@@ -133,7 +130,6 @@ fn test_transfer() {
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
@@ -152,7 +148,6 @@ fn test_transfer() {
|
||||
nonce_account: 1,
|
||||
seed: None,
|
||||
nonce_authority: None,
|
||||
memo: None,
|
||||
amount: SpendAmount::Some(minimum_nonce_balance),
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -184,7 +179,6 @@ fn test_transfer() {
|
||||
),
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
@@ -207,7 +201,6 @@ fn test_transfer() {
|
||||
config.command = CliCommand::AuthorizeNonceAccount {
|
||||
nonce_account: nonce_account.pubkey(),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
new_authority: offline_pubkey,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -236,7 +229,6 @@ fn test_transfer() {
|
||||
blockhash_query: BlockhashQuery::None(nonce_hash),
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
@@ -260,7 +252,6 @@ fn test_transfer() {
|
||||
),
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
@@ -274,30 +265,32 @@ fn test_transfer() {
|
||||
fn test_transfer_multisession_signing() {
|
||||
solana_logger::setup();
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
|
||||
|
||||
let to_pubkey = Pubkey::new(&[1u8; 32]);
|
||||
let offline_from_signer = keypair_from_seed(&[2u8; 32]).unwrap();
|
||||
let offline_fee_payer_signer = keypair_from_seed(&[3u8; 32]).unwrap();
|
||||
let from_null_signer = NullSigner::new(&offline_from_signer.pubkey());
|
||||
let config = CliConfig::recent_for_tests();
|
||||
|
||||
// Setup accounts
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&CliConfig::recent_for_tests(),
|
||||
&faucet_addr,
|
||||
&offline_from_signer.pubkey(),
|
||||
43,
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&CliConfig::recent_for_tests(),
|
||||
&faucet_addr,
|
||||
&offline_fee_payer_signer.pubkey(),
|
||||
3,
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
check_recent_balance(43, &rpc_client, &offline_from_signer.pubkey());
|
||||
@@ -326,7 +319,6 @@ fn test_transfer_multisession_signing() {
|
||||
blockhash_query: BlockhashQuery::None(blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
@@ -357,7 +349,6 @@ fn test_transfer_multisession_signing() {
|
||||
blockhash_query: BlockhashQuery::None(blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
@@ -385,7 +376,6 @@ fn test_transfer_multisession_signing() {
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
@@ -401,9 +391,8 @@ fn test_transfer_multisession_signing() {
|
||||
fn test_transfer_all() {
|
||||
solana_logger::setup();
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -417,7 +406,8 @@ fn test_transfer_all() {
|
||||
let sender_pubkey = config.signers[0].pubkey();
|
||||
let recipient_pubkey = Pubkey::new(&[1u8; 32]);
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 50_000).unwrap();
|
||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &sender_pubkey, 50_000, &config)
|
||||
.unwrap();
|
||||
check_recent_balance(50_000, &rpc_client, &sender_pubkey);
|
||||
check_recent_balance(0, &rpc_client, &recipient_pubkey);
|
||||
|
||||
@@ -435,7 +425,6 @@ fn test_transfer_all() {
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
@@ -449,9 +438,8 @@ fn test_transfer_all() {
|
||||
fn test_transfer_unfunded_recipient() {
|
||||
solana_logger::setup();
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -465,7 +453,8 @@ fn test_transfer_unfunded_recipient() {
|
||||
let sender_pubkey = config.signers[0].pubkey();
|
||||
let recipient_pubkey = Pubkey::new(&[1u8; 32]);
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 50_000).unwrap();
|
||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &sender_pubkey, 50_000, &config)
|
||||
.unwrap();
|
||||
check_recent_balance(50_000, &rpc_client, &sender_pubkey);
|
||||
check_recent_balance(0, &rpc_client, &recipient_pubkey);
|
||||
|
||||
@@ -483,7 +472,6 @@ fn test_transfer_unfunded_recipient() {
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
@@ -497,9 +485,8 @@ fn test_transfer_unfunded_recipient() {
|
||||
fn test_transfer_with_seed() {
|
||||
solana_logger::setup();
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -521,8 +508,9 @@ fn test_transfer_with_seed() {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 1).unwrap();
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &derived_address, 50_000).unwrap();
|
||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &sender_pubkey, 1, &config).unwrap();
|
||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &derived_address, 50_000, &config)
|
||||
.unwrap();
|
||||
check_recent_balance(1, &rpc_client, &sender_pubkey);
|
||||
check_recent_balance(50_000, &rpc_client, &derived_address);
|
||||
check_recent_balance(0, &rpc_client, &recipient_pubkey);
|
||||
@@ -541,7 +529,6 @@ fn test_transfer_with_seed() {
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: Some(derived_address_seed),
|
||||
derived_address_program_id: Some(derived_address_program_id),
|
||||
|
@@ -19,9 +19,8 @@ use solana_vote_program::vote_state::{VoteAuthorize, VoteState, VoteStateVersion
|
||||
#[test]
|
||||
fn test_vote_authorize_and_withdraw() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -31,8 +30,14 @@ fn test_vote_authorize_and_withdraw() {
|
||||
config.json_rpc_url = test_validator.rpc_url();
|
||||
config.signers = vec![&default_signer];
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &config.signers[0].pubkey(), 100_000)
|
||||
.unwrap();
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&faucet_addr,
|
||||
&config.signers[0].pubkey(),
|
||||
100_000,
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Create vote account
|
||||
let vote_account_keypair = Keypair::new();
|
||||
@@ -45,7 +50,6 @@ fn test_vote_authorize_and_withdraw() {
|
||||
authorized_voter: None,
|
||||
authorized_withdrawer: Some(config.signers[0].pubkey()),
|
||||
commission: 0,
|
||||
memo: None,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let vote_account = rpc_client
|
||||
@@ -73,7 +77,6 @@ fn test_vote_authorize_and_withdraw() {
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
@@ -89,7 +92,6 @@ fn test_vote_authorize_and_withdraw() {
|
||||
vote_account_pubkey,
|
||||
new_authorized_pubkey: withdraw_authority.pubkey(),
|
||||
vote_authorize: VoteAuthorize::Withdrawer,
|
||||
memo: None,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let vote_account = rpc_client
|
||||
@@ -107,7 +109,6 @@ fn test_vote_authorize_and_withdraw() {
|
||||
withdraw_authority: 1,
|
||||
withdraw_amount: SpendAmount::Some(100),
|
||||
destination_account_pubkey: destination_account,
|
||||
memo: None,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_recent_balance(expected_balance - 100, &rpc_client, &vote_account_pubkey);
|
||||
@@ -120,7 +121,6 @@ fn test_vote_authorize_and_withdraw() {
|
||||
vote_account_pubkey,
|
||||
new_identity_account: 2,
|
||||
withdraw_authority: 1,
|
||||
memo: None,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-client"
|
||||
version = "1.7.1"
|
||||
version = "1.6.2"
|
||||
description = "Solana Client"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -24,23 +24,21 @@ semver = "0.11.0"
|
||||
serde = "1.0.122"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.56"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.7.1" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.1" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.7.1" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.1" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.1" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.7.1" }
|
||||
solana-version = { path = "../version", version = "=1.7.1" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.7.1" }
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.6.2" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.6.2" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.6.2" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.2" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.6.2" }
|
||||
solana-version = { path = "../version", version = "=1.6.2" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.6.2" }
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tungstenite = "0.10.1"
|
||||
url = "2.1.1"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_matches = "1.3.0"
|
||||
jsonrpc-http-server = "17.0.0"
|
||||
solana-logger = { path = "../logger", version = "=1.7.1" }
|
||||
solana-logger = { path = "../logger", version = "=1.6.2" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -361,7 +361,7 @@ mod tests {
|
||||
let nonce_pubkey = Pubkey::new(&[4u8; 32]);
|
||||
let rpc_nonce_account = UiAccount::encode(
|
||||
&nonce_pubkey,
|
||||
&nonce_account,
|
||||
nonce_account,
|
||||
UiAccountEncoding::Base64,
|
||||
None,
|
||||
None,
|
||||
|
@@ -1,6 +1,5 @@
|
||||
use {
|
||||
crate::rpc_request,
|
||||
solana_faucet::faucet::FaucetError,
|
||||
solana_sdk::{
|
||||
signature::SignerError, transaction::TransactionError, transport::TransportError,
|
||||
},
|
||||
@@ -24,8 +23,6 @@ pub enum ClientErrorKind {
|
||||
SigningError(#[from] SignerError),
|
||||
#[error(transparent)]
|
||||
TransactionError(#[from] TransactionError),
|
||||
#[error(transparent)]
|
||||
FaucetError(#[from] FaucetError),
|
||||
#[error("Custom: {0}")]
|
||||
Custom(String),
|
||||
}
|
||||
@@ -49,7 +46,6 @@ impl From<ClientErrorKind> for TransportError {
|
||||
ClientErrorKind::RpcError(err) => Self::Custom(format!("{:?}", err)),
|
||||
ClientErrorKind::SerdeJson(err) => Self::Custom(format!("{:?}", err)),
|
||||
ClientErrorKind::SigningError(err) => Self::Custom(format!("{:?}", err)),
|
||||
ClientErrorKind::FaucetError(err) => Self::Custom(format!("{:?}", err)),
|
||||
ClientErrorKind::Custom(err) => Self::Custom(format!("{:?}", err)),
|
||||
}
|
||||
}
|
||||
@@ -166,13 +162,4 @@ impl From<TransactionError> for ClientError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FaucetError> for ClientError {
|
||||
fn from(err: FaucetError) -> Self {
|
||||
Self {
|
||||
request: None,
|
||||
kind: err.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, ClientError>;
|
||||
|
@@ -7,25 +7,13 @@ use {
|
||||
rpc_sender::RpcSender,
|
||||
},
|
||||
log::*,
|
||||
reqwest::{
|
||||
self,
|
||||
header::{CONTENT_TYPE, RETRY_AFTER},
|
||||
StatusCode,
|
||||
},
|
||||
std::{
|
||||
sync::{
|
||||
atomic::{AtomicU64, Ordering},
|
||||
Arc,
|
||||
},
|
||||
thread::sleep,
|
||||
time::Duration,
|
||||
},
|
||||
reqwest::{self, header::CONTENT_TYPE, StatusCode},
|
||||
std::{thread::sleep, time::Duration},
|
||||
};
|
||||
|
||||
pub struct HttpSender {
|
||||
client: Arc<reqwest::blocking::Client>,
|
||||
client: reqwest::blocking::Client,
|
||||
url: String,
|
||||
request_id: AtomicU64,
|
||||
}
|
||||
|
||||
impl HttpSender {
|
||||
@@ -34,22 +22,12 @@ impl HttpSender {
|
||||
}
|
||||
|
||||
pub fn new_with_timeout(url: String, timeout: Duration) -> Self {
|
||||
// `reqwest::blocking::Client` panics if run in a tokio async context. Shuttle the
|
||||
// request to a different tokio thread to avoid this
|
||||
let client = Arc::new(
|
||||
tokio::task::block_in_place(move || {
|
||||
reqwest::blocking::Client::builder()
|
||||
.timeout(timeout)
|
||||
.build()
|
||||
})
|
||||
.expect("build rpc client"),
|
||||
);
|
||||
let client = reqwest::blocking::Client::builder()
|
||||
.timeout(timeout)
|
||||
.build()
|
||||
.expect("build rpc client");
|
||||
|
||||
Self {
|
||||
client,
|
||||
url,
|
||||
request_id: AtomicU64::new(0),
|
||||
}
|
||||
Self { client, url }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,57 +40,39 @@ struct RpcErrorObject {
|
||||
|
||||
impl RpcSender for HttpSender {
|
||||
fn send(&self, request: RpcRequest, params: serde_json::Value) -> Result<serde_json::Value> {
|
||||
let request_id = self.request_id.fetch_add(1, Ordering::Relaxed);
|
||||
let request_json = request.build_request_json(request_id, params).to_string();
|
||||
// Concurrent requests are not supported so reuse the same request id for all requests
|
||||
let request_id = 1;
|
||||
|
||||
let request_json = request.build_request_json(request_id, params);
|
||||
|
||||
let mut too_many_requests_retries = 5;
|
||||
loop {
|
||||
// `reqwest::blocking::Client` panics if run in a tokio async context. Shuttle the
|
||||
// request to a different tokio thread to avoid this
|
||||
let response = {
|
||||
let client = self.client.clone();
|
||||
let request_json = request_json.clone();
|
||||
tokio::task::block_in_place(move || {
|
||||
client
|
||||
.post(&self.url)
|
||||
.header(CONTENT_TYPE, "application/json")
|
||||
.body(request_json)
|
||||
.send()
|
||||
})
|
||||
};
|
||||
|
||||
match response {
|
||||
match self
|
||||
.client
|
||||
.post(&self.url)
|
||||
.header(CONTENT_TYPE, "application/json")
|
||||
.body(request_json.to_string())
|
||||
.send()
|
||||
{
|
||||
Ok(response) => {
|
||||
if !response.status().is_success() {
|
||||
if response.status() == StatusCode::TOO_MANY_REQUESTS
|
||||
&& too_many_requests_retries > 0
|
||||
{
|
||||
let mut duration = Duration::from_millis(500);
|
||||
if let Some(retry_after) = response.headers().get(RETRY_AFTER) {
|
||||
if let Ok(retry_after) = retry_after.to_str() {
|
||||
if let Ok(retry_after) = retry_after.parse::<u64>() {
|
||||
if retry_after < 120 {
|
||||
duration = Duration::from_secs(retry_after);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
too_many_requests_retries -= 1;
|
||||
debug!(
|
||||
"Too many requests: server responded with {:?}, {} retries left, pausing for {:?}",
|
||||
response, too_many_requests_retries, duration
|
||||
"Server responded with {:?}, {} retries left",
|
||||
response, too_many_requests_retries
|
||||
);
|
||||
|
||||
sleep(duration);
|
||||
// Sleep for 500ms to give the server a break
|
||||
sleep(Duration::from_millis(500));
|
||||
continue;
|
||||
}
|
||||
return Err(response.error_for_status().unwrap_err().into());
|
||||
}
|
||||
|
||||
let response_text = tokio::task::block_in_place(move || response.text())?;
|
||||
|
||||
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() {
|
||||
return match serde_json::from_value::<RpcErrorObject>(json["error"].clone())
|
||||
{
|
||||
@@ -162,22 +122,3 @@ impl RpcSender for HttpSender {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn http_sender_on_tokio_multi_thread() {
|
||||
let http_sender = HttpSender::new("http://localhost:1234".to_string());
|
||||
let _ = http_sender.send(RpcRequest::GetVersion, serde_json::Value::Null);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
#[should_panic(expected = "can call blocking only when running on the multi-threaded runtime")]
|
||||
async fn http_sender_ontokio_current_thread_should_panic() {
|
||||
// RpcClient::new() will panic in the tokio current-thread runtime due to `tokio::task::block_in_place()` usage, and there
|
||||
// doesn't seem to be a way to detect whether the tokio runtime is multi_thread or current_thread...
|
||||
let _ = HttpSender::new("http://localhost:1234".to_string());
|
||||
}
|
||||
}
|
||||
|
@@ -13,10 +13,8 @@ pub mod rpc_cache;
|
||||
pub mod rpc_client;
|
||||
pub mod rpc_config;
|
||||
pub mod rpc_custom_error;
|
||||
pub mod rpc_deprecated_config;
|
||||
pub mod rpc_filter;
|
||||
pub mod rpc_request;
|
||||
pub mod rpc_response;
|
||||
pub mod rpc_sender;
|
||||
pub mod thin_client;
|
||||
pub mod tpu_client;
|
||||
|
@@ -124,8 +124,6 @@ impl RpcSender for MockSender {
|
||||
}
|
||||
RpcRequest::GetTransactionCount => Value::Number(Number::from(1234)),
|
||||
RpcRequest::GetSlot => Value::Number(Number::from(0)),
|
||||
RpcRequest::GetMaxShredInsertSlot => Value::Number(Number::from(0)),
|
||||
RpcRequest::RequestAirdrop => Value::String(Signature::new(&[8; 64]).to_string()),
|
||||
RpcRequest::SendTransaction => {
|
||||
let signature = if self.url == "malicious" {
|
||||
Signature::new(&[8; 64]).to_string()
|
||||
|
@@ -48,7 +48,10 @@ pub fn get_account_with_commitment(
|
||||
.value
|
||||
.ok_or_else(|| Error::Client(format!("AccountNotFound: pubkey={}", nonce_pubkey)))
|
||||
})
|
||||
.and_then(|a| account_identity_ok(&a).map(|()| a))
|
||||
.and_then(|a| match account_identity_ok(&a) {
|
||||
Ok(()) => Ok(a),
|
||||
Err(e) => Err(e),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn account_identity_ok<T: ReadableAccount>(account: &T) -> Result<(), Error> {
|
||||
|
@@ -3,9 +3,7 @@ use {
|
||||
rpc_config::{
|
||||
RpcSignatureSubscribeConfig, RpcTransactionLogsConfig, RpcTransactionLogsFilter,
|
||||
},
|
||||
rpc_response::{
|
||||
Response as RpcResponse, RpcLogsResponse, RpcSignatureResult, SlotInfo, SlotUpdate,
|
||||
},
|
||||
rpc_response::{Response as RpcResponse, RpcLogsResponse, RpcSignatureResult, SlotInfo},
|
||||
},
|
||||
log::*,
|
||||
serde::de::DeserializeOwned,
|
||||
@@ -342,54 +340,6 @@ impl PubsubClient {
|
||||
|
||||
Ok((result, receiver))
|
||||
}
|
||||
|
||||
pub fn slot_updates_subscribe(
|
||||
url: &str,
|
||||
handler: impl Fn(SlotUpdate) + Send + 'static,
|
||||
) -> Result<PubsubClientSubscription<SlotUpdate>, PubsubClientError> {
|
||||
let url = Url::parse(url)?;
|
||||
let (socket, _response) = connect(url)?;
|
||||
|
||||
let socket = Arc::new(RwLock::new(socket));
|
||||
let exit = Arc::new(AtomicBool::new(false));
|
||||
let exit_clone = exit.clone();
|
||||
let subscription_id = PubsubClientSubscription::<SlotUpdate>::send_subscribe(
|
||||
&socket,
|
||||
json!({
|
||||
"jsonrpc":"2.0","id":1,"method":"slotsUpdatesSubscribe","params":[]
|
||||
})
|
||||
.to_string(),
|
||||
)?;
|
||||
|
||||
let t_cleanup = {
|
||||
let socket = socket.clone();
|
||||
std::thread::spawn(move || {
|
||||
loop {
|
||||
if exit_clone.load(Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
match PubsubClientSubscription::read_message(&socket) {
|
||||
Ok(message) => handler(message),
|
||||
Err(err) => {
|
||||
info!("receive error: {:?}", err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info!("websocket - exited receive loop");
|
||||
})
|
||||
};
|
||||
|
||||
Ok(PubsubClientSubscription {
|
||||
message_type: PhantomData,
|
||||
operation: "slotsUpdates",
|
||||
socket,
|
||||
subscription_id,
|
||||
t_cleanup: Some(t_cleanup),
|
||||
exit,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@@ -1,15 +1,15 @@
|
||||
#[allow(deprecated)]
|
||||
use crate::rpc_deprecated_config::{
|
||||
RpcConfirmedBlockConfig, RpcConfirmedTransactionConfig,
|
||||
RpcGetConfirmedSignaturesForAddress2Config,
|
||||
};
|
||||
use {
|
||||
crate::{
|
||||
client_error::{ClientError, ClientErrorKind, Result as ClientResult},
|
||||
http_sender::HttpSender,
|
||||
mock_sender::{MockSender, Mocks},
|
||||
rpc_config::RpcAccountInfoConfig,
|
||||
rpc_config::*,
|
||||
rpc_config::{
|
||||
RpcConfirmedBlockConfig, RpcConfirmedTransactionConfig,
|
||||
RpcGetConfirmedSignaturesForAddress2Config, RpcLargestAccountsConfig,
|
||||
RpcProgramAccountsConfig, RpcSendTransactionConfig, RpcSimulateTransactionConfig,
|
||||
RpcStakeConfig, RpcTokenAccountsFilter,
|
||||
},
|
||||
rpc_request::{RpcError, RpcRequest, RpcResponseErrorData, TokenAccountsFilter},
|
||||
rpc_response::*,
|
||||
rpc_sender::RpcSender,
|
||||
@@ -182,23 +182,6 @@ impl RpcClient {
|
||||
Ok(requested_commitment)
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
fn maybe_map_request(&self, mut request: RpcRequest) -> Result<RpcRequest, RpcError> {
|
||||
if self.get_node_version()? < semver::Version::new(1, 7, 0) {
|
||||
request = match request {
|
||||
RpcRequest::GetBlock => RpcRequest::GetConfirmedBlock,
|
||||
RpcRequest::GetBlocks => RpcRequest::GetConfirmedBlocks,
|
||||
RpcRequest::GetBlocksWithLimit => RpcRequest::GetConfirmedBlocksWithLimit,
|
||||
RpcRequest::GetSignaturesForAddress => {
|
||||
RpcRequest::GetConfirmedSignaturesForAddress2
|
||||
}
|
||||
RpcRequest::GetTransaction => RpcRequest::GetConfirmedTransaction,
|
||||
_ => request,
|
||||
};
|
||||
}
|
||||
Ok(request)
|
||||
}
|
||||
|
||||
pub fn confirm_transaction(&self, signature: &Signature) -> ClientResult<bool> {
|
||||
Ok(self
|
||||
.confirm_transaction_with_commitment(signature, self.commitment_config)?
|
||||
@@ -426,20 +409,6 @@ impl RpcClient {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_block_height(&self) -> ClientResult<u64> {
|
||||
self.get_block_height_with_commitment(self.commitment_config)
|
||||
}
|
||||
|
||||
pub fn get_block_height_with_commitment(
|
||||
&self,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> ClientResult<u64> {
|
||||
self.send(
|
||||
RpcRequest::GetBlockHeight,
|
||||
json!([self.maybe_map_commitment(commitment_config)?]),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_slot_leaders(&self, start_slot: Slot, limit: u64) -> ClientResult<Vec<Pubkey>> {
|
||||
self.send(RpcRequest::GetSlotLeaders, json!([start_slot, limit]))
|
||||
.and_then(|slot_leaders: Vec<String>| {
|
||||
@@ -458,18 +427,6 @@ impl RpcClient {
|
||||
})
|
||||
}
|
||||
|
||||
/// Get block production for the current epoch
|
||||
pub fn get_block_production(&self) -> RpcResult<RpcBlockProduction> {
|
||||
self.send(RpcRequest::GetBlockProduction, Value::Null)
|
||||
}
|
||||
|
||||
pub fn get_block_production_with_config(
|
||||
&self,
|
||||
config: RpcBlockProductionConfig,
|
||||
) -> RpcResult<RpcBlockProduction> {
|
||||
self.send(RpcRequest::GetBlockProduction, json!(config))
|
||||
}
|
||||
|
||||
pub fn get_stake_activation(
|
||||
&self,
|
||||
stake_account: Pubkey,
|
||||
@@ -479,7 +436,7 @@ impl RpcClient {
|
||||
RpcRequest::GetStakeActivation,
|
||||
json!([
|
||||
stake_account.to_string(),
|
||||
RpcEpochConfig {
|
||||
RpcStakeConfig {
|
||||
epoch,
|
||||
commitment: Some(self.commitment_config),
|
||||
}
|
||||
@@ -501,6 +458,20 @@ impl RpcClient {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn total_supply(&self) -> ClientResult<u64> {
|
||||
self.total_supply_with_commitment(self.commitment_config)
|
||||
}
|
||||
|
||||
pub fn total_supply_with_commitment(
|
||||
&self,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> ClientResult<u64> {
|
||||
self.send(
|
||||
RpcRequest::GetTotalSupply,
|
||||
json!([self.maybe_map_commitment(commitment_config)?]),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_largest_accounts_with_config(
|
||||
&self,
|
||||
config: RpcLargestAccountsConfig,
|
||||
@@ -522,17 +493,10 @@ impl RpcClient {
|
||||
&self,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> ClientResult<RpcVoteAccountStatus> {
|
||||
self.get_vote_accounts_with_config(RpcGetVoteAccountsConfig {
|
||||
commitment: Some(self.maybe_map_commitment(commitment_config)?),
|
||||
..RpcGetVoteAccountsConfig::default()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_vote_accounts_with_config(
|
||||
&self,
|
||||
config: RpcGetVoteAccountsConfig,
|
||||
) -> ClientResult<RpcVoteAccountStatus> {
|
||||
self.send(RpcRequest::GetVoteAccounts, json!([config]))
|
||||
self.send(
|
||||
RpcRequest::GetVoteAccounts,
|
||||
json!([self.maybe_map_commitment(commitment_config)?]),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn wait_for_max_stake(
|
||||
@@ -571,43 +535,10 @@ impl RpcClient {
|
||||
self.send(RpcRequest::GetClusterNodes, Value::Null)
|
||||
}
|
||||
|
||||
pub fn get_block(&self, slot: Slot) -> ClientResult<EncodedConfirmedBlock> {
|
||||
self.get_block_with_encoding(slot, UiTransactionEncoding::Json)
|
||||
}
|
||||
|
||||
pub fn get_block_with_encoding(
|
||||
&self,
|
||||
slot: Slot,
|
||||
encoding: UiTransactionEncoding,
|
||||
) -> ClientResult<EncodedConfirmedBlock> {
|
||||
self.send(
|
||||
self.maybe_map_request(RpcRequest::GetBlock)?,
|
||||
json!([slot, encoding]),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_block_with_config(
|
||||
&self,
|
||||
slot: Slot,
|
||||
config: RpcBlockConfig,
|
||||
) -> ClientResult<UiConfirmedBlock> {
|
||||
self.send(
|
||||
self.maybe_map_request(RpcRequest::GetBlock)?,
|
||||
json!([slot, config]),
|
||||
)
|
||||
}
|
||||
|
||||
#[deprecated(since = "1.7.0", note = "Please use RpcClient::get_block() instead")]
|
||||
#[allow(deprecated)]
|
||||
pub fn get_confirmed_block(&self, slot: Slot) -> ClientResult<EncodedConfirmedBlock> {
|
||||
self.get_confirmed_block_with_encoding(slot, UiTransactionEncoding::Json)
|
||||
}
|
||||
|
||||
#[deprecated(
|
||||
since = "1.7.0",
|
||||
note = "Please use RpcClient::get_block_with_encoding() instead"
|
||||
)]
|
||||
#[allow(deprecated)]
|
||||
pub fn get_confirmed_block_with_encoding(
|
||||
&self,
|
||||
slot: Slot,
|
||||
@@ -616,11 +547,6 @@ impl RpcClient {
|
||||
self.send(RpcRequest::GetConfirmedBlock, json!([slot, encoding]))
|
||||
}
|
||||
|
||||
#[deprecated(
|
||||
since = "1.7.0",
|
||||
note = "Please use RpcClient::get_block_with_config() instead"
|
||||
)]
|
||||
#[allow(deprecated)]
|
||||
pub fn get_confirmed_block_with_config(
|
||||
&self,
|
||||
slot: Slot,
|
||||
@@ -629,56 +555,6 @@ impl RpcClient {
|
||||
self.send(RpcRequest::GetConfirmedBlock, json!([slot, config]))
|
||||
}
|
||||
|
||||
pub fn get_blocks(&self, start_slot: Slot, end_slot: Option<Slot>) -> ClientResult<Vec<Slot>> {
|
||||
self.send(
|
||||
self.maybe_map_request(RpcRequest::GetBlocks)?,
|
||||
json!([start_slot, end_slot]),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_blocks_with_commitment(
|
||||
&self,
|
||||
start_slot: Slot,
|
||||
end_slot: Option<Slot>,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> ClientResult<Vec<Slot>> {
|
||||
let json = if end_slot.is_some() {
|
||||
json!([
|
||||
start_slot,
|
||||
end_slot,
|
||||
self.maybe_map_commitment(commitment_config)?
|
||||
])
|
||||
} else {
|
||||
json!([start_slot, self.maybe_map_commitment(commitment_config)?])
|
||||
};
|
||||
self.send(self.maybe_map_request(RpcRequest::GetBlocks)?, json)
|
||||
}
|
||||
|
||||
pub fn get_blocks_with_limit(&self, start_slot: Slot, limit: usize) -> ClientResult<Vec<Slot>> {
|
||||
self.send(
|
||||
self.maybe_map_request(RpcRequest::GetBlocksWithLimit)?,
|
||||
json!([start_slot, limit]),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_blocks_with_limit_and_commitment(
|
||||
&self,
|
||||
start_slot: Slot,
|
||||
limit: usize,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> ClientResult<Vec<Slot>> {
|
||||
self.send(
|
||||
self.maybe_map_request(RpcRequest::GetBlocksWithLimit)?,
|
||||
json!([
|
||||
start_slot,
|
||||
limit,
|
||||
self.maybe_map_commitment(commitment_config)?
|
||||
]),
|
||||
)
|
||||
}
|
||||
|
||||
#[deprecated(since = "1.7.0", note = "Please use RpcClient::get_blocks() instead")]
|
||||
#[allow(deprecated)]
|
||||
pub fn get_confirmed_blocks(
|
||||
&self,
|
||||
start_slot: Slot,
|
||||
@@ -690,11 +566,6 @@ impl RpcClient {
|
||||
)
|
||||
}
|
||||
|
||||
#[deprecated(
|
||||
since = "1.7.0",
|
||||
note = "Please use RpcClient::get_blocks_with_commitment() instead"
|
||||
)]
|
||||
#[allow(deprecated)]
|
||||
pub fn get_confirmed_blocks_with_commitment(
|
||||
&self,
|
||||
start_slot: Slot,
|
||||
@@ -713,11 +584,6 @@ impl RpcClient {
|
||||
self.send(RpcRequest::GetConfirmedBlocks, json)
|
||||
}
|
||||
|
||||
#[deprecated(
|
||||
since = "1.7.0",
|
||||
note = "Please use RpcClient::get_blocks_with_limit() instead"
|
||||
)]
|
||||
#[allow(deprecated)]
|
||||
pub fn get_confirmed_blocks_with_limit(
|
||||
&self,
|
||||
start_slot: Slot,
|
||||
@@ -729,11 +595,6 @@ impl RpcClient {
|
||||
)
|
||||
}
|
||||
|
||||
#[deprecated(
|
||||
since = "1.7.0",
|
||||
note = "Please use RpcClient::get_blocks_with_limit_and_commitment() instead"
|
||||
)]
|
||||
#[allow(deprecated)]
|
||||
pub fn get_confirmed_blocks_with_limit_and_commitment(
|
||||
&self,
|
||||
start_slot: Slot,
|
||||
@@ -750,41 +611,28 @@ impl RpcClient {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_signatures_for_address(
|
||||
pub fn get_confirmed_signatures_for_address(
|
||||
&self,
|
||||
address: &Pubkey,
|
||||
) -> ClientResult<Vec<RpcConfirmedTransactionStatusWithSignature>> {
|
||||
self.get_signatures_for_address_with_config(
|
||||
address,
|
||||
GetConfirmedSignaturesForAddress2Config::default(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_signatures_for_address_with_config(
|
||||
&self,
|
||||
address: &Pubkey,
|
||||
config: GetConfirmedSignaturesForAddress2Config,
|
||||
) -> ClientResult<Vec<RpcConfirmedTransactionStatusWithSignature>> {
|
||||
let config = RpcSignaturesForAddressConfig {
|
||||
before: config.before.map(|signature| signature.to_string()),
|
||||
until: config.until.map(|signature| signature.to_string()),
|
||||
limit: config.limit,
|
||||
commitment: config.commitment,
|
||||
};
|
||||
|
||||
let result: Vec<RpcConfirmedTransactionStatusWithSignature> = self.send(
|
||||
self.maybe_map_request(RpcRequest::GetSignaturesForAddress)?,
|
||||
json!([address.to_string(), config]),
|
||||
start_slot: Slot,
|
||||
end_slot: Slot,
|
||||
) -> ClientResult<Vec<Signature>> {
|
||||
let signatures_base58_str: Vec<String> = self.send(
|
||||
RpcRequest::GetConfirmedSignaturesForAddress,
|
||||
json!([address.to_string(), start_slot, end_slot]),
|
||||
)?;
|
||||
|
||||
Ok(result)
|
||||
let mut signatures = vec![];
|
||||
for signature_base58_str in signatures_base58_str {
|
||||
signatures.push(
|
||||
signature_base58_str.parse::<Signature>().map_err(|err| {
|
||||
Into::<ClientError>::into(RpcError::ParseError(err.to_string()))
|
||||
})?,
|
||||
);
|
||||
}
|
||||
Ok(signatures)
|
||||
}
|
||||
|
||||
#[deprecated(
|
||||
since = "1.7.0",
|
||||
note = "Please use RpcClient::get_signatures_for_address() instead"
|
||||
)]
|
||||
#[allow(deprecated)]
|
||||
pub fn get_confirmed_signatures_for_address2(
|
||||
&self,
|
||||
address: &Pubkey,
|
||||
@@ -795,11 +643,6 @@ impl RpcClient {
|
||||
)
|
||||
}
|
||||
|
||||
#[deprecated(
|
||||
since = "1.7.0",
|
||||
note = "Please use RpcClient::get_signatures_for_address_with_config() instead"
|
||||
)]
|
||||
#[allow(deprecated)]
|
||||
pub fn get_confirmed_signatures_for_address2_with_config(
|
||||
&self,
|
||||
address: &Pubkey,
|
||||
@@ -809,7 +652,6 @@ impl RpcClient {
|
||||
before: config.before.map(|signature| signature.to_string()),
|
||||
until: config.until.map(|signature| signature.to_string()),
|
||||
limit: config.limit,
|
||||
commitment: config.commitment,
|
||||
};
|
||||
|
||||
let result: Vec<RpcConfirmedTransactionStatusWithSignature> = self.send(
|
||||
@@ -820,33 +662,6 @@ impl RpcClient {
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn get_transaction(
|
||||
&self,
|
||||
signature: &Signature,
|
||||
encoding: UiTransactionEncoding,
|
||||
) -> ClientResult<EncodedConfirmedTransaction> {
|
||||
self.send(
|
||||
self.maybe_map_request(RpcRequest::GetTransaction)?,
|
||||
json!([signature.to_string(), encoding]),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_transaction_with_config(
|
||||
&self,
|
||||
signature: &Signature,
|
||||
config: RpcTransactionConfig,
|
||||
) -> ClientResult<EncodedConfirmedTransaction> {
|
||||
self.send(
|
||||
self.maybe_map_request(RpcRequest::GetTransaction)?,
|
||||
json!([signature.to_string(), config]),
|
||||
)
|
||||
}
|
||||
|
||||
#[deprecated(
|
||||
since = "1.7.0",
|
||||
note = "Please use RpcClient::get_transaction() instead"
|
||||
)]
|
||||
#[allow(deprecated)]
|
||||
pub fn get_confirmed_transaction(
|
||||
&self,
|
||||
signature: &Signature,
|
||||
@@ -858,11 +673,6 @@ impl RpcClient {
|
||||
)
|
||||
}
|
||||
|
||||
#[deprecated(
|
||||
since = "1.7.0",
|
||||
note = "Please use RpcClient::get_transaction_with_config() instead"
|
||||
)]
|
||||
#[allow(deprecated)]
|
||||
pub fn get_confirmed_transaction_with_config(
|
||||
&self,
|
||||
signature: &Signature,
|
||||
@@ -917,23 +727,12 @@ impl RpcClient {
|
||||
slot: Option<Slot>,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> ClientResult<Option<RpcLeaderSchedule>> {
|
||||
self.get_leader_schedule_with_config(
|
||||
slot,
|
||||
RpcLeaderScheduleConfig {
|
||||
commitment: Some(self.maybe_map_commitment(commitment_config)?),
|
||||
..RpcLeaderScheduleConfig::default()
|
||||
},
|
||||
self.send(
|
||||
RpcRequest::GetLeaderSchedule,
|
||||
json!([slot, self.maybe_map_commitment(commitment_config)?]),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_leader_schedule_with_config(
|
||||
&self,
|
||||
slot: Option<Slot>,
|
||||
config: RpcLeaderScheduleConfig,
|
||||
) -> ClientResult<Option<RpcLeaderSchedule>> {
|
||||
self.send(RpcRequest::GetLeaderSchedule, json!([slot, config]))
|
||||
}
|
||||
|
||||
pub fn get_epoch_schedule(&self) -> ClientResult<EpochSchedule> {
|
||||
self.send(RpcRequest::GetEpochSchedule, Value::Null)
|
||||
}
|
||||
@@ -964,27 +763,6 @@ impl RpcClient {
|
||||
self.send(RpcRequest::GetInflationRate, Value::Null)
|
||||
}
|
||||
|
||||
pub fn get_inflation_reward(
|
||||
&self,
|
||||
addresses: &[Pubkey],
|
||||
epoch: Option<Epoch>,
|
||||
) -> ClientResult<Vec<Option<RpcInflationReward>>> {
|
||||
let addresses: Vec<_> = addresses
|
||||
.iter()
|
||||
.map(|address| address.to_string())
|
||||
.collect();
|
||||
self.send(
|
||||
RpcRequest::GetInflationReward,
|
||||
json!([
|
||||
addresses,
|
||||
RpcEpochConfig {
|
||||
epoch,
|
||||
commitment: Some(self.commitment_config),
|
||||
}
|
||||
]),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_version(&self) -> ClientResult<RpcVersionInfo> {
|
||||
self.send(RpcRequest::GetVersion, Value::Null)
|
||||
}
|
||||
@@ -1056,7 +834,7 @@ impl RpcClient {
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> RpcResult<Option<Account>> {
|
||||
let config = RpcAccountInfoConfig {
|
||||
encoding: Some(UiAccountEncoding::Base64Zstd),
|
||||
encoding: Some(UiAccountEncoding::Base64),
|
||||
commitment: Some(self.maybe_map_commitment(commitment_config)?),
|
||||
data_slice: None,
|
||||
};
|
||||
@@ -1091,14 +869,6 @@ impl RpcClient {
|
||||
})?
|
||||
}
|
||||
|
||||
pub fn get_max_retransmit_slot(&self) -> ClientResult<Slot> {
|
||||
self.send(RpcRequest::GetMaxRetransmitSlot, Value::Null)
|
||||
}
|
||||
|
||||
pub fn get_max_shred_insert_slot(&self) -> ClientResult<Slot> {
|
||||
self.send(RpcRequest::GetMaxShredInsertSlot, Value::Null)
|
||||
}
|
||||
|
||||
pub fn get_multiple_accounts(&self, pubkeys: &[Pubkey]) -> ClientResult<Vec<Option<Account>>> {
|
||||
Ok(self
|
||||
.get_multiple_accounts_with_commitment(pubkeys, self.commitment_config)?
|
||||
@@ -1111,7 +881,7 @@ impl RpcClient {
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> RpcResult<Vec<Option<Account>>> {
|
||||
let config = RpcAccountInfoConfig {
|
||||
encoding: Some(UiAccountEncoding::Base64Zstd),
|
||||
encoding: Some(UiAccountEncoding::Base64),
|
||||
commitment: Some(self.maybe_map_commitment(commitment_config)?),
|
||||
data_slice: None,
|
||||
};
|
||||
@@ -1177,11 +947,12 @@ impl RpcClient {
|
||||
self.get_program_accounts_with_config(
|
||||
pubkey,
|
||||
RpcProgramAccountsConfig {
|
||||
filters: None,
|
||||
account_config: RpcAccountInfoConfig {
|
||||
encoding: Some(UiAccountEncoding::Base64Zstd),
|
||||
encoding: Some(UiAccountEncoding::Base64),
|
||||
commitment: Some(self.commitment_config),
|
||||
..RpcAccountInfoConfig::default()
|
||||
},
|
||||
..RpcProgramAccountsConfig::default()
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -1191,10 +962,7 @@ impl RpcClient {
|
||||
pubkey: &Pubkey,
|
||||
config: RpcProgramAccountsConfig,
|
||||
) -> ClientResult<Vec<(Pubkey, Account)>> {
|
||||
let commitment = config
|
||||
.account_config
|
||||
.commitment
|
||||
.unwrap_or_else(|| self.commitment());
|
||||
let commitment = config.account_config.commitment.unwrap_or_default();
|
||||
let commitment = self.maybe_map_commitment(commitment)?;
|
||||
let account_config = RpcAccountInfoConfig {
|
||||
commitment: Some(commitment),
|
||||
@@ -1244,7 +1012,6 @@ impl RpcClient {
|
||||
blockhash,
|
||||
fee_calculator,
|
||||
last_valid_slot,
|
||||
..
|
||||
},
|
||||
}) = self
|
||||
.send::<Response<RpcFees>>(
|
||||
@@ -1252,19 +1019,6 @@ impl RpcClient {
|
||||
json!([self.maybe_map_commitment(commitment_config)?]),
|
||||
) {
|
||||
(context, blockhash, fee_calculator, last_valid_slot)
|
||||
} else if let Ok(Response {
|
||||
context,
|
||||
value:
|
||||
DeprecatedRpcFees {
|
||||
blockhash,
|
||||
fee_calculator,
|
||||
last_valid_slot,
|
||||
},
|
||||
}) = self.send::<Response<DeprecatedRpcFees>>(
|
||||
RpcRequest::GetFees,
|
||||
json!([self.maybe_map_commitment(commitment_config)?]),
|
||||
) {
|
||||
(context, blockhash, fee_calculator, last_valid_slot)
|
||||
} else if let Ok(Response {
|
||||
context,
|
||||
value:
|
||||
@@ -1560,64 +1314,6 @@ impl RpcClient {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn request_airdrop(&self, pubkey: &Pubkey, lamports: u64) -> ClientResult<Signature> {
|
||||
self.request_airdrop_with_config(
|
||||
pubkey,
|
||||
lamports,
|
||||
RpcRequestAirdropConfig {
|
||||
commitment: Some(self.commitment_config),
|
||||
..RpcRequestAirdropConfig::default()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn request_airdrop_with_blockhash(
|
||||
&self,
|
||||
pubkey: &Pubkey,
|
||||
lamports: u64,
|
||||
recent_blockhash: &Hash,
|
||||
) -> ClientResult<Signature> {
|
||||
self.request_airdrop_with_config(
|
||||
pubkey,
|
||||
lamports,
|
||||
RpcRequestAirdropConfig {
|
||||
commitment: Some(self.commitment_config),
|
||||
recent_blockhash: Some(recent_blockhash.to_string()),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn request_airdrop_with_config(
|
||||
&self,
|
||||
pubkey: &Pubkey,
|
||||
lamports: u64,
|
||||
config: RpcRequestAirdropConfig,
|
||||
) -> ClientResult<Signature> {
|
||||
let commitment = config.commitment.unwrap_or_default();
|
||||
let commitment = self.maybe_map_commitment(commitment)?;
|
||||
let config = RpcRequestAirdropConfig {
|
||||
commitment: Some(commitment),
|
||||
..config
|
||||
};
|
||||
self.send(
|
||||
RpcRequest::RequestAirdrop,
|
||||
json!([pubkey.to_string(), lamports, config]),
|
||||
)
|
||||
.and_then(|signature: String| {
|
||||
Signature::from_str(&signature).map_err(|err| {
|
||||
ClientErrorKind::Custom(format!("signature deserialization failed: {}", err)).into()
|
||||
})
|
||||
})
|
||||
.map_err(|_| {
|
||||
RpcError::ForUser(
|
||||
"airdrop request failed. \
|
||||
This can happen when the rate limit is reached."
|
||||
.to_string(),
|
||||
)
|
||||
.into()
|
||||
})
|
||||
}
|
||||
|
||||
fn poll_balance_with_timeout_and_commitment(
|
||||
&self,
|
||||
pubkey: &Pubkey,
|
||||
@@ -1819,24 +1515,6 @@ impl RpcClient {
|
||||
commitment: CommitmentConfig,
|
||||
config: RpcSendTransactionConfig,
|
||||
) -> ClientResult<Signature> {
|
||||
let recent_blockhash = if uses_durable_nonce(transaction).is_some() {
|
||||
self.get_recent_blockhash_with_commitment(CommitmentConfig::processed())?
|
||||
.value
|
||||
.0
|
||||
} else {
|
||||
transaction.message.recent_blockhash
|
||||
};
|
||||
let signature = self.send_transaction_with_config(transaction, config)?;
|
||||
self.confirm_transaction_with_spinner(&signature, &recent_blockhash, commitment)?;
|
||||
Ok(signature)
|
||||
}
|
||||
|
||||
pub fn confirm_transaction_with_spinner(
|
||||
&self,
|
||||
signature: &Signature,
|
||||
recent_blockhash: &Hash,
|
||||
commitment: CommitmentConfig,
|
||||
) -> ClientResult<()> {
|
||||
let desired_confirmations = if commitment.is_finalized() {
|
||||
MAX_LOCKOUT_HISTORY + 1
|
||||
} else {
|
||||
@@ -1848,8 +1526,16 @@ impl RpcClient {
|
||||
|
||||
progress_bar.set_message(&format!(
|
||||
"[{}/{}] Finalizing transaction {}",
|
||||
confirmations, desired_confirmations, signature,
|
||||
confirmations, desired_confirmations, transaction.signatures[0],
|
||||
));
|
||||
let recent_blockhash = if uses_durable_nonce(transaction).is_some() {
|
||||
self.get_recent_blockhash_with_commitment(CommitmentConfig::processed())?
|
||||
.value
|
||||
.0
|
||||
} else {
|
||||
transaction.message.recent_blockhash
|
||||
};
|
||||
let signature = self.send_transaction_with_config(transaction, config)?;
|
||||
let (signature, status) = loop {
|
||||
// Get recent commitment in order to count confirmations for successful transactions
|
||||
let status = self
|
||||
@@ -1896,7 +1582,7 @@ impl RpcClient {
|
||||
{
|
||||
progress_bar.set_message("Transaction confirmed");
|
||||
progress_bar.finish_and_clear();
|
||||
return Ok(());
|
||||
return Ok(signature);
|
||||
}
|
||||
|
||||
progress_bar.set_message(&format!(
|
||||
@@ -1938,7 +1624,6 @@ pub struct GetConfirmedSignaturesForAddress2Config {
|
||||
pub before: Option<Signature>,
|
||||
pub until: Option<Signature>,
|
||||
pub limit: Option<usize>,
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
}
|
||||
|
||||
fn new_spinner_progress_bar() -> ProgressBar {
|
||||
@@ -1998,21 +1683,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_send() {
|
||||
_test_send();
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
#[should_panic(expected = "can call blocking only when running on the multi-threaded runtime")]
|
||||
async fn test_send_async_current_thread_should_panic() {
|
||||
_test_send();
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_send_async_multi_thread() {
|
||||
_test_send();
|
||||
}
|
||||
|
||||
fn _test_send() {
|
||||
let (sender, receiver) = channel();
|
||||
thread::spawn(move || {
|
||||
let rpc_addr = "0.0.0.0:0".parse().unwrap();
|
||||
@@ -2062,7 +1732,7 @@ mod tests {
|
||||
// Send erroneous parameter
|
||||
let blockhash: ClientResult<String> =
|
||||
rpc_client.send(RpcRequest::GetRecentBlockhash, json!(["parameter"]));
|
||||
assert!(blockhash.is_err());
|
||||
assert_eq!(blockhash.is_err(), true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@@ -23,80 +23,14 @@ pub struct RpcSendTransactionConfig {
|
||||
pub encoding: Option<UiTransactionEncoding>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcSimulateTransactionAccountsConfig {
|
||||
pub encoding: Option<UiAccountEncoding>,
|
||||
pub addresses: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcSimulateTransactionConfig {
|
||||
#[serde(default)]
|
||||
pub sig_verify: bool,
|
||||
#[serde(default)]
|
||||
pub replace_recent_blockhash: bool,
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
pub encoding: Option<UiTransactionEncoding>,
|
||||
pub accounts: Option<RpcSimulateTransactionAccountsConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcRequestAirdropConfig {
|
||||
pub recent_blockhash: Option<String>, // base-58 encoded blockhash
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcLeaderScheduleConfig {
|
||||
pub identity: Option<String>, // validator identity, as a base-58 encoded string
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcBlockProductionConfigRange {
|
||||
pub first_slot: Slot,
|
||||
pub last_slot: Option<Slot>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcBlockProductionConfig {
|
||||
pub identity: Option<String>, // validator identity, as a base-58 encoded string
|
||||
pub range: Option<RpcBlockProductionConfigRange>, // current epoch if `None`
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcGetVoteAccountsConfig {
|
||||
pub vote_pubkey: Option<String>, // validator vote address, as a base-58 encoded string
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum RpcLeaderScheduleConfigWrapper {
|
||||
SlotOnly(Option<Slot>),
|
||||
ConfigOnly(Option<RpcLeaderScheduleConfig>),
|
||||
}
|
||||
|
||||
impl RpcLeaderScheduleConfigWrapper {
|
||||
pub fn unzip(&self) -> (Option<Slot>, Option<RpcLeaderScheduleConfig>) {
|
||||
match &self {
|
||||
RpcLeaderScheduleConfigWrapper::SlotOnly(slot) => (*slot, None),
|
||||
RpcLeaderScheduleConfigWrapper::ConfigOnly(config) => (None, config.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
@@ -116,7 +50,7 @@ pub struct RpcLargestAccountsConfig {
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcEpochConfig {
|
||||
pub struct RpcStakeConfig {
|
||||
pub epoch: Option<Epoch>,
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
@@ -137,7 +71,6 @@ pub struct RpcProgramAccountsConfig {
|
||||
pub filters: Option<Vec<RpcFilterType>>,
|
||||
#[serde(flatten)]
|
||||
pub account_config: RpcAccountInfoConfig,
|
||||
pub with_context: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
@@ -172,12 +105,10 @@ pub struct RpcSignatureSubscribeConfig {
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcSignaturesForAddressConfig {
|
||||
pub struct RpcGetConfirmedSignaturesForAddress2Config {
|
||||
pub before: Option<String>, // Signature as base-58 string
|
||||
pub until: Option<String>, // Signature as base-58 string
|
||||
pub limit: Option<usize>,
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
@@ -194,17 +125,6 @@ impl<T: EncodingConfig + Default + Copy> RpcEncodingConfigWrapper<T> {
|
||||
RpcEncodingConfigWrapper::Current(config) => config.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert<U: EncodingConfig + From<T>>(&self) -> RpcEncodingConfigWrapper<U> {
|
||||
match self {
|
||||
RpcEncodingConfigWrapper::Deprecated(encoding) => {
|
||||
RpcEncodingConfigWrapper::Deprecated(*encoding)
|
||||
}
|
||||
RpcEncodingConfigWrapper::Current(config) => {
|
||||
RpcEncodingConfigWrapper::Current(config.map(|config| config.into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait EncodingConfig {
|
||||
@@ -213,7 +133,7 @@ pub trait EncodingConfig {
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcBlockConfig {
|
||||
pub struct RpcConfirmedBlockConfig {
|
||||
pub encoding: Option<UiTransactionEncoding>,
|
||||
pub transaction_details: Option<TransactionDetails>,
|
||||
pub rewards: Option<bool>,
|
||||
@@ -221,7 +141,7 @@ pub struct RpcBlockConfig {
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
}
|
||||
|
||||
impl EncodingConfig for RpcBlockConfig {
|
||||
impl EncodingConfig for RpcConfirmedBlockConfig {
|
||||
fn new_with_encoding(encoding: &Option<UiTransactionEncoding>) -> Self {
|
||||
Self {
|
||||
encoding: *encoding,
|
||||
@@ -230,38 +150,24 @@ impl EncodingConfig for RpcBlockConfig {
|
||||
}
|
||||
}
|
||||
|
||||
impl RpcBlockConfig {
|
||||
impl RpcConfirmedBlockConfig {
|
||||
pub fn rewards_only() -> Self {
|
||||
Self {
|
||||
transaction_details: Some(TransactionDetails::None),
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rewards_with_commitment(commitment: Option<CommitmentConfig>) -> Self {
|
||||
Self {
|
||||
transaction_details: Some(TransactionDetails::None),
|
||||
commitment,
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RpcBlockConfig> for RpcEncodingConfigWrapper<RpcBlockConfig> {
|
||||
fn from(config: RpcBlockConfig) -> Self {
|
||||
RpcEncodingConfigWrapper::Current(Some(config))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcTransactionConfig {
|
||||
pub struct RpcConfirmedTransactionConfig {
|
||||
pub encoding: Option<UiTransactionEncoding>,
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
}
|
||||
|
||||
impl EncodingConfig for RpcTransactionConfig {
|
||||
impl EncodingConfig for RpcConfirmedTransactionConfig {
|
||||
fn new_with_encoding(encoding: &Option<UiTransactionEncoding>) -> Self {
|
||||
Self {
|
||||
encoding: *encoding,
|
||||
@@ -272,16 +178,16 @@ impl EncodingConfig for RpcTransactionConfig {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum RpcBlocksConfigWrapper {
|
||||
pub enum RpcConfirmedBlocksConfigWrapper {
|
||||
EndSlotOnly(Option<Slot>),
|
||||
CommitmentOnly(Option<CommitmentConfig>),
|
||||
}
|
||||
|
||||
impl RpcBlocksConfigWrapper {
|
||||
impl RpcConfirmedBlocksConfigWrapper {
|
||||
pub fn unzip(&self) -> (Option<Slot>, Option<CommitmentConfig>) {
|
||||
match &self {
|
||||
RpcBlocksConfigWrapper::EndSlotOnly(end_slot) => (*end_slot, None),
|
||||
RpcBlocksConfigWrapper::CommitmentOnly(commitment) => (None, *commitment),
|
||||
RpcConfirmedBlocksConfigWrapper::EndSlotOnly(end_slot) => (*end_slot, None),
|
||||
RpcConfirmedBlocksConfigWrapper::CommitmentOnly(commitment) => (None, *commitment),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -15,8 +15,6 @@ pub const JSON_RPC_SERVER_ERROR_TRANSACTION_PRECOMPILE_VERIFICATION_FAILURE: i64
|
||||
pub const JSON_RPC_SERVER_ERROR_SLOT_SKIPPED: i64 = -32007;
|
||||
pub const JSON_RPC_SERVER_ERROR_NO_SNAPSHOT: i64 = -32008;
|
||||
pub const JSON_RPC_SERVER_ERROR_LONG_TERM_STORAGE_SLOT_SKIPPED: i64 = -32009;
|
||||
pub const JSON_RPC_SERVER_ERROR_KEY_EXCLUDED_FROM_SECONDARY_INDEX: i64 = -32010;
|
||||
pub const JSON_RPC_SERVER_ERROR_TRANSACTION_HISTORY_NOT_AVAILABLE: i64 = -32011;
|
||||
|
||||
pub enum RpcCustomError {
|
||||
BlockCleanedUp {
|
||||
@@ -42,10 +40,6 @@ pub enum RpcCustomError {
|
||||
LongTermStorageSlotSkipped {
|
||||
slot: Slot,
|
||||
},
|
||||
KeyExcludedFromSecondaryIndex {
|
||||
index_key: String,
|
||||
},
|
||||
TransactionHistoryNotAvailable,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
@@ -123,24 +117,6 @@ impl From<RpcCustomError> for Error {
|
||||
message: format!("Slot {} was skipped, or missing in long-term storage", slot),
|
||||
data: None,
|
||||
},
|
||||
RpcCustomError::KeyExcludedFromSecondaryIndex { index_key } => Self {
|
||||
code: ErrorCode::ServerError(
|
||||
JSON_RPC_SERVER_ERROR_KEY_EXCLUDED_FROM_SECONDARY_INDEX,
|
||||
),
|
||||
message: format!(
|
||||
"{} excluded from account secondary indexes; \
|
||||
this RPC method unavailable for key",
|
||||
index_key
|
||||
),
|
||||
data: None,
|
||||
},
|
||||
RpcCustomError::TransactionHistoryNotAvailable => Self {
|
||||
code: ErrorCode::ServerError(
|
||||
JSON_RPC_SERVER_ERROR_TRANSACTION_HISTORY_NOT_AVAILABLE,
|
||||
),
|
||||
message: "Transaction history is not available from this node".to_string(),
|
||||
data: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,120 +0,0 @@
|
||||
#![allow(deprecated)]
|
||||
use {
|
||||
crate::rpc_config::{
|
||||
EncodingConfig, RpcBlockConfig, RpcEncodingConfigWrapper, RpcTransactionConfig,
|
||||
},
|
||||
solana_sdk::{clock::Slot, commitment_config::CommitmentConfig},
|
||||
solana_transaction_status::{TransactionDetails, UiTransactionEncoding},
|
||||
};
|
||||
|
||||
#[deprecated(
|
||||
since = "1.7.0",
|
||||
note = "Please use RpcSignaturesForAddressConfig instead"
|
||||
)]
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcGetConfirmedSignaturesForAddress2Config {
|
||||
pub before: Option<String>, // Signature as base-58 string
|
||||
pub until: Option<String>, // Signature as base-58 string
|
||||
pub limit: Option<usize>,
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
}
|
||||
|
||||
#[deprecated(since = "1.7.0", note = "Please use RpcBlockConfig instead")]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcConfirmedBlockConfig {
|
||||
pub encoding: Option<UiTransactionEncoding>,
|
||||
pub transaction_details: Option<TransactionDetails>,
|
||||
pub rewards: Option<bool>,
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
}
|
||||
|
||||
impl EncodingConfig for RpcConfirmedBlockConfig {
|
||||
fn new_with_encoding(encoding: &Option<UiTransactionEncoding>) -> Self {
|
||||
Self {
|
||||
encoding: *encoding,
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RpcConfirmedBlockConfig {
|
||||
pub fn rewards_only() -> Self {
|
||||
Self {
|
||||
transaction_details: Some(TransactionDetails::None),
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rewards_with_commitment(commitment: Option<CommitmentConfig>) -> Self {
|
||||
Self {
|
||||
transaction_details: Some(TransactionDetails::None),
|
||||
commitment,
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RpcConfirmedBlockConfig> for RpcEncodingConfigWrapper<RpcConfirmedBlockConfig> {
|
||||
fn from(config: RpcConfirmedBlockConfig) -> Self {
|
||||
RpcEncodingConfigWrapper::Current(Some(config))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RpcConfirmedBlockConfig> for RpcBlockConfig {
|
||||
fn from(config: RpcConfirmedBlockConfig) -> Self {
|
||||
Self {
|
||||
encoding: config.encoding,
|
||||
transaction_details: config.transaction_details,
|
||||
rewards: config.rewards,
|
||||
commitment: config.commitment,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[deprecated(since = "1.7.0", note = "Please use RpcTransactionConfig instead")]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcConfirmedTransactionConfig {
|
||||
pub encoding: Option<UiTransactionEncoding>,
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
}
|
||||
|
||||
impl EncodingConfig for RpcConfirmedTransactionConfig {
|
||||
fn new_with_encoding(encoding: &Option<UiTransactionEncoding>) -> Self {
|
||||
Self {
|
||||
encoding: *encoding,
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RpcConfirmedTransactionConfig> for RpcTransactionConfig {
|
||||
fn from(config: RpcConfirmedTransactionConfig) -> Self {
|
||||
Self {
|
||||
encoding: config.encoding,
|
||||
commitment: config.commitment,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[deprecated(since = "1.7.0", note = "Please use RpcBlocksConfigWrapper instead")]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum RpcConfirmedBlocksConfigWrapper {
|
||||
EndSlotOnly(Option<Slot>),
|
||||
CommitmentOnly(Option<CommitmentConfig>),
|
||||
}
|
||||
|
||||
impl RpcConfirmedBlocksConfigWrapper {
|
||||
pub fn unzip(&self) -> (Option<Slot>, Option<CommitmentConfig>) {
|
||||
match &self {
|
||||
RpcConfirmedBlocksConfigWrapper::EndSlotOnly(end_slot) => (*end_slot, None),
|
||||
RpcConfirmedBlocksConfigWrapper::CommitmentOnly(commitment) => (None, *commitment),
|
||||
}
|
||||
}
|
||||
}
|
@@ -11,34 +11,14 @@ pub enum RpcRequest {
|
||||
DeregisterNode,
|
||||
GetAccountInfo,
|
||||
GetBalance,
|
||||
GetBlock,
|
||||
GetBlockHeight,
|
||||
GetBlockProduction,
|
||||
GetBlocks,
|
||||
GetBlocksWithLimit,
|
||||
GetBlockTime,
|
||||
GetClusterNodes,
|
||||
|
||||
#[deprecated(since = "1.7.0", note = "Please use RpcRequest::GetBlock instead")]
|
||||
GetConfirmedBlock,
|
||||
#[deprecated(since = "1.7.0", note = "Please use RpcRequest::GetBlocks instead")]
|
||||
GetConfirmedBlocks,
|
||||
#[deprecated(
|
||||
since = "1.7.0",
|
||||
note = "Please use RpcRequest::GetBlocksWithLimit instead"
|
||||
)]
|
||||
GetConfirmedBlocksWithLimit,
|
||||
#[deprecated(
|
||||
since = "1.7.0",
|
||||
note = "Please use RpcRequest::GetSignaturesForAddress instead"
|
||||
)]
|
||||
GetConfirmedSignaturesForAddress,
|
||||
GetConfirmedSignaturesForAddress2,
|
||||
#[deprecated(
|
||||
since = "1.7.0",
|
||||
note = "Please use RpcRequest::GetTransaction instead"
|
||||
)]
|
||||
GetConfirmedTransaction,
|
||||
|
||||
GetEpochInfo,
|
||||
GetEpochSchedule,
|
||||
GetFeeCalculatorForBlockhash,
|
||||
@@ -50,18 +30,14 @@ pub enum RpcRequest {
|
||||
GetIdentity,
|
||||
GetInflationGovernor,
|
||||
GetInflationRate,
|
||||
GetInflationReward,
|
||||
GetLargestAccounts,
|
||||
GetLeaderSchedule,
|
||||
GetMaxRetransmitSlot,
|
||||
GetMaxShredInsertSlot,
|
||||
GetMinimumBalanceForRentExemption,
|
||||
GetMultipleAccounts,
|
||||
GetProgramAccounts,
|
||||
GetRecentBlockhash,
|
||||
GetRecentPerformanceSamples,
|
||||
GetSnapshotSlot,
|
||||
GetSignaturesForAddress,
|
||||
GetSignatureStatuses,
|
||||
GetSlot,
|
||||
GetSlotLeader,
|
||||
@@ -76,7 +52,7 @@ pub enum RpcRequest {
|
||||
GetTokenAccountsByDelegate,
|
||||
GetTokenAccountsByOwner,
|
||||
GetTokenSupply,
|
||||
GetTransaction,
|
||||
GetTotalSupply,
|
||||
GetTransactionCount,
|
||||
GetVersion,
|
||||
GetVoteAccounts,
|
||||
@@ -88,23 +64,18 @@ pub enum RpcRequest {
|
||||
SignVote,
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl fmt::Display for RpcRequest {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let method = match self {
|
||||
RpcRequest::DeregisterNode => "deregisterNode",
|
||||
RpcRequest::GetAccountInfo => "getAccountInfo",
|
||||
RpcRequest::GetBalance => "getBalance",
|
||||
RpcRequest::GetBlock => "getBlock",
|
||||
RpcRequest::GetBlockHeight => "getBlockHeight",
|
||||
RpcRequest::GetBlockProduction => "getBlockProduction",
|
||||
RpcRequest::GetBlocks => "getBlocks",
|
||||
RpcRequest::GetBlocksWithLimit => "getBlocksWithLimit",
|
||||
RpcRequest::GetBlockTime => "getBlockTime",
|
||||
RpcRequest::GetClusterNodes => "getClusterNodes",
|
||||
RpcRequest::GetConfirmedBlock => "getConfirmedBlock",
|
||||
RpcRequest::GetConfirmedBlocks => "getConfirmedBlocks",
|
||||
RpcRequest::GetConfirmedBlocksWithLimit => "getConfirmedBlocksWithLimit",
|
||||
RpcRequest::GetConfirmedSignaturesForAddress => "getConfirmedSignaturesForAddress",
|
||||
RpcRequest::GetConfirmedSignaturesForAddress2 => "getConfirmedSignaturesForAddress2",
|
||||
RpcRequest::GetConfirmedTransaction => "getConfirmedTransaction",
|
||||
RpcRequest::GetEpochInfo => "getEpochInfo",
|
||||
@@ -118,18 +89,14 @@ impl fmt::Display for RpcRequest {
|
||||
RpcRequest::GetIdentity => "getIdentity",
|
||||
RpcRequest::GetInflationGovernor => "getInflationGovernor",
|
||||
RpcRequest::GetInflationRate => "getInflationRate",
|
||||
RpcRequest::GetInflationReward => "getInflationReward",
|
||||
RpcRequest::GetLargestAccounts => "getLargestAccounts",
|
||||
RpcRequest::GetLeaderSchedule => "getLeaderSchedule",
|
||||
RpcRequest::GetMaxRetransmitSlot => "getMaxRetransmitSlot",
|
||||
RpcRequest::GetMaxShredInsertSlot => "getMaxShredInsertSlot",
|
||||
RpcRequest::GetMinimumBalanceForRentExemption => "getMinimumBalanceForRentExemption",
|
||||
RpcRequest::GetMultipleAccounts => "getMultipleAccounts",
|
||||
RpcRequest::GetProgramAccounts => "getProgramAccounts",
|
||||
RpcRequest::GetRecentBlockhash => "getRecentBlockhash",
|
||||
RpcRequest::GetRecentPerformanceSamples => "getRecentPerformanceSamples",
|
||||
RpcRequest::GetSnapshotSlot => "getSnapshotSlot",
|
||||
RpcRequest::GetSignaturesForAddress => "getSignaturesForAddress",
|
||||
RpcRequest::GetSignatureStatuses => "getSignatureStatuses",
|
||||
RpcRequest::GetSlot => "getSlot",
|
||||
RpcRequest::GetSlotLeader => "getSlotLeader",
|
||||
@@ -144,7 +111,7 @@ impl fmt::Display for RpcRequest {
|
||||
RpcRequest::GetTokenAccountsByDelegate => "getTokenAccountsByDelegate",
|
||||
RpcRequest::GetTokenAccountsByOwner => "getTokenAccountsByOwner",
|
||||
RpcRequest::GetTokenSupply => "getTokenSupply",
|
||||
RpcRequest::GetTransaction => "getTransaction",
|
||||
RpcRequest::GetTotalSupply => "getTotalSupply",
|
||||
RpcRequest::GetTransactionCount => "getTransactionCount",
|
||||
RpcRequest::GetVersion => "getVersion",
|
||||
RpcRequest::GetVoteAccounts => "getVoteAccounts",
|
||||
|
@@ -7,9 +7,7 @@ use {
|
||||
inflation::Inflation,
|
||||
transaction::{Result, TransactionError},
|
||||
},
|
||||
solana_transaction_status::{
|
||||
ConfirmedTransactionStatusWithSignature, TransactionConfirmationStatus,
|
||||
},
|
||||
solana_transaction_status::ConfirmedTransactionStatusWithSignature,
|
||||
std::{collections::HashMap, fmt, net::SocketAddr},
|
||||
};
|
||||
|
||||
@@ -46,15 +44,6 @@ pub struct RpcFees {
|
||||
pub blockhash: String,
|
||||
pub fee_calculator: FeeCalculator,
|
||||
pub last_valid_slot: Slot,
|
||||
pub last_valid_block_height: u64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DeprecatedRpcFees {
|
||||
pub blockhash: String,
|
||||
pub fee_calculator: FeeCalculator,
|
||||
pub last_valid_slot: Slot,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
@@ -215,28 +204,11 @@ pub struct RpcContactInfo {
|
||||
pub version: Option<String>,
|
||||
/// First 4 bytes of the FeatureSet identifier
|
||||
pub feature_set: Option<u32>,
|
||||
/// Shred version
|
||||
pub shred_version: Option<u16>,
|
||||
}
|
||||
|
||||
/// Map of leader base58 identity pubkeys to the slot indices relative to the first epoch slot
|
||||
pub type RpcLeaderSchedule = HashMap<String, Vec<usize>>;
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcBlockProductionRange {
|
||||
pub first_slot: Slot,
|
||||
pub last_slot: Slot,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcBlockProduction {
|
||||
/// Map of leader base58 identity pubkeys to a tuple of `(number of leader slots, number of blocks produced)`
|
||||
pub by_identity: HashMap<String, (usize, usize)>,
|
||||
pub range: RpcBlockProductionRange,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct RpcVersionInfo {
|
||||
@@ -280,10 +252,10 @@ pub struct RpcVoteAccountStatus {
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcVoteAccountInfo {
|
||||
/// Vote account address, as base-58 encoded string
|
||||
/// Vote account pubkey as base-58 encoded string
|
||||
pub vote_pubkey: String,
|
||||
|
||||
/// The validator identity, as base-58 encoded string
|
||||
/// The pubkey of the node that votes using this account
|
||||
pub node_pubkey: String,
|
||||
|
||||
/// The current stake, in lamports, delegated to this vote account
|
||||
@@ -318,7 +290,6 @@ pub struct RpcSignatureConfirmation {
|
||||
pub struct RpcSimulateTransactionResult {
|
||||
pub err: Option<TransactionError>,
|
||||
pub logs: Option<Vec<String>>,
|
||||
pub accounts: Option<Vec<Option<UiAccount>>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
@@ -353,7 +324,7 @@ pub enum StakeActivationState {
|
||||
Inactive,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcStakeActivation {
|
||||
pub state: StakeActivationState,
|
||||
@@ -377,7 +348,6 @@ pub struct RpcConfirmedTransactionStatusWithSignature {
|
||||
pub err: Option<TransactionError>,
|
||||
pub memo: Option<String>,
|
||||
pub block_time: Option<UnixTimestamp>,
|
||||
pub confirmation_status: Option<TransactionConfirmationStatus>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
@@ -389,15 +359,6 @@ pub struct RpcPerfSample {
|
||||
pub sample_period_secs: u16,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcInflationReward {
|
||||
pub epoch: Epoch,
|
||||
pub effective_slot: Slot,
|
||||
pub amount: u64, // lamports
|
||||
pub post_balance: u64, // lamports
|
||||
}
|
||||
|
||||
impl From<ConfirmedTransactionStatusWithSignature> for RpcConfirmedTransactionStatusWithSignature {
|
||||
fn from(value: ConfirmedTransactionStatusWithSignature) -> Self {
|
||||
let ConfirmedTransactionStatusWithSignature {
|
||||
@@ -413,7 +374,6 @@ impl From<ConfirmedTransactionStatusWithSignature> for RpcConfirmedTransactionSt
|
||||
err,
|
||||
memo,
|
||||
block_time,
|
||||
confirmation_status: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -169,8 +169,8 @@ impl ThinClient {
|
||||
let rpc_clients: Vec<_> = rpc_addrs.into_iter().map(RpcClient::new_socket).collect();
|
||||
let optimizer = ClientOptimizer::new(rpc_clients.len());
|
||||
Self {
|
||||
transactions_socket,
|
||||
tpu_addrs,
|
||||
transactions_socket,
|
||||
rpc_clients,
|
||||
optimizer,
|
||||
}
|
||||
|
@@ -1,393 +0,0 @@
|
||||
use crate::{
|
||||
pubsub_client::{PubsubClient, PubsubClientError, PubsubClientSubscription},
|
||||
rpc_client::RpcClient,
|
||||
rpc_response::SlotUpdate,
|
||||
};
|
||||
use bincode::serialize;
|
||||
use log::*;
|
||||
use solana_sdk::{clock::Slot, pubkey::Pubkey, transaction::Transaction};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet, VecDeque},
|
||||
net::{SocketAddr, UdpSocket},
|
||||
str::FromStr,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc, RwLock,
|
||||
},
|
||||
thread::JoinHandle,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum TpuSenderError {
|
||||
#[error("Pubsub error: {0:?}")]
|
||||
PubsubError(#[from] PubsubClientError),
|
||||
#[error("RPC error: {0:?}")]
|
||||
RpcError(#[from] crate::client_error::ClientError),
|
||||
#[error("IO error: {0:?}")]
|
||||
IoError(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
type Result<T> = std::result::Result<T, TpuSenderError>;
|
||||
|
||||
/// Default number of slots used to build TPU socket fanout set
|
||||
pub const DEFAULT_FANOUT_SLOTS: u64 = 12;
|
||||
|
||||
/// Maximum number of slots used to build TPU socket fanout set
|
||||
pub const MAX_FANOUT_SLOTS: u64 = 100;
|
||||
|
||||
/// Config params for `TpuClient`
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TpuClientConfig {
|
||||
/// The range of upcoming slots to include when determining which
|
||||
/// leaders to send transactions to (min: 1, max: 100)
|
||||
pub fanout_slots: u64,
|
||||
}
|
||||
|
||||
impl Default for TpuClientConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
fanout_slots: DEFAULT_FANOUT_SLOTS,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Client which sends transactions directly to the current leader's TPU port over UDP.
|
||||
/// The client uses RPC to determine the current leader and fetch node contact info
|
||||
pub struct TpuClient {
|
||||
send_socket: UdpSocket,
|
||||
fanout_slots: u64,
|
||||
leader_tpu_service: LeaderTpuService,
|
||||
exit: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl TpuClient {
|
||||
/// Serializes and sends a transaction to the current leader's TPU port
|
||||
pub fn send_transaction(&self, transaction: &Transaction) -> bool {
|
||||
let wire_transaction = serialize(transaction).expect("serialization should succeed");
|
||||
self.send_wire_transaction(&wire_transaction)
|
||||
}
|
||||
|
||||
/// Sends a transaction to the current leader's TPU port
|
||||
pub fn send_wire_transaction(&self, wire_transaction: &[u8]) -> bool {
|
||||
let mut sent = false;
|
||||
for tpu_address in self
|
||||
.leader_tpu_service
|
||||
.leader_tpu_sockets(self.fanout_slots)
|
||||
{
|
||||
if self
|
||||
.send_socket
|
||||
.send_to(wire_transaction, tpu_address)
|
||||
.is_ok()
|
||||
{
|
||||
sent = true;
|
||||
}
|
||||
}
|
||||
sent
|
||||
}
|
||||
|
||||
/// Create a new client that disconnects when dropped
|
||||
pub fn new(
|
||||
rpc_client: Arc<RpcClient>,
|
||||
websocket_url: &str,
|
||||
config: TpuClientConfig,
|
||||
) -> Result<Self> {
|
||||
let exit = Arc::new(AtomicBool::new(false));
|
||||
let leader_tpu_service = LeaderTpuService::new(rpc_client, websocket_url, exit.clone())?;
|
||||
|
||||
Ok(Self {
|
||||
send_socket: UdpSocket::bind("0.0.0.0:0").unwrap(),
|
||||
fanout_slots: config.fanout_slots.min(MAX_FANOUT_SLOTS).max(1),
|
||||
leader_tpu_service,
|
||||
exit,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TpuClient {
|
||||
fn drop(&mut self) {
|
||||
self.exit.store(true, Ordering::Relaxed);
|
||||
self.leader_tpu_service.join();
|
||||
}
|
||||
}
|
||||
|
||||
struct LeaderTpuCache {
|
||||
first_slot: Slot,
|
||||
leaders: Vec<Pubkey>,
|
||||
leader_tpu_map: HashMap<Pubkey, SocketAddr>,
|
||||
}
|
||||
|
||||
impl LeaderTpuCache {
|
||||
fn new(rpc_client: &RpcClient, first_slot: Slot) -> Self {
|
||||
let leaders = Self::fetch_slot_leaders(rpc_client, first_slot).unwrap_or_default();
|
||||
let leader_tpu_map = Self::fetch_cluster_tpu_sockets(&rpc_client).unwrap_or_default();
|
||||
Self {
|
||||
first_slot,
|
||||
leaders,
|
||||
leader_tpu_map,
|
||||
}
|
||||
}
|
||||
|
||||
// Last slot that has a cached leader pubkey
|
||||
fn last_slot(&self) -> Slot {
|
||||
self.first_slot + self.leaders.len().saturating_sub(1) as u64
|
||||
}
|
||||
|
||||
// Get the TPU sockets for the current leader and upcoming leaders according to fanout size
|
||||
fn get_leader_sockets(&self, current_slot: Slot, fanout_slots: u64) -> Vec<SocketAddr> {
|
||||
let mut leader_set = HashSet::new();
|
||||
let mut leader_sockets = Vec::new();
|
||||
for leader_slot in current_slot..current_slot + fanout_slots {
|
||||
if let Some(leader) = self.get_slot_leader(leader_slot) {
|
||||
if let Some(tpu_socket) = self.leader_tpu_map.get(leader) {
|
||||
if leader_set.insert(*leader) {
|
||||
leader_sockets.push(*tpu_socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
leader_sockets
|
||||
}
|
||||
|
||||
fn get_slot_leader(&self, slot: Slot) -> Option<&Pubkey> {
|
||||
if slot >= self.first_slot {
|
||||
let index = slot - self.first_slot;
|
||||
self.leaders.get(index as usize)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch_cluster_tpu_sockets(rpc_client: &RpcClient) -> Result<HashMap<Pubkey, SocketAddr>> {
|
||||
let cluster_contact_info = rpc_client.get_cluster_nodes()?;
|
||||
Ok(cluster_contact_info
|
||||
.into_iter()
|
||||
.filter_map(|contact_info| {
|
||||
Some((
|
||||
Pubkey::from_str(&contact_info.pubkey).ok()?,
|
||||
contact_info.tpu?,
|
||||
))
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn fetch_slot_leaders(rpc_client: &RpcClient, start_slot: Slot) -> Result<Vec<Pubkey>> {
|
||||
Ok(rpc_client.get_slot_leaders(start_slot, 2 * MAX_FANOUT_SLOTS)?)
|
||||
}
|
||||
}
|
||||
|
||||
// 48 chosen because it's unlikely that 12 leaders in a row will miss their slots
|
||||
const MAX_SLOT_SKIP_DISTANCE: u64 = 48;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct RecentLeaderSlots(Arc<RwLock<VecDeque<Slot>>>);
|
||||
impl RecentLeaderSlots {
|
||||
fn new(current_slot: Slot) -> Self {
|
||||
let mut recent_slots = VecDeque::new();
|
||||
recent_slots.push_back(current_slot);
|
||||
Self(Arc::new(RwLock::new(recent_slots)))
|
||||
}
|
||||
|
||||
fn record_slot(&self, current_slot: Slot) {
|
||||
let mut recent_slots = self.0.write().unwrap();
|
||||
recent_slots.push_back(current_slot);
|
||||
// 12 recent slots should be large enough to avoid a misbehaving
|
||||
// validator from affecting the median recent slot
|
||||
while recent_slots.len() > 12 {
|
||||
recent_slots.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
// Estimate the current slot from recent slot notifications.
|
||||
fn estimated_current_slot(&self) -> Slot {
|
||||
let mut recent_slots: Vec<Slot> = self.0.read().unwrap().iter().cloned().collect();
|
||||
assert!(!recent_slots.is_empty());
|
||||
recent_slots.sort_unstable();
|
||||
|
||||
// Validators can broadcast invalid blocks that are far in the future
|
||||
// so check if the current slot is in line with the recent progression.
|
||||
let max_index = recent_slots.len() - 1;
|
||||
let median_index = max_index / 2;
|
||||
let median_recent_slot = recent_slots[median_index];
|
||||
let expected_current_slot = median_recent_slot + (max_index - median_index) as u64;
|
||||
let max_reasonable_current_slot = expected_current_slot + MAX_SLOT_SKIP_DISTANCE;
|
||||
|
||||
// Return the highest slot that doesn't exceed what we believe is a
|
||||
// reasonable slot.
|
||||
recent_slots
|
||||
.into_iter()
|
||||
.rev()
|
||||
.find(|slot| *slot <= max_reasonable_current_slot)
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl From<Vec<Slot>> for RecentLeaderSlots {
|
||||
fn from(recent_slots: Vec<Slot>) -> Self {
|
||||
assert!(!recent_slots.is_empty());
|
||||
Self(Arc::new(RwLock::new(recent_slots.into_iter().collect())))
|
||||
}
|
||||
}
|
||||
|
||||
/// Service that tracks upcoming leaders and maintains an up-to-date mapping
|
||||
/// of leader id to TPU socket address.
|
||||
struct LeaderTpuService {
|
||||
recent_slots: RecentLeaderSlots,
|
||||
leader_tpu_cache: Arc<RwLock<LeaderTpuCache>>,
|
||||
subscription: Option<PubsubClientSubscription<SlotUpdate>>,
|
||||
t_leader_tpu_service: Option<JoinHandle<()>>,
|
||||
}
|
||||
|
||||
impl LeaderTpuService {
|
||||
fn new(rpc_client: Arc<RpcClient>, websocket_url: &str, exit: Arc<AtomicBool>) -> Result<Self> {
|
||||
let start_slot = rpc_client.get_max_shred_insert_slot()?;
|
||||
|
||||
let recent_slots = RecentLeaderSlots::new(start_slot);
|
||||
let leader_tpu_cache = Arc::new(RwLock::new(LeaderTpuCache::new(&rpc_client, start_slot)));
|
||||
|
||||
let subscription = if !websocket_url.is_empty() {
|
||||
let recent_slots = recent_slots.clone();
|
||||
Some(PubsubClient::slot_updates_subscribe(
|
||||
websocket_url,
|
||||
move |update| {
|
||||
let current_slot = match update {
|
||||
// This update indicates that a full slot was received by the connected
|
||||
// node so we can stop sending transactions to the leader for that slot
|
||||
SlotUpdate::Completed { slot, .. } => slot.saturating_add(1),
|
||||
// This update indicates that we have just received the first shred from
|
||||
// the leader for this slot and they are probably still accepting transactions.
|
||||
SlotUpdate::FirstShredReceived { slot, .. } => slot,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
recent_slots.record_slot(current_slot);
|
||||
},
|
||||
)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let t_leader_tpu_service = Some({
|
||||
let recent_slots = recent_slots.clone();
|
||||
let leader_tpu_cache = leader_tpu_cache.clone();
|
||||
std::thread::Builder::new()
|
||||
.name("ldr-tpu-srv".to_string())
|
||||
.spawn(move || Self::run(rpc_client, recent_slots, leader_tpu_cache, exit))
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
Ok(LeaderTpuService {
|
||||
recent_slots,
|
||||
leader_tpu_cache,
|
||||
subscription,
|
||||
t_leader_tpu_service,
|
||||
})
|
||||
}
|
||||
|
||||
fn join(&mut self) {
|
||||
if let Some(mut subscription) = self.subscription.take() {
|
||||
let _ = subscription.send_unsubscribe();
|
||||
let _ = subscription.shutdown();
|
||||
}
|
||||
if let Some(t_handle) = self.t_leader_tpu_service.take() {
|
||||
t_handle.join().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn leader_tpu_sockets(&self, fanout_slots: u64) -> Vec<SocketAddr> {
|
||||
let current_slot = self.recent_slots.estimated_current_slot();
|
||||
self.leader_tpu_cache
|
||||
.read()
|
||||
.unwrap()
|
||||
.get_leader_sockets(current_slot, fanout_slots)
|
||||
}
|
||||
|
||||
fn run(
|
||||
rpc_client: Arc<RpcClient>,
|
||||
recent_slots: RecentLeaderSlots,
|
||||
leader_tpu_cache: Arc<RwLock<LeaderTpuCache>>,
|
||||
exit: Arc<AtomicBool>,
|
||||
) {
|
||||
let mut last_cluster_refresh = Instant::now();
|
||||
let mut sleep_ms = 1000;
|
||||
loop {
|
||||
if exit.load(Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Refresh cluster TPU ports every 5min in case validators restart with new port configuration
|
||||
// or new validators come online
|
||||
if last_cluster_refresh.elapsed() > Duration::from_secs(5 * 60) {
|
||||
if let Ok(leader_tpu_map) = LeaderTpuCache::fetch_cluster_tpu_sockets(&rpc_client) {
|
||||
leader_tpu_cache.write().unwrap().leader_tpu_map = leader_tpu_map;
|
||||
last_cluster_refresh = Instant::now();
|
||||
} else {
|
||||
sleep_ms = 100;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Sleep a few slots before checking if leader cache needs to be refreshed again
|
||||
std::thread::sleep(Duration::from_millis(sleep_ms));
|
||||
|
||||
let current_slot = recent_slots.estimated_current_slot();
|
||||
if current_slot
|
||||
>= leader_tpu_cache
|
||||
.read()
|
||||
.unwrap()
|
||||
.last_slot()
|
||||
.saturating_sub(MAX_FANOUT_SLOTS)
|
||||
{
|
||||
if let Ok(slot_leaders) =
|
||||
LeaderTpuCache::fetch_slot_leaders(&rpc_client, current_slot)
|
||||
{
|
||||
let mut leader_tpu_cache = leader_tpu_cache.write().unwrap();
|
||||
leader_tpu_cache.first_slot = current_slot;
|
||||
leader_tpu_cache.leaders = slot_leaders;
|
||||
} else {
|
||||
sleep_ms = 100;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
sleep_ms = 1000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn assert_slot(recent_slots: RecentLeaderSlots, expected_slot: Slot) {
|
||||
assert_eq!(recent_slots.estimated_current_slot(), expected_slot);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_recent_leader_slots() {
|
||||
assert_slot(RecentLeaderSlots::new(0), 0);
|
||||
|
||||
let mut recent_slots: Vec<Slot> = (1..=12).collect();
|
||||
assert_slot(RecentLeaderSlots::from(recent_slots.clone()), 12);
|
||||
|
||||
recent_slots.reverse();
|
||||
assert_slot(RecentLeaderSlots::from(recent_slots), 12);
|
||||
|
||||
assert_slot(
|
||||
RecentLeaderSlots::from(vec![0, 1 + MAX_SLOT_SKIP_DISTANCE]),
|
||||
1 + MAX_SLOT_SKIP_DISTANCE,
|
||||
);
|
||||
assert_slot(
|
||||
RecentLeaderSlots::from(vec![0, 2 + MAX_SLOT_SKIP_DISTANCE]),
|
||||
0,
|
||||
);
|
||||
|
||||
assert_slot(RecentLeaderSlots::from(vec![1]), 1);
|
||||
assert_slot(RecentLeaderSlots::from(vec![1, 100]), 1);
|
||||
assert_slot(RecentLeaderSlots::from(vec![1, 2, 100]), 2);
|
||||
assert_slot(RecentLeaderSlots::from(vec![1, 2, 3, 100]), 3);
|
||||
assert_slot(RecentLeaderSlots::from(vec![1, 2, 3, 99, 100]), 3);
|
||||
}
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "solana-core"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.7.1"
|
||||
version = "1.6.2"
|
||||
homepage = "https://solana.com/"
|
||||
documentation = "https://docs.rs/solana-core"
|
||||
readme = "../README.md"
|
||||
@@ -17,17 +17,23 @@ codecov = { repository = "solana-labs/solana", branch = "master", service = "git
|
||||
ahash = "0.6.1"
|
||||
base64 = "0.12.3"
|
||||
bincode = "1.3.1"
|
||||
blake3 = "0.3.7"
|
||||
bv = { version = "0.11.1", features = ["serde"] }
|
||||
bs58 = "0.3.1"
|
||||
byteorder = "1.3.4"
|
||||
chrono = { version = "0.4.11", features = ["serde"] }
|
||||
core_affinity = "0.5.10"
|
||||
crossbeam-channel = "0.4"
|
||||
ed25519-dalek = "=1.0.1"
|
||||
fs_extra = "1.2.0"
|
||||
flate2 = "1.0"
|
||||
indexmap = { version = "1.5", features = ["rayon"] }
|
||||
itertools = "0.9.0"
|
||||
jsonrpc-core = "17.0.0"
|
||||
jsonrpc-core-client = { version = "17.0.0", features = ["ipc", "ws"] }
|
||||
jsonrpc-derive = "17.0.0"
|
||||
jsonrpc-http-server = "17.0.0"
|
||||
jsonrpc-pubsub = "17.0.0"
|
||||
jsonrpc-ws-server = "17.0.0"
|
||||
libc = "0.2.81"
|
||||
log = "0.4.11"
|
||||
lru = "0.6.1"
|
||||
@@ -39,52 +45,53 @@ rand_chacha = "0.2.2"
|
||||
rand_core = "0.6.2"
|
||||
raptorq = "1.4.2"
|
||||
rayon = "1.5.0"
|
||||
regex = "1.3.9"
|
||||
retain_mut = "0.1.2"
|
||||
rustversion = "1.0.4"
|
||||
serde = "1.0.122"
|
||||
serde_bytes = "0.11"
|
||||
serde_derive = "1.0.103"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.7.1" }
|
||||
solana-banks-server = { path = "../banks-server", version = "=1.7.1" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.7.1" }
|
||||
solana-client = { path = "../client", version = "=1.7.1" }
|
||||
solana-gossip = { path = "../gossip", version = "=1.7.1" }
|
||||
solana-ledger = { path = "../ledger", version = "=1.7.1" }
|
||||
solana-logger = { path = "../logger", version = "=1.7.1" }
|
||||
solana-merkle-tree = { path = "../merkle-tree", version = "=1.7.1" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.7.1" }
|
||||
solana-measure = { path = "../measure", version = "=1.7.1" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.7.1" }
|
||||
solana-perf = { path = "../perf", version = "=1.7.1" }
|
||||
solana-poh = { path = "../poh", version = "=1.7.1" }
|
||||
solana-program-test = { path = "../program-test", version = "=1.7.1" }
|
||||
solana-rpc = { path = "../rpc", version = "=1.7.1" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.7.1" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.7.1" }
|
||||
solana-frozen-abi = { path = "../frozen-abi", version = "=1.7.1" }
|
||||
solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.7.1" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "=1.7.1" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.7.1" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.7.1" }
|
||||
solana-version = { path = "../version", version = "=1.7.1" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.7.1" }
|
||||
serde_json = "1.0.56"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.6.2" }
|
||||
solana-banks-server = { path = "../banks-server", version = "=1.6.2" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.6.2" }
|
||||
solana-client = { path = "../client", version = "=1.6.2" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.6.2" }
|
||||
solana-ledger = { path = "../ledger", version = "=1.6.2" }
|
||||
solana-logger = { path = "../logger", version = "=1.6.2" }
|
||||
solana-merkle-tree = { path = "../merkle-tree", version = "=1.6.2" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.6.2" }
|
||||
solana-measure = { path = "../measure", version = "=1.6.2" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.6.2" }
|
||||
solana-perf = { path = "../perf", version = "=1.6.2" }
|
||||
solana-program-test = { path = "../program-test", version = "=1.6.2" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.6.2" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.2" }
|
||||
solana-frozen-abi = { path = "../frozen-abi", version = "=1.6.2" }
|
||||
solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.6.2" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "=1.6.2" }
|
||||
solana-storage-bigtable = { path = "../storage-bigtable", version = "=1.6.2" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.6.2" }
|
||||
solana-sys-tuner = { path = "../sys-tuner", version = "=1.6.2" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.6.2" }
|
||||
solana-version = { path = "../version", version = "=1.6.2" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.6.2" }
|
||||
spl-token-v2-0 = { package = "spl-token", version = "=3.1.0", features = ["no-entrypoint"] }
|
||||
tempfile = "3.1.0"
|
||||
thiserror = "1.0"
|
||||
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.7.1" }
|
||||
tokio = { version = "1.1", features = ["full"] }
|
||||
tokio_02 = { version = "0.2", package = "tokio", features = ["full"] }
|
||||
tokio-util = { version = "0.3", features = ["codec"] } # This crate needs to stay in sync with tokio_02, until that dependency can be removed
|
||||
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.6.2" }
|
||||
trees = "0.2.1"
|
||||
|
||||
[dev-dependencies]
|
||||
jsonrpc-core = "17.1.0"
|
||||
jsonrpc-core-client = { version = "17.1.0", features = ["ipc", "ws"] }
|
||||
matches = "0.1.6"
|
||||
num_cpus = "1.13.0"
|
||||
reqwest = { version = "0.11.2", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
serde_json = "1.0.56"
|
||||
serial_test = "0.4.0"
|
||||
solana-version = { path = "../version", version = "=1.7.1" }
|
||||
symlink = "0.1.0"
|
||||
systemstat = "0.1.5"
|
||||
tokio_02 = { version = "0.2", package = "tokio", features = ["full"] }
|
||||
|
||||
[build-dependencies]
|
||||
rustc_version = "0.2"
|
||||
@@ -96,7 +103,13 @@ name = "banking_stage"
|
||||
name = "blockstore"
|
||||
|
||||
[[bench]]
|
||||
name = "cluster_info"
|
||||
name = "crds"
|
||||
|
||||
[[bench]]
|
||||
name = "crds_gossip_pull"
|
||||
|
||||
[[bench]]
|
||||
name = "crds_shards"
|
||||
|
||||
[[bench]]
|
||||
name = "gen_keys"
|
||||
@@ -104,8 +117,14 @@ name = "gen_keys"
|
||||
[[bench]]
|
||||
name = "sigverify_stage"
|
||||
|
||||
[[bench]]
|
||||
name = "poh"
|
||||
|
||||
[[bench]]
|
||||
name = "retransmit_stage"
|
||||
|
||||
[[bench]]
|
||||
name = "cluster_info"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -7,16 +7,16 @@ use crossbeam_channel::unbounded;
|
||||
use log::*;
|
||||
use rand::{thread_rng, Rng};
|
||||
use rayon::prelude::*;
|
||||
use solana_core::banking_stage::{BankingStage, BankingStageStats};
|
||||
use solana_gossip::cluster_info::ClusterInfo;
|
||||
use solana_gossip::cluster_info::Node;
|
||||
use solana_core::banking_stage::{create_test_recorder, BankingStage};
|
||||
use solana_core::cluster_info::ClusterInfo;
|
||||
use solana_core::cluster_info::Node;
|
||||
use solana_core::poh_recorder::WorkingBankEntry;
|
||||
use solana_ledger::blockstore_processor::process_entries;
|
||||
use solana_ledger::entry::{next_hash, Entry};
|
||||
use solana_ledger::genesis_utils::{create_genesis_config, GenesisConfigInfo};
|
||||
use solana_ledger::{blockstore::Blockstore, get_tmp_ledger_path};
|
||||
use solana_perf::packet::to_packets_chunked;
|
||||
use solana_perf::test_tx::test_tx;
|
||||
use solana_poh::poh_recorder::{create_test_recorder, WorkingBankEntry};
|
||||
use solana_runtime::bank::Bank;
|
||||
use solana_sdk::genesis_config::GenesisConfig;
|
||||
use solana_sdk::hash::Hash;
|
||||
@@ -89,7 +89,7 @@ fn bench_consume_buffered(bencher: &mut Bencher) {
|
||||
None,
|
||||
&s,
|
||||
None::<Box<dyn Fn()>>,
|
||||
&BankingStageStats::default(),
|
||||
None,
|
||||
&recorder,
|
||||
);
|
||||
});
|
||||
@@ -302,7 +302,7 @@ fn simulate_process_entries(
|
||||
hash: next_hash(&bank.last_blockhash(), 1, &tx_vector),
|
||||
transactions: tx_vector,
|
||||
};
|
||||
process_entries(&bank, &mut [entry], randomize_txs, None, None).unwrap();
|
||||
process_entries(&bank, &[entry], randomize_txs, None, None).unwrap();
|
||||
}
|
||||
|
||||
#[allow(clippy::same_item_push)]
|
||||
|
@@ -5,8 +5,8 @@ extern crate test;
|
||||
use rand::{thread_rng, Rng};
|
||||
use solana_core::broadcast_stage::broadcast_metrics::TransmitShredsStats;
|
||||
use solana_core::broadcast_stage::{broadcast_shreds, get_broadcast_peers};
|
||||
use solana_gossip::cluster_info::{ClusterInfo, Node};
|
||||
use solana_gossip::contact_info::ContactInfo;
|
||||
use solana_core::cluster_info::{ClusterInfo, Node};
|
||||
use solana_core::contact_info::ContactInfo;
|
||||
use solana_ledger::shred::Shred;
|
||||
use solana_sdk::pubkey;
|
||||
use solana_sdk::timing::timestamp;
|
||||
|
@@ -2,16 +2,14 @@
|
||||
|
||||
extern crate test;
|
||||
|
||||
use {
|
||||
rand::{thread_rng, Rng},
|
||||
rayon::ThreadPoolBuilder,
|
||||
solana_gossip::{
|
||||
crds::Crds, crds_gossip_pull::CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS, crds_value::CrdsValue,
|
||||
},
|
||||
solana_sdk::pubkey::Pubkey,
|
||||
std::collections::HashMap,
|
||||
test::Bencher,
|
||||
};
|
||||
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) {
|
@@ -2,18 +2,14 @@
|
||||
|
||||
extern crate test;
|
||||
|
||||
use {
|
||||
rand::{thread_rng, Rng},
|
||||
rayon::ThreadPoolBuilder,
|
||||
solana_gossip::{
|
||||
cluster_info::MAX_BLOOM_SIZE,
|
||||
crds::Crds,
|
||||
crds_gossip_pull::{CrdsFilter, CrdsGossipPull},
|
||||
crds_value::CrdsValue,
|
||||
},
|
||||
solana_sdk::hash,
|
||||
test::Bencher,
|
||||
};
|
||||
use rand::{thread_rng, Rng};
|
||||
use rayon::ThreadPoolBuilder;
|
||||
use solana_core::cluster_info::MAX_BLOOM_SIZE;
|
||||
use solana_core::crds::Crds;
|
||||
use solana_core::crds_gossip_pull::{CrdsFilter, CrdsGossipPull};
|
||||
use solana_core::crds_value::CrdsValue;
|
||||
use solana_sdk::hash;
|
||||
use test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn bench_hash_as_u64(bencher: &mut Bencher) {
|
||||
@@ -33,8 +29,13 @@ fn bench_hash_as_u64(bencher: &mut Bencher) {
|
||||
fn bench_build_crds_filters(bencher: &mut Bencher) {
|
||||
let thread_pool = ThreadPoolBuilder::new().build().unwrap();
|
||||
let mut rng = thread_rng();
|
||||
let crds_gossip_pull = CrdsGossipPull::default();
|
||||
let mut crds_gossip_pull = CrdsGossipPull::default();
|
||||
let mut crds = Crds::default();
|
||||
for _ in 0..50_000 {
|
||||
crds_gossip_pull
|
||||
.purged_values
|
||||
.push_back((solana_sdk::hash::new_rand(&mut rng), rng.gen()));
|
||||
}
|
||||
let mut num_inserts = 0;
|
||||
for _ in 0..90_000 {
|
||||
if crds
|
@@ -2,37 +2,31 @@
|
||||
|
||||
extern crate test;
|
||||
|
||||
use {
|
||||
rand::{thread_rng, Rng},
|
||||
solana_gossip::{
|
||||
crds::{Crds, VersionedCrdsValue},
|
||||
crds_shards::CrdsShards,
|
||||
crds_value::CrdsValue,
|
||||
},
|
||||
solana_sdk::timing::timestamp,
|
||||
std::iter::repeat_with,
|
||||
test::Bencher,
|
||||
};
|
||||
use rand::{thread_rng, Rng};
|
||||
use solana_core::contact_info::ContactInfo;
|
||||
use solana_core::crds::VersionedCrdsValue;
|
||||
use solana_core::crds_shards::CrdsShards;
|
||||
use solana_core::crds_value::{CrdsData, CrdsValue};
|
||||
use solana_sdk::pubkey;
|
||||
use solana_sdk::timing::timestamp;
|
||||
use test::Bencher;
|
||||
|
||||
const CRDS_SHARDS_BITS: u32 = 8;
|
||||
|
||||
fn new_test_crds_value<R: Rng>(rng: &mut R) -> VersionedCrdsValue {
|
||||
let value = CrdsValue::new_rand(rng, None);
|
||||
let label = value.label();
|
||||
let mut crds = Crds::default();
|
||||
crds.insert(value, timestamp()).unwrap();
|
||||
crds.get(&label).cloned().unwrap()
|
||||
fn new_test_crds_value() -> VersionedCrdsValue {
|
||||
let data = CrdsData::ContactInfo(ContactInfo::new_localhost(&pubkey::new_rand(), timestamp()));
|
||||
VersionedCrdsValue::new(timestamp(), CrdsValue::new_unsigned(data))
|
||||
}
|
||||
|
||||
fn bench_crds_shards_find(bencher: &mut Bencher, num_values: usize, mask_bits: u32) {
|
||||
let mut rng = thread_rng();
|
||||
let values: Vec<_> = repeat_with(|| new_test_crds_value(&mut rng))
|
||||
let values: Vec<VersionedCrdsValue> = std::iter::repeat_with(new_test_crds_value)
|
||||
.take(num_values)
|
||||
.collect();
|
||||
let mut shards = CrdsShards::new(CRDS_SHARDS_BITS);
|
||||
for (index, value) in values.iter().enumerate() {
|
||||
assert!(shards.insert(index, value));
|
||||
}
|
||||
let mut rng = thread_rng();
|
||||
bencher.iter(|| {
|
||||
let mask = rng.gen();
|
||||
let _hits = shards.find(mask, mask_bits).count();
|
@@ -3,16 +3,12 @@
|
||||
#![feature(test)]
|
||||
extern crate test;
|
||||
|
||||
use {
|
||||
solana_ledger::poh::Poh,
|
||||
solana_poh::poh_service::DEFAULT_HASHES_PER_BATCH,
|
||||
solana_sdk::hash::Hash,
|
||||
std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc, Mutex,
|
||||
},
|
||||
test::Bencher,
|
||||
};
|
||||
use solana_core::poh_service::DEFAULT_HASHES_PER_BATCH;
|
||||
use solana_ledger::poh::Poh;
|
||||
use solana_sdk::hash::Hash;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use test::Bencher;
|
||||
|
||||
const NUM_HASHES: u64 = 30_000; // Should require ~10ms on a 2017 MacBook Pro
|
||||
|
@@ -1,15 +1,11 @@
|
||||
#![feature(test)]
|
||||
extern crate test;
|
||||
|
||||
use {
|
||||
solana_ledger::entry::{next_entry_mut, Entry, EntrySlice},
|
||||
solana_sdk::{
|
||||
hash::{hash, Hash},
|
||||
signature::{Keypair, Signer},
|
||||
system_transaction,
|
||||
},
|
||||
test::Bencher,
|
||||
};
|
||||
use solana_ledger::entry::{next_entry_mut, Entry, EntrySlice};
|
||||
use solana_sdk::hash::{hash, Hash};
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use solana_sdk::system_transaction;
|
||||
use test::Bencher;
|
||||
|
||||
const NUM_HASHES: u64 = 400;
|
||||
const NUM_ENTRIES: usize = 800;
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user