Compare commits

..

34 Commits

Author SHA1 Message Date
mergify[bot]
69e53ec92a Gossip log (#11555) (#11562)
Co-authored-by: Carl <carl@solana.com>
(cherry picked from commit 1b238dd63e)

Co-authored-by: carllin <wumu727@gmail.com>
2020-08-11 22:55:18 +00:00
mergify[bot]
b8ac76066c Move cluster slots update to separate thread (#11523) (#11558)
* Add cluster_slots_service

Co-authored-by: Carl <carl@solana.com>
(cherry picked from commit 7ef50a9352)

Co-authored-by: carllin <wumu727@gmail.com>
2020-08-11 21:24:34 +00:00
mergify[bot]
1a1d7744bd Add getTokenLargestAccounts to docs (#11560) (#11564)
(cherry picked from commit 697a0e2947)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-11 21:21:35 +00:00
mergify[bot]
61af485732 filter out old gossip pull requests (#11448) (#11553)
* init

* builds

* stats

* revert

* tests

* clippy

* add some jitter

* shorter jitter timer

* update

* fixup! update

* use saturating_sub

* fix filters

(cherry picked from commit 713851b68d)

Co-authored-by: anatoly yakovenko <anatoly@solana.com>
2020-08-11 19:42:32 +00:00
mergify[bot]
6a60d7bf8e Adapt RpcClient to recent token method changes (#11519) (#11548)
* Avoid skip_serializing_if since that breaks deserialization

* Adapt RpcClient to recent token method changes

(cherry picked from commit 17645ee20c)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-08-11 17:31:57 +00:00
mergify[bot]
53e917b54f Fix bad rent in Bank::deposit as if since epoch 0 (#10468) (#11539)
* Fix bad rent in Bank::deposit as if since epoch 0

* Remove redundant predicate

* Rename

* Start to add tests with some cleanup

* Forgot to add refactor code...

* Enchance test

* Really fix rent timing in deposit with robust test

* Simplify new behavior by disabling rent altogether

(cherry picked from commit 6c242f3fec)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2020-08-11 16:20:06 +00:00
mergify[bot]
4d49c99ac9 Fix simulateTransaction JSON-RPC docs (#11533) (#11535)
(cherry picked from commit f12fc66a69)

Co-authored-by: Justin Starry <justin@solana.com>
2020-08-11 10:26:20 +00:00
mergify[bot]
83597a5ce1 Fix solana CLI deploy (#11520) (#11530)
* Refresh blockhash for program writes and finalize transactions

* Refactor to use current api, eliminating an rpc call

* Review comment

(cherry picked from commit c0d6761f63)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-11 09:52:11 +00:00
mergify[bot]
1589a41178 Add config param to specify offset/length for single and program account info (bp #11515) (#11518)
* Add config param to specify offset/length for single and program account info (#11515)

* Add config param to specify dataSlice for account info and program accounts

* Use match instead of if

(cherry picked from commit 88ca04dbdb)

# Conflicts:
#	cli/src/cli.rs

* Fix conflicts

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2020-08-11 00:03:18 +00:00
mergify[bot]
23a381b995 Fix parsing of spl-token Mint (#11512) (#11514)
* Add failing test

* Fix jsonParsed mint

(cherry picked from commit da210ddd51)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-10 21:51:20 +00:00
mergify[bot]
eb7ac42b2e Return account data size with parsed accounts (#11506) (#11511)
(cherry picked from commit 1925b0bd0b)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-10 20:09:13 +00:00
mergify[bot]
88cf5e79f5 Unified signature for create_program_address (#11460) (#11509)
(cherry picked from commit 140b2392f6)

Co-authored-by: Jack May <jack@solana.com>
2020-08-10 18:31:51 +00:00
mergify[bot]
520453e1f3 Blockstore address signatures: handle slots that cross primary indexes, and refactor get_confirmed_signatures_for_address2 (#11497) (#11508)
* Freeze address-signature index in the middle of slot to show failure case

* Secondary filter on signature

* Use AddressSignatures iterator instead of manually decrementing slots

* Remove unused method

* Add metrics

* Add transaction-status-index doccumentation

(cherry picked from commit de5fb3ba0e)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-10 17:43:00 +00:00
mergify[bot]
27815555a1 Fallback to base64 account encoding if json parse fails (#11483) (#11488)
* Fallback to base64 account encoding if json parse fails

* Remove default binary conversion

(cherry picked from commit ebc45bd73f)

Co-authored-by: Justin Starry <justin@solana.com>
2020-08-09 16:15:09 +00:00
mergify[bot]
3e483314b6 Decode native-program and sysvar accounts (bp #11463) (#11485)
* Decode native-program and sysvar accounts (#11463)

* Pass pubkey in to account-decoder for sysvars

* Decode sysvar accounts

* Decode config accounts; move validator-info lower

* Decode stake accounts

* Review comments

* Stringify any account lamports and epochs that can be set to u64::MAX

(cherry picked from commit a9f76862fb)

# Conflicts:
#	Cargo.lock
#	account-decoder/Cargo.toml

* Fix conflicts

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2020-08-09 09:18:17 +00:00
mergify[bot]
8a67504578 Add Binary64 option for account data (#11474) (#11481)
* Add Binary64 option for account data

* Decode into binary64

* Reword docs

(cherry picked from commit 068d23f298)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-08-09 06:50:24 +00:00
mergify[bot]
a1b238280b Return delegated amount as UiTokenAmount (#11475) (#11477)
* Return delegated amount as UiTokenAmount

* Omit delegate and delegatedAmount when none

(cherry picked from commit 88d8d3d02a)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-09 00:01:01 +00:00
Trent Nelson
f9e07f575e Bump version to v1.3.2 2020-08-08 06:26:20 +00:00
Trent Nelson
c8bad57455 Bump version to v1.3.1 2020-08-07 19:21:41 -06:00
mergify[bot]
71654c0457 Fix cbindgen compatibility (#11455) (#11459)
(cherry picked from commit 5a7e99f283)

Co-authored-by: Jack May <jack@solana.com>
2020-08-07 23:44:49 +00:00
mergify[bot]
f9d6fb48a4 Send votes from banking stage to vote listener (#11434) (#11454)
*  Send votes from banking stage to vote listener

Co-authored-by: Carl <carl@solana.com>
(cherry picked from commit 7e25130529)

Co-authored-by: carllin <wumu727@gmail.com>
2020-08-07 19:45:40 +00:00
mergify[bot]
fa9aa0a1d7 Token Accounts: return ui_amount, decimals with decoded account (#11407) (#11453)
* Return ui_amount, decimals from token client methods

* Return ui_amount, decimals in RPC jsonParsed token accounts

* Fixup docs

* Return ui_amount, decimals in pubsub jsonParsed token accounts

* Remove unnecessary duplicate struct

* StringAmount rename

(cherry picked from commit b7c2681903)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-07 19:05:35 +00:00
mergify[bot]
9758ebfc99 Only run web3.js/explorer CI when targeting master branch (#11440)
(cherry picked from commit a3165c6a61)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-08-07 06:24:42 +00:00
mergify[bot]
8be23a2bb2 Add address-based lower bound to get_confirmed_signatures_for_address2 loop (#11426) (#11432)
(cherry picked from commit 5530ee4c95)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-07 00:06:44 +00:00
mergify[bot]
4ff9a6910d Fix blockstore empty panic (#11423) (#11430)
* Add panicking test

* Add failing test: fresh transaction-status column shouldn't point at valid root 0

* Prevent transaction status match outside of primary-index bounds

* Initialize transaction-status and address-signature primer entries with Slot::MAX

* Revert "Add failing test: fresh transaction-status column shouldn't point at valid root 0"

This reverts commit cbad2a9fae.

* Revert "Initialize transaction-status and address-signature primer entries with Slot::MAX"

This reverts commit ffaeac0669.

(cherry picked from commit 1061b50665)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2020-08-06 23:39:01 +00:00
mergify[bot]
fd192e3641 Link fix (#11368) (#11425)
* fixes logo

* cleans up homepage on docs

* adds icon files and tightens margins

* cleans up sidenav, adds top nav items

* fixes a link

* removes icon files

Co-authored-by: Dan Albert <dan@solana.com>
(cherry picked from commit 14dcaaee6c)

Co-authored-by: Raj Gokal <rajivgokal@gmail.com>
2020-08-06 19:37:04 +00:00
mergify[bot]
a8de467ef8 Realloc not supported (#11418)
(cherry picked from commit bc4c5c5a97)

Co-authored-by: Jack May <jack@solana.com>
2020-08-06 16:24:14 +00:00
Michael Vines
1a186beb5c Update lib.rs
(cherry picked from commit 5a63c9d535)
2020-08-06 07:58:05 -07:00
Michael Vines
66a21ed65e Enable cross program support in mainnet-beta epoch 63
(cherry picked from commit c9b1d08218)
2020-08-06 07:58:05 -07:00
mergify[bot]
1a42a40492 RPC: Plug getConfirmedSignaturesForAddress2 into bigtable storage (bp #11395) (#11406)
* Plug getConfirmedSignaturesForAddress2 into bigtable storage

(cherry picked from commit 4222932e08)

* Upgrade help description

(cherry picked from commit 9abb7db5f8)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-08-06 07:14:46 +00:00
mergify[bot]
5841e4d665 Long-term ledger storage with BigTable (bp #11222) (#11392)
* ledger-storage-bigtable boilerplate

(cherry picked from commit 9d2293bb32)

* $ wget https://pki.goog/roots.pem -O pki-goog-roots.pem

(cherry picked from commit 1617a025ce)

* Add access_token module

(cherry picked from commit 59d266a111)

* Add root_ca_certificate

(cherry picked from commit faa016e4b7)

* Add build-proto

(cherry picked from commit c31e1f5bf0)

* UiTransactionEncoding is now copy

(cherry picked from commit 494968be66)

* Increase timeout

(cherry picked from commit 57dfebc5ba)

* Add build-proto/build.sh output

(cherry picked from commit 54dae6ba2c)

* Supress doctest errors

(cherry picked from commit 019c75797d)

* Add compression

(cherry picked from commit 243e05d59f)

* Add bigtable

(cherry picked from commit 6e0353965a)

* Add configuration info

(cherry picked from commit 98cca1e774)

* Add ledger-tool bigtable subcommands

(cherry picked from commit f9049d6ee4)

# Conflicts:
#	ledger-tool/Cargo.toml

* Make room for tokio 0.2

(cherry picked from commit b876fb84ba)

# Conflicts:
#	core/Cargo.toml

* Setup a tokio 0.2 runtime for RPC usage

(cherry picked from commit 0e02740565)

# Conflicts:
#	core/Cargo.toml

* Plumb Bigtable ledger storage into the RPC subsystem

(cherry picked from commit dfae9a9864)

# Conflicts:
#	core/Cargo.toml

* Add RPC transaction history design

(cherry picked from commit e56ea138c7)

* Simplify access token refreshing

(cherry picked from commit 1f7af14386)

* Report block status more frequently

(cherry picked from commit 22c46ebf96)

* after -> before

(cherry picked from commit 227ea934ff)

* Rebase

* Cargo.lock

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-08-06 04:06:44 +00:00
mergify[bot]
6542a04521 Mark token-specific rpcs as unstable (#11402)
(cherry picked from commit 7430896c79)

Co-authored-by: Tyera Eulberg <tyera@solana.com>
2020-08-06 04:00:18 +00:00
mergify[bot]
5c27009758 Force program address off the curve (#11323) (#11398)
(cherry picked from commit 03263c850a)

Co-authored-by: Jack May <jack@solana.com>
2020-08-06 01:00:42 +00:00
mergify[bot]
888f3522d8 Add getConfirmedSignaturesForAddress2 RPC method (bp #11259) (#11394)
* Add getConfirmedSignaturesForAddress2 RPC method

(cherry picked from commit 1b2276520b)

* Reimplement transaction-history command with getConfirmedSignaturesForAddress2

(cherry picked from commit 087fd32ce3)

* Rework get_confirmed_signatures_for_address2

(cherry picked from commit a11f137810)

* Rename startAfter to before

(cherry picked from commit 02c0981ecf)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-08-05 22:47:50 +00:00
800 changed files with 26501 additions and 69551 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -1,7 +1,12 @@
{ {
"_public_key": "ae29f4f7ad2fc92de70d470e411c8426d5d48db8817c9e3dae574b122192335f", "_public_key": "ae29f4f7ad2fc92de70d470e411c8426d5d48db8817c9e3dae574b122192335f",
"_comment": "These credentials are encrypted and pose no risk",
"environment": { "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]"
} }
} }

View File

@@ -3,19 +3,16 @@
# #
# Save target/ for the next CI build on this machine # Save target/ for the next CI build on this machine
# #
if [[ -z $CARGO_TARGET_CACHE ]]; then (
echo "+++ CARGO_TARGET_CACHE not defined" # pre-command should have defined it set -x
else d=$HOME/cargo-target-cache/"$BUILDKITE_LABEL"
( mkdir -p "$d"
set -x set -x
mkdir -p "$CARGO_TARGET_CACHE" rsync -a --delete --link-dest="$PWD" target "$d"
set -x du -hs "$d"
rsync -a --delete --link-dest="$PWD" target "$CARGO_TARGET_CACHE" read -r cacheSizeInGB _ < <(du -s --block-size=1800000000 "$d")
du -hs "$CARGO_TARGET_CACHE" echo "--- ${cacheSizeInGB}GB: $d"
read -r cacheSizeInGB _ < <(du -s --block-size=1800000000 "$CARGO_TARGET_CACHE") )
echo "--- ${cacheSizeInGB}GB: $CARGO_TARGET_CACHE"
)
fi
# #
# Add job_stats data point # Add job_stats data point

View File

@@ -11,29 +11,23 @@ export PS4="++"
# #
# Restore target/ from the previous CI build on this machine # Restore target/ from the previous CI build on this machine
# #
eval "$(ci/channel-info.sh)"
export CARGO_TARGET_CACHE=$HOME/cargo-target-cache/"$CHANNEL"-"$BUILDKITE_LABEL"
( (
set -x set -x
d=$HOME/cargo-target-cache/"$BUILDKITE_LABEL"
MAX_CACHE_SIZE=18 # gigabytes MAX_CACHE_SIZE=18 # gigabytes
if [[ -d $CARGO_TARGET_CACHE ]]; then if [[ -d $d ]]; then
du -hs "$CARGO_TARGET_CACHE" du -hs "$d"
read -r cacheSizeInGB _ < <(du -s --block-size=1800000000 "$CARGO_TARGET_CACHE") read -r cacheSizeInGB _ < <(du -s --block-size=1800000000 "$d")
echo "--- ${cacheSizeInGB}GB: $CARGO_TARGET_CACHE" echo "--- ${cacheSizeInGB}GB: $d"
if [[ $cacheSizeInGB -gt $MAX_CACHE_SIZE ]]; then if [[ $cacheSizeInGB -gt $MAX_CACHE_SIZE ]]; then
echo "--- $CARGO_TARGET_CACHE is too large, removing it" echo "--- $d is too large, removing it"
rm -rf "$CARGO_TARGET_CACHE" rm -rf "$d"
fi fi
else else
echo "--- $CARGO_TARGET_CACHE not present" echo "--- $d not present"
fi fi
mkdir -p "$CARGO_TARGET_CACHE"/target mkdir -p "$d"/target
rsync -a --delete --link-dest="$CARGO_TARGET_CACHE" "$CARGO_TARGET_CACHE"/target . rsync -a --delete --link-dest="$d" "$d"/target .
# Don't reuse BPF target build artifacts due to incremental build issues with
# `std:
# "found possibly newer version of crate `std` which `xyz` depends on
rm -rf target/bpfel-unknown-unknown
) )

46
.github/stale.yml vendored
View File

@@ -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 # Issues with these labels will never be considered stale
exemptLabels: exemptLabels:
- security - security
@@ -6,34 +14,12 @@ exemptLabels:
# Label to use when marking a pull request as stale # Label to use when marking a pull request as stale
staleLabel: stale staleLabel: stale
pulls: # Comment to post when marking a pull request as stale. Set to `false` to disable
# Number of days of inactivity before a pull request becomes stale markComment: >
daysUntilStale: 7 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 # Comment to post when closing a stale pull request. Set to `false` to disable
daysUntilClose: 7 closeComment: >
# Comment to post when marking a pull request as stale. Set to `false` to disable This stale pull request has been automatically closed.
markComment: > Thank you for your contributions.
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.

View File

@@ -50,6 +50,14 @@ pull_request_rules:
label: label:
add: add:
- automerge - automerge
- name: v1.1 backport
conditions:
- label=v1.1
actions:
backport:
ignore_conflicts: true
branches:
- v1.1
- name: v1.2 backport - name: v1.2 backport
conditions: conditions:
- label=v1.2 - label=v1.2
@@ -66,11 +74,3 @@ pull_request_rules:
ignore_conflicts: true ignore_conflicts: true
branches: branches:
- v1.3 - v1.3
- name: v1.4 backport
conditions:
- label=v1.4
actions:
backport:
ignore_conflicts: true
branches:
- v1.4

View File

@@ -34,8 +34,6 @@ jobs:
- stable - stable
install: install:
- source ci/rust-version.sh - source ci/rust-version.sh
- PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH"
- readlink -f .
script: script:
- source ci/env.sh - source ci/env.sh
- ci/publish-tarball.sh - ci/publish-tarball.sh
@@ -126,8 +124,6 @@ jobs:
- ~/.npm - ~/.npm
before_install: before_install:
- source ci/env.sh
- .travis/channel_restriction.sh edge beta || travis_terminate 0
- .travis/affects.sh docs/ .travis || travis_terminate 0 - .travis/affects.sh docs/ .travis || travis_terminate 0
- cd docs/ - cd docs/
- source .travis/before_install.sh - source .travis/before_install.sh

View File

@@ -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

View File

@@ -232,7 +232,7 @@ confused with 3-letter acronyms.
Solana's architecture is described by docs generated from markdown files in Solana's architecture is described by docs generated from markdown files in
the `docs/src/` directory, maintained by an *editor* (currently @garious). To the `docs/src/` directory, maintained by an *editor* (currently @garious). To
add a design proposal, you'll need to include it in the 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: 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 1. Propose a design by creating a PR that adds a markdown document to the

1789
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,21 +5,17 @@ members = [
"bench-tps", "bench-tps",
"accounts-bench", "accounts-bench",
"banking-bench", "banking-bench",
"banks-client",
"banks-interface",
"banks-server",
"clap-utils", "clap-utils",
"cli-config", "cli-config",
"cli-output",
"client", "client",
"core", "core",
"dos", "dos",
"download-utils", "download-utils",
"faucet", "faucet",
"frozen-abi",
"perf", "perf",
"validator", "validator",
"genesis", "genesis",
"genesis-programs",
"gossip", "gossip",
"install", "install",
"keygen", "keygen",
@@ -31,15 +27,12 @@ members = [
"merkle-tree", "merkle-tree",
"stake-o-matic", "stake-o-matic",
"storage-bigtable", "storage-bigtable",
"storage-proto",
"streamer", "streamer",
"measure", "measure",
"metrics", "metrics",
"net-shaper", "net-shaper",
"notifier", "notifier",
"poh-bench", "poh-bench",
"program-test",
"programs/secp256k1",
"programs/bpf_loader", "programs/bpf_loader",
"programs/budget", "programs/budget",
"programs/config", "programs/config",
@@ -54,8 +47,6 @@ members = [
"ramp-tps", "ramp-tps",
"runtime", "runtime",
"sdk", "sdk",
"sdk/cargo-build-bpf",
"sdk/cargo-test-bpf",
"scripts", "scripts",
"stake-accounts", "stake-accounts",
"stake-monitor", "stake-monitor",

View File

@@ -19,7 +19,7 @@ $ source $HOME/.cargo/env
$ rustup component add rustfmt $ 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 ```bash
$ rustup update $ rustup update
@@ -59,11 +59,10 @@ $ cargo test
``` ```
### Starting a local testnet ### Starting a local testnet
Start your own testnet locally, instructions are in the [online docs](https://docs.solana.com/cluster/bench-tps). Start your own testnet locally, instructions are in the [online docs](https://docs.solana.com/bench-tps).
### Accessing the remote development cluster ### Accessing the remote testnet
* `devnet` - stable public cluster for development accessible via * `testnet` - public stable testnet accessible via devnet.solana.com. Runs 24/7
devnet.solana.com. Runs 24/7. Learn more about the [public clusters](https://docs.solana.com/clusters)
# Benchmarking # Benchmarking

View File

@@ -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. 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. 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. 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
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.
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. Click "Save Draft", then confirm the release notes look good and the tag name and branch are correct. 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 then need to be be removed once the the Linux binary artifacts appear later.
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" when ready.
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.
### Update release branch with the next patch version ### 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. 1. Open a PR against origin/vX.Y and then merge the PR after passing CI.
### Prepare for the next release ### 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. 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. Also, point the branch field to the same branch and mark the relese as **"This is a pre-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 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. 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. [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 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/ See the documentation at https://github.com/solana-labs/cluster-ops/

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "solana-account-decoder" name = "solana-account-decoder"
version = "1.4.7" version = "1.3.2"
description = "Solana account decoder" description = "Solana account decoder"
authors = ["Solana Maintainers <maintainers@solana.foundation>"] authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
@@ -15,14 +15,14 @@ bs58 = "0.3.1"
bv = "0.11.1" bv = "0.11.1"
Inflector = "0.11.4" Inflector = "0.11.4"
lazy_static = "1.4.0" lazy_static = "1.4.0"
solana-config-program = { path = "../programs/config", version = "1.3.2" }
solana-sdk = { path = "../sdk", version = "1.3.2" }
solana-stake-program = { path = "../programs/stake", version = "1.3.2" }
solana-vote-program = { path = "../programs/vote", version = "1.3.2" }
spl-token-v1-0 = { package = "spl-token", version = "1.0.6", features = ["skip-no-mangle"] }
serde = "1.0.112" serde = "1.0.112"
serde_derive = "1.0.103" serde_derive = "1.0.103"
serde_json = "1.0.56" serde_json = "1.0.56"
solana-config-program = { path = "../programs/config", version = "1.4.7" }
solana-sdk = { path = "../sdk", version = "1.4.7" }
solana-stake-program = { path = "../programs/stake", version = "1.4.7" }
solana-vote-program = { path = "../programs/vote", version = "1.4.7" }
spl-token-v2-0 = { package = "spl-token", version = "=3.0.0", features = ["no-entrypoint"] }
thiserror = "1.0" thiserror = "1.0"
[package.metadata.docs.rs] [package.metadata.docs.rs]

View File

@@ -32,18 +32,17 @@ pub struct UiAccount {
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", untagged)] #[serde(rename_all = "camelCase", untagged)]
pub enum UiAccountData { pub enum UiAccountData {
LegacyBinary(String), // Legacy. Retained for RPC backwards compatibility Binary(String),
Json(ParsedAccount), Json(ParsedAccount),
Binary(String, UiAccountEncoding), Binary64(String),
} }
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub enum UiAccountEncoding { pub enum UiAccountEncoding {
Binary, // Legacy. Retained for RPC backwards compatibility Binary,
Base58,
Base64,
JsonParsed, JsonParsed,
Binary64,
} }
impl UiAccount { impl UiAccount {
@@ -55,24 +54,20 @@ impl UiAccount {
data_slice_config: Option<UiDataSliceConfig>, data_slice_config: Option<UiDataSliceConfig>,
) -> Self { ) -> Self {
let data = match encoding { let data = match encoding {
UiAccountEncoding::Binary => UiAccountData::LegacyBinary( UiAccountEncoding::Binary => UiAccountData::Binary(
bs58::encode(slice_data(&account.data, data_slice_config)).into_string(), bs58::encode(slice_data(&account.data, data_slice_config)).into_string(),
), ),
UiAccountEncoding::Base58 => UiAccountData::Binary( UiAccountEncoding::Binary64 => UiAccountData::Binary64(base64::encode(slice_data(
bs58::encode(slice_data(&account.data, data_slice_config)).into_string(), &account.data,
encoding, data_slice_config,
), ))),
UiAccountEncoding::Base64 => UiAccountData::Binary(
base64::encode(slice_data(&account.data, data_slice_config)),
encoding,
),
UiAccountEncoding::JsonParsed => { UiAccountEncoding::JsonParsed => {
if let Ok(parsed_data) = if let Ok(parsed_data) =
parse_account_data(pubkey, &account.owner, &account.data, additional_data) parse_account_data(pubkey, &account.owner, &account.data, additional_data)
{ {
UiAccountData::Json(parsed_data) UiAccountData::Json(parsed_data)
} else { } else {
UiAccountData::Binary(base64::encode(&account.data), UiAccountEncoding::Base64) UiAccountData::Binary64(base64::encode(&account.data))
} }
} }
}; };
@@ -88,12 +83,8 @@ impl UiAccount {
pub fn decode(&self) -> Option<Account> { pub fn decode(&self) -> Option<Account> {
let data = match &self.data { let data = match &self.data {
UiAccountData::Json(_) => None, UiAccountData::Json(_) => None,
UiAccountData::LegacyBinary(blob) => bs58::decode(blob).into_vec().ok(), UiAccountData::Binary(blob) => bs58::decode(blob).into_vec().ok(),
UiAccountData::Binary(blob, encoding) => match encoding { UiAccountData::Binary64(blob) => base64::decode(blob).ok(),
UiAccountEncoding::Base58 => bs58::decode(blob).into_vec().ok(),
UiAccountEncoding::Base64 => base64::decode(blob).ok(),
UiAccountEncoding::Binary | UiAccountEncoding::JsonParsed => None,
},
}?; }?;
Some(Account { Some(Account {
lamports: self.lamports, lamports: self.lamports,

View File

@@ -3,7 +3,7 @@ use crate::{
parse_nonce::parse_nonce, parse_nonce::parse_nonce,
parse_stake::parse_stake, parse_stake::parse_stake,
parse_sysvar::parse_sysvar, parse_sysvar::parse_sysvar,
parse_token::{parse_token, spl_token_id_v2_0}, parse_token::{parse_token, spl_token_id_v1_0},
parse_vote::parse_vote, parse_vote::parse_vote,
}; };
use inflector::Inflector; use inflector::Inflector;
@@ -17,7 +17,7 @@ lazy_static! {
static ref STAKE_PROGRAM_ID: Pubkey = solana_stake_program::id(); static ref STAKE_PROGRAM_ID: Pubkey = solana_stake_program::id();
static ref SYSTEM_PROGRAM_ID: Pubkey = system_program::id(); static ref SYSTEM_PROGRAM_ID: Pubkey = system_program::id();
static ref SYSVAR_PROGRAM_ID: Pubkey = sysvar::id(); static ref SYSVAR_PROGRAM_ID: Pubkey = sysvar::id();
static ref TOKEN_PROGRAM_ID: Pubkey = spl_token_id_v2_0(); static ref TOKEN_PROGRAM_ID: Pubkey = spl_token_id_v1_0();
static ref VOTE_PROGRAM_ID: Pubkey = solana_vote_program::id(); static ref VOTE_PROGRAM_ID: Pubkey = solana_vote_program::id();
pub static ref PARSABLE_PROGRAM_IDS: HashMap<Pubkey, ParsableAccount> = { pub static ref PARSABLE_PROGRAM_IDS: HashMap<Pubkey, ParsableAccount> = {
let mut m = HashMap::new(); let mut m = HashMap::new();
@@ -111,8 +111,8 @@ mod test {
#[test] #[test]
fn test_parse_account_data() { fn test_parse_account_data() {
let account_pubkey = solana_sdk::pubkey::new_rand(); let account_pubkey = Pubkey::new_rand();
let other_program = solana_sdk::pubkey::new_rand(); let other_program = Pubkey::new_rand();
let data = vec![0; 4]; let data = vec![0; 4];
assert!(parse_account_data(&account_pubkey, &other_program, &data, None).is_err()); assert!(parse_account_data(&account_pubkey, &other_program, &data, None).is_err());

View File

@@ -117,7 +117,7 @@ mod test {
})) }))
.unwrap(), .unwrap(),
}; };
let info_pubkey = solana_sdk::pubkey::new_rand(); let info_pubkey = Pubkey::new_rand();
let validator_info_config_account = create_config_account( let validator_info_config_account = create_config_account(
vec![(validator_info::id(), false), (info_pubkey, true)], vec![(validator_info::id(), false), (info_pubkey, true)],
&validator_info, &validator_info,

View File

@@ -134,6 +134,7 @@ impl From<Delegation> for UiDelegation {
mod test { mod test {
use super::*; use super::*;
use bincode::serialize; use bincode::serialize;
use solana_sdk::pubkey::Pubkey;
#[test] #[test]
fn test_parse_stake() { fn test_parse_stake() {
@@ -144,8 +145,8 @@ mod test {
StakeAccountType::Uninitialized StakeAccountType::Uninitialized
); );
let pubkey = solana_sdk::pubkey::new_rand(); let pubkey = Pubkey::new_rand();
let custodian = solana_sdk::pubkey::new_rand(); let custodian = Pubkey::new_rand();
let authorized = Authorized::auto(&pubkey); let authorized = Authorized::auto(&pubkey);
let lockup = Lockup { let lockup = Lockup {
unix_timestamp: 0, unix_timestamp: 0,
@@ -179,7 +180,7 @@ mod test {
}) })
); );
let voter_pubkey = solana_sdk::pubkey::new_rand(); let voter_pubkey = Pubkey::new_rand();
let stake = Stake { let stake = Stake {
delegation: Delegation { delegation: Delegation {
voter_pubkey, voter_pubkey,

View File

@@ -212,14 +212,15 @@ pub struct UiStakeHistoryEntry {
mod test { mod test {
use super::*; use super::*;
use solana_sdk::{ use solana_sdk::{
account::create_account, fee_calculator::FeeCalculator, hash::Hash, fee_calculator::FeeCalculator,
sysvar::recent_blockhashes::IterItem, hash::Hash,
sysvar::{recent_blockhashes::IterItem, Sysvar},
}; };
use std::iter::FromIterator; use std::iter::FromIterator;
#[test] #[test]
fn test_parse_sysvars() { fn test_parse_sysvars() {
let clock_sysvar = create_account(&Clock::default(), 1); let clock_sysvar = Clock::default().create_account(1);
assert_eq!( assert_eq!(
parse_sysvar(&clock_sysvar.data, &sysvar::clock::id()).unwrap(), parse_sysvar(&clock_sysvar.data, &sysvar::clock::id()).unwrap(),
SysvarAccountType::Clock(UiClock::default()), SysvarAccountType::Clock(UiClock::default()),
@@ -232,13 +233,13 @@ mod test {
first_normal_epoch: 1, first_normal_epoch: 1,
first_normal_slot: 12, first_normal_slot: 12,
}; };
let epoch_schedule_sysvar = create_account(&epoch_schedule, 1); let epoch_schedule_sysvar = epoch_schedule.create_account(1);
assert_eq!( assert_eq!(
parse_sysvar(&epoch_schedule_sysvar.data, &sysvar::epoch_schedule::id()).unwrap(), parse_sysvar(&epoch_schedule_sysvar.data, &sysvar::epoch_schedule::id()).unwrap(),
SysvarAccountType::EpochSchedule(epoch_schedule), SysvarAccountType::EpochSchedule(epoch_schedule),
); );
let fees_sysvar = create_account(&Fees::default(), 1); let fees_sysvar = Fees::default().create_account(1);
assert_eq!( assert_eq!(
parse_sysvar(&fees_sysvar.data, &sysvar::fees::id()).unwrap(), parse_sysvar(&fees_sysvar.data, &sysvar::fees::id()).unwrap(),
SysvarAccountType::Fees(UiFees::default()), SysvarAccountType::Fees(UiFees::default()),
@@ -250,7 +251,7 @@ mod test {
}; };
let recent_blockhashes = let recent_blockhashes =
RecentBlockhashes::from_iter(vec![IterItem(0, &hash, &fee_calculator)].into_iter()); RecentBlockhashes::from_iter(vec![IterItem(0, &hash, &fee_calculator)].into_iter());
let recent_blockhashes_sysvar = create_account(&recent_blockhashes, 1); let recent_blockhashes_sysvar = recent_blockhashes.create_account(1);
assert_eq!( assert_eq!(
parse_sysvar( parse_sysvar(
&recent_blockhashes_sysvar.data, &recent_blockhashes_sysvar.data,
@@ -268,13 +269,13 @@ mod test {
exemption_threshold: 2.0, exemption_threshold: 2.0,
burn_percent: 5, burn_percent: 5,
}; };
let rent_sysvar = create_account(&rent, 1); let rent_sysvar = rent.create_account(1);
assert_eq!( assert_eq!(
parse_sysvar(&rent_sysvar.data, &sysvar::rent::id()).unwrap(), parse_sysvar(&rent_sysvar.data, &sysvar::rent::id()).unwrap(),
SysvarAccountType::Rent(rent.into()), SysvarAccountType::Rent(rent.into()),
); );
let rewards_sysvar = create_account(&Rewards::default(), 1); let rewards_sysvar = Rewards::default().create_account(1);
assert_eq!( assert_eq!(
parse_sysvar(&rewards_sysvar.data, &sysvar::rewards::id()).unwrap(), parse_sysvar(&rewards_sysvar.data, &sysvar::rewards::id()).unwrap(),
SysvarAccountType::Rewards(UiRewards::default()), SysvarAccountType::Rewards(UiRewards::default()),
@@ -282,7 +283,7 @@ mod test {
let mut slot_hashes = SlotHashes::default(); let mut slot_hashes = SlotHashes::default();
slot_hashes.add(1, hash); slot_hashes.add(1, hash);
let slot_hashes_sysvar = create_account(&slot_hashes, 1); let slot_hashes_sysvar = slot_hashes.create_account(1);
assert_eq!( assert_eq!(
parse_sysvar(&slot_hashes_sysvar.data, &sysvar::slot_hashes::id()).unwrap(), parse_sysvar(&slot_hashes_sysvar.data, &sysvar::slot_hashes::id()).unwrap(),
SysvarAccountType::SlotHashes(vec![UiSlotHashEntry { SysvarAccountType::SlotHashes(vec![UiSlotHashEntry {
@@ -293,7 +294,7 @@ mod test {
let mut slot_history = SlotHistory::default(); let mut slot_history = SlotHistory::default();
slot_history.add(42); slot_history.add(42);
let slot_history_sysvar = create_account(&slot_history, 1); let slot_history_sysvar = slot_history.create_account(1);
assert_eq!( assert_eq!(
parse_sysvar(&slot_history_sysvar.data, &sysvar::slot_history::id()).unwrap(), parse_sysvar(&slot_history_sysvar.data, &sysvar::slot_history::id()).unwrap(),
SysvarAccountType::SlotHistory(UiSlotHistory { SysvarAccountType::SlotHistory(UiSlotHistory {
@@ -309,7 +310,7 @@ mod test {
deactivating: 3, deactivating: 3,
}; };
stake_history.add(1, stake_history_entry.clone()); stake_history.add(1, stake_history_entry.clone());
let stake_history_sysvar = create_account(&stake_history, 1); let stake_history_sysvar = stake_history.create_account(1);
assert_eq!( assert_eq!(
parse_sysvar(&stake_history_sysvar.data, &sysvar::stake_history::id()).unwrap(), parse_sysvar(&stake_history_sysvar.data, &sysvar::stake_history::id()).unwrap(),
SysvarAccountType::StakeHistory(vec![UiStakeHistoryEntry { SysvarAccountType::StakeHistory(vec![UiStakeHistoryEntry {
@@ -318,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()); assert!(parse_sysvar(&stake_history_sysvar.data, &bad_pubkey).is_err());
let bad_data = vec![0; 4]; let bad_data = vec![0; 4];

View File

@@ -3,32 +3,32 @@ use crate::{
StringAmount, StringAmount,
}; };
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
use spl_token_v2_0::{ use spl_token_v1_0::{
solana_program::{ option::COption,
program_option::COption, program_pack::Pack, pubkey::Pubkey as SplTokenPubkey, solana_sdk::pubkey::Pubkey as SplTokenPubkey,
}, state::{unpack, Account, Mint, Multisig},
state::{Account, AccountState, Mint, Multisig},
}; };
use std::str::FromStr; use std::{mem::size_of, str::FromStr};
// A helper function to convert spl_token_v2_0::id() as spl_sdk::pubkey::Pubkey to // A helper function to convert spl_token_v1_0::id() as spl_sdk::pubkey::Pubkey to
// solana_sdk::pubkey::Pubkey // solana_sdk::pubkey::Pubkey
pub fn spl_token_id_v2_0() -> Pubkey { pub fn spl_token_id_v1_0() -> Pubkey {
Pubkey::from_str(&spl_token_v2_0::id().to_string()).unwrap() Pubkey::from_str(&spl_token_v1_0::id().to_string()).unwrap()
} }
// A helper function to convert spl_token_v2_0::native_mint::id() as spl_sdk::pubkey::Pubkey to // A helper function to convert spl_token_v1_0::native_mint::id() as spl_sdk::pubkey::Pubkey to
// solana_sdk::pubkey::Pubkey // solana_sdk::pubkey::Pubkey
pub fn spl_token_v2_0_native_mint() -> Pubkey { pub fn spl_token_v1_0_native_mint() -> Pubkey {
Pubkey::from_str(&spl_token_v2_0::native_mint::id().to_string()).unwrap() Pubkey::from_str(&spl_token_v1_0::native_mint::id().to_string()).unwrap()
} }
pub fn parse_token( pub fn parse_token(
data: &[u8], data: &[u8],
mint_decimals: Option<u8>, mint_decimals: Option<u8>,
) -> Result<TokenAccountType, ParseAccountError> { ) -> Result<TokenAccountType, ParseAccountError> {
if data.len() == Account::get_packed_len() { let mut data = data.to_vec();
let account = Account::unpack(data) if data.len() == size_of::<Account>() {
let account: Account = *unpack(&mut data)
.map_err(|_| ParseAccountError::AccountNotParsable(ParsableAccount::SplToken))?; .map_err(|_| ParseAccountError::AccountNotParsable(ParsableAccount::SplToken))?;
let decimals = mint_decimals.ok_or_else(|| { let decimals = mint_decimals.ok_or_else(|| {
ParseAccountError::AdditionalDataMissing( ParseAccountError::AdditionalDataMissing(
@@ -43,12 +43,8 @@ pub fn parse_token(
COption::Some(pubkey) => Some(pubkey.to_string()), COption::Some(pubkey) => Some(pubkey.to_string()),
COption::None => None, COption::None => None,
}, },
state: account.state.into(), is_initialized: account.is_initialized,
is_native: account.is_native(), is_native: account.is_native,
rent_exempt_reserve: match account.is_native {
COption::Some(reserve) => Some(token_amount_to_ui_amount(reserve, decimals)),
COption::None => None,
},
delegated_amount: if account.delegate.is_none() { delegated_amount: if account.delegate.is_none() {
None None
} else { } else {
@@ -57,29 +53,20 @@ pub fn parse_token(
decimals, decimals,
)) ))
}, },
close_authority: match account.close_authority {
COption::Some(pubkey) => Some(pubkey.to_string()),
COption::None => None,
},
})) }))
} else if data.len() == Mint::get_packed_len() { } else if data.len() == size_of::<Mint>() {
let mint = Mint::unpack(data) let mint: Mint = *unpack(&mut data)
.map_err(|_| ParseAccountError::AccountNotParsable(ParsableAccount::SplToken))?; .map_err(|_| ParseAccountError::AccountNotParsable(ParsableAccount::SplToken))?;
Ok(TokenAccountType::Mint(UiMint { Ok(TokenAccountType::Mint(UiMint {
mint_authority: match mint.mint_authority { owner: match mint.owner {
COption::Some(pubkey) => Some(pubkey.to_string()), COption::Some(pubkey) => Some(pubkey.to_string()),
COption::None => None, COption::None => None,
}, },
supply: mint.supply.to_string(),
decimals: mint.decimals, decimals: mint.decimals,
is_initialized: mint.is_initialized, is_initialized: mint.is_initialized,
freeze_authority: match mint.freeze_authority {
COption::Some(pubkey) => Some(pubkey.to_string()),
COption::None => None,
},
})) }))
} else if data.len() == Multisig::get_packed_len() { } else if data.len() == size_of::<Multisig>() {
let multisig = Multisig::unpack(data) let multisig: Multisig = *unpack(&mut data)
.map_err(|_| ParseAccountError::AccountNotParsable(ParsableAccount::SplToken))?; .map_err(|_| ParseAccountError::AccountNotParsable(ParsableAccount::SplToken))?;
Ok(TokenAccountType::Multisig(UiMultisig { Ok(TokenAccountType::Multisig(UiMultisig {
num_required_signers: multisig.m, num_required_signers: multisig.m,
@@ -120,32 +107,10 @@ pub struct UiTokenAccount {
pub token_amount: UiTokenAmount, pub token_amount: UiTokenAmount,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub delegate: Option<String>, pub delegate: Option<String>,
pub state: UiAccountState, pub is_initialized: bool,
pub is_native: bool, pub is_native: bool,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub rent_exempt_reserve: Option<UiTokenAmount>,
#[serde(skip_serializing_if = "Option::is_none")]
pub delegated_amount: Option<UiTokenAmount>, pub delegated_amount: Option<UiTokenAmount>,
#[serde(skip_serializing_if = "Option::is_none")]
pub close_authority: Option<String>,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum UiAccountState {
Uninitialized,
Initialized,
Frozen,
}
impl From<AccountState> for UiAccountState {
fn from(state: AccountState) -> Self {
match state {
AccountState::Uninitialized => UiAccountState::Uninitialized,
AccountState::Initialized => UiAccountState::Initialized,
AccountState::Frozen => UiAccountState::Frozen,
}
}
} }
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
@@ -156,31 +121,6 @@ pub struct UiTokenAmount {
pub amount: StringAmount, 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 { 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 // 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; let amount_decimals = amount as f64 / 10_usize.pow(decimals as u32) as f64;
@@ -194,11 +134,9 @@ pub fn token_amount_to_ui_amount(amount: u64, decimals: u8) -> UiTokenAmount {
#[derive(Debug, Serialize, Deserialize, PartialEq)] #[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct UiMint { pub struct UiMint {
pub mint_authority: Option<String>, pub owner: Option<String>,
pub supply: StringAmount,
pub decimals: u8, pub decimals: u8,
pub is_initialized: bool, pub is_initialized: bool,
pub freeze_authority: Option<String>,
} }
#[derive(Debug, Serialize, Deserialize, PartialEq)] #[derive(Debug, Serialize, Deserialize, PartialEq)]
@@ -211,7 +149,7 @@ pub struct UiMultisig {
} }
pub fn get_token_account_mint(data: &[u8]) -> Option<Pubkey> { pub fn get_token_account_mint(data: &[u8]) -> Option<Pubkey> {
if data.len() == Account::get_packed_len() { if data.len() == size_of::<Account>() {
Some(Pubkey::new(&data[0..32])) Some(Pubkey::new(&data[0..32]))
} else { } else {
None None
@@ -221,21 +159,18 @@ pub fn get_token_account_mint(data: &[u8]) -> Option<Pubkey> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use spl_token_v1_0::state::unpack_unchecked;
#[test] #[test]
fn test_parse_token() { fn test_parse_token() {
let mint_pubkey = SplTokenPubkey::new(&[2; 32]); let mint_pubkey = SplTokenPubkey::new(&[2; 32]);
let owner_pubkey = SplTokenPubkey::new(&[3; 32]); let owner_pubkey = SplTokenPubkey::new(&[3; 32]);
let mut account_data = vec![0; Account::get_packed_len()]; let mut account_data = [0; size_of::<Account>()];
let mut account = Account::unpack_unchecked(&account_data).unwrap(); let mut account: &mut Account = unpack_unchecked(&mut account_data).unwrap();
account.mint = mint_pubkey; account.mint = mint_pubkey;
account.owner = owner_pubkey; account.owner = owner_pubkey;
account.amount = 42; account.amount = 42;
account.state = AccountState::Initialized; account.is_initialized = true;
account.is_native = COption::None;
account.close_authority = COption::Some(owner_pubkey);
Account::pack(account, &mut account_data).unwrap();
assert!(parse_token(&account_data, None).is_err()); assert!(parse_token(&account_data, None).is_err());
assert_eq!( assert_eq!(
parse_token(&account_data, Some(2)).unwrap(), parse_token(&account_data, Some(2)).unwrap(),
@@ -248,49 +183,39 @@ mod test {
amount: "42".to_string() amount: "42".to_string()
}, },
delegate: None, delegate: None,
state: UiAccountState::Initialized, is_initialized: true,
is_native: false, is_native: false,
rent_exempt_reserve: None,
delegated_amount: None, delegated_amount: None,
close_authority: Some(owner_pubkey.to_string()),
}), }),
); );
let mut mint_data = vec![0; Mint::get_packed_len()]; let mut mint_data = [0; size_of::<Mint>()];
let mut mint = Mint::unpack_unchecked(&mint_data).unwrap(); let mut mint: &mut Mint = unpack_unchecked(&mut mint_data).unwrap();
mint.mint_authority = COption::Some(owner_pubkey); mint.owner = COption::Some(owner_pubkey);
mint.supply = 42;
mint.decimals = 3; mint.decimals = 3;
mint.is_initialized = true; mint.is_initialized = true;
mint.freeze_authority = COption::Some(owner_pubkey);
Mint::pack(mint, &mut mint_data).unwrap();
assert_eq!( assert_eq!(
parse_token(&mint_data, None).unwrap(), parse_token(&mint_data, None).unwrap(),
TokenAccountType::Mint(UiMint { TokenAccountType::Mint(UiMint {
mint_authority: Some(owner_pubkey.to_string()), owner: Some(owner_pubkey.to_string()),
supply: 42.to_string(),
decimals: 3, decimals: 3,
is_initialized: true, is_initialized: true,
freeze_authority: Some(owner_pubkey.to_string()),
}), }),
); );
let signer1 = SplTokenPubkey::new(&[1; 32]); let signer1 = SplTokenPubkey::new(&[1; 32]);
let signer2 = SplTokenPubkey::new(&[2; 32]); let signer2 = SplTokenPubkey::new(&[2; 32]);
let signer3 = SplTokenPubkey::new(&[3; 32]); let signer3 = SplTokenPubkey::new(&[3; 32]);
let mut multisig_data = vec![0; Multisig::get_packed_len()]; let mut multisig_data = [0; size_of::<Multisig>()];
let mut multisig: &mut Multisig = unpack_unchecked(&mut multisig_data).unwrap();
let mut signers = [SplTokenPubkey::default(); 11]; let mut signers = [SplTokenPubkey::default(); 11];
signers[0] = signer1; signers[0] = signer1;
signers[1] = signer2; signers[1] = signer2;
signers[2] = signer3; signers[2] = signer3;
let mut multisig = Multisig::unpack_unchecked(&multisig_data).unwrap();
multisig.m = 2; multisig.m = 2;
multisig.n = 3; multisig.n = 3;
multisig.is_initialized = true; multisig.is_initialized = true;
multisig.signers = signers; multisig.signers = signers;
Multisig::pack(multisig, &mut multisig_data).unwrap();
assert_eq!( assert_eq!(
parse_token(&multisig_data, None).unwrap(), parse_token(&multisig_data, None).unwrap(),
TokenAccountType::Multisig(UiMultisig { TokenAccountType::Multisig(UiMultisig {
@@ -312,10 +237,9 @@ mod test {
#[test] #[test]
fn test_get_token_account_mint() { fn test_get_token_account_mint() {
let mint_pubkey = SplTokenPubkey::new(&[2; 32]); let mint_pubkey = SplTokenPubkey::new(&[2; 32]);
let mut account_data = vec![0; Account::get_packed_len()]; let mut account_data = [0; size_of::<Account>()];
let mut account = Account::unpack_unchecked(&account_data).unwrap(); let mut account: &mut Account = unpack_unchecked(&mut account_data).unwrap();
account.mint = mint_pubkey; account.mint = mint_pubkey;
Account::pack(account, &mut account_data).unwrap();
let expected_mint_pubkey = Pubkey::new(&[2; 32]); let expected_mint_pubkey = Pubkey::new(&[2; 32]);
assert_eq!( assert_eq!(
@@ -323,20 +247,4 @@ mod test {
Some(expected_mint_pubkey) 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");
}
} }

View File

@@ -2,20 +2,18 @@
authors = ["Solana Maintainers <maintainers@solana.foundation>"] authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2018" edition = "2018"
name = "solana-accounts-bench" name = "solana-accounts-bench"
version = "1.4.7" version = "1.3.2"
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0" license = "Apache-2.0"
homepage = "https://solana.com/" homepage = "https://solana.com/"
publish = false
[dependencies] [dependencies]
log = "0.4.6" log = "0.4.6"
rayon = "1.4.0" rayon = "1.3.1"
solana-logger = { path = "../logger", version = "1.4.7" } solana-logger = { path = "../logger", version = "1.3.2" }
solana-runtime = { path = "../runtime", version = "1.4.7" } solana-runtime = { path = "../runtime", version = "1.3.2" }
solana-measure = { path = "../measure", version = "1.4.7" } solana-measure = { path = "../measure", version = "1.3.2" }
solana-sdk = { path = "../sdk", version = "1.4.7" } solana-sdk = { path = "../sdk", version = "1.3.2" }
solana-version = { path = "../version", version = "1.4.7" }
rand = "0.7.0" rand = "0.7.0"
clap = "2.33.1" clap = "2.33.1"
crossbeam-channel = "0.4" crossbeam-channel = "0.4"

View File

@@ -1,21 +1,20 @@
use clap::{crate_description, crate_name, value_t, App, Arg}; use clap::{value_t, App, Arg};
use rayon::prelude::*; use rayon::prelude::*;
use solana_measure::measure::Measure; use solana_measure::measure::Measure;
use solana_runtime::{ use solana_runtime::{
accounts::{create_test_accounts, update_accounts, Accounts}, accounts::{create_test_accounts, update_accounts, Accounts},
accounts_index::Ancestors, accounts_index::Ancestors,
}; };
use solana_sdk::{genesis_config::ClusterType, pubkey::Pubkey}; use solana_sdk::pubkey::Pubkey;
use std::env;
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
fn main() { fn main() {
solana_logger::setup(); solana_logger::setup();
let matches = App::new(crate_name!()) let matches = App::new("crate")
.about(crate_description!()) .about("about")
.version(solana_version::version!()) .version("version")
.arg( .arg(
Arg::with_name("num_slots") Arg::with_name("num_slots")
.long("num_slots") .long("num_slots")
@@ -51,12 +50,11 @@ fn main() {
let clean = matches.is_present("clean"); let clean = matches.is_present("clean");
println!("clean: {:?}", clean); println!("clean: {:?}", clean);
let path = PathBuf::from(env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_owned())) let path = PathBuf::from("farf/accounts-bench");
.join("accounts-bench");
if fs::remove_dir_all(path.clone()).is_err() { if fs::remove_dir_all(path.clone()).is_err() {
println!("Warning: Couldn't remove {:?}", path); 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); println!("Creating {} accounts", num_accounts);
let mut create_time = Measure::start("create accounts"); let mut create_time = Measure::start("create accounts");
let pubkeys: Vec<_> = (0..num_slots) let pubkeys: Vec<_> = (0..num_slots)
@@ -88,7 +86,7 @@ fn main() {
for x in 0..iterations { for x in 0..iterations {
if clean { if clean {
let mut time = Measure::start("clean"); let mut time = Measure::start("clean");
accounts.accounts_db.clean_accounts(None); accounts.accounts_db.clean_accounts();
time.stop(); time.stop();
println!("{}", time); println!("{}", time);
for slot in 0..num_slots { for slot in 0..num_slots {
@@ -98,7 +96,7 @@ fn main() {
} else { } else {
let mut pubkeys: Vec<Pubkey> = vec![]; let mut pubkeys: Vec<Pubkey> = vec![];
let mut time = Measure::start("hash"); 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(); time.stop();
println!("hash: {} {}", hash, time); println!("hash: {} {}", hash, time);
create_test_accounts(&accounts, &mut pubkeys, 1, 0); create_test_accounts(&accounts, &mut pubkeys, 1, 0);

View File

@@ -2,28 +2,27 @@
authors = ["Solana Maintainers <maintainers@solana.foundation>"] authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2018" edition = "2018"
name = "solana-banking-bench" name = "solana-banking-bench"
version = "1.4.7" version = "1.3.2"
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0" license = "Apache-2.0"
homepage = "https://solana.com/" homepage = "https://solana.com/"
publish = false
[dependencies] [dependencies]
clap = "2.33.1" clap = "2.33.1"
crossbeam-channel = "0.4" crossbeam-channel = "0.4"
log = "0.4.6" log = "0.4.6"
rand = "0.7.0" rand = "0.7.0"
rayon = "1.4.0" rayon = "1.3.1"
solana-core = { path = "../core", version = "1.4.7" } solana-core = { path = "../core", version = "1.3.2" }
solana-clap-utils = { path = "../clap-utils", version = "1.4.7" } solana-clap-utils = { path = "../clap-utils", version = "1.3.2" }
solana-streamer = { path = "../streamer", version = "1.4.7" } solana-streamer = { path = "../streamer", version = "1.3.2" }
solana-perf = { path = "../perf", version = "1.4.7" } solana-perf = { path = "../perf", version = "1.3.2" }
solana-ledger = { path = "../ledger", version = "1.4.7" } solana-ledger = { path = "../ledger", version = "1.3.2" }
solana-logger = { path = "../logger", version = "1.4.7" } solana-logger = { path = "../logger", version = "1.3.2" }
solana-runtime = { path = "../runtime", version = "1.4.7" } solana-runtime = { path = "../runtime", version = "1.3.2" }
solana-measure = { path = "../measure", version = "1.4.7" } solana-measure = { path = "../measure", version = "1.3.2" }
solana-sdk = { path = "../sdk", version = "1.4.7" } solana-sdk = { path = "../sdk", version = "1.3.2" }
solana-version = { path = "../version", version = "1.4.7" } solana-version = { path = "../version", version = "1.3.2" }
[package.metadata.docs.rs] [package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"] targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -20,6 +20,7 @@ use solana_perf::packet::to_packets_chunked;
use solana_runtime::{bank::Bank, bank_forks::BankForks}; use solana_runtime::{bank::Bank, bank_forks::BankForks};
use solana_sdk::{ use solana_sdk::{
hash::Hash, hash::Hash,
pubkey::Pubkey,
signature::Keypair, signature::Keypair,
signature::Signature, signature::Signature,
system_transaction, system_transaction,
@@ -68,7 +69,7 @@ fn make_accounts_txs(
hash: Hash, hash: Hash,
same_payer: bool, same_payer: bool,
) -> Vec<Transaction> { ) -> Vec<Transaction> {
let to_pubkey = solana_sdk::pubkey::new_rand(); let to_pubkey = Pubkey::new_rand();
let payer_key = Keypair::new(); let payer_key = Keypair::new();
let dummy = system_transaction::transfer(&payer_key, &to_pubkey, 1, hash); let dummy = system_transaction::transfer(&payer_key, &to_pubkey, 1, hash);
(0..total_num_transactions) (0..total_num_transactions)
@@ -77,9 +78,9 @@ fn make_accounts_txs(
let mut new = dummy.clone(); let mut new = dummy.clone();
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect(); let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
if !same_payer { if !same_payer {
new.message.account_keys[0] = 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.signatures = vec![Signature::new(&sig[0..64])];
new new
}) })
@@ -240,7 +241,7 @@ fn main() {
let base_tx_count = bank.transaction_count(); let base_tx_count = bank.transaction_count();
let mut txs_processed = 0; let mut txs_processed = 0;
let mut root = 1; let mut root = 1;
let collector = solana_sdk::pubkey::new_rand(); let collector = Pubkey::new_rand();
let config = Config { let config = Config {
packets_per_batch: packets_per_chunk, packets_per_batch: packets_per_chunk,
chunk_len, chunk_len,

View File

@@ -1,30 +0,0 @@
[package]
name = "solana-banks-client"
version = "1.4.7"
description = "Solana banks client"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
edition = "2018"
[dependencies]
async-trait = "0.1.36"
bincode = "1.3.1"
futures = "0.3"
solana-banks-interface = { path = "../banks-interface", version = "1.4.7" }
solana-sdk = { path = "../sdk", version = "1.4.7" }
tarpc = { version = "0.23.0", features = ["full"] }
tokio = { version = "0.3", features = ["full"] }
tokio-serde = { version = "0.6", features = ["bincode"] }
[dev-dependencies]
solana-runtime = { path = "../runtime", version = "1.4.7" }
solana-banks-server = { path = "../banks-server", version = "1.4.7" }
[lib]
crate-type = ["lib"]
name = "solana_banks_client"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -1,318 +0,0 @@
//! A client for the ledger state, from the perspective of an arbitrary validator.
//!
//! Use start_tcp_client() to create a client and then import BanksClientExt to
//! access its methods. Additional "*_with_context" methods are also available,
//! but they are undocumented, may change over time, and are generally more
//! cumbersome to use.
use async_trait::async_trait;
use futures::future::join_all;
pub use solana_banks_interface::{BanksClient, TransactionStatus};
use solana_banks_interface::{BanksRequest, BanksResponse};
use solana_sdk::{
account::{from_account, Account},
clock::Slot,
commitment_config::CommitmentLevel,
fee_calculator::FeeCalculator,
hash::Hash,
pubkey::Pubkey,
rent::Rent,
signature::Signature,
sysvar,
transaction::Transaction,
transport,
};
use std::io::{self, Error, ErrorKind};
use tarpc::{
client, context,
rpc::{transport::channel::UnboundedChannel, ClientMessage, Response},
serde_transport::tcp,
};
use tokio::{net::ToSocketAddrs, time::Duration};
use tokio_serde::formats::Bincode;
#[async_trait]
pub trait BanksClientExt {
/// Send a transaction and return immediately. The server will resend the
/// transaction until either it is accepted by the cluster or the transaction's
/// blockhash expires.
async fn send_transaction(&mut self, transaction: Transaction) -> io::Result<()>;
/// Return a recent, rooted blockhash from the server. The cluster will only accept
/// transactions with a blockhash that has not yet expired. Use the `get_fees`
/// method to get both a blockhash and the blockhash's last valid slot.
async fn get_recent_blockhash(&mut self) -> io::Result<Hash>;
/// Return the fee parameters associated with a recent, rooted blockhash. The cluster
/// will use the transaction's blockhash to look up these same fee parameters and
/// use them to calculate the transaction fee.
async fn get_fees(&mut self) -> io::Result<(FeeCalculator, Hash, Slot)>;
/// Return the cluster rent
async fn get_rent(&mut self) -> io::Result<Rent>;
/// Send a transaction and return after the transaction has been rejected or
/// reached the given level of commitment.
async fn process_transaction_with_commitment(
&mut self,
transaction: Transaction,
commitment: CommitmentLevel,
) -> transport::Result<()>;
/// Send a transaction and return after the transaction has been finalized or rejected.
async fn process_transaction(&mut self, transaction: Transaction) -> transport::Result<()>;
/// Return the status of a transaction with a signature matching the transaction's first
/// signature. Return None if the transaction is not found, which may be because the
/// blockhash was expired or the fee-paying account had insufficient funds to pay the
/// transaction fee. Note that servers rarely store the full transaction history. This
/// method may return None if the transaction status has been discarded.
async fn get_transaction_status(
&mut self,
signature: Signature,
) -> io::Result<Option<TransactionStatus>>;
/// Same as get_transaction_status, but for multiple transactions.
async fn get_transaction_statuses(
&mut self,
signatures: Vec<Signature>,
) -> io::Result<Vec<Option<TransactionStatus>>>;
/// Return the most recent rooted slot height. All transactions at or below this height
/// 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>>;
/// Return the balance in lamports of an account at the given address at the slot
/// corresponding to the given commitment level.
async fn get_balance_with_commitment(
&mut self,
address: Pubkey,
commitment: CommitmentLevel,
) -> io::Result<u64>;
/// Return the balance in lamports of an account at the given address at the time
/// of the most recent root slot.
async fn get_balance(&mut self, address: Pubkey) -> io::Result<u64>;
}
#[async_trait]
impl BanksClientExt for BanksClient {
async fn send_transaction(&mut self, transaction: Transaction) -> io::Result<()> {
self.send_transaction_with_context(context::current(), transaction)
.await
}
async fn get_fees(&mut self) -> io::Result<(FeeCalculator, Hash, Slot)> {
self.get_fees_with_commitment_and_context(context::current(), CommitmentLevel::Root)
.await
}
async fn get_rent(&mut self) -> io::Result<Rent> {
let rent_sysvar = self
.get_account(sysvar::rent::id())
.await?
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Rent sysvar not present"))?;
from_account::<Rent>(&rent_sysvar).ok_or_else(|| {
io::Error::new(io::ErrorKind::Other, "Failed to deserialize Rent sysvar")
})
}
async fn get_recent_blockhash(&mut self) -> io::Result<Hash> {
Ok(self.get_fees().await?.1)
}
async fn process_transaction_with_commitment(
&mut self,
transaction: Transaction,
commitment: CommitmentLevel,
) -> transport::Result<()> {
let mut ctx = context::current();
ctx.deadline += Duration::from_secs(50);
let result = self
.process_transaction_with_commitment_and_context(ctx, transaction, commitment)
.await?;
match result {
None => Err(Error::new(ErrorKind::TimedOut, "invalid blockhash or fee-payer").into()),
Some(transaction_result) => Ok(transaction_result?),
}
}
async fn process_transaction(&mut self, transaction: Transaction) -> transport::Result<()> {
self.process_transaction_with_commitment(transaction, CommitmentLevel::default())
.await
}
async fn get_root_slot(&mut self) -> io::Result<Slot> {
self.get_slot_with_context(context::current(), CommitmentLevel::Root)
.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
}
async fn get_balance_with_commitment(
&mut self,
address: Pubkey,
commitment: CommitmentLevel,
) -> io::Result<u64> {
let account = self
.get_account_with_commitment_and_context(context::current(), address, commitment)
.await?;
Ok(account.map(|x| x.lamports).unwrap_or(0))
}
async fn get_balance(&mut self, address: Pubkey) -> io::Result<u64> {
self.get_balance_with_commitment(address, CommitmentLevel::default())
.await
}
async fn get_transaction_status(
&mut self,
signature: Signature,
) -> io::Result<Option<TransactionStatus>> {
self.get_transaction_status_with_context(context::current(), signature)
.await
}
async fn get_transaction_statuses(
&mut self,
signatures: Vec<Signature>,
) -> io::Result<Vec<Option<TransactionStatus>>> {
// tarpc futures oddly hold a mutable reference back to the client so clone the client upfront
let mut clients_and_signatures: Vec<_> = signatures
.into_iter()
.map(|signature| (self.clone(), signature))
.collect();
let futs = clients_and_signatures
.iter_mut()
.map(|(client, signature)| client.get_transaction_status(*signature));
let statuses = join_all(futs).await;
// Convert Vec<Result<_, _>> to Result<Vec<_>>
statuses.into_iter().collect()
}
}
pub async fn start_client(
transport: UnboundedChannel<Response<BanksResponse>, ClientMessage<BanksRequest>>,
) -> io::Result<BanksClient> {
BanksClient::new(client::Config::default(), transport).spawn()
}
pub async fn start_tcp_client<T: ToSocketAddrs>(addr: T) -> io::Result<BanksClient> {
let transport = tcp::connect(addr, Bincode::default).await?;
BanksClient::new(client::Config::default(), transport).spawn()
}
#[cfg(test)]
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 std::sync::{Arc, RwLock};
use tarpc::transport;
use tokio::{runtime::Runtime, time::sleep};
#[test]
fn test_banks_client_new() {
let (client_transport, _server_transport) = transport::channel::unbounded();
BanksClient::new(client::Config::default(), client_transport);
}
#[test]
fn test_banks_server_transfer_via_server() -> io::Result<()> {
// This test shows the preferred way to interact with BanksServer.
// It creates a runtime explicitly (no globals via tokio macros) and calls
// `runtime.block_on()` just once, to run all the async code.
let genesis = create_genesis_config(10);
let bank_forks = Arc::new(RwLock::new(BankForks::new(Bank::new(
&genesis.genesis_config,
))));
let bob_pubkey = solana_sdk::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));
Runtime::new()?.block_on(async {
let client_transport = start_local_server(&bank_forks).await;
let mut banks_client =
BanksClient::new(client::Config::default(), client_transport).spawn()?;
let recent_blockhash = banks_client.get_recent_blockhash().await?;
let transaction = Transaction::new(&[&genesis.mint_keypair], message, recent_blockhash);
banks_client.process_transaction(transaction).await.unwrap();
assert_eq!(banks_client.get_balance(bob_pubkey).await?, 1);
Ok(())
})
}
#[test]
fn test_banks_server_transfer_via_client() -> io::Result<()> {
// The caller may not want to hold the connection open until the transaction
// is processed (or blockhash expires). In this test, we verify the
// server-side functionality is available to the client.
let genesis = create_genesis_config(10);
let bank_forks = Arc::new(RwLock::new(BankForks::new(Bank::new(
&genesis.genesis_config,
))));
let mint_pubkey = &genesis.mint_keypair.pubkey();
let bob_pubkey = solana_sdk::pubkey::new_rand();
let instruction = system_instruction::transfer(&mint_pubkey, &bob_pubkey, 1);
let message = Message::new(&[instruction], Some(&mint_pubkey));
Runtime::new()?.block_on(async {
let client_transport = start_local_server(&bank_forks).await;
let mut banks_client =
BanksClient::new(client::Config::default(), client_transport).spawn()?;
let (_, recent_blockhash, last_valid_slot) = banks_client.get_fees().await?;
let transaction = Transaction::new(&[&genesis.mint_keypair], message, recent_blockhash);
let signature = transaction.signatures[0];
banks_client.send_transaction(transaction).await?;
let mut status = banks_client.get_transaction_status(signature).await?;
while status.is_none() {
let root_slot = banks_client.get_root_slot().await?;
if root_slot > last_valid_slot {
break;
}
sleep(Duration::from_millis(100)).await;
status = banks_client.get_transaction_status(signature).await?;
}
assert!(status.unwrap().err.is_none());
assert_eq!(banks_client.get_balance(bob_pubkey).await?, 1);
Ok(())
})
}
}

View File

@@ -1,21 +0,0 @@
[package]
name = "solana-banks-interface"
version = "1.4.7"
description = "Solana banks RPC interface"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
edition = "2018"
[dependencies]
serde = { version = "1.0.112", features = ["derive"] }
solana-sdk = { path = "../sdk", version = "1.4.7" }
tarpc = { version = "0.23.0", features = ["full"] }
[lib]
crate-type = ["lib"]
name = "solana_banks_interface"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -1,49 +0,0 @@
use serde::{Deserialize, Serialize};
use solana_sdk::{
account::Account,
clock::Slot,
commitment_config::CommitmentLevel,
fee_calculator::FeeCalculator,
hash::Hash,
pubkey::Pubkey,
signature::Signature,
transaction::{self, Transaction, TransactionError},
};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct TransactionStatus {
pub slot: Slot,
pub confirmations: Option<usize>, // None = rooted
pub err: Option<TransactionError>,
}
#[tarpc::service]
pub trait Banks {
async fn send_transaction_with_context(transaction: Transaction);
async fn get_fees_with_commitment_and_context(
commitment: CommitmentLevel,
) -> (FeeCalculator, Hash, Slot);
async fn get_transaction_status_with_context(signature: Signature)
-> Option<TransactionStatus>;
async fn get_slot_with_context(commitment: CommitmentLevel) -> Slot;
async fn process_transaction_with_commitment_and_context(
transaction: Transaction,
commitment: CommitmentLevel,
) -> Option<transaction::Result<()>>;
async fn get_account_with_commitment_and_context(
address: Pubkey,
commitment: CommitmentLevel,
) -> Option<Account>;
}
#[cfg(test)]
mod tests {
use super::*;
use tarpc::{client, transport};
#[test]
fn test_banks_client_new() {
let (client_transport, _server_transport) = transport::channel::unbounded();
BanksClient::new(client::Config::default(), client_transport);
}
}

View File

@@ -1,28 +0,0 @@
[package]
name = "solana-banks-server"
version = "1.4.7"
description = "Solana banks server"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
edition = "2018"
[dependencies]
bincode = "1.3.1"
futures = "0.3"
log = "0.4.8"
solana-banks-interface = { path = "../banks-interface", version = "1.4.7" }
solana-runtime = { path = "../runtime", version = "1.4.7" }
solana-sdk = { path = "../sdk", version = "1.4.7" }
solana-metrics = { path = "../metrics", version = "1.4.7" }
tarpc = { version = "0.23.0", features = ["full"] }
tokio = { version = "0.3", features = ["full"] }
tokio-serde = { version = "0.6", features = ["bincode"] }
[lib]
crate-type = ["lib"]
name = "solana_banks_server"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -1,261 +0,0 @@
use crate::send_transaction_service::{SendTransactionService, TransactionInfo};
use bincode::{deserialize, serialize};
use futures::{
future,
prelude::stream::{self, StreamExt},
};
use solana_banks_interface::{Banks, BanksRequest, BanksResponse, TransactionStatus};
use solana_runtime::{bank::Bank, bank_forks::BankForks, commitment::BlockCommitmentCache};
use solana_sdk::{
account::Account,
clock::Slot,
commitment_config::CommitmentLevel,
fee_calculator::FeeCalculator,
hash::Hash,
pubkey::Pubkey,
signature::Signature,
transaction::{self, Transaction},
};
use std::{
io,
net::{Ipv4Addr, SocketAddr},
sync::{
mpsc::{channel, Receiver, Sender},
Arc, RwLock,
},
thread::Builder,
time::Duration,
};
use tarpc::{
context::Context,
rpc::{transport::channel::UnboundedChannel, ClientMessage, Response},
serde_transport::tcp,
server::{self, Channel, Handler},
transport,
};
use tokio::time::sleep;
use tokio_serde::formats::Bincode;
#[derive(Clone)]
struct BanksServer {
bank_forks: Arc<RwLock<BankForks>>,
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
transaction_sender: Sender<TransactionInfo>,
}
impl BanksServer {
/// Return a BanksServer that forwards transactions to the
/// given sender. If unit-testing, those transactions can go to
/// a bank in the given BankForks. Otherwise, the receiver should
/// forward them to a validator in the leader schedule.
fn new(
bank_forks: Arc<RwLock<BankForks>>,
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
transaction_sender: Sender<TransactionInfo>,
) -> Self {
Self {
bank_forks,
block_commitment_cache,
transaction_sender,
}
}
fn run(bank: &Bank, transaction_receiver: Receiver<TransactionInfo>) {
while let Ok(info) = transaction_receiver.recv() {
let mut transaction_infos = vec![info];
while let Ok(info) = transaction_receiver.try_recv() {
transaction_infos.push(info);
}
let transactions: Vec<_> = transaction_infos
.into_iter()
.map(|info| deserialize(&info.wire_transaction).unwrap())
.collect();
let _ = bank.process_transactions(&transactions);
}
}
/// Useful for unit-testing
fn new_loopback(bank_forks: Arc<RwLock<BankForks>>) -> Self {
let (transaction_sender, transaction_receiver) = channel();
let bank = bank_forks.read().unwrap().working_bank();
let slot = bank.slot();
let block_commitment_cache = Arc::new(RwLock::new(
BlockCommitmentCache::new_for_tests_with_slots(slot, slot),
));
Builder::new()
.name("solana-bank-forks-client".to_string())
.spawn(move || Self::run(&bank, transaction_receiver))
.unwrap();
Self::new(bank_forks, block_commitment_cache, transaction_sender)
}
fn slot(&self, commitment: CommitmentLevel) -> Slot {
self.block_commitment_cache
.read()
.unwrap()
.slot_with_commitment(commitment)
}
fn bank(&self, commitment: CommitmentLevel) -> Arc<Bank> {
self.bank_forks.read().unwrap()[self.slot(commitment)].clone()
}
async fn poll_signature_status(
self,
signature: &Signature,
blockhash: &Hash,
last_valid_slot: Slot,
commitment: CommitmentLevel,
) -> Option<transaction::Result<()>> {
let mut status = self
.bank(commitment)
.get_signature_status_with_blockhash(signature, blockhash);
while status.is_none() {
sleep(Duration::from_millis(200)).await;
let bank = self.bank(commitment);
if bank.slot() > last_valid_slot {
break;
}
status = bank.get_signature_status_with_blockhash(signature, blockhash);
}
status
}
}
#[tarpc::server]
impl Banks for BanksServer {
async fn send_transaction_with_context(self, _: Context, transaction: Transaction) {
let blockhash = &transaction.message.recent_blockhash;
let last_valid_slot = self
.bank_forks
.read()
.unwrap()
.root_bank()
.get_blockhash_last_valid_slot(&blockhash)
.unwrap();
let signature = transaction.signatures.get(0).cloned().unwrap_or_default();
let info =
TransactionInfo::new(signature, serialize(&transaction).unwrap(), last_valid_slot);
self.transaction_sender.send(info).unwrap();
}
async fn get_fees_with_commitment_and_context(
self,
_: Context,
commitment: CommitmentLevel,
) -> (FeeCalculator, Hash, Slot) {
let bank = self.bank(commitment);
let (blockhash, fee_calculator) = bank.last_blockhash_with_fee_calculator();
let last_valid_slot = bank.get_blockhash_last_valid_slot(&blockhash).unwrap();
(fee_calculator, blockhash, last_valid_slot)
}
async fn get_transaction_status_with_context(
self,
_: Context,
signature: Signature,
) -> Option<TransactionStatus> {
let bank = self.bank(CommitmentLevel::Recent);
let (slot, status) = bank.get_signature_status_slot(&signature)?;
let r_block_commitment_cache = self.block_commitment_cache.read().unwrap();
let confirmations = if r_block_commitment_cache.root() >= slot {
None
} else {
r_block_commitment_cache
.get_confirmation_count(slot)
.or(Some(0))
};
Some(TransactionStatus {
slot,
confirmations,
err: status.err(),
})
}
async fn get_slot_with_context(self, _: Context, commitment: CommitmentLevel) -> Slot {
self.slot(commitment)
}
async fn process_transaction_with_commitment_and_context(
self,
_: Context,
transaction: Transaction,
commitment: CommitmentLevel,
) -> Option<transaction::Result<()>> {
let blockhash = &transaction.message.recent_blockhash;
let last_valid_slot = self
.bank_forks
.read()
.unwrap()
.root_bank()
.get_blockhash_last_valid_slot(blockhash)
.unwrap();
let signature = transaction.signatures.get(0).cloned().unwrap_or_default();
let info =
TransactionInfo::new(signature, serialize(&transaction).unwrap(), last_valid_slot);
self.transaction_sender.send(info).unwrap();
self.poll_signature_status(&signature, blockhash, last_valid_slot, commitment)
.await
}
async fn get_account_with_commitment_and_context(
self,
_: Context,
address: Pubkey,
commitment: CommitmentLevel,
) -> Option<Account> {
let bank = self.bank(commitment);
bank.get_account(&address)
}
}
pub async fn start_local_server(
bank_forks: &Arc<RwLock<BankForks>>,
) -> UnboundedChannel<Response<BanksResponse>, ClientMessage<BanksRequest>> {
let banks_server = BanksServer::new_loopback(bank_forks.clone());
let (client_transport, server_transport) = transport::channel::unbounded();
let server = server::new(server::Config::default())
.incoming(stream::once(future::ready(server_transport)))
.respond_with(banks_server.serve());
tokio::spawn(server);
client_transport
}
pub async fn start_tcp_server(
listen_addr: SocketAddr,
tpu_addr: SocketAddr,
bank_forks: Arc<RwLock<BankForks>>,
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
) -> io::Result<()> {
// Note: These settings are copied straight from the tarpc example.
let server = tcp::listen(listen_addr, Bincode::default)
.await?
// Ignore accept errors.
.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())
})
// 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();
SendTransactionService::new(tpu_addr, &bank_forks, receiver);
let server =
BanksServer::new(bank_forks.clone(), block_commitment_cache.clone(), sender);
chan.respond_with(server.serve()).execute()
})
// Max 10 channels.
.buffer_unordered(10)
.for_each(|_| async {});
server.await;
Ok(())
}

View File

@@ -1,6 +0,0 @@
pub mod banks_server;
pub mod rpc_banks_service;
pub mod send_transaction_service;
#[macro_use]
extern crate solana_metrics;

View File

@@ -1,116 +0,0 @@
//! The `rpc_banks_service` module implements the Solana Banks RPC API.
use crate::banks_server::start_tcp_server;
use futures::{future::FutureExt, pin_mut, prelude::stream::StreamExt, select};
use solana_runtime::{bank_forks::BankForks, commitment::BlockCommitmentCache};
use std::{
net::SocketAddr,
sync::{
atomic::{AtomicBool, Ordering},
Arc, RwLock,
},
thread::{self, Builder, JoinHandle},
};
use tokio::{
runtime::Runtime,
time::{self, Duration},
};
pub struct RpcBanksService {
thread_hdl: JoinHandle<()>,
}
/// Run the TCP service until `exit` is set to true
async fn start_abortable_tcp_server(
listen_addr: SocketAddr,
tpu_addr: SocketAddr,
bank_forks: Arc<RwLock<BankForks>>,
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
exit: Arc<AtomicBool>,
) {
let server = start_tcp_server(
listen_addr,
tpu_addr,
bank_forks.clone(),
block_commitment_cache.clone(),
)
.fuse();
let interval = time::interval(Duration::from_millis(100)).fuse();
pin_mut!(server, interval);
loop {
select! {
_ = server => {},
_ = interval.select_next_some() => {
if exit.load(Ordering::Relaxed) {
break;
}
}
}
}
}
impl RpcBanksService {
fn run(
listen_addr: SocketAddr,
tpu_addr: SocketAddr,
bank_forks: Arc<RwLock<BankForks>>,
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
exit: Arc<AtomicBool>,
) {
let server = start_abortable_tcp_server(
listen_addr,
tpu_addr,
bank_forks,
block_commitment_cache,
exit,
);
Runtime::new().unwrap().block_on(server);
}
pub fn new(
listen_addr: SocketAddr,
tpu_addr: SocketAddr,
bank_forks: &Arc<RwLock<BankForks>>,
block_commitment_cache: &Arc<RwLock<BlockCommitmentCache>>,
exit: &Arc<AtomicBool>,
) -> Self {
let bank_forks = bank_forks.clone();
let block_commitment_cache = block_commitment_cache.clone();
let exit = exit.clone();
let thread_hdl = Builder::new()
.name("solana-rpc-banks".to_string())
.spawn(move || {
Self::run(
listen_addr,
tpu_addr,
bank_forks,
block_commitment_cache,
exit,
)
})
.unwrap();
Self { thread_hdl }
}
pub fn join(self) -> thread::Result<()> {
self.thread_hdl.join()
}
}
#[cfg(test)]
mod tests {
use super::*;
use solana_runtime::bank::Bank;
#[test]
fn test_rpc_banks_server_exit() {
let bank_forks = Arc::new(RwLock::new(BankForks::new(Bank::default())));
let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::default()));
let exit = Arc::new(AtomicBool::new(false));
let addr = "127.0.0.1:0".parse().unwrap();
let service = RpcBanksService::new(addr, addr, &bank_forks, &block_commitment_cache, &exit);
exit.store(true, Ordering::Relaxed);
service.join().unwrap();
}
}

View File

@@ -2,7 +2,7 @@
authors = ["Solana Maintainers <maintainers@solana.foundation>"] authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2018" edition = "2018"
name = "solana-bench-exchange" name = "solana-bench-exchange"
version = "1.4.7" version = "1.3.2"
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0" license = "Apache-2.0"
homepage = "https://solana.com/" homepage = "https://solana.com/"
@@ -15,24 +15,24 @@ log = "0.4.8"
num-derive = "0.3" num-derive = "0.3"
num-traits = "0.2" num-traits = "0.2"
rand = "0.7.0" rand = "0.7.0"
rayon = "1.4.0" rayon = "1.3.1"
serde_json = "1.0.56" serde_json = "1.0.56"
serde_yaml = "0.8.13" serde_yaml = "0.8.13"
solana-clap-utils = { path = "../clap-utils", version = "1.4.7" } solana-clap-utils = { path = "../clap-utils", version = "1.3.2" }
solana-core = { path = "../core", version = "1.4.7" } solana-core = { path = "../core", version = "1.3.2" }
solana-genesis = { path = "../genesis", version = "1.4.7" } solana-genesis = { path = "../genesis", version = "1.3.2" }
solana-client = { path = "../client", version = "1.4.7" } solana-client = { path = "../client", version = "1.3.2" }
solana-faucet = { path = "../faucet", version = "1.4.7" } solana-faucet = { path = "../faucet", version = "1.3.2" }
solana-exchange-program = { path = "../programs/exchange", version = "1.4.7" } solana-exchange-program = { path = "../programs/exchange", version = "1.3.2" }
solana-logger = { path = "../logger", version = "1.4.7" } solana-logger = { path = "../logger", version = "1.3.2" }
solana-metrics = { path = "../metrics", version = "1.4.7" } solana-metrics = { path = "../metrics", version = "1.3.2" }
solana-net-utils = { path = "../net-utils", version = "1.4.7" } solana-net-utils = { path = "../net-utils", version = "1.3.2" }
solana-runtime = { path = "../runtime", version = "1.4.7" } solana-runtime = { path = "../runtime", version = "1.3.2" }
solana-sdk = { path = "../sdk", version = "1.4.7" } solana-sdk = { path = "../sdk", version = "1.3.2" }
solana-version = { path = "../version", version = "1.4.7" } solana-version = { path = "../version", version = "1.3.2" }
[dev-dependencies] [dev-dependencies]
solana-local-cluster = { path = "../local-cluster", version = "1.4.7" } solana-local-cluster = { path = "../local-cluster", version = "1.3.2" }
[package.metadata.docs.rs] [package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"] targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -86,7 +86,7 @@ fn test_exchange_bank_client() {
solana_logger::setup(); solana_logger::setup();
let (genesis_config, identity) = create_genesis_config(100_000_000_000_000); let (genesis_config, identity) = create_genesis_config(100_000_000_000_000);
let mut bank = Bank::new(&genesis_config); let mut bank = Bank::new(&genesis_config);
bank.add_builtin("exchange_program", id(), process_instruction); bank.add_builtin_program("exchange_program", id(), process_instruction);
let clients = vec![BankClient::new(bank)]; let clients = vec![BankClient::new(bank)];
let mut config = Config::default(); let mut config = Config::default();

View File

@@ -2,19 +2,18 @@
authors = ["Solana Maintainers <maintainers@solana.foundation>"] authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2018" edition = "2018"
name = "solana-bench-streamer" name = "solana-bench-streamer"
version = "1.4.7" version = "1.3.2"
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0" license = "Apache-2.0"
homepage = "https://solana.com/" homepage = "https://solana.com/"
publish = false
[dependencies] [dependencies]
clap = "2.33.1" clap = "2.33.1"
solana-clap-utils = { path = "../clap-utils", version = "1.4.7" } solana-clap-utils = { path = "../clap-utils", version = "1.3.2" }
solana-streamer = { path = "../streamer", version = "1.4.7" } solana-streamer = { path = "../streamer", version = "1.3.2" }
solana-logger = { path = "../logger", version = "1.4.7" } solana-logger = { path = "../logger", version = "1.3.2" }
solana-net-utils = { path = "../net-utils", version = "1.4.7" } solana-net-utils = { path = "../net-utils", version = "1.3.2" }
solana-version = { path = "../version", version = "1.4.7" } solana-version = { path = "../version", version = "1.3.2" }
[package.metadata.docs.rs] [package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"] targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -27,7 +27,7 @@ fn producer(addr: &SocketAddr, exit: Arc<AtomicBool>) -> JoinHandle<()> {
let mut num = 0; let mut num = 0;
for p in &msgs.packets { for p in &msgs.packets {
let a = p.meta.addr(); 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(); send.send_to(&p.data[..p.meta.size], &a).unwrap();
num += 1; num += 1;
} }

View File

@@ -2,36 +2,35 @@
authors = ["Solana Maintainers <maintainers@solana.foundation>"] authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2018" edition = "2018"
name = "solana-bench-tps" name = "solana-bench-tps"
version = "1.4.7" version = "1.3.2"
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0" license = "Apache-2.0"
homepage = "https://solana.com/" homepage = "https://solana.com/"
publish = false
[dependencies] [dependencies]
bincode = "1.3.1" bincode = "1.3.1"
clap = "2.33.1" clap = "2.33.1"
log = "0.4.8" log = "0.4.8"
rayon = "1.4.0" rayon = "1.3.1"
serde_json = "1.0.56" serde_json = "1.0.56"
serde_yaml = "0.8.13" serde_yaml = "0.8.13"
solana-clap-utils = { path = "../clap-utils", version = "1.4.7" } solana-clap-utils = { path = "../clap-utils", version = "1.3.2" }
solana-core = { path = "../core", version = "1.4.7" } solana-core = { path = "../core", version = "1.3.2" }
solana-genesis = { path = "../genesis", version = "1.4.7" } solana-genesis = { path = "../genesis", version = "1.3.2" }
solana-client = { path = "../client", version = "1.4.7" } solana-client = { path = "../client", version = "1.3.2" }
solana-faucet = { path = "../faucet", version = "1.4.7" } solana-faucet = { path = "../faucet", version = "1.3.2" }
solana-logger = { path = "../logger", version = "1.4.7" } solana-logger = { path = "../logger", version = "1.3.2" }
solana-metrics = { path = "../metrics", version = "1.4.7" } solana-metrics = { path = "../metrics", version = "1.3.2" }
solana-measure = { path = "../measure", version = "1.4.7" } solana-measure = { path = "../measure", version = "1.3.2" }
solana-net-utils = { path = "../net-utils", version = "1.4.7" } solana-net-utils = { path = "../net-utils", version = "1.3.2" }
solana-runtime = { path = "../runtime", version = "1.4.7" } solana-runtime = { path = "../runtime", version = "1.3.2" }
solana-sdk = { path = "../sdk", version = "1.4.7" } solana-sdk = { path = "../sdk", version = "1.3.2" }
solana-version = { path = "../version", version = "1.4.7" } solana-version = { path = "../version", version = "1.3.2" }
[dev-dependencies] [dev-dependencies]
serial_test = "0.4.0" serial_test = "0.4.0"
serial_test_derive = "0.4.0" serial_test_derive = "0.4.0"
solana-local-cluster = { path = "../local-cluster", version = "1.4.7" } solana-local-cluster = { path = "../local-cluster", version = "1.3.2" }
[package.metadata.docs.rs] [package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"] targets = ["x86_64-unknown-linux-gnu"]

31
cargo
View File

@@ -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}" "${@}"

View File

@@ -1,13 +0,0 @@
#!/usr/bin/env bash
here=$(dirname "$0")
maybe_bpf_sdk="--bpf-sdk $here/sdk/bpf"
for a in "$@"; do
if [[ $a = --bpf-sdk ]]; then
maybe_bpf_sdk=
fi
done
set -x
exec "$here"/cargo run --manifest-path "$here"/sdk/cargo-build-bpf/Cargo.toml -- $maybe_bpf_sdk "$@"

View File

@@ -1,14 +0,0 @@
#!/usr/bin/env bash
here=$(dirname "$0")
maybe_bpf_sdk="--bpf-sdk $here/sdk/bpf"
for a in "$@"; do
if [[ $a = --bpf-sdk ]]; then
maybe_bpf_sdk=
fi
done
export CARGO_BUILD_BPF="$here"/cargo-build-bpf
set -x
exec "$here"/cargo run --manifest-path "$here"/sdk/cargo-test-bpf/Cargo.toml -- $maybe_bpf_sdk "$@"

View File

@@ -175,30 +175,6 @@ EOF
"Stable-perf skipped as no relevant files were modified" "Stable-perf skipped as no relevant files were modified"
fi fi
# Downstream backwards compatibility
if affects \
.rs$ \
Cargo.lock$ \
Cargo.toml$ \
^ci/rust-version.sh \
^ci/test-stable-perf.sh \
^ci/test-stable.sh \
^ci/test-local-cluster.sh \
^core/build.rs \
^fetch-perf-libs.sh \
^programs/ \
^sdk/ \
^scripts/build-downstream-projects.sh \
; then
cat >> "$output_file" <<"EOF"
- command: "scripts/build-downstream-projects.sh"
name: "downstream-projects"
timeout_in_minutes: 30
EOF
else
annotate --style info \
"downstream-projects skipped as no relevant files were modified"
fi
# Benches... # Benches...
if affects \ if affects \
.rs$ \ .rs$ \

View File

@@ -89,20 +89,12 @@ BETA_CHANNEL_LATEST_TAG=${beta_tag:+v$beta_tag}
STABLE_CHANNEL_LATEST_TAG=${stable_tag:+v$stable_tag} STABLE_CHANNEL_LATEST_TAG=${stable_tag:+v$stable_tag}
if [[ -n $CI_BASE_BRANCH ]]; then if [[ $CI_BRANCH = "$STABLE_CHANNEL" ]]; then
BRANCH="$CI_BASE_BRANCH" CHANNEL=stable
elif [[ -n $CI_BRANCH ]]; then elif [[ $CI_BRANCH = "$EDGE_CHANNEL" ]]; then
BRANCH="$CI_BRANCH" CHANNEL=edge
fi elif [[ $CI_BRANCH = "$BETA_CHANNEL" ]]; then
CHANNEL=beta
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
fi fi
echo EDGE_CHANNEL="$EDGE_CHANNEL" echo EDGE_CHANNEL="$EDGE_CHANNEL"

View File

@@ -1,4 +1,4 @@
FROM solanalabs/rust:1.46.0 FROM solanalabs/rust:1.45.1
ARG date ARG date
RUN set -x \ RUN set -x \

View File

@@ -20,7 +20,7 @@ To update the pinned version:
specific YYYY-MM-DD that is desired (default is today's build). specific YYYY-MM-DD that is desired (default is today's build).
Check https://rust-lang.github.io/rustup-components-history/ for build Check https://rust-lang.github.io/rustup-components-history/ for build
status 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` 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]...` 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 to confirm the new nightly image builds. Fix any issues as needed

View File

@@ -1,6 +1,6 @@
# Note: when the rust version is changed also modify # Note: when the rust version is changed also modify
# ci/rust-version.sh to pick up the new image tag # 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. # Add Google Protocol Buffers for Libra's metrics library.
ENV PROTOC_VERSION 3.8.0 ENV PROTOC_VERSION 3.8.0

View File

@@ -26,9 +26,6 @@ declare print_free_tree=(
':runtime/src/**.rs' ':runtime/src/**.rs'
':sdk/bpf/rust/rust-utils/**.rs' ':sdk/bpf/rust/rust-utils/**.rs'
':sdk/**.rs' ':sdk/**.rs'
':^sdk/cargo-build-bpf/**.rs'
':^sdk/program/src/program_option.rs'
':^sdk/program/src/program_stubs.rs'
':programs/**.rs' ':programs/**.rs'
':^**bin**.rs' ':^**bin**.rs'
':^**bench**.rs' ':^**bench**.rs'

View File

@@ -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 # 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 # crates.io. Along the way it also ensures there are no circular dependencies
@@ -45,27 +45,21 @@ def get_packages():
sorted_dependency_graph = [] sorted_dependency_graph = []
max_iterations = pow(len(dependency_graph),2) max_iterations = pow(len(dependency_graph),2)
while dependency_graph: while dependency_graph:
deleted_packages = []
if max_iterations == 0: if max_iterations == 0:
# One day be more helpful and find the actual cycle for the user... # One day be more helpful and find the actual cycle for the user...
sys.exit('Error: Circular dependency suspected between these packages: \n {}\n'.format('\n '.join(dependency_graph.keys()))) sys.exit('Error: Circular dependency suspected between these packages: \n {}\n'.format('\n '.join(dependency_graph.keys())))
max_iterations -= 1 max_iterations -= 1
for package, dependencies in dependency_graph.items(): for package, dependencies in dependency_graph.items():
if package in deleted_packages:
continue
for dependency in dependencies: for dependency in dependencies:
if dependency in dependency_graph: if dependency in dependency_graph:
break break
else: else:
deleted_packages.append(package) del dependency_graph[package]
sorted_dependency_graph.append((package, manifest_path[package])) sorted_dependency_graph.append((package, manifest_path[package]))
dependency_graph = {p: d for p, d in dependency_graph.items() if not p in deleted_packages }
return sorted_dependency_graph return sorted_dependency_graph
for package, manifest in get_packages(): for package, manifest in get_packages():
print(os.path.relpath(manifest)) print os.path.relpath(manifest)

View File

@@ -4,8 +4,6 @@ cd "$(dirname "$0")/.."
source ci/semver_bash/semver.sh source ci/semver_bash/semver.sh
source ci/rust-version.sh stable source ci/rust-version.sh stable
cargo="$(readlink -f ./cargo)"
# shellcheck disable=SC2086 # shellcheck disable=SC2086
is_crate_version_uploaded() { is_crate_version_uploaded() {
name=$1 name=$1
@@ -40,7 +38,7 @@ for Cargo_toml in $Cargo_tomls; do
crate_name=$(grep -m 1 '^name = ' "$Cargo_toml" | cut -f 3 -d ' ' | tr -d \") crate_name=$(grep -m 1 '^name = ' "$Cargo_toml" | cut -f 3 -d ' ' | tr -d \")
if grep -q "^publish = false" "$Cargo_toml"; then if grep -q "^publish = false" "$Cargo_toml"; then
echo "$crate_name is marked as unpublishable" echo "$crate_name is is marked as unpublishable"
continue continue
fi fi
@@ -68,11 +66,11 @@ for Cargo_toml in $Cargo_tomls; do
( (
set -x set -x
rm -rf crate-test rm -rf crate-test
"$cargo" stable init crate-test cargo +"$rust_stable" init crate-test
cd crate-test/ cd crate-test/
echo "${crate_name} = \"${expectedCrateVersion}\"" >> Cargo.toml echo "${crate_name} = \"${expectedCrateVersion}\"" >> Cargo.toml
echo "[workspace]" >> Cargo.toml echo "[workspace]" >> Cargo.toml
"$cargo" stable check cargo +"$rust_stable" check
) && really_uploaded=1 ) && really_uploaded=1
if ((really_uploaded)); then if ((really_uploaded)); then
break; break;

View File

@@ -91,15 +91,17 @@ echo --- Creating release tarball
cp "${RELEASE_BASENAME}"/version.yml "${TARBALL_BASENAME}"-$TARGET.yml 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= MAYBE_TARBALLS=
if [[ "$CI_OS_NAME" = linux ]]; then if [[ "$CI_OS_NAME" = linux ]]; then
metrics/create-metrics-tarball.sh
( (
set -x set -x
sdk/bpf/scripts/package.sh sdk/bpf/scripts/package.sh
[[ -f bpf-sdk.tar.bz2 ]] [[ -f bpf-sdk.tar.bz2 ]]
) )
MAYBE_TARBALLS="bpf-sdk.tar.bz2" MAYBE_TARBALLS="bpf-sdk.tar.bz2 solana-metrics.tar.bz2"
fi fi
source ci/upload-ci-artifact.sh source ci/upload-ci-artifact.sh
@@ -124,7 +126,7 @@ for file in "${TARBALL_BASENAME}"-$TARGET.tar.bz2 "${TARBALL_BASENAME}"-$TARGET.
/usr/bin/s3cmd --acl-public put /solana/"$file" s3://release.solana.com/"$CHANNEL_OR_TAG"/"$file" /usr/bin/s3cmd --acl-public put /solana/"$file" s3://release.solana.com/"$CHANNEL_OR_TAG"/"$file"
echo Published to: echo Published to:
$DRYRUN ci/format-url.sh https://release.solana.com/"$CHANNEL_OR_TAG"/"$file" $DRYRUN ci/format-url.sh http://release.solana.com/"$CHANNEL_OR_TAG"/"$file"
) )
if [[ -n $TAG ]]; then if [[ -n $TAG ]]; then
@@ -147,30 +149,4 @@ for file in "${TARBALL_BASENAME}"-$TARGET.tar.bz2 "${TARBALL_BASENAME}"-$TARGET.
fi fi
done done
# Create install wrapper for release.solana.com
if [[ -n $BUILDKITE ]]; then
cat > release.solana.com-install <<EOF
SOLANA_RELEASE=$CHANNEL_OR_TAG
SOLANA_INSTALL_INIT_ARGS=$CHANNEL_OR_TAG
SOLANA_DOWNLOAD_ROOT=http://release.solana.com
EOF
cat install/solana-install-init.sh >> release.solana.com-install
echo --- AWS S3 Store: "install"
(
set -x
$DRYRUN docker run \
--rm \
--env AWS_ACCESS_KEY_ID \
--env AWS_SECRET_ACCESS_KEY \
--volume "$PWD:/solana" \
eremite/aws-cli:2018.12.18 \
/usr/bin/s3cmd --acl-public put /solana/release.solana.com-install s3://release.solana.com/"$CHANNEL_OR_TAG"/install
echo Published to:
$DRYRUN ci/format-url.sh https://release.solana.com/"$CHANNEL_OR_TAG"/install
)
fi
echo --- ok echo --- ok

View File

@@ -18,13 +18,13 @@
if [[ -n $RUST_STABLE_VERSION ]]; then if [[ -n $RUST_STABLE_VERSION ]]; then
stable_version="$RUST_STABLE_VERSION" stable_version="$RUST_STABLE_VERSION"
else else
stable_version=1.46.0 stable_version=1.45.1
fi fi
if [[ -n $RUST_NIGHTLY_VERSION ]]; then if [[ -n $RUST_NIGHTLY_VERSION ]]; then
nightly_version="$RUST_NIGHTLY_VERSION" nightly_version="$RUST_NIGHTLY_VERSION"
else else
nightly_version=2020-08-17 nightly_version=2020-07-27
fi fi

View File

@@ -76,7 +76,7 @@ RestartForceExitStatus=SIGPIPE
TimeoutStartSec=10 TimeoutStartSec=10
TimeoutStopSec=0 TimeoutStopSec=0
KillMode=process KillMode=process
LimitNOFILE=500000 LimitNOFILE=65536
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

View File

@@ -8,5 +8,5 @@ source "$HERE"/utils.sh
ensure_env || exit 1 ensure_env || exit 1
# Allow more files to be opened by a user # Allow more files to be opened by a user
echo "* - nofile 500000" > /etc/security/limits.d/90-solana-nofiles.conf sed -i 's/^\(# End of file\)/* soft nofile 65535\n\n\1/' /etc/security/limits.conf

View File

@@ -6,8 +6,7 @@ source ci/_
source ci/upload-ci-artifact.sh source ci/upload-ci-artifact.sh
eval "$(ci/channel-info.sh)" eval "$(ci/channel-info.sh)"
source ci/rust-version.sh all
cargo="$(readlink -f "./cargo")"
set -o pipefail set -o pipefail
export RUST_BACKTRACE=1 export RUST_BACKTRACE=1
@@ -28,35 +27,35 @@ test -d target/debug/bpf && find target/debug/bpf -name '*.d' -delete
test -d target/release/bpf && find target/release/bpf -name '*.d' -delete test -d target/release/bpf && find target/release/bpf -name '*.d' -delete
# Ensure all dependencies are built # Ensure all dependencies are built
_ "$cargo" nightly build --release _ cargo +$rust_nightly build --release
# Remove "BENCH_FILE", if it exists so that the following commands can append # Remove "BENCH_FILE", if it exists so that the following commands can append
rm -f "$BENCH_FILE" rm -f "$BENCH_FILE"
# Run sdk benches # Run sdk benches
_ "$cargo" nightly bench --manifest-path sdk/Cargo.toml ${V:+--verbose} \ _ cargo +$rust_nightly bench --manifest-path sdk/Cargo.toml ${V:+--verbose} \
-- -Z unstable-options --format=json | tee -a "$BENCH_FILE" -- -Z unstable-options --format=json | tee -a "$BENCH_FILE"
# Run runtime benches # Run runtime benches
_ "$cargo" nightly bench --manifest-path runtime/Cargo.toml ${V:+--verbose} \ _ cargo +$rust_nightly bench --manifest-path runtime/Cargo.toml ${V:+--verbose} \
-- -Z unstable-options --format=json | tee -a "$BENCH_FILE" -- -Z unstable-options --format=json | tee -a "$BENCH_FILE"
# Run core benches # Run core benches
_ "$cargo" nightly bench --manifest-path core/Cargo.toml ${V:+--verbose} \ _ cargo +$rust_nightly bench --manifest-path core/Cargo.toml ${V:+--verbose} \
-- -Z unstable-options --format=json | tee -a "$BENCH_FILE" -- -Z unstable-options --format=json | tee -a "$BENCH_FILE"
# Run bpf benches # Run bpf benches
_ "$cargo" nightly bench --manifest-path programs/bpf/Cargo.toml ${V:+--verbose} --features=bpf_c \ _ cargo +$rust_nightly bench --manifest-path programs/bpf/Cargo.toml ${V:+--verbose} --features=bpf_c \
-- -Z unstable-options --format=json --nocapture | tee -a "$BENCH_FILE" -- -Z unstable-options --format=json --nocapture | tee -a "$BENCH_FILE"
# Run banking/accounts bench. Doesn't require nightly, but use since it is already built. # Run banking/accounts bench. Doesn't require nightly, but use since it is already built.
_ "$cargo" nightly run --release --manifest-path banking-bench/Cargo.toml ${V:+--verbose} | tee -a "$BENCH_FILE" _ cargo +$rust_nightly run --release --manifest-path banking-bench/Cargo.toml ${V:+--verbose} | tee -a "$BENCH_FILE"
_ "$cargo" nightly run --release --manifest-path accounts-bench/Cargo.toml ${V:+--verbose} -- --num_accounts 10000 --num_slots 4 | tee -a "$BENCH_FILE" _ cargo +$rust_nightly run --release --manifest-path accounts-bench/Cargo.toml ${V:+--verbose} -- --num_accounts 10000 --num_slots 4 | tee -a "$BENCH_FILE"
# `solana-upload-perf` disabled as it can take over 30 minutes to complete for some # `solana-upload-perf` disabled as it can take over 30 minutes to complete for some
# reason # reason
exit 0 exit 0
_ "$cargo" nightly run --release --package solana-upload-perf \ _ cargo +$rust_nightly run --release --package solana-upload-perf \
-- "$BENCH_FILE" "$TARGET_BRANCH" "$UPLOAD_METRICS" | tee "$BENCH_ARTIFACT" -- "$BENCH_FILE" "$TARGET_BRANCH" "$UPLOAD_METRICS" | tee "$BENCH_ARTIFACT"
upload-ci-artifact "$BENCH_FILE" upload-ci-artifact "$BENCH_FILE"

View File

@@ -8,9 +8,6 @@ source ci/_
source ci/rust-version.sh stable source ci/rust-version.sh stable
source ci/rust-version.sh nightly source ci/rust-version.sh nightly
eval "$(ci/channel-info.sh)" eval "$(ci/channel-info.sh)"
cargo="$(readlink -f "./cargo")"
scripts/increment-cargo-version.sh check
echo --- build environment echo --- build environment
( (
@@ -19,14 +16,14 @@ echo --- build environment
rustup run "$rust_stable" rustc --version --verbose rustup run "$rust_stable" rustc --version --verbose
rustup run "$rust_nightly" rustc --version --verbose rustup run "$rust_nightly" rustc --version --verbose
"$cargo" stable --version --verbose cargo +"$rust_stable" --version --verbose
"$cargo" nightly --version --verbose cargo +"$rust_nightly" --version --verbose
"$cargo" stable clippy --version --verbose cargo +"$rust_stable" clippy --version --verbose
"$cargo" nightly clippy --version --verbose cargo +"$rust_nightly" clippy --version --verbose
# audit is done only with stable # audit is done only with stable
"$cargo" stable audit --version cargo +"$rust_stable" audit --version
) )
export RUST_BACKTRACE=1 export RUST_BACKTRACE=1
@@ -44,52 +41,32 @@ if [[ $CI_BASE_BRANCH = "$EDGE_CHANNEL" ]]; then
echo "$0: [tree (for outdated Cargo.lock sync)|check (for compilation error)|update -p foo --precise x.y.z (for your Cargo.toml update)] ..." >&2 echo "$0: [tree (for outdated Cargo.lock sync)|check (for compilation error)|update -p foo --precise x.y.z (for your Cargo.toml update)] ..." >&2
exit "$check_status" exit "$check_status"
fi fi
# Ensure nightly and --benches
_ scripts/cargo-for-all-lock-files.sh +"$rust_nightly" check --locked --all-targets
else else
echo "Note: cargo-for-all-lock-files.sh skipped because $CI_BASE_BRANCH != $EDGE_CHANNEL" echo "Note: cargo-for-all-lock-files.sh skipped because $CI_BASE_BRANCH != $EDGE_CHANNEL"
fi fi
# Ensure nightly and --benches
_ scripts/cargo-for-all-lock-files.sh +"$rust_nightly" check --locked --all-targets
_ ci/order-crates-for-publishing.py _ ci/order-crates-for-publishing.py
_ "$cargo" stable fmt --all -- --check _ cargo +"$rust_stable" fmt --all -- --check
# -Z... is needed because of clippy bug: https://github.com/rust-lang/rust-clippy/issues/4612 # -Z... is needed because of clippy bug: https://github.com/rust-lang/rust-clippy/issues/4612
# run nightly clippy for `sdk/` as there's a moderate amount of nightly-only code there # run nightly clippy for `sdk/` as there's a moderate amount of nightly-only code there
_ "$cargo" nightly clippy \ _ cargo +"$rust_nightly" clippy -Zunstable-options --workspace --all-targets -- --deny=warnings
-Zunstable-options --workspace --all-targets \
-- --deny=warnings --allow=clippy::stable_sort_primitive
cargo_audit_ignores=( _ scripts/cargo-for-all-lock-files.sh +"$rust_stable" audit --ignore RUSTSEC-2020-0002 --ignore RUSTSEC-2020-0008
# failure is officially deprecated/unmaintained
#
# Blocked on multiple upstream crates removing their `failure` dependency.
--ignore RUSTSEC-2020-0036
# `net2` crate has been deprecated; use `socket2` instead
#
# Blocked on https://github.com/paritytech/jsonrpc/issues/575
--ignore RUSTSEC-2020-0016
# stdweb is unmaintained
#
# Blocked on multiple upstream crates removing their `stdweb` dependency.
--ignore RUSTSEC-2020-0056
)
_ scripts/cargo-for-all-lock-files.sh +"$rust_stable" audit "${cargo_audit_ignores[@]}"
{ {
cd programs/bpf cd programs/bpf
_ "$cargo" stable audit _ cargo +"$rust_stable" audit
for project in rust/*/ ; do for project in rust/*/ ; do
echo "+++ do_bpf_checks $project" echo "+++ do_bpf_checks $project"
( (
cd "$project" cd "$project"
_ "$cargo" stable fmt -- --check _ cargo +"$rust_stable" fmt -- --check
_ "$cargo" nightly test _ cargo +"$rust_nightly" test
_ "$cargo" nightly clippy -- --deny=warnings \ _ cargo +"$rust_nightly" clippy -- --deny=warnings --allow=clippy::missing_safety_doc
--allow=clippy::missing_safety_doc \
--allow=clippy::stable_sort_primitive
) )
done done
} }

View File

@@ -2,8 +2,6 @@
set -e set -e
cd "$(dirname "$0")/.." cd "$(dirname "$0")/.."
cargo="$(readlink -f "./cargo")"
source ci/_ source ci/_
annotate() { annotate() {
@@ -39,15 +37,12 @@ NPROC=$((NPROC>14 ? 14 : NPROC))
echo "Executing $testName" echo "Executing $testName"
case $testName in case $testName in
test-stable) test-stable)
_ "$cargo" stable test --jobs "$NPROC" --all --exclude solana-local-cluster ${V:+--verbose} -- --nocapture _ cargo +"$rust_stable" test --jobs "$NPROC" --all --exclude solana-local-cluster ${V:+--verbose} -- --nocapture
;; ;;
test-stable-perf) test-stable-perf)
# BPF solana-sdk legacy compile test
./cargo-build-bpf --manifest-path sdk/Cargo.toml
# BPF program tests # BPF program tests
_ make -C programs/bpf/c tests _ make -C programs/bpf/c tests
_ "$cargo" stable test \ _ cargo +"$rust_stable" test \
--manifest-path programs/bpf/Cargo.toml \ --manifest-path programs/bpf/Cargo.toml \
--no-default-features --features=bpf_c,bpf_rust -- --nocapture --no-default-features --features=bpf_c,bpf_rust -- --nocapture
@@ -67,13 +62,13 @@ test-stable-perf)
export SOLANA_CUDA=1 export SOLANA_CUDA=1
fi fi
_ "$cargo" stable build --bins ${V:+--verbose} _ cargo +"$rust_stable" build --bins ${V:+--verbose}
_ "$cargo" stable test --package solana-perf --package solana-ledger --package solana-core --lib ${V:+--verbose} -- --nocapture _ cargo +"$rust_stable" test --package solana-perf --package solana-ledger --package solana-core --lib ${V:+--verbose} -- --nocapture
_ "$cargo" stable run --manifest-path poh-bench/Cargo.toml ${V:+--verbose} -- --hashes-per-tick 10 _ cargo +"$rust_stable" run --manifest-path poh-bench/Cargo.toml ${V:+--verbose} -- --hashes-per-tick 10
;; ;;
test-local-cluster) test-local-cluster)
_ "$cargo" stable build --release --bins ${V:+--verbose} _ cargo +"$rust_stable" build --release --bins ${V:+--verbose}
_ "$cargo" stable test --release --package solana-local-cluster ${V:+--verbose} -- --nocapture --test-threads=1 _ cargo +"$rust_stable" test --release --package solana-local-cluster ${V:+--verbose} -- --nocapture --test-threads=1
exit 0 exit 0
;; ;;
*) *)

View File

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

View File

@@ -15,7 +15,7 @@ pub fn commitment_arg_with_default<'a, 'b>(default_value: &'static str) -> Arg<'
Arg::with_name(COMMITMENT_ARG.name) Arg::with_name(COMMITMENT_ARG.name)
.long(COMMITMENT_ARG.long) .long(COMMITMENT_ARG.long)
.takes_value(true) .takes_value(true)
.possible_values(&["recent", "single", "singleGossip", "root", "max"]) .possible_values(&["recent", "single", "root", "max"])
.default_value(default_value) .default_value(default_value)
.value_name("COMMITMENT_LEVEL") .value_name("COMMITMENT_LEVEL")
.help(COMMITMENT_ARG.help) .help(COMMITMENT_ARG.help)

View File

@@ -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)
}

View File

@@ -8,7 +8,6 @@ use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{ use solana_sdk::{
clock::UnixTimestamp, clock::UnixTimestamp,
commitment_config::CommitmentConfig, commitment_config::CommitmentConfig,
genesis_config::ClusterType,
native_token::sol_to_lamports, native_token::sol_to_lamports,
pubkey::Pubkey, pubkey::Pubkey,
signature::{read_keypair_file, Keypair, Signature, Signer}, 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) 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> { pub fn commitment_of(matches: &ArgMatches<'_>, name: &str) -> Option<CommitmentConfig> {
matches.value_of(name).map(|value| match value { matches.value_of(name).map(|value| match value {
"max" => CommitmentConfig::max(), "max" => CommitmentConfig::max(),
@@ -228,8 +223,8 @@ mod tests {
assert_eq!(values_of(&matches, "multiple"), Some(vec![50, 39])); assert_eq!(values_of(&matches, "multiple"), Some(vec![50, 39]));
assert_eq!(values_of::<u64>(&matches, "single"), None); assert_eq!(values_of::<u64>(&matches, "single"), None);
let pubkey0 = solana_sdk::pubkey::new_rand(); let pubkey0 = Pubkey::new_rand();
let pubkey1 = solana_sdk::pubkey::new_rand(); let pubkey1 = Pubkey::new_rand();
let matches = app().clone().get_matches_from(vec![ let matches = app().clone().get_matches_from(vec![
"test", "test",
"--multiple", "--multiple",
@@ -251,7 +246,7 @@ mod tests {
assert_eq!(value_of(&matches, "single"), Some(50)); assert_eq!(value_of(&matches, "single"), Some(50));
assert_eq!(value_of::<u64>(&matches, "multiple"), None); assert_eq!(value_of::<u64>(&matches, "multiple"), None);
let pubkey = solana_sdk::pubkey::new_rand(); let pubkey = Pubkey::new_rand();
let matches = app() let matches = app()
.clone() .clone()
.get_matches_from(vec!["test", "--single", &pubkey.to_string()]); .get_matches_from(vec!["test", "--single", &pubkey.to_string()]);
@@ -331,8 +326,8 @@ mod tests {
#[test] #[test]
fn test_pubkeys_sigs_of() { fn test_pubkeys_sigs_of() {
let key1 = solana_sdk::pubkey::new_rand(); let key1 = Pubkey::new_rand();
let key2 = solana_sdk::pubkey::new_rand(); let key2 = Pubkey::new_rand();
let sig1 = Keypair::new().sign_message(&[0u8]); let sig1 = Keypair::new().sign_message(&[0u8]);
let sig2 = Keypair::new().sign_message(&[1u8]); let sig2 = Keypair::new().sign_message(&[1u8]);
let signer1 = format!("{}={}", key1, sig1); let signer1 = format!("{}={}", key1, sig1);

View File

@@ -11,7 +11,6 @@ use solana_remote_wallet::{
remote_wallet::{maybe_wallet_manager, RemoteWalletError, RemoteWalletManager}, remote_wallet::{maybe_wallet_manager, RemoteWalletError, RemoteWalletManager},
}; };
use solana_sdk::{ use solana_sdk::{
hash::Hash,
pubkey::Pubkey, pubkey::Pubkey,
signature::{ signature::{
keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair, keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair,
@@ -26,81 +25,6 @@ use std::{
sync::Arc, 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 { pub enum KeypairUrl {
Ask, Ask,
Filepath(String), Filepath(String),
@@ -154,7 +78,7 @@ pub fn signer_from_path(
KeypairUrl::Filepath(path) => match read_keypair_file(&path) { KeypairUrl::Filepath(path) => match read_keypair_file(&path) {
Err(e) => Err(std::io::Error::new( Err(e) => Err(std::io::Error::new(
std::io::ErrorKind::Other, 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()), .into()),
Ok(file) => Ok(Box::new(file)), Ok(file) => Ok(Box::new(file)),
@@ -225,7 +149,7 @@ pub fn resolve_signer_from_path(
KeypairUrl::Filepath(path) => match read_keypair_file(&path) { KeypairUrl::Filepath(path) => match read_keypair_file(&path) {
Err(e) => Err(std::io::Error::new( Err(e) => Err(std::io::Error::new(
std::io::ErrorKind::Other, 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()), .into()),
Ok(_) => Ok(Some(path.to_string())), 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)? keypair_from_seed_phrase_and_passphrase(&seed_phrase, &passphrase)?
} else { } else {
let sanitized = sanitize_seed_phrase(seed_phrase); let sanitized = sanitize_seed_phrase(seed_phrase);
let parse_language_fn = || { let mnemonic = Mnemonic::from_phrase(&sanitized, Language::English)?;
for language in &[
Language::English,
Language::ChineseSimplified,
Language::ChineseTraditional,
Language::Japanese,
Language::Spanish,
Language::Korean,
Language::French,
Language::Italian,
] {
if let Ok(mnemonic) = Mnemonic::from_phrase(&sanitized, *language) {
return Ok(mnemonic);
}
}
Err("Can't get mnemonic from seed phrases")
};
let mnemonic = parse_language_fn()?;
let passphrase = prompt_passphrase(&passphrase_prompt)?; let passphrase = prompt_passphrase(&passphrase_prompt)?;
let seed = Seed::new(&mnemonic, &passphrase); let seed = Seed::new(&mnemonic, &passphrase);
keypair_from_seed(seed.as_bytes())? keypair_from_seed(seed.as_bytes())?

View File

@@ -24,9 +24,7 @@ impl std::fmt::Debug for DisplayError {
} }
pub mod commitment; pub mod commitment;
pub mod fee_payer;
pub mod input_parsers; pub mod input_parsers;
pub mod input_validators; pub mod input_validators;
pub mod keypair; pub mod keypair;
pub mod nonce;
pub mod offline; pub mod offline;

View File

@@ -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),
)
}
}

View File

@@ -1,5 +1,4 @@
use crate::{input_validators::*, ArgConstant}; use crate::ArgConstant;
use clap::{App, Arg};
pub const BLOCKHASH_ARG: ArgConstant<'static> = ArgConstant { pub const BLOCKHASH_ARG: ArgConstant<'static> = ArgConstant {
name: "blockhash", name: "blockhash",
@@ -18,61 +17,3 @@ pub const SIGNER_ARG: ArgConstant<'static> = ArgConstant {
long: "signer", long: "signer",
help: "Provide a public-key/signature pair for the transaction", 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 {})
}
}

View File

@@ -3,13 +3,13 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2018" edition = "2018"
name = "solana-cli-config" name = "solana-cli-config"
description = "Blockchain, Rebuilt for Scale" description = "Blockchain, Rebuilt for Scale"
version = "1.4.7" version = "1.3.2"
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0" license = "Apache-2.0"
homepage = "https://solana.com/" homepage = "https://solana.com/"
[dependencies] [dependencies]
dirs-next = "2.0.0" dirs = "2.0.2"
lazy_static = "1.4.0" lazy_static = "1.4.0"
serde = "1.0.112" serde = "1.0.112"
serde_derive = "1.0.103" serde_derive = "1.0.103"

View File

@@ -5,7 +5,7 @@ use url::Url;
lazy_static! { lazy_static! {
pub static ref CONFIG_FILE: Option<String> = { pub static ref CONFIG_FILE: Option<String> = {
dirs_next::home_dir().map(|mut path| { dirs::home_dir().map(|mut path| {
path.extend(&[".config", "solana", "cli", "config.yml"]); path.extend(&[".config", "solana", "cli", "config.yml"]);
path.to_str().unwrap().to_string() path.to_str().unwrap().to_string()
}) })
@@ -25,7 +25,7 @@ pub struct Config {
impl Default for Config { impl Default for Config {
fn default() -> Self { fn default() -> Self {
let keypair_path = { let keypair_path = {
let mut keypair_path = dirs_next::home_dir().expect("home directory"); let mut keypair_path = dirs::home_dir().expect("home directory");
keypair_path.extend(&[".config", "solana", "id.json"]); keypair_path.extend(&[".config", "solana", "id.json"]);
keypair_path.to_str().unwrap().to_string() keypair_path.to_str().unwrap().to_string()
}; };
@@ -76,17 +76,6 @@ impl Config {
ws_url.to_string() ws_url.to_string()
} }
pub fn compute_rpc_banks_url(json_rpc_url: &str) -> String {
let json_rpc_url: Option<Url> = json_rpc_url.parse().ok();
if json_rpc_url.is_none() {
return "".to_string();
}
let mut url = json_rpc_url.unwrap();
let port = url.port().unwrap_or(8899);
url.set_port(Some(port + 3)).expect("unable to set port");
url.to_string()
}
pub fn import_address_labels<P>(&mut self, filename: P) -> Result<(), io::Error> pub fn import_address_labels<P>(&mut self, filename: P) -> Result<(), io::Error>
where where
P: AsRef<Path>, P: AsRef<Path>,
@@ -133,28 +122,4 @@ mod test {
assert_eq!(Config::compute_websocket_url(&"garbage"), String::new()); assert_eq!(Config::compute_websocket_url(&"garbage"), String::new());
} }
#[test]
fn compute_rpc_banks_url() {
assert_eq!(
Config::compute_rpc_banks_url(&"http://devnet.solana.com"),
"http://devnet.solana.com:8902/".to_string()
);
assert_eq!(
Config::compute_rpc_banks_url(&"https://devnet.solana.com"),
"https://devnet.solana.com:8902/".to_string()
);
assert_eq!(
Config::compute_rpc_banks_url(&"http://example.com:8899"),
"http://example.com:8902/".to_string()
);
assert_eq!(
Config::compute_rpc_banks_url(&"https://example.com:1234"),
"https://example.com:1237/".to_string()
);
assert_eq!(Config::compute_rpc_banks_url(&"garbage"), String::new());
}
} }

View File

@@ -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.7"
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.7" }
solana-clap-utils = { path = "../clap-utils", version = "1.4.7" }
solana-client = { path = "../client", version = "1.4.7" }
solana-sdk = { path = "../sdk", version = "1.4.7" }
solana-stake-program = { path = "../programs/stake", version = "1.4.7" }
solana-transaction-status = { path = "../transaction-status", version = "1.4.7" }
solana-vote-program = { path = "../programs/vote", version = "1.4.7" }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -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)
}
}

View File

@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2018" edition = "2018"
name = "solana-cli" name = "solana-cli"
description = "Blockchain, Rebuilt for Scale" description = "Blockchain, Rebuilt for Scale"
version = "1.4.7" version = "1.3.2"
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0" license = "Apache-2.0"
homepage = "https://solana.com/" homepage = "https://solana.com/"
@@ -16,41 +16,40 @@ clap = "2.33.1"
criterion-stats = "0.3.0" criterion-stats = "0.3.0"
ctrlc = { version = "3.1.5", features = ["termination"] } ctrlc = { version = "3.1.5", features = ["termination"] }
console = "0.11.3" console = "0.11.3"
dirs-next = "2.0.0" dirs = "2.0.2"
log = "0.4.8" log = "0.4.8"
Inflector = "0.11.4" Inflector = "0.11.4"
indicatif = "0.15.0" indicatif = "0.15.0"
humantime = "2.0.1" humantime = "2.0.1"
num-traits = "0.2" num-traits = "0.2"
pretty-hex = "0.1.1" pretty-hex = "0.1.1"
reqwest = { version = "0.10.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 = "1.0.112"
serde_derive = "1.0.103" serde_derive = "1.0.103"
serde_json = "1.0.56" serde_json = "1.0.56"
solana-account-decoder = { path = "../account-decoder", version = "1.4.7" } solana-account-decoder = { path = "../account-decoder", version = "1.3.2" }
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "1.4.7" } solana-budget-program = { path = "../programs/budget", version = "1.3.2" }
solana-clap-utils = { path = "../clap-utils", version = "1.4.7" } solana-clap-utils = { path = "../clap-utils", version = "1.3.2" }
solana-cli-config = { path = "../cli-config", version = "1.4.7" } solana-cli-config = { path = "../cli-config", version = "1.3.2" }
solana-cli-output = { path = "../cli-output", version = "1.4.7" } solana-client = { path = "../client", version = "1.3.2" }
solana-client = { path = "../client", version = "1.4.7" } solana-config-program = { path = "../programs/config", version = "1.3.2" }
solana-config-program = { path = "../programs/config", version = "1.4.7" } solana-faucet = { path = "../faucet", version = "1.3.2" }
solana-faucet = { path = "../faucet", version = "1.4.7" } solana-logger = { path = "../logger", version = "1.3.2" }
solana-logger = { path = "../logger", version = "1.4.7" } solana-net-utils = { path = "../net-utils", version = "1.3.2" }
solana-net-utils = { path = "../net-utils", version = "1.4.7" } solana-remote-wallet = { path = "../remote-wallet", version = "1.3.2" }
solana_rbpf = "=0.1.32" solana-runtime = { path = "../runtime", version = "1.3.2" }
solana-remote-wallet = { path = "../remote-wallet", version = "1.4.7" } solana-sdk = { path = "../sdk", version = "1.3.2" }
solana-sdk = { path = "../sdk", version = "1.4.7" } solana-stake-program = { path = "../programs/stake", version = "1.3.2" }
solana-stake-program = { path = "../programs/stake", version = "1.4.7" } solana-transaction-status = { path = "../transaction-status", version = "1.3.2" }
solana-transaction-status = { path = "../transaction-status", version = "1.4.7" } solana-version = { path = "../version", version = "1.3.2" }
solana-version = { path = "../version", version = "1.4.7" } solana-vote-program = { path = "../programs/vote", version = "1.3.2" }
solana-vote-program = { path = "../programs/vote", version = "1.4.7" } solana-vote-signer = { path = "../vote-signer", version = "1.3.2" }
solana-vote-signer = { path = "../vote-signer", version = "1.4.7" }
thiserror = "1.0.20" thiserror = "1.0.20"
tiny-bip39 = "0.7.0"
url = "2.1.1" url = "2.1.1"
[dev-dependencies] [dev-dependencies]
solana-core = { path = "../core", version = "1.4.7" } solana-core = { path = "../core", version = "1.3.2" }
solana-budget-program = { path = "../programs/budget", version = "1.3.2" }
tempfile = "3.1.0" tempfile = "3.1.0"
[[bin]] [[bin]]

View File

@@ -54,42 +54,12 @@ pub fn check_account_for_multiple_fees_with_commitment(
fee_calculator: &FeeCalculator, fee_calculator: &FeeCalculator,
messages: &[&Message], messages: &[&Message],
commitment: CommitmentConfig, commitment: CommitmentConfig,
) -> Result<(), CliError> {
check_account_for_spend_multiple_fees_with_commitment(
rpc_client,
account_pubkey,
0,
fee_calculator,
messages,
commitment,
)
}
pub fn check_account_for_spend_multiple_fees_with_commitment(
rpc_client: &RpcClient,
account_pubkey: &Pubkey,
balance: u64,
fee_calculator: &FeeCalculator,
messages: &[&Message],
commitment: CommitmentConfig,
) -> Result<(), CliError> { ) -> Result<(), CliError> {
let fee = calculate_fee(fee_calculator, messages); let fee = calculate_fee(fee_calculator, messages);
if !check_account_for_balance_with_commitment( if !check_account_for_balance_with_commitment(rpc_client, account_pubkey, fee, commitment)
rpc_client, .map_err(Into::<ClientError>::into)?
account_pubkey,
balance + fee,
commitment,
)
.map_err(Into::<ClientError>::into)?
{ {
if balance > 0 { return Err(CliError::InsufficientFundsForFee(lamports_to_sol(fee)));
return Err(CliError::InsufficientFundsForSpendAndFee(
lamports_to_sol(balance),
lamports_to_sol(fee),
));
} else {
return Err(CliError::InsufficientFundsForFee(lamports_to_sol(fee)));
}
} }
Ok(()) Ok(())
} }
@@ -161,7 +131,7 @@ mod tests {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext { slot: 1 },
value: json!(account_balance), value: json!(account_balance),
}); });
let pubkey = solana_sdk::pubkey::new_rand(); let pubkey = Pubkey::new_rand();
let fee_calculator = FeeCalculator::new(1); let fee_calculator = FeeCalculator::new(1);
let pubkey0 = Pubkey::new(&[0; 32]); let pubkey0 = Pubkey::new(&[0; 32]);
@@ -221,7 +191,7 @@ mod tests {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext { slot: 1 },
value: json!(account_balance), value: json!(account_balance),
}); });
let pubkey = solana_sdk::pubkey::new_rand(); let pubkey = Pubkey::new_rand();
let mut mocks = HashMap::new(); let mut mocks = HashMap::new();
mocks.insert(RpcRequest::GetBalance, account_balance_response); mocks.insert(RpcRequest::GetBalance, account_balance_response);
@@ -267,9 +237,9 @@ mod tests {
#[test] #[test]
fn test_check_unique_pubkeys() { fn test_check_unique_pubkeys() {
let pubkey0 = solana_sdk::pubkey::new_rand(); let pubkey0 = Pubkey::new_rand();
let pubkey_clone = pubkey0; 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())) check_unique_pubkeys((&pubkey0, "foo".to_string()), (&pubkey1, "bar".to_string()))
.expect("unexpected result"); .expect("unexpected result");

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +1,20 @@
use crate::{ use crate::{
display::{build_balance_message, format_labeled_address, writeln_name_value}, cli::build_balance_message,
QuietDisplay, VerboseDisplay, display::{format_labeled_address, writeln_name_value},
}; };
use chrono::{DateTime, NaiveDateTime, SecondsFormat, Utc}; use chrono::{DateTime, NaiveDateTime, SecondsFormat, Utc};
use console::{style, Emoji}; use console::{style, Emoji};
use inflector::cases::titlecase::to_title_case; use inflector::cases::titlecase::to_title_case;
use serde::{Deserialize, Serialize}; use serde::Serialize;
use serde_json::{Map, Value}; use serde_json::{Map, Value};
use solana_account_decoder::parse_token::UiTokenAccount;
use solana_clap_utils::keypair::SignOnly;
use solana_client::rpc_response::{ use solana_client::rpc_response::{
RpcAccountBalance, RpcKeyedAccount, RpcSupply, RpcVoteAccountInfo, RpcAccountBalance, RpcKeyedAccount, RpcSupply, RpcVoteAccountInfo,
}; };
use solana_sdk::{ use solana_sdk::{
clock::{self, Epoch, Slot, UnixTimestamp}, clock::{self, Epoch, Slot, UnixTimestamp},
epoch_info::EpochInfo, epoch_info::EpochInfo,
hash::Hash,
native_token::lamports_to_sol, native_token::lamports_to_sol,
pubkey::Pubkey,
signature::Signature,
stake_history::StakeHistoryEntry, stake_history::StakeHistoryEntry,
transaction::Transaction,
}; };
use solana_stake_program::stake_state::{Authorized, Lockup}; use solana_stake_program::stake_state::{Authorized, Lockup};
use solana_vote_program::{ use solana_vote_program::{
@@ -30,7 +24,6 @@ use solana_vote_program::{
use std::{ use std::{
collections::{BTreeMap, HashMap}, collections::{BTreeMap, HashMap},
fmt, fmt,
str::FromStr,
time::Duration, time::Duration,
}; };
@@ -41,27 +34,15 @@ pub enum OutputFormat {
Display, Display,
Json, Json,
JsonCompact, JsonCompact,
DisplayQuiet,
DisplayVerbose,
} }
impl OutputFormat { impl OutputFormat {
pub fn formatted_string<T>(&self, item: &T) -> String pub fn formatted_string<T>(&self, item: &T) -> String
where where
T: Serialize + fmt::Display + QuietDisplay + VerboseDisplay, T: Serialize + fmt::Display,
{ {
match self { match self {
OutputFormat::Display => format!("{}", item), 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::Json => serde_json::to_string_pretty(item).unwrap(),
OutputFormat::JsonCompact => serde_json::to_value(item).unwrap().to_string(), OutputFormat::JsonCompact => serde_json::to_value(item).unwrap().to_string(),
} }
@@ -76,9 +57,6 @@ pub struct CliAccount {
pub use_lamports_unit: bool, pub use_lamports_unit: bool,
} }
impl QuietDisplay for CliAccount {}
impl VerboseDisplay for CliAccount {}
impl fmt::Display for CliAccount { impl fmt::Display for CliAccount {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f)?; writeln!(f)?;
@@ -121,9 +99,6 @@ pub struct CliBlockProduction {
pub verbose: bool, pub verbose: bool,
} }
impl QuietDisplay for CliBlockProduction {}
impl VerboseDisplay for CliBlockProduction {}
impl fmt::Display for CliBlockProduction { impl fmt::Display for CliBlockProduction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f)?; writeln!(f)?;
@@ -132,7 +107,7 @@ impl fmt::Display for CliBlockProduction {
"{}", "{}",
style(format!( style(format!(
" {:<44} {:>15} {:>15} {:>15} {:>23}", " {:<44} {:>15} {:>15} {:>15} {:>23}",
"Identity", "Identity Pubkey",
"Leader Slots", "Leader Slots",
"Blocks Produced", "Blocks Produced",
"Skipped Slots", "Skipped Slots",
@@ -228,9 +203,6 @@ impl From<EpochInfo> for CliEpochInfo {
} }
} }
impl QuietDisplay for CliEpochInfo {}
impl VerboseDisplay for CliEpochInfo {}
impl fmt::Display for CliEpochInfo { impl fmt::Display for CliEpochInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f)?; writeln!(f)?;
@@ -301,7 +273,7 @@ pub struct CliValidatorsStakeByVersion {
pub struct CliValidators { pub struct CliValidators {
pub total_active_stake: u64, pub total_active_stake: u64,
pub total_current_stake: u64, pub total_current_stake: u64,
pub total_delinquent_stake: u64, pub total_deliquent_stake: u64,
pub current_validators: Vec<CliValidator>, pub current_validators: Vec<CliValidator>,
pub delinquent_validators: Vec<CliValidator>, pub delinquent_validators: Vec<CliValidator>,
pub stake_by_version: BTreeMap<String, CliValidatorsStakeByVersion>, pub stake_by_version: BTreeMap<String, CliValidatorsStakeByVersion>,
@@ -309,9 +281,6 @@ pub struct CliValidators {
pub use_lamports_unit: bool, pub use_lamports_unit: bool,
} }
impl QuietDisplay for CliValidators {}
impl VerboseDisplay for CliValidators {}
impl fmt::Display for CliValidators { impl fmt::Display for CliValidators {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn write_vote_account( fn write_vote_account(
@@ -331,7 +300,7 @@ impl fmt::Display for CliValidators {
writeln!( writeln!(
f, f,
"{} {:<44} {:<44} {:>3}% {:>8} {:>10} {:>10} {:>8} {}", "{} {:<44} {:<44} {:>3}% {:>8} {:>10} {:>10} {:>17} {}",
if delinquent { if delinquent {
WARNING.to_string() WARNING.to_string()
} else { } else {
@@ -360,7 +329,7 @@ impl fmt::Display for CliValidators {
"Active Stake:", "Active Stake:",
&build_balance_message(self.total_active_stake, self.use_lamports_unit, true), &build_balance_message(self.total_active_stake, self.use_lamports_unit, true),
)?; )?;
if self.total_delinquent_stake > 0 { if self.total_deliquent_stake > 0 {
writeln_name_value( writeln_name_value(
f, f,
"Current Stake:", "Current Stake:",
@@ -376,11 +345,11 @@ impl fmt::Display for CliValidators {
&format!( &format!(
"{} ({:0.2}%)", "{} ({:0.2}%)",
&build_balance_message( &build_balance_message(
self.total_delinquent_stake, self.total_deliquent_stake,
self.use_lamports_unit, self.use_lamports_unit,
true 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() { for (version, info) in self.stake_by_version.iter() {
writeln!( writeln!(
f, f,
"{:<8} - {:3} current validators ({:>5.2}%){}", "{:<16} - {:3} current validators ({:>5.2}%){}",
version, version,
info.current_validators, info.current_validators,
100. * info.current_active_stake as f64 / self.total_active_stake as f64, 100. * info.current_active_stake as f64 / self.total_active_stake as f64,
@@ -411,9 +380,9 @@ impl fmt::Display for CliValidators {
f, f,
"{}", "{}",
style(format!( style(format!(
" {:<44} {:<38} {} {} {} {:>10} {:^8} {}", " {:<44} {:<38} {} {} {} {:>10} {:^17} {}",
"Identity", "Identity Pubkey",
"Vote Account", "Vote Account Pubkey",
"Commission", "Commission",
"Last Vote", "Last Vote",
"Root Block", "Root Block",
@@ -500,9 +469,6 @@ pub struct CliNonceAccount {
pub use_lamports_unit: bool, pub use_lamports_unit: bool,
} }
impl QuietDisplay for CliNonceAccount {}
impl VerboseDisplay for CliNonceAccount {}
impl fmt::Display for CliNonceAccount { impl fmt::Display for CliNonceAccount {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!( writeln!(
@@ -520,7 +486,7 @@ impl fmt::Display for CliNonceAccount {
) )
)?; )?;
let nonce = self.nonce.as_deref().unwrap_or("uninitialized"); let nonce = self.nonce.as_deref().unwrap_or("uninitialized");
writeln!(f, "Nonce blockhash: {}", nonce)?; writeln!(f, "Nonce: {}", nonce)?;
if let Some(fees) = self.lamports_per_signature { if let Some(fees) = self.lamports_per_signature {
writeln!(f, "Fee: {} lamports per signature", fees)?; writeln!(f, "Fee: {} lamports per signature", fees)?;
} else { } else {
@@ -540,9 +506,6 @@ impl CliStakeVec {
} }
} }
impl QuietDisplay for CliStakeVec {}
impl VerboseDisplay for CliStakeVec {}
impl fmt::Display for CliStakeVec { impl fmt::Display for CliStakeVec {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for state in &self.0 { for state in &self.0 {
@@ -561,9 +524,6 @@ pub struct CliKeyedStakeState {
pub stake_state: CliStakeState, pub stake_state: CliStakeState,
} }
impl QuietDisplay for CliKeyedStakeState {}
impl VerboseDisplay for CliKeyedStakeState {}
impl fmt::Display for CliKeyedStakeState { impl fmt::Display for CliKeyedStakeState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "Stake Pubkey: {}", self.stake_pubkey)?; 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)] #[derive(Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct CliStakeState { pub struct CliStakeState {
@@ -642,13 +560,8 @@ pub struct CliStakeState {
pub activating_stake: Option<u64>, pub activating_stake: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub deactivating_stake: Option<u64>, 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 { impl fmt::Display for CliStakeState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn show_authorized(f: &mut fmt::Formatter, authorized: &CliAuthorized) -> 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() { if lockup.unix_timestamp != UnixTimestamp::default() {
writeln!( writeln!(
f, f,
"Lockup Timestamp: {}", "Lockup Timestamp: {} (UnixTimestamp: {})",
unix_timestamp_to_string(lockup.unix_timestamp) 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() { if lockup.epoch != Epoch::default() {
@@ -792,14 +710,13 @@ impl fmt::Display for CliStakeState {
} }
show_authorized(f, self.authorized.as_ref().unwrap())?; show_authorized(f, self.authorized.as_ref().unwrap())?;
show_lockup(f, self.lockup.as_ref())?; show_lockup(f, self.lockup.as_ref())?;
show_epoch_rewards(f, &self.epoch_rewards)?
} }
} }
Ok(()) Ok(())
} }
} }
#[derive(Serialize, Deserialize, PartialEq)] #[derive(Serialize, Deserialize)]
pub enum CliStakeType { pub enum CliStakeType {
Stake, Stake,
RewardsPool, RewardsPool,
@@ -821,9 +738,6 @@ pub struct CliStakeHistory {
pub use_lamports_unit: bool, pub use_lamports_unit: bool,
} }
impl QuietDisplay for CliStakeHistory {}
impl VerboseDisplay for CliStakeHistory {}
impl fmt::Display for CliStakeHistory { impl fmt::Display for CliStakeHistory {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f)?; writeln!(f)?;
@@ -918,9 +832,6 @@ impl CliValidatorInfoVec {
} }
} }
impl QuietDisplay for CliValidatorInfoVec {}
impl VerboseDisplay for CliValidatorInfoVec {}
impl fmt::Display for CliValidatorInfoVec { impl fmt::Display for CliValidatorInfoVec {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.0.is_empty() { if self.0.is_empty() {
@@ -942,13 +853,10 @@ pub struct CliValidatorInfo {
pub info: Map<String, Value>, pub info: Map<String, Value>,
} }
impl QuietDisplay for CliValidatorInfo {}
impl VerboseDisplay for CliValidatorInfo {}
impl fmt::Display for CliValidatorInfo { impl fmt::Display for CliValidatorInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln_name_value(f, "Validator Identity:", &self.identity_pubkey)?; writeln_name_value(f, "Validator Identity Pubkey:", &self.identity_pubkey)?;
writeln_name_value(f, " Info Address:", &self.info_pubkey)?; writeln_name_value(f, " Info Pubkey:", &self.info_pubkey)?;
for (key, value) in self.info.iter() { for (key, value) in self.info.iter() {
writeln_name_value( writeln_name_value(
f, f,
@@ -976,13 +884,8 @@ pub struct CliVoteAccount {
pub epoch_voting_history: Vec<CliEpochVotingHistory>, pub epoch_voting_history: Vec<CliEpochVotingHistory>,
#[serde(skip_serializing)] #[serde(skip_serializing)]
pub use_lamports_unit: bool, 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 { impl fmt::Display for CliVoteAccount {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!( writeln!(
@@ -1003,12 +906,7 @@ impl fmt::Display for CliVoteAccount {
None => "~".to_string(), None => "~".to_string(),
} }
)?; )?;
writeln!( writeln!(f, "Recent Timestamp: {:?}", self.recent_timestamp)?;
f,
"Recent Timestamp: {} from slot {}",
unix_timestamp_to_string(self.recent_timestamp.timestamp),
self.recent_timestamp.slot
)?;
if !self.votes.is_empty() { if !self.votes.is_empty() {
writeln!(f, "Recent Votes:")?; writeln!(f, "Recent Votes:")?;
for vote in &self.votes { for vote in &self.votes {
@@ -1027,7 +925,6 @@ impl fmt::Display for CliVoteAccount {
)?; )?;
} }
} }
show_epoch_rewards(f, &self.epoch_rewards)?;
Ok(()) Ok(())
} }
} }
@@ -1038,9 +935,6 @@ pub struct CliAuthorizedVoters {
authorized_voters: BTreeMap<Epoch, String>, authorized_voters: BTreeMap<Epoch, String>,
} }
impl QuietDisplay for CliAuthorizedVoters {}
impl VerboseDisplay for CliAuthorizedVoters {}
impl fmt::Display for CliAuthorizedVoters { impl fmt::Display for CliAuthorizedVoters {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.authorized_voters) write!(f, "{:?}", self.authorized_voters)
@@ -1090,25 +984,19 @@ pub struct CliBlockTime {
pub timestamp: UnixTimestamp, 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 { impl fmt::Display for CliBlockTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln_name_value(f, "Block:", &self.slot.to_string())?; writeln_name_value(f, "Block:", &self.slot.to_string())?;
writeln_name_value(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>, pub bad_sig: Vec<String>,
} }
impl QuietDisplay for CliSignOnlyData {}
impl VerboseDisplay for CliSignOnlyData {}
impl fmt::Display for CliSignOnlyData { impl fmt::Display for CliSignOnlyData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f)?; writeln!(f)?;
@@ -1159,9 +1044,6 @@ pub struct CliSignature {
pub signature: String, pub signature: String,
} }
impl QuietDisplay for CliSignature {}
impl VerboseDisplay for CliSignature {}
impl fmt::Display for CliSignature { impl fmt::Display for CliSignature {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f)?; writeln!(f)?;
@@ -1176,9 +1058,6 @@ pub struct CliAccountBalances {
pub accounts: Vec<RpcAccountBalance>, pub accounts: Vec<RpcAccountBalance>,
} }
impl QuietDisplay for CliAccountBalances {}
impl VerboseDisplay for CliAccountBalances {}
impl fmt::Display for CliAccountBalances { impl fmt::Display for CliAccountBalances {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!( writeln!(
@@ -1221,9 +1100,6 @@ impl From<RpcSupply> for CliSupply {
} }
} }
impl QuietDisplay for CliSupply {}
impl VerboseDisplay for CliSupply {}
impl fmt::Display for CliSupply { impl fmt::Display for CliSupply {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln_name_value(f, "Total:", &format!("{} SOL", lamports_to_sol(self.total)))?; writeln_name_value(f, "Total:", &format!("{} SOL", lamports_to_sol(self.total)))?;
@@ -1257,9 +1133,6 @@ pub struct CliFees {
pub last_valid_slot: Slot, pub last_valid_slot: Slot,
} }
impl QuietDisplay for CliFees {}
impl VerboseDisplay for CliFees {}
impl fmt::Display for CliFees { impl fmt::Display for CliFees {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln_name_value(f, "Blockhash:", &self.blockhash)?; writeln_name_value(f, "Blockhash:", &self.blockhash)?;
@@ -1272,240 +1145,3 @@ impl fmt::Display for CliFees {
Ok(()) 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"
);
}
}

View File

@@ -1,34 +1,21 @@
use crate::{ use crate::{
cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult}, 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}, spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
stake::is_stake_program_v2_enabled,
}; };
use chrono::{Local, TimeZone};
use clap::{value_t, value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand}; use clap::{value_t, value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand};
use console::{style, Emoji}; use console::{style, Emoji};
use solana_clap_utils::{ use solana_clap_utils::{
commitment::commitment_arg, input_parsers::*, input_validators::*, keypair::DefaultSigner, commitment::commitment_arg, input_parsers::*, input_validators::*, keypair::signer_from_path,
};
use solana_cli_output::{
display::{
format_labeled_address, new_spinner_progress_bar, println_name_value, println_transaction,
},
*,
}; };
use solana_client::{ use solana_client::{
client_error::ClientErrorKind, pubsub_client::{PubsubClient, SlotInfoMessage},
pubsub_client::PubsubClient, rpc_client::RpcClient,
rpc_client::{GetConfirmedSignaturesForAddress2Config, RpcClient}, rpc_config::{RpcLargestAccountsConfig, RpcLargestAccountsFilter},
rpc_config::{
RpcAccountInfoConfig, RpcLargestAccountsConfig, RpcLargestAccountsFilter,
RpcProgramAccountsConfig,
},
rpc_filter,
rpc_response::SlotInfo,
}; };
use solana_remote_wallet::remote_wallet::RemoteWalletManager; use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{ use solana_sdk::{
account::from_account,
account_utils::StateMut, account_utils::StateMut,
clock::{self, Clock, Slot}, clock::{self, Clock, Slot},
commitment_config::CommitmentConfig, commitment_config::CommitmentConfig,
@@ -40,11 +27,11 @@ use solana_sdk::{
system_instruction, system_program, system_instruction, system_program,
sysvar::{ sysvar::{
self, self,
stake_history::{self}, stake_history::{self, StakeHistory},
Sysvar,
}, },
transaction::Transaction, transaction::Transaction,
}; };
use solana_transaction_status::UiTransactionEncoding;
use std::{ use std::{
collections::{BTreeMap, HashMap, VecDeque}, collections::{BTreeMap, HashMap, VecDeque},
net::SocketAddr, net::SocketAddr,
@@ -66,18 +53,6 @@ pub trait ClusterQuerySubCommands {
impl ClusterQuerySubCommands for App<'_, '_> { impl ClusterQuerySubCommands for App<'_, '_> {
fn cluster_query_subcommands(self) -> Self { fn cluster_query_subcommands(self) -> Self {
self.subcommand( 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") SubCommand::with_name("catchup")
.about("Wait for a validator to catch up to the cluster") .about("Wait for a validator to catch up to the cluster")
.arg( .arg(
@@ -112,10 +87,6 @@ impl ClusterQuerySubCommands for App<'_, '_> {
.about("Get the version of the cluster entrypoint"), .about("Get the version of the cluster entrypoint"),
) )
.subcommand(SubCommand::with_name("fees").about("Display current cluster fees")) .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") .subcommand(SubCommand::with_name("block-time")
.about("Get estimated production time of a block") .about("Get estimated production time of a block")
.alias("get-block-time") .alias("get-block-time")
@@ -311,12 +282,6 @@ impl ClusterQuerySubCommands for App<'_, '_> {
.takes_value(true) .takes_value(true)
.help("Start with the first signature older than this one"), .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"),
)
) )
} }
} }
@@ -340,7 +305,7 @@ pub fn parse_catchup(
pub fn parse_cluster_ping( pub fn parse_cluster_ping(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer: &DefaultSigner, default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let lamports = value_t_or_exit!(matches, "lamports", u64); let lamports = value_t_or_exit!(matches, "lamports", u64);
@@ -358,15 +323,12 @@ pub fn parse_cluster_ping(
count, count,
timeout, timeout,
}, },
signers: vec![default_signer.signer_from_path(matches, wallet_manager)?], signers: vec![signer_from_path(
}) matches,
} default_signer_path,
"keypair",
pub fn parse_get_block(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> { wallet_manager,
let slot = value_of(matches, "slot"); )?],
Ok(CliCommandInfo {
command: CliCommand::GetBlock { slot },
signers: vec![],
}) })
} }
@@ -482,24 +444,13 @@ pub fn parse_transaction_history(
), ),
None => None, None => None,
}; };
let until = match matches.value_of("until") {
Some(signature) => Some(
signature
.parse()
.map_err(|err| CliError::BadParameter(format!("Invalid signature: {}", err)))?,
),
None => None,
};
let limit = value_t_or_exit!(matches, "limit", usize); let limit = value_t_or_exit!(matches, "limit", usize);
let show_transactions = matches.is_present("show_transactions");
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::TransactionHistory { command: CliCommand::TransactionHistory {
address, address,
before, before,
until,
limit, limit,
show_transactions,
}, },
signers: vec![], signers: vec![],
}) })
@@ -542,20 +493,7 @@ pub fn process_catchup(
RpcClient::new_socket(rpc_addr) RpcClient::new_socket(rpc_addr)
}; };
let reported_node_pubkey = loop { let reported_node_pubkey = node_client.get_identity()?;
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));
}
}
};
if reported_node_pubkey != *node_pubkey { if reported_node_pubkey != *node_pubkey {
return Err(format!( return Err(format!(
"The identity reported by node RPC URL does not match. Expected: {:?}. Reported: {:?}", "The identity reported by node RPC URL does not match. Expected: {:?}. Reported: {:?}",
@@ -625,7 +563,7 @@ pub fn process_cluster_date(rpc_client: &RpcClient, config: &CliConfig) -> Proce
let result = rpc_client let result = rpc_client
.get_account_with_commitment(&sysvar::clock::id(), CommitmentConfig::default())?; .get_account_with_commitment(&sysvar::clock::id(), CommitmentConfig::default())?;
if let Some(clock_account) = result.value { if let Some(clock_account) = result.value {
let clock: Clock = from_account(&clock_account).ok_or_else(|| { let clock: Clock = Sysvar::from_account(&clock_account).ok_or_else(|| {
CliError::RpcRequestError("Failed to deserialize clock sysvar".to_string()) CliError::RpcRequestError("Failed to deserialize clock sysvar".to_string())
})?; })?;
let block_time = CliBlockTime { let block_time = CliBlockTime {
@@ -638,14 +576,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()?; let remote_version = rpc_client.get_version()?;
Ok(remote_version.solana_core)
if config.verbose {
Ok(format!("{:?}", remote_version))
} else {
Ok(remote_version.to_string())
}
} }
pub fn process_fees(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult { pub fn process_fees(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
@@ -660,11 +593,6 @@ pub fn process_fees(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult
Ok(config.output_format.formatted_string(&fees)) 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 { pub fn process_leader_schedule(rpc_client: &RpcClient) -> ProcessResult {
let epoch_info = rpc_client.get_epoch_info()?; let epoch_info = rpc_client.get_epoch_info()?;
let first_slot_in_epoch = epoch_info.absolute_slot - epoch_info.slot_index; let first_slot_in_epoch = epoch_info.absolute_slot - epoch_info.slot_index;
@@ -700,83 +628,6 @@ pub fn process_leader_schedule(rpc_client: &RpcClient) -> ProcessResult {
Ok("".to_string()) Ok("".to_string())
} }
pub fn process_get_block(
rpc_client: &RpcClient,
_config: &CliConfig,
slot: 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( pub fn process_get_block_time(
rpc_client: &RpcClient, rpc_client: &RpcClient,
config: &CliConfig, config: &CliConfig,
@@ -1173,7 +1024,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 mut message = "".to_string();
let slot_progress = new_spinner_progress_bar(); let slot_progress = new_spinner_progress_bar();
@@ -1268,16 +1119,14 @@ pub fn process_show_gossip(rpc_client: &RpcClient, config: &CliConfig) -> Proces
.into_iter() .into_iter()
.map(|node| { .map(|node| {
format!( format!(
"{:15} | {:44} | {:6} | {:5} | {:21} | {}", "{:15} | {:44} | {:6} | {:5} | {:5} | {}",
node.gossip node.gossip
.map(|addr| addr.ip().to_string()) .map(|addr| addr.ip().to_string())
.unwrap_or_else(|| "none".to_string()), .unwrap_or_else(|| "none".to_string()),
format_labeled_address(&node.pubkey, &config.address_labels), format_labeled_address(&node.pubkey, &config.address_labels),
format_port(node.gossip), format_port(node.gossip),
format_port(node.tpu), format_port(node.tpu),
node.rpc format_port(node.rpc),
.map(|addr| addr.to_string())
.unwrap_or_else(|| "none".to_string()),
node.version.unwrap_or_else(|| "unknown".to_string()), node.version.unwrap_or_else(|| "unknown".to_string()),
) )
}) })
@@ -1285,9 +1134,9 @@ pub fn process_show_gossip(rpc_client: &RpcClient, config: &CliConfig) -> Proces
Ok(format!( Ok(format!(
"IP Address | Node identifier \ "IP Address | Node identifier \
| Gossip | TPU | RPC Address | Version\n\ | Gossip | TPU | RPC | Version\n\
----------------+----------------------------------------------+\ ----------------+----------------------------------------------+\
--------+-------+-----------------------+----------------\n\ --------+-------+-------+----------------\n\
{}\n\ {}\n\
Nodes: {}", Nodes: {}",
s.join("\n"), s.join("\n"),
@@ -1306,52 +1155,17 @@ pub fn process_show_stakes(
let progress_bar = new_spinner_progress_bar(); let progress_bar = new_spinner_progress_bar();
progress_bar.set_message("Fetching stake accounts..."); progress_bar.set_message("Fetching stake accounts...");
let all_stake_accounts = rpc_client.get_program_accounts(&solana_stake_program::id())?;
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 stake_history_account = rpc_client.get_account(&stake_history::id())?; let stake_history_account = rpc_client.get_account(&stake_history::id())?;
let clock_account = rpc_client.get_account(&sysvar::clock::id())?;
let clock: Clock = from_account(&clock_account).ok_or_else(|| {
CliError::RpcRequestError("Failed to deserialize clock sysvar".to_string())
})?;
progress_bar.finish_and_clear(); progress_bar.finish_and_clear();
let clock_account = rpc_client.get_account(&sysvar::clock::id())?;
let stake_history = from_account(&stake_history_account).ok_or_else(|| { let stake_history = StakeHistory::from_account(&stake_history_account).ok_or_else(|| {
CliError::RpcRequestError("Failed to deserialize stake history".to_string()) CliError::RpcRequestError("Failed to deserialize stake history".to_string())
})?; })?;
// At v1.6, this check can be removed and simply passed as `true` let clock: Clock = Sysvar::from_account(&clock_account).ok_or_else(|| {
let stake_program_v2_enabled = is_stake_program_v2_enabled(rpc_client); CliError::RpcRequestError("Failed to deserialize clock sysvar".to_string())
})?;
let mut stake_accounts: Vec<CliKeyedStakeState> = vec![]; let mut stake_accounts: Vec<CliKeyedStakeState> = vec![];
for (stake_pubkey, stake_account) in all_stake_accounts { for (stake_pubkey, stake_account) in all_stake_accounts {
@@ -1367,7 +1181,6 @@ pub fn process_show_stakes(
use_lamports_unit, use_lamports_unit,
&stake_history, &stake_history,
&clock, &clock,
stake_program_v2_enabled,
), ),
}); });
} }
@@ -1386,7 +1199,6 @@ pub fn process_show_stakes(
use_lamports_unit, use_lamports_unit,
&stake_history, &stake_history,
&clock, &clock,
stake_program_v2_enabled,
), ),
}); });
} }
@@ -1426,12 +1238,12 @@ pub fn process_show_validators(
.map(|vote_account| vote_account.activated_stake) .map(|vote_account| vote_account.activated_stake)
.sum(); .sum();
let total_delinquent_stake = vote_accounts let total_deliquent_stake = vote_accounts
.delinquent .delinquent
.iter() .iter()
.map(|vote_account| vote_account.activated_stake) .map(|vote_account| vote_account.activated_stake)
.sum(); .sum();
let total_current_stake = total_active_stake - total_delinquent_stake; let total_current_stake = total_active_stake - total_deliquent_stake;
let mut current = vote_accounts.current; let mut current = vote_accounts.current;
current.sort_by(|a, b| b.activated_stake.cmp(&a.activated_stake)); current.sort_by(|a, b| b.activated_stake.cmp(&a.activated_stake));
@@ -1485,7 +1297,7 @@ pub fn process_show_validators(
let cli_validators = CliValidators { let cli_validators = CliValidators {
total_active_stake, total_active_stake,
total_current_stake, total_current_stake,
total_delinquent_stake, total_deliquent_stake,
current_validators, current_validators,
delinquent_validators, delinquent_validators,
stake_by_version, stake_by_version,
@@ -1499,17 +1311,12 @@ pub fn process_transaction_history(
config: &CliConfig, config: &CliConfig,
address: &Pubkey, address: &Pubkey,
before: Option<Signature>, before: Option<Signature>,
until: Option<Signature>,
limit: usize, limit: usize,
show_transactions: bool,
) -> ProcessResult { ) -> ProcessResult {
let results = rpc_client.get_confirmed_signatures_for_address2_with_config( let results = rpc_client.get_confirmed_signatures_for_address2_with_config(
address, address,
GetConfirmedSignaturesForAddress2Config { before,
before, Some(limit),
until,
limit: Some(limit),
},
)?; )?;
let transactions_found = format!("{} transactions found", results.len()); let transactions_found = format!("{} transactions found", results.len());
@@ -1529,28 +1336,6 @@ pub fn process_transaction_history(
} else { } else {
println!("{}", result.signature); 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) Ok(transactions_found)
} }
@@ -1573,16 +1358,12 @@ mod tests {
let default_keypair = Keypair::new(); let default_keypair = Keypair::new();
let (default_keypair_file, mut tmp_file) = make_tmp_file(); let (default_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap(); 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 let test_cluster_version = test_commands
.clone() .clone()
.get_matches_from(vec!["test", "cluster-date"]); .get_matches_from(vec!["test", "cluster-date"]);
assert_eq!( assert_eq!(
parse_command(&test_cluster_version, &default_signer, &mut None).unwrap(), parse_command(&test_cluster_version, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::ClusterDate, command: CliCommand::ClusterDate,
signers: vec![], signers: vec![],
@@ -1593,7 +1374,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "cluster-version"]); .get_matches_from(vec!["test", "cluster-version"]);
assert_eq!( assert_eq!(
parse_command(&test_cluster_version, &default_signer, &mut None).unwrap(), parse_command(&test_cluster_version, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::ClusterVersion, command: CliCommand::ClusterVersion,
signers: vec![], signers: vec![],
@@ -1602,7 +1383,7 @@ mod tests {
let test_fees = test_commands.clone().get_matches_from(vec!["test", "fees"]); let test_fees = test_commands.clone().get_matches_from(vec!["test", "fees"]);
assert_eq!( assert_eq!(
parse_command(&test_fees, &default_signer, &mut None).unwrap(), parse_command(&test_fees, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Fees, command: CliCommand::Fees,
signers: vec![], signers: vec![],
@@ -1615,7 +1396,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "block-time", &slot.to_string()]); .get_matches_from(vec!["test", "block-time", &slot.to_string()]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::GetBlockTime { slot: Some(slot) }, command: CliCommand::GetBlockTime { slot: Some(slot) },
signers: vec![], signers: vec![],
@@ -1626,7 +1407,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "epoch"]); .get_matches_from(vec!["test", "epoch"]);
assert_eq!( assert_eq!(
parse_command(&test_get_epoch, &default_signer, &mut None).unwrap(), parse_command(&test_get_epoch, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::GetEpoch, command: CliCommand::GetEpoch,
signers: vec![], signers: vec![],
@@ -1637,7 +1418,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "epoch-info"]); .get_matches_from(vec!["test", "epoch-info"]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::GetEpochInfo, command: CliCommand::GetEpochInfo,
signers: vec![], signers: vec![],
@@ -1648,7 +1429,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "genesis-hash"]); .get_matches_from(vec!["test", "genesis-hash"]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::GetGenesisHash, command: CliCommand::GetGenesisHash,
signers: vec![], signers: vec![],
@@ -1657,7 +1438,7 @@ mod tests {
let test_get_slot = test_commands.clone().get_matches_from(vec!["test", "slot"]); let test_get_slot = test_commands.clone().get_matches_from(vec!["test", "slot"]);
assert_eq!( assert_eq!(
parse_command(&test_get_slot, &default_signer, &mut None).unwrap(), parse_command(&test_get_slot, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::GetSlot, command: CliCommand::GetSlot,
signers: vec![], signers: vec![],
@@ -1668,7 +1449,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "total-supply"]); .get_matches_from(vec!["test", "total-supply"]);
assert_eq!( assert_eq!(
parse_command(&test_total_supply, &default_signer, &mut None).unwrap(), parse_command(&test_total_supply, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::TotalSupply, command: CliCommand::TotalSupply,
signers: vec![], signers: vec![],
@@ -1679,7 +1460,7 @@ mod tests {
.clone() .clone()
.get_matches_from(vec!["test", "transaction-count"]); .get_matches_from(vec!["test", "transaction-count"]);
assert_eq!( assert_eq!(
parse_command(&test_transaction_count, &default_signer, &mut None).unwrap(), parse_command(&test_transaction_count, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::GetTransactionCount, command: CliCommand::GetTransactionCount,
signers: vec![], signers: vec![],
@@ -1699,7 +1480,7 @@ mod tests {
"max", "max",
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_ping, &default_signer, &mut None).unwrap(), parse_command(&test_ping, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::Ping { command: CliCommand::Ping {
lamports: 1, lamports: 1,

View File

@@ -1,3 +1,4 @@
use crate::cli::SettingType;
use console::style; use console::style;
use indicatif::{ProgressBar, ProgressStyle}; use indicatif::{ProgressBar, ProgressStyle};
use solana_sdk::{ use solana_sdk::{
@@ -7,24 +8,6 @@ use solana_sdk::{
use solana_transaction_status::UiTransactionStatusMeta; use solana_transaction_status::UiTransactionStatusMeta;
use std::{collections::HashMap, fmt, io}; 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" // Pretty print a "name value"
pub fn println_name_value(name: &str, value: &str) { pub fn println_name_value(name: &str, value: &str) {
let styled_value = if value == "" { 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( pub fn println_signers(
blockhash: &Hash, blockhash: &Hash,
signers: &[String], signers: &[String],
@@ -164,7 +162,7 @@ pub fn write_transaction<W: io::Write>(
)?; )?;
writeln!( writeln!(
w, w,
"{} Fee: {}", "{} Fee: {} SOL",
prefix, prefix,
lamports_to_sol(transaction_status.fee) lamports_to_sol(transaction_status.fee)
)?; )?;
@@ -181,7 +179,7 @@ pub fn write_transaction<W: io::Write>(
if pre == post { if pre == post {
writeln!( writeln!(
w, w,
"{} Account {} balance: {}", "{} Account {} balance: {} SOL",
prefix, prefix,
i, i,
lamports_to_sol(*pre) lamports_to_sol(*pre)
@@ -189,7 +187,7 @@ pub fn write_transaction<W: io::Write>(
} else { } else {
writeln!( writeln!(
w, w,
"{} Account {} balance: {} -> {}", "{} Account {} balance: {} SOL -> {} SOL",
prefix, prefix,
i, i,
lamports_to_sol(*pre), 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 { } else {
writeln!(w, "{}Status: Unavailable", prefix)?; writeln!(w, "{}Status: Unavailable", prefix)?;
} }

View File

@@ -1,379 +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_sdk::{
clock::Slot,
feature::{self, Feature},
feature_set::FEATURE_NAMES,
message::Message,
pubkey::Pubkey,
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 {
if active_stake_by_feature_set.get(&my_feature_set).is_none() {
println!(
"{}",
style("To activate features the tool and cluster feature sets must match, select a tool version that matches the cluster")
.bold());
} else {
println!(
"{}",
style("To activate features the stake must be >= 95%").bold()
);
}
println!(
"{}",
style(format!("Tool Feture Set: {}", my_feature_set)).bold()
);
println!("{}", style("Cluster Feature Sets and Stakes:").bold());
for (feature_set, percentage) in active_stake_by_feature_set.iter() {
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(
&feature::activate_with_lamports(
&feature_id,
&config.signers[0].pubkey(),
lamports,
),
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())
}

View File

@@ -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())
}

View File

@@ -18,15 +18,16 @@ macro_rules! pubkey {
}; };
} }
#[macro_use]
extern crate serde_derive; extern crate serde_derive;
pub mod checks; pub mod checks;
pub mod cli; pub mod cli;
pub mod cli_output;
pub mod cluster_query; pub mod cluster_query;
pub mod feature; pub mod display;
pub mod inflation;
pub mod nonce; pub mod nonce;
pub mod send_tpu; pub mod offline;
pub mod spend_utils; pub mod spend_utils;
pub mod stake; pub mod stake;
pub mod test_utils; pub mod test_utils;

View File

@@ -5,37 +5,22 @@ use clap::{
use console::style; use console::style;
use solana_clap_utils::{ use solana_clap_utils::{
commitment::COMMITMENT_ARG, commitment::COMMITMENT_ARG, input_parsers::commitment_of, input_validators::is_url,
input_parsers::commitment_of, keypair::SKIP_SEED_PHRASE_VALIDATION_ARG, DisplayError,
input_validators::is_url,
keypair::{CliSigners, DefaultSigner, SKIP_SEED_PHRASE_VALIDATION_ARG},
DisplayError,
}; };
use solana_cli::cli::{ use solana_cli::{
app, parse_command, process_command, CliCommandInfo, CliConfig, SettingType, cli::{
DEFAULT_RPC_TIMEOUT_SECONDS, 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_config::{Config, CONFIG_FILE};
use solana_cli_output::{display::println_name_value, OutputFormat};
use solana_client::rpc_config::RpcSendTransactionConfig; use solana_client::rpc_config::RpcSendTransactionConfig;
use solana_remote_wallet::remote_wallet::RemoteWalletManager; use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use std::{collections::HashMap, error, path::PathBuf, sync::Arc, time::Duration}; 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>> { fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error>> {
let parse_args = match matches.subcommand() { let parse_args = match matches.subcommand() {
("config", Some(matches)) => { ("config", Some(matches)) => {
@@ -154,19 +139,13 @@ pub fn parse_args<'a>(
matches.value_of("json_rpc_url").unwrap_or(""), matches.value_of("json_rpc_url").unwrap_or(""),
&config.json_rpc_url, &config.json_rpc_url,
); );
let default_signer_arg_name = "keypair".to_string();
let (_, default_signer_path) = CliConfig::compute_keypair_path_setting( 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, &config.keypair_path,
); );
let default_signer = DefaultSigner {
arg_name: default_signer_arg_name,
path: default_signer_path.clone(),
};
let CliCommandInfo { command, signers } = 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 let output_format = matches
.value_of("output_format") .value_of("output_format")

View File

@@ -1,26 +1,29 @@
use crate::{ use crate::{
checks::{check_account_for_fee_with_commitment, check_unique_pubkeys}, checks::{check_account_for_fee_with_commitment, check_unique_pubkeys},
cli::{ cli::{
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError, generate_unique_signers, log_instruction_custom_error, CliCommand, CliCommandInfo,
ProcessResult, CliConfig, CliError, ProcessResult, SignerIndex,
}, },
cli_output::CliNonceAccount,
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount}, spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
}; };
use clap::{App, Arg, ArgMatches, SubCommand}; use clap::{App, Arg, ArgMatches, SubCommand};
use solana_clap_utils::{ use solana_clap_utils::{
input_parsers::*, input_parsers::*, input_validators::*, offline::BLOCKHASH_ARG, ArgConstant,
input_validators::*,
keypair::{DefaultSigner, SignerIndex},
nonce::*,
}; };
use solana_cli_output::CliNonceAccount; use solana_client::rpc_client::RpcClient;
use solana_client::{nonce_utils::*, rpc_client::RpcClient};
use solana_remote_wallet::remote_wallet::RemoteWalletManager; use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{ use solana_sdk::{
account::Account, account::Account,
account_utils::StateMut,
commitment_config::CommitmentConfig,
hash::Hash, hash::Hash,
message::Message, message::Message,
nonce::{self, State}, nonce::{
self,
state::{Data, Versions},
State,
},
pubkey::Pubkey, pubkey::Pubkey,
system_instruction::{ system_instruction::{
advance_nonce_account, authorize_nonce_account, create_nonce_account, advance_nonce_account, authorize_nonce_account, create_nonce_account,
@@ -30,11 +33,64 @@ use solana_sdk::{
transaction::Transaction, transaction::Transaction,
}; };
use std::sync::Arc; 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 { pub trait NonceSubCommands {
fn nonce_subcommands(self) -> Self; 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<'_, '_> { impl NonceSubCommands for App<'_, '_> {
fn nonce_subcommands(self) -> Self { fn nonce_subcommands(self) -> Self {
self.subcommand( 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( pub fn parse_authorize_nonce_account(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer: &DefaultSigner, default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap(); 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)?; signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
let payer_provided = None; let payer_provided = None;
let signer_info = default_signer.generate_unique_signers( let signer_info = generate_unique_signers(
vec![payer_provided, nonce_authority], vec![payer_provided, nonce_authority],
matches, matches,
default_signer_path,
wallet_manager, wallet_manager,
)?; )?;
@@ -193,7 +305,7 @@ pub fn parse_authorize_nonce_account(
pub fn parse_nonce_create_account( pub fn parse_nonce_create_account(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer: &DefaultSigner, default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let (nonce_account, nonce_account_pubkey) = 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 nonce_authority = pubkey_of_signer(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
let payer_provided = None; let payer_provided = None;
let signer_info = default_signer.generate_unique_signers( let signer_info = generate_unique_signers(
vec![payer_provided, nonce_account], vec![payer_provided, nonce_account],
matches, matches,
default_signer_path,
wallet_manager, wallet_manager,
)?; )?;
@@ -235,7 +348,7 @@ pub fn parse_get_nonce(
pub fn parse_new_nonce( pub fn parse_new_nonce(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer: &DefaultSigner, default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap(); 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)?; signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
let payer_provided = None; let payer_provided = None;
let signer_info = default_signer.generate_unique_signers( let signer_info = generate_unique_signers(
vec![payer_provided, nonce_authority], vec![payer_provided, nonce_authority],
matches, matches,
default_signer_path,
wallet_manager, wallet_manager,
)?; )?;
@@ -277,7 +391,7 @@ pub fn parse_show_nonce_account(
pub fn parse_withdraw_from_nonce_account( pub fn parse_withdraw_from_nonce_account(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer: &DefaultSigner, default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap(); 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)?; signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
let payer_provided = None; let payer_provided = None;
let signer_info = default_signer.generate_unique_signers( let signer_info = generate_unique_signers(
vec![payer_provided, nonce_authority], vec![payer_provided, nonce_authority],
matches, matches,
default_signer_path,
wallet_manager, wallet_manager,
)?; )?;
@@ -314,14 +429,14 @@ pub fn check_nonce_account(
match state_from_account(nonce_account)? { match state_from_account(nonce_account)? {
State::Initialized(ref data) => { State::Initialized(ref data) => {
if &data.blockhash != nonce_hash { if &data.blockhash != nonce_hash {
Err(Error::InvalidHash.into()) Err(CliNonceError::InvalidHash.into())
} else if nonce_authority != &data.authority { } else if nonce_authority != &data.authority {
Err(Error::InvalidAuthority.into()) Err(CliNonceError::InvalidAuthority.into())
} else { } else {
Ok(()) Ok(())
} }
} }
State::Uninitialized => Err(Error::InvalidStateForOperation.into()), State::Uninitialized => Err(CliNonceError::InvalidStateForOperation.into()),
} }
} }
@@ -576,11 +691,9 @@ mod tests {
use crate::cli::{app, parse_command}; use crate::cli::{app, parse_command};
use solana_sdk::{ use solana_sdk::{
account::Account, account::Account,
account_utils::StateMut,
fee_calculator::FeeCalculator, fee_calculator::FeeCalculator,
hash::hash, hash::hash,
nonce::{self, state::Versions, State}, nonce::{self, State},
nonce_account,
signature::{read_keypair_file, write_keypair, Keypair, Signer}, signature::{read_keypair_file, write_keypair, Keypair, Signer},
system_program, system_program,
}; };
@@ -597,10 +710,6 @@ mod tests {
let default_keypair = Keypair::new(); let default_keypair = Keypair::new();
let (default_keypair_file, mut tmp_file) = make_tmp_file(); let (default_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap(); 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 (keypair_file, mut tmp_file) = make_tmp_file();
let nonce_account_keypair = Keypair::new(); let nonce_account_keypair = Keypair::new();
write_keypair(&nonce_account_keypair, tmp_file.as_file_mut()).unwrap(); write_keypair(&nonce_account_keypair, tmp_file.as_file_mut()).unwrap();
@@ -619,7 +728,12 @@ mod tests {
&Pubkey::default().to_string(), &Pubkey::default().to_string(),
]); ]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::AuthorizeNonceAccount { command: CliCommand::AuthorizeNonceAccount {
nonce_account: nonce_account_pubkey, nonce_account: nonce_account_pubkey,
@@ -640,7 +754,12 @@ mod tests {
&authority_keypair_file, &authority_keypair_file,
]); ]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::AuthorizeNonceAccount { command: CliCommand::AuthorizeNonceAccount {
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(), nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
@@ -662,7 +781,7 @@ mod tests {
"50", "50",
]); ]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::CreateNonceAccount { command: CliCommand::CreateNonceAccount {
nonce_account: 1, nonce_account: 1,
@@ -687,7 +806,7 @@ mod tests {
&authority_keypair_file, &authority_keypair_file,
]); ]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::CreateNonceAccount { command: CliCommand::CreateNonceAccount {
nonce_account: 1, nonce_account: 1,
@@ -709,7 +828,7 @@ mod tests {
&nonce_account_string, &nonce_account_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_get_nonce, &default_signer, &mut None).unwrap(), parse_command(&test_get_nonce, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::GetNonce(nonce_account_keypair.pubkey()), command: CliCommand::GetNonce(nonce_account_keypair.pubkey()),
signers: vec![], signers: vec![],
@@ -723,7 +842,7 @@ mod tests {
.get_matches_from(vec!["test", "new-nonce", &keypair_file]); .get_matches_from(vec!["test", "new-nonce", &keypair_file]);
let nonce_account = read_keypair_file(&keypair_file).unwrap(); let nonce_account = read_keypair_file(&keypair_file).unwrap();
assert_eq!( assert_eq!(
parse_command(&test_new_nonce, &default_signer, &mut None).unwrap(), parse_command(&test_new_nonce, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::NewNonce { command: CliCommand::NewNonce {
nonce_account: nonce_account.pubkey(), nonce_account: nonce_account.pubkey(),
@@ -743,7 +862,7 @@ mod tests {
]); ]);
let nonce_account = read_keypair_file(&keypair_file).unwrap(); let nonce_account = read_keypair_file(&keypair_file).unwrap();
assert_eq!( assert_eq!(
parse_command(&test_new_nonce, &default_signer, &mut None).unwrap(), parse_command(&test_new_nonce, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::NewNonce { command: CliCommand::NewNonce {
nonce_account: nonce_account.pubkey(), nonce_account: nonce_account.pubkey(),
@@ -763,7 +882,7 @@ mod tests {
&nonce_account_string, &nonce_account_string,
]); ]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::ShowNonceAccount { command: CliCommand::ShowNonceAccount {
nonce_account_pubkey: nonce_account_keypair.pubkey(), nonce_account_pubkey: nonce_account_keypair.pubkey(),
@@ -784,7 +903,7 @@ mod tests {
assert_eq!( assert_eq!(
parse_command( parse_command(
&test_withdraw_from_nonce_account, &test_withdraw_from_nonce_account,
&default_signer, &default_keypair_file,
&mut None &mut None
) )
.unwrap(), .unwrap(),
@@ -812,7 +931,7 @@ mod tests {
assert_eq!( assert_eq!(
parse_command( parse_command(
&test_withdraw_from_nonce_account, &test_withdraw_from_nonce_account,
&default_signer, &default_keypair_file,
&mut None &mut None
) )
.unwrap(), .unwrap(),
@@ -834,7 +953,7 @@ mod tests {
#[test] #[test]
fn test_check_nonce_account() { fn test_check_nonce_account() {
let blockhash = Hash::default(); let blockhash = Hash::default();
let nonce_pubkey = solana_sdk::pubkey::new_rand(); let nonce_pubkey = Pubkey::new_rand();
let data = Versions::new_current(State::Initialized(nonce::state::Data { let data = Versions::new_current(State::Initialized(nonce::state::Data {
authority: nonce_pubkey, authority: nonce_pubkey,
blockhash, blockhash,
@@ -847,14 +966,14 @@ mod tests {
if let CliError::InvalidNonce(err) = if let CliError::InvalidNonce(err) =
check_nonce_account(&invalid_owner.unwrap(), &nonce_pubkey, &blockhash).unwrap_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); let invalid_data = Account::new_data(1, &"invalid", &system_program::ID);
if let CliError::InvalidNonce(err) = if let CliError::InvalidNonce(err) =
check_nonce_account(&invalid_data.unwrap(), &nonce_pubkey, &blockhash).unwrap_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 { let data = Versions::new_current(State::Initialized(nonce::state::Data {
@@ -866,11 +985,11 @@ mod tests {
if let CliError::InvalidNonce(err) = if let CliError::InvalidNonce(err) =
check_nonce_account(&invalid_hash.unwrap(), &nonce_pubkey, &blockhash).unwrap_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 { let data = Versions::new_current(State::Initialized(nonce::state::Data {
authority: solana_sdk::pubkey::new_rand(), authority: Pubkey::new_rand(),
blockhash, blockhash,
fee_calculator: FeeCalculator::default(), fee_calculator: FeeCalculator::default(),
})); }));
@@ -878,7 +997,7 @@ mod tests {
if let CliError::InvalidNonce(err) = if let CliError::InvalidNonce(err) =
check_nonce_account(&invalid_authority.unwrap(), &nonce_pubkey, &blockhash).unwrap_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); let data = Versions::new_current(State::Uninitialized);
@@ -886,32 +1005,32 @@ mod tests {
if let CliError::InvalidNonce(err) = if let CliError::InvalidNonce(err) =
check_nonce_account(&invalid_state.unwrap(), &nonce_pubkey, &blockhash).unwrap_err() check_nonce_account(&invalid_state.unwrap(), &nonce_pubkey, &blockhash).unwrap_err()
{ {
assert_eq!(err, Error::InvalidStateForOperation,); assert_eq!(err, CliNonceError::InvalidStateForOperation,);
} }
} }
#[test] #[test]
fn test_account_identity_ok() { fn test_account_identity_ok() {
let nonce_account = nonce_account::create_account(1).into_inner(); let nonce_account = nonce::create_account(1).into_inner();
assert_eq!(account_identity_ok(&nonce_account), Ok(())); assert_eq!(account_identity_ok(&nonce_account), Ok(()));
let system_account = Account::new(1, 0, &system_program::id()); let system_account = Account::new(1, 0, &system_program::id());
assert_eq!( assert_eq!(
account_identity_ok(&system_account), account_identity_ok(&system_account),
Err(Error::UnexpectedDataSize), Err(CliNonceError::UnexpectedDataSize),
); );
let other_program = Pubkey::new(&[1u8; 32]); let other_program = Pubkey::new(&[1u8; 32]);
let other_account_no_data = Account::new(1, 0, &other_program); let other_account_no_data = Account::new(1, 0, &other_program);
assert_eq!( assert_eq!(
account_identity_ok(&other_account_no_data), account_identity_ok(&other_account_no_data),
Err(Error::InvalidAccountOwner), Err(CliNonceError::InvalidAccountOwner),
); );
} }
#[test] #[test]
fn test_state_from_account() { fn test_state_from_account() {
let mut nonce_account = nonce_account::create_account(1).into_inner(); let mut nonce_account = nonce::create_account(1).into_inner();
assert_eq!(state_from_account(&nonce_account), Ok(State::Uninitialized)); assert_eq!(state_from_account(&nonce_account), Ok(State::Uninitialized));
let data = nonce::state::Data { let data = nonce::state::Data {
@@ -930,21 +1049,21 @@ mod tests {
let wrong_data_size_account = Account::new(1, 1, &system_program::id()); let wrong_data_size_account = Account::new(1, 1, &system_program::id());
assert_eq!( assert_eq!(
state_from_account(&wrong_data_size_account), state_from_account(&wrong_data_size_account),
Err(Error::InvalidAccountData), Err(CliNonceError::InvalidAccountData),
); );
} }
#[test] #[test]
fn test_data_from_helpers() { fn test_data_from_helpers() {
let mut nonce_account = nonce_account::create_account(1).into_inner(); let mut nonce_account = nonce::create_account(1).into_inner();
let state = state_from_account(&nonce_account).unwrap(); let state = state_from_account(&nonce_account).unwrap();
assert_eq!( assert_eq!(
data_from_state(&state), data_from_state(&state),
Err(Error::InvalidStateForOperation) Err(CliNonceError::InvalidStateForOperation)
); );
assert_eq!( assert_eq!(
data_from_account(&nonce_account), data_from_account(&nonce_account),
Err(Error::InvalidStateForOperation) Err(CliNonceError::InvalidStateForOperation)
); );
let data = nonce::state::Data { let data = nonce::state::Data {

View File

@@ -1,13 +1,5 @@
use crate::{nonce_utils, rpc_client::RpcClient}; use super::*;
use clap::ArgMatches; use solana_sdk::commitment_config::CommitmentConfig;
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,
};
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum Source { pub enum Source {
@@ -29,8 +21,8 @@ impl Source {
Ok((res.0, res.1)) Ok((res.0, res.1))
} }
Self::NonceAccount(ref pubkey) => { Self::NonceAccount(ref pubkey) => {
let data = nonce_utils::get_account_with_commitment(rpc_client, pubkey, commitment) let data = nonce::get_account_with_commitment(rpc_client, pubkey, commitment)
.and_then(|ref a| nonce_utils::data_from_account(a))?; .and_then(|ref a| nonce::data_from_account(a))?;
Ok((data.blockhash, data.fee_calculator)) Ok((data.blockhash, data.fee_calculator))
} }
} }
@@ -50,8 +42,8 @@ impl Source {
Ok(res) Ok(res)
} }
Self::NonceAccount(ref pubkey) => { Self::NonceAccount(ref pubkey) => {
let res = nonce_utils::get_account_with_commitment(rpc_client, pubkey, commitment)?; let res = nonce::get_account_with_commitment(rpc_client, pubkey, commitment)?;
let res = nonce_utils::data_from_account(&res)?; let res = nonce::data_from_account(&res)?;
Ok(Some(res) Ok(Some(res)
.filter(|d| d.blockhash == *blockhash) .filter(|d| d.blockhash == *blockhash)
.map(|d| d.fee_calculator)) .map(|d| d.fee_calculator))
@@ -83,7 +75,7 @@ impl BlockhashQuery {
pub fn new_from_matches(matches: &ArgMatches<'_>) -> Self { pub fn new_from_matches(matches: &ArgMatches<'_>) -> Self {
let blockhash = value_of(matches, BLOCKHASH_ARG.name); let blockhash = value_of(matches, BLOCKHASH_ARG.name);
let sign_only = matches.is_present(SIGN_ONLY_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) BlockhashQuery::new(blockhash, sign_only, nonce_account)
} }
@@ -116,15 +108,17 @@ impl Default for BlockhashQuery {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::{ use crate::{nonce::nonce_arg, offline::blockhash_query::BlockhashQuery};
blockhash_query,
rpc_request::RpcRequest,
rpc_response::{Response, RpcFeeCalculator, RpcResponseContext},
};
use clap::App; use clap::App;
use serde_json::{self, json, Value}; use serde_json::{self, json, Value};
use solana_account_decoder::{UiAccount, UiAccountEncoding}; use solana_account_decoder::{UiAccount, UiAccountEncoding};
use solana_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; use std::collections::HashMap;
#[test] #[test]
@@ -178,7 +172,7 @@ mod tests {
#[test] #[test]
fn test_blockhash_query_new_from_matches_ok() { fn test_blockhash_query_new_from_matches_ok() {
let test_commands = App::new("blockhash_query_test") let test_commands = App::new("blockhash_query_test")
.nonce_args(false) .arg(nonce_arg())
.offline_args(); .offline_args();
let blockhash = hash(&[1u8]); let blockhash = hash(&[1u8]);
let blockhash_string = blockhash.to_string(); let blockhash_string = blockhash.to_string();
@@ -359,7 +353,7 @@ mod tests {
let rpc_nonce_account = UiAccount::encode( let rpc_nonce_account = UiAccount::encode(
&nonce_pubkey, &nonce_pubkey,
nonce_account, nonce_account,
UiAccountEncoding::Base64, UiAccountEncoding::Binary64,
None, None,
None, None,
); );

129
cli/src/offline/mod.rs Normal file
View 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,
}
}

View File

@@ -1,29 +0,0 @@
use log::*;
use solana_client::rpc_response::{RpcContactInfo, RpcLeaderSchedule};
use std::net::{SocketAddr, UdpSocket};
pub fn get_leader_tpu(
slot_index: u64,
leader_schedule: Option<&RpcLeaderSchedule>,
cluster_nodes: Option<&Vec<RpcContactInfo>>,
) -> Option<SocketAddr> {
leader_schedule?
.iter()
.find(|(_pubkey, slots)| slots.iter().any(|slot| *slot as u64 == slot_index))
.and_then(|(pubkey, _)| {
cluster_nodes?
.iter()
.find(|contact_info| contact_info.pubkey == *pubkey)
.and_then(|contact_info| contact_info.tpu)
})
}
pub fn send_transaction_tpu(
send_socket: &UdpSocket,
tpu_address: &SocketAddr,
wire_transaction: &[u8],
) {
if let Err(err) = send_socket.send_to(wire_transaction, tpu_address) {
warn!("Failed to send transaction to {}: {:?}", tpu_address, err);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
use crate::{ use crate::{
cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult}, cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult},
cli_output::{CliValidatorInfo, CliValidatorInfoVec},
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount}, spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
}; };
use bincode::deserialize; use bincode::deserialize;
@@ -12,9 +13,8 @@ use solana_account_decoder::validator_info::{
use solana_clap_utils::{ use solana_clap_utils::{
input_parsers::pubkey_of, input_parsers::pubkey_of,
input_validators::{is_pubkey, is_url}, 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_client::rpc_client::RpcClient;
use solana_config_program::{config_instruction, get_config_data, ConfigKeys, ConfigState}; use solana_config_program::{config_instruction, get_config_data, ConfigKeys, ConfigState};
use solana_remote_wallet::remote_wallet::RemoteWalletManager; use solana_remote_wallet::remote_wallet::RemoteWalletManager;
@@ -212,7 +212,7 @@ impl ValidatorInfoSubCommands for App<'_, '_> {
pub fn parse_validator_info_command( pub fn parse_validator_info_command(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer: &DefaultSigner, default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let info_pubkey = pubkey_of(matches, "info_pubkey"); let info_pubkey = pubkey_of(matches, "info_pubkey");
@@ -224,7 +224,12 @@ pub fn parse_validator_info_command(
force_keybase: matches.is_present("force"), force_keybase: matches.is_present("force"),
info_pubkey, 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 all_config = rpc_client.get_program_accounts(&solana_config_program::id())?;
let existing_account = all_config let existing_account = all_config
.iter() .iter()
.filter( .filter(|(_, account)| {
|(_, account)| match deserialize::<ConfigKeys>(&account.data) { let key_list: ConfigKeys = deserialize(&account.data).map_err(|_| false).unwrap();
Ok(key_list) => key_list.keys.contains(&(validator_info::id(), false)), key_list.keys.contains(&(validator_info::id(), false))
Err(_) => false, })
},
)
.find(|(pubkey, account)| { .find(|(pubkey, account)| {
let (validator_pubkey, _) = parse_validator_info(&pubkey, &account).unwrap(); let (validator_pubkey, _) = parse_validator_info(&pubkey, &account).unwrap();
validator_pubkey == config.signers[0].pubkey() validator_pubkey == config.signers[0].pubkey()
@@ -382,10 +385,10 @@ pub fn process_get_validator_info(
all_config all_config
.into_iter() .into_iter()
.filter(|(_, validator_info_account)| { .filter(|(_, validator_info_account)| {
match deserialize::<ConfigKeys>(&validator_info_account.data) { let key_list: ConfigKeys = deserialize(&validator_info_account.data)
Ok(key_list) => key_list.keys.contains(&(validator_info::id(), false)), .map_err(|_| false)
Err(_) => false, .unwrap();
} key_list.keys.contains(&(validator_info::id(), false))
}) })
.collect() .collect()
}; };
@@ -486,7 +489,7 @@ mod tests {
#[test] #[test]
fn test_parse_validator_info() { 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 keys = vec![(validator_info::id(), false), (pubkey, true)];
let config = ConfigKeys { keys }; let config = ConfigKeys { keys };

View File

@@ -1,19 +1,14 @@
use crate::{ use crate::{
checks::{check_account_for_fee_with_commitment, check_unique_pubkeys}, checks::{check_account_for_fee_with_commitment, check_unique_pubkeys},
cli::{ cli::{
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError, generate_unique_signers, log_instruction_custom_error, CliCommand, CliCommandInfo,
ProcessResult, CliConfig, CliError, ProcessResult, SignerIndex,
}, },
cli_output::{CliEpochVotingHistory, CliLockout, CliVoteAccount},
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount}, spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
}; };
use clap::{value_t_or_exit, App, Arg, ArgMatches, SubCommand}; use clap::{value_t_or_exit, App, Arg, ArgMatches, SubCommand};
use solana_clap_utils::{ use solana_clap_utils::{commitment::commitment_arg, input_parsers::*, input_validators::*};
commitment::commitment_arg,
input_parsers::*,
input_validators::*,
keypair::{DefaultSigner, SignerIndex},
};
use solana_cli_output::{CliEpochVotingHistory, CliLockout, CliVoteAccount};
use solana_client::rpc_client::RpcClient; use solana_client::rpc_client::RpcClient;
use solana_remote_wallet::remote_wallet::RemoteWalletManager; use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{ use solana_sdk::{
@@ -251,7 +246,7 @@ impl VoteSubCommands for App<'_, '_> {
pub fn parse_create_vote_account( pub fn parse_create_vote_account(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer: &DefaultSigner, default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let (vote_account, vote_account_pubkey) = signer_of(matches, "vote_account", wallet_manager)?; 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 authorized_withdrawer = pubkey_of_signer(matches, "authorized_withdrawer", wallet_manager)?;
let payer_provided = None; 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], vec![payer_provided, vote_account, identity_account],
matches, matches,
default_signer_path,
wallet_manager, wallet_manager,
)?; )?;
@@ -284,7 +280,7 @@ pub fn parse_create_vote_account(
pub fn parse_vote_authorize( pub fn parse_vote_authorize(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer: &DefaultSigner, default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
vote_authorize: VoteAuthorize, vote_authorize: VoteAuthorize,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
@@ -295,9 +291,10 @@ pub fn parse_vote_authorize(
let (authorized, _) = signer_of(matches, "authorized", wallet_manager)?; let (authorized, _) = signer_of(matches, "authorized", wallet_manager)?;
let payer_provided = None; let payer_provided = None;
let signer_info = default_signer.generate_unique_signers( let signer_info = generate_unique_signers(
vec![payer_provided, authorized], vec![payer_provided, authorized],
matches, matches,
default_signer_path,
wallet_manager, wallet_manager,
)?; )?;
@@ -313,7 +310,7 @@ pub fn parse_vote_authorize(
pub fn parse_vote_update_validator( pub fn parse_vote_update_validator(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer: &DefaultSigner, default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let vote_account_pubkey = let vote_account_pubkey =
@@ -324,9 +321,10 @@ pub fn parse_vote_update_validator(
signer_of(matches, "authorized_withdrawer", wallet_manager)?; signer_of(matches, "authorized_withdrawer", wallet_manager)?;
let payer_provided = None; 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], vec![payer_provided, authorized_withdrawer, new_identity_account],
matches, matches,
default_signer_path,
wallet_manager, wallet_manager,
)?; )?;
@@ -342,7 +340,7 @@ pub fn parse_vote_update_validator(
pub fn parse_vote_update_commission( pub fn parse_vote_update_commission(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer: &DefaultSigner, default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let vote_account_pubkey = let vote_account_pubkey =
@@ -352,9 +350,10 @@ pub fn parse_vote_update_commission(
let commission = value_t_or_exit!(matches, "commission", u8); let commission = value_t_or_exit!(matches, "commission", u8);
let payer_provided = None; let payer_provided = None;
let signer_info = default_signer.generate_unique_signers( let signer_info = generate_unique_signers(
vec![payer_provided, authorized_withdrawer], vec![payer_provided, authorized_withdrawer],
matches, matches,
default_signer_path,
wallet_manager, wallet_manager,
)?; )?;
@@ -386,7 +385,7 @@ pub fn parse_vote_get_account_command(
pub fn parse_withdraw_from_vote_account( pub fn parse_withdraw_from_vote_account(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
default_signer: &DefaultSigner, default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>, wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> { ) -> Result<CliCommandInfo, CliError> {
let vote_account_pubkey = let vote_account_pubkey =
@@ -399,9 +398,10 @@ pub fn parse_withdraw_from_vote_account(
signer_of(matches, "authorized_withdrawer", wallet_manager)?; signer_of(matches, "authorized_withdrawer", wallet_manager)?;
let payer_provided = None; let payer_provided = None;
let signer_info = default_signer.generate_unique_signers( let signer_info = generate_unique_signers(
vec![payer_provided, withdraw_authority], vec![payer_provided, withdraw_authority],
matches, matches,
default_signer_path,
wallet_manager, wallet_manager,
)?; )?;
@@ -671,11 +671,11 @@ fn get_vote_account(
pub fn process_show_vote_account( pub fn process_show_vote_account(
rpc_client: &RpcClient, rpc_client: &RpcClient,
config: &CliConfig, config: &CliConfig,
vote_account_address: &Pubkey, vote_account_pubkey: &Pubkey,
use_lamports_unit: bool, use_lamports_unit: bool,
) -> ProcessResult { ) -> ProcessResult {
let (vote_account, vote_state) = 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()?; 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 { let vote_account_data = CliVoteAccount {
account_balance: vote_account.lamports, account_balance: vote_account.lamports,
validator_identity: vote_state.node_pubkey.to_string(), validator_identity: vote_state.node_pubkey.to_string(),
@@ -714,7 +708,6 @@ pub fn process_show_vote_account(
votes, votes,
epoch_voting_history, epoch_voting_history,
use_lamports_unit, use_lamports_unit,
epoch_rewards,
}; };
Ok(config.output_format.formatted_string(&vote_account_data)) Ok(config.output_format.formatted_string(&vote_account_data))
@@ -801,10 +794,6 @@ mod tests {
let default_keypair = Keypair::new(); let default_keypair = Keypair::new();
let (default_keypair_file, mut tmp_file) = make_tmp_file(); let (default_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap(); 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![ let test_authorize_voter = test_commands.clone().get_matches_from(vec![
"test", "test",
@@ -814,7 +803,7 @@ mod tests {
&pubkey2_string, &pubkey2_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_authorize_voter, &default_signer, &mut None).unwrap(), parse_command(&test_authorize_voter, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::VoteAuthorize { command: CliCommand::VoteAuthorize {
vote_account_pubkey: pubkey, vote_account_pubkey: pubkey,
@@ -837,7 +826,7 @@ mod tests {
&pubkey2_string, &pubkey2_string,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_authorize_voter, &default_signer, &mut None).unwrap(), parse_command(&test_authorize_voter, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::VoteAuthorize { command: CliCommand::VoteAuthorize {
vote_account_pubkey: pubkey, vote_account_pubkey: pubkey,
@@ -867,7 +856,7 @@ mod tests {
"10", "10",
]); ]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::CreateVoteAccount { command: CliCommand::CreateVoteAccount {
vote_account: 1, vote_account: 1,
@@ -896,7 +885,7 @@ mod tests {
&identity_keypair_file, &identity_keypair_file,
]); ]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::CreateVoteAccount { command: CliCommand::CreateVoteAccount {
vote_account: 1, vote_account: 1,
@@ -915,7 +904,7 @@ mod tests {
); );
// test init with an authed voter // 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_file, mut tmp_file) = make_tmp_file();
let keypair = Keypair::new(); let keypair = Keypair::new();
write_keypair(&keypair, tmp_file.as_file_mut()).unwrap(); write_keypair(&keypair, tmp_file.as_file_mut()).unwrap();
@@ -929,7 +918,7 @@ mod tests {
&authed.to_string(), &authed.to_string(),
]); ]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::CreateVoteAccount { command: CliCommand::CreateVoteAccount {
vote_account: 1, vote_account: 1,
@@ -960,7 +949,7 @@ mod tests {
&authed.to_string(), &authed.to_string(),
]); ]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::CreateVoteAccount { command: CliCommand::CreateVoteAccount {
vote_account: 1, vote_account: 1,
@@ -986,7 +975,7 @@ mod tests {
&keypair_file, &keypair_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_update_validator, &default_signer, &mut None).unwrap(), parse_command(&test_update_validator, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::VoteUpdateValidator { command: CliCommand::VoteUpdateValidator {
vote_account_pubkey: pubkey, vote_account_pubkey: pubkey,
@@ -1009,7 +998,7 @@ mod tests {
&keypair_file, &keypair_file,
]); ]);
assert_eq!( assert_eq!(
parse_command(&test_update_commission, &default_signer, &mut None).unwrap(), parse_command(&test_update_commission, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo { CliCommandInfo {
command: CliCommand::VoteUpdateCommission { command: CliCommand::VoteUpdateCommission {
vote_account_pubkey: pubkey, vote_account_pubkey: pubkey,
@@ -1032,7 +1021,12 @@ mod tests {
"42", "42",
]); ]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::WithdrawFromVoteAccount { command: CliCommand::WithdrawFromVoteAccount {
vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(), vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(),
@@ -1053,7 +1047,12 @@ mod tests {
"ALL", "ALL",
]); ]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::WithdrawFromVoteAccount { command: CliCommand::WithdrawFromVoteAccount {
vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(), vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(),
@@ -1079,7 +1078,12 @@ mod tests {
&withdraw_authority_file, &withdraw_authority_file,
]); ]);
assert_eq!( 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 { CliCommandInfo {
command: CliCommand::WithdrawFromVoteAccount { command: CliCommand::WithdrawFromVoteAccount {
vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(), vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(),

View File

@@ -1,7 +1,7 @@
use serde_json::Value; use serde_json::Value;
use solana_cli::cli::{process_command, CliCommand, CliConfig}; use solana_cli::cli::{process_command, CliCommand, CliConfig};
use solana_client::rpc_client::RpcClient; 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_faucet::faucet::run_local_faucet;
use solana_sdk::{ use solana_sdk::{
bpf_loader, bpf_loader,
@@ -55,7 +55,7 @@ fn test_cli_deploy_program() {
faucet_host: None, faucet_host: None,
faucet_port: faucet_addr.port(), faucet_port: faucet_addr.port(),
pubkey: None, pubkey: None,
lamports: 4 * minimum_balance_for_rent_exemption, // min balance for rent exemption for three programs + leftover for tx processing lamports: 3 * minimum_balance_for_rent_exemption, // min balance for rent exemption for two programs + leftover for tx processing
}; };
config.signers = vec![&keypair]; config.signers = vec![&keypair];
process_command(&config).unwrap(); process_command(&config).unwrap();
@@ -63,8 +63,6 @@ fn test_cli_deploy_program() {
config.command = CliCommand::Deploy { config.command = CliCommand::Deploy {
program_location: pathbuf.to_str().unwrap().to_string(), program_location: pathbuf.to_str().unwrap().to_string(),
address: None, address: None,
use_deprecated_loader: false,
allow_excessive_balance: false,
}; };
let response = process_command(&config); let response = process_command(&config);
@@ -98,8 +96,6 @@ fn test_cli_deploy_program() {
config.command = CliCommand::Deploy { config.command = CliCommand::Deploy {
program_location: pathbuf.to_str().unwrap().to_string(), program_location: pathbuf.to_str().unwrap().to_string(),
address: Some(1), address: Some(1),
use_deprecated_loader: false,
allow_excessive_balance: false,
}; };
process_command(&config).unwrap(); process_command(&config).unwrap();
let account1 = rpc_client let account1 = rpc_client
@@ -115,44 +111,6 @@ fn test_cli_deploy_program() {
// Attempt to redeploy to the same address // Attempt to redeploy to the same address
process_command(&config).unwrap_err(); process_command(&config).unwrap_err();
// Attempt to deploy to account with excess balance
let custom_address_keypair = Keypair::new();
config.command = CliCommand::Airdrop {
faucet_host: None,
faucet_port: faucet_addr.port(),
pubkey: None,
lamports: 2 * minimum_balance_for_rent_exemption, // Anything over minimum_balance_for_rent_exemption should trigger err
};
config.signers = vec![&custom_address_keypair];
process_command(&config).unwrap();
config.signers = vec![&keypair, &custom_address_keypair];
config.command = CliCommand::Deploy {
program_location: pathbuf.to_str().unwrap().to_string(),
address: Some(1),
use_deprecated_loader: false,
allow_excessive_balance: false,
};
process_command(&config).unwrap_err();
// Use forcing parameter to deploy to account with excess balance
config.command = CliCommand::Deploy {
program_location: pathbuf.to_str().unwrap().to_string(),
address: Some(1),
use_deprecated_loader: false,
allow_excessive_balance: true,
};
process_command(&config).unwrap();
let account2 = rpc_client
.get_account_with_commitment(&custom_address_keypair.pubkey(), CommitmentConfig::recent())
.unwrap()
.value
.unwrap();
assert_eq!(account2.lamports, 2 * minimum_balance_for_rent_exemption);
assert_eq!(account2.owner, bpf_loader::id());
assert_eq!(account2.executable, true);
assert_eq!(account0.data, account2.data);
server.close().unwrap(); server.close().unwrap();
remove_dir_all(ledger_path).unwrap(); remove_dir_all(ledger_path).unwrap();
} }

View File

@@ -1,16 +1,17 @@
use solana_cli::{ use solana_cli::{
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig}, 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, spend_utils::SpendAmount,
test_utils::{check_ready, check_recent_balance}, test_utils::{check_ready, check_recent_balance},
}; };
use solana_cli_output::{parse_sign_only_reply_string, OutputFormat}; use solana_client::rpc_client::RpcClient;
use solana_client::{
blockhash_query::{self, BlockhashQuery},
nonce_utils,
rpc_client::RpcClient,
};
use solana_core::contact_info::ContactInfo; use solana_core::contact_info::ContactInfo;
use solana_core::test_validator::{TestValidator, TestValidatorOptions}; use solana_core::validator::{TestValidator, TestValidatorOptions};
use solana_faucet::faucet::run_local_faucet; use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{ use solana_sdk::{
commitment_config::CommitmentConfig, commitment_config::CommitmentConfig,
@@ -172,7 +173,7 @@ fn full_battery_tests(
assert_ne!(first_nonce, third_nonce); assert_ne!(first_nonce, third_nonce);
// Withdraw from nonce account // Withdraw from nonce account
let payee_pubkey = solana_sdk::pubkey::new_rand(); let payee_pubkey = Pubkey::new_rand();
config_payer.signers = authorized_signers; config_payer.signers = authorized_signers;
config_payer.command = CliCommand::WithdrawFromNonceAccount { config_payer.command = CliCommand::WithdrawFromNonceAccount {
nonce_account, nonce_account,
@@ -301,14 +302,11 @@ fn test_create_account_with_seed() {
check_recent_balance(0, &rpc_client, &to_address); check_recent_balance(0, &rpc_client, &to_address);
// Fetch nonce hash // Fetch nonce hash
let nonce_hash = nonce_utils::get_account_with_commitment( let nonce_hash =
&rpc_client, nonce::get_account_with_commitment(&rpc_client, &nonce_address, CommitmentConfig::recent())
&nonce_address, .and_then(|ref a| nonce::data_from_account(a))
CommitmentConfig::recent(), .unwrap()
) .blockhash;
.and_then(|ref a| nonce_utils::data_from_account(a))
.unwrap()
.blockhash;
// Test by creating transfer TX with nonce, fully offline // Test by creating transfer TX with nonce, fully offline
let mut authority_config = CliConfig::recent_for_tests(); let mut authority_config = CliConfig::recent_for_tests();

453
cli/tests/pay.rs Normal file
View 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();
}

View File

@@ -1,6 +1,6 @@
use solana_cli::cli::{process_command, CliCommand, CliConfig}; use solana_cli::cli::{process_command, CliCommand, CliConfig};
use solana_client::rpc_client::RpcClient; 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_faucet::faucet::run_local_faucet;
use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair}; use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair};
use std::{fs::remove_dir_all, sync::mpsc::channel}; use std::{fs::remove_dir_all, sync::mpsc::channel};

View File

@@ -1,15 +1,16 @@
use solana_cli::{ use solana_cli::{
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig}, 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, spend_utils::SpendAmount,
test_utils::{check_ready, check_recent_balance}, test_utils::{check_ready, check_recent_balance},
}; };
use solana_cli_output::{parse_sign_only_reply_string, OutputFormat}; use solana_client::rpc_client::RpcClient;
use solana_client::{ use solana_core::validator::{TestValidator, TestValidatorOptions};
blockhash_query::{self, BlockhashQuery},
nonce_utils,
rpc_client::RpcClient,
};
use solana_core::test_validator::{TestValidator, TestValidatorOptions};
use solana_faucet::faucet::run_local_faucet; use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{ use solana_sdk::{
account_utils::StateMut, account_utils::StateMut,
@@ -502,12 +503,12 @@ fn test_nonced_stake_delegation_and_deactivation() {
process_command(&config).unwrap(); process_command(&config).unwrap();
// Fetch nonce hash // Fetch nonce hash
let nonce_hash = nonce_utils::get_account_with_commitment( let nonce_hash = nonce::get_account_with_commitment(
&rpc_client, &rpc_client,
&nonce_account.pubkey(), &nonce_account.pubkey(),
CommitmentConfig::recent(), CommitmentConfig::recent(),
) )
.and_then(|ref a| nonce_utils::data_from_account(a)) .and_then(|ref a| nonce::data_from_account(a))
.unwrap() .unwrap()
.blockhash; .blockhash;
@@ -530,12 +531,12 @@ fn test_nonced_stake_delegation_and_deactivation() {
process_command(&config).unwrap(); process_command(&config).unwrap();
// Fetch nonce hash // Fetch nonce hash
let nonce_hash = nonce_utils::get_account_with_commitment( let nonce_hash = nonce::get_account_with_commitment(
&rpc_client, &rpc_client,
&nonce_account.pubkey(), &nonce_account.pubkey(),
CommitmentConfig::recent(), CommitmentConfig::recent(),
) )
.and_then(|ref a| nonce_utils::data_from_account(a)) .and_then(|ref a| nonce::data_from_account(a))
.unwrap() .unwrap()
.blockhash; .blockhash;
@@ -769,12 +770,12 @@ fn test_stake_authorize() {
process_command(&config).unwrap(); process_command(&config).unwrap();
// Fetch nonce hash // Fetch nonce hash
let nonce_hash = nonce_utils::get_account_with_commitment( let nonce_hash = nonce::get_account_with_commitment(
&rpc_client, &rpc_client,
&nonce_account.pubkey(), &nonce_account.pubkey(),
CommitmentConfig::recent(), CommitmentConfig::recent(),
) )
.and_then(|ref a| nonce_utils::data_from_account(a)) .and_then(|ref a| nonce::data_from_account(a))
.unwrap() .unwrap()
.blockhash; .blockhash;
@@ -823,12 +824,12 @@ fn test_stake_authorize() {
}; };
assert_eq!(current_authority, online_authority_pubkey); 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, &rpc_client,
&nonce_account.pubkey(), &nonce_account.pubkey(),
CommitmentConfig::recent(), CommitmentConfig::recent(),
) )
.and_then(|ref a| nonce_utils::data_from_account(a)) .and_then(|ref a| nonce::data_from_account(a))
.unwrap() .unwrap()
.blockhash; .blockhash;
assert_ne!(nonce_hash, new_nonce_hash); 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()); check_recent_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey());
// Fetch nonce hash // Fetch nonce hash
let nonce_hash = nonce_utils::get_account_with_commitment( let nonce_hash = nonce::get_account_with_commitment(
&rpc_client, &rpc_client,
&nonce_account.pubkey(), &nonce_account.pubkey(),
CommitmentConfig::recent(), CommitmentConfig::recent(),
) )
.and_then(|ref a| nonce_utils::data_from_account(a)) .and_then(|ref a| nonce::data_from_account(a))
.unwrap() .unwrap()
.blockhash; .blockhash;
@@ -1337,12 +1338,12 @@ fn test_stake_set_lockup() {
check_recent_balance(minimum_nonce_balance, &rpc_client, &nonce_account_pubkey); check_recent_balance(minimum_nonce_balance, &rpc_client, &nonce_account_pubkey);
// Fetch nonce hash // Fetch nonce hash
let nonce_hash = nonce_utils::get_account_with_commitment( let nonce_hash = nonce::get_account_with_commitment(
&rpc_client, &rpc_client,
&nonce_account.pubkey(), &nonce_account.pubkey(),
CommitmentConfig::recent(), CommitmentConfig::recent(),
) )
.and_then(|ref a| nonce_utils::data_from_account(a)) .and_then(|ref a| nonce::data_from_account(a))
.unwrap() .unwrap()
.blockhash; .blockhash;
@@ -1464,12 +1465,12 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
process_command(&config).unwrap(); process_command(&config).unwrap();
// Fetch nonce hash // Fetch nonce hash
let nonce_hash = nonce_utils::get_account_with_commitment( let nonce_hash = nonce::get_account_with_commitment(
&rpc_client, &rpc_client,
&nonce_account.pubkey(), &nonce_account.pubkey(),
CommitmentConfig::recent(), CommitmentConfig::recent(),
) )
.and_then(|ref a| nonce_utils::data_from_account(a)) .and_then(|ref a| nonce::data_from_account(a))
.unwrap() .unwrap()
.blockhash; .blockhash;
@@ -1519,12 +1520,12 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
check_recent_balance(50_000, &rpc_client, &stake_pubkey); check_recent_balance(50_000, &rpc_client, &stake_pubkey);
// Fetch nonce hash // Fetch nonce hash
let nonce_hash = nonce_utils::get_account_with_commitment( let nonce_hash = nonce::get_account_with_commitment(
&rpc_client, &rpc_client,
&nonce_account.pubkey(), &nonce_account.pubkey(),
CommitmentConfig::recent(), CommitmentConfig::recent(),
) )
.and_then(|ref a| nonce_utils::data_from_account(a)) .and_then(|ref a| nonce::data_from_account(a))
.unwrap() .unwrap()
.blockhash; .blockhash;
@@ -1567,12 +1568,12 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
check_recent_balance(42, &rpc_client, &recipient_pubkey); check_recent_balance(42, &rpc_client, &recipient_pubkey);
// Fetch nonce hash // Fetch nonce hash
let nonce_hash = nonce_utils::get_account_with_commitment( let nonce_hash = nonce::get_account_with_commitment(
&rpc_client, &rpc_client,
&nonce_account.pubkey(), &nonce_account.pubkey(),
CommitmentConfig::recent(), CommitmentConfig::recent(),
) )
.and_then(|ref a| nonce_utils::data_from_account(a)) .and_then(|ref a| nonce::data_from_account(a))
.unwrap() .unwrap()
.blockhash; .blockhash;

View File

@@ -1,15 +1,16 @@
use solana_cli::{ use solana_cli::{
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig}, 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, spend_utils::SpendAmount,
test_utils::{check_ready, check_recent_balance}, test_utils::{check_ready, check_recent_balance},
}; };
use solana_cli_output::{parse_sign_only_reply_string, OutputFormat}; use solana_client::rpc_client::RpcClient;
use solana_client::{ use solana_core::validator::{TestValidator, TestValidatorOptions};
blockhash_query::{self, BlockhashQuery},
nonce_utils,
rpc_client::RpcClient,
};
use solana_core::test_validator::{TestValidator, TestValidatorOptions};
use solana_faucet::faucet::run_local_faucet; use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{ use solana_sdk::{
commitment_config::CommitmentConfig, commitment_config::CommitmentConfig,
@@ -152,12 +153,12 @@ fn test_transfer() {
check_recent_balance(49_987 - minimum_nonce_balance, &rpc_client, &sender_pubkey); check_recent_balance(49_987 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
// Fetch nonce hash // Fetch nonce hash
let nonce_hash = nonce_utils::get_account_with_commitment( let nonce_hash = nonce::get_account_with_commitment(
&rpc_client, &rpc_client,
&nonce_account.pubkey(), &nonce_account.pubkey(),
CommitmentConfig::recent(), CommitmentConfig::recent(),
) )
.and_then(|ref a| nonce_utils::data_from_account(a)) .and_then(|ref a| nonce::data_from_account(a))
.unwrap() .unwrap()
.blockhash; .blockhash;
@@ -180,12 +181,12 @@ fn test_transfer() {
process_command(&config).unwrap(); process_command(&config).unwrap();
check_recent_balance(49_976 - minimum_nonce_balance, &rpc_client, &sender_pubkey); check_recent_balance(49_976 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
check_recent_balance(30, &rpc_client, &recipient_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, &rpc_client,
&nonce_account.pubkey(), &nonce_account.pubkey(),
CommitmentConfig::recent(), CommitmentConfig::recent(),
) )
.and_then(|ref a| nonce_utils::data_from_account(a)) .and_then(|ref a| nonce::data_from_account(a))
.unwrap() .unwrap()
.blockhash; .blockhash;
assert_ne!(nonce_hash, new_nonce_hash); 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); check_recent_balance(49_975 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
// Fetch nonce hash // Fetch nonce hash
let nonce_hash = nonce_utils::get_account_with_commitment( let nonce_hash = nonce::get_account_with_commitment(
&rpc_client, &rpc_client,
&nonce_account.pubkey(), &nonce_account.pubkey(),
CommitmentConfig::recent(), CommitmentConfig::recent(),
) )
.and_then(|ref a| nonce_utils::data_from_account(a)) .and_then(|ref a| nonce::data_from_account(a))
.unwrap() .unwrap()
.blockhash; .blockhash;

View File

@@ -1,17 +1,16 @@
use solana_cli::{ use solana_cli::{
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig}, cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
offline::{blockhash_query::BlockhashQuery, *},
spend_utils::SpendAmount, spend_utils::SpendAmount,
test_utils::check_recent_balance, test_utils::check_recent_balance,
}; };
use solana_client::{ use solana_client::rpc_client::RpcClient;
blockhash_query::{self, BlockhashQuery}, use solana_core::validator::TestValidator;
rpc_client::RpcClient,
};
use solana_core::test_validator::TestValidator;
use solana_faucet::faucet::run_local_faucet; use solana_faucet::faucet::run_local_faucet;
use solana_sdk::{ use solana_sdk::{
account_utils::StateMut, account_utils::StateMut,
commitment_config::CommitmentConfig, commitment_config::CommitmentConfig,
pubkey::Pubkey,
signature::{Keypair, Signer}, signature::{Keypair, Signer},
}; };
use solana_vote_program::vote_state::{VoteAuthorize, VoteState, VoteStateVersions}; use solana_vote_program::vote_state::{VoteAuthorize, VoteState, VoteStateVersions};
@@ -109,7 +108,7 @@ fn test_vote_authorize_and_withdraw() {
assert_eq!(authorized_withdrawer, withdraw_authority.pubkey()); assert_eq!(authorized_withdrawer, withdraw_authority.pubkey());
// Withdraw from vote account // Withdraw from vote account
let destination_account = 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.signers = vec![&default_signer, &withdraw_authority];
config.command = CliCommand::WithdrawFromVoteAccount { config.command = CliCommand::WithdrawFromVoteAccount {
vote_account_pubkey, vote_account_pubkey,

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "solana-client" name = "solana-client"
version = "1.4.7" version = "1.3.2"
description = "Solana Client" description = "Solana Client"
authors = ["Solana Maintainers <maintainers@solana.foundation>"] authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana" repository = "https://github.com/solana-labs/solana"
@@ -9,35 +9,30 @@ license = "Apache-2.0"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
base64 = "0.13.0"
bincode = "1.3.1" bincode = "1.3.1"
bs58 = "0.3.1" bs58 = "0.3.1"
clap = "2.33.0"
indicatif = "0.15.0" indicatif = "0.15.0"
jsonrpc-core = "15.0.0" jsonrpc-core = "14.2.0"
log = "0.4.8" log = "0.4.8"
rayon = "1.4.0" rayon = "1.3.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"] }
semver = "0.11.0"
serde = "1.0.112" serde = "1.0.112"
serde_derive = "1.0.103" serde_derive = "1.0.103"
serde_json = "1.0.56" serde_json = "1.0.56"
solana-account-decoder = { path = "../account-decoder", version = "1.4.7" } solana-account-decoder = { path = "../account-decoder", version = "1.3.2" }
solana-clap-utils = { path = "../clap-utils", version = "1.4.7" } solana-net-utils = { path = "../net-utils", version = "1.3.2" }
solana-net-utils = { path = "../net-utils", version = "1.4.7" } solana-sdk = { path = "../sdk", version = "1.3.2" }
solana-sdk = { path = "../sdk", version = "1.4.7" } solana-transaction-status = { path = "../transaction-status", version = "1.3.2" }
solana-transaction-status = { path = "../transaction-status", version = "1.4.7" } solana-vote-program = { path = "../programs/vote", version = "1.3.2" }
solana-version = { path = "../version", version = "1.4.7" }
solana-vote-program = { path = "../programs/vote", version = "1.4.7" }
thiserror = "1.0" thiserror = "1.0"
tungstenite = "0.10.1" tungstenite = "0.10.1"
url = "2.1.1" url = "2.1.1"
[dev-dependencies] [dev-dependencies]
assert_matches = "1.3.0" assert_matches = "1.3.0"
jsonrpc-core = "15.0.0" jsonrpc-core = "14.2.0"
jsonrpc-http-server = "15.0.0" jsonrpc-http-server = "14.2.0"
solana-logger = { path = "../logger", version = "1.4.7" } solana-logger = { path = "../logger", version = "1.3.2" }
[package.metadata.docs.rs] [package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"] targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -50,10 +50,10 @@ impl Into<TransportError> for ClientErrorKind {
#[derive(Error, Debug)] #[derive(Error, Debug)]
#[error("{kind}")] #[error("{kind}")]
pub struct ClientError { pub struct ClientError {
pub request: Option<rpc_request::RpcRequest>, request: Option<rpc_request::RpcRequest>,
#[source] #[source]
pub kind: ClientErrorKind, kind: ClientErrorKind,
} }
impl ClientError { impl ClientError {

View File

@@ -1,8 +1,6 @@
use crate::{ use crate::{
client_error::Result, client_error::Result,
rpc_custom_error, rpc_request::{RpcError, RpcRequest},
rpc_request::{RpcError, RpcRequest, RpcResponseErrorData},
rpc_response::RpcSimulateTransactionResult,
rpc_sender::RpcSender, rpc_sender::RpcSender,
}; };
use log::*; use log::*;
@@ -29,13 +27,6 @@ impl HttpSender {
} }
} }
#[derive(Deserialize, Debug)]
struct RpcErrorObject {
code: i64,
message: String,
data: serde_json::Value,
}
impl RpcSender for HttpSender { impl RpcSender for HttpSender {
fn send(&self, request: RpcRequest, params: serde_json::Value) -> Result<serde_json::Value> { fn send(&self, request: RpcRequest, params: serde_json::Value) -> Result<serde_json::Value> {
// Concurrent requests are not supported so reuse the same request id for all requests // Concurrent requests are not supported so reuse the same request id for all requests
@@ -72,36 +63,11 @@ impl RpcSender for HttpSender {
let json: serde_json::Value = serde_json::from_str(&response.text()?)?; let json: serde_json::Value = serde_json::from_str(&response.text()?)?;
if json["error"].is_object() { if json["error"].is_object() {
return match serde_json::from_value::<RpcErrorObject>(json["error"].clone()) return Err(RpcError::RpcRequestError(format!(
{ "RPC Error response: {}",
Ok(rpc_error_object) => { serde_json::to_string(&json["error"]).unwrap()
let data = match rpc_error_object.code { ))
rpc_custom_error::JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE => { .into());
match serde_json::from_value::<RpcSimulateTransactionResult>(json["error"]["data"].clone()) {
Ok(data) => RpcResponseErrorData::SendTransactionPreflightFailure(data),
Err(err) => {
debug!("Failed to deserialize RpcSimulateTransactionResult: {:?}", err);
RpcResponseErrorData::Empty
}
}
},
_ => RpcResponseErrorData::Empty
};
Err(RpcError::RpcResponseError {
code: rpc_error_object.code,
message: rpc_error_object.message,
data,
}
.into())
}
Err(err) => Err(RpcError::RpcRequestError(format!(
"Failed to deserialize RPC error response: {} [{}]",
serde_json::to_string(&json["error"]).unwrap(),
err
))
.into()),
};
} }
return Ok(json["result"].clone()); return Ok(json["result"].clone());
} }

View File

@@ -1,16 +1,13 @@
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
pub mod blockhash_query;
pub mod client_error; pub mod client_error;
pub mod http_sender; pub mod http_sender;
pub mod mock_sender; pub mod mock_sender;
pub mod nonce_utils;
pub mod perf_utils; pub mod perf_utils;
pub mod pubsub_client; pub mod pubsub_client;
pub mod rpc_client; pub mod rpc_client;
pub mod rpc_config; pub mod rpc_config;
pub mod rpc_custom_error;
pub mod rpc_filter; pub mod rpc_filter;
pub mod rpc_request; pub mod rpc_request;
pub mod rpc_response; pub mod rpc_response;

View File

@@ -1,19 +1,17 @@
use crate::{ use crate::{
client_error::Result, client_error::Result,
rpc_request::RpcRequest, rpc_request::RpcRequest,
rpc_response::{Response, RpcResponseContext, RpcVersionInfo}, rpc_response::{Response, RpcResponseContext},
rpc_sender::RpcSender, rpc_sender::RpcSender,
}; };
use serde_json::{json, Number, Value}; use serde_json::{Number, Value};
use solana_sdk::{ use solana_sdk::{
epoch_info::EpochInfo,
fee_calculator::{FeeCalculator, FeeRateGovernor}, fee_calculator::{FeeCalculator, FeeRateGovernor},
instruction::InstructionError, instruction::InstructionError,
signature::Signature, signature::Signature,
transaction::{self, Transaction, TransactionError}, transaction::{self, Transaction, TransactionError},
}; };
use solana_transaction_status::TransactionStatus; use solana_transaction_status::TransactionStatus;
use solana_version::Version;
use std::{collections::HashMap, sync::RwLock}; use std::{collections::HashMap, sync::RwLock};
pub const PUBKEY: &str = "7RoSF9fUmdphVCpabEoefH81WwrW7orsWonXWqTXkKV8"; pub const PUBKEY: &str = "7RoSF9fUmdphVCpabEoefH81WwrW7orsWonXWqTXkKV8";
@@ -59,13 +57,6 @@ impl RpcSender for MockSender {
serde_json::to_value(FeeCalculator::default()).unwrap(), serde_json::to_value(FeeCalculator::default()).unwrap(),
), ),
})?, })?,
RpcRequest::GetEpochInfo => serde_json::to_value(EpochInfo {
epoch: 1,
slot_index: 2,
slots_in_epoch: 32,
absolute_slot: 34,
block_height: 34,
})?,
RpcRequest::GetFeeCalculatorForBlockhash => { RpcRequest::GetFeeCalculatorForBlockhash => {
let value = if self.url == "blockhash_expired" { let value = if self.url == "blockhash_expired" {
Value::Null Value::Null
@@ -103,15 +94,9 @@ impl RpcSender for MockSender {
err, err,
}) })
}; };
let statuses: Vec<Option<TransactionStatus>> = params.as_array().unwrap()[0]
.as_array()
.unwrap()
.iter()
.map(|_| status.clone())
.collect();
serde_json::to_value(Response { serde_json::to_value(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext { slot: 1 },
value: statuses, value: vec![status],
})? })?
} }
RpcRequest::GetTransactionCount => Value::Number(Number::from(1234)), RpcRequest::GetTransactionCount => Value::Number(Number::from(1234)),
@@ -121,20 +106,13 @@ impl RpcSender for MockSender {
Signature::new(&[8; 64]).to_string() Signature::new(&[8; 64]).to_string()
} else { } else {
let tx_str = params.as_array().unwrap()[0].as_str().unwrap().to_string(); let tx_str = params.as_array().unwrap()[0].as_str().unwrap().to_string();
let data = base64::decode(tx_str).unwrap(); let data = bs58::decode(tx_str).into_vec().unwrap();
let tx: Transaction = bincode::deserialize(&data).unwrap(); let tx: Transaction = bincode::deserialize(&data).unwrap();
tx.signatures[0].to_string() tx.signatures[0].to_string()
}; };
Value::String(signature) Value::String(signature)
} }
RpcRequest::GetMinimumBalanceForRentExemption => Value::Number(Number::from(20)), RpcRequest::GetMinimumBalanceForRentExemption => Value::Number(Number::from(20)),
RpcRequest::GetVersion => {
let version = Version::default();
json!(RpcVersionInfo {
solana_core: version.to_string(),
feature_set: Some(version.feature_set),
})
}
_ => Value::Null, _ => Value::Null,
}; };
Ok(val) Ok(val)

View File

@@ -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),
}
}

View File

@@ -1,12 +1,10 @@
use crate::rpc_response::{Response as RpcResponse, RpcSignatureResult, SlotInfo};
use log::*; use log::*;
use serde::de::DeserializeOwned; use serde::{de::DeserializeOwned, Deserialize};
use serde_json::{ use serde_json::{
json, json,
value::Value::{Number, Object}, value::Value::{Number, Object},
Map, Value, Map, Value,
}; };
use solana_sdk::signature::Signature;
use std::{ use std::{
marker::PhantomData, marker::PhantomData,
sync::{ sync::{
@@ -20,8 +18,6 @@ use thiserror::Error;
use tungstenite::{client::AutoStream, connect, Message, WebSocket}; use tungstenite::{client::AutoStream, connect, Message, WebSocket};
use url::{ParseError, Url}; use url::{ParseError, Url};
type PubsubSignatureResponse = PubsubClientSubscription<RpcResponse<RpcSignatureResult>>;
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum PubsubClientError { pub enum PubsubClientError {
#[error("url parse error")] #[error("url parse error")]
@@ -37,6 +33,13 @@ pub enum PubsubClientError {
UnexpectedMessageError, UnexpectedMessageError,
} }
#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)]
pub struct SlotInfoMessage {
pub parent: u64,
pub root: u64,
pub slot: u64,
}
pub struct PubsubClientSubscription<T> pub struct PubsubClientSubscription<T>
where where
T: DeserializeOwned, T: DeserializeOwned,
@@ -70,12 +73,18 @@ where
{ {
fn send_subscribe( fn send_subscribe(
writable_socket: &Arc<RwLock<WebSocket<AutoStream>>>, writable_socket: &Arc<RwLock<WebSocket<AutoStream>>>,
body: String, operation: &str,
) -> Result<u64, PubsubClientError> { ) -> Result<u64, PubsubClientError> {
let method = format!("{}Subscribe", operation);
writable_socket writable_socket
.write() .write()
.unwrap() .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()?; let message = writable_socket.write().unwrap().read_message()?;
Self::extract_subscription_id(message) Self::extract_subscription_id(message)
} }
@@ -139,28 +148,30 @@ where
} }
const SLOT_OPERATION: &str = "slot"; const SLOT_OPERATION: &str = "slot";
const SIGNATURE_OPERATION: &str = "signature";
pub struct PubsubClient {} pub struct PubsubClient {}
impl PubsubClient { impl PubsubClient {
pub fn slot_subscribe( pub fn slot_subscribe(
url: &str, url: &str,
) -> Result<(PubsubClientSubscription<SlotInfo>, Receiver<SlotInfo>), PubsubClientError> { ) -> Result<
(
PubsubClientSubscription<SlotInfoMessage>,
Receiver<SlotInfoMessage>,
),
PubsubClientError,
> {
let url = Url::parse(url)?; let url = Url::parse(url)?;
let (socket, _response) = connect(url)?; let (socket, _response) = connect(url)?;
let (sender, receiver) = channel::<SlotInfo>(); let (sender, receiver) = channel::<SlotInfoMessage>();
let socket = Arc::new(RwLock::new(socket)); let socket = Arc::new(RwLock::new(socket));
let socket_clone = socket.clone(); let socket_clone = socket.clone();
let exit = Arc::new(AtomicBool::new(false)); let exit = Arc::new(AtomicBool::new(false));
let exit_clone = exit.clone(); let exit_clone = exit.clone();
let subscription_id = PubsubClientSubscription::<SlotInfo>::send_subscribe( let subscription_id = PubsubClientSubscription::<SlotInfoMessage>::send_subscribe(
&socket_clone, &socket_clone,
json!({ SLOT_OPERATION,
"jsonrpc":"2.0","id":1,"method":format!("{}Subscribe", SLOT_OPERATION),"params":[]
})
.to_string(),
) )
.unwrap(); .unwrap();
@@ -170,80 +181,7 @@ impl PubsubClient {
break; break;
} }
let message: Result<SlotInfo, PubsubClientError> = let message: Result<SlotInfoMessage, 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> =
PubsubClientSubscription::read_message(&socket_clone); PubsubClientSubscription::read_message(&socket_clone);
if let Ok(msg) = message { if let Ok(msg) = message {
@@ -263,15 +201,14 @@ impl PubsubClient {
info!("websocket - exited receive loop"); info!("websocket - exited receive loop");
}); });
let result: PubsubClientSubscription<RpcResponse<RpcSignatureResult>> = let result: PubsubClientSubscription<SlotInfoMessage> = PubsubClientSubscription {
PubsubClientSubscription { message_type: PhantomData,
message_type: PhantomData, operation: SLOT_OPERATION,
operation: SIGNATURE_OPERATION, socket,
socket, subscription_id,
subscription_id, t_cleanup: Some(t_cleanup),
t_cleanup: Some(t_cleanup), exit,
exit, };
};
Ok((result, receiver)) Ok((result, receiver))
} }

View File

@@ -5,10 +5,9 @@ use crate::{
rpc_config::RpcAccountInfoConfig, rpc_config::RpcAccountInfoConfig,
rpc_config::{ rpc_config::{
RpcGetConfirmedSignaturesForAddress2Config, RpcLargestAccountsConfig, RpcGetConfirmedSignaturesForAddress2Config, RpcLargestAccountsConfig,
RpcProgramAccountsConfig, RpcSendTransactionConfig, RpcSimulateTransactionConfig, RpcSendTransactionConfig, RpcTokenAccountsFilter,
RpcTokenAccountsFilter,
}, },
rpc_request::{RpcError, RpcRequest, RpcResponseErrorData, TokenAccountsFilter}, rpc_request::{RpcError, RpcRequest, TokenAccountsFilter},
rpc_response::*, rpc_response::*,
rpc_sender::RpcSender, rpc_sender::RpcSender,
}; };
@@ -17,8 +16,10 @@ use indicatif::{ProgressBar, ProgressStyle};
use log::*; use log::*;
use serde_json::{json, Value}; use serde_json::{json, Value};
use solana_account_decoder::{ use solana_account_decoder::{
parse_token::{TokenAccountType, UiTokenAccount, UiTokenAmount}, parse_token::UiTokenAmount,
UiAccount, UiAccountData, UiAccountEncoding, UiAccount,
UiAccountData::{Binary, Binary64},
UiAccountEncoding,
}; };
use solana_sdk::{ use solana_sdk::{
account::Account, account::Account,
@@ -33,49 +34,26 @@ use solana_sdk::{
hash::Hash, hash::Hash,
pubkey::Pubkey, pubkey::Pubkey,
signature::Signature, signature::Signature,
transaction::{self, uses_durable_nonce, Transaction}, transaction::{self, Transaction},
}; };
use solana_transaction_status::{ use solana_transaction_status::{
EncodedConfirmedBlock, EncodedConfirmedTransaction, TransactionStatus, UiTransactionEncoding, ConfirmedBlock, ConfirmedTransaction, TransactionStatus, UiTransactionEncoding,
}; };
use solana_vote_program::vote_state::MAX_LOCKOUT_HISTORY; use solana_vote_program::vote_state::MAX_LOCKOUT_HISTORY;
use std::{ use std::{
net::SocketAddr, net::SocketAddr,
sync::RwLock,
thread::sleep, thread::sleep,
time::{Duration, Instant}, time::{Duration, Instant},
}; };
pub struct RpcClient { pub struct RpcClient {
sender: Box<dyn RpcSender + Send + Sync + 'static>, sender: Box<dyn RpcSender + Send + Sync + 'static>,
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 { impl RpcClient {
pub fn new_sender<T: RpcSender + Send + Sync + 'static>(sender: T) -> Self { pub fn new_sender<T: RpcSender + Send + Sync + 'static>(sender: T) -> Self {
Self { Self {
sender: Box::new(sender), sender: Box::new(sender),
default_cluster_transaction_encoding: RwLock::new(None),
} }
} }
@@ -131,73 +109,17 @@ impl RpcClient {
self.send_transaction_with_config(transaction, RpcSendTransactionConfig::default()) 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( pub fn send_transaction_with_config(
&self, &self,
transaction: &Transaction, transaction: &Transaction,
config: RpcSendTransactionConfig, config: RpcSendTransactionConfig,
) -> ClientResult<Signature> { ) -> ClientResult<Signature> {
let encoding = if let Some(encoding) = config.encoding { let serialized_encoded = bs58::encode(serialize(transaction).unwrap()).into_string();
encoding
} else { let signature_base58_str: String = self.send(
self.default_cluster_transaction_encoding()?
};
let config = RpcSendTransactionConfig {
encoding: Some(encoding),
..config
};
let serialized_encoded = serialize_encode_transaction(transaction, encoding)?;
let signature_base58_str: String = match self.send(
RpcRequest::SendTransaction, RpcRequest::SendTransaction,
json!([serialized_encoded, config]), json!([serialized_encoded, config]),
) { )?;
Ok(signature_base58_str) => signature_base58_str,
Err(err) => {
if let ClientErrorKind::RpcError(RpcError::RpcResponseError {
code,
message,
data,
}) = &err.kind
{
debug!("{} {}", code, message);
if let RpcResponseErrorData::SendTransactionPreflightFailure(
RpcSimulateTransactionResult {
logs: Some(logs), ..
},
) = data
{
for (i, log) in logs.iter().enumerate() {
debug!("{:>3}: {}", i + 1, log);
}
}
}
return Err(err);
}
};
let signature = signature_base58_str let signature = signature_base58_str
.parse::<Signature>() .parse::<Signature>()
@@ -220,28 +142,12 @@ impl RpcClient {
pub fn simulate_transaction( pub fn simulate_transaction(
&self, &self,
transaction: &Transaction, transaction: &Transaction,
sig_verify: bool,
) -> RpcResult<RpcSimulateTransactionResult> { ) -> RpcResult<RpcSimulateTransactionResult> {
self.simulate_transaction_with_config(transaction, RpcSimulateTransactionConfig::default()) let serialized_encoded = bs58::encode(serialize(transaction).unwrap()).into_string();
}
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)?;
self.send( self.send(
RpcRequest::SimulateTransaction, RpcRequest::SimulateTransaction,
json!([serialized_encoded, config]), json!([serialized_encoded, { "sigVerify": sig_verify }]),
) )
} }
@@ -260,19 +166,6 @@ impl RpcClient {
self.send(RpcRequest::GetSignatureStatuses, json!([signatures])) 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( pub fn get_signature_status_with_commitment(
&self, &self,
signature: &Signature, signature: &Signature,
@@ -357,7 +250,7 @@ impl RpcClient {
self.send(RpcRequest::GetClusterNodes, Value::Null) 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) self.get_confirmed_block_with_encoding(slot, UiTransactionEncoding::Json)
} }
@@ -365,7 +258,7 @@ impl RpcClient {
&self, &self,
slot: Slot, slot: Slot,
encoding: UiTransactionEncoding, encoding: UiTransactionEncoding,
) -> ClientResult<EncodedConfirmedBlock> { ) -> ClientResult<ConfirmedBlock> {
self.send(RpcRequest::GetConfirmedBlock, json!([slot, encoding])) self.send(RpcRequest::GetConfirmedBlock, json!([slot, encoding]))
} }
@@ -380,17 +273,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( pub fn get_confirmed_signatures_for_address(
&self, &self,
address: &Pubkey, address: &Pubkey,
@@ -417,21 +299,18 @@ impl RpcClient {
&self, &self,
address: &Pubkey, address: &Pubkey,
) -> ClientResult<Vec<RpcConfirmedTransactionStatusWithSignature>> { ) -> ClientResult<Vec<RpcConfirmedTransactionStatusWithSignature>> {
self.get_confirmed_signatures_for_address2_with_config( self.get_confirmed_signatures_for_address2_with_config(address, None, None)
address,
GetConfirmedSignaturesForAddress2Config::default(),
)
} }
pub fn get_confirmed_signatures_for_address2_with_config( pub fn get_confirmed_signatures_for_address2_with_config(
&self, &self,
address: &Pubkey, address: &Pubkey,
config: GetConfirmedSignaturesForAddress2Config, before: Option<Signature>,
limit: Option<usize>,
) -> ClientResult<Vec<RpcConfirmedTransactionStatusWithSignature>> { ) -> ClientResult<Vec<RpcConfirmedTransactionStatusWithSignature>> {
let config = RpcGetConfirmedSignaturesForAddress2Config { let config = RpcGetConfirmedSignaturesForAddress2Config {
before: config.before.map(|signature| signature.to_string()), before: before.map(|signature| signature.to_string()),
until: config.until.map(|signature| signature.to_string()), limit,
limit: config.limit,
}; };
let result: Vec<RpcConfirmedTransactionStatusWithSignature> = self.send( let result: Vec<RpcConfirmedTransactionStatusWithSignature> = self.send(
@@ -446,7 +325,7 @@ impl RpcClient {
&self, &self,
signature: &Signature, signature: &Signature,
encoding: UiTransactionEncoding, encoding: UiTransactionEncoding,
) -> ClientResult<EncodedConfirmedTransaction> { ) -> ClientResult<ConfirmedTransaction> {
self.send( self.send(
RpcRequest::GetConfirmedTransaction, RpcRequest::GetConfirmedTransaction,
json!([signature.to_string(), encoding]), json!([signature.to_string(), encoding]),
@@ -534,48 +413,44 @@ impl RpcClient {
&self, &self,
transaction: &Transaction, transaction: &Transaction,
) -> ClientResult<Signature> { ) -> ClientResult<Signature> {
let signature = self.send_transaction(transaction)?; let mut send_retries = 20;
let recent_blockhash = if uses_durable_nonce(transaction).is_some() { loop {
self.get_recent_blockhash_with_commitment(CommitmentConfig::recent())? let mut status_retries = 15;
.value let signature = self.send_transaction(transaction)?;
.0 let status = loop {
} else { let status = self.get_signature_status(&signature)?;
transaction.message.recent_blockhash if status.is_none() {
}; status_retries -= 1;
let status = loop { if status_retries == 0 {
let status = self.get_signature_status(&signature)?; break status;
if status.is_none() { }
if self } else {
.get_fee_calculator_for_blockhash_with_commitment(
&recent_blockhash,
CommitmentConfig::recent(),
)?
.value
.is_none()
{
break status; 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 { } 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())
} }
} }
@@ -591,7 +466,7 @@ impl RpcClient {
commitment_config: CommitmentConfig, commitment_config: CommitmentConfig,
) -> RpcResult<Option<Account>> { ) -> RpcResult<Option<Account>> {
let config = RpcAccountInfoConfig { let config = RpcAccountInfoConfig {
encoding: Some(UiAccountEncoding::Base64), encoding: Some(UiAccountEncoding::Binary64),
commitment: Some(commitment_config), commitment: Some(commitment_config),
data_slice: None, data_slice: None,
}; };
@@ -609,8 +484,17 @@ impl RpcClient {
} }
let Response { let Response {
context, context,
value: rpc_account, value: mut rpc_account,
} = serde_json::from_value::<Response<Option<UiAccount>>>(result_json)?; } = serde_json::from_value::<Response<Option<UiAccount>>>(result_json)?;
if let Some(ref mut account) = rpc_account {
if let Binary(_) = &account.data {
let tmp = Binary64(String::new());
match std::mem::replace(&mut account.data, tmp) {
Binary(new_data) => account.data = Binary64(new_data),
_ => panic!("should have gotten binary here."),
}
}
}
trace!("Response account {:?} {:?}", pubkey, rpc_account); trace!("Response account {:?} {:?}", pubkey, rpc_account);
let account = rpc_account.and_then(|rpc_account| rpc_account.decode()); let account = rpc_account.and_then(|rpc_account| rpc_account.decode());
Ok(Response { Ok(Response {
@@ -626,38 +510,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>> { pub fn get_account_data(&self, pubkey: &Pubkey) -> ClientResult<Vec<u8>> {
Ok(self.get_account(pubkey)?.data) Ok(self.get_account(pubkey)?.data)
} }
@@ -698,27 +550,8 @@ impl RpcClient {
} }
pub fn get_program_accounts(&self, pubkey: &Pubkey) -> ClientResult<Vec<(Pubkey, Account)>> { pub fn get_program_accounts(&self, pubkey: &Pubkey) -> ClientResult<Vec<(Pubkey, Account)>> {
self.get_program_accounts_with_config( let accounts: Vec<RpcKeyedAccount> =
pubkey, self.send(RpcRequest::GetProgramAccounts, json!([pubkey.to_string()]))?;
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]),
)?;
parse_keyed_accounts(accounts, RpcRequest::GetProgramAccounts) parse_keyed_accounts(accounts, RpcRequest::GetProgramAccounts)
} }
@@ -855,10 +688,6 @@ impl RpcClient {
.into()) .into())
} }
pub fn get_first_available_block(&self) -> ClientResult<Slot> {
self.send(RpcRequest::GetFirstAvailableBlock, Value::Null)
}
pub fn get_genesis_hash(&self) -> ClientResult<Hash> { pub fn get_genesis_hash(&self) -> ClientResult<Hash> {
let hash_str: String = self.send(RpcRequest::GetGenesisHash, Value::Null)?; let hash_str: String = self.send(RpcRequest::GetGenesisHash, Value::Null)?;
let hash = hash_str.parse().map_err(|_| { let hash = hash_str.parse().map_err(|_| {
@@ -870,67 +699,6 @@ impl RpcClient {
Ok(hash) 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> { pub fn get_token_account_balance(&self, pubkey: &Pubkey) -> ClientResult<UiTokenAmount> {
Ok(self Ok(self
.get_token_account_balance_with_commitment(pubkey, CommitmentConfig::default())? .get_token_account_balance_with_commitment(pubkey, CommitmentConfig::default())?
@@ -1250,54 +1018,62 @@ impl RpcClient {
let progress_bar = new_spinner_progress_bar(); let progress_bar = new_spinner_progress_bar();
progress_bar.set_message(&format!( let mut send_retries = 20;
"[{}/{}] Finalizing transaction {}", let signature = loop {
confirmations, desired_confirmations, transaction.signatures[0], progress_bar.set_message(&format!(
)); "[{}/{}] Finalizing transaction {}",
let recent_blockhash = if uses_durable_nonce(transaction).is_some() { confirmations, desired_confirmations, transaction.signatures[0],
self.get_recent_blockhash_with_commitment(CommitmentConfig::recent())? ));
.value let mut status_retries = 15;
.0 let (signature, status) = loop {
} else { let signature = self.send_transaction_with_config(transaction, config)?;
transaction.message.recent_blockhash
}; // Get recent commitment in order to count confirmations for successful transactions
let signature = self.send_transaction_with_config(transaction, config)?; let status = self
let (signature, status) = loop { .get_signature_status_with_commitment(&signature, CommitmentConfig::recent())?;
// Get recent commitment in order to count confirmations for successful transactions if status.is_none() {
let status = status_retries -= 1;
self.get_signature_status_with_commitment(&signature, CommitmentConfig::recent())?; if status_retries == 0 {
if status.is_none() { break (signature, status);
if self }
.get_fee_calculator_for_blockhash_with_commitment( } else {
&recent_blockhash,
CommitmentConfig::recent(),
)?
.value
.is_none()
{
break (signature, status); break (signature, status);
} }
} else {
break (signature, status);
}
if cfg!(not(test)) { if cfg!(not(test)) {
sleep(Duration::from_millis(500)); 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(); let now = Instant::now();
loop { loop {
match commitment.commitment { match commitment.commitment {
@@ -1358,13 +1134,6 @@ impl RpcClient {
} }
} }
#[derive(Debug, Default)]
pub struct GetConfirmedSignaturesForAddress2Config {
pub before: Option<Signature>,
pub until: Option<Signature>,
pub limit: Option<usize>,
}
fn new_spinner_progress_bar() -> ProgressBar { fn new_spinner_progress_bar() -> ProgressBar {
let progress_bar = ProgressBar::new(42); let progress_bar = ProgressBar::new(42);
progress_bar progress_bar
@@ -1479,7 +1248,7 @@ mod tests {
let rpc_client = RpcClient::new_mock("succeeds".to_string()); let rpc_client = RpcClient::new_mock("succeeds".to_string());
let key = Keypair::new(); let key = Keypair::new();
let to = solana_sdk::pubkey::new_rand(); let to = Pubkey::new_rand();
let blockhash = Hash::default(); let blockhash = Hash::default();
let tx = system_transaction::transfer(&key, &to, 50, blockhash); let tx = system_transaction::transfer(&key, &to, 50, blockhash);
@@ -1532,7 +1301,7 @@ mod tests {
let rpc_client = RpcClient::new_mock("succeeds".to_string()); let rpc_client = RpcClient::new_mock("succeeds".to_string());
let key = Keypair::new(); let key = Keypair::new();
let to = solana_sdk::pubkey::new_rand(); let to = Pubkey::new_rand();
let blockhash = Hash::default(); let blockhash = Hash::default();
let tx = system_transaction::transfer(&key, &to, 50, blockhash); let tx = system_transaction::transfer(&key, &to, 50, blockhash);
let result = rpc_client.send_and_confirm_transaction(&tx); let result = rpc_client.send_and_confirm_transaction(&tx);

View File

@@ -1,10 +1,6 @@
use crate::rpc_filter::RpcFilterType; use crate::rpc_filter::RpcFilterType;
use solana_account_decoder::{UiAccountEncoding, UiDataSliceConfig}; use solana_account_decoder::{UiAccountEncoding, UiDataSliceConfig};
use solana_sdk::{ use solana_sdk::{clock::Epoch, commitment_config::CommitmentConfig};
clock::Epoch,
commitment_config::{CommitmentConfig, CommitmentLevel},
};
use solana_transaction_status::UiTransactionEncoding;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@@ -15,20 +11,13 @@ pub struct RpcSignatureStatusConfig {
#[derive(Debug, Default, Clone, Copy, PartialEq, Serialize, Deserialize)] #[derive(Debug, Default, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct RpcSendTransactionConfig { pub struct RpcSendTransactionConfig {
#[serde(default)]
pub skip_preflight: bool, pub skip_preflight: bool,
pub preflight_commitment: Option<CommitmentLevel>,
pub encoding: Option<UiTransactionEncoding>,
} }
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct RpcSimulateTransactionConfig { pub struct RpcSimulateTransactionConfig {
#[serde(default)]
pub sig_verify: bool, pub sig_verify: bool,
#[serde(flatten)]
pub commitment: Option<CommitmentConfig>,
pub encoding: Option<UiTransactionEncoding>,
} }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
@@ -78,18 +67,9 @@ pub enum RpcTokenAccountsFilter {
ProgramId(String), 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)] #[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct RpcGetConfirmedSignaturesForAddress2Config { pub struct RpcGetConfirmedSignaturesForAddress2Config {
pub before: Option<String>, // Signature as base-58 string pub before: Option<String>, // Signature as base-58 string
pub until: Option<String>, // Signature as base-58 string
pub limit: Option<usize>, pub limit: Option<usize>,
} }

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