Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9cbc39ec9 | ||
|
|
606a392d50 | ||
|
|
c67596ceb4 | ||
|
|
9a42cc7555 | ||
|
|
2e5ef2a802 | ||
|
|
8c8e2c4b2b | ||
|
|
0578801f99 | ||
|
|
6141e1410a | ||
|
|
4fc86807ff | ||
|
|
d2a2eba69e |
14
.buildkite/env/secrets.ejson
vendored
14
.buildkite/env/secrets.ejson
vendored
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"_public_key": "ae29f4f7ad2fc92de70d470e411c8426d5d48db8817c9e3dae574b122192335f",
|
"_public_key": "ae29f4f7ad2fc92de70d470e411c8426d5d48db8817c9e3dae574b122192335f",
|
||||||
"environment": {
|
"environment": {
|
||||||
"CODECOV_TOKEN": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:3K68mE38LJ2RB98VWmjuNLFBNn1XTGR4:cR4r05/TOZQKmEZp1v4CSgUJtC6QJiOaL85QjXW0qZ061fMnsBA8AtAPMDoDq4WCGOZM1A==]",
|
"CODECOV_TOKEN": "EJ[1:yGpTmjdbyjW2kjgIHkFoJv7Ue7EbUvUbqHyw6anGgWg=:JnxhrIxh09AvqdJgrVSYmb7PxSrh19aE:07WzVExCHEd1lJ1m8QizRRthGri+WBNeZRKjjEvsy5eo4gv3HD7zVEm42tVTGkqITKkBNQ==]",
|
||||||
"CRATES_IO_TOKEN": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:GGRTYDjMXksevzR6kq4Jx+FaIQZz50RU:xkbwDxcgoCyU+aT2tiI9mymigrEl6YiOr3axe3aX70ELIBKbCdPGilXP/wixvKi94g2u]",
|
"CRATES_IO_TOKEN": "EJ[1:yGpTmjdbyjW2kjgIHkFoJv7Ue7EbUvUbqHyw6anGgWg=:d0jJqC32/axwzq/N7kMRmpxKhnRrhtpt:zvcPHwkOzGnjhNkAQSejwdy1Jkr9wR1qXFFCnfIjyt/XQYubzB1tLkoly/qdmeb5]",
|
||||||
"GEOLOCATION_API_KEY": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:U2PZLi5MU3Ru/zK1SilianEeizcMvxml:AJKf2OAtDHmJh0KyXrBnNnistItZvVVP3cZ7ZLtrVupjmWN/PzmKwSsXeCNObWS+]",
|
"GEOLOCATION_API_KEY": "EJ[1:yGpTmjdbyjW2kjgIHkFoJv7Ue7EbUvUbqHyw6anGgWg=:R4gfB6Ey4i50HyfLt4UZDLBqg3qHEUye:UfZCOgt8XI6Y2g+ivCRVoS1fjFycFs7/GSevvCqh1B50mG0+hzpEyzXQLuKG5OeI]",
|
||||||
"GITHUB_TOKEN": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:0NJNlpD/O19mvOakCGBYDhIDfySxWFSC:Dz4NXv9x6ncRQ1u9sVoWOcqmkg0sI09qmefghB0GXZgPcFGgn6T0mw7ynNnbUvjyH8dLruKHauk=]",
|
"GITHUB_TOKEN": "EJ[1:yGpTmjdbyjW2kjgIHkFoJv7Ue7EbUvUbqHyw6anGgWg=:Vq2dkGTOzfEpRht0BAGHFp/hDogMvXJe:tFXHg1epVt2mq9hkuc5sRHe+KAnVREi/p8S+IZu67XRyzdiA/nGak1k860FXYuuzuaE0QWekaEc=]",
|
||||||
"INFLUX_DATABASE": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:SzwHIeOVpmbTcGQOGngoFgYumsLZJUGq:t7Rpk49njsWvoM+ztv5Uwuiz]",
|
"INFLUX_DATABASE": "EJ[1:yGpTmjdbyjW2kjgIHkFoJv7Ue7EbUvUbqHyw6anGgWg=:5KI9WBkXx3R/W4m256mU5MJOE7N8aAT9:Cb8QFELZ9I60t5zhJ9h55Kcs]",
|
||||||
"INFLUX_PASSWORD": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:/MUs+q7pdGrUjzwcq+6pgIFxur4hxdqu:am22z2E2dtmw1f1J1Mq5JLcUHZsrEjQAJ0pp21M4AZeJbNO6bVb44d9zSkHj7xdN6U+GNlCk+wU=]",
|
"INFLUX_PASSWORD": "EJ[1:yGpTmjdbyjW2kjgIHkFoJv7Ue7EbUvUbqHyw6anGgWg=:hQRMpLCrav+OYkNphkeM4hagdVoZv5Iw:AUO76rr6+gF1OLJA8ZLSG8wHKXgYCPNk6gRCV8rBhZBJ4KwDaxpvOhMl7bxxXG6jol7v4aRa/Lk=]",
|
||||||
"INFLUX_USERNAME": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:XjghH20xGVWro9B+epGlJaJcW8Wze0Bi:ZIdOtXudTY5TqKseDU7gVvQXfmXV99Xh]"
|
"INFLUX_USERNAME": "EJ[1:yGpTmjdbyjW2kjgIHkFoJv7Ue7EbUvUbqHyw6anGgWg=:R7BNmQjfeqoGDAFTJu9bYTGHol2NgnYN:Q2tOT/EBcFvhFk+DKLKmVU7tLCpVC3Ui]"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
else
|
|
||||||
(
|
(
|
||||||
set -x
|
set -x
|
||||||
mkdir -p "$CARGO_TARGET_CACHE"
|
d=$HOME/cargo-target-cache/"$BUILDKITE_LABEL"
|
||||||
|
mkdir -p "$d"
|
||||||
set -x
|
set -x
|
||||||
rsync -a --delete --link-dest="$PWD" target "$CARGO_TARGET_CACHE"
|
rsync -a --delete --link-dest="$PWD" target "$d"
|
||||||
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"
|
||||||
)
|
)
|
||||||
fi
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Add job_stats data point
|
# Add job_stats data point
|
||||||
|
|||||||
@@ -11,24 +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 .
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -9,10 +9,23 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
cd "$(dirname "$0")"/..
|
cd "$(dirname "$0")"/..
|
||||||
source ci/_
|
|
||||||
|
|
||||||
_ ci/buildkite-pipeline.sh pipeline.yml
|
if [[ -n $BUILDKITE_TAG ]]; then
|
||||||
echo +++ pipeline
|
buildkite-agent annotate --style info --context release-tag \
|
||||||
cat pipeline.yml
|
"https://github.com/solana-labs/solana/releases/$BUILDKITE_TAG"
|
||||||
|
buildkite-agent pipeline upload ci/buildkite-release.yml
|
||||||
|
else
|
||||||
|
if [[ $BUILDKITE_BRANCH =~ ^pull ]]; then
|
||||||
|
# Add helpful link back to the corresponding Github Pull Request
|
||||||
|
buildkite-agent annotate --style info --context pr-backlink \
|
||||||
|
"Github Pull Request: https://github.com/solana-labs/solana/$BUILDKITE_BRANCH"
|
||||||
|
fi
|
||||||
|
|
||||||
_ buildkite-agent pipeline upload pipeline.yml
|
if [[ $BUILDKITE_MESSAGE =~ GitBook: ]]; then
|
||||||
|
buildkite-agent annotate --style info --context gitbook-ci-skip \
|
||||||
|
"GitBook commit detected, CI skipped"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
buildkite-agent pipeline upload ci/buildkite.yml
|
||||||
|
fi
|
||||||
|
|||||||
18
.gitbook.yaml
Normal file
18
.gitbook.yaml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
root: ./docs/src
|
||||||
|
|
||||||
|
structure:
|
||||||
|
readme: introduction.md
|
||||||
|
summary: SUMMARY.md
|
||||||
|
|
||||||
|
redirects:
|
||||||
|
wallet: ./wallet-guide/README.md
|
||||||
|
wallet/app-wallets: ./wallet-guide/apps.md
|
||||||
|
wallet/app-wallets/trust-wallet: ./wallet-guide/trust-wallet.md
|
||||||
|
wallet/app-wallets/ledger-live: ./wallet-guide/ledger-live.md
|
||||||
|
wallet/cli-wallets: ./wallet-guide/cli.md
|
||||||
|
wallet/cli-wallets/paper-wallet: ./paper-wallet/README.md
|
||||||
|
wallet/cli-wallets/paper-wallet/paper-wallet-usage: ./paper-wallet/paper-wallet-usage.md
|
||||||
|
wallet/cli-wallets/remote-wallet: ./hardware-wallets/README.md
|
||||||
|
wallet/cli-wallets/remote-wallet/ledger: ./hardware-wallets/ledger.md
|
||||||
|
wallet/cli-wallets/file-system-wallet: ./file-system-wallet/README.md
|
||||||
|
wallet/support: ./wallet-guide/support.md
|
||||||
41
.github/dependabot.yml
vendored
41
.github/dependabot.yml
vendored
@@ -1,41 +0,0 @@
|
|||||||
# To get started with Dependabot version updates, you'll need to specify which
|
|
||||||
# package ecosystems to update and where the package manifests are located.
|
|
||||||
# Please see the documentation for all configuration options:
|
|
||||||
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
|
||||||
|
|
||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: cargo
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: daily
|
|
||||||
time: "01:00"
|
|
||||||
timezone: America/Los_Angeles
|
|
||||||
#labels:
|
|
||||||
# - "automerge"
|
|
||||||
open-pull-requests-limit: 3
|
|
||||||
|
|
||||||
- package-ecosystem: npm
|
|
||||||
directory: "/web3.js"
|
|
||||||
schedule:
|
|
||||||
interval: daily
|
|
||||||
time: "01:00"
|
|
||||||
timezone: America/Los_Angeles
|
|
||||||
labels:
|
|
||||||
- "automerge"
|
|
||||||
commit-message:
|
|
||||||
prefix: "chore:"
|
|
||||||
open-pull-requests-limit: 3
|
|
||||||
|
|
||||||
- package-ecosystem: npm
|
|
||||||
directory: "/explorer"
|
|
||||||
schedule:
|
|
||||||
interval: daily
|
|
||||||
time: "01:00"
|
|
||||||
timezone: America/Los_Angeles
|
|
||||||
labels:
|
|
||||||
- "automerge"
|
|
||||||
commit-message:
|
|
||||||
prefix: "chore:"
|
|
||||||
include: "scope"
|
|
||||||
open-pull-requests-limit: 3
|
|
||||||
1
.github/stale.yml
vendored
1
.github/stale.yml
vendored
@@ -9,7 +9,6 @@ daysUntilClose: 7
|
|||||||
# Issues with these labels will never be considered stale
|
# Issues with these labels will never be considered stale
|
||||||
exemptLabels:
|
exemptLabels:
|
||||||
- security
|
- security
|
||||||
- blocked
|
|
||||||
|
|
||||||
# Label to use when marking a pull request as stale
|
# Label to use when marking a pull request as stale
|
||||||
staleLabel: stale
|
staleLabel: stale
|
||||||
|
|||||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -23,9 +23,3 @@ log-*/
|
|||||||
/.idea/
|
/.idea/
|
||||||
/solana.iml
|
/solana.iml
|
||||||
/.vscode/
|
/.vscode/
|
||||||
|
|
||||||
# fetch-spl.sh artifacts
|
|
||||||
/spl-genesis-args.sh
|
|
||||||
/spl_*.so
|
|
||||||
|
|
||||||
.DS_Store
|
|
||||||
|
|||||||
49
.mergify.yml
49
.mergify.yml
@@ -1,40 +1,9 @@
|
|||||||
# Validate your changes with:
|
# Validate your changes with:
|
||||||
#
|
#
|
||||||
# $ curl -F 'data=@.mergify.yml' https://gh.mergify.io/validate/
|
# $ curl -F 'data=@.mergify.yml' https://gh.mergify.io/validate
|
||||||
#
|
#
|
||||||
# https://doc.mergify.io/
|
# https://doc.mergify.io/
|
||||||
pull_request_rules:
|
pull_request_rules:
|
||||||
- name: automatic merge (squash) on CI success
|
|
||||||
conditions:
|
|
||||||
- status-success=buildkite/solana
|
|
||||||
- status-success=Travis CI - Pull Request
|
|
||||||
- status-success=ci-gate
|
|
||||||
- label=automerge
|
|
||||||
- author≠@dont-squash-my-commits
|
|
||||||
actions:
|
|
||||||
merge:
|
|
||||||
method: squash
|
|
||||||
# Join the dont-squash-my-commits group if you won't like your commits squashed
|
|
||||||
- name: automatic merge (rebase) on CI success
|
|
||||||
conditions:
|
|
||||||
- status-success=buildkite/solana
|
|
||||||
- status-success=Travis CI - Pull Request
|
|
||||||
- status-success=ci-gate
|
|
||||||
- label=automerge
|
|
||||||
- author=@dont-squash-my-commits
|
|
||||||
actions:
|
|
||||||
merge:
|
|
||||||
method: rebase
|
|
||||||
- name: remove automerge label on CI failure
|
|
||||||
conditions:
|
|
||||||
- label=automerge
|
|
||||||
- "#status-failure!=0"
|
|
||||||
actions:
|
|
||||||
label:
|
|
||||||
remove:
|
|
||||||
- automerge
|
|
||||||
comment:
|
|
||||||
message: automerge label removed due to a CI failure
|
|
||||||
- name: remove outdated reviews
|
- name: remove outdated reviews
|
||||||
conditions:
|
conditions:
|
||||||
- base=master
|
- base=master
|
||||||
@@ -50,6 +19,14 @@ pull_request_rules:
|
|||||||
label:
|
label:
|
||||||
add:
|
add:
|
||||||
- automerge
|
- automerge
|
||||||
|
- name: v1.0 backport
|
||||||
|
conditions:
|
||||||
|
- label=v1.0
|
||||||
|
actions:
|
||||||
|
backport:
|
||||||
|
ignore_conflicts: true
|
||||||
|
branches:
|
||||||
|
- v1.0
|
||||||
- name: v1.1 backport
|
- name: v1.1 backport
|
||||||
conditions:
|
conditions:
|
||||||
- label=v1.1
|
- label=v1.1
|
||||||
@@ -66,11 +43,3 @@ pull_request_rules:
|
|||||||
ignore_conflicts: true
|
ignore_conflicts: true
|
||||||
branches:
|
branches:
|
||||||
- v1.2
|
- v1.2
|
||||||
- name: v1.3 backport
|
|
||||||
conditions:
|
|
||||||
- label=v1.3
|
|
||||||
actions:
|
|
||||||
backport:
|
|
||||||
ignore_conflicts: true
|
|
||||||
branches:
|
|
||||||
- v1.3
|
|
||||||
|
|||||||
121
.travis.yml
121
.travis.yml
@@ -1,42 +1,28 @@
|
|||||||
|
os:
|
||||||
|
- osx
|
||||||
|
- windows
|
||||||
|
|
||||||
|
language: rust
|
||||||
|
rust:
|
||||||
|
- stable
|
||||||
|
|
||||||
|
install:
|
||||||
|
- source ci/rust-version.sh
|
||||||
|
|
||||||
|
script:
|
||||||
|
- source ci/env.sh
|
||||||
|
- ci/publish-tarball.sh
|
||||||
|
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
- /^v\d+\.\d+/
|
- /^v\d+\.\d+/
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
email: false
|
|
||||||
slack:
|
slack:
|
||||||
on_success: change
|
on_success: change
|
||||||
if: NOT type = pull_request
|
|
||||||
secure: F4IjOE05MyaMOdPRL+r8qhs7jBvv4yDM3RmFKE1zNXnfUOqV4X38oQM1EI+YVsgpMQLj/pxnEB7wcTE4Bf86N6moLssEULCpvAuMVoXj4QbWdomLX+01WbFa6fLVeNQIg45NHrz2XzVBhoKOrMNnl+QI5mbR2AlS5oqsudHsXDnyLzZtd4Y5SDMdYG1zVWM01+oNNjgNfjcCGmOE/K0CnOMl6GPi3X9C34tJ19P2XT7MTDsz1/IfEF7fro2Q8DHEYL9dchJMoisXSkem5z7IDQkGzXsWdWT4NnndUvmd1MlTCE9qgoXDqRf95Qh8sB1Dz08HtvgfaosP2XjtNTfDI9BBYS15Ibw9y7PchAJE1luteNjF35EOy6OgmCLw/YpnweqfuNViBZz+yOPWXVC0kxnPIXKZ1wyH9ibeH6E4hr7a8o9SV/6SiWIlbYF+IR9jPXyTCLP/cc3sYljPWxDnhWFwFdRVIi3PbVAhVu7uWtVUO17Oc9gtGPgs/GrhOMkJfwQPXaudRJDpVZowxTX4x9kefNotlMAMRgq+Drbmgt4eEBiCNp0ITWgh17BiE1U09WS3myuduhoct85+FoVeaUkp1sxzHVtGsNQH0hcz7WcpZyOM+AwistJA/qzeEDQao5zi1eKWPbO2xAhi2rV1bDH6bPf/4lDBwLRqSiwvlWU=
|
secure: F4IjOE05MyaMOdPRL+r8qhs7jBvv4yDM3RmFKE1zNXnfUOqV4X38oQM1EI+YVsgpMQLj/pxnEB7wcTE4Bf86N6moLssEULCpvAuMVoXj4QbWdomLX+01WbFa6fLVeNQIg45NHrz2XzVBhoKOrMNnl+QI5mbR2AlS5oqsudHsXDnyLzZtd4Y5SDMdYG1zVWM01+oNNjgNfjcCGmOE/K0CnOMl6GPi3X9C34tJ19P2XT7MTDsz1/IfEF7fro2Q8DHEYL9dchJMoisXSkem5z7IDQkGzXsWdWT4NnndUvmd1MlTCE9qgoXDqRf95Qh8sB1Dz08HtvgfaosP2XjtNTfDI9BBYS15Ibw9y7PchAJE1luteNjF35EOy6OgmCLw/YpnweqfuNViBZz+yOPWXVC0kxnPIXKZ1wyH9ibeH6E4hr7a8o9SV/6SiWIlbYF+IR9jPXyTCLP/cc3sYljPWxDnhWFwFdRVIi3PbVAhVu7uWtVUO17Oc9gtGPgs/GrhOMkJfwQPXaudRJDpVZowxTX4x9kefNotlMAMRgq+Drbmgt4eEBiCNp0ITWgh17BiE1U09WS3myuduhoct85+FoVeaUkp1sxzHVtGsNQH0hcz7WcpZyOM+AwistJA/qzeEDQao5zi1eKWPbO2xAhi2rV1bDH6bPf/4lDBwLRqSiwvlWU=
|
||||||
|
|
||||||
os: linux
|
|
||||||
dist: bionic
|
|
||||||
language: minimal
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
include:
|
|
||||||
- name: "Export Github Repositories"
|
|
||||||
if: type IN (push, cron) AND branch = master
|
|
||||||
language: python
|
|
||||||
git:
|
|
||||||
depth: false
|
|
||||||
script:
|
|
||||||
- .travis/export-github-repo.sh web3.js/ solana-web3.js
|
|
||||||
- .travis/export-github-repo.sh explorer/ explorer
|
|
||||||
|
|
||||||
- &release-artifacts
|
|
||||||
if: type IN (api, cron) OR tag IS present
|
|
||||||
name: "macOS release artifacts"
|
|
||||||
os: osx
|
|
||||||
language: rust
|
|
||||||
rust:
|
|
||||||
- stable
|
|
||||||
install:
|
|
||||||
- source ci/rust-version.sh
|
|
||||||
script:
|
|
||||||
- source ci/env.sh
|
|
||||||
- ci/publish-tarball.sh
|
|
||||||
deploy:
|
deploy:
|
||||||
- provider: s3
|
- provider: s3
|
||||||
access_key_id: $AWS_ACCESS_KEY_ID
|
access_key_id: $AWS_ACCESS_KEY_ID
|
||||||
@@ -49,84 +35,9 @@ jobs:
|
|||||||
on:
|
on:
|
||||||
all_branches: true
|
all_branches: true
|
||||||
- provider: releases
|
- provider: releases
|
||||||
token: $GITHUB_TOKEN
|
api_key: $GITHUB_TOKEN
|
||||||
skip_cleanup: true
|
skip_cleanup: true
|
||||||
file_glob: true
|
file_glob: true
|
||||||
file: travis-release-upload/*
|
file: travis-release-upload/*
|
||||||
on:
|
on:
|
||||||
tags: true
|
tags: true
|
||||||
- <<: *release-artifacts
|
|
||||||
name: "Windows release artifacts"
|
|
||||||
os: windows
|
|
||||||
# Linux release artifacts are still built by ci/buildkite-secondary.yml
|
|
||||||
#- <<: *release-artifacts
|
|
||||||
# name: "Linux release artifacts"
|
|
||||||
# os: linux
|
|
||||||
# before_install:
|
|
||||||
# - sudo apt-get install libssl-dev libudev-dev
|
|
||||||
|
|
||||||
# explorer pull request
|
|
||||||
- name: "explorer"
|
|
||||||
if: type = pull_request AND branch = master
|
|
||||||
|
|
||||||
language: node_js
|
|
||||||
node_js:
|
|
||||||
- "node"
|
|
||||||
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- ~/.npm
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- .travis/affects.sh explorer/ .travis || travis_terminate 0
|
|
||||||
- cd explorer
|
|
||||||
|
|
||||||
script:
|
|
||||||
- npm run build
|
|
||||||
- npm run format
|
|
||||||
|
|
||||||
# web3.js pull request
|
|
||||||
- name: "web3.js"
|
|
||||||
if: type = pull_request AND branch = master
|
|
||||||
|
|
||||||
language: node_js
|
|
||||||
node_js:
|
|
||||||
- "lts/*"
|
|
||||||
|
|
||||||
services:
|
|
||||||
- docker
|
|
||||||
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- ~/.npm
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- .travis/affects.sh web3.js/ .travis || travis_terminate 0
|
|
||||||
- cd web3.js/
|
|
||||||
- source .travis/before_install.sh
|
|
||||||
|
|
||||||
script:
|
|
||||||
- ../.travis/commitlint.sh
|
|
||||||
- source .travis/script.sh
|
|
||||||
|
|
||||||
# docs pull request
|
|
||||||
- name: "docs"
|
|
||||||
if: type IN (push, pull_request) OR tag IS present
|
|
||||||
language: node_js
|
|
||||||
node_js:
|
|
||||||
- "node"
|
|
||||||
|
|
||||||
services:
|
|
||||||
- docker
|
|
||||||
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- ~/.npm
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- .travis/affects.sh docs/ .travis || travis_terminate 0
|
|
||||||
- cd docs/
|
|
||||||
- source .travis/before_install.sh
|
|
||||||
|
|
||||||
script:
|
|
||||||
- source .travis/script.sh
|
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
#
|
|
||||||
# Check if files in the commit range match one or more prefixes
|
|
||||||
#
|
|
||||||
|
|
||||||
# Always run the job if we are on a tagged release
|
|
||||||
if [[ -n "$TRAVIS_TAG" ]]; then
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
(
|
|
||||||
set -x
|
|
||||||
git diff --name-only "$TRAVIS_COMMIT_RANGE"
|
|
||||||
)
|
|
||||||
|
|
||||||
for file in $(git diff --name-only "$TRAVIS_COMMIT_RANGE"); do
|
|
||||||
for prefix in "$@"; do
|
|
||||||
if [[ $file =~ ^"$prefix" ]]; then
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "No modifications to $*"
|
|
||||||
exit 1
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
#
|
|
||||||
# Runs commitlint in the provided subdirectory
|
|
||||||
#
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
basedir=$1
|
|
||||||
if [[ -z "$basedir" ]]; then
|
|
||||||
basedir=.
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ! -d "$basedir" ]]; then
|
|
||||||
echo "Error: not a directory: $basedir"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ! -f "$basedir"/commitlint.config.js ]]; then
|
|
||||||
echo "Error: No commitlint configuration found"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -z $TRAVIS_COMMIT_RANGE ]]; then
|
|
||||||
echo "Error: TRAVIS_COMMIT_RANGE not defined"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd "$basedir"
|
|
||||||
echo "Checking commits in TRAVIS_COMMIT_RANGE: $TRAVIS_COMMIT_RANGE"
|
|
||||||
while IFS= read -r line; do
|
|
||||||
echo "$line" | npx commitlint
|
|
||||||
done < <(git log "$TRAVIS_COMMIT_RANGE" --format=%s -- .)
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
#
|
|
||||||
# Exports a subdirectory into another github repository
|
|
||||||
#
|
|
||||||
|
|
||||||
set -e
|
|
||||||
if [[ -z $GITHUB_TOKEN ]]; then
|
|
||||||
echo GITHUB_TOKEN not defined
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd "$(dirname "$0")/.."
|
|
||||||
|
|
||||||
pip3 install git-filter-repo
|
|
||||||
|
|
||||||
declare subdir=$1
|
|
||||||
declare repo_name=$2
|
|
||||||
|
|
||||||
[[ -n "$subdir" ]] || {
|
|
||||||
echo "Error: subdir not specified"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
[[ -n "$repo_name" ]] || {
|
|
||||||
echo "Error: repo_name not specified"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "Exporting $subdir"
|
|
||||||
|
|
||||||
set -x
|
|
||||||
rm -rf .github_export/"$repo_name"
|
|
||||||
git clone https://"$GITHUB_TOKEN"@github.com/solana-labs/"$repo_name" .github_export/"$repo_name"
|
|
||||||
git filter-repo --subdirectory-filter "$subdir" --target .github_export/"$repo_name"
|
|
||||||
git -C .github_export/"$repo_name" push https://"$GITHUB_TOKEN"@github.com/solana-labs/"$repo_name"
|
|
||||||
3231
Cargo.lock
generated
3231
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
11
Cargo.toml
11
Cargo.toml
@@ -5,10 +5,6 @@ members = [
|
|||||||
"bench-tps",
|
"bench-tps",
|
||||||
"accounts-bench",
|
"accounts-bench",
|
||||||
"banking-bench",
|
"banking-bench",
|
||||||
"banks-client",
|
|
||||||
"banks-interface",
|
|
||||||
"banks-server",
|
|
||||||
"clap-utils",
|
|
||||||
"cli-config",
|
"cli-config",
|
||||||
"client",
|
"client",
|
||||||
"core",
|
"core",
|
||||||
@@ -29,15 +25,15 @@ members = [
|
|||||||
"log-analyzer",
|
"log-analyzer",
|
||||||
"merkle-tree",
|
"merkle-tree",
|
||||||
"stake-o-matic",
|
"stake-o-matic",
|
||||||
"storage-bigtable",
|
|
||||||
"streamer",
|
"streamer",
|
||||||
"measure",
|
"measure",
|
||||||
"metrics",
|
"metrics",
|
||||||
"net-shaper",
|
"net-shaper",
|
||||||
"notifier",
|
"notifier",
|
||||||
"poh-bench",
|
|
||||||
"programs/bpf_loader",
|
"programs/bpf_loader",
|
||||||
"programs/budget",
|
"programs/budget",
|
||||||
|
"programs/btc_spv",
|
||||||
|
"programs/btc_spv_bin",
|
||||||
"programs/config",
|
"programs/config",
|
||||||
"programs/exchange",
|
"programs/exchange",
|
||||||
"programs/failure",
|
"programs/failure",
|
||||||
@@ -56,7 +52,6 @@ members = [
|
|||||||
"sys-tuner",
|
"sys-tuner",
|
||||||
"tokens",
|
"tokens",
|
||||||
"transaction-status",
|
"transaction-status",
|
||||||
"account-decoder",
|
|
||||||
"upload-perf",
|
"upload-perf",
|
||||||
"net-utils",
|
"net-utils",
|
||||||
"version",
|
"version",
|
||||||
@@ -68,4 +63,6 @@ members = [
|
|||||||
|
|
||||||
exclude = [
|
exclude = [
|
||||||
"programs/bpf",
|
"programs/bpf",
|
||||||
|
"programs/move_loader",
|
||||||
|
"programs/librapay",
|
||||||
]
|
]
|
||||||
|
|||||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
|||||||
Copyright 2020 Solana Foundation.
|
Copyright 2018 Solana Labs, Inc.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|||||||
104
RELEASE.md
104
RELEASE.md
@@ -76,20 +76,21 @@ There are three release channels that map to branches as follows:
|
|||||||
git push -u origin <branchname>
|
git push -u origin <branchname>
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively use the Github UI.
|
### Update master branch with the next version
|
||||||
|
|
||||||
### Update master branch to the next release minor version
|
|
||||||
|
|
||||||
1. After the new branch has been created and pushed, update the Cargo.toml files on **master** to the next semantic version (e.g. 0.9.0 -> 0.10.0) with:
|
1. After the new branch has been created and pushed, update the Cargo.toml files on **master** to the next semantic version (e.g. 0.9.0 -> 0.10.0) with:
|
||||||
```
|
```
|
||||||
$ scripts/increment-cargo-version.sh minor
|
scripts/increment-cargo-version.sh minor
|
||||||
$ ./scripts/cargo-for-all-lock-files.sh update
|
```
|
||||||
|
1. Rebuild to get an updated version of `Cargo.lock`:
|
||||||
|
```
|
||||||
|
cargo build
|
||||||
```
|
```
|
||||||
1. Push all the changed Cargo.toml and Cargo.lock files to the `master` branch with something like:
|
1. Push all the changed Cargo.toml and Cargo.lock files to the `master` branch with something like:
|
||||||
```
|
```
|
||||||
git co -b version_update
|
git co -b version_update
|
||||||
git ls-files -m | xargs git add
|
git ls-files -m | xargs git add
|
||||||
git commit -m 'Bump version to X.Y+1.0'
|
git commit -m 'Update Cargo.toml versions from X.Y to X.Y+1'
|
||||||
git push -u origin version_update
|
git push -u origin version_update
|
||||||
```
|
```
|
||||||
1. Confirm that your freshly cut release branch is shown as `BETA_CHANNEL` and the previous release branch as `STABLE_CHANNEL`:
|
1. Confirm that your freshly cut release branch is shown as `BETA_CHANNEL` and the previous release branch as `STABLE_CHANNEL`:
|
||||||
@@ -101,51 +102,84 @@ Alternatively use the Github UI.
|
|||||||
|
|
||||||
### Create the Release Tag on GitHub
|
### Create the Release Tag on GitHub
|
||||||
|
|
||||||
1. Go to [GitHub Releases](https://github.com/solana-labs/solana/releases) for tagging a release.
|
1. Go to [GitHub's Releases UI](https://github.com/solana-labs/solana/releases) for tagging a release.
|
||||||
1. Click "Draft new release". The release tag must exactly match the `version`
|
1. Click "Draft new release". The release tag must exactly match the `version`
|
||||||
field in `/Cargo.toml` prefixed by `v`.
|
field in `/Cargo.toml` prefixed by `v`.
|
||||||
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 verion 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. 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. 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. Go back into edit the release and click "Publish release" when ready.
|
||||||
1. Ensure the release is marked **"This is a pre-release"**. This flag will then need to be be removed once the the Linux binary artifacts appear later.
|
|
||||||
1. Go back into edit the release and click "Publish release" when ready.
|
|
||||||
|
|
||||||
|
|
||||||
### Update release branch with the next patch version
|
### Update release branch with the next patch version
|
||||||
|
|
||||||
1. After the new release has been tagged, update the Cargo.toml files on **release branch** to the next semantic version (e.g. 0.9.0 -> 0.9.1) with:
|
1. After the new release has been tagged, update the Cargo.toml files on **release branch** to the next semantic version (e.g. 0.9.0 -> 0.9.1) with:
|
||||||
```
|
```
|
||||||
$ scripts/increment-cargo-version.sh patch
|
scripts/increment-cargo-version.sh patch
|
||||||
$ ./scripts/cargo-for-all-lock-files.sh tree
|
```
|
||||||
|
1. Rebuild to get an updated version of `Cargo.lock`:
|
||||||
|
```
|
||||||
|
cargo build
|
||||||
```
|
```
|
||||||
1. Push all the changed Cargo.toml and Cargo.lock files to the **release branch** with something like:
|
1. Push all the changed Cargo.toml and Cargo.lock files to the **release branch** with something like:
|
||||||
```
|
```
|
||||||
git co -b version_update origin/vX.Y
|
git co -b version_update
|
||||||
git add -u
|
git ls-files -m | xargs git add
|
||||||
git commit -m 'Bump version to X.Y.Z+1'
|
git commit -m 'Update Cargo.toml versions from X.Y.Z to X.Y.Z+1'
|
||||||
git push -u <user-remote> version_update
|
git push -u origin version_update
|
||||||
```
|
```
|
||||||
1. Open a PR against origin/vX.Y and then merge the PR after passing CI.
|
|
||||||
|
|
||||||
### Prepare for the next release
|
|
||||||
1. Go to [GitHub Releases](https://github.com/solana-labs/solana/releases) and create a new draft release for `X.Y.Z+1` with empty release nodes. This allows people to incrementally add new release notes until it's time for the next release
|
|
||||||
1. Go to the [Github Milestones](https://github.com/solana-labs/solana/milestones). Create a new milestone for the `X.Y.Z+1`, move over
|
|
||||||
unresolved issues still in the `X.Y.Z` milestone, then close the `X.Y.Z` milestone.
|
|
||||||
|
|
||||||
### Verify release automation success
|
### Verify release automation success
|
||||||
Go to [Solana Releases](https://github.com/solana-labs/solana/releases) and click on the latest release that you just published.
|
1. Go to [Solana Releases](https://github.com/solana-labs/solana/releases) and click on the latest release that you just published. Verify that all of the build artifacts are present. This can take up to 90 minutes after creating the tag.
|
||||||
Verify that all of the build artifacts are present, then the uncheck **"This is a pre-release"** for the release.
|
1. The `solana-secondary` Buildkite pipeline handles creating the binary tarballs and updated crates. Look for a job under the tag name of the release: https://buildkite.com/solana-labs/solana-secondary
|
||||||
|
1. [Crates.io](https://crates.io/crates/solana) should have an updated Solana version.
|
||||||
|
|
||||||
Build artifacts can take up to 60 minutes after creating the tag before
|
### Update documentation
|
||||||
appearing. To check for progress:
|
TODO: Documentation update procedure is WIP as we move to gitbook
|
||||||
* The `solana-secondary` Buildkite pipeline handles creating the Linux release artifacts and updated crates. Look for a job under the tag name of the release: https://buildkite.com/solana-labs/solana-secondary.
|
|
||||||
* The macOS and Windows release artifacts are produced by Travis CI: https://travis-ci.com/github/solana-labs/solana/branches
|
|
||||||
|
|
||||||
[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.
|
Document the new recommended version by updating `docs/src/running-archiver.md` and `docs/src/validator-testnet.md` on the release (beta) branch to point at the `solana-install` for the upcoming release version.
|
||||||
If this happens and the error is non-fatal, click "Retry" on the "publish crate" job
|
|
||||||
|
|
||||||
### Update software on devnet.solana.com/testnet.solama.com/mainnet-beta.solana.com
|
### Update software on devnet.solana.com
|
||||||
See the documentation at https://github.com/solana-labs/cluster-ops/
|
|
||||||
|
The testnet running on devnet.solana.com is set to use a fixed release tag
|
||||||
|
which is set in the Buildkite testnet-management pipeline.
|
||||||
|
This tag needs to be updated and the testnet restarted after a new release
|
||||||
|
tag is created.
|
||||||
|
|
||||||
|
#### Update testnet schedules
|
||||||
|
|
||||||
|
Go to https://buildkite.com/solana-labs and click through: Pipelines ->
|
||||||
|
testnet-management -> Pipeline Settings -> Schedules
|
||||||
|
Or just click here:
|
||||||
|
https://buildkite.com/solana-labs/testnet-management/settings/schedules
|
||||||
|
|
||||||
|
There are two scheduled jobs for testnet: a daily restart and an hourly sanity-or-restart. \
|
||||||
|
https://buildkite.com/solana-labs/testnet-management/settings/schedules/0efd7856-7143-4713-8817-47e6bdb05387
|
||||||
|
https://buildkite.com/solana-labs/testnet-management/settings/schedules/2a926646-d972-42b5-aeb9-bb6759592a53
|
||||||
|
|
||||||
|
On each schedule:
|
||||||
|
1. Set TESTNET_TAG environment variable to the desired release tag.
|
||||||
|
1. Example, TESTNET_TAG=v0.13.2
|
||||||
|
1. Set the Build Branch to the branch that TESTNET_TAG is from.
|
||||||
|
1. Example: v0.13
|
||||||
|
|
||||||
|
#### Restart the testnet
|
||||||
|
|
||||||
|
Trigger a TESTNET_OP=create-and-start to refresh the cluster with the new version
|
||||||
|
|
||||||
|
1. Go to https://buildkite.com/solana-labs/testnet-management
|
||||||
|
2. Click "New Build" and use the following settings, then click "Create Build"
|
||||||
|
1. Commit: HEAD
|
||||||
|
1. Branch: [channel branch as set in the schedules]
|
||||||
|
1. Environment Variables:
|
||||||
|
```
|
||||||
|
TESTNET=testnet
|
||||||
|
TESTNET_TAG=[same value as used in TESTNET_TAG in the schedules]
|
||||||
|
TESTNET_OP=create-and-start
|
||||||
|
```
|
||||||
|
|
||||||
|
### Alert the community
|
||||||
|
|
||||||
|
Notify Discord users on #validator-support that a new release for
|
||||||
|
devnet.solana.com is available
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "solana-account-decoder"
|
|
||||||
version = "1.3.6"
|
|
||||||
description = "Solana account decoder"
|
|
||||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
|
||||||
repository = "https://github.com/solana-labs/solana"
|
|
||||||
homepage = "https://solana.com/"
|
|
||||||
license = "Apache-2.0"
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
base64 = "0.12.3"
|
|
||||||
bincode = "1.3.1"
|
|
||||||
bs58 = "0.3.1"
|
|
||||||
bv = "0.11.1"
|
|
||||||
Inflector = "0.11.4"
|
|
||||||
lazy_static = "1.4.0"
|
|
||||||
serde = "1.0.112"
|
|
||||||
serde_derive = "1.0.103"
|
|
||||||
serde_json = "1.0.56"
|
|
||||||
solana-config-program = { path = "../programs/config", version = "1.3.6" }
|
|
||||||
solana-sdk = { path = "../sdk", version = "1.3.6" }
|
|
||||||
solana-stake-program = { path = "../programs/stake", version = "1.3.6" }
|
|
||||||
solana-vote-program = { path = "../programs/vote", version = "1.3.6" }
|
|
||||||
spl-token-v2-0 = { package = "spl-token", version = "2.0.3", features = ["skip-no-mangle"] }
|
|
||||||
thiserror = "1.0"
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
|
||||||
targets = ["x86_64-unknown-linux-gnu"]
|
|
||||||
@@ -1,182 +0,0 @@
|
|||||||
#[macro_use]
|
|
||||||
extern crate lazy_static;
|
|
||||||
#[macro_use]
|
|
||||||
extern crate serde_derive;
|
|
||||||
|
|
||||||
pub mod parse_account_data;
|
|
||||||
pub mod parse_config;
|
|
||||||
pub mod parse_nonce;
|
|
||||||
pub mod parse_stake;
|
|
||||||
pub mod parse_sysvar;
|
|
||||||
pub mod parse_token;
|
|
||||||
pub mod parse_vote;
|
|
||||||
pub mod validator_info;
|
|
||||||
|
|
||||||
use crate::parse_account_data::{parse_account_data, AccountAdditionalData, ParsedAccount};
|
|
||||||
use solana_sdk::{account::Account, clock::Epoch, fee_calculator::FeeCalculator, pubkey::Pubkey};
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
pub type StringAmount = String;
|
|
||||||
|
|
||||||
/// A duplicate representation of an Account for pretty JSON serialization
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UiAccount {
|
|
||||||
pub lamports: u64,
|
|
||||||
pub data: UiAccountData,
|
|
||||||
pub owner: String,
|
|
||||||
pub executable: bool,
|
|
||||||
pub rent_epoch: Epoch,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase", untagged)]
|
|
||||||
pub enum UiAccountData {
|
|
||||||
LegacyBinary(String), // Legacy. Retained for RPC backwards compatibility
|
|
||||||
Json(ParsedAccount),
|
|
||||||
Binary(String, UiAccountEncoding),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub enum UiAccountEncoding {
|
|
||||||
Binary, // Legacy. Retained for RPC backwards compatibility
|
|
||||||
Base58,
|
|
||||||
Base64,
|
|
||||||
JsonParsed,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UiAccount {
|
|
||||||
pub fn encode(
|
|
||||||
pubkey: &Pubkey,
|
|
||||||
account: Account,
|
|
||||||
encoding: UiAccountEncoding,
|
|
||||||
additional_data: Option<AccountAdditionalData>,
|
|
||||||
data_slice_config: Option<UiDataSliceConfig>,
|
|
||||||
) -> Self {
|
|
||||||
let data = match encoding {
|
|
||||||
UiAccountEncoding::Binary => UiAccountData::LegacyBinary(
|
|
||||||
bs58::encode(slice_data(&account.data, data_slice_config)).into_string(),
|
|
||||||
),
|
|
||||||
UiAccountEncoding::Base58 => UiAccountData::Binary(
|
|
||||||
bs58::encode(slice_data(&account.data, data_slice_config)).into_string(),
|
|
||||||
encoding,
|
|
||||||
),
|
|
||||||
UiAccountEncoding::Base64 => UiAccountData::Binary(
|
|
||||||
base64::encode(slice_data(&account.data, data_slice_config)),
|
|
||||||
encoding,
|
|
||||||
),
|
|
||||||
UiAccountEncoding::JsonParsed => {
|
|
||||||
if let Ok(parsed_data) =
|
|
||||||
parse_account_data(pubkey, &account.owner, &account.data, additional_data)
|
|
||||||
{
|
|
||||||
UiAccountData::Json(parsed_data)
|
|
||||||
} else {
|
|
||||||
UiAccountData::Binary(base64::encode(&account.data), UiAccountEncoding::Base64)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
UiAccount {
|
|
||||||
lamports: account.lamports,
|
|
||||||
data,
|
|
||||||
owner: account.owner.to_string(),
|
|
||||||
executable: account.executable,
|
|
||||||
rent_epoch: account.rent_epoch,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn decode(&self) -> Option<Account> {
|
|
||||||
let data = match &self.data {
|
|
||||||
UiAccountData::Json(_) => None,
|
|
||||||
UiAccountData::LegacyBinary(blob) => bs58::decode(blob).into_vec().ok(),
|
|
||||||
UiAccountData::Binary(blob, encoding) => match encoding {
|
|
||||||
UiAccountEncoding::Base58 => bs58::decode(blob).into_vec().ok(),
|
|
||||||
UiAccountEncoding::Base64 => base64::decode(blob).ok(),
|
|
||||||
UiAccountEncoding::Binary | UiAccountEncoding::JsonParsed => None,
|
|
||||||
},
|
|
||||||
}?;
|
|
||||||
Some(Account {
|
|
||||||
lamports: self.lamports,
|
|
||||||
data,
|
|
||||||
owner: Pubkey::from_str(&self.owner).ok()?,
|
|
||||||
executable: self.executable,
|
|
||||||
rent_epoch: self.rent_epoch,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UiFeeCalculator {
|
|
||||||
pub lamports_per_signature: StringAmount,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<FeeCalculator> for UiFeeCalculator {
|
|
||||||
fn from(fee_calculator: FeeCalculator) -> Self {
|
|
||||||
Self {
|
|
||||||
lamports_per_signature: fee_calculator.lamports_per_signature.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for UiFeeCalculator {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
lamports_per_signature: "0".to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UiDataSliceConfig {
|
|
||||||
pub offset: usize,
|
|
||||||
pub length: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn slice_data(data: &[u8], data_slice_config: Option<UiDataSliceConfig>) -> &[u8] {
|
|
||||||
if let Some(UiDataSliceConfig { offset, length }) = data_slice_config {
|
|
||||||
if offset >= data.len() {
|
|
||||||
&[]
|
|
||||||
} else if length > data.len() - offset {
|
|
||||||
&data[offset..]
|
|
||||||
} else {
|
|
||||||
&data[offset..offset + length]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_slice_data() {
|
|
||||||
let data = vec![1, 2, 3, 4, 5];
|
|
||||||
let slice_config = Some(UiDataSliceConfig {
|
|
||||||
offset: 0,
|
|
||||||
length: 5,
|
|
||||||
});
|
|
||||||
assert_eq!(slice_data(&data, slice_config), &data[..]);
|
|
||||||
|
|
||||||
let slice_config = Some(UiDataSliceConfig {
|
|
||||||
offset: 0,
|
|
||||||
length: 10,
|
|
||||||
});
|
|
||||||
assert_eq!(slice_data(&data, slice_config), &data[..]);
|
|
||||||
|
|
||||||
let slice_config = Some(UiDataSliceConfig {
|
|
||||||
offset: 1,
|
|
||||||
length: 2,
|
|
||||||
});
|
|
||||||
assert_eq!(slice_data(&data, slice_config), &data[1..3]);
|
|
||||||
|
|
||||||
let slice_config = Some(UiDataSliceConfig {
|
|
||||||
offset: 10,
|
|
||||||
length: 2,
|
|
||||||
});
|
|
||||||
assert_eq!(slice_data(&data, slice_config), &[] as &[u8]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
parse_config::parse_config,
|
|
||||||
parse_nonce::parse_nonce,
|
|
||||||
parse_stake::parse_stake,
|
|
||||||
parse_sysvar::parse_sysvar,
|
|
||||||
parse_token::{parse_token, spl_token_id_v2_0},
|
|
||||||
parse_vote::parse_vote,
|
|
||||||
};
|
|
||||||
use inflector::Inflector;
|
|
||||||
use serde_json::Value;
|
|
||||||
use solana_sdk::{instruction::InstructionError, pubkey::Pubkey, system_program, sysvar};
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref CONFIG_PROGRAM_ID: Pubkey = solana_config_program::id();
|
|
||||||
static ref STAKE_PROGRAM_ID: Pubkey = solana_stake_program::id();
|
|
||||||
static ref SYSTEM_PROGRAM_ID: Pubkey = system_program::id();
|
|
||||||
static ref SYSVAR_PROGRAM_ID: Pubkey = sysvar::id();
|
|
||||||
static ref TOKEN_PROGRAM_ID: Pubkey = spl_token_id_v2_0();
|
|
||||||
static ref VOTE_PROGRAM_ID: Pubkey = solana_vote_program::id();
|
|
||||||
pub static ref PARSABLE_PROGRAM_IDS: HashMap<Pubkey, ParsableAccount> = {
|
|
||||||
let mut m = HashMap::new();
|
|
||||||
m.insert(*CONFIG_PROGRAM_ID, ParsableAccount::Config);
|
|
||||||
m.insert(*SYSTEM_PROGRAM_ID, ParsableAccount::Nonce);
|
|
||||||
m.insert(*TOKEN_PROGRAM_ID, ParsableAccount::SplToken);
|
|
||||||
m.insert(*STAKE_PROGRAM_ID, ParsableAccount::Stake);
|
|
||||||
m.insert(*SYSVAR_PROGRAM_ID, ParsableAccount::Sysvar);
|
|
||||||
m.insert(*VOTE_PROGRAM_ID, ParsableAccount::Vote);
|
|
||||||
m
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub enum ParseAccountError {
|
|
||||||
#[error("{0:?} account not parsable")]
|
|
||||||
AccountNotParsable(ParsableAccount),
|
|
||||||
|
|
||||||
#[error("Program not parsable")]
|
|
||||||
ProgramNotParsable,
|
|
||||||
|
|
||||||
#[error("Additional data required to parse: {0}")]
|
|
||||||
AdditionalDataMissing(String),
|
|
||||||
|
|
||||||
#[error("Instruction error")]
|
|
||||||
InstructionError(#[from] InstructionError),
|
|
||||||
|
|
||||||
#[error("Serde json error")]
|
|
||||||
SerdeJsonError(#[from] serde_json::error::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct ParsedAccount {
|
|
||||||
pub program: String,
|
|
||||||
pub parsed: Value,
|
|
||||||
pub space: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub enum ParsableAccount {
|
|
||||||
Config,
|
|
||||||
Nonce,
|
|
||||||
SplToken,
|
|
||||||
Stake,
|
|
||||||
Sysvar,
|
|
||||||
Vote,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct AccountAdditionalData {
|
|
||||||
pub spl_token_decimals: Option<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_account_data(
|
|
||||||
pubkey: &Pubkey,
|
|
||||||
program_id: &Pubkey,
|
|
||||||
data: &[u8],
|
|
||||||
additional_data: Option<AccountAdditionalData>,
|
|
||||||
) -> Result<ParsedAccount, ParseAccountError> {
|
|
||||||
let program_name = PARSABLE_PROGRAM_IDS
|
|
||||||
.get(program_id)
|
|
||||||
.ok_or_else(|| ParseAccountError::ProgramNotParsable)?;
|
|
||||||
let additional_data = additional_data.unwrap_or_default();
|
|
||||||
let parsed_json = match program_name {
|
|
||||||
ParsableAccount::Config => serde_json::to_value(parse_config(data, pubkey)?)?,
|
|
||||||
ParsableAccount::Nonce => serde_json::to_value(parse_nonce(data)?)?,
|
|
||||||
ParsableAccount::SplToken => {
|
|
||||||
serde_json::to_value(parse_token(data, additional_data.spl_token_decimals)?)?
|
|
||||||
}
|
|
||||||
ParsableAccount::Stake => serde_json::to_value(parse_stake(data)?)?,
|
|
||||||
ParsableAccount::Sysvar => serde_json::to_value(parse_sysvar(data, pubkey)?)?,
|
|
||||||
ParsableAccount::Vote => serde_json::to_value(parse_vote(data)?)?,
|
|
||||||
};
|
|
||||||
Ok(ParsedAccount {
|
|
||||||
program: format!("{:?}", program_name).to_kebab_case(),
|
|
||||||
parsed: parsed_json,
|
|
||||||
space: data.len() as u64,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
use solana_sdk::nonce::{
|
|
||||||
state::{Data, Versions},
|
|
||||||
State,
|
|
||||||
};
|
|
||||||
use solana_vote_program::vote_state::{VoteState, VoteStateVersions};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_parse_account_data() {
|
|
||||||
let account_pubkey = Pubkey::new_rand();
|
|
||||||
let other_program = Pubkey::new_rand();
|
|
||||||
let data = vec![0; 4];
|
|
||||||
assert!(parse_account_data(&account_pubkey, &other_program, &data, None).is_err());
|
|
||||||
|
|
||||||
let vote_state = VoteState::default();
|
|
||||||
let mut vote_account_data: Vec<u8> = vec![0; VoteState::size_of()];
|
|
||||||
let versioned = VoteStateVersions::Current(Box::new(vote_state));
|
|
||||||
VoteState::serialize(&versioned, &mut vote_account_data).unwrap();
|
|
||||||
let parsed = parse_account_data(
|
|
||||||
&account_pubkey,
|
|
||||||
&solana_vote_program::id(),
|
|
||||||
&vote_account_data,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(parsed.program, "vote".to_string());
|
|
||||||
assert_eq!(parsed.space, VoteState::size_of() as u64);
|
|
||||||
|
|
||||||
let nonce_data = Versions::new_current(State::Initialized(Data::default()));
|
|
||||||
let nonce_account_data = bincode::serialize(&nonce_data).unwrap();
|
|
||||||
let parsed = parse_account_data(
|
|
||||||
&account_pubkey,
|
|
||||||
&system_program::id(),
|
|
||||||
&nonce_account_data,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(parsed.program, "nonce".to_string());
|
|
||||||
assert_eq!(parsed.space, State::size() as u64);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
parse_account_data::{ParsableAccount, ParseAccountError},
|
|
||||||
validator_info,
|
|
||||||
};
|
|
||||||
use bincode::deserialize;
|
|
||||||
use serde_json::Value;
|
|
||||||
use solana_config_program::{get_config_data, ConfigKeys};
|
|
||||||
use solana_sdk::pubkey::Pubkey;
|
|
||||||
use solana_stake_program::config::Config as StakeConfig;
|
|
||||||
|
|
||||||
pub fn parse_config(data: &[u8], pubkey: &Pubkey) -> Result<ConfigAccountType, ParseAccountError> {
|
|
||||||
let parsed_account = if pubkey == &solana_stake_program::config::id() {
|
|
||||||
get_config_data(data)
|
|
||||||
.ok()
|
|
||||||
.and_then(|data| deserialize::<StakeConfig>(data).ok())
|
|
||||||
.map(|config| ConfigAccountType::StakeConfig(config.into()))
|
|
||||||
} else {
|
|
||||||
deserialize::<ConfigKeys>(data).ok().and_then(|key_list| {
|
|
||||||
if !key_list.keys.is_empty() && key_list.keys[0].0 == validator_info::id() {
|
|
||||||
parse_config_data::<String>(data, key_list.keys).and_then(|validator_info| {
|
|
||||||
Some(ConfigAccountType::ValidatorInfo(UiConfig {
|
|
||||||
keys: validator_info.keys,
|
|
||||||
config_data: serde_json::from_str(&validator_info.config_data).ok()?,
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
parsed_account.ok_or(ParseAccountError::AccountNotParsable(
|
|
||||||
ParsableAccount::Config,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_config_data<T>(data: &[u8], keys: Vec<(Pubkey, bool)>) -> Option<UiConfig<T>>
|
|
||||||
where
|
|
||||||
T: serde::de::DeserializeOwned,
|
|
||||||
{
|
|
||||||
let config_data: T = deserialize(&get_config_data(data).ok()?).ok()?;
|
|
||||||
let keys = keys
|
|
||||||
.iter()
|
|
||||||
.map(|key| UiConfigKey {
|
|
||||||
pubkey: key.0.to_string(),
|
|
||||||
signer: key.1,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
Some(UiConfig { keys, config_data })
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase", tag = "type", content = "info")]
|
|
||||||
pub enum ConfigAccountType {
|
|
||||||
StakeConfig(UiStakeConfig),
|
|
||||||
ValidatorInfo(UiConfig<Value>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UiConfigKey {
|
|
||||||
pub pubkey: String,
|
|
||||||
pub signer: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UiStakeConfig {
|
|
||||||
pub warmup_cooldown_rate: f64,
|
|
||||||
pub slash_penalty: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<StakeConfig> for UiStakeConfig {
|
|
||||||
fn from(config: StakeConfig) -> Self {
|
|
||||||
Self {
|
|
||||||
warmup_cooldown_rate: config.warmup_cooldown_rate,
|
|
||||||
slash_penalty: config.slash_penalty,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UiConfig<T> {
|
|
||||||
pub keys: Vec<UiConfigKey>,
|
|
||||||
pub config_data: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
use crate::validator_info::ValidatorInfo;
|
|
||||||
use serde_json::json;
|
|
||||||
use solana_config_program::create_config_account;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_parse_config() {
|
|
||||||
let stake_config = StakeConfig {
|
|
||||||
warmup_cooldown_rate: 0.25,
|
|
||||||
slash_penalty: 50,
|
|
||||||
};
|
|
||||||
let stake_config_account = create_config_account(vec![], &stake_config, 10);
|
|
||||||
assert_eq!(
|
|
||||||
parse_config(
|
|
||||||
&stake_config_account.data,
|
|
||||||
&solana_stake_program::config::id()
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
ConfigAccountType::StakeConfig(UiStakeConfig {
|
|
||||||
warmup_cooldown_rate: 0.25,
|
|
||||||
slash_penalty: 50,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
let validator_info = ValidatorInfo {
|
|
||||||
info: serde_json::to_string(&json!({
|
|
||||||
"name": "Solana",
|
|
||||||
}))
|
|
||||||
.unwrap(),
|
|
||||||
};
|
|
||||||
let info_pubkey = Pubkey::new_rand();
|
|
||||||
let validator_info_config_account = create_config_account(
|
|
||||||
vec![(validator_info::id(), false), (info_pubkey, true)],
|
|
||||||
&validator_info,
|
|
||||||
10,
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
parse_config(&validator_info_config_account.data, &info_pubkey).unwrap(),
|
|
||||||
ConfigAccountType::ValidatorInfo(UiConfig {
|
|
||||||
keys: vec![
|
|
||||||
UiConfigKey {
|
|
||||||
pubkey: validator_info::id().to_string(),
|
|
||||||
signer: false,
|
|
||||||
},
|
|
||||||
UiConfigKey {
|
|
||||||
pubkey: info_pubkey.to_string(),
|
|
||||||
signer: true,
|
|
||||||
}
|
|
||||||
],
|
|
||||||
config_data: serde_json::from_str(r#"{"name":"Solana"}"#).unwrap(),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
let bad_data = vec![0; 4];
|
|
||||||
assert!(parse_config(&bad_data, &info_pubkey).is_err());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
use crate::{parse_account_data::ParseAccountError, UiFeeCalculator};
|
|
||||||
use solana_sdk::{
|
|
||||||
instruction::InstructionError,
|
|
||||||
nonce::{state::Versions, State},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn parse_nonce(data: &[u8]) -> Result<UiNonceState, ParseAccountError> {
|
|
||||||
let nonce_state: Versions = bincode::deserialize(data)
|
|
||||||
.map_err(|_| ParseAccountError::from(InstructionError::InvalidAccountData))?;
|
|
||||||
let nonce_state = nonce_state.convert_to_current();
|
|
||||||
match nonce_state {
|
|
||||||
State::Uninitialized => Ok(UiNonceState::Uninitialized),
|
|
||||||
State::Initialized(data) => Ok(UiNonceState::Initialized(UiNonceData {
|
|
||||||
authority: data.authority.to_string(),
|
|
||||||
blockhash: data.blockhash.to_string(),
|
|
||||||
fee_calculator: data.fee_calculator.into(),
|
|
||||||
})),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A duplicate representation of NonceState for pretty JSON serialization
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase", tag = "type", content = "info")]
|
|
||||||
pub enum UiNonceState {
|
|
||||||
Uninitialized,
|
|
||||||
Initialized(UiNonceData),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UiNonceData {
|
|
||||||
pub authority: String,
|
|
||||||
pub blockhash: String,
|
|
||||||
pub fee_calculator: UiFeeCalculator,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
use solana_sdk::{
|
|
||||||
hash::Hash,
|
|
||||||
nonce::{
|
|
||||||
state::{Data, Versions},
|
|
||||||
State,
|
|
||||||
},
|
|
||||||
pubkey::Pubkey,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_parse_nonce() {
|
|
||||||
let nonce_data = Versions::new_current(State::Initialized(Data::default()));
|
|
||||||
let nonce_account_data = bincode::serialize(&nonce_data).unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
parse_nonce(&nonce_account_data).unwrap(),
|
|
||||||
UiNonceState::Initialized(UiNonceData {
|
|
||||||
authority: Pubkey::default().to_string(),
|
|
||||||
blockhash: Hash::default().to_string(),
|
|
||||||
fee_calculator: UiFeeCalculator {
|
|
||||||
lamports_per_signature: 0.to_string(),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
let bad_data = vec![0; 4];
|
|
||||||
assert!(parse_nonce(&bad_data).is_err());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,235 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
parse_account_data::{ParsableAccount, ParseAccountError},
|
|
||||||
StringAmount,
|
|
||||||
};
|
|
||||||
use bincode::deserialize;
|
|
||||||
use solana_sdk::clock::{Epoch, UnixTimestamp};
|
|
||||||
use solana_stake_program::stake_state::{Authorized, Delegation, Lockup, Meta, Stake, StakeState};
|
|
||||||
|
|
||||||
pub fn parse_stake(data: &[u8]) -> Result<StakeAccountType, ParseAccountError> {
|
|
||||||
let stake_state: StakeState = deserialize(data)
|
|
||||||
.map_err(|_| ParseAccountError::AccountNotParsable(ParsableAccount::Stake))?;
|
|
||||||
let parsed_account = match stake_state {
|
|
||||||
StakeState::Uninitialized => StakeAccountType::Uninitialized,
|
|
||||||
StakeState::Initialized(meta) => StakeAccountType::Initialized(UiStakeAccount {
|
|
||||||
meta: meta.into(),
|
|
||||||
stake: None,
|
|
||||||
}),
|
|
||||||
StakeState::Stake(meta, stake) => StakeAccountType::Delegated(UiStakeAccount {
|
|
||||||
meta: meta.into(),
|
|
||||||
stake: Some(stake.into()),
|
|
||||||
}),
|
|
||||||
StakeState::RewardsPool => StakeAccountType::RewardsPool,
|
|
||||||
};
|
|
||||||
Ok(parsed_account)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase", tag = "type", content = "info")]
|
|
||||||
pub enum StakeAccountType {
|
|
||||||
Uninitialized,
|
|
||||||
Initialized(UiStakeAccount),
|
|
||||||
Delegated(UiStakeAccount),
|
|
||||||
RewardsPool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UiStakeAccount {
|
|
||||||
pub meta: UiMeta,
|
|
||||||
pub stake: Option<UiStake>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UiMeta {
|
|
||||||
pub rent_exempt_reserve: StringAmount,
|
|
||||||
pub authorized: UiAuthorized,
|
|
||||||
pub lockup: UiLockup,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Meta> for UiMeta {
|
|
||||||
fn from(meta: Meta) -> Self {
|
|
||||||
Self {
|
|
||||||
rent_exempt_reserve: meta.rent_exempt_reserve.to_string(),
|
|
||||||
authorized: meta.authorized.into(),
|
|
||||||
lockup: meta.lockup.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UiLockup {
|
|
||||||
pub unix_timestamp: UnixTimestamp,
|
|
||||||
pub epoch: Epoch,
|
|
||||||
pub custodian: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Lockup> for UiLockup {
|
|
||||||
fn from(lockup: Lockup) -> Self {
|
|
||||||
Self {
|
|
||||||
unix_timestamp: lockup.unix_timestamp,
|
|
||||||
epoch: lockup.epoch,
|
|
||||||
custodian: lockup.custodian.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UiAuthorized {
|
|
||||||
pub staker: String,
|
|
||||||
pub withdrawer: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Authorized> for UiAuthorized {
|
|
||||||
fn from(authorized: Authorized) -> Self {
|
|
||||||
Self {
|
|
||||||
staker: authorized.staker.to_string(),
|
|
||||||
withdrawer: authorized.withdrawer.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UiStake {
|
|
||||||
pub delegation: UiDelegation,
|
|
||||||
pub credits_observed: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Stake> for UiStake {
|
|
||||||
fn from(stake: Stake) -> Self {
|
|
||||||
Self {
|
|
||||||
delegation: stake.delegation.into(),
|
|
||||||
credits_observed: stake.credits_observed,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UiDelegation {
|
|
||||||
pub voter: String,
|
|
||||||
pub stake: StringAmount,
|
|
||||||
pub activation_epoch: StringAmount,
|
|
||||||
pub deactivation_epoch: StringAmount,
|
|
||||||
pub warmup_cooldown_rate: f64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Delegation> for UiDelegation {
|
|
||||||
fn from(delegation: Delegation) -> Self {
|
|
||||||
Self {
|
|
||||||
voter: delegation.voter_pubkey.to_string(),
|
|
||||||
stake: delegation.stake.to_string(),
|
|
||||||
activation_epoch: delegation.activation_epoch.to_string(),
|
|
||||||
deactivation_epoch: delegation.deactivation_epoch.to_string(),
|
|
||||||
warmup_cooldown_rate: delegation.warmup_cooldown_rate,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
use bincode::serialize;
|
|
||||||
use solana_sdk::pubkey::Pubkey;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_parse_stake() {
|
|
||||||
let stake_state = StakeState::Uninitialized;
|
|
||||||
let stake_data = serialize(&stake_state).unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
parse_stake(&stake_data).unwrap(),
|
|
||||||
StakeAccountType::Uninitialized
|
|
||||||
);
|
|
||||||
|
|
||||||
let pubkey = Pubkey::new_rand();
|
|
||||||
let custodian = Pubkey::new_rand();
|
|
||||||
let authorized = Authorized::auto(&pubkey);
|
|
||||||
let lockup = Lockup {
|
|
||||||
unix_timestamp: 0,
|
|
||||||
epoch: 1,
|
|
||||||
custodian,
|
|
||||||
};
|
|
||||||
let meta = Meta {
|
|
||||||
rent_exempt_reserve: 42,
|
|
||||||
authorized,
|
|
||||||
lockup,
|
|
||||||
};
|
|
||||||
|
|
||||||
let stake_state = StakeState::Initialized(meta);
|
|
||||||
let stake_data = serialize(&stake_state).unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
parse_stake(&stake_data).unwrap(),
|
|
||||||
StakeAccountType::Initialized(UiStakeAccount {
|
|
||||||
meta: UiMeta {
|
|
||||||
rent_exempt_reserve: 42.to_string(),
|
|
||||||
authorized: UiAuthorized {
|
|
||||||
staker: pubkey.to_string(),
|
|
||||||
withdrawer: pubkey.to_string(),
|
|
||||||
},
|
|
||||||
lockup: UiLockup {
|
|
||||||
unix_timestamp: 0,
|
|
||||||
epoch: 1,
|
|
||||||
custodian: custodian.to_string(),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
stake: None,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
let voter_pubkey = Pubkey::new_rand();
|
|
||||||
let stake = Stake {
|
|
||||||
delegation: Delegation {
|
|
||||||
voter_pubkey,
|
|
||||||
stake: 20,
|
|
||||||
activation_epoch: 2,
|
|
||||||
deactivation_epoch: std::u64::MAX,
|
|
||||||
warmup_cooldown_rate: 0.25,
|
|
||||||
},
|
|
||||||
credits_observed: 10,
|
|
||||||
};
|
|
||||||
|
|
||||||
let stake_state = StakeState::Stake(meta, stake);
|
|
||||||
let stake_data = serialize(&stake_state).unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
parse_stake(&stake_data).unwrap(),
|
|
||||||
StakeAccountType::Delegated(UiStakeAccount {
|
|
||||||
meta: UiMeta {
|
|
||||||
rent_exempt_reserve: 42.to_string(),
|
|
||||||
authorized: UiAuthorized {
|
|
||||||
staker: pubkey.to_string(),
|
|
||||||
withdrawer: pubkey.to_string(),
|
|
||||||
},
|
|
||||||
lockup: UiLockup {
|
|
||||||
unix_timestamp: 0,
|
|
||||||
epoch: 1,
|
|
||||||
custodian: custodian.to_string(),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
stake: Some(UiStake {
|
|
||||||
delegation: UiDelegation {
|
|
||||||
voter: voter_pubkey.to_string(),
|
|
||||||
stake: 20.to_string(),
|
|
||||||
activation_epoch: 2.to_string(),
|
|
||||||
deactivation_epoch: std::u64::MAX.to_string(),
|
|
||||||
warmup_cooldown_rate: 0.25,
|
|
||||||
},
|
|
||||||
credits_observed: 10,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
let stake_state = StakeState::RewardsPool;
|
|
||||||
let stake_data = serialize(&stake_state).unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
parse_stake(&stake_data).unwrap(),
|
|
||||||
StakeAccountType::RewardsPool
|
|
||||||
);
|
|
||||||
|
|
||||||
let bad_data = vec![1, 2, 3, 4];
|
|
||||||
assert!(parse_stake(&bad_data).is_err());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,328 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
parse_account_data::{ParsableAccount, ParseAccountError},
|
|
||||||
StringAmount, UiFeeCalculator,
|
|
||||||
};
|
|
||||||
use bincode::deserialize;
|
|
||||||
use bv::BitVec;
|
|
||||||
use solana_sdk::{
|
|
||||||
clock::{Clock, Epoch, Slot, UnixTimestamp},
|
|
||||||
epoch_schedule::EpochSchedule,
|
|
||||||
pubkey::Pubkey,
|
|
||||||
rent::Rent,
|
|
||||||
slot_hashes::SlotHashes,
|
|
||||||
slot_history::{self, SlotHistory},
|
|
||||||
stake_history::{StakeHistory, StakeHistoryEntry},
|
|
||||||
sysvar::{self, fees::Fees, recent_blockhashes::RecentBlockhashes, rewards::Rewards},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn parse_sysvar(data: &[u8], pubkey: &Pubkey) -> Result<SysvarAccountType, ParseAccountError> {
|
|
||||||
let parsed_account = {
|
|
||||||
if pubkey == &sysvar::clock::id() {
|
|
||||||
deserialize::<Clock>(data)
|
|
||||||
.ok()
|
|
||||||
.map(|clock| SysvarAccountType::Clock(clock.into()))
|
|
||||||
} else if pubkey == &sysvar::epoch_schedule::id() {
|
|
||||||
deserialize(data).ok().map(SysvarAccountType::EpochSchedule)
|
|
||||||
} else if pubkey == &sysvar::fees::id() {
|
|
||||||
deserialize::<Fees>(data)
|
|
||||||
.ok()
|
|
||||||
.map(|fees| SysvarAccountType::Fees(fees.into()))
|
|
||||||
} else if pubkey == &sysvar::recent_blockhashes::id() {
|
|
||||||
deserialize::<RecentBlockhashes>(data)
|
|
||||||
.ok()
|
|
||||||
.map(|recent_blockhashes| {
|
|
||||||
let recent_blockhashes = recent_blockhashes
|
|
||||||
.iter()
|
|
||||||
.map(|entry| UiRecentBlockhashesEntry {
|
|
||||||
blockhash: entry.blockhash.to_string(),
|
|
||||||
fee_calculator: entry.fee_calculator.clone().into(),
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
SysvarAccountType::RecentBlockhashes(recent_blockhashes)
|
|
||||||
})
|
|
||||||
} else if pubkey == &sysvar::rent::id() {
|
|
||||||
deserialize::<Rent>(data)
|
|
||||||
.ok()
|
|
||||||
.map(|rent| SysvarAccountType::Rent(rent.into()))
|
|
||||||
} else if pubkey == &sysvar::rewards::id() {
|
|
||||||
deserialize::<Rewards>(data)
|
|
||||||
.ok()
|
|
||||||
.map(|rewards| SysvarAccountType::Rewards(rewards.into()))
|
|
||||||
} else if pubkey == &sysvar::slot_hashes::id() {
|
|
||||||
deserialize::<SlotHashes>(data).ok().map(|slot_hashes| {
|
|
||||||
let slot_hashes = slot_hashes
|
|
||||||
.iter()
|
|
||||||
.map(|slot_hash| UiSlotHashEntry {
|
|
||||||
slot: slot_hash.0,
|
|
||||||
hash: slot_hash.1.to_string(),
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
SysvarAccountType::SlotHashes(slot_hashes)
|
|
||||||
})
|
|
||||||
} else if pubkey == &sysvar::slot_history::id() {
|
|
||||||
deserialize::<SlotHistory>(data).ok().map(|slot_history| {
|
|
||||||
SysvarAccountType::SlotHistory(UiSlotHistory {
|
|
||||||
next_slot: slot_history.next_slot,
|
|
||||||
bits: format!("{:?}", SlotHistoryBits(slot_history.bits)),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} else if pubkey == &sysvar::stake_history::id() {
|
|
||||||
deserialize::<StakeHistory>(data).ok().map(|stake_history| {
|
|
||||||
let stake_history = stake_history
|
|
||||||
.iter()
|
|
||||||
.map(|entry| UiStakeHistoryEntry {
|
|
||||||
epoch: entry.0,
|
|
||||||
stake_history: entry.1.clone(),
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
SysvarAccountType::StakeHistory(stake_history)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
};
|
|
||||||
parsed_account.ok_or(ParseAccountError::AccountNotParsable(
|
|
||||||
ParsableAccount::Sysvar,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase", tag = "type", content = "info")]
|
|
||||||
pub enum SysvarAccountType {
|
|
||||||
Clock(UiClock),
|
|
||||||
EpochSchedule(EpochSchedule),
|
|
||||||
Fees(UiFees),
|
|
||||||
RecentBlockhashes(Vec<UiRecentBlockhashesEntry>),
|
|
||||||
Rent(UiRent),
|
|
||||||
Rewards(UiRewards),
|
|
||||||
SlotHashes(Vec<UiSlotHashEntry>),
|
|
||||||
SlotHistory(UiSlotHistory),
|
|
||||||
StakeHistory(Vec<UiStakeHistoryEntry>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Default)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UiClock {
|
|
||||||
pub slot: Slot,
|
|
||||||
pub epoch: Epoch,
|
|
||||||
pub leader_schedule_epoch: Epoch,
|
|
||||||
pub unix_timestamp: UnixTimestamp,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Clock> for UiClock {
|
|
||||||
fn from(clock: Clock) -> Self {
|
|
||||||
Self {
|
|
||||||
slot: clock.slot,
|
|
||||||
epoch: clock.epoch,
|
|
||||||
leader_schedule_epoch: clock.leader_schedule_epoch,
|
|
||||||
unix_timestamp: clock.unix_timestamp,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Default)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UiFees {
|
|
||||||
pub fee_calculator: UiFeeCalculator,
|
|
||||||
}
|
|
||||||
impl From<Fees> for UiFees {
|
|
||||||
fn from(fees: Fees) -> Self {
|
|
||||||
Self {
|
|
||||||
fee_calculator: fees.fee_calculator.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Default)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UiRent {
|
|
||||||
pub lamports_per_byte_year: StringAmount,
|
|
||||||
pub exemption_threshold: f64,
|
|
||||||
pub burn_percent: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Rent> for UiRent {
|
|
||||||
fn from(rent: Rent) -> Self {
|
|
||||||
Self {
|
|
||||||
lamports_per_byte_year: rent.lamports_per_byte_year.to_string(),
|
|
||||||
exemption_threshold: rent.exemption_threshold,
|
|
||||||
burn_percent: rent.burn_percent,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Default)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UiRewards {
|
|
||||||
pub validator_point_value: f64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Rewards> for UiRewards {
|
|
||||||
fn from(rewards: Rewards) -> Self {
|
|
||||||
Self {
|
|
||||||
validator_point_value: rewards.validator_point_value,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UiRecentBlockhashesEntry {
|
|
||||||
pub blockhash: String,
|
|
||||||
pub fee_calculator: UiFeeCalculator,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UiSlotHashEntry {
|
|
||||||
pub slot: Slot,
|
|
||||||
pub hash: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UiSlotHistory {
|
|
||||||
pub next_slot: Slot,
|
|
||||||
pub bits: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SlotHistoryBits(BitVec<u64>);
|
|
||||||
|
|
||||||
impl std::fmt::Debug for SlotHistoryBits {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
for i in 0..slot_history::MAX_ENTRIES {
|
|
||||||
if self.0.get(i) {
|
|
||||||
write!(f, "1")?;
|
|
||||||
} else {
|
|
||||||
write!(f, "0")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UiStakeHistoryEntry {
|
|
||||||
pub epoch: Epoch,
|
|
||||||
pub stake_history: StakeHistoryEntry,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
use solana_sdk::{
|
|
||||||
fee_calculator::FeeCalculator,
|
|
||||||
hash::Hash,
|
|
||||||
sysvar::{recent_blockhashes::IterItem, Sysvar},
|
|
||||||
};
|
|
||||||
use std::iter::FromIterator;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_parse_sysvars() {
|
|
||||||
let clock_sysvar = Clock::default().create_account(1);
|
|
||||||
assert_eq!(
|
|
||||||
parse_sysvar(&clock_sysvar.data, &sysvar::clock::id()).unwrap(),
|
|
||||||
SysvarAccountType::Clock(UiClock::default()),
|
|
||||||
);
|
|
||||||
|
|
||||||
let epoch_schedule = EpochSchedule {
|
|
||||||
slots_per_epoch: 12,
|
|
||||||
leader_schedule_slot_offset: 0,
|
|
||||||
warmup: false,
|
|
||||||
first_normal_epoch: 1,
|
|
||||||
first_normal_slot: 12,
|
|
||||||
};
|
|
||||||
let epoch_schedule_sysvar = epoch_schedule.create_account(1);
|
|
||||||
assert_eq!(
|
|
||||||
parse_sysvar(&epoch_schedule_sysvar.data, &sysvar::epoch_schedule::id()).unwrap(),
|
|
||||||
SysvarAccountType::EpochSchedule(epoch_schedule),
|
|
||||||
);
|
|
||||||
|
|
||||||
let fees_sysvar = Fees::default().create_account(1);
|
|
||||||
assert_eq!(
|
|
||||||
parse_sysvar(&fees_sysvar.data, &sysvar::fees::id()).unwrap(),
|
|
||||||
SysvarAccountType::Fees(UiFees::default()),
|
|
||||||
);
|
|
||||||
|
|
||||||
let hash = Hash::new(&[1; 32]);
|
|
||||||
let fee_calculator = FeeCalculator {
|
|
||||||
lamports_per_signature: 10,
|
|
||||||
};
|
|
||||||
let recent_blockhashes =
|
|
||||||
RecentBlockhashes::from_iter(vec![IterItem(0, &hash, &fee_calculator)].into_iter());
|
|
||||||
let recent_blockhashes_sysvar = recent_blockhashes.create_account(1);
|
|
||||||
assert_eq!(
|
|
||||||
parse_sysvar(
|
|
||||||
&recent_blockhashes_sysvar.data,
|
|
||||||
&sysvar::recent_blockhashes::id()
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
SysvarAccountType::RecentBlockhashes(vec![UiRecentBlockhashesEntry {
|
|
||||||
blockhash: hash.to_string(),
|
|
||||||
fee_calculator: fee_calculator.into(),
|
|
||||||
}]),
|
|
||||||
);
|
|
||||||
|
|
||||||
let rent = Rent {
|
|
||||||
lamports_per_byte_year: 10,
|
|
||||||
exemption_threshold: 2.0,
|
|
||||||
burn_percent: 5,
|
|
||||||
};
|
|
||||||
let rent_sysvar = rent.create_account(1);
|
|
||||||
assert_eq!(
|
|
||||||
parse_sysvar(&rent_sysvar.data, &sysvar::rent::id()).unwrap(),
|
|
||||||
SysvarAccountType::Rent(rent.into()),
|
|
||||||
);
|
|
||||||
|
|
||||||
let rewards_sysvar = Rewards::default().create_account(1);
|
|
||||||
assert_eq!(
|
|
||||||
parse_sysvar(&rewards_sysvar.data, &sysvar::rewards::id()).unwrap(),
|
|
||||||
SysvarAccountType::Rewards(UiRewards::default()),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut slot_hashes = SlotHashes::default();
|
|
||||||
slot_hashes.add(1, hash);
|
|
||||||
let slot_hashes_sysvar = slot_hashes.create_account(1);
|
|
||||||
assert_eq!(
|
|
||||||
parse_sysvar(&slot_hashes_sysvar.data, &sysvar::slot_hashes::id()).unwrap(),
|
|
||||||
SysvarAccountType::SlotHashes(vec![UiSlotHashEntry {
|
|
||||||
slot: 1,
|
|
||||||
hash: hash.to_string(),
|
|
||||||
}]),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut slot_history = SlotHistory::default();
|
|
||||||
slot_history.add(42);
|
|
||||||
let slot_history_sysvar = slot_history.create_account(1);
|
|
||||||
assert_eq!(
|
|
||||||
parse_sysvar(&slot_history_sysvar.data, &sysvar::slot_history::id()).unwrap(),
|
|
||||||
SysvarAccountType::SlotHistory(UiSlotHistory {
|
|
||||||
next_slot: slot_history.next_slot,
|
|
||||||
bits: format!("{:?}", SlotHistoryBits(slot_history.bits)),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut stake_history = StakeHistory::default();
|
|
||||||
let stake_history_entry = StakeHistoryEntry {
|
|
||||||
effective: 10,
|
|
||||||
activating: 2,
|
|
||||||
deactivating: 3,
|
|
||||||
};
|
|
||||||
stake_history.add(1, stake_history_entry.clone());
|
|
||||||
let stake_history_sysvar = stake_history.create_account(1);
|
|
||||||
assert_eq!(
|
|
||||||
parse_sysvar(&stake_history_sysvar.data, &sysvar::stake_history::id()).unwrap(),
|
|
||||||
SysvarAccountType::StakeHistory(vec![UiStakeHistoryEntry {
|
|
||||||
epoch: 1,
|
|
||||||
stake_history: stake_history_entry,
|
|
||||||
}]),
|
|
||||||
);
|
|
||||||
|
|
||||||
let bad_pubkey = Pubkey::new_rand();
|
|
||||||
assert!(parse_sysvar(&stake_history_sysvar.data, &bad_pubkey).is_err());
|
|
||||||
|
|
||||||
let bad_data = vec![0; 4];
|
|
||||||
assert!(parse_sysvar(&bad_data, &sysvar::stake_history::id()).is_err());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,308 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
parse_account_data::{ParsableAccount, ParseAccountError},
|
|
||||||
StringAmount,
|
|
||||||
};
|
|
||||||
use solana_sdk::pubkey::Pubkey;
|
|
||||||
use spl_token_v2_0::{
|
|
||||||
option::COption,
|
|
||||||
pack::Pack,
|
|
||||||
solana_sdk::pubkey::Pubkey as SplTokenPubkey,
|
|
||||||
state::{Account, AccountState, Mint, Multisig},
|
|
||||||
};
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
// A helper function to convert spl_token_v2_0::id() as spl_sdk::pubkey::Pubkey to
|
|
||||||
// solana_sdk::pubkey::Pubkey
|
|
||||||
pub fn spl_token_id_v2_0() -> Pubkey {
|
|
||||||
Pubkey::from_str(&spl_token_v2_0::id().to_string()).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
// A helper function to convert spl_token_v2_0::native_mint::id() as spl_sdk::pubkey::Pubkey to
|
|
||||||
// solana_sdk::pubkey::Pubkey
|
|
||||||
pub fn spl_token_v2_0_native_mint() -> Pubkey {
|
|
||||||
Pubkey::from_str(&spl_token_v2_0::native_mint::id().to_string()).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_token(
|
|
||||||
data: &[u8],
|
|
||||||
mint_decimals: Option<u8>,
|
|
||||||
) -> Result<TokenAccountType, ParseAccountError> {
|
|
||||||
if data.len() == Account::get_packed_len() {
|
|
||||||
let account = Account::unpack(data)
|
|
||||||
.map_err(|_| ParseAccountError::AccountNotParsable(ParsableAccount::SplToken))?;
|
|
||||||
let decimals = mint_decimals.ok_or_else(|| {
|
|
||||||
ParseAccountError::AdditionalDataMissing(
|
|
||||||
"no mint_decimals provided to parse spl-token account".to_string(),
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
Ok(TokenAccountType::Account(UiTokenAccount {
|
|
||||||
mint: account.mint.to_string(),
|
|
||||||
owner: account.owner.to_string(),
|
|
||||||
token_amount: token_amount_to_ui_amount(account.amount, decimals),
|
|
||||||
delegate: match account.delegate {
|
|
||||||
COption::Some(pubkey) => Some(pubkey.to_string()),
|
|
||||||
COption::None => None,
|
|
||||||
},
|
|
||||||
state: account.state.into(),
|
|
||||||
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() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(token_amount_to_ui_amount(
|
|
||||||
account.delegated_amount,
|
|
||||||
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() {
|
|
||||||
let mint = Mint::unpack(data)
|
|
||||||
.map_err(|_| ParseAccountError::AccountNotParsable(ParsableAccount::SplToken))?;
|
|
||||||
Ok(TokenAccountType::Mint(UiMint {
|
|
||||||
mint_authority: match mint.mint_authority {
|
|
||||||
COption::Some(pubkey) => Some(pubkey.to_string()),
|
|
||||||
COption::None => None,
|
|
||||||
},
|
|
||||||
supply: mint.supply.to_string(),
|
|
||||||
decimals: mint.decimals,
|
|
||||||
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() {
|
|
||||||
let multisig = Multisig::unpack(data)
|
|
||||||
.map_err(|_| ParseAccountError::AccountNotParsable(ParsableAccount::SplToken))?;
|
|
||||||
Ok(TokenAccountType::Multisig(UiMultisig {
|
|
||||||
num_required_signers: multisig.m,
|
|
||||||
num_valid_signers: multisig.n,
|
|
||||||
is_initialized: multisig.is_initialized,
|
|
||||||
signers: multisig
|
|
||||||
.signers
|
|
||||||
.iter()
|
|
||||||
.filter_map(|pubkey| {
|
|
||||||
if pubkey != &SplTokenPubkey::default() {
|
|
||||||
Some(pubkey.to_string())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
Err(ParseAccountError::AccountNotParsable(
|
|
||||||
ParsableAccount::SplToken,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase", tag = "type", content = "info")]
|
|
||||||
pub enum TokenAccountType {
|
|
||||||
Account(UiTokenAccount),
|
|
||||||
Mint(UiMint),
|
|
||||||
Multisig(UiMultisig),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UiTokenAccount {
|
|
||||||
pub mint: String,
|
|
||||||
pub owner: String,
|
|
||||||
pub token_amount: UiTokenAmount,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub delegate: Option<String>,
|
|
||||||
pub state: UiAccountState,
|
|
||||||
pub is_native: bool,
|
|
||||||
#[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>,
|
|
||||||
#[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)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UiTokenAmount {
|
|
||||||
pub ui_amount: f64,
|
|
||||||
pub decimals: u8,
|
|
||||||
pub amount: StringAmount,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn token_amount_to_ui_amount(amount: u64, decimals: u8) -> UiTokenAmount {
|
|
||||||
// Use `amount_to_ui_amount()` once spl_token is bumped to a version that supports it: https://github.com/solana-labs/solana-program-library/pull/211
|
|
||||||
let amount_decimals = amount as f64 / 10_usize.pow(decimals as u32) as f64;
|
|
||||||
UiTokenAmount {
|
|
||||||
ui_amount: amount_decimals,
|
|
||||||
decimals,
|
|
||||||
amount: amount.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UiMint {
|
|
||||||
pub mint_authority: Option<String>,
|
|
||||||
pub supply: StringAmount,
|
|
||||||
pub decimals: u8,
|
|
||||||
pub is_initialized: bool,
|
|
||||||
pub freeze_authority: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UiMultisig {
|
|
||||||
pub num_required_signers: u8,
|
|
||||||
pub num_valid_signers: u8,
|
|
||||||
pub is_initialized: bool,
|
|
||||||
pub signers: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_token_account_mint(data: &[u8]) -> Option<Pubkey> {
|
|
||||||
if data.len() == Account::get_packed_len() {
|
|
||||||
Some(Pubkey::new(&data[0..32]))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_parse_token() {
|
|
||||||
let mint_pubkey = SplTokenPubkey::new(&[2; 32]);
|
|
||||||
let owner_pubkey = SplTokenPubkey::new(&[3; 32]);
|
|
||||||
let mut account_data = vec![0; Account::get_packed_len()];
|
|
||||||
Account::unpack_unchecked_mut(&mut account_data, &mut |account: &mut Account| {
|
|
||||||
account.mint = mint_pubkey;
|
|
||||||
account.owner = owner_pubkey;
|
|
||||||
account.amount = 42;
|
|
||||||
account.state = AccountState::Initialized;
|
|
||||||
account.is_native = COption::None;
|
|
||||||
account.close_authority = COption::Some(owner_pubkey);
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert!(parse_token(&account_data, None).is_err());
|
|
||||||
assert_eq!(
|
|
||||||
parse_token(&account_data, Some(2)).unwrap(),
|
|
||||||
TokenAccountType::Account(UiTokenAccount {
|
|
||||||
mint: mint_pubkey.to_string(),
|
|
||||||
owner: owner_pubkey.to_string(),
|
|
||||||
token_amount: UiTokenAmount {
|
|
||||||
ui_amount: 0.42,
|
|
||||||
decimals: 2,
|
|
||||||
amount: "42".to_string()
|
|
||||||
},
|
|
||||||
delegate: None,
|
|
||||||
state: UiAccountState::Initialized,
|
|
||||||
is_native: false,
|
|
||||||
rent_exempt_reserve: None,
|
|
||||||
delegated_amount: None,
|
|
||||||
close_authority: Some(owner_pubkey.to_string()),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut mint_data = vec![0; Mint::get_packed_len()];
|
|
||||||
Mint::unpack_unchecked_mut(&mut mint_data, &mut |mint: &mut Mint| {
|
|
||||||
mint.mint_authority = COption::Some(owner_pubkey);
|
|
||||||
mint.supply = 42;
|
|
||||||
mint.decimals = 3;
|
|
||||||
mint.is_initialized = true;
|
|
||||||
mint.freeze_authority = COption::Some(owner_pubkey);
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
parse_token(&mint_data, None).unwrap(),
|
|
||||||
TokenAccountType::Mint(UiMint {
|
|
||||||
mint_authority: Some(owner_pubkey.to_string()),
|
|
||||||
supply: 42.to_string(),
|
|
||||||
decimals: 3,
|
|
||||||
is_initialized: true,
|
|
||||||
freeze_authority: Some(owner_pubkey.to_string()),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
let signer1 = SplTokenPubkey::new(&[1; 32]);
|
|
||||||
let signer2 = SplTokenPubkey::new(&[2; 32]);
|
|
||||||
let signer3 = SplTokenPubkey::new(&[3; 32]);
|
|
||||||
let mut multisig_data = vec![0; Multisig::get_packed_len()];
|
|
||||||
let mut signers = [SplTokenPubkey::default(); 11];
|
|
||||||
signers[0] = signer1;
|
|
||||||
signers[1] = signer2;
|
|
||||||
signers[2] = signer3;
|
|
||||||
Multisig::unpack_unchecked_mut(&mut multisig_data, &mut |multisig: &mut Multisig| {
|
|
||||||
multisig.m = 2;
|
|
||||||
multisig.n = 3;
|
|
||||||
multisig.is_initialized = true;
|
|
||||||
multisig.signers = signers;
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
parse_token(&multisig_data, None).unwrap(),
|
|
||||||
TokenAccountType::Multisig(UiMultisig {
|
|
||||||
num_required_signers: 2,
|
|
||||||
num_valid_signers: 3,
|
|
||||||
is_initialized: true,
|
|
||||||
signers: vec![
|
|
||||||
signer1.to_string(),
|
|
||||||
signer2.to_string(),
|
|
||||||
signer3.to_string()
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
let bad_data = vec![0; 4];
|
|
||||||
assert!(parse_token(&bad_data, None).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_get_token_account_mint() {
|
|
||||||
let mint_pubkey = SplTokenPubkey::new(&[2; 32]);
|
|
||||||
let mut account_data = vec![0; Account::get_packed_len()];
|
|
||||||
Account::unpack_unchecked_mut(&mut account_data, &mut |account: &mut Account| {
|
|
||||||
account.mint = mint_pubkey;
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let expected_mint_pubkey = Pubkey::new(&[2; 32]);
|
|
||||||
assert_eq!(
|
|
||||||
get_token_account_mint(&account_data),
|
|
||||||
Some(expected_mint_pubkey)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
use crate::{parse_account_data::ParseAccountError, StringAmount};
|
|
||||||
use solana_sdk::{
|
|
||||||
clock::{Epoch, Slot},
|
|
||||||
pubkey::Pubkey,
|
|
||||||
};
|
|
||||||
use solana_vote_program::vote_state::{BlockTimestamp, Lockout, VoteState};
|
|
||||||
|
|
||||||
pub fn parse_vote(data: &[u8]) -> Result<VoteAccountType, ParseAccountError> {
|
|
||||||
let mut vote_state = VoteState::deserialize(data).map_err(ParseAccountError::from)?;
|
|
||||||
let epoch_credits = vote_state
|
|
||||||
.epoch_credits()
|
|
||||||
.iter()
|
|
||||||
.map(|(epoch, credits, previous_credits)| UiEpochCredits {
|
|
||||||
epoch: *epoch,
|
|
||||||
credits: credits.to_string(),
|
|
||||||
previous_credits: previous_credits.to_string(),
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let votes = vote_state
|
|
||||||
.votes
|
|
||||||
.iter()
|
|
||||||
.map(|lockout| UiLockout {
|
|
||||||
slot: lockout.slot,
|
|
||||||
confirmation_count: lockout.confirmation_count,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let authorized_voters = vote_state
|
|
||||||
.authorized_voters()
|
|
||||||
.iter()
|
|
||||||
.map(|(epoch, authorized_voter)| UiAuthorizedVoters {
|
|
||||||
epoch: *epoch,
|
|
||||||
authorized_voter: authorized_voter.to_string(),
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let prior_voters = vote_state
|
|
||||||
.prior_voters()
|
|
||||||
.buf()
|
|
||||||
.iter()
|
|
||||||
.filter(|(pubkey, _, _)| pubkey != &Pubkey::default())
|
|
||||||
.map(
|
|
||||||
|(authorized_pubkey, epoch_of_last_authorized_switch, target_epoch)| UiPriorVoters {
|
|
||||||
authorized_pubkey: authorized_pubkey.to_string(),
|
|
||||||
epoch_of_last_authorized_switch: *epoch_of_last_authorized_switch,
|
|
||||||
target_epoch: *target_epoch,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.collect();
|
|
||||||
Ok(VoteAccountType::Vote(UiVoteState {
|
|
||||||
node_pubkey: vote_state.node_pubkey.to_string(),
|
|
||||||
authorized_withdrawer: vote_state.authorized_withdrawer.to_string(),
|
|
||||||
commission: vote_state.commission,
|
|
||||||
votes,
|
|
||||||
root_slot: vote_state.root_slot,
|
|
||||||
authorized_voters,
|
|
||||||
prior_voters,
|
|
||||||
epoch_credits,
|
|
||||||
last_timestamp: vote_state.last_timestamp,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A wrapper enum for consistency across programs
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase", tag = "type", content = "info")]
|
|
||||||
pub enum VoteAccountType {
|
|
||||||
Vote(UiVoteState),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A duplicate representation of VoteState for pretty JSON serialization
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Default, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UiVoteState {
|
|
||||||
node_pubkey: String,
|
|
||||||
authorized_withdrawer: String,
|
|
||||||
commission: u8,
|
|
||||||
votes: Vec<UiLockout>,
|
|
||||||
root_slot: Option<Slot>,
|
|
||||||
authorized_voters: Vec<UiAuthorizedVoters>,
|
|
||||||
prior_voters: Vec<UiPriorVoters>,
|
|
||||||
epoch_credits: Vec<UiEpochCredits>,
|
|
||||||
last_timestamp: BlockTimestamp,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct UiLockout {
|
|
||||||
slot: Slot,
|
|
||||||
confirmation_count: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&Lockout> for UiLockout {
|
|
||||||
fn from(lockout: &Lockout) -> Self {
|
|
||||||
Self {
|
|
||||||
slot: lockout.slot,
|
|
||||||
confirmation_count: lockout.confirmation_count,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct UiAuthorizedVoters {
|
|
||||||
epoch: Epoch,
|
|
||||||
authorized_voter: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct UiPriorVoters {
|
|
||||||
authorized_pubkey: String,
|
|
||||||
epoch_of_last_authorized_switch: Epoch,
|
|
||||||
target_epoch: Epoch,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct UiEpochCredits {
|
|
||||||
epoch: Epoch,
|
|
||||||
credits: StringAmount,
|
|
||||||
previous_credits: StringAmount,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
use solana_vote_program::vote_state::VoteStateVersions;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_parse_vote() {
|
|
||||||
let vote_state = VoteState::default();
|
|
||||||
let mut vote_account_data: Vec<u8> = vec![0; VoteState::size_of()];
|
|
||||||
let versioned = VoteStateVersions::Current(Box::new(vote_state));
|
|
||||||
VoteState::serialize(&versioned, &mut vote_account_data).unwrap();
|
|
||||||
let mut expected_vote_state = UiVoteState::default();
|
|
||||||
expected_vote_state.node_pubkey = Pubkey::default().to_string();
|
|
||||||
expected_vote_state.authorized_withdrawer = Pubkey::default().to_string();
|
|
||||||
assert_eq!(
|
|
||||||
parse_vote(&vote_account_data).unwrap(),
|
|
||||||
VoteAccountType::Vote(expected_vote_state)
|
|
||||||
);
|
|
||||||
|
|
||||||
let bad_data = vec![0; 4];
|
|
||||||
assert!(parse_vote(&bad_data).is_err());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
use solana_config_program::ConfigState;
|
|
||||||
|
|
||||||
pub const MAX_SHORT_FIELD_LENGTH: usize = 70;
|
|
||||||
pub const MAX_LONG_FIELD_LENGTH: usize = 300;
|
|
||||||
pub const MAX_VALIDATOR_INFO: u64 = 576;
|
|
||||||
|
|
||||||
solana_sdk::declare_id!("Va1idator1nfo111111111111111111111111111111");
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq, Serialize, Default)]
|
|
||||||
pub struct ValidatorInfo {
|
|
||||||
pub info: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConfigState for ValidatorInfo {
|
|
||||||
fn max_space() -> u64 {
|
|
||||||
MAX_VALIDATOR_INFO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +1,19 @@
|
|||||||
[package]
|
[package]
|
||||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "solana-accounts-bench"
|
name = "solana-accounts-bench"
|
||||||
version = "1.3.6"
|
version = "1.2.0"
|
||||||
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]
|
||||||
log = "0.4.6"
|
log = "0.4.6"
|
||||||
rayon = "1.3.1"
|
rayon = "1.3.0"
|
||||||
solana-logger = { path = "../logger", version = "1.3.6" }
|
solana-logger = { path = "../logger", version = "1.2.0" }
|
||||||
solana-runtime = { path = "../runtime", version = "1.3.6" }
|
solana-runtime = { path = "../runtime", version = "1.2.0" }
|
||||||
solana-measure = { path = "../measure", version = "1.3.6" }
|
solana-measure = { path = "../measure", version = "1.2.0" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.3.6" }
|
solana-sdk = { path = "../sdk", version = "1.2.0" }
|
||||||
rand = "0.7.0"
|
rand = "0.7.0"
|
||||||
clap = "2.33.1"
|
clap = "2.33.1"
|
||||||
crossbeam-channel = "0.4"
|
crossbeam-channel = "0.4"
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "solana-banking-bench"
|
name = "solana-banking-bench"
|
||||||
version = "1.3.6"
|
version = "1.2.0"
|
||||||
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/"
|
||||||
@@ -12,17 +12,17 @@ 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.3.1"
|
rayon = "1.3.0"
|
||||||
solana-core = { path = "../core", version = "1.3.6" }
|
solana-core = { path = "../core", version = "1.2.0" }
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "1.3.6" }
|
solana-clap-utils = { path = "../clap-utils", version = "1.2.0" }
|
||||||
solana-streamer = { path = "../streamer", version = "1.3.6" }
|
solana-streamer = { path = "../streamer", version = "1.2.0" }
|
||||||
solana-perf = { path = "../perf", version = "1.3.6" }
|
solana-perf = { path = "../perf", version = "1.2.0" }
|
||||||
solana-ledger = { path = "../ledger", version = "1.3.6" }
|
solana-ledger = { path = "../ledger", version = "1.2.0" }
|
||||||
solana-logger = { path = "../logger", version = "1.3.6" }
|
solana-logger = { path = "../logger", version = "1.2.0" }
|
||||||
solana-runtime = { path = "../runtime", version = "1.3.6" }
|
solana-runtime = { path = "../runtime", version = "1.2.0" }
|
||||||
solana-measure = { path = "../measure", version = "1.3.6" }
|
solana-measure = { path = "../measure", version = "1.2.0" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.3.6" }
|
solana-sdk = { path = "../sdk", version = "1.2.0" }
|
||||||
solana-version = { path = "../version", version = "1.3.6" }
|
solana-version = { path = "../version", version = "1.2.0" }
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
targets = ["x86_64-unknown-linux-gnu"]
|
targets = ["x86_64-unknown-linux-gnu"]
|
||||||
|
|||||||
@@ -11,13 +11,14 @@ use solana_core::{
|
|||||||
poh_recorder::WorkingBankEntry,
|
poh_recorder::WorkingBankEntry,
|
||||||
};
|
};
|
||||||
use solana_ledger::{
|
use solana_ledger::{
|
||||||
|
bank_forks::BankForks,
|
||||||
blockstore::Blockstore,
|
blockstore::Blockstore,
|
||||||
genesis_utils::{create_genesis_config, GenesisConfigInfo},
|
genesis_utils::{create_genesis_config, GenesisConfigInfo},
|
||||||
get_tmp_ledger_path,
|
get_tmp_ledger_path,
|
||||||
};
|
};
|
||||||
use solana_measure::measure::Measure;
|
use solana_measure::measure::Measure;
|
||||||
use solana_perf::packet::to_packets_chunked;
|
use solana_perf::packet::to_packets_chunked;
|
||||||
use solana_runtime::{bank::Bank, bank_forks::BankForks};
|
use solana_runtime::bank::Bank;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
@@ -167,9 +168,8 @@ fn main() {
|
|||||||
|
|
||||||
let (verified_sender, verified_receiver) = unbounded();
|
let (verified_sender, verified_receiver) = unbounded();
|
||||||
let (vote_sender, vote_receiver) = unbounded();
|
let (vote_sender, vote_receiver) = unbounded();
|
||||||
let (replay_vote_sender, _replay_vote_receiver) = unbounded();
|
|
||||||
let bank0 = Bank::new(&genesis_config);
|
let bank0 = Bank::new(&genesis_config);
|
||||||
let mut bank_forks = BankForks::new(bank0);
|
let mut bank_forks = BankForks::new(0, bank0);
|
||||||
let mut bank = bank_forks.working_bank();
|
let mut bank = bank_forks.working_bank();
|
||||||
|
|
||||||
info!("threads: {} txs: {}", num_threads, total_num_transactions);
|
info!("threads: {} txs: {}", num_threads, total_num_transactions);
|
||||||
@@ -209,7 +209,7 @@ fn main() {
|
|||||||
bank.clear_signatures();
|
bank.clear_signatures();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut verified: Vec<_> = to_packets_chunked(&transactions, packets_per_chunk);
|
let mut verified: Vec<_> = to_packets_chunked(&transactions.clone(), packets_per_chunk);
|
||||||
let ledger_path = get_tmp_ledger_path!();
|
let ledger_path = get_tmp_ledger_path!();
|
||||||
{
|
{
|
||||||
let blockstore = Arc::new(
|
let blockstore = Arc::new(
|
||||||
@@ -225,7 +225,6 @@ fn main() {
|
|||||||
verified_receiver,
|
verified_receiver,
|
||||||
vote_receiver,
|
vote_receiver,
|
||||||
None,
|
None,
|
||||||
replay_vote_sender,
|
|
||||||
);
|
);
|
||||||
poh_recorder.lock().unwrap().set_bank(&bank);
|
poh_recorder.lock().unwrap().set_bank(&bank);
|
||||||
|
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "solana-banks-client"
|
|
||||||
version = "1.3.6"
|
|
||||||
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.3.6" }
|
|
||||||
solana-sdk = { path = "../sdk", version = "1.3.6" }
|
|
||||||
tarpc = { version = "0.21.0", features = ["full"] }
|
|
||||||
tokio = "0.2"
|
|
||||||
tokio-serde = { version = "0.6", features = ["bincode"] }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
solana-runtime = { path = "../runtime", version = "1.3.6" }
|
|
||||||
solana-banks-server = { path = "../banks-server", version = "1.3.6" }
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
crate-type = ["lib"]
|
|
||||||
name = "solana_banks_client"
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
|
||||||
targets = ["x86_64-unknown-linux-gnu"]
|
|
||||||
@@ -1,283 +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::Account, clock::Slot, commitment_config::CommitmentLevel,
|
|
||||||
fee_calculator::FeeCalculator, hash::Hash, pubkey::Pubkey, signature::Signature,
|
|
||||||
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)>;
|
|
||||||
|
|
||||||
/// 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 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_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(&mut self, address: Pubkey) -> io::Result<Option<Account>> {
|
|
||||||
self.get_account_with_commitment_and_context(
|
|
||||||
context::current(),
|
|
||||||
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, pubkey::Pubkey, signature::Signer, system_instruction};
|
|
||||||
use std::sync::{Arc, RwLock};
|
|
||||||
use tarpc::transport;
|
|
||||||
use tokio::{runtime::Runtime, time::delay_for};
|
|
||||||
|
|
||||||
#[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 = 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 = 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;
|
|
||||||
}
|
|
||||||
delay_for(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(())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "solana-banks-interface"
|
|
||||||
version = "1.3.6"
|
|
||||||
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.3.6" }
|
|
||||||
tarpc = { version = "0.21.0", features = ["full"] }
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
crate-type = ["lib"]
|
|
||||||
name = "solana_banks_interface"
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
|
||||||
targets = ["x86_64-unknown-linux-gnu"]
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "solana-banks-server"
|
|
||||||
version = "1.3.6"
|
|
||||||
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"
|
|
||||||
solana-banks-interface = { path = "../banks-interface", version = "1.3.6" }
|
|
||||||
solana-runtime = { path = "../runtime", version = "1.3.6" }
|
|
||||||
solana-sdk = { path = "../sdk", version = "1.3.6" }
|
|
||||||
tarpc = { version = "0.21.0", features = ["full"] }
|
|
||||||
tokio = "0.2"
|
|
||||||
tokio-serde = { version = "0.6", features = ["bincode"] }
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
crate-type = ["lib"]
|
|
||||||
name = "solana_banks_server"
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
|
||||||
targets = ["x86_64-unknown-linux-gnu"]
|
|
||||||
@@ -1,272 +0,0 @@
|
|||||||
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, CommitmentSlots},
|
|
||||||
send_transaction_service::{SendTransactionService, TransactionInfo},
|
|
||||||
};
|
|
||||||
use solana_sdk::{
|
|
||||||
account::Account,
|
|
||||||
clock::Slot,
|
|
||||||
commitment_config::CommitmentLevel,
|
|
||||||
fee_calculator::FeeCalculator,
|
|
||||||
hash::Hash,
|
|
||||||
pubkey::Pubkey,
|
|
||||||
signature::Signature,
|
|
||||||
transaction::{self, Transaction},
|
|
||||||
};
|
|
||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
io,
|
|
||||||
net::SocketAddr,
|
|
||||||
sync::{
|
|
||||||
atomic::AtomicBool,
|
|
||||||
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::delay_for;
|
|
||||||
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(
|
|
||||||
HashMap::default(),
|
|
||||||
0,
|
|
||||||
CommitmentSlots {
|
|
||||||
slot,
|
|
||||||
root: 0,
|
|
||||||
highest_confirmed_slot: 0,
|
|
||||||
highest_confirmed_root: 0,
|
|
||||||
},
|
|
||||||
)));
|
|
||||||
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,
|
|
||||||
last_valid_slot: Slot,
|
|
||||||
commitment: CommitmentLevel,
|
|
||||||
) -> Option<transaction::Result<()>> {
|
|
||||||
let mut status = self.bank(commitment).get_signature_status(&signature);
|
|
||||||
while status.is_none() {
|
|
||||||
delay_for(Duration::from_millis(200)).await;
|
|
||||||
let bank = self.bank(commitment);
|
|
||||||
if bank.slot() > last_valid_slot {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
status = bank.get_signature_status(&signature);
|
|
||||||
}
|
|
||||||
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, 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().unwrap().ip())
|
|
||||||
// serve is generated by the service attribute. It takes as input any type implementing
|
|
||||||
// the generated Banks trait.
|
|
||||||
.map(move |chan| {
|
|
||||||
let (sender, receiver) = channel();
|
|
||||||
let exit_send_transaction_service = Arc::new(AtomicBool::new(false));
|
|
||||||
|
|
||||||
SendTransactionService::new(
|
|
||||||
tpu_addr,
|
|
||||||
&bank_forks,
|
|
||||||
&exit_send_transaction_service,
|
|
||||||
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(())
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
pub mod banks_server;
|
|
||||||
pub mod rpc_banks_service;
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "solana-bench-exchange"
|
name = "solana-bench-exchange"
|
||||||
version = "1.3.6"
|
version = "1.2.0"
|
||||||
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.3.1"
|
rayon = "1.3.0"
|
||||||
serde_json = "1.0.56"
|
serde_json = "1.0.53"
|
||||||
serde_yaml = "0.8.13"
|
serde_yaml = "0.8.12"
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "1.3.6" }
|
solana-clap-utils = { path = "../clap-utils", version = "1.2.0" }
|
||||||
solana-core = { path = "../core", version = "1.3.6" }
|
solana-core = { path = "../core", version = "1.2.0" }
|
||||||
solana-genesis = { path = "../genesis", version = "1.3.6" }
|
solana-genesis = { path = "../genesis", version = "1.2.0" }
|
||||||
solana-client = { path = "../client", version = "1.3.6" }
|
solana-client = { path = "../client", version = "1.2.0" }
|
||||||
solana-faucet = { path = "../faucet", version = "1.3.6" }
|
solana-faucet = { path = "../faucet", version = "1.2.0" }
|
||||||
solana-exchange-program = { path = "../programs/exchange", version = "1.3.6" }
|
solana-exchange-program = { path = "../programs/exchange", version = "1.2.0" }
|
||||||
solana-logger = { path = "../logger", version = "1.3.6" }
|
solana-logger = { path = "../logger", version = "1.2.0" }
|
||||||
solana-metrics = { path = "../metrics", version = "1.3.6" }
|
solana-metrics = { path = "../metrics", version = "1.2.0" }
|
||||||
solana-net-utils = { path = "../net-utils", version = "1.3.6" }
|
solana-net-utils = { path = "../net-utils", version = "1.2.0" }
|
||||||
solana-runtime = { path = "../runtime", version = "1.3.6" }
|
solana-runtime = { path = "../runtime", version = "1.2.0" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.3.6" }
|
solana-sdk = { path = "../sdk", version = "1.2.0" }
|
||||||
solana-version = { path = "../version", version = "1.3.6" }
|
solana-version = { path = "../version", version = "1.2.0" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
solana-local-cluster = { path = "../local-cluster", version = "1.3.6" }
|
solana-local-cluster = { path = "../local-cluster", version = "1.2.0" }
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
targets = ["x86_64-unknown-linux-gnu"]
|
targets = ["x86_64-unknown-linux-gnu"]
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ use solana_metrics::datapoint_info;
|
|||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
client::{Client, SyncClient},
|
client::{Client, SyncClient},
|
||||||
commitment_config::CommitmentConfig,
|
commitment_config::CommitmentConfig,
|
||||||
message::Message,
|
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{Keypair, Signer},
|
signature::{Keypair, Signer},
|
||||||
timing::{duration_as_ms, duration_as_s},
|
timing::{duration_as_ms, duration_as_s},
|
||||||
@@ -179,13 +178,19 @@ where
|
|||||||
|
|
||||||
info!("Generating {:?} account keys", total_keys);
|
info!("Generating {:?} account keys", total_keys);
|
||||||
let mut account_keypairs = generate_keypairs(total_keys);
|
let mut account_keypairs = generate_keypairs(total_keys);
|
||||||
let src_keypairs: Vec<_> = account_keypairs.drain(0..accounts_in_groups).collect();
|
let src_keypairs: Vec<_> = account_keypairs
|
||||||
|
.drain(0..accounts_in_groups)
|
||||||
|
.map(|keypair| keypair)
|
||||||
|
.collect();
|
||||||
let src_pubkeys: Vec<Pubkey> = src_keypairs
|
let src_pubkeys: Vec<Pubkey> = src_keypairs
|
||||||
.iter()
|
.iter()
|
||||||
.map(|keypair| keypair.pubkey())
|
.map(|keypair| keypair.pubkey())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let profit_keypairs: Vec<_> = account_keypairs.drain(0..accounts_in_groups).collect();
|
let profit_keypairs: Vec<_> = account_keypairs
|
||||||
|
.drain(0..accounts_in_groups)
|
||||||
|
.map(|keypair| keypair)
|
||||||
|
.collect();
|
||||||
let profit_pubkeys: Vec<Pubkey> = profit_keypairs
|
let profit_pubkeys: Vec<Pubkey> = profit_keypairs
|
||||||
.iter()
|
.iter()
|
||||||
.map(|keypair| keypair.pubkey())
|
.map(|keypair| keypair.pubkey())
|
||||||
@@ -444,7 +449,7 @@ fn swapper<T>(
|
|||||||
}
|
}
|
||||||
account_group = (account_group + 1) % account_groups as usize;
|
account_group = (account_group + 1) % account_groups as usize;
|
||||||
|
|
||||||
let (blockhash, _fee_calculator, _last_valid_slot) = client
|
let (blockhash, _fee_calculator) = client
|
||||||
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
|
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
|
||||||
.expect("Failed to get blockhash");
|
.expect("Failed to get blockhash");
|
||||||
let to_swap_txs: Vec<_> = to_swap
|
let to_swap_txs: Vec<_> = to_swap
|
||||||
@@ -452,14 +457,16 @@ fn swapper<T>(
|
|||||||
.map(|(signer, swap, profit)| {
|
.map(|(signer, swap, profit)| {
|
||||||
let s: &Keypair = &signer;
|
let s: &Keypair = &signer;
|
||||||
let owner = &signer.pubkey();
|
let owner = &signer.pubkey();
|
||||||
let instruction = exchange_instruction::swap_request(
|
Transaction::new_signed_instructions(
|
||||||
|
&[s],
|
||||||
|
&[exchange_instruction::swap_request(
|
||||||
owner,
|
owner,
|
||||||
&swap.0.pubkey,
|
&swap.0.pubkey,
|
||||||
&swap.1.pubkey,
|
&swap.1.pubkey,
|
||||||
&profit,
|
&profit,
|
||||||
);
|
)],
|
||||||
let message = Message::new(&[instruction], Some(&s.pubkey()));
|
blockhash,
|
||||||
Transaction::new(&[s], message, blockhash)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@@ -570,7 +577,7 @@ fn trader<T>(
|
|||||||
}
|
}
|
||||||
account_group = (account_group + 1) % account_groups as usize;
|
account_group = (account_group + 1) % account_groups as usize;
|
||||||
|
|
||||||
let (blockhash, _fee_calculator, _last_valid_slot) = client
|
let (blockhash, _fee_calculator) = client
|
||||||
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
|
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
|
||||||
.expect("Failed to get blockhash");
|
.expect("Failed to get blockhash");
|
||||||
|
|
||||||
@@ -581,7 +588,9 @@ fn trader<T>(
|
|||||||
let owner_pubkey = &owner.pubkey();
|
let owner_pubkey = &owner.pubkey();
|
||||||
let trade_pubkey = &trade.pubkey();
|
let trade_pubkey = &trade.pubkey();
|
||||||
let space = mem::size_of::<ExchangeState>() as u64;
|
let space = mem::size_of::<ExchangeState>() as u64;
|
||||||
let instructions = [
|
Transaction::new_signed_instructions(
|
||||||
|
&[owner.as_ref(), trade],
|
||||||
|
&[
|
||||||
system_instruction::create_account(
|
system_instruction::create_account(
|
||||||
owner_pubkey,
|
owner_pubkey,
|
||||||
trade_pubkey,
|
trade_pubkey,
|
||||||
@@ -598,9 +607,9 @@ fn trader<T>(
|
|||||||
price,
|
price,
|
||||||
src,
|
src,
|
||||||
),
|
),
|
||||||
];
|
],
|
||||||
let message = Message::new(&instructions, Some(&owner_pubkey));
|
blockhash,
|
||||||
Transaction::new(&[owner.as_ref(), trade], message, blockhash)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@@ -738,9 +747,13 @@ pub fn fund_keys<T: Client>(client: &T, source: &Keypair, dests: &[Arc<Keypair>]
|
|||||||
let mut to_fund_txs: Vec<_> = chunk
|
let mut to_fund_txs: Vec<_> = chunk
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.map(|(k, m)| {
|
.map(|(k, m)| {
|
||||||
let instructions = system_instruction::transfer_many(&k.pubkey(), &m);
|
(
|
||||||
let message = Message::new(&instructions, Some(&k.pubkey()));
|
k.clone(),
|
||||||
(k.clone(), Transaction::new_unsigned(message))
|
Transaction::new_unsigned_instructions(&system_instruction::transfer_many(
|
||||||
|
&k.pubkey(),
|
||||||
|
&m,
|
||||||
|
)),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@@ -763,7 +776,7 @@ pub fn fund_keys<T: Client>(client: &T, source: &Keypair, dests: &[Arc<Keypair>]
|
|||||||
to_fund_txs.len(),
|
to_fund_txs.len(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (blockhash, _fee_calculator, _last_valid_slot) = client
|
let (blockhash, _fee_calculator) = client
|
||||||
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
|
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
|
||||||
.expect("blockhash");
|
.expect("blockhash");
|
||||||
to_fund_txs.par_iter_mut().for_each(|(k, tx)| {
|
to_fund_txs.par_iter_mut().for_each(|(k, tx)| {
|
||||||
@@ -835,10 +848,9 @@ pub fn create_token_accounts<T: Client>(
|
|||||||
);
|
);
|
||||||
let request_ix =
|
let request_ix =
|
||||||
exchange_instruction::account_request(owner_pubkey, &new_keypair.pubkey());
|
exchange_instruction::account_request(owner_pubkey, &new_keypair.pubkey());
|
||||||
let message = Message::new(&[create_ix, request_ix], Some(&owner_pubkey));
|
|
||||||
(
|
(
|
||||||
(from_keypair, new_keypair),
|
(from_keypair, new_keypair),
|
||||||
Transaction::new_unsigned(message),
|
Transaction::new_unsigned_instructions(&[create_ix, request_ix]),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@@ -856,7 +868,7 @@ pub fn create_token_accounts<T: Client>(
|
|||||||
|
|
||||||
let mut retries = 0;
|
let mut retries = 0;
|
||||||
while !to_create_txs.is_empty() {
|
while !to_create_txs.is_empty() {
|
||||||
let (blockhash, _fee_calculator, _last_valid_slot) = client
|
let (blockhash, _fee_calculator) = client
|
||||||
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
|
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
|
||||||
.expect("Failed to get blockhash");
|
.expect("Failed to get blockhash");
|
||||||
to_create_txs
|
to_create_txs
|
||||||
@@ -985,7 +997,7 @@ pub fn airdrop_lamports<T: Client>(
|
|||||||
|
|
||||||
let mut tries = 0;
|
let mut tries = 0;
|
||||||
loop {
|
loop {
|
||||||
let (blockhash, _fee_calculator, _last_valid_slot) = client
|
let (blockhash, _fee_calculator) = client
|
||||||
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
|
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
|
||||||
.expect("Failed to get blockhash");
|
.expect("Failed to get blockhash");
|
||||||
match request_airdrop_transaction(&faucet_addr, &id.pubkey(), amount_to_drop, blockhash) {
|
match request_airdrop_transaction(&faucet_addr, &id.pubkey(), amount_to_drop, blockhash) {
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
[package]
|
[package]
|
||||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "solana-bench-streamer"
|
name = "solana-bench-streamer"
|
||||||
version = "1.3.6"
|
version = "1.2.0"
|
||||||
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]
|
||||||
clap = "2.33.1"
|
clap = "2.33.1"
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "1.3.6" }
|
solana-clap-utils = { path = "../clap-utils", version = "1.2.0" }
|
||||||
solana-streamer = { path = "../streamer", version = "1.3.6" }
|
solana-streamer = { path = "../streamer", version = "1.2.0" }
|
||||||
solana-logger = { path = "../logger", version = "1.3.6" }
|
solana-logger = { path = "../logger", version = "1.2.0" }
|
||||||
solana-net-utils = { path = "../net-utils", version = "1.3.6" }
|
solana-net-utils = { path = "../net-utils", version = "1.2.0" }
|
||||||
solana-version = { path = "../version", version = "1.3.6" }
|
solana-version = { path = "../version", version = "1.2.0" }
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
targets = ["x86_64-unknown-linux-gnu"]
|
targets = ["x86_64-unknown-linux-gnu"]
|
||||||
|
|||||||
@@ -1,36 +1,41 @@
|
|||||||
[package]
|
[package]
|
||||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "solana-bench-tps"
|
name = "solana-bench-tps"
|
||||||
version = "1.3.6"
|
version = "1.2.0"
|
||||||
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]
|
||||||
bincode = "1.3.1"
|
bincode = "1.2.1"
|
||||||
clap = "2.33.1"
|
clap = "2.33.1"
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
rayon = "1.3.1"
|
rayon = "1.3.0"
|
||||||
serde_json = "1.0.56"
|
serde_json = "1.0.53"
|
||||||
serde_yaml = "0.8.13"
|
serde_yaml = "0.8.12"
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "1.3.6" }
|
solana-clap-utils = { path = "../clap-utils", version = "1.2.0" }
|
||||||
solana-core = { path = "../core", version = "1.3.6" }
|
solana-core = { path = "../core", version = "1.2.0" }
|
||||||
solana-genesis = { path = "../genesis", version = "1.3.6" }
|
solana-genesis = { path = "../genesis", version = "1.2.0" }
|
||||||
solana-client = { path = "../client", version = "1.3.6" }
|
solana-client = { path = "../client", version = "1.2.0" }
|
||||||
solana-faucet = { path = "../faucet", version = "1.3.6" }
|
solana-faucet = { path = "../faucet", version = "1.2.0" }
|
||||||
solana-logger = { path = "../logger", version = "1.3.6" }
|
solana-librapay = { path = "../programs/librapay", version = "1.2.0", optional = true }
|
||||||
solana-metrics = { path = "../metrics", version = "1.3.6" }
|
solana-logger = { path = "../logger", version = "1.2.0" }
|
||||||
solana-measure = { path = "../measure", version = "1.3.6" }
|
solana-metrics = { path = "../metrics", version = "1.2.0" }
|
||||||
solana-net-utils = { path = "../net-utils", version = "1.3.6" }
|
solana-measure = { path = "../measure", version = "1.2.0" }
|
||||||
solana-runtime = { path = "../runtime", version = "1.3.6" }
|
solana-net-utils = { path = "../net-utils", version = "1.2.0" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.3.6" }
|
solana-runtime = { path = "../runtime", version = "1.2.0" }
|
||||||
solana-version = { path = "../version", version = "1.3.6" }
|
solana-sdk = { path = "../sdk", version = "1.2.0" }
|
||||||
|
solana-move-loader-program = { path = "../programs/move_loader", version = "1.2.0", optional = true }
|
||||||
|
solana-version = { path = "../version", version = "1.2.0" }
|
||||||
|
|
||||||
[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.3.6" }
|
solana-local-cluster = { path = "../local-cluster", version = "1.2.0" }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
move = ["solana-librapay", "solana-move-loader-program"]
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
targets = ["x86_64-unknown-linux-gnu"]
|
targets = ["x86_64-unknown-linux-gnu"]
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ use rayon::prelude::*;
|
|||||||
use solana_client::perf_utils::{sample_txs, SampleStats};
|
use solana_client::perf_utils::{sample_txs, SampleStats};
|
||||||
use solana_core::gen_keys::GenKeys;
|
use solana_core::gen_keys::GenKeys;
|
||||||
use solana_faucet::faucet::request_airdrop_transaction;
|
use solana_faucet::faucet::request_airdrop_transaction;
|
||||||
|
#[cfg(feature = "move")]
|
||||||
|
use solana_librapay::{create_genesis, upload_mint_script, upload_payment_script};
|
||||||
use solana_measure::measure::Measure;
|
use solana_measure::measure::Measure;
|
||||||
use solana_metrics::{self, datapoint_info};
|
use solana_metrics::{self, datapoint_info};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
@@ -12,7 +14,6 @@ use solana_sdk::{
|
|||||||
commitment_config::CommitmentConfig,
|
commitment_config::CommitmentConfig,
|
||||||
fee_calculator::FeeCalculator,
|
fee_calculator::FeeCalculator,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
message::Message,
|
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{Keypair, Signer},
|
signature::{Keypair, Signer},
|
||||||
system_instruction, system_transaction,
|
system_instruction, system_transaction,
|
||||||
@@ -35,6 +36,9 @@ use std::{
|
|||||||
const MAX_TX_QUEUE_AGE: u64 =
|
const MAX_TX_QUEUE_AGE: u64 =
|
||||||
MAX_PROCESSING_AGE as u64 * DEFAULT_TICKS_PER_SLOT / DEFAULT_TICKS_PER_SECOND;
|
MAX_PROCESSING_AGE as u64 * DEFAULT_TICKS_PER_SLOT / DEFAULT_TICKS_PER_SECOND;
|
||||||
|
|
||||||
|
#[cfg(feature = "move")]
|
||||||
|
use solana_librapay::librapay_transaction;
|
||||||
|
|
||||||
pub const MAX_SPENDS_PER_TX: u64 = 4;
|
pub const MAX_SPENDS_PER_TX: u64 = 4;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -46,12 +50,12 @@ pub type Result<T> = std::result::Result<T, BenchTpsError>;
|
|||||||
|
|
||||||
pub type SharedTransactions = Arc<RwLock<VecDeque<Vec<(Transaction, u64)>>>>;
|
pub type SharedTransactions = Arc<RwLock<VecDeque<Vec<(Transaction, u64)>>>>;
|
||||||
|
|
||||||
|
type LibraKeys = (Keypair, Pubkey, Pubkey, Vec<Keypair>);
|
||||||
|
|
||||||
fn get_recent_blockhash<T: Client>(client: &T) -> (Hash, FeeCalculator) {
|
fn get_recent_blockhash<T: Client>(client: &T) -> (Hash, FeeCalculator) {
|
||||||
loop {
|
loop {
|
||||||
match client.get_recent_blockhash_with_commitment(CommitmentConfig::recent()) {
|
match client.get_recent_blockhash_with_commitment(CommitmentConfig::recent()) {
|
||||||
Ok((blockhash, fee_calculator, _last_valid_slot)) => {
|
Ok((blockhash, fee_calculator)) => return (blockhash, fee_calculator),
|
||||||
return (blockhash, fee_calculator)
|
|
||||||
}
|
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
info!("Couldn't get recent blockhash: {:?}", err);
|
info!("Couldn't get recent blockhash: {:?}", err);
|
||||||
sleep(Duration::from_secs(1));
|
sleep(Duration::from_secs(1));
|
||||||
@@ -115,6 +119,7 @@ fn generate_chunked_transfers(
|
|||||||
threads: usize,
|
threads: usize,
|
||||||
duration: Duration,
|
duration: Duration,
|
||||||
sustained: bool,
|
sustained: bool,
|
||||||
|
libra_args: Option<LibraKeys>,
|
||||||
) {
|
) {
|
||||||
// generate and send transactions for the specified duration
|
// generate and send transactions for the specified duration
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
@@ -129,6 +134,7 @@ fn generate_chunked_transfers(
|
|||||||
&dest_keypair_chunks[chunk_index],
|
&dest_keypair_chunks[chunk_index],
|
||||||
threads,
|
threads,
|
||||||
reclaim_lamports_back_to_source_account,
|
reclaim_lamports_back_to_source_account,
|
||||||
|
&libra_args,
|
||||||
);
|
);
|
||||||
|
|
||||||
// In sustained mode, overlap the transfers with generation. This has higher average
|
// In sustained mode, overlap the transfers with generation. This has higher average
|
||||||
@@ -196,7 +202,12 @@ where
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn do_bench_tps<T>(client: Arc<T>, config: Config, gen_keypairs: Vec<Keypair>) -> u64
|
pub fn do_bench_tps<T>(
|
||||||
|
client: Arc<T>,
|
||||||
|
config: Config,
|
||||||
|
gen_keypairs: Vec<Keypair>,
|
||||||
|
libra_args: Option<LibraKeys>,
|
||||||
|
) -> u64
|
||||||
where
|
where
|
||||||
T: 'static + Client + Send + Sync,
|
T: 'static + Client + Send + Sync,
|
||||||
{
|
{
|
||||||
@@ -280,6 +291,7 @@ where
|
|||||||
threads,
|
threads,
|
||||||
duration,
|
duration,
|
||||||
sustained,
|
sustained,
|
||||||
|
libra_args,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Stop the sampling threads so it will collect the stats
|
// Stop the sampling threads so it will collect the stats
|
||||||
@@ -325,6 +337,52 @@ fn metrics_submit_lamport_balance(lamport_balance: u64) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "move")]
|
||||||
|
fn generate_move_txs(
|
||||||
|
source: &[&Keypair],
|
||||||
|
dest: &VecDeque<&Keypair>,
|
||||||
|
reclaim: bool,
|
||||||
|
move_keypairs: &[Keypair],
|
||||||
|
libra_pay_program_id: &Pubkey,
|
||||||
|
libra_mint_id: &Pubkey,
|
||||||
|
blockhash: &Hash,
|
||||||
|
) -> Vec<(Transaction, u64)> {
|
||||||
|
let count = move_keypairs.len() / 2;
|
||||||
|
let source_move = &move_keypairs[..count];
|
||||||
|
let dest_move = &move_keypairs[count..];
|
||||||
|
let pairs: Vec<_> = if !reclaim {
|
||||||
|
source_move
|
||||||
|
.iter()
|
||||||
|
.zip(dest_move.iter())
|
||||||
|
.zip(source.iter())
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
dest_move
|
||||||
|
.iter()
|
||||||
|
.zip(source_move.iter())
|
||||||
|
.zip(dest.iter())
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
|
||||||
|
pairs
|
||||||
|
.par_iter()
|
||||||
|
.map(|((from, to), payer)| {
|
||||||
|
(
|
||||||
|
librapay_transaction::transfer(
|
||||||
|
libra_pay_program_id,
|
||||||
|
libra_mint_id,
|
||||||
|
&payer,
|
||||||
|
&from,
|
||||||
|
&to.pubkey(),
|
||||||
|
1,
|
||||||
|
*blockhash,
|
||||||
|
),
|
||||||
|
timestamp(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
fn generate_system_txs(
|
fn generate_system_txs(
|
||||||
source: &[&Keypair],
|
source: &[&Keypair],
|
||||||
dest: &VecDeque<&Keypair>,
|
dest: &VecDeque<&Keypair>,
|
||||||
@@ -355,6 +413,7 @@ fn generate_txs(
|
|||||||
dest: &VecDeque<&Keypair>,
|
dest: &VecDeque<&Keypair>,
|
||||||
threads: usize,
|
threads: usize,
|
||||||
reclaim: bool,
|
reclaim: bool,
|
||||||
|
libra_args: &Option<LibraKeys>,
|
||||||
) {
|
) {
|
||||||
let blockhash = *blockhash.read().unwrap();
|
let blockhash = *blockhash.read().unwrap();
|
||||||
let tx_count = source.len();
|
let tx_count = source.len();
|
||||||
@@ -364,7 +423,33 @@ fn generate_txs(
|
|||||||
);
|
);
|
||||||
let signing_start = Instant::now();
|
let signing_start = Instant::now();
|
||||||
|
|
||||||
let transactions = generate_system_txs(source, dest, reclaim, &blockhash);
|
let transactions = if let Some((
|
||||||
|
_libra_genesis_keypair,
|
||||||
|
_libra_pay_program_id,
|
||||||
|
_libra_mint_program_id,
|
||||||
|
_libra_keys,
|
||||||
|
)) = libra_args
|
||||||
|
{
|
||||||
|
#[cfg(not(feature = "move"))]
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "move")]
|
||||||
|
{
|
||||||
|
generate_move_txs(
|
||||||
|
source,
|
||||||
|
dest,
|
||||||
|
reclaim,
|
||||||
|
&_libra_keys,
|
||||||
|
_libra_pay_program_id,
|
||||||
|
&_libra_genesis_keypair.pubkey(),
|
||||||
|
&blockhash,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
generate_system_txs(source, dest, reclaim, &blockhash)
|
||||||
|
};
|
||||||
|
|
||||||
let duration = signing_start.elapsed();
|
let duration = signing_start.elapsed();
|
||||||
let ns = duration.as_secs() * 1_000_000_000 + u64::from(duration.subsec_nanos());
|
let ns = duration.as_secs() * 1_000_000_000 + u64::from(duration.subsec_nanos());
|
||||||
@@ -565,9 +650,10 @@ impl<'a> FundingTransactions<'a> for Vec<(&'a Keypair, Transaction)> {
|
|||||||
let to_fund_txs: Vec<(&Keypair, Transaction)> = to_fund
|
let to_fund_txs: Vec<(&Keypair, Transaction)> = to_fund
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.map(|(k, t)| {
|
.map(|(k, t)| {
|
||||||
let instructions = system_instruction::transfer_many(&k.pubkey(), &t);
|
let tx = Transaction::new_unsigned_instructions(
|
||||||
let message = Message::new(&instructions, Some(&k.pubkey()));
|
&system_instruction::transfer_many(&k.pubkey(), &t),
|
||||||
(*k, Transaction::new_unsigned(message))
|
);
|
||||||
|
(*k, tx)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
make_txs.stop();
|
make_txs.stop();
|
||||||
@@ -866,13 +952,181 @@ pub fn generate_keypairs(seed_keypair: &Keypair, count: u64) -> (Vec<Keypair>, u
|
|||||||
(rnd.gen_n_keypairs(total_keys), extra)
|
(rnd.gen_n_keypairs(total_keys), extra)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "move")]
|
||||||
|
fn fund_move_keys<T: Client>(
|
||||||
|
client: &T,
|
||||||
|
funding_key: &Keypair,
|
||||||
|
keypairs: &[Keypair],
|
||||||
|
total: u64,
|
||||||
|
libra_pay_program_id: &Pubkey,
|
||||||
|
libra_mint_program_id: &Pubkey,
|
||||||
|
libra_genesis_key: &Keypair,
|
||||||
|
) {
|
||||||
|
let (mut blockhash, _fee_calculator) = get_recent_blockhash(client);
|
||||||
|
|
||||||
|
info!("creating the libra funding account..");
|
||||||
|
let libra_funding_key = Keypair::new();
|
||||||
|
let tx = librapay_transaction::create_account(funding_key, &libra_funding_key, 1, blockhash);
|
||||||
|
client
|
||||||
|
.send_message(&[funding_key, &libra_funding_key], tx.message)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
info!("minting to funding keypair");
|
||||||
|
let tx = librapay_transaction::mint_tokens(
|
||||||
|
&libra_mint_program_id,
|
||||||
|
funding_key,
|
||||||
|
libra_genesis_key,
|
||||||
|
&libra_funding_key.pubkey(),
|
||||||
|
total,
|
||||||
|
blockhash,
|
||||||
|
);
|
||||||
|
client
|
||||||
|
.send_message(&[funding_key, libra_genesis_key], tx.message)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
info!("creating {} move accounts...", keypairs.len());
|
||||||
|
let total_len = keypairs.len();
|
||||||
|
let create_len = 5;
|
||||||
|
let mut funding_time = Measure::start("funding_time");
|
||||||
|
for (i, keys) in keypairs.chunks(create_len).enumerate() {
|
||||||
|
if client
|
||||||
|
.get_balance_with_commitment(&keys[0].pubkey(), CommitmentConfig::recent())
|
||||||
|
.unwrap_or(0)
|
||||||
|
> 0
|
||||||
|
{
|
||||||
|
// already created these accounts.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let keypairs: Vec<_> = keys.iter().map(|k| k).collect();
|
||||||
|
let tx = librapay_transaction::create_accounts(funding_key, &keypairs, 1, blockhash);
|
||||||
|
let ser_size = bincode::serialized_size(&tx).unwrap();
|
||||||
|
let mut keys = vec![funding_key];
|
||||||
|
keys.extend(&keypairs);
|
||||||
|
client.send_message(&keys, tx.message).unwrap();
|
||||||
|
|
||||||
|
if i % 10 == 0 {
|
||||||
|
info!(
|
||||||
|
"created {} accounts of {} (size {})",
|
||||||
|
i,
|
||||||
|
total_len / create_len,
|
||||||
|
ser_size,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const NUM_FUNDING_KEYS: usize = 10;
|
||||||
|
let funding_keys: Vec<_> = (0..NUM_FUNDING_KEYS).map(|_| Keypair::new()).collect();
|
||||||
|
let pubkey_amounts: Vec<_> = funding_keys
|
||||||
|
.iter()
|
||||||
|
.map(|key| (key.pubkey(), total / NUM_FUNDING_KEYS as u64))
|
||||||
|
.collect();
|
||||||
|
let tx = Transaction::new_signed_instructions(
|
||||||
|
&[funding_key],
|
||||||
|
&system_instruction::transfer_many(&funding_key.pubkey(), &pubkey_amounts),
|
||||||
|
blockhash,
|
||||||
|
);
|
||||||
|
client.send_message(&[funding_key], tx.message).unwrap();
|
||||||
|
let mut balance = 0;
|
||||||
|
for _ in 0..20 {
|
||||||
|
if let Ok(balance_) = client
|
||||||
|
.get_balance_with_commitment(&funding_keys[0].pubkey(), CommitmentConfig::recent())
|
||||||
|
{
|
||||||
|
if balance_ > 0 {
|
||||||
|
balance = balance_;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sleep(Duration::from_millis(100));
|
||||||
|
}
|
||||||
|
assert!(balance > 0);
|
||||||
|
info!(
|
||||||
|
"funded multiple funding accounts with {:?} lanports",
|
||||||
|
balance
|
||||||
|
);
|
||||||
|
|
||||||
|
let libra_funding_keys: Vec<_> = (0..NUM_FUNDING_KEYS).map(|_| Keypair::new()).collect();
|
||||||
|
for (i, key) in libra_funding_keys.iter().enumerate() {
|
||||||
|
let tx = librapay_transaction::create_account(&funding_keys[i], &key, 1, blockhash);
|
||||||
|
client
|
||||||
|
.send_message(&[&funding_keys[i], &key], tx.message)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let tx = librapay_transaction::transfer(
|
||||||
|
libra_pay_program_id,
|
||||||
|
&libra_genesis_key.pubkey(),
|
||||||
|
&funding_keys[i],
|
||||||
|
&libra_funding_key,
|
||||||
|
&key.pubkey(),
|
||||||
|
total / NUM_FUNDING_KEYS as u64,
|
||||||
|
blockhash,
|
||||||
|
);
|
||||||
|
client
|
||||||
|
.send_message(&[&funding_keys[i], &libra_funding_key], tx.message)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
info!("funded libra funding key {}", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
let keypair_count = keypairs.len();
|
||||||
|
let amount = total / (keypair_count as u64);
|
||||||
|
for (i, keys) in keypairs[..keypair_count]
|
||||||
|
.chunks(NUM_FUNDING_KEYS)
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
for (j, key) in keys.iter().enumerate() {
|
||||||
|
let tx = librapay_transaction::transfer(
|
||||||
|
libra_pay_program_id,
|
||||||
|
&libra_genesis_key.pubkey(),
|
||||||
|
&funding_keys[j],
|
||||||
|
&libra_funding_keys[j],
|
||||||
|
&key.pubkey(),
|
||||||
|
amount,
|
||||||
|
blockhash,
|
||||||
|
);
|
||||||
|
|
||||||
|
let _sig = client
|
||||||
|
.async_send_transaction(tx.clone())
|
||||||
|
.expect("create_account in generate_and_fund_keypairs");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j, key) in keys.iter().enumerate() {
|
||||||
|
let mut times = 0;
|
||||||
|
loop {
|
||||||
|
let balance =
|
||||||
|
librapay_transaction::get_libra_balance(client, &key.pubkey()).unwrap();
|
||||||
|
if balance >= amount {
|
||||||
|
break;
|
||||||
|
} else if times > 20 {
|
||||||
|
info!("timed out.. {} key: {} balance: {}", i, j, balance);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
times += 1;
|
||||||
|
sleep(Duration::from_millis(100));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"funded group {} of {}",
|
||||||
|
i + 1,
|
||||||
|
keypairs.len() / NUM_FUNDING_KEYS
|
||||||
|
);
|
||||||
|
blockhash = get_recent_blockhash(client).0;
|
||||||
|
}
|
||||||
|
|
||||||
|
funding_time.stop();
|
||||||
|
info!("done funding keys, took {} ms", funding_time.as_ms());
|
||||||
|
}
|
||||||
|
|
||||||
pub fn generate_and_fund_keypairs<T: 'static + Client + Send + Sync>(
|
pub fn generate_and_fund_keypairs<T: 'static + Client + Send + Sync>(
|
||||||
client: Arc<T>,
|
client: Arc<T>,
|
||||||
faucet_addr: Option<SocketAddr>,
|
faucet_addr: Option<SocketAddr>,
|
||||||
funding_key: &Keypair,
|
funding_key: &Keypair,
|
||||||
keypair_count: usize,
|
keypair_count: usize,
|
||||||
lamports_per_account: u64,
|
lamports_per_account: u64,
|
||||||
) -> Result<Vec<Keypair>> {
|
use_move: bool,
|
||||||
|
) -> Result<(Vec<Keypair>, Option<LibraKeys>)> {
|
||||||
info!("Creating {} keypairs...", keypair_count);
|
info!("Creating {} keypairs...", keypair_count);
|
||||||
let (mut keypairs, extra) = generate_keypairs(funding_key, keypair_count as u64);
|
let (mut keypairs, extra) = generate_keypairs(funding_key, keypair_count as u64);
|
||||||
info!("Get lamports...");
|
info!("Get lamports...");
|
||||||
@@ -885,6 +1139,12 @@ pub fn generate_and_fund_keypairs<T: 'static + Client + Send + Sync>(
|
|||||||
let last_key = keypairs[keypair_count - 1].pubkey();
|
let last_key = keypairs[keypair_count - 1].pubkey();
|
||||||
let last_keypair_balance = client.get_balance(&last_key).unwrap_or(0);
|
let last_keypair_balance = client.get_balance(&last_key).unwrap_or(0);
|
||||||
|
|
||||||
|
#[cfg(feature = "move")]
|
||||||
|
let mut move_keypairs_ret = None;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "move"))]
|
||||||
|
let move_keypairs_ret = None;
|
||||||
|
|
||||||
// Repeated runs will eat up keypair balances from transaction fees. In order to quickly
|
// Repeated runs will eat up keypair balances from transaction fees. In order to quickly
|
||||||
// start another bench-tps run without re-funding all of the keypairs, check if the
|
// start another bench-tps run without re-funding all of the keypairs, check if the
|
||||||
// keypairs still have at least 80% of the expected funds. That should be enough to
|
// keypairs still have at least 80% of the expected funds. That should be enough to
|
||||||
@@ -895,7 +1155,10 @@ pub fn generate_and_fund_keypairs<T: 'static + Client + Send + Sync>(
|
|||||||
let max_fee = fee_rate_governor.max_lamports_per_signature;
|
let max_fee = fee_rate_governor.max_lamports_per_signature;
|
||||||
let extra_fees = extra * max_fee;
|
let extra_fees = extra * max_fee;
|
||||||
let total_keypairs = keypairs.len() as u64 + 1; // Add one for funding keypair
|
let total_keypairs = keypairs.len() as u64 + 1; // Add one for funding keypair
|
||||||
let total = lamports_per_account * total_keypairs + extra_fees;
|
let mut total = lamports_per_account * total_keypairs + extra_fees;
|
||||||
|
if use_move {
|
||||||
|
total *= 3;
|
||||||
|
}
|
||||||
|
|
||||||
let funding_key_balance = client.get_balance(&funding_key.pubkey()).unwrap_or(0);
|
let funding_key_balance = client.get_balance(&funding_key.pubkey()).unwrap_or(0);
|
||||||
info!(
|
info!(
|
||||||
@@ -907,6 +1170,40 @@ pub fn generate_and_fund_keypairs<T: 'static + Client + Send + Sync>(
|
|||||||
airdrop_lamports(client.as_ref(), &faucet_addr.unwrap(), funding_key, total)?;
|
airdrop_lamports(client.as_ref(), &faucet_addr.unwrap(), funding_key, total)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "move")]
|
||||||
|
{
|
||||||
|
if use_move {
|
||||||
|
let libra_genesis_keypair =
|
||||||
|
create_genesis(&funding_key, client.as_ref(), 10_000_000);
|
||||||
|
let libra_mint_program_id = upload_mint_script(&funding_key, client.as_ref());
|
||||||
|
let libra_pay_program_id = upload_payment_script(&funding_key, client.as_ref());
|
||||||
|
|
||||||
|
// Generate another set of keypairs for move accounts.
|
||||||
|
// Still fund the solana ones which will be used for fees.
|
||||||
|
let seed = [0u8; 32];
|
||||||
|
let mut rnd = GenKeys::new(seed);
|
||||||
|
let move_keypairs = rnd.gen_n_keypairs(keypair_count as u64);
|
||||||
|
fund_move_keys(
|
||||||
|
client.as_ref(),
|
||||||
|
funding_key,
|
||||||
|
&move_keypairs,
|
||||||
|
total / 3,
|
||||||
|
&libra_pay_program_id,
|
||||||
|
&libra_mint_program_id,
|
||||||
|
&libra_genesis_keypair,
|
||||||
|
);
|
||||||
|
move_keypairs_ret = Some((
|
||||||
|
libra_genesis_keypair,
|
||||||
|
libra_pay_program_id,
|
||||||
|
libra_mint_program_id,
|
||||||
|
move_keypairs,
|
||||||
|
));
|
||||||
|
|
||||||
|
// Give solana keys 1/3 and move keys 1/3 the lamports. Keep 1/3 for fees.
|
||||||
|
total /= 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fund_keys(
|
fund_keys(
|
||||||
client,
|
client,
|
||||||
funding_key,
|
funding_key,
|
||||||
@@ -920,7 +1217,7 @@ pub fn generate_and_fund_keypairs<T: 'static + Client + Send + Sync>(
|
|||||||
// 'generate_keypairs' generates extra keys to be able to have size-aligned funding batches for fund_keys.
|
// 'generate_keypairs' generates extra keys to be able to have size-aligned funding batches for fund_keys.
|
||||||
keypairs.truncate(keypair_count);
|
keypairs.truncate(keypair_count);
|
||||||
|
|
||||||
Ok(keypairs)
|
Ok((keypairs, move_keypairs_ret))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -944,11 +1241,11 @@ mod tests {
|
|||||||
config.duration = Duration::from_secs(5);
|
config.duration = Duration::from_secs(5);
|
||||||
|
|
||||||
let keypair_count = config.tx_count * config.keypair_multiplier;
|
let keypair_count = config.tx_count * config.keypair_multiplier;
|
||||||
let keypairs =
|
let (keypairs, _move_keypairs) =
|
||||||
generate_and_fund_keypairs(client.clone(), None, &config.id, keypair_count, 20)
|
generate_and_fund_keypairs(client.clone(), None, &config.id, keypair_count, 20, false)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
do_bench_tps(client, config, keypairs);
|
do_bench_tps(client, config, keypairs, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -959,8 +1256,9 @@ mod tests {
|
|||||||
let keypair_count = 20;
|
let keypair_count = 20;
|
||||||
let lamports = 20;
|
let lamports = 20;
|
||||||
|
|
||||||
let keypairs =
|
let (keypairs, _move_keypairs) =
|
||||||
generate_and_fund_keypairs(client.clone(), None, &id, keypair_count, lamports).unwrap();
|
generate_and_fund_keypairs(client.clone(), None, &id, keypair_count, lamports, false)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
for kp in &keypairs {
|
for kp in &keypairs {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -982,8 +1280,9 @@ mod tests {
|
|||||||
let keypair_count = 20;
|
let keypair_count = 20;
|
||||||
let lamports = 20;
|
let lamports = 20;
|
||||||
|
|
||||||
let keypairs =
|
let (keypairs, _move_keypairs) =
|
||||||
generate_and_fund_keypairs(client.clone(), None, &id, keypair_count, lamports).unwrap();
|
generate_and_fund_keypairs(client.clone(), None, &id, keypair_count, lamports, false)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
for kp in &keypairs {
|
for kp in &keypairs {
|
||||||
assert_eq!(client.get_balance(&kp.pubkey()).unwrap(), lamports);
|
assert_eq!(client.get_balance(&kp.pubkey()).unwrap(), lamports);
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
use clap::{crate_description, crate_name, App, Arg, ArgMatches};
|
use clap::{crate_description, crate_name, App, Arg, ArgMatches};
|
||||||
use solana_faucet::faucet::FAUCET_PORT;
|
use solana_faucet::faucet::FAUCET_PORT;
|
||||||
use solana_sdk::fee_calculator::FeeRateGovernor;
|
use solana_sdk::fee_calculator::FeeRateGovernor;
|
||||||
use solana_sdk::{
|
use solana_sdk::signature::{read_keypair_file, Keypair};
|
||||||
pubkey::Pubkey,
|
|
||||||
signature::{read_keypair_file, Keypair},
|
|
||||||
};
|
|
||||||
use std::{net::SocketAddr, process::exit, time::Duration};
|
use std::{net::SocketAddr, process::exit, time::Duration};
|
||||||
|
|
||||||
const NUM_LAMPORTS_PER_ACCOUNT_DEFAULT: u64 = solana_sdk::native_token::LAMPORTS_PER_SOL;
|
const NUM_LAMPORTS_PER_ACCOUNT_DEFAULT: u64 = solana_sdk::native_token::LAMPORTS_PER_SOL;
|
||||||
@@ -26,9 +23,9 @@ pub struct Config {
|
|||||||
pub read_from_client_file: bool,
|
pub read_from_client_file: bool,
|
||||||
pub target_lamports_per_signature: u64,
|
pub target_lamports_per_signature: u64,
|
||||||
pub multi_client: bool,
|
pub multi_client: bool,
|
||||||
|
pub use_move: bool,
|
||||||
pub num_lamports_per_account: u64,
|
pub num_lamports_per_account: u64,
|
||||||
pub target_slots_per_epoch: u64,
|
pub target_slots_per_epoch: u64,
|
||||||
pub target_node: Option<Pubkey>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
@@ -49,9 +46,9 @@ impl Default for Config {
|
|||||||
read_from_client_file: false,
|
read_from_client_file: false,
|
||||||
target_lamports_per_signature: FeeRateGovernor::default().target_lamports_per_signature,
|
target_lamports_per_signature: FeeRateGovernor::default().target_lamports_per_signature,
|
||||||
multi_client: true,
|
multi_client: true,
|
||||||
|
use_move: false,
|
||||||
num_lamports_per_account: NUM_LAMPORTS_PER_ACCOUNT_DEFAULT,
|
num_lamports_per_account: NUM_LAMPORTS_PER_ACCOUNT_DEFAULT,
|
||||||
target_slots_per_epoch: 0,
|
target_slots_per_epoch: 0,
|
||||||
target_node: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,19 +109,16 @@ pub fn build_args<'a, 'b>(version: &'b str) -> App<'a, 'b> {
|
|||||||
.long("sustained")
|
.long("sustained")
|
||||||
.help("Use sustained performance mode vs. peak mode. This overlaps the tx generation with transfers."),
|
.help("Use sustained performance mode vs. peak mode. This overlaps the tx generation with transfers."),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("use-move")
|
||||||
|
.long("use-move")
|
||||||
|
.help("Use Move language transactions to perform transfers."),
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("no-multi-client")
|
Arg::with_name("no-multi-client")
|
||||||
.long("no-multi-client")
|
.long("no-multi-client")
|
||||||
.help("Disable multi-client support, only transact with the entrypoint."),
|
.help("Disable multi-client support, only transact with the entrypoint."),
|
||||||
)
|
)
|
||||||
.arg(
|
|
||||||
Arg::with_name("target_node")
|
|
||||||
.long("target-node")
|
|
||||||
.requires("no-multi-client")
|
|
||||||
.takes_value(true)
|
|
||||||
.value_name("PUBKEY")
|
|
||||||
.help("Specify an exact node to send transactions to."),
|
|
||||||
)
|
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("tx_count")
|
Arg::with_name("tx_count")
|
||||||
.long("tx_count")
|
.long("tx_count")
|
||||||
@@ -269,10 +263,8 @@ pub fn extract_args<'a>(matches: &ArgMatches<'a>) -> Config {
|
|||||||
args.target_lamports_per_signature = v.to_string().parse().expect("can't parse lamports");
|
args.target_lamports_per_signature = v.to_string().parse().expect("can't parse lamports");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
args.use_move = matches.is_present("use-move");
|
||||||
args.multi_client = !matches.is_present("no-multi-client");
|
args.multi_client = !matches.is_present("no-multi-client");
|
||||||
args.target_node = matches
|
|
||||||
.value_of("target_node")
|
|
||||||
.map(|target_str| target_str.parse().unwrap());
|
|
||||||
|
|
||||||
if let Some(v) = matches.value_of("num_lamports_per_account") {
|
if let Some(v) = matches.value_of("num_lamports_per_account") {
|
||||||
args.num_lamports_per_account = v.to_string().parse().expect("can't parse lamports");
|
args.num_lamports_per_account = v.to_string().parse().expect("can't parse lamports");
|
||||||
|
|||||||
@@ -29,9 +29,9 @@ fn main() {
|
|||||||
write_to_client_file,
|
write_to_client_file,
|
||||||
read_from_client_file,
|
read_from_client_file,
|
||||||
target_lamports_per_signature,
|
target_lamports_per_signature,
|
||||||
|
use_move,
|
||||||
multi_client,
|
multi_client,
|
||||||
num_lamports_per_account,
|
num_lamports_per_account,
|
||||||
target_node,
|
|
||||||
..
|
..
|
||||||
} = &cli_config;
|
} = &cli_config;
|
||||||
|
|
||||||
@@ -82,24 +82,11 @@ fn main() {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
Arc::new(client)
|
Arc::new(client)
|
||||||
} else if let Some(target_node) = target_node {
|
|
||||||
info!("Searching for target_node: {:?}", target_node);
|
|
||||||
let mut target_client = None;
|
|
||||||
for node in nodes {
|
|
||||||
if node.id == *target_node {
|
|
||||||
target_client = Some(Arc::new(get_client(&[node])));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
target_client.unwrap_or_else(|| {
|
|
||||||
eprintln!("Target node {} not found", target_node);
|
|
||||||
exit(1);
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
Arc::new(get_client(&nodes))
|
Arc::new(get_client(&nodes))
|
||||||
};
|
};
|
||||||
|
|
||||||
let keypairs = if *read_from_client_file {
|
let (keypairs, move_keypairs) = if *read_from_client_file && !use_move {
|
||||||
let path = Path::new(&client_ids_and_stake_file);
|
let path = Path::new(&client_ids_and_stake_file);
|
||||||
let file = File::open(path).unwrap();
|
let file = File::open(path).unwrap();
|
||||||
|
|
||||||
@@ -128,8 +115,8 @@ fn main() {
|
|||||||
// Sort keypairs so that do_bench_tps() uses the same subset of accounts for each run.
|
// Sort keypairs so that do_bench_tps() uses the same subset of accounts for each run.
|
||||||
// This prevents the amount of storage needed for bench-tps accounts from creeping up
|
// This prevents the amount of storage needed for bench-tps accounts from creeping up
|
||||||
// across multiple runs.
|
// across multiple runs.
|
||||||
keypairs.sort_by_key(|x| x.pubkey().to_string());
|
keypairs.sort_by(|x, y| x.pubkey().to_string().cmp(&y.pubkey().to_string()));
|
||||||
keypairs
|
(keypairs, None)
|
||||||
} else {
|
} else {
|
||||||
generate_and_fund_keypairs(
|
generate_and_fund_keypairs(
|
||||||
client.clone(),
|
client.clone(),
|
||||||
@@ -137,6 +124,7 @@ fn main() {
|
|||||||
&id,
|
&id,
|
||||||
keypair_count,
|
keypair_count,
|
||||||
*num_lamports_per_account,
|
*num_lamports_per_account,
|
||||||
|
*use_move,
|
||||||
)
|
)
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
eprintln!("Error could not fund keys: {:?}", e);
|
eprintln!("Error could not fund keys: {:?}", e);
|
||||||
@@ -144,5 +132,5 @@ fn main() {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
do_bench_tps(client, cli_config, keypairs);
|
do_bench_tps(client, cli_config, keypairs, move_keypairs);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,17 @@ use solana_core::cluster_info::VALIDATOR_PORT_RANGE;
|
|||||||
use solana_core::validator::ValidatorConfig;
|
use solana_core::validator::ValidatorConfig;
|
||||||
use solana_faucet::faucet::run_local_faucet;
|
use solana_faucet::faucet::run_local_faucet;
|
||||||
use solana_local_cluster::local_cluster::{ClusterConfig, LocalCluster};
|
use solana_local_cluster::local_cluster::{ClusterConfig, LocalCluster};
|
||||||
|
#[cfg(feature = "move")]
|
||||||
|
use solana_sdk::move_loader::solana_move_loader_program;
|
||||||
use solana_sdk::signature::{Keypair, Signer};
|
use solana_sdk::signature::{Keypair, Signer};
|
||||||
use std::sync::{mpsc::channel, Arc};
|
use std::sync::{mpsc::channel, Arc};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
fn test_bench_tps_local_cluster(config: Config) {
|
fn test_bench_tps_local_cluster(config: Config) {
|
||||||
|
#[cfg(feature = "move")]
|
||||||
|
let native_instruction_processors = vec![solana_move_loader_program()];
|
||||||
|
|
||||||
|
#[cfg(not(feature = "move"))]
|
||||||
let native_instruction_processors = vec![];
|
let native_instruction_processors = vec![];
|
||||||
|
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
@@ -42,16 +48,17 @@ fn test_bench_tps_local_cluster(config: Config) {
|
|||||||
let lamports_per_account = 100;
|
let lamports_per_account = 100;
|
||||||
|
|
||||||
let keypair_count = config.tx_count * config.keypair_multiplier;
|
let keypair_count = config.tx_count * config.keypair_multiplier;
|
||||||
let keypairs = generate_and_fund_keypairs(
|
let (keypairs, move_keypairs) = generate_and_fund_keypairs(
|
||||||
client.clone(),
|
client.clone(),
|
||||||
Some(faucet_addr),
|
Some(faucet_addr),
|
||||||
&config.id,
|
&config.id,
|
||||||
keypair_count,
|
keypair_count,
|
||||||
lamports_per_account,
|
lamports_per_account,
|
||||||
|
config.use_move,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let _total = do_bench_tps(client, config, keypairs);
|
let _total = do_bench_tps(client, config, keypairs, move_keypairs);
|
||||||
|
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
assert!(_total > 100);
|
assert!(_total > 100);
|
||||||
@@ -66,3 +73,14 @@ fn test_bench_tps_local_cluster_solana() {
|
|||||||
|
|
||||||
test_bench_tps_local_cluster(config);
|
test_bench_tps_local_cluster(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn test_bench_tps_local_cluster_move() {
|
||||||
|
let mut config = Config::default();
|
||||||
|
config.tx_count = 100;
|
||||||
|
config.duration = Duration::from_secs(10);
|
||||||
|
config.use_move = true;
|
||||||
|
|
||||||
|
test_bench_tps_local_cluster(config);
|
||||||
|
}
|
||||||
|
|||||||
43
ci/affects-files.sh
Executable file
43
ci/affects-files.sh
Executable file
@@ -0,0 +1,43 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Checks if a CI build affects one or more path patterns. Each command-line
|
||||||
|
# argument is checked in series.
|
||||||
|
#
|
||||||
|
# Bash regular expressions are permitted in the pattern:
|
||||||
|
# ./affects-files.sh .rs$ -- any file or directory ending in .rs
|
||||||
|
# ./affects-files.sh .rs -- also matches foo.rs.bar
|
||||||
|
# ./affects-files.sh ^snap/ -- anything under the snap/ subdirectory
|
||||||
|
# ./affects-files.sh snap/ -- also matches foo/snap/
|
||||||
|
# Any pattern starting with the ! character will be negated:
|
||||||
|
# ./affects-files.sh !^docs/ -- anything *not* under the docs/ subdirectory
|
||||||
|
#
|
||||||
|
set -e
|
||||||
|
cd "$(dirname "$0")"/..
|
||||||
|
|
||||||
|
if [[ -n $CI_PULL_REQUEST ]]; then
|
||||||
|
affectedFiles="$(buildkite-agent meta-data get affected_files)"
|
||||||
|
echo "Affected files in this PR: $affectedFiles"
|
||||||
|
|
||||||
|
IFS=':' read -ra files <<< "$affectedFiles"
|
||||||
|
for pattern in "$@"; do
|
||||||
|
if [[ ${pattern:0:1} = "!" ]]; then
|
||||||
|
for file in "${files[@]}"; do
|
||||||
|
if [[ ! $file =~ ${pattern:1} ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
else
|
||||||
|
for file in "${files[@]}"; do
|
||||||
|
if [[ $file =~ $pattern ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# affected_files metadata is not currently available for non-PR builds, so assume
|
||||||
|
# the worse (affected)
|
||||||
|
exit 0
|
||||||
@@ -1,254 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
#
|
|
||||||
# Builds a buildkite pipeline based on the environment variables
|
|
||||||
#
|
|
||||||
|
|
||||||
set -e
|
|
||||||
cd "$(dirname "$0")"/..
|
|
||||||
|
|
||||||
output_file=${1:-/dev/stderr}
|
|
||||||
|
|
||||||
if [[ -n $CI_PULL_REQUEST ]]; then
|
|
||||||
IFS=':' read -ra affected_files <<< "$(buildkite-agent meta-data get affected_files)"
|
|
||||||
if [[ ${#affected_files[*]} -eq 0 ]]; then
|
|
||||||
echo "Unable to determine the files affected by this PR"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
affected_files=()
|
|
||||||
fi
|
|
||||||
|
|
||||||
annotate() {
|
|
||||||
if [[ -n $BUILDKITE ]]; then
|
|
||||||
buildkite-agent annotate "$@"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Checks if a CI pull request affects one or more path patterns. Each
|
|
||||||
# pattern argument is checked in series. If one of them found to be affected,
|
|
||||||
# return immediately as such.
|
|
||||||
#
|
|
||||||
# Bash regular expressions are permitted in the pattern:
|
|
||||||
# affects .rs$ -- any file or directory ending in .rs
|
|
||||||
# affects .rs -- also matches foo.rs.bar
|
|
||||||
# affects ^snap/ -- anything under the snap/ subdirectory
|
|
||||||
# affects snap/ -- also matches foo/snap/
|
|
||||||
# Any pattern starting with the ! character will be negated:
|
|
||||||
# affects !^docs/ -- anything *not* under the docs/ subdirectory
|
|
||||||
#
|
|
||||||
affects() {
|
|
||||||
if [[ -z $CI_PULL_REQUEST ]]; then
|
|
||||||
# affected_files metadata is not currently available for non-PR builds so assume
|
|
||||||
# the worse (affected)
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
# Assume everyting needs to be tested when any Dockerfile changes
|
|
||||||
for pattern in ^ci/docker-rust/Dockerfile ^ci/docker-rust-nightly/Dockerfile "$@"; do
|
|
||||||
if [[ ${pattern:0:1} = "!" ]]; then
|
|
||||||
for file in "${affected_files[@]}"; do
|
|
||||||
if [[ ! $file =~ ${pattern:1} ]]; then
|
|
||||||
return 0 # affected
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
else
|
|
||||||
for file in "${affected_files[@]}"; do
|
|
||||||
if [[ $file =~ $pattern ]]; then
|
|
||||||
return 0 # affected
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
return 1 # not affected
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Checks if a CI pull request affects anything other than the provided path patterns
|
|
||||||
#
|
|
||||||
# Syntax is the same as `affects()` except that the negation prefix is not
|
|
||||||
# supported
|
|
||||||
#
|
|
||||||
affects_other_than() {
|
|
||||||
if [[ -z $CI_PULL_REQUEST ]]; then
|
|
||||||
# affected_files metadata is not currently available for non-PR builds so assume
|
|
||||||
# the worse (affected)
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
for file in "${affected_files[@]}"; do
|
|
||||||
declare matched=false
|
|
||||||
for pattern in "$@"; do
|
|
||||||
if [[ $file =~ $pattern ]]; then
|
|
||||||
matched=true
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
if ! $matched; then
|
|
||||||
return 0 # affected
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
return 1 # not affected
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
start_pipeline() {
|
|
||||||
echo "# $*" > "$output_file"
|
|
||||||
echo "steps:" >> "$output_file"
|
|
||||||
}
|
|
||||||
|
|
||||||
command_step() {
|
|
||||||
cat >> "$output_file" <<EOF
|
|
||||||
- name: "$1"
|
|
||||||
command: "$2"
|
|
||||||
timeout_in_minutes: $3
|
|
||||||
artifact_paths: "log-*.txt"
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
trigger_secondary_step() {
|
|
||||||
cat >> "$output_file" <<"EOF"
|
|
||||||
- trigger: "solana-secondary"
|
|
||||||
branches: "!pull/*"
|
|
||||||
async: true
|
|
||||||
build:
|
|
||||||
message: "${BUILDKITE_MESSAGE}"
|
|
||||||
commit: "${BUILDKITE_COMMIT}"
|
|
||||||
branch: "${BUILDKITE_BRANCH}"
|
|
||||||
env:
|
|
||||||
TRIGGERED_BUILDKITE_TAG: "${BUILDKITE_TAG}"
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
wait_step() {
|
|
||||||
echo " - wait" >> "$output_file"
|
|
||||||
}
|
|
||||||
|
|
||||||
all_test_steps() {
|
|
||||||
command_step checks ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_nightly_docker_image ci/test-checks.sh" 20
|
|
||||||
wait_step
|
|
||||||
|
|
||||||
# Coverage...
|
|
||||||
if affects \
|
|
||||||
.rs$ \
|
|
||||||
Cargo.lock$ \
|
|
||||||
Cargo.toml$ \
|
|
||||||
^ci/rust-version.sh \
|
|
||||||
^ci/test-coverage.sh \
|
|
||||||
^scripts/coverage.sh \
|
|
||||||
; then
|
|
||||||
command_step coverage ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_nightly_docker_image ci/test-coverage.sh" 30
|
|
||||||
wait_step
|
|
||||||
else
|
|
||||||
annotate --style info --context test-coverage \
|
|
||||||
"Coverage skipped as no .rs files were modified"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Full test suite
|
|
||||||
command_step stable ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_stable_docker_image ci/test-stable.sh" 60
|
|
||||||
wait_step
|
|
||||||
|
|
||||||
# Perf test suite
|
|
||||||
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/ \
|
|
||||||
; then
|
|
||||||
cat >> "$output_file" <<"EOF"
|
|
||||||
- command: "ci/test-stable-perf.sh"
|
|
||||||
name: "stable-perf"
|
|
||||||
timeout_in_minutes: 40
|
|
||||||
artifact_paths: "log-*.txt"
|
|
||||||
agents:
|
|
||||||
- "queue=cuda"
|
|
||||||
EOF
|
|
||||||
else
|
|
||||||
annotate --style info \
|
|
||||||
"Stable-perf skipped as no relevant files were modified"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Benches...
|
|
||||||
if affects \
|
|
||||||
.rs$ \
|
|
||||||
Cargo.lock$ \
|
|
||||||
Cargo.toml$ \
|
|
||||||
^ci/rust-version.sh \
|
|
||||||
^ci/test-coverage.sh \
|
|
||||||
^ci/test-bench.sh \
|
|
||||||
; then
|
|
||||||
command_step bench "ci/test-bench.sh" 30
|
|
||||||
else
|
|
||||||
annotate --style info --context test-bench \
|
|
||||||
"Bench skipped as no .rs files were modified"
|
|
||||||
fi
|
|
||||||
|
|
||||||
command_step "local-cluster" \
|
|
||||||
". ci/rust-version.sh; ci/docker-run.sh \$\$rust_stable_docker_image ci/test-local-cluster.sh" \
|
|
||||||
45
|
|
||||||
}
|
|
||||||
|
|
||||||
pull_or_push_steps() {
|
|
||||||
command_step sanity "ci/test-sanity.sh" 5
|
|
||||||
wait_step
|
|
||||||
|
|
||||||
# Check for any .sh file changes
|
|
||||||
if affects .sh$; then
|
|
||||||
command_step shellcheck "ci/shellcheck.sh" 5
|
|
||||||
wait_step
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Run the full test suite by default, skipping only if modifications are local
|
|
||||||
# to some particular areas of the tree
|
|
||||||
if affects_other_than ^.buildkite ^.mergify .md$ ^docs/ ^web3.js/ ^explorer/ ^.gitbook; then
|
|
||||||
all_test_steps
|
|
||||||
fi
|
|
||||||
|
|
||||||
# web3.js, explorer and docs changes run on Travis...
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if [[ -n $BUILDKITE_TAG ]]; then
|
|
||||||
start_pipeline "Tag pipeline for $BUILDKITE_TAG"
|
|
||||||
|
|
||||||
annotate --style info --context release-tag \
|
|
||||||
"https://github.com/solana-labs/solana/releases/$BUILDKITE_TAG"
|
|
||||||
|
|
||||||
# Jump directly to the secondary build to publish release artifacts quickly
|
|
||||||
trigger_secondary_step
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
if [[ $BUILDKITE_BRANCH =~ ^pull ]]; then
|
|
||||||
echo "+++ Affected files in this PR"
|
|
||||||
for file in "${affected_files[@]}"; do
|
|
||||||
echo "- $file"
|
|
||||||
done
|
|
||||||
|
|
||||||
start_pipeline "Pull request pipeline for $BUILDKITE_BRANCH"
|
|
||||||
|
|
||||||
# Add helpful link back to the corresponding Github Pull Request
|
|
||||||
annotate --style info --context pr-backlink \
|
|
||||||
"Github Pull Request: https://github.com/solana-labs/solana/$BUILDKITE_BRANCH"
|
|
||||||
|
|
||||||
if [[ $GITHUB_USER = "dependabot-preview[bot]" ]]; then
|
|
||||||
command_step dependabot "ci/dependabot-pr.sh" 5
|
|
||||||
wait_step
|
|
||||||
fi
|
|
||||||
pull_or_push_steps
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
start_pipeline "Push pipeline for ${BUILDKITE_BRANCH:-?unknown branch?}"
|
|
||||||
pull_or_push_steps
|
|
||||||
wait_step
|
|
||||||
trigger_secondary_step
|
|
||||||
exit 0
|
|
||||||
15
ci/buildkite-release.yml
Normal file
15
ci/buildkite-release.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Build steps that run on a release tag
|
||||||
|
#
|
||||||
|
# All the steps in `buildkite.yml` are skipped and we jump directly to the
|
||||||
|
# secondary build steps since it's assumed the commit that was tagged is known
|
||||||
|
# to be good so there's no need to rebuild and retest it.
|
||||||
|
steps:
|
||||||
|
- trigger: "solana-secondary"
|
||||||
|
branches: "!pull/*"
|
||||||
|
async: true
|
||||||
|
build:
|
||||||
|
message: "${BUILDKITE_MESSAGE}"
|
||||||
|
commit: "${BUILDKITE_COMMIT}"
|
||||||
|
branch: "${BUILDKITE_BRANCH}"
|
||||||
|
env:
|
||||||
|
TRIGGERED_BUILDKITE_TAG: "${BUILDKITE_TAG}"
|
||||||
@@ -5,6 +5,9 @@ steps:
|
|||||||
- command: "ci/publish-tarball.sh"
|
- command: "ci/publish-tarball.sh"
|
||||||
timeout_in_minutes: 60
|
timeout_in_minutes: 60
|
||||||
name: "publish tarball"
|
name: "publish tarball"
|
||||||
|
- command: "ci/publish-docs.sh"
|
||||||
|
timeout_in_minutes: 15
|
||||||
|
name: "publish docs"
|
||||||
- command: "ci/publish-bpf-sdk.sh"
|
- command: "ci/publish-bpf-sdk.sh"
|
||||||
timeout_in_minutes: 5
|
timeout_in_minutes: 5
|
||||||
name: "publish bpf sdk"
|
name: "publish bpf sdk"
|
||||||
@@ -16,3 +19,6 @@ steps:
|
|||||||
timeout_in_minutes: 240
|
timeout_in_minutes: 240
|
||||||
name: "publish crate"
|
name: "publish crate"
|
||||||
branches: "!master"
|
branches: "!master"
|
||||||
|
# - command: ". ci/rust-version.sh; ci/docker-run.sh $$rust_stable_docker_image ci/test-move.sh"
|
||||||
|
# name: "move"
|
||||||
|
# timeout_in_minutes: 20
|
||||||
|
|||||||
26
ci/buildkite-tests.yml
Normal file
26
ci/buildkite-tests.yml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# These steps are conditionally triggered by ci/buildkite.yml when files
|
||||||
|
# other than those in docs/ are modified
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- command: ". ci/rust-version.sh; ci/docker-run.sh $$rust_nightly_docker_image ci/test-coverage.sh"
|
||||||
|
name: "coverage"
|
||||||
|
timeout_in_minutes: 30
|
||||||
|
- wait
|
||||||
|
- command: ". ci/rust-version.sh; ci/docker-run.sh $$rust_stable_docker_image ci/test-stable.sh"
|
||||||
|
name: "stable"
|
||||||
|
timeout_in_minutes: 60
|
||||||
|
artifact_paths: "log-*.txt"
|
||||||
|
- wait
|
||||||
|
- command: "ci/test-stable-perf.sh"
|
||||||
|
name: "stable-perf"
|
||||||
|
timeout_in_minutes: 40
|
||||||
|
artifact_paths: "log-*.txt"
|
||||||
|
agents:
|
||||||
|
- "queue=cuda"
|
||||||
|
- command: "ci/test-bench.sh"
|
||||||
|
name: "bench"
|
||||||
|
timeout_in_minutes: 30
|
||||||
|
- command: ". ci/rust-version.sh; ci/docker-run.sh $$rust_stable_docker_image ci/test-local-cluster.sh"
|
||||||
|
name: "local-cluster"
|
||||||
|
timeout_in_minutes: 45
|
||||||
|
artifact_paths: "log-*.txt"
|
||||||
38
ci/buildkite.yml
Normal file
38
ci/buildkite.yml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# Build steps that run on pushes and pull requests.
|
||||||
|
# If files other than those in docs/ were modified, this will be followed up by
|
||||||
|
# ci/buildkite-tests.yml
|
||||||
|
#
|
||||||
|
# Release tags use buildkite-release.yml instead
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- command: "ci/dependabot-pr.sh"
|
||||||
|
name: "dependabot"
|
||||||
|
timeout_in_minutes: 5
|
||||||
|
if: build.env("GITHUB_USER") == "dependabot-preview[bot]"
|
||||||
|
|
||||||
|
- wait
|
||||||
|
|
||||||
|
- command: ". ci/rust-version.sh; ci/docker-run.sh $$rust_nightly_docker_image ci/test-checks.sh"
|
||||||
|
name: "checks"
|
||||||
|
timeout_in_minutes: 20
|
||||||
|
- command: "ci/shellcheck.sh"
|
||||||
|
name: "shellcheck"
|
||||||
|
timeout_in_minutes: 5
|
||||||
|
|
||||||
|
- wait
|
||||||
|
|
||||||
|
- command: "ci/maybe-trigger-tests.sh"
|
||||||
|
name: "maybe-trigger-tests"
|
||||||
|
timeout_in_minutes: 2
|
||||||
|
|
||||||
|
- wait
|
||||||
|
|
||||||
|
- trigger: "solana-secondary"
|
||||||
|
branches: "!pull/*"
|
||||||
|
async: true
|
||||||
|
build:
|
||||||
|
message: "${BUILDKITE_MESSAGE}"
|
||||||
|
commit: "${BUILDKITE_COMMIT}"
|
||||||
|
branch: "${BUILDKITE_BRANCH}"
|
||||||
|
env:
|
||||||
|
TRIGGERED_BUILDKITE_TAG: "${BUILDKITE_TAG}"
|
||||||
@@ -89,17 +89,11 @@ 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"
|
|
||||||
elif [[ -n $CI_BRANCH ]]; then
|
|
||||||
BRANCH="$CI_BRANCH"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ $BRANCH = "$STABLE_CHANNEL" ]]; then
|
|
||||||
CHANNEL=stable
|
CHANNEL=stable
|
||||||
elif [[ $BRANCH = "$EDGE_CHANNEL" ]]; then
|
elif [[ $CI_BRANCH = "$EDGE_CHANNEL" ]]; then
|
||||||
CHANNEL=edge
|
CHANNEL=edge
|
||||||
elif [[ $BRANCH = "$BETA_CHANNEL" ]]; then
|
elif [[ $CI_BRANCH = "$BETA_CHANNEL" ]]; then
|
||||||
CHANNEL=beta
|
CHANNEL=beta
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ source ci/_
|
|||||||
commit_range="$(git merge-base HEAD origin/master)..HEAD"
|
commit_range="$(git merge-base HEAD origin/master)..HEAD"
|
||||||
parsed_update_args="$(
|
parsed_update_args="$(
|
||||||
git log "$commit_range" --author "dependabot-preview" --oneline -n1 |
|
git log "$commit_range" --author "dependabot-preview" --oneline -n1 |
|
||||||
grep -o '[Bb]ump.*$' |
|
grep -o 'Bump.*$' |
|
||||||
sed -r 's/[Bb]ump ([^ ]+) from ([^ ]+) to ([^ ]+)/-p \1:\2 --precise \3/'
|
sed -r 's/Bump ([^ ]+) from ([^ ]+) to ([^ ]+)/-p \1:\2 --precise \3/'
|
||||||
)"
|
)"
|
||||||
# relaxed_parsed_update_args is temporal measure...
|
# relaxed_parsed_update_args is temporal measure...
|
||||||
relaxed_parsed_update_args="$(
|
relaxed_parsed_update_args="$(
|
||||||
git log "$commit_range" --author "dependabot-preview" --oneline -n1 |
|
git log "$commit_range" --author "dependabot-preview" --oneline -n1 |
|
||||||
grep -o '[Bb]ump.*$' |
|
grep -o 'Bump.*$' |
|
||||||
sed -r 's/[Bb]ump ([^ ]+) from [^ ]+ to ([^ ]+)/-p \1 --precise \2/'
|
sed -r 's/Bump ([^ ]+) from [^ ]+ to ([^ ]+)/-p \1 --precise \2/'
|
||||||
)"
|
)"
|
||||||
package=$(echo "$parsed_update_args" | awk '{print $2}' | grep -o "^[^:]*")
|
package=$(echo "$parsed_update_args" | awk '{print $2}' | grep -o "^[^:]*")
|
||||||
if [[ -n $parsed_update_args ]]; then
|
if [[ -n $parsed_update_args ]]; then
|
||||||
|
|||||||
@@ -60,12 +60,6 @@ if [[ -z "$SOLANA_DOCKER_RUN_NOSETUID" ]]; then
|
|||||||
ARGS+=(--user "$(id -u):$(id -g)")
|
ARGS+=(--user "$(id -u):$(id -g)")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n $SOLANA_ALLOCATE_TTY ]]; then
|
|
||||||
# Colored output, progress bar and Ctrl-C:
|
|
||||||
# https://stackoverflow.com/a/41099052/10242004
|
|
||||||
ARGS+=(--interactive --tty)
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Environment variables to propagate into the container
|
# Environment variables to propagate into the container
|
||||||
ARGS+=(
|
ARGS+=(
|
||||||
--env BUILDKITE
|
--env BUILDKITE
|
||||||
@@ -73,7 +67,6 @@ ARGS+=(
|
|||||||
--env BUILDKITE_JOB_ID
|
--env BUILDKITE_JOB_ID
|
||||||
--env CI
|
--env CI
|
||||||
--env CI_BRANCH
|
--env CI_BRANCH
|
||||||
--env CI_BASE_BRANCH
|
|
||||||
--env CI_TAG
|
--env CI_TAG
|
||||||
--env CI_BUILD_ID
|
--env CI_BUILD_ID
|
||||||
--env CI_COMMIT
|
--env CI_COMMIT
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
FROM solanalabs/rust:1.45.1
|
FROM solanalabs/rust:1.43.0
|
||||||
ARG date
|
ARG date
|
||||||
|
|
||||||
RUN set -x \
|
RUN set -x \
|
||||||
&& rustup install nightly-$date \
|
&& rustup install nightly-$date \
|
||||||
&& rustup component add clippy --toolchain=nightly-$date \
|
&& rustup component add clippy --toolchain=nightly-$date \
|
||||||
&& rustup component add rustfmt --toolchain=nightly-$date \
|
|
||||||
&& rustup show \
|
&& rustup show \
|
||||||
&& rustc --version \
|
&& rustc --version \
|
||||||
&& cargo --version \
|
&& cargo --version \
|
||||||
|
|||||||
@@ -2,27 +2,23 @@ Docker image containing rust nightly and some preinstalled crates used in CI.
|
|||||||
|
|
||||||
This image may be manually updated by running `CI=true ./build.sh` if you are a member
|
This image may be manually updated by running `CI=true ./build.sh` if you are a member
|
||||||
of the [Solana Labs](https://hub.docker.com/u/solanalabs/) Docker Hub
|
of the [Solana Labs](https://hub.docker.com/u/solanalabs/) Docker Hub
|
||||||
organization.
|
organization, but it is also automatically updated periodically by
|
||||||
|
[this automation](https://buildkite.com/solana-labs/solana-ci-docker-rust-nightly).
|
||||||
|
|
||||||
## Moving to a newer nightly
|
## Moving to a newer nightly
|
||||||
|
|
||||||
NOTE: Follow instructions in docker-rust/README.md before this when updating the stable
|
|
||||||
rust version as well.
|
|
||||||
|
|
||||||
We pin the version of nightly (see the `ARG nightly=xyz` line in `Dockerfile`)
|
We pin the version of nightly (see the `ARG nightly=xyz` line in `Dockerfile`)
|
||||||
to avoid the build breaking at unexpected times, as occasionally nightly will
|
to avoid the build breaking at unexpected times, as occasionally nightly will
|
||||||
introduce breaking changes.
|
introduce breaking changes.
|
||||||
|
|
||||||
To update the pinned version:
|
To update the pinned version:
|
||||||
1. Edit `Dockerfile` to match the desired stable rust version to base on if needed.
|
|
||||||
1. Run `ci/docker-rust-nightly/build.sh` to rebuild the nightly image locally,
|
1. Run `ci/docker-rust-nightly/build.sh` to rebuild the nightly image locally,
|
||||||
or potentially `ci/docker-rust-nightly/build.sh YYYY-MM-DD` if there's a
|
or potentially `ci/docker-rust-nightly/build.sh YYYY-MM-DD` if there's a
|
||||||
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 `YYY-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_DOCKER_RUN_NOSETUID=1 ci/docker-run.sh --nopull solanalabs/rust-nightly:YYYY-MM-DD ci/test-coverage.sh`
|
||||||
and `SOLANA_ALLOCATE_TTY=1 SOLANA_DOCKER_RUN_NOSETUID=1 ci/docker-run.sh --nopull solanalabs/rust-nightly:YYYY-MM-DD ci/test-coverage.sh [args]...`
|
|
||||||
to confirm the new nightly image builds. Fix any issues as needed
|
to confirm the new nightly image builds. Fix any issues as needed
|
||||||
1. Run `docker login` to enable pushing images to Docker Hub, if you're authorized.
|
1. Run `docker login` to enable pushing images to Docker Hub, if you're authorized.
|
||||||
1. Run `CI=true ci/docker-rust-nightly/build.sh YYYY-MM-DD` to push the new nightly image to dockerhub.com.
|
1. Run `CI=true ci/docker-rust-nightly/build.sh YYYY-MM-DD` to push the new nightly image to dockerhub.com.
|
||||||
|
|||||||
@@ -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.45.1
|
FROM rust:1.43.0
|
||||||
|
|
||||||
# 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
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
Docker image containing rust and some preinstalled packages used in CI.
|
Docker image containing rust and some preinstalled packages used in CI.
|
||||||
|
|
||||||
NOTE: Recreate rust-nightly docker image after this when updating the stable rust
|
|
||||||
version! Both of docker images must be updated in tandem.
|
|
||||||
|
|
||||||
This image manually maintained:
|
This image manually maintained:
|
||||||
1. Edit `Dockerfile` to match the desired rust version
|
1. Edit `Dockerfile` to match the desired rust version
|
||||||
1. Run `docker login` to enable pushing images to Docker Hub, if you're authorized.
|
2. Run `./build.sh` to publish the new image, if you are a member of the [Solana
|
||||||
1. Run `./build.sh` to publish the new image, if you are a member of the [Solana
|
|
||||||
Labs](https://hub.docker.com/u/solanalabs/) Docker Hub organization.
|
Labs](https://hub.docker.com/u/solanalabs/) Docker Hub organization.
|
||||||
|
|
||||||
|
|||||||
@@ -8,11 +8,10 @@ if [[ -n $CI ]]; then
|
|||||||
export CI=1
|
export CI=1
|
||||||
if [[ -n $TRAVIS ]]; then
|
if [[ -n $TRAVIS ]]; then
|
||||||
export CI_BRANCH=$TRAVIS_BRANCH
|
export CI_BRANCH=$TRAVIS_BRANCH
|
||||||
export CI_BASE_BRANCH=$TRAVIS_BRANCH
|
|
||||||
export CI_BUILD_ID=$TRAVIS_BUILD_ID
|
export CI_BUILD_ID=$TRAVIS_BUILD_ID
|
||||||
export CI_COMMIT=$TRAVIS_COMMIT
|
export CI_COMMIT=$TRAVIS_COMMIT
|
||||||
export CI_JOB_ID=$TRAVIS_JOB_ID
|
export CI_JOB_ID=$TRAVIS_JOB_ID
|
||||||
if [[ $TRAVIS_PULL_REQUEST != false ]]; then
|
if $TRAVIS_PULL_REQUEST; then
|
||||||
export CI_PULL_REQUEST=true
|
export CI_PULL_REQUEST=true
|
||||||
else
|
else
|
||||||
export CI_PULL_REQUEST=
|
export CI_PULL_REQUEST=
|
||||||
@@ -29,10 +28,8 @@ if [[ -n $CI ]]; then
|
|||||||
# to how solana-ci-gate is used to trigger PR builds rather than using the
|
# to how solana-ci-gate is used to trigger PR builds rather than using the
|
||||||
# standard Buildkite PR trigger.
|
# standard Buildkite PR trigger.
|
||||||
if [[ $CI_BRANCH =~ pull/* ]]; then
|
if [[ $CI_BRANCH =~ pull/* ]]; then
|
||||||
export CI_BASE_BRANCH=$BUILDKITE_PULL_REQUEST_BASE_BRANCH
|
|
||||||
export CI_PULL_REQUEST=true
|
export CI_PULL_REQUEST=true
|
||||||
else
|
else
|
||||||
export CI_BASE_BRANCH=$BUILDKITE_BRANCH
|
|
||||||
export CI_PULL_REQUEST=
|
export CI_PULL_REQUEST=
|
||||||
fi
|
fi
|
||||||
export CI_OS_NAME=linux
|
export CI_OS_NAME=linux
|
||||||
|
|||||||
21
ci/maybe-trigger-tests.sh
Executable file
21
ci/maybe-trigger-tests.sh
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
annotate() {
|
||||||
|
${BUILDKITE:-false} && {
|
||||||
|
buildkite-agent annotate "$@"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Skip if only the docs have been modified
|
||||||
|
ci/affects-files.sh \
|
||||||
|
\!^docs/ \
|
||||||
|
|| {
|
||||||
|
annotate --style info \
|
||||||
|
"Skipping all further tests as only docs/ files were modified"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
annotate --style info "Triggering tests"
|
||||||
|
buildkite-agent pipeline upload ci/buildkite-tests.yml
|
||||||
32
ci/publish-docs.sh
Executable file
32
ci/publish-docs.sh
Executable file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
echo --- build docs
|
||||||
|
(
|
||||||
|
set -x
|
||||||
|
. ci/rust-version.sh stable
|
||||||
|
ci/docker-run.sh "$rust_stable_docker_image" docs/build.sh
|
||||||
|
)
|
||||||
|
|
||||||
|
echo --- update gitbook-cage
|
||||||
|
if [[ -n $CI_BRANCH ]]; then
|
||||||
|
(
|
||||||
|
# make a local commit for the svgs and generated/updated markdown
|
||||||
|
set -x
|
||||||
|
git add -f docs/src
|
||||||
|
if ! git diff-index --quiet HEAD; then
|
||||||
|
git config user.email maintainers@solana.com
|
||||||
|
git config user.name "$(basename "$0")"
|
||||||
|
git commit -m "gitbook-cage update $(date -Is)"
|
||||||
|
git push -f git@github.com:solana-labs/solana-gitbook-cage.git HEAD:refs/heads/"$CI_BRANCH"
|
||||||
|
# pop off the local commit
|
||||||
|
git reset --hard HEAD~
|
||||||
|
fi
|
||||||
|
)
|
||||||
|
else
|
||||||
|
echo CI_BRANCH not set
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -45,16 +45,7 @@ linux)
|
|||||||
TARGET=x86_64-unknown-linux-gnu
|
TARGET=x86_64-unknown-linux-gnu
|
||||||
;;
|
;;
|
||||||
windows)
|
windows)
|
||||||
TARGET=x86_64-pc-windows-msvc
|
TARGET=x86_64-pc-windows-gnu
|
||||||
# Enable symlinks used by some build.rs files
|
|
||||||
# source: https://stackoverflow.com/a/52097145/10242004
|
|
||||||
(
|
|
||||||
set -x
|
|
||||||
git --version
|
|
||||||
git config core.symlinks true
|
|
||||||
find . -type l -delete
|
|
||||||
git reset --hard
|
|
||||||
)
|
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo CI_OS_NAME unset
|
echo CI_OS_NAME unset
|
||||||
@@ -62,14 +53,11 @@ windows)
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
RELEASE_BASENAME="${RELEASE_BASENAME:=solana-release}"
|
|
||||||
TARBALL_BASENAME="${TARBALL_BASENAME:="$RELEASE_BASENAME"}"
|
|
||||||
|
|
||||||
echo --- Creating release tarball
|
echo --- Creating release tarball
|
||||||
(
|
(
|
||||||
set -x
|
set -x
|
||||||
rm -rf "${RELEASE_BASENAME:?}"/
|
rm -rf solana-release/
|
||||||
mkdir "${RELEASE_BASENAME}"/
|
mkdir solana-release/
|
||||||
|
|
||||||
COMMIT="$(git rev-parse HEAD)"
|
COMMIT="$(git rev-parse HEAD)"
|
||||||
|
|
||||||
@@ -77,18 +65,18 @@ echo --- Creating release tarball
|
|||||||
echo "channel: $CHANNEL_OR_TAG"
|
echo "channel: $CHANNEL_OR_TAG"
|
||||||
echo "commit: $COMMIT"
|
echo "commit: $COMMIT"
|
||||||
echo "target: $TARGET"
|
echo "target: $TARGET"
|
||||||
) > "${RELEASE_BASENAME}"/version.yml
|
) > solana-release/version.yml
|
||||||
|
|
||||||
# Make CHANNEL available to include in the software version information
|
# Make CHANNEL available to include in the software version information
|
||||||
export CHANNEL
|
export CHANNEL
|
||||||
|
|
||||||
source ci/rust-version.sh stable
|
source ci/rust-version.sh stable
|
||||||
scripts/cargo-install-all.sh +"$rust_stable" "${RELEASE_BASENAME}"
|
scripts/cargo-install-all.sh +"$rust_stable" solana-release
|
||||||
|
|
||||||
tar cvf "${TARBALL_BASENAME}"-$TARGET.tar "${RELEASE_BASENAME}"
|
tar cvf solana-release-$TARGET.tar solana-release
|
||||||
bzip2 "${TARBALL_BASENAME}"-$TARGET.tar
|
bzip2 solana-release-$TARGET.tar
|
||||||
cp "${RELEASE_BASENAME}"/bin/solana-install-init solana-install-init-$TARGET
|
cp solana-release/bin/solana-install-init solana-install-init-$TARGET
|
||||||
cp "${RELEASE_BASENAME}"/version.yml "${TARBALL_BASENAME}"-$TARGET.yml
|
cp solana-release/version.yml solana-release-$TARGET.yml
|
||||||
)
|
)
|
||||||
|
|
||||||
# Metrics tarball is platform agnostic, only publish it from Linux
|
# Metrics tarball is platform agnostic, only publish it from Linux
|
||||||
@@ -106,7 +94,7 @@ fi
|
|||||||
|
|
||||||
source ci/upload-ci-artifact.sh
|
source ci/upload-ci-artifact.sh
|
||||||
|
|
||||||
for file in "${TARBALL_BASENAME}"-$TARGET.tar.bz2 "${TARBALL_BASENAME}"-$TARGET.yml solana-install-init-"$TARGET"* $MAYBE_TARBALLS; do
|
for file in solana-release-$TARGET.tar.bz2 solana-release-$TARGET.yml solana-install-init-"$TARGET"* $MAYBE_TARBALLS; do
|
||||||
if [[ -n $DO_NOT_PUBLISH_TAR ]]; then
|
if [[ -n $DO_NOT_PUBLISH_TAR ]]; then
|
||||||
upload-ci-artifact "$file"
|
upload-ci-artifact "$file"
|
||||||
echo "Skipped $file due to DO_NOT_PUBLISH_TAR"
|
echo "Skipped $file due to DO_NOT_PUBLISH_TAR"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ source multinode-demo/common.sh
|
|||||||
|
|
||||||
rm -rf config/run/init-completed config/ledger config/snapshot-ledger
|
rm -rf config/run/init-completed config/ledger config/snapshot-ledger
|
||||||
|
|
||||||
timeout 120 ./run.sh &
|
timeout 15 ./run.sh &
|
||||||
pid=$!
|
pid=$!
|
||||||
|
|
||||||
attempts=20
|
attempts=20
|
||||||
@@ -19,16 +19,13 @@ while [[ ! -f config/run/init-completed ]]; do
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
snapshot_slot=1
|
while [[ $($solana_cli slot --commitment recent) -eq 0 ]]; do
|
||||||
|
|
||||||
# wait a bit longer than snapshot_slot
|
|
||||||
while [[ $($solana_cli --url http://localhost:8899 slot --commitment recent) -le $((snapshot_slot + 1)) ]]; do
|
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
curl -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1, "method":"validatorExit"}' http://localhost:8899
|
curl -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1, "method":"validatorExit"}' http://localhost:8899
|
||||||
|
|
||||||
wait $pid
|
wait $pid
|
||||||
|
|
||||||
$solana_ledger_tool create-snapshot --ledger config/ledger "$snapshot_slot" config/snapshot-ledger
|
$solana_ledger_tool create-snapshot --ledger config/ledger 1 config/snapshot-ledger
|
||||||
cp config/ledger/genesis.tar.bz2 config/snapshot-ledger
|
cp config/ledger/genesis.tar.bz2 config/snapshot-ledger
|
||||||
$solana_ledger_tool verify --ledger config/snapshot-ledger
|
$solana_ledger_tool verify --ledger config/snapshot-ledger
|
||||||
|
|||||||
@@ -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.45.1
|
stable_version=1.43.0
|
||||||
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-07-27
|
nightly_version=2020-04-23
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
@@ -38,8 +38,7 @@ export rust_nightly_docker_image=solanalabs/rust-nightly:"$nightly_version"
|
|||||||
|
|
||||||
rustup_install() {
|
rustup_install() {
|
||||||
declare toolchain=$1
|
declare toolchain=$1
|
||||||
if ! cargo +"$toolchain" -V > /dev/null; then
|
if ! cargo +"$toolchain" -V; then
|
||||||
echo "$0: Missing toolchain? Installing...: $toolchain" >&2
|
|
||||||
rustup install "$toolchain"
|
rustup install "$toolchain"
|
||||||
cargo +"$toolchain" -V
|
cargo +"$toolchain" -V
|
||||||
fi
|
fi
|
||||||
@@ -59,7 +58,7 @@ export rust_nightly_docker_image=solanalabs/rust-nightly:"$nightly_version"
|
|||||||
rustup_install "$rust_nightly"
|
rustup_install "$rust_nightly"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "$0: Note: ignoring unknown argument: $1" >&2
|
echo "Note: ignoring unknown argument: $1"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,25 @@
|
|||||||
set -e
|
set -e
|
||||||
cd "$(dirname "$0")/.."
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
annotate() {
|
||||||
|
${BUILDKITE:-false} && {
|
||||||
|
buildkite-agent annotate "$@"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ci/affects-files.sh \
|
||||||
|
.rs$ \
|
||||||
|
Cargo.lock$ \
|
||||||
|
Cargo.toml$ \
|
||||||
|
^ci/rust-version.sh \
|
||||||
|
^ci/test-bench.sh \
|
||||||
|
|| {
|
||||||
|
annotate --style info --context test-bench \
|
||||||
|
"Bench skipped as no .rs files were modified"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
source ci/_
|
source ci/_
|
||||||
source ci/upload-ci-artifact.sh
|
source ci/upload-ci-artifact.sh
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
cd "$(dirname "$0")/.."
|
cd "$(dirname "$0")/.."
|
||||||
@@ -7,55 +6,33 @@ cd "$(dirname "$0")/.."
|
|||||||
source ci/_
|
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)"
|
|
||||||
|
|
||||||
echo --- build environment
|
|
||||||
(
|
|
||||||
set -x
|
|
||||||
|
|
||||||
rustup run "$rust_stable" rustc --version --verbose
|
|
||||||
rustup run "$rust_nightly" rustc --version --verbose
|
|
||||||
|
|
||||||
cargo +"$rust_stable" --version --verbose
|
|
||||||
cargo +"$rust_nightly" --version --verbose
|
|
||||||
|
|
||||||
cargo +"$rust_stable" clippy --version --verbose
|
|
||||||
cargo +"$rust_nightly" clippy --version --verbose
|
|
||||||
|
|
||||||
# audit is done only with stable
|
|
||||||
cargo +"$rust_stable" audit --version
|
|
||||||
)
|
|
||||||
|
|
||||||
export RUST_BACKTRACE=1
|
export RUST_BACKTRACE=1
|
||||||
export RUSTFLAGS="-D warnings -A incomplete_features"
|
export RUSTFLAGS="-D warnings"
|
||||||
|
|
||||||
# Only force up-to-date lock files on edge
|
# Look for failed mergify.io backports
|
||||||
if [[ $CI_BASE_BRANCH = "$EDGE_CHANNEL" ]]; then
|
_ git show HEAD --check --oneline
|
||||||
# Exclude --benches as it's not available in rust stable yet
|
|
||||||
if _ scripts/cargo-for-all-lock-files.sh +"$rust_stable" check --locked --tests --bins --examples; then
|
if _ scripts/cargo-for-all-lock-files.sh +"$rust_nightly" check --locked --all-targets; then
|
||||||
true
|
true
|
||||||
else
|
else
|
||||||
check_status=$?
|
check_status=$?
|
||||||
echo "$0: Some Cargo.lock might be outdated; sync them (or just be a compilation error?)" >&2
|
echo "Some Cargo.lock is outdated; please update them as well"
|
||||||
echo "$0: protip: $ ./scripts/cargo-for-all-lock-files.sh [--ignore-exit-code] ... \\" >&2
|
echo "protip: you can use ./scripts/cargo-for-all-lock-files.sh update ..."
|
||||||
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
|
||||||
else
|
|
||||||
echo "Note: cargo-for-all-lock-files.sh skipped because $CI_BASE_BRANCH != $EDGE_CHANNEL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Ensure nightly and --benches
|
|
||||||
_ scripts/cargo-for-all-lock-files.sh +"$rust_nightly" check --locked --all-targets
|
|
||||||
|
|
||||||
_ ci/order-crates-for-publishing.py
|
|
||||||
_ cargo +"$rust_stable" fmt --all -- --check
|
_ cargo +"$rust_stable" fmt --all -- --check
|
||||||
|
|
||||||
# -Z... is needed because of clippy bug: https://github.com/rust-lang/rust-clippy/issues/4612
|
_ cargo +"$rust_stable" clippy --version
|
||||||
# run nightly clippy for `sdk/` as there's a moderate amount of nightly-only code there
|
_ cargo +"$rust_stable" clippy --workspace -- --deny=warnings
|
||||||
_ cargo +"$rust_nightly" clippy -Zunstable-options --workspace --all-targets -- --deny=warnings
|
|
||||||
|
|
||||||
|
_ cargo +"$rust_stable" audit --version
|
||||||
_ scripts/cargo-for-all-lock-files.sh +"$rust_stable" audit --ignore RUSTSEC-2020-0002 --ignore RUSTSEC-2020-0008
|
_ scripts/cargo-for-all-lock-files.sh +"$rust_stable" audit --ignore RUSTSEC-2020-0002 --ignore RUSTSEC-2020-0008
|
||||||
|
_ ci/nits.sh
|
||||||
|
_ ci/order-crates-for-publishing.py
|
||||||
|
_ docs/build.sh
|
||||||
|
_ ci/check-ssh-keys.sh
|
||||||
|
|
||||||
{
|
{
|
||||||
cd programs/bpf
|
cd programs/bpf
|
||||||
@@ -66,6 +43,7 @@ _ scripts/cargo-for-all-lock-files.sh +"$rust_stable" audit --ignore RUSTSEC-202
|
|||||||
cd "$project"
|
cd "$project"
|
||||||
_ cargo +"$rust_stable" fmt -- --check
|
_ cargo +"$rust_stable" fmt -- --check
|
||||||
_ cargo +"$rust_nightly" test
|
_ cargo +"$rust_nightly" test
|
||||||
|
_ cargo +"$rust_nightly" clippy --version
|
||||||
_ cargo +"$rust_nightly" clippy -- --deny=warnings --allow=clippy::missing_safety_doc
|
_ cargo +"$rust_nightly" clippy -- --deny=warnings --allow=clippy::missing_safety_doc
|
||||||
)
|
)
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -8,14 +8,23 @@ annotate() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ci/affects-files.sh \
|
||||||
|
.rs$ \
|
||||||
|
Cargo.lock$ \
|
||||||
|
Cargo.toml$ \
|
||||||
|
^ci/rust-version.sh \
|
||||||
|
^ci/test-coverage.sh \
|
||||||
|
^scripts/coverage.sh \
|
||||||
|
|| {
|
||||||
|
annotate --style info --context test-coverage \
|
||||||
|
"Coverage skipped as no .rs files were modified"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
source ci/upload-ci-artifact.sh
|
source ci/upload-ci-artifact.sh
|
||||||
source scripts/ulimit-n.sh
|
source scripts/ulimit-n.sh
|
||||||
|
|
||||||
scripts/coverage.sh "$@"
|
scripts/coverage.sh
|
||||||
|
|
||||||
if [[ -z $CI ]]; then
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
report=coverage-"${CI_COMMIT:0:9}".tar.gz
|
report=coverage-"${CI_COMMIT:0:9}".tar.gz
|
||||||
mv target/cov/report.tar.gz "$report"
|
mv target/cov/report.tar.gz "$report"
|
||||||
|
|||||||
1
ci/test-move.sh
Symbolic link
1
ci/test-move.sh
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
test-stable.sh
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
cd "$(dirname "$0")/.."
|
|
||||||
|
|
||||||
source ci/_
|
|
||||||
|
|
||||||
(
|
|
||||||
echo --- git diff --check
|
|
||||||
set -x
|
|
||||||
# Look for failed mergify.io backports by searching leftover conflict markers
|
|
||||||
# Also check for any trailing whitespaces!
|
|
||||||
git fetch origin "$CI_BASE_BRANCH"
|
|
||||||
git diff "$(git merge-base HEAD "origin/$CI_BASE_BRANCH")..HEAD" --check --oneline
|
|
||||||
)
|
|
||||||
|
|
||||||
echo
|
|
||||||
|
|
||||||
_ ci/nits.sh
|
|
||||||
_ ci/check-ssh-keys.sh
|
|
||||||
|
|
||||||
echo --- ok
|
|
||||||
@@ -13,6 +13,15 @@ annotate() {
|
|||||||
# Run the appropriate test based on entrypoint
|
# Run the appropriate test based on entrypoint
|
||||||
testName=$(basename "$0" .sh)
|
testName=$(basename "$0" .sh)
|
||||||
|
|
||||||
|
# Skip if only the docs have been modified
|
||||||
|
ci/affects-files.sh \
|
||||||
|
\!^docs/ \
|
||||||
|
|| {
|
||||||
|
annotate --style info \
|
||||||
|
"Skipped $testName as only docs/ files were modified"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
source ci/rust-version.sh stable
|
source ci/rust-version.sh stable
|
||||||
|
|
||||||
export RUST_BACKTRACE=1
|
export RUST_BACKTRACE=1
|
||||||
@@ -30,16 +39,35 @@ test -d target/release/bpf && find target/release/bpf -name '*.d' -delete
|
|||||||
rm -rf target/xargo # Issue #3105
|
rm -rf target/xargo # Issue #3105
|
||||||
|
|
||||||
# Limit compiler jobs to reduce memory usage
|
# Limit compiler jobs to reduce memory usage
|
||||||
# on machines with 2gb/thread of memory
|
# on machines with 1gb/thread of memory
|
||||||
NPROC=$(nproc)
|
NPROC=$(nproc)
|
||||||
NPROC=$((NPROC>14 ? 14 : NPROC))
|
NPROC=$((NPROC>16 ? 16 : NPROC))
|
||||||
|
|
||||||
echo "Executing $testName"
|
echo "Executing $testName"
|
||||||
case $testName in
|
case $testName in
|
||||||
test-stable)
|
test-stable)
|
||||||
_ cargo +"$rust_stable" test --jobs "$NPROC" --all --exclude solana-local-cluster ${V:+--verbose} -- --nocapture
|
_ cargo +"$rust_stable" test --jobs "$NPROC" --all --exclude solana-local-cluster ${V:+--verbose} -- --nocapture
|
||||||
|
_ cargo +"$rust_stable" test --manifest-path bench-tps/Cargo.toml --features=move ${V:+--verbose} test_bench_tps_local_cluster_move -- --nocapture
|
||||||
;;
|
;;
|
||||||
test-stable-perf)
|
test-stable-perf)
|
||||||
|
ci/affects-files.sh \
|
||||||
|
.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/ \
|
||||||
|
|| {
|
||||||
|
annotate --style info \
|
||||||
|
"Skipped $testName as no relevant files were modified"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
# BPF program tests
|
# BPF program tests
|
||||||
_ make -C programs/bpf/c tests
|
_ make -C programs/bpf/c tests
|
||||||
_ cargo +"$rust_stable" test \
|
_ cargo +"$rust_stable" test \
|
||||||
@@ -64,7 +92,27 @@ test-stable-perf)
|
|||||||
|
|
||||||
_ cargo +"$rust_stable" build --bins ${V:+--verbose}
|
_ cargo +"$rust_stable" build --bins ${V:+--verbose}
|
||||||
_ cargo +"$rust_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 +"$rust_stable" run --manifest-path poh-bench/Cargo.toml ${V:+--verbose} -- --hashes-per-tick 10
|
;;
|
||||||
|
test-move)
|
||||||
|
ci/affects-files.sh \
|
||||||
|
Cargo.lock$ \
|
||||||
|
Cargo.toml$ \
|
||||||
|
^ci/rust-version.sh \
|
||||||
|
^ci/test-stable.sh \
|
||||||
|
^ci/test-move.sh \
|
||||||
|
^programs/move_loader \
|
||||||
|
^programs/librapay \
|
||||||
|
^logger/ \
|
||||||
|
^runtime/ \
|
||||||
|
^sdk/ \
|
||||||
|
|| {
|
||||||
|
annotate --style info \
|
||||||
|
"Skipped $testName as no relevant files were modified"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
_ cargo +"$rust_stable" test --manifest-path programs/move_loader/Cargo.toml ${V:+--verbose} -- --nocapture
|
||||||
|
_ cargo +"$rust_stable" test --manifest-path programs/librapay/Cargo.toml ${V:+--verbose} -- --nocapture
|
||||||
|
exit 0
|
||||||
;;
|
;;
|
||||||
test-local-cluster)
|
test-local-cluster)
|
||||||
_ cargo +"$rust_stable" build --release --bins ${V:+--verbose}
|
_ cargo +"$rust_stable" build --release --bins ${V:+--verbose}
|
||||||
|
|||||||
@@ -23,14 +23,10 @@ if [[ -z $CI_TAG ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Force CI_REPO_SLUG since sometimes
|
if [[ -z $CI_REPO_SLUG ]]; then
|
||||||
# BUILDKITE_TRIGGERED_FROM_BUILD_PIPELINE_SLUG is not set correctly, causing the
|
echo Error: CI_REPO_SLUG not defined
|
||||||
# artifact upload to fail
|
exit 1
|
||||||
CI_REPO_SLUG=solana-labs/solana
|
fi
|
||||||
#if [[ -z $CI_REPO_SLUG ]]; then
|
|
||||||
# echo Error: CI_REPO_SLUG not defined
|
|
||||||
# exit 1
|
|
||||||
#fi
|
|
||||||
|
|
||||||
releaseId=$( \
|
releaseId=$( \
|
||||||
curl -s "https://api.github.com/repos/$CI_REPO_SLUG/releases/tags/$CI_TAG" \
|
curl -s "https://api.github.com/repos/$CI_REPO_SLUG/releases/tags/$CI_TAG" \
|
||||||
@@ -42,7 +38,6 @@ echo "Github release id for $CI_TAG is $releaseId"
|
|||||||
for file in "$@"; do
|
for file in "$@"; do
|
||||||
echo "--- Uploading $file to tag $CI_TAG of $CI_REPO_SLUG"
|
echo "--- Uploading $file to tag $CI_TAG of $CI_REPO_SLUG"
|
||||||
curl \
|
curl \
|
||||||
--verbose \
|
|
||||||
--data-binary @"$file" \
|
--data-binary @"$file" \
|
||||||
-H "Authorization: token $GITHUB_TOKEN" \
|
-H "Authorization: token $GITHUB_TOKEN" \
|
||||||
-H "Content-Type: application/octet-stream" \
|
-H "Content-Type: application/octet-stream" \
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "solana-clap-utils"
|
name = "solana-clap-utils"
|
||||||
version = "1.3.6"
|
version = "1.2.0"
|
||||||
description = "Solana utilities for the clap"
|
description = "Solana utilities for the clap"
|
||||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
homepage = "https://solana.com/"
|
homepage = "https://solana.com/"
|
||||||
@@ -11,9 +11,9 @@ 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.3.6" }
|
solana-remote-wallet = { path = "../remote-wallet", version = "1.2.0" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.3.6" }
|
solana-sdk = { path = "../sdk", version = "1.2.0" }
|
||||||
thiserror = "1.0.20"
|
thiserror = "1.0.11"
|
||||||
tiny-bip39 = "0.7.0"
|
tiny-bip39 = "0.7.0"
|
||||||
url = "2.1.0"
|
url = "2.1.0"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
|
|||||||
@@ -6,86 +6,50 @@ use solana_sdk::{
|
|||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{read_keypair_file, Signature},
|
signature::{read_keypair_file, Signature},
|
||||||
};
|
};
|
||||||
use std::fmt::Display;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
fn is_parsable_generic<U, T>(string: T) -> Result<(), String>
|
|
||||||
where
|
|
||||||
T: AsRef<str> + Display,
|
|
||||||
U: FromStr,
|
|
||||||
U::Err: Display,
|
|
||||||
{
|
|
||||||
string
|
|
||||||
.as_ref()
|
|
||||||
.parse::<U>()
|
|
||||||
.map(|_| ())
|
|
||||||
.map_err(|err| format!("error parsing '{}': {}", string, err))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return an error if string cannot be parsed as type T.
|
|
||||||
// Takes a String to avoid second type parameter when used as a clap validator
|
|
||||||
pub fn is_parsable<T>(string: String) -> Result<(), String>
|
|
||||||
where
|
|
||||||
T: FromStr,
|
|
||||||
T::Err: Display,
|
|
||||||
{
|
|
||||||
is_parsable_generic::<T, String>(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return an error if a pubkey cannot be parsed.
|
// Return an error if a pubkey cannot be parsed.
|
||||||
pub fn is_pubkey<T>(string: T) -> Result<(), String>
|
pub fn is_pubkey(string: String) -> Result<(), String> {
|
||||||
where
|
match string.parse::<Pubkey>() {
|
||||||
T: AsRef<str> + Display,
|
Ok(_) => Ok(()),
|
||||||
{
|
Err(err) => Err(format!("{}", err)),
|
||||||
is_parsable_generic::<Pubkey, _>(string)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return an error if a hash cannot be parsed.
|
// Return an error if a hash cannot be parsed.
|
||||||
pub fn is_hash<T>(string: T) -> Result<(), String>
|
pub fn is_hash(string: String) -> Result<(), String> {
|
||||||
where
|
match string.parse::<Hash>() {
|
||||||
T: AsRef<str> + Display,
|
Ok(_) => Ok(()),
|
||||||
{
|
Err(err) => Err(format!("{}", err)),
|
||||||
is_parsable_generic::<Hash, _>(string)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return an error if a keypair file cannot be parsed.
|
// Return an error if a keypair file cannot be parsed.
|
||||||
pub fn is_keypair<T>(string: T) -> Result<(), String>
|
pub fn is_keypair(string: String) -> Result<(), String> {
|
||||||
where
|
read_keypair_file(&string)
|
||||||
T: AsRef<str> + Display,
|
|
||||||
{
|
|
||||||
read_keypair_file(string.as_ref())
|
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
.map_err(|err| format!("{}", err))
|
.map_err(|err| format!("{}", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return an error if a keypair file cannot be parsed
|
// Return an error if a keypair file cannot be parsed
|
||||||
pub fn is_keypair_or_ask_keyword<T>(string: T) -> Result<(), String>
|
pub fn is_keypair_or_ask_keyword(string: String) -> Result<(), String> {
|
||||||
where
|
if string.as_str() == ASK_KEYWORD {
|
||||||
T: AsRef<str> + Display,
|
|
||||||
{
|
|
||||||
if string.as_ref() == ASK_KEYWORD {
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
read_keypair_file(string.as_ref())
|
read_keypair_file(&string)
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
.map_err(|err| format!("{}", err))
|
.map_err(|err| format!("{}", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return an error if string cannot be parsed as pubkey string or keypair file location
|
// Return an error if string cannot be parsed as pubkey string or keypair file location
|
||||||
pub fn is_pubkey_or_keypair<T>(string: T) -> Result<(), String>
|
pub fn is_pubkey_or_keypair(string: String) -> Result<(), String> {
|
||||||
where
|
is_pubkey(string.clone()).or_else(|_| is_keypair(string))
|
||||||
T: AsRef<str> + Display,
|
|
||||||
{
|
|
||||||
is_pubkey(string.as_ref()).or_else(|_| is_keypair(string))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return an error if string cannot be parsed as a pubkey string, or a valid Signer that can
|
// Return an error if string cannot be parsed as a pubkey string, or a valid Signer that can
|
||||||
// produce a pubkey()
|
// produce a pubkey()
|
||||||
pub fn is_valid_pubkey<T>(string: T) -> Result<(), String>
|
pub fn is_valid_pubkey(string: String) -> Result<(), String> {
|
||||||
where
|
match parse_keypair_path(&string) {
|
||||||
T: AsRef<str> + Display,
|
|
||||||
{
|
|
||||||
match parse_keypair_path(string.as_ref()) {
|
|
||||||
KeypairUrl::Filepath(path) => is_keypair(path),
|
KeypairUrl::Filepath(path) => is_keypair(path),
|
||||||
_ => Ok(()),
|
_ => Ok(()),
|
||||||
}
|
}
|
||||||
@@ -99,19 +63,13 @@ where
|
|||||||
// when paired with an offline `--signer` argument to provide a Presigner (pubkey + signature).
|
// when paired with an offline `--signer` argument to provide a Presigner (pubkey + signature).
|
||||||
// Clap validators can't check multiple fields at once, so the verification that a `--signer` is
|
// Clap validators can't check multiple fields at once, so the verification that a `--signer` is
|
||||||
// also provided and correct happens in parsing, not in validation.
|
// also provided and correct happens in parsing, not in validation.
|
||||||
pub fn is_valid_signer<T>(string: T) -> Result<(), String>
|
pub fn is_valid_signer(string: String) -> Result<(), String> {
|
||||||
where
|
|
||||||
T: AsRef<str> + Display,
|
|
||||||
{
|
|
||||||
is_valid_pubkey(string)
|
is_valid_pubkey(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return an error if string cannot be parsed as pubkey=signature string
|
// Return an error if string cannot be parsed as pubkey=signature string
|
||||||
pub fn is_pubkey_sig<T>(string: T) -> Result<(), String>
|
pub fn is_pubkey_sig(string: String) -> Result<(), String> {
|
||||||
where
|
let mut signer = string.split('=');
|
||||||
T: AsRef<str> + Display,
|
|
||||||
{
|
|
||||||
let mut signer = string.as_ref().split('=');
|
|
||||||
match Pubkey::from_str(
|
match Pubkey::from_str(
|
||||||
signer
|
signer
|
||||||
.next()
|
.next()
|
||||||
@@ -132,11 +90,8 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return an error if a url cannot be parsed.
|
// Return an error if a url cannot be parsed.
|
||||||
pub fn is_url<T>(string: T) -> Result<(), String>
|
pub fn is_url(string: String) -> Result<(), String> {
|
||||||
where
|
match url::Url::parse(&string) {
|
||||||
T: AsRef<str> + Display,
|
|
||||||
{
|
|
||||||
match url::Url::parse(string.as_ref()) {
|
|
||||||
Ok(url) => {
|
Ok(url) => {
|
||||||
if url.has_host() {
|
if url.has_host() {
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -148,26 +103,20 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_slot<T>(slot: T) -> Result<(), String>
|
pub fn is_slot(slot: String) -> Result<(), String> {
|
||||||
where
|
slot.parse::<Slot>()
|
||||||
T: AsRef<str> + Display,
|
.map(|_| ())
|
||||||
{
|
.map_err(|e| format!("{}", e))
|
||||||
is_parsable_generic::<Slot, _>(slot)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_port<T>(port: T) -> Result<(), String>
|
pub fn is_port(port: String) -> Result<(), String> {
|
||||||
where
|
port.parse::<u16>()
|
||||||
T: AsRef<str> + Display,
|
.map(|_| ())
|
||||||
{
|
.map_err(|e| format!("{}", e))
|
||||||
is_parsable_generic::<u16, _>(port)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_valid_percentage<T>(percentage: T) -> Result<(), String>
|
pub fn is_valid_percentage(percentage: String) -> Result<(), String> {
|
||||||
where
|
|
||||||
T: AsRef<str> + Display,
|
|
||||||
{
|
|
||||||
percentage
|
percentage
|
||||||
.as_ref()
|
|
||||||
.parse::<u8>()
|
.parse::<u8>()
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
format!(
|
format!(
|
||||||
@@ -187,11 +136,8 @@ where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_amount<T>(amount: T) -> Result<(), String>
|
pub fn is_amount(amount: String) -> Result<(), String> {
|
||||||
where
|
if amount.parse::<u64>().is_ok() || amount.parse::<f64>().is_ok() {
|
||||||
T: AsRef<str> + Display,
|
|
||||||
{
|
|
||||||
if amount.as_ref().parse::<u64>().is_ok() || amount.as_ref().parse::<f64>().is_ok() {
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(format!(
|
Err(format!(
|
||||||
@@ -201,14 +147,8 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_amount_or_all<T>(amount: T) -> Result<(), String>
|
pub fn is_amount_or_all(amount: String) -> Result<(), String> {
|
||||||
where
|
if amount.parse::<u64>().is_ok() || amount.parse::<f64>().is_ok() || amount == "ALL" {
|
||||||
T: AsRef<str> + Display,
|
|
||||||
{
|
|
||||||
if amount.as_ref().parse::<u64>().is_ok()
|
|
||||||
|| amount.as_ref().parse::<f64>().is_ok()
|
|
||||||
|| amount.as_ref() == "ALL"
|
|
||||||
{
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(format!(
|
Err(format!(
|
||||||
@@ -218,20 +158,14 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_rfc3339_datetime<T>(value: T) -> Result<(), String>
|
pub fn is_rfc3339_datetime(value: String) -> Result<(), String> {
|
||||||
where
|
DateTime::parse_from_rfc3339(&value)
|
||||||
T: AsRef<str> + Display,
|
|
||||||
{
|
|
||||||
DateTime::parse_from_rfc3339(value.as_ref())
|
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
.map_err(|e| format!("{}", e))
|
.map_err(|e| format!("{}", e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_derivation<T>(value: T) -> Result<(), String>
|
pub fn is_derivation(value: String) -> Result<(), String> {
|
||||||
where
|
let value = value.replace("'", "");
|
||||||
T: AsRef<str> + Display,
|
|
||||||
{
|
|
||||||
let value = value.as_ref().replace("'", "");
|
|
||||||
let mut parts = value.split('/');
|
let mut parts = value.split('/');
|
||||||
let account = parts.next().unwrap();
|
let account = parts.next().unwrap();
|
||||||
account
|
account
|
||||||
@@ -263,14 +197,14 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_derivation() {
|
fn test_is_derivation() {
|
||||||
assert_eq!(is_derivation("2"), Ok(()));
|
assert_eq!(is_derivation("2".to_string()), Ok(()));
|
||||||
assert_eq!(is_derivation("0"), Ok(()));
|
assert_eq!(is_derivation("0".to_string()), Ok(()));
|
||||||
assert_eq!(is_derivation("65537"), Ok(()));
|
assert_eq!(is_derivation("65537".to_string()), Ok(()));
|
||||||
assert_eq!(is_derivation("0/2"), Ok(()));
|
assert_eq!(is_derivation("0/2".to_string()), Ok(()));
|
||||||
assert_eq!(is_derivation("0'/2'"), Ok(()));
|
assert_eq!(is_derivation("0'/2'".to_string()), Ok(()));
|
||||||
assert!(is_derivation("a").is_err());
|
assert!(is_derivation("a".to_string()).is_err());
|
||||||
assert!(is_derivation("4294967296").is_err());
|
assert!(is_derivation("4294967296".to_string()).is_err());
|
||||||
assert!(is_derivation("a/b").is_err());
|
assert!(is_derivation("a/b".to_string()).is_err());
|
||||||
assert!(is_derivation("0/4294967296").is_err());
|
assert!(is_derivation("0/4294967296".to_string()).is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
[package]
|
[package]
|
||||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "solana-cli-config"
|
name = "solana-cli-config"
|
||||||
description = "Blockchain, Rebuilt for Scale"
|
description = "Blockchain, Rebuilt for Scale"
|
||||||
version = "1.3.6"
|
version = "1.2.0"
|
||||||
repository = "https://github.com/solana-labs/solana"
|
repository = "https://github.com/solana-labs/solana"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
homepage = "https://solana.com/"
|
homepage = "https://solana.com/"
|
||||||
@@ -11,9 +11,9 @@ homepage = "https://solana.com/"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
dirs = "2.0.2"
|
dirs = "2.0.2"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
serde = "1.0.112"
|
serde = "1.0.110"
|
||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
serde_yaml = "0.8.13"
|
serde_yaml = "0.8.12"
|
||||||
url = "2.1.1"
|
url = "2.1.1"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Wallet settings that can be configured for long-term use
|
// Wallet settings that can be configured for long-term use
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::{collections::HashMap, io, path::Path};
|
use std::io;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
@@ -17,9 +17,6 @@ pub struct Config {
|
|||||||
pub json_rpc_url: String,
|
pub json_rpc_url: String,
|
||||||
pub websocket_url: String,
|
pub websocket_url: String,
|
||||||
pub keypair_path: String,
|
pub keypair_path: String,
|
||||||
|
|
||||||
#[serde(default)]
|
|
||||||
pub address_labels: HashMap<String, String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
@@ -35,17 +32,10 @@ impl Default for Config {
|
|||||||
// `Config::compute_websocket_url(&json_rpc_url)`
|
// `Config::compute_websocket_url(&json_rpc_url)`
|
||||||
let websocket_url = "".to_string();
|
let websocket_url = "".to_string();
|
||||||
|
|
||||||
let mut address_labels = HashMap::new();
|
|
||||||
address_labels.insert(
|
|
||||||
"11111111111111111111111111111111".to_string(),
|
|
||||||
"System Program".to_string(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
json_rpc_url,
|
json_rpc_url,
|
||||||
websocket_url,
|
websocket_url,
|
||||||
keypair_path,
|
keypair_path,
|
||||||
address_labels,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -75,35 +65,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_or_known_default().unwrap_or(80);
|
|
||||||
url.set_port(Some(port + 2)).expect("unable to set port");
|
|
||||||
url.to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn import_address_labels<P>(&mut self, filename: P) -> Result<(), io::Error>
|
|
||||||
where
|
|
||||||
P: AsRef<Path>,
|
|
||||||
{
|
|
||||||
let imports: HashMap<String, String> = crate::load_config_file(filename)?;
|
|
||||||
for (address, label) in imports.into_iter() {
|
|
||||||
self.address_labels.insert(address, label);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn export_address_labels<P>(&self, filename: P) -> Result<(), io::Error>
|
|
||||||
where
|
|
||||||
P: AsRef<Path>,
|
|
||||||
{
|
|
||||||
crate::save_config_file(&self.address_labels, filename)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -133,28 +94,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:82/".to_string()
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
Config::compute_rpc_banks_url(&"https://devnet.solana.com"),
|
|
||||||
"https://devnet.solana.com:445/".to_string()
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
Config::compute_rpc_banks_url(&"http://example.com:8899"),
|
|
||||||
"http://example.com:8901/".to_string()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Config::compute_rpc_banks_url(&"https://example.com:1234"),
|
|
||||||
"https://example.com:1236/".to_string()
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(Config::compute_rpc_banks_url(&"garbage"), String::new());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,55 +1,54 @@
|
|||||||
[package]
|
[package]
|
||||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "solana-cli"
|
name = "solana-cli"
|
||||||
description = "Blockchain, Rebuilt for Scale"
|
description = "Blockchain, Rebuilt for Scale"
|
||||||
version = "1.3.6"
|
version = "1.2.0"
|
||||||
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]
|
||||||
bincode = "1.3.1"
|
bincode = "1.2.1"
|
||||||
bs58 = "0.3.1"
|
bs58 = "0.3.1"
|
||||||
chrono = { version = "0.4.11", features = ["serde"] }
|
chrono = { version = "0.4.11", features = ["serde"] }
|
||||||
clap = "2.33.1"
|
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.4", features = ["termination"] }
|
||||||
console = "0.11.3"
|
console = "0.10.1"
|
||||||
dirs = "2.0.2"
|
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.14.0"
|
||||||
humantime = "2.0.1"
|
humantime = "2.0.0"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
pretty-hex = "0.1.1"
|
pretty-hex = "0.1.1"
|
||||||
reqwest = { version = "0.10.6", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
reqwest = { version = "0.10.4", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||||
serde = "1.0.112"
|
serde = "1.0.110"
|
||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
serde_json = "1.0.56"
|
serde_json = "1.0.53"
|
||||||
solana-account-decoder = { path = "../account-decoder", version = "1.3.6" }
|
solana-budget-program = { path = "../programs/budget", version = "1.2.0" }
|
||||||
solana-budget-program = { path = "../programs/budget", version = "1.3.6" }
|
solana-clap-utils = { path = "../clap-utils", version = "1.2.0" }
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "1.3.6" }
|
solana-cli-config = { path = "../cli-config", version = "1.2.0" }
|
||||||
solana-cli-config = { path = "../cli-config", version = "1.3.6" }
|
solana-client = { path = "../client", version = "1.2.0" }
|
||||||
solana-client = { path = "../client", version = "1.3.6" }
|
solana-config-program = { path = "../programs/config", version = "1.2.0" }
|
||||||
solana-config-program = { path = "../programs/config", version = "1.3.6" }
|
solana-faucet = { path = "../faucet", version = "1.2.0" }
|
||||||
solana-faucet = { path = "../faucet", version = "1.3.6" }
|
solana-logger = { path = "../logger", version = "1.2.0" }
|
||||||
solana-logger = { path = "../logger", version = "1.3.6" }
|
solana-net-utils = { path = "../net-utils", version = "1.2.0" }
|
||||||
solana-net-utils = { path = "../net-utils", version = "1.3.6" }
|
solana-remote-wallet = { path = "../remote-wallet", version = "1.2.0" }
|
||||||
solana-remote-wallet = { path = "../remote-wallet", version = "1.3.6" }
|
solana-runtime = { path = "../runtime", version = "1.2.0" }
|
||||||
solana-runtime = { path = "../runtime", version = "1.3.6" }
|
solana-sdk = { path = "../sdk", version = "1.2.0" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.3.6" }
|
solana-stake-program = { path = "../programs/stake", version = "1.2.0" }
|
||||||
solana-stake-program = { path = "../programs/stake", version = "1.3.6" }
|
solana-transaction-status = { path = "../transaction-status", version = "1.2.0" }
|
||||||
solana-transaction-status = { path = "../transaction-status", version = "1.3.6" }
|
solana-version = { path = "../version", version = "1.2.0" }
|
||||||
solana-version = { path = "../version", version = "1.3.6" }
|
solana-vote-program = { path = "../programs/vote", version = "1.2.0" }
|
||||||
solana-vote-program = { path = "../programs/vote", version = "1.3.6" }
|
solana-vote-signer = { path = "../vote-signer", version = "1.2.0" }
|
||||||
solana-vote-signer = { path = "../vote-signer", version = "1.3.6" }
|
thiserror = "1.0.19"
|
||||||
thiserror = "1.0.20"
|
|
||||||
url = "2.1.1"
|
url = "2.1.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
solana-core = { path = "../core", version = "1.3.6" }
|
solana-core = { path = "../core", version = "1.2.0" }
|
||||||
solana-budget-program = { path = "../programs/budget", version = "1.3.6" }
|
solana-budget-program = { path = "../programs/budget", version = "1.2.0" }
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ use solana_client::{
|
|||||||
rpc_client::RpcClient,
|
rpc_client::RpcClient,
|
||||||
};
|
};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
commitment_config::CommitmentConfig, fee_calculator::FeeCalculator, message::Message,
|
fee_calculator::FeeCalculator, message::Message, native_token::lamports_to_sol, pubkey::Pubkey,
|
||||||
native_token::lamports_to_sol, pubkey::Pubkey,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn check_account_for_fee(
|
pub fn check_account_for_fee(
|
||||||
@@ -17,46 +16,14 @@ pub fn check_account_for_fee(
|
|||||||
check_account_for_multiple_fees(rpc_client, account_pubkey, fee_calculator, &[message])
|
check_account_for_multiple_fees(rpc_client, account_pubkey, fee_calculator, &[message])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_account_for_fee_with_commitment(
|
|
||||||
rpc_client: &RpcClient,
|
|
||||||
account_pubkey: &Pubkey,
|
|
||||||
fee_calculator: &FeeCalculator,
|
|
||||||
message: &Message,
|
|
||||||
commitment: CommitmentConfig,
|
|
||||||
) -> Result<(), CliError> {
|
|
||||||
check_account_for_multiple_fees_with_commitment(
|
|
||||||
rpc_client,
|
|
||||||
account_pubkey,
|
|
||||||
fee_calculator,
|
|
||||||
&[message],
|
|
||||||
commitment,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_account_for_multiple_fees(
|
pub fn check_account_for_multiple_fees(
|
||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
account_pubkey: &Pubkey,
|
account_pubkey: &Pubkey,
|
||||||
fee_calculator: &FeeCalculator,
|
fee_calculator: &FeeCalculator,
|
||||||
messages: &[&Message],
|
messages: &[&Message],
|
||||||
) -> Result<(), CliError> {
|
|
||||||
check_account_for_multiple_fees_with_commitment(
|
|
||||||
rpc_client,
|
|
||||||
account_pubkey,
|
|
||||||
fee_calculator,
|
|
||||||
messages,
|
|
||||||
CommitmentConfig::default(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_account_for_multiple_fees_with_commitment(
|
|
||||||
rpc_client: &RpcClient,
|
|
||||||
account_pubkey: &Pubkey,
|
|
||||||
fee_calculator: &FeeCalculator,
|
|
||||||
messages: &[&Message],
|
|
||||||
commitment: CommitmentConfig,
|
|
||||||
) -> Result<(), CliError> {
|
) -> Result<(), CliError> {
|
||||||
let fee = calculate_fee(fee_calculator, messages);
|
let fee = calculate_fee(fee_calculator, messages);
|
||||||
if !check_account_for_balance_with_commitment(rpc_client, account_pubkey, fee, commitment)
|
if !check_account_for_balance(rpc_client, account_pubkey, fee)
|
||||||
.map_err(Into::<ClientError>::into)?
|
.map_err(Into::<ClientError>::into)?
|
||||||
{
|
{
|
||||||
return Err(CliError::InsufficientFundsForFee(lamports_to_sol(fee)));
|
return Err(CliError::InsufficientFundsForFee(lamports_to_sol(fee)));
|
||||||
@@ -76,23 +43,7 @@ pub fn check_account_for_balance(
|
|||||||
account_pubkey: &Pubkey,
|
account_pubkey: &Pubkey,
|
||||||
balance: u64,
|
balance: u64,
|
||||||
) -> ClientResult<bool> {
|
) -> ClientResult<bool> {
|
||||||
check_account_for_balance_with_commitment(
|
let lamports = rpc_client.get_balance(account_pubkey)?;
|
||||||
rpc_client,
|
|
||||||
account_pubkey,
|
|
||||||
balance,
|
|
||||||
CommitmentConfig::default(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_account_for_balance_with_commitment(
|
|
||||||
rpc_client: &RpcClient,
|
|
||||||
account_pubkey: &Pubkey,
|
|
||||||
balance: u64,
|
|
||||||
commitment: CommitmentConfig,
|
|
||||||
) -> ClientResult<bool> {
|
|
||||||
let lamports = rpc_client
|
|
||||||
.get_balance_with_commitment(account_pubkey, commitment)?
|
|
||||||
.value;
|
|
||||||
if lamports != 0 && lamports >= balance {
|
if lamports != 0 && lamports >= balance {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
@@ -137,11 +88,11 @@ mod tests {
|
|||||||
let pubkey0 = Pubkey::new(&[0; 32]);
|
let pubkey0 = Pubkey::new(&[0; 32]);
|
||||||
let pubkey1 = Pubkey::new(&[1; 32]);
|
let pubkey1 = Pubkey::new(&[1; 32]);
|
||||||
let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1);
|
let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1);
|
||||||
let message0 = Message::new(&[ix0], Some(&pubkey0));
|
let message0 = Message::new(&[ix0]);
|
||||||
|
|
||||||
let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1);
|
let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1);
|
||||||
let ix1 = system_instruction::transfer(&pubkey1, &pubkey0, 1);
|
let ix1 = system_instruction::transfer(&pubkey1, &pubkey0, 1);
|
||||||
let message1 = Message::new(&[ix0, ix1], Some(&pubkey0));
|
let message1 = Message::new(&[ix0, ix1]);
|
||||||
|
|
||||||
let mut mocks = HashMap::new();
|
let mut mocks = HashMap::new();
|
||||||
mocks.insert(RpcRequest::GetBalance, account_balance_response.clone());
|
mocks.insert(RpcRequest::GetBalance, account_balance_response.clone());
|
||||||
@@ -225,13 +176,13 @@ mod tests {
|
|||||||
let pubkey0 = Pubkey::new(&[0; 32]);
|
let pubkey0 = Pubkey::new(&[0; 32]);
|
||||||
let pubkey1 = Pubkey::new(&[1; 32]);
|
let pubkey1 = Pubkey::new(&[1; 32]);
|
||||||
let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1);
|
let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1);
|
||||||
let message0 = Message::new(&[ix0], Some(&pubkey0));
|
let message0 = Message::new(&[ix0]);
|
||||||
assert_eq!(calculate_fee(&fee_calculator, &[&message0]), 1);
|
assert_eq!(calculate_fee(&fee_calculator, &[&message0]), 1);
|
||||||
|
|
||||||
// Two messages, additive fees.
|
// Two messages, additive fees.
|
||||||
let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1);
|
let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1);
|
||||||
let ix1 = system_instruction::transfer(&pubkey1, &pubkey0, 1);
|
let ix1 = system_instruction::transfer(&pubkey1, &pubkey0, 1);
|
||||||
let message1 = Message::new(&[ix0, ix1], Some(&pubkey0));
|
let message1 = Message::new(&[ix0, ix1]);
|
||||||
assert_eq!(calculate_fee(&fee_calculator, &[&message0, &message1]), 3);
|
assert_eq!(calculate_fee(&fee_calculator, &[&message0, &message1]), 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
775
cli/src/cli.rs
775
cli/src/cli.rs
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,4 @@
|
|||||||
use crate::{
|
use crate::{cli::build_balance_message, display::writeln_name_value};
|
||||||
cli::build_balance_message,
|
|
||||||
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;
|
||||||
@@ -21,11 +18,7 @@ use solana_vote_program::{
|
|||||||
authorized_voters::AuthorizedVoters,
|
authorized_voters::AuthorizedVoters,
|
||||||
vote_state::{BlockTimestamp, Lockout},
|
vote_state::{BlockTimestamp, Lockout},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{collections::BTreeMap, fmt, time::Duration};
|
||||||
collections::{BTreeMap, HashMap},
|
|
||||||
fmt,
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
static WARNING: Emoji = Emoji("⚠️", "!");
|
static WARNING: Emoji = Emoji("⚠️", "!");
|
||||||
|
|
||||||
@@ -206,11 +199,6 @@ impl From<EpochInfo> 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)?;
|
||||||
writeln_name_value(
|
|
||||||
f,
|
|
||||||
"Block height:",
|
|
||||||
&self.epoch_info.block_height.to_string(),
|
|
||||||
)?;
|
|
||||||
writeln_name_value(f, "Slot:", &self.epoch_info.absolute_slot.to_string())?;
|
writeln_name_value(f, "Slot:", &self.epoch_info.absolute_slot.to_string())?;
|
||||||
writeln_name_value(f, "Epoch:", &self.epoch_info.epoch.to_string())?;
|
writeln_name_value(f, "Epoch:", &self.epoch_info.epoch.to_string())?;
|
||||||
let start_slot = self.epoch_info.absolute_slot - self.epoch_info.slot_index;
|
let start_slot = self.epoch_info.absolute_slot - self.epoch_info.slot_index;
|
||||||
@@ -259,15 +247,6 @@ fn slot_to_human_time(slot: Slot) -> String {
|
|||||||
.to_string()
|
.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Default)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct CliValidatorsStakeByVersion {
|
|
||||||
pub current_validators: usize,
|
|
||||||
pub delinquent_validators: usize,
|
|
||||||
pub current_active_stake: u64,
|
|
||||||
pub delinquent_active_stake: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct CliValidators {
|
pub struct CliValidators {
|
||||||
@@ -276,7 +255,6 @@ pub struct CliValidators {
|
|||||||
pub total_deliquent_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>,
|
|
||||||
#[serde(skip_serializing)]
|
#[serde(skip_serializing)]
|
||||||
pub use_lamports_unit: bool,
|
pub use_lamports_unit: bool,
|
||||||
}
|
}
|
||||||
@@ -300,7 +278,7 @@ impl fmt::Display for CliValidators {
|
|||||||
|
|
||||||
writeln!(
|
writeln!(
|
||||||
f,
|
f,
|
||||||
"{} {:<44} {:<44} {:>3}% {:>8} {:>10} {:>10} {:>17} {}",
|
"{} {:<44} {:<44} {:>9}% {:>8} {:>10} {:>7} {}",
|
||||||
if delinquent {
|
if delinquent {
|
||||||
WARNING.to_string()
|
WARNING.to_string()
|
||||||
} else {
|
} else {
|
||||||
@@ -312,12 +290,11 @@ impl fmt::Display for CliValidators {
|
|||||||
non_zero_or_dash(validator.last_vote),
|
non_zero_or_dash(validator.last_vote),
|
||||||
non_zero_or_dash(validator.root_slot),
|
non_zero_or_dash(validator.root_slot),
|
||||||
validator.credits,
|
validator.credits,
|
||||||
validator.version,
|
|
||||||
if validator.activated_stake > 0 {
|
if validator.activated_stake > 0 {
|
||||||
format!(
|
format!(
|
||||||
"{} ({:.2}%)",
|
"{} ({:.2}%)",
|
||||||
build_balance_message(validator.activated_stake, use_lamports_unit, true),
|
build_balance_message(validator.activated_stake, use_lamports_unit, true),
|
||||||
100. * validator.activated_stake as f64 / total_active_stake as f64,
|
100. * validator.activated_stake as f64 / total_active_stake as f64
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
"-".into()
|
"-".into()
|
||||||
@@ -353,41 +330,18 @@ impl fmt::Display for CliValidators {
|
|||||||
),
|
),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
writeln!(f)?;
|
|
||||||
writeln!(f, "{}", style("Stake By Version:").bold())?;
|
|
||||||
for (version, info) in self.stake_by_version.iter() {
|
|
||||||
writeln!(
|
|
||||||
f,
|
|
||||||
"{:<16} - {:3} current validators ({:>5.2}%){}",
|
|
||||||
version,
|
|
||||||
info.current_validators,
|
|
||||||
100. * info.current_active_stake as f64 / self.total_active_stake as f64,
|
|
||||||
if info.delinquent_validators > 0 {
|
|
||||||
format!(
|
|
||||||
", {:3} delinquent validators ({:>5.2}%)",
|
|
||||||
info.delinquent_validators,
|
|
||||||
100. * info.delinquent_active_stake as f64 / self.total_active_stake as f64
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
"".to_string()
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
writeln!(f)?;
|
writeln!(f)?;
|
||||||
writeln!(
|
writeln!(
|
||||||
f,
|
f,
|
||||||
"{}",
|
"{}",
|
||||||
style(format!(
|
style(format!(
|
||||||
" {:<44} {:<38} {} {} {} {:>10} {:^17} {}",
|
" {:<44} {:<44} {} {} {} {:>7} {}",
|
||||||
"Identity Pubkey",
|
"Identity Pubkey",
|
||||||
"Vote Account Pubkey",
|
"Vote Account Pubkey",
|
||||||
"Commission",
|
"Commission",
|
||||||
"Last Vote",
|
"Last Vote",
|
||||||
"Root Block",
|
"Root Block",
|
||||||
"Credits",
|
"Credits",
|
||||||
"Version",
|
|
||||||
"Active Stake",
|
"Active Stake",
|
||||||
))
|
))
|
||||||
.bold()
|
.bold()
|
||||||
@@ -424,19 +378,13 @@ pub struct CliValidator {
|
|||||||
pub root_slot: u64,
|
pub root_slot: u64,
|
||||||
pub credits: u64,
|
pub credits: u64,
|
||||||
pub activated_stake: u64,
|
pub activated_stake: u64,
|
||||||
pub version: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CliValidator {
|
impl CliValidator {
|
||||||
pub fn new(
|
pub fn new(vote_account: &RpcVoteAccountInfo, current_epoch: Epoch) -> Self {
|
||||||
vote_account: &RpcVoteAccountInfo,
|
|
||||||
current_epoch: Epoch,
|
|
||||||
version: String,
|
|
||||||
address_labels: &HashMap<String, String>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
identity_pubkey: format_labeled_address(&vote_account.node_pubkey, address_labels),
|
identity_pubkey: vote_account.node_pubkey.to_string(),
|
||||||
vote_account_pubkey: format_labeled_address(&vote_account.vote_pubkey, address_labels),
|
vote_account_pubkey: vote_account.vote_pubkey.to_string(),
|
||||||
commission: vote_account.commission,
|
commission: vote_account.commission,
|
||||||
last_vote: vote_account.last_vote,
|
last_vote: vote_account.last_vote,
|
||||||
root_slot: vote_account.root_slot,
|
root_slot: vote_account.root_slot,
|
||||||
@@ -452,7 +400,6 @@ impl CliValidator {
|
|||||||
})
|
})
|
||||||
.unwrap_or(0),
|
.unwrap_or(0),
|
||||||
activated_stake: vote_account.activated_stake,
|
activated_stake: vote_account.activated_stake,
|
||||||
version,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -535,7 +482,7 @@ impl fmt::Display for CliKeyedStakeState {
|
|||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct CliStakeState {
|
pub struct CliStakeState {
|
||||||
pub stake_type: CliStakeType,
|
pub stake_type: CliStakeType,
|
||||||
pub account_balance: u64,
|
pub total_stake: u64,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub delegated_stake: Option<u64>,
|
pub delegated_stake: Option<u64>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
@@ -550,16 +497,6 @@ pub struct CliStakeState {
|
|||||||
pub lockup: Option<CliLockup>,
|
pub lockup: Option<CliLockup>,
|
||||||
#[serde(skip_serializing)]
|
#[serde(skip_serializing)]
|
||||||
pub use_lamports_unit: bool,
|
pub use_lamports_unit: bool,
|
||||||
#[serde(skip_serializing)]
|
|
||||||
pub current_epoch: Epoch,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub rent_exempt_reserve: Option<u64>,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub active_stake: Option<u64>,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub activating_stake: Option<u64>,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub deactivating_stake: Option<u64>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for CliStakeState {
|
impl fmt::Display for CliStakeState {
|
||||||
@@ -569,9 +506,7 @@ impl fmt::Display for CliStakeState {
|
|||||||
writeln!(f, "Withdraw Authority: {}", authorized.withdrawer)?;
|
writeln!(f, "Withdraw Authority: {}", authorized.withdrawer)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn show_lockup(f: &mut fmt::Formatter, lockup: Option<&CliLockup>) -> fmt::Result {
|
fn show_lockup(f: &mut fmt::Formatter, lockup: &CliLockup) -> fmt::Result {
|
||||||
if let Some(lockup) = lockup {
|
|
||||||
if lockup.unix_timestamp != UnixTimestamp::default() {
|
|
||||||
writeln!(
|
writeln!(
|
||||||
f,
|
f,
|
||||||
"Lockup Timestamp: {} (UnixTimestamp: {})",
|
"Lockup Timestamp: {} (UnixTimestamp: {})",
|
||||||
@@ -582,134 +517,60 @@ impl fmt::Display for CliStakeState {
|
|||||||
.to_rfc3339_opts(SecondsFormat::Secs, true),
|
.to_rfc3339_opts(SecondsFormat::Secs, true),
|
||||||
lockup.unix_timestamp
|
lockup.unix_timestamp
|
||||||
)?;
|
)?;
|
||||||
}
|
|
||||||
if lockup.epoch != Epoch::default() {
|
|
||||||
writeln!(f, "Lockup Epoch: {}", lockup.epoch)?;
|
writeln!(f, "Lockup Epoch: {}", lockup.epoch)?;
|
||||||
}
|
|
||||||
writeln!(f, "Lockup Custodian: {}", lockup.custodian)?;
|
writeln!(f, "Lockup Custodian: {}", lockup.custodian)?;
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
writeln!(
|
|
||||||
f,
|
|
||||||
"Balance: {}",
|
|
||||||
build_balance_message(self.account_balance, self.use_lamports_unit, true)
|
|
||||||
)?;
|
|
||||||
|
|
||||||
if let Some(rent_exempt_reserve) = self.rent_exempt_reserve {
|
|
||||||
writeln!(
|
|
||||||
f,
|
|
||||||
"Rent Exempt Reserve: {}",
|
|
||||||
build_balance_message(rent_exempt_reserve, self.use_lamports_unit, true)
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.stake_type {
|
match self.stake_type {
|
||||||
CliStakeType::RewardsPool => writeln!(f, "Stake account is a rewards pool")?,
|
CliStakeType::RewardsPool => writeln!(f, "Stake account is a rewards pool")?,
|
||||||
CliStakeType::Uninitialized => writeln!(f, "Stake account is uninitialized")?,
|
CliStakeType::Uninitialized => writeln!(f, "Stake account is uninitialized")?,
|
||||||
CliStakeType::Initialized => {
|
CliStakeType::Initialized => {
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
"Total Stake: {}",
|
||||||
|
build_balance_message(self.total_stake, self.use_lamports_unit, true)
|
||||||
|
)?;
|
||||||
writeln!(f, "Stake account is undelegated")?;
|
writeln!(f, "Stake account is undelegated")?;
|
||||||
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().unwrap())?;
|
||||||
}
|
}
|
||||||
CliStakeType::Stake => {
|
CliStakeType::Stake => {
|
||||||
let show_delegation = {
|
writeln!(
|
||||||
self.active_stake.is_some()
|
f,
|
||||||
|| self.activating_stake.is_some()
|
"Total Stake: {}",
|
||||||
|| self.deactivating_stake.is_some()
|
build_balance_message(self.total_stake, self.use_lamports_unit, true)
|
||||||
|| self
|
)?;
|
||||||
.deactivation_epoch
|
|
||||||
.map(|de| de > self.current_epoch)
|
|
||||||
.unwrap_or(true)
|
|
||||||
};
|
|
||||||
if show_delegation {
|
|
||||||
let delegated_stake = self.delegated_stake.unwrap();
|
|
||||||
writeln!(
|
writeln!(
|
||||||
f,
|
f,
|
||||||
"Delegated Stake: {}",
|
"Delegated Stake: {}",
|
||||||
build_balance_message(delegated_stake, self.use_lamports_unit, true)
|
|
||||||
)?;
|
|
||||||
if self
|
|
||||||
.deactivation_epoch
|
|
||||||
.map(|d| self.current_epoch <= d)
|
|
||||||
.unwrap_or(true)
|
|
||||||
{
|
|
||||||
let active_stake = self.active_stake.unwrap_or(0);
|
|
||||||
writeln!(
|
|
||||||
f,
|
|
||||||
"Active Stake: {}",
|
|
||||||
build_balance_message(active_stake, self.use_lamports_unit, true),
|
|
||||||
)?;
|
|
||||||
let activating_stake = self.activating_stake.or_else(|| {
|
|
||||||
if self.active_stake.is_none() {
|
|
||||||
Some(delegated_stake)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if let Some(activating_stake) = activating_stake {
|
|
||||||
writeln!(
|
|
||||||
f,
|
|
||||||
"Activating Stake: {}",
|
|
||||||
build_balance_message(
|
build_balance_message(
|
||||||
activating_stake,
|
self.delegated_stake.unwrap(),
|
||||||
self.use_lamports_unit,
|
self.use_lamports_unit,
|
||||||
true
|
true
|
||||||
),
|
)
|
||||||
)?;
|
)?;
|
||||||
writeln!(
|
if let Some(delegated_vote_account_address) = &self.delegated_vote_account_address {
|
||||||
f,
|
|
||||||
"Stake activates starting from epoch: {}",
|
|
||||||
self.activation_epoch.unwrap()
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(deactivation_epoch) = self.deactivation_epoch {
|
|
||||||
if self.current_epoch > deactivation_epoch {
|
|
||||||
let deactivating_stake = self.deactivating_stake.or(self.active_stake);
|
|
||||||
if let Some(deactivating_stake) = deactivating_stake {
|
|
||||||
writeln!(
|
|
||||||
f,
|
|
||||||
"Inactive Stake: {}",
|
|
||||||
build_balance_message(
|
|
||||||
delegated_stake - deactivating_stake,
|
|
||||||
self.use_lamports_unit,
|
|
||||||
true
|
|
||||||
),
|
|
||||||
)?;
|
|
||||||
writeln!(
|
|
||||||
f,
|
|
||||||
"Deactivating Stake: {}",
|
|
||||||
build_balance_message(
|
|
||||||
deactivating_stake,
|
|
||||||
self.use_lamports_unit,
|
|
||||||
true
|
|
||||||
),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writeln!(
|
|
||||||
f,
|
|
||||||
"Stake deactivates starting from epoch: {}",
|
|
||||||
deactivation_epoch
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
if let Some(delegated_vote_account_address) =
|
|
||||||
&self.delegated_vote_account_address
|
|
||||||
{
|
|
||||||
writeln!(
|
writeln!(
|
||||||
f,
|
f,
|
||||||
"Delegated Vote Account Address: {}",
|
"Delegated Vote Account Address: {}",
|
||||||
delegated_vote_account_address
|
delegated_vote_account_address
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
} else {
|
writeln!(
|
||||||
writeln!(f, "Stake account is undelegated")?;
|
f,
|
||||||
|
"Stake activates starting from epoch: {}",
|
||||||
|
self.activation_epoch.unwrap()
|
||||||
|
)?;
|
||||||
|
if let Some(deactivation_epoch) = self.deactivation_epoch {
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
"Stake deactivates starting from epoch: {}",
|
||||||
|
deactivation_epoch
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
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().unwrap())?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -1,18 +1,23 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult},
|
cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult},
|
||||||
cli_output::*,
|
cli_output::*,
|
||||||
display::{format_labeled_address, new_spinner_progress_bar, println_name_value},
|
display::println_name_value,
|
||||||
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
|
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
|
||||||
};
|
};
|
||||||
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 indicatif::{ProgressBar, ProgressStyle};
|
||||||
use solana_clap_utils::{
|
use solana_clap_utils::{
|
||||||
commitment::commitment_arg, input_parsers::*, input_validators::*, keypair::signer_from_path,
|
commitment::{commitment_arg, COMMITMENT_ARG},
|
||||||
|
input_parsers::*,
|
||||||
|
input_validators::*,
|
||||||
|
keypair::signer_from_path,
|
||||||
};
|
};
|
||||||
use solana_client::{
|
use solana_client::{
|
||||||
pubsub_client::{PubsubClient, SlotInfoMessage},
|
pubsub_client::{PubsubClient, SlotInfoMessage},
|
||||||
rpc_client::{GetConfirmedSignaturesForAddress2Config, RpcClient},
|
rpc_client::RpcClient,
|
||||||
rpc_config::{RpcLargestAccountsConfig, RpcLargestAccountsFilter},
|
rpc_config::{RpcLargestAccountsConfig, RpcLargestAccountsFilter},
|
||||||
|
rpc_request::MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS_SLOT_RANGE,
|
||||||
};
|
};
|
||||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
@@ -23,17 +28,12 @@ use solana_sdk::{
|
|||||||
message::Message,
|
message::Message,
|
||||||
native_token::lamports_to_sol,
|
native_token::lamports_to_sol,
|
||||||
pubkey::{self, Pubkey},
|
pubkey::{self, Pubkey},
|
||||||
signature::Signature,
|
|
||||||
system_instruction, system_program,
|
system_instruction, system_program,
|
||||||
sysvar::{
|
sysvar::{self, Sysvar},
|
||||||
self,
|
|
||||||
stake_history::{self, StakeHistory},
|
|
||||||
Sysvar,
|
|
||||||
},
|
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, HashMap, VecDeque},
|
collections::{HashMap, VecDeque},
|
||||||
net::SocketAddr,
|
net::SocketAddr,
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicBool, Ordering},
|
atomic::{AtomicBool, Ordering},
|
||||||
@@ -115,10 +115,6 @@ impl ClusterQuerySubCommands for App<'_, '_> {
|
|||||||
.alias("get-slot")
|
.alias("get-slot")
|
||||||
.arg(commitment_arg()),
|
.arg(commitment_arg()),
|
||||||
)
|
)
|
||||||
.subcommand(
|
|
||||||
SubCommand::with_name("block-height").about("Get current block height")
|
|
||||||
.arg(commitment_arg()),
|
|
||||||
)
|
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("epoch").about("Get current epoch")
|
SubCommand::with_name("epoch").about("Get current epoch")
|
||||||
.arg(commitment_arg()),
|
.arg(commitment_arg()),
|
||||||
@@ -257,8 +253,9 @@ impl ClusterQuerySubCommands for App<'_, '_> {
|
|||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("transaction-history")
|
SubCommand::with_name("transaction-history")
|
||||||
.about("Show historical transactions affecting the given address \
|
.about("Show historical transactions affecting the given address, \
|
||||||
from newest to oldest")
|
ordered based on the slot in which they were confirmed in \
|
||||||
|
from lowest to highest slot")
|
||||||
.arg(
|
.arg(
|
||||||
pubkey!(Arg::with_name("address")
|
pubkey!(Arg::with_name("address")
|
||||||
.index(1)
|
.index(1)
|
||||||
@@ -266,22 +263,26 @@ impl ClusterQuerySubCommands for App<'_, '_> {
|
|||||||
.required(true),
|
.required(true),
|
||||||
"Account address"),
|
"Account address"),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("end_slot")
|
||||||
|
.takes_value(false)
|
||||||
|
.value_name("SLOT")
|
||||||
|
.index(2)
|
||||||
|
.validator(is_slot)
|
||||||
|
.help(
|
||||||
|
"Slot to start from [default: latest slot at maximum commitment]"
|
||||||
|
),
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("limit")
|
Arg::with_name("limit")
|
||||||
.long("limit")
|
.long("limit")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.value_name("LIMIT")
|
.value_name("NUMBER OF SLOTS")
|
||||||
.validator(is_slot)
|
.validator(is_slot)
|
||||||
.default_value("1000")
|
.help(
|
||||||
.help("Maximum number of transaction signatures to return"),
|
"Limit the search to this many slots"
|
||||||
)
|
),
|
||||||
.arg(
|
),
|
||||||
Arg::with_name("before")
|
|
||||||
.long("before")
|
|
||||||
.value_name("TRANSACTION_SIGNATURE")
|
|
||||||
.takes_value(true)
|
|
||||||
.help("Start with the first signature older than this one"),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -292,11 +293,13 @@ pub fn parse_catchup(
|
|||||||
) -> Result<CliCommandInfo, CliError> {
|
) -> Result<CliCommandInfo, CliError> {
|
||||||
let node_pubkey = pubkey_of_signer(matches, "node_pubkey", wallet_manager)?.unwrap();
|
let node_pubkey = pubkey_of_signer(matches, "node_pubkey", wallet_manager)?.unwrap();
|
||||||
let node_json_rpc_url = value_t!(matches, "node_json_rpc_url", String).ok();
|
let node_json_rpc_url = value_t!(matches, "node_json_rpc_url", String).ok();
|
||||||
|
let commitment_config = commitment_of(matches, COMMITMENT_ARG.long).unwrap();
|
||||||
let follow = matches.is_present("follow");
|
let follow = matches.is_present("follow");
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::Catchup {
|
command: CliCommand::Catchup {
|
||||||
node_pubkey,
|
node_pubkey,
|
||||||
node_json_rpc_url,
|
node_json_rpc_url,
|
||||||
|
commitment_config,
|
||||||
follow,
|
follow,
|
||||||
},
|
},
|
||||||
signers: vec![],
|
signers: vec![],
|
||||||
@@ -316,12 +319,14 @@ pub fn parse_cluster_ping(
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
let timeout = Duration::from_secs(value_t_or_exit!(matches, "timeout", u64));
|
let timeout = Duration::from_secs(value_t_or_exit!(matches, "timeout", u64));
|
||||||
|
let commitment_config = commitment_of(matches, COMMITMENT_ARG.long).unwrap();
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::Ping {
|
command: CliCommand::Ping {
|
||||||
lamports,
|
lamports,
|
||||||
interval,
|
interval,
|
||||||
count,
|
count,
|
||||||
timeout,
|
timeout,
|
||||||
|
commitment_config,
|
||||||
},
|
},
|
||||||
signers: vec![signer_from_path(
|
signers: vec![signer_from_path(
|
||||||
matches,
|
matches,
|
||||||
@@ -340,35 +345,32 @@ pub fn parse_get_block_time(matches: &ArgMatches<'_>) -> Result<CliCommandInfo,
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_get_epoch(_matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
pub fn parse_get_epoch_info(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||||
|
let commitment_config = commitment_of(matches, COMMITMENT_ARG.long).unwrap();
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::GetEpoch,
|
command: CliCommand::GetEpochInfo { commitment_config },
|
||||||
signers: vec![],
|
signers: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_get_epoch_info(_matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
pub fn parse_get_slot(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||||
|
let commitment_config = commitment_of(matches, COMMITMENT_ARG.long).unwrap();
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::GetEpochInfo,
|
command: CliCommand::GetSlot { commitment_config },
|
||||||
signers: vec![],
|
signers: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_get_slot(_matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
pub fn parse_get_epoch(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||||
|
let commitment_config = commitment_of(matches, COMMITMENT_ARG.long).unwrap();
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::GetSlot,
|
command: CliCommand::GetEpoch { commitment_config },
|
||||||
signers: vec![],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_get_block_height(_matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
|
||||||
Ok(CliCommandInfo {
|
|
||||||
command: CliCommand::GetBlockHeight,
|
|
||||||
signers: vec![],
|
signers: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_largest_accounts(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
pub fn parse_largest_accounts(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||||
|
let commitment_config = commitment_of(matches, COMMITMENT_ARG.long).unwrap();
|
||||||
let filter = if matches.is_present("circulating") {
|
let filter = if matches.is_present("circulating") {
|
||||||
Some(RpcLargestAccountsFilter::Circulating)
|
Some(RpcLargestAccountsFilter::Circulating)
|
||||||
} else if matches.is_present("non_circulating") {
|
} else if matches.is_present("non_circulating") {
|
||||||
@@ -377,29 +379,38 @@ pub fn parse_largest_accounts(matches: &ArgMatches<'_>) -> Result<CliCommandInfo
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::LargestAccounts { filter },
|
command: CliCommand::LargestAccounts {
|
||||||
|
commitment_config,
|
||||||
|
filter,
|
||||||
|
},
|
||||||
signers: vec![],
|
signers: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_supply(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
pub fn parse_supply(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||||
|
let commitment_config = commitment_of(matches, COMMITMENT_ARG.long).unwrap();
|
||||||
let print_accounts = matches.is_present("print_accounts");
|
let print_accounts = matches.is_present("print_accounts");
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::Supply { print_accounts },
|
command: CliCommand::Supply {
|
||||||
|
commitment_config,
|
||||||
|
print_accounts,
|
||||||
|
},
|
||||||
signers: vec![],
|
signers: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_total_supply(_matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
pub fn parse_total_supply(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||||
|
let commitment_config = commitment_of(matches, COMMITMENT_ARG.long).unwrap();
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::TotalSupply,
|
command: CliCommand::TotalSupply { commitment_config },
|
||||||
signers: vec![],
|
signers: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_get_transaction_count(_matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
pub fn parse_get_transaction_count(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||||
|
let commitment_config = commitment_of(matches, COMMITMENT_ARG.long).unwrap();
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::GetTransactionCount,
|
command: CliCommand::GetTransactionCount { commitment_config },
|
||||||
signers: vec![],
|
signers: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -423,9 +434,13 @@ pub fn parse_show_stakes(
|
|||||||
|
|
||||||
pub fn parse_show_validators(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
pub fn parse_show_validators(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||||
let use_lamports_unit = matches.is_present("lamports");
|
let use_lamports_unit = matches.is_present("lamports");
|
||||||
|
let commitment_config = commitment_of(matches, COMMITMENT_ARG.long).unwrap();
|
||||||
|
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::ShowValidators { use_lamports_unit },
|
command: CliCommand::ShowValidators {
|
||||||
|
use_lamports_unit,
|
||||||
|
commitment_config,
|
||||||
|
},
|
||||||
signers: vec![],
|
signers: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -435,41 +450,33 @@ pub fn parse_transaction_history(
|
|||||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||||
) -> Result<CliCommandInfo, CliError> {
|
) -> Result<CliCommandInfo, CliError> {
|
||||||
let address = pubkey_of_signer(matches, "address", wallet_manager)?.unwrap();
|
let address = pubkey_of_signer(matches, "address", wallet_manager)?.unwrap();
|
||||||
|
let end_slot = value_t!(matches, "end_slot", Slot).ok();
|
||||||
let before = match matches.value_of("before") {
|
let slot_limit = value_t!(matches, "limit", u64).ok();
|
||||||
Some(signature) => Some(
|
|
||||||
signature
|
|
||||||
.parse()
|
|
||||||
.map_err(|err| CliError::BadParameter(format!("Invalid signature: {}", err)))?,
|
|
||||||
),
|
|
||||||
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);
|
|
||||||
|
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::TransactionHistory {
|
command: CliCommand::TransactionHistory {
|
||||||
address,
|
address,
|
||||||
before,
|
end_slot,
|
||||||
until,
|
slot_limit,
|
||||||
limit,
|
|
||||||
},
|
},
|
||||||
signers: vec![],
|
signers: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new process bar for processing that will take an unknown amount of time
|
||||||
|
fn new_spinner_progress_bar() -> ProgressBar {
|
||||||
|
let progress_bar = ProgressBar::new(42);
|
||||||
|
progress_bar
|
||||||
|
.set_style(ProgressStyle::default_spinner().template("{spinner:.green} {wide_msg}"));
|
||||||
|
progress_bar.enable_steady_tick(100);
|
||||||
|
progress_bar
|
||||||
|
}
|
||||||
|
|
||||||
pub fn process_catchup(
|
pub fn process_catchup(
|
||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
config: &CliConfig,
|
|
||||||
node_pubkey: &Pubkey,
|
node_pubkey: &Pubkey,
|
||||||
node_json_rpc_url: &Option<String>,
|
node_json_rpc_url: &Option<String>,
|
||||||
|
commitment_config: CommitmentConfig,
|
||||||
follow: bool,
|
follow: bool,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let sleep_interval = 5;
|
let sleep_interval = 5;
|
||||||
@@ -518,8 +525,8 @@ pub fn process_catchup(
|
|||||||
let mut previous_rpc_slot = std::u64::MAX;
|
let mut previous_rpc_slot = std::u64::MAX;
|
||||||
let mut previous_slot_distance = 0;
|
let mut previous_slot_distance = 0;
|
||||||
loop {
|
loop {
|
||||||
let rpc_slot = rpc_client.get_slot_with_commitment(config.commitment)?;
|
let rpc_slot = rpc_client.get_slot_with_commitment(commitment_config)?;
|
||||||
let node_slot = node_client.get_slot_with_commitment(config.commitment)?;
|
let node_slot = node_client.get_slot_with_commitment(commitment_config)?;
|
||||||
if !follow && node_slot > std::cmp::min(previous_rpc_slot, rpc_slot) {
|
if !follow && node_slot > std::cmp::min(previous_rpc_slot, rpc_slot) {
|
||||||
progress_bar.finish_and_clear();
|
progress_bar.finish_and_clear();
|
||||||
return Ok(format!(
|
return Ok(format!(
|
||||||
@@ -652,14 +659,13 @@ pub fn process_get_block_time(
|
|||||||
Ok(config.output_format.formatted_string(&block_time))
|
Ok(config.output_format.formatted_string(&block_time))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_get_epoch(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
|
pub fn process_get_epoch_info(
|
||||||
let epoch_info = rpc_client.get_epoch_info_with_commitment(config.commitment)?;
|
rpc_client: &RpcClient,
|
||||||
Ok(epoch_info.epoch.to_string())
|
config: &CliConfig,
|
||||||
}
|
commitment_config: CommitmentConfig,
|
||||||
|
) -> ProcessResult {
|
||||||
pub fn process_get_epoch_info(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
|
|
||||||
let epoch_info: CliEpochInfo = rpc_client
|
let epoch_info: CliEpochInfo = rpc_client
|
||||||
.get_epoch_info_with_commitment(config.commitment)?
|
.get_epoch_info_with_commitment(commitment_config.clone())?
|
||||||
.into();
|
.into();
|
||||||
Ok(config.output_format.formatted_string(&epoch_info))
|
Ok(config.output_format.formatted_string(&epoch_info))
|
||||||
}
|
}
|
||||||
@@ -669,16 +675,20 @@ pub fn process_get_genesis_hash(rpc_client: &RpcClient) -> ProcessResult {
|
|||||||
Ok(genesis_hash.to_string())
|
Ok(genesis_hash.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_get_slot(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
|
pub fn process_get_slot(
|
||||||
let slot = rpc_client.get_slot_with_commitment(config.commitment)?;
|
rpc_client: &RpcClient,
|
||||||
|
commitment_config: CommitmentConfig,
|
||||||
|
) -> ProcessResult {
|
||||||
|
let slot = rpc_client.get_slot_with_commitment(commitment_config.clone())?;
|
||||||
Ok(slot.to_string())
|
Ok(slot.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_get_block_height(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
|
pub fn process_get_epoch(
|
||||||
let epoch_info: CliEpochInfo = rpc_client
|
rpc_client: &RpcClient,
|
||||||
.get_epoch_info_with_commitment(config.commitment)?
|
commitment_config: CommitmentConfig,
|
||||||
.into();
|
) -> ProcessResult {
|
||||||
Ok(epoch_info.epoch_info.block_height.to_string())
|
let epoch_info = rpc_client.get_epoch_info_with_commitment(commitment_config.clone())?;
|
||||||
|
Ok(epoch_info.epoch.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_show_block_production(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
pub fn parse_show_block_production(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||||
@@ -764,12 +774,11 @@ pub fn process_show_block_production(
|
|||||||
let leader_schedule = leader_schedule.unwrap();
|
let leader_schedule = leader_schedule.unwrap();
|
||||||
|
|
||||||
let mut leader_per_slot_index = Vec::new();
|
let mut leader_per_slot_index = Vec::new();
|
||||||
leader_per_slot_index.resize(total_slots, "?".to_string());
|
leader_per_slot_index.resize(total_slots, "?");
|
||||||
for (pubkey, leader_slots) in leader_schedule.iter() {
|
for (pubkey, leader_slots) in leader_schedule.iter() {
|
||||||
let pubkey = format_labeled_address(pubkey, &config.address_labels);
|
|
||||||
for slot_index in leader_slots.iter() {
|
for slot_index in leader_slots.iter() {
|
||||||
if *slot_index >= start_slot_index && *slot_index <= end_slot_index {
|
if *slot_index >= start_slot_index && *slot_index <= end_slot_index {
|
||||||
leader_per_slot_index[*slot_index - start_slot_index] = pubkey.clone();
|
leader_per_slot_index[*slot_index - start_slot_index] = pubkey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -846,11 +855,12 @@ pub fn process_show_block_production(
|
|||||||
pub fn process_largest_accounts(
|
pub fn process_largest_accounts(
|
||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
config: &CliConfig,
|
config: &CliConfig,
|
||||||
|
commitment_config: CommitmentConfig,
|
||||||
filter: Option<RpcLargestAccountsFilter>,
|
filter: Option<RpcLargestAccountsFilter>,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let accounts = rpc_client
|
let accounts = rpc_client
|
||||||
.get_largest_accounts_with_config(RpcLargestAccountsConfig {
|
.get_largest_accounts_with_config(RpcLargestAccountsConfig {
|
||||||
commitment: Some(config.commitment),
|
commitment: Some(commitment_config),
|
||||||
filter,
|
filter,
|
||||||
})?
|
})?
|
||||||
.value;
|
.value;
|
||||||
@@ -861,21 +871,29 @@ pub fn process_largest_accounts(
|
|||||||
pub fn process_supply(
|
pub fn process_supply(
|
||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
config: &CliConfig,
|
config: &CliConfig,
|
||||||
|
commitment_config: CommitmentConfig,
|
||||||
print_accounts: bool,
|
print_accounts: bool,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let supply_response = rpc_client.supply_with_commitment(config.commitment)?;
|
let supply_response = rpc_client.supply_with_commitment(commitment_config.clone())?;
|
||||||
let mut supply: CliSupply = supply_response.value.into();
|
let mut supply: CliSupply = supply_response.value.into();
|
||||||
supply.print_accounts = print_accounts;
|
supply.print_accounts = print_accounts;
|
||||||
Ok(config.output_format.formatted_string(&supply))
|
Ok(config.output_format.formatted_string(&supply))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_total_supply(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
|
pub fn process_total_supply(
|
||||||
let total_supply = rpc_client.total_supply_with_commitment(config.commitment)?;
|
rpc_client: &RpcClient,
|
||||||
|
commitment_config: CommitmentConfig,
|
||||||
|
) -> ProcessResult {
|
||||||
|
let total_supply = rpc_client.total_supply_with_commitment(commitment_config.clone())?;
|
||||||
Ok(format!("{} SOL", lamports_to_sol(total_supply)))
|
Ok(format!("{} SOL", lamports_to_sol(total_supply)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_get_transaction_count(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
|
pub fn process_get_transaction_count(
|
||||||
let transaction_count = rpc_client.get_transaction_count_with_commitment(config.commitment)?;
|
rpc_client: &RpcClient,
|
||||||
|
commitment_config: CommitmentConfig,
|
||||||
|
) -> ProcessResult {
|
||||||
|
let transaction_count =
|
||||||
|
rpc_client.get_transaction_count_with_commitment(commitment_config.clone())?;
|
||||||
Ok(transaction_count.to_string())
|
Ok(transaction_count.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -886,6 +904,7 @@ pub fn process_ping(
|
|||||||
interval: &Duration,
|
interval: &Duration,
|
||||||
count: &Option<u64>,
|
count: &Option<u64>,
|
||||||
timeout: &Duration,
|
timeout: &Duration,
|
||||||
|
commitment_config: CommitmentConfig,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
println_name_value("Source Account:", &config.signers[0].pubkey().to_string());
|
println_name_value("Source Account:", &config.signers[0].pubkey().to_string());
|
||||||
println!();
|
println!();
|
||||||
@@ -922,7 +941,7 @@ pub fn process_ping(
|
|||||||
|
|
||||||
let build_message = |lamports| {
|
let build_message = |lamports| {
|
||||||
let ix = system_instruction::transfer(&config.signers[0].pubkey(), &to, lamports);
|
let ix = system_instruction::transfer(&config.signers[0].pubkey(), &to, lamports);
|
||||||
Message::new(&[ix], Some(&config.signers[0].pubkey()))
|
Message::new(&[ix])
|
||||||
};
|
};
|
||||||
let (message, _) = resolve_spend_tx_and_check_account_balance(
|
let (message, _) = resolve_spend_tx_and_check_account_balance(
|
||||||
rpc_client,
|
rpc_client,
|
||||||
@@ -931,7 +950,6 @@ pub fn process_ping(
|
|||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
&config.signers[0].pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
build_message,
|
build_message,
|
||||||
config.commitment,
|
|
||||||
)?;
|
)?;
|
||||||
let mut tx = Transaction::new_unsigned(message);
|
let mut tx = Transaction::new_unsigned(message);
|
||||||
tx.try_sign(&config.signers, blockhash)?;
|
tx.try_sign(&config.signers, blockhash)?;
|
||||||
@@ -940,8 +958,10 @@ pub fn process_ping(
|
|||||||
Ok(signature) => {
|
Ok(signature) => {
|
||||||
let transaction_sent = Instant::now();
|
let transaction_sent = Instant::now();
|
||||||
loop {
|
loop {
|
||||||
let signature_status = rpc_client
|
let signature_status = rpc_client.get_signature_status_with_commitment(
|
||||||
.get_signature_status_with_commitment(&signature, config.commitment)?;
|
&signature,
|
||||||
|
commitment_config.clone(),
|
||||||
|
)?;
|
||||||
let elapsed_time = Instant::now().duration_since(transaction_sent);
|
let elapsed_time = Instant::now().duration_since(transaction_sent);
|
||||||
if let Some(transaction_status) = signature_status {
|
if let Some(transaction_status) = signature_status {
|
||||||
match transaction_status {
|
match transaction_status {
|
||||||
@@ -1116,7 +1136,7 @@ pub fn process_live_slots(url: &str) -> ProcessResult {
|
|||||||
Ok("".to_string())
|
Ok("".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_show_gossip(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
|
pub fn process_show_gossip(rpc_client: &RpcClient) -> ProcessResult {
|
||||||
let cluster_nodes = rpc_client.get_cluster_nodes()?;
|
let cluster_nodes = rpc_client.get_cluster_nodes()?;
|
||||||
|
|
||||||
fn format_port(addr: Option<SocketAddr>) -> String {
|
fn format_port(addr: Option<SocketAddr>) -> String {
|
||||||
@@ -1125,27 +1145,26 @@ pub fn process_show_gossip(rpc_client: &RpcClient, config: &CliConfig) -> Proces
|
|||||||
}
|
}
|
||||||
|
|
||||||
let s: Vec<_> = cluster_nodes
|
let s: Vec<_> = cluster_nodes
|
||||||
.into_iter()
|
.iter()
|
||||||
.map(|node| {
|
.map(|node| {
|
||||||
format!(
|
format!(
|
||||||
"{:15} | {:44} | {:6} | {:5} | {:5} | {}",
|
"{: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),
|
node.pubkey,
|
||||||
format_port(node.gossip),
|
format_port(node.gossip),
|
||||||
format_port(node.tpu),
|
format_port(node.tpu),
|
||||||
format_port(node.rpc),
|
format_port(node.rpc),
|
||||||
node.version.unwrap_or_else(|| "unknown".to_string()),
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Ok(format!(
|
Ok(format!(
|
||||||
"IP Address | Node identifier \
|
"IP Address | Node identifier \
|
||||||
| Gossip | TPU | RPC | Version\n\
|
| Gossip | TPU | RPC\n\
|
||||||
----------------+----------------------------------------------+\
|
----------------+----------------------------------------------+\
|
||||||
--------+-------+-------+----------------\n\
|
--------+-------+-------\n\
|
||||||
{}\n\
|
{}\n\
|
||||||
Nodes: {}",
|
Nodes: {}",
|
||||||
s.join("\n"),
|
s.join("\n"),
|
||||||
@@ -1165,16 +1184,7 @@ 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 all_stake_accounts = rpc_client.get_program_accounts(&solana_stake_program::id())?;
|
||||||
let stake_history_account = rpc_client.get_account(&stake_history::id())?;
|
|
||||||
progress_bar.finish_and_clear();
|
progress_bar.finish_and_clear();
|
||||||
let clock_account = rpc_client.get_account(&sysvar::clock::id())?;
|
|
||||||
|
|
||||||
let stake_history = StakeHistory::from_account(&stake_history_account).ok_or_else(|| {
|
|
||||||
CliError::RpcRequestError("Failed to deserialize stake history".to_string())
|
|
||||||
})?;
|
|
||||||
let clock: Clock = Sysvar::from_account(&clock_account).ok_or_else(|| {
|
|
||||||
CliError::RpcRequestError("Failed to deserialize clock sysvar".to_string())
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let mut stake_accounts: Vec<CliKeyedStakeState> = vec![];
|
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 {
|
||||||
@@ -1188,8 +1198,6 @@ pub fn process_show_stakes(
|
|||||||
stake_account.lamports,
|
stake_account.lamports,
|
||||||
&stake_state,
|
&stake_state,
|
||||||
use_lamports_unit,
|
use_lamports_unit,
|
||||||
&stake_history,
|
|
||||||
&clock,
|
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1206,8 +1214,6 @@ pub fn process_show_stakes(
|
|||||||
stake_account.lamports,
|
stake_account.lamports,
|
||||||
&stake_state,
|
&stake_state,
|
||||||
use_lamports_unit,
|
use_lamports_unit,
|
||||||
&stake_history,
|
|
||||||
&clock,
|
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1225,21 +1231,10 @@ pub fn process_show_validators(
|
|||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
config: &CliConfig,
|
config: &CliConfig,
|
||||||
use_lamports_unit: bool,
|
use_lamports_unit: bool,
|
||||||
|
commitment_config: CommitmentConfig,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let epoch_info = rpc_client.get_epoch_info_with_commitment(config.commitment)?;
|
let epoch_info = rpc_client.get_epoch_info_with_commitment(commitment_config)?;
|
||||||
let vote_accounts = rpc_client.get_vote_accounts_with_commitment(config.commitment)?;
|
let vote_accounts = rpc_client.get_vote_accounts_with_commitment(commitment_config)?;
|
||||||
|
|
||||||
let mut node_version = HashMap::new();
|
|
||||||
let unknown_version = "unknown".to_string();
|
|
||||||
for contact_info in rpc_client.get_cluster_nodes()? {
|
|
||||||
node_version.insert(
|
|
||||||
contact_info.pubkey,
|
|
||||||
contact_info
|
|
||||||
.version
|
|
||||||
.unwrap_or_else(|| unknown_version.clone()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let total_active_stake = vote_accounts
|
let total_active_stake = vote_accounts
|
||||||
.current
|
.current
|
||||||
.iter()
|
.iter()
|
||||||
@@ -1258,58 +1253,21 @@ pub fn process_show_validators(
|
|||||||
current.sort_by(|a, b| b.activated_stake.cmp(&a.activated_stake));
|
current.sort_by(|a, b| b.activated_stake.cmp(&a.activated_stake));
|
||||||
let current_validators: Vec<CliValidator> = current
|
let current_validators: Vec<CliValidator> = current
|
||||||
.iter()
|
.iter()
|
||||||
.map(|vote_account| {
|
.map(|vote_account| CliValidator::new(vote_account, epoch_info.epoch))
|
||||||
CliValidator::new(
|
|
||||||
vote_account,
|
|
||||||
epoch_info.epoch,
|
|
||||||
node_version
|
|
||||||
.get(&vote_account.node_pubkey)
|
|
||||||
.unwrap_or(&unknown_version)
|
|
||||||
.clone(),
|
|
||||||
&config.address_labels,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
let mut delinquent = vote_accounts.delinquent;
|
let mut delinquent = vote_accounts.delinquent;
|
||||||
delinquent.sort_by(|a, b| b.activated_stake.cmp(&a.activated_stake));
|
delinquent.sort_by(|a, b| b.activated_stake.cmp(&a.activated_stake));
|
||||||
let delinquent_validators: Vec<CliValidator> = delinquent
|
let delinquent_validators: Vec<CliValidator> = delinquent
|
||||||
.iter()
|
.iter()
|
||||||
.map(|vote_account| {
|
.map(|vote_account| CliValidator::new(vote_account, epoch_info.epoch))
|
||||||
CliValidator::new(
|
|
||||||
vote_account,
|
|
||||||
epoch_info.epoch,
|
|
||||||
node_version
|
|
||||||
.get(&vote_account.node_pubkey)
|
|
||||||
.unwrap_or(&unknown_version)
|
|
||||||
.clone(),
|
|
||||||
&config.address_labels,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut stake_by_version: BTreeMap<_, CliValidatorsStakeByVersion> = BTreeMap::new();
|
|
||||||
for validator in current_validators.iter() {
|
|
||||||
let mut entry = stake_by_version
|
|
||||||
.entry(validator.version.clone())
|
|
||||||
.or_default();
|
|
||||||
entry.current_validators += 1;
|
|
||||||
entry.current_active_stake += validator.activated_stake;
|
|
||||||
}
|
|
||||||
for validator in delinquent_validators.iter() {
|
|
||||||
let mut entry = stake_by_version
|
|
||||||
.entry(validator.version.clone())
|
|
||||||
.or_default();
|
|
||||||
entry.delinquent_validators += 1;
|
|
||||||
entry.delinquent_active_stake += validator.activated_stake;
|
|
||||||
}
|
|
||||||
|
|
||||||
let cli_validators = CliValidators {
|
let cli_validators = CliValidators {
|
||||||
total_active_stake,
|
total_active_stake,
|
||||||
total_current_stake,
|
total_current_stake,
|
||||||
total_deliquent_stake,
|
total_deliquent_stake,
|
||||||
current_validators,
|
current_validators,
|
||||||
delinquent_validators,
|
delinquent_validators,
|
||||||
stake_by_version,
|
|
||||||
use_lamports_unit,
|
use_lamports_unit,
|
||||||
};
|
};
|
||||||
Ok(config.output_format.formatted_string(&cli_validators))
|
Ok(config.output_format.formatted_string(&cli_validators))
|
||||||
@@ -1317,40 +1275,41 @@ pub fn process_show_validators(
|
|||||||
|
|
||||||
pub fn process_transaction_history(
|
pub fn process_transaction_history(
|
||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
config: &CliConfig,
|
|
||||||
address: &Pubkey,
|
address: &Pubkey,
|
||||||
before: Option<Signature>,
|
end_slot: Option<Slot>, // None == use latest slot
|
||||||
until: Option<Signature>,
|
slot_limit: Option<u64>,
|
||||||
limit: usize,
|
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let results = rpc_client.get_confirmed_signatures_for_address2_with_config(
|
let end_slot = {
|
||||||
address,
|
if let Some(end_slot) = end_slot {
|
||||||
GetConfirmedSignaturesForAddress2Config {
|
end_slot
|
||||||
before,
|
|
||||||
until,
|
|
||||||
limit: Some(limit),
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let transactions_found = format!("{} transactions found", results.len());
|
|
||||||
|
|
||||||
for result in results {
|
|
||||||
if config.verbose {
|
|
||||||
println!(
|
|
||||||
"{} [slot={} status={}] {}",
|
|
||||||
result.signature,
|
|
||||||
result.slot,
|
|
||||||
match result.err {
|
|
||||||
None => "Confirmed".to_string(),
|
|
||||||
Some(err) => format!("Failed: {:?}", err),
|
|
||||||
},
|
|
||||||
result.memo.unwrap_or_else(|| "".to_string()),
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
println!("{}", result.signature);
|
rpc_client.get_slot_with_commitment(CommitmentConfig::max())?
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
let mut start_slot = match slot_limit {
|
||||||
|
Some(slot_limit) => end_slot.saturating_sub(slot_limit),
|
||||||
|
None => rpc_client.minimum_ledger_slot()?,
|
||||||
|
};
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Transactions affecting {} within slots [{},{}]",
|
||||||
|
address, start_slot, end_slot
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut transaction_count = 0;
|
||||||
|
while start_slot < end_slot {
|
||||||
|
let signatures = rpc_client.get_confirmed_signatures_for_address(
|
||||||
|
address,
|
||||||
|
start_slot,
|
||||||
|
(start_slot + MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS_SLOT_RANGE).min(end_slot),
|
||||||
|
)?;
|
||||||
|
for signature in &signatures {
|
||||||
|
println!("{}", signature);
|
||||||
}
|
}
|
||||||
Ok(transactions_found)
|
transaction_count += signatures.len();
|
||||||
|
start_slot += MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS_SLOT_RANGE;
|
||||||
|
}
|
||||||
|
Ok(format!("{} transactions found", transaction_count))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -1416,24 +1375,15 @@ mod tests {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
let test_get_epoch = test_commands
|
|
||||||
.clone()
|
|
||||||
.get_matches_from(vec!["test", "epoch"]);
|
|
||||||
assert_eq!(
|
|
||||||
parse_command(&test_get_epoch, &default_keypair_file, &mut None).unwrap(),
|
|
||||||
CliCommandInfo {
|
|
||||||
command: CliCommand::GetEpoch,
|
|
||||||
signers: vec![],
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let test_get_epoch_info = test_commands
|
let test_get_epoch_info = test_commands
|
||||||
.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_keypair_file, &mut None).unwrap(),
|
parse_command(&test_get_epoch_info, &default_keypair_file, &mut None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::GetEpochInfo,
|
command: CliCommand::GetEpochInfo {
|
||||||
|
commitment_config: CommitmentConfig::recent(),
|
||||||
|
},
|
||||||
signers: vec![],
|
signers: vec![],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -1453,7 +1403,22 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_get_slot, &default_keypair_file, &mut None).unwrap(),
|
parse_command(&test_get_slot, &default_keypair_file, &mut None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::GetSlot,
|
command: CliCommand::GetSlot {
|
||||||
|
commitment_config: CommitmentConfig::recent(),
|
||||||
|
},
|
||||||
|
signers: vec![],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let test_get_epoch = test_commands
|
||||||
|
.clone()
|
||||||
|
.get_matches_from(vec!["test", "epoch"]);
|
||||||
|
assert_eq!(
|
||||||
|
parse_command(&test_get_epoch, &default_keypair_file, &mut None).unwrap(),
|
||||||
|
CliCommandInfo {
|
||||||
|
command: CliCommand::GetEpoch {
|
||||||
|
commitment_config: CommitmentConfig::recent(),
|
||||||
|
},
|
||||||
signers: vec![],
|
signers: vec![],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -1464,7 +1429,9 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_total_supply, &default_keypair_file, &mut None).unwrap(),
|
parse_command(&test_total_supply, &default_keypair_file, &mut None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::TotalSupply,
|
command: CliCommand::TotalSupply {
|
||||||
|
commitment_config: CommitmentConfig::recent(),
|
||||||
|
},
|
||||||
signers: vec![],
|
signers: vec![],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -1475,7 +1442,9 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&test_transaction_count, &default_keypair_file, &mut None).unwrap(),
|
parse_command(&test_transaction_count, &default_keypair_file, &mut None).unwrap(),
|
||||||
CliCommandInfo {
|
CliCommandInfo {
|
||||||
command: CliCommand::GetTransactionCount,
|
command: CliCommand::GetTransactionCount {
|
||||||
|
commitment_config: CommitmentConfig::recent(),
|
||||||
|
},
|
||||||
signers: vec![],
|
signers: vec![],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -1500,6 +1469,7 @@ mod tests {
|
|||||||
interval: Duration::from_secs(1),
|
interval: Duration::from_secs(1),
|
||||||
count: Some(2),
|
count: Some(2),
|
||||||
timeout: Duration::from_secs(3),
|
timeout: Duration::from_secs(3),
|
||||||
|
commitment_config: CommitmentConfig::max(),
|
||||||
},
|
},
|
||||||
signers: vec![default_keypair.into()],
|
signers: vec![default_keypair.into()],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
use crate::cli::SettingType;
|
use crate::cli::SettingType;
|
||||||
use console::style;
|
use console::style;
|
||||||
use indicatif::{ProgressBar, ProgressStyle};
|
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
hash::Hash, native_token::lamports_to_sol, program_utils::limited_deserialize,
|
hash::Hash, native_token::lamports_to_sol, program_utils::limited_deserialize,
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
};
|
};
|
||||||
use solana_transaction_status::UiTransactionStatusMeta;
|
use solana_transaction_status::RpcTransactionStatusMeta;
|
||||||
use std::{collections::HashMap, fmt, io};
|
use std::{fmt, io};
|
||||||
|
|
||||||
// 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) {
|
||||||
@@ -27,19 +26,6 @@ pub fn writeln_name_value(f: &mut fmt::Formatter, name: &str, value: &str) -> fm
|
|||||||
writeln!(f, "{} {}", style(name).bold(), styled_value)
|
writeln!(f, "{} {}", style(name).bold(), styled_value)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn format_labeled_address(pubkey: &str, address_labels: &HashMap<String, String>) -> String {
|
|
||||||
let label = address_labels.get(pubkey);
|
|
||||||
match label {
|
|
||||||
Some(label) => format!(
|
|
||||||
"{:.31} ({:.4}..{})",
|
|
||||||
label,
|
|
||||||
pubkey,
|
|
||||||
pubkey.split_at(pubkey.len() - 4).1
|
|
||||||
),
|
|
||||||
None => pubkey.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn println_name_value_or(name: &str, value: &str, setting_type: SettingType) {
|
pub fn println_name_value_or(name: &str, value: &str, setting_type: SettingType) {
|
||||||
let description = match setting_type {
|
let description = match setting_type {
|
||||||
SettingType::Explicit => "",
|
SettingType::Explicit => "",
|
||||||
@@ -81,7 +67,7 @@ pub fn println_signers(
|
|||||||
pub fn write_transaction<W: io::Write>(
|
pub fn write_transaction<W: io::Write>(
|
||||||
w: &mut W,
|
w: &mut W,
|
||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
transaction_status: &Option<UiTransactionStatusMeta>,
|
transaction_status: &Option<RpcTransactionStatusMeta>,
|
||||||
prefix: &str,
|
prefix: &str,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
let message = &transaction.message;
|
let message = &transaction.message;
|
||||||
@@ -204,7 +190,7 @@ pub fn write_transaction<W: io::Write>(
|
|||||||
|
|
||||||
pub fn println_transaction(
|
pub fn println_transaction(
|
||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
transaction_status: &Option<UiTransactionStatusMeta>,
|
transaction_status: &Option<RpcTransactionStatusMeta>,
|
||||||
prefix: &str,
|
prefix: &str,
|
||||||
) {
|
) {
|
||||||
let mut w = Vec::new();
|
let mut w = Vec::new();
|
||||||
@@ -214,41 +200,3 @@ pub fn println_transaction(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new process bar for processing that will take an unknown amount of time
|
|
||||||
pub fn new_spinner_progress_bar() -> ProgressBar {
|
|
||||||
let progress_bar = ProgressBar::new(42);
|
|
||||||
progress_bar
|
|
||||||
.set_style(ProgressStyle::default_spinner().template("{spinner:.green} {wide_msg}"));
|
|
||||||
progress_bar.enable_steady_tick(100);
|
|
||||||
progress_bar
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
use solana_sdk::pubkey::Pubkey;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_format_labeled_address() {
|
|
||||||
let pubkey = Pubkey::default().to_string();
|
|
||||||
let mut address_labels = HashMap::new();
|
|
||||||
|
|
||||||
assert_eq!(format_labeled_address(&pubkey, &address_labels), pubkey);
|
|
||||||
|
|
||||||
address_labels.insert(pubkey.to_string(), "Default Address".to_string());
|
|
||||||
assert_eq!(
|
|
||||||
&format_labeled_address(&pubkey, &address_labels),
|
|
||||||
"Default Address (1111..1111)"
|
|
||||||
);
|
|
||||||
|
|
||||||
address_labels.insert(
|
|
||||||
pubkey.to_string(),
|
|
||||||
"abcdefghijklmnopqrstuvwxyz1234567890".to_string(),
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
&format_labeled_address(&pubkey, &address_labels),
|
|
||||||
"abcdefghijklmnopqrstuvwxyz12345 (1111..1111)"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
122
cli/src/main.rs
122
cli/src/main.rs
@@ -1,43 +1,25 @@
|
|||||||
use clap::{
|
use clap::{crate_description, crate_name, AppSettings, Arg, ArgGroup, ArgMatches, SubCommand};
|
||||||
crate_description, crate_name, value_t_or_exit, AppSettings, Arg, ArgGroup, ArgMatches,
|
|
||||||
SubCommand,
|
|
||||||
};
|
|
||||||
use console::style;
|
use console::style;
|
||||||
|
|
||||||
use solana_clap_utils::{
|
use solana_clap_utils::{
|
||||||
commitment::COMMITMENT_ARG, input_parsers::commitment_of, input_validators::is_url,
|
input_validators::is_url, keypair::SKIP_SEED_PHRASE_VALIDATION_ARG, DisplayError,
|
||||||
keypair::SKIP_SEED_PHRASE_VALIDATION_ARG, DisplayError,
|
|
||||||
};
|
};
|
||||||
use solana_cli::{
|
use solana_cli::{
|
||||||
cli::{
|
cli::{app, parse_command, process_command, CliCommandInfo, CliConfig, CliSigners},
|
||||||
app, parse_command, process_command, CliCommandInfo, CliConfig, CliSigners,
|
|
||||||
DEFAULT_RPC_TIMEOUT_SECONDS,
|
|
||||||
},
|
|
||||||
cli_output::OutputFormat,
|
cli_output::OutputFormat,
|
||||||
display::{println_name_value, println_name_value_or},
|
display::{println_name_value, println_name_value_or},
|
||||||
};
|
};
|
||||||
use solana_cli_config::{Config, CONFIG_FILE};
|
use solana_cli_config::{Config, CONFIG_FILE};
|
||||||
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::{error, sync::Arc};
|
||||||
|
|
||||||
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)) => match matches.subcommand() {
|
||||||
let config_file = match matches.value_of("config_file") {
|
|
||||||
None => {
|
|
||||||
println!(
|
|
||||||
"{} Either provide the `--config` arg or ensure home directory exists to use the default config location",
|
|
||||||
style("No config file found.").bold()
|
|
||||||
);
|
|
||||||
return Ok(false);
|
|
||||||
}
|
|
||||||
Some(config_file) => config_file,
|
|
||||||
};
|
|
||||||
let mut config = Config::load(config_file).unwrap_or_default();
|
|
||||||
|
|
||||||
match matches.subcommand() {
|
|
||||||
("get", Some(subcommand_matches)) => {
|
("get", Some(subcommand_matches)) => {
|
||||||
|
if let Some(config_file) = matches.value_of("config_file") {
|
||||||
|
let config = Config::load(config_file).unwrap_or_default();
|
||||||
|
|
||||||
let (url_setting_type, json_rpc_url) =
|
let (url_setting_type, json_rpc_url) =
|
||||||
CliConfig::compute_json_rpc_url_setting("", &config.json_rpc_url);
|
CliConfig::compute_json_rpc_url_setting("", &config.json_rpc_url);
|
||||||
let (ws_setting_type, websocket_url) = CliConfig::compute_websocket_url_setting(
|
let (ws_setting_type, websocket_url) = CliConfig::compute_websocket_url_setting(
|
||||||
@@ -63,8 +45,17 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
|
|||||||
println_name_value_or("WebSocket URL:", &websocket_url, ws_setting_type);
|
println_name_value_or("WebSocket URL:", &websocket_url, ws_setting_type);
|
||||||
println_name_value_or("Keypair Path:", &keypair_path, keypair_setting_type);
|
println_name_value_or("Keypair Path:", &keypair_path, keypair_setting_type);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
println!(
|
||||||
|
"{} Either provide the `--config` arg or ensure home directory exists to use the default config location",
|
||||||
|
style("No config file found.").bold()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
("set", Some(subcommand_matches)) => {
|
("set", Some(subcommand_matches)) => {
|
||||||
|
if let Some(config_file) = matches.value_of("config_file") {
|
||||||
|
let mut config = Config::load(config_file).unwrap_or_default();
|
||||||
if let Some(url) = subcommand_matches.value_of("json_rpc_url") {
|
if let Some(url) = subcommand_matches.value_of("json_rpc_url") {
|
||||||
config.json_rpc_url = url.to_string();
|
config.json_rpc_url = url.to_string();
|
||||||
// Revert to a computed `websocket_url` value when `json_rpc_url` is
|
// Revert to a computed `websocket_url` value when `json_rpc_url` is
|
||||||
@@ -77,7 +68,6 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
|
|||||||
if let Some(keypair) = subcommand_matches.value_of("keypair") {
|
if let Some(keypair) = subcommand_matches.value_of("keypair") {
|
||||||
config.keypair_path = keypair.to_string();
|
config.keypair_path = keypair.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
config.save(config_file)?;
|
config.save(config_file)?;
|
||||||
|
|
||||||
let (url_setting_type, json_rpc_url) =
|
let (url_setting_type, json_rpc_url) =
|
||||||
@@ -95,22 +85,16 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
|
|||||||
println_name_value_or("RPC URL:", &json_rpc_url, url_setting_type);
|
println_name_value_or("RPC URL:", &json_rpc_url, url_setting_type);
|
||||||
println_name_value_or("WebSocket URL:", &websocket_url, ws_setting_type);
|
println_name_value_or("WebSocket URL:", &websocket_url, ws_setting_type);
|
||||||
println_name_value_or("Keypair Path:", &keypair_path, keypair_setting_type);
|
println_name_value_or("Keypair Path:", &keypair_path, keypair_setting_type);
|
||||||
}
|
} else {
|
||||||
("import-address-labels", Some(subcommand_matches)) => {
|
println!(
|
||||||
let filename = value_t_or_exit!(subcommand_matches, "filename", PathBuf);
|
"{} Either provide the `--config` arg or ensure home directory exists to use the default config location",
|
||||||
config.import_address_labels(&filename)?;
|
style("No config file found.").bold()
|
||||||
config.save(config_file)?;
|
);
|
||||||
println!("Address labels imported from {:?}", filename);
|
|
||||||
}
|
|
||||||
("export-address-labels", Some(subcommand_matches)) => {
|
|
||||||
let filename = value_t_or_exit!(subcommand_matches, "filename", PathBuf);
|
|
||||||
config.export_address_labels(&filename)?;
|
|
||||||
println!("Address labels exported to {:?}", filename);
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
_ => true,
|
_ => true,
|
||||||
};
|
};
|
||||||
Ok(parse_args)
|
Ok(parse_args)
|
||||||
@@ -129,10 +113,6 @@ 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 rpc_timeout = value_t_or_exit!(matches, "rpc_timeout", u64);
|
|
||||||
let rpc_timeout = Duration::from_secs(rpc_timeout);
|
|
||||||
|
|
||||||
let (_, websocket_url) = CliConfig::compute_websocket_url_setting(
|
let (_, websocket_url) = CliConfig::compute_websocket_url_setting(
|
||||||
matches.value_of("websocket_url").unwrap_or(""),
|
matches.value_of("websocket_url").unwrap_or(""),
|
||||||
&config.websocket_url,
|
&config.websocket_url,
|
||||||
@@ -156,18 +136,6 @@ pub fn parse_args<'a>(
|
|||||||
})
|
})
|
||||||
.unwrap_or(OutputFormat::Display);
|
.unwrap_or(OutputFormat::Display);
|
||||||
|
|
||||||
let commitment = matches
|
|
||||||
.subcommand_name()
|
|
||||||
.and_then(|name| matches.subcommand_matches(name))
|
|
||||||
.and_then(|sub_matches| commitment_of(sub_matches, COMMITMENT_ARG.long))
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let address_labels = if matches.is_present("no_address_labels") {
|
|
||||||
HashMap::new()
|
|
||||||
} else {
|
|
||||||
config.address_labels
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
CliConfig {
|
CliConfig {
|
||||||
command,
|
command,
|
||||||
@@ -176,12 +144,8 @@ pub fn parse_args<'a>(
|
|||||||
signers: vec![],
|
signers: vec![],
|
||||||
keypair_path: default_signer_path,
|
keypair_path: default_signer_path,
|
||||||
rpc_client: None,
|
rpc_client: None,
|
||||||
rpc_timeout,
|
|
||||||
verbose: matches.is_present("verbose"),
|
verbose: matches.is_present("verbose"),
|
||||||
output_format,
|
output_format,
|
||||||
commitment,
|
|
||||||
send_transaction_config: RpcSendTransactionConfig::default(),
|
|
||||||
address_labels,
|
|
||||||
},
|
},
|
||||||
signers,
|
signers,
|
||||||
))
|
))
|
||||||
@@ -243,12 +207,6 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||||||
.global(true)
|
.global(true)
|
||||||
.help("Show additional information"),
|
.help("Show additional information"),
|
||||||
)
|
)
|
||||||
.arg(
|
|
||||||
Arg::with_name("no_address_labels")
|
|
||||||
.long("no-address-labels")
|
|
||||||
.global(true)
|
|
||||||
.help("Do not use address labels in the output"),
|
|
||||||
)
|
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("output_format")
|
Arg::with_name("output_format")
|
||||||
.long("output")
|
.long("output")
|
||||||
@@ -264,16 +222,6 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||||||
.global(true)
|
.global(true)
|
||||||
.help(SKIP_SEED_PHRASE_VALIDATION_ARG.help),
|
.help(SKIP_SEED_PHRASE_VALIDATION_ARG.help),
|
||||||
)
|
)
|
||||||
.arg(
|
|
||||||
Arg::with_name("rpc_timeout")
|
|
||||||
.long("rpc-timeout")
|
|
||||||
.value_name("SECONDS")
|
|
||||||
.takes_value(true)
|
|
||||||
.default_value(DEFAULT_RPC_TIMEOUT_SECONDS)
|
|
||||||
.global(true)
|
|
||||||
.hidden(true)
|
|
||||||
.help("Timeout value for RPC requests"),
|
|
||||||
)
|
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("config")
|
SubCommand::with_name("config")
|
||||||
.about("Solana command-line tool configuration settings")
|
.about("Solana command-line tool configuration settings")
|
||||||
@@ -300,28 +248,6 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||||||
.multiple(true)
|
.multiple(true)
|
||||||
.required(true),
|
.required(true),
|
||||||
),
|
),
|
||||||
)
|
|
||||||
.subcommand(
|
|
||||||
SubCommand::with_name("import-address-labels")
|
|
||||||
.about("Import a list of address labels")
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("filename")
|
|
||||||
.index(1)
|
|
||||||
.value_name("FILENAME")
|
|
||||||
.takes_value(true)
|
|
||||||
.help("YAML file of address labels"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.subcommand(
|
|
||||||
SubCommand::with_name("export-address-labels")
|
|
||||||
.about("Export the current address labels")
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("filename")
|
|
||||||
.index(1)
|
|
||||||
.value_name("FILENAME")
|
|
||||||
.takes_value(true)
|
|
||||||
.help("YAML file to receive the current address labels"),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
checks::{check_account_for_fee_with_commitment, check_unique_pubkeys},
|
checks::{check_account_for_fee, check_unique_pubkeys},
|
||||||
cli::{
|
cli::{
|
||||||
generate_unique_signers, log_instruction_custom_error, CliCommand, CliCommandInfo,
|
generate_unique_signers, log_instruction_custom_error, CliCommand, CliCommandInfo,
|
||||||
CliConfig, CliError, ProcessResult, SignerIndex,
|
CliConfig, CliError, ProcessResult, SignerIndex,
|
||||||
@@ -16,7 +16,6 @@ use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
|||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::Account,
|
account::Account,
|
||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
commitment_config::CommitmentConfig,
|
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
message::Message,
|
message::Message,
|
||||||
nonce::{
|
nonce::{
|
||||||
@@ -223,23 +222,10 @@ impl NonceSubCommands for App<'_, '_> {
|
|||||||
pub fn get_account(
|
pub fn get_account(
|
||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
nonce_pubkey: &Pubkey,
|
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> {
|
) -> Result<Account, CliNonceError> {
|
||||||
rpc_client
|
rpc_client
|
||||||
.get_account_with_commitment(nonce_pubkey, commitment)
|
.get_account(nonce_pubkey)
|
||||||
.map_err(|e| CliNonceError::Client(format!("{}", e)))
|
.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) {
|
.and_then(|a| match account_identity_ok(&a) {
|
||||||
Ok(()) => Ok(a),
|
Ok(()) => Ok(a),
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
@@ -447,28 +433,21 @@ pub fn process_authorize_nonce_account(
|
|||||||
nonce_authority: SignerIndex,
|
nonce_authority: SignerIndex,
|
||||||
new_authority: &Pubkey,
|
new_authority: &Pubkey,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let (recent_blockhash, fee_calculator, _) = rpc_client
|
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||||
.get_recent_blockhash_with_commitment(config.commitment)?
|
|
||||||
.value;
|
|
||||||
|
|
||||||
let nonce_authority = config.signers[nonce_authority];
|
let nonce_authority = config.signers[nonce_authority];
|
||||||
let ix = authorize_nonce_account(nonce_account, &nonce_authority.pubkey(), new_authority);
|
let ix = authorize_nonce_account(nonce_account, &nonce_authority.pubkey(), new_authority);
|
||||||
let message = Message::new(&[ix], Some(&config.signers[0].pubkey()));
|
let message = Message::new_with_payer(&[ix], Some(&config.signers[0].pubkey()));
|
||||||
let mut tx = Transaction::new_unsigned(message);
|
let mut tx = Transaction::new_unsigned(message);
|
||||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||||
|
|
||||||
check_account_for_fee_with_commitment(
|
check_account_for_fee(
|
||||||
rpc_client,
|
rpc_client,
|
||||||
&config.signers[0].pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
&tx.message,
|
&tx.message,
|
||||||
config.commitment,
|
|
||||||
)?;
|
)?;
|
||||||
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
|
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||||
&tx,
|
|
||||||
config.commitment,
|
|
||||||
config.send_transaction_config,
|
|
||||||
);
|
|
||||||
log_instruction_custom_error::<NonceError>(result, &config)
|
log_instruction_custom_error::<NonceError>(result, &config)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -512,12 +491,10 @@ pub fn process_create_nonce_account(
|
|||||||
lamports,
|
lamports,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
Message::new(&ixs, Some(&config.signers[0].pubkey()))
|
Message::new_with_payer(&ixs, Some(&config.signers[0].pubkey()))
|
||||||
};
|
};
|
||||||
|
|
||||||
let (recent_blockhash, fee_calculator, _) = rpc_client
|
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||||
.get_recent_blockhash_with_commitment(config.commitment)?
|
|
||||||
.value;
|
|
||||||
|
|
||||||
let (message, lamports) = resolve_spend_tx_and_check_account_balance(
|
let (message, lamports) = resolve_spend_tx_and_check_account_balance(
|
||||||
rpc_client,
|
rpc_client,
|
||||||
@@ -526,12 +503,9 @@ pub fn process_create_nonce_account(
|
|||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
&config.signers[0].pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
build_message,
|
build_message,
|
||||||
config.commitment,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if let Ok(nonce_account) =
|
if let Ok(nonce_account) = get_account(rpc_client, &nonce_account_address) {
|
||||||
get_account_with_commitment(rpc_client, &nonce_account_address, config.commitment)
|
|
||||||
{
|
|
||||||
let err_msg = if state_from_account(&nonce_account).is_ok() {
|
let err_msg = if state_from_account(&nonce_account).is_ok() {
|
||||||
format!("Nonce account {} already exists", nonce_account_address)
|
format!("Nonce account {} already exists", nonce_account_address)
|
||||||
} else {
|
} else {
|
||||||
@@ -554,22 +528,12 @@ pub fn process_create_nonce_account(
|
|||||||
|
|
||||||
let mut tx = Transaction::new_unsigned(message);
|
let mut tx = Transaction::new_unsigned(message);
|
||||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||||
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
|
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||||
&tx,
|
|
||||||
config.commitment,
|
|
||||||
config.send_transaction_config,
|
|
||||||
);
|
|
||||||
log_instruction_custom_error::<SystemError>(result, &config)
|
log_instruction_custom_error::<SystemError>(result, &config)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_get_nonce(
|
pub fn process_get_nonce(rpc_client: &RpcClient, nonce_account_pubkey: &Pubkey) -> ProcessResult {
|
||||||
rpc_client: &RpcClient,
|
match get_account(rpc_client, nonce_account_pubkey).and_then(|ref a| state_from_account(a))? {
|
||||||
config: &CliConfig,
|
|
||||||
nonce_account_pubkey: &Pubkey,
|
|
||||||
) -> ProcessResult {
|
|
||||||
match get_account_with_commitment(rpc_client, nonce_account_pubkey, config.commitment)
|
|
||||||
.and_then(|ref a| state_from_account(a))?
|
|
||||||
{
|
|
||||||
State::Uninitialized => Ok("Nonce account is uninitialized".to_string()),
|
State::Uninitialized => Ok("Nonce account is uninitialized".to_string()),
|
||||||
State::Initialized(ref data) => Ok(format!("{:?}", data.blockhash)),
|
State::Initialized(ref data) => Ok(format!("{:?}", data.blockhash)),
|
||||||
}
|
}
|
||||||
@@ -586,9 +550,7 @@ pub fn process_new_nonce(
|
|||||||
(&nonce_account, "nonce_account_pubkey".to_string()),
|
(&nonce_account, "nonce_account_pubkey".to_string()),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let nonce_account_check =
|
if rpc_client.get_account(&nonce_account).is_err() {
|
||||||
rpc_client.get_account_with_commitment(&nonce_account, config.commitment);
|
|
||||||
if nonce_account_check.is_err() || nonce_account_check.unwrap().value.is_none() {
|
|
||||||
return Err(CliError::BadParameter(
|
return Err(CliError::BadParameter(
|
||||||
"Unable to create new nonce, no nonce account found".to_string(),
|
"Unable to create new nonce, no nonce account found".to_string(),
|
||||||
)
|
)
|
||||||
@@ -597,24 +559,17 @@ pub fn process_new_nonce(
|
|||||||
|
|
||||||
let nonce_authority = config.signers[nonce_authority];
|
let nonce_authority = config.signers[nonce_authority];
|
||||||
let ix = advance_nonce_account(&nonce_account, &nonce_authority.pubkey());
|
let ix = advance_nonce_account(&nonce_account, &nonce_authority.pubkey());
|
||||||
let (recent_blockhash, fee_calculator, _) = rpc_client
|
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||||
.get_recent_blockhash_with_commitment(config.commitment)?
|
let message = Message::new_with_payer(&[ix], Some(&config.signers[0].pubkey()));
|
||||||
.value;
|
|
||||||
let message = Message::new(&[ix], Some(&config.signers[0].pubkey()));
|
|
||||||
let mut tx = Transaction::new_unsigned(message);
|
let mut tx = Transaction::new_unsigned(message);
|
||||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||||
check_account_for_fee_with_commitment(
|
check_account_for_fee(
|
||||||
rpc_client,
|
rpc_client,
|
||||||
&config.signers[0].pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
&tx.message,
|
&tx.message,
|
||||||
config.commitment,
|
|
||||||
)?;
|
)?;
|
||||||
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
|
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||||
&tx,
|
|
||||||
config.commitment,
|
|
||||||
config.send_transaction_config,
|
|
||||||
);
|
|
||||||
log_instruction_custom_error::<SystemError>(result, &config)
|
log_instruction_custom_error::<SystemError>(result, &config)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -624,8 +579,7 @@ pub fn process_show_nonce_account(
|
|||||||
nonce_account_pubkey: &Pubkey,
|
nonce_account_pubkey: &Pubkey,
|
||||||
use_lamports_unit: bool,
|
use_lamports_unit: bool,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let nonce_account =
|
let nonce_account = get_account(rpc_client, nonce_account_pubkey)?;
|
||||||
get_account_with_commitment(rpc_client, nonce_account_pubkey, config.commitment)?;
|
|
||||||
let print_account = |data: Option<&nonce::state::Data>| {
|
let print_account = |data: Option<&nonce::state::Data>| {
|
||||||
let mut nonce_account = CliNonceAccount {
|
let mut nonce_account = CliNonceAccount {
|
||||||
balance: nonce_account.lamports,
|
balance: nonce_account.lamports,
|
||||||
@@ -656,9 +610,7 @@ pub fn process_withdraw_from_nonce_account(
|
|||||||
destination_account_pubkey: &Pubkey,
|
destination_account_pubkey: &Pubkey,
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let (recent_blockhash, fee_calculator, _) = rpc_client
|
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||||
.get_recent_blockhash_with_commitment(config.commitment)?
|
|
||||||
.value;
|
|
||||||
|
|
||||||
let nonce_authority = config.signers[nonce_authority];
|
let nonce_authority = config.signers[nonce_authority];
|
||||||
let ix = withdraw_nonce_account(
|
let ix = withdraw_nonce_account(
|
||||||
@@ -667,21 +619,16 @@ pub fn process_withdraw_from_nonce_account(
|
|||||||
destination_account_pubkey,
|
destination_account_pubkey,
|
||||||
lamports,
|
lamports,
|
||||||
);
|
);
|
||||||
let message = Message::new(&[ix], Some(&config.signers[0].pubkey()));
|
let message = Message::new_with_payer(&[ix], Some(&config.signers[0].pubkey()));
|
||||||
let mut tx = Transaction::new_unsigned(message);
|
let mut tx = Transaction::new_unsigned(message);
|
||||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||||
check_account_for_fee_with_commitment(
|
check_account_for_fee(
|
||||||
rpc_client,
|
rpc_client,
|
||||||
&config.signers[0].pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
&tx.message,
|
&tx.message,
|
||||||
config.commitment,
|
|
||||||
)?;
|
)?;
|
||||||
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
|
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||||
&tx,
|
|
||||||
config.commitment,
|
|
||||||
config.send_transaction_config,
|
|
||||||
);
|
|
||||||
log_instruction_custom_error::<NonceError>(result, &config)
|
log_instruction_custom_error::<NonceError>(result, &config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use solana_sdk::commitment_config::CommitmentConfig;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Source {
|
pub enum Source {
|
||||||
@@ -11,17 +10,14 @@ impl Source {
|
|||||||
pub fn get_blockhash_and_fee_calculator(
|
pub fn get_blockhash_and_fee_calculator(
|
||||||
&self,
|
&self,
|
||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
commitment: CommitmentConfig,
|
|
||||||
) -> Result<(Hash, FeeCalculator), Box<dyn std::error::Error>> {
|
) -> Result<(Hash, FeeCalculator), Box<dyn std::error::Error>> {
|
||||||
match self {
|
match self {
|
||||||
Self::Cluster => {
|
Self::Cluster => {
|
||||||
let res = rpc_client
|
let res = rpc_client.get_recent_blockhash()?;
|
||||||
.get_recent_blockhash_with_commitment(commitment)?
|
Ok(res)
|
||||||
.value;
|
|
||||||
Ok((res.0, res.1))
|
|
||||||
}
|
}
|
||||||
Self::NonceAccount(ref pubkey) => {
|
Self::NonceAccount(ref pubkey) => {
|
||||||
let data = nonce::get_account_with_commitment(rpc_client, pubkey, commitment)
|
let data = nonce::get_account(rpc_client, pubkey)
|
||||||
.and_then(|ref a| nonce::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))
|
||||||
}
|
}
|
||||||
@@ -32,21 +28,23 @@ impl Source {
|
|||||||
&self,
|
&self,
|
||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
blockhash: &Hash,
|
blockhash: &Hash,
|
||||||
commitment: CommitmentConfig,
|
|
||||||
) -> Result<Option<FeeCalculator>, Box<dyn std::error::Error>> {
|
) -> Result<Option<FeeCalculator>, Box<dyn std::error::Error>> {
|
||||||
match self {
|
match self {
|
||||||
Self::Cluster => {
|
Self::Cluster => {
|
||||||
let res = rpc_client
|
let res = rpc_client.get_fee_calculator_for_blockhash(blockhash)?;
|
||||||
.get_fee_calculator_for_blockhash_with_commitment(blockhash, commitment)?
|
|
||||||
.value;
|
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
Self::NonceAccount(ref pubkey) => {
|
Self::NonceAccount(ref pubkey) => {
|
||||||
let res = nonce::get_account_with_commitment(rpc_client, pubkey, commitment)?;
|
let res = nonce::get_account(rpc_client, pubkey)
|
||||||
let res = nonce::data_from_account(&res)?;
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
Ok(Some(res)
|
.and_then(|d| {
|
||||||
.filter(|d| d.blockhash == *blockhash)
|
if d.blockhash == *blockhash {
|
||||||
.map(|d| d.fee_calculator))
|
Ok(Some(d.fee_calculator))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
Ok(res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,19 +80,16 @@ impl BlockhashQuery {
|
|||||||
pub fn get_blockhash_and_fee_calculator(
|
pub fn get_blockhash_and_fee_calculator(
|
||||||
&self,
|
&self,
|
||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
commitment: CommitmentConfig,
|
|
||||||
) -> Result<(Hash, FeeCalculator), Box<dyn std::error::Error>> {
|
) -> Result<(Hash, FeeCalculator), Box<dyn std::error::Error>> {
|
||||||
match self {
|
match self {
|
||||||
BlockhashQuery::None(hash) => Ok((*hash, FeeCalculator::default())),
|
BlockhashQuery::None(hash) => Ok((*hash, FeeCalculator::default())),
|
||||||
BlockhashQuery::FeeCalculator(source, hash) => {
|
BlockhashQuery::FeeCalculator(source, hash) => {
|
||||||
let fee_calculator = source
|
let fee_calculator = source
|
||||||
.get_fee_calculator(rpc_client, hash, commitment)?
|
.get_fee_calculator(rpc_client, hash)?
|
||||||
.ok_or(format!("Hash has expired {:?}", hash))?;
|
.ok_or(format!("Hash has expired {:?}", hash))?;
|
||||||
Ok((*hash, fee_calculator))
|
Ok((*hash, fee_calculator))
|
||||||
}
|
}
|
||||||
BlockhashQuery::All(source) => {
|
BlockhashQuery::All(source) => source.get_blockhash_and_fee_calculator(rpc_client),
|
||||||
source.get_blockhash_and_fee_calculator(rpc_client, commitment)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -111,10 +106,9 @@ mod tests {
|
|||||||
use crate::{nonce::nonce_arg, offline::blockhash_query::BlockhashQuery};
|
use crate::{nonce::nonce_arg, offline::blockhash_query::BlockhashQuery};
|
||||||
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_client::{
|
use solana_client::{
|
||||||
rpc_request::RpcRequest,
|
rpc_request::RpcRequest,
|
||||||
rpc_response::{Response, RpcFeeCalculator, RpcResponseContext},
|
rpc_response::{Response, RpcAccount, RpcFeeCalculator, RpcResponseContext},
|
||||||
};
|
};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::Account, fee_calculator::FeeCalculator, hash::hash, nonce, system_program,
|
account::Account, fee_calculator::FeeCalculator, hash::hash, nonce, system_program,
|
||||||
@@ -298,7 +292,7 @@ mod tests {
|
|||||||
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
|
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
BlockhashQuery::default()
|
BlockhashQuery::default()
|
||||||
.get_blockhash_and_fee_calculator(&rpc_client, CommitmentConfig::default())
|
.get_blockhash_and_fee_calculator(&rpc_client)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
(rpc_blockhash, rpc_fee_calc.clone()),
|
(rpc_blockhash, rpc_fee_calc.clone()),
|
||||||
);
|
);
|
||||||
@@ -314,7 +308,7 @@ mod tests {
|
|||||||
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
|
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
BlockhashQuery::FeeCalculator(Source::Cluster, test_blockhash)
|
BlockhashQuery::FeeCalculator(Source::Cluster, test_blockhash)
|
||||||
.get_blockhash_and_fee_calculator(&rpc_client, CommitmentConfig::default())
|
.get_blockhash_and_fee_calculator(&rpc_client)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
(test_blockhash, rpc_fee_calc),
|
(test_blockhash, rpc_fee_calc),
|
||||||
);
|
);
|
||||||
@@ -326,13 +320,13 @@ mod tests {
|
|||||||
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
|
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
BlockhashQuery::None(test_blockhash)
|
BlockhashQuery::None(test_blockhash)
|
||||||
.get_blockhash_and_fee_calculator(&rpc_client, CommitmentConfig::default())
|
.get_blockhash_and_fee_calculator(&rpc_client)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
(test_blockhash, FeeCalculator::default()),
|
(test_blockhash, FeeCalculator::default()),
|
||||||
);
|
);
|
||||||
let rpc_client = RpcClient::new_mock("fails".to_string());
|
let rpc_client = RpcClient::new_mock("fails".to_string());
|
||||||
assert!(BlockhashQuery::default()
|
assert!(BlockhashQuery::default()
|
||||||
.get_blockhash_and_fee_calculator(&rpc_client, CommitmentConfig::default())
|
.get_blockhash_and_fee_calculator(&rpc_client)
|
||||||
.is_err());
|
.is_err());
|
||||||
|
|
||||||
let nonce_blockhash = Hash::new(&[2u8; 32]);
|
let nonce_blockhash = Hash::new(&[2u8; 32]);
|
||||||
@@ -350,13 +344,7 @@ mod tests {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let nonce_pubkey = Pubkey::new(&[4u8; 32]);
|
let nonce_pubkey = Pubkey::new(&[4u8; 32]);
|
||||||
let rpc_nonce_account = UiAccount::encode(
|
let rpc_nonce_account = RpcAccount::encode(nonce_account);
|
||||||
&nonce_pubkey,
|
|
||||||
nonce_account,
|
|
||||||
UiAccountEncoding::Base64,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
let get_account_response = json!(Response {
|
let get_account_response = json!(Response {
|
||||||
context: RpcResponseContext { slot: 1 },
|
context: RpcResponseContext { slot: 1 },
|
||||||
value: json!(Some(rpc_nonce_account)),
|
value: json!(Some(rpc_nonce_account)),
|
||||||
@@ -367,7 +355,7 @@ mod tests {
|
|||||||
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
|
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
BlockhashQuery::All(Source::NonceAccount(nonce_pubkey))
|
BlockhashQuery::All(Source::NonceAccount(nonce_pubkey))
|
||||||
.get_blockhash_and_fee_calculator(&rpc_client, CommitmentConfig::default())
|
.get_blockhash_and_fee_calculator(&rpc_client)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
(nonce_blockhash, nonce_fee_calc.clone()),
|
(nonce_blockhash, nonce_fee_calc.clone()),
|
||||||
);
|
);
|
||||||
@@ -376,7 +364,7 @@ mod tests {
|
|||||||
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
|
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
BlockhashQuery::FeeCalculator(Source::NonceAccount(nonce_pubkey), nonce_blockhash)
|
BlockhashQuery::FeeCalculator(Source::NonceAccount(nonce_pubkey), nonce_blockhash)
|
||||||
.get_blockhash_and_fee_calculator(&rpc_client, CommitmentConfig::default())
|
.get_blockhash_and_fee_calculator(&rpc_client)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
(nonce_blockhash, nonce_fee_calc),
|
(nonce_blockhash, nonce_fee_calc),
|
||||||
);
|
);
|
||||||
@@ -385,7 +373,7 @@ mod tests {
|
|||||||
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
|
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
|
||||||
assert!(
|
assert!(
|
||||||
BlockhashQuery::FeeCalculator(Source::NonceAccount(nonce_pubkey), test_blockhash)
|
BlockhashQuery::FeeCalculator(Source::NonceAccount(nonce_pubkey), test_blockhash)
|
||||||
.get_blockhash_and_fee_calculator(&rpc_client, CommitmentConfig::default())
|
.get_blockhash_and_fee_calculator(&rpc_client)
|
||||||
.is_err()
|
.is_err()
|
||||||
);
|
);
|
||||||
let mut mocks = HashMap::new();
|
let mut mocks = HashMap::new();
|
||||||
@@ -393,14 +381,14 @@ mod tests {
|
|||||||
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
|
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
BlockhashQuery::None(nonce_blockhash)
|
BlockhashQuery::None(nonce_blockhash)
|
||||||
.get_blockhash_and_fee_calculator(&rpc_client, CommitmentConfig::default())
|
.get_blockhash_and_fee_calculator(&rpc_client)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
(nonce_blockhash, FeeCalculator::default()),
|
(nonce_blockhash, FeeCalculator::default()),
|
||||||
);
|
);
|
||||||
|
|
||||||
let rpc_client = RpcClient::new_mock("fails".to_string());
|
let rpc_client = RpcClient::new_mock("fails".to_string());
|
||||||
assert!(BlockhashQuery::All(Source::NonceAccount(nonce_pubkey))
|
assert!(BlockhashQuery::All(Source::NonceAccount(nonce_pubkey))
|
||||||
.get_blockhash_and_fee_calculator(&rpc_client, CommitmentConfig::default())
|
.get_blockhash_and_fee_calculator(&rpc_client)
|
||||||
.is_err());
|
.is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
checks::{calculate_fee, check_account_for_balance_with_commitment},
|
checks::{calculate_fee, check_account_for_balance},
|
||||||
cli::CliError,
|
cli::CliError,
|
||||||
};
|
};
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
use solana_clap_utils::{input_parsers::lamports_of_sol, offline::SIGN_ONLY_ARG};
|
use solana_clap_utils::{input_parsers::lamports_of_sol, offline::SIGN_ONLY_ARG};
|
||||||
use solana_client::rpc_client::RpcClient;
|
use solana_client::rpc_client::RpcClient;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
commitment_config::CommitmentConfig, fee_calculator::FeeCalculator, message::Message,
|
fee_calculator::FeeCalculator, message::Message, native_token::lamports_to_sol, pubkey::Pubkey,
|
||||||
native_token::lamports_to_sol, pubkey::Pubkey,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||||
@@ -50,7 +49,6 @@ pub fn resolve_spend_tx_and_check_account_balance<F>(
|
|||||||
fee_calculator: &FeeCalculator,
|
fee_calculator: &FeeCalculator,
|
||||||
from_pubkey: &Pubkey,
|
from_pubkey: &Pubkey,
|
||||||
build_message: F,
|
build_message: F,
|
||||||
commitment: CommitmentConfig,
|
|
||||||
) -> Result<(Message, u64), CliError>
|
) -> Result<(Message, u64), CliError>
|
||||||
where
|
where
|
||||||
F: Fn(u64) -> Message,
|
F: Fn(u64) -> Message,
|
||||||
@@ -63,7 +61,6 @@ where
|
|||||||
from_pubkey,
|
from_pubkey,
|
||||||
from_pubkey,
|
from_pubkey,
|
||||||
build_message,
|
build_message,
|
||||||
commitment,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +72,6 @@ pub fn resolve_spend_tx_and_check_account_balances<F>(
|
|||||||
from_pubkey: &Pubkey,
|
from_pubkey: &Pubkey,
|
||||||
fee_pubkey: &Pubkey,
|
fee_pubkey: &Pubkey,
|
||||||
build_message: F,
|
build_message: F,
|
||||||
commitment: CommitmentConfig,
|
|
||||||
) -> Result<(Message, u64), CliError>
|
) -> Result<(Message, u64), CliError>
|
||||||
where
|
where
|
||||||
F: Fn(u64) -> Message,
|
F: Fn(u64) -> Message,
|
||||||
@@ -91,9 +87,7 @@ where
|
|||||||
);
|
);
|
||||||
Ok((message, spend))
|
Ok((message, spend))
|
||||||
} else {
|
} else {
|
||||||
let from_balance = rpc_client
|
let from_balance = rpc_client.get_balance(&from_pubkey)?;
|
||||||
.get_balance_with_commitment(&from_pubkey, commitment)?
|
|
||||||
.value;
|
|
||||||
let (message, SpendAndFee { spend, fee }) = resolve_spend_message(
|
let (message, SpendAndFee { spend, fee }) = resolve_spend_message(
|
||||||
amount,
|
amount,
|
||||||
fee_calculator,
|
fee_calculator,
|
||||||
@@ -113,8 +107,7 @@ where
|
|||||||
if from_balance < spend {
|
if from_balance < spend {
|
||||||
return Err(CliError::InsufficientFundsForSpend(lamports_to_sol(spend)));
|
return Err(CliError::InsufficientFundsForSpend(lamports_to_sol(spend)));
|
||||||
}
|
}
|
||||||
if !check_account_for_balance_with_commitment(rpc_client, fee_pubkey, fee, commitment)?
|
if !check_account_for_balance(rpc_client, fee_pubkey, fee)? {
|
||||||
{
|
|
||||||
return Err(CliError::InsufficientFundsForFee(lamports_to_sol(fee)));
|
return Err(CliError::InsufficientFundsForFee(lamports_to_sol(fee)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
427
cli/src/stake.rs
427
cli/src/stake.rs
@@ -1,27 +1,25 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
checks::{check_account_for_fee_with_commitment, check_unique_pubkeys},
|
checks::{check_account_for_fee, check_unique_pubkeys},
|
||||||
cli::{
|
cli::{
|
||||||
fee_payer_arg, generate_unique_signers, log_instruction_custom_error, nonce_authority_arg,
|
fee_payer_arg, generate_unique_signers, log_instruction_custom_error, nonce_authority_arg,
|
||||||
return_signers, CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult,
|
return_signers, CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult,
|
||||||
SignerIndex, FEE_PAYER_ARG,
|
SignerIndex, FEE_PAYER_ARG,
|
||||||
},
|
},
|
||||||
cli_output::{CliStakeHistory, CliStakeHistoryEntry, CliStakeState, CliStakeType},
|
cli_output::{CliStakeHistory, CliStakeHistoryEntry, CliStakeState, CliStakeType},
|
||||||
nonce::{self, check_nonce_account, nonce_arg, NONCE_ARG, NONCE_AUTHORITY_ARG},
|
nonce::{check_nonce_account, nonce_arg, NONCE_ARG, NONCE_AUTHORITY_ARG},
|
||||||
offline::{blockhash_query::BlockhashQuery, *},
|
offline::{blockhash_query::BlockhashQuery, *},
|
||||||
spend_utils::{resolve_spend_tx_and_check_account_balances, SpendAmount},
|
spend_utils::{resolve_spend_tx_and_check_account_balances, SpendAmount},
|
||||||
};
|
};
|
||||||
use clap::{App, Arg, ArgGroup, ArgMatches, SubCommand};
|
use clap::{App, Arg, ArgGroup, ArgMatches, SubCommand};
|
||||||
use solana_clap_utils::{input_parsers::*, input_validators::*, offline::*, ArgConstant};
|
use solana_clap_utils::{input_parsers::*, input_validators::*, offline::*, ArgConstant};
|
||||||
use solana_client::{rpc_client::RpcClient, rpc_request::DELINQUENT_VALIDATOR_SLOT_DISTANCE};
|
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::{
|
||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
clock::Clock,
|
|
||||||
message::Message,
|
message::Message,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
system_instruction::SystemError,
|
system_instruction::SystemError,
|
||||||
sysvar::{
|
sysvar::{
|
||||||
clock,
|
|
||||||
stake_history::{self, StakeHistory},
|
stake_history::{self, StakeHistory},
|
||||||
Sysvar,
|
Sysvar,
|
||||||
},
|
},
|
||||||
@@ -266,29 +264,6 @@ impl StakeSubCommands for App<'_, '_> {
|
|||||||
.arg(nonce_authority_arg())
|
.arg(nonce_authority_arg())
|
||||||
.arg(fee_payer_arg())
|
.arg(fee_payer_arg())
|
||||||
)
|
)
|
||||||
.subcommand(
|
|
||||||
SubCommand::with_name("merge-stake")
|
|
||||||
.about("Merges one stake account into another")
|
|
||||||
.arg(
|
|
||||||
pubkey!(Arg::with_name("stake_account_pubkey")
|
|
||||||
.index(1)
|
|
||||||
.value_name("STAKE_ACCOUNT_ADDRESS")
|
|
||||||
.required(true),
|
|
||||||
"Stake account to merge into")
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
pubkey!(Arg::with_name("source_stake_account_pubkey")
|
|
||||||
.index(2)
|
|
||||||
.value_name("SOURCE_STAKE_ACCOUNT_ADDRESS")
|
|
||||||
.required(true),
|
|
||||||
"Source stake account for the merge. If successful, this stake account will no longer exist after the merge")
|
|
||||||
)
|
|
||||||
.arg(stake_authority_arg())
|
|
||||||
.offline_args()
|
|
||||||
.arg(nonce_arg())
|
|
||||||
.arg(nonce_authority_arg())
|
|
||||||
.arg(fee_payer_arg())
|
|
||||||
)
|
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("withdraw-stake")
|
SubCommand::with_name("withdraw-stake")
|
||||||
.about("Withdraw the unstaked SOL from the stake account")
|
.about("Withdraw the unstaked SOL from the stake account")
|
||||||
@@ -631,47 +606,6 @@ pub fn parse_split_stake(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_merge_stake(
|
|
||||||
matches: &ArgMatches<'_>,
|
|
||||||
default_signer_path: &str,
|
|
||||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
|
||||||
) -> Result<CliCommandInfo, CliError> {
|
|
||||||
let stake_account_pubkey =
|
|
||||||
pubkey_of_signer(matches, "stake_account_pubkey", wallet_manager)?.unwrap();
|
|
||||||
|
|
||||||
let source_stake_account_pubkey = pubkey_of(matches, "source_stake_account_pubkey").unwrap();
|
|
||||||
|
|
||||||
let sign_only = matches.is_present(SIGN_ONLY_ARG.name);
|
|
||||||
let blockhash_query = BlockhashQuery::new_from_matches(matches);
|
|
||||||
let nonce_account = pubkey_of(matches, NONCE_ARG.name);
|
|
||||||
let (stake_authority, stake_authority_pubkey) =
|
|
||||||
signer_of(matches, STAKE_AUTHORITY_ARG.name, wallet_manager)?;
|
|
||||||
let (nonce_authority, nonce_authority_pubkey) =
|
|
||||||
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
|
|
||||||
let (fee_payer, fee_payer_pubkey) = signer_of(matches, FEE_PAYER_ARG.name, wallet_manager)?;
|
|
||||||
|
|
||||||
let mut bulk_signers = vec![stake_authority, fee_payer];
|
|
||||||
if nonce_account.is_some() {
|
|
||||||
bulk_signers.push(nonce_authority);
|
|
||||||
}
|
|
||||||
let signer_info =
|
|
||||||
generate_unique_signers(bulk_signers, matches, default_signer_path, wallet_manager)?;
|
|
||||||
|
|
||||||
Ok(CliCommandInfo {
|
|
||||||
command: CliCommand::MergeStake {
|
|
||||||
stake_account_pubkey,
|
|
||||||
source_stake_account_pubkey,
|
|
||||||
stake_authority: signer_info.index_of(stake_authority_pubkey).unwrap(),
|
|
||||||
sign_only,
|
|
||||||
blockhash_query,
|
|
||||||
nonce_account,
|
|
||||||
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
|
|
||||||
fee_payer: signer_info.index_of(fee_payer_pubkey).unwrap(),
|
|
||||||
},
|
|
||||||
signers: signer_info.signers,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_stake_deactivate_stake(
|
pub fn parse_stake_deactivate_stake(
|
||||||
matches: &ArgMatches<'_>,
|
matches: &ArgMatches<'_>,
|
||||||
default_signer_path: &str,
|
default_signer_path: &str,
|
||||||
@@ -891,12 +825,12 @@ pub fn process_create_stake_account(
|
|||||||
&nonce_authority.pubkey(),
|
&nonce_authority.pubkey(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Message::new(&ixs, Some(&fee_payer.pubkey()))
|
Message::new_with_payer(&ixs, Some(&fee_payer.pubkey()))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let (recent_blockhash, fee_calculator) =
|
let (recent_blockhash, fee_calculator) =
|
||||||
blockhash_query.get_blockhash_and_fee_calculator(rpc_client, config.commitment)?;
|
blockhash_query.get_blockhash_and_fee_calculator(rpc_client)?;
|
||||||
|
|
||||||
let (message, lamports) = resolve_spend_tx_and_check_account_balances(
|
let (message, lamports) = resolve_spend_tx_and_check_account_balances(
|
||||||
rpc_client,
|
rpc_client,
|
||||||
@@ -906,7 +840,6 @@ pub fn process_create_stake_account(
|
|||||||
&from.pubkey(),
|
&from.pubkey(),
|
||||||
&fee_payer.pubkey(),
|
&fee_payer.pubkey(),
|
||||||
build_message,
|
build_message,
|
||||||
config.commitment,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if !sign_only {
|
if !sign_only {
|
||||||
@@ -934,8 +867,7 @@ pub fn process_create_stake_account(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(nonce_account) = &nonce_account {
|
if let Some(nonce_account) = &nonce_account {
|
||||||
let nonce_account =
|
let nonce_account = rpc_client.get_account(nonce_account)?;
|
||||||
nonce::get_account_with_commitment(rpc_client, nonce_account, config.commitment)?;
|
|
||||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -946,11 +878,7 @@ pub fn process_create_stake_account(
|
|||||||
return_signers(&tx, &config)
|
return_signers(&tx, &config)
|
||||||
} else {
|
} else {
|
||||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||||
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
|
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||||
&tx,
|
|
||||||
config.commitment,
|
|
||||||
config.send_transaction_config,
|
|
||||||
);
|
|
||||||
log_instruction_custom_error::<SystemError>(result, &config)
|
log_instruction_custom_error::<SystemError>(result, &config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -983,7 +911,7 @@ pub fn process_stake_authorize(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let (recent_blockhash, fee_calculator) =
|
let (recent_blockhash, fee_calculator) =
|
||||||
blockhash_query.get_blockhash_and_fee_calculator(rpc_client, config.commitment)?;
|
blockhash_query.get_blockhash_and_fee_calculator(rpc_client)?;
|
||||||
|
|
||||||
let nonce_authority = config.signers[nonce_authority];
|
let nonce_authority = config.signers[nonce_authority];
|
||||||
let fee_payer = config.signers[fee_payer];
|
let fee_payer = config.signers[fee_payer];
|
||||||
@@ -996,7 +924,7 @@ pub fn process_stake_authorize(
|
|||||||
&nonce_authority.pubkey(),
|
&nonce_authority.pubkey(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Message::new(&ixs, Some(&fee_payer.pubkey()))
|
Message::new_with_payer(&ixs, Some(&fee_payer.pubkey()))
|
||||||
};
|
};
|
||||||
let mut tx = Transaction::new_unsigned(message);
|
let mut tx = Transaction::new_unsigned(message);
|
||||||
|
|
||||||
@@ -1006,22 +934,16 @@ pub fn process_stake_authorize(
|
|||||||
} else {
|
} else {
|
||||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||||
if let Some(nonce_account) = &nonce_account {
|
if let Some(nonce_account) = &nonce_account {
|
||||||
let nonce_account =
|
let nonce_account = rpc_client.get_account(nonce_account)?;
|
||||||
nonce::get_account_with_commitment(rpc_client, nonce_account, config.commitment)?;
|
|
||||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
||||||
}
|
}
|
||||||
check_account_for_fee_with_commitment(
|
check_account_for_fee(
|
||||||
rpc_client,
|
rpc_client,
|
||||||
&tx.message.account_keys[0],
|
&tx.message.account_keys[0],
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
&tx.message,
|
&tx.message,
|
||||||
config.commitment,
|
|
||||||
)?;
|
)?;
|
||||||
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
|
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||||
&tx,
|
|
||||||
config.commitment,
|
|
||||||
config.send_transaction_config,
|
|
||||||
);
|
|
||||||
log_instruction_custom_error::<StakeError>(result, &config)
|
log_instruction_custom_error::<StakeError>(result, &config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1039,7 +961,7 @@ pub fn process_deactivate_stake_account(
|
|||||||
fee_payer: SignerIndex,
|
fee_payer: SignerIndex,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let (recent_blockhash, fee_calculator) =
|
let (recent_blockhash, fee_calculator) =
|
||||||
blockhash_query.get_blockhash_and_fee_calculator(rpc_client, config.commitment)?;
|
blockhash_query.get_blockhash_and_fee_calculator(rpc_client)?;
|
||||||
let stake_authority = config.signers[stake_authority];
|
let stake_authority = config.signers[stake_authority];
|
||||||
let ixs = vec![stake_instruction::deactivate_stake(
|
let ixs = vec![stake_instruction::deactivate_stake(
|
||||||
stake_account_pubkey,
|
stake_account_pubkey,
|
||||||
@@ -1056,7 +978,7 @@ pub fn process_deactivate_stake_account(
|
|||||||
&nonce_authority.pubkey(),
|
&nonce_authority.pubkey(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Message::new(&ixs, Some(&fee_payer.pubkey()))
|
Message::new_with_payer(&ixs, Some(&fee_payer.pubkey()))
|
||||||
};
|
};
|
||||||
let mut tx = Transaction::new_unsigned(message);
|
let mut tx = Transaction::new_unsigned(message);
|
||||||
|
|
||||||
@@ -1066,22 +988,16 @@ pub fn process_deactivate_stake_account(
|
|||||||
} else {
|
} else {
|
||||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||||
if let Some(nonce_account) = &nonce_account {
|
if let Some(nonce_account) = &nonce_account {
|
||||||
let nonce_account =
|
let nonce_account = rpc_client.get_account(nonce_account)?;
|
||||||
nonce::get_account_with_commitment(rpc_client, nonce_account, config.commitment)?;
|
|
||||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
||||||
}
|
}
|
||||||
check_account_for_fee_with_commitment(
|
check_account_for_fee(
|
||||||
rpc_client,
|
rpc_client,
|
||||||
&tx.message.account_keys[0],
|
&tx.message.account_keys[0],
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
&tx.message,
|
&tx.message,
|
||||||
config.commitment,
|
|
||||||
)?;
|
)?;
|
||||||
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
|
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||||
&tx,
|
|
||||||
config.commitment,
|
|
||||||
config.send_transaction_config,
|
|
||||||
);
|
|
||||||
log_instruction_custom_error::<StakeError>(result, &config)
|
log_instruction_custom_error::<StakeError>(result, &config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1102,7 +1018,7 @@ pub fn process_withdraw_stake(
|
|||||||
fee_payer: SignerIndex,
|
fee_payer: SignerIndex,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let (recent_blockhash, fee_calculator) =
|
let (recent_blockhash, fee_calculator) =
|
||||||
blockhash_query.get_blockhash_and_fee_calculator(rpc_client, config.commitment)?;
|
blockhash_query.get_blockhash_and_fee_calculator(rpc_client)?;
|
||||||
let withdraw_authority = config.signers[withdraw_authority];
|
let withdraw_authority = config.signers[withdraw_authority];
|
||||||
let custodian = custodian.map(|index| config.signers[index]);
|
let custodian = custodian.map(|index| config.signers[index]);
|
||||||
|
|
||||||
@@ -1125,7 +1041,7 @@ pub fn process_withdraw_stake(
|
|||||||
&nonce_authority.pubkey(),
|
&nonce_authority.pubkey(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Message::new(&ixs, Some(&fee_payer.pubkey()))
|
Message::new_with_payer(&ixs, Some(&fee_payer.pubkey()))
|
||||||
};
|
};
|
||||||
let mut tx = Transaction::new_unsigned(message);
|
let mut tx = Transaction::new_unsigned(message);
|
||||||
|
|
||||||
@@ -1135,22 +1051,16 @@ pub fn process_withdraw_stake(
|
|||||||
} else {
|
} else {
|
||||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||||
if let Some(nonce_account) = &nonce_account {
|
if let Some(nonce_account) = &nonce_account {
|
||||||
let nonce_account =
|
let nonce_account = rpc_client.get_account(nonce_account)?;
|
||||||
nonce::get_account_with_commitment(rpc_client, nonce_account, config.commitment)?;
|
|
||||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
||||||
}
|
}
|
||||||
check_account_for_fee_with_commitment(
|
check_account_for_fee(
|
||||||
rpc_client,
|
rpc_client,
|
||||||
&tx.message.account_keys[0],
|
&tx.message.account_keys[0],
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
&tx.message,
|
&tx.message,
|
||||||
config.commitment,
|
|
||||||
)?;
|
)?;
|
||||||
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
|
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||||
&tx,
|
|
||||||
config.commitment,
|
|
||||||
config.send_transaction_config,
|
|
||||||
);
|
|
||||||
log_instruction_custom_error::<SystemError>(result, &config)
|
log_instruction_custom_error::<SystemError>(result, &config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1235,7 +1145,7 @@ pub fn process_split_stake(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let (recent_blockhash, fee_calculator) =
|
let (recent_blockhash, fee_calculator) =
|
||||||
blockhash_query.get_blockhash_and_fee_calculator(rpc_client, config.commitment)?;
|
blockhash_query.get_blockhash_and_fee_calculator(rpc_client)?;
|
||||||
|
|
||||||
let ixs = if let Some(seed) = split_stake_account_seed {
|
let ixs = if let Some(seed) = split_stake_account_seed {
|
||||||
stake_instruction::split_with_seed(
|
stake_instruction::split_with_seed(
|
||||||
@@ -1265,7 +1175,7 @@ pub fn process_split_stake(
|
|||||||
&nonce_authority.pubkey(),
|
&nonce_authority.pubkey(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Message::new(&ixs, Some(&fee_payer.pubkey()))
|
Message::new_with_payer(&ixs, Some(&fee_payer.pubkey()))
|
||||||
};
|
};
|
||||||
let mut tx = Transaction::new_unsigned(message);
|
let mut tx = Transaction::new_unsigned(message);
|
||||||
|
|
||||||
@@ -1275,121 +1185,16 @@ pub fn process_split_stake(
|
|||||||
} else {
|
} else {
|
||||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||||
if let Some(nonce_account) = &nonce_account {
|
if let Some(nonce_account) = &nonce_account {
|
||||||
let nonce_account =
|
let nonce_account = rpc_client.get_account(nonce_account)?;
|
||||||
nonce::get_account_with_commitment(rpc_client, nonce_account, config.commitment)?;
|
|
||||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
||||||
}
|
}
|
||||||
check_account_for_fee_with_commitment(
|
check_account_for_fee(
|
||||||
rpc_client,
|
rpc_client,
|
||||||
&tx.message.account_keys[0],
|
&tx.message.account_keys[0],
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
&tx.message,
|
&tx.message,
|
||||||
config.commitment,
|
|
||||||
)?;
|
)?;
|
||||||
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
|
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||||
&tx,
|
|
||||||
config.commitment,
|
|
||||||
config.send_transaction_config,
|
|
||||||
);
|
|
||||||
log_instruction_custom_error::<StakeError>(result, &config)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn process_merge_stake(
|
|
||||||
rpc_client: &RpcClient,
|
|
||||||
config: &CliConfig,
|
|
||||||
stake_account_pubkey: &Pubkey,
|
|
||||||
source_stake_account_pubkey: &Pubkey,
|
|
||||||
stake_authority: SignerIndex,
|
|
||||||
sign_only: bool,
|
|
||||||
blockhash_query: &BlockhashQuery,
|
|
||||||
nonce_account: Option<Pubkey>,
|
|
||||||
nonce_authority: SignerIndex,
|
|
||||||
fee_payer: SignerIndex,
|
|
||||||
) -> ProcessResult {
|
|
||||||
let fee_payer = config.signers[fee_payer];
|
|
||||||
|
|
||||||
check_unique_pubkeys(
|
|
||||||
(&fee_payer.pubkey(), "fee-payer keypair".to_string()),
|
|
||||||
(&stake_account_pubkey, "stake_account".to_string()),
|
|
||||||
)?;
|
|
||||||
check_unique_pubkeys(
|
|
||||||
(&fee_payer.pubkey(), "fee-payer keypair".to_string()),
|
|
||||||
(
|
|
||||||
&source_stake_account_pubkey,
|
|
||||||
"source_stake_account".to_string(),
|
|
||||||
),
|
|
||||||
)?;
|
|
||||||
check_unique_pubkeys(
|
|
||||||
(&stake_account_pubkey, "stake_account".to_string()),
|
|
||||||
(
|
|
||||||
&source_stake_account_pubkey,
|
|
||||||
"source_stake_account".to_string(),
|
|
||||||
),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let stake_authority = config.signers[stake_authority];
|
|
||||||
|
|
||||||
if !sign_only {
|
|
||||||
for stake_account_address in &[stake_account_pubkey, source_stake_account_pubkey] {
|
|
||||||
if let Ok(stake_account) = rpc_client.get_account(stake_account_address) {
|
|
||||||
if stake_account.owner != solana_stake_program::id() {
|
|
||||||
return Err(CliError::BadParameter(format!(
|
|
||||||
"Account {} is not a stake account",
|
|
||||||
stake_account_address
|
|
||||||
))
|
|
||||||
.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let (recent_blockhash, fee_calculator) =
|
|
||||||
blockhash_query.get_blockhash_and_fee_calculator(rpc_client, config.commitment)?;
|
|
||||||
|
|
||||||
let ixs = stake_instruction::merge(
|
|
||||||
&stake_account_pubkey,
|
|
||||||
&source_stake_account_pubkey,
|
|
||||||
&stake_authority.pubkey(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let nonce_authority = config.signers[nonce_authority];
|
|
||||||
|
|
||||||
let message = if let Some(nonce_account) = &nonce_account {
|
|
||||||
Message::new_with_nonce(
|
|
||||||
ixs,
|
|
||||||
Some(&fee_payer.pubkey()),
|
|
||||||
nonce_account,
|
|
||||||
&nonce_authority.pubkey(),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Message::new(&ixs, Some(&fee_payer.pubkey()))
|
|
||||||
};
|
|
||||||
let mut tx = Transaction::new_unsigned(message);
|
|
||||||
|
|
||||||
if sign_only {
|
|
||||||
tx.try_partial_sign(&config.signers, recent_blockhash)?;
|
|
||||||
return_signers(&tx, &config)
|
|
||||||
} else {
|
|
||||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
|
||||||
if let Some(nonce_account) = &nonce_account {
|
|
||||||
let nonce_account =
|
|
||||||
nonce::get_account_with_commitment(rpc_client, nonce_account, config.commitment)?;
|
|
||||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
|
||||||
}
|
|
||||||
check_account_for_fee_with_commitment(
|
|
||||||
rpc_client,
|
|
||||||
&tx.message.account_keys[0],
|
|
||||||
&fee_calculator,
|
|
||||||
&tx.message,
|
|
||||||
config.commitment,
|
|
||||||
)?;
|
|
||||||
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
|
|
||||||
&tx,
|
|
||||||
config.commitment,
|
|
||||||
config.send_transaction_config,
|
|
||||||
);
|
|
||||||
log_instruction_custom_error::<StakeError>(result, &config)
|
log_instruction_custom_error::<StakeError>(result, &config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1408,7 +1213,7 @@ pub fn process_stake_set_lockup(
|
|||||||
fee_payer: SignerIndex,
|
fee_payer: SignerIndex,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let (recent_blockhash, fee_calculator) =
|
let (recent_blockhash, fee_calculator) =
|
||||||
blockhash_query.get_blockhash_and_fee_calculator(rpc_client, config.commitment)?;
|
blockhash_query.get_blockhash_and_fee_calculator(rpc_client)?;
|
||||||
let custodian = config.signers[custodian];
|
let custodian = config.signers[custodian];
|
||||||
|
|
||||||
let ixs = vec![stake_instruction::set_lockup(
|
let ixs = vec![stake_instruction::set_lockup(
|
||||||
@@ -1427,7 +1232,7 @@ pub fn process_stake_set_lockup(
|
|||||||
&nonce_authority.pubkey(),
|
&nonce_authority.pubkey(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Message::new(&ixs, Some(&fee_payer.pubkey()))
|
Message::new_with_payer(&ixs, Some(&fee_payer.pubkey()))
|
||||||
};
|
};
|
||||||
let mut tx = Transaction::new_unsigned(message);
|
let mut tx = Transaction::new_unsigned(message);
|
||||||
|
|
||||||
@@ -1437,66 +1242,36 @@ pub fn process_stake_set_lockup(
|
|||||||
} else {
|
} else {
|
||||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||||
if let Some(nonce_account) = &nonce_account {
|
if let Some(nonce_account) = &nonce_account {
|
||||||
let nonce_account =
|
let nonce_account = rpc_client.get_account(nonce_account)?;
|
||||||
nonce::get_account_with_commitment(rpc_client, nonce_account, config.commitment)?;
|
|
||||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
||||||
}
|
}
|
||||||
check_account_for_fee_with_commitment(
|
check_account_for_fee(
|
||||||
rpc_client,
|
rpc_client,
|
||||||
&tx.message.account_keys[0],
|
&tx.message.account_keys[0],
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
&tx.message,
|
&tx.message,
|
||||||
config.commitment,
|
|
||||||
)?;
|
)?;
|
||||||
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
|
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||||
&tx,
|
|
||||||
config.commitment,
|
|
||||||
config.send_transaction_config,
|
|
||||||
);
|
|
||||||
log_instruction_custom_error::<StakeError>(result, &config)
|
log_instruction_custom_error::<StakeError>(result, &config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn u64_some_if_not_zero(n: u64) -> Option<u64> {
|
|
||||||
if n > 0 {
|
|
||||||
Some(n)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build_stake_state(
|
pub fn build_stake_state(
|
||||||
account_balance: u64,
|
stake_lamports: u64,
|
||||||
stake_state: &StakeState,
|
stake_state: &StakeState,
|
||||||
use_lamports_unit: bool,
|
use_lamports_unit: bool,
|
||||||
stake_history: &StakeHistory,
|
|
||||||
clock: &Clock,
|
|
||||||
) -> CliStakeState {
|
) -> CliStakeState {
|
||||||
match stake_state {
|
match stake_state {
|
||||||
StakeState::Stake(
|
StakeState::Stake(
|
||||||
Meta {
|
Meta {
|
||||||
rent_exempt_reserve,
|
authorized, lockup, ..
|
||||||
authorized,
|
|
||||||
lockup,
|
|
||||||
},
|
},
|
||||||
stake,
|
stake,
|
||||||
) => {
|
) => CliStakeState {
|
||||||
let current_epoch = clock.epoch;
|
|
||||||
let (active_stake, activating_stake, deactivating_stake) = stake
|
|
||||||
.delegation
|
|
||||||
.stake_activating_and_deactivating(current_epoch, Some(stake_history));
|
|
||||||
let lockup = if lockup.is_in_force(clock, None) {
|
|
||||||
Some(lockup.into())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
CliStakeState {
|
|
||||||
stake_type: CliStakeType::Stake,
|
stake_type: CliStakeType::Stake,
|
||||||
account_balance,
|
total_stake: stake_lamports,
|
||||||
delegated_stake: Some(stake.delegation.stake),
|
delegated_stake: Some(stake.delegation.stake),
|
||||||
delegated_vote_account_address: if stake.delegation.voter_pubkey
|
delegated_vote_account_address: if stake.delegation.voter_pubkey != Pubkey::default() {
|
||||||
!= Pubkey::default()
|
|
||||||
{
|
|
||||||
Some(stake.delegation.voter_pubkey.to_string())
|
Some(stake.delegation.voter_pubkey.to_string())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@@ -1512,44 +1287,24 @@ pub fn build_stake_state(
|
|||||||
None
|
None
|
||||||
},
|
},
|
||||||
authorized: Some(authorized.into()),
|
authorized: Some(authorized.into()),
|
||||||
lockup,
|
lockup: Some(lockup.into()),
|
||||||
use_lamports_unit,
|
use_lamports_unit,
|
||||||
current_epoch,
|
},
|
||||||
rent_exempt_reserve: Some(*rent_exempt_reserve),
|
|
||||||
active_stake: u64_some_if_not_zero(active_stake),
|
|
||||||
activating_stake: u64_some_if_not_zero(activating_stake),
|
|
||||||
deactivating_stake: u64_some_if_not_zero(deactivating_stake),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
StakeState::RewardsPool => CliStakeState {
|
StakeState::RewardsPool => CliStakeState {
|
||||||
stake_type: CliStakeType::RewardsPool,
|
stake_type: CliStakeType::RewardsPool,
|
||||||
account_balance,
|
|
||||||
..CliStakeState::default()
|
|
||||||
},
|
|
||||||
StakeState::Uninitialized => CliStakeState {
|
|
||||||
account_balance,
|
|
||||||
..CliStakeState::default()
|
..CliStakeState::default()
|
||||||
},
|
},
|
||||||
|
StakeState::Uninitialized => CliStakeState::default(),
|
||||||
StakeState::Initialized(Meta {
|
StakeState::Initialized(Meta {
|
||||||
rent_exempt_reserve,
|
authorized, lockup, ..
|
||||||
authorized,
|
}) => CliStakeState {
|
||||||
lockup,
|
|
||||||
}) => {
|
|
||||||
let lockup = if lockup.is_in_force(clock, None) {
|
|
||||||
Some(lockup.into())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
CliStakeState {
|
|
||||||
stake_type: CliStakeType::Initialized,
|
stake_type: CliStakeType::Initialized,
|
||||||
account_balance,
|
total_stake: stake_lamports,
|
||||||
authorized: Some(authorized.into()),
|
authorized: Some(authorized.into()),
|
||||||
lockup,
|
lockup: Some(lockup.into()),
|
||||||
use_lamports_unit,
|
use_lamports_unit,
|
||||||
rent_exempt_reserve: Some(*rent_exempt_reserve),
|
|
||||||
..CliStakeState::default()
|
..CliStakeState::default()
|
||||||
}
|
},
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1569,23 +1324,7 @@ pub fn process_show_stake_account(
|
|||||||
}
|
}
|
||||||
match stake_account.state() {
|
match stake_account.state() {
|
||||||
Ok(stake_state) => {
|
Ok(stake_state) => {
|
||||||
let stake_history_account = rpc_client.get_account(&stake_history::id())?;
|
let state = build_stake_state(stake_account.lamports, &stake_state, use_lamports_unit);
|
||||||
let stake_history =
|
|
||||||
StakeHistory::from_account(&stake_history_account).ok_or_else(|| {
|
|
||||||
CliError::RpcRequestError("Failed to deserialize stake history".to_string())
|
|
||||||
})?;
|
|
||||||
let clock_account = rpc_client.get_account(&clock::id())?;
|
|
||||||
let clock: Clock = Sysvar::from_account(&clock_account).ok_or_else(|| {
|
|
||||||
CliError::RpcRequestError("Failed to deserialize clock sysvar".to_string())
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let state = build_stake_state(
|
|
||||||
stake_account.lamports,
|
|
||||||
&stake_state,
|
|
||||||
use_lamports_unit,
|
|
||||||
&stake_history,
|
|
||||||
&clock,
|
|
||||||
);
|
|
||||||
Ok(config.output_format.formatted_string(&state))
|
Ok(config.output_format.formatted_string(&state))
|
||||||
}
|
}
|
||||||
Err(err) => Err(CliError::RpcRequestError(format!(
|
Err(err) => Err(CliError::RpcRequestError(format!(
|
||||||
@@ -1640,23 +1379,14 @@ pub fn process_delegate_stake(
|
|||||||
if !sign_only {
|
if !sign_only {
|
||||||
// Sanity check the vote account to ensure it is attached to a validator that has recently
|
// Sanity check the vote account to ensure it is attached to a validator that has recently
|
||||||
// voted at the tip of the ledger
|
// voted at the tip of the ledger
|
||||||
let vote_account = rpc_client
|
let vote_account_data = rpc_client
|
||||||
.get_account_with_commitment(vote_account_pubkey, config.commitment)
|
.get_account_data(vote_account_pubkey)
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
CliError::RpcRequestError(format!(
|
CliError::RpcRequestError(format!(
|
||||||
"Vote account not found: {}",
|
"Vote account not found: {}",
|
||||||
vote_account_pubkey
|
vote_account_pubkey
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
let vote_account_data = if let Some(account) = vote_account.value {
|
|
||||||
account.data
|
|
||||||
} else {
|
|
||||||
return Err(CliError::RpcRequestError(format!(
|
|
||||||
"Vote account not found: {}",
|
|
||||||
vote_account_pubkey
|
|
||||||
))
|
|
||||||
.into());
|
|
||||||
};
|
|
||||||
|
|
||||||
let vote_state = VoteState::deserialize(&vote_account_data).map_err(|_| {
|
let vote_state = VoteState::deserialize(&vote_account_data).map_err(|_| {
|
||||||
CliError::RpcRequestError(
|
CliError::RpcRequestError(
|
||||||
@@ -1669,15 +1399,13 @@ pub fn process_delegate_stake(
|
|||||||
"Unable to delegate. Vote account has no root slot".to_string(),
|
"Unable to delegate. Vote account has no root slot".to_string(),
|
||||||
)),
|
)),
|
||||||
Some(root_slot) => {
|
Some(root_slot) => {
|
||||||
let min_root_slot = rpc_client
|
let slot = rpc_client.get_slot()?;
|
||||||
.get_slot()?
|
if root_slot + solana_sdk::clock::DEFAULT_SLOTS_PER_TURN < slot {
|
||||||
.saturating_sub(DELINQUENT_VALIDATOR_SLOT_DISTANCE);
|
Err(CliError::BadParameter(
|
||||||
if root_slot < min_root_slot {
|
format!(
|
||||||
Err(CliError::DynamicProgramError(format!(
|
"Unable to delegate. Vote account root slot ({}) is too old, the current slot is {}", root_slot, slot
|
||||||
"Unable to delegate. Vote account appears delinquent \
|
)
|
||||||
because its current root slot, {}, is less than {}",
|
))
|
||||||
root_slot, min_root_slot
|
|
||||||
)))
|
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -1694,7 +1422,7 @@ pub fn process_delegate_stake(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let (recent_blockhash, fee_calculator) =
|
let (recent_blockhash, fee_calculator) =
|
||||||
blockhash_query.get_blockhash_and_fee_calculator(rpc_client, config.commitment)?;
|
blockhash_query.get_blockhash_and_fee_calculator(rpc_client)?;
|
||||||
|
|
||||||
let ixs = vec![stake_instruction::delegate_stake(
|
let ixs = vec![stake_instruction::delegate_stake(
|
||||||
stake_account_pubkey,
|
stake_account_pubkey,
|
||||||
@@ -1712,7 +1440,7 @@ pub fn process_delegate_stake(
|
|||||||
&nonce_authority.pubkey(),
|
&nonce_authority.pubkey(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Message::new(&ixs, Some(&fee_payer.pubkey()))
|
Message::new_with_payer(&ixs, Some(&fee_payer.pubkey()))
|
||||||
};
|
};
|
||||||
let mut tx = Transaction::new_unsigned(message);
|
let mut tx = Transaction::new_unsigned(message);
|
||||||
|
|
||||||
@@ -1722,22 +1450,16 @@ pub fn process_delegate_stake(
|
|||||||
} else {
|
} else {
|
||||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||||
if let Some(nonce_account) = &nonce_account {
|
if let Some(nonce_account) = &nonce_account {
|
||||||
let nonce_account =
|
let nonce_account = rpc_client.get_account(nonce_account)?;
|
||||||
nonce::get_account_with_commitment(rpc_client, nonce_account, config.commitment)?;
|
|
||||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
||||||
}
|
}
|
||||||
check_account_for_fee_with_commitment(
|
check_account_for_fee(
|
||||||
rpc_client,
|
rpc_client,
|
||||||
&tx.message.account_keys[0],
|
&tx.message.account_keys[0],
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
&tx.message,
|
&tx.message,
|
||||||
config.commitment,
|
|
||||||
)?;
|
)?;
|
||||||
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
|
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||||
&tx,
|
|
||||||
config.commitment,
|
|
||||||
config.send_transaction_config,
|
|
||||||
);
|
|
||||||
log_instruction_custom_error::<StakeError>(result, &config)
|
log_instruction_custom_error::<StakeError>(result, &config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3156,34 +2878,5 @@ mod tests {
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Test MergeStake SubCommand
|
|
||||||
let (keypair_file, mut tmp_file) = make_tmp_file();
|
|
||||||
let stake_account_keypair = Keypair::new();
|
|
||||||
write_keypair(&stake_account_keypair, tmp_file.as_file_mut()).unwrap();
|
|
||||||
|
|
||||||
let source_stake_account_pubkey = Pubkey::new_rand();
|
|
||||||
let test_merge_stake_account = test_commands.clone().get_matches_from(vec![
|
|
||||||
"test",
|
|
||||||
"merge-stake",
|
|
||||||
&keypair_file,
|
|
||||||
&source_stake_account_pubkey.to_string(),
|
|
||||||
]);
|
|
||||||
assert_eq!(
|
|
||||||
parse_command(&test_merge_stake_account, &default_keypair_file, &mut None).unwrap(),
|
|
||||||
CliCommandInfo {
|
|
||||||
command: CliCommand::MergeStake {
|
|
||||||
stake_account_pubkey: stake_account_keypair.pubkey(),
|
|
||||||
source_stake_account_pubkey,
|
|
||||||
stake_authority: 0,
|
|
||||||
sign_only: false,
|
|
||||||
blockhash_query: BlockhashQuery::default(),
|
|
||||||
nonce_account: None,
|
|
||||||
nonce_authority: 0,
|
|
||||||
fee_payer: 0,
|
|
||||||
},
|
|
||||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into(),],
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
use solana_client::rpc_client::RpcClient;
|
use solana_client::rpc_client::RpcClient;
|
||||||
use solana_sdk::{clock::DEFAULT_MS_PER_SLOT, commitment_config::CommitmentConfig, pubkey::Pubkey};
|
use solana_sdk::pubkey::Pubkey;
|
||||||
use std::{thread::sleep, time::Duration};
|
use std::{thread::sleep, time::Duration};
|
||||||
|
|
||||||
pub fn check_recent_balance(expected_balance: u64, client: &RpcClient, pubkey: &Pubkey) {
|
pub fn check_balance(expected_balance: u64, client: &RpcClient, pubkey: &Pubkey) {
|
||||||
(0..5).for_each(|tries| {
|
(0..5).for_each(|tries| {
|
||||||
let balance = client
|
let balance = client.get_balance(pubkey).unwrap();
|
||||||
.get_balance_with_commitment(pubkey, CommitmentConfig::recent())
|
|
||||||
.unwrap()
|
|
||||||
.value;
|
|
||||||
if balance == expected_balance {
|
if balance == expected_balance {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -17,13 +14,3 @@ pub fn check_recent_balance(expected_balance: u64, client: &RpcClient, pubkey: &
|
|||||||
sleep(Duration::from_millis(500));
|
sleep(Duration::from_millis(500));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_ready(rpc_client: &RpcClient) {
|
|
||||||
while rpc_client
|
|
||||||
.get_slot_with_commitment(CommitmentConfig::recent())
|
|
||||||
.unwrap()
|
|
||||||
< 5
|
|
||||||
{
|
|
||||||
sleep(Duration::from_millis(DEFAULT_MS_PER_SLOT));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,10 +6,9 @@ use crate::{
|
|||||||
use bincode::deserialize;
|
use bincode::deserialize;
|
||||||
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
|
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
|
||||||
use reqwest::blocking::Client;
|
use reqwest::blocking::Client;
|
||||||
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use serde_json::{Map, Value};
|
use serde_json::{Map, Value};
|
||||||
use solana_account_decoder::validator_info::{
|
|
||||||
self, ValidatorInfo, MAX_LONG_FIELD_LENGTH, MAX_SHORT_FIELD_LENGTH,
|
|
||||||
};
|
|
||||||
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},
|
||||||
@@ -28,6 +27,23 @@ use solana_sdk::{
|
|||||||
};
|
};
|
||||||
use std::{error, sync::Arc};
|
use std::{error, sync::Arc};
|
||||||
|
|
||||||
|
pub const MAX_SHORT_FIELD_LENGTH: usize = 70;
|
||||||
|
pub const MAX_LONG_FIELD_LENGTH: usize = 300;
|
||||||
|
pub const MAX_VALIDATOR_INFO: u64 = 576;
|
||||||
|
|
||||||
|
solana_sdk::declare_id!("Va1idator1nfo111111111111111111111111111111");
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, PartialEq, Serialize, Default)]
|
||||||
|
pub struct ValidatorInfo {
|
||||||
|
info: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConfigState for ValidatorInfo {
|
||||||
|
fn max_space() -> u64 {
|
||||||
|
MAX_VALIDATOR_INFO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Return an error if a validator details are longer than the max length.
|
// Return an error if a validator details are longer than the max length.
|
||||||
pub fn check_details_length(string: String) -> Result<(), String> {
|
pub fn check_details_length(string: String) -> Result<(), String> {
|
||||||
if string.len() > MAX_LONG_FIELD_LENGTH {
|
if string.len() > MAX_LONG_FIELD_LENGTH {
|
||||||
@@ -273,7 +289,7 @@ pub fn process_set_validator_info(
|
|||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, account)| {
|
.filter(|(_, account)| {
|
||||||
let key_list: ConfigKeys = deserialize(&account.data).map_err(|_| false).unwrap();
|
let key_list: ConfigKeys = deserialize(&account.data).map_err(|_| false).unwrap();
|
||||||
key_list.keys.contains(&(validator_info::id(), false))
|
key_list.keys.contains(&(id(), 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();
|
||||||
@@ -312,10 +328,7 @@ pub fn process_set_validator_info(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let build_message = |lamports| {
|
let build_message = |lamports| {
|
||||||
let keys = vec![
|
let keys = vec![(id(), false), (config.signers[0].pubkey(), true)];
|
||||||
(validator_info::id(), false),
|
|
||||||
(config.signers[0].pubkey(), true),
|
|
||||||
];
|
|
||||||
if balance == 0 {
|
if balance == 0 {
|
||||||
println!(
|
println!(
|
||||||
"Publishing info for Validator {:?}",
|
"Publishing info for Validator {:?}",
|
||||||
@@ -333,7 +346,7 @@ pub fn process_set_validator_info(
|
|||||||
keys,
|
keys,
|
||||||
&validator_info,
|
&validator_info,
|
||||||
)]);
|
)]);
|
||||||
Message::new(&instructions, Some(&config.signers[0].pubkey()))
|
Message::new(&instructions)
|
||||||
} else {
|
} else {
|
||||||
println!(
|
println!(
|
||||||
"Updating Validator {:?} info at: {:?}",
|
"Updating Validator {:?} info at: {:?}",
|
||||||
@@ -346,7 +359,7 @@ pub fn process_set_validator_info(
|
|||||||
keys,
|
keys,
|
||||||
&validator_info,
|
&validator_info,
|
||||||
)];
|
)];
|
||||||
Message::new(&instructions, Some(&config.signers[0].pubkey()))
|
Message::new_with_payer(&instructions, Some(&config.signers[0].pubkey()))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -359,7 +372,6 @@ pub fn process_set_validator_info(
|
|||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
&config.signers[0].pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
build_message,
|
build_message,
|
||||||
config.commitment,
|
|
||||||
)?;
|
)?;
|
||||||
let mut tx = Transaction::new_unsigned(message);
|
let mut tx = Transaction::new_unsigned(message);
|
||||||
tx.try_sign(&signers, recent_blockhash)?;
|
tx.try_sign(&signers, recent_blockhash)?;
|
||||||
@@ -388,7 +400,7 @@ pub fn process_get_validator_info(
|
|||||||
let key_list: ConfigKeys = deserialize(&validator_info_account.data)
|
let key_list: ConfigKeys = deserialize(&validator_info_account.data)
|
||||||
.map_err(|_| false)
|
.map_err(|_| false)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
key_list.keys.contains(&(validator_info::id(), false))
|
key_list.keys.contains(&(id(), false))
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
@@ -490,7 +502,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_parse_validator_info() {
|
fn test_parse_validator_info() {
|
||||||
let pubkey = Pubkey::new_rand();
|
let pubkey = Pubkey::new_rand();
|
||||||
let keys = vec![(validator_info::id(), false), (pubkey, true)];
|
let keys = vec![(id(), false), (pubkey, true)];
|
||||||
let config = ConfigKeys { keys };
|
let config = ConfigKeys { keys };
|
||||||
|
|
||||||
let mut info = Map::new();
|
let mut info = Map::new();
|
||||||
|
|||||||
274
cli/src/vote.rs
274
cli/src/vote.rs
@@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
checks::{check_account_for_fee_with_commitment, check_unique_pubkeys},
|
checks::{check_account_for_fee, check_unique_pubkeys},
|
||||||
cli::{
|
cli::{
|
||||||
generate_unique_signers, log_instruction_custom_error, CliCommand, CliCommandInfo,
|
generate_unique_signers, log_instruction_custom_error, CliCommand, CliCommandInfo,
|
||||||
CliConfig, CliError, ProcessResult, SignerIndex,
|
CliConfig, CliError, ProcessResult, SignerIndex,
|
||||||
@@ -8,13 +8,16 @@ use crate::{
|
|||||||
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::{commitment::commitment_arg, input_parsers::*, input_validators::*};
|
use solana_clap_utils::{
|
||||||
|
commitment::{commitment_arg, COMMITMENT_ARG},
|
||||||
|
input_parsers::*,
|
||||||
|
input_validators::*,
|
||||||
|
};
|
||||||
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::{
|
||||||
account::Account, commitment_config::CommitmentConfig, message::Message,
|
account::Account, commitment_config::CommitmentConfig, message::Message, pubkey::Pubkey,
|
||||||
native_token::lamports_to_sol, pubkey::Pubkey, system_instruction::SystemError,
|
system_instruction::SystemError, transaction::Transaction,
|
||||||
transaction::Transaction,
|
|
||||||
};
|
};
|
||||||
use solana_vote_program::{
|
use solana_vote_program::{
|
||||||
vote_instruction::{self, withdraw, VoteError},
|
vote_instruction::{self, withdraw, VoteError},
|
||||||
@@ -158,35 +161,6 @@ impl VoteSubCommands for App<'_, '_> {
|
|||||||
.help("Authorized withdrawer keypair"),
|
.help("Authorized withdrawer keypair"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.subcommand(
|
|
||||||
SubCommand::with_name("vote-update-commission")
|
|
||||||
.about("Update the vote account's commission")
|
|
||||||
.arg(
|
|
||||||
pubkey!(Arg::with_name("vote_account_pubkey")
|
|
||||||
.index(1)
|
|
||||||
.value_name("VOTE_ACCOUNT_ADDRESS")
|
|
||||||
.required(true),
|
|
||||||
"Vote account to update. "),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("commission")
|
|
||||||
.index(2)
|
|
||||||
.value_name("PERCENTAGE")
|
|
||||||
.takes_value(true)
|
|
||||||
.required(true)
|
|
||||||
.validator(is_valid_percentage)
|
|
||||||
.help("The new commission")
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("authorized_withdrawer")
|
|
||||||
.index(3)
|
|
||||||
.value_name("AUTHORIZED_KEYPAIR")
|
|
||||||
.takes_value(true)
|
|
||||||
.required(true)
|
|
||||||
.validator(is_valid_signer)
|
|
||||||
.help("Authorized withdrawer keypair"),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("vote-account")
|
SubCommand::with_name("vote-account")
|
||||||
.about("Show the contents of a vote account")
|
.about("Show the contents of a vote account")
|
||||||
@@ -229,8 +203,8 @@ impl VoteSubCommands for App<'_, '_> {
|
|||||||
.value_name("AMOUNT")
|
.value_name("AMOUNT")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.required(true)
|
.required(true)
|
||||||
.validator(is_amount_or_all)
|
.validator(is_amount)
|
||||||
.help("The amount to withdraw, in SOL; accepts keyword ALL"),
|
.help("The amount to withdraw, in SOL"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("authorized_withdrawer")
|
Arg::with_name("authorized_withdrawer")
|
||||||
@@ -249,7 +223,7 @@ pub fn parse_create_vote_account(
|
|||||||
default_signer_path: &str,
|
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, _) = signer_of(matches, "vote_account", wallet_manager)?;
|
||||||
let seed = matches.value_of("seed").map(|s| s.to_string());
|
let seed = matches.value_of("seed").map(|s| s.to_string());
|
||||||
let (identity_account, identity_pubkey) =
|
let (identity_account, identity_pubkey) =
|
||||||
signer_of(matches, "identity_account", wallet_manager)?;
|
signer_of(matches, "identity_account", wallet_manager)?;
|
||||||
@@ -267,7 +241,6 @@ pub fn parse_create_vote_account(
|
|||||||
|
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::CreateVoteAccount {
|
command: CliCommand::CreateVoteAccount {
|
||||||
vote_account: signer_info.index_of(vote_account_pubkey).unwrap(),
|
|
||||||
seed,
|
seed,
|
||||||
identity_account: signer_info.index_of(identity_pubkey).unwrap(),
|
identity_account: signer_info.index_of(identity_pubkey).unwrap(),
|
||||||
authorized_voter,
|
authorized_voter,
|
||||||
@@ -317,8 +290,7 @@ pub fn parse_vote_update_validator(
|
|||||||
pubkey_of_signer(matches, "vote_account_pubkey", wallet_manager)?.unwrap();
|
pubkey_of_signer(matches, "vote_account_pubkey", wallet_manager)?.unwrap();
|
||||||
let (new_identity_account, new_identity_pubkey) =
|
let (new_identity_account, new_identity_pubkey) =
|
||||||
signer_of(matches, "new_identity_account", wallet_manager)?;
|
signer_of(matches, "new_identity_account", wallet_manager)?;
|
||||||
let (authorized_withdrawer, authorized_withdrawer_pubkey) =
|
let (authorized_withdrawer, _) = 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 = generate_unique_signers(
|
let signer_info = generate_unique_signers(
|
||||||
@@ -332,36 +304,6 @@ pub fn parse_vote_update_validator(
|
|||||||
command: CliCommand::VoteUpdateValidator {
|
command: CliCommand::VoteUpdateValidator {
|
||||||
vote_account_pubkey,
|
vote_account_pubkey,
|
||||||
new_identity_account: signer_info.index_of(new_identity_pubkey).unwrap(),
|
new_identity_account: signer_info.index_of(new_identity_pubkey).unwrap(),
|
||||||
withdraw_authority: signer_info.index_of(authorized_withdrawer_pubkey).unwrap(),
|
|
||||||
},
|
|
||||||
signers: signer_info.signers,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_vote_update_commission(
|
|
||||||
matches: &ArgMatches<'_>,
|
|
||||||
default_signer_path: &str,
|
|
||||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
|
||||||
) -> Result<CliCommandInfo, CliError> {
|
|
||||||
let vote_account_pubkey =
|
|
||||||
pubkey_of_signer(matches, "vote_account_pubkey", wallet_manager)?.unwrap();
|
|
||||||
let (authorized_withdrawer, authorized_withdrawer_pubkey) =
|
|
||||||
signer_of(matches, "authorized_withdrawer", wallet_manager)?;
|
|
||||||
let commission = value_t_or_exit!(matches, "commission", u8);
|
|
||||||
|
|
||||||
let payer_provided = None;
|
|
||||||
let signer_info = generate_unique_signers(
|
|
||||||
vec![payer_provided, authorized_withdrawer],
|
|
||||||
matches,
|
|
||||||
default_signer_path,
|
|
||||||
wallet_manager,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(CliCommandInfo {
|
|
||||||
command: CliCommand::VoteUpdateCommission {
|
|
||||||
vote_account_pubkey,
|
|
||||||
commission,
|
|
||||||
withdraw_authority: signer_info.index_of(authorized_withdrawer_pubkey).unwrap(),
|
|
||||||
},
|
},
|
||||||
signers: signer_info.signers,
|
signers: signer_info.signers,
|
||||||
})
|
})
|
||||||
@@ -374,10 +316,12 @@ pub fn parse_vote_get_account_command(
|
|||||||
let vote_account_pubkey =
|
let vote_account_pubkey =
|
||||||
pubkey_of_signer(matches, "vote_account_pubkey", wallet_manager)?.unwrap();
|
pubkey_of_signer(matches, "vote_account_pubkey", wallet_manager)?.unwrap();
|
||||||
let use_lamports_unit = matches.is_present("lamports");
|
let use_lamports_unit = matches.is_present("lamports");
|
||||||
|
let commitment_config = commitment_of(matches, COMMITMENT_ARG.long).unwrap();
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
command: CliCommand::ShowVoteAccount {
|
command: CliCommand::ShowVoteAccount {
|
||||||
pubkey: vote_account_pubkey,
|
pubkey: vote_account_pubkey,
|
||||||
use_lamports_unit,
|
use_lamports_unit,
|
||||||
|
commitment_config,
|
||||||
},
|
},
|
||||||
signers: vec![],
|
signers: vec![],
|
||||||
})
|
})
|
||||||
@@ -392,8 +336,7 @@ pub fn parse_withdraw_from_vote_account(
|
|||||||
pubkey_of_signer(matches, "vote_account_pubkey", wallet_manager)?.unwrap();
|
pubkey_of_signer(matches, "vote_account_pubkey", wallet_manager)?.unwrap();
|
||||||
let destination_account_pubkey =
|
let destination_account_pubkey =
|
||||||
pubkey_of_signer(matches, "destination_account_pubkey", wallet_manager)?.unwrap();
|
pubkey_of_signer(matches, "destination_account_pubkey", wallet_manager)?.unwrap();
|
||||||
let withdraw_amount = SpendAmount::new_from_matches(matches, "amount");
|
let lamports = lamports_of_sol(matches, "amount").unwrap();
|
||||||
|
|
||||||
let (withdraw_authority, withdraw_authority_pubkey) =
|
let (withdraw_authority, withdraw_authority_pubkey) =
|
||||||
signer_of(matches, "authorized_withdrawer", wallet_manager)?;
|
signer_of(matches, "authorized_withdrawer", wallet_manager)?;
|
||||||
|
|
||||||
@@ -410,7 +353,7 @@ pub fn parse_withdraw_from_vote_account(
|
|||||||
vote_account_pubkey,
|
vote_account_pubkey,
|
||||||
destination_account_pubkey,
|
destination_account_pubkey,
|
||||||
withdraw_authority: signer_info.index_of(withdraw_authority_pubkey).unwrap(),
|
withdraw_authority: signer_info.index_of(withdraw_authority_pubkey).unwrap(),
|
||||||
withdraw_amount,
|
lamports,
|
||||||
},
|
},
|
||||||
signers: signer_info.signers,
|
signers: signer_info.signers,
|
||||||
})
|
})
|
||||||
@@ -419,14 +362,13 @@ pub fn parse_withdraw_from_vote_account(
|
|||||||
pub fn process_create_vote_account(
|
pub fn process_create_vote_account(
|
||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
config: &CliConfig,
|
config: &CliConfig,
|
||||||
vote_account: SignerIndex,
|
|
||||||
seed: &Option<String>,
|
seed: &Option<String>,
|
||||||
identity_account: SignerIndex,
|
identity_account: SignerIndex,
|
||||||
authorized_voter: &Option<Pubkey>,
|
authorized_voter: &Option<Pubkey>,
|
||||||
authorized_withdrawer: &Option<Pubkey>,
|
authorized_withdrawer: &Option<Pubkey>,
|
||||||
commission: u8,
|
commission: u8,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let vote_account = config.signers[vote_account];
|
let vote_account = config.signers[1];
|
||||||
let vote_account_pubkey = vote_account.pubkey();
|
let vote_account_pubkey = vote_account.pubkey();
|
||||||
let vote_account_address = if let Some(seed) = seed {
|
let vote_account_address = if let Some(seed) = seed {
|
||||||
Pubkey::create_with_seed(&vote_account_pubkey, &seed, &solana_vote_program::id())?
|
Pubkey::create_with_seed(&vote_account_pubkey, &seed, &solana_vote_program::id())?
|
||||||
@@ -475,13 +417,10 @@ pub fn process_create_vote_account(
|
|||||||
lamports,
|
lamports,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
Message::new(&ixs, Some(&config.signers[0].pubkey()))
|
Message::new(&ixs)
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Ok(response) =
|
if let Ok(vote_account) = rpc_client.get_account(&vote_account_address) {
|
||||||
rpc_client.get_account_with_commitment(&vote_account_address, config.commitment)
|
|
||||||
{
|
|
||||||
if let Some(vote_account) = response.value {
|
|
||||||
let err_msg = if vote_account.owner == solana_vote_program::id() {
|
let err_msg = if vote_account.owner == solana_vote_program::id() {
|
||||||
format!("Vote account {} already exists", vote_account_address)
|
format!("Vote account {} already exists", vote_account_address)
|
||||||
} else {
|
} else {
|
||||||
@@ -492,11 +431,8 @@ pub fn process_create_vote_account(
|
|||||||
};
|
};
|
||||||
return Err(CliError::BadParameter(err_msg).into());
|
return Err(CliError::BadParameter(err_msg).into());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let (recent_blockhash, fee_calculator, _) = rpc_client
|
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||||
.get_recent_blockhash_with_commitment(config.commitment)?
|
|
||||||
.value;
|
|
||||||
|
|
||||||
let (message, _) = resolve_spend_tx_and_check_account_balance(
|
let (message, _) = resolve_spend_tx_and_check_account_balance(
|
||||||
rpc_client,
|
rpc_client,
|
||||||
@@ -505,15 +441,10 @@ pub fn process_create_vote_account(
|
|||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
&config.signers[0].pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
build_message,
|
build_message,
|
||||||
config.commitment,
|
|
||||||
)?;
|
)?;
|
||||||
let mut tx = Transaction::new_unsigned(message);
|
let mut tx = Transaction::new_unsigned(message);
|
||||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||||
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
|
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||||
&tx,
|
|
||||||
config.commitment,
|
|
||||||
config.send_transaction_config,
|
|
||||||
);
|
|
||||||
log_instruction_custom_error::<SystemError>(result, &config)
|
log_instruction_custom_error::<SystemError>(result, &config)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -536,9 +467,7 @@ pub fn process_vote_authorize(
|
|||||||
(&authorized.pubkey(), "authorized_account".to_string()),
|
(&authorized.pubkey(), "authorized_account".to_string()),
|
||||||
(new_authorized_pubkey, "new_authorized_pubkey".to_string()),
|
(new_authorized_pubkey, "new_authorized_pubkey".to_string()),
|
||||||
)?;
|
)?;
|
||||||
let (recent_blockhash, fee_calculator, _) = rpc_client
|
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||||
.get_recent_blockhash_with_commitment(config.commitment)?
|
|
||||||
.value;
|
|
||||||
let ixs = vec![vote_instruction::authorize(
|
let ixs = vec![vote_instruction::authorize(
|
||||||
vote_account_pubkey, // vote account to update
|
vote_account_pubkey, // vote account to update
|
||||||
&authorized.pubkey(), // current authorized
|
&authorized.pubkey(), // current authorized
|
||||||
@@ -546,21 +475,16 @@ pub fn process_vote_authorize(
|
|||||||
vote_authorize, // vote or withdraw
|
vote_authorize, // vote or withdraw
|
||||||
)];
|
)];
|
||||||
|
|
||||||
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
|
let message = Message::new_with_payer(&ixs, Some(&config.signers[0].pubkey()));
|
||||||
let mut tx = Transaction::new_unsigned(message);
|
let mut tx = Transaction::new_unsigned(message);
|
||||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||||
check_account_for_fee_with_commitment(
|
check_account_for_fee(
|
||||||
rpc_client,
|
rpc_client,
|
||||||
&config.signers[0].pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
&tx.message,
|
&tx.message,
|
||||||
config.commitment,
|
|
||||||
)?;
|
)?;
|
||||||
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
|
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||||
&tx,
|
|
||||||
config.commitment,
|
|
||||||
config.send_transaction_config,
|
|
||||||
);
|
|
||||||
log_instruction_custom_error::<VoteError>(result, &config)
|
log_instruction_custom_error::<VoteError>(result, &config)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -569,74 +493,31 @@ pub fn process_vote_update_validator(
|
|||||||
config: &CliConfig,
|
config: &CliConfig,
|
||||||
vote_account_pubkey: &Pubkey,
|
vote_account_pubkey: &Pubkey,
|
||||||
new_identity_account: SignerIndex,
|
new_identity_account: SignerIndex,
|
||||||
withdraw_authority: SignerIndex,
|
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let authorized_withdrawer = config.signers[withdraw_authority];
|
let authorized_withdrawer = config.signers[1];
|
||||||
let new_identity_account = config.signers[new_identity_account];
|
let new_identity_account = config.signers[new_identity_account];
|
||||||
let new_identity_pubkey = new_identity_account.pubkey();
|
let new_identity_pubkey = new_identity_account.pubkey();
|
||||||
check_unique_pubkeys(
|
check_unique_pubkeys(
|
||||||
(vote_account_pubkey, "vote_account_pubkey".to_string()),
|
(vote_account_pubkey, "vote_account_pubkey".to_string()),
|
||||||
(&new_identity_pubkey, "new_identity_account".to_string()),
|
(&new_identity_pubkey, "new_identity_account".to_string()),
|
||||||
)?;
|
)?;
|
||||||
let (recent_blockhash, fee_calculator, _) = rpc_client
|
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||||
.get_recent_blockhash_with_commitment(config.commitment)?
|
|
||||||
.value;
|
|
||||||
let ixs = vec![vote_instruction::update_validator_identity(
|
let ixs = vec![vote_instruction::update_validator_identity(
|
||||||
vote_account_pubkey,
|
vote_account_pubkey,
|
||||||
&authorized_withdrawer.pubkey(),
|
&authorized_withdrawer.pubkey(),
|
||||||
&new_identity_pubkey,
|
&new_identity_pubkey,
|
||||||
)];
|
)];
|
||||||
|
|
||||||
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
|
let message = Message::new_with_payer(&ixs, Some(&config.signers[0].pubkey()));
|
||||||
let mut tx = Transaction::new_unsigned(message);
|
let mut tx = Transaction::new_unsigned(message);
|
||||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||||
check_account_for_fee_with_commitment(
|
check_account_for_fee(
|
||||||
rpc_client,
|
rpc_client,
|
||||||
&config.signers[0].pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
&tx.message,
|
&tx.message,
|
||||||
config.commitment,
|
|
||||||
)?;
|
)?;
|
||||||
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
|
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||||
&tx,
|
|
||||||
config.commitment,
|
|
||||||
config.send_transaction_config,
|
|
||||||
);
|
|
||||||
log_instruction_custom_error::<VoteError>(result, &config)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn process_vote_update_commission(
|
|
||||||
rpc_client: &RpcClient,
|
|
||||||
config: &CliConfig,
|
|
||||||
vote_account_pubkey: &Pubkey,
|
|
||||||
commission: u8,
|
|
||||||
withdraw_authority: SignerIndex,
|
|
||||||
) -> ProcessResult {
|
|
||||||
let authorized_withdrawer = config.signers[withdraw_authority];
|
|
||||||
let (recent_blockhash, fee_calculator, _) = rpc_client
|
|
||||||
.get_recent_blockhash_with_commitment(config.commitment)?
|
|
||||||
.value;
|
|
||||||
let ixs = vec![vote_instruction::update_commission(
|
|
||||||
vote_account_pubkey,
|
|
||||||
&authorized_withdrawer.pubkey(),
|
|
||||||
commission,
|
|
||||||
)];
|
|
||||||
|
|
||||||
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
|
|
||||||
let mut tx = Transaction::new_unsigned(message);
|
|
||||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
|
||||||
check_account_for_fee_with_commitment(
|
|
||||||
rpc_client,
|
|
||||||
&config.signers[0].pubkey(),
|
|
||||||
&fee_calculator,
|
|
||||||
&tx.message,
|
|
||||||
config.commitment,
|
|
||||||
)?;
|
|
||||||
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
|
|
||||||
&tx,
|
|
||||||
config.commitment,
|
|
||||||
config.send_transaction_config,
|
|
||||||
);
|
|
||||||
log_instruction_custom_error::<VoteError>(result, &config)
|
log_instruction_custom_error::<VoteError>(result, &config)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -673,9 +554,10 @@ pub fn process_show_vote_account(
|
|||||||
config: &CliConfig,
|
config: &CliConfig,
|
||||||
vote_account_pubkey: &Pubkey,
|
vote_account_pubkey: &Pubkey,
|
||||||
use_lamports_unit: bool,
|
use_lamports_unit: bool,
|
||||||
|
commitment_config: CommitmentConfig,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let (vote_account, vote_state) =
|
let (vote_account, vote_state) =
|
||||||
get_vote_account(rpc_client, vote_account_pubkey, config.commitment)?;
|
get_vote_account(rpc_client, vote_account_pubkey, commitment_config)?;
|
||||||
|
|
||||||
let epoch_schedule = rpc_client.get_epoch_schedule()?;
|
let epoch_schedule = rpc_client.get_epoch_schedule()?;
|
||||||
|
|
||||||
@@ -718,32 +600,12 @@ pub fn process_withdraw_from_vote_account(
|
|||||||
config: &CliConfig,
|
config: &CliConfig,
|
||||||
vote_account_pubkey: &Pubkey,
|
vote_account_pubkey: &Pubkey,
|
||||||
withdraw_authority: SignerIndex,
|
withdraw_authority: SignerIndex,
|
||||||
withdraw_amount: SpendAmount,
|
lamports: u64,
|
||||||
destination_account_pubkey: &Pubkey,
|
destination_account_pubkey: &Pubkey,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let (recent_blockhash, fee_calculator, _) = rpc_client
|
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||||
.get_recent_blockhash_with_commitment(config.commitment)?
|
|
||||||
.value;
|
|
||||||
let withdraw_authority = config.signers[withdraw_authority];
|
let withdraw_authority = config.signers[withdraw_authority];
|
||||||
|
|
||||||
let current_balance = rpc_client
|
|
||||||
.get_balance_with_commitment(&vote_account_pubkey, config.commitment)?
|
|
||||||
.value;
|
|
||||||
let minimum_balance = rpc_client.get_minimum_balance_for_rent_exemption(VoteState::size_of())?;
|
|
||||||
|
|
||||||
let lamports = match withdraw_amount {
|
|
||||||
SpendAmount::All => current_balance.saturating_sub(minimum_balance),
|
|
||||||
SpendAmount::Some(withdraw_amount) => {
|
|
||||||
if current_balance.saturating_sub(withdraw_amount) < minimum_balance {
|
|
||||||
return Err(CliError::BadParameter(format!(
|
|
||||||
"Withdraw amount too large. The vote account balance must be at least {} SOL to remain rent exempt", lamports_to_sol(minimum_balance)
|
|
||||||
))
|
|
||||||
.into());
|
|
||||||
}
|
|
||||||
withdraw_amount
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let ix = withdraw(
|
let ix = withdraw(
|
||||||
vote_account_pubkey,
|
vote_account_pubkey,
|
||||||
&withdraw_authority.pubkey(),
|
&withdraw_authority.pubkey(),
|
||||||
@@ -751,21 +613,16 @@ pub fn process_withdraw_from_vote_account(
|
|||||||
destination_account_pubkey,
|
destination_account_pubkey,
|
||||||
);
|
);
|
||||||
|
|
||||||
let message = Message::new(&[ix], Some(&config.signers[0].pubkey()));
|
let message = Message::new_with_payer(&[ix], Some(&config.signers[0].pubkey()));
|
||||||
let mut transaction = Transaction::new_unsigned(message);
|
let mut transaction = Transaction::new_unsigned(message);
|
||||||
transaction.try_sign(&config.signers, recent_blockhash)?;
|
transaction.try_sign(&config.signers, recent_blockhash)?;
|
||||||
check_account_for_fee_with_commitment(
|
check_account_for_fee(
|
||||||
rpc_client,
|
rpc_client,
|
||||||
&config.signers[0].pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
&fee_calculator,
|
&fee_calculator,
|
||||||
&transaction.message,
|
&transaction.message,
|
||||||
config.commitment,
|
|
||||||
)?;
|
)?;
|
||||||
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
|
let result = rpc_client.send_and_confirm_transaction_with_spinner(&transaction);
|
||||||
&transaction,
|
|
||||||
config.commitment,
|
|
||||||
config.send_transaction_config,
|
|
||||||
);
|
|
||||||
log_instruction_custom_error::<VoteError>(result, &config)
|
log_instruction_custom_error::<VoteError>(result, &config)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -859,7 +716,6 @@ mod tests {
|
|||||||
parse_command(&test_create_vote_account, &default_keypair_file, &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,
|
|
||||||
seed: None,
|
seed: None,
|
||||||
identity_account: 2,
|
identity_account: 2,
|
||||||
authorized_voter: None,
|
authorized_voter: None,
|
||||||
@@ -888,7 +744,6 @@ mod tests {
|
|||||||
parse_command(&test_create_vote_account2, &default_keypair_file, &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,
|
|
||||||
seed: None,
|
seed: None,
|
||||||
identity_account: 2,
|
identity_account: 2,
|
||||||
authorized_voter: None,
|
authorized_voter: None,
|
||||||
@@ -921,7 +776,6 @@ mod tests {
|
|||||||
parse_command(&test_create_vote_account3, &default_keypair_file, &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,
|
|
||||||
seed: None,
|
seed: None,
|
||||||
identity_account: 2,
|
identity_account: 2,
|
||||||
authorized_voter: Some(authed),
|
authorized_voter: Some(authed),
|
||||||
@@ -952,7 +806,6 @@ mod tests {
|
|||||||
parse_command(&test_create_vote_account4, &default_keypair_file, &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,
|
|
||||||
seed: None,
|
seed: None,
|
||||||
identity_account: 2,
|
identity_account: 2,
|
||||||
authorized_voter: None,
|
authorized_voter: None,
|
||||||
@@ -980,7 +833,6 @@ mod tests {
|
|||||||
command: CliCommand::VoteUpdateValidator {
|
command: CliCommand::VoteUpdateValidator {
|
||||||
vote_account_pubkey: pubkey,
|
vote_account_pubkey: pubkey,
|
||||||
new_identity_account: 2,
|
new_identity_account: 2,
|
||||||
withdraw_authority: 1,
|
|
||||||
},
|
},
|
||||||
signers: vec![
|
signers: vec![
|
||||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||||
@@ -990,28 +842,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
let test_update_commission = test_commands.clone().get_matches_from(vec![
|
|
||||||
"test",
|
|
||||||
"vote-update-commission",
|
|
||||||
&pubkey_string,
|
|
||||||
"42",
|
|
||||||
&keypair_file,
|
|
||||||
]);
|
|
||||||
assert_eq!(
|
|
||||||
parse_command(&test_update_commission, &default_keypair_file, &mut None).unwrap(),
|
|
||||||
CliCommandInfo {
|
|
||||||
command: CliCommand::VoteUpdateCommission {
|
|
||||||
vote_account_pubkey: pubkey,
|
|
||||||
commission: 42,
|
|
||||||
withdraw_authority: 1,
|
|
||||||
},
|
|
||||||
signers: vec![
|
|
||||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
|
||||||
Box::new(read_keypair_file(&keypair_file).unwrap()),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Test WithdrawFromVoteAccount subcommand
|
// Test WithdrawFromVoteAccount subcommand
|
||||||
let test_withdraw_from_vote_account = test_commands.clone().get_matches_from(vec![
|
let test_withdraw_from_vote_account = test_commands.clone().get_matches_from(vec![
|
||||||
"test",
|
"test",
|
||||||
@@ -1032,33 +862,7 @@ mod tests {
|
|||||||
vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(),
|
vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(),
|
||||||
destination_account_pubkey: pubkey,
|
destination_account_pubkey: pubkey,
|
||||||
withdraw_authority: 0,
|
withdraw_authority: 0,
|
||||||
withdraw_amount: SpendAmount::Some(42_000_000_000),
|
lamports: 42_000_000_000
|
||||||
},
|
|
||||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Test WithdrawFromVoteAccount subcommand
|
|
||||||
let test_withdraw_from_vote_account = test_commands.clone().get_matches_from(vec![
|
|
||||||
"test",
|
|
||||||
"withdraw-from-vote-account",
|
|
||||||
&keypair_file,
|
|
||||||
&pubkey_string,
|
|
||||||
"ALL",
|
|
||||||
]);
|
|
||||||
assert_eq!(
|
|
||||||
parse_command(
|
|
||||||
&test_withdraw_from_vote_account,
|
|
||||||
&default_keypair_file,
|
|
||||||
&mut None
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
CliCommandInfo {
|
|
||||||
command: CliCommand::WithdrawFromVoteAccount {
|
|
||||||
vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(),
|
|
||||||
destination_account_pubkey: pubkey,
|
|
||||||
withdraw_authority: 0,
|
|
||||||
withdraw_amount: SpendAmount::All,
|
|
||||||
},
|
},
|
||||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
||||||
}
|
}
|
||||||
@@ -1089,7 +893,7 @@ mod tests {
|
|||||||
vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(),
|
vote_account_pubkey: read_keypair_file(&keypair_file).unwrap().pubkey(),
|
||||||
destination_account_pubkey: pubkey,
|
destination_account_pubkey: pubkey,
|
||||||
withdraw_authority: 1,
|
withdraw_authority: 1,
|
||||||
withdraw_amount: SpendAmount::Some(42_000_000_000),
|
lamports: 42_000_000_000
|
||||||
},
|
},
|
||||||
signers: vec![
|
signers: vec![
|
||||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||||
|
|||||||
@@ -3,12 +3,7 @@ use solana_cli::cli::{process_command, CliCommand, CliConfig};
|
|||||||
use solana_client::rpc_client::RpcClient;
|
use solana_client::rpc_client::RpcClient;
|
||||||
use solana_core::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, pubkey::Pubkey, signature::Keypair};
|
||||||
bpf_loader,
|
|
||||||
commitment_config::CommitmentConfig,
|
|
||||||
pubkey::Pubkey,
|
|
||||||
signature::{Keypair, Signer},
|
|
||||||
};
|
|
||||||
use std::{
|
use std::{
|
||||||
fs::{remove_dir_all, File},
|
fs::{remove_dir_all, File},
|
||||||
io::Read,
|
io::Read,
|
||||||
@@ -48,23 +43,19 @@ fn test_cli_deploy_program() {
|
|||||||
.get_minimum_balance_for_rent_exemption(program_data.len())
|
.get_minimum_balance_for_rent_exemption(program_data.len())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut config = CliConfig::recent_for_tests();
|
let mut config = CliConfig::default();
|
||||||
let keypair = Keypair::new();
|
let keypair = Keypair::new();
|
||||||
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
config.command = CliCommand::Airdrop {
|
config.command = CliCommand::Airdrop {
|
||||||
faucet_host: None,
|
faucet_host: None,
|
||||||
faucet_port: faucet_addr.port(),
|
faucet_port: faucet_addr.port(),
|
||||||
pubkey: None,
|
pubkey: None,
|
||||||
lamports: 3 * minimum_balance_for_rent_exemption, // min balance for rent exemption for two programs + leftover for tx processing
|
lamports: minimum_balance_for_rent_exemption + 1, // min balance for rent exemption + leftover for tx processing
|
||||||
};
|
};
|
||||||
config.signers = vec![&keypair];
|
config.signers = vec![&keypair];
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
|
|
||||||
config.command = CliCommand::Deploy {
|
config.command = CliCommand::Deploy(pathbuf.to_str().unwrap().to_string());
|
||||||
program_location: pathbuf.to_str().unwrap().to_string(),
|
|
||||||
address: None,
|
|
||||||
use_deprecated_loader: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
let response = process_command(&config);
|
let response = process_command(&config);
|
||||||
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
|
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
|
||||||
@@ -76,42 +67,16 @@ fn test_cli_deploy_program() {
|
|||||||
.as_str()
|
.as_str()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let program_id = Pubkey::from_str(&program_id_str).unwrap();
|
let program_id = Pubkey::from_str(&program_id_str).unwrap();
|
||||||
let account0 = rpc_client
|
let account = rpc_client.get_account(&program_id).unwrap();
|
||||||
.get_account_with_commitment(&program_id, CommitmentConfig::recent())
|
assert_eq!(account.lamports, minimum_balance_for_rent_exemption);
|
||||||
.unwrap()
|
assert_eq!(account.owner, bpf_loader::id());
|
||||||
.value
|
assert_eq!(account.executable, true);
|
||||||
.unwrap();
|
|
||||||
assert_eq!(account0.lamports, minimum_balance_for_rent_exemption);
|
|
||||||
assert_eq!(account0.owner, bpf_loader::id());
|
|
||||||
assert_eq!(account0.executable, true);
|
|
||||||
|
|
||||||
let mut file = File::open(pathbuf.to_str().unwrap().to_string()).unwrap();
|
let mut file = File::open(pathbuf.to_str().unwrap().to_string()).unwrap();
|
||||||
let mut elf = Vec::new();
|
let mut elf = Vec::new();
|
||||||
file.read_to_end(&mut elf).unwrap();
|
file.read_to_end(&mut elf).unwrap();
|
||||||
|
|
||||||
assert_eq!(account0.data, elf);
|
assert_eq!(account.data, elf);
|
||||||
|
|
||||||
// Test custom address
|
|
||||||
let custom_address_keypair = Keypair::new();
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
process_command(&config).unwrap();
|
|
||||||
let account1 = rpc_client
|
|
||||||
.get_account_with_commitment(&custom_address_keypair.pubkey(), CommitmentConfig::recent())
|
|
||||||
.unwrap()
|
|
||||||
.value
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(account1.lamports, minimum_balance_for_rent_exemption);
|
|
||||||
assert_eq!(account1.owner, bpf_loader::id());
|
|
||||||
assert_eq!(account1.executable, true);
|
|
||||||
assert_eq!(account0.data, account1.data);
|
|
||||||
|
|
||||||
// Attempt to redeploy to the same address
|
|
||||||
process_command(&config).unwrap_err();
|
|
||||||
|
|
||||||
server.close().unwrap();
|
server.close().unwrap();
|
||||||
remove_dir_all(ledger_path).unwrap();
|
remove_dir_all(ledger_path).unwrap();
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use solana_cli::test_utils::check_balance;
|
||||||
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,
|
cli_output::OutputFormat,
|
||||||
@@ -7,14 +8,12 @@ use solana_cli::{
|
|||||||
parse_sign_only_reply_string,
|
parse_sign_only_reply_string,
|
||||||
},
|
},
|
||||||
spend_utils::SpendAmount,
|
spend_utils::SpendAmount,
|
||||||
test_utils::{check_ready, check_recent_balance},
|
|
||||||
};
|
};
|
||||||
use solana_client::rpc_client::RpcClient;
|
use solana_client::rpc_client::RpcClient;
|
||||||
use solana_core::contact_info::ContactInfo;
|
use solana_core::contact_info::ContactInfo;
|
||||||
use solana_core::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,
|
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{keypair_from_seed, Keypair, Signer},
|
signature::{keypair_from_seed, Keypair, Signer},
|
||||||
@@ -84,7 +83,7 @@ fn full_battery_tests(
|
|||||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||||
let json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
let json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
|
|
||||||
let mut config_payer = CliConfig::recent_for_tests();
|
let mut config_payer = CliConfig::default();
|
||||||
config_payer.json_rpc_url = json_rpc_url.clone();
|
config_payer.json_rpc_url = json_rpc_url.clone();
|
||||||
let payer = Keypair::new();
|
let payer = Keypair::new();
|
||||||
config_payer.signers = vec![&payer];
|
config_payer.signers = vec![&payer];
|
||||||
@@ -97,9 +96,9 @@ fn full_battery_tests(
|
|||||||
&config_payer,
|
&config_payer,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_recent_balance(2000, &rpc_client, &config_payer.signers[0].pubkey());
|
check_balance(2000, &rpc_client, &config_payer.signers[0].pubkey());
|
||||||
|
|
||||||
let mut config_nonce = CliConfig::recent_for_tests();
|
let mut config_nonce = CliConfig::default();
|
||||||
config_nonce.json_rpc_url = json_rpc_url;
|
config_nonce.json_rpc_url = json_rpc_url;
|
||||||
let nonce_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
let nonce_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
||||||
config_nonce.signers = vec![&nonce_keypair];
|
config_nonce.signers = vec![&nonce_keypair];
|
||||||
@@ -132,8 +131,8 @@ fn full_battery_tests(
|
|||||||
};
|
};
|
||||||
|
|
||||||
process_command(&config_payer).unwrap();
|
process_command(&config_payer).unwrap();
|
||||||
check_recent_balance(1000, &rpc_client, &config_payer.signers[0].pubkey());
|
check_balance(1000, &rpc_client, &config_payer.signers[0].pubkey());
|
||||||
check_recent_balance(1000, &rpc_client, &nonce_account);
|
check_balance(1000, &rpc_client, &nonce_account);
|
||||||
|
|
||||||
// Get nonce
|
// Get nonce
|
||||||
config_payer.signers.pop();
|
config_payer.signers.pop();
|
||||||
@@ -182,9 +181,9 @@ fn full_battery_tests(
|
|||||||
lamports: 100,
|
lamports: 100,
|
||||||
};
|
};
|
||||||
process_command(&config_payer).unwrap();
|
process_command(&config_payer).unwrap();
|
||||||
check_recent_balance(1000, &rpc_client, &config_payer.signers[0].pubkey());
|
check_balance(1000, &rpc_client, &config_payer.signers[0].pubkey());
|
||||||
check_recent_balance(900, &rpc_client, &nonce_account);
|
check_balance(900, &rpc_client, &nonce_account);
|
||||||
check_recent_balance(100, &rpc_client, &payee_pubkey);
|
check_balance(100, &rpc_client, &payee_pubkey);
|
||||||
|
|
||||||
// Show nonce account
|
// Show nonce account
|
||||||
config_payer.command = CliCommand::ShowNonceAccount {
|
config_payer.command = CliCommand::ShowNonceAccount {
|
||||||
@@ -225,9 +224,9 @@ fn full_battery_tests(
|
|||||||
lamports: 100,
|
lamports: 100,
|
||||||
};
|
};
|
||||||
process_command(&config_payer).unwrap();
|
process_command(&config_payer).unwrap();
|
||||||
check_recent_balance(1000, &rpc_client, &config_payer.signers[0].pubkey());
|
check_balance(1000, &rpc_client, &config_payer.signers[0].pubkey());
|
||||||
check_recent_balance(800, &rpc_client, &nonce_account);
|
check_balance(800, &rpc_client, &nonce_account);
|
||||||
check_recent_balance(200, &rpc_client, &payee_pubkey);
|
check_balance(200, &rpc_client, &payee_pubkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -251,7 +250,7 @@ fn test_create_account_with_seed() {
|
|||||||
let offline_nonce_authority_signer = keypair_from_seed(&[1u8; 32]).unwrap();
|
let offline_nonce_authority_signer = keypair_from_seed(&[1u8; 32]).unwrap();
|
||||||
let online_nonce_creator_signer = keypair_from_seed(&[2u8; 32]).unwrap();
|
let online_nonce_creator_signer = keypair_from_seed(&[2u8; 32]).unwrap();
|
||||||
let to_address = Pubkey::new(&[3u8; 32]);
|
let to_address = Pubkey::new(&[3u8; 32]);
|
||||||
let config = CliConfig::recent_for_tests();
|
let config = CliConfig::default();
|
||||||
|
|
||||||
// Setup accounts
|
// Setup accounts
|
||||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||||
@@ -271,11 +270,9 @@ fn test_create_account_with_seed() {
|
|||||||
&config,
|
&config,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_recent_balance(42, &rpc_client, &offline_nonce_authority_signer.pubkey());
|
check_balance(42, &rpc_client, &offline_nonce_authority_signer.pubkey());
|
||||||
check_recent_balance(4242, &rpc_client, &online_nonce_creator_signer.pubkey());
|
check_balance(4242, &rpc_client, &online_nonce_creator_signer.pubkey());
|
||||||
check_recent_balance(0, &rpc_client, &to_address);
|
check_balance(0, &rpc_client, &to_address);
|
||||||
|
|
||||||
check_ready(&rpc_client);
|
|
||||||
|
|
||||||
// Create nonce account
|
// Create nonce account
|
||||||
let creator_pubkey = online_nonce_creator_signer.pubkey();
|
let creator_pubkey = online_nonce_creator_signer.pubkey();
|
||||||
@@ -283,9 +280,9 @@ fn test_create_account_with_seed() {
|
|||||||
let seed = authority_pubkey.to_string()[0..32].to_string();
|
let seed = authority_pubkey.to_string()[0..32].to_string();
|
||||||
let nonce_address =
|
let nonce_address =
|
||||||
Pubkey::create_with_seed(&creator_pubkey, &seed, &system_program::id()).unwrap();
|
Pubkey::create_with_seed(&creator_pubkey, &seed, &system_program::id()).unwrap();
|
||||||
check_recent_balance(0, &rpc_client, &nonce_address);
|
check_balance(0, &rpc_client, &nonce_address);
|
||||||
|
|
||||||
let mut creator_config = CliConfig::recent_for_tests();
|
let mut creator_config = CliConfig::default();
|
||||||
creator_config.json_rpc_url =
|
creator_config.json_rpc_url =
|
||||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
creator_config.signers = vec![&online_nonce_creator_signer];
|
creator_config.signers = vec![&online_nonce_creator_signer];
|
||||||
@@ -296,20 +293,19 @@ fn test_create_account_with_seed() {
|
|||||||
amount: SpendAmount::Some(241),
|
amount: SpendAmount::Some(241),
|
||||||
};
|
};
|
||||||
process_command(&creator_config).unwrap();
|
process_command(&creator_config).unwrap();
|
||||||
check_recent_balance(241, &rpc_client, &nonce_address);
|
check_balance(241, &rpc_client, &nonce_address);
|
||||||
check_recent_balance(42, &rpc_client, &offline_nonce_authority_signer.pubkey());
|
check_balance(42, &rpc_client, &offline_nonce_authority_signer.pubkey());
|
||||||
check_recent_balance(4000, &rpc_client, &online_nonce_creator_signer.pubkey());
|
check_balance(4000, &rpc_client, &online_nonce_creator_signer.pubkey());
|
||||||
check_recent_balance(0, &rpc_client, &to_address);
|
check_balance(0, &rpc_client, &to_address);
|
||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let nonce_hash =
|
let nonce_hash = nonce::get_account(&rpc_client, &nonce_address)
|
||||||
nonce::get_account_with_commitment(&rpc_client, &nonce_address, CommitmentConfig::recent())
|
|
||||||
.and_then(|ref a| nonce::data_from_account(a))
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.blockhash;
|
.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::default();
|
||||||
authority_config.json_rpc_url = String::default();
|
authority_config.json_rpc_url = String::default();
|
||||||
authority_config.signers = vec![&offline_nonce_authority_signer];
|
authority_config.signers = vec![&offline_nonce_authority_signer];
|
||||||
// Verify we cannot contact the cluster
|
// Verify we cannot contact the cluster
|
||||||
@@ -333,7 +329,7 @@ fn test_create_account_with_seed() {
|
|||||||
assert_eq!(sign_only.blockhash, nonce_hash);
|
assert_eq!(sign_only.blockhash, nonce_hash);
|
||||||
|
|
||||||
// And submit it
|
// And submit it
|
||||||
let mut submit_config = CliConfig::recent_for_tests();
|
let mut submit_config = CliConfig::default();
|
||||||
submit_config.json_rpc_url =
|
submit_config.json_rpc_url =
|
||||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
submit_config.signers = vec![&authority_presigner];
|
submit_config.signers = vec![&authority_presigner];
|
||||||
@@ -352,10 +348,10 @@ fn test_create_account_with_seed() {
|
|||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
};
|
};
|
||||||
process_command(&submit_config).unwrap();
|
process_command(&submit_config).unwrap();
|
||||||
check_recent_balance(241, &rpc_client, &nonce_address);
|
check_balance(241, &rpc_client, &nonce_address);
|
||||||
check_recent_balance(31, &rpc_client, &offline_nonce_authority_signer.pubkey());
|
check_balance(31, &rpc_client, &offline_nonce_authority_signer.pubkey());
|
||||||
check_recent_balance(4000, &rpc_client, &online_nonce_creator_signer.pubkey());
|
check_balance(4000, &rpc_client, &online_nonce_creator_signer.pubkey());
|
||||||
check_recent_balance(10, &rpc_client, &to_address);
|
check_balance(10, &rpc_client, &to_address);
|
||||||
|
|
||||||
server.close().unwrap();
|
server.close().unwrap();
|
||||||
remove_dir_all(ledger_path).unwrap();
|
remove_dir_all(ledger_path).unwrap();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
use solana_cli::test_utils::check_balance;
|
||||||
use solana_cli::{
|
use solana_cli::{
|
||||||
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig, PayCommand},
|
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig, PayCommand},
|
||||||
cli_output::OutputFormat,
|
cli_output::OutputFormat,
|
||||||
@@ -9,13 +10,11 @@ use solana_cli::{
|
|||||||
parse_sign_only_reply_string,
|
parse_sign_only_reply_string,
|
||||||
},
|
},
|
||||||
spend_utils::SpendAmount,
|
spend_utils::SpendAmount,
|
||||||
test_utils::check_recent_balance,
|
|
||||||
};
|
};
|
||||||
use solana_client::rpc_client::RpcClient;
|
use solana_client::rpc_client::RpcClient;
|
||||||
use solana_core::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::{
|
||||||
commitment_config::CommitmentConfig,
|
|
||||||
nonce::State as NonceState,
|
nonce::State as NonceState,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{Keypair, Signer},
|
signature::{Keypair, Signer},
|
||||||
@@ -41,12 +40,12 @@ fn test_cli_timestamp_tx() {
|
|||||||
let default_signer0 = Keypair::new();
|
let default_signer0 = Keypair::new();
|
||||||
let default_signer1 = Keypair::new();
|
let default_signer1 = Keypair::new();
|
||||||
|
|
||||||
let mut config_payer = CliConfig::recent_for_tests();
|
let mut config_payer = CliConfig::default();
|
||||||
config_payer.json_rpc_url =
|
config_payer.json_rpc_url =
|
||||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
config_payer.signers = vec![&default_signer0];
|
config_payer.signers = vec![&default_signer0];
|
||||||
|
|
||||||
let mut config_witness = CliConfig::recent_for_tests();
|
let mut config_witness = CliConfig::default();
|
||||||
config_witness.json_rpc_url = config_payer.json_rpc_url.clone();
|
config_witness.json_rpc_url = config_payer.json_rpc_url.clone();
|
||||||
config_witness.signers = vec![&default_signer1];
|
config_witness.signers = vec![&default_signer1];
|
||||||
|
|
||||||
@@ -63,7 +62,7 @@ fn test_cli_timestamp_tx() {
|
|||||||
&config_witness,
|
&config_witness,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_recent_balance(50, &rpc_client, &config_payer.signers[0].pubkey());
|
check_balance(50, &rpc_client, &config_payer.signers[0].pubkey());
|
||||||
|
|
||||||
request_and_confirm_airdrop(
|
request_and_confirm_airdrop(
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
@@ -93,17 +92,17 @@ fn test_cli_timestamp_tx() {
|
|||||||
.expect("base58-encoded public key");
|
.expect("base58-encoded public key");
|
||||||
let process_id = Pubkey::new(&process_id_vec);
|
let process_id = Pubkey::new(&process_id_vec);
|
||||||
|
|
||||||
check_recent_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
check_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||||
check_recent_balance(10, &rpc_client, &process_id); // contract balance
|
check_balance(10, &rpc_client, &process_id); // contract balance
|
||||||
check_recent_balance(0, &rpc_client, &bob_pubkey); // recipient balance
|
check_balance(0, &rpc_client, &bob_pubkey); // recipient balance
|
||||||
|
|
||||||
// Sign transaction by config_witness
|
// Sign transaction by config_witness
|
||||||
config_witness.command = CliCommand::TimeElapsed(bob_pubkey, process_id, dt);
|
config_witness.command = CliCommand::TimeElapsed(bob_pubkey, process_id, dt);
|
||||||
process_command(&config_witness).unwrap();
|
process_command(&config_witness).unwrap();
|
||||||
|
|
||||||
check_recent_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
check_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||||
check_recent_balance(0, &rpc_client, &process_id); // contract balance
|
check_balance(0, &rpc_client, &process_id); // contract balance
|
||||||
check_recent_balance(10, &rpc_client, &bob_pubkey); // recipient balance
|
check_balance(10, &rpc_client, &bob_pubkey); // recipient balance
|
||||||
|
|
||||||
server.close().unwrap();
|
server.close().unwrap();
|
||||||
remove_dir_all(ledger_path).unwrap();
|
remove_dir_all(ledger_path).unwrap();
|
||||||
@@ -128,12 +127,12 @@ fn test_cli_witness_tx() {
|
|||||||
let default_signer0 = Keypair::new();
|
let default_signer0 = Keypair::new();
|
||||||
let default_signer1 = Keypair::new();
|
let default_signer1 = Keypair::new();
|
||||||
|
|
||||||
let mut config_payer = CliConfig::recent_for_tests();
|
let mut config_payer = CliConfig::default();
|
||||||
config_payer.json_rpc_url =
|
config_payer.json_rpc_url =
|
||||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
config_payer.signers = vec![&default_signer0];
|
config_payer.signers = vec![&default_signer0];
|
||||||
|
|
||||||
let mut config_witness = CliConfig::recent_for_tests();
|
let mut config_witness = CliConfig::default();
|
||||||
config_witness.json_rpc_url = config_payer.json_rpc_url.clone();
|
config_witness.json_rpc_url = config_payer.json_rpc_url.clone();
|
||||||
config_witness.signers = vec![&default_signer1];
|
config_witness.signers = vec![&default_signer1];
|
||||||
|
|
||||||
@@ -175,17 +174,17 @@ fn test_cli_witness_tx() {
|
|||||||
.expect("base58-encoded public key");
|
.expect("base58-encoded public key");
|
||||||
let process_id = Pubkey::new(&process_id_vec);
|
let process_id = Pubkey::new(&process_id_vec);
|
||||||
|
|
||||||
check_recent_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
check_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||||
check_recent_balance(10, &rpc_client, &process_id); // contract balance
|
check_balance(10, &rpc_client, &process_id); // contract balance
|
||||||
check_recent_balance(0, &rpc_client, &bob_pubkey); // recipient balance
|
check_balance(0, &rpc_client, &bob_pubkey); // recipient balance
|
||||||
|
|
||||||
// Sign transaction by config_witness
|
// Sign transaction by config_witness
|
||||||
config_witness.command = CliCommand::Witness(bob_pubkey, process_id);
|
config_witness.command = CliCommand::Witness(bob_pubkey, process_id);
|
||||||
process_command(&config_witness).unwrap();
|
process_command(&config_witness).unwrap();
|
||||||
|
|
||||||
check_recent_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
check_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||||
check_recent_balance(0, &rpc_client, &process_id); // contract balance
|
check_balance(0, &rpc_client, &process_id); // contract balance
|
||||||
check_recent_balance(10, &rpc_client, &bob_pubkey); // recipient balance
|
check_balance(10, &rpc_client, &bob_pubkey); // recipient balance
|
||||||
|
|
||||||
server.close().unwrap();
|
server.close().unwrap();
|
||||||
remove_dir_all(ledger_path).unwrap();
|
remove_dir_all(ledger_path).unwrap();
|
||||||
@@ -210,12 +209,12 @@ fn test_cli_cancel_tx() {
|
|||||||
let default_signer0 = Keypair::new();
|
let default_signer0 = Keypair::new();
|
||||||
let default_signer1 = Keypair::new();
|
let default_signer1 = Keypair::new();
|
||||||
|
|
||||||
let mut config_payer = CliConfig::recent_for_tests();
|
let mut config_payer = CliConfig::default();
|
||||||
config_payer.json_rpc_url =
|
config_payer.json_rpc_url =
|
||||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
config_payer.signers = vec![&default_signer0];
|
config_payer.signers = vec![&default_signer0];
|
||||||
|
|
||||||
let mut config_witness = CliConfig::recent_for_tests();
|
let mut config_witness = CliConfig::default();
|
||||||
config_witness.json_rpc_url = config_payer.json_rpc_url.clone();
|
config_witness.json_rpc_url = config_payer.json_rpc_url.clone();
|
||||||
config_witness.signers = vec![&default_signer1];
|
config_witness.signers = vec![&default_signer1];
|
||||||
|
|
||||||
@@ -250,17 +249,17 @@ fn test_cli_cancel_tx() {
|
|||||||
.expect("base58-encoded public key");
|
.expect("base58-encoded public key");
|
||||||
let process_id = Pubkey::new(&process_id_vec);
|
let process_id = Pubkey::new(&process_id_vec);
|
||||||
|
|
||||||
check_recent_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
check_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||||
check_recent_balance(10, &rpc_client, &process_id); // contract balance
|
check_balance(10, &rpc_client, &process_id); // contract balance
|
||||||
check_recent_balance(0, &rpc_client, &bob_pubkey); // recipient balance
|
check_balance(0, &rpc_client, &bob_pubkey); // recipient balance
|
||||||
|
|
||||||
// Sign transaction by config_witness
|
// Sign transaction by config_witness
|
||||||
config_payer.command = CliCommand::Cancel(process_id);
|
config_payer.command = CliCommand::Cancel(process_id);
|
||||||
process_command(&config_payer).unwrap();
|
process_command(&config_payer).unwrap();
|
||||||
|
|
||||||
check_recent_balance(50, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
check_balance(50, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||||
check_recent_balance(0, &rpc_client, &process_id); // contract balance
|
check_balance(0, &rpc_client, &process_id); // contract balance
|
||||||
check_recent_balance(0, &rpc_client, &bob_pubkey); // recipient balance
|
check_balance(0, &rpc_client, &bob_pubkey); // recipient balance
|
||||||
|
|
||||||
server.close().unwrap();
|
server.close().unwrap();
|
||||||
remove_dir_all(ledger_path).unwrap();
|
remove_dir_all(ledger_path).unwrap();
|
||||||
@@ -285,11 +284,11 @@ fn test_offline_pay_tx() {
|
|||||||
let default_signer = Keypair::new();
|
let default_signer = Keypair::new();
|
||||||
let default_offline_signer = Keypair::new();
|
let default_offline_signer = Keypair::new();
|
||||||
|
|
||||||
let mut config_offline = CliConfig::recent_for_tests();
|
let mut config_offline = CliConfig::default();
|
||||||
config_offline.json_rpc_url =
|
config_offline.json_rpc_url =
|
||||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
config_offline.signers = vec![&default_offline_signer];
|
config_offline.signers = vec![&default_offline_signer];
|
||||||
let mut config_online = CliConfig::recent_for_tests();
|
let mut config_online = CliConfig::default();
|
||||||
config_online.json_rpc_url =
|
config_online.json_rpc_url =
|
||||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
config_online.signers = vec![&default_signer];
|
config_online.signers = vec![&default_signer];
|
||||||
@@ -315,8 +314,8 @@ fn test_offline_pay_tx() {
|
|||||||
&config_offline,
|
&config_offline,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_recent_balance(50, &rpc_client, &config_offline.signers[0].pubkey());
|
check_balance(50, &rpc_client, &config_offline.signers[0].pubkey());
|
||||||
check_recent_balance(50, &rpc_client, &config_online.signers[0].pubkey());
|
check_balance(50, &rpc_client, &config_online.signers[0].pubkey());
|
||||||
|
|
||||||
let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap();
|
let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap();
|
||||||
config_offline.command = CliCommand::Pay(PayCommand {
|
config_offline.command = CliCommand::Pay(PayCommand {
|
||||||
@@ -329,9 +328,9 @@ fn test_offline_pay_tx() {
|
|||||||
config_offline.output_format = OutputFormat::JsonCompact;
|
config_offline.output_format = OutputFormat::JsonCompact;
|
||||||
let sig_response = process_command(&config_offline).unwrap();
|
let sig_response = process_command(&config_offline).unwrap();
|
||||||
|
|
||||||
check_recent_balance(50, &rpc_client, &config_offline.signers[0].pubkey());
|
check_balance(50, &rpc_client, &config_offline.signers[0].pubkey());
|
||||||
check_recent_balance(50, &rpc_client, &config_online.signers[0].pubkey());
|
check_balance(50, &rpc_client, &config_online.signers[0].pubkey());
|
||||||
check_recent_balance(0, &rpc_client, &bob_pubkey);
|
check_balance(0, &rpc_client, &bob_pubkey);
|
||||||
|
|
||||||
let sign_only = parse_sign_only_reply_string(&sig_response);
|
let sign_only = parse_sign_only_reply_string(&sig_response);
|
||||||
assert!(sign_only.has_all_signers());
|
assert!(sign_only.has_all_signers());
|
||||||
@@ -348,9 +347,9 @@ fn test_offline_pay_tx() {
|
|||||||
});
|
});
|
||||||
process_command(&config_online).unwrap();
|
process_command(&config_online).unwrap();
|
||||||
|
|
||||||
check_recent_balance(40, &rpc_client, &config_offline.signers[0].pubkey());
|
check_balance(40, &rpc_client, &config_offline.signers[0].pubkey());
|
||||||
check_recent_balance(50, &rpc_client, &online_pubkey);
|
check_balance(50, &rpc_client, &online_pubkey);
|
||||||
check_recent_balance(10, &rpc_client, &bob_pubkey);
|
check_balance(10, &rpc_client, &bob_pubkey);
|
||||||
|
|
||||||
server.close().unwrap();
|
server.close().unwrap();
|
||||||
remove_dir_all(ledger_path).unwrap();
|
remove_dir_all(ledger_path).unwrap();
|
||||||
@@ -374,7 +373,7 @@ fn test_nonced_pay_tx() {
|
|||||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||||
let default_signer = Keypair::new();
|
let default_signer = Keypair::new();
|
||||||
|
|
||||||
let mut config = CliConfig::recent_for_tests();
|
let mut config = CliConfig::default();
|
||||||
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
config.signers = vec![&default_signer];
|
config.signers = vec![&default_signer];
|
||||||
|
|
||||||
@@ -390,7 +389,7 @@ fn test_nonced_pay_tx() {
|
|||||||
&config,
|
&config,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_recent_balance(
|
check_balance(
|
||||||
50 + minimum_nonce_balance,
|
50 + minimum_nonce_balance,
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
&config.signers[0].pubkey(),
|
&config.signers[0].pubkey(),
|
||||||
@@ -407,15 +406,11 @@ fn test_nonced_pay_tx() {
|
|||||||
config.signers.push(&nonce_account);
|
config.signers.push(&nonce_account);
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
|
|
||||||
check_recent_balance(50, &rpc_client, &config.signers[0].pubkey());
|
check_balance(50, &rpc_client, &config.signers[0].pubkey());
|
||||||
check_recent_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey());
|
check_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey());
|
||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let nonce_hash = nonce::get_account_with_commitment(
|
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||||
&rpc_client,
|
|
||||||
&nonce_account.pubkey(),
|
|
||||||
CommitmentConfig::recent(),
|
|
||||||
)
|
|
||||||
.and_then(|ref a| nonce::data_from_account(a))
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.blockhash;
|
.blockhash;
|
||||||
@@ -434,15 +429,11 @@ fn test_nonced_pay_tx() {
|
|||||||
});
|
});
|
||||||
process_command(&config).expect("failed to process pay command");
|
process_command(&config).expect("failed to process pay command");
|
||||||
|
|
||||||
check_recent_balance(40, &rpc_client, &config.signers[0].pubkey());
|
check_balance(40, &rpc_client, &config.signers[0].pubkey());
|
||||||
check_recent_balance(10, &rpc_client, &bob_pubkey);
|
check_balance(10, &rpc_client, &bob_pubkey);
|
||||||
|
|
||||||
// Verify that nonce has been used
|
// Verify that nonce has been used
|
||||||
let nonce_hash2 = nonce::get_account_with_commitment(
|
let nonce_hash2 = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||||
&rpc_client,
|
|
||||||
&nonce_account.pubkey(),
|
|
||||||
CommitmentConfig::recent(),
|
|
||||||
)
|
|
||||||
.and_then(|ref a| nonce::data_from_account(a))
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.blockhash;
|
.blockhash;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use solana_cli::cli::{process_command, CliCommand, CliConfig};
|
|||||||
use solana_client::rpc_client::RpcClient;
|
use solana_client::rpc_client::RpcClient;
|
||||||
use solana_core::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::signature::Keypair;
|
||||||
use std::{fs::remove_dir_all, sync::mpsc::channel};
|
use std::{fs::remove_dir_all, sync::mpsc::channel};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -18,7 +18,7 @@ fn test_cli_request_airdrop() {
|
|||||||
run_local_faucet(alice, sender, None);
|
run_local_faucet(alice, sender, None);
|
||||||
let faucet_addr = receiver.recv().unwrap();
|
let faucet_addr = receiver.recv().unwrap();
|
||||||
|
|
||||||
let mut bob_config = CliConfig::recent_for_tests();
|
let mut bob_config = CliConfig::default();
|
||||||
bob_config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
bob_config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
bob_config.command = CliCommand::Airdrop {
|
bob_config.command = CliCommand::Airdrop {
|
||||||
faucet_host: None,
|
faucet_host: None,
|
||||||
@@ -35,9 +35,8 @@ fn test_cli_request_airdrop() {
|
|||||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||||
|
|
||||||
let balance = rpc_client
|
let balance = rpc_client
|
||||||
.get_balance_with_commitment(&bob_config.signers[0].pubkey(), CommitmentConfig::recent())
|
.get_balance(&bob_config.signers[0].pubkey())
|
||||||
.unwrap()
|
.unwrap();
|
||||||
.value;
|
|
||||||
assert_eq!(balance, 50);
|
assert_eq!(balance, 50);
|
||||||
|
|
||||||
server.close().unwrap();
|
server.close().unwrap();
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use solana_cli::test_utils::check_balance;
|
||||||
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,
|
cli_output::OutputFormat,
|
||||||
@@ -7,14 +8,12 @@ use solana_cli::{
|
|||||||
parse_sign_only_reply_string,
|
parse_sign_only_reply_string,
|
||||||
},
|
},
|
||||||
spend_utils::SpendAmount,
|
spend_utils::SpendAmount,
|
||||||
test_utils::{check_ready, check_recent_balance},
|
|
||||||
};
|
};
|
||||||
use solana_client::rpc_client::RpcClient;
|
use solana_client::rpc_client::RpcClient;
|
||||||
use solana_core::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::{
|
||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
commitment_config::CommitmentConfig,
|
|
||||||
nonce::State as NonceState,
|
nonce::State as NonceState,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{keypair_from_seed, Keypair, Signer},
|
signature::{keypair_from_seed, Keypair, Signer},
|
||||||
@@ -41,7 +40,7 @@ fn test_stake_delegation_force() {
|
|||||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||||
let default_signer = Keypair::new();
|
let default_signer = Keypair::new();
|
||||||
|
|
||||||
let mut config = CliConfig::recent_for_tests();
|
let mut config = CliConfig::default();
|
||||||
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
config.signers = vec![&default_signer];
|
config.signers = vec![&default_signer];
|
||||||
|
|
||||||
@@ -58,7 +57,6 @@ fn test_stake_delegation_force() {
|
|||||||
let vote_keypair = Keypair::new();
|
let vote_keypair = Keypair::new();
|
||||||
config.signers = vec![&default_signer, &vote_keypair];
|
config.signers = vec![&default_signer, &vote_keypair];
|
||||||
config.command = CliCommand::CreateVoteAccount {
|
config.command = CliCommand::CreateVoteAccount {
|
||||||
vote_account: 1,
|
|
||||||
seed: None,
|
seed: None,
|
||||||
identity_account: 0,
|
identity_account: 0,
|
||||||
authorized_voter: None,
|
authorized_voter: None,
|
||||||
@@ -138,7 +136,7 @@ fn test_seed_stake_delegation_and_deactivation() {
|
|||||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||||
|
|
||||||
let validator_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
let validator_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
||||||
let mut config_validator = CliConfig::recent_for_tests();
|
let mut config_validator = CliConfig::default();
|
||||||
config_validator.json_rpc_url =
|
config_validator.json_rpc_url =
|
||||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
config_validator.signers = vec![&validator_keypair];
|
config_validator.signers = vec![&validator_keypair];
|
||||||
@@ -151,7 +149,7 @@ fn test_seed_stake_delegation_and_deactivation() {
|
|||||||
&config_validator,
|
&config_validator,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_recent_balance(100_000, &rpc_client, &config_validator.signers[0].pubkey());
|
check_balance(100_000, &rpc_client, &config_validator.signers[0].pubkey());
|
||||||
|
|
||||||
let stake_address = Pubkey::create_with_seed(
|
let stake_address = Pubkey::create_with_seed(
|
||||||
&config_validator.signers[0].pubkey(),
|
&config_validator.signers[0].pubkey(),
|
||||||
@@ -183,7 +181,7 @@ fn test_seed_stake_delegation_and_deactivation() {
|
|||||||
stake_account_pubkey: stake_address,
|
stake_account_pubkey: stake_address,
|
||||||
vote_account_pubkey: vote_pubkey,
|
vote_account_pubkey: vote_pubkey,
|
||||||
stake_authority: 0,
|
stake_authority: 0,
|
||||||
force: true,
|
force: false,
|
||||||
sign_only: false,
|
sign_only: false,
|
||||||
blockhash_query: BlockhashQuery::default(),
|
blockhash_query: BlockhashQuery::default(),
|
||||||
nonce_account: None,
|
nonce_account: None,
|
||||||
@@ -227,7 +225,7 @@ fn test_stake_delegation_and_deactivation() {
|
|||||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||||
let validator_keypair = Keypair::new();
|
let validator_keypair = Keypair::new();
|
||||||
|
|
||||||
let mut config_validator = CliConfig::recent_for_tests();
|
let mut config_validator = CliConfig::default();
|
||||||
config_validator.json_rpc_url =
|
config_validator.json_rpc_url =
|
||||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
config_validator.signers = vec![&validator_keypair];
|
config_validator.signers = vec![&validator_keypair];
|
||||||
@@ -242,7 +240,7 @@ fn test_stake_delegation_and_deactivation() {
|
|||||||
&config_validator,
|
&config_validator,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_recent_balance(100_000, &rpc_client, &config_validator.signers[0].pubkey());
|
check_balance(100_000, &rpc_client, &config_validator.signers[0].pubkey());
|
||||||
|
|
||||||
// Create stake account
|
// Create stake account
|
||||||
config_validator.signers.push(&stake_keypair);
|
config_validator.signers.push(&stake_keypair);
|
||||||
@@ -268,7 +266,7 @@ fn test_stake_delegation_and_deactivation() {
|
|||||||
stake_account_pubkey: stake_keypair.pubkey(),
|
stake_account_pubkey: stake_keypair.pubkey(),
|
||||||
vote_account_pubkey: vote_pubkey,
|
vote_account_pubkey: vote_pubkey,
|
||||||
stake_authority: 0,
|
stake_authority: 0,
|
||||||
force: true,
|
force: false,
|
||||||
sign_only: false,
|
sign_only: false,
|
||||||
blockhash_query: BlockhashQuery::default(),
|
blockhash_query: BlockhashQuery::default(),
|
||||||
nonce_account: None,
|
nonce_account: None,
|
||||||
@@ -311,19 +309,19 @@ fn test_offline_stake_delegation_and_deactivation() {
|
|||||||
|
|
||||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||||
|
|
||||||
let mut config_validator = CliConfig::recent_for_tests();
|
let mut config_validator = CliConfig::default();
|
||||||
config_validator.json_rpc_url =
|
config_validator.json_rpc_url =
|
||||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
let validator_keypair = Keypair::new();
|
let validator_keypair = Keypair::new();
|
||||||
config_validator.signers = vec![&validator_keypair];
|
config_validator.signers = vec![&validator_keypair];
|
||||||
|
|
||||||
let mut config_payer = CliConfig::recent_for_tests();
|
let mut config_payer = CliConfig::default();
|
||||||
config_payer.json_rpc_url =
|
config_payer.json_rpc_url =
|
||||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
|
|
||||||
let stake_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
let stake_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
||||||
|
|
||||||
let mut config_offline = CliConfig::recent_for_tests();
|
let mut config_offline = CliConfig::default();
|
||||||
config_offline.json_rpc_url = String::default();
|
config_offline.json_rpc_url = String::default();
|
||||||
config_offline.command = CliCommand::ClusterVersion;
|
config_offline.command = CliCommand::ClusterVersion;
|
||||||
let offline_keypair = Keypair::new();
|
let offline_keypair = Keypair::new();
|
||||||
@@ -339,7 +337,7 @@ fn test_offline_stake_delegation_and_deactivation() {
|
|||||||
&config_offline,
|
&config_offline,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_recent_balance(100_000, &rpc_client, &config_validator.signers[0].pubkey());
|
check_balance(100_000, &rpc_client, &config_validator.signers[0].pubkey());
|
||||||
|
|
||||||
request_and_confirm_airdrop(
|
request_and_confirm_airdrop(
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
@@ -349,7 +347,7 @@ fn test_offline_stake_delegation_and_deactivation() {
|
|||||||
&config_validator,
|
&config_validator,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_recent_balance(100_000, &rpc_client, &config_offline.signers[0].pubkey());
|
check_balance(100_000, &rpc_client, &config_offline.signers[0].pubkey());
|
||||||
|
|
||||||
// Create stake account
|
// Create stake account
|
||||||
config_validator.signers.push(&stake_keypair);
|
config_validator.signers.push(&stake_keypair);
|
||||||
@@ -375,7 +373,7 @@ fn test_offline_stake_delegation_and_deactivation() {
|
|||||||
stake_account_pubkey: stake_keypair.pubkey(),
|
stake_account_pubkey: stake_keypair.pubkey(),
|
||||||
vote_account_pubkey: vote_pubkey,
|
vote_account_pubkey: vote_pubkey,
|
||||||
stake_authority: 0,
|
stake_authority: 0,
|
||||||
force: true,
|
force: false,
|
||||||
sign_only: true,
|
sign_only: true,
|
||||||
blockhash_query: BlockhashQuery::None(blockhash),
|
blockhash_query: BlockhashQuery::None(blockhash),
|
||||||
nonce_account: None,
|
nonce_account: None,
|
||||||
@@ -394,7 +392,7 @@ fn test_offline_stake_delegation_and_deactivation() {
|
|||||||
stake_account_pubkey: stake_keypair.pubkey(),
|
stake_account_pubkey: stake_keypair.pubkey(),
|
||||||
vote_account_pubkey: vote_pubkey,
|
vote_account_pubkey: vote_pubkey,
|
||||||
stake_authority: 0,
|
stake_authority: 0,
|
||||||
force: true,
|
force: false,
|
||||||
sign_only: false,
|
sign_only: false,
|
||||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
|
blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
|
||||||
nonce_account: None,
|
nonce_account: None,
|
||||||
@@ -455,7 +453,7 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
|||||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||||
|
|
||||||
let config_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
let config_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
||||||
let mut config = CliConfig::recent_for_tests();
|
let mut config = CliConfig::default();
|
||||||
config.signers = vec![&config_keypair];
|
config.signers = vec![&config_keypair];
|
||||||
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
|
|
||||||
@@ -503,11 +501,7 @@ 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::get_account_with_commitment(
|
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||||
&rpc_client,
|
|
||||||
&nonce_account.pubkey(),
|
|
||||||
CommitmentConfig::recent(),
|
|
||||||
)
|
|
||||||
.and_then(|ref a| nonce::data_from_account(a))
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.blockhash;
|
.blockhash;
|
||||||
@@ -518,7 +512,7 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
|||||||
stake_account_pubkey: stake_keypair.pubkey(),
|
stake_account_pubkey: stake_keypair.pubkey(),
|
||||||
vote_account_pubkey: vote_pubkey,
|
vote_account_pubkey: vote_pubkey,
|
||||||
stake_authority: 0,
|
stake_authority: 0,
|
||||||
force: true,
|
force: false,
|
||||||
sign_only: false,
|
sign_only: false,
|
||||||
blockhash_query: BlockhashQuery::FeeCalculator(
|
blockhash_query: BlockhashQuery::FeeCalculator(
|
||||||
blockhash_query::Source::NonceAccount(nonce_account.pubkey()),
|
blockhash_query::Source::NonceAccount(nonce_account.pubkey()),
|
||||||
@@ -531,11 +525,7 @@ 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::get_account_with_commitment(
|
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||||
&rpc_client,
|
|
||||||
&nonce_account.pubkey(),
|
|
||||||
CommitmentConfig::recent(),
|
|
||||||
)
|
|
||||||
.and_then(|ref a| nonce::data_from_account(a))
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.blockhash;
|
.blockhash;
|
||||||
@@ -577,7 +567,7 @@ fn test_stake_authorize() {
|
|||||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||||
let default_signer = Keypair::new();
|
let default_signer = Keypair::new();
|
||||||
|
|
||||||
let mut config = CliConfig::recent_for_tests();
|
let mut config = CliConfig::default();
|
||||||
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
config.signers = vec![&default_signer];
|
config.signers = vec![&default_signer];
|
||||||
|
|
||||||
@@ -591,7 +581,7 @@ fn test_stake_authorize() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let offline_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
let offline_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
||||||
let mut config_offline = CliConfig::recent_for_tests();
|
let mut config_offline = CliConfig::default();
|
||||||
config_offline.signers = vec![&offline_keypair];
|
config_offline.signers = vec![&offline_keypair];
|
||||||
config_offline.json_rpc_url = String::default();
|
config_offline.json_rpc_url = String::default();
|
||||||
let offline_authority_pubkey = config_offline.signers[0].pubkey();
|
let offline_authority_pubkey = config_offline.signers[0].pubkey();
|
||||||
@@ -642,11 +632,7 @@ fn test_stake_authorize() {
|
|||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
let stake_account = rpc_client
|
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||||
.get_account_with_commitment(&stake_account_pubkey, CommitmentConfig::recent())
|
|
||||||
.unwrap()
|
|
||||||
.value
|
|
||||||
.unwrap();
|
|
||||||
let stake_state: StakeState = stake_account.state().unwrap();
|
let stake_state: StakeState = stake_account.state().unwrap();
|
||||||
let current_authority = match stake_state {
|
let current_authority = match stake_state {
|
||||||
StakeState::Initialized(meta) => meta.authorized.staker,
|
StakeState::Initialized(meta) => meta.authorized.staker,
|
||||||
@@ -673,11 +659,7 @@ fn test_stake_authorize() {
|
|||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
let stake_account = rpc_client
|
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||||
.get_account_with_commitment(&stake_account_pubkey, CommitmentConfig::recent())
|
|
||||||
.unwrap()
|
|
||||||
.value
|
|
||||||
.unwrap();
|
|
||||||
let stake_state: StakeState = stake_account.state().unwrap();
|
let stake_state: StakeState = stake_account.state().unwrap();
|
||||||
let (current_staker, current_withdrawer) = match stake_state {
|
let (current_staker, current_withdrawer) = match stake_state {
|
||||||
StakeState::Initialized(meta) => (meta.authorized.staker, meta.authorized.withdrawer),
|
StakeState::Initialized(meta) => (meta.authorized.staker, meta.authorized.withdrawer),
|
||||||
@@ -699,11 +681,7 @@ fn test_stake_authorize() {
|
|||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
let stake_account = rpc_client
|
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||||
.get_account_with_commitment(&stake_account_pubkey, CommitmentConfig::recent())
|
|
||||||
.unwrap()
|
|
||||||
.value
|
|
||||||
.unwrap();
|
|
||||||
let stake_state: StakeState = stake_account.state().unwrap();
|
let stake_state: StakeState = stake_account.state().unwrap();
|
||||||
let current_authority = match stake_state {
|
let current_authority = match stake_state {
|
||||||
StakeState::Initialized(meta) => meta.authorized.staker,
|
StakeState::Initialized(meta) => meta.authorized.staker,
|
||||||
@@ -714,10 +692,7 @@ fn test_stake_authorize() {
|
|||||||
// Offline assignment of new nonced stake authority
|
// Offline assignment of new nonced stake authority
|
||||||
let nonced_authority = Keypair::new();
|
let nonced_authority = Keypair::new();
|
||||||
let nonced_authority_pubkey = nonced_authority.pubkey();
|
let nonced_authority_pubkey = nonced_authority.pubkey();
|
||||||
let (blockhash, _, _) = rpc_client
|
let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap();
|
||||||
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
|
|
||||||
.unwrap()
|
|
||||||
.value;
|
|
||||||
config_offline.command = CliCommand::StakeAuthorize {
|
config_offline.command = CliCommand::StakeAuthorize {
|
||||||
stake_account_pubkey,
|
stake_account_pubkey,
|
||||||
new_authorizations: vec![(StakeAuthorize::Staker, nonced_authority_pubkey, 0)],
|
new_authorizations: vec![(StakeAuthorize::Staker, nonced_authority_pubkey, 0)],
|
||||||
@@ -743,11 +718,7 @@ fn test_stake_authorize() {
|
|||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
let stake_account = rpc_client
|
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||||
.get_account_with_commitment(&stake_account_pubkey, CommitmentConfig::recent())
|
|
||||||
.unwrap()
|
|
||||||
.value
|
|
||||||
.unwrap();
|
|
||||||
let stake_state: StakeState = stake_account.state().unwrap();
|
let stake_state: StakeState = stake_account.state().unwrap();
|
||||||
let current_authority = match stake_state {
|
let current_authority = match stake_state {
|
||||||
StakeState::Initialized(meta) => meta.authorized.staker,
|
StakeState::Initialized(meta) => meta.authorized.staker,
|
||||||
@@ -770,11 +741,7 @@ fn test_stake_authorize() {
|
|||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let nonce_hash = nonce::get_account_with_commitment(
|
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||||
&rpc_client,
|
|
||||||
&nonce_account.pubkey(),
|
|
||||||
CommitmentConfig::recent(),
|
|
||||||
)
|
|
||||||
.and_then(|ref a| nonce::data_from_account(a))
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.blockhash;
|
.blockhash;
|
||||||
@@ -812,11 +779,7 @@ fn test_stake_authorize() {
|
|||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
let stake_account = rpc_client
|
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||||
.get_account_with_commitment(&stake_account_pubkey, CommitmentConfig::recent())
|
|
||||||
.unwrap()
|
|
||||||
.value
|
|
||||||
.unwrap();
|
|
||||||
let stake_state: StakeState = stake_account.state().unwrap();
|
let stake_state: StakeState = stake_account.state().unwrap();
|
||||||
let current_authority = match stake_state {
|
let current_authority = match stake_state {
|
||||||
StakeState::Initialized(meta) => meta.authorized.staker,
|
StakeState::Initialized(meta) => meta.authorized.staker,
|
||||||
@@ -824,11 +787,7 @@ fn test_stake_authorize() {
|
|||||||
};
|
};
|
||||||
assert_eq!(current_authority, online_authority_pubkey);
|
assert_eq!(current_authority, online_authority_pubkey);
|
||||||
|
|
||||||
let new_nonce_hash = nonce::get_account_with_commitment(
|
let new_nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||||
&rpc_client,
|
|
||||||
&nonce_account.pubkey(),
|
|
||||||
CommitmentConfig::recent(),
|
|
||||||
)
|
|
||||||
.and_then(|ref a| nonce::data_from_account(a))
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.blockhash;
|
.blockhash;
|
||||||
@@ -862,18 +821,18 @@ fn test_stake_authorize_with_fee_payer() {
|
|||||||
let default_signer = Keypair::new();
|
let default_signer = Keypair::new();
|
||||||
let default_pubkey = default_signer.pubkey();
|
let default_pubkey = default_signer.pubkey();
|
||||||
|
|
||||||
let mut config = CliConfig::recent_for_tests();
|
let mut config = CliConfig::default();
|
||||||
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
config.signers = vec![&default_signer];
|
config.signers = vec![&default_signer];
|
||||||
|
|
||||||
let payer_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
let payer_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
||||||
let mut config_payer = CliConfig::recent_for_tests();
|
let mut config_payer = CliConfig::default();
|
||||||
config_payer.signers = vec![&payer_keypair];
|
config_payer.signers = vec![&payer_keypair];
|
||||||
config_payer.json_rpc_url =
|
config_payer.json_rpc_url =
|
||||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
let payer_pubkey = config_payer.signers[0].pubkey();
|
let payer_pubkey = config_payer.signers[0].pubkey();
|
||||||
|
|
||||||
let mut config_offline = CliConfig::recent_for_tests();
|
let mut config_offline = CliConfig::default();
|
||||||
let offline_signer = Keypair::new();
|
let offline_signer = Keypair::new();
|
||||||
config_offline.signers = vec![&offline_signer];
|
config_offline.signers = vec![&offline_signer];
|
||||||
config_offline.json_rpc_url = String::new();
|
config_offline.json_rpc_url = String::new();
|
||||||
@@ -884,17 +843,15 @@ fn test_stake_authorize_with_fee_payer() {
|
|||||||
|
|
||||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &default_pubkey, 100_000, &config)
|
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &default_pubkey, 100_000, &config)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_recent_balance(100_000, &rpc_client, &config.signers[0].pubkey());
|
check_balance(100_000, &rpc_client, &config.signers[0].pubkey());
|
||||||
|
|
||||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &payer_pubkey, 100_000, &config)
|
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &payer_pubkey, 100_000, &config)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_recent_balance(100_000, &rpc_client, &payer_pubkey);
|
check_balance(100_000, &rpc_client, &payer_pubkey);
|
||||||
|
|
||||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 100_000, &config)
|
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 100_000, &config)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_recent_balance(100_000, &rpc_client, &offline_pubkey);
|
check_balance(100_000, &rpc_client, &offline_pubkey);
|
||||||
|
|
||||||
check_ready(&rpc_client);
|
|
||||||
|
|
||||||
// Create stake account, identity is authority
|
// Create stake account, identity is authority
|
||||||
let stake_keypair = Keypair::new();
|
let stake_keypair = Keypair::new();
|
||||||
@@ -916,7 +873,7 @@ fn test_stake_authorize_with_fee_payer() {
|
|||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
// `config` balance should be 50,000 - 1 stake account sig - 1 fee sig
|
// `config` balance should be 50,000 - 1 stake account sig - 1 fee sig
|
||||||
check_recent_balance(50_000 - SIG_FEE - SIG_FEE, &rpc_client, &default_pubkey);
|
check_balance(50_000 - SIG_FEE - SIG_FEE, &rpc_client, &default_pubkey);
|
||||||
|
|
||||||
// Assign authority with separate fee payer
|
// Assign authority with separate fee payer
|
||||||
config.signers = vec![&default_signer, &payer_keypair];
|
config.signers = vec![&default_signer, &payer_keypair];
|
||||||
@@ -931,16 +888,13 @@ fn test_stake_authorize_with_fee_payer() {
|
|||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
// `config` balance has not changed, despite submitting the TX
|
// `config` balance has not changed, despite submitting the TX
|
||||||
check_recent_balance(50_000 - SIG_FEE - SIG_FEE, &rpc_client, &default_pubkey);
|
check_balance(50_000 - SIG_FEE - SIG_FEE, &rpc_client, &default_pubkey);
|
||||||
// `config_payer` however has paid `config`'s authority sig
|
// `config_payer` however has paid `config`'s authority sig
|
||||||
// and `config_payer`'s fee sig
|
// and `config_payer`'s fee sig
|
||||||
check_recent_balance(100_000 - SIG_FEE - SIG_FEE, &rpc_client, &payer_pubkey);
|
check_balance(100_000 - SIG_FEE - SIG_FEE, &rpc_client, &payer_pubkey);
|
||||||
|
|
||||||
// Assign authority with offline fee payer
|
// Assign authority with offline fee payer
|
||||||
let (blockhash, _, _) = rpc_client
|
let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap();
|
||||||
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
|
|
||||||
.unwrap()
|
|
||||||
.value;
|
|
||||||
config_offline.command = CliCommand::StakeAuthorize {
|
config_offline.command = CliCommand::StakeAuthorize {
|
||||||
stake_account_pubkey,
|
stake_account_pubkey,
|
||||||
new_authorizations: vec![(StakeAuthorize::Staker, payer_pubkey, 0)],
|
new_authorizations: vec![(StakeAuthorize::Staker, payer_pubkey, 0)],
|
||||||
@@ -967,10 +921,10 @@ fn test_stake_authorize_with_fee_payer() {
|
|||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
// `config`'s balance again has not changed
|
// `config`'s balance again has not changed
|
||||||
check_recent_balance(50_000 - SIG_FEE - SIG_FEE, &rpc_client, &default_pubkey);
|
check_balance(50_000 - SIG_FEE - SIG_FEE, &rpc_client, &default_pubkey);
|
||||||
// `config_offline` however has paid 1 sig due to being both authority
|
// `config_offline` however has paid 1 sig due to being both authority
|
||||||
// and fee payer
|
// and fee payer
|
||||||
check_recent_balance(100_000 - SIG_FEE, &rpc_client, &offline_pubkey);
|
check_balance(100_000 - SIG_FEE, &rpc_client, &offline_pubkey);
|
||||||
|
|
||||||
server.close().unwrap();
|
server.close().unwrap();
|
||||||
remove_dir_all(ledger_path).unwrap();
|
remove_dir_all(ledger_path).unwrap();
|
||||||
@@ -999,11 +953,11 @@ fn test_stake_split() {
|
|||||||
let default_signer = Keypair::new();
|
let default_signer = Keypair::new();
|
||||||
let offline_signer = Keypair::new();
|
let offline_signer = Keypair::new();
|
||||||
|
|
||||||
let mut config = CliConfig::recent_for_tests();
|
let mut config = CliConfig::default();
|
||||||
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
config.signers = vec![&default_signer];
|
config.signers = vec![&default_signer];
|
||||||
|
|
||||||
let mut config_offline = CliConfig::recent_for_tests();
|
let mut config_offline = CliConfig::default();
|
||||||
config_offline.json_rpc_url = String::default();
|
config_offline.json_rpc_url = String::default();
|
||||||
config_offline.signers = vec![&offline_signer];
|
config_offline.signers = vec![&offline_signer];
|
||||||
let offline_pubkey = config_offline.signers[0].pubkey();
|
let offline_pubkey = config_offline.signers[0].pubkey();
|
||||||
@@ -1019,11 +973,11 @@ fn test_stake_split() {
|
|||||||
&config,
|
&config,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_recent_balance(500_000, &rpc_client, &config.signers[0].pubkey());
|
check_balance(500_000, &rpc_client, &config.signers[0].pubkey());
|
||||||
|
|
||||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 100_000, &config)
|
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 100_000, &config)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_recent_balance(100_000, &rpc_client, &offline_pubkey);
|
check_balance(100_000, &rpc_client, &offline_pubkey);
|
||||||
|
|
||||||
// Create stake account, identity is authority
|
// Create stake account, identity is authority
|
||||||
let minimum_stake_balance = rpc_client
|
let minimum_stake_balance = rpc_client
|
||||||
@@ -1047,7 +1001,7 @@ fn test_stake_split() {
|
|||||||
from: 0,
|
from: 0,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
check_recent_balance(
|
check_balance(
|
||||||
10 * minimum_stake_balance,
|
10 * minimum_stake_balance,
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
&stake_account_pubkey,
|
&stake_account_pubkey,
|
||||||
@@ -1066,21 +1020,17 @@ fn test_stake_split() {
|
|||||||
amount: SpendAmount::Some(minimum_nonce_balance),
|
amount: SpendAmount::Some(minimum_nonce_balance),
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
check_recent_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey());
|
check_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey());
|
||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let nonce_hash = nonce::get_account_with_commitment(
|
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||||
&rpc_client,
|
|
||||||
&nonce_account.pubkey(),
|
|
||||||
CommitmentConfig::recent(),
|
|
||||||
)
|
|
||||||
.and_then(|ref a| nonce::data_from_account(a))
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.blockhash;
|
.blockhash;
|
||||||
|
|
||||||
// Nonced offline split
|
// Nonced offline split
|
||||||
let split_account = keypair_from_seed(&[2u8; 32]).unwrap();
|
let split_account = keypair_from_seed(&[2u8; 32]).unwrap();
|
||||||
check_recent_balance(0, &rpc_client, &split_account.pubkey());
|
check_balance(0, &rpc_client, &split_account.pubkey());
|
||||||
config_offline.signers.push(&split_account);
|
config_offline.signers.push(&split_account);
|
||||||
config_offline.command = CliCommand::SplitStake {
|
config_offline.command = CliCommand::SplitStake {
|
||||||
stake_account_pubkey,
|
stake_account_pubkey,
|
||||||
@@ -1116,12 +1066,12 @@ fn test_stake_split() {
|
|||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
check_recent_balance(
|
check_balance(
|
||||||
8 * minimum_stake_balance,
|
8 * minimum_stake_balance,
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
&stake_account_pubkey,
|
&stake_account_pubkey,
|
||||||
);
|
);
|
||||||
check_recent_balance(
|
check_balance(
|
||||||
2 * minimum_stake_balance,
|
2 * minimum_stake_balance,
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
&split_account.pubkey(),
|
&split_account.pubkey(),
|
||||||
@@ -1154,11 +1104,11 @@ fn test_stake_set_lockup() {
|
|||||||
let default_signer = Keypair::new();
|
let default_signer = Keypair::new();
|
||||||
let offline_signer = Keypair::new();
|
let offline_signer = Keypair::new();
|
||||||
|
|
||||||
let mut config = CliConfig::recent_for_tests();
|
let mut config = CliConfig::default();
|
||||||
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
config.signers = vec![&default_signer];
|
config.signers = vec![&default_signer];
|
||||||
|
|
||||||
let mut config_offline = CliConfig::recent_for_tests();
|
let mut config_offline = CliConfig::default();
|
||||||
config_offline.json_rpc_url = String::default();
|
config_offline.json_rpc_url = String::default();
|
||||||
config_offline.signers = vec![&offline_signer];
|
config_offline.signers = vec![&offline_signer];
|
||||||
let offline_pubkey = config_offline.signers[0].pubkey();
|
let offline_pubkey = config_offline.signers[0].pubkey();
|
||||||
@@ -1174,11 +1124,11 @@ fn test_stake_set_lockup() {
|
|||||||
&config,
|
&config,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_recent_balance(500_000, &rpc_client, &config.signers[0].pubkey());
|
check_balance(500_000, &rpc_client, &config.signers[0].pubkey());
|
||||||
|
|
||||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 100_000, &config)
|
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 100_000, &config)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_recent_balance(100_000, &rpc_client, &offline_pubkey);
|
check_balance(100_000, &rpc_client, &offline_pubkey);
|
||||||
|
|
||||||
// Create stake account, identity is authority
|
// Create stake account, identity is authority
|
||||||
let minimum_stake_balance = rpc_client
|
let minimum_stake_balance = rpc_client
|
||||||
@@ -1207,7 +1157,7 @@ fn test_stake_set_lockup() {
|
|||||||
from: 0,
|
from: 0,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
check_recent_balance(
|
check_balance(
|
||||||
10 * minimum_stake_balance,
|
10 * minimum_stake_balance,
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
&stake_account_pubkey,
|
&stake_account_pubkey,
|
||||||
@@ -1231,11 +1181,7 @@ fn test_stake_set_lockup() {
|
|||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
let stake_account = rpc_client
|
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||||
.get_account_with_commitment(&stake_account_pubkey, CommitmentConfig::recent())
|
|
||||||
.unwrap()
|
|
||||||
.value
|
|
||||||
.unwrap();
|
|
||||||
let stake_state: StakeState = stake_account.state().unwrap();
|
let stake_state: StakeState = stake_account.state().unwrap();
|
||||||
let current_lockup = match stake_state {
|
let current_lockup = match stake_state {
|
||||||
StakeState::Initialized(meta) => meta.lockup,
|
StakeState::Initialized(meta) => meta.lockup,
|
||||||
@@ -1286,11 +1232,7 @@ fn test_stake_set_lockup() {
|
|||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
let stake_account = rpc_client
|
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||||
.get_account_with_commitment(&stake_account_pubkey, CommitmentConfig::recent())
|
|
||||||
.unwrap()
|
|
||||||
.value
|
|
||||||
.unwrap();
|
|
||||||
let stake_state: StakeState = stake_account.state().unwrap();
|
let stake_state: StakeState = stake_account.state().unwrap();
|
||||||
let current_lockup = match stake_state {
|
let current_lockup = match stake_state {
|
||||||
StakeState::Initialized(meta) => meta.lockup,
|
StakeState::Initialized(meta) => meta.lockup,
|
||||||
@@ -1335,14 +1277,10 @@ fn test_stake_set_lockup() {
|
|||||||
amount: SpendAmount::Some(minimum_nonce_balance),
|
amount: SpendAmount::Some(minimum_nonce_balance),
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
check_recent_balance(minimum_nonce_balance, &rpc_client, &nonce_account_pubkey);
|
check_balance(minimum_nonce_balance, &rpc_client, &nonce_account_pubkey);
|
||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let nonce_hash = nonce::get_account_with_commitment(
|
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||||
&rpc_client,
|
|
||||||
&nonce_account.pubkey(),
|
|
||||||
CommitmentConfig::recent(),
|
|
||||||
)
|
|
||||||
.and_then(|ref a| nonce::data_from_account(a))
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.blockhash;
|
.blockhash;
|
||||||
@@ -1383,11 +1321,7 @@ fn test_stake_set_lockup() {
|
|||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
let stake_account = rpc_client
|
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||||
.get_account_with_commitment(&stake_account_pubkey, CommitmentConfig::recent())
|
|
||||||
.unwrap()
|
|
||||||
.value
|
|
||||||
.unwrap();
|
|
||||||
let stake_state: StakeState = stake_account.state().unwrap();
|
let stake_state: StakeState = stake_account.state().unwrap();
|
||||||
let current_lockup = match stake_state {
|
let current_lockup = match stake_state {
|
||||||
StakeState::Initialized(meta) => meta.lockup,
|
StakeState::Initialized(meta) => meta.lockup,
|
||||||
@@ -1421,12 +1355,12 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
|||||||
|
|
||||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||||
|
|
||||||
let mut config = CliConfig::recent_for_tests();
|
let mut config = CliConfig::default();
|
||||||
let default_signer = keypair_from_seed(&[1u8; 32]).unwrap();
|
let default_signer = keypair_from_seed(&[1u8; 32]).unwrap();
|
||||||
config.signers = vec![&default_signer];
|
config.signers = vec![&default_signer];
|
||||||
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
|
|
||||||
let mut config_offline = CliConfig::recent_for_tests();
|
let mut config_offline = CliConfig::default();
|
||||||
let offline_signer = keypair_from_seed(&[2u8; 32]).unwrap();
|
let offline_signer = keypair_from_seed(&[2u8; 32]).unwrap();
|
||||||
config_offline.signers = vec![&offline_signer];
|
config_offline.signers = vec![&offline_signer];
|
||||||
let offline_pubkey = config_offline.signers[0].pubkey();
|
let offline_pubkey = config_offline.signers[0].pubkey();
|
||||||
@@ -1443,11 +1377,11 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
|||||||
&config,
|
&config,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_recent_balance(200_000, &rpc_client, &config.signers[0].pubkey());
|
check_balance(200_000, &rpc_client, &config.signers[0].pubkey());
|
||||||
|
|
||||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 100_000, &config)
|
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 100_000, &config)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_recent_balance(100_000, &rpc_client, &offline_pubkey);
|
check_balance(100_000, &rpc_client, &offline_pubkey);
|
||||||
|
|
||||||
// Create nonce account
|
// Create nonce account
|
||||||
let minimum_nonce_balance = rpc_client
|
let minimum_nonce_balance = rpc_client
|
||||||
@@ -1465,11 +1399,7 @@ 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::get_account_with_commitment(
|
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||||
&rpc_client,
|
|
||||||
&nonce_account.pubkey(),
|
|
||||||
CommitmentConfig::recent(),
|
|
||||||
)
|
|
||||||
.and_then(|ref a| nonce::data_from_account(a))
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.blockhash;
|
.blockhash;
|
||||||
@@ -1517,14 +1447,10 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
|||||||
from: 0,
|
from: 0,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
check_recent_balance(50_000, &rpc_client, &stake_pubkey);
|
check_balance(50_000, &rpc_client, &stake_pubkey);
|
||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let nonce_hash = nonce::get_account_with_commitment(
|
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||||
&rpc_client,
|
|
||||||
&nonce_account.pubkey(),
|
|
||||||
CommitmentConfig::recent(),
|
|
||||||
)
|
|
||||||
.and_then(|ref a| nonce::data_from_account(a))
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.blockhash;
|
.blockhash;
|
||||||
@@ -1565,14 +1491,10 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
|||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
check_recent_balance(42, &rpc_client, &recipient_pubkey);
|
check_balance(42, &rpc_client, &recipient_pubkey);
|
||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let nonce_hash = nonce::get_account_with_commitment(
|
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||||
&rpc_client,
|
|
||||||
&nonce_account.pubkey(),
|
|
||||||
CommitmentConfig::recent(),
|
|
||||||
)
|
|
||||||
.and_then(|ref a| nonce::data_from_account(a))
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.blockhash;
|
.blockhash;
|
||||||
@@ -1619,7 +1541,7 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
|||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
let seed_address =
|
let seed_address =
|
||||||
Pubkey::create_with_seed(&stake_pubkey, seed, &solana_stake_program::id()).unwrap();
|
Pubkey::create_with_seed(&stake_pubkey, seed, &solana_stake_program::id()).unwrap();
|
||||||
check_recent_balance(50_000, &rpc_client, &seed_address);
|
check_balance(50_000, &rpc_client, &seed_address);
|
||||||
|
|
||||||
server.close().unwrap();
|
server.close().unwrap();
|
||||||
remove_dir_all(ledger_path).unwrap();
|
remove_dir_all(ledger_path).unwrap();
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use solana_cli::test_utils::check_balance;
|
||||||
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,
|
cli_output::OutputFormat,
|
||||||
@@ -7,13 +8,11 @@ use solana_cli::{
|
|||||||
parse_sign_only_reply_string,
|
parse_sign_only_reply_string,
|
||||||
},
|
},
|
||||||
spend_utils::SpendAmount,
|
spend_utils::SpendAmount,
|
||||||
test_utils::{check_ready, check_recent_balance},
|
|
||||||
};
|
};
|
||||||
use solana_client::rpc_client::RpcClient;
|
use solana_client::rpc_client::RpcClient;
|
||||||
use solana_core::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,
|
|
||||||
nonce::State as NonceState,
|
nonce::State as NonceState,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{keypair_from_seed, Keypair, NullSigner, Signer},
|
signature::{keypair_from_seed, Keypair, NullSigner, Signer},
|
||||||
@@ -43,7 +42,7 @@ fn test_transfer() {
|
|||||||
let default_signer = Keypair::new();
|
let default_signer = Keypair::new();
|
||||||
let default_offline_signer = Keypair::new();
|
let default_offline_signer = Keypair::new();
|
||||||
|
|
||||||
let mut config = CliConfig::recent_for_tests();
|
let mut config = CliConfig::default();
|
||||||
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
config.signers = vec![&default_signer];
|
config.signers = vec![&default_signer];
|
||||||
|
|
||||||
@@ -52,10 +51,8 @@ fn test_transfer() {
|
|||||||
|
|
||||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &sender_pubkey, 50_000, &config)
|
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &sender_pubkey, 50_000, &config)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_recent_balance(50_000, &rpc_client, &sender_pubkey);
|
check_balance(50_000, &rpc_client, &sender_pubkey);
|
||||||
check_recent_balance(0, &rpc_client, &recipient_pubkey);
|
check_balance(0, &rpc_client, &recipient_pubkey);
|
||||||
|
|
||||||
check_ready(&rpc_client);
|
|
||||||
|
|
||||||
// Plain ole transfer
|
// Plain ole transfer
|
||||||
config.command = CliCommand::Transfer {
|
config.command = CliCommand::Transfer {
|
||||||
@@ -70,8 +67,8 @@ fn test_transfer() {
|
|||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
check_recent_balance(49_989, &rpc_client, &sender_pubkey);
|
check_balance(49_989, &rpc_client, &sender_pubkey);
|
||||||
check_recent_balance(10, &rpc_client, &recipient_pubkey);
|
check_balance(10, &rpc_client, &recipient_pubkey);
|
||||||
|
|
||||||
// Plain ole transfer, failure due to InsufficientFundsForSpendAndFee
|
// Plain ole transfer, failure due to InsufficientFundsForSpendAndFee
|
||||||
config.command = CliCommand::Transfer {
|
config.command = CliCommand::Transfer {
|
||||||
@@ -86,10 +83,10 @@ fn test_transfer() {
|
|||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
};
|
};
|
||||||
assert!(process_command(&config).is_err());
|
assert!(process_command(&config).is_err());
|
||||||
check_recent_balance(49_989, &rpc_client, &sender_pubkey);
|
check_balance(49_989, &rpc_client, &sender_pubkey);
|
||||||
check_recent_balance(10, &rpc_client, &recipient_pubkey);
|
check_balance(10, &rpc_client, &recipient_pubkey);
|
||||||
|
|
||||||
let mut offline = CliConfig::recent_for_tests();
|
let mut offline = CliConfig::default();
|
||||||
offline.json_rpc_url = String::default();
|
offline.json_rpc_url = String::default();
|
||||||
offline.signers = vec![&default_offline_signer];
|
offline.signers = vec![&default_offline_signer];
|
||||||
// Verify we cannot contact the cluster
|
// Verify we cannot contact the cluster
|
||||||
@@ -98,13 +95,10 @@ fn test_transfer() {
|
|||||||
|
|
||||||
let offline_pubkey = offline.signers[0].pubkey();
|
let offline_pubkey = offline.signers[0].pubkey();
|
||||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 50, &config).unwrap();
|
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 50, &config).unwrap();
|
||||||
check_recent_balance(50, &rpc_client, &offline_pubkey);
|
check_balance(50, &rpc_client, &offline_pubkey);
|
||||||
|
|
||||||
// Offline transfer
|
// Offline transfer
|
||||||
let (blockhash, _, _) = rpc_client
|
let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap();
|
||||||
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
|
|
||||||
.unwrap()
|
|
||||||
.value;
|
|
||||||
offline.command = CliCommand::Transfer {
|
offline.command = CliCommand::Transfer {
|
||||||
amount: SpendAmount::Some(10),
|
amount: SpendAmount::Some(10),
|
||||||
to: recipient_pubkey,
|
to: recipient_pubkey,
|
||||||
@@ -134,8 +128,8 @@ fn test_transfer() {
|
|||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
check_recent_balance(39, &rpc_client, &offline_pubkey);
|
check_balance(39, &rpc_client, &offline_pubkey);
|
||||||
check_recent_balance(20, &rpc_client, &recipient_pubkey);
|
check_balance(20, &rpc_client, &recipient_pubkey);
|
||||||
|
|
||||||
// Create nonce account
|
// Create nonce account
|
||||||
let nonce_account = keypair_from_seed(&[3u8; 32]).unwrap();
|
let nonce_account = keypair_from_seed(&[3u8; 32]).unwrap();
|
||||||
@@ -150,14 +144,10 @@ fn test_transfer() {
|
|||||||
amount: SpendAmount::Some(minimum_nonce_balance),
|
amount: SpendAmount::Some(minimum_nonce_balance),
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
check_recent_balance(49_987 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
|
check_balance(49_987 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
|
||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let nonce_hash = nonce::get_account_with_commitment(
|
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||||
&rpc_client,
|
|
||||||
&nonce_account.pubkey(),
|
|
||||||
CommitmentConfig::recent(),
|
|
||||||
)
|
|
||||||
.and_then(|ref a| nonce::data_from_account(a))
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.blockhash;
|
.blockhash;
|
||||||
@@ -179,13 +169,9 @@ fn test_transfer() {
|
|||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
check_recent_balance(49_976 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
|
check_balance(49_976 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
|
||||||
check_recent_balance(30, &rpc_client, &recipient_pubkey);
|
check_balance(30, &rpc_client, &recipient_pubkey);
|
||||||
let new_nonce_hash = nonce::get_account_with_commitment(
|
let new_nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||||
&rpc_client,
|
|
||||||
&nonce_account.pubkey(),
|
|
||||||
CommitmentConfig::recent(),
|
|
||||||
)
|
|
||||||
.and_then(|ref a| nonce::data_from_account(a))
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.blockhash;
|
.blockhash;
|
||||||
@@ -199,14 +185,10 @@ fn test_transfer() {
|
|||||||
new_authority: offline_pubkey,
|
new_authority: offline_pubkey,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
check_recent_balance(49_975 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
|
check_balance(49_975 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
|
||||||
|
|
||||||
// Fetch nonce hash
|
// Fetch nonce hash
|
||||||
let nonce_hash = nonce::get_account_with_commitment(
|
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||||
&rpc_client,
|
|
||||||
&nonce_account.pubkey(),
|
|
||||||
CommitmentConfig::recent(),
|
|
||||||
)
|
|
||||||
.and_then(|ref a| nonce::data_from_account(a))
|
.and_then(|ref a| nonce::data_from_account(a))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.blockhash;
|
.blockhash;
|
||||||
@@ -244,8 +226,8 @@ fn test_transfer() {
|
|||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
check_recent_balance(28, &rpc_client, &offline_pubkey);
|
check_balance(28, &rpc_client, &offline_pubkey);
|
||||||
check_recent_balance(40, &rpc_client, &recipient_pubkey);
|
check_balance(40, &rpc_client, &recipient_pubkey);
|
||||||
|
|
||||||
server.close().unwrap();
|
server.close().unwrap();
|
||||||
remove_dir_all(ledger_path).unwrap();
|
remove_dir_all(ledger_path).unwrap();
|
||||||
@@ -273,7 +255,7 @@ fn test_transfer_multisession_signing() {
|
|||||||
let offline_from_signer = keypair_from_seed(&[2u8; 32]).unwrap();
|
let offline_from_signer = keypair_from_seed(&[2u8; 32]).unwrap();
|
||||||
let offline_fee_payer_signer = keypair_from_seed(&[3u8; 32]).unwrap();
|
let offline_fee_payer_signer = keypair_from_seed(&[3u8; 32]).unwrap();
|
||||||
let from_null_signer = NullSigner::new(&offline_from_signer.pubkey());
|
let from_null_signer = NullSigner::new(&offline_from_signer.pubkey());
|
||||||
let config = CliConfig::recent_for_tests();
|
let config = CliConfig::default();
|
||||||
|
|
||||||
// Setup accounts
|
// Setup accounts
|
||||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||||
@@ -293,19 +275,14 @@ fn test_transfer_multisession_signing() {
|
|||||||
&config,
|
&config,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_recent_balance(43, &rpc_client, &offline_from_signer.pubkey());
|
check_balance(43, &rpc_client, &offline_from_signer.pubkey());
|
||||||
check_recent_balance(3, &rpc_client, &offline_fee_payer_signer.pubkey());
|
check_balance(3, &rpc_client, &offline_fee_payer_signer.pubkey());
|
||||||
check_recent_balance(0, &rpc_client, &to_pubkey);
|
check_balance(0, &rpc_client, &to_pubkey);
|
||||||
|
|
||||||
check_ready(&rpc_client);
|
let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap();
|
||||||
|
|
||||||
let (blockhash, _, _) = rpc_client
|
|
||||||
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
|
|
||||||
.unwrap()
|
|
||||||
.value;
|
|
||||||
|
|
||||||
// Offline fee-payer signs first
|
// Offline fee-payer signs first
|
||||||
let mut fee_payer_config = CliConfig::recent_for_tests();
|
let mut fee_payer_config = CliConfig::default();
|
||||||
fee_payer_config.json_rpc_url = String::default();
|
fee_payer_config.json_rpc_url = String::default();
|
||||||
fee_payer_config.signers = vec![&offline_fee_payer_signer, &from_null_signer];
|
fee_payer_config.signers = vec![&offline_fee_payer_signer, &from_null_signer];
|
||||||
// Verify we cannot contact the cluster
|
// Verify we cannot contact the cluster
|
||||||
@@ -331,7 +308,7 @@ fn test_transfer_multisession_signing() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Now the offline fund source
|
// Now the offline fund source
|
||||||
let mut from_config = CliConfig::recent_for_tests();
|
let mut from_config = CliConfig::default();
|
||||||
from_config.json_rpc_url = String::default();
|
from_config.json_rpc_url = String::default();
|
||||||
from_config.signers = vec![&fee_payer_presigner, &offline_from_signer];
|
from_config.signers = vec![&fee_payer_presigner, &offline_from_signer];
|
||||||
// Verify we cannot contact the cluster
|
// Verify we cannot contact the cluster
|
||||||
@@ -357,7 +334,7 @@ fn test_transfer_multisession_signing() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Finally submit to the cluster
|
// Finally submit to the cluster
|
||||||
let mut config = CliConfig::recent_for_tests();
|
let mut config = CliConfig::default();
|
||||||
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
config.signers = vec![&fee_payer_presigner, &from_presigner];
|
config.signers = vec![&fee_payer_presigner, &from_presigner];
|
||||||
config.command = CliCommand::Transfer {
|
config.command = CliCommand::Transfer {
|
||||||
@@ -373,9 +350,9 @@ fn test_transfer_multisession_signing() {
|
|||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
|
|
||||||
check_recent_balance(1, &rpc_client, &offline_from_signer.pubkey());
|
check_balance(1, &rpc_client, &offline_from_signer.pubkey());
|
||||||
check_recent_balance(1, &rpc_client, &offline_fee_payer_signer.pubkey());
|
check_balance(1, &rpc_client, &offline_fee_payer_signer.pubkey());
|
||||||
check_recent_balance(42, &rpc_client, &to_pubkey);
|
check_balance(42, &rpc_client, &to_pubkey);
|
||||||
|
|
||||||
server.close().unwrap();
|
server.close().unwrap();
|
||||||
remove_dir_all(ledger_path).unwrap();
|
remove_dir_all(ledger_path).unwrap();
|
||||||
@@ -403,7 +380,7 @@ fn test_transfer_all() {
|
|||||||
|
|
||||||
let default_signer = Keypair::new();
|
let default_signer = Keypair::new();
|
||||||
|
|
||||||
let mut config = CliConfig::recent_for_tests();
|
let mut config = CliConfig::default();
|
||||||
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
config.signers = vec![&default_signer];
|
config.signers = vec![&default_signer];
|
||||||
|
|
||||||
@@ -412,10 +389,8 @@ fn test_transfer_all() {
|
|||||||
|
|
||||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &sender_pubkey, 50_000, &config)
|
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &sender_pubkey, 50_000, &config)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_recent_balance(50_000, &rpc_client, &sender_pubkey);
|
check_balance(50_000, &rpc_client, &sender_pubkey);
|
||||||
check_recent_balance(0, &rpc_client, &recipient_pubkey);
|
check_balance(0, &rpc_client, &recipient_pubkey);
|
||||||
|
|
||||||
check_ready(&rpc_client);
|
|
||||||
|
|
||||||
// Plain ole transfer
|
// Plain ole transfer
|
||||||
config.command = CliCommand::Transfer {
|
config.command = CliCommand::Transfer {
|
||||||
@@ -430,8 +405,8 @@ fn test_transfer_all() {
|
|||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
check_recent_balance(0, &rpc_client, &sender_pubkey);
|
check_balance(0, &rpc_client, &sender_pubkey);
|
||||||
check_recent_balance(49_999, &rpc_client, &recipient_pubkey);
|
check_balance(49_999, &rpc_client, &recipient_pubkey);
|
||||||
|
|
||||||
server.close().unwrap();
|
server.close().unwrap();
|
||||||
remove_dir_all(ledger_path).unwrap();
|
remove_dir_all(ledger_path).unwrap();
|
||||||
|
|||||||
@@ -1,15 +1,10 @@
|
|||||||
use solana_cli::{
|
use solana_cli::cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig};
|
||||||
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
|
use solana_cli::test_utils::check_balance;
|
||||||
offline::{blockhash_query::BlockhashQuery, *},
|
|
||||||
spend_utils::SpendAmount,
|
|
||||||
test_utils::check_recent_balance,
|
|
||||||
};
|
|
||||||
use solana_client::rpc_client::RpcClient;
|
use solana_client::rpc_client::RpcClient;
|
||||||
use solana_core::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::{
|
||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
commitment_config::CommitmentConfig,
|
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{Keypair, Signer},
|
signature::{Keypair, Signer},
|
||||||
};
|
};
|
||||||
@@ -32,7 +27,7 @@ fn test_vote_authorize_and_withdraw() {
|
|||||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||||
let default_signer = Keypair::new();
|
let default_signer = Keypair::new();
|
||||||
|
|
||||||
let mut config = CliConfig::recent_for_tests();
|
let mut config = CliConfig::default();
|
||||||
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
config.signers = vec![&default_signer];
|
config.signers = vec![&default_signer];
|
||||||
|
|
||||||
@@ -50,7 +45,6 @@ fn test_vote_authorize_and_withdraw() {
|
|||||||
let vote_account_pubkey = vote_account_keypair.pubkey();
|
let vote_account_pubkey = vote_account_keypair.pubkey();
|
||||||
config.signers = vec![&default_signer, &vote_account_keypair];
|
config.signers = vec![&default_signer, &vote_account_keypair];
|
||||||
config.command = CliCommand::CreateVoteAccount {
|
config.command = CliCommand::CreateVoteAccount {
|
||||||
vote_account: 1,
|
|
||||||
seed: None,
|
seed: None,
|
||||||
identity_account: 0,
|
identity_account: 0,
|
||||||
authorized_voter: None,
|
authorized_voter: None,
|
||||||
@@ -59,9 +53,7 @@ fn test_vote_authorize_and_withdraw() {
|
|||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
let vote_account = rpc_client
|
let vote_account = rpc_client
|
||||||
.get_account_with_commitment(&vote_account_keypair.pubkey(), CommitmentConfig::recent())
|
.get_account(&vote_account_keypair.pubkey())
|
||||||
.unwrap()
|
|
||||||
.value
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let vote_state: VoteStateVersions = vote_account.state().unwrap();
|
let vote_state: VoteStateVersions = vote_account.state().unwrap();
|
||||||
let authorized_withdrawer = vote_state.convert_to_current().authorized_withdrawer;
|
let authorized_withdrawer = vote_state.convert_to_current().authorized_withdrawer;
|
||||||
@@ -70,24 +62,7 @@ fn test_vote_authorize_and_withdraw() {
|
|||||||
.get_minimum_balance_for_rent_exemption(VoteState::size_of())
|
.get_minimum_balance_for_rent_exemption(VoteState::size_of())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.max(1);
|
.max(1);
|
||||||
check_recent_balance(expected_balance, &rpc_client, &vote_account_pubkey);
|
check_balance(expected_balance, &rpc_client, &vote_account_pubkey);
|
||||||
|
|
||||||
// Transfer in some more SOL
|
|
||||||
config.signers = vec![&default_signer];
|
|
||||||
config.command = CliCommand::Transfer {
|
|
||||||
amount: SpendAmount::Some(1_000),
|
|
||||||
to: vote_account_pubkey,
|
|
||||||
from: 0,
|
|
||||||
sign_only: false,
|
|
||||||
no_wait: false,
|
|
||||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
|
||||||
nonce_account: None,
|
|
||||||
nonce_authority: 0,
|
|
||||||
fee_payer: 0,
|
|
||||||
};
|
|
||||||
process_command(&config).unwrap();
|
|
||||||
let expected_balance = expected_balance + 1_000;
|
|
||||||
check_recent_balance(expected_balance, &rpc_client, &vote_account_pubkey);
|
|
||||||
|
|
||||||
// Authorize vote account withdrawal to another signer
|
// Authorize vote account withdrawal to another signer
|
||||||
let withdraw_authority = Keypair::new();
|
let withdraw_authority = Keypair::new();
|
||||||
@@ -99,9 +74,7 @@ fn test_vote_authorize_and_withdraw() {
|
|||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
let vote_account = rpc_client
|
let vote_account = rpc_client
|
||||||
.get_account_with_commitment(&vote_account_keypair.pubkey(), CommitmentConfig::recent())
|
.get_account(&vote_account_keypair.pubkey())
|
||||||
.unwrap()
|
|
||||||
.value
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let vote_state: VoteStateVersions = vote_account.state().unwrap();
|
let vote_state: VoteStateVersions = vote_account.state().unwrap();
|
||||||
let authorized_withdrawer = vote_state.convert_to_current().authorized_withdrawer;
|
let authorized_withdrawer = vote_state.convert_to_current().authorized_withdrawer;
|
||||||
@@ -113,12 +86,12 @@ fn test_vote_authorize_and_withdraw() {
|
|||||||
config.command = CliCommand::WithdrawFromVoteAccount {
|
config.command = CliCommand::WithdrawFromVoteAccount {
|
||||||
vote_account_pubkey,
|
vote_account_pubkey,
|
||||||
withdraw_authority: 1,
|
withdraw_authority: 1,
|
||||||
withdraw_amount: SpendAmount::Some(100),
|
lamports: 100,
|
||||||
destination_account_pubkey: destination_account,
|
destination_account_pubkey: destination_account,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
check_recent_balance(expected_balance - 100, &rpc_client, &vote_account_pubkey);
|
check_balance(expected_balance - 100, &rpc_client, &vote_account_pubkey);
|
||||||
check_recent_balance(100, &rpc_client, &destination_account);
|
check_balance(100, &rpc_client, &destination_account);
|
||||||
|
|
||||||
// Re-assign validator identity
|
// Re-assign validator identity
|
||||||
let new_identity_keypair = Keypair::new();
|
let new_identity_keypair = Keypair::new();
|
||||||
@@ -126,7 +99,6 @@ fn test_vote_authorize_and_withdraw() {
|
|||||||
config.command = CliCommand::VoteUpdateValidator {
|
config.command = CliCommand::VoteUpdateValidator {
|
||||||
vote_account_pubkey,
|
vote_account_pubkey,
|
||||||
new_identity_account: 2,
|
new_identity_account: 2,
|
||||||
withdraw_authority: 1,
|
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user