Compare commits
105 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
acb992c3be | ||
|
61aca235a3 | ||
|
bad0709ff1 | ||
|
2a649e990d | ||
|
1a25889f72 | ||
|
e15dca6961 | ||
|
bb12f48014 | ||
|
c6416fca6e | ||
|
2f5d60bef7 | ||
|
ef1a9df507 | ||
|
d2611f54a0 | ||
|
6117f8d64e | ||
|
67d9faaefc | ||
|
aaa551ca7c | ||
|
85df8cb4c5 | ||
|
65f189d932 | ||
|
99001f7f2e | ||
|
39eeb0142e | ||
|
d5d1a344c3 | ||
|
62e3c084d3 | ||
|
3027ceb53a | ||
|
4e604e1211 | ||
|
6749bfd1d2 | ||
|
d05b39c4f6 | ||
|
c8e1fbd568 | ||
|
ad36ddedf1 | ||
|
08bece7651 | ||
|
f162c6d1d0 | ||
|
f3904b5765 | ||
|
67bf7515a7 | ||
|
0dcbc6d4d1 | ||
|
79a2ccabb4 | ||
|
f5e583ef0d | ||
|
132550cd7a | ||
|
08a789323f | ||
|
2d9781b101 | ||
|
6540d3c63e | ||
|
a227b813d8 | ||
|
2b4e0abb43 | ||
|
cdf6ff7907 | ||
|
6775e01747 | ||
|
4be9d5030d | ||
|
a9f914da8e | ||
|
096375584b | ||
|
d3ab1ec9cf | ||
|
e41d9c87c5 | ||
|
e938925b95 | ||
|
6c03e6c4b5 | ||
|
abce60efdf | ||
|
b36510a565 | ||
|
a8220ae653 | ||
|
2c642d4639 | ||
|
04eb35e679 | ||
|
8738241567 | ||
|
0ef9185c9e | ||
|
e41004f185 | ||
|
41cad9ccd5 | ||
|
d9ae092637 | ||
|
9015e47cc5 | ||
|
15b92e9c8d | ||
|
c72bd900cd | ||
|
ecb75ccdcf | ||
|
cf22e7a273 | ||
|
4f7bfbdbe9 | ||
|
011e325359 | ||
|
ba05852475 | ||
|
8ee656edde | ||
|
311d9a56c4 | ||
|
b9f46fd904 | ||
|
39f2d346b8 | ||
|
4a27bfa2fe | ||
|
69e53ec92a | ||
|
b8ac76066c | ||
|
1a1d7744bd | ||
|
61af485732 | ||
|
6a60d7bf8e | ||
|
53e917b54f | ||
|
4d49c99ac9 | ||
|
83597a5ce1 | ||
|
1589a41178 | ||
|
23a381b995 | ||
|
eb7ac42b2e | ||
|
88cf5e79f5 | ||
|
520453e1f3 | ||
|
27815555a1 | ||
|
3e483314b6 | ||
|
8a67504578 | ||
|
a1b238280b | ||
|
f9e07f575e | ||
|
c8bad57455 | ||
|
71654c0457 | ||
|
f9d6fb48a4 | ||
|
fa9aa0a1d7 | ||
|
9758ebfc99 | ||
|
8be23a2bb2 | ||
|
4ff9a6910d | ||
|
fd192e3641 | ||
|
a8de467ef8 | ||
|
1a186beb5c | ||
|
66a21ed65e | ||
|
1a42a40492 | ||
|
5841e4d665 | ||
|
6542a04521 | ||
|
5c27009758 | ||
|
888f3522d8 |
9
.buildkite/env/secrets.ejson
vendored
9
.buildkite/env/secrets.ejson
vendored
@@ -1,7 +1,12 @@
|
||||
{
|
||||
"_public_key": "ae29f4f7ad2fc92de70d470e411c8426d5d48db8817c9e3dae574b122192335f",
|
||||
"_comment": "These credentials are encrypted and pose no risk",
|
||||
"environment": {
|
||||
"CODECOV_TOKEN": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:3K68mE38LJ2RB98VWmjuNLFBNn1XTGR4:cR4r05/TOZQKmEZp1v4CSgUJtC6QJiOaL85QjXW0qZ061fMnsBA8AtAPMDoDq4WCGOZM1A==]"
|
||||
"CODECOV_TOKEN": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:3K68mE38LJ2RB98VWmjuNLFBNn1XTGR4:cR4r05/TOZQKmEZp1v4CSgUJtC6QJiOaL85QjXW0qZ061fMnsBA8AtAPMDoDq4WCGOZM1A==]",
|
||||
"CRATES_IO_TOKEN": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:GGRTYDjMXksevzR6kq4Jx+FaIQZz50RU:xkbwDxcgoCyU+aT2tiI9mymigrEl6YiOr3axe3aX70ELIBKbCdPGilXP/wixvKi94g2u]",
|
||||
"GEOLOCATION_API_KEY": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:U2PZLi5MU3Ru/zK1SilianEeizcMvxml:AJKf2OAtDHmJh0KyXrBnNnistItZvVVP3cZ7ZLtrVupjmWN/PzmKwSsXeCNObWS+]",
|
||||
"GITHUB_TOKEN": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:0NJNlpD/O19mvOakCGBYDhIDfySxWFSC:Dz4NXv9x6ncRQ1u9sVoWOcqmkg0sI09qmefghB0GXZgPcFGgn6T0mw7ynNnbUvjyH8dLruKHauk=]",
|
||||
"INFLUX_DATABASE": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:SzwHIeOVpmbTcGQOGngoFgYumsLZJUGq:t7Rpk49njsWvoM+ztv5Uwuiz]",
|
||||
"INFLUX_PASSWORD": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:/MUs+q7pdGrUjzwcq+6pgIFxur4hxdqu:am22z2E2dtmw1f1J1Mq5JLcUHZsrEjQAJ0pp21M4AZeJbNO6bVb44d9zSkHj7xdN6U+GNlCk+wU=]",
|
||||
"INFLUX_USERNAME": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:XjghH20xGVWro9B+epGlJaJcW8Wze0Bi:ZIdOtXudTY5TqKseDU7gVvQXfmXV99Xh]"
|
||||
}
|
||||
}
|
||||
|
46
.github/stale.yml
vendored
46
.github/stale.yml
vendored
@@ -1,3 +1,11 @@
|
||||
only: pulls
|
||||
|
||||
# Number of days of inactivity before a pull request becomes stale
|
||||
daysUntilStale: 7
|
||||
|
||||
# Number of days of inactivity before a stale pull request is closed
|
||||
daysUntilClose: 7
|
||||
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- security
|
||||
@@ -6,34 +14,12 @@ exemptLabels:
|
||||
# Label to use when marking a pull request as stale
|
||||
staleLabel: stale
|
||||
|
||||
pulls:
|
||||
# Number of days of inactivity before a pull request becomes stale
|
||||
daysUntilStale: 7
|
||||
# Comment to post when marking a pull request as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This pull request has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs.
|
||||
|
||||
# Number of days of inactivity before a stale pull request is closed
|
||||
daysUntilClose: 7
|
||||
# Comment to post when marking a pull request as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This pull request has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs.
|
||||
|
||||
# Comment to post when closing a stale pull request. Set to `false` to disable
|
||||
closeComment: >
|
||||
This stale pull request has been automatically closed.
|
||||
Thank you for your contributions.
|
||||
|
||||
issues:
|
||||
# Number of days of inactivity before a issue becomes stale
|
||||
daysUntilStale: 365
|
||||
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
# Comment to post when marking a issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs.
|
||||
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: >
|
||||
This stale issue has been automatically closed.
|
||||
Thank you for your contributions.
|
||||
# Comment to post when closing a stale pull request. Set to `false` to disable
|
||||
closeComment: >
|
||||
This stale pull request has been automatically closed.
|
||||
Thank you for your contributions.
|
||||
|
16
.mergify.yml
16
.mergify.yml
@@ -50,6 +50,14 @@ pull_request_rules:
|
||||
label:
|
||||
add:
|
||||
- automerge
|
||||
- name: v1.1 backport
|
||||
conditions:
|
||||
- label=v1.1
|
||||
actions:
|
||||
backport:
|
||||
ignore_conflicts: true
|
||||
branches:
|
||||
- v1.1
|
||||
- name: v1.2 backport
|
||||
conditions:
|
||||
- label=v1.2
|
||||
@@ -66,11 +74,3 @@ pull_request_rules:
|
||||
ignore_conflicts: true
|
||||
branches:
|
||||
- v1.3
|
||||
- name: v1.4 backport
|
||||
conditions:
|
||||
- label=v1.4
|
||||
actions:
|
||||
backport:
|
||||
ignore_conflicts: true
|
||||
branches:
|
||||
- v1.4
|
||||
|
@@ -124,8 +124,6 @@ jobs:
|
||||
- ~/.npm
|
||||
|
||||
before_install:
|
||||
- source ci/env.sh
|
||||
- .travis/channel_restriction.sh edge beta || travis_terminate 0
|
||||
- .travis/affects.sh docs/ .travis || travis_terminate 0
|
||||
- cd docs/
|
||||
- source .travis/before_install.sh
|
||||
|
@@ -1,19 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Only proceed if we are on one of the channels passed in, or a tag build
|
||||
#
|
||||
|
||||
set -ex
|
||||
|
||||
[[ -n $CI_TAG ]] && exit 0
|
||||
|
||||
eval "$(ci/channel-info.sh)"
|
||||
|
||||
for acceptable_channel in "$@"; do
|
||||
if [[ "$CHANNEL" == "$acceptable_channel" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Not running from one of the following channels: $*"
|
||||
exit 1
|
@@ -232,7 +232,7 @@ confused with 3-letter acronyms.
|
||||
Solana's architecture is described by docs generated from markdown files in
|
||||
the `docs/src/` directory, maintained by an *editor* (currently @garious). To
|
||||
add a design proposal, you'll need to include it in the
|
||||
[Accepted Design Proposals](https://docs.solana.com/proposals/accepted-design-proposals)
|
||||
[Accepted Design Proposals](https://docs.solana.com/proposals)
|
||||
section of the Solana docs. Here's the full process:
|
||||
|
||||
1. Propose a design by creating a PR that adds a markdown document to the
|
||||
|
1024
Cargo.lock
generated
1024
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -10,16 +10,15 @@ members = [
|
||||
"banks-server",
|
||||
"clap-utils",
|
||||
"cli-config",
|
||||
"cli-output",
|
||||
"client",
|
||||
"core",
|
||||
"dos",
|
||||
"download-utils",
|
||||
"faucet",
|
||||
"frozen-abi",
|
||||
"perf",
|
||||
"validator",
|
||||
"genesis",
|
||||
"genesis-programs",
|
||||
"gossip",
|
||||
"install",
|
||||
"keygen",
|
||||
@@ -31,14 +30,12 @@ members = [
|
||||
"merkle-tree",
|
||||
"stake-o-matic",
|
||||
"storage-bigtable",
|
||||
"storage-proto",
|
||||
"streamer",
|
||||
"measure",
|
||||
"metrics",
|
||||
"net-shaper",
|
||||
"notifier",
|
||||
"poh-bench",
|
||||
"programs/secp256k1",
|
||||
"programs/bpf_loader",
|
||||
"programs/budget",
|
||||
"programs/config",
|
||||
@@ -53,7 +50,6 @@ members = [
|
||||
"ramp-tps",
|
||||
"runtime",
|
||||
"sdk",
|
||||
"sdk/cargo-build-bpf",
|
||||
"scripts",
|
||||
"stake-accounts",
|
||||
"stake-monitor",
|
||||
|
@@ -19,7 +19,7 @@ $ source $HOME/.cargo/env
|
||||
$ rustup component add rustfmt
|
||||
```
|
||||
|
||||
Please sure you are always using the latest stable rust version by running:
|
||||
If your rustc version is lower than 1.39.0, please update it:
|
||||
|
||||
```bash
|
||||
$ rustup update
|
||||
@@ -59,7 +59,7 @@ $ cargo test
|
||||
```
|
||||
|
||||
### Starting a local testnet
|
||||
Start your own testnet locally, instructions are in the [online docs](https://docs.solana.com/cluster/bench-tps).
|
||||
Start your own testnet locally, instructions are in the [online docs](https://docs.solana.com/bench-tps).
|
||||
|
||||
### Accessing the remote testnet
|
||||
* `testnet` - public stable testnet accessible via devnet.solana.com. Runs 24/7
|
||||
|
17
RELEASE.md
17
RELEASE.md
@@ -107,15 +107,11 @@ Alternatively use the Github UI.
|
||||
1. If the Cargo.toml version field is **0.12.3**, then the release tag must be **v0.12.3**
|
||||
1. Make sure the Target Branch field matches the branch you want to make a release on.
|
||||
1. If you want to release v0.12.0, the target branch must be v0.12
|
||||
1. Fill the release notes.
|
||||
1. If this is the first release on the branch (e.g. v0.13.**0**), paste in [this
|
||||
template](https://raw.githubusercontent.com/solana-labs/solana/master/.github/RELEASE_TEMPLATE.md). Engineering Lead can provide summary contents for release notes if needed.
|
||||
1. If this is a patch release, review all the commits since the previous release on this branch and add details as needed.
|
||||
1. If this is the first release on the branch (e.g. v0.13.**0**), paste in [this
|
||||
template](https://raw.githubusercontent.com/solana-labs/solana/master/.github/RELEASE_TEMPLATE.md). Engineering Lead can provide summary contents for release notes if needed. If this is a patch release, review all the commits since the previous release on this branch and add details as needed.
|
||||
1. Click "Save Draft", then confirm the release notes look good and the tag name and branch are correct.
|
||||
1. Ensure all desired commits (usually backports) are landed on the branch by now.
|
||||
1. Ensure the release is marked **"This is a pre-release"**. This flag will need to be be removed manually after confirming the the Linux binary artifacts appear at a later step.
|
||||
1. Go back into edit the release and click "Publish release" while being marked as a pre-release.
|
||||
1. Confirm there is new git tag with intended version number at the intended revision after running `git fetch` locally.
|
||||
1. Ensure the release is marked **"This is a pre-release"**. This flag will then need to be be removed once the the Linux binary artifacts appear later.
|
||||
1. Go back into edit the release and click "Publish release" when ready.
|
||||
|
||||
|
||||
### Update release branch with the next patch version
|
||||
@@ -135,8 +131,7 @@ Alternatively use the Github UI.
|
||||
1. Open a PR against origin/vX.Y and then merge the PR after passing CI.
|
||||
|
||||
### Prepare for the next release
|
||||
1. Go to [GitHub Releases](https://github.com/solana-labs/solana/releases) and create a new draft release for `X.Y.Z+1` with empty release notes. This allows people to incrementally add new release notes until it's time for the next release
|
||||
1. Also, point the branch field to the same branch and mark the relese as **"This is a pre-release"**.
|
||||
1. Go to [GitHub Releases](https://github.com/solana-labs/solana/releases) and create a new draft release for `X.Y.Z+1` with empty release nodes. This allows people to incrementally add new release notes until it's time for the next release
|
||||
1. Go to the [Github Milestones](https://github.com/solana-labs/solana/milestones). Create a new milestone for the `X.Y.Z+1`, move over
|
||||
unresolved issues still in the `X.Y.Z` milestone, then close the `X.Y.Z` milestone.
|
||||
|
||||
@@ -152,5 +147,5 @@ appearing. To check for progress:
|
||||
[Crates.io](https://crates.io/crates/solana) should have an updated Solana version. This can take 2-3 hours, and sometimes fails in the `solana-secondary` job.
|
||||
If this happens and the error is non-fatal, click "Retry" on the "publish crate" job
|
||||
|
||||
### Update software on devnet.solana.com/testnet.solana.com/mainnet-beta.solana.com
|
||||
### Update software on devnet.solana.com/testnet.solama.com/mainnet-beta.solana.com
|
||||
See the documentation at https://github.com/solana-labs/cluster-ops/
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-account-decoder"
|
||||
version = "1.4.2"
|
||||
version = "1.3.6"
|
||||
description = "Solana account decoder"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -18,11 +18,11 @@ lazy_static = "1.4.0"
|
||||
serde = "1.0.112"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.56"
|
||||
solana-config-program = { path = "../programs/config", version = "1.4.2" }
|
||||
solana-sdk = { path = "../sdk", version = "1.4.2" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "1.4.2" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.4.2" }
|
||||
spl-token-v2-0 = { package = "spl-token", version = "=2.0.8" }
|
||||
solana-config-program = { path = "../programs/config", version = "1.3.6" }
|
||||
solana-sdk = { path = "../sdk", version = "1.3.6" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "1.3.6" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.3.6" }
|
||||
spl-token-v2-0 = { package = "spl-token", version = "2.0.3", features = ["skip-no-mangle"] }
|
||||
thiserror = "1.0"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
|
@@ -111,8 +111,8 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_parse_account_data() {
|
||||
let account_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let other_program = solana_sdk::pubkey::new_rand();
|
||||
let account_pubkey = Pubkey::new_rand();
|
||||
let other_program = Pubkey::new_rand();
|
||||
let data = vec![0; 4];
|
||||
assert!(parse_account_data(&account_pubkey, &other_program, &data, None).is_err());
|
||||
|
||||
|
@@ -117,7 +117,7 @@ mod test {
|
||||
}))
|
||||
.unwrap(),
|
||||
};
|
||||
let info_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let info_pubkey = Pubkey::new_rand();
|
||||
let validator_info_config_account = create_config_account(
|
||||
vec![(validator_info::id(), false), (info_pubkey, true)],
|
||||
&validator_info,
|
||||
|
@@ -134,6 +134,7 @@ impl From<Delegation> for UiDelegation {
|
||||
mod test {
|
||||
use super::*;
|
||||
use bincode::serialize;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
|
||||
#[test]
|
||||
fn test_parse_stake() {
|
||||
@@ -144,8 +145,8 @@ mod test {
|
||||
StakeAccountType::Uninitialized
|
||||
);
|
||||
|
||||
let pubkey = solana_sdk::pubkey::new_rand();
|
||||
let custodian = solana_sdk::pubkey::new_rand();
|
||||
let pubkey = Pubkey::new_rand();
|
||||
let custodian = Pubkey::new_rand();
|
||||
let authorized = Authorized::auto(&pubkey);
|
||||
let lockup = Lockup {
|
||||
unix_timestamp: 0,
|
||||
@@ -179,7 +180,7 @@ mod test {
|
||||
})
|
||||
);
|
||||
|
||||
let voter_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let voter_pubkey = Pubkey::new_rand();
|
||||
let stake = Stake {
|
||||
delegation: Delegation {
|
||||
voter_pubkey,
|
||||
|
@@ -319,7 +319,7 @@ mod test {
|
||||
}]),
|
||||
);
|
||||
|
||||
let bad_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let bad_pubkey = Pubkey::new_rand();
|
||||
assert!(parse_sysvar(&stake_history_sysvar.data, &bad_pubkey).is_err());
|
||||
|
||||
let bad_data = vec![0; 4];
|
||||
|
@@ -4,7 +4,9 @@ use crate::{
|
||||
};
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use spl_token_v2_0::{
|
||||
solana_sdk::{program_option::COption, program_pack::Pack, pubkey::Pubkey as SplTokenPubkey},
|
||||
option::COption,
|
||||
pack::Pack,
|
||||
solana_sdk::pubkey::Pubkey as SplTokenPubkey,
|
||||
state::{Account, AccountState, Mint, Multisig},
|
||||
};
|
||||
use std::str::FromStr;
|
||||
@@ -154,31 +156,6 @@ pub struct UiTokenAmount {
|
||||
pub amount: StringAmount,
|
||||
}
|
||||
|
||||
impl UiTokenAmount {
|
||||
pub fn real_number_string(&self) -> String {
|
||||
let decimals = self.decimals as usize;
|
||||
if decimals > 0 {
|
||||
let amount = u64::from_str(&self.amount).unwrap_or(0);
|
||||
|
||||
// Left-pad zeros to decimals + 1, so we at least have an integer zero
|
||||
let mut s = format!("{:01$}", amount, decimals + 1);
|
||||
|
||||
// Add the decimal point (Sorry, "," locales!)
|
||||
s.insert(s.len() - decimals, '.');
|
||||
s
|
||||
} else {
|
||||
self.amount.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn real_number_string_trimmed(&self) -> String {
|
||||
let s = self.real_number_string();
|
||||
let zeros_trimmed = s.trim_end_matches('0');
|
||||
let decimal_trimmed = zeros_trimmed.trim_end_matches('.');
|
||||
decimal_trimmed.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn token_amount_to_ui_amount(amount: u64, decimals: u8) -> UiTokenAmount {
|
||||
// Use `amount_to_ui_amount()` once spl_token is bumped to a version that supports it: https://github.com/solana-labs/solana-program-library/pull/211
|
||||
let amount_decimals = amount as f64 / 10_usize.pow(decimals as u32) as f64;
|
||||
@@ -225,14 +202,16 @@ mod test {
|
||||
let mint_pubkey = SplTokenPubkey::new(&[2; 32]);
|
||||
let owner_pubkey = SplTokenPubkey::new(&[3; 32]);
|
||||
let mut account_data = vec![0; Account::get_packed_len()];
|
||||
let mut account = Account::unpack_unchecked(&account_data).unwrap();
|
||||
account.mint = mint_pubkey;
|
||||
account.owner = owner_pubkey;
|
||||
account.amount = 42;
|
||||
account.state = AccountState::Initialized;
|
||||
account.is_native = COption::None;
|
||||
account.close_authority = COption::Some(owner_pubkey);
|
||||
Account::pack(account, &mut account_data).unwrap();
|
||||
Account::unpack_unchecked_mut(&mut account_data, &mut |account: &mut Account| {
|
||||
account.mint = mint_pubkey;
|
||||
account.owner = owner_pubkey;
|
||||
account.amount = 42;
|
||||
account.state = AccountState::Initialized;
|
||||
account.is_native = COption::None;
|
||||
account.close_authority = COption::Some(owner_pubkey);
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert!(parse_token(&account_data, None).is_err());
|
||||
assert_eq!(
|
||||
@@ -255,13 +234,15 @@ mod test {
|
||||
);
|
||||
|
||||
let mut mint_data = vec![0; Mint::get_packed_len()];
|
||||
let mut mint = Mint::unpack_unchecked(&mint_data).unwrap();
|
||||
mint.mint_authority = COption::Some(owner_pubkey);
|
||||
mint.supply = 42;
|
||||
mint.decimals = 3;
|
||||
mint.is_initialized = true;
|
||||
mint.freeze_authority = COption::Some(owner_pubkey);
|
||||
Mint::pack(mint, &mut mint_data).unwrap();
|
||||
Mint::unpack_unchecked_mut(&mut mint_data, &mut |mint: &mut Mint| {
|
||||
mint.mint_authority = COption::Some(owner_pubkey);
|
||||
mint.supply = 42;
|
||||
mint.decimals = 3;
|
||||
mint.is_initialized = true;
|
||||
mint.freeze_authority = COption::Some(owner_pubkey);
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
parse_token(&mint_data, None).unwrap(),
|
||||
@@ -282,13 +263,14 @@ mod test {
|
||||
signers[0] = signer1;
|
||||
signers[1] = signer2;
|
||||
signers[2] = signer3;
|
||||
let mut multisig = Multisig::unpack_unchecked(&multisig_data).unwrap();
|
||||
multisig.m = 2;
|
||||
multisig.n = 3;
|
||||
multisig.is_initialized = true;
|
||||
multisig.signers = signers;
|
||||
Multisig::pack(multisig, &mut multisig_data).unwrap();
|
||||
|
||||
Multisig::unpack_unchecked_mut(&mut multisig_data, &mut |multisig: &mut Multisig| {
|
||||
multisig.m = 2;
|
||||
multisig.n = 3;
|
||||
multisig.is_initialized = true;
|
||||
multisig.signers = signers;
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
parse_token(&multisig_data, None).unwrap(),
|
||||
TokenAccountType::Multisig(UiMultisig {
|
||||
@@ -311,9 +293,11 @@ mod test {
|
||||
fn test_get_token_account_mint() {
|
||||
let mint_pubkey = SplTokenPubkey::new(&[2; 32]);
|
||||
let mut account_data = vec![0; Account::get_packed_len()];
|
||||
let mut account = Account::unpack_unchecked(&account_data).unwrap();
|
||||
account.mint = mint_pubkey;
|
||||
Account::pack(account, &mut account_data).unwrap();
|
||||
Account::unpack_unchecked_mut(&mut account_data, &mut |account: &mut Account| {
|
||||
account.mint = mint_pubkey;
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let expected_mint_pubkey = Pubkey::new(&[2; 32]);
|
||||
assert_eq!(
|
||||
@@ -321,20 +305,4 @@ mod test {
|
||||
Some(expected_mint_pubkey)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ui_token_amount_real_string() {
|
||||
let token_amount = token_amount_to_ui_amount(1, 0);
|
||||
assert_eq!(&token_amount.real_number_string(), "1");
|
||||
assert_eq!(&token_amount.real_number_string_trimmed(), "1");
|
||||
let token_amount = token_amount_to_ui_amount(1, 9);
|
||||
assert_eq!(&token_amount.real_number_string(), "0.000000001");
|
||||
assert_eq!(&token_amount.real_number_string_trimmed(), "0.000000001");
|
||||
let token_amount = token_amount_to_ui_amount(1_000_000_000, 9);
|
||||
assert_eq!(&token_amount.real_number_string(), "1.000000000");
|
||||
assert_eq!(&token_amount.real_number_string_trimmed(), "1");
|
||||
let token_amount = token_amount_to_ui_amount(1_234_567_890, 3);
|
||||
assert_eq!(&token_amount.real_number_string(), "1234567.890");
|
||||
assert_eq!(&token_amount.real_number_string_trimmed(), "1234567.89");
|
||||
}
|
||||
}
|
||||
|
@@ -2,20 +2,18 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-accounts-bench"
|
||||
version = "1.4.2"
|
||||
version = "1.3.6"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.6"
|
||||
rayon = "1.4.0"
|
||||
solana-logger = { path = "../logger", version = "1.4.2" }
|
||||
solana-runtime = { path = "../runtime", version = "1.4.2" }
|
||||
solana-measure = { path = "../measure", version = "1.4.2" }
|
||||
solana-sdk = { path = "../sdk", version = "1.4.2" }
|
||||
solana-version = { path = "../version", version = "1.4.2" }
|
||||
rayon = "1.3.1"
|
||||
solana-logger = { path = "../logger", version = "1.3.6" }
|
||||
solana-runtime = { path = "../runtime", version = "1.3.6" }
|
||||
solana-measure = { path = "../measure", version = "1.3.6" }
|
||||
solana-sdk = { path = "../sdk", version = "1.3.6" }
|
||||
rand = "0.7.0"
|
||||
clap = "2.33.1"
|
||||
crossbeam-channel = "0.4"
|
||||
|
@@ -1,21 +1,20 @@
|
||||
use clap::{crate_description, crate_name, value_t, App, Arg};
|
||||
use clap::{value_t, App, Arg};
|
||||
use rayon::prelude::*;
|
||||
use solana_measure::measure::Measure;
|
||||
use solana_runtime::{
|
||||
accounts::{create_test_accounts, update_accounts, Accounts},
|
||||
accounts_index::Ancestors,
|
||||
};
|
||||
use solana_sdk::{genesis_config::ClusterType, pubkey::Pubkey};
|
||||
use std::env;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() {
|
||||
solana_logger::setup();
|
||||
|
||||
let matches = App::new(crate_name!())
|
||||
.about(crate_description!())
|
||||
.version(solana_version::version!())
|
||||
let matches = App::new("crate")
|
||||
.about("about")
|
||||
.version("version")
|
||||
.arg(
|
||||
Arg::with_name("num_slots")
|
||||
.long("num_slots")
|
||||
@@ -51,12 +50,11 @@ fn main() {
|
||||
let clean = matches.is_present("clean");
|
||||
println!("clean: {:?}", clean);
|
||||
|
||||
let path = PathBuf::from(env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_owned()))
|
||||
.join("accounts-bench");
|
||||
let path = PathBuf::from("farf/accounts-bench");
|
||||
if fs::remove_dir_all(path.clone()).is_err() {
|
||||
println!("Warning: Couldn't remove {:?}", path);
|
||||
}
|
||||
let accounts = Accounts::new(vec![path], &ClusterType::Testnet);
|
||||
let accounts = Accounts::new(vec![path]);
|
||||
println!("Creating {} accounts", num_accounts);
|
||||
let mut create_time = Measure::start("create accounts");
|
||||
let pubkeys: Vec<_> = (0..num_slots)
|
||||
@@ -88,7 +86,7 @@ fn main() {
|
||||
for x in 0..iterations {
|
||||
if clean {
|
||||
let mut time = Measure::start("clean");
|
||||
accounts.accounts_db.clean_accounts(None);
|
||||
accounts.accounts_db.clean_accounts();
|
||||
time.stop();
|
||||
println!("{}", time);
|
||||
for slot in 0..num_slots {
|
||||
@@ -98,7 +96,7 @@ fn main() {
|
||||
} else {
|
||||
let mut pubkeys: Vec<Pubkey> = vec![];
|
||||
let mut time = Measure::start("hash");
|
||||
let hash = accounts.accounts_db.update_accounts_hash(0, &ancestors).0;
|
||||
let hash = accounts.accounts_db.update_accounts_hash(0, &ancestors);
|
||||
time.stop();
|
||||
println!("hash: {} {}", hash, time);
|
||||
create_test_accounts(&accounts, &mut pubkeys, 1, 0);
|
||||
|
@@ -2,28 +2,27 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-banking-bench"
|
||||
version = "1.4.2"
|
||||
version = "1.3.6"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33.1"
|
||||
crossbeam-channel = "0.4"
|
||||
log = "0.4.6"
|
||||
rand = "0.7.0"
|
||||
rayon = "1.4.0"
|
||||
solana-core = { path = "../core", version = "1.4.2" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.4.2" }
|
||||
solana-streamer = { path = "../streamer", version = "1.4.2" }
|
||||
solana-perf = { path = "../perf", version = "1.4.2" }
|
||||
solana-ledger = { path = "../ledger", version = "1.4.2" }
|
||||
solana-logger = { path = "../logger", version = "1.4.2" }
|
||||
solana-runtime = { path = "../runtime", version = "1.4.2" }
|
||||
solana-measure = { path = "../measure", version = "1.4.2" }
|
||||
solana-sdk = { path = "../sdk", version = "1.4.2" }
|
||||
solana-version = { path = "../version", version = "1.4.2" }
|
||||
rayon = "1.3.1"
|
||||
solana-core = { path = "../core", version = "1.3.6" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.3.6" }
|
||||
solana-streamer = { path = "../streamer", version = "1.3.6" }
|
||||
solana-perf = { path = "../perf", version = "1.3.6" }
|
||||
solana-ledger = { path = "../ledger", version = "1.3.6" }
|
||||
solana-logger = { path = "../logger", version = "1.3.6" }
|
||||
solana-runtime = { path = "../runtime", version = "1.3.6" }
|
||||
solana-measure = { path = "../measure", version = "1.3.6" }
|
||||
solana-sdk = { path = "../sdk", version = "1.3.6" }
|
||||
solana-version = { path = "../version", version = "1.3.6" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -20,6 +20,7 @@ use solana_perf::packet::to_packets_chunked;
|
||||
use solana_runtime::{bank::Bank, bank_forks::BankForks};
|
||||
use solana_sdk::{
|
||||
hash::Hash,
|
||||
pubkey::Pubkey,
|
||||
signature::Keypair,
|
||||
signature::Signature,
|
||||
system_transaction,
|
||||
@@ -68,7 +69,7 @@ fn make_accounts_txs(
|
||||
hash: Hash,
|
||||
same_payer: bool,
|
||||
) -> Vec<Transaction> {
|
||||
let to_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let to_pubkey = Pubkey::new_rand();
|
||||
let payer_key = Keypair::new();
|
||||
let dummy = system_transaction::transfer(&payer_key, &to_pubkey, 1, hash);
|
||||
(0..total_num_transactions)
|
||||
@@ -77,9 +78,9 @@ fn make_accounts_txs(
|
||||
let mut new = dummy.clone();
|
||||
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
|
||||
if !same_payer {
|
||||
new.message.account_keys[0] = solana_sdk::pubkey::new_rand();
|
||||
new.message.account_keys[0] = Pubkey::new_rand();
|
||||
}
|
||||
new.message.account_keys[1] = solana_sdk::pubkey::new_rand();
|
||||
new.message.account_keys[1] = Pubkey::new_rand();
|
||||
new.signatures = vec![Signature::new(&sig[0..64])];
|
||||
new
|
||||
})
|
||||
@@ -240,7 +241,7 @@ fn main() {
|
||||
let base_tx_count = bank.transaction_count();
|
||||
let mut txs_processed = 0;
|
||||
let mut root = 1;
|
||||
let collector = solana_sdk::pubkey::new_rand();
|
||||
let collector = Pubkey::new_rand();
|
||||
let config = Config {
|
||||
packets_per_batch: packets_per_chunk,
|
||||
chunk_len,
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-banks-client"
|
||||
version = "1.4.2"
|
||||
version = "1.3.6"
|
||||
description = "Solana banks client"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -12,15 +12,15 @@ edition = "2018"
|
||||
async-trait = "0.1.36"
|
||||
bincode = "1.3.1"
|
||||
futures = "0.3"
|
||||
solana-banks-interface = { path = "../banks-interface", version = "1.4.2" }
|
||||
solana-sdk = { path = "../sdk", version = "1.4.2" }
|
||||
tarpc = { version = "0.22.0", features = ["full"] }
|
||||
solana-banks-interface = { path = "../banks-interface", version = "1.3.6" }
|
||||
solana-sdk = { path = "../sdk", version = "1.3.6" }
|
||||
tarpc = { version = "0.21.0", features = ["full"] }
|
||||
tokio = "0.2"
|
||||
tokio-serde = { version = "0.6", features = ["bincode"] }
|
||||
|
||||
[dev-dependencies]
|
||||
solana-runtime = { path = "../runtime", version = "1.4.2" }
|
||||
solana-banks-server = { path = "../banks-server", version = "1.4.2" }
|
||||
solana-runtime = { path = "../runtime", version = "1.3.6" }
|
||||
solana-banks-server = { path = "../banks-server", version = "1.3.6" }
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib"]
|
||||
|
@@ -71,14 +71,6 @@ pub trait BanksClientExt {
|
||||
/// are said to be finalized. The cluster will not fork to a higher slot height.
|
||||
async fn get_root_slot(&mut self) -> io::Result<Slot>;
|
||||
|
||||
/// Return the account at the given address at the slot corresponding to the given
|
||||
/// commitment level. If the account is not found, None is returned.
|
||||
async fn get_account_with_commitment(
|
||||
&mut self,
|
||||
address: Pubkey,
|
||||
commitment: CommitmentLevel,
|
||||
) -> io::Result<Option<Account>>;
|
||||
|
||||
/// Return the account at the given address at the time of the most recent root slot.
|
||||
/// If the account is not found, None is returned.
|
||||
async fn get_account(&mut self, address: Pubkey) -> io::Result<Option<Account>>;
|
||||
@@ -138,18 +130,13 @@ impl BanksClientExt for BanksClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_account_with_commitment(
|
||||
&mut self,
|
||||
address: Pubkey,
|
||||
commitment: CommitmentLevel,
|
||||
) -> io::Result<Option<Account>> {
|
||||
self.get_account_with_commitment_and_context(context::current(), address, commitment)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_account(&mut self, address: Pubkey) -> io::Result<Option<Account>> {
|
||||
self.get_account_with_commitment(address, CommitmentLevel::default())
|
||||
.await
|
||||
self.get_account_with_commitment_and_context(
|
||||
context::current(),
|
||||
address,
|
||||
CommitmentLevel::default(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_balance_with_commitment(
|
||||
@@ -204,7 +191,7 @@ pub async fn start_client(
|
||||
}
|
||||
|
||||
pub async fn start_tcp_client<T: ToSocketAddrs>(addr: T) -> io::Result<BanksClient> {
|
||||
let transport = tcp::connect(addr, Bincode::default).await?;
|
||||
let transport = tcp::connect(addr, Bincode::default()).await?;
|
||||
BanksClient::new(client::Config::default(), transport).spawn()
|
||||
}
|
||||
|
||||
@@ -213,7 +200,7 @@ mod tests {
|
||||
use super::*;
|
||||
use solana_banks_server::banks_server::start_local_server;
|
||||
use solana_runtime::{bank::Bank, bank_forks::BankForks, genesis_utils::create_genesis_config};
|
||||
use solana_sdk::{message::Message, signature::Signer, system_instruction};
|
||||
use solana_sdk::{message::Message, pubkey::Pubkey, signature::Signer, system_instruction};
|
||||
use std::sync::{Arc, RwLock};
|
||||
use tarpc::transport;
|
||||
use tokio::{runtime::Runtime, time::delay_for};
|
||||
@@ -235,7 +222,7 @@ mod tests {
|
||||
&genesis.genesis_config,
|
||||
))));
|
||||
|
||||
let bob_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let bob_pubkey = Pubkey::new_rand();
|
||||
let mint_pubkey = genesis.mint_keypair.pubkey();
|
||||
let instruction = system_instruction::transfer(&mint_pubkey, &bob_pubkey, 1);
|
||||
let message = Message::new(&[instruction], Some(&mint_pubkey));
|
||||
@@ -265,7 +252,7 @@ mod tests {
|
||||
))));
|
||||
|
||||
let mint_pubkey = &genesis.mint_keypair.pubkey();
|
||||
let bob_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let bob_pubkey = Pubkey::new_rand();
|
||||
let instruction = system_instruction::transfer(&mint_pubkey, &bob_pubkey, 1);
|
||||
let message = Message::new(&[instruction], Some(&mint_pubkey));
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-banks-interface"
|
||||
version = "1.4.2"
|
||||
version = "1.3.6"
|
||||
description = "Solana banks RPC interface"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -10,8 +10,8 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0.112", features = ["derive"] }
|
||||
solana-sdk = { path = "../sdk", version = "1.4.2" }
|
||||
tarpc = { version = "0.22.0", features = ["full"] }
|
||||
solana-sdk = { path = "../sdk", version = "1.3.6" }
|
||||
tarpc = { version = "0.21.0", features = ["full"] }
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib"]
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-banks-server"
|
||||
version = "1.4.2"
|
||||
version = "1.3.6"
|
||||
description = "Solana banks server"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -11,12 +11,10 @@ edition = "2018"
|
||||
[dependencies]
|
||||
bincode = "1.3.1"
|
||||
futures = "0.3"
|
||||
log = "0.4.8"
|
||||
solana-banks-interface = { path = "../banks-interface", version = "1.4.2" }
|
||||
solana-runtime = { path = "../runtime", version = "1.4.2" }
|
||||
solana-sdk = { path = "../sdk", version = "1.4.2" }
|
||||
solana-metrics = { path = "../metrics", version = "1.4.2" }
|
||||
tarpc = { version = "0.22.0", features = ["full"] }
|
||||
solana-banks-interface = { path = "../banks-interface", version = "1.3.6" }
|
||||
solana-runtime = { path = "../runtime", version = "1.3.6" }
|
||||
solana-sdk = { path = "../sdk", version = "1.3.6" }
|
||||
tarpc = { version = "0.21.0", features = ["full"] }
|
||||
tokio = "0.2"
|
||||
tokio-serde = { version = "0.6", features = ["bincode"] }
|
||||
|
||||
|
@@ -1,4 +1,3 @@
|
||||
use crate::send_transaction_service::{SendTransactionService, TransactionInfo};
|
||||
use bincode::{deserialize, serialize};
|
||||
use futures::{
|
||||
future,
|
||||
@@ -9,6 +8,7 @@ use solana_runtime::{
|
||||
bank::Bank,
|
||||
bank_forks::BankForks,
|
||||
commitment::{BlockCommitmentCache, CommitmentSlots},
|
||||
send_transaction_service::{SendTransactionService, TransactionInfo},
|
||||
};
|
||||
use solana_sdk::{
|
||||
account::Account,
|
||||
@@ -23,8 +23,9 @@ use solana_sdk::{
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
io,
|
||||
net::{Ipv4Addr, SocketAddr},
|
||||
net::SocketAddr,
|
||||
sync::{
|
||||
atomic::AtomicBool,
|
||||
mpsc::{channel, Receiver, Sender},
|
||||
Arc, RwLock,
|
||||
},
|
||||
@@ -87,7 +88,12 @@ impl BanksServer {
|
||||
let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::new(
|
||||
HashMap::default(),
|
||||
0,
|
||||
CommitmentSlots::new_from_slot(slot),
|
||||
CommitmentSlots {
|
||||
slot,
|
||||
root: 0,
|
||||
highest_confirmed_slot: 0,
|
||||
highest_confirmed_root: 0,
|
||||
},
|
||||
)));
|
||||
Builder::new()
|
||||
.name("solana-bank-forks-client".to_string())
|
||||
@@ -239,18 +245,19 @@ pub async fn start_tcp_server(
|
||||
.filter_map(|r| future::ready(r.ok()))
|
||||
.map(server::BaseChannel::with_defaults)
|
||||
// Limit channels to 1 per IP.
|
||||
.max_channels_per_key(1, |t| {
|
||||
t.as_ref()
|
||||
.peer_addr()
|
||||
.map(|x| x.ip())
|
||||
.unwrap_or_else(|_| Ipv4Addr::new(0, 0, 0, 0).into())
|
||||
})
|
||||
.max_channels_per_key(1, |t| t.as_ref().peer_addr().unwrap().ip())
|
||||
// serve is generated by the service attribute. It takes as input any type implementing
|
||||
// the generated Banks trait.
|
||||
.map(move |chan| {
|
||||
let (sender, receiver) = channel();
|
||||
let exit_send_transaction_service = Arc::new(AtomicBool::new(false));
|
||||
|
||||
SendTransactionService::new(tpu_addr, &bank_forks, receiver);
|
||||
SendTransactionService::new(
|
||||
tpu_addr,
|
||||
&bank_forks,
|
||||
&exit_send_transaction_service,
|
||||
receiver,
|
||||
);
|
||||
|
||||
let server =
|
||||
BanksServer::new(bank_forks.clone(), block_commitment_cache.clone(), sender);
|
||||
|
@@ -1,6 +1,2 @@
|
||||
pub mod banks_server;
|
||||
pub mod rpc_banks_service;
|
||||
pub mod send_transaction_service;
|
||||
|
||||
#[macro_use]
|
||||
extern crate solana_metrics;
|
||||
|
@@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-bench-exchange"
|
||||
version = "1.4.2"
|
||||
version = "1.3.6"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -15,24 +15,24 @@ log = "0.4.8"
|
||||
num-derive = "0.3"
|
||||
num-traits = "0.2"
|
||||
rand = "0.7.0"
|
||||
rayon = "1.4.0"
|
||||
rayon = "1.3.1"
|
||||
serde_json = "1.0.56"
|
||||
serde_yaml = "0.8.13"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.4.2" }
|
||||
solana-core = { path = "../core", version = "1.4.2" }
|
||||
solana-genesis = { path = "../genesis", version = "1.4.2" }
|
||||
solana-client = { path = "../client", version = "1.4.2" }
|
||||
solana-faucet = { path = "../faucet", version = "1.4.2" }
|
||||
solana-exchange-program = { path = "../programs/exchange", version = "1.4.2" }
|
||||
solana-logger = { path = "../logger", version = "1.4.2" }
|
||||
solana-metrics = { path = "../metrics", version = "1.4.2" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.4.2" }
|
||||
solana-runtime = { path = "../runtime", version = "1.4.2" }
|
||||
solana-sdk = { path = "../sdk", version = "1.4.2" }
|
||||
solana-version = { path = "../version", version = "1.4.2" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.3.6" }
|
||||
solana-core = { path = "../core", version = "1.3.6" }
|
||||
solana-genesis = { path = "../genesis", version = "1.3.6" }
|
||||
solana-client = { path = "../client", version = "1.3.6" }
|
||||
solana-faucet = { path = "../faucet", version = "1.3.6" }
|
||||
solana-exchange-program = { path = "../programs/exchange", version = "1.3.6" }
|
||||
solana-logger = { path = "../logger", version = "1.3.6" }
|
||||
solana-metrics = { path = "../metrics", version = "1.3.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.3.6" }
|
||||
solana-runtime = { path = "../runtime", version = "1.3.6" }
|
||||
solana-sdk = { path = "../sdk", version = "1.3.6" }
|
||||
solana-version = { path = "../version", version = "1.3.6" }
|
||||
|
||||
[dev-dependencies]
|
||||
solana-local-cluster = { path = "../local-cluster", version = "1.4.2" }
|
||||
solana-local-cluster = { path = "../local-cluster", version = "1.3.6" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -2,19 +2,18 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-bench-streamer"
|
||||
version = "1.4.2"
|
||||
version = "1.3.6"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33.1"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.4.2" }
|
||||
solana-streamer = { path = "../streamer", version = "1.4.2" }
|
||||
solana-logger = { path = "../logger", version = "1.4.2" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.4.2" }
|
||||
solana-version = { path = "../version", version = "1.4.2" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.3.6" }
|
||||
solana-streamer = { path = "../streamer", version = "1.3.6" }
|
||||
solana-logger = { path = "../logger", version = "1.3.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.3.6" }
|
||||
solana-version = { path = "../version", version = "1.3.6" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -27,7 +27,7 @@ fn producer(addr: &SocketAddr, exit: Arc<AtomicBool>) -> JoinHandle<()> {
|
||||
let mut num = 0;
|
||||
for p in &msgs.packets {
|
||||
let a = p.meta.addr();
|
||||
assert!(p.meta.size <= PACKET_DATA_SIZE);
|
||||
assert!(p.meta.size < PACKET_DATA_SIZE);
|
||||
send.send_to(&p.data[..p.meta.size], &a).unwrap();
|
||||
num += 1;
|
||||
}
|
||||
|
@@ -2,36 +2,35 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-bench-tps"
|
||||
version = "1.4.2"
|
||||
version = "1.3.6"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
bincode = "1.3.1"
|
||||
clap = "2.33.1"
|
||||
log = "0.4.8"
|
||||
rayon = "1.4.0"
|
||||
rayon = "1.3.1"
|
||||
serde_json = "1.0.56"
|
||||
serde_yaml = "0.8.13"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.4.2" }
|
||||
solana-core = { path = "../core", version = "1.4.2" }
|
||||
solana-genesis = { path = "../genesis", version = "1.4.2" }
|
||||
solana-client = { path = "../client", version = "1.4.2" }
|
||||
solana-faucet = { path = "../faucet", version = "1.4.2" }
|
||||
solana-logger = { path = "../logger", version = "1.4.2" }
|
||||
solana-metrics = { path = "../metrics", version = "1.4.2" }
|
||||
solana-measure = { path = "../measure", version = "1.4.2" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.4.2" }
|
||||
solana-runtime = { path = "../runtime", version = "1.4.2" }
|
||||
solana-sdk = { path = "../sdk", version = "1.4.2" }
|
||||
solana-version = { path = "../version", version = "1.4.2" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.3.6" }
|
||||
solana-core = { path = "../core", version = "1.3.6" }
|
||||
solana-genesis = { path = "../genesis", version = "1.3.6" }
|
||||
solana-client = { path = "../client", version = "1.3.6" }
|
||||
solana-faucet = { path = "../faucet", version = "1.3.6" }
|
||||
solana-logger = { path = "../logger", version = "1.3.6" }
|
||||
solana-metrics = { path = "../metrics", version = "1.3.6" }
|
||||
solana-measure = { path = "../measure", version = "1.3.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.3.6" }
|
||||
solana-runtime = { path = "../runtime", version = "1.3.6" }
|
||||
solana-sdk = { path = "../sdk", version = "1.3.6" }
|
||||
solana-version = { path = "../version", version = "1.3.6" }
|
||||
|
||||
[dev-dependencies]
|
||||
serial_test = "0.4.0"
|
||||
serial_test_derive = "0.4.0"
|
||||
solana-local-cluster = { path = "../local-cluster", version = "1.4.2" }
|
||||
solana-local-cluster = { path = "../local-cluster", version = "1.3.6" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
31
cargo
31
cargo
@@ -1,31 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# shellcheck source=ci/rust-version.sh
|
||||
here=$(dirname "$0")
|
||||
|
||||
source "${here}"/ci/rust-version.sh all
|
||||
|
||||
toolchain=
|
||||
case "$1" in
|
||||
stable)
|
||||
# shellcheck disable=SC2054 # rust_stable is sourced from rust-version.sh
|
||||
toolchain="$rust_stable"
|
||||
shift
|
||||
;;
|
||||
nightly)
|
||||
# shellcheck disable=SC2054 # rust_nightly is sourced from rust-version.sh
|
||||
toolchain="$rust_nightly"
|
||||
shift
|
||||
;;
|
||||
+*)
|
||||
toolchain="${1#+}"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
# shellcheck disable=SC2054 # rust_stable is sourced from rust-version.sh
|
||||
toolchain="$rust_stable"
|
||||
;;
|
||||
esac
|
||||
|
||||
set -x
|
||||
exec cargo "+${toolchain}" "${@}"
|
@@ -1,5 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
here=$(dirname "$0")
|
||||
set -x
|
||||
exec cargo run --manifest-path $here/sdk/cargo-build-bpf/Cargo.toml -- --bpf-sdk $here/sdk/bpf "$@"
|
@@ -95,14 +95,12 @@ elif [[ -n $CI_BRANCH ]]; then
|
||||
BRANCH="$CI_BRANCH"
|
||||
fi
|
||||
|
||||
if [[ -z "$CHANNEL" ]]; then
|
||||
if [[ $BRANCH = "$STABLE_CHANNEL" ]]; then
|
||||
CHANNEL=stable
|
||||
elif [[ $BRANCH = "$EDGE_CHANNEL" ]]; then
|
||||
CHANNEL=edge
|
||||
elif [[ $BRANCH = "$BETA_CHANNEL" ]]; then
|
||||
CHANNEL=beta
|
||||
fi
|
||||
if [[ $BRANCH = "$STABLE_CHANNEL" ]]; then
|
||||
CHANNEL=stable
|
||||
elif [[ $BRANCH = "$EDGE_CHANNEL" ]]; then
|
||||
CHANNEL=edge
|
||||
elif [[ $BRANCH = "$BETA_CHANNEL" ]]; then
|
||||
CHANNEL=beta
|
||||
fi
|
||||
|
||||
echo EDGE_CHANNEL="$EDGE_CHANNEL"
|
||||
|
@@ -1,4 +1,4 @@
|
||||
FROM solanalabs/rust:1.46.0
|
||||
FROM solanalabs/rust:1.45.1
|
||||
ARG date
|
||||
|
||||
RUN set -x \
|
||||
|
@@ -20,7 +20,7 @@ To update the pinned version:
|
||||
specific YYYY-MM-DD that is desired (default is today's build).
|
||||
Check https://rust-lang.github.io/rustup-components-history/ for build
|
||||
status
|
||||
1. Update `ci/rust-version.sh` to reflect the new nightly `YYYY-MM-DD`
|
||||
1. Update `ci/rust-version.sh` to reflect the new nightly `YYY-MM-DD`
|
||||
1. Run `SOLANA_ALLOCATE_TTY=1 SOLANA_DOCKER_RUN_NOSETUID=1 ci/docker-run.sh --nopull solanalabs/rust-nightly:YYYY-MM-DD ci/test-checks.sh`
|
||||
and `SOLANA_ALLOCATE_TTY=1 SOLANA_DOCKER_RUN_NOSETUID=1 ci/docker-run.sh --nopull solanalabs/rust-nightly:YYYY-MM-DD ci/test-coverage.sh [args]...`
|
||||
to confirm the new nightly image builds. Fix any issues as needed
|
||||
|
@@ -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.46.0
|
||||
FROM rust:1.45.1
|
||||
|
||||
# Add Google Protocol Buffers for Libra's metrics library.
|
||||
ENV PROTOC_VERSION 3.8.0
|
||||
|
@@ -26,9 +26,6 @@ declare print_free_tree=(
|
||||
':runtime/src/**.rs'
|
||||
':sdk/bpf/rust/rust-utils/**.rs'
|
||||
':sdk/**.rs'
|
||||
':^sdk/cargo-build-bpf/**.rs'
|
||||
':^sdk/src/program_option.rs'
|
||||
':^sdk/src/program_stubs.rs'
|
||||
':programs/**.rs'
|
||||
':^**bin**.rs'
|
||||
':^**bench**.rs'
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python2.7
|
||||
#
|
||||
# This script figures the order in which workspace crates must be published to
|
||||
# crates.io. Along the way it also ensures there are no circular dependencies
|
||||
@@ -45,27 +45,21 @@ def get_packages():
|
||||
sorted_dependency_graph = []
|
||||
max_iterations = pow(len(dependency_graph),2)
|
||||
while dependency_graph:
|
||||
deleted_packages = []
|
||||
if max_iterations == 0:
|
||||
# One day be more helpful and find the actual cycle for the user...
|
||||
sys.exit('Error: Circular dependency suspected between these packages: \n {}\n'.format('\n '.join(dependency_graph.keys())))
|
||||
|
||||
max_iterations -= 1
|
||||
|
||||
for package, dependencies in dependency_graph.items():
|
||||
if package in deleted_packages:
|
||||
continue
|
||||
for dependency in dependencies:
|
||||
if dependency in dependency_graph:
|
||||
break
|
||||
else:
|
||||
deleted_packages.append(package)
|
||||
del dependency_graph[package]
|
||||
sorted_dependency_graph.append((package, manifest_path[package]))
|
||||
|
||||
dependency_graph = {p: d for p, d in dependency_graph.items() if not p in deleted_packages }
|
||||
|
||||
|
||||
return sorted_dependency_graph
|
||||
|
||||
for package, manifest in get_packages():
|
||||
print(os.path.relpath(manifest))
|
||||
print os.path.relpath(manifest)
|
||||
|
@@ -38,7 +38,7 @@ for Cargo_toml in $Cargo_tomls; do
|
||||
crate_name=$(grep -m 1 '^name = ' "$Cargo_toml" | cut -f 3 -d ' ' | tr -d \")
|
||||
|
||||
if grep -q "^publish = false" "$Cargo_toml"; then
|
||||
echo "$crate_name is marked as unpublishable"
|
||||
echo "$crate_name is is marked as unpublishable"
|
||||
continue
|
||||
fi
|
||||
|
||||
|
@@ -85,24 +85,23 @@ echo --- Creating release tarball
|
||||
source ci/rust-version.sh stable
|
||||
scripts/cargo-install-all.sh +"$rust_stable" "${RELEASE_BASENAME}"
|
||||
|
||||
mkdir -p "${RELEASE_BASENAME}"/bin/sdk/bpf
|
||||
cp -a sdk/bpf/* "${RELEASE_BASENAME}"/bin/sdk/bpf
|
||||
|
||||
tar cvf "${TARBALL_BASENAME}"-$TARGET.tar "${RELEASE_BASENAME}"
|
||||
bzip2 "${TARBALL_BASENAME}"-$TARGET.tar
|
||||
cp "${RELEASE_BASENAME}"/bin/solana-install-init solana-install-init-$TARGET
|
||||
cp "${RELEASE_BASENAME}"/version.yml "${TARBALL_BASENAME}"-$TARGET.yml
|
||||
)
|
||||
|
||||
# Maybe tarballs are platform agnostic, only publish them from the Linux build
|
||||
# Metrics tarball is platform agnostic, only publish it from Linux
|
||||
MAYBE_TARBALLS=
|
||||
if [[ "$CI_OS_NAME" = linux ]]; then
|
||||
metrics/create-metrics-tarball.sh
|
||||
(
|
||||
set -x
|
||||
sdk/bpf/scripts/package.sh
|
||||
[[ -f bpf-sdk.tar.bz2 ]]
|
||||
|
||||
)
|
||||
MAYBE_TARBALLS="bpf-sdk.tar.bz2"
|
||||
MAYBE_TARBALLS="bpf-sdk.tar.bz2 solana-metrics.tar.bz2"
|
||||
fi
|
||||
|
||||
source ci/upload-ci-artifact.sh
|
||||
|
@@ -18,13 +18,13 @@
|
||||
if [[ -n $RUST_STABLE_VERSION ]]; then
|
||||
stable_version="$RUST_STABLE_VERSION"
|
||||
else
|
||||
stable_version=1.46.0
|
||||
stable_version=1.45.1
|
||||
fi
|
||||
|
||||
if [[ -n $RUST_NIGHTLY_VERSION ]]; then
|
||||
nightly_version="$RUST_NIGHTLY_VERSION"
|
||||
else
|
||||
nightly_version=2020-08-17
|
||||
nightly_version=2020-07-27
|
||||
fi
|
||||
|
||||
|
||||
|
@@ -41,34 +41,21 @@ if [[ $CI_BASE_BRANCH = "$EDGE_CHANNEL" ]]; then
|
||||
echo "$0: [tree (for outdated Cargo.lock sync)|check (for compilation error)|update -p foo --precise x.y.z (for your Cargo.toml update)] ..." >&2
|
||||
exit "$check_status"
|
||||
fi
|
||||
|
||||
# Ensure nightly and --benches
|
||||
_ 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
|
||||
|
||||
# Ensure nightly and --benches
|
||||
_ scripts/cargo-for-all-lock-files.sh +"$rust_nightly" check --locked --all-targets
|
||||
|
||||
_ ci/order-crates-for-publishing.py
|
||||
_ cargo +"$rust_stable" fmt --all -- --check
|
||||
|
||||
# -Z... is needed because of clippy bug: https://github.com/rust-lang/rust-clippy/issues/4612
|
||||
# run nightly clippy for `sdk/` as there's a moderate amount of nightly-only code there
|
||||
_ cargo +"$rust_nightly" clippy \
|
||||
-Zunstable-options --workspace --all-targets \
|
||||
-- --deny=warnings --allow=clippy::stable_sort_primitive
|
||||
_ cargo +"$rust_nightly" clippy -Zunstable-options --workspace --all-targets -- --deny=warnings
|
||||
|
||||
cargo_audit_ignores=(
|
||||
# failure is officially deprecated/unmaintained
|
||||
#
|
||||
# Blocked on multiple upstream crates removing their `failure` dependency.
|
||||
--ignore RUSTSEC-2020-0036
|
||||
|
||||
# `net2` crate has been deprecated; use `socket2` instead
|
||||
#
|
||||
# Blocked on https://github.com/paritytech/jsonrpc/issues/575
|
||||
--ignore RUSTSEC-2020-0016
|
||||
)
|
||||
_ scripts/cargo-for-all-lock-files.sh +"$rust_stable" audit "${cargo_audit_ignores[@]}"
|
||||
_ scripts/cargo-for-all-lock-files.sh +"$rust_stable" audit --ignore RUSTSEC-2020-0002 --ignore RUSTSEC-2020-0008
|
||||
|
||||
{
|
||||
cd programs/bpf
|
||||
@@ -79,9 +66,7 @@ _ scripts/cargo-for-all-lock-files.sh +"$rust_stable" audit "${cargo_audit_ignor
|
||||
cd "$project"
|
||||
_ cargo +"$rust_stable" fmt -- --check
|
||||
_ cargo +"$rust_nightly" test
|
||||
_ cargo +"$rust_nightly" clippy -- --deny=warnings \
|
||||
--allow=clippy::missing_safety_doc \
|
||||
--allow=clippy::stable_sort_primitive
|
||||
_ cargo +"$rust_nightly" clippy -- --deny=warnings --allow=clippy::missing_safety_doc
|
||||
)
|
||||
done
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-clap-utils"
|
||||
version = "1.4.2"
|
||||
version = "1.3.6"
|
||||
description = "Solana utilities for the clap"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -11,8 +11,8 @@ edition = "2018"
|
||||
[dependencies]
|
||||
clap = "2.33.0"
|
||||
rpassword = "4.0"
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "1.4.2" }
|
||||
solana-sdk = { path = "../sdk", version = "1.4.2" }
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "1.3.6" }
|
||||
solana-sdk = { path = "../sdk", version = "1.3.6" }
|
||||
thiserror = "1.0.20"
|
||||
tiny-bip39 = "0.7.0"
|
||||
url = "2.1.0"
|
||||
|
@@ -15,7 +15,7 @@ pub fn commitment_arg_with_default<'a, 'b>(default_value: &'static str) -> Arg<'
|
||||
Arg::with_name(COMMITMENT_ARG.name)
|
||||
.long(COMMITMENT_ARG.long)
|
||||
.takes_value(true)
|
||||
.possible_values(&["recent", "single", "singleGossip", "root", "max"])
|
||||
.possible_values(&["recent", "single", "root", "max"])
|
||||
.default_value(default_value)
|
||||
.value_name("COMMITMENT_LEVEL")
|
||||
.help(COMMITMENT_ARG.help)
|
||||
|
@@ -1,19 +0,0 @@
|
||||
use crate::{input_validators, ArgConstant};
|
||||
use clap::Arg;
|
||||
|
||||
pub const FEE_PAYER_ARG: ArgConstant<'static> = ArgConstant {
|
||||
name: "fee_payer",
|
||||
long: "fee-payer",
|
||||
help: "Specify the fee-payer account. This may be a keypair file, the ASK keyword \n\
|
||||
or the pubkey of an offline signer, provided an appropriate --signer argument \n\
|
||||
is also passed. Defaults to the client keypair.",
|
||||
};
|
||||
|
||||
pub fn fee_payer_arg<'a, 'b>() -> Arg<'a, 'b> {
|
||||
Arg::with_name(FEE_PAYER_ARG.name)
|
||||
.long(FEE_PAYER_ARG.long)
|
||||
.takes_value(true)
|
||||
.value_name("KEYPAIR")
|
||||
.validator(input_validators::is_valid_signer)
|
||||
.help(FEE_PAYER_ARG.help)
|
||||
}
|
@@ -8,7 +8,6 @@ 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},
|
||||
@@ -179,10 +178,6 @@ pub fn lamports_of_sol(matches: &ArgMatches<'_>, name: &str) -> Option<u64> {
|
||||
value_of(matches, name).map(sol_to_lamports)
|
||||
}
|
||||
|
||||
pub fn cluster_type_of(matches: &ArgMatches<'_>, name: &str) -> Option<ClusterType> {
|
||||
value_of(matches, name)
|
||||
}
|
||||
|
||||
pub fn commitment_of(matches: &ArgMatches<'_>, name: &str) -> Option<CommitmentConfig> {
|
||||
matches.value_of(name).map(|value| match value {
|
||||
"max" => CommitmentConfig::max(),
|
||||
@@ -228,8 +223,8 @@ mod tests {
|
||||
assert_eq!(values_of(&matches, "multiple"), Some(vec![50, 39]));
|
||||
assert_eq!(values_of::<u64>(&matches, "single"), None);
|
||||
|
||||
let pubkey0 = solana_sdk::pubkey::new_rand();
|
||||
let pubkey1 = solana_sdk::pubkey::new_rand();
|
||||
let pubkey0 = Pubkey::new_rand();
|
||||
let pubkey1 = Pubkey::new_rand();
|
||||
let matches = app().clone().get_matches_from(vec![
|
||||
"test",
|
||||
"--multiple",
|
||||
@@ -251,7 +246,7 @@ mod tests {
|
||||
assert_eq!(value_of(&matches, "single"), Some(50));
|
||||
assert_eq!(value_of::<u64>(&matches, "multiple"), None);
|
||||
|
||||
let pubkey = solana_sdk::pubkey::new_rand();
|
||||
let pubkey = Pubkey::new_rand();
|
||||
let matches = app()
|
||||
.clone()
|
||||
.get_matches_from(vec!["test", "--single", &pubkey.to_string()]);
|
||||
@@ -331,8 +326,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_pubkeys_sigs_of() {
|
||||
let key1 = solana_sdk::pubkey::new_rand();
|
||||
let key2 = solana_sdk::pubkey::new_rand();
|
||||
let key1 = Pubkey::new_rand();
|
||||
let key2 = Pubkey::new_rand();
|
||||
let sig1 = Keypair::new().sign_message(&[0u8]);
|
||||
let sig2 = Keypair::new().sign_message(&[1u8]);
|
||||
let signer1 = format!("{}={}", key1, sig1);
|
||||
|
@@ -11,7 +11,6 @@ use solana_remote_wallet::{
|
||||
remote_wallet::{maybe_wallet_manager, RemoteWalletError, RemoteWalletManager},
|
||||
};
|
||||
use solana_sdk::{
|
||||
hash::Hash,
|
||||
pubkey::Pubkey,
|
||||
signature::{
|
||||
keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair,
|
||||
@@ -26,81 +25,6 @@ use std::{
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
pub struct SignOnly {
|
||||
pub blockhash: Hash,
|
||||
pub present_signers: Vec<(Pubkey, Signature)>,
|
||||
pub absent_signers: Vec<Pubkey>,
|
||||
pub bad_signers: Vec<Pubkey>,
|
||||
}
|
||||
|
||||
impl SignOnly {
|
||||
pub fn has_all_signers(&self) -> bool {
|
||||
self.absent_signers.is_empty() && self.bad_signers.is_empty()
|
||||
}
|
||||
|
||||
pub fn presigner_of(&self, pubkey: &Pubkey) -> Option<Presigner> {
|
||||
presigner_from_pubkey_sigs(pubkey, &self.present_signers)
|
||||
}
|
||||
}
|
||||
pub type CliSigners = Vec<Box<dyn Signer>>;
|
||||
pub type SignerIndex = usize;
|
||||
pub struct CliSignerInfo {
|
||||
pub signers: CliSigners,
|
||||
}
|
||||
|
||||
impl CliSignerInfo {
|
||||
pub fn index_of(&self, pubkey: Option<Pubkey>) -> Option<usize> {
|
||||
if let Some(pubkey) = pubkey {
|
||||
self.signers
|
||||
.iter()
|
||||
.position(|signer| signer.pubkey() == pubkey)
|
||||
} else {
|
||||
Some(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DefaultSigner {
|
||||
pub arg_name: String,
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
impl DefaultSigner {
|
||||
pub fn generate_unique_signers(
|
||||
&self,
|
||||
bulk_signers: Vec<Option<Box<dyn Signer>>>,
|
||||
matches: &ArgMatches<'_>,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliSignerInfo, Box<dyn error::Error>> {
|
||||
let mut unique_signers = vec![];
|
||||
|
||||
// Determine if the default signer is needed
|
||||
if bulk_signers.iter().any(|signer| signer.is_none()) {
|
||||
let default_signer = self.signer_from_path(matches, wallet_manager)?;
|
||||
unique_signers.push(default_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 {
|
||||
signers: unique_signers,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn signer_from_path(
|
||||
&self,
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum KeypairUrl {
|
||||
Ask,
|
||||
Filepath(String),
|
||||
@@ -154,7 +78,7 @@ pub fn signer_from_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 find keypair file: {} error: {}", path, e),
|
||||
)
|
||||
.into()),
|
||||
Ok(file) => Ok(Box::new(file)),
|
||||
@@ -225,7 +149,7 @@ pub fn resolve_signer_from_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 find keypair file: {} error: {}", path, e),
|
||||
)
|
||||
.into()),
|
||||
Ok(_) => Ok(Some(path.to_string())),
|
||||
@@ -298,24 +222,7 @@ pub fn keypair_from_seed_phrase(
|
||||
keypair_from_seed_phrase_and_passphrase(&seed_phrase, &passphrase)?
|
||||
} else {
|
||||
let sanitized = sanitize_seed_phrase(seed_phrase);
|
||||
let parse_language_fn = || {
|
||||
for language in &[
|
||||
Language::English,
|
||||
Language::ChineseSimplified,
|
||||
Language::ChineseTraditional,
|
||||
Language::Japanese,
|
||||
Language::Spanish,
|
||||
Language::Korean,
|
||||
Language::French,
|
||||
Language::Italian,
|
||||
] {
|
||||
if let Ok(mnemonic) = Mnemonic::from_phrase(&sanitized, *language) {
|
||||
return Ok(mnemonic);
|
||||
}
|
||||
}
|
||||
Err("Can't get mnemonic from seed phrases")
|
||||
};
|
||||
let mnemonic = parse_language_fn()?;
|
||||
let mnemonic = Mnemonic::from_phrase(&sanitized, Language::English)?;
|
||||
let passphrase = prompt_passphrase(&passphrase_prompt)?;
|
||||
let seed = Seed::new(&mnemonic, &passphrase);
|
||||
keypair_from_seed(seed.as_bytes())?
|
||||
|
@@ -24,9 +24,7 @@ impl std::fmt::Debug for DisplayError {
|
||||
}
|
||||
|
||||
pub mod commitment;
|
||||
pub mod fee_payer;
|
||||
pub mod input_parsers;
|
||||
pub mod input_validators;
|
||||
pub mod keypair;
|
||||
pub mod nonce;
|
||||
pub mod offline;
|
||||
|
@@ -1,50 +0,0 @@
|
||||
use crate::{input_validators::*, offline::BLOCKHASH_ARG, ArgConstant};
|
||||
use clap::{App, Arg};
|
||||
|
||||
pub const NONCE_ARG: ArgConstant<'static> = ArgConstant {
|
||||
name: "nonce",
|
||||
long: "nonce",
|
||||
help: "Provide the nonce account to use when creating a nonced \n\
|
||||
transaction. Nonced transactions are useful when a transaction \n\
|
||||
requires a lengthy signing process. Learn more about nonced \n\
|
||||
transactions at https://docs.solana.com/offline-signing/durable-nonce",
|
||||
};
|
||||
|
||||
pub const NONCE_AUTHORITY_ARG: ArgConstant<'static> = ArgConstant {
|
||||
name: "nonce_authority",
|
||||
long: "nonce-authority",
|
||||
help: "Provide the nonce authority keypair to use when signing a nonced transaction",
|
||||
};
|
||||
|
||||
fn nonce_arg<'a, 'b>() -> Arg<'a, 'b> {
|
||||
Arg::with_name(NONCE_ARG.name)
|
||||
.long(NONCE_ARG.long)
|
||||
.takes_value(true)
|
||||
.value_name("PUBKEY")
|
||||
.requires(BLOCKHASH_ARG.name)
|
||||
.validator(is_valid_pubkey)
|
||||
.help(NONCE_ARG.help)
|
||||
}
|
||||
|
||||
pub fn nonce_authority_arg<'a, 'b>() -> Arg<'a, 'b> {
|
||||
Arg::with_name(NONCE_AUTHORITY_ARG.name)
|
||||
.long(NONCE_AUTHORITY_ARG.long)
|
||||
.takes_value(true)
|
||||
.value_name("KEYPAIR")
|
||||
.validator(is_valid_signer)
|
||||
.help(NONCE_AUTHORITY_ARG.help)
|
||||
}
|
||||
|
||||
pub trait NonceArgs {
|
||||
fn nonce_args(self, global: bool) -> Self;
|
||||
}
|
||||
|
||||
impl NonceArgs for App<'_, '_> {
|
||||
fn nonce_args(self, global: bool) -> Self {
|
||||
self.arg(nonce_arg().global(global)).arg(
|
||||
nonce_authority_arg()
|
||||
.requires(NONCE_ARG.name)
|
||||
.global(global),
|
||||
)
|
||||
}
|
||||
}
|
@@ -1,5 +1,4 @@
|
||||
use crate::{input_validators::*, ArgConstant};
|
||||
use clap::{App, Arg};
|
||||
use crate::ArgConstant;
|
||||
|
||||
pub const BLOCKHASH_ARG: ArgConstant<'static> = ArgConstant {
|
||||
name: "blockhash",
|
||||
@@ -18,61 +17,3 @@ pub const SIGNER_ARG: ArgConstant<'static> = ArgConstant {
|
||||
long: "signer",
|
||||
help: "Provide a public-key/signature pair for the transaction",
|
||||
};
|
||||
|
||||
pub fn blockhash_arg<'a, 'b>() -> Arg<'a, 'b> {
|
||||
Arg::with_name(BLOCKHASH_ARG.name)
|
||||
.long(BLOCKHASH_ARG.long)
|
||||
.takes_value(true)
|
||||
.value_name("BLOCKHASH")
|
||||
.validator(is_hash)
|
||||
.help(BLOCKHASH_ARG.help)
|
||||
}
|
||||
|
||||
pub fn sign_only_arg<'a, 'b>() -> Arg<'a, 'b> {
|
||||
Arg::with_name(SIGN_ONLY_ARG.name)
|
||||
.long(SIGN_ONLY_ARG.long)
|
||||
.takes_value(false)
|
||||
.requires(BLOCKHASH_ARG.name)
|
||||
.help(SIGN_ONLY_ARG.help)
|
||||
}
|
||||
|
||||
fn signer_arg<'a, 'b>() -> Arg<'a, 'b> {
|
||||
Arg::with_name(SIGNER_ARG.name)
|
||||
.long(SIGNER_ARG.long)
|
||||
.takes_value(true)
|
||||
.value_name("PUBKEY=SIGNATURE")
|
||||
.validator(is_pubkey_sig)
|
||||
.requires(BLOCKHASH_ARG.name)
|
||||
.multiple(true)
|
||||
.help(SIGNER_ARG.help)
|
||||
}
|
||||
|
||||
pub trait ArgsConfig {
|
||||
fn blockhash_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> {
|
||||
arg
|
||||
}
|
||||
fn sign_only_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> {
|
||||
arg
|
||||
}
|
||||
fn signer_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> {
|
||||
arg
|
||||
}
|
||||
}
|
||||
|
||||
pub trait OfflineArgs {
|
||||
fn offline_args(self) -> Self;
|
||||
fn offline_args_config(self, config: &dyn ArgsConfig) -> Self;
|
||||
}
|
||||
|
||||
impl OfflineArgs for App<'_, '_> {
|
||||
fn offline_args_config(self, config: &dyn ArgsConfig) -> Self {
|
||||
self.arg(config.blockhash_arg(blockhash_arg()))
|
||||
.arg(config.sign_only_arg(sign_only_arg()))
|
||||
.arg(config.signer_arg(signer_arg()))
|
||||
}
|
||||
fn offline_args(self) -> Self {
|
||||
struct NullArgsConfig {}
|
||||
impl ArgsConfig for NullArgsConfig {}
|
||||
self.offline_args_config(&NullArgsConfig {})
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-cli-config"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.4.2"
|
||||
version = "1.3.6"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
|
@@ -82,8 +82,8 @@ impl Config {
|
||||
return "".to_string();
|
||||
}
|
||||
let mut url = json_rpc_url.unwrap();
|
||||
let port = url.port().unwrap_or(8899);
|
||||
url.set_port(Some(port + 3)).expect("unable to set port");
|
||||
let port = url.port_or_known_default().unwrap_or(80);
|
||||
url.set_port(Some(port + 2)).expect("unable to set port");
|
||||
url.to_string()
|
||||
}
|
||||
|
||||
@@ -138,21 +138,21 @@ mod test {
|
||||
fn compute_rpc_banks_url() {
|
||||
assert_eq!(
|
||||
Config::compute_rpc_banks_url(&"http://devnet.solana.com"),
|
||||
"http://devnet.solana.com:8902/".to_string()
|
||||
"http://devnet.solana.com:82/".to_string()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Config::compute_rpc_banks_url(&"https://devnet.solana.com"),
|
||||
"https://devnet.solana.com:8902/".to_string()
|
||||
"https://devnet.solana.com:445/".to_string()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Config::compute_rpc_banks_url(&"http://example.com:8899"),
|
||||
"http://example.com:8902/".to_string()
|
||||
"http://example.com:8901/".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
Config::compute_rpc_banks_url(&"https://example.com:1234"),
|
||||
"https://example.com:1237/".to_string()
|
||||
"https://example.com:1236/".to_string()
|
||||
);
|
||||
|
||||
assert_eq!(Config::compute_rpc_banks_url(&"garbage"), String::new());
|
||||
|
@@ -1,29 +0,0 @@
|
||||
[package]
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-cli-output"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.4.2"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4.11", features = ["serde"] }
|
||||
console = "0.11.3"
|
||||
humantime = "2.0.1"
|
||||
Inflector = "0.11.4"
|
||||
indicatif = "0.15.0"
|
||||
serde = "1.0.112"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.56"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "1.4.2" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.4.2" }
|
||||
solana-client = { path = "../client", version = "1.4.2" }
|
||||
solana-sdk = { path = "../sdk", version = "1.4.2" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "1.4.2" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "1.4.2" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.4.2" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
@@ -1,15 +0,0 @@
|
||||
mod cli_output;
|
||||
pub mod display;
|
||||
pub use cli_output::*;
|
||||
|
||||
pub trait QuietDisplay: std::fmt::Display {
|
||||
fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result {
|
||||
write!(w, "{}", self)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait VerboseDisplay: std::fmt::Display {
|
||||
fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result {
|
||||
write!(w, "{}", self)
|
||||
}
|
||||
}
|
@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-cli"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.4.2"
|
||||
version = "1.3.6"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -23,35 +23,33 @@ indicatif = "0.15.0"
|
||||
humantime = "2.0.1"
|
||||
num-traits = "0.2"
|
||||
pretty-hex = "0.1.1"
|
||||
reqwest = { version = "0.10.8", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
reqwest = { version = "0.10.6", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
serde = "1.0.112"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.56"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "1.4.2" }
|
||||
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "1.4.2" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.4.2" }
|
||||
solana-cli-config = { path = "../cli-config", version = "1.4.2" }
|
||||
solana-cli-output = { path = "../cli-output", version = "1.4.2" }
|
||||
solana-client = { path = "../client", version = "1.4.2" }
|
||||
solana-config-program = { path = "../programs/config", version = "1.4.2" }
|
||||
solana-faucet = { path = "../faucet", version = "1.4.2" }
|
||||
solana-logger = { path = "../logger", version = "1.4.2" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.4.2" }
|
||||
solana_rbpf = "=0.1.32"
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "1.4.2" }
|
||||
solana-runtime = { path = "../runtime", version = "1.4.2" }
|
||||
solana-sdk = { path = "../sdk", version = "1.4.2" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "1.4.2" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "1.4.2" }
|
||||
solana-version = { path = "../version", version = "1.4.2" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.4.2" }
|
||||
solana-vote-signer = { path = "../vote-signer", version = "1.4.2" }
|
||||
solana-account-decoder = { path = "../account-decoder", version = "1.3.6" }
|
||||
solana-budget-program = { path = "../programs/budget", version = "1.3.6" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.3.6" }
|
||||
solana-cli-config = { path = "../cli-config", version = "1.3.6" }
|
||||
solana-client = { path = "../client", version = "1.3.6" }
|
||||
solana-config-program = { path = "../programs/config", version = "1.3.6" }
|
||||
solana-faucet = { path = "../faucet", version = "1.3.6" }
|
||||
solana-logger = { path = "../logger", version = "1.3.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.3.6" }
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "1.3.6" }
|
||||
solana-runtime = { path = "../runtime", version = "1.3.6" }
|
||||
solana-sdk = { path = "../sdk", version = "1.3.6" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "1.3.6" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "1.3.6" }
|
||||
solana-version = { path = "../version", version = "1.3.6" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.3.6" }
|
||||
solana-vote-signer = { path = "../vote-signer", version = "1.3.6" }
|
||||
thiserror = "1.0.20"
|
||||
tiny-bip39 = "0.7.0"
|
||||
url = "2.1.1"
|
||||
|
||||
[dev-dependencies]
|
||||
solana-core = { path = "../core", version = "1.4.2" }
|
||||
solana-core = { path = "../core", version = "1.3.6" }
|
||||
solana-budget-program = { path = "../programs/budget", version = "1.3.6" }
|
||||
tempfile = "3.1.0"
|
||||
|
||||
[[bin]]
|
||||
|
@@ -54,42 +54,12 @@ pub fn check_account_for_multiple_fees_with_commitment(
|
||||
fee_calculator: &FeeCalculator,
|
||||
messages: &[&Message],
|
||||
commitment: CommitmentConfig,
|
||||
) -> Result<(), CliError> {
|
||||
check_account_for_spend_multiple_fees_with_commitment(
|
||||
rpc_client,
|
||||
account_pubkey,
|
||||
0,
|
||||
fee_calculator,
|
||||
messages,
|
||||
commitment,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn check_account_for_spend_multiple_fees_with_commitment(
|
||||
rpc_client: &RpcClient,
|
||||
account_pubkey: &Pubkey,
|
||||
balance: u64,
|
||||
fee_calculator: &FeeCalculator,
|
||||
messages: &[&Message],
|
||||
commitment: CommitmentConfig,
|
||||
) -> Result<(), CliError> {
|
||||
let fee = calculate_fee(fee_calculator, messages);
|
||||
if !check_account_for_balance_with_commitment(
|
||||
rpc_client,
|
||||
account_pubkey,
|
||||
balance + fee,
|
||||
commitment,
|
||||
)
|
||||
.map_err(Into::<ClientError>::into)?
|
||||
if !check_account_for_balance_with_commitment(rpc_client, account_pubkey, fee, commitment)
|
||||
.map_err(Into::<ClientError>::into)?
|
||||
{
|
||||
if balance > 0 {
|
||||
return Err(CliError::InsufficientFundsForSpendAndFee(
|
||||
lamports_to_sol(balance),
|
||||
lamports_to_sol(fee),
|
||||
));
|
||||
} else {
|
||||
return Err(CliError::InsufficientFundsForFee(lamports_to_sol(fee)));
|
||||
}
|
||||
return Err(CliError::InsufficientFundsForFee(lamports_to_sol(fee)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -161,7 +131,7 @@ mod tests {
|
||||
context: RpcResponseContext { slot: 1 },
|
||||
value: json!(account_balance),
|
||||
});
|
||||
let pubkey = solana_sdk::pubkey::new_rand();
|
||||
let pubkey = Pubkey::new_rand();
|
||||
let fee_calculator = FeeCalculator::new(1);
|
||||
|
||||
let pubkey0 = Pubkey::new(&[0; 32]);
|
||||
@@ -221,7 +191,7 @@ mod tests {
|
||||
context: RpcResponseContext { slot: 1 },
|
||||
value: json!(account_balance),
|
||||
});
|
||||
let pubkey = solana_sdk::pubkey::new_rand();
|
||||
let pubkey = Pubkey::new_rand();
|
||||
|
||||
let mut mocks = HashMap::new();
|
||||
mocks.insert(RpcRequest::GetBalance, account_balance_response);
|
||||
@@ -267,9 +237,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_check_unique_pubkeys() {
|
||||
let pubkey0 = solana_sdk::pubkey::new_rand();
|
||||
let pubkey0 = Pubkey::new_rand();
|
||||
let pubkey_clone = pubkey0;
|
||||
let pubkey1 = solana_sdk::pubkey::new_rand();
|
||||
let pubkey1 = Pubkey::new_rand();
|
||||
|
||||
check_unique_pubkeys((&pubkey0, "foo".to_string()), (&pubkey1, "bar".to_string()))
|
||||
.expect("unexpected result");
|
||||
|
1741
cli/src/cli.rs
1741
cli/src/cli.rs
File diff suppressed because it is too large
Load Diff
@@ -1,26 +1,20 @@
|
||||
use crate::{
|
||||
display::{build_balance_message, format_labeled_address, writeln_name_value},
|
||||
QuietDisplay, VerboseDisplay,
|
||||
cli::build_balance_message,
|
||||
display::{format_labeled_address, writeln_name_value},
|
||||
};
|
||||
use chrono::{DateTime, NaiveDateTime, SecondsFormat, Utc};
|
||||
use console::{style, Emoji};
|
||||
use inflector::cases::titlecase::to_title_case;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::Serialize;
|
||||
use serde_json::{Map, Value};
|
||||
use solana_account_decoder::parse_token::UiTokenAccount;
|
||||
use solana_clap_utils::keypair::SignOnly;
|
||||
use solana_client::rpc_response::{
|
||||
RpcAccountBalance, RpcKeyedAccount, RpcSupply, RpcVoteAccountInfo,
|
||||
};
|
||||
use solana_sdk::{
|
||||
clock::{self, Epoch, Slot, UnixTimestamp},
|
||||
epoch_info::EpochInfo,
|
||||
hash::Hash,
|
||||
native_token::lamports_to_sol,
|
||||
pubkey::Pubkey,
|
||||
signature::Signature,
|
||||
stake_history::StakeHistoryEntry,
|
||||
transaction::Transaction,
|
||||
};
|
||||
use solana_stake_program::stake_state::{Authorized, Lockup};
|
||||
use solana_vote_program::{
|
||||
@@ -30,7 +24,6 @@ use solana_vote_program::{
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
fmt,
|
||||
str::FromStr,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
@@ -41,27 +34,15 @@ pub enum OutputFormat {
|
||||
Display,
|
||||
Json,
|
||||
JsonCompact,
|
||||
DisplayQuiet,
|
||||
DisplayVerbose,
|
||||
}
|
||||
|
||||
impl OutputFormat {
|
||||
pub fn formatted_string<T>(&self, item: &T) -> String
|
||||
where
|
||||
T: Serialize + fmt::Display + QuietDisplay + VerboseDisplay,
|
||||
T: Serialize + fmt::Display,
|
||||
{
|
||||
match self {
|
||||
OutputFormat::Display => format!("{}", item),
|
||||
OutputFormat::DisplayQuiet => {
|
||||
let mut s = String::new();
|
||||
QuietDisplay::write_str(item, &mut s).unwrap();
|
||||
s
|
||||
}
|
||||
OutputFormat::DisplayVerbose => {
|
||||
let mut s = String::new();
|
||||
VerboseDisplay::write_str(item, &mut s).unwrap();
|
||||
s
|
||||
}
|
||||
OutputFormat::Json => serde_json::to_string_pretty(item).unwrap(),
|
||||
OutputFormat::JsonCompact => serde_json::to_value(item).unwrap().to_string(),
|
||||
}
|
||||
@@ -76,9 +57,6 @@ pub struct CliAccount {
|
||||
pub use_lamports_unit: bool,
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliAccount {}
|
||||
impl VerboseDisplay for CliAccount {}
|
||||
|
||||
impl fmt::Display for CliAccount {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(f)?;
|
||||
@@ -121,9 +99,6 @@ pub struct CliBlockProduction {
|
||||
pub verbose: bool,
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliBlockProduction {}
|
||||
impl VerboseDisplay for CliBlockProduction {}
|
||||
|
||||
impl fmt::Display for CliBlockProduction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(f)?;
|
||||
@@ -132,7 +107,7 @@ impl fmt::Display for CliBlockProduction {
|
||||
"{}",
|
||||
style(format!(
|
||||
" {:<44} {:>15} {:>15} {:>15} {:>23}",
|
||||
"Identity",
|
||||
"Identity Pubkey",
|
||||
"Leader Slots",
|
||||
"Blocks Produced",
|
||||
"Skipped Slots",
|
||||
@@ -228,9 +203,6 @@ impl From<EpochInfo> for CliEpochInfo {
|
||||
}
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliEpochInfo {}
|
||||
impl VerboseDisplay for CliEpochInfo {}
|
||||
|
||||
impl fmt::Display for CliEpochInfo {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(f)?;
|
||||
@@ -301,7 +273,7 @@ pub struct CliValidatorsStakeByVersion {
|
||||
pub struct CliValidators {
|
||||
pub total_active_stake: u64,
|
||||
pub total_current_stake: u64,
|
||||
pub total_delinquent_stake: u64,
|
||||
pub total_deliquent_stake: u64,
|
||||
pub current_validators: Vec<CliValidator>,
|
||||
pub delinquent_validators: Vec<CliValidator>,
|
||||
pub stake_by_version: BTreeMap<String, CliValidatorsStakeByVersion>,
|
||||
@@ -309,9 +281,6 @@ pub struct CliValidators {
|
||||
pub use_lamports_unit: bool,
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliValidators {}
|
||||
impl VerboseDisplay for CliValidators {}
|
||||
|
||||
impl fmt::Display for CliValidators {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fn write_vote_account(
|
||||
@@ -331,7 +300,7 @@ impl fmt::Display for CliValidators {
|
||||
|
||||
writeln!(
|
||||
f,
|
||||
"{} {:<44} {:<44} {:>3}% {:>8} {:>10} {:>10} {:>8} {}",
|
||||
"{} {:<44} {:<44} {:>3}% {:>8} {:>10} {:>10} {:>17} {}",
|
||||
if delinquent {
|
||||
WARNING.to_string()
|
||||
} else {
|
||||
@@ -360,7 +329,7 @@ impl fmt::Display for CliValidators {
|
||||
"Active Stake:",
|
||||
&build_balance_message(self.total_active_stake, self.use_lamports_unit, true),
|
||||
)?;
|
||||
if self.total_delinquent_stake > 0 {
|
||||
if self.total_deliquent_stake > 0 {
|
||||
writeln_name_value(
|
||||
f,
|
||||
"Current Stake:",
|
||||
@@ -376,11 +345,11 @@ impl fmt::Display for CliValidators {
|
||||
&format!(
|
||||
"{} ({:0.2}%)",
|
||||
&build_balance_message(
|
||||
self.total_delinquent_stake,
|
||||
self.total_deliquent_stake,
|
||||
self.use_lamports_unit,
|
||||
true
|
||||
),
|
||||
100. * self.total_delinquent_stake as f64 / self.total_active_stake as f64
|
||||
100. * self.total_deliquent_stake as f64 / self.total_active_stake as f64
|
||||
),
|
||||
)?;
|
||||
}
|
||||
@@ -390,7 +359,7 @@ impl fmt::Display for CliValidators {
|
||||
for (version, info) in self.stake_by_version.iter() {
|
||||
writeln!(
|
||||
f,
|
||||
"{:<8} - {:3} current validators ({:>5.2}%){}",
|
||||
"{:<16} - {:3} current validators ({:>5.2}%){}",
|
||||
version,
|
||||
info.current_validators,
|
||||
100. * info.current_active_stake as f64 / self.total_active_stake as f64,
|
||||
@@ -411,9 +380,9 @@ impl fmt::Display for CliValidators {
|
||||
f,
|
||||
"{}",
|
||||
style(format!(
|
||||
" {:<44} {:<38} {} {} {} {:>10} {:^8} {}",
|
||||
"Identity",
|
||||
"Vote Account",
|
||||
" {:<44} {:<38} {} {} {} {:>10} {:^17} {}",
|
||||
"Identity Pubkey",
|
||||
"Vote Account Pubkey",
|
||||
"Commission",
|
||||
"Last Vote",
|
||||
"Root Block",
|
||||
@@ -500,9 +469,6 @@ pub struct CliNonceAccount {
|
||||
pub use_lamports_unit: bool,
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliNonceAccount {}
|
||||
impl VerboseDisplay for CliNonceAccount {}
|
||||
|
||||
impl fmt::Display for CliNonceAccount {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(
|
||||
@@ -540,9 +506,6 @@ impl CliStakeVec {
|
||||
}
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliStakeVec {}
|
||||
impl VerboseDisplay for CliStakeVec {}
|
||||
|
||||
impl fmt::Display for CliStakeVec {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
for state in &self.0 {
|
||||
@@ -561,9 +524,6 @@ pub struct CliKeyedStakeState {
|
||||
pub stake_state: CliStakeState,
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliKeyedStakeState {}
|
||||
impl VerboseDisplay for CliKeyedStakeState {}
|
||||
|
||||
impl fmt::Display for CliKeyedStakeState {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(f, "Stake Pubkey: {}", self.stake_pubkey)?;
|
||||
@@ -571,48 +531,6 @@ impl fmt::Display for CliKeyedStakeState {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliEpochReward {
|
||||
pub epoch: Epoch,
|
||||
pub effective_slot: Slot,
|
||||
pub amount: u64, // lamports
|
||||
pub post_balance: u64, // lamports
|
||||
pub percent_change: f64,
|
||||
pub apr: f64,
|
||||
}
|
||||
|
||||
fn show_epoch_rewards(
|
||||
f: &mut fmt::Formatter,
|
||||
epoch_rewards: &Option<Vec<CliEpochReward>>,
|
||||
) -> fmt::Result {
|
||||
if let Some(epoch_rewards) = epoch_rewards {
|
||||
if epoch_rewards.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
writeln!(f, "Epoch Rewards:")?;
|
||||
writeln!(
|
||||
f,
|
||||
" {:<8} {:<11} {:<15} {:<15} {:>14} {:>14}",
|
||||
"Epoch", "Reward Slot", "Amount", "New Balance", "Percent Change", "APR"
|
||||
)?;
|
||||
for reward in epoch_rewards {
|
||||
writeln!(
|
||||
f,
|
||||
" {:<8} {:<11} ◎{:<14.9} ◎{:<14.9} {:>13.9}% {:>13.9}%",
|
||||
reward.epoch,
|
||||
reward.effective_slot,
|
||||
lamports_to_sol(reward.amount),
|
||||
lamports_to_sol(reward.post_balance),
|
||||
reward.percent_change,
|
||||
reward.apr,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliStakeState {
|
||||
@@ -642,13 +560,8 @@ pub struct CliStakeState {
|
||||
pub activating_stake: Option<u64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub deactivating_stake: Option<u64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub epoch_rewards: Option<Vec<CliEpochReward>>,
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliStakeState {}
|
||||
impl VerboseDisplay for CliStakeState {}
|
||||
|
||||
impl fmt::Display for CliStakeState {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fn show_authorized(f: &mut fmt::Formatter, authorized: &CliAuthorized) -> fmt::Result {
|
||||
@@ -661,8 +574,13 @@ impl fmt::Display for CliStakeState {
|
||||
if lockup.unix_timestamp != UnixTimestamp::default() {
|
||||
writeln!(
|
||||
f,
|
||||
"Lockup Timestamp: {}",
|
||||
unix_timestamp_to_string(lockup.unix_timestamp)
|
||||
"Lockup Timestamp: {} (UnixTimestamp: {})",
|
||||
DateTime::<Utc>::from_utc(
|
||||
NaiveDateTime::from_timestamp(lockup.unix_timestamp, 0),
|
||||
Utc
|
||||
)
|
||||
.to_rfc3339_opts(SecondsFormat::Secs, true),
|
||||
lockup.unix_timestamp
|
||||
)?;
|
||||
}
|
||||
if lockup.epoch != Epoch::default() {
|
||||
@@ -792,14 +710,13 @@ impl fmt::Display for CliStakeState {
|
||||
}
|
||||
show_authorized(f, self.authorized.as_ref().unwrap())?;
|
||||
show_lockup(f, self.lockup.as_ref())?;
|
||||
show_epoch_rewards(f, &self.epoch_rewards)?
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub enum CliStakeType {
|
||||
Stake,
|
||||
RewardsPool,
|
||||
@@ -821,9 +738,6 @@ pub struct CliStakeHistory {
|
||||
pub use_lamports_unit: bool,
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliStakeHistory {}
|
||||
impl VerboseDisplay for CliStakeHistory {}
|
||||
|
||||
impl fmt::Display for CliStakeHistory {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(f)?;
|
||||
@@ -918,9 +832,6 @@ impl CliValidatorInfoVec {
|
||||
}
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliValidatorInfoVec {}
|
||||
impl VerboseDisplay for CliValidatorInfoVec {}
|
||||
|
||||
impl fmt::Display for CliValidatorInfoVec {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.0.is_empty() {
|
||||
@@ -942,13 +853,10 @@ pub struct CliValidatorInfo {
|
||||
pub info: Map<String, Value>,
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliValidatorInfo {}
|
||||
impl VerboseDisplay for CliValidatorInfo {}
|
||||
|
||||
impl fmt::Display for CliValidatorInfo {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln_name_value(f, "Validator Identity:", &self.identity_pubkey)?;
|
||||
writeln_name_value(f, " Info Address:", &self.info_pubkey)?;
|
||||
writeln_name_value(f, "Validator Identity Pubkey:", &self.identity_pubkey)?;
|
||||
writeln_name_value(f, " Info Pubkey:", &self.info_pubkey)?;
|
||||
for (key, value) in self.info.iter() {
|
||||
writeln_name_value(
|
||||
f,
|
||||
@@ -976,13 +884,8 @@ pub struct CliVoteAccount {
|
||||
pub epoch_voting_history: Vec<CliEpochVotingHistory>,
|
||||
#[serde(skip_serializing)]
|
||||
pub use_lamports_unit: bool,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub epoch_rewards: Option<Vec<CliEpochReward>>,
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliVoteAccount {}
|
||||
impl VerboseDisplay for CliVoteAccount {}
|
||||
|
||||
impl fmt::Display for CliVoteAccount {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(
|
||||
@@ -1003,12 +906,7 @@ impl fmt::Display for CliVoteAccount {
|
||||
None => "~".to_string(),
|
||||
}
|
||||
)?;
|
||||
writeln!(
|
||||
f,
|
||||
"Recent Timestamp: {} from slot {}",
|
||||
unix_timestamp_to_string(self.recent_timestamp.timestamp),
|
||||
self.recent_timestamp.slot
|
||||
)?;
|
||||
writeln!(f, "Recent Timestamp: {:?}", self.recent_timestamp)?;
|
||||
if !self.votes.is_empty() {
|
||||
writeln!(f, "Recent Votes:")?;
|
||||
for vote in &self.votes {
|
||||
@@ -1027,7 +925,6 @@ impl fmt::Display for CliVoteAccount {
|
||||
)?;
|
||||
}
|
||||
}
|
||||
show_epoch_rewards(f, &self.epoch_rewards)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1038,9 +935,6 @@ pub struct CliAuthorizedVoters {
|
||||
authorized_voters: BTreeMap<Epoch, String>,
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliAuthorizedVoters {}
|
||||
impl VerboseDisplay for CliAuthorizedVoters {}
|
||||
|
||||
impl fmt::Display for CliAuthorizedVoters {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}", self.authorized_voters)
|
||||
@@ -1090,25 +984,19 @@ pub struct CliBlockTime {
|
||||
pub timestamp: UnixTimestamp,
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliBlockTime {}
|
||||
impl VerboseDisplay for CliBlockTime {}
|
||||
|
||||
fn unix_timestamp_to_string(unix_timestamp: UnixTimestamp) -> String {
|
||||
format!(
|
||||
"{} (UnixTimestamp: {})",
|
||||
match NaiveDateTime::from_timestamp_opt(unix_timestamp, 0) {
|
||||
Some(ndt) =>
|
||||
DateTime::<Utc>::from_utc(ndt, Utc).to_rfc3339_opts(SecondsFormat::Secs, true),
|
||||
None => "unknown".to_string(),
|
||||
},
|
||||
unix_timestamp,
|
||||
)
|
||||
}
|
||||
|
||||
impl fmt::Display for CliBlockTime {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln_name_value(f, "Block:", &self.slot.to_string())?;
|
||||
writeln_name_value(f, "Date:", &unix_timestamp_to_string(self.timestamp))
|
||||
writeln_name_value(
|
||||
f,
|
||||
"Date:",
|
||||
&format!(
|
||||
"{} (UnixTimestamp: {})",
|
||||
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(self.timestamp, 0), Utc)
|
||||
.to_rfc3339_opts(SecondsFormat::Secs, true),
|
||||
self.timestamp
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1124,9 +1012,6 @@ pub struct CliSignOnlyData {
|
||||
pub bad_sig: Vec<String>,
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliSignOnlyData {}
|
||||
impl VerboseDisplay for CliSignOnlyData {}
|
||||
|
||||
impl fmt::Display for CliSignOnlyData {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(f)?;
|
||||
@@ -1159,9 +1044,6 @@ pub struct CliSignature {
|
||||
pub signature: String,
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliSignature {}
|
||||
impl VerboseDisplay for CliSignature {}
|
||||
|
||||
impl fmt::Display for CliSignature {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(f)?;
|
||||
@@ -1176,9 +1058,6 @@ pub struct CliAccountBalances {
|
||||
pub accounts: Vec<RpcAccountBalance>,
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliAccountBalances {}
|
||||
impl VerboseDisplay for CliAccountBalances {}
|
||||
|
||||
impl fmt::Display for CliAccountBalances {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(
|
||||
@@ -1221,9 +1100,6 @@ impl From<RpcSupply> for CliSupply {
|
||||
}
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliSupply {}
|
||||
impl VerboseDisplay for CliSupply {}
|
||||
|
||||
impl fmt::Display for CliSupply {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln_name_value(f, "Total:", &format!("{} SOL", lamports_to_sol(self.total)))?;
|
||||
@@ -1257,9 +1133,6 @@ pub struct CliFees {
|
||||
pub last_valid_slot: Slot,
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliFees {}
|
||||
impl VerboseDisplay for CliFees {}
|
||||
|
||||
impl fmt::Display for CliFees {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln_name_value(f, "Blockhash:", &self.blockhash)?;
|
||||
@@ -1272,240 +1145,3 @@ impl fmt::Display for CliFees {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliTokenAccount {
|
||||
pub address: String,
|
||||
#[serde(flatten)]
|
||||
pub token_account: UiTokenAccount,
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliTokenAccount {}
|
||||
impl VerboseDisplay for CliTokenAccount {}
|
||||
|
||||
impl fmt::Display for CliTokenAccount {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(f)?;
|
||||
writeln_name_value(f, "Address:", &self.address)?;
|
||||
let account = &self.token_account;
|
||||
writeln_name_value(
|
||||
f,
|
||||
"Balance:",
|
||||
&account.token_amount.real_number_string_trimmed(),
|
||||
)?;
|
||||
let mint = format!(
|
||||
"{}{}",
|
||||
account.mint,
|
||||
if account.is_native { " (native)" } else { "" }
|
||||
);
|
||||
writeln_name_value(f, "Mint:", &mint)?;
|
||||
writeln_name_value(f, "Owner:", &account.owner)?;
|
||||
writeln_name_value(f, "State:", &format!("{:?}", account.state))?;
|
||||
if let Some(delegate) = &account.delegate {
|
||||
writeln!(f, "Delegation:")?;
|
||||
writeln_name_value(f, " Delegate:", delegate)?;
|
||||
let allowance = account.delegated_amount.as_ref().unwrap();
|
||||
writeln_name_value(f, " Allowance:", &allowance.real_number_string_trimmed())?;
|
||||
}
|
||||
writeln_name_value(
|
||||
f,
|
||||
"Close authority:",
|
||||
&account.close_authority.as_ref().unwrap_or(&String::new()),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn return_signers(
|
||||
tx: &Transaction,
|
||||
output_format: &OutputFormat,
|
||||
) -> Result<String, Box<dyn std::error::Error>> {
|
||||
let verify_results = tx.verify_with_results();
|
||||
let mut signers = Vec::new();
|
||||
let mut absent = Vec::new();
|
||||
let mut bad_sig = Vec::new();
|
||||
tx.signatures
|
||||
.iter()
|
||||
.zip(tx.message.account_keys.iter())
|
||||
.zip(verify_results.into_iter())
|
||||
.for_each(|((sig, key), res)| {
|
||||
if res {
|
||||
signers.push(format!("{}={}", key, sig))
|
||||
} else if *sig == Signature::default() {
|
||||
absent.push(key.to_string());
|
||||
} else {
|
||||
bad_sig.push(key.to_string());
|
||||
}
|
||||
});
|
||||
|
||||
let cli_command = CliSignOnlyData {
|
||||
blockhash: tx.message.recent_blockhash.to_string(),
|
||||
signers,
|
||||
absent,
|
||||
bad_sig,
|
||||
};
|
||||
|
||||
Ok(output_format.formatted_string(&cli_command))
|
||||
}
|
||||
|
||||
pub fn parse_sign_only_reply_string(reply: &str) -> SignOnly {
|
||||
let object: Value = serde_json::from_str(&reply).unwrap();
|
||||
let blockhash_str = object.get("blockhash").unwrap().as_str().unwrap();
|
||||
let blockhash = blockhash_str.parse::<Hash>().unwrap();
|
||||
let mut present_signers: Vec<(Pubkey, Signature)> = Vec::new();
|
||||
let signer_strings = object.get("signers");
|
||||
if let Some(sig_strings) = signer_strings {
|
||||
present_signers = sig_strings
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|signer_string| {
|
||||
let mut signer = signer_string.as_str().unwrap().split('=');
|
||||
let key = Pubkey::from_str(signer.next().unwrap()).unwrap();
|
||||
let sig = Signature::from_str(signer.next().unwrap()).unwrap();
|
||||
(key, sig)
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
let mut absent_signers: Vec<Pubkey> = Vec::new();
|
||||
let signer_strings = object.get("absent");
|
||||
if let Some(sig_strings) = signer_strings {
|
||||
absent_signers = sig_strings
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|val| {
|
||||
let s = val.as_str().unwrap();
|
||||
Pubkey::from_str(s).unwrap()
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
let mut bad_signers: Vec<Pubkey> = Vec::new();
|
||||
let signer_strings = object.get("badSig");
|
||||
if let Some(sig_strings) = signer_strings {
|
||||
bad_signers = sig_strings
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|val| {
|
||||
let s = val.as_str().unwrap();
|
||||
Pubkey::from_str(s).unwrap()
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
|
||||
SignOnly {
|
||||
blockhash,
|
||||
present_signers,
|
||||
absent_signers,
|
||||
bad_signers,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use solana_sdk::{
|
||||
message::Message,
|
||||
pubkey::Pubkey,
|
||||
signature::{keypair_from_seed, NullSigner, Signature, Signer, SignerError},
|
||||
system_instruction,
|
||||
transaction::Transaction,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_return_signers() {
|
||||
struct BadSigner {
|
||||
pubkey: Pubkey,
|
||||
}
|
||||
|
||||
impl BadSigner {
|
||||
pub fn new(pubkey: Pubkey) -> Self {
|
||||
Self { pubkey }
|
||||
}
|
||||
}
|
||||
|
||||
impl Signer for BadSigner {
|
||||
fn try_pubkey(&self) -> Result<Pubkey, SignerError> {
|
||||
Ok(self.pubkey)
|
||||
}
|
||||
|
||||
fn try_sign_message(&self, _message: &[u8]) -> Result<Signature, SignerError> {
|
||||
Ok(Signature::new(&[1u8; 64]))
|
||||
}
|
||||
}
|
||||
|
||||
let present: Box<dyn Signer> = Box::new(keypair_from_seed(&[2u8; 32]).unwrap());
|
||||
let absent: Box<dyn Signer> = Box::new(NullSigner::new(&Pubkey::new(&[3u8; 32])));
|
||||
let bad: Box<dyn Signer> = Box::new(BadSigner::new(Pubkey::new(&[4u8; 32])));
|
||||
let to = Pubkey::new(&[5u8; 32]);
|
||||
let nonce = Pubkey::new(&[6u8; 32]);
|
||||
let from = present.pubkey();
|
||||
let fee_payer = absent.pubkey();
|
||||
let nonce_auth = bad.pubkey();
|
||||
let mut tx = Transaction::new_unsigned(Message::new_with_nonce(
|
||||
vec![system_instruction::transfer(&from, &to, 42)],
|
||||
Some(&fee_payer),
|
||||
&nonce,
|
||||
&nonce_auth,
|
||||
));
|
||||
|
||||
let signers = vec![present.as_ref(), absent.as_ref(), bad.as_ref()];
|
||||
let blockhash = Hash::new(&[7u8; 32]);
|
||||
tx.try_partial_sign(&signers, blockhash).unwrap();
|
||||
let res = return_signers(&tx, &OutputFormat::JsonCompact).unwrap();
|
||||
let sign_only = parse_sign_only_reply_string(&res);
|
||||
assert_eq!(sign_only.blockhash, blockhash);
|
||||
assert_eq!(sign_only.present_signers[0].0, present.pubkey());
|
||||
assert_eq!(sign_only.absent_signers[0], absent.pubkey());
|
||||
assert_eq!(sign_only.bad_signers[0], bad.pubkey());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verbose_quiet_output_formats() {
|
||||
#[derive(Deserialize, Serialize)]
|
||||
struct FallbackToDisplay {}
|
||||
impl std::fmt::Display for FallbackToDisplay {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "display")
|
||||
}
|
||||
}
|
||||
impl QuietDisplay for FallbackToDisplay {}
|
||||
impl VerboseDisplay for FallbackToDisplay {}
|
||||
|
||||
let f = FallbackToDisplay {};
|
||||
assert_eq!(&OutputFormat::Display.formatted_string(&f), "display");
|
||||
assert_eq!(&OutputFormat::DisplayQuiet.formatted_string(&f), "display");
|
||||
assert_eq!(
|
||||
&OutputFormat::DisplayVerbose.formatted_string(&f),
|
||||
"display"
|
||||
);
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
struct DiscreteVerbosityDisplay {}
|
||||
impl std::fmt::Display for DiscreteVerbosityDisplay {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "display")
|
||||
}
|
||||
}
|
||||
impl QuietDisplay for DiscreteVerbosityDisplay {
|
||||
fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result {
|
||||
write!(w, "quiet")
|
||||
}
|
||||
}
|
||||
impl VerboseDisplay for DiscreteVerbosityDisplay {
|
||||
fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result {
|
||||
write!(w, "verbose")
|
||||
}
|
||||
}
|
||||
|
||||
let f = DiscreteVerbosityDisplay {};
|
||||
assert_eq!(&OutputFormat::Display.formatted_string(&f), "display");
|
||||
assert_eq!(&OutputFormat::DisplayQuiet.formatted_string(&f), "quiet");
|
||||
assert_eq!(
|
||||
&OutputFormat::DisplayVerbose.formatted_string(&f),
|
||||
"verbose"
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,29 +1,18 @@
|
||||
use crate::{
|
||||
cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult},
|
||||
cli_output::*,
|
||||
display::{format_labeled_address, new_spinner_progress_bar, println_name_value},
|
||||
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
|
||||
};
|
||||
use chrono::{Local, TimeZone};
|
||||
use clap::{value_t, value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand};
|
||||
use console::{style, Emoji};
|
||||
use solana_clap_utils::{
|
||||
commitment::commitment_arg, input_parsers::*, input_validators::*, keypair::DefaultSigner,
|
||||
};
|
||||
use solana_cli_output::{
|
||||
display::{
|
||||
format_labeled_address, new_spinner_progress_bar, println_name_value, println_transaction,
|
||||
},
|
||||
*,
|
||||
commitment::commitment_arg, input_parsers::*, input_validators::*, keypair::signer_from_path,
|
||||
};
|
||||
use solana_client::{
|
||||
client_error::ClientErrorKind,
|
||||
pubsub_client::PubsubClient,
|
||||
pubsub_client::{PubsubClient, SlotInfoMessage},
|
||||
rpc_client::{GetConfirmedSignaturesForAddress2Config, RpcClient},
|
||||
rpc_config::{
|
||||
RpcAccountInfoConfig, RpcLargestAccountsConfig, RpcLargestAccountsFilter,
|
||||
RpcProgramAccountsConfig,
|
||||
},
|
||||
rpc_filter,
|
||||
rpc_response::SlotInfo,
|
||||
rpc_config::{RpcLargestAccountsConfig, RpcLargestAccountsFilter},
|
||||
};
|
||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||
use solana_sdk::{
|
||||
@@ -43,7 +32,6 @@ use solana_sdk::{
|
||||
},
|
||||
transaction::Transaction,
|
||||
};
|
||||
use solana_transaction_status::UiTransactionEncoding;
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap, VecDeque},
|
||||
net::SocketAddr,
|
||||
@@ -65,18 +53,6 @@ pub trait ClusterQuerySubCommands {
|
||||
impl ClusterQuerySubCommands for App<'_, '_> {
|
||||
fn cluster_query_subcommands(self) -> Self {
|
||||
self.subcommand(
|
||||
SubCommand::with_name("block")
|
||||
.about("Get a confirmed block")
|
||||
.arg(
|
||||
Arg::with_name("slot")
|
||||
.long("slot")
|
||||
.validator(is_slot)
|
||||
.value_name("SLOT")
|
||||
.takes_value(true)
|
||||
.index(1),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("catchup")
|
||||
.about("Wait for a validator to catch up to the cluster")
|
||||
.arg(
|
||||
@@ -111,10 +87,6 @@ impl ClusterQuerySubCommands for App<'_, '_> {
|
||||
.about("Get the version of the cluster entrypoint"),
|
||||
)
|
||||
.subcommand(SubCommand::with_name("fees").about("Display current cluster fees"))
|
||||
.subcommand(
|
||||
SubCommand::with_name("first-available-block")
|
||||
.about("Get the first available block in the storage"),
|
||||
)
|
||||
.subcommand(SubCommand::with_name("block-time")
|
||||
.about("Get estimated production time of a block")
|
||||
.alias("get-block-time")
|
||||
@@ -310,12 +282,6 @@ impl ClusterQuerySubCommands for App<'_, '_> {
|
||||
.takes_value(true)
|
||||
.help("Start with the first signature older than this one"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("show_transactions")
|
||||
.long("show-transactions")
|
||||
.takes_value(false)
|
||||
.help("Display the full transactions"),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -339,7 +305,7 @@ pub fn parse_catchup(
|
||||
|
||||
pub fn parse_cluster_ping(
|
||||
matches: &ArgMatches<'_>,
|
||||
default_signer: &DefaultSigner,
|
||||
default_signer_path: &str,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let lamports = value_t_or_exit!(matches, "lamports", u64);
|
||||
@@ -357,15 +323,12 @@ pub fn parse_cluster_ping(
|
||||
count,
|
||||
timeout,
|
||||
},
|
||||
signers: vec![default_signer.signer_from_path(matches, wallet_manager)?],
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_get_block(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||
let slot = value_of(matches, "slot");
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::GetBlock { slot },
|
||||
signers: vec![],
|
||||
signers: vec![signer_from_path(
|
||||
matches,
|
||||
default_signer_path,
|
||||
"keypair",
|
||||
wallet_manager,
|
||||
)?],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -490,7 +453,6 @@ pub fn parse_transaction_history(
|
||||
None => None,
|
||||
};
|
||||
let limit = value_t_or_exit!(matches, "limit", usize);
|
||||
let show_transactions = matches.is_present("show_transactions");
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::TransactionHistory {
|
||||
@@ -498,7 +460,6 @@ pub fn parse_transaction_history(
|
||||
before,
|
||||
until,
|
||||
limit,
|
||||
show_transactions,
|
||||
},
|
||||
signers: vec![],
|
||||
})
|
||||
@@ -541,20 +502,7 @@ pub fn process_catchup(
|
||||
RpcClient::new_socket(rpc_addr)
|
||||
};
|
||||
|
||||
let reported_node_pubkey = loop {
|
||||
match node_client.get_identity() {
|
||||
Ok(reported_node_pubkey) => break reported_node_pubkey,
|
||||
Err(err) => {
|
||||
if let ClientErrorKind::Reqwest(err) = err.kind() {
|
||||
progress_bar.set_message(&format!("Connection failed: {}", err));
|
||||
sleep(Duration::from_secs(sleep_interval as u64));
|
||||
continue;
|
||||
}
|
||||
return Err(Box::new(err));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let reported_node_pubkey = node_client.get_identity()?;
|
||||
if reported_node_pubkey != *node_pubkey {
|
||||
return Err(format!(
|
||||
"The identity reported by node RPC URL does not match. Expected: {:?}. Reported: {:?}",
|
||||
@@ -637,14 +585,9 @@ pub fn process_cluster_date(rpc_client: &RpcClient, config: &CliConfig) -> Proce
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_cluster_version(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
|
||||
pub fn process_cluster_version(rpc_client: &RpcClient) -> ProcessResult {
|
||||
let remote_version = rpc_client.get_version()?;
|
||||
|
||||
if config.verbose {
|
||||
Ok(format!("{:?}", remote_version))
|
||||
} else {
|
||||
Ok(remote_version.to_string())
|
||||
}
|
||||
Ok(remote_version.solana_core)
|
||||
}
|
||||
|
||||
pub fn process_fees(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
|
||||
@@ -659,11 +602,6 @@ pub fn process_fees(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult
|
||||
Ok(config.output_format.formatted_string(&fees))
|
||||
}
|
||||
|
||||
pub fn process_first_available_block(rpc_client: &RpcClient) -> ProcessResult {
|
||||
let first_available_block = rpc_client.get_first_available_block()?;
|
||||
Ok(format!("{}", first_available_block))
|
||||
}
|
||||
|
||||
pub fn process_leader_schedule(rpc_client: &RpcClient) -> ProcessResult {
|
||||
let epoch_info = rpc_client.get_epoch_info()?;
|
||||
let first_slot_in_epoch = epoch_info.absolute_slot - epoch_info.slot_index;
|
||||
@@ -699,83 +637,6 @@ pub fn process_leader_schedule(rpc_client: &RpcClient) -> ProcessResult {
|
||||
Ok("".to_string())
|
||||
}
|
||||
|
||||
pub fn process_get_block(
|
||||
rpc_client: &RpcClient,
|
||||
_config: &CliConfig,
|
||||
slot: Option<Slot>,
|
||||
) -> ProcessResult {
|
||||
let slot = if let Some(slot) = slot {
|
||||
slot
|
||||
} else {
|
||||
rpc_client.get_slot()?
|
||||
};
|
||||
|
||||
let mut block =
|
||||
rpc_client.get_confirmed_block_with_encoding(slot, UiTransactionEncoding::Base64)?;
|
||||
|
||||
println!("Slot: {}", slot);
|
||||
println!("Parent Slot: {}", block.parent_slot);
|
||||
println!("Blockhash: {}", block.blockhash);
|
||||
println!("Previous Blockhash: {}", block.previous_blockhash);
|
||||
if let Some(block_time) = block.block_time {
|
||||
println!("Block Time: {:?}", Local.timestamp(block_time, 0));
|
||||
}
|
||||
if !block.rewards.is_empty() {
|
||||
block.rewards.sort_by(|a, b| a.pubkey.cmp(&b.pubkey));
|
||||
let mut total_rewards = 0;
|
||||
println!("Rewards:",);
|
||||
println!(
|
||||
" {:<44} {:^15} {:<15} {:<20} {:>14}",
|
||||
"Address", "Type", "Amount", "New Balance", "Percent Change"
|
||||
);
|
||||
for reward in block.rewards {
|
||||
let sign = if reward.lamports < 0 { "-" } else { "" };
|
||||
|
||||
total_rewards += reward.lamports;
|
||||
println!(
|
||||
" {:<44} {:^15} {:>15} {}",
|
||||
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)
|
||||
),
|
||||
if reward.post_balance == 0 {
|
||||
" - -".to_string()
|
||||
} else {
|
||||
format!(
|
||||
"◎{:<19.9} {:>13.9}%",
|
||||
lamports_to_sol(reward.post_balance),
|
||||
reward.lamports.abs() as f64
|
||||
/ (reward.post_balance as f64 - reward.lamports as f64)
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
let sign = if total_rewards < 0 { "-" } else { "" };
|
||||
println!(
|
||||
"Total Rewards: {}◎{:<12.9}",
|
||||
sign,
|
||||
lamports_to_sol(total_rewards.abs() as u64)
|
||||
);
|
||||
}
|
||||
for (index, transaction_with_meta) in block.transactions.iter().enumerate() {
|
||||
println!("Transaction {}:", index);
|
||||
println_transaction(
|
||||
&transaction_with_meta.transaction.decode().unwrap(),
|
||||
&transaction_with_meta.meta,
|
||||
" ",
|
||||
);
|
||||
}
|
||||
Ok("".to_string())
|
||||
}
|
||||
|
||||
pub fn process_get_block_time(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
@@ -1172,7 +1033,7 @@ pub fn process_live_slots(url: &str) -> ProcessResult {
|
||||
})?;
|
||||
*/
|
||||
|
||||
let mut current: Option<SlotInfo> = None;
|
||||
let mut current: Option<SlotInfoMessage> = None;
|
||||
let mut message = "".to_string();
|
||||
|
||||
let slot_progress = new_spinner_progress_bar();
|
||||
@@ -1267,16 +1128,14 @@ pub fn process_show_gossip(rpc_client: &RpcClient, config: &CliConfig) -> Proces
|
||||
.into_iter()
|
||||
.map(|node| {
|
||||
format!(
|
||||
"{:15} | {:44} | {:6} | {:5} | {:21} | {}",
|
||||
"{:15} | {:44} | {:6} | {:5} | {:5} | {}",
|
||||
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()),
|
||||
format_port(node.rpc),
|
||||
node.version.unwrap_or_else(|| "unknown".to_string()),
|
||||
)
|
||||
})
|
||||
@@ -1284,9 +1143,9 @@ pub fn process_show_gossip(rpc_client: &RpcClient, config: &CliConfig) -> Proces
|
||||
|
||||
Ok(format!(
|
||||
"IP Address | Node identifier \
|
||||
| Gossip | TPU | RPC Address | Version\n\
|
||||
| Gossip | TPU | RPC | Version\n\
|
||||
----------------+----------------------------------------------+\
|
||||
--------+-------+-----------------------+----------------\n\
|
||||
--------+-------+-------+----------------\n\
|
||||
{}\n\
|
||||
Nodes: {}",
|
||||
s.join("\n"),
|
||||
@@ -1305,50 +1164,17 @@ pub fn process_show_stakes(
|
||||
|
||||
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()
|
||||
},
|
||||
};
|
||||
|
||||
if let Some(vote_account_pubkeys) = vote_account_pubkeys {
|
||||
// Use server-side filtering if only one vote account is provided
|
||||
if vote_account_pubkeys.len() == 1 {
|
||||
program_accounts_config.filters = Some(vec![
|
||||
// Filter by `StakeState::Stake(_, _)`
|
||||
rpc_filter::RpcFilterType::Memcmp(rpc_filter::Memcmp {
|
||||
offset: 0,
|
||||
bytes: rpc_filter::MemcmpEncodedBytes::Binary(
|
||||
bs58::encode([2, 0, 0, 0]).into_string(),
|
||||
),
|
||||
encoding: Some(rpc_filter::MemcmpEncoding::Binary),
|
||||
}),
|
||||
// Filter by `Delegation::voter_pubkey`, which begins at byte offset 124
|
||||
rpc_filter::RpcFilterType::Memcmp(rpc_filter::Memcmp {
|
||||
offset: 124,
|
||||
bytes: rpc_filter::MemcmpEncodedBytes::Binary(
|
||||
vote_account_pubkeys[0].to_string(),
|
||||
),
|
||||
encoding: Some(rpc_filter::MemcmpEncoding::Binary),
|
||||
}),
|
||||
]);
|
||||
}
|
||||
}
|
||||
let all_stake_accounts = rpc_client
|
||||
.get_program_accounts_with_config(&solana_stake_program::id(), program_accounts_config)?;
|
||||
let all_stake_accounts = rpc_client.get_program_accounts(&solana_stake_program::id())?;
|
||||
let stake_history_account = rpc_client.get_account(&stake_history::id())?;
|
||||
let clock_account = rpc_client.get_account(&sysvar::clock::id())?;
|
||||
let clock: Clock = Sysvar::from_account(&clock_account).ok_or_else(|| {
|
||||
CliError::RpcRequestError("Failed to deserialize clock sysvar".to_string())
|
||||
})?;
|
||||
progress_bar.finish_and_clear();
|
||||
let clock_account = rpc_client.get_account(&sysvar::clock::id())?;
|
||||
|
||||
let stake_history = StakeHistory::from_account(&stake_history_account).ok_or_else(|| {
|
||||
CliError::RpcRequestError("Failed to deserialize stake history".to_string())
|
||||
})?;
|
||||
let clock: Clock = Sysvar::from_account(&clock_account).ok_or_else(|| {
|
||||
CliError::RpcRequestError("Failed to deserialize clock sysvar".to_string())
|
||||
})?;
|
||||
|
||||
let mut stake_accounts: Vec<CliKeyedStakeState> = vec![];
|
||||
for (stake_pubkey, stake_account) in all_stake_accounts {
|
||||
@@ -1421,12 +1247,12 @@ pub fn process_show_validators(
|
||||
.map(|vote_account| vote_account.activated_stake)
|
||||
.sum();
|
||||
|
||||
let total_delinquent_stake = vote_accounts
|
||||
let total_deliquent_stake = vote_accounts
|
||||
.delinquent
|
||||
.iter()
|
||||
.map(|vote_account| vote_account.activated_stake)
|
||||
.sum();
|
||||
let total_current_stake = total_active_stake - total_delinquent_stake;
|
||||
let total_current_stake = total_active_stake - total_deliquent_stake;
|
||||
|
||||
let mut current = vote_accounts.current;
|
||||
current.sort_by(|a, b| b.activated_stake.cmp(&a.activated_stake));
|
||||
@@ -1480,7 +1306,7 @@ pub fn process_show_validators(
|
||||
let cli_validators = CliValidators {
|
||||
total_active_stake,
|
||||
total_current_stake,
|
||||
total_delinquent_stake,
|
||||
total_deliquent_stake,
|
||||
current_validators,
|
||||
delinquent_validators,
|
||||
stake_by_version,
|
||||
@@ -1496,7 +1322,6 @@ pub fn process_transaction_history(
|
||||
before: Option<Signature>,
|
||||
until: Option<Signature>,
|
||||
limit: usize,
|
||||
show_transactions: bool,
|
||||
) -> ProcessResult {
|
||||
let results = rpc_client.get_confirmed_signatures_for_address2_with_config(
|
||||
address,
|
||||
@@ -1524,28 +1349,6 @@ pub fn process_transaction_history(
|
||||
} else {
|
||||
println!("{}", result.signature);
|
||||
}
|
||||
|
||||
if show_transactions {
|
||||
if let Ok(signature) = result.signature.parse::<Signature>() {
|
||||
match rpc_client
|
||||
.get_confirmed_transaction(&signature, UiTransactionEncoding::Base64)
|
||||
{
|
||||
Ok(confirmed_transaction) => {
|
||||
println_transaction(
|
||||
&confirmed_transaction
|
||||
.transaction
|
||||
.transaction
|
||||
.decode()
|
||||
.expect("Successful decode"),
|
||||
&confirmed_transaction.transaction.meta,
|
||||
" ",
|
||||
);
|
||||
}
|
||||
Err(err) => println!(" Unable to get confirmed transaction details: {}", err),
|
||||
}
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
Ok(transactions_found)
|
||||
}
|
||||
@@ -1568,16 +1371,12 @@ 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 {
|
||||
path: default_keypair_file,
|
||||
arg_name: String::new(),
|
||||
};
|
||||
|
||||
let test_cluster_version = test_commands
|
||||
.clone()
|
||||
.get_matches_from(vec!["test", "cluster-date"]);
|
||||
assert_eq!(
|
||||
parse_command(&test_cluster_version, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_cluster_version, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::ClusterDate,
|
||||
signers: vec![],
|
||||
@@ -1588,7 +1387,7 @@ mod tests {
|
||||
.clone()
|
||||
.get_matches_from(vec!["test", "cluster-version"]);
|
||||
assert_eq!(
|
||||
parse_command(&test_cluster_version, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_cluster_version, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::ClusterVersion,
|
||||
signers: vec![],
|
||||
@@ -1597,7 +1396,7 @@ mod tests {
|
||||
|
||||
let test_fees = test_commands.clone().get_matches_from(vec!["test", "fees"]);
|
||||
assert_eq!(
|
||||
parse_command(&test_fees, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_fees, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Fees,
|
||||
signers: vec![],
|
||||
@@ -1610,7 +1409,7 @@ mod tests {
|
||||
.clone()
|
||||
.get_matches_from(vec!["test", "block-time", &slot.to_string()]);
|
||||
assert_eq!(
|
||||
parse_command(&test_get_block_time, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_get_block_time, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::GetBlockTime { slot: Some(slot) },
|
||||
signers: vec![],
|
||||
@@ -1621,7 +1420,7 @@ mod tests {
|
||||
.clone()
|
||||
.get_matches_from(vec!["test", "epoch"]);
|
||||
assert_eq!(
|
||||
parse_command(&test_get_epoch, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_get_epoch, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::GetEpoch,
|
||||
signers: vec![],
|
||||
@@ -1632,7 +1431,7 @@ mod tests {
|
||||
.clone()
|
||||
.get_matches_from(vec!["test", "epoch-info"]);
|
||||
assert_eq!(
|
||||
parse_command(&test_get_epoch_info, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_get_epoch_info, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::GetEpochInfo,
|
||||
signers: vec![],
|
||||
@@ -1643,7 +1442,7 @@ mod tests {
|
||||
.clone()
|
||||
.get_matches_from(vec!["test", "genesis-hash"]);
|
||||
assert_eq!(
|
||||
parse_command(&test_get_genesis_hash, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_get_genesis_hash, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::GetGenesisHash,
|
||||
signers: vec![],
|
||||
@@ -1652,7 +1451,7 @@ mod tests {
|
||||
|
||||
let test_get_slot = test_commands.clone().get_matches_from(vec!["test", "slot"]);
|
||||
assert_eq!(
|
||||
parse_command(&test_get_slot, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_get_slot, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::GetSlot,
|
||||
signers: vec![],
|
||||
@@ -1663,7 +1462,7 @@ mod tests {
|
||||
.clone()
|
||||
.get_matches_from(vec!["test", "total-supply"]);
|
||||
assert_eq!(
|
||||
parse_command(&test_total_supply, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_total_supply, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::TotalSupply,
|
||||
signers: vec![],
|
||||
@@ -1674,7 +1473,7 @@ mod tests {
|
||||
.clone()
|
||||
.get_matches_from(vec!["test", "transaction-count"]);
|
||||
assert_eq!(
|
||||
parse_command(&test_transaction_count, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_transaction_count, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::GetTransactionCount,
|
||||
signers: vec![],
|
||||
@@ -1694,7 +1493,7 @@ mod tests {
|
||||
"max",
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_ping, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_ping, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Ping {
|
||||
lamports: 1,
|
||||
|
@@ -1,3 +1,4 @@
|
||||
use crate::cli::SettingType;
|
||||
use console::style;
|
||||
use indicatif::{ProgressBar, ProgressStyle};
|
||||
use solana_sdk::{
|
||||
@@ -7,24 +8,6 @@ use solana_sdk::{
|
||||
use solana_transaction_status::UiTransactionStatusMeta;
|
||||
use std::{collections::HashMap, fmt, io};
|
||||
|
||||
pub fn build_balance_message(lamports: u64, use_lamports_unit: bool, show_unit: bool) -> String {
|
||||
if use_lamports_unit {
|
||||
let ess = if lamports == 1 { "" } else { "s" };
|
||||
let unit = if show_unit {
|
||||
format!(" lamport{}", ess)
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
format!("{:?}{}", lamports, unit)
|
||||
} else {
|
||||
let sol = lamports_to_sol(lamports);
|
||||
let sol_str = format!("{:.9}", sol);
|
||||
let pretty_sol = sol_str.trim_end_matches('0').trim_end_matches('.');
|
||||
let unit = if show_unit { " SOL" } else { "" };
|
||||
format!("{}{}", pretty_sol, unit)
|
||||
}
|
||||
}
|
||||
|
||||
// Pretty print a "name value"
|
||||
pub fn println_name_value(name: &str, value: &str) {
|
||||
let styled_value = if value == "" {
|
||||
@@ -57,6 +40,21 @@ pub fn format_labeled_address(pubkey: &str, address_labels: &HashMap<String, Str
|
||||
}
|
||||
}
|
||||
|
||||
pub fn println_name_value_or(name: &str, value: &str, setting_type: SettingType) {
|
||||
let description = match setting_type {
|
||||
SettingType::Explicit => "",
|
||||
SettingType::Computed => "(computed)",
|
||||
SettingType::SystemDefault => "(default)",
|
||||
};
|
||||
|
||||
println!(
|
||||
"{} {} {}",
|
||||
style(name).bold(),
|
||||
style(value),
|
||||
style(description).italic(),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn println_signers(
|
||||
blockhash: &Hash,
|
||||
signers: &[String],
|
||||
@@ -164,7 +162,7 @@ pub fn write_transaction<W: io::Write>(
|
||||
)?;
|
||||
writeln!(
|
||||
w,
|
||||
"{} Fee: ◎{}",
|
||||
"{} Fee: {} SOL",
|
||||
prefix,
|
||||
lamports_to_sol(transaction_status.fee)
|
||||
)?;
|
||||
@@ -181,7 +179,7 @@ pub fn write_transaction<W: io::Write>(
|
||||
if pre == post {
|
||||
writeln!(
|
||||
w,
|
||||
"{} Account {} balance: ◎{}",
|
||||
"{} Account {} balance: {} SOL",
|
||||
prefix,
|
||||
i,
|
||||
lamports_to_sol(*pre)
|
||||
@@ -189,7 +187,7 @@ pub fn write_transaction<W: io::Write>(
|
||||
} else {
|
||||
writeln!(
|
||||
w,
|
||||
"{} Account {} balance: ◎{} -> ◎{}",
|
||||
"{} Account {} balance: {} SOL -> {} SOL",
|
||||
prefix,
|
||||
i,
|
||||
lamports_to_sol(*pre),
|
||||
@@ -197,15 +195,6 @@ pub fn write_transaction<W: io::Write>(
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(log_messages) = &transaction_status.log_messages {
|
||||
if !log_messages.is_empty() {
|
||||
writeln!(w, "{}Log Messages:", prefix,)?;
|
||||
for log_message in log_messages {
|
||||
writeln!(w, "{} {}", prefix, log_message,)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
writeln!(w, "{}Status: Unavailable", prefix)?;
|
||||
}
|
@@ -1,367 +0,0 @@
|
||||
use crate::{
|
||||
cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult},
|
||||
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
|
||||
};
|
||||
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
|
||||
use console::style;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use solana_clap_utils::{input_parsers::*, input_validators::*, keypair::*};
|
||||
use solana_cli_output::{QuietDisplay, VerboseDisplay};
|
||||
use solana_client::{client_error::ClientError, rpc_client::RpcClient};
|
||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||
use solana_runtime::{
|
||||
feature::{self, Feature},
|
||||
feature_set::FEATURE_NAMES,
|
||||
};
|
||||
use solana_sdk::{
|
||||
clock::Slot, message::Message, pubkey::Pubkey, system_instruction, transaction::Transaction,
|
||||
};
|
||||
use std::{collections::HashMap, fmt, sync::Arc};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum FeatureCliCommand {
|
||||
Status { features: Vec<Pubkey> },
|
||||
Activate { feature: Pubkey },
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase", tag = "status", content = "sinceSlot")]
|
||||
pub enum CliFeatureStatus {
|
||||
Inactive,
|
||||
Pending,
|
||||
Active(Slot),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliFeature {
|
||||
pub id: String,
|
||||
pub description: String,
|
||||
#[serde(flatten)]
|
||||
pub status: CliFeatureStatus,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliFeatures {
|
||||
pub features: Vec<CliFeature>,
|
||||
pub feature_activation_allowed: bool,
|
||||
#[serde(skip)]
|
||||
pub inactive: bool,
|
||||
}
|
||||
|
||||
impl fmt::Display for CliFeatures {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.features.len() > 1 {
|
||||
writeln!(
|
||||
f,
|
||||
"{}",
|
||||
style(format!(
|
||||
"{:<44} {:<40} {}",
|
||||
"Feature", "Description", "Status"
|
||||
))
|
||||
.bold()
|
||||
)?;
|
||||
}
|
||||
for feature in &self.features {
|
||||
writeln!(
|
||||
f,
|
||||
"{:<44} {:<40} {}",
|
||||
feature.id,
|
||||
feature.description,
|
||||
match feature.status {
|
||||
CliFeatureStatus::Inactive => style("inactive".to_string()).red(),
|
||||
CliFeatureStatus::Pending => style("activation pending".to_string()).yellow(),
|
||||
CliFeatureStatus::Active(activation_slot) =>
|
||||
style(format!("active since slot {}", activation_slot)).green(),
|
||||
}
|
||||
)?;
|
||||
}
|
||||
if self.inactive && !self.feature_activation_allowed {
|
||||
writeln!(
|
||||
f,
|
||||
"{}",
|
||||
style("\nFeature activation is not allowed at this time")
|
||||
.bold()
|
||||
.red()
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliFeatures {}
|
||||
impl VerboseDisplay for CliFeatures {}
|
||||
|
||||
pub trait FeatureSubCommands {
|
||||
fn feature_subcommands(self) -> Self;
|
||||
}
|
||||
|
||||
impl FeatureSubCommands for App<'_, '_> {
|
||||
fn feature_subcommands(self) -> Self {
|
||||
self.subcommand(
|
||||
SubCommand::with_name("feature")
|
||||
.about("Runtime feature management")
|
||||
.setting(AppSettings::SubcommandRequiredElseHelp)
|
||||
.subcommand(
|
||||
SubCommand::with_name("status")
|
||||
.about("Query runtime feature status")
|
||||
.arg(
|
||||
Arg::with_name("features")
|
||||
.value_name("ADDRESS")
|
||||
.validator(is_valid_pubkey)
|
||||
.index(1)
|
||||
.multiple(true)
|
||||
.help("Feature status to query [default: all known features]"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("activate")
|
||||
.about("Activate a runtime feature")
|
||||
.arg(
|
||||
Arg::with_name("feature")
|
||||
.value_name("FEATURE_KEYPAIR")
|
||||
.validator(is_valid_signer)
|
||||
.index(1)
|
||||
.required(true)
|
||||
.help("The signer for the feature to activate"),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn known_feature(feature: &Pubkey) -> Result<(), CliError> {
|
||||
if FEATURE_NAMES.contains_key(feature) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(CliError::BadParameter(format!(
|
||||
"Unknown feature: {}",
|
||||
feature
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_feature_subcommand(
|
||||
matches: &ArgMatches<'_>,
|
||||
default_signer: &DefaultSigner,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let response = match matches.subcommand() {
|
||||
("activate", Some(matches)) => {
|
||||
let (feature_signer, feature) = signer_of(matches, "feature", wallet_manager)?;
|
||||
let mut signers = vec![default_signer.signer_from_path(matches, wallet_manager)?];
|
||||
signers.push(feature_signer.unwrap());
|
||||
let feature = feature.unwrap();
|
||||
|
||||
known_feature(&feature)?;
|
||||
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Feature(FeatureCliCommand::Activate { feature }),
|
||||
signers,
|
||||
}
|
||||
}
|
||||
("status", Some(matches)) => {
|
||||
let mut features = if let Some(features) = pubkeys_of(matches, "features") {
|
||||
for feature in &features {
|
||||
known_feature(feature)?;
|
||||
}
|
||||
features
|
||||
} else {
|
||||
FEATURE_NAMES.keys().cloned().collect()
|
||||
};
|
||||
features.sort();
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Feature(FeatureCliCommand::Status { features }),
|
||||
signers: vec![],
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
pub fn process_feature_subcommand(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
feature_subcommand: &FeatureCliCommand,
|
||||
) -> ProcessResult {
|
||||
match feature_subcommand {
|
||||
FeatureCliCommand::Status { features } => process_status(rpc_client, config, features),
|
||||
FeatureCliCommand::Activate { feature } => process_activate(rpc_client, config, *feature),
|
||||
}
|
||||
}
|
||||
|
||||
fn active_stake_by_feature_set(rpc_client: &RpcClient) -> Result<HashMap<u32, u64>, ClientError> {
|
||||
// Validator identity -> feature set
|
||||
let feature_set_map = rpc_client
|
||||
.get_cluster_nodes()?
|
||||
.into_iter()
|
||||
.map(|contact_info| (contact_info.pubkey, contact_info.feature_set))
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
let vote_accounts = rpc_client.get_vote_accounts()?;
|
||||
|
||||
let total_active_stake: u64 = vote_accounts
|
||||
.current
|
||||
.iter()
|
||||
.chain(vote_accounts.delinquent.iter())
|
||||
.map(|vote_account| vote_account.activated_stake)
|
||||
.sum();
|
||||
|
||||
// Sum all active stake by feature set
|
||||
let mut active_stake_by_feature_set = HashMap::new();
|
||||
for vote_account in vote_accounts.current {
|
||||
if let Some(Some(feature_set)) = feature_set_map.get(&vote_account.node_pubkey) {
|
||||
*active_stake_by_feature_set.entry(*feature_set).or_default() +=
|
||||
vote_account.activated_stake;
|
||||
} else {
|
||||
*active_stake_by_feature_set
|
||||
.entry(0 /* "unknown" */)
|
||||
.or_default() += vote_account.activated_stake;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert active stake to a percentage so the caller doesn't need `total_active_stake`
|
||||
for (_, val) in active_stake_by_feature_set.iter_mut() {
|
||||
*val = *val * 100 / total_active_stake;
|
||||
}
|
||||
Ok(active_stake_by_feature_set)
|
||||
}
|
||||
|
||||
// Feature activation is only allowed when 95% of the active stake is on the current feature set
|
||||
fn feature_activation_allowed(rpc_client: &RpcClient, quiet: bool) -> Result<bool, ClientError> {
|
||||
let my_feature_set = solana_version::Version::default().feature_set;
|
||||
|
||||
let active_stake_by_feature_set = active_stake_by_feature_set(rpc_client)?;
|
||||
|
||||
let feature_activation_allowed = active_stake_by_feature_set
|
||||
.get(&my_feature_set)
|
||||
.map(|percentage| *percentage >= 95)
|
||||
.unwrap_or(false);
|
||||
|
||||
if !feature_activation_allowed && !quiet {
|
||||
println!("{}", style("Stake By Feature Set:").bold());
|
||||
for (feature_set, percentage) in active_stake_by_feature_set.iter() {
|
||||
if *feature_set == 0 {
|
||||
println!("unknown - {}%", percentage);
|
||||
} else {
|
||||
println!(
|
||||
"{} - {}% {}",
|
||||
feature_set,
|
||||
percentage,
|
||||
if *feature_set == my_feature_set {
|
||||
" <-- me"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
Ok(feature_activation_allowed)
|
||||
}
|
||||
|
||||
fn process_status(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
feature_ids: &[Pubkey],
|
||||
) -> ProcessResult {
|
||||
let mut features: Vec<CliFeature> = vec![];
|
||||
let mut inactive = false;
|
||||
for (i, account) in rpc_client
|
||||
.get_multiple_accounts(feature_ids)?
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
let feature_id = &feature_ids[i];
|
||||
let feature_name = FEATURE_NAMES.get(feature_id).unwrap();
|
||||
if let Some(account) = account {
|
||||
if let Some(feature) = Feature::from_account(&account) {
|
||||
let feature_status = match feature.activated_at {
|
||||
None => CliFeatureStatus::Pending,
|
||||
Some(activation_slot) => CliFeatureStatus::Active(activation_slot),
|
||||
};
|
||||
features.push(CliFeature {
|
||||
id: feature_id.to_string(),
|
||||
description: feature_name.to_string(),
|
||||
status: feature_status,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
}
|
||||
inactive = true;
|
||||
features.push(CliFeature {
|
||||
id: feature_id.to_string(),
|
||||
description: feature_name.to_string(),
|
||||
status: CliFeatureStatus::Inactive,
|
||||
});
|
||||
}
|
||||
|
||||
let feature_activation_allowed = feature_activation_allowed(rpc_client, features.len() <= 1)?;
|
||||
let feature_set = CliFeatures {
|
||||
features,
|
||||
feature_activation_allowed,
|
||||
inactive,
|
||||
};
|
||||
Ok(config.output_format.formatted_string(&feature_set))
|
||||
}
|
||||
|
||||
fn process_activate(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
feature_id: Pubkey,
|
||||
) -> ProcessResult {
|
||||
let account = rpc_client
|
||||
.get_multiple_accounts(&[feature_id])?
|
||||
.into_iter()
|
||||
.next()
|
||||
.unwrap();
|
||||
if let Some(account) = account {
|
||||
if Feature::from_account(&account).is_some() {
|
||||
return Err(format!("{} has already been activated", feature_id).into());
|
||||
}
|
||||
}
|
||||
|
||||
if !feature_activation_allowed(rpc_client, false)? {
|
||||
return Err("Feature activation is not allowed at this time".into());
|
||||
}
|
||||
|
||||
let rent = rpc_client.get_minimum_balance_for_rent_exemption(Feature::size_of())?;
|
||||
|
||||
let (blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||
let (message, _) = resolve_spend_tx_and_check_account_balance(
|
||||
rpc_client,
|
||||
false,
|
||||
SpendAmount::Some(rent),
|
||||
&fee_calculator,
|
||||
&config.signers[0].pubkey(),
|
||||
|lamports| {
|
||||
Message::new(
|
||||
&[
|
||||
system_instruction::transfer(
|
||||
&config.signers[0].pubkey(),
|
||||
&feature_id,
|
||||
lamports,
|
||||
),
|
||||
system_instruction::allocate(&feature_id, Feature::size_of() as u64),
|
||||
system_instruction::assign(&feature_id, &feature::id()),
|
||||
],
|
||||
Some(&config.signers[0].pubkey()),
|
||||
)
|
||||
},
|
||||
config.commitment,
|
||||
)?;
|
||||
let mut transaction = Transaction::new_unsigned(message);
|
||||
transaction.try_sign(&config.signers, blockhash)?;
|
||||
|
||||
println!(
|
||||
"Activating {} ({})",
|
||||
FEATURE_NAMES.get(&feature_id).unwrap(),
|
||||
feature_id
|
||||
);
|
||||
rpc_client.send_and_confirm_transaction_with_spinner(&transaction)?;
|
||||
Ok("".to_string())
|
||||
}
|
@@ -1,89 +0,0 @@
|
||||
use crate::cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult};
|
||||
use clap::{App, ArgMatches, SubCommand};
|
||||
use console::style;
|
||||
use solana_clap_utils::keypair::*;
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum InflationCliCommand {
|
||||
Show,
|
||||
}
|
||||
|
||||
pub trait InflationSubCommands {
|
||||
fn inflation_subcommands(self) -> Self;
|
||||
}
|
||||
|
||||
impl InflationSubCommands for App<'_, '_> {
|
||||
fn inflation_subcommands(self) -> Self {
|
||||
self.subcommand(SubCommand::with_name("inflation").about("Show inflation information"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_inflation_subcommand(
|
||||
_matches: &ArgMatches<'_>,
|
||||
_default_signer: &DefaultSigner,
|
||||
_wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::Inflation(InflationCliCommand::Show),
|
||||
signers: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
pub fn process_inflation_subcommand(
|
||||
rpc_client: &RpcClient,
|
||||
_config: &CliConfig,
|
||||
inflation_subcommand: &InflationCliCommand,
|
||||
) -> ProcessResult {
|
||||
assert_eq!(*inflation_subcommand, InflationCliCommand::Show);
|
||||
|
||||
let governor = rpc_client.get_inflation_governor()?;
|
||||
let current_inflation_rate = rpc_client.get_inflation_rate()?;
|
||||
|
||||
println!("{}", style("Inflation Governor:").bold());
|
||||
if (governor.initial - governor.terminal).abs() < f64::EPSILON {
|
||||
println!(
|
||||
"Fixed APR: {:>5.2}%",
|
||||
governor.terminal * 100.
|
||||
);
|
||||
} else {
|
||||
println!("Initial APR: {:>5.2}%", governor.initial * 100.);
|
||||
println!(
|
||||
"Terminal APR: {:>5.2}%",
|
||||
governor.terminal * 100.
|
||||
);
|
||||
println!("Rate reduction per year: {:>5.2}%", governor.taper * 100.);
|
||||
}
|
||||
if governor.foundation_term > 0. {
|
||||
println!("Foundation percentage: {:>5.2}%", governor.foundation);
|
||||
println!(
|
||||
"Foundation term: {:.1} years",
|
||||
governor.foundation_term
|
||||
);
|
||||
}
|
||||
|
||||
println!(
|
||||
"\n{}",
|
||||
style(format!(
|
||||
"Inflation for Epoch {}:",
|
||||
current_inflation_rate.epoch
|
||||
))
|
||||
.bold()
|
||||
);
|
||||
println!(
|
||||
"Total APR: {:>5.2}%",
|
||||
current_inflation_rate.total * 100.
|
||||
);
|
||||
println!(
|
||||
"Staking APR: {:>5.2}%",
|
||||
current_inflation_rate.validator * 100.
|
||||
);
|
||||
println!(
|
||||
"Foundation APR: {:>5.2}%",
|
||||
current_inflation_rate.foundation * 100.
|
||||
);
|
||||
|
||||
Ok("".to_string())
|
||||
}
|
@@ -18,14 +18,16 @@ macro_rules! pubkey {
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
pub mod checks;
|
||||
pub mod cli;
|
||||
pub mod cli_output;
|
||||
pub mod cluster_query;
|
||||
pub mod feature;
|
||||
pub mod inflation;
|
||||
pub mod display;
|
||||
pub mod nonce;
|
||||
pub mod offline;
|
||||
pub mod spend_utils;
|
||||
pub mod stake;
|
||||
pub mod test_utils;
|
||||
|
@@ -5,37 +5,22 @@ use clap::{
|
||||
use console::style;
|
||||
|
||||
use solana_clap_utils::{
|
||||
commitment::COMMITMENT_ARG,
|
||||
input_parsers::commitment_of,
|
||||
input_validators::is_url,
|
||||
keypair::{CliSigners, DefaultSigner, SKIP_SEED_PHRASE_VALIDATION_ARG},
|
||||
DisplayError,
|
||||
commitment::COMMITMENT_ARG, input_parsers::commitment_of, input_validators::is_url,
|
||||
keypair::SKIP_SEED_PHRASE_VALIDATION_ARG, DisplayError,
|
||||
};
|
||||
use solana_cli::cli::{
|
||||
app, parse_command, process_command, CliCommandInfo, CliConfig, SettingType,
|
||||
DEFAULT_RPC_TIMEOUT_SECONDS,
|
||||
use solana_cli::{
|
||||
cli::{
|
||||
app, parse_command, process_command, CliCommandInfo, CliConfig, CliSigners,
|
||||
DEFAULT_RPC_TIMEOUT_SECONDS,
|
||||
},
|
||||
cli_output::OutputFormat,
|
||||
display::{println_name_value, println_name_value_or},
|
||||
};
|
||||
use solana_cli_config::{Config, CONFIG_FILE};
|
||||
use solana_cli_output::{display::println_name_value, OutputFormat};
|
||||
use solana_client::rpc_config::RpcSendTransactionConfig;
|
||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||
use std::{collections::HashMap, error, path::PathBuf, sync::Arc, time::Duration};
|
||||
|
||||
pub fn println_name_value_or(name: &str, value: &str, setting_type: SettingType) {
|
||||
let description = match setting_type {
|
||||
SettingType::Explicit => "",
|
||||
SettingType::Computed => "(computed)",
|
||||
SettingType::SystemDefault => "(default)",
|
||||
};
|
||||
|
||||
println!(
|
||||
"{} {} {}",
|
||||
style(name).bold(),
|
||||
style(value),
|
||||
style(description).italic(),
|
||||
);
|
||||
}
|
||||
|
||||
fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error>> {
|
||||
let parse_args = match matches.subcommand() {
|
||||
("config", Some(matches)) => {
|
||||
@@ -154,19 +139,13 @@ pub fn parse_args<'a>(
|
||||
matches.value_of("json_rpc_url").unwrap_or(""),
|
||||
&config.json_rpc_url,
|
||||
);
|
||||
let default_signer_arg_name = "keypair".to_string();
|
||||
let (_, default_signer_path) = CliConfig::compute_keypair_path_setting(
|
||||
matches.value_of(&default_signer_arg_name).unwrap_or(""),
|
||||
matches.value_of("keypair").unwrap_or(""),
|
||||
&config.keypair_path,
|
||||
);
|
||||
|
||||
let default_signer = DefaultSigner {
|
||||
arg_name: default_signer_arg_name,
|
||||
path: default_signer_path.clone(),
|
||||
};
|
||||
|
||||
let CliCommandInfo { command, signers } =
|
||||
parse_command(&matches, &default_signer, &mut wallet_manager)?;
|
||||
parse_command(&matches, &default_signer_path, &mut wallet_manager)?;
|
||||
|
||||
let output_format = matches
|
||||
.value_of("output_format")
|
||||
|
216
cli/src/nonce.rs
216
cli/src/nonce.rs
@@ -1,26 +1,29 @@
|
||||
use crate::{
|
||||
checks::{check_account_for_fee_with_commitment, check_unique_pubkeys},
|
||||
cli::{
|
||||
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
|
||||
ProcessResult,
|
||||
generate_unique_signers, log_instruction_custom_error, CliCommand, CliCommandInfo,
|
||||
CliConfig, CliError, ProcessResult, SignerIndex,
|
||||
},
|
||||
cli_output::CliNonceAccount,
|
||||
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
|
||||
};
|
||||
use clap::{App, Arg, ArgMatches, SubCommand};
|
||||
use solana_clap_utils::{
|
||||
input_parsers::*,
|
||||
input_validators::*,
|
||||
keypair::{DefaultSigner, SignerIndex},
|
||||
nonce::*,
|
||||
input_parsers::*, input_validators::*, offline::BLOCKHASH_ARG, ArgConstant,
|
||||
};
|
||||
use solana_cli_output::CliNonceAccount;
|
||||
use solana_client::{nonce_utils::*, rpc_client::RpcClient};
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||
use solana_sdk::{
|
||||
account::Account,
|
||||
account_utils::StateMut,
|
||||
commitment_config::CommitmentConfig,
|
||||
hash::Hash,
|
||||
message::Message,
|
||||
nonce::{self, State},
|
||||
nonce::{
|
||||
self,
|
||||
state::{Data, Versions},
|
||||
State,
|
||||
},
|
||||
pubkey::Pubkey,
|
||||
system_instruction::{
|
||||
advance_nonce_account, authorize_nonce_account, create_nonce_account,
|
||||
@@ -30,11 +33,64 @@ use solana_sdk::{
|
||||
transaction::Transaction,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error, PartialEq)]
|
||||
pub enum CliNonceError {
|
||||
#[error("invalid account owner")]
|
||||
InvalidAccountOwner,
|
||||
#[error("invalid account data")]
|
||||
InvalidAccountData,
|
||||
#[error("unexpected account data size")]
|
||||
UnexpectedDataSize,
|
||||
#[error("query hash does not match stored hash")]
|
||||
InvalidHash,
|
||||
#[error("query authority does not match account authority")]
|
||||
InvalidAuthority,
|
||||
#[error("invalid state for requested operation")]
|
||||
InvalidStateForOperation,
|
||||
#[error("client error: {0}")]
|
||||
Client(String),
|
||||
}
|
||||
|
||||
pub const NONCE_ARG: ArgConstant<'static> = ArgConstant {
|
||||
name: "nonce",
|
||||
long: "nonce",
|
||||
help: "Provide the nonce account to use when creating a nonced \n\
|
||||
transaction. Nonced transactions are useful when a transaction \n\
|
||||
requires a lengthy signing process. Learn more about nonced \n\
|
||||
transactions at https://docs.solana.com/offline-signing/durable-nonce",
|
||||
};
|
||||
|
||||
pub const NONCE_AUTHORITY_ARG: ArgConstant<'static> = ArgConstant {
|
||||
name: "nonce_authority",
|
||||
long: "nonce-authority",
|
||||
help: "Provide the nonce authority keypair to use when signing a nonced transaction",
|
||||
};
|
||||
|
||||
pub trait NonceSubCommands {
|
||||
fn nonce_subcommands(self) -> Self;
|
||||
}
|
||||
|
||||
pub fn nonce_arg<'a, 'b>() -> Arg<'a, 'b> {
|
||||
Arg::with_name(NONCE_ARG.name)
|
||||
.long(NONCE_ARG.long)
|
||||
.takes_value(true)
|
||||
.value_name("PUBKEY")
|
||||
.requires(BLOCKHASH_ARG.name)
|
||||
.validator(is_valid_pubkey)
|
||||
.help(NONCE_ARG.help)
|
||||
}
|
||||
|
||||
pub fn nonce_authority_arg<'a, 'b>() -> Arg<'a, 'b> {
|
||||
Arg::with_name(NONCE_AUTHORITY_ARG.name)
|
||||
.long(NONCE_AUTHORITY_ARG.long)
|
||||
.takes_value(true)
|
||||
.value_name("KEYPAIR")
|
||||
.validator(is_valid_signer)
|
||||
.help(NONCE_AUTHORITY_ARG.help)
|
||||
}
|
||||
|
||||
impl NonceSubCommands for App<'_, '_> {
|
||||
fn nonce_subcommands(self) -> Self {
|
||||
self.subcommand(
|
||||
@@ -164,9 +220,64 @@ impl NonceSubCommands for App<'_, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_account(
|
||||
rpc_client: &RpcClient,
|
||||
nonce_pubkey: &Pubkey,
|
||||
) -> Result<Account, CliNonceError> {
|
||||
get_account_with_commitment(rpc_client, nonce_pubkey, CommitmentConfig::default())
|
||||
}
|
||||
|
||||
pub fn get_account_with_commitment(
|
||||
rpc_client: &RpcClient,
|
||||
nonce_pubkey: &Pubkey,
|
||||
commitment: CommitmentConfig,
|
||||
) -> Result<Account, CliNonceError> {
|
||||
rpc_client
|
||||
.get_account_with_commitment(nonce_pubkey, commitment)
|
||||
.map_err(|e| CliNonceError::Client(format!("{}", e)))
|
||||
.and_then(|result| {
|
||||
result.value.ok_or_else(|| {
|
||||
CliNonceError::Client(format!("AccountNotFound: pubkey={}", nonce_pubkey))
|
||||
})
|
||||
})
|
||||
.and_then(|a| match account_identity_ok(&a) {
|
||||
Ok(()) => Ok(a),
|
||||
Err(e) => Err(e),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn account_identity_ok(account: &Account) -> Result<(), CliNonceError> {
|
||||
if account.owner != system_program::id() {
|
||||
Err(CliNonceError::InvalidAccountOwner)
|
||||
} else if account.data.is_empty() {
|
||||
Err(CliNonceError::UnexpectedDataSize)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn state_from_account(account: &Account) -> Result<State, CliNonceError> {
|
||||
account_identity_ok(account)?;
|
||||
StateMut::<Versions>::state(account)
|
||||
.map_err(|_| CliNonceError::InvalidAccountData)
|
||||
.map(|v| v.convert_to_current())
|
||||
}
|
||||
|
||||
pub fn data_from_account(account: &Account) -> Result<Data, CliNonceError> {
|
||||
account_identity_ok(account)?;
|
||||
state_from_account(account).and_then(|ref s| data_from_state(s).map(|d| d.clone()))
|
||||
}
|
||||
|
||||
pub fn data_from_state(state: &State) -> Result<&Data, CliNonceError> {
|
||||
match state {
|
||||
State::Uninitialized => Err(CliNonceError::InvalidStateForOperation),
|
||||
State::Initialized(data) => Ok(data),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_authorize_nonce_account(
|
||||
matches: &ArgMatches<'_>,
|
||||
default_signer: &DefaultSigner,
|
||||
default_signer_path: &str,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap();
|
||||
@@ -175,9 +286,10 @@ pub fn parse_authorize_nonce_account(
|
||||
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
|
||||
|
||||
let payer_provided = None;
|
||||
let signer_info = default_signer.generate_unique_signers(
|
||||
let signer_info = generate_unique_signers(
|
||||
vec![payer_provided, nonce_authority],
|
||||
matches,
|
||||
default_signer_path,
|
||||
wallet_manager,
|
||||
)?;
|
||||
|
||||
@@ -193,7 +305,7 @@ pub fn parse_authorize_nonce_account(
|
||||
|
||||
pub fn parse_nonce_create_account(
|
||||
matches: &ArgMatches<'_>,
|
||||
default_signer: &DefaultSigner,
|
||||
default_signer_path: &str,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let (nonce_account, nonce_account_pubkey) =
|
||||
@@ -203,9 +315,10 @@ pub fn parse_nonce_create_account(
|
||||
let nonce_authority = pubkey_of_signer(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
|
||||
|
||||
let payer_provided = None;
|
||||
let signer_info = default_signer.generate_unique_signers(
|
||||
let signer_info = generate_unique_signers(
|
||||
vec![payer_provided, nonce_account],
|
||||
matches,
|
||||
default_signer_path,
|
||||
wallet_manager,
|
||||
)?;
|
||||
|
||||
@@ -235,7 +348,7 @@ pub fn parse_get_nonce(
|
||||
|
||||
pub fn parse_new_nonce(
|
||||
matches: &ArgMatches<'_>,
|
||||
default_signer: &DefaultSigner,
|
||||
default_signer_path: &str,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap();
|
||||
@@ -243,9 +356,10 @@ pub fn parse_new_nonce(
|
||||
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
|
||||
|
||||
let payer_provided = None;
|
||||
let signer_info = default_signer.generate_unique_signers(
|
||||
let signer_info = generate_unique_signers(
|
||||
vec![payer_provided, nonce_authority],
|
||||
matches,
|
||||
default_signer_path,
|
||||
wallet_manager,
|
||||
)?;
|
||||
|
||||
@@ -277,7 +391,7 @@ pub fn parse_show_nonce_account(
|
||||
|
||||
pub fn parse_withdraw_from_nonce_account(
|
||||
matches: &ArgMatches<'_>,
|
||||
default_signer: &DefaultSigner,
|
||||
default_signer_path: &str,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap();
|
||||
@@ -288,9 +402,10 @@ pub fn parse_withdraw_from_nonce_account(
|
||||
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
|
||||
|
||||
let payer_provided = None;
|
||||
let signer_info = default_signer.generate_unique_signers(
|
||||
let signer_info = generate_unique_signers(
|
||||
vec![payer_provided, nonce_authority],
|
||||
matches,
|
||||
default_signer_path,
|
||||
wallet_manager,
|
||||
)?;
|
||||
|
||||
@@ -314,14 +429,14 @@ pub fn check_nonce_account(
|
||||
match state_from_account(nonce_account)? {
|
||||
State::Initialized(ref data) => {
|
||||
if &data.blockhash != nonce_hash {
|
||||
Err(Error::InvalidHash.into())
|
||||
Err(CliNonceError::InvalidHash.into())
|
||||
} else if nonce_authority != &data.authority {
|
||||
Err(Error::InvalidAuthority.into())
|
||||
Err(CliNonceError::InvalidAuthority.into())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
State::Uninitialized => Err(Error::InvalidStateForOperation.into()),
|
||||
State::Uninitialized => Err(CliNonceError::InvalidStateForOperation.into()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -576,10 +691,9 @@ mod tests {
|
||||
use crate::cli::{app, parse_command};
|
||||
use solana_sdk::{
|
||||
account::Account,
|
||||
account_utils::StateMut,
|
||||
fee_calculator::FeeCalculator,
|
||||
hash::hash,
|
||||
nonce::{self, state::Versions, State},
|
||||
nonce::{self, State},
|
||||
signature::{read_keypair_file, write_keypair, Keypair, Signer},
|
||||
system_program,
|
||||
};
|
||||
@@ -596,10 +710,6 @@ 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 {
|
||||
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();
|
||||
@@ -618,7 +728,12 @@ mod tests {
|
||||
&Pubkey::default().to_string(),
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_authorize_nonce_account, &default_signer, &mut None).unwrap(),
|
||||
parse_command(
|
||||
&test_authorize_nonce_account,
|
||||
&default_keypair_file,
|
||||
&mut None
|
||||
)
|
||||
.unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::AuthorizeNonceAccount {
|
||||
nonce_account: nonce_account_pubkey,
|
||||
@@ -639,7 +754,12 @@ mod tests {
|
||||
&authority_keypair_file,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_authorize_nonce_account, &default_signer, &mut None).unwrap(),
|
||||
parse_command(
|
||||
&test_authorize_nonce_account,
|
||||
&default_keypair_file,
|
||||
&mut None
|
||||
)
|
||||
.unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::AuthorizeNonceAccount {
|
||||
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
|
||||
@@ -661,7 +781,7 @@ mod tests {
|
||||
"50",
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_create_nonce_account, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_create_nonce_account, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::CreateNonceAccount {
|
||||
nonce_account: 1,
|
||||
@@ -686,7 +806,7 @@ mod tests {
|
||||
&authority_keypair_file,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_create_nonce_account, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_create_nonce_account, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::CreateNonceAccount {
|
||||
nonce_account: 1,
|
||||
@@ -708,7 +828,7 @@ mod tests {
|
||||
&nonce_account_string,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_get_nonce, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_get_nonce, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::GetNonce(nonce_account_keypair.pubkey()),
|
||||
signers: vec![],
|
||||
@@ -722,7 +842,7 @@ mod tests {
|
||||
.get_matches_from(vec!["test", "new-nonce", &keypair_file]);
|
||||
let nonce_account = read_keypair_file(&keypair_file).unwrap();
|
||||
assert_eq!(
|
||||
parse_command(&test_new_nonce, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_new_nonce, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::NewNonce {
|
||||
nonce_account: nonce_account.pubkey(),
|
||||
@@ -742,7 +862,7 @@ mod tests {
|
||||
]);
|
||||
let nonce_account = read_keypair_file(&keypair_file).unwrap();
|
||||
assert_eq!(
|
||||
parse_command(&test_new_nonce, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_new_nonce, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::NewNonce {
|
||||
nonce_account: nonce_account.pubkey(),
|
||||
@@ -762,7 +882,7 @@ mod tests {
|
||||
&nonce_account_string,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_show_nonce_account, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_show_nonce_account, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::ShowNonceAccount {
|
||||
nonce_account_pubkey: nonce_account_keypair.pubkey(),
|
||||
@@ -783,7 +903,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
parse_command(
|
||||
&test_withdraw_from_nonce_account,
|
||||
&default_signer,
|
||||
&default_keypair_file,
|
||||
&mut None
|
||||
)
|
||||
.unwrap(),
|
||||
@@ -811,7 +931,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
parse_command(
|
||||
&test_withdraw_from_nonce_account,
|
||||
&default_signer,
|
||||
&default_keypair_file,
|
||||
&mut None
|
||||
)
|
||||
.unwrap(),
|
||||
@@ -833,7 +953,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_check_nonce_account() {
|
||||
let blockhash = Hash::default();
|
||||
let nonce_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let nonce_pubkey = Pubkey::new_rand();
|
||||
let data = Versions::new_current(State::Initialized(nonce::state::Data {
|
||||
authority: nonce_pubkey,
|
||||
blockhash,
|
||||
@@ -846,14 +966,14 @@ mod tests {
|
||||
if let CliError::InvalidNonce(err) =
|
||||
check_nonce_account(&invalid_owner.unwrap(), &nonce_pubkey, &blockhash).unwrap_err()
|
||||
{
|
||||
assert_eq!(err, Error::InvalidAccountOwner,);
|
||||
assert_eq!(err, CliNonceError::InvalidAccountOwner,);
|
||||
}
|
||||
|
||||
let invalid_data = Account::new_data(1, &"invalid", &system_program::ID);
|
||||
if let CliError::InvalidNonce(err) =
|
||||
check_nonce_account(&invalid_data.unwrap(), &nonce_pubkey, &blockhash).unwrap_err()
|
||||
{
|
||||
assert_eq!(err, Error::InvalidAccountData,);
|
||||
assert_eq!(err, CliNonceError::InvalidAccountData,);
|
||||
}
|
||||
|
||||
let data = Versions::new_current(State::Initialized(nonce::state::Data {
|
||||
@@ -865,11 +985,11 @@ mod tests {
|
||||
if let CliError::InvalidNonce(err) =
|
||||
check_nonce_account(&invalid_hash.unwrap(), &nonce_pubkey, &blockhash).unwrap_err()
|
||||
{
|
||||
assert_eq!(err, Error::InvalidHash,);
|
||||
assert_eq!(err, CliNonceError::InvalidHash,);
|
||||
}
|
||||
|
||||
let data = Versions::new_current(State::Initialized(nonce::state::Data {
|
||||
authority: solana_sdk::pubkey::new_rand(),
|
||||
authority: Pubkey::new_rand(),
|
||||
blockhash,
|
||||
fee_calculator: FeeCalculator::default(),
|
||||
}));
|
||||
@@ -877,7 +997,7 @@ mod tests {
|
||||
if let CliError::InvalidNonce(err) =
|
||||
check_nonce_account(&invalid_authority.unwrap(), &nonce_pubkey, &blockhash).unwrap_err()
|
||||
{
|
||||
assert_eq!(err, Error::InvalidAuthority,);
|
||||
assert_eq!(err, CliNonceError::InvalidAuthority,);
|
||||
}
|
||||
|
||||
let data = Versions::new_current(State::Uninitialized);
|
||||
@@ -885,7 +1005,7 @@ mod tests {
|
||||
if let CliError::InvalidNonce(err) =
|
||||
check_nonce_account(&invalid_state.unwrap(), &nonce_pubkey, &blockhash).unwrap_err()
|
||||
{
|
||||
assert_eq!(err, Error::InvalidStateForOperation,);
|
||||
assert_eq!(err, CliNonceError::InvalidStateForOperation,);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -897,14 +1017,14 @@ mod tests {
|
||||
let system_account = Account::new(1, 0, &system_program::id());
|
||||
assert_eq!(
|
||||
account_identity_ok(&system_account),
|
||||
Err(Error::UnexpectedDataSize),
|
||||
Err(CliNonceError::UnexpectedDataSize),
|
||||
);
|
||||
|
||||
let other_program = Pubkey::new(&[1u8; 32]);
|
||||
let other_account_no_data = Account::new(1, 0, &other_program);
|
||||
assert_eq!(
|
||||
account_identity_ok(&other_account_no_data),
|
||||
Err(Error::InvalidAccountOwner),
|
||||
Err(CliNonceError::InvalidAccountOwner),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -929,7 +1049,7 @@ mod tests {
|
||||
let wrong_data_size_account = Account::new(1, 1, &system_program::id());
|
||||
assert_eq!(
|
||||
state_from_account(&wrong_data_size_account),
|
||||
Err(Error::InvalidAccountData),
|
||||
Err(CliNonceError::InvalidAccountData),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -939,11 +1059,11 @@ mod tests {
|
||||
let state = state_from_account(&nonce_account).unwrap();
|
||||
assert_eq!(
|
||||
data_from_state(&state),
|
||||
Err(Error::InvalidStateForOperation)
|
||||
Err(CliNonceError::InvalidStateForOperation)
|
||||
);
|
||||
assert_eq!(
|
||||
data_from_account(&nonce_account),
|
||||
Err(Error::InvalidStateForOperation)
|
||||
Err(CliNonceError::InvalidStateForOperation)
|
||||
);
|
||||
|
||||
let data = nonce::state::Data {
|
||||
|
@@ -1,13 +1,5 @@
|
||||
use crate::{nonce_utils, rpc_client::RpcClient};
|
||||
use clap::ArgMatches;
|
||||
use solana_clap_utils::{
|
||||
input_parsers::{pubkey_of, value_of},
|
||||
nonce::*,
|
||||
offline::*,
|
||||
};
|
||||
use solana_sdk::{
|
||||
commitment_config::CommitmentConfig, fee_calculator::FeeCalculator, hash::Hash, pubkey::Pubkey,
|
||||
};
|
||||
use super::*;
|
||||
use solana_sdk::commitment_config::CommitmentConfig;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Source {
|
||||
@@ -29,8 +21,8 @@ impl Source {
|
||||
Ok((res.0, res.1))
|
||||
}
|
||||
Self::NonceAccount(ref pubkey) => {
|
||||
let data = nonce_utils::get_account_with_commitment(rpc_client, pubkey, commitment)
|
||||
.and_then(|ref a| nonce_utils::data_from_account(a))?;
|
||||
let data = nonce::get_account_with_commitment(rpc_client, pubkey, commitment)
|
||||
.and_then(|ref a| nonce::data_from_account(a))?;
|
||||
Ok((data.blockhash, data.fee_calculator))
|
||||
}
|
||||
}
|
||||
@@ -50,8 +42,8 @@ impl Source {
|
||||
Ok(res)
|
||||
}
|
||||
Self::NonceAccount(ref pubkey) => {
|
||||
let res = nonce_utils::get_account_with_commitment(rpc_client, pubkey, commitment)?;
|
||||
let res = nonce_utils::data_from_account(&res)?;
|
||||
let res = nonce::get_account_with_commitment(rpc_client, pubkey, commitment)?;
|
||||
let res = nonce::data_from_account(&res)?;
|
||||
Ok(Some(res)
|
||||
.filter(|d| d.blockhash == *blockhash)
|
||||
.map(|d| d.fee_calculator))
|
||||
@@ -83,7 +75,7 @@ impl BlockhashQuery {
|
||||
pub fn new_from_matches(matches: &ArgMatches<'_>) -> Self {
|
||||
let blockhash = value_of(matches, BLOCKHASH_ARG.name);
|
||||
let sign_only = matches.is_present(SIGN_ONLY_ARG.name);
|
||||
let nonce_account = pubkey_of(matches, NONCE_ARG.name);
|
||||
let nonce_account = pubkey_of(matches, nonce::NONCE_ARG.name);
|
||||
BlockhashQuery::new(blockhash, sign_only, nonce_account)
|
||||
}
|
||||
|
||||
@@ -116,15 +108,17 @@ impl Default for BlockhashQuery {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
blockhash_query,
|
||||
rpc_request::RpcRequest,
|
||||
rpc_response::{Response, RpcFeeCalculator, RpcResponseContext},
|
||||
};
|
||||
use crate::{nonce::nonce_arg, offline::blockhash_query::BlockhashQuery};
|
||||
use clap::App;
|
||||
use serde_json::{self, json, Value};
|
||||
use solana_account_decoder::{UiAccount, UiAccountEncoding};
|
||||
use solana_sdk::{account::Account, hash::hash, nonce, system_program};
|
||||
use solana_client::{
|
||||
rpc_request::RpcRequest,
|
||||
rpc_response::{Response, RpcFeeCalculator, RpcResponseContext},
|
||||
};
|
||||
use solana_sdk::{
|
||||
account::Account, fee_calculator::FeeCalculator, hash::hash, nonce, system_program,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[test]
|
||||
@@ -178,7 +172,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_blockhash_query_new_from_matches_ok() {
|
||||
let test_commands = App::new("blockhash_query_test")
|
||||
.nonce_args(false)
|
||||
.arg(nonce_arg())
|
||||
.offline_args();
|
||||
let blockhash = hash(&[1u8]);
|
||||
let blockhash_string = blockhash.to_string();
|
129
cli/src/offline/mod.rs
Normal file
129
cli/src/offline/mod.rs
Normal file
@@ -0,0 +1,129 @@
|
||||
pub mod blockhash_query;
|
||||
|
||||
use crate::nonce;
|
||||
use clap::{App, Arg, ArgMatches};
|
||||
use serde_json::Value;
|
||||
use solana_clap_utils::{
|
||||
input_parsers::{pubkey_of, value_of},
|
||||
input_validators::{is_hash, is_pubkey_sig},
|
||||
keypair::presigner_from_pubkey_sigs,
|
||||
offline::{BLOCKHASH_ARG, SIGNER_ARG, SIGN_ONLY_ARG},
|
||||
};
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_sdk::{
|
||||
fee_calculator::FeeCalculator,
|
||||
hash::Hash,
|
||||
pubkey::Pubkey,
|
||||
signature::{Presigner, Signature},
|
||||
};
|
||||
use std::str::FromStr;
|
||||
|
||||
fn blockhash_arg<'a, 'b>() -> Arg<'a, 'b> {
|
||||
Arg::with_name(BLOCKHASH_ARG.name)
|
||||
.long(BLOCKHASH_ARG.long)
|
||||
.takes_value(true)
|
||||
.value_name("BLOCKHASH")
|
||||
.validator(is_hash)
|
||||
.help(BLOCKHASH_ARG.help)
|
||||
}
|
||||
|
||||
fn sign_only_arg<'a, 'b>() -> Arg<'a, 'b> {
|
||||
Arg::with_name(SIGN_ONLY_ARG.name)
|
||||
.long(SIGN_ONLY_ARG.long)
|
||||
.takes_value(false)
|
||||
.requires(BLOCKHASH_ARG.name)
|
||||
.help(SIGN_ONLY_ARG.help)
|
||||
}
|
||||
|
||||
fn signer_arg<'a, 'b>() -> Arg<'a, 'b> {
|
||||
Arg::with_name(SIGNER_ARG.name)
|
||||
.long(SIGNER_ARG.long)
|
||||
.takes_value(true)
|
||||
.value_name("PUBKEY=SIGNATURE")
|
||||
.validator(is_pubkey_sig)
|
||||
.requires(BLOCKHASH_ARG.name)
|
||||
.multiple(true)
|
||||
.help(SIGNER_ARG.help)
|
||||
}
|
||||
|
||||
pub trait OfflineArgs {
|
||||
fn offline_args(self) -> Self;
|
||||
}
|
||||
|
||||
impl OfflineArgs for App<'_, '_> {
|
||||
fn offline_args(self) -> Self {
|
||||
self.arg(blockhash_arg())
|
||||
.arg(sign_only_arg())
|
||||
.arg(signer_arg())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SignOnly {
|
||||
pub blockhash: Hash,
|
||||
pub present_signers: Vec<(Pubkey, Signature)>,
|
||||
pub absent_signers: Vec<Pubkey>,
|
||||
pub bad_signers: Vec<Pubkey>,
|
||||
}
|
||||
|
||||
impl SignOnly {
|
||||
pub fn has_all_signers(&self) -> bool {
|
||||
self.absent_signers.is_empty() && self.bad_signers.is_empty()
|
||||
}
|
||||
|
||||
pub fn presigner_of(&self, pubkey: &Pubkey) -> Option<Presigner> {
|
||||
presigner_from_pubkey_sigs(pubkey, &self.present_signers)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_sign_only_reply_string(reply: &str) -> SignOnly {
|
||||
let object: Value = serde_json::from_str(&reply).unwrap();
|
||||
let blockhash_str = object.get("blockhash").unwrap().as_str().unwrap();
|
||||
let blockhash = blockhash_str.parse::<Hash>().unwrap();
|
||||
let mut present_signers: Vec<(Pubkey, Signature)> = Vec::new();
|
||||
let signer_strings = object.get("signers");
|
||||
if let Some(sig_strings) = signer_strings {
|
||||
present_signers = sig_strings
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|signer_string| {
|
||||
let mut signer = signer_string.as_str().unwrap().split('=');
|
||||
let key = Pubkey::from_str(signer.next().unwrap()).unwrap();
|
||||
let sig = Signature::from_str(signer.next().unwrap()).unwrap();
|
||||
(key, sig)
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
let mut absent_signers: Vec<Pubkey> = Vec::new();
|
||||
let signer_strings = object.get("absent");
|
||||
if let Some(sig_strings) = signer_strings {
|
||||
absent_signers = sig_strings
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|val| {
|
||||
let s = val.as_str().unwrap();
|
||||
Pubkey::from_str(s).unwrap()
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
let mut bad_signers: Vec<Pubkey> = Vec::new();
|
||||
let signer_strings = object.get("badSig");
|
||||
if let Some(sig_strings) = signer_strings {
|
||||
bad_signers = sig_strings
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|val| {
|
||||
let s = val.as_str().unwrap();
|
||||
Pubkey::from_str(s).unwrap()
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
SignOnly {
|
||||
blockhash,
|
||||
present_signers,
|
||||
absent_signers,
|
||||
bad_signers,
|
||||
}
|
||||
}
|
392
cli/src/stake.rs
392
cli/src/stake.rs
@@ -1,39 +1,22 @@
|
||||
use crate::{
|
||||
checks::{check_account_for_fee_with_commitment, check_unique_pubkeys},
|
||||
cli::{
|
||||
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
|
||||
ProcessResult,
|
||||
fee_payer_arg, generate_unique_signers, log_instruction_custom_error, nonce_authority_arg,
|
||||
return_signers, CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult,
|
||||
SignerIndex, FEE_PAYER_ARG,
|
||||
},
|
||||
nonce::check_nonce_account,
|
||||
cli_output::{CliStakeHistory, CliStakeHistoryEntry, CliStakeState, CliStakeType},
|
||||
nonce::{self, check_nonce_account, nonce_arg, NONCE_ARG, NONCE_AUTHORITY_ARG},
|
||||
offline::{blockhash_query::BlockhashQuery, *},
|
||||
spend_utils::{resolve_spend_tx_and_check_account_balances, SpendAmount},
|
||||
};
|
||||
use chrono::{Local, TimeZone};
|
||||
use clap::{App, Arg, ArgGroup, ArgMatches, SubCommand};
|
||||
use solana_clap_utils::{
|
||||
fee_payer::{fee_payer_arg, FEE_PAYER_ARG},
|
||||
input_parsers::*,
|
||||
input_validators::*,
|
||||
keypair::{DefaultSigner, SignerIndex},
|
||||
nonce::*,
|
||||
offline::*,
|
||||
ArgConstant,
|
||||
};
|
||||
use solana_cli_output::{
|
||||
return_signers, CliEpochReward, CliStakeHistory, CliStakeHistoryEntry, CliStakeState,
|
||||
CliStakeType,
|
||||
};
|
||||
use solana_client::{
|
||||
blockhash_query::BlockhashQuery,
|
||||
client_error::{ClientError, ClientErrorKind},
|
||||
nonce_utils,
|
||||
rpc_client::RpcClient,
|
||||
rpc_custom_error,
|
||||
rpc_request::{self, DELINQUENT_VALIDATOR_SLOT_DISTANCE},
|
||||
};
|
||||
use solana_clap_utils::{input_parsers::*, input_validators::*, offline::*, ArgConstant};
|
||||
use solana_client::{rpc_client::RpcClient, rpc_request::DELINQUENT_VALIDATOR_SLOT_DISTANCE};
|
||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||
use solana_sdk::{
|
||||
account_utils::StateMut,
|
||||
clock::{Clock, Epoch, Slot, UnixTimestamp, SECONDS_PER_DAY},
|
||||
clock::Clock,
|
||||
message::Message,
|
||||
pubkey::Pubkey,
|
||||
system_instruction::SystemError,
|
||||
@@ -49,7 +32,7 @@ use solana_stake_program::{
|
||||
stake_state::{Authorized, Lockup, Meta, StakeAuthorize, StakeState},
|
||||
};
|
||||
use solana_vote_program::vote_state::VoteState;
|
||||
use std::{convert::TryInto, ops::Deref, sync::Arc};
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
pub const STAKE_AUTHORITY_ARG: ArgConstant<'static> = ArgConstant {
|
||||
name: "stake_authority",
|
||||
@@ -161,7 +144,8 @@ impl StakeSubCommands for App<'_, '_> {
|
||||
.help("Source account of funds [default: cli config keypair]"),
|
||||
)
|
||||
.offline_args()
|
||||
.nonce_args(false)
|
||||
.arg(nonce_arg())
|
||||
.arg(nonce_authority_arg())
|
||||
.arg(fee_payer_arg())
|
||||
)
|
||||
.subcommand(
|
||||
@@ -190,7 +174,8 @@ impl StakeSubCommands for App<'_, '_> {
|
||||
)
|
||||
.arg(stake_authority_arg())
|
||||
.offline_args()
|
||||
.nonce_args(false)
|
||||
.arg(nonce_arg())
|
||||
.arg(nonce_authority_arg())
|
||||
.arg(fee_payer_arg())
|
||||
)
|
||||
.subcommand(
|
||||
@@ -220,7 +205,8 @@ impl StakeSubCommands for App<'_, '_> {
|
||||
.arg(stake_authority_arg())
|
||||
.arg(withdraw_authority_arg())
|
||||
.offline_args()
|
||||
.nonce_args(false)
|
||||
.arg(nonce_arg())
|
||||
.arg(nonce_authority_arg())
|
||||
.arg(fee_payer_arg())
|
||||
)
|
||||
.subcommand(
|
||||
@@ -235,7 +221,8 @@ impl StakeSubCommands for App<'_, '_> {
|
||||
)
|
||||
.arg(stake_authority_arg())
|
||||
.offline_args()
|
||||
.nonce_args(false)
|
||||
.arg(nonce_arg())
|
||||
.arg(nonce_authority_arg())
|
||||
.arg(fee_payer_arg())
|
||||
)
|
||||
.subcommand(
|
||||
@@ -275,7 +262,8 @@ impl StakeSubCommands for App<'_, '_> {
|
||||
)
|
||||
.arg(stake_authority_arg())
|
||||
.offline_args()
|
||||
.nonce_args(false)
|
||||
.arg(nonce_arg())
|
||||
.arg(nonce_authority_arg())
|
||||
.arg(fee_payer_arg())
|
||||
)
|
||||
.subcommand(
|
||||
@@ -297,7 +285,8 @@ impl StakeSubCommands for App<'_, '_> {
|
||||
)
|
||||
.arg(stake_authority_arg())
|
||||
.offline_args()
|
||||
.nonce_args(false)
|
||||
.arg(nonce_arg())
|
||||
.arg(nonce_authority_arg())
|
||||
.arg(fee_payer_arg())
|
||||
)
|
||||
.subcommand(
|
||||
@@ -328,7 +317,8 @@ impl StakeSubCommands for App<'_, '_> {
|
||||
)
|
||||
.arg(withdraw_authority_arg())
|
||||
.offline_args()
|
||||
.nonce_args(false)
|
||||
.arg(nonce_arg())
|
||||
.arg(nonce_authority_arg())
|
||||
.arg(fee_payer_arg())
|
||||
.arg(
|
||||
Arg::with_name("custodian")
|
||||
@@ -383,7 +373,8 @@ impl StakeSubCommands for App<'_, '_> {
|
||||
.help("Keypair of the existing custodian [default: cli config pubkey]")
|
||||
)
|
||||
.offline_args()
|
||||
.nonce_args(false)
|
||||
.arg(nonce_arg())
|
||||
.arg(nonce_authority_arg())
|
||||
.arg(fee_payer_arg())
|
||||
)
|
||||
.subcommand(
|
||||
@@ -420,7 +411,7 @@ impl StakeSubCommands for App<'_, '_> {
|
||||
|
||||
pub fn parse_stake_create_account(
|
||||
matches: &ArgMatches<'_>,
|
||||
default_signer: &DefaultSigner,
|
||||
default_signer_path: &str,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let seed = matches.value_of("seed").map(|s| s.to_string());
|
||||
@@ -445,7 +436,7 @@ pub fn parse_stake_create_account(
|
||||
bulk_signers.push(nonce_authority);
|
||||
}
|
||||
let signer_info =
|
||||
default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
|
||||
generate_unique_signers(bulk_signers, matches, default_signer_path, wallet_manager)?;
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::CreateStakeAccount {
|
||||
@@ -472,7 +463,7 @@ pub fn parse_stake_create_account(
|
||||
|
||||
pub fn parse_stake_delegate_stake(
|
||||
matches: &ArgMatches<'_>,
|
||||
default_signer: &DefaultSigner,
|
||||
default_signer_path: &str,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let stake_account_pubkey =
|
||||
@@ -494,7 +485,7 @@ pub fn parse_stake_delegate_stake(
|
||||
bulk_signers.push(nonce_authority);
|
||||
}
|
||||
let signer_info =
|
||||
default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
|
||||
generate_unique_signers(bulk_signers, matches, default_signer_path, wallet_manager)?;
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::DelegateStake {
|
||||
@@ -514,7 +505,7 @@ pub fn parse_stake_delegate_stake(
|
||||
|
||||
pub fn parse_stake_authorize(
|
||||
matches: &ArgMatches<'_>,
|
||||
default_signer: &DefaultSigner,
|
||||
default_signer_path: &str,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let stake_account_pubkey =
|
||||
@@ -566,7 +557,7 @@ pub fn parse_stake_authorize(
|
||||
bulk_signers.push(nonce_authority);
|
||||
}
|
||||
let signer_info =
|
||||
default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
|
||||
generate_unique_signers(bulk_signers, matches, default_signer_path, wallet_manager)?;
|
||||
|
||||
let new_authorizations = new_authorizations
|
||||
.into_iter()
|
||||
@@ -597,7 +588,7 @@ pub fn parse_stake_authorize(
|
||||
|
||||
pub fn parse_split_stake(
|
||||
matches: &ArgMatches<'_>,
|
||||
default_signer: &DefaultSigner,
|
||||
default_signer_path: &str,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let stake_account_pubkey =
|
||||
@@ -621,7 +612,7 @@ pub fn parse_split_stake(
|
||||
bulk_signers.push(nonce_authority);
|
||||
}
|
||||
let signer_info =
|
||||
default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
|
||||
generate_unique_signers(bulk_signers, matches, default_signer_path, wallet_manager)?;
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::SplitStake {
|
||||
@@ -642,7 +633,7 @@ pub fn parse_split_stake(
|
||||
|
||||
pub fn parse_merge_stake(
|
||||
matches: &ArgMatches<'_>,
|
||||
default_signer: &DefaultSigner,
|
||||
default_signer_path: &str,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let stake_account_pubkey =
|
||||
@@ -664,7 +655,7 @@ pub fn parse_merge_stake(
|
||||
bulk_signers.push(nonce_authority);
|
||||
}
|
||||
let signer_info =
|
||||
default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
|
||||
generate_unique_signers(bulk_signers, matches, default_signer_path, wallet_manager)?;
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::MergeStake {
|
||||
@@ -683,7 +674,7 @@ pub fn parse_merge_stake(
|
||||
|
||||
pub fn parse_stake_deactivate_stake(
|
||||
matches: &ArgMatches<'_>,
|
||||
default_signer: &DefaultSigner,
|
||||
default_signer_path: &str,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let stake_account_pubkey =
|
||||
@@ -702,7 +693,7 @@ pub fn parse_stake_deactivate_stake(
|
||||
bulk_signers.push(nonce_authority);
|
||||
}
|
||||
let signer_info =
|
||||
default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
|
||||
generate_unique_signers(bulk_signers, matches, default_signer_path, wallet_manager)?;
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::DeactivateStake {
|
||||
@@ -720,7 +711,7 @@ pub fn parse_stake_deactivate_stake(
|
||||
|
||||
pub fn parse_stake_withdraw_stake(
|
||||
matches: &ArgMatches<'_>,
|
||||
default_signer: &DefaultSigner,
|
||||
default_signer_path: &str,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let stake_account_pubkey =
|
||||
@@ -746,7 +737,7 @@ pub fn parse_stake_withdraw_stake(
|
||||
bulk_signers.push(custodian);
|
||||
}
|
||||
let signer_info =
|
||||
default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
|
||||
generate_unique_signers(bulk_signers, matches, default_signer_path, wallet_manager)?;
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::WithdrawStake {
|
||||
@@ -767,7 +758,7 @@ pub fn parse_stake_withdraw_stake(
|
||||
|
||||
pub fn parse_stake_set_lockup(
|
||||
matches: &ArgMatches<'_>,
|
||||
default_signer: &DefaultSigner,
|
||||
default_signer_path: &str,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let stake_account_pubkey =
|
||||
@@ -790,7 +781,7 @@ pub fn parse_stake_set_lockup(
|
||||
bulk_signers.push(nonce_authority);
|
||||
}
|
||||
let signer_info =
|
||||
default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
|
||||
generate_unique_signers(bulk_signers, matches, default_signer_path, wallet_manager)?;
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::StakeSetLockup {
|
||||
@@ -943,11 +934,8 @@ pub fn process_create_stake_account(
|
||||
}
|
||||
|
||||
if let Some(nonce_account) = &nonce_account {
|
||||
let nonce_account = nonce_utils::get_account_with_commitment(
|
||||
rpc_client,
|
||||
nonce_account,
|
||||
config.commitment,
|
||||
)?;
|
||||
let nonce_account =
|
||||
nonce::get_account_with_commitment(rpc_client, nonce_account, config.commitment)?;
|
||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
||||
}
|
||||
}
|
||||
@@ -955,7 +943,7 @@ pub fn process_create_stake_account(
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
if sign_only {
|
||||
tx.try_partial_sign(&config.signers, recent_blockhash)?;
|
||||
return_signers(&tx, &config.output_format)
|
||||
return_signers(&tx, &config)
|
||||
} else {
|
||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
|
||||
@@ -1014,15 +1002,12 @@ pub fn process_stake_authorize(
|
||||
|
||||
if sign_only {
|
||||
tx.try_partial_sign(&config.signers, recent_blockhash)?;
|
||||
return_signers(&tx, &config.output_format)
|
||||
return_signers(&tx, &config)
|
||||
} else {
|
||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||
if let Some(nonce_account) = &nonce_account {
|
||||
let nonce_account = nonce_utils::get_account_with_commitment(
|
||||
rpc_client,
|
||||
nonce_account,
|
||||
config.commitment,
|
||||
)?;
|
||||
let nonce_account =
|
||||
nonce::get_account_with_commitment(rpc_client, nonce_account, config.commitment)?;
|
||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
||||
}
|
||||
check_account_for_fee_with_commitment(
|
||||
@@ -1077,15 +1062,12 @@ pub fn process_deactivate_stake_account(
|
||||
|
||||
if sign_only {
|
||||
tx.try_partial_sign(&config.signers, recent_blockhash)?;
|
||||
return_signers(&tx, &config.output_format)
|
||||
return_signers(&tx, &config)
|
||||
} else {
|
||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||
if let Some(nonce_account) = &nonce_account {
|
||||
let nonce_account = nonce_utils::get_account_with_commitment(
|
||||
rpc_client,
|
||||
nonce_account,
|
||||
config.commitment,
|
||||
)?;
|
||||
let nonce_account =
|
||||
nonce::get_account_with_commitment(rpc_client, nonce_account, config.commitment)?;
|
||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
||||
}
|
||||
check_account_for_fee_with_commitment(
|
||||
@@ -1149,15 +1131,12 @@ pub fn process_withdraw_stake(
|
||||
|
||||
if sign_only {
|
||||
tx.try_partial_sign(&config.signers, recent_blockhash)?;
|
||||
return_signers(&tx, &config.output_format)
|
||||
return_signers(&tx, &config)
|
||||
} else {
|
||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||
if let Some(nonce_account) = &nonce_account {
|
||||
let nonce_account = nonce_utils::get_account_with_commitment(
|
||||
rpc_client,
|
||||
nonce_account,
|
||||
config.commitment,
|
||||
)?;
|
||||
let nonce_account =
|
||||
nonce::get_account_with_commitment(rpc_client, nonce_account, config.commitment)?;
|
||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
||||
}
|
||||
check_account_for_fee_with_commitment(
|
||||
@@ -1292,15 +1271,12 @@ pub fn process_split_stake(
|
||||
|
||||
if sign_only {
|
||||
tx.try_partial_sign(&config.signers, recent_blockhash)?;
|
||||
return_signers(&tx, &config.output_format)
|
||||
return_signers(&tx, &config)
|
||||
} else {
|
||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||
if let Some(nonce_account) = &nonce_account {
|
||||
let nonce_account = nonce_utils::get_account_with_commitment(
|
||||
rpc_client,
|
||||
nonce_account,
|
||||
config.commitment,
|
||||
)?;
|
||||
let nonce_account =
|
||||
nonce::get_account_with_commitment(rpc_client, nonce_account, config.commitment)?;
|
||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
||||
}
|
||||
check_account_for_fee_with_commitment(
|
||||
@@ -1394,15 +1370,12 @@ pub fn process_merge_stake(
|
||||
|
||||
if sign_only {
|
||||
tx.try_partial_sign(&config.signers, recent_blockhash)?;
|
||||
return_signers(&tx, &config.output_format)
|
||||
return_signers(&tx, &config)
|
||||
} else {
|
||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||
if let Some(nonce_account) = &nonce_account {
|
||||
let nonce_account = nonce_utils::get_account_with_commitment(
|
||||
rpc_client,
|
||||
nonce_account,
|
||||
config.commitment,
|
||||
)?;
|
||||
let nonce_account =
|
||||
nonce::get_account_with_commitment(rpc_client, nonce_account, config.commitment)?;
|
||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
||||
}
|
||||
check_account_for_fee_with_commitment(
|
||||
@@ -1460,15 +1433,12 @@ pub fn process_stake_set_lockup(
|
||||
|
||||
if sign_only {
|
||||
tx.try_partial_sign(&config.signers, recent_blockhash)?;
|
||||
return_signers(&tx, &config.output_format)
|
||||
return_signers(&tx, &config)
|
||||
} else {
|
||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||
if let Some(nonce_account) = &nonce_account {
|
||||
let nonce_account = nonce_utils::get_account_with_commitment(
|
||||
rpc_client,
|
||||
nonce_account,
|
||||
config.commitment,
|
||||
)?;
|
||||
let nonce_account =
|
||||
nonce::get_account_with_commitment(rpc_client, nonce_account, config.commitment)?;
|
||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
||||
}
|
||||
check_account_for_fee_with_commitment(
|
||||
@@ -1549,7 +1519,6 @@ pub fn build_stake_state(
|
||||
active_stake: u64_some_if_not_zero(active_stake),
|
||||
activating_stake: u64_some_if_not_zero(activating_stake),
|
||||
deactivating_stake: u64_some_if_not_zero(deactivating_stake),
|
||||
..CliStakeState::default()
|
||||
}
|
||||
}
|
||||
StakeState::RewardsPool => CliStakeState {
|
||||
@@ -1584,112 +1553,17 @@ pub fn build_stake_state(
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn fetch_epoch_rewards(
|
||||
rpc_client: &RpcClient,
|
||||
address: &Pubkey,
|
||||
lowest_epoch: Epoch,
|
||||
) -> Result<Vec<CliEpochReward>, Box<dyn std::error::Error>> {
|
||||
let mut all_epoch_rewards = vec![];
|
||||
|
||||
let epoch_schedule = rpc_client.get_epoch_schedule()?;
|
||||
let slot = rpc_client.get_slot()?;
|
||||
let first_available_block = rpc_client.get_first_available_block()?;
|
||||
|
||||
let mut epoch = epoch_schedule.get_epoch_and_slot_index(slot).0;
|
||||
let mut epoch_info: Option<(Slot, UnixTimestamp, solana_transaction_status::Rewards)> = None;
|
||||
while epoch > lowest_epoch {
|
||||
let first_slot_in_epoch = epoch_schedule.get_first_slot_in_epoch(epoch);
|
||||
if first_slot_in_epoch < first_available_block {
|
||||
// RPC node is out of history data
|
||||
break;
|
||||
}
|
||||
|
||||
let first_confirmed_block_in_epoch = *rpc_client
|
||||
.get_confirmed_blocks_with_limit(first_slot_in_epoch, 1)?
|
||||
.get(0)
|
||||
.ok_or_else(|| format!("Unable to fetch first confirmed block for epoch {}", epoch))?;
|
||||
|
||||
let first_confirmed_block = match rpc_client.get_confirmed_block_with_encoding(
|
||||
first_confirmed_block_in_epoch,
|
||||
solana_transaction_status::UiTransactionEncoding::Base64,
|
||||
) {
|
||||
Ok(first_confirmed_block) => first_confirmed_block,
|
||||
Err(ClientError {
|
||||
kind:
|
||||
ClientErrorKind::RpcError(rpc_request::RpcError::RpcResponseError {
|
||||
code: rpc_custom_error::JSON_RPC_SERVER_ERROR_BLOCK_NOT_AVAILABLE,
|
||||
message: _,
|
||||
}),
|
||||
request: _,
|
||||
}) => {
|
||||
// RPC node doesn't have this block
|
||||
break;
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(err.into());
|
||||
}
|
||||
};
|
||||
|
||||
let epoch_start_time = if let Some(block_time) = first_confirmed_block.block_time {
|
||||
block_time
|
||||
} else {
|
||||
break;
|
||||
};
|
||||
|
||||
// Rewards for the previous epoch are found in the first confirmed block of the current epoch
|
||||
let previous_epoch_rewards = first_confirmed_block.rewards;
|
||||
|
||||
if let Some((effective_slot, epoch_end_time, epoch_rewards)) = epoch_info {
|
||||
let wallclock_epoch_duration =
|
||||
{ Local.timestamp(epoch_end_time, 0) - Local.timestamp(epoch_start_time, 0) }
|
||||
.to_std()?
|
||||
.as_secs_f64();
|
||||
|
||||
let wallclock_epochs_per_year =
|
||||
(SECONDS_PER_DAY * 356) as f64 / wallclock_epoch_duration;
|
||||
|
||||
if let Some(reward) = epoch_rewards
|
||||
.into_iter()
|
||||
.find(|reward| reward.pubkey == address.to_string())
|
||||
{
|
||||
if reward.post_balance > reward.lamports.try_into().unwrap_or(0) {
|
||||
let balance_increase_percent = reward.lamports.abs() as f64
|
||||
/ (reward.post_balance as f64 - reward.lamports as f64);
|
||||
|
||||
all_epoch_rewards.push(CliEpochReward {
|
||||
epoch,
|
||||
effective_slot,
|
||||
amount: reward.lamports.abs() as u64,
|
||||
post_balance: reward.post_balance,
|
||||
percent_change: balance_increase_percent,
|
||||
apr: balance_increase_percent * wallclock_epochs_per_year,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
epoch -= 1;
|
||||
epoch_info = Some((
|
||||
first_confirmed_block_in_epoch,
|
||||
epoch_start_time,
|
||||
previous_epoch_rewards,
|
||||
));
|
||||
}
|
||||
|
||||
Ok(all_epoch_rewards)
|
||||
}
|
||||
|
||||
pub fn process_show_stake_account(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
stake_account_address: &Pubkey,
|
||||
stake_account_pubkey: &Pubkey,
|
||||
use_lamports_unit: bool,
|
||||
) -> ProcessResult {
|
||||
let stake_account = rpc_client.get_account(stake_account_address)?;
|
||||
let stake_account = rpc_client.get_account(stake_account_pubkey)?;
|
||||
if stake_account.owner != solana_stake_program::id() {
|
||||
return Err(CliError::RpcRequestError(format!(
|
||||
"{:?} is not a stake account",
|
||||
stake_account_address,
|
||||
stake_account_pubkey,
|
||||
))
|
||||
.into());
|
||||
}
|
||||
@@ -1705,23 +1579,13 @@ pub fn process_show_stake_account(
|
||||
CliError::RpcRequestError("Failed to deserialize clock sysvar".to_string())
|
||||
})?;
|
||||
|
||||
let mut state = build_stake_state(
|
||||
let state = build_stake_state(
|
||||
stake_account.lamports,
|
||||
&stake_state,
|
||||
use_lamports_unit,
|
||||
&stake_history,
|
||||
&clock,
|
||||
);
|
||||
|
||||
if state.stake_type == CliStakeType::Stake {
|
||||
if let Some(activation_epoch) = state.activation_epoch {
|
||||
state.epoch_rewards = Some(fetch_epoch_rewards(
|
||||
rpc_client,
|
||||
stake_account_address,
|
||||
activation_epoch,
|
||||
)?);
|
||||
}
|
||||
}
|
||||
Ok(config.output_format.formatted_string(&state))
|
||||
}
|
||||
Err(err) => Err(CliError::RpcRequestError(format!(
|
||||
@@ -1854,15 +1718,12 @@ pub fn process_delegate_stake(
|
||||
|
||||
if sign_only {
|
||||
tx.try_partial_sign(&config.signers, recent_blockhash)?;
|
||||
return_signers(&tx, &config.output_format)
|
||||
return_signers(&tx, &config)
|
||||
} else {
|
||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||
if let Some(nonce_account) = &nonce_account {
|
||||
let nonce_account = nonce_utils::get_account_with_commitment(
|
||||
rpc_client,
|
||||
nonce_account,
|
||||
config.commitment,
|
||||
)?;
|
||||
let nonce_account =
|
||||
nonce::get_account_with_commitment(rpc_client, nonce_account, config.commitment)?;
|
||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
||||
}
|
||||
check_account_for_fee_with_commitment(
|
||||
@@ -1885,7 +1746,6 @@ pub fn process_delegate_stake(
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::cli::{app, parse_command};
|
||||
use solana_client::blockhash_query;
|
||||
use solana_sdk::{
|
||||
hash::Hash,
|
||||
signature::{
|
||||
@@ -1906,10 +1766,6 @@ 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 {
|
||||
path: default_keypair_file.clone(),
|
||||
arg_name: String::new(),
|
||||
};
|
||||
let (keypair_file, mut tmp_file) = make_tmp_file();
|
||||
let stake_account_keypair = Keypair::new();
|
||||
write_keypair(&stake_account_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
@@ -1937,7 +1793,7 @@ mod tests {
|
||||
&new_withdraw_string,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_stake_authorize, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::StakeAuthorize {
|
||||
stake_account_pubkey,
|
||||
@@ -1971,7 +1827,7 @@ mod tests {
|
||||
&withdraw_authority_keypair_file,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_stake_authorize, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::StakeAuthorize {
|
||||
stake_account_pubkey,
|
||||
@@ -2009,7 +1865,7 @@ mod tests {
|
||||
&withdraw_authority_keypair_file,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_stake_authorize, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::StakeAuthorize {
|
||||
stake_account_pubkey,
|
||||
@@ -2039,7 +1895,7 @@ mod tests {
|
||||
&new_stake_string,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_stake_authorize, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::StakeAuthorize {
|
||||
stake_account_pubkey,
|
||||
@@ -2063,7 +1919,7 @@ mod tests {
|
||||
&stake_authority_keypair_file,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_stake_authorize, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::StakeAuthorize {
|
||||
stake_account_pubkey,
|
||||
@@ -2093,7 +1949,7 @@ mod tests {
|
||||
&withdraw_authority_keypair_file,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_stake_authorize, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::StakeAuthorize {
|
||||
stake_account_pubkey,
|
||||
@@ -2120,7 +1976,7 @@ mod tests {
|
||||
&new_withdraw_string,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_stake_authorize, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::StakeAuthorize {
|
||||
stake_account_pubkey,
|
||||
@@ -2148,7 +2004,7 @@ mod tests {
|
||||
&withdraw_authority_keypair_file,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_stake_authorize, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::StakeAuthorize {
|
||||
stake_account_pubkey,
|
||||
@@ -2186,7 +2042,7 @@ mod tests {
|
||||
"--sign-only",
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_authorize, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_authorize, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::StakeAuthorize {
|
||||
stake_account_pubkey,
|
||||
@@ -2219,7 +2075,7 @@ mod tests {
|
||||
&pubkey.to_string(),
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_authorize, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_authorize, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::StakeAuthorize {
|
||||
stake_account_pubkey,
|
||||
@@ -2265,7 +2121,7 @@ mod tests {
|
||||
&pubkey2.to_string(),
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_authorize, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_authorize, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::StakeAuthorize {
|
||||
stake_account_pubkey,
|
||||
@@ -2297,7 +2153,7 @@ mod tests {
|
||||
&blockhash_string,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_authorize, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_authorize, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::StakeAuthorize {
|
||||
stake_account_pubkey,
|
||||
@@ -2334,7 +2190,7 @@ mod tests {
|
||||
&nonce_keypair_file,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_authorize, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_authorize, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::StakeAuthorize {
|
||||
stake_account_pubkey,
|
||||
@@ -2370,7 +2226,7 @@ mod tests {
|
||||
&fee_payer_keypair_file,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_authorize, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_authorize, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::StakeAuthorize {
|
||||
stake_account_pubkey,
|
||||
@@ -2404,7 +2260,7 @@ mod tests {
|
||||
&signer,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_authorize, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_authorize, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::StakeAuthorize {
|
||||
stake_account_pubkey,
|
||||
@@ -2426,9 +2282,9 @@ mod tests {
|
||||
);
|
||||
|
||||
// Test CreateStakeAccount SubCommand
|
||||
let custodian = solana_sdk::pubkey::new_rand();
|
||||
let custodian = Pubkey::new_rand();
|
||||
let custodian_string = format!("{}", custodian);
|
||||
let authorized = solana_sdk::pubkey::new_rand();
|
||||
let authorized = Pubkey::new_rand();
|
||||
let authorized_string = format!("{}", authorized);
|
||||
let test_create_stake_account = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
@@ -2445,7 +2301,7 @@ mod tests {
|
||||
"43",
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_create_stake_account, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_create_stake_account, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::CreateStakeAccount {
|
||||
stake_account: 1,
|
||||
@@ -2486,7 +2342,12 @@ mod tests {
|
||||
]);
|
||||
|
||||
assert_eq!(
|
||||
parse_command(&test_create_stake_account2, &default_signer, &mut None).unwrap(),
|
||||
parse_command(
|
||||
&test_create_stake_account2,
|
||||
&default_keypair_file,
|
||||
&mut None
|
||||
)
|
||||
.unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::CreateStakeAccount {
|
||||
stake_account: 1,
|
||||
@@ -2539,7 +2400,12 @@ mod tests {
|
||||
]);
|
||||
|
||||
assert_eq!(
|
||||
parse_command(&test_create_stake_account2, &default_signer, &mut None).unwrap(),
|
||||
parse_command(
|
||||
&test_create_stake_account2,
|
||||
&default_keypair_file,
|
||||
&mut None
|
||||
)
|
||||
.unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::CreateStakeAccount {
|
||||
stake_account: 1,
|
||||
@@ -2566,7 +2432,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Test DelegateStake Subcommand
|
||||
let vote_account_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let vote_account_pubkey = Pubkey::new_rand();
|
||||
let vote_account_string = vote_account_pubkey.to_string();
|
||||
let test_delegate_stake = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
@@ -2575,7 +2441,7 @@ mod tests {
|
||||
&vote_account_string,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_delegate_stake, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::DelegateStake {
|
||||
stake_account_pubkey,
|
||||
@@ -2593,7 +2459,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Test DelegateStake Subcommand w/ authority
|
||||
let vote_account_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let vote_account_pubkey = Pubkey::new_rand();
|
||||
let vote_account_string = vote_account_pubkey.to_string();
|
||||
let test_delegate_stake = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
@@ -2604,7 +2470,7 @@ mod tests {
|
||||
&stake_authority_keypair_file,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_delegate_stake, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::DelegateStake {
|
||||
stake_account_pubkey,
|
||||
@@ -2635,7 +2501,7 @@ mod tests {
|
||||
&vote_account_string,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_delegate_stake, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::DelegateStake {
|
||||
stake_account_pubkey,
|
||||
@@ -2664,7 +2530,7 @@ mod tests {
|
||||
&blockhash_string,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_delegate_stake, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::DelegateStake {
|
||||
stake_account_pubkey,
|
||||
@@ -2694,7 +2560,7 @@ mod tests {
|
||||
"--sign-only",
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_delegate_stake, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::DelegateStake {
|
||||
stake_account_pubkey,
|
||||
@@ -2712,7 +2578,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Test Delegate Subcommand w/ absent fee payer
|
||||
let key1 = solana_sdk::pubkey::new_rand();
|
||||
let key1 = Pubkey::new_rand();
|
||||
let sig1 = Keypair::new().sign_message(&[0u8]);
|
||||
let signer1 = format!("{}={}", key1, sig1);
|
||||
let test_delegate_stake = test_commands.clone().get_matches_from(vec![
|
||||
@@ -2728,7 +2594,7 @@ mod tests {
|
||||
&key1.to_string(),
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_delegate_stake, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::DelegateStake {
|
||||
stake_account_pubkey,
|
||||
@@ -2752,7 +2618,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Test Delegate Subcommand w/ absent fee payer and absent nonce authority
|
||||
let key2 = solana_sdk::pubkey::new_rand();
|
||||
let key2 = Pubkey::new_rand();
|
||||
let sig2 = Keypair::new().sign_message(&[0u8]);
|
||||
let signer2 = format!("{}={}", key2, sig2);
|
||||
let test_delegate_stake = test_commands.clone().get_matches_from(vec![
|
||||
@@ -2774,7 +2640,7 @@ mod tests {
|
||||
&key2.to_string(),
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_delegate_stake, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::DelegateStake {
|
||||
stake_account_pubkey,
|
||||
@@ -2811,7 +2677,7 @@ mod tests {
|
||||
&fee_payer_keypair_file,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_delegate_stake, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::DelegateStake {
|
||||
stake_account_pubkey,
|
||||
@@ -2841,7 +2707,7 @@ mod tests {
|
||||
]);
|
||||
|
||||
assert_eq!(
|
||||
parse_command(&test_withdraw_stake, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_withdraw_stake, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::WithdrawStake {
|
||||
stake_account_pubkey,
|
||||
@@ -2871,7 +2737,7 @@ mod tests {
|
||||
]);
|
||||
|
||||
assert_eq!(
|
||||
parse_command(&test_withdraw_stake, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_withdraw_stake, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::WithdrawStake {
|
||||
stake_account_pubkey,
|
||||
@@ -2906,7 +2772,7 @@ mod tests {
|
||||
]);
|
||||
|
||||
assert_eq!(
|
||||
parse_command(&test_withdraw_stake, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_withdraw_stake, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::WithdrawStake {
|
||||
stake_account_pubkey,
|
||||
@@ -2949,7 +2815,7 @@ mod tests {
|
||||
]);
|
||||
|
||||
assert_eq!(
|
||||
parse_command(&test_withdraw_stake, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_withdraw_stake, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::WithdrawStake {
|
||||
stake_account_pubkey,
|
||||
@@ -2982,7 +2848,7 @@ mod tests {
|
||||
&stake_account_string,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_deactivate_stake, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_deactivate_stake, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::DeactivateStake {
|
||||
stake_account_pubkey,
|
||||
@@ -3006,7 +2872,7 @@ mod tests {
|
||||
&stake_authority_keypair_file,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_deactivate_stake, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_deactivate_stake, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::DeactivateStake {
|
||||
stake_account_pubkey,
|
||||
@@ -3037,7 +2903,7 @@ mod tests {
|
||||
&blockhash_string,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_deactivate_stake, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_deactivate_stake, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::DeactivateStake {
|
||||
stake_account_pubkey,
|
||||
@@ -3064,7 +2930,7 @@ mod tests {
|
||||
"--sign-only",
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_deactivate_stake, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_deactivate_stake, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::DeactivateStake {
|
||||
stake_account_pubkey,
|
||||
@@ -3080,7 +2946,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Test Deactivate Subcommand w/ absent fee payer
|
||||
let key1 = solana_sdk::pubkey::new_rand();
|
||||
let key1 = Pubkey::new_rand();
|
||||
let sig1 = Keypair::new().sign_message(&[0u8]);
|
||||
let signer1 = format!("{}={}", key1, sig1);
|
||||
let test_deactivate_stake = test_commands.clone().get_matches_from(vec![
|
||||
@@ -3095,7 +2961,7 @@ mod tests {
|
||||
&key1.to_string(),
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_deactivate_stake, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_deactivate_stake, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::DeactivateStake {
|
||||
stake_account_pubkey,
|
||||
@@ -3117,7 +2983,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Test Deactivate Subcommand w/ absent fee payer and nonce authority
|
||||
let key2 = solana_sdk::pubkey::new_rand();
|
||||
let key2 = Pubkey::new_rand();
|
||||
let sig2 = Keypair::new().sign_message(&[0u8]);
|
||||
let signer2 = format!("{}={}", key2, sig2);
|
||||
let test_deactivate_stake = test_commands.clone().get_matches_from(vec![
|
||||
@@ -3138,7 +3004,7 @@ mod tests {
|
||||
&key2.to_string(),
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_deactivate_stake, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_deactivate_stake, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::DeactivateStake {
|
||||
stake_account_pubkey,
|
||||
@@ -3169,7 +3035,7 @@ mod tests {
|
||||
&fee_payer_keypair_file,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_deactivate_stake, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_deactivate_stake, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::DeactivateStake {
|
||||
stake_account_pubkey,
|
||||
@@ -3203,7 +3069,7 @@ mod tests {
|
||||
"50",
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_split_stake_account, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_split_stake_account, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::SplitStake {
|
||||
stake_account_pubkey: stake_account_keypair.pubkey(),
|
||||
@@ -3264,7 +3130,7 @@ mod tests {
|
||||
&stake_signer,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_split_stake_account, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_split_stake_account, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::SplitStake {
|
||||
stake_account_pubkey: stake_account_keypair.pubkey(),
|
||||
@@ -3296,7 +3162,7 @@ mod tests {
|
||||
let stake_account_keypair = Keypair::new();
|
||||
write_keypair(&stake_account_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
|
||||
let source_stake_account_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let source_stake_account_pubkey = Pubkey::new_rand();
|
||||
let test_merge_stake_account = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
"merge-stake",
|
||||
@@ -3304,7 +3170,7 @@ mod tests {
|
||||
&source_stake_account_pubkey.to_string(),
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_merge_stake_account, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_merge_stake_account, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::MergeStake {
|
||||
stake_account_pubkey: stake_account_keypair.pubkey(),
|
||||
|
@@ -1,5 +1,6 @@
|
||||
use crate::{
|
||||
cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult},
|
||||
cli_output::{CliValidatorInfo, CliValidatorInfoVec},
|
||||
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
|
||||
};
|
||||
use bincode::deserialize;
|
||||
@@ -12,9 +13,8 @@ use solana_account_decoder::validator_info::{
|
||||
use solana_clap_utils::{
|
||||
input_parsers::pubkey_of,
|
||||
input_validators::{is_pubkey, is_url},
|
||||
keypair::DefaultSigner,
|
||||
keypair::signer_from_path,
|
||||
};
|
||||
use solana_cli_output::{CliValidatorInfo, CliValidatorInfoVec};
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_config_program::{config_instruction, get_config_data, ConfigKeys, ConfigState};
|
||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||
@@ -212,7 +212,7 @@ impl ValidatorInfoSubCommands for App<'_, '_> {
|
||||
|
||||
pub fn parse_validator_info_command(
|
||||
matches: &ArgMatches<'_>,
|
||||
default_signer: &DefaultSigner,
|
||||
default_signer_path: &str,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let info_pubkey = pubkey_of(matches, "info_pubkey");
|
||||
@@ -224,7 +224,12 @@ pub fn parse_validator_info_command(
|
||||
force_keybase: matches.is_present("force"),
|
||||
info_pubkey,
|
||||
},
|
||||
signers: vec![default_signer.signer_from_path(matches, wallet_manager)?],
|
||||
signers: vec![signer_from_path(
|
||||
matches,
|
||||
default_signer_path,
|
||||
"keypair",
|
||||
wallet_manager,
|
||||
)?],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -266,12 +271,10 @@ pub fn process_set_validator_info(
|
||||
let all_config = rpc_client.get_program_accounts(&solana_config_program::id())?;
|
||||
let existing_account = all_config
|
||||
.iter()
|
||||
.filter(
|
||||
|(_, account)| match deserialize::<ConfigKeys>(&account.data) {
|
||||
Ok(key_list) => key_list.keys.contains(&(validator_info::id(), false)),
|
||||
Err(_) => false,
|
||||
},
|
||||
)
|
||||
.filter(|(_, account)| {
|
||||
let key_list: ConfigKeys = deserialize(&account.data).map_err(|_| false).unwrap();
|
||||
key_list.keys.contains(&(validator_info::id(), false))
|
||||
})
|
||||
.find(|(pubkey, account)| {
|
||||
let (validator_pubkey, _) = parse_validator_info(&pubkey, &account).unwrap();
|
||||
validator_pubkey == config.signers[0].pubkey()
|
||||
@@ -382,10 +385,10 @@ pub fn process_get_validator_info(
|
||||
all_config
|
||||
.into_iter()
|
||||
.filter(|(_, validator_info_account)| {
|
||||
match deserialize::<ConfigKeys>(&validator_info_account.data) {
|
||||
Ok(key_list) => key_list.keys.contains(&(validator_info::id(), false)),
|
||||
Err(_) => false,
|
||||
}
|
||||
let key_list: ConfigKeys = deserialize(&validator_info_account.data)
|
||||
.map_err(|_| false)
|
||||
.unwrap();
|
||||
key_list.keys.contains(&(validator_info::id(), false))
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
@@ -486,7 +489,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_parse_validator_info() {
|
||||
let pubkey = solana_sdk::pubkey::new_rand();
|
||||
let pubkey = Pubkey::new_rand();
|
||||
let keys = vec![(validator_info::id(), false), (pubkey, true)];
|
||||
let config = ConfigKeys { keys };
|
||||
|
||||
|
@@ -1,19 +1,14 @@
|
||||
use crate::{
|
||||
checks::{check_account_for_fee_with_commitment, check_unique_pubkeys},
|
||||
cli::{
|
||||
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
|
||||
ProcessResult,
|
||||
generate_unique_signers, log_instruction_custom_error, CliCommand, CliCommandInfo,
|
||||
CliConfig, CliError, ProcessResult, SignerIndex,
|
||||
},
|
||||
cli_output::{CliEpochVotingHistory, CliLockout, CliVoteAccount},
|
||||
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
|
||||
};
|
||||
use clap::{value_t_or_exit, App, Arg, ArgMatches, SubCommand};
|
||||
use solana_clap_utils::{
|
||||
commitment::commitment_arg,
|
||||
input_parsers::*,
|
||||
input_validators::*,
|
||||
keypair::{DefaultSigner, SignerIndex},
|
||||
};
|
||||
use solana_cli_output::{CliEpochVotingHistory, CliLockout, CliVoteAccount};
|
||||
use solana_clap_utils::{commitment::commitment_arg, input_parsers::*, input_validators::*};
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||
use solana_sdk::{
|
||||
@@ -251,7 +246,7 @@ impl VoteSubCommands for App<'_, '_> {
|
||||
|
||||
pub fn parse_create_vote_account(
|
||||
matches: &ArgMatches<'_>,
|
||||
default_signer: &DefaultSigner,
|
||||
default_signer_path: &str,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let (vote_account, vote_account_pubkey) = signer_of(matches, "vote_account", wallet_manager)?;
|
||||
@@ -263,9 +258,10 @@ pub fn parse_create_vote_account(
|
||||
let authorized_withdrawer = pubkey_of_signer(matches, "authorized_withdrawer", wallet_manager)?;
|
||||
|
||||
let payer_provided = None;
|
||||
let signer_info = default_signer.generate_unique_signers(
|
||||
let signer_info = generate_unique_signers(
|
||||
vec![payer_provided, vote_account, identity_account],
|
||||
matches,
|
||||
default_signer_path,
|
||||
wallet_manager,
|
||||
)?;
|
||||
|
||||
@@ -284,7 +280,7 @@ pub fn parse_create_vote_account(
|
||||
|
||||
pub fn parse_vote_authorize(
|
||||
matches: &ArgMatches<'_>,
|
||||
default_signer: &DefaultSigner,
|
||||
default_signer_path: &str,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
vote_authorize: VoteAuthorize,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
@@ -295,9 +291,10 @@ pub fn parse_vote_authorize(
|
||||
let (authorized, _) = signer_of(matches, "authorized", wallet_manager)?;
|
||||
|
||||
let payer_provided = None;
|
||||
let signer_info = default_signer.generate_unique_signers(
|
||||
let signer_info = generate_unique_signers(
|
||||
vec![payer_provided, authorized],
|
||||
matches,
|
||||
default_signer_path,
|
||||
wallet_manager,
|
||||
)?;
|
||||
|
||||
@@ -313,7 +310,7 @@ pub fn parse_vote_authorize(
|
||||
|
||||
pub fn parse_vote_update_validator(
|
||||
matches: &ArgMatches<'_>,
|
||||
default_signer: &DefaultSigner,
|
||||
default_signer_path: &str,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let vote_account_pubkey =
|
||||
@@ -324,9 +321,10 @@ pub fn parse_vote_update_validator(
|
||||
signer_of(matches, "authorized_withdrawer", wallet_manager)?;
|
||||
|
||||
let payer_provided = None;
|
||||
let signer_info = default_signer.generate_unique_signers(
|
||||
let signer_info = generate_unique_signers(
|
||||
vec![payer_provided, authorized_withdrawer, new_identity_account],
|
||||
matches,
|
||||
default_signer_path,
|
||||
wallet_manager,
|
||||
)?;
|
||||
|
||||
@@ -342,7 +340,7 @@ pub fn parse_vote_update_validator(
|
||||
|
||||
pub fn parse_vote_update_commission(
|
||||
matches: &ArgMatches<'_>,
|
||||
default_signer: &DefaultSigner,
|
||||
default_signer_path: &str,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let vote_account_pubkey =
|
||||
@@ -352,9 +350,10 @@ pub fn parse_vote_update_commission(
|
||||
let commission = value_t_or_exit!(matches, "commission", u8);
|
||||
|
||||
let payer_provided = None;
|
||||
let signer_info = default_signer.generate_unique_signers(
|
||||
let signer_info = generate_unique_signers(
|
||||
vec![payer_provided, authorized_withdrawer],
|
||||
matches,
|
||||
default_signer_path,
|
||||
wallet_manager,
|
||||
)?;
|
||||
|
||||
@@ -386,7 +385,7 @@ pub fn parse_vote_get_account_command(
|
||||
|
||||
pub fn parse_withdraw_from_vote_account(
|
||||
matches: &ArgMatches<'_>,
|
||||
default_signer: &DefaultSigner,
|
||||
default_signer_path: &str,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let vote_account_pubkey =
|
||||
@@ -399,9 +398,10 @@ pub fn parse_withdraw_from_vote_account(
|
||||
signer_of(matches, "authorized_withdrawer", wallet_manager)?;
|
||||
|
||||
let payer_provided = None;
|
||||
let signer_info = default_signer.generate_unique_signers(
|
||||
let signer_info = generate_unique_signers(
|
||||
vec![payer_provided, withdraw_authority],
|
||||
matches,
|
||||
default_signer_path,
|
||||
wallet_manager,
|
||||
)?;
|
||||
|
||||
@@ -671,11 +671,11 @@ fn get_vote_account(
|
||||
pub fn process_show_vote_account(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
vote_account_address: &Pubkey,
|
||||
vote_account_pubkey: &Pubkey,
|
||||
use_lamports_unit: bool,
|
||||
) -> ProcessResult {
|
||||
let (vote_account, vote_state) =
|
||||
get_vote_account(rpc_client, vote_account_address, config.commitment)?;
|
||||
get_vote_account(rpc_client, vote_account_pubkey, config.commitment)?;
|
||||
|
||||
let epoch_schedule = rpc_client.get_epoch_schedule()?;
|
||||
|
||||
@@ -696,12 +696,6 @@ pub fn process_show_vote_account(
|
||||
}
|
||||
}
|
||||
|
||||
let epoch_rewards = Some(crate::stake::fetch_epoch_rewards(
|
||||
rpc_client,
|
||||
vote_account_address,
|
||||
1,
|
||||
)?);
|
||||
|
||||
let vote_account_data = CliVoteAccount {
|
||||
account_balance: vote_account.lamports,
|
||||
validator_identity: vote_state.node_pubkey.to_string(),
|
||||
@@ -714,7 +708,6 @@ pub fn process_show_vote_account(
|
||||
votes,
|
||||
epoch_voting_history,
|
||||
use_lamports_unit,
|
||||
epoch_rewards,
|
||||
};
|
||||
|
||||
Ok(config.output_format.formatted_string(&vote_account_data))
|
||||
@@ -801,10 +794,6 @@ 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 {
|
||||
path: default_keypair_file.clone(),
|
||||
arg_name: String::new(),
|
||||
};
|
||||
|
||||
let test_authorize_voter = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
@@ -814,7 +803,7 @@ mod tests {
|
||||
&pubkey2_string,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_authorize_voter, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_authorize_voter, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::VoteAuthorize {
|
||||
vote_account_pubkey: pubkey,
|
||||
@@ -837,7 +826,7 @@ mod tests {
|
||||
&pubkey2_string,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_authorize_voter, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_authorize_voter, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::VoteAuthorize {
|
||||
vote_account_pubkey: pubkey,
|
||||
@@ -867,7 +856,7 @@ mod tests {
|
||||
"10",
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_create_vote_account, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_create_vote_account, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::CreateVoteAccount {
|
||||
vote_account: 1,
|
||||
@@ -896,7 +885,7 @@ mod tests {
|
||||
&identity_keypair_file,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_create_vote_account2, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_create_vote_account2, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::CreateVoteAccount {
|
||||
vote_account: 1,
|
||||
@@ -915,7 +904,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// test init with an authed voter
|
||||
let authed = solana_sdk::pubkey::new_rand();
|
||||
let authed = Pubkey::new_rand();
|
||||
let (keypair_file, mut tmp_file) = make_tmp_file();
|
||||
let keypair = Keypair::new();
|
||||
write_keypair(&keypair, tmp_file.as_file_mut()).unwrap();
|
||||
@@ -929,7 +918,7 @@ mod tests {
|
||||
&authed.to_string(),
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_create_vote_account3, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_create_vote_account3, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::CreateVoteAccount {
|
||||
vote_account: 1,
|
||||
@@ -960,7 +949,7 @@ mod tests {
|
||||
&authed.to_string(),
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_create_vote_account4, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_create_vote_account4, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::CreateVoteAccount {
|
||||
vote_account: 1,
|
||||
@@ -986,7 +975,7 @@ mod tests {
|
||||
&keypair_file,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_update_validator, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_update_validator, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::VoteUpdateValidator {
|
||||
vote_account_pubkey: pubkey,
|
||||
@@ -1009,7 +998,7 @@ mod tests {
|
||||
&keypair_file,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_update_commission, &default_signer, &mut None).unwrap(),
|
||||
parse_command(&test_update_commission, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::VoteUpdateCommission {
|
||||
vote_account_pubkey: pubkey,
|
||||
@@ -1032,7 +1021,12 @@ mod tests {
|
||||
"42",
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_withdraw_from_vote_account, &default_signer, &mut None).unwrap(),
|
||||
parse_command(
|
||||
&test_withdraw_from_vote_account,
|
||||
&default_keypair_file,
|
||||
&mut None
|
||||
)
|
||||
.unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::WithdrawFromVoteAccount {
|
||||
vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(),
|
||||
@@ -1053,7 +1047,12 @@ mod tests {
|
||||
"ALL",
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_withdraw_from_vote_account, &default_signer, &mut None).unwrap(),
|
||||
parse_command(
|
||||
&test_withdraw_from_vote_account,
|
||||
&default_keypair_file,
|
||||
&mut None
|
||||
)
|
||||
.unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::WithdrawFromVoteAccount {
|
||||
vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(),
|
||||
@@ -1079,7 +1078,12 @@ mod tests {
|
||||
&withdraw_authority_file,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_withdraw_from_vote_account, &default_signer, &mut None).unwrap(),
|
||||
parse_command(
|
||||
&test_withdraw_from_vote_account,
|
||||
&default_keypair_file,
|
||||
&mut None
|
||||
)
|
||||
.unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::WithdrawFromVoteAccount {
|
||||
vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(),
|
||||
|
@@ -1,7 +1,7 @@
|
||||
use serde_json::Value;
|
||||
use solana_cli::cli::{process_command, CliCommand, CliConfig};
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_core::test_validator::TestValidator;
|
||||
use solana_core::validator::TestValidator;
|
||||
use solana_faucet::faucet::run_local_faucet;
|
||||
use solana_sdk::{
|
||||
bpf_loader,
|
||||
|
@@ -1,16 +1,17 @@
|
||||
use solana_cli::{
|
||||
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
|
||||
cli_output::OutputFormat,
|
||||
nonce,
|
||||
offline::{
|
||||
blockhash_query::{self, BlockhashQuery},
|
||||
parse_sign_only_reply_string,
|
||||
},
|
||||
spend_utils::SpendAmount,
|
||||
test_utils::{check_ready, check_recent_balance},
|
||||
};
|
||||
use solana_cli_output::{parse_sign_only_reply_string, OutputFormat};
|
||||
use solana_client::{
|
||||
blockhash_query::{self, BlockhashQuery},
|
||||
nonce_utils,
|
||||
rpc_client::RpcClient,
|
||||
};
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_core::contact_info::ContactInfo;
|
||||
use solana_core::test_validator::{TestValidator, TestValidatorOptions};
|
||||
use solana_core::validator::{TestValidator, TestValidatorOptions};
|
||||
use solana_faucet::faucet::run_local_faucet;
|
||||
use solana_sdk::{
|
||||
commitment_config::CommitmentConfig,
|
||||
@@ -172,7 +173,7 @@ fn full_battery_tests(
|
||||
assert_ne!(first_nonce, third_nonce);
|
||||
|
||||
// Withdraw from nonce account
|
||||
let payee_pubkey = solana_sdk::pubkey::new_rand();
|
||||
let payee_pubkey = Pubkey::new_rand();
|
||||
config_payer.signers = authorized_signers;
|
||||
config_payer.command = CliCommand::WithdrawFromNonceAccount {
|
||||
nonce_account,
|
||||
@@ -301,14 +302,11 @@ fn test_create_account_with_seed() {
|
||||
check_recent_balance(0, &rpc_client, &to_address);
|
||||
|
||||
// Fetch nonce hash
|
||||
let nonce_hash = nonce_utils::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_address,
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce_utils::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
let nonce_hash =
|
||||
nonce::get_account_with_commitment(&rpc_client, &nonce_address, CommitmentConfig::recent())
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
|
||||
// Test by creating transfer TX with nonce, fully offline
|
||||
let mut authority_config = CliConfig::recent_for_tests();
|
||||
|
453
cli/tests/pay.rs
Normal file
453
cli/tests/pay.rs
Normal file
@@ -0,0 +1,453 @@
|
||||
use chrono::prelude::*;
|
||||
use serde_json::Value;
|
||||
use solana_cli::{
|
||||
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig, PayCommand},
|
||||
cli_output::OutputFormat,
|
||||
nonce,
|
||||
offline::{
|
||||
blockhash_query::{self, BlockhashQuery},
|
||||
parse_sign_only_reply_string,
|
||||
},
|
||||
spend_utils::SpendAmount,
|
||||
test_utils::check_recent_balance,
|
||||
};
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_core::validator::TestValidator;
|
||||
use solana_faucet::faucet::run_local_faucet;
|
||||
use solana_sdk::{
|
||||
commitment_config::CommitmentConfig,
|
||||
nonce::State as NonceState,
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, Signer},
|
||||
};
|
||||
use std::{fs::remove_dir_all, sync::mpsc::channel};
|
||||
|
||||
#[test]
|
||||
fn test_cli_timestamp_tx() {
|
||||
let TestValidator {
|
||||
server,
|
||||
leader_data,
|
||||
alice,
|
||||
ledger_path,
|
||||
..
|
||||
} = TestValidator::run();
|
||||
let bob_pubkey = Pubkey::new_rand();
|
||||
|
||||
let (sender, receiver) = channel();
|
||||
run_local_faucet(alice, sender, None);
|
||||
let faucet_addr = receiver.recv().unwrap();
|
||||
|
||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||
let default_signer0 = Keypair::new();
|
||||
let default_signer1 = Keypair::new();
|
||||
|
||||
let mut config_payer = CliConfig::recent_for_tests();
|
||||
config_payer.json_rpc_url =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
config_payer.signers = vec![&default_signer0];
|
||||
|
||||
let mut config_witness = CliConfig::recent_for_tests();
|
||||
config_witness.json_rpc_url = config_payer.json_rpc_url.clone();
|
||||
config_witness.signers = vec![&default_signer1];
|
||||
|
||||
assert_ne!(
|
||||
config_payer.signers[0].pubkey(),
|
||||
config_witness.signers[0].pubkey()
|
||||
);
|
||||
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&faucet_addr,
|
||||
&config_payer.signers[0].pubkey(),
|
||||
50,
|
||||
&config_witness,
|
||||
)
|
||||
.unwrap();
|
||||
check_recent_balance(50, &rpc_client, &config_payer.signers[0].pubkey());
|
||||
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&faucet_addr,
|
||||
&config_witness.signers[0].pubkey(),
|
||||
1,
|
||||
&config_witness,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Make transaction (from config_payer to bob_pubkey) requiring timestamp from config_witness
|
||||
let date_string = "\"2018-09-19T17:30:59Z\"";
|
||||
let dt: DateTime<Utc> = serde_json::from_str(&date_string).unwrap();
|
||||
config_payer.command = CliCommand::Pay(PayCommand {
|
||||
amount: SpendAmount::Some(10),
|
||||
to: bob_pubkey,
|
||||
timestamp: Some(dt),
|
||||
timestamp_pubkey: Some(config_witness.signers[0].pubkey()),
|
||||
..PayCommand::default()
|
||||
});
|
||||
let sig_response = process_command(&config_payer);
|
||||
|
||||
let object: Value = serde_json::from_str(&sig_response.unwrap()).unwrap();
|
||||
let process_id_str = object.get("processId").unwrap().as_str().unwrap();
|
||||
let process_id_vec = bs58::decode(process_id_str)
|
||||
.into_vec()
|
||||
.expect("base58-encoded public key");
|
||||
let process_id = Pubkey::new(&process_id_vec);
|
||||
|
||||
check_recent_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||
check_recent_balance(10, &rpc_client, &process_id); // contract balance
|
||||
check_recent_balance(0, &rpc_client, &bob_pubkey); // recipient balance
|
||||
|
||||
// Sign transaction by config_witness
|
||||
config_witness.command = CliCommand::TimeElapsed(bob_pubkey, process_id, dt);
|
||||
process_command(&config_witness).unwrap();
|
||||
|
||||
check_recent_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||
check_recent_balance(0, &rpc_client, &process_id); // contract balance
|
||||
check_recent_balance(10, &rpc_client, &bob_pubkey); // recipient balance
|
||||
|
||||
server.close().unwrap();
|
||||
remove_dir_all(ledger_path).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cli_witness_tx() {
|
||||
let TestValidator {
|
||||
server,
|
||||
leader_data,
|
||||
alice,
|
||||
ledger_path,
|
||||
..
|
||||
} = TestValidator::run();
|
||||
let bob_pubkey = Pubkey::new_rand();
|
||||
|
||||
let (sender, receiver) = channel();
|
||||
run_local_faucet(alice, sender, None);
|
||||
let faucet_addr = receiver.recv().unwrap();
|
||||
|
||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||
let default_signer0 = Keypair::new();
|
||||
let default_signer1 = Keypair::new();
|
||||
|
||||
let mut config_payer = CliConfig::recent_for_tests();
|
||||
config_payer.json_rpc_url =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
config_payer.signers = vec![&default_signer0];
|
||||
|
||||
let mut config_witness = CliConfig::recent_for_tests();
|
||||
config_witness.json_rpc_url = config_payer.json_rpc_url.clone();
|
||||
config_witness.signers = vec![&default_signer1];
|
||||
|
||||
assert_ne!(
|
||||
config_payer.signers[0].pubkey(),
|
||||
config_witness.signers[0].pubkey()
|
||||
);
|
||||
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&faucet_addr,
|
||||
&config_payer.signers[0].pubkey(),
|
||||
50,
|
||||
&config_witness,
|
||||
)
|
||||
.unwrap();
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&faucet_addr,
|
||||
&config_witness.signers[0].pubkey(),
|
||||
1,
|
||||
&config_witness,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Make transaction (from config_payer to bob_pubkey) requiring witness signature from config_witness
|
||||
config_payer.command = CliCommand::Pay(PayCommand {
|
||||
amount: SpendAmount::Some(10),
|
||||
to: bob_pubkey,
|
||||
witnesses: Some(vec![config_witness.signers[0].pubkey()]),
|
||||
..PayCommand::default()
|
||||
});
|
||||
let sig_response = process_command(&config_payer);
|
||||
|
||||
let object: Value = serde_json::from_str(&sig_response.unwrap()).unwrap();
|
||||
let process_id_str = object.get("processId").unwrap().as_str().unwrap();
|
||||
let process_id_vec = bs58::decode(process_id_str)
|
||||
.into_vec()
|
||||
.expect("base58-encoded public key");
|
||||
let process_id = Pubkey::new(&process_id_vec);
|
||||
|
||||
check_recent_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||
check_recent_balance(10, &rpc_client, &process_id); // contract balance
|
||||
check_recent_balance(0, &rpc_client, &bob_pubkey); // recipient balance
|
||||
|
||||
// Sign transaction by config_witness
|
||||
config_witness.command = CliCommand::Witness(bob_pubkey, process_id);
|
||||
process_command(&config_witness).unwrap();
|
||||
|
||||
check_recent_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||
check_recent_balance(0, &rpc_client, &process_id); // contract balance
|
||||
check_recent_balance(10, &rpc_client, &bob_pubkey); // recipient balance
|
||||
|
||||
server.close().unwrap();
|
||||
remove_dir_all(ledger_path).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cli_cancel_tx() {
|
||||
let TestValidator {
|
||||
server,
|
||||
leader_data,
|
||||
alice,
|
||||
ledger_path,
|
||||
..
|
||||
} = TestValidator::run();
|
||||
let bob_pubkey = Pubkey::new_rand();
|
||||
|
||||
let (sender, receiver) = channel();
|
||||
run_local_faucet(alice, sender, None);
|
||||
let faucet_addr = receiver.recv().unwrap();
|
||||
|
||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||
let default_signer0 = Keypair::new();
|
||||
let default_signer1 = Keypair::new();
|
||||
|
||||
let mut config_payer = CliConfig::recent_for_tests();
|
||||
config_payer.json_rpc_url =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
config_payer.signers = vec![&default_signer0];
|
||||
|
||||
let mut config_witness = CliConfig::recent_for_tests();
|
||||
config_witness.json_rpc_url = config_payer.json_rpc_url.clone();
|
||||
config_witness.signers = vec![&default_signer1];
|
||||
|
||||
assert_ne!(
|
||||
config_payer.signers[0].pubkey(),
|
||||
config_witness.signers[0].pubkey()
|
||||
);
|
||||
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&faucet_addr,
|
||||
&config_payer.signers[0].pubkey(),
|
||||
50,
|
||||
&config_witness,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Make transaction (from config_payer to bob_pubkey) requiring witness signature from config_witness
|
||||
config_payer.command = CliCommand::Pay(PayCommand {
|
||||
amount: SpendAmount::Some(10),
|
||||
to: bob_pubkey,
|
||||
witnesses: Some(vec![config_witness.signers[0].pubkey()]),
|
||||
cancelable: true,
|
||||
..PayCommand::default()
|
||||
});
|
||||
let sig_response = process_command(&config_payer).unwrap();
|
||||
|
||||
let object: Value = serde_json::from_str(&sig_response).unwrap();
|
||||
let process_id_str = object.get("processId").unwrap().as_str().unwrap();
|
||||
let process_id_vec = bs58::decode(process_id_str)
|
||||
.into_vec()
|
||||
.expect("base58-encoded public key");
|
||||
let process_id = Pubkey::new(&process_id_vec);
|
||||
|
||||
check_recent_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||
check_recent_balance(10, &rpc_client, &process_id); // contract balance
|
||||
check_recent_balance(0, &rpc_client, &bob_pubkey); // recipient balance
|
||||
|
||||
// Sign transaction by config_witness
|
||||
config_payer.command = CliCommand::Cancel(process_id);
|
||||
process_command(&config_payer).unwrap();
|
||||
|
||||
check_recent_balance(50, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||
check_recent_balance(0, &rpc_client, &process_id); // contract balance
|
||||
check_recent_balance(0, &rpc_client, &bob_pubkey); // recipient balance
|
||||
|
||||
server.close().unwrap();
|
||||
remove_dir_all(ledger_path).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_offline_pay_tx() {
|
||||
let TestValidator {
|
||||
server,
|
||||
leader_data,
|
||||
alice,
|
||||
ledger_path,
|
||||
..
|
||||
} = TestValidator::run();
|
||||
let bob_pubkey = Pubkey::new_rand();
|
||||
|
||||
let (sender, receiver) = channel();
|
||||
run_local_faucet(alice, sender, None);
|
||||
let faucet_addr = receiver.recv().unwrap();
|
||||
|
||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||
let default_signer = Keypair::new();
|
||||
let default_offline_signer = Keypair::new();
|
||||
|
||||
let mut config_offline = CliConfig::recent_for_tests();
|
||||
config_offline.json_rpc_url =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
config_offline.signers = vec![&default_offline_signer];
|
||||
let mut config_online = CliConfig::recent_for_tests();
|
||||
config_online.json_rpc_url =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
config_online.signers = vec![&default_signer];
|
||||
assert_ne!(
|
||||
config_offline.signers[0].pubkey(),
|
||||
config_online.signers[0].pubkey()
|
||||
);
|
||||
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&faucet_addr,
|
||||
&config_offline.signers[0].pubkey(),
|
||||
50,
|
||||
&config_offline,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&faucet_addr,
|
||||
&config_online.signers[0].pubkey(),
|
||||
50,
|
||||
&config_offline,
|
||||
)
|
||||
.unwrap();
|
||||
check_recent_balance(50, &rpc_client, &config_offline.signers[0].pubkey());
|
||||
check_recent_balance(50, &rpc_client, &config_online.signers[0].pubkey());
|
||||
|
||||
let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap();
|
||||
config_offline.command = CliCommand::Pay(PayCommand {
|
||||
amount: SpendAmount::Some(10),
|
||||
to: bob_pubkey,
|
||||
blockhash_query: BlockhashQuery::None(blockhash),
|
||||
sign_only: true,
|
||||
..PayCommand::default()
|
||||
});
|
||||
config_offline.output_format = OutputFormat::JsonCompact;
|
||||
let sig_response = process_command(&config_offline).unwrap();
|
||||
|
||||
check_recent_balance(50, &rpc_client, &config_offline.signers[0].pubkey());
|
||||
check_recent_balance(50, &rpc_client, &config_online.signers[0].pubkey());
|
||||
check_recent_balance(0, &rpc_client, &bob_pubkey);
|
||||
|
||||
let sign_only = parse_sign_only_reply_string(&sig_response);
|
||||
assert!(sign_only.has_all_signers());
|
||||
let offline_presigner = sign_only
|
||||
.presigner_of(&config_offline.signers[0].pubkey())
|
||||
.unwrap();
|
||||
let online_pubkey = config_online.signers[0].pubkey();
|
||||
config_online.signers = vec![&offline_presigner];
|
||||
config_online.command = CliCommand::Pay(PayCommand {
|
||||
amount: SpendAmount::Some(10),
|
||||
to: bob_pubkey,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
|
||||
..PayCommand::default()
|
||||
});
|
||||
process_command(&config_online).unwrap();
|
||||
|
||||
check_recent_balance(40, &rpc_client, &config_offline.signers[0].pubkey());
|
||||
check_recent_balance(50, &rpc_client, &online_pubkey);
|
||||
check_recent_balance(10, &rpc_client, &bob_pubkey);
|
||||
|
||||
server.close().unwrap();
|
||||
remove_dir_all(ledger_path).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nonced_pay_tx() {
|
||||
solana_logger::setup();
|
||||
|
||||
let TestValidator {
|
||||
server,
|
||||
leader_data,
|
||||
alice,
|
||||
ledger_path,
|
||||
..
|
||||
} = TestValidator::run();
|
||||
let (sender, receiver) = channel();
|
||||
run_local_faucet(alice, sender, None);
|
||||
let faucet_addr = receiver.recv().unwrap();
|
||||
|
||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||
let default_signer = Keypair::new();
|
||||
|
||||
let mut config = CliConfig::recent_for_tests();
|
||||
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
config.signers = vec![&default_signer];
|
||||
|
||||
let minimum_nonce_balance = rpc_client
|
||||
.get_minimum_balance_for_rent_exemption(NonceState::size())
|
||||
.unwrap();
|
||||
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&faucet_addr,
|
||||
&config.signers[0].pubkey(),
|
||||
50 + minimum_nonce_balance,
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
check_recent_balance(
|
||||
50 + minimum_nonce_balance,
|
||||
&rpc_client,
|
||||
&config.signers[0].pubkey(),
|
||||
);
|
||||
|
||||
// Create nonce account
|
||||
let nonce_account = Keypair::new();
|
||||
config.command = CliCommand::CreateNonceAccount {
|
||||
nonce_account: 1,
|
||||
seed: None,
|
||||
nonce_authority: Some(config.signers[0].pubkey()),
|
||||
amount: SpendAmount::Some(minimum_nonce_balance),
|
||||
};
|
||||
config.signers.push(&nonce_account);
|
||||
process_command(&config).unwrap();
|
||||
|
||||
check_recent_balance(50, &rpc_client, &config.signers[0].pubkey());
|
||||
check_recent_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey());
|
||||
|
||||
// Fetch nonce hash
|
||||
let nonce_hash = nonce::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_account.pubkey(),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
|
||||
let bob_pubkey = Pubkey::new_rand();
|
||||
config.signers = vec![&default_signer];
|
||||
config.command = CliCommand::Pay(PayCommand {
|
||||
amount: SpendAmount::Some(10),
|
||||
to: bob_pubkey,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(
|
||||
blockhash_query::Source::NonceAccount(nonce_account.pubkey()),
|
||||
nonce_hash,
|
||||
),
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
..PayCommand::default()
|
||||
});
|
||||
process_command(&config).expect("failed to process pay command");
|
||||
|
||||
check_recent_balance(40, &rpc_client, &config.signers[0].pubkey());
|
||||
check_recent_balance(10, &rpc_client, &bob_pubkey);
|
||||
|
||||
// Verify that nonce has been used
|
||||
let nonce_hash2 = nonce::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_account.pubkey(),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
assert_ne!(nonce_hash, nonce_hash2);
|
||||
|
||||
server.close().unwrap();
|
||||
remove_dir_all(ledger_path).unwrap();
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
use solana_cli::cli::{process_command, CliCommand, CliConfig};
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_core::test_validator::TestValidator;
|
||||
use solana_core::validator::TestValidator;
|
||||
use solana_faucet::faucet::run_local_faucet;
|
||||
use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair};
|
||||
use std::{fs::remove_dir_all, sync::mpsc::channel};
|
||||
|
@@ -1,15 +1,16 @@
|
||||
use solana_cli::{
|
||||
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
|
||||
cli_output::OutputFormat,
|
||||
nonce,
|
||||
offline::{
|
||||
blockhash_query::{self, BlockhashQuery},
|
||||
parse_sign_only_reply_string,
|
||||
},
|
||||
spend_utils::SpendAmount,
|
||||
test_utils::{check_ready, check_recent_balance},
|
||||
};
|
||||
use solana_cli_output::{parse_sign_only_reply_string, OutputFormat};
|
||||
use solana_client::{
|
||||
blockhash_query::{self, BlockhashQuery},
|
||||
nonce_utils,
|
||||
rpc_client::RpcClient,
|
||||
};
|
||||
use solana_core::test_validator::{TestValidator, TestValidatorOptions};
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_core::validator::{TestValidator, TestValidatorOptions};
|
||||
use solana_faucet::faucet::run_local_faucet;
|
||||
use solana_sdk::{
|
||||
account_utils::StateMut,
|
||||
@@ -502,12 +503,12 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
||||
process_command(&config).unwrap();
|
||||
|
||||
// Fetch nonce hash
|
||||
let nonce_hash = nonce_utils::get_account_with_commitment(
|
||||
let nonce_hash = nonce::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_account.pubkey(),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce_utils::data_from_account(a))
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
|
||||
@@ -530,12 +531,12 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
||||
process_command(&config).unwrap();
|
||||
|
||||
// Fetch nonce hash
|
||||
let nonce_hash = nonce_utils::get_account_with_commitment(
|
||||
let nonce_hash = nonce::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_account.pubkey(),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce_utils::data_from_account(a))
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
|
||||
@@ -769,12 +770,12 @@ fn test_stake_authorize() {
|
||||
process_command(&config).unwrap();
|
||||
|
||||
// Fetch nonce hash
|
||||
let nonce_hash = nonce_utils::get_account_with_commitment(
|
||||
let nonce_hash = nonce::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_account.pubkey(),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce_utils::data_from_account(a))
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
|
||||
@@ -823,12 +824,12 @@ fn test_stake_authorize() {
|
||||
};
|
||||
assert_eq!(current_authority, online_authority_pubkey);
|
||||
|
||||
let new_nonce_hash = nonce_utils::get_account_with_commitment(
|
||||
let new_nonce_hash = nonce::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_account.pubkey(),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce_utils::data_from_account(a))
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
assert_ne!(nonce_hash, new_nonce_hash);
|
||||
@@ -1068,12 +1069,12 @@ fn test_stake_split() {
|
||||
check_recent_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey());
|
||||
|
||||
// Fetch nonce hash
|
||||
let nonce_hash = nonce_utils::get_account_with_commitment(
|
||||
let nonce_hash = nonce::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_account.pubkey(),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce_utils::data_from_account(a))
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
|
||||
@@ -1337,12 +1338,12 @@ fn test_stake_set_lockup() {
|
||||
check_recent_balance(minimum_nonce_balance, &rpc_client, &nonce_account_pubkey);
|
||||
|
||||
// Fetch nonce hash
|
||||
let nonce_hash = nonce_utils::get_account_with_commitment(
|
||||
let nonce_hash = nonce::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_account.pubkey(),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce_utils::data_from_account(a))
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
|
||||
@@ -1464,12 +1465,12 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
process_command(&config).unwrap();
|
||||
|
||||
// Fetch nonce hash
|
||||
let nonce_hash = nonce_utils::get_account_with_commitment(
|
||||
let nonce_hash = nonce::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_account.pubkey(),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce_utils::data_from_account(a))
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
|
||||
@@ -1519,12 +1520,12 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
check_recent_balance(50_000, &rpc_client, &stake_pubkey);
|
||||
|
||||
// Fetch nonce hash
|
||||
let nonce_hash = nonce_utils::get_account_with_commitment(
|
||||
let nonce_hash = nonce::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_account.pubkey(),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce_utils::data_from_account(a))
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
|
||||
@@ -1567,12 +1568,12 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
check_recent_balance(42, &rpc_client, &recipient_pubkey);
|
||||
|
||||
// Fetch nonce hash
|
||||
let nonce_hash = nonce_utils::get_account_with_commitment(
|
||||
let nonce_hash = nonce::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_account.pubkey(),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce_utils::data_from_account(a))
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
|
||||
|
@@ -1,15 +1,16 @@
|
||||
use solana_cli::{
|
||||
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
|
||||
cli_output::OutputFormat,
|
||||
nonce,
|
||||
offline::{
|
||||
blockhash_query::{self, BlockhashQuery},
|
||||
parse_sign_only_reply_string,
|
||||
},
|
||||
spend_utils::SpendAmount,
|
||||
test_utils::{check_ready, check_recent_balance},
|
||||
};
|
||||
use solana_cli_output::{parse_sign_only_reply_string, OutputFormat};
|
||||
use solana_client::{
|
||||
blockhash_query::{self, BlockhashQuery},
|
||||
nonce_utils,
|
||||
rpc_client::RpcClient,
|
||||
};
|
||||
use solana_core::test_validator::{TestValidator, TestValidatorOptions};
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_core::validator::{TestValidator, TestValidatorOptions};
|
||||
use solana_faucet::faucet::run_local_faucet;
|
||||
use solana_sdk::{
|
||||
commitment_config::CommitmentConfig,
|
||||
@@ -152,12 +153,12 @@ fn test_transfer() {
|
||||
check_recent_balance(49_987 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
|
||||
|
||||
// Fetch nonce hash
|
||||
let nonce_hash = nonce_utils::get_account_with_commitment(
|
||||
let nonce_hash = nonce::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_account.pubkey(),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce_utils::data_from_account(a))
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
|
||||
@@ -180,12 +181,12 @@ fn test_transfer() {
|
||||
process_command(&config).unwrap();
|
||||
check_recent_balance(49_976 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
|
||||
check_recent_balance(30, &rpc_client, &recipient_pubkey);
|
||||
let new_nonce_hash = nonce_utils::get_account_with_commitment(
|
||||
let new_nonce_hash = nonce::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_account.pubkey(),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce_utils::data_from_account(a))
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
assert_ne!(nonce_hash, new_nonce_hash);
|
||||
@@ -201,12 +202,12 @@ fn test_transfer() {
|
||||
check_recent_balance(49_975 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
|
||||
|
||||
// Fetch nonce hash
|
||||
let nonce_hash = nonce_utils::get_account_with_commitment(
|
||||
let nonce_hash = nonce::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_account.pubkey(),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce_utils::data_from_account(a))
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
|
||||
|
@@ -1,17 +1,16 @@
|
||||
use solana_cli::{
|
||||
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
|
||||
offline::{blockhash_query::BlockhashQuery, *},
|
||||
spend_utils::SpendAmount,
|
||||
test_utils::check_recent_balance,
|
||||
};
|
||||
use solana_client::{
|
||||
blockhash_query::{self, BlockhashQuery},
|
||||
rpc_client::RpcClient,
|
||||
};
|
||||
use solana_core::test_validator::TestValidator;
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_core::validator::TestValidator;
|
||||
use solana_faucet::faucet::run_local_faucet;
|
||||
use solana_sdk::{
|
||||
account_utils::StateMut,
|
||||
commitment_config::CommitmentConfig,
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, Signer},
|
||||
};
|
||||
use solana_vote_program::vote_state::{VoteAuthorize, VoteState, VoteStateVersions};
|
||||
@@ -109,7 +108,7 @@ fn test_vote_authorize_and_withdraw() {
|
||||
assert_eq!(authorized_withdrawer, withdraw_authority.pubkey());
|
||||
|
||||
// Withdraw from vote account
|
||||
let destination_account = solana_sdk::pubkey::new_rand(); // Send withdrawal to new account to make balance check easy
|
||||
let destination_account = Pubkey::new_rand(); // Send withdrawal to new account to make balance check easy
|
||||
config.signers = vec![&default_signer, &withdraw_authority];
|
||||
config.command = CliCommand::WithdrawFromVoteAccount {
|
||||
vote_account_pubkey,
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-client"
|
||||
version = "1.4.2"
|
||||
version = "1.3.6"
|
||||
description = "Solana Client"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -9,35 +9,30 @@ license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.13.0"
|
||||
bincode = "1.3.1"
|
||||
bs58 = "0.3.1"
|
||||
clap = "2.33.0"
|
||||
indicatif = "0.15.0"
|
||||
jsonrpc-core = "15.0.0"
|
||||
jsonrpc-core = "14.2.0"
|
||||
log = "0.4.8"
|
||||
rayon = "1.4.0"
|
||||
reqwest = { version = "0.10.8", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
semver = "0.11.0"
|
||||
rayon = "1.3.1"
|
||||
reqwest = { version = "0.10.6", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
serde = "1.0.112"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.56"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "1.4.2" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.4.2" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.4.2" }
|
||||
solana-sdk = { path = "../sdk", version = "1.4.2" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "1.4.2" }
|
||||
solana-version = { path = "../version", version = "1.4.2" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.4.2" }
|
||||
solana-account-decoder = { path = "../account-decoder", version = "1.3.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.3.6" }
|
||||
solana-sdk = { path = "../sdk", version = "1.3.6" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "1.3.6" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.3.6" }
|
||||
thiserror = "1.0"
|
||||
tungstenite = "0.10.1"
|
||||
url = "2.1.1"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_matches = "1.3.0"
|
||||
jsonrpc-core = "15.0.0"
|
||||
jsonrpc-http-server = "15.0.0"
|
||||
solana-logger = { path = "../logger", version = "1.4.2" }
|
||||
jsonrpc-core = "14.2.0"
|
||||
jsonrpc-http-server = "14.2.0"
|
||||
solana-logger = { path = "../logger", version = "1.3.6" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -50,10 +50,10 @@ impl Into<TransportError> for ClientErrorKind {
|
||||
#[derive(Error, Debug)]
|
||||
#[error("{kind}")]
|
||||
pub struct ClientError {
|
||||
pub request: Option<rpc_request::RpcRequest>,
|
||||
request: Option<rpc_request::RpcRequest>,
|
||||
|
||||
#[source]
|
||||
pub kind: ClientErrorKind,
|
||||
kind: ClientErrorKind,
|
||||
}
|
||||
|
||||
impl ClientError {
|
||||
|
@@ -27,13 +27,6 @@ impl HttpSender {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct RpcErrorObject {
|
||||
code: i64,
|
||||
message: String,
|
||||
/*data field omitted*/
|
||||
}
|
||||
|
||||
impl RpcSender for HttpSender {
|
||||
fn send(&self, request: RpcRequest, params: serde_json::Value) -> Result<serde_json::Value> {
|
||||
// Concurrent requests are not supported so reuse the same request id for all requests
|
||||
@@ -70,20 +63,11 @@ impl RpcSender for HttpSender {
|
||||
|
||||
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())
|
||||
{
|
||||
Ok(rpc_error_object) => Err(RpcError::RpcResponseError {
|
||||
code: rpc_error_object.code,
|
||||
message: rpc_error_object.message,
|
||||
}
|
||||
.into()),
|
||||
Err(err) => Err(RpcError::RpcRequestError(format!(
|
||||
"Failed to deserialize RPC error response: {} [{}]",
|
||||
serde_json::to_string(&json["error"]).unwrap(),
|
||||
err
|
||||
))
|
||||
.into()),
|
||||
};
|
||||
return Err(RpcError::RpcRequestError(format!(
|
||||
"RPC Error response: {}",
|
||||
serde_json::to_string(&json["error"]).unwrap()
|
||||
))
|
||||
.into());
|
||||
}
|
||||
return Ok(json["result"].clone());
|
||||
}
|
||||
|
@@ -1,16 +1,13 @@
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
pub mod blockhash_query;
|
||||
pub mod client_error;
|
||||
pub mod http_sender;
|
||||
pub mod mock_sender;
|
||||
pub mod nonce_utils;
|
||||
pub mod perf_utils;
|
||||
pub mod pubsub_client;
|
||||
pub mod rpc_client;
|
||||
pub mod rpc_config;
|
||||
pub mod rpc_custom_error;
|
||||
pub mod rpc_filter;
|
||||
pub mod rpc_request;
|
||||
pub mod rpc_response;
|
||||
|
@@ -1,10 +1,10 @@
|
||||
use crate::{
|
||||
client_error::Result,
|
||||
rpc_request::RpcRequest,
|
||||
rpc_response::{Response, RpcResponseContext, RpcVersionInfo},
|
||||
rpc_response::{Response, RpcResponseContext},
|
||||
rpc_sender::RpcSender,
|
||||
};
|
||||
use serde_json::{json, Number, Value};
|
||||
use serde_json::{Number, Value};
|
||||
use solana_sdk::{
|
||||
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
||||
instruction::InstructionError,
|
||||
@@ -12,7 +12,6 @@ use solana_sdk::{
|
||||
transaction::{self, Transaction, TransactionError},
|
||||
};
|
||||
use solana_transaction_status::TransactionStatus;
|
||||
use solana_version::Version;
|
||||
use std::{collections::HashMap, sync::RwLock};
|
||||
|
||||
pub const PUBKEY: &str = "7RoSF9fUmdphVCpabEoefH81WwrW7orsWonXWqTXkKV8";
|
||||
@@ -95,15 +94,9 @@ impl RpcSender for MockSender {
|
||||
err,
|
||||
})
|
||||
};
|
||||
let statuses: Vec<Option<TransactionStatus>> = params.as_array().unwrap()[0]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|_| status.clone())
|
||||
.collect();
|
||||
serde_json::to_value(Response {
|
||||
context: RpcResponseContext { slot: 1 },
|
||||
value: statuses,
|
||||
value: vec![status],
|
||||
})?
|
||||
}
|
||||
RpcRequest::GetTransactionCount => Value::Number(Number::from(1234)),
|
||||
@@ -113,20 +106,13 @@ impl RpcSender for MockSender {
|
||||
Signature::new(&[8; 64]).to_string()
|
||||
} else {
|
||||
let tx_str = params.as_array().unwrap()[0].as_str().unwrap().to_string();
|
||||
let data = base64::decode(tx_str).unwrap();
|
||||
let data = bs58::decode(tx_str).into_vec().unwrap();
|
||||
let tx: Transaction = bincode::deserialize(&data).unwrap();
|
||||
tx.signatures[0].to_string()
|
||||
};
|
||||
Value::String(signature)
|
||||
}
|
||||
RpcRequest::GetMinimumBalanceForRentExemption => Value::Number(Number::from(20)),
|
||||
RpcRequest::GetVersion => {
|
||||
let version = Version::default();
|
||||
json!(RpcVersionInfo {
|
||||
solana_core: version.to_string(),
|
||||
feature_set: Some(version.feature_set),
|
||||
})
|
||||
}
|
||||
_ => Value::Null,
|
||||
};
|
||||
Ok(val)
|
||||
|
@@ -1,82 +0,0 @@
|
||||
use crate::rpc_client::RpcClient;
|
||||
use solana_sdk::{
|
||||
account::Account,
|
||||
account_utils::StateMut,
|
||||
commitment_config::CommitmentConfig,
|
||||
nonce::{
|
||||
state::{Data, Versions},
|
||||
State,
|
||||
},
|
||||
pubkey::Pubkey,
|
||||
system_program,
|
||||
};
|
||||
|
||||
#[derive(Debug, thiserror::Error, PartialEq)]
|
||||
pub enum Error {
|
||||
#[error("invalid account owner")]
|
||||
InvalidAccountOwner,
|
||||
#[error("invalid account data")]
|
||||
InvalidAccountData,
|
||||
#[error("unexpected account data size")]
|
||||
UnexpectedDataSize,
|
||||
#[error("query hash does not match stored hash")]
|
||||
InvalidHash,
|
||||
#[error("query authority does not match account authority")]
|
||||
InvalidAuthority,
|
||||
#[error("invalid state for requested operation")]
|
||||
InvalidStateForOperation,
|
||||
#[error("client error: {0}")]
|
||||
Client(String),
|
||||
}
|
||||
|
||||
pub fn get_account(rpc_client: &RpcClient, nonce_pubkey: &Pubkey) -> Result<Account, Error> {
|
||||
get_account_with_commitment(rpc_client, nonce_pubkey, CommitmentConfig::default())
|
||||
}
|
||||
|
||||
pub fn get_account_with_commitment(
|
||||
rpc_client: &RpcClient,
|
||||
nonce_pubkey: &Pubkey,
|
||||
commitment: CommitmentConfig,
|
||||
) -> Result<Account, Error> {
|
||||
rpc_client
|
||||
.get_account_with_commitment(nonce_pubkey, commitment)
|
||||
.map_err(|e| Error::Client(format!("{}", e)))
|
||||
.and_then(|result| {
|
||||
result
|
||||
.value
|
||||
.ok_or_else(|| Error::Client(format!("AccountNotFound: pubkey={}", nonce_pubkey)))
|
||||
})
|
||||
.and_then(|a| match account_identity_ok(&a) {
|
||||
Ok(()) => Ok(a),
|
||||
Err(e) => Err(e),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn account_identity_ok(account: &Account) -> Result<(), Error> {
|
||||
if account.owner != system_program::id() {
|
||||
Err(Error::InvalidAccountOwner)
|
||||
} else if account.data.is_empty() {
|
||||
Err(Error::UnexpectedDataSize)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn state_from_account(account: &Account) -> Result<State, Error> {
|
||||
account_identity_ok(account)?;
|
||||
StateMut::<Versions>::state(account)
|
||||
.map_err(|_| Error::InvalidAccountData)
|
||||
.map(|v| v.convert_to_current())
|
||||
}
|
||||
|
||||
pub fn data_from_account(account: &Account) -> Result<Data, Error> {
|
||||
account_identity_ok(account)?;
|
||||
state_from_account(account).and_then(|ref s| data_from_state(s).map(|d| d.clone()))
|
||||
}
|
||||
|
||||
pub fn data_from_state(state: &State) -> Result<&Data, Error> {
|
||||
match state {
|
||||
State::Uninitialized => Err(Error::InvalidStateForOperation),
|
||||
State::Initialized(data) => Ok(data),
|
||||
}
|
||||
}
|
@@ -1,12 +1,10 @@
|
||||
use crate::rpc_response::{Response as RpcResponse, RpcSignatureResult, SlotInfo};
|
||||
use log::*;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{de::DeserializeOwned, Deserialize};
|
||||
use serde_json::{
|
||||
json,
|
||||
value::Value::{Number, Object},
|
||||
Map, Value,
|
||||
};
|
||||
use solana_sdk::signature::Signature;
|
||||
use std::{
|
||||
marker::PhantomData,
|
||||
sync::{
|
||||
@@ -20,8 +18,6 @@ use thiserror::Error;
|
||||
use tungstenite::{client::AutoStream, connect, Message, WebSocket};
|
||||
use url::{ParseError, Url};
|
||||
|
||||
type PubsubSignatureResponse = PubsubClientSubscription<RpcResponse<RpcSignatureResult>>;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum PubsubClientError {
|
||||
#[error("url parse error")]
|
||||
@@ -37,6 +33,13 @@ pub enum PubsubClientError {
|
||||
UnexpectedMessageError,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)]
|
||||
pub struct SlotInfoMessage {
|
||||
pub parent: u64,
|
||||
pub root: u64,
|
||||
pub slot: u64,
|
||||
}
|
||||
|
||||
pub struct PubsubClientSubscription<T>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
@@ -70,12 +73,18 @@ where
|
||||
{
|
||||
fn send_subscribe(
|
||||
writable_socket: &Arc<RwLock<WebSocket<AutoStream>>>,
|
||||
body: String,
|
||||
operation: &str,
|
||||
) -> Result<u64, PubsubClientError> {
|
||||
let method = format!("{}Subscribe", operation);
|
||||
writable_socket
|
||||
.write()
|
||||
.unwrap()
|
||||
.write_message(Message::Text(body))?;
|
||||
.write_message(Message::Text(
|
||||
json!({
|
||||
"jsonrpc":"2.0","id":1,"method":method,"params":[]
|
||||
})
|
||||
.to_string(),
|
||||
))?;
|
||||
let message = writable_socket.write().unwrap().read_message()?;
|
||||
Self::extract_subscription_id(message)
|
||||
}
|
||||
@@ -139,28 +148,30 @@ where
|
||||
}
|
||||
|
||||
const SLOT_OPERATION: &str = "slot";
|
||||
const SIGNATURE_OPERATION: &str = "signature";
|
||||
|
||||
pub struct PubsubClient {}
|
||||
|
||||
impl PubsubClient {
|
||||
pub fn slot_subscribe(
|
||||
url: &str,
|
||||
) -> Result<(PubsubClientSubscription<SlotInfo>, Receiver<SlotInfo>), PubsubClientError> {
|
||||
) -> Result<
|
||||
(
|
||||
PubsubClientSubscription<SlotInfoMessage>,
|
||||
Receiver<SlotInfoMessage>,
|
||||
),
|
||||
PubsubClientError,
|
||||
> {
|
||||
let url = Url::parse(url)?;
|
||||
let (socket, _response) = connect(url)?;
|
||||
let (sender, receiver) = channel::<SlotInfo>();
|
||||
let (sender, receiver) = channel::<SlotInfoMessage>();
|
||||
|
||||
let socket = Arc::new(RwLock::new(socket));
|
||||
let socket_clone = socket.clone();
|
||||
let exit = Arc::new(AtomicBool::new(false));
|
||||
let exit_clone = exit.clone();
|
||||
let subscription_id = PubsubClientSubscription::<SlotInfo>::send_subscribe(
|
||||
let subscription_id = PubsubClientSubscription::<SlotInfoMessage>::send_subscribe(
|
||||
&socket_clone,
|
||||
json!({
|
||||
"jsonrpc":"2.0","id":1,"method":format!("{}Subscribe", SLOT_OPERATION),"params":[]
|
||||
})
|
||||
.to_string(),
|
||||
SLOT_OPERATION,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -170,80 +181,7 @@ impl PubsubClient {
|
||||
break;
|
||||
}
|
||||
|
||||
let message: Result<SlotInfo, PubsubClientError> =
|
||||
PubsubClientSubscription::read_message(&socket_clone);
|
||||
|
||||
if let Ok(msg) = message {
|
||||
match sender.send(msg) {
|
||||
Ok(_) => (),
|
||||
Err(err) => {
|
||||
info!("receive error: {:?}", err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
info!("receive error: {:?}", message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
info!("websocket - exited receive loop");
|
||||
});
|
||||
|
||||
let result: PubsubClientSubscription<SlotInfo> = PubsubClientSubscription {
|
||||
message_type: PhantomData,
|
||||
operation: SLOT_OPERATION,
|
||||
socket,
|
||||
subscription_id,
|
||||
t_cleanup: Some(t_cleanup),
|
||||
exit,
|
||||
};
|
||||
|
||||
Ok((result, receiver))
|
||||
}
|
||||
|
||||
pub fn signature_subscribe(
|
||||
url: &str,
|
||||
signature: &Signature,
|
||||
) -> Result<
|
||||
(
|
||||
PubsubSignatureResponse,
|
||||
Receiver<RpcResponse<RpcSignatureResult>>,
|
||||
),
|
||||
PubsubClientError,
|
||||
> {
|
||||
let url = Url::parse(url)?;
|
||||
let (socket, _response) = connect(url)?;
|
||||
let (sender, receiver) = channel::<RpcResponse<RpcSignatureResult>>();
|
||||
|
||||
let socket = Arc::new(RwLock::new(socket));
|
||||
let socket_clone = socket.clone();
|
||||
let exit = Arc::new(AtomicBool::new(false));
|
||||
let exit_clone = exit.clone();
|
||||
let body = json!({
|
||||
"jsonrpc":"2.0",
|
||||
"id":1,
|
||||
"method":format!("{}Subscribe", SIGNATURE_OPERATION),
|
||||
"params":[
|
||||
signature.to_string(),
|
||||
{"enableReceivedNotification": true }
|
||||
]
|
||||
})
|
||||
.to_string();
|
||||
let subscription_id =
|
||||
PubsubClientSubscription::<RpcResponse<RpcSignatureResult>>::send_subscribe(
|
||||
&socket_clone,
|
||||
body,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let t_cleanup = std::thread::spawn(move || {
|
||||
loop {
|
||||
if exit_clone.load(Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
|
||||
let message: Result<RpcResponse<RpcSignatureResult>, PubsubClientError> =
|
||||
let message: Result<SlotInfoMessage, PubsubClientError> =
|
||||
PubsubClientSubscription::read_message(&socket_clone);
|
||||
|
||||
if let Ok(msg) = message {
|
||||
@@ -263,15 +201,14 @@ impl PubsubClient {
|
||||
info!("websocket - exited receive loop");
|
||||
});
|
||||
|
||||
let result: PubsubClientSubscription<RpcResponse<RpcSignatureResult>> =
|
||||
PubsubClientSubscription {
|
||||
message_type: PhantomData,
|
||||
operation: SIGNATURE_OPERATION,
|
||||
socket,
|
||||
subscription_id,
|
||||
t_cleanup: Some(t_cleanup),
|
||||
exit,
|
||||
};
|
||||
let result: PubsubClientSubscription<SlotInfoMessage> = PubsubClientSubscription {
|
||||
message_type: PhantomData,
|
||||
operation: SLOT_OPERATION,
|
||||
socket,
|
||||
subscription_id,
|
||||
t_cleanup: Some(t_cleanup),
|
||||
exit,
|
||||
};
|
||||
|
||||
Ok((result, receiver))
|
||||
}
|
||||
|
@@ -5,8 +5,7 @@ use crate::{
|
||||
rpc_config::RpcAccountInfoConfig,
|
||||
rpc_config::{
|
||||
RpcGetConfirmedSignaturesForAddress2Config, RpcLargestAccountsConfig,
|
||||
RpcProgramAccountsConfig, RpcSendTransactionConfig, RpcSimulateTransactionConfig,
|
||||
RpcTokenAccountsFilter,
|
||||
RpcSendTransactionConfig, RpcTokenAccountsFilter,
|
||||
},
|
||||
rpc_request::{RpcError, RpcRequest, TokenAccountsFilter},
|
||||
rpc_response::*,
|
||||
@@ -16,10 +15,7 @@ use bincode::serialize;
|
||||
use indicatif::{ProgressBar, ProgressStyle};
|
||||
use log::*;
|
||||
use serde_json::{json, Value};
|
||||
use solana_account_decoder::{
|
||||
parse_token::{TokenAccountType, UiTokenAccount, UiTokenAmount},
|
||||
UiAccount, UiAccountData, UiAccountEncoding,
|
||||
};
|
||||
use solana_account_decoder::{parse_token::UiTokenAmount, UiAccount, UiAccountEncoding};
|
||||
use solana_sdk::{
|
||||
account::Account,
|
||||
clock::{
|
||||
@@ -33,49 +29,26 @@ use solana_sdk::{
|
||||
hash::Hash,
|
||||
pubkey::Pubkey,
|
||||
signature::Signature,
|
||||
transaction::{self, uses_durable_nonce, Transaction},
|
||||
transaction::{self, Transaction},
|
||||
};
|
||||
use solana_transaction_status::{
|
||||
EncodedConfirmedBlock, EncodedConfirmedTransaction, TransactionStatus, UiTransactionEncoding,
|
||||
ConfirmedBlock, ConfirmedTransaction, TransactionStatus, UiTransactionEncoding,
|
||||
};
|
||||
use solana_vote_program::vote_state::MAX_LOCKOUT_HISTORY;
|
||||
use std::{
|
||||
net::SocketAddr,
|
||||
sync::RwLock,
|
||||
thread::sleep,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
pub struct RpcClient {
|
||||
sender: Box<dyn RpcSender + Send + Sync + 'static>,
|
||||
default_cluster_transaction_encoding: RwLock<Option<UiTransactionEncoding>>,
|
||||
}
|
||||
|
||||
fn serialize_encode_transaction(
|
||||
transaction: &Transaction,
|
||||
encoding: UiTransactionEncoding,
|
||||
) -> ClientResult<String> {
|
||||
let serialized = serialize(transaction)
|
||||
.map_err(|e| ClientErrorKind::Custom(format!("transaction serialization failed: {}", e)))?;
|
||||
let encoded = match encoding {
|
||||
UiTransactionEncoding::Base58 => bs58::encode(serialized).into_string(),
|
||||
UiTransactionEncoding::Base64 => base64::encode(serialized),
|
||||
_ => {
|
||||
return Err(ClientErrorKind::Custom(format!(
|
||||
"unsupported transaction encoding: {}. Supported encodings: base58, base64",
|
||||
encoding
|
||||
))
|
||||
.into())
|
||||
}
|
||||
};
|
||||
Ok(encoded)
|
||||
}
|
||||
|
||||
impl RpcClient {
|
||||
pub fn new_sender<T: RpcSender + Send + Sync + 'static>(sender: T) -> Self {
|
||||
Self {
|
||||
sender: Box::new(sender),
|
||||
default_cluster_transaction_encoding: RwLock::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,46 +104,13 @@ impl RpcClient {
|
||||
self.send_transaction_with_config(transaction, RpcSendTransactionConfig::default())
|
||||
}
|
||||
|
||||
fn default_cluster_transaction_encoding(&self) -> Result<UiTransactionEncoding, RpcError> {
|
||||
let default_cluster_transaction_encoding =
|
||||
self.default_cluster_transaction_encoding.read().unwrap();
|
||||
if let Some(encoding) = *default_cluster_transaction_encoding {
|
||||
Ok(encoding)
|
||||
} else {
|
||||
drop(default_cluster_transaction_encoding);
|
||||
let cluster_version = self.get_version().map_err(|e| {
|
||||
RpcError::RpcRequestError(format!("cluster version query failed: {}", e))
|
||||
})?;
|
||||
let cluster_version =
|
||||
semver::Version::parse(&cluster_version.solana_core).map_err(|e| {
|
||||
RpcError::RpcRequestError(format!("failed to parse cluster version: {}", e))
|
||||
})?;
|
||||
// Prefer base64 since 1.3.16
|
||||
let encoding = if cluster_version < semver::Version::new(1, 3, 16) {
|
||||
UiTransactionEncoding::Base58
|
||||
} else {
|
||||
UiTransactionEncoding::Base64
|
||||
};
|
||||
*self.default_cluster_transaction_encoding.write().unwrap() = Some(encoding);
|
||||
Ok(encoding)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_transaction_with_config(
|
||||
&self,
|
||||
transaction: &Transaction,
|
||||
config: RpcSendTransactionConfig,
|
||||
) -> ClientResult<Signature> {
|
||||
let encoding = if let Some(encoding) = config.encoding {
|
||||
encoding
|
||||
} else {
|
||||
self.default_cluster_transaction_encoding()?
|
||||
};
|
||||
let config = RpcSendTransactionConfig {
|
||||
encoding: Some(encoding),
|
||||
..config
|
||||
};
|
||||
let serialized_encoded = serialize_encode_transaction(transaction, encoding)?;
|
||||
let serialized_encoded = bs58::encode(serialize(transaction).unwrap()).into_string();
|
||||
|
||||
let signature_base58_str: String = self.send(
|
||||
RpcRequest::SendTransaction,
|
||||
json!([serialized_encoded, config]),
|
||||
@@ -197,28 +137,12 @@ impl RpcClient {
|
||||
pub fn simulate_transaction(
|
||||
&self,
|
||||
transaction: &Transaction,
|
||||
sig_verify: bool,
|
||||
) -> RpcResult<RpcSimulateTransactionResult> {
|
||||
self.simulate_transaction_with_config(transaction, RpcSimulateTransactionConfig::default())
|
||||
}
|
||||
|
||||
pub fn simulate_transaction_with_config(
|
||||
&self,
|
||||
transaction: &Transaction,
|
||||
config: RpcSimulateTransactionConfig,
|
||||
) -> RpcResult<RpcSimulateTransactionResult> {
|
||||
let encoding = if let Some(encoding) = config.encoding {
|
||||
encoding
|
||||
} else {
|
||||
self.default_cluster_transaction_encoding()?
|
||||
};
|
||||
let config = RpcSimulateTransactionConfig {
|
||||
encoding: Some(encoding),
|
||||
..config
|
||||
};
|
||||
let serialized_encoded = serialize_encode_transaction(transaction, encoding)?;
|
||||
let serialized_encoded = bs58::encode(serialize(transaction).unwrap()).into_string();
|
||||
self.send(
|
||||
RpcRequest::SimulateTransaction,
|
||||
json!([serialized_encoded, config]),
|
||||
json!([serialized_encoded, { "sigVerify": sig_verify }]),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -237,19 +161,6 @@ impl RpcClient {
|
||||
self.send(RpcRequest::GetSignatureStatuses, json!([signatures]))
|
||||
}
|
||||
|
||||
pub fn get_signature_statuses_with_history(
|
||||
&self,
|
||||
signatures: &[Signature],
|
||||
) -> RpcResult<Vec<Option<TransactionStatus>>> {
|
||||
let signatures: Vec<_> = signatures.iter().map(|s| s.to_string()).collect();
|
||||
self.send(
|
||||
RpcRequest::GetSignatureStatuses,
|
||||
json!([signatures, {
|
||||
"searchTransactionHistory": true
|
||||
}]),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_signature_status_with_commitment(
|
||||
&self,
|
||||
signature: &Signature,
|
||||
@@ -334,7 +245,7 @@ impl RpcClient {
|
||||
self.send(RpcRequest::GetClusterNodes, Value::Null)
|
||||
}
|
||||
|
||||
pub fn get_confirmed_block(&self, slot: Slot) -> ClientResult<EncodedConfirmedBlock> {
|
||||
pub fn get_confirmed_block(&self, slot: Slot) -> ClientResult<ConfirmedBlock> {
|
||||
self.get_confirmed_block_with_encoding(slot, UiTransactionEncoding::Json)
|
||||
}
|
||||
|
||||
@@ -342,7 +253,7 @@ impl RpcClient {
|
||||
&self,
|
||||
slot: Slot,
|
||||
encoding: UiTransactionEncoding,
|
||||
) -> ClientResult<EncodedConfirmedBlock> {
|
||||
) -> ClientResult<ConfirmedBlock> {
|
||||
self.send(RpcRequest::GetConfirmedBlock, json!([slot, encoding]))
|
||||
}
|
||||
|
||||
@@ -357,17 +268,6 @@ impl RpcClient {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_confirmed_blocks_with_limit(
|
||||
&self,
|
||||
start_slot: Slot,
|
||||
limit: usize,
|
||||
) -> ClientResult<Vec<Slot>> {
|
||||
self.send(
|
||||
RpcRequest::GetConfirmedBlocksWithLimit,
|
||||
json!([start_slot, limit]),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_confirmed_signatures_for_address(
|
||||
&self,
|
||||
address: &Pubkey,
|
||||
@@ -423,7 +323,7 @@ impl RpcClient {
|
||||
&self,
|
||||
signature: &Signature,
|
||||
encoding: UiTransactionEncoding,
|
||||
) -> ClientResult<EncodedConfirmedTransaction> {
|
||||
) -> ClientResult<ConfirmedTransaction> {
|
||||
self.send(
|
||||
RpcRequest::GetConfirmedTransaction,
|
||||
json!([signature.to_string(), encoding]),
|
||||
@@ -511,48 +411,44 @@ impl RpcClient {
|
||||
&self,
|
||||
transaction: &Transaction,
|
||||
) -> ClientResult<Signature> {
|
||||
let signature = self.send_transaction(transaction)?;
|
||||
let recent_blockhash = if uses_durable_nonce(transaction).is_some() {
|
||||
self.get_recent_blockhash_with_commitment(CommitmentConfig::recent())?
|
||||
.value
|
||||
.0
|
||||
} else {
|
||||
transaction.message.recent_blockhash
|
||||
};
|
||||
let status = loop {
|
||||
let status = self.get_signature_status(&signature)?;
|
||||
if status.is_none() {
|
||||
if self
|
||||
.get_fee_calculator_for_blockhash_with_commitment(
|
||||
&recent_blockhash,
|
||||
CommitmentConfig::recent(),
|
||||
)?
|
||||
.value
|
||||
.is_none()
|
||||
{
|
||||
let mut send_retries = 20;
|
||||
loop {
|
||||
let mut status_retries = 15;
|
||||
let signature = self.send_transaction(transaction)?;
|
||||
let status = loop {
|
||||
let status = self.get_signature_status(&signature)?;
|
||||
if status.is_none() {
|
||||
status_retries -= 1;
|
||||
if status_retries == 0 {
|
||||
break status;
|
||||
}
|
||||
} else {
|
||||
break status;
|
||||
}
|
||||
if cfg!(not(test)) {
|
||||
// Retry twice a second
|
||||
sleep(Duration::from_millis(500));
|
||||
}
|
||||
};
|
||||
send_retries = if let Some(result) = status.clone() {
|
||||
match result {
|
||||
Ok(_) => return Ok(signature),
|
||||
Err(_) => 0,
|
||||
}
|
||||
} else {
|
||||
break status;
|
||||
send_retries - 1
|
||||
};
|
||||
if send_retries == 0 {
|
||||
if let Some(err) = status {
|
||||
return Err(err.unwrap_err().into());
|
||||
} else {
|
||||
return Err(
|
||||
RpcError::ForUser("unable to confirm transaction. \
|
||||
This can happen in situations such as transaction expiration \
|
||||
and insufficient fee-payer funds".to_string()).into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
if cfg!(not(test)) {
|
||||
// Retry twice a second
|
||||
sleep(Duration::from_millis(500));
|
||||
}
|
||||
};
|
||||
if let Some(result) = status {
|
||||
match result {
|
||||
Ok(_) => Ok(signature),
|
||||
Err(err) => Err(err.into()),
|
||||
}
|
||||
} else {
|
||||
Err(RpcError::ForUser(
|
||||
"unable to confirm transaction. \
|
||||
This can happen in situations such as transaction expiration \
|
||||
and insufficient fee-payer funds"
|
||||
.to_string(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -603,38 +499,6 @@ impl RpcClient {
|
||||
})?
|
||||
}
|
||||
|
||||
pub fn get_multiple_accounts(&self, pubkeys: &[Pubkey]) -> ClientResult<Vec<Option<Account>>> {
|
||||
Ok(self
|
||||
.get_multiple_accounts_with_commitment(pubkeys, CommitmentConfig::default())?
|
||||
.value)
|
||||
}
|
||||
|
||||
pub fn get_multiple_accounts_with_commitment(
|
||||
&self,
|
||||
pubkeys: &[Pubkey],
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> RpcResult<Vec<Option<Account>>> {
|
||||
let config = RpcAccountInfoConfig {
|
||||
encoding: Some(UiAccountEncoding::Base64),
|
||||
commitment: Some(commitment_config),
|
||||
data_slice: None,
|
||||
};
|
||||
let pubkeys: Vec<_> = pubkeys.iter().map(|pubkey| pubkey.to_string()).collect();
|
||||
let response = self.send(RpcRequest::GetMultipleAccounts, json!([pubkeys, config]))?;
|
||||
let Response {
|
||||
context,
|
||||
value: accounts,
|
||||
} = serde_json::from_value::<Response<Vec<Option<UiAccount>>>>(response)?;
|
||||
let accounts: Vec<Option<Account>> = accounts
|
||||
.into_iter()
|
||||
.map(|rpc_account| rpc_account.map(|a| a.decode()).flatten())
|
||||
.collect();
|
||||
Ok(Response {
|
||||
context,
|
||||
value: accounts,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_account_data(&self, pubkey: &Pubkey) -> ClientResult<Vec<u8>> {
|
||||
Ok(self.get_account(pubkey)?.data)
|
||||
}
|
||||
@@ -675,27 +539,8 @@ impl RpcClient {
|
||||
}
|
||||
|
||||
pub fn get_program_accounts(&self, pubkey: &Pubkey) -> ClientResult<Vec<(Pubkey, Account)>> {
|
||||
self.get_program_accounts_with_config(
|
||||
pubkey,
|
||||
RpcProgramAccountsConfig {
|
||||
filters: None,
|
||||
account_config: RpcAccountInfoConfig {
|
||||
encoding: Some(UiAccountEncoding::Base64),
|
||||
..RpcAccountInfoConfig::default()
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_program_accounts_with_config(
|
||||
&self,
|
||||
pubkey: &Pubkey,
|
||||
config: RpcProgramAccountsConfig,
|
||||
) -> ClientResult<Vec<(Pubkey, Account)>> {
|
||||
let accounts: Vec<RpcKeyedAccount> = self.send(
|
||||
RpcRequest::GetProgramAccounts,
|
||||
json!([pubkey.to_string(), config]),
|
||||
)?;
|
||||
let accounts: Vec<RpcKeyedAccount> =
|
||||
self.send(RpcRequest::GetProgramAccounts, json!([pubkey.to_string()]))?;
|
||||
parse_keyed_accounts(accounts, RpcRequest::GetProgramAccounts)
|
||||
}
|
||||
|
||||
@@ -832,10 +677,6 @@ impl RpcClient {
|
||||
.into())
|
||||
}
|
||||
|
||||
pub fn get_first_available_block(&self) -> ClientResult<Slot> {
|
||||
self.send(RpcRequest::GetFirstAvailableBlock, Value::Null)
|
||||
}
|
||||
|
||||
pub fn get_genesis_hash(&self) -> ClientResult<Hash> {
|
||||
let hash_str: String = self.send(RpcRequest::GetGenesisHash, Value::Null)?;
|
||||
let hash = hash_str.parse().map_err(|_| {
|
||||
@@ -847,67 +688,6 @@ impl RpcClient {
|
||||
Ok(hash)
|
||||
}
|
||||
|
||||
pub fn get_token_account(&self, pubkey: &Pubkey) -> ClientResult<Option<UiTokenAccount>> {
|
||||
Ok(self
|
||||
.get_token_account_with_commitment(pubkey, CommitmentConfig::default())?
|
||||
.value)
|
||||
}
|
||||
|
||||
pub fn get_token_account_with_commitment(
|
||||
&self,
|
||||
pubkey: &Pubkey,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> RpcResult<Option<UiTokenAccount>> {
|
||||
let config = RpcAccountInfoConfig {
|
||||
encoding: Some(UiAccountEncoding::JsonParsed),
|
||||
commitment: Some(commitment_config),
|
||||
data_slice: None,
|
||||
};
|
||||
let response = self.sender.send(
|
||||
RpcRequest::GetAccountInfo,
|
||||
json!([pubkey.to_string(), config]),
|
||||
);
|
||||
|
||||
response
|
||||
.map(|result_json| {
|
||||
if result_json.is_null() {
|
||||
return Err(
|
||||
RpcError::ForUser(format!("AccountNotFound: pubkey={}", pubkey)).into(),
|
||||
);
|
||||
}
|
||||
let Response {
|
||||
context,
|
||||
value: rpc_account,
|
||||
} = serde_json::from_value::<Response<Option<UiAccount>>>(result_json)?;
|
||||
trace!("Response account {:?} {:?}", pubkey, rpc_account);
|
||||
let response = {
|
||||
if let Some(rpc_account) = rpc_account {
|
||||
if let UiAccountData::Json(account_data) = rpc_account.data {
|
||||
let token_account_type: TokenAccountType =
|
||||
serde_json::from_value(account_data.parsed)?;
|
||||
if let TokenAccountType::Account(token_account) = token_account_type {
|
||||
return Ok(Response {
|
||||
context,
|
||||
value: Some(token_account),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(Into::<ClientError>::into(RpcError::ForUser(format!(
|
||||
"Account could not be parsed as token account: pubkey={}",
|
||||
pubkey
|
||||
))))
|
||||
};
|
||||
response?
|
||||
})
|
||||
.map_err(|err| {
|
||||
Into::<ClientError>::into(RpcError::ForUser(format!(
|
||||
"AccountNotFound: pubkey={}: {}",
|
||||
pubkey, err
|
||||
)))
|
||||
})?
|
||||
}
|
||||
|
||||
pub fn get_token_account_balance(&self, pubkey: &Pubkey) -> ClientResult<UiTokenAmount> {
|
||||
Ok(self
|
||||
.get_token_account_balance_with_commitment(pubkey, CommitmentConfig::default())?
|
||||
@@ -1227,54 +1007,62 @@ impl RpcClient {
|
||||
|
||||
let progress_bar = new_spinner_progress_bar();
|
||||
|
||||
progress_bar.set_message(&format!(
|
||||
"[{}/{}] Finalizing transaction {}",
|
||||
confirmations, desired_confirmations, transaction.signatures[0],
|
||||
));
|
||||
let recent_blockhash = if uses_durable_nonce(transaction).is_some() {
|
||||
self.get_recent_blockhash_with_commitment(CommitmentConfig::recent())?
|
||||
.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.get_signature_status_with_commitment(&signature, CommitmentConfig::recent())?;
|
||||
if status.is_none() {
|
||||
if self
|
||||
.get_fee_calculator_for_blockhash_with_commitment(
|
||||
&recent_blockhash,
|
||||
CommitmentConfig::recent(),
|
||||
)?
|
||||
.value
|
||||
.is_none()
|
||||
{
|
||||
let mut send_retries = 20;
|
||||
let signature = loop {
|
||||
progress_bar.set_message(&format!(
|
||||
"[{}/{}] Finalizing transaction {}",
|
||||
confirmations, desired_confirmations, transaction.signatures[0],
|
||||
));
|
||||
let mut status_retries = 15;
|
||||
let (signature, status) = loop {
|
||||
let signature = self.send_transaction_with_config(transaction, config)?;
|
||||
|
||||
// Get recent commitment in order to count confirmations for successful transactions
|
||||
let status = self
|
||||
.get_signature_status_with_commitment(&signature, CommitmentConfig::recent())?;
|
||||
if status.is_none() {
|
||||
status_retries -= 1;
|
||||
if status_retries == 0 {
|
||||
break (signature, status);
|
||||
}
|
||||
} else {
|
||||
break (signature, status);
|
||||
}
|
||||
} else {
|
||||
break (signature, status);
|
||||
}
|
||||
|
||||
if cfg!(not(test)) {
|
||||
sleep(Duration::from_millis(500));
|
||||
if cfg!(not(test)) {
|
||||
sleep(Duration::from_millis(500));
|
||||
}
|
||||
};
|
||||
send_retries = if let Some(result) = status.clone() {
|
||||
match result {
|
||||
Ok(_) => 0,
|
||||
// If transaction errors, return right away; no point in counting confirmations
|
||||
Err(_) => 0,
|
||||
}
|
||||
} else {
|
||||
send_retries - 1
|
||||
};
|
||||
if send_retries == 0 {
|
||||
if let Some(result) = status {
|
||||
match result {
|
||||
Ok(_) => {
|
||||
break signature;
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(err.into());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(RpcError::ForUser(
|
||||
"unable to confirm transaction. \
|
||||
This can happen in situations such as transaction \
|
||||
expiration and insufficient fee-payer funds"
|
||||
.to_string(),
|
||||
)
|
||||
.into());
|
||||
}
|
||||
}
|
||||
};
|
||||
if let Some(result) = status {
|
||||
if let Err(err) = result {
|
||||
return Err(err.into());
|
||||
}
|
||||
} else {
|
||||
return Err(RpcError::ForUser(
|
||||
"unable to confirm transaction. \
|
||||
This can happen in situations such as transaction expiration \
|
||||
and insufficient fee-payer funds"
|
||||
.to_string(),
|
||||
)
|
||||
.into());
|
||||
}
|
||||
let now = Instant::now();
|
||||
loop {
|
||||
match commitment.commitment {
|
||||
@@ -1456,7 +1244,7 @@ mod tests {
|
||||
let rpc_client = RpcClient::new_mock("succeeds".to_string());
|
||||
|
||||
let key = Keypair::new();
|
||||
let to = solana_sdk::pubkey::new_rand();
|
||||
let to = Pubkey::new_rand();
|
||||
let blockhash = Hash::default();
|
||||
let tx = system_transaction::transfer(&key, &to, 50, blockhash);
|
||||
|
||||
@@ -1509,7 +1297,7 @@ mod tests {
|
||||
let rpc_client = RpcClient::new_mock("succeeds".to_string());
|
||||
|
||||
let key = Keypair::new();
|
||||
let to = solana_sdk::pubkey::new_rand();
|
||||
let to = Pubkey::new_rand();
|
||||
let blockhash = Hash::default();
|
||||
let tx = system_transaction::transfer(&key, &to, 50, blockhash);
|
||||
let result = rpc_client.send_and_confirm_transaction(&tx);
|
||||
|
@@ -1,10 +1,6 @@
|
||||
use crate::rpc_filter::RpcFilterType;
|
||||
use solana_account_decoder::{UiAccountEncoding, UiDataSliceConfig};
|
||||
use solana_sdk::{
|
||||
clock::Epoch,
|
||||
commitment_config::{CommitmentConfig, CommitmentLevel},
|
||||
};
|
||||
use solana_transaction_status::UiTransactionEncoding;
|
||||
use solana_sdk::{clock::Epoch, commitment_config::CommitmentConfig};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -15,20 +11,15 @@ pub struct RpcSignatureStatusConfig {
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcSendTransactionConfig {
|
||||
#[serde(default)]
|
||||
pub skip_preflight: bool,
|
||||
pub preflight_commitment: Option<CommitmentLevel>,
|
||||
pub encoding: Option<UiTransactionEncoding>,
|
||||
pub preflight_commitment: Option<CommitmentConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcSimulateTransactionConfig {
|
||||
#[serde(default)]
|
||||
pub sig_verify: bool,
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
pub encoding: Option<UiTransactionEncoding>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
@@ -78,14 +69,6 @@ pub enum RpcTokenAccountsFilter {
|
||||
ProgramId(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcSignatureSubscribeConfig {
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
pub enable_received_notification: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcGetConfirmedSignaturesForAddress2Config {
|
||||
|
@@ -1,78 +0,0 @@
|
||||
//! Implementation defined RPC server errors
|
||||
|
||||
use crate::rpc_response::RpcSimulateTransactionResult;
|
||||
use jsonrpc_core::{Error, ErrorCode};
|
||||
use solana_sdk::clock::Slot;
|
||||
|
||||
pub const JSON_RPC_SERVER_ERROR_BLOCK_CLEANED_UP: i64 = -32001;
|
||||
pub const JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE: i64 = -32002;
|
||||
pub const JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE: i64 = -32003;
|
||||
pub const JSON_RPC_SERVER_ERROR_BLOCK_NOT_AVAILABLE: i64 = -32004;
|
||||
pub const JSON_RPC_SERVER_ERROR_NODE_UNHEALTHLY: i64 = -32005;
|
||||
pub const JSON_RPC_SERVER_ERROR_TRANSACTION_PRECOMPILE_VERIFICATION_FAILURE: i64 = -32006;
|
||||
|
||||
pub enum RpcCustomError {
|
||||
BlockCleanedUp {
|
||||
slot: Slot,
|
||||
first_available_block: Slot,
|
||||
},
|
||||
SendTransactionPreflightFailure {
|
||||
message: String,
|
||||
result: RpcSimulateTransactionResult,
|
||||
},
|
||||
TransactionSignatureVerificationFailure,
|
||||
BlockNotAvailable {
|
||||
slot: Slot,
|
||||
},
|
||||
RpcNodeUnhealthy,
|
||||
TransactionPrecompileVerificationFailure(solana_sdk::transaction::TransactionError),
|
||||
}
|
||||
|
||||
impl From<RpcCustomError> for Error {
|
||||
fn from(e: RpcCustomError) -> Self {
|
||||
match e {
|
||||
RpcCustomError::BlockCleanedUp {
|
||||
slot,
|
||||
first_available_block,
|
||||
} => Self {
|
||||
code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_BLOCK_CLEANED_UP),
|
||||
message: format!(
|
||||
"Block {} cleaned up, does not exist on node. First available block: {}",
|
||||
slot, first_available_block,
|
||||
),
|
||||
data: None,
|
||||
},
|
||||
RpcCustomError::SendTransactionPreflightFailure { message, result } => Self {
|
||||
code: ErrorCode::ServerError(
|
||||
JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE,
|
||||
),
|
||||
message,
|
||||
data: Some(serde_json::json!(result)),
|
||||
},
|
||||
RpcCustomError::TransactionSignatureVerificationFailure => Self {
|
||||
code: ErrorCode::ServerError(
|
||||
JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE,
|
||||
),
|
||||
message: "Transaction signature verification failure".to_string(),
|
||||
data: None,
|
||||
},
|
||||
RpcCustomError::BlockNotAvailable { slot } => Self {
|
||||
code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_BLOCK_NOT_AVAILABLE),
|
||||
message: format!("Block not available for slot {}", slot),
|
||||
data: None,
|
||||
},
|
||||
RpcCustomError::RpcNodeUnhealthy => Self {
|
||||
code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_NODE_UNHEALTHLY),
|
||||
message: "RPC node is unhealthy".to_string(),
|
||||
data: None,
|
||||
},
|
||||
RpcCustomError::TransactionPrecompileVerificationFailure(e) => Self {
|
||||
code: ErrorCode::ServerError(
|
||||
JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE,
|
||||
),
|
||||
message: format!("Transaction precompile verification failure {:?}", e),
|
||||
data: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
@@ -13,7 +13,6 @@ pub enum RpcRequest {
|
||||
GetClusterNodes,
|
||||
GetConfirmedBlock,
|
||||
GetConfirmedBlocks,
|
||||
GetConfirmedBlocksWithLimit,
|
||||
GetConfirmedSignaturesForAddress,
|
||||
GetConfirmedSignaturesForAddress2,
|
||||
GetConfirmedTransaction,
|
||||
@@ -22,7 +21,6 @@ pub enum RpcRequest {
|
||||
GetFeeCalculatorForBlockhash,
|
||||
GetFeeRateGovernor,
|
||||
GetFees,
|
||||
GetFirstAvailableBlock,
|
||||
GetGenesisHash,
|
||||
GetIdentity,
|
||||
GetInflationGovernor,
|
||||
@@ -30,7 +28,6 @@ pub enum RpcRequest {
|
||||
GetLargestAccounts,
|
||||
GetLeaderSchedule,
|
||||
GetMinimumBalanceForRentExemption,
|
||||
GetMultipleAccounts,
|
||||
GetProgramAccounts,
|
||||
GetRecentBlockhash,
|
||||
GetSignatureStatuses,
|
||||
@@ -68,7 +65,6 @@ impl fmt::Display for RpcRequest {
|
||||
RpcRequest::GetClusterNodes => "getClusterNodes",
|
||||
RpcRequest::GetConfirmedBlock => "getConfirmedBlock",
|
||||
RpcRequest::GetConfirmedBlocks => "getConfirmedBlocks",
|
||||
RpcRequest::GetConfirmedBlocksWithLimit => "getConfirmedBlocksWithLimit",
|
||||
RpcRequest::GetConfirmedSignaturesForAddress => "getConfirmedSignaturesForAddress",
|
||||
RpcRequest::GetConfirmedSignaturesForAddress2 => "getConfirmedSignaturesForAddress2",
|
||||
RpcRequest::GetConfirmedTransaction => "getConfirmedTransaction",
|
||||
@@ -77,7 +73,6 @@ impl fmt::Display for RpcRequest {
|
||||
RpcRequest::GetFeeCalculatorForBlockhash => "getFeeCalculatorForBlockhash",
|
||||
RpcRequest::GetFeeRateGovernor => "getFeeRateGovernor",
|
||||
RpcRequest::GetFees => "getFees",
|
||||
RpcRequest::GetFirstAvailableBlock => "getFirstAvailableBlock",
|
||||
RpcRequest::GetGenesisHash => "getGenesisHash",
|
||||
RpcRequest::GetIdentity => "getIdentity",
|
||||
RpcRequest::GetInflationGovernor => "getInflationGovernor",
|
||||
@@ -85,7 +80,6 @@ impl fmt::Display for RpcRequest {
|
||||
RpcRequest::GetLargestAccounts => "getLargestAccounts",
|
||||
RpcRequest::GetLeaderSchedule => "getLeaderSchedule",
|
||||
RpcRequest::GetMinimumBalanceForRentExemption => "getMinimumBalanceForRentExemption",
|
||||
RpcRequest::GetMultipleAccounts => "getMultipleAccounts",
|
||||
RpcRequest::GetProgramAccounts => "getProgramAccounts",
|
||||
RpcRequest::GetRecentBlockhash => "getRecentBlockhash",
|
||||
RpcRequest::GetSignatureStatuses => "getSignatureStatuses",
|
||||
@@ -116,12 +110,11 @@ impl fmt::Display for RpcRequest {
|
||||
}
|
||||
}
|
||||
|
||||
pub const NUM_LARGEST_ACCOUNTS: usize = 20;
|
||||
pub const MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS: usize = 256;
|
||||
pub const MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS_SLOT_RANGE: u64 = 10_000;
|
||||
pub const MAX_GET_CONFIRMED_BLOCKS_RANGE: u64 = 500_000;
|
||||
pub const MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS2_LIMIT: usize = 1_000;
|
||||
pub const MAX_MULTIPLE_ACCOUNTS: usize = 100;
|
||||
pub const NUM_LARGEST_ACCOUNTS: usize = 20;
|
||||
|
||||
// Validators that are this number of slots behind are considered delinquent
|
||||
pub const DELINQUENT_VALIDATOR_SLOT_DISTANCE: u64 = 128;
|
||||
@@ -140,10 +133,8 @@ impl RpcRequest {
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum RpcError {
|
||||
#[error("RPC request error: {0}")]
|
||||
#[error("rpc request error: {0}")]
|
||||
RpcRequestError(String),
|
||||
#[error("RPC response error {code}: {message}")]
|
||||
RpcResponseError { code: i64, message: String },
|
||||
#[error("parse error: expected {0}")]
|
||||
ParseError(String), /* "expected" */
|
||||
// Anything in a `ForUser` needs to die. The caller should be
|
||||
@@ -228,7 +219,7 @@ mod tests {
|
||||
|
||||
// Test request with CommitmentConfig and params
|
||||
let test_request = RpcRequest::GetTokenAccountsByOwner;
|
||||
let mint = solana_sdk::pubkey::new_rand();
|
||||
let mint = Pubkey::new_rand();
|
||||
let token_account_filter = RpcTokenAccountsFilter::Mint(mint.to_string());
|
||||
let request = test_request
|
||||
.build_request_json(1, json!([addr, token_account_filter, commitment_config]));
|
||||
|
@@ -7,7 +7,7 @@ use solana_sdk::{
|
||||
transaction::{Result, TransactionError},
|
||||
};
|
||||
use solana_transaction_status::ConfirmedTransactionStatusWithSignature;
|
||||
use std::{collections::HashMap, fmt, net::SocketAddr};
|
||||
use std::{collections::HashMap, net::SocketAddr};
|
||||
|
||||
pub type RpcResult<T> = client_error::Result<Response<T>>;
|
||||
|
||||
@@ -94,34 +94,13 @@ pub struct RpcKeyedAccount {
|
||||
pub account: UiAccount,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)]
|
||||
pub struct SlotInfo {
|
||||
pub slot: Slot,
|
||||
pub parent: Slot,
|
||||
pub root: Slot,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(rename_all = "camelCase", untagged)]
|
||||
pub enum RpcSignatureResult {
|
||||
ProcessedSignature(ProcessedSignatureResult),
|
||||
ReceivedSignature(ReceivedSignatureResult),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ProcessedSignatureResult {
|
||||
pub struct RpcSignatureResult {
|
||||
pub err: Option<TransactionError>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum ReceivedSignatureResult {
|
||||
ReceivedSignature,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcContactInfo {
|
||||
/// Pubkey of the node as a base-58 string
|
||||
pub pubkey: String,
|
||||
@@ -133,37 +112,16 @@ pub struct RpcContactInfo {
|
||||
pub rpc: Option<SocketAddr>,
|
||||
/// Software version
|
||||
pub version: Option<String>,
|
||||
/// First 4 bytes of the FeatureSet identifier
|
||||
pub feature_set: Option<u32>,
|
||||
}
|
||||
|
||||
/// Map of leader base58 identity pubkeys to the slot indices relative to the first epoch slot
|
||||
pub type RpcLeaderSchedule = HashMap<String, Vec<usize>>;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct RpcVersionInfo {
|
||||
/// The current version of solana-core
|
||||
pub solana_core: String,
|
||||
/// first 4 bytes of the FeatureSet identifier
|
||||
pub feature_set: Option<u32>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for RpcVersionInfo {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.solana_core)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for RpcVersionInfo {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if let Some(version) = self.solana_core.split_whitespace().next() {
|
||||
// Display just the semver if possible
|
||||
write!(f, "{}", version)
|
||||
} else {
|
||||
write!(f, "{}", self.solana_core)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
@@ -280,15 +238,6 @@ pub struct RpcConfirmedTransactionStatusWithSignature {
|
||||
pub memo: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcPerfSample {
|
||||
pub slot: Slot,
|
||||
pub num_transactions: u64,
|
||||
pub num_slots: u64,
|
||||
pub sample_period_secs: u16,
|
||||
}
|
||||
|
||||
impl From<ConfirmedTransactionStatusWithSignature> for RpcConfirmedTransactionStatusWithSignature {
|
||||
fn from(value: ConfirmedTransactionStatusWithSignature) -> Self {
|
||||
let ConfirmedTransactionStatusWithSignature {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "solana-core"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.4.2"
|
||||
version = "1.3.6"
|
||||
documentation = "https://docs.rs/solana"
|
||||
homepage = "https://solana.com/"
|
||||
readme = "../README.md"
|
||||
@@ -14,7 +14,6 @@ edition = "2018"
|
||||
codecov = { repository = "solana-labs/solana", branch = "master", service = "github" }
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.12.3"
|
||||
bincode = "1.3.1"
|
||||
bv = { version = "0.11.1", features = ["serde"] }
|
||||
bs58 = "0.3.1"
|
||||
@@ -25,62 +24,65 @@ crossbeam-channel = "0.4"
|
||||
ed25519-dalek = "=1.0.0-pre.4"
|
||||
fs_extra = "1.1.0"
|
||||
flate2 = "1.0"
|
||||
indexmap = { version = "1.5", features = ["rayon"] }
|
||||
indexmap = "1.4"
|
||||
itertools = "0.9.0"
|
||||
jsonrpc-core = "15.0.0"
|
||||
jsonrpc-core-client = { version = "15.0.0", features = ["ws"] }
|
||||
jsonrpc-derive = "15.0.0"
|
||||
jsonrpc-http-server = "15.0.0"
|
||||
jsonrpc-pubsub = "15.0.0"
|
||||
jsonrpc-ws-server = "15.0.0"
|
||||
jsonrpc-core = "14.2.0"
|
||||
jsonrpc-core-client = { version = "14.2.0", features = ["ws"] }
|
||||
jsonrpc-derive = "14.2.1"
|
||||
jsonrpc-http-server = "14.2.0"
|
||||
jsonrpc-pubsub = "14.2.0"
|
||||
jsonrpc-ws-server = "14.2.0"
|
||||
log = "0.4.8"
|
||||
num_cpus = "1.13.0"
|
||||
num-traits = "0.2"
|
||||
rand = "0.7.0"
|
||||
rand_chacha = "0.2.2"
|
||||
raptorq = "1.4.2"
|
||||
rayon = "1.4.1"
|
||||
rayon = "1.3.1"
|
||||
regex = "1.3.9"
|
||||
serde = "1.0.112"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.56"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "1.4.2" }
|
||||
solana-banks-server = { path = "../banks-server", version = "1.4.2" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.4.2" }
|
||||
solana-client = { path = "../client", version = "1.4.2" }
|
||||
solana-faucet = { path = "../faucet", version = "1.4.2" }
|
||||
solana-frozen-abi = { path = "../frozen-abi", version = "1.4.2" }
|
||||
solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "1.4.2" }
|
||||
solana-ledger = { path = "../ledger", version = "1.4.2" }
|
||||
solana-logger = { path = "../logger", version = "1.4.2" }
|
||||
solana-merkle-tree = { path = "../merkle-tree", version = "1.4.2" }
|
||||
solana-metrics = { path = "../metrics", version = "1.4.2" }
|
||||
solana-measure = { path = "../measure", version = "1.4.2" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.4.2" }
|
||||
solana-perf = { path = "../perf", version = "1.4.2" }
|
||||
solana-runtime = { path = "../runtime", version = "1.4.2" }
|
||||
solana-sdk = { path = "../sdk", version = "1.4.2" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "1.4.2" }
|
||||
solana-storage-bigtable = { path = "../storage-bigtable", version = "1.4.2" }
|
||||
solana-streamer = { path = "../streamer", version = "1.4.2" }
|
||||
solana-sys-tuner = { path = "../sys-tuner", version = "1.4.2" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "1.4.2" }
|
||||
solana-version = { path = "../version", version = "1.4.2" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.4.2" }
|
||||
solana-vote-signer = { path = "../vote-signer", version = "1.4.2" }
|
||||
spl-token-v2-0 = { package = "spl-token", version = "=2.0.8" }
|
||||
solana-account-decoder = { path = "../account-decoder", version = "1.3.6" }
|
||||
solana-banks-server = { path = "../banks-server", version = "1.3.6" }
|
||||
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "1.3.6" }
|
||||
solana-budget-program = { path = "../programs/budget", version = "1.3.6" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.3.6" }
|
||||
solana-client = { path = "../client", version = "1.3.6" }
|
||||
solana-faucet = { path = "../faucet", version = "1.3.6" }
|
||||
solana-genesis-programs = { path = "../genesis-programs", version = "1.3.6" }
|
||||
solana-ledger = { path = "../ledger", version = "1.3.6" }
|
||||
solana-logger = { path = "../logger", version = "1.3.6" }
|
||||
solana-merkle-tree = { path = "../merkle-tree", version = "1.3.6" }
|
||||
solana-metrics = { path = "../metrics", version = "1.3.6" }
|
||||
solana-measure = { path = "../measure", version = "1.3.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.3.6" }
|
||||
solana-perf = { path = "../perf", version = "1.3.6" }
|
||||
solana-runtime = { path = "../runtime", version = "1.3.6" }
|
||||
solana-sdk = { path = "../sdk", version = "1.3.6" }
|
||||
solana-sdk-macro-frozen-abi = { path = "../sdk/macro-frozen-abi", version = "1.3.6" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "1.3.6" }
|
||||
solana-storage-bigtable = { path = "../storage-bigtable", version = "1.3.6" }
|
||||
solana-streamer = { path = "../streamer", version = "1.3.6" }
|
||||
solana-sys-tuner = { path = "../sys-tuner", version = "1.3.6" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "1.3.6" }
|
||||
solana-version = { path = "../version", version = "1.3.6" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.3.6" }
|
||||
solana-vote-signer = { path = "../vote-signer", version = "1.3.6" }
|
||||
spl-token-v2-0 = { package = "spl-token", version = "2.0.3", features = ["skip-no-mangle"] }
|
||||
tempfile = "3.1.0"
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "0.2.22", features = ["full"] }
|
||||
tokio_01 = { version = "0.1", package = "tokio" }
|
||||
tokio_fs_01 = { version = "0.1", package = "tokio-fs" }
|
||||
tokio_io_01 = { version = "0.1", package = "tokio-io" }
|
||||
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.4.2" }
|
||||
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.3.6" }
|
||||
tokio = { version = "0.2.22", features = ["full"] }
|
||||
trees = "0.2.1"
|
||||
|
||||
[dev-dependencies]
|
||||
base64 = "0.12.3"
|
||||
matches = "0.1.6"
|
||||
reqwest = { version = "0.10.8", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
reqwest = { version = "0.10.6", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
serial_test = "0.4.0"
|
||||
serial_test_derive = "0.4.0"
|
||||
systemstat = "0.1.5"
|
||||
@@ -94,12 +96,6 @@ name = "banking_stage"
|
||||
[[bench]]
|
||||
name = "blockstore"
|
||||
|
||||
[[bench]]
|
||||
name = "crds_gossip_pull"
|
||||
|
||||
[[bench]]
|
||||
name = "crds_shards"
|
||||
|
||||
[[bench]]
|
||||
name = "gen_keys"
|
||||
|
||||
|
@@ -20,7 +20,7 @@ use solana_runtime::bank::Bank;
|
||||
use solana_sdk::genesis_config::GenesisConfig;
|
||||
use solana_sdk::hash::Hash;
|
||||
use solana_sdk::message::Message;
|
||||
use solana_sdk::pubkey;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::signature::Keypair;
|
||||
use solana_sdk::signature::Signature;
|
||||
use solana_sdk::signature::Signer;
|
||||
@@ -56,7 +56,7 @@ fn bench_consume_buffered(bencher: &mut Bencher) {
|
||||
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(100_000);
|
||||
let bank = Arc::new(Bank::new(&genesis_config));
|
||||
let ledger_path = get_tmp_ledger_path!();
|
||||
let my_pubkey = pubkey::new_rand();
|
||||
let my_pubkey = Pubkey::new_rand();
|
||||
{
|
||||
let blockstore = Arc::new(
|
||||
Blockstore::open(&ledger_path).expect("Expected to be able to open database ledger"),
|
||||
@@ -94,22 +94,21 @@ fn bench_consume_buffered(bencher: &mut Bencher) {
|
||||
}
|
||||
|
||||
fn make_accounts_txs(txes: usize, mint_keypair: &Keypair, hash: Hash) -> Vec<Transaction> {
|
||||
let to_pubkey = pubkey::new_rand();
|
||||
let to_pubkey = Pubkey::new_rand();
|
||||
let dummy = system_transaction::transfer(mint_keypair, &to_pubkey, 1, hash);
|
||||
(0..txes)
|
||||
.into_par_iter()
|
||||
.map(|_| {
|
||||
let mut new = dummy.clone();
|
||||
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
|
||||
new.message.account_keys[0] = pubkey::new_rand();
|
||||
new.message.account_keys[1] = pubkey::new_rand();
|
||||
new.message.account_keys[0] = Pubkey::new_rand();
|
||||
new.message.account_keys[1] = Pubkey::new_rand();
|
||||
new.signatures = vec![Signature::new(&sig[0..64])];
|
||||
new
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[allow(clippy::same_item_push)]
|
||||
fn make_programs_txs(txes: usize, hash: Hash) -> Vec<Transaction> {
|
||||
let progs = 4;
|
||||
(0..txes)
|
||||
@@ -117,7 +116,7 @@ fn make_programs_txs(txes: usize, hash: Hash) -> Vec<Transaction> {
|
||||
let mut instructions = vec![];
|
||||
let from_key = Keypair::new();
|
||||
for _ in 1..progs {
|
||||
let to_key = pubkey::new_rand();
|
||||
let to_key = Pubkey::new_rand();
|
||||
instructions.push(system_instruction::transfer(&from_key.pubkey(), &to_key, 1));
|
||||
}
|
||||
let message = Message::new(&instructions, Some(&from_key.pubkey()));
|
||||
@@ -295,7 +294,6 @@ fn simulate_process_entries(
|
||||
process_entries(&bank, &[entry], randomize_txs, None, None).unwrap();
|
||||
}
|
||||
|
||||
#[allow(clippy::same_item_push)]
|
||||
fn bench_process_entries(randomize_txs: bool, bencher: &mut Bencher) {
|
||||
// entropy multiplier should be big enough to provide sufficient entropy
|
||||
// but small enough to not take too much time while executing the test.
|
||||
|
@@ -8,7 +8,7 @@ use solana_core::broadcast_stage::{broadcast_shreds, get_broadcast_peers};
|
||||
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::pubkey::Pubkey;
|
||||
use solana_sdk::timing::timestamp;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
@@ -20,7 +20,7 @@ use test::Bencher;
|
||||
#[bench]
|
||||
fn broadcast_shreds_bench(bencher: &mut Bencher) {
|
||||
solana_logger::setup();
|
||||
let leader_pubkey = pubkey::new_rand();
|
||||
let leader_pubkey = Pubkey::new_rand();
|
||||
let leader_info = Node::new_localhost_with_pubkey(&leader_pubkey);
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(leader_info.info);
|
||||
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
|
||||
@@ -30,7 +30,7 @@ fn broadcast_shreds_bench(bencher: &mut Bencher) {
|
||||
let mut stakes = HashMap::new();
|
||||
const NUM_PEERS: usize = 200;
|
||||
for _ in 0..NUM_PEERS {
|
||||
let id = pubkey::new_rand();
|
||||
let id = Pubkey::new_rand();
|
||||
let contact_info = ContactInfo::new_localhost(&id, timestamp());
|
||||
cluster_info.insert_info(contact_info);
|
||||
stakes.insert(id, thread_rng().gen_range(1, NUM_PEERS) as u64);
|
||||
|
@@ -1,36 +0,0 @@
|
||||
#![feature(test)]
|
||||
|
||||
extern crate solana_core;
|
||||
extern crate test;
|
||||
|
||||
use solana_core::consensus::Tower;
|
||||
use solana_runtime::bank::Bank;
|
||||
use solana_runtime::bank_forks::BankForks;
|
||||
use solana_sdk::{
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, Signer},
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use tempfile::TempDir;
|
||||
use test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn bench_save_tower(bench: &mut Bencher) {
|
||||
let dir = TempDir::new().unwrap();
|
||||
let path = dir.path();
|
||||
|
||||
let vote_account_pubkey = &Pubkey::default();
|
||||
let node_keypair = Arc::new(Keypair::new());
|
||||
let heaviest_bank = BankForks::new(Bank::default()).working_bank();
|
||||
let tower = Tower::new(
|
||||
&node_keypair.pubkey(),
|
||||
&vote_account_pubkey,
|
||||
0,
|
||||
&heaviest_bank,
|
||||
&path,
|
||||
);
|
||||
|
||||
bench.iter(move || {
|
||||
tower.save(&node_keypair).unwrap();
|
||||
});
|
||||
}
|
@@ -1,53 +0,0 @@
|
||||
#![feature(test)]
|
||||
|
||||
extern crate test;
|
||||
|
||||
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) {
|
||||
let mut rng = thread_rng();
|
||||
let hashes: Vec<_> = std::iter::repeat_with(|| hash::new_rand(&mut rng))
|
||||
.take(1000)
|
||||
.collect();
|
||||
bencher.iter(|| {
|
||||
hashes
|
||||
.iter()
|
||||
.map(CrdsFilter::hash_as_u64)
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_build_crds_filters(bencher: &mut Bencher) {
|
||||
let thread_pool = ThreadPoolBuilder::new().build().unwrap();
|
||||
let mut rng = thread_rng();
|
||||
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
|
||||
.insert(CrdsValue::new_rand(&mut rng), rng.gen())
|
||||
.is_ok()
|
||||
{
|
||||
num_inserts += 1;
|
||||
}
|
||||
}
|
||||
assert_eq!(num_inserts, 90_000);
|
||||
bencher.iter(|| {
|
||||
let filters = crds_gossip_pull.build_crds_filters(&thread_pool, &crds, MAX_BLOOM_SIZE);
|
||||
assert_eq!(filters.len(), 128);
|
||||
});
|
||||
}
|
@@ -1,69 +0,0 @@
|
||||
#![feature(test)]
|
||||
|
||||
extern crate test;
|
||||
|
||||
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() -> 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 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();
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_crds_shards_find_0(bencher: &mut Bencher) {
|
||||
bench_crds_shards_find(bencher, 100_000, 0);
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_crds_shards_find_1(bencher: &mut Bencher) {
|
||||
bench_crds_shards_find(bencher, 100_000, 1);
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_crds_shards_find_3(bencher: &mut Bencher) {
|
||||
bench_crds_shards_find(bencher, 100_000, 3);
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_crds_shards_find_5(bencher: &mut Bencher) {
|
||||
bench_crds_shards_find(bencher, 100_000, 5);
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_crds_shards_find_7(bencher: &mut Bencher) {
|
||||
bench_crds_shards_find(bencher, 100_000, 7);
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_crds_shards_find_8(bencher: &mut Bencher) {
|
||||
bench_crds_shards_find(bencher, 100_000, 8);
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_crds_shards_find_9(bencher: &mut Bencher) {
|
||||
bench_crds_shards_find(bencher, 100_000, 9);
|
||||
}
|
@@ -14,7 +14,7 @@ use solana_perf::packet::to_packets_chunked;
|
||||
use solana_perf::test_tx::test_tx;
|
||||
use solana_runtime::bank::Bank;
|
||||
use solana_runtime::bank_forks::BankForks;
|
||||
use solana_sdk::pubkey;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::timing::timestamp;
|
||||
use std::net::UdpSocket;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
@@ -27,14 +27,13 @@ use std::time::Duration;
|
||||
use test::Bencher;
|
||||
|
||||
#[bench]
|
||||
#[allow(clippy::same_item_push)]
|
||||
fn bench_retransmitter(bencher: &mut Bencher) {
|
||||
solana_logger::setup();
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(Node::new_localhost().info);
|
||||
const NUM_PEERS: usize = 4;
|
||||
let mut peer_sockets = Vec::new();
|
||||
for _ in 0..NUM_PEERS {
|
||||
let id = pubkey::new_rand();
|
||||
let id = Pubkey::new_rand();
|
||||
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
|
||||
let mut contact_info = ContactInfo::new_localhost(&id, timestamp());
|
||||
contact_info.tvu = socket.local_addr().unwrap();
|
||||
|
@@ -1 +1 @@
|
||||
../frozen-abi/build.rs
|
||||
../sdk/build.rs
|
59
core/src/accounts_background_service.rs
Normal file
59
core/src/accounts_background_service.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
// Service to clean up dead slots in accounts_db
|
||||
//
|
||||
// This can be expensive since we have to walk the append vecs being cleaned up.
|
||||
|
||||
use rand::{thread_rng, Rng};
|
||||
use solana_runtime::bank_forks::BankForks;
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc, RwLock,
|
||||
};
|
||||
use std::thread::{self, sleep, Builder, JoinHandle};
|
||||
use std::time::Duration;
|
||||
|
||||
pub struct AccountsBackgroundService {
|
||||
t_background: JoinHandle<()>,
|
||||
}
|
||||
|
||||
const INTERVAL_MS: u64 = 100;
|
||||
const SHRUNKEN_ACCOUNT_PER_SEC: usize = 250;
|
||||
const SHRUNKEN_ACCOUNT_PER_INTERVAL: usize =
|
||||
SHRUNKEN_ACCOUNT_PER_SEC / (1000 / INTERVAL_MS as usize);
|
||||
const CLEAN_INTERVAL_SLOTS: u64 = 100;
|
||||
|
||||
impl AccountsBackgroundService {
|
||||
pub fn new(bank_forks: Arc<RwLock<BankForks>>, exit: &Arc<AtomicBool>) -> Self {
|
||||
info!("AccountsBackgroundService active");
|
||||
let exit = exit.clone();
|
||||
let mut consumed_budget = 0;
|
||||
let mut last_cleaned_slot = 0;
|
||||
let t_background = Builder::new()
|
||||
.name("solana-accounts-background".to_string())
|
||||
.spawn(move || loop {
|
||||
if exit.load(Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
let bank = bank_forks.read().unwrap().root_bank().clone();
|
||||
|
||||
bank.process_dead_slots();
|
||||
|
||||
consumed_budget = bank
|
||||
.process_stale_slot_with_budget(consumed_budget, SHRUNKEN_ACCOUNT_PER_INTERVAL);
|
||||
|
||||
if bank.block_height() - last_cleaned_slot
|
||||
> (CLEAN_INTERVAL_SLOTS + thread_rng().gen_range(0, 10))
|
||||
{
|
||||
bank.clean_accounts();
|
||||
last_cleaned_slot = bank.block_height();
|
||||
}
|
||||
|
||||
sleep(Duration::from_millis(INTERVAL_MS));
|
||||
})
|
||||
.unwrap();
|
||||
Self { t_background }
|
||||
}
|
||||
|
||||
pub fn join(self) -> thread::Result<()> {
|
||||
self.t_background.join()
|
||||
}
|
||||
}
|
@@ -204,7 +204,6 @@ mod tests {
|
||||
{
|
||||
let message = make_accounts_hashes_message(&validator1, vec![(0, hash1)]).unwrap();
|
||||
cluster_info.push_message(message);
|
||||
cluster_info.flush_push_queue();
|
||||
}
|
||||
slot_to_hash.insert(0, hash2);
|
||||
trusted_validators.insert(validator1.pubkey());
|
||||
@@ -255,7 +254,6 @@ mod tests {
|
||||
100,
|
||||
);
|
||||
}
|
||||
cluster_info.flush_push_queue();
|
||||
let cluster_hashes = cluster_info
|
||||
.get_accounts_hash_for_node(&keypair.pubkey(), |c| c.clone())
|
||||
.unwrap();
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user