Compare commits
429 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
687254497c | ||
|
b8932ad0c8 | ||
|
1d4dbe0090 | ||
|
1f8e9c2364 | ||
|
ac07fb392d | ||
|
a492357964 | ||
|
e0bb26d9db | ||
|
d687dd9f13 | ||
|
4b649a71df | ||
|
388a285517 | ||
|
45c368d670 | ||
|
bf5609b4fc | ||
|
d8c0633f0f | ||
|
aebb8187a8 | ||
|
159722a960 | ||
|
c5b36cf18c | ||
|
fae90ff397 | ||
|
a91a106319 | ||
|
7c8debd14a | ||
|
9c64e5e3b1 | ||
|
e48ce1e682 | ||
|
2c239904cc | ||
|
9e459f0093 | ||
|
166251fccd | ||
|
ebbb106fb7 | ||
|
a018d78056 | ||
|
67daa6f01e | ||
|
d5aa648947 | ||
|
acfd72d7c4 | ||
|
096d9ce5c7 | ||
|
a8e522702c | ||
|
e3753186af | ||
|
82d9624736 | ||
|
6101c1d690 | ||
|
7b7b7be99c | ||
|
2b4af48537 | ||
|
2a842408bd | ||
|
755ccbc253 | ||
|
cf0de48dc0 | ||
|
d44058edc3 | ||
|
5496d52097 | ||
|
b4da705f97 | ||
|
75d334147c | ||
|
006d36dd95 | ||
|
8c9e17bbab | ||
|
42017ebe69 | ||
|
b34f179546 | ||
|
1afd1db4fc | ||
|
daba428a3d | ||
|
11f4443a7b | ||
|
a8eea4f42d | ||
|
18e7112608 | ||
|
857e44c147 | ||
|
bd0536c80f | ||
|
184e4253c7 | ||
|
5828d2cff7 | ||
|
eeb7503fb6 | ||
|
7e1aa02ce4 | ||
|
734e669581 | ||
|
81db361d77 | ||
|
97c3ff8a4f | ||
|
5f85ecd457 | ||
|
fb1985fe5e | ||
|
3296c13ef2 | ||
|
b8cc10749a | ||
|
c1f9a9a021 | ||
|
f88b0c4827 | ||
|
c3564203e9 | ||
|
de4e548105 | ||
|
8d67204123 | ||
|
99a01abfd7 | ||
|
973b00debb | ||
|
900139da3e | ||
|
d94ad7523b | ||
|
4e0dbd6a73 | ||
|
9ee69017dc | ||
|
7c956a87e5 | ||
|
0beb443d44 | ||
|
5d8ae9628c | ||
|
de76df0cbb | ||
|
a0190b4105 | ||
|
5255a6ebd2 | ||
|
8575514235 | ||
|
2a1946436b | ||
|
80649a9c3d | ||
|
ad74ba0eb0 | ||
|
fd41ad5d8f | ||
|
63855d5c2a | ||
|
00251f710e | ||
|
14bc623989 | ||
|
fb90fb3feb | ||
|
d6ca879d39 | ||
|
2dcde5281d | ||
|
e2626dad83 | ||
|
070fbeb69a | ||
|
497ec24754 | ||
|
1bda09bf0e | ||
|
7ca7f8604d | ||
|
e2b5f2dd9c | ||
|
3652bd57a9 | ||
|
5077d6bfb3 | ||
|
f0ee3e9deb | ||
|
babad39846 | ||
|
c15aa4a968 | ||
|
3124a88284 | ||
|
e76a2065e3 | ||
|
45f8e453a9 | ||
|
20f9c12855 | ||
|
4218414c87 | ||
|
60c91d386f | ||
|
e477501687 | ||
|
20463e141e | ||
|
e699462ed3 | ||
|
8b345f3258 | ||
|
56436a6271 | ||
|
805ea6f469 | ||
|
1db1d173fc | ||
|
11476038cd | ||
|
a669ef3abb | ||
|
dbbdfa1dbb | ||
|
768c6b4bef | ||
|
8bcc04c275 | ||
|
2fd822887f | ||
|
e2c8aa0847 | ||
|
9b049402c9 | ||
|
d0e1779893 | ||
|
929ffc5a4e | ||
|
1f63fb06f1 | ||
|
b49aa125c9 | ||
|
55836d133e | ||
|
277e402d55 | ||
|
0ab8312b23 | ||
|
bc4c5c5a97 | ||
|
1a9aa78129 | ||
|
798a6db915 | ||
|
0a4a3fd37e | ||
|
66242eab41 | ||
|
7f0d4f0656 | ||
|
acba8d6026 | ||
|
1ff9555099 | ||
|
72a13e2a72 | ||
|
74cdfc2213 | ||
|
7b8e5a9f47 | ||
|
80525ac862 | ||
|
c14f98c6fc | ||
|
c6edfc3944 | ||
|
b95c493d66 | ||
|
5871462241 | ||
|
53bb826375 | ||
|
c769bcc418 | ||
|
f06a4c7861 | ||
|
0cae099d12 | ||
|
4bc3653906 | ||
|
3e7050983a | ||
|
9f1bb75445 | ||
|
139bb32dba | ||
|
158f6f3725 | ||
|
e33f9ea6b5 | ||
|
473037db86 | ||
|
b0e14ea83c | ||
|
782a549613 | ||
|
c805f7dc4e | ||
|
782829152e | ||
|
da6f09afb8 | ||
|
004b1b9c3f | ||
|
2f8d0f88d6 | ||
|
177d241160 | ||
|
5323622842 | ||
|
c852923347 | ||
|
5dc4410d58 | ||
|
da4642d634 | ||
|
a264be1791 | ||
|
9aff121949 | ||
|
a7f4d1487a | ||
|
11e43e1654 | ||
|
82be47bc18 | ||
|
6498e4fbf6 | ||
|
9978971bd9 | ||
|
e28ac2c377 | ||
|
ef296aa7db | ||
|
43e7107f65 | ||
|
752fa29390 | ||
|
7bb7b42356 | ||
|
2a7fc744f9 | ||
|
90e3da0389 | ||
|
1a62bcee42 | ||
|
b83a4cae90 | ||
|
05ef21cd3b | ||
|
dfa27b04d7 | ||
|
880b04906e | ||
|
1fe0b1e516 | ||
|
f9fd4bd24c | ||
|
c55a11d160 | ||
|
92118de0e1 | ||
|
0d9802a2cd | ||
|
f6beede01b | ||
|
ff48ea20de | ||
|
dd9cb18d65 | ||
|
71932aed0a | ||
|
24dc6680e1 | ||
|
61d9d40e48 | ||
|
e9b40db319 | ||
|
316356861d | ||
|
e07c00710a | ||
|
bc47c80610 | ||
|
14baa511f0 | ||
|
e773faeb24 | ||
|
42847516a2 | ||
|
47e9a1ae4f | ||
|
549a154394 | ||
|
dca00d1bde | ||
|
45ce1b4f96 | ||
|
a9232c0633 | ||
|
3da254c745 | ||
|
9ba3ee9683 | ||
|
b0addba2a9 | ||
|
bb59525ff8 | ||
|
acd25124d4 | ||
|
d718ab2491 | ||
|
1860aacd1f | ||
|
d4bbb7f516 | ||
|
d1c0f4b4f1 | ||
|
b72b837ba2 | ||
|
fde85c96c0 | ||
|
121418dad2 | ||
|
f44f94fe23 | ||
|
55a4481022 | ||
|
e859ad37a8 | ||
|
1a28c7fc12 | ||
|
c706a07764 | ||
|
59568e5776 | ||
|
33ca8fa72a | ||
|
4bb66a81fb | ||
|
468c14b14f | ||
|
03e505897a | ||
|
5205eb382e | ||
|
b07b6e56fa | ||
|
bcc890e705 | ||
|
07d14f6f07 | ||
|
03b213e296 | ||
|
1bfce24c9f | ||
|
94b2565969 | ||
|
2896fdb603 | ||
|
50970bc8f9 | ||
|
10df45b173 | ||
|
d3b8129593 | ||
|
f7fb5aebac | ||
|
9311a6e356 | ||
|
8c706892df | ||
|
7f2b11756c | ||
|
f324547600 | ||
|
36e8977f1d | ||
|
b88db2689e | ||
|
1584ec220c | ||
|
fb366a7236 | ||
|
b903158543 | ||
|
9dad9c6333 | ||
|
a6658b9d75 | ||
|
a97feedcc1 | ||
|
8021bce41f | ||
|
d8fa19336c | ||
|
191483cf9f | ||
|
1eb8314d42 | ||
|
88eeb817e4 | ||
|
b777126bd2 | ||
|
89d78dcfcf | ||
|
1cf142c193 | ||
|
3e29325410 | ||
|
4dc98c3dbd | ||
|
9caad645e2 | ||
|
6cb76ac326 | ||
|
0001e5c0a1 | ||
|
ab32d13da1 | ||
|
cefe46e981 | ||
|
f4d70e78b6 | ||
|
d130adf582 | ||
|
1e6285e64e | ||
|
e3c90c3807 | ||
|
85750307aa | ||
|
0ee4a5e799 | ||
|
55cb9cf681 | ||
|
d3af7e0653 | ||
|
729a24d557 | ||
|
55b92c16da | ||
|
835bacce4f | ||
|
ccb7b1a698 | ||
|
85dbdeb4c3 | ||
|
397f9f11c5 | ||
|
a11986ad1d | ||
|
a4d373f0af | ||
|
52eea215ce | ||
|
6f48aafd3a | ||
|
2d94c09aee | ||
|
9699b61679 | ||
|
8865bfbd59 | ||
|
5f80c1d37d | ||
|
f616f5dec6 | ||
|
db1003b5f8 | ||
|
f52ff777b7 | ||
|
4314a29953 | ||
|
e560fff840 | ||
|
5ac747ea7d | ||
|
f522dc1e18 | ||
|
486812bf54 | ||
|
7df8f76df1 | ||
|
bbe4990e80 | ||
|
a5baaf790d | ||
|
0a36ed1b8c | ||
|
b7ad240375 | ||
|
2cc71f2d55 | ||
|
3125c74681 | ||
|
d5b1dee8d6 | ||
|
4b33a2a1b8 | ||
|
58e6a5c281 | ||
|
7eb61074ab | ||
|
9b2edbaa9b | ||
|
e8659b45c7 | ||
|
a9553cb401 | ||
|
800c409698 | ||
|
b6f484ddee | ||
|
3c39fee5a8 | ||
|
560f34d1f6 | ||
|
dbda50941a | ||
|
f1e68ac25c | ||
|
95029b9b05 | ||
|
a789bf4761 | ||
|
d2e7ffa8b9 | ||
|
0914519f6a | ||
|
43cd5f3730 | ||
|
d396a5f45a | ||
|
76a7071dba | ||
|
133baa8ce6 | ||
|
5df3510fde | ||
|
357339273f | ||
|
2500881e0b | ||
|
0013bfff4e | ||
|
f13498b428 | ||
|
b567138170 | ||
|
653982cae5 | ||
|
605f4906ba | ||
|
d27f24e312 | ||
|
c9c1cb5c9c | ||
|
1cc6493ccf | ||
|
ae47862be2 | ||
|
8590184df7 | ||
|
d840bbab08 | ||
|
63314de516 | ||
|
c47a6e12c7 | ||
|
7937c45ba4 | ||
|
813b11ac56 | ||
|
ad6883b66a | ||
|
a8f4c4e297 | ||
|
6d68e94e4e | ||
|
5dd40d7d88 | ||
|
3f58177670 | ||
|
edfd65b115 | ||
|
51da66ec84 | ||
|
ba36308d69 | ||
|
ee450b2dd0 | ||
|
84b28fb261 | ||
|
1586b86797 | ||
|
8f065e487e | ||
|
953eadd983 | ||
|
a4a792facd | ||
|
055f808f98 | ||
|
0404878445 | ||
|
053907f8a4 | ||
|
f76dcc1f05 | ||
|
823bc138cd | ||
|
18f746b025 | ||
|
c81adaf901 | ||
|
2d12ddd0f6 | ||
|
bee36cc8d0 | ||
|
f7aee67023 | ||
|
c021727009 | ||
|
6653136e1d | ||
|
06c40c807c | ||
|
9b262b4915 | ||
|
cc2d3ecfd7 | ||
|
92743499bf | ||
|
aa6a00a03e | ||
|
bd19f7c4cb | ||
|
988bf65ba4 | ||
|
d5b03bd824 | ||
|
6a72dab111 | ||
|
56e8319a6d | ||
|
aed1e51ef1 | ||
|
f4278d61df | ||
|
a5c3ae3cef | ||
|
05c052e212 | ||
|
dc05bb648a | ||
|
800b65b2f6 | ||
|
ae1a0f57c5 | ||
|
df7c44bd0c | ||
|
3e29cfd712 | ||
|
202031538f | ||
|
29ff1b925d | ||
|
5a91db6e62 | ||
|
94ba700e58 | ||
|
1964c6ec29 | ||
|
4dd6591bfd | ||
|
163217815b | ||
|
37c182cd5d | ||
|
0c68f27ac3 | ||
|
5fb8da9b35 | ||
|
74d9fd1e4f | ||
|
e71206c578 | ||
|
0141c80238 | ||
|
ed928cfdf7 | ||
|
2fd319ab7a | ||
|
7813a1decd | ||
|
93e4ed1f75 | ||
|
a70f31b3da | ||
|
2d25227d0a | ||
|
fc7bfd0f67 | ||
|
2996291b37 | ||
|
3e80b9231c | ||
|
78231a8682 | ||
|
ace711e7f1 | ||
|
c9cbc39ec9 | ||
|
606a392d50 | ||
|
c67596ceb4 | ||
|
9a42cc7555 | ||
|
2e5ef2a802 | ||
|
8c8e2c4b2b | ||
|
0578801f99 | ||
|
6141e1410a | ||
|
4fc86807ff | ||
|
d2a2eba69e |
@@ -3,16 +3,19 @@
|
||||
#
|
||||
# Save target/ for the next CI build on this machine
|
||||
#
|
||||
(
|
||||
set -x
|
||||
d=$HOME/cargo-target-cache/"$BUILDKITE_LABEL"
|
||||
mkdir -p "$d"
|
||||
set -x
|
||||
rsync -a --delete --link-dest="$PWD" target "$d"
|
||||
du -hs "$d"
|
||||
read -r cacheSizeInGB _ < <(du -s --block-size=1800000000 "$d")
|
||||
echo "--- ${cacheSizeInGB}GB: $d"
|
||||
)
|
||||
if [[ -z $CARGO_TARGET_CACHE ]]; then
|
||||
echo "+++ CARGO_TARGET_CACHE not defined" # pre-command should have defined it
|
||||
else
|
||||
(
|
||||
set -x
|
||||
mkdir -p "$CARGO_TARGET_CACHE"
|
||||
set -x
|
||||
rsync -a --delete --link-dest="$PWD" target "$CARGO_TARGET_CACHE"
|
||||
du -hs "$CARGO_TARGET_CACHE"
|
||||
read -r cacheSizeInGB _ < <(du -s --block-size=1800000000 "$CARGO_TARGET_CACHE")
|
||||
echo "--- ${cacheSizeInGB}GB: $CARGO_TARGET_CACHE"
|
||||
)
|
||||
fi
|
||||
|
||||
#
|
||||
# Add job_stats data point
|
||||
|
@@ -11,23 +11,24 @@ export PS4="++"
|
||||
#
|
||||
# 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
|
||||
d=$HOME/cargo-target-cache/"$BUILDKITE_LABEL"
|
||||
MAX_CACHE_SIZE=18 # gigabytes
|
||||
|
||||
if [[ -d $d ]]; then
|
||||
du -hs "$d"
|
||||
read -r cacheSizeInGB _ < <(du -s --block-size=1800000000 "$d")
|
||||
echo "--- ${cacheSizeInGB}GB: $d"
|
||||
if [[ -d $CARGO_TARGET_CACHE ]]; then
|
||||
du -hs "$CARGO_TARGET_CACHE"
|
||||
read -r cacheSizeInGB _ < <(du -s --block-size=1800000000 "$CARGO_TARGET_CACHE")
|
||||
echo "--- ${cacheSizeInGB}GB: $CARGO_TARGET_CACHE"
|
||||
if [[ $cacheSizeInGB -gt $MAX_CACHE_SIZE ]]; then
|
||||
echo "--- $d is too large, removing it"
|
||||
rm -rf "$d"
|
||||
echo "--- $CARGO_TARGET_CACHE is too large, removing it"
|
||||
rm -rf "$CARGO_TARGET_CACHE"
|
||||
fi
|
||||
else
|
||||
echo "--- $d not present"
|
||||
echo "--- $CARGO_TARGET_CACHE not present"
|
||||
fi
|
||||
|
||||
mkdir -p "$d"/target
|
||||
rsync -a --delete --link-dest="$d" "$d"/target .
|
||||
mkdir -p "$CARGO_TARGET_CACHE"/target
|
||||
rsync -a --delete --link-dest="$CARGO_TARGET_CACHE" "$CARGO_TARGET_CACHE"/target .
|
||||
)
|
||||
|
@@ -9,10 +9,23 @@
|
||||
|
||||
set -e
|
||||
cd "$(dirname "$0")"/..
|
||||
source ci/_
|
||||
|
||||
_ ci/buildkite-pipeline.sh pipeline.yml
|
||||
echo +++ pipeline
|
||||
cat pipeline.yml
|
||||
if [[ -n $BUILDKITE_TAG ]]; then
|
||||
buildkite-agent annotate --style info --context release-tag \
|
||||
"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
|
||||
|
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
|
||||
exemptLabels:
|
||||
- security
|
||||
- blocked
|
||||
|
||||
# Label to use when marking a pull request as stale
|
||||
staleLabel: stale
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -27,5 +27,3 @@ log-*/
|
||||
# fetch-spl.sh artifacts
|
||||
/spl-genesis-args.sh
|
||||
/spl_*.so
|
||||
|
||||
.DS_Store
|
||||
|
20
.mergify.yml
20
.mergify.yml
@@ -7,7 +7,7 @@ pull_request_rules:
|
||||
- name: automatic merge (squash) on CI success
|
||||
conditions:
|
||||
- status-success=buildkite/solana
|
||||
- status-success=Travis CI - Pull Request
|
||||
#- status-success=Travis CI - Pull Request
|
||||
- status-success=ci-gate
|
||||
- label=automerge
|
||||
- author≠@dont-squash-my-commits
|
||||
@@ -18,7 +18,7 @@ pull_request_rules:
|
||||
- name: automatic merge (rebase) on CI success
|
||||
conditions:
|
||||
- status-success=buildkite/solana
|
||||
- status-success=Travis CI - Pull Request
|
||||
#- status-success=Travis CI - Pull Request
|
||||
- status-success=ci-gate
|
||||
- label=automerge
|
||||
- author=@dont-squash-my-commits
|
||||
@@ -50,6 +50,14 @@ pull_request_rules:
|
||||
label:
|
||||
add:
|
||||
- automerge
|
||||
- name: v1.0 backport
|
||||
conditions:
|
||||
- label=v1.0
|
||||
actions:
|
||||
backport:
|
||||
ignore_conflicts: true
|
||||
branches:
|
||||
- v1.0
|
||||
- name: v1.1 backport
|
||||
conditions:
|
||||
- label=v1.1
|
||||
@@ -66,11 +74,3 @@ pull_request_rules:
|
||||
ignore_conflicts: true
|
||||
branches:
|
||||
- v1.2
|
||||
- name: v1.3 backport
|
||||
conditions:
|
||||
- label=v1.3
|
||||
actions:
|
||||
backport:
|
||||
ignore_conflicts: true
|
||||
branches:
|
||||
- v1.3
|
||||
|
65
.travis.yml
65
.travis.yml
@@ -4,10 +4,8 @@ branches:
|
||||
- /^v\d+\.\d+/
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
slack:
|
||||
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=
|
||||
|
||||
os: linux
|
||||
@@ -16,17 +14,8 @@ 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
|
||||
if: type = push
|
||||
name: "macOS release artifacts"
|
||||
os: osx
|
||||
language: rust
|
||||
@@ -58,58 +47,8 @@ jobs:
|
||||
- <<: *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
|
||||
# docs pull request or commit
|
||||
- name: "docs"
|
||||
if: type IN (push, pull_request) OR tag IS present
|
||||
language: node_js
|
||||
|
@@ -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"
|
1063
Cargo.lock
generated
1063
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,6 @@ members = [
|
||||
"bench-tps",
|
||||
"accounts-bench",
|
||||
"banking-bench",
|
||||
"clap-utils",
|
||||
"cli-config",
|
||||
"client",
|
||||
"core",
|
||||
@@ -32,9 +31,10 @@ members = [
|
||||
"metrics",
|
||||
"net-shaper",
|
||||
"notifier",
|
||||
"poh-bench",
|
||||
"programs/bpf_loader",
|
||||
"programs/budget",
|
||||
"programs/btc_spv",
|
||||
"programs/btc_spv_bin",
|
||||
"programs/config",
|
||||
"programs/exchange",
|
||||
"programs/failure",
|
||||
|
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");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
101
RELEASE.md
101
RELEASE.md
@@ -76,20 +76,21 @@ There are three release channels that map to branches as follows:
|
||||
git push -u origin <branchname>
|
||||
```
|
||||
|
||||
Alternatively use the Github UI.
|
||||
|
||||
### Update master branch to the next release minor version
|
||||
### Update master branch with the next 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:
|
||||
```
|
||||
$ scripts/increment-cargo-version.sh minor
|
||||
$ ./scripts/cargo-for-all-lock-files.sh update
|
||||
scripts/increment-cargo-version.sh minor
|
||||
```
|
||||
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:
|
||||
```
|
||||
git co -b version_update
|
||||
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
|
||||
```
|
||||
1. Confirm that your freshly cut release branch is shown as `BETA_CHANNEL` and the previous release branch as `STABLE_CHANNEL`:
|
||||
@@ -101,18 +102,15 @@ Alternatively use the Github UI.
|
||||
|
||||
### 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`
|
||||
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. 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
|
||||
template](https://raw.githubusercontent.com/solana-labs/solana/master/.github/RELEASE_TEMPLATE.md). Engineering Lead can provide summary contents for release notes if needed. If this is a patch release, review all the commits since the previous release on this branch and add details as needed.
|
||||
1. Click "Save Draft", then confirm the release notes look good and the tag name and branch are correct.
|
||||
1. Ensure 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.
|
||||
|
||||
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. Go back into edit the release and click "Publish release" when ready.
|
||||
|
||||
### Update release branch with the next patch version
|
||||
|
||||
@@ -121,31 +119,68 @@ Alternatively use the Github UI.
|
||||
$ 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:
|
||||
```
|
||||
git co -b version_update origin/vX.Y
|
||||
git add -u
|
||||
git commit -m 'Bump version to X.Y.Z+1'
|
||||
git push -u <user-remote> version_update
|
||||
git co -b version_update
|
||||
git ls-files -m | xargs git add
|
||||
git commit -m 'Update Cargo.toml versions from X.Y.Z to X.Y.Z+1'
|
||||
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
|
||||
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, then the uncheck **"This is a pre-release"** for the release.
|
||||
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.
|
||||
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
|
||||
appearing. To check for progress:
|
||||
* 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
|
||||
### Update documentation
|
||||
TODO: Documentation update procedure is WIP as we move to gitbook
|
||||
|
||||
[Crates.io](https://crates.io/crates/solana) should have an updated Solana version. This can take 2-3 hours, and sometimes fails in the `solana-secondary` job.
|
||||
If this happens and the error is non-fatal, click "Retry" on the "publish crate" job
|
||||
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.
|
||||
|
||||
### Update software on devnet.solana.com/testnet.solama.com/mainnet-beta.solana.com
|
||||
See the documentation at https://github.com/solana-labs/cluster-ops/
|
||||
### Update software on devnet.solana.com
|
||||
|
||||
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,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-account-decoder"
|
||||
version = "1.3.1"
|
||||
version = "1.2.29"
|
||||
description = "Solana account decoder"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
@@ -10,15 +10,19 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
bincode = "1.3.1"
|
||||
base64 = "0.12.3"
|
||||
bs58 = "0.3.1"
|
||||
bv = "0.11.1"
|
||||
Inflector = "0.11.4"
|
||||
lazy_static = "1.4.0"
|
||||
solana-sdk = { path = "../sdk", version = "1.3.1" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.3.1" }
|
||||
spl-token-v1-0 = { package = "spl-token", version = "1.0.6", features = ["skip-no-mangle"] }
|
||||
serde = "1.0.112"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.56"
|
||||
serde_json = "1.0.54"
|
||||
solana-config-program = { path = "../programs/config", version = "1.2.29" }
|
||||
solana-sdk = { path = "../sdk", version = "1.2.29" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "1.2.29" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.2.29" }
|
||||
spl-token-v2-0 = { package = "spl-token", version = "2.0.3", features = ["skip-no-mangle"] }
|
||||
thiserror = "1.0"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
|
@@ -4,12 +4,16 @@ extern crate lazy_static;
|
||||
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, pubkey::Pubkey};
|
||||
use solana_sdk::{account::Account, clock::Epoch, fee_calculator::FeeCalculator, pubkey::Pubkey};
|
||||
use std::str::FromStr;
|
||||
|
||||
pub type StringAmount = String;
|
||||
@@ -28,38 +32,47 @@ pub struct UiAccount {
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase", untagged)]
|
||||
pub enum UiAccountData {
|
||||
Binary(String),
|
||||
LegacyBinary(String), // Legacy. Retained for RPC backwards compatibility
|
||||
Json(ParsedAccount),
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for UiAccountData {
|
||||
fn from(data: Vec<u8>) -> Self {
|
||||
Self::Binary(bs58::encode(data).into_string())
|
||||
}
|
||||
Binary(String, UiAccountEncoding),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum UiAccountEncoding {
|
||||
Binary,
|
||||
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 => account.data.into(),
|
||||
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(&account.owner, &account.data, additional_data)
|
||||
parse_account_data(pubkey, &account.owner, &account.data, additional_data)
|
||||
{
|
||||
UiAccountData::Json(parsed_data)
|
||||
} else {
|
||||
account.data.into()
|
||||
UiAccountData::Binary(base64::encode(&account.data), UiAccountEncoding::Base64)
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -75,7 +88,12 @@ impl UiAccount {
|
||||
pub fn decode(&self) -> Option<Account> {
|
||||
let data = match &self.data {
|
||||
UiAccountData::Json(_) => None,
|
||||
UiAccountData::Binary(blob) => bs58::decode(blob).into_vec().ok(),
|
||||
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,
|
||||
@@ -86,3 +104,79 @@ impl UiAccount {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[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,22 +1,31 @@
|
||||
use crate::{
|
||||
parse_config::parse_config,
|
||||
parse_nonce::parse_nonce,
|
||||
parse_token::{parse_token, spl_token_id_v1_0},
|
||||
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};
|
||||
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 TOKEN_PROGRAM_ID: Pubkey = spl_token_id_v1_0();
|
||||
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
|
||||
};
|
||||
@@ -45,13 +54,17 @@ pub enum ParseAccountError {
|
||||
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,
|
||||
}
|
||||
|
||||
@@ -61,6 +74,7 @@ pub struct AccountAdditionalData {
|
||||
}
|
||||
|
||||
pub fn parse_account_data(
|
||||
pubkey: &Pubkey,
|
||||
program_id: &Pubkey,
|
||||
data: &[u8],
|
||||
additional_data: Option<AccountAdditionalData>,
|
||||
@@ -70,15 +84,19 @@ pub fn parse_account_data(
|
||||
.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,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -93,21 +111,35 @@ mod test {
|
||||
|
||||
#[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(&other_program, &data, None).is_err());
|
||||
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(&solana_vote_program::id(), &vote_account_data, None).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(&system_program::id(), &nonce_account_data, None).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);
|
||||
}
|
||||
}
|
||||
|
146
account-decoder/src/parse_config.rs
Normal file
146
account-decoder/src/parse_config.rs
Normal file
@@ -0,0 +1,146 @@
|
||||
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,6 +1,5 @@
|
||||
use crate::parse_account_data::ParseAccountError;
|
||||
use crate::{parse_account_data::ParseAccountError, UiFeeCalculator};
|
||||
use solana_sdk::{
|
||||
fee_calculator::FeeCalculator,
|
||||
instruction::InstructionError,
|
||||
nonce::{state::Versions, State},
|
||||
};
|
||||
@@ -14,7 +13,7 @@ pub fn parse_nonce(data: &[u8]) -> Result<UiNonceState, ParseAccountError> {
|
||||
State::Initialized(data) => Ok(UiNonceState::Initialized(UiNonceData {
|
||||
authority: data.authority.to_string(),
|
||||
blockhash: data.blockhash.to_string(),
|
||||
fee_calculator: data.fee_calculator,
|
||||
fee_calculator: data.fee_calculator.into(),
|
||||
})),
|
||||
}
|
||||
}
|
||||
@@ -32,7 +31,7 @@ pub enum UiNonceState {
|
||||
pub struct UiNonceData {
|
||||
pub authority: String,
|
||||
pub blockhash: String,
|
||||
pub fee_calculator: FeeCalculator,
|
||||
pub fee_calculator: UiFeeCalculator,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -56,7 +55,9 @@ mod test {
|
||||
UiNonceState::Initialized(UiNonceData {
|
||||
authority: Pubkey::default().to_string(),
|
||||
blockhash: Hash::default().to_string(),
|
||||
fee_calculator: FeeCalculator::default(),
|
||||
fee_calculator: UiFeeCalculator {
|
||||
lamports_per_signature: 0.to_string(),
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
|
236
account-decoder/src/parse_stake.rs
Normal file
236
account-decoder/src/parse_stake.rs
Normal file
@@ -0,0 +1,236 @@
|
||||
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")]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
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());
|
||||
}
|
||||
}
|
328
account-decoder/src/parse_sysvar.rs
Normal file
328
account-decoder/src/parse_sysvar.rs
Normal file
@@ -0,0 +1,328 @@
|
||||
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());
|
||||
}
|
||||
}
|
@@ -3,64 +3,83 @@ use crate::{
|
||||
StringAmount,
|
||||
};
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use spl_token_v1_0::{
|
||||
use spl_token_v2_0::{
|
||||
option::COption,
|
||||
pack::Pack,
|
||||
solana_sdk::pubkey::Pubkey as SplTokenPubkey,
|
||||
state::{unpack, Account, Mint, Multisig},
|
||||
state::{Account, AccountState, Mint, Multisig},
|
||||
};
|
||||
use std::{mem::size_of, str::FromStr};
|
||||
use std::str::FromStr;
|
||||
|
||||
// A helper function to convert spl_token_v1_0::id() as spl_sdk::pubkey::Pubkey to
|
||||
// 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_v1_0() -> Pubkey {
|
||||
Pubkey::from_str(&spl_token_v1_0::id().to_string()).unwrap()
|
||||
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_v1_0::native_mint::id() as spl_sdk::pubkey::Pubkey to
|
||||
// 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_v1_0_native_mint() -> Pubkey {
|
||||
Pubkey::from_str(&spl_token_v1_0::native_mint::id().to_string()).unwrap()
|
||||
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> {
|
||||
let mut data = data.to_vec();
|
||||
if data.len() == size_of::<Account>() {
|
||||
let account: Account = *unpack(&mut data)
|
||||
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(),
|
||||
)
|
||||
})?;
|
||||
let ui_token_amount = token_amount_to_ui_amount(account.amount, decimals);
|
||||
Ok(TokenAccountType::Account(UiTokenAccount {
|
||||
mint: account.mint.to_string(),
|
||||
owner: account.owner.to_string(),
|
||||
token_amount: ui_token_amount,
|
||||
token_amount: token_amount_to_ui_amount(account.amount, decimals),
|
||||
delegate: match account.delegate {
|
||||
COption::Some(pubkey) => Some(pubkey.to_string()),
|
||||
COption::None => None,
|
||||
},
|
||||
is_initialized: account.is_initialized,
|
||||
is_native: account.is_native,
|
||||
delegated_amount: account.delegated_amount,
|
||||
}))
|
||||
} else if data.len() == size_of::<Mint>() {
|
||||
let mint: Mint = *unpack(&mut data)
|
||||
.map_err(|_| ParseAccountError::AccountNotParsable(ParsableAccount::SplToken))?;
|
||||
Ok(TokenAccountType::Mint(UiMint {
|
||||
owner: match mint.owner {
|
||||
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() == size_of::<Multisig>() {
|
||||
let multisig: Multisig = *unpack(&mut data)
|
||||
} 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,
|
||||
@@ -99,10 +118,34 @@ 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 is_initialized: bool,
|
||||
pub state: UiAccountState,
|
||||
pub is_native: bool,
|
||||
pub delegated_amount: u64,
|
||||
#[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)]
|
||||
@@ -126,9 +169,11 @@ pub fn token_amount_to_ui_amount(amount: u64, decimals: u8) -> UiTokenAmount {
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UiMint {
|
||||
pub owner: Option<String>,
|
||||
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)]
|
||||
@@ -141,7 +186,7 @@ pub struct UiMultisig {
|
||||
}
|
||||
|
||||
pub fn get_token_account_mint(data: &[u8]) -> Option<Pubkey> {
|
||||
if data.len() == size_of::<Account>() {
|
||||
if data.len() == Account::get_packed_len() {
|
||||
Some(Pubkey::new(&data[0..32]))
|
||||
} else {
|
||||
None
|
||||
@@ -151,18 +196,23 @@ pub fn get_token_account_mint(data: &[u8]) -> Option<Pubkey> {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use spl_token_v1_0::state::unpack_unchecked;
|
||||
|
||||
#[test]
|
||||
fn test_parse_token() {
|
||||
let mint_pubkey = SplTokenPubkey::new(&[2; 32]);
|
||||
let owner_pubkey = SplTokenPubkey::new(&[3; 32]);
|
||||
let mut account_data = [0; size_of::<Account>()];
|
||||
let mut account: &mut Account = unpack_unchecked(&mut account_data).unwrap();
|
||||
account.mint = mint_pubkey;
|
||||
account.owner = owner_pubkey;
|
||||
account.amount = 42;
|
||||
account.is_initialized = true;
|
||||
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(),
|
||||
@@ -175,39 +225,52 @@ mod test {
|
||||
amount: "42".to_string()
|
||||
},
|
||||
delegate: None,
|
||||
is_initialized: true,
|
||||
state: UiAccountState::Initialized,
|
||||
is_native: false,
|
||||
delegated_amount: 0,
|
||||
rent_exempt_reserve: None,
|
||||
delegated_amount: None,
|
||||
close_authority: Some(owner_pubkey.to_string()),
|
||||
}),
|
||||
);
|
||||
|
||||
let mut mint_data = [0; size_of::<Mint>()];
|
||||
let mut mint: &mut Mint = unpack_unchecked(&mut mint_data).unwrap();
|
||||
mint.owner = COption::Some(owner_pubkey);
|
||||
mint.decimals = 3;
|
||||
mint.is_initialized = true;
|
||||
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 {
|
||||
owner: Some(owner_pubkey.to_string()),
|
||||
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 = [0; size_of::<Multisig>()];
|
||||
let mut multisig: &mut Multisig = unpack_unchecked(&mut multisig_data).unwrap();
|
||||
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.m = 2;
|
||||
multisig.n = 3;
|
||||
multisig.is_initialized = true;
|
||||
multisig.signers = signers;
|
||||
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 {
|
||||
@@ -229,9 +292,12 @@ mod test {
|
||||
#[test]
|
||||
fn test_get_token_account_mint() {
|
||||
let mint_pubkey = SplTokenPubkey::new(&[2; 32]);
|
||||
let mut account_data = [0; size_of::<Account>()];
|
||||
let mut account: &mut Account = unpack_unchecked(&mut account_data).unwrap();
|
||||
account.mint = mint_pubkey;
|
||||
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!(
|
||||
|
@@ -1,4 +1,4 @@
|
||||
use crate::parse_account_data::ParseAccountError;
|
||||
use crate::{parse_account_data::ParseAccountError, StringAmount};
|
||||
use solana_sdk::{
|
||||
clock::{Epoch, Slot},
|
||||
pubkey::Pubkey,
|
||||
@@ -12,8 +12,8 @@ pub fn parse_vote(data: &[u8]) -> Result<VoteAccountType, ParseAccountError> {
|
||||
.iter()
|
||||
.map(|(epoch, credits, previous_credits)| UiEpochCredits {
|
||||
epoch: *epoch,
|
||||
credits: *credits,
|
||||
previous_credits: *previous_credits,
|
||||
credits: credits.to_string(),
|
||||
previous_credits: previous_credits.to_string(),
|
||||
})
|
||||
.collect();
|
||||
let votes = vote_state
|
||||
@@ -115,8 +115,8 @@ struct UiPriorVoters {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct UiEpochCredits {
|
||||
epoch: Epoch,
|
||||
credits: u64,
|
||||
previous_credits: u64,
|
||||
credits: StringAmount,
|
||||
previous_credits: StringAmount,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
18
account-decoder/src/validator_info.rs
Normal file
18
account-decoder/src/validator_info.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
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]
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
edition = "2018"
|
||||
name = "solana-accounts-bench"
|
||||
version = "1.3.1"
|
||||
version = "1.2.29"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.6"
|
||||
rayon = "1.3.1"
|
||||
solana-logger = { path = "../logger", version = "1.3.1" }
|
||||
solana-runtime = { path = "../runtime", version = "1.3.1" }
|
||||
solana-measure = { path = "../measure", version = "1.3.1" }
|
||||
solana-sdk = { path = "../sdk", version = "1.3.1" }
|
||||
rayon = "1.4.0"
|
||||
solana-logger = { path = "../logger", version = "1.2.29" }
|
||||
solana-runtime = { path = "../runtime", version = "1.2.29" }
|
||||
solana-measure = { path = "../measure", version = "1.2.29" }
|
||||
solana-sdk = { path = "../sdk", version = "1.2.29" }
|
||||
rand = "0.7.0"
|
||||
clap = "2.33.1"
|
||||
crossbeam-channel = "0.4"
|
||||
|
@@ -5,7 +5,7 @@ use solana_runtime::{
|
||||
accounts::{create_test_accounts, update_accounts, Accounts},
|
||||
accounts_index::Ancestors,
|
||||
};
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::{genesis_config::OperatingMode, pubkey::Pubkey};
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -54,7 +54,7 @@ fn main() {
|
||||
if fs::remove_dir_all(path.clone()).is_err() {
|
||||
println!("Warning: Couldn't remove {:?}", path);
|
||||
}
|
||||
let accounts = Accounts::new(vec![path]);
|
||||
let accounts = Accounts::new(vec![path], OperatingMode::Preview);
|
||||
println!("Creating {} accounts", num_accounts);
|
||||
let mut create_time = Measure::start("create accounts");
|
||||
let pubkeys: Vec<_> = (0..num_slots)
|
||||
|
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
edition = "2018"
|
||||
name = "solana-banking-bench"
|
||||
version = "1.3.1"
|
||||
version = "1.2.29"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -12,17 +12,17 @@ clap = "2.33.1"
|
||||
crossbeam-channel = "0.4"
|
||||
log = "0.4.6"
|
||||
rand = "0.7.0"
|
||||
rayon = "1.3.1"
|
||||
solana-core = { path = "../core", version = "1.3.1" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.3.1" }
|
||||
solana-streamer = { path = "../streamer", version = "1.3.1" }
|
||||
solana-perf = { path = "../perf", version = "1.3.1" }
|
||||
solana-ledger = { path = "../ledger", version = "1.3.1" }
|
||||
solana-logger = { path = "../logger", version = "1.3.1" }
|
||||
solana-runtime = { path = "../runtime", version = "1.3.1" }
|
||||
solana-measure = { path = "../measure", version = "1.3.1" }
|
||||
solana-sdk = { path = "../sdk", version = "1.3.1" }
|
||||
solana-version = { path = "../version", version = "1.3.1" }
|
||||
rayon = "1.4.0"
|
||||
solana-core = { path = "../core", version = "1.2.29" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.2.29" }
|
||||
solana-streamer = { path = "../streamer", version = "1.2.29" }
|
||||
solana-perf = { path = "../perf", version = "1.2.29" }
|
||||
solana-ledger = { path = "../ledger", version = "1.2.29" }
|
||||
solana-logger = { path = "../logger", version = "1.2.29" }
|
||||
solana-runtime = { path = "../runtime", version = "1.2.29" }
|
||||
solana-measure = { path = "../measure", version = "1.2.29" }
|
||||
solana-sdk = { path = "../sdk", version = "1.2.29" }
|
||||
solana-version = { path = "../version", version = "1.2.29" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -11,13 +11,14 @@ use solana_core::{
|
||||
poh_recorder::WorkingBankEntry,
|
||||
};
|
||||
use solana_ledger::{
|
||||
bank_forks::BankForks,
|
||||
blockstore::Blockstore,
|
||||
genesis_utils::{create_genesis_config, GenesisConfigInfo},
|
||||
get_tmp_ledger_path,
|
||||
};
|
||||
use solana_measure::measure::Measure;
|
||||
use solana_perf::packet::to_packets_chunked;
|
||||
use solana_runtime::{bank::Bank, bank_forks::BankForks};
|
||||
use solana_runtime::bank::Bank;
|
||||
use solana_sdk::{
|
||||
hash::Hash,
|
||||
pubkey::Pubkey,
|
||||
@@ -167,7 +168,6 @@ fn main() {
|
||||
|
||||
let (verified_sender, verified_receiver) = unbounded();
|
||||
let (vote_sender, vote_receiver) = unbounded();
|
||||
let (replay_vote_sender, _replay_vote_receiver) = unbounded();
|
||||
let bank0 = Bank::new(&genesis_config);
|
||||
let mut bank_forks = BankForks::new(bank0);
|
||||
let mut bank = bank_forks.working_bank();
|
||||
@@ -209,7 +209,7 @@ fn main() {
|
||||
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 blockstore = Arc::new(
|
||||
@@ -225,7 +225,6 @@ fn main() {
|
||||
verified_receiver,
|
||||
vote_receiver,
|
||||
None,
|
||||
replay_vote_sender,
|
||||
);
|
||||
poh_recorder.lock().unwrap().set_bank(&bank);
|
||||
|
||||
|
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
edition = "2018"
|
||||
name = "solana-bench-exchange"
|
||||
version = "1.3.1"
|
||||
version = "1.2.29"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -15,24 +15,24 @@ log = "0.4.8"
|
||||
num-derive = "0.3"
|
||||
num-traits = "0.2"
|
||||
rand = "0.7.0"
|
||||
rayon = "1.3.1"
|
||||
serde_json = "1.0.56"
|
||||
serde_yaml = "0.8.13"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.3.1" }
|
||||
solana-core = { path = "../core", version = "1.3.1" }
|
||||
solana-genesis = { path = "../genesis", version = "1.3.1" }
|
||||
solana-client = { path = "../client", version = "1.3.1" }
|
||||
solana-faucet = { path = "../faucet", version = "1.3.1" }
|
||||
solana-exchange-program = { path = "../programs/exchange", version = "1.3.1" }
|
||||
solana-logger = { path = "../logger", version = "1.3.1" }
|
||||
solana-metrics = { path = "../metrics", version = "1.3.1" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.3.1" }
|
||||
solana-runtime = { path = "../runtime", version = "1.3.1" }
|
||||
solana-sdk = { path = "../sdk", version = "1.3.1" }
|
||||
solana-version = { path = "../version", version = "1.3.1" }
|
||||
rayon = "1.4.0"
|
||||
serde_json = "1.0.53"
|
||||
serde_yaml = "0.8.12"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.2.29" }
|
||||
solana-core = { path = "../core", version = "1.2.29" }
|
||||
solana-genesis = { path = "../genesis", version = "1.2.29" }
|
||||
solana-client = { path = "../client", version = "1.2.29" }
|
||||
solana-faucet = { path = "../faucet", version = "1.2.29" }
|
||||
solana-exchange-program = { path = "../programs/exchange", version = "1.2.29" }
|
||||
solana-logger = { path = "../logger", version = "1.2.29" }
|
||||
solana-metrics = { path = "../metrics", version = "1.2.29" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.2.29" }
|
||||
solana-runtime = { path = "../runtime", version = "1.2.29" }
|
||||
solana-sdk = { path = "../sdk", version = "1.2.29" }
|
||||
solana-version = { path = "../version", version = "1.2.29" }
|
||||
|
||||
[dev-dependencies]
|
||||
solana-local-cluster = { path = "../local-cluster", version = "1.3.1" }
|
||||
solana-local-cluster = { path = "../local-cluster", version = "1.2.29" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -179,13 +179,19 @@ where
|
||||
|
||||
info!("Generating {:?} account keys", 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
|
||||
.iter()
|
||||
.map(|keypair| keypair.pubkey())
|
||||
.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
|
||||
.iter()
|
||||
.map(|keypair| keypair.pubkey())
|
||||
|
@@ -1,19 +1,19 @@
|
||||
[package]
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
edition = "2018"
|
||||
name = "solana-bench-streamer"
|
||||
version = "1.3.1"
|
||||
version = "1.2.29"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33.1"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.3.1" }
|
||||
solana-streamer = { path = "../streamer", version = "1.3.1" }
|
||||
solana-logger = { path = "../logger", version = "1.3.1" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.3.1" }
|
||||
solana-version = { path = "../version", version = "1.3.1" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.2.29" }
|
||||
solana-streamer = { path = "../streamer", version = "1.2.29" }
|
||||
solana-logger = { path = "../logger", version = "1.2.29" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.2.29" }
|
||||
solana-version = { path = "../version", version = "1.2.29" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -27,7 +27,7 @@ fn producer(addr: &SocketAddr, exit: Arc<AtomicBool>) -> JoinHandle<()> {
|
||||
let mut num = 0;
|
||||
for p in &msgs.packets {
|
||||
let a = p.meta.addr();
|
||||
assert!(p.meta.size < PACKET_DATA_SIZE);
|
||||
assert!(p.meta.size <= PACKET_DATA_SIZE);
|
||||
send.send_to(&p.data[..p.meta.size], &a).unwrap();
|
||||
num += 1;
|
||||
}
|
||||
|
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
edition = "2018"
|
||||
name = "solana-bench-tps"
|
||||
version = "1.3.1"
|
||||
version = "1.2.29"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -11,26 +11,26 @@ homepage = "https://solana.com/"
|
||||
bincode = "1.3.1"
|
||||
clap = "2.33.1"
|
||||
log = "0.4.8"
|
||||
rayon = "1.3.1"
|
||||
serde_json = "1.0.56"
|
||||
serde_yaml = "0.8.13"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.3.1" }
|
||||
solana-core = { path = "../core", version = "1.3.1" }
|
||||
solana-genesis = { path = "../genesis", version = "1.3.1" }
|
||||
solana-client = { path = "../client", version = "1.3.1" }
|
||||
solana-faucet = { path = "../faucet", version = "1.3.1" }
|
||||
solana-logger = { path = "../logger", version = "1.3.1" }
|
||||
solana-metrics = { path = "../metrics", version = "1.3.1" }
|
||||
solana-measure = { path = "../measure", version = "1.3.1" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.3.1" }
|
||||
solana-runtime = { path = "../runtime", version = "1.3.1" }
|
||||
solana-sdk = { path = "../sdk", version = "1.3.1" }
|
||||
solana-version = { path = "../version", version = "1.3.1" }
|
||||
rayon = "1.4.0"
|
||||
serde_json = "1.0.53"
|
||||
serde_yaml = "0.8.12"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.2.29" }
|
||||
solana-core = { path = "../core", version = "1.2.29" }
|
||||
solana-genesis = { path = "../genesis", version = "1.2.29" }
|
||||
solana-client = { path = "../client", version = "1.2.29" }
|
||||
solana-faucet = { path = "../faucet", version = "1.2.29" }
|
||||
solana-logger = { path = "../logger", version = "1.2.29" }
|
||||
solana-metrics = { path = "../metrics", version = "1.2.29" }
|
||||
solana-measure = { path = "../measure", version = "1.2.29" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.2.29" }
|
||||
solana-runtime = { path = "../runtime", version = "1.2.29" }
|
||||
solana-sdk = { path = "../sdk", version = "1.2.29" }
|
||||
solana-version = { path = "../version", version = "1.2.29" }
|
||||
|
||||
[dev-dependencies]
|
||||
serial_test = "0.4.0"
|
||||
serial_test_derive = "0.4.0"
|
||||
solana-local-cluster = { path = "../local-cluster", version = "1.3.1" }
|
||||
solana-local-cluster = { path = "../local-cluster", version = "1.2.29" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -1,10 +1,7 @@
|
||||
use clap::{crate_description, crate_name, App, Arg, ArgMatches};
|
||||
use solana_faucet::faucet::FAUCET_PORT;
|
||||
use solana_sdk::fee_calculator::FeeRateGovernor;
|
||||
use solana_sdk::{
|
||||
pubkey::Pubkey,
|
||||
signature::{read_keypair_file, Keypair},
|
||||
};
|
||||
use solana_sdk::signature::{read_keypair_file, Keypair};
|
||||
use std::{net::SocketAddr, process::exit, time::Duration};
|
||||
|
||||
const NUM_LAMPORTS_PER_ACCOUNT_DEFAULT: u64 = solana_sdk::native_token::LAMPORTS_PER_SOL;
|
||||
@@ -28,7 +25,6 @@ pub struct Config {
|
||||
pub multi_client: bool,
|
||||
pub num_lamports_per_account: u64,
|
||||
pub target_slots_per_epoch: u64,
|
||||
pub target_node: Option<Pubkey>,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
@@ -51,7 +47,6 @@ impl Default for Config {
|
||||
multi_client: true,
|
||||
num_lamports_per_account: NUM_LAMPORTS_PER_ACCOUNT_DEFAULT,
|
||||
target_slots_per_epoch: 0,
|
||||
target_node: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,14 +112,6 @@ pub fn build_args<'a, 'b>(version: &'b str) -> App<'a, 'b> {
|
||||
.long("no-multi-client")
|
||||
.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::with_name("tx_count")
|
||||
.long("tx_count")
|
||||
@@ -270,9 +257,6 @@ pub fn extract_args<'a>(matches: &ArgMatches<'a>) -> Config {
|
||||
}
|
||||
|
||||
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") {
|
||||
args.num_lamports_per_account = v.to_string().parse().expect("can't parse lamports");
|
||||
|
@@ -31,7 +31,6 @@ fn main() {
|
||||
target_lamports_per_signature,
|
||||
multi_client,
|
||||
num_lamports_per_account,
|
||||
target_node,
|
||||
..
|
||||
} = &cli_config;
|
||||
|
||||
@@ -82,19 +81,6 @@ fn main() {
|
||||
exit(1);
|
||||
}
|
||||
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 {
|
||||
Arc::new(get_client(&nodes))
|
||||
};
|
||||
|
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
|
@@ -207,7 +207,7 @@ pull_or_push_steps() {
|
||||
|
||||
# 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
|
||||
if affects_other_than ^.buildkite ^.travis .md$ ^docs/ ^web3.js/ ^explorer/ ^.gitbook; then
|
||||
all_test_steps
|
||||
fi
|
||||
|
||||
|
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}"
|
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"
|
41
ci/buildkite.yml
Normal file
41
ci/buildkite.yml
Normal file
@@ -0,0 +1,41 @@
|
||||
# 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/test-sanity.sh"
|
||||
name: "sanity"
|
||||
timeout_in_minutes: 5
|
||||
- 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,11 +89,17 @@ BETA_CHANNEL_LATEST_TAG=${beta_tag:+v$beta_tag}
|
||||
STABLE_CHANNEL_LATEST_TAG=${stable_tag:+v$stable_tag}
|
||||
|
||||
|
||||
if [[ $CI_BRANCH = "$STABLE_CHANNEL" ]]; then
|
||||
if [[ -n $CI_BASE_BRANCH ]]; then
|
||||
BRANCH="$CI_BASE_BRANCH"
|
||||
elif [[ -n $CI_BRANCH ]]; then
|
||||
BRANCH="$CI_BRANCH"
|
||||
fi
|
||||
|
||||
if [[ $BRANCH = "$STABLE_CHANNEL" ]]; then
|
||||
CHANNEL=stable
|
||||
elif [[ $CI_BRANCH = "$EDGE_CHANNEL" ]]; then
|
||||
elif [[ $BRANCH = "$EDGE_CHANNEL" ]]; then
|
||||
CHANNEL=edge
|
||||
elif [[ $CI_BRANCH = "$BETA_CHANNEL" ]]; then
|
||||
elif [[ $BRANCH = "$BETA_CHANNEL" ]]; then
|
||||
CHANNEL=beta
|
||||
fi
|
||||
|
||||
|
@@ -7,14 +7,14 @@ source ci/_
|
||||
commit_range="$(git merge-base HEAD origin/master)..HEAD"
|
||||
parsed_update_args="$(
|
||||
git log "$commit_range" --author "dependabot-preview" --oneline -n1 |
|
||||
grep -o '[Bb]ump.*$' |
|
||||
sed -r 's/[Bb]ump ([^ ]+) from ([^ ]+) to ([^ ]+)/-p \1:\2 --precise \3/'
|
||||
grep -o 'Bump.*$' |
|
||||
sed -r 's/Bump ([^ ]+) from ([^ ]+) to ([^ ]+)/-p \1:\2 --precise \3/'
|
||||
)"
|
||||
# relaxed_parsed_update_args is temporal measure...
|
||||
relaxed_parsed_update_args="$(
|
||||
git log "$commit_range" --author "dependabot-preview" --oneline -n1 |
|
||||
grep -o '[Bb]ump.*$' |
|
||||
sed -r 's/[Bb]ump ([^ ]+) from [^ ]+ to ([^ ]+)/-p \1 --precise \2/'
|
||||
grep -o 'Bump.*$' |
|
||||
sed -r 's/Bump ([^ ]+) from [^ ]+ to ([^ ]+)/-p \1 --precise \2/'
|
||||
)"
|
||||
package=$(echo "$parsed_update_args" | awk '{print $2}' | grep -o "^[^:]*")
|
||||
if [[ -n $parsed_update_args ]]; then
|
||||
|
@@ -60,12 +60,6 @@ if [[ -z "$SOLANA_DOCKER_RUN_NOSETUID" ]]; then
|
||||
ARGS+=(--user "$(id -u):$(id -g)")
|
||||
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
|
||||
ARGS+=(
|
||||
--env BUILDKITE
|
||||
|
@@ -1,10 +1,9 @@
|
||||
FROM solanalabs/rust:1.45.1
|
||||
FROM solanalabs/rust:1.43.0
|
||||
ARG date
|
||||
|
||||
RUN set -x \
|
||||
&& rustup install nightly-$date \
|
||||
&& rustup component add clippy --toolchain=nightly-$date \
|
||||
&& rustup component add rustfmt --toolchain=nightly-$date \
|
||||
&& rustup show \
|
||||
&& rustc --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
|
||||
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
|
||||
|
||||
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`)
|
||||
to avoid the build breaking at unexpected times, as occasionally nightly will
|
||||
introduce breaking changes.
|
||||
|
||||
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,
|
||||
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).
|
||||
Check https://rust-lang.github.io/rustup-components-history/ for build
|
||||
status
|
||||
1. Update `ci/rust-version.sh` to reflect the new nightly `YYY-MM-DD`
|
||||
1. Run `SOLANA_ALLOCATE_TTY=1 SOLANA_DOCKER_RUN_NOSETUID=1 ci/docker-run.sh --nopull solanalabs/rust-nightly:YYYY-MM-DD ci/test-checks.sh`
|
||||
and `SOLANA_ALLOCATE_TTY=1 SOLANA_DOCKER_RUN_NOSETUID=1 ci/docker-run.sh --nopull solanalabs/rust-nightly:YYYY-MM-DD ci/test-coverage.sh [args]...`
|
||||
1. Run `SOLANA_DOCKER_RUN_NOSETUID=1 ci/docker-run.sh --nopull solanalabs/rust-nightly:YYYY-MM-DD ci/test-coverage.sh`
|
||||
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 `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
|
||||
# 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.
|
||||
ENV PROTOC_VERSION 3.8.0
|
||||
|
@@ -1,11 +1,7 @@
|
||||
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:
|
||||
1. Edit `Dockerfile` to match the desired rust version
|
||||
1. Run `docker login` to enable pushing images to Docker Hub, if you're authorized.
|
||||
1. Run `./build.sh` to publish the new image, if you are a member of the [Solana
|
||||
2. 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.
|
||||
|
||||
|
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
|
@@ -46,15 +46,6 @@ linux)
|
||||
;;
|
||||
windows)
|
||||
TARGET=x86_64-pc-windows-msvc
|
||||
# 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
|
||||
@@ -62,14 +53,11 @@ windows)
|
||||
;;
|
||||
esac
|
||||
|
||||
RELEASE_BASENAME="${RELEASE_BASENAME:=solana-release}"
|
||||
TARBALL_BASENAME="${TARBALL_BASENAME:="$RELEASE_BASENAME"}"
|
||||
|
||||
echo --- Creating release tarball
|
||||
(
|
||||
set -x
|
||||
rm -rf "${RELEASE_BASENAME:?}"/
|
||||
mkdir "${RELEASE_BASENAME}"/
|
||||
rm -rf solana-release/
|
||||
mkdir solana-release/
|
||||
|
||||
COMMIT="$(git rev-parse HEAD)"
|
||||
|
||||
@@ -77,18 +65,18 @@ echo --- Creating release tarball
|
||||
echo "channel: $CHANNEL_OR_TAG"
|
||||
echo "commit: $COMMIT"
|
||||
echo "target: $TARGET"
|
||||
) > "${RELEASE_BASENAME}"/version.yml
|
||||
) > solana-release/version.yml
|
||||
|
||||
# Make CHANNEL available to include in the software version information
|
||||
export CHANNEL
|
||||
|
||||
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}"
|
||||
bzip2 "${TARBALL_BASENAME}"-$TARGET.tar
|
||||
cp "${RELEASE_BASENAME}"/bin/solana-install-init solana-install-init-$TARGET
|
||||
cp "${RELEASE_BASENAME}"/version.yml "${TARBALL_BASENAME}"-$TARGET.yml
|
||||
tar cvf solana-release-$TARGET.tar solana-release
|
||||
bzip2 solana-release-$TARGET.tar
|
||||
cp solana-release/bin/solana-install-init solana-install-init-$TARGET
|
||||
cp solana-release/version.yml solana-release-$TARGET.yml
|
||||
)
|
||||
|
||||
# Metrics tarball is platform agnostic, only publish it from Linux
|
||||
@@ -106,7 +94,7 @@ fi
|
||||
|
||||
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
|
||||
upload-ci-artifact "$file"
|
||||
echo "Skipped $file due to DO_NOT_PUBLISH_TAR"
|
||||
|
@@ -18,13 +18,13 @@
|
||||
if [[ -n $RUST_STABLE_VERSION ]]; then
|
||||
stable_version="$RUST_STABLE_VERSION"
|
||||
else
|
||||
stable_version=1.45.1
|
||||
stable_version=1.43.0
|
||||
fi
|
||||
|
||||
if [[ -n $RUST_NIGHTLY_VERSION ]]; then
|
||||
nightly_version="$RUST_NIGHTLY_VERSION"
|
||||
else
|
||||
nightly_version=2020-07-27
|
||||
nightly_version=2020-04-23
|
||||
fi
|
||||
|
||||
|
||||
@@ -38,8 +38,7 @@ export rust_nightly_docker_image=solanalabs/rust-nightly:"$nightly_version"
|
||||
|
||||
rustup_install() {
|
||||
declare toolchain=$1
|
||||
if ! cargo +"$toolchain" -V > /dev/null; then
|
||||
echo "$0: Missing toolchain? Installing...: $toolchain" >&2
|
||||
if ! cargo +"$toolchain" -V; then
|
||||
rustup install "$toolchain"
|
||||
cargo +"$toolchain" -V
|
||||
fi
|
||||
@@ -59,7 +58,7 @@ export rust_nightly_docker_image=solanalabs/rust-nightly:"$nightly_version"
|
||||
rustup_install "$rust_nightly"
|
||||
;;
|
||||
*)
|
||||
echo "$0: Note: ignoring unknown argument: $1" >&2
|
||||
echo "Note: ignoring unknown argument: $1"
|
||||
;;
|
||||
esac
|
||||
)
|
||||
|
@@ -27,5 +27,5 @@ Alternatively, you can source it from within a script:
|
||||
local PATCH=0
|
||||
local SPECIAL=""
|
||||
|
||||
semverParseInto "1.2.3" MAJOR MINOR PATCH SPECIAL
|
||||
semverParseInto "3.2.1" MAJOR MINOR PATCH SPECIAL
|
||||
semverParseInto "1.2.29" MAJOR MINOR PATCH SPECIAL
|
||||
semverParseInto "3.2.1" MAJOR MINOR PATCH SPECIAL
|
||||
|
@@ -76,7 +76,7 @@ RestartForceExitStatus=SIGPIPE
|
||||
TimeoutStartSec=10
|
||||
TimeoutStopSec=0
|
||||
KillMode=process
|
||||
LimitNOFILE=65536
|
||||
LimitNOFILE=500000
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
@@ -8,5 +8,5 @@ source "$HERE"/utils.sh
|
||||
ensure_env || exit 1
|
||||
|
||||
# Allow more files to be opened by a user
|
||||
sed -i 's/^\(# End of file\)/* soft nofile 65535\n\n\1/' /etc/security/limits.conf
|
||||
echo "* - nofile 500000" > /etc/security/limits.d/90-solana-nofiles.conf
|
||||
|
||||
|
@@ -2,6 +2,25 @@
|
||||
set -e
|
||||
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/upload-ci-artifact.sh
|
||||
|
||||
|
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
@@ -9,53 +8,32 @@ source ci/rust-version.sh stable
|
||||
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 RUSTFLAGS="-D warnings -A incomplete_features"
|
||||
export RUSTFLAGS="-D warnings"
|
||||
|
||||
|
||||
# Only force up-to-date lock files on edge
|
||||
if [[ $CI_BASE_BRANCH = "$EDGE_CHANNEL" ]]; then
|
||||
# Exclude --benches as it's not available in rust stable yet
|
||||
if _ scripts/cargo-for-all-lock-files.sh +"$rust_stable" check --locked --tests --bins --examples; then
|
||||
if _ scripts/cargo-for-all-lock-files.sh +"$rust_nightly" check --locked --all-targets; then
|
||||
true
|
||||
else
|
||||
check_status=$?
|
||||
echo "$0: Some Cargo.lock might be outdated; sync them (or just be a compilation error?)" >&2
|
||||
echo "$0: protip: $ ./scripts/cargo-for-all-lock-files.sh [--ignore-exit-code] ... \\" >&2
|
||||
echo "$0: [tree (for outdated Cargo.lock sync)|check (for compilation error)|update -p foo --precise x.y.z (for your Cargo.toml update)] ..." >&2
|
||||
echo "Some Cargo.lock is outdated; please update them as well"
|
||||
echo "protip: you can use ./scripts/cargo-for-all-lock-files.sh update ..."
|
||||
exit "$check_status"
|
||||
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
|
||||
|
||||
# -Z... is needed because of clippy bug: https://github.com/rust-lang/rust-clippy/issues/4612
|
||||
# run nightly clippy for `sdk/` as there's a moderate amount of nightly-only code there
|
||||
_ cargo +"$rust_nightly" clippy -Zunstable-options --workspace --all-targets -- --deny=warnings
|
||||
_ cargo +"$rust_stable" clippy --version
|
||||
_ cargo +"$rust_stable" clippy --workspace -- --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
|
||||
_ ci/order-crates-for-publishing.py
|
||||
|
||||
{
|
||||
cd programs/bpf
|
||||
@@ -66,6 +44,7 @@ _ scripts/cargo-for-all-lock-files.sh +"$rust_stable" audit --ignore RUSTSEC-202
|
||||
cd "$project"
|
||||
_ cargo +"$rust_stable" fmt -- --check
|
||||
_ cargo +"$rust_nightly" test
|
||||
_ cargo +"$rust_nightly" clippy --version
|
||||
_ cargo +"$rust_nightly" clippy -- --deny=warnings --allow=clippy::missing_safety_doc
|
||||
)
|
||||
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 scripts/ulimit-n.sh
|
||||
|
||||
scripts/coverage.sh "$@"
|
||||
|
||||
if [[ -z $CI ]]; then
|
||||
exit
|
||||
fi
|
||||
scripts/coverage.sh
|
||||
|
||||
report=coverage-"${CI_COMMIT:0:9}".tar.gz
|
||||
mv target/cov/report.tar.gz "$report"
|
||||
|
@@ -13,6 +13,15 @@ annotate() {
|
||||
# Run the appropriate test based on entrypoint
|
||||
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
|
||||
|
||||
export RUST_BACKTRACE=1
|
||||
@@ -40,6 +49,24 @@ test-stable)
|
||||
_ cargo +"$rust_stable" test --jobs "$NPROC" --all --exclude solana-local-cluster ${V:+--verbose} -- --nocapture
|
||||
;;
|
||||
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
|
||||
_ make -C programs/bpf/c tests
|
||||
_ cargo +"$rust_stable" test \
|
||||
@@ -64,7 +91,6 @@ test-stable-perf)
|
||||
|
||||
_ 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" run --manifest-path poh-bench/Cargo.toml ${V:+--verbose} -- --hashes-per-tick 10
|
||||
;;
|
||||
test-local-cluster)
|
||||
_ cargo +"$rust_stable" build --release --bins ${V:+--verbose}
|
||||
|
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "solana-clap-utils"
|
||||
version = "1.3.1"
|
||||
version = "1.2.29"
|
||||
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"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -11,9 +11,9 @@ edition = "2018"
|
||||
[dependencies]
|
||||
clap = "2.33.0"
|
||||
rpassword = "4.0"
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "1.3.1" }
|
||||
solana-sdk = { path = "../sdk", version = "1.3.1" }
|
||||
thiserror = "1.0.20"
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "1.2.29" }
|
||||
solana-sdk = { path = "../sdk", version = "1.2.29" }
|
||||
thiserror = "1.0.11"
|
||||
tiny-bip39 = "0.7.0"
|
||||
url = "2.1.0"
|
||||
chrono = "0.4"
|
||||
|
@@ -1,9 +1,9 @@
|
||||
[package]
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
edition = "2018"
|
||||
name = "solana-cli-config"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.3.1"
|
||||
version = "1.2.29"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -11,9 +11,9 @@ homepage = "https://solana.com/"
|
||||
[dependencies]
|
||||
dirs = "2.0.2"
|
||||
lazy_static = "1.4.0"
|
||||
serde = "1.0.112"
|
||||
serde = "1.0.110"
|
||||
serde_derive = "1.0.103"
|
||||
serde_yaml = "0.8.13"
|
||||
serde_yaml = "0.8.12"
|
||||
url = "2.1.1"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
|
@@ -1,6 +1,6 @@
|
||||
// Wallet settings that can be configured for long-term use
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::{collections::HashMap, io, path::Path};
|
||||
use std::{collections::HashMap, io};
|
||||
use url::Url;
|
||||
|
||||
lazy_static! {
|
||||
@@ -17,7 +17,6 @@ pub struct Config {
|
||||
pub json_rpc_url: String,
|
||||
pub websocket_url: String,
|
||||
pub keypair_path: String,
|
||||
|
||||
#[serde(default)]
|
||||
pub address_labels: HashMap<String, String>,
|
||||
}
|
||||
@@ -35,17 +34,11 @@ impl Default for Config {
|
||||
// `Config::compute_websocket_url(&json_rpc_url)`
|
||||
let websocket_url = "".to_string();
|
||||
|
||||
let mut address_labels = HashMap::new();
|
||||
address_labels.insert(
|
||||
"11111111111111111111111111111111".to_string(),
|
||||
"System Program".to_string(),
|
||||
);
|
||||
|
||||
Self {
|
||||
json_rpc_url,
|
||||
websocket_url,
|
||||
keypair_path,
|
||||
address_labels,
|
||||
address_labels: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,24 +68,6 @@ impl Config {
|
||||
}
|
||||
ws_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)]
|
||||
|
@@ -1,9 +1,9 @@
|
||||
[package]
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
edition = "2018"
|
||||
name = "solana-cli"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.3.1"
|
||||
version = "1.2.29"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -14,42 +14,42 @@ bs58 = "0.3.1"
|
||||
chrono = { version = "0.4.11", features = ["serde"] }
|
||||
clap = "2.33.1"
|
||||
criterion-stats = "0.3.0"
|
||||
ctrlc = { version = "3.1.5", features = ["termination"] }
|
||||
console = "0.11.3"
|
||||
ctrlc = { version = "3.1.4", features = ["termination"] }
|
||||
console = "0.10.1"
|
||||
dirs = "2.0.2"
|
||||
log = "0.4.8"
|
||||
Inflector = "0.11.4"
|
||||
indicatif = "0.15.0"
|
||||
humantime = "2.0.1"
|
||||
indicatif = "0.14.0"
|
||||
humantime = "2.0.0"
|
||||
num-traits = "0.2"
|
||||
pretty-hex = "0.1.1"
|
||||
reqwest = { version = "0.10.6", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
serde = "1.0.112"
|
||||
reqwest = { version = "0.10.4", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
serde = "1.0.110"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.56"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "1.3.1" }
|
||||
solana-budget-program = { path = "../programs/budget", version = "1.3.1" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.3.1" }
|
||||
solana-cli-config = { path = "../cli-config", version = "1.3.1" }
|
||||
solana-client = { path = "../client", version = "1.3.1" }
|
||||
solana-config-program = { path = "../programs/config", version = "1.3.1" }
|
||||
solana-faucet = { path = "../faucet", version = "1.3.1" }
|
||||
solana-logger = { path = "../logger", version = "1.3.1" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.3.1" }
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "1.3.1" }
|
||||
solana-runtime = { path = "../runtime", version = "1.3.1" }
|
||||
solana-sdk = { path = "../sdk", version = "1.3.1" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "1.3.1" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "1.3.1" }
|
||||
solana-version = { path = "../version", version = "1.3.1" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.3.1" }
|
||||
solana-vote-signer = { path = "../vote-signer", version = "1.3.1" }
|
||||
thiserror = "1.0.20"
|
||||
serde_json = "1.0.53"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "1.2.29" }
|
||||
solana-budget-program = { path = "../programs/budget", version = "1.2.29" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.2.29" }
|
||||
solana-cli-config = { path = "../cli-config", version = "1.2.29" }
|
||||
solana-client = { path = "../client", version = "1.2.29" }
|
||||
solana-config-program = { path = "../programs/config", version = "1.2.29" }
|
||||
solana-faucet = { path = "../faucet", version = "1.2.29" }
|
||||
solana-logger = { path = "../logger", version = "1.2.29" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.2.29" }
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "1.2.29" }
|
||||
solana-runtime = { path = "../runtime", version = "1.2.29" }
|
||||
solana-sdk = { path = "../sdk", version = "1.2.29" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "1.2.29" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "1.2.29" }
|
||||
solana-version = { path = "../version", version = "1.2.29" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.2.29" }
|
||||
solana-vote-signer = { path = "../vote-signer", version = "1.2.29" }
|
||||
thiserror = "1.0.19"
|
||||
url = "2.1.1"
|
||||
|
||||
[dev-dependencies]
|
||||
solana-core = { path = "../core", version = "1.3.1" }
|
||||
solana-budget-program = { path = "../programs/budget", version = "1.3.1" }
|
||||
solana-core = { path = "../core", version = "1.2.29" }
|
||||
solana-budget-program = { path = "../programs/budget", version = "1.2.29" }
|
||||
tempfile = "3.1.0"
|
||||
|
||||
[[bin]]
|
||||
|
@@ -4,8 +4,7 @@ use solana_client::{
|
||||
rpc_client::RpcClient,
|
||||
};
|
||||
use solana_sdk::{
|
||||
commitment_config::CommitmentConfig, fee_calculator::FeeCalculator, message::Message,
|
||||
native_token::lamports_to_sol, pubkey::Pubkey,
|
||||
fee_calculator::FeeCalculator, message::Message, native_token::lamports_to_sol, pubkey::Pubkey,
|
||||
};
|
||||
|
||||
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])
|
||||
}
|
||||
|
||||
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(
|
||||
rpc_client: &RpcClient,
|
||||
account_pubkey: &Pubkey,
|
||||
fee_calculator: &FeeCalculator,
|
||||
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> {
|
||||
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)?
|
||||
{
|
||||
return Err(CliError::InsufficientFundsForFee(lamports_to_sol(fee)));
|
||||
@@ -76,23 +43,7 @@ pub fn check_account_for_balance(
|
||||
account_pubkey: &Pubkey,
|
||||
balance: u64,
|
||||
) -> ClientResult<bool> {
|
||||
check_account_for_balance_with_commitment(
|
||||
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;
|
||||
let lamports = rpc_client.get_balance(account_pubkey)?;
|
||||
if lamports != 0 && lamports >= balance {
|
||||
return Ok(true);
|
||||
}
|
||||
|
492
cli/src/cli.rs
492
cli/src/cli.rs
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,4 @@
|
||||
use crate::{
|
||||
cli::build_balance_message,
|
||||
display::{format_labeled_address, writeln_name_value},
|
||||
};
|
||||
use crate::{cli::build_balance_message, display::writeln_name_value};
|
||||
use chrono::{DateTime, NaiveDateTime, SecondsFormat, Utc};
|
||||
use console::{style, Emoji};
|
||||
use inflector::cases::titlecase::to_title_case;
|
||||
@@ -21,11 +18,7 @@ use solana_vote_program::{
|
||||
authorized_voters::AuthorizedVoters,
|
||||
vote_state::{BlockTimestamp, Lockout},
|
||||
};
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
fmt,
|
||||
time::Duration,
|
||||
};
|
||||
use std::{collections::BTreeMap, fmt, time::Duration};
|
||||
|
||||
static WARNING: Emoji = Emoji("⚠️", "!");
|
||||
|
||||
@@ -206,11 +199,6 @@ impl From<EpochInfo> for CliEpochInfo {
|
||||
impl fmt::Display for CliEpochInfo {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
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, "Epoch:", &self.epoch_info.epoch.to_string())?;
|
||||
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()
|
||||
}
|
||||
|
||||
#[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)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliValidators {
|
||||
@@ -276,7 +255,6 @@ pub struct CliValidators {
|
||||
pub total_deliquent_stake: u64,
|
||||
pub current_validators: Vec<CliValidator>,
|
||||
pub delinquent_validators: Vec<CliValidator>,
|
||||
pub stake_by_version: BTreeMap<String, CliValidatorsStakeByVersion>,
|
||||
#[serde(skip_serializing)]
|
||||
pub use_lamports_unit: bool,
|
||||
}
|
||||
@@ -300,7 +278,7 @@ impl fmt::Display for CliValidators {
|
||||
|
||||
writeln!(
|
||||
f,
|
||||
"{} {:<44} {:<44} {:>3}% {:>8} {:>10} {:>10} {:>17} {}",
|
||||
"{} {:<44} {:<44} {:>9}% {:>8} {:>10} {:>7} {}",
|
||||
if delinquent {
|
||||
WARNING.to_string()
|
||||
} else {
|
||||
@@ -312,12 +290,11 @@ impl fmt::Display for CliValidators {
|
||||
non_zero_or_dash(validator.last_vote),
|
||||
non_zero_or_dash(validator.root_slot),
|
||||
validator.credits,
|
||||
validator.version,
|
||||
if validator.activated_stake > 0 {
|
||||
format!(
|
||||
"{} ({:.2}%)",
|
||||
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 {
|
||||
"-".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,
|
||||
"{}",
|
||||
style(format!(
|
||||
" {:<44} {:<38} {} {} {} {:>10} {:^17} {}",
|
||||
" {:<44} {:<44} {} {} {} {:>7} {}",
|
||||
"Identity Pubkey",
|
||||
"Vote Account Pubkey",
|
||||
"Commission",
|
||||
"Last Vote",
|
||||
"Root Block",
|
||||
"Credits",
|
||||
"Version",
|
||||
"Active Stake",
|
||||
))
|
||||
.bold()
|
||||
@@ -424,19 +378,13 @@ pub struct CliValidator {
|
||||
pub root_slot: u64,
|
||||
pub credits: u64,
|
||||
pub activated_stake: u64,
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
impl CliValidator {
|
||||
pub fn new(
|
||||
vote_account: &RpcVoteAccountInfo,
|
||||
current_epoch: Epoch,
|
||||
version: String,
|
||||
address_labels: &HashMap<String, String>,
|
||||
) -> Self {
|
||||
pub fn new(vote_account: &RpcVoteAccountInfo, current_epoch: Epoch) -> Self {
|
||||
Self {
|
||||
identity_pubkey: format_labeled_address(&vote_account.node_pubkey, address_labels),
|
||||
vote_account_pubkey: format_labeled_address(&vote_account.vote_pubkey, address_labels),
|
||||
identity_pubkey: vote_account.node_pubkey.to_string(),
|
||||
vote_account_pubkey: vote_account.vote_pubkey.to_string(),
|
||||
commission: vote_account.commission,
|
||||
last_vote: vote_account.last_vote,
|
||||
root_slot: vote_account.root_slot,
|
||||
@@ -452,7 +400,6 @@ impl CliValidator {
|
||||
})
|
||||
.unwrap_or(0),
|
||||
activated_stake: vote_account.activated_stake,
|
||||
version,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -569,25 +516,19 @@ impl fmt::Display for CliStakeState {
|
||||
writeln!(f, "Withdraw Authority: {}", authorized.withdrawer)?;
|
||||
Ok(())
|
||||
}
|
||||
fn show_lockup(f: &mut fmt::Formatter, lockup: Option<&CliLockup>) -> fmt::Result {
|
||||
if let Some(lockup) = lockup {
|
||||
if lockup.unix_timestamp != UnixTimestamp::default() {
|
||||
writeln!(
|
||||
f,
|
||||
"Lockup Timestamp: {} (UnixTimestamp: {})",
|
||||
DateTime::<Utc>::from_utc(
|
||||
NaiveDateTime::from_timestamp(lockup.unix_timestamp, 0),
|
||||
Utc
|
||||
)
|
||||
.to_rfc3339_opts(SecondsFormat::Secs, true),
|
||||
lockup.unix_timestamp
|
||||
)?;
|
||||
}
|
||||
if lockup.epoch != Epoch::default() {
|
||||
writeln!(f, "Lockup Epoch: {}", lockup.epoch)?;
|
||||
}
|
||||
writeln!(f, "Lockup Custodian: {}", lockup.custodian)?;
|
||||
}
|
||||
fn show_lockup(f: &mut fmt::Formatter, lockup: &CliLockup) -> fmt::Result {
|
||||
writeln!(
|
||||
f,
|
||||
"Lockup Timestamp: {} (UnixTimestamp: {})",
|
||||
DateTime::<Utc>::from_utc(
|
||||
NaiveDateTime::from_timestamp(lockup.unix_timestamp, 0),
|
||||
Utc
|
||||
)
|
||||
.to_rfc3339_opts(SecondsFormat::Secs, true),
|
||||
lockup.unix_timestamp
|
||||
)?;
|
||||
writeln!(f, "Lockup Epoch: {}", lockup.epoch)?;
|
||||
writeln!(f, "Lockup Custodian: {}", lockup.custodian)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -611,7 +552,7 @@ impl fmt::Display for CliStakeState {
|
||||
CliStakeType::Initialized => {
|
||||
writeln!(f, "Stake account is undelegated")?;
|
||||
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 => {
|
||||
let show_delegation = {
|
||||
@@ -709,7 +650,7 @@ impl fmt::Display for CliStakeState {
|
||||
writeln!(f, "Stake account is undelegated")?;
|
||||
}
|
||||
show_authorized(f, self.authorized.as_ref().unwrap())?;
|
||||
show_lockup(f, self.lockup.as_ref())?;
|
||||
show_lockup(f, self.lockup.as_ref().unwrap())?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
@@ -1,17 +1,20 @@
|
||||
use crate::{
|
||||
cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult},
|
||||
cli_output::*,
|
||||
display::{format_labeled_address, new_spinner_progress_bar, println_name_value},
|
||||
display::{new_spinner_progress_bar, println_name_value},
|
||||
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
|
||||
};
|
||||
use clap::{value_t, value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand};
|
||||
use console::{style, Emoji};
|
||||
use solana_clap_utils::{
|
||||
commitment::commitment_arg, input_parsers::*, input_validators::*, keypair::signer_from_path,
|
||||
commitment::{commitment_arg, COMMITMENT_ARG},
|
||||
input_parsers::*,
|
||||
input_validators::*,
|
||||
keypair::signer_from_path,
|
||||
};
|
||||
use solana_client::{
|
||||
pubsub_client::{PubsubClient, SlotInfoMessage},
|
||||
rpc_client::RpcClient,
|
||||
rpc_client::{GetConfirmedSignaturesForAddress2Config, RpcClient},
|
||||
rpc_config::{RpcLargestAccountsConfig, RpcLargestAccountsFilter},
|
||||
};
|
||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||
@@ -33,7 +36,7 @@ use solana_sdk::{
|
||||
transaction::Transaction,
|
||||
};
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap, VecDeque},
|
||||
collections::{HashMap, VecDeque},
|
||||
net::SocketAddr,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
@@ -115,10 +118,6 @@ impl ClusterQuerySubCommands for App<'_, '_> {
|
||||
.alias("get-slot")
|
||||
.arg(commitment_arg()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("block-height").about("Get current block height")
|
||||
.arg(commitment_arg()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("epoch").about("Get current epoch")
|
||||
.arg(commitment_arg()),
|
||||
@@ -292,11 +291,13 @@ pub fn parse_catchup(
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
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 commitment_config = commitment_of(matches, COMMITMENT_ARG.long).unwrap();
|
||||
let follow = matches.is_present("follow");
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::Catchup {
|
||||
node_pubkey,
|
||||
node_json_rpc_url,
|
||||
commitment_config,
|
||||
follow,
|
||||
},
|
||||
signers: vec![],
|
||||
@@ -316,12 +317,14 @@ pub fn parse_cluster_ping(
|
||||
None
|
||||
};
|
||||
let timeout = Duration::from_secs(value_t_or_exit!(matches, "timeout", u64));
|
||||
let commitment_config = commitment_of(matches, COMMITMENT_ARG.long).unwrap();
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::Ping {
|
||||
lamports,
|
||||
interval,
|
||||
count,
|
||||
timeout,
|
||||
commitment_config,
|
||||
},
|
||||
signers: vec![signer_from_path(
|
||||
matches,
|
||||
@@ -340,35 +343,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 {
|
||||
command: CliCommand::GetEpoch,
|
||||
command: CliCommand::GetEpochInfo { commitment_config },
|
||||
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 {
|
||||
command: CliCommand::GetEpochInfo,
|
||||
command: CliCommand::GetSlot { commitment_config },
|
||||
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 {
|
||||
command: CliCommand::GetSlot,
|
||||
signers: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_get_block_height(_matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::GetBlockHeight,
|
||||
command: CliCommand::GetEpoch { commitment_config },
|
||||
signers: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
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") {
|
||||
Some(RpcLargestAccountsFilter::Circulating)
|
||||
} else if matches.is_present("non_circulating") {
|
||||
@@ -377,29 +377,38 @@ pub fn parse_largest_accounts(matches: &ArgMatches<'_>) -> Result<CliCommandInfo
|
||||
None
|
||||
};
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::LargestAccounts { filter },
|
||||
command: CliCommand::LargestAccounts {
|
||||
commitment_config,
|
||||
filter,
|
||||
},
|
||||
signers: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
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");
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::Supply { print_accounts },
|
||||
command: CliCommand::Supply {
|
||||
commitment_config,
|
||||
print_accounts,
|
||||
},
|
||||
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 {
|
||||
command: CliCommand::TotalSupply,
|
||||
command: CliCommand::TotalSupply { commitment_config },
|
||||
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 {
|
||||
command: CliCommand::GetTransactionCount,
|
||||
command: CliCommand::GetTransactionCount { commitment_config },
|
||||
signers: vec![],
|
||||
})
|
||||
}
|
||||
@@ -423,9 +432,13 @@ pub fn parse_show_stakes(
|
||||
|
||||
pub fn parse_show_validators(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||
let use_lamports_unit = matches.is_present("lamports");
|
||||
let commitment_config = commitment_of(matches, COMMITMENT_ARG.long).unwrap();
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::ShowValidators { use_lamports_unit },
|
||||
command: CliCommand::ShowValidators {
|
||||
use_lamports_unit,
|
||||
commitment_config,
|
||||
},
|
||||
signers: vec![],
|
||||
})
|
||||
}
|
||||
@@ -444,12 +457,21 @@ pub fn parse_transaction_history(
|
||||
),
|
||||
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 {
|
||||
command: CliCommand::TransactionHistory {
|
||||
address,
|
||||
before,
|
||||
until,
|
||||
limit,
|
||||
},
|
||||
signers: vec![],
|
||||
@@ -458,9 +480,9 @@ pub fn parse_transaction_history(
|
||||
|
||||
pub fn process_catchup(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
node_pubkey: &Pubkey,
|
||||
node_json_rpc_url: &Option<String>,
|
||||
commitment_config: CommitmentConfig,
|
||||
follow: bool,
|
||||
) -> ProcessResult {
|
||||
let sleep_interval = 5;
|
||||
@@ -509,8 +531,8 @@ pub fn process_catchup(
|
||||
let mut previous_rpc_slot = std::u64::MAX;
|
||||
let mut previous_slot_distance = 0;
|
||||
loop {
|
||||
let rpc_slot = rpc_client.get_slot_with_commitment(config.commitment)?;
|
||||
let node_slot = node_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(commitment_config)?;
|
||||
if !follow && node_slot > std::cmp::min(previous_rpc_slot, rpc_slot) {
|
||||
progress_bar.finish_and_clear();
|
||||
return Ok(format!(
|
||||
@@ -643,14 +665,13 @@ pub fn process_get_block_time(
|
||||
Ok(config.output_format.formatted_string(&block_time))
|
||||
}
|
||||
|
||||
pub fn process_get_epoch(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
|
||||
let epoch_info = rpc_client.get_epoch_info_with_commitment(config.commitment)?;
|
||||
Ok(epoch_info.epoch.to_string())
|
||||
}
|
||||
|
||||
pub fn process_get_epoch_info(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
|
||||
pub fn process_get_epoch_info(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> ProcessResult {
|
||||
let epoch_info: CliEpochInfo = rpc_client
|
||||
.get_epoch_info_with_commitment(config.commitment)?
|
||||
.get_epoch_info_with_commitment(commitment_config.clone())?
|
||||
.into();
|
||||
Ok(config.output_format.formatted_string(&epoch_info))
|
||||
}
|
||||
@@ -660,16 +681,20 @@ pub fn process_get_genesis_hash(rpc_client: &RpcClient) -> ProcessResult {
|
||||
Ok(genesis_hash.to_string())
|
||||
}
|
||||
|
||||
pub fn process_get_slot(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
|
||||
let slot = rpc_client.get_slot_with_commitment(config.commitment)?;
|
||||
pub fn process_get_slot(
|
||||
rpc_client: &RpcClient,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> ProcessResult {
|
||||
let slot = rpc_client.get_slot_with_commitment(commitment_config.clone())?;
|
||||
Ok(slot.to_string())
|
||||
}
|
||||
|
||||
pub fn process_get_block_height(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
|
||||
let epoch_info: CliEpochInfo = rpc_client
|
||||
.get_epoch_info_with_commitment(config.commitment)?
|
||||
.into();
|
||||
Ok(epoch_info.epoch_info.block_height.to_string())
|
||||
pub fn process_get_epoch(
|
||||
rpc_client: &RpcClient,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> ProcessResult {
|
||||
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> {
|
||||
@@ -755,12 +780,11 @@ pub fn process_show_block_production(
|
||||
let leader_schedule = leader_schedule.unwrap();
|
||||
|
||||
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() {
|
||||
let pubkey = format_labeled_address(pubkey, &config.address_labels);
|
||||
for slot_index in leader_slots.iter() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -837,11 +861,12 @@ pub fn process_show_block_production(
|
||||
pub fn process_largest_accounts(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
commitment_config: CommitmentConfig,
|
||||
filter: Option<RpcLargestAccountsFilter>,
|
||||
) -> ProcessResult {
|
||||
let accounts = rpc_client
|
||||
.get_largest_accounts_with_config(RpcLargestAccountsConfig {
|
||||
commitment: Some(config.commitment),
|
||||
commitment: Some(commitment_config),
|
||||
filter,
|
||||
})?
|
||||
.value;
|
||||
@@ -852,21 +877,29 @@ pub fn process_largest_accounts(
|
||||
pub fn process_supply(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
commitment_config: CommitmentConfig,
|
||||
print_accounts: bool,
|
||||
) -> 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();
|
||||
supply.print_accounts = print_accounts;
|
||||
Ok(config.output_format.formatted_string(&supply))
|
||||
}
|
||||
|
||||
pub fn process_total_supply(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
|
||||
let total_supply = rpc_client.total_supply_with_commitment(config.commitment)?;
|
||||
pub fn process_total_supply(
|
||||
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)))
|
||||
}
|
||||
|
||||
pub fn process_get_transaction_count(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
|
||||
let transaction_count = rpc_client.get_transaction_count_with_commitment(config.commitment)?;
|
||||
pub fn process_get_transaction_count(
|
||||
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())
|
||||
}
|
||||
|
||||
@@ -877,6 +910,7 @@ pub fn process_ping(
|
||||
interval: &Duration,
|
||||
count: &Option<u64>,
|
||||
timeout: &Duration,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> ProcessResult {
|
||||
println_name_value("Source Account:", &config.signers[0].pubkey().to_string());
|
||||
println!();
|
||||
@@ -922,7 +956,6 @@ pub fn process_ping(
|
||||
&fee_calculator,
|
||||
&config.signers[0].pubkey(),
|
||||
build_message,
|
||||
config.commitment,
|
||||
)?;
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
tx.try_sign(&config.signers, blockhash)?;
|
||||
@@ -931,8 +964,10 @@ pub fn process_ping(
|
||||
Ok(signature) => {
|
||||
let transaction_sent = Instant::now();
|
||||
loop {
|
||||
let signature_status = rpc_client
|
||||
.get_signature_status_with_commitment(&signature, config.commitment)?;
|
||||
let signature_status = rpc_client.get_signature_status_with_commitment(
|
||||
&signature,
|
||||
commitment_config.clone(),
|
||||
)?;
|
||||
let elapsed_time = Instant::now().duration_since(transaction_sent);
|
||||
if let Some(transaction_status) = signature_status {
|
||||
match transaction_status {
|
||||
@@ -1107,7 +1142,7 @@ pub fn process_live_slots(url: &str) -> ProcessResult {
|
||||
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()?;
|
||||
|
||||
fn format_port(addr: Option<SocketAddr>) -> String {
|
||||
@@ -1116,27 +1151,26 @@ pub fn process_show_gossip(rpc_client: &RpcClient, config: &CliConfig) -> Proces
|
||||
}
|
||||
|
||||
let s: Vec<_> = cluster_nodes
|
||||
.into_iter()
|
||||
.iter()
|
||||
.map(|node| {
|
||||
format!(
|
||||
"{:15} | {:44} | {:6} | {:5} | {:5} | {}",
|
||||
"{:15} | {:44} | {:6} | {:5} | {:5}",
|
||||
node.gossip
|
||||
.map(|addr| addr.ip().to_string())
|
||||
.unwrap_or_else(|| "none".to_string()),
|
||||
format_labeled_address(&node.pubkey, &config.address_labels),
|
||||
node.pubkey,
|
||||
format_port(node.gossip),
|
||||
format_port(node.tpu),
|
||||
format_port(node.rpc),
|
||||
node.version.unwrap_or_else(|| "unknown".to_string()),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(format!(
|
||||
"IP Address | Node identifier \
|
||||
| Gossip | TPU | RPC | Version\n\
|
||||
| Gossip | TPU | RPC\n\
|
||||
----------------+----------------------------------------------+\
|
||||
--------+-------+-------+----------------\n\
|
||||
--------+-------+-------\n\
|
||||
{}\n\
|
||||
Nodes: {}",
|
||||
s.join("\n"),
|
||||
@@ -1158,14 +1192,10 @@ pub fn process_show_stakes(
|
||||
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();
|
||||
let clock_account = rpc_client.get_account(&sysvar::clock::id())?;
|
||||
|
||||
let stake_history = StakeHistory::from_account(&stake_history_account).ok_or_else(|| {
|
||||
CliError::RpcRequestError("Failed to deserialize stake history".to_string())
|
||||
})?;
|
||||
let clock: Clock = Sysvar::from_account(&clock_account).ok_or_else(|| {
|
||||
CliError::RpcRequestError("Failed to deserialize clock sysvar".to_string())
|
||||
})?;
|
||||
|
||||
let mut stake_accounts: Vec<CliKeyedStakeState> = vec![];
|
||||
for (stake_pubkey, stake_account) in all_stake_accounts {
|
||||
@@ -1180,7 +1210,6 @@ pub fn process_show_stakes(
|
||||
&stake_state,
|
||||
use_lamports_unit,
|
||||
&stake_history,
|
||||
&clock,
|
||||
),
|
||||
});
|
||||
}
|
||||
@@ -1198,7 +1227,6 @@ pub fn process_show_stakes(
|
||||
&stake_state,
|
||||
use_lamports_unit,
|
||||
&stake_history,
|
||||
&clock,
|
||||
),
|
||||
});
|
||||
}
|
||||
@@ -1216,21 +1244,10 @@ pub fn process_show_validators(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
use_lamports_unit: bool,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> ProcessResult {
|
||||
let epoch_info = rpc_client.get_epoch_info_with_commitment(config.commitment)?;
|
||||
let vote_accounts = rpc_client.get_vote_accounts_with_commitment(config.commitment)?;
|
||||
|
||||
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 epoch_info = rpc_client.get_epoch_info_with_commitment(commitment_config)?;
|
||||
let vote_accounts = rpc_client.get_vote_accounts_with_commitment(commitment_config)?;
|
||||
let total_active_stake = vote_accounts
|
||||
.current
|
||||
.iter()
|
||||
@@ -1249,58 +1266,21 @@ pub fn process_show_validators(
|
||||
current.sort_by(|a, b| b.activated_stake.cmp(&a.activated_stake));
|
||||
let current_validators: Vec<CliValidator> = current
|
||||
.iter()
|
||||
.map(|vote_account| {
|
||||
CliValidator::new(
|
||||
vote_account,
|
||||
epoch_info.epoch,
|
||||
node_version
|
||||
.get(&vote_account.node_pubkey)
|
||||
.unwrap_or(&unknown_version)
|
||||
.clone(),
|
||||
&config.address_labels,
|
||||
)
|
||||
})
|
||||
.map(|vote_account| CliValidator::new(vote_account, epoch_info.epoch))
|
||||
.collect();
|
||||
let mut delinquent = vote_accounts.delinquent;
|
||||
delinquent.sort_by(|a, b| b.activated_stake.cmp(&a.activated_stake));
|
||||
let delinquent_validators: Vec<CliValidator> = delinquent
|
||||
.iter()
|
||||
.map(|vote_account| {
|
||||
CliValidator::new(
|
||||
vote_account,
|
||||
epoch_info.epoch,
|
||||
node_version
|
||||
.get(&vote_account.node_pubkey)
|
||||
.unwrap_or(&unknown_version)
|
||||
.clone(),
|
||||
&config.address_labels,
|
||||
)
|
||||
})
|
||||
.map(|vote_account| CliValidator::new(vote_account, epoch_info.epoch))
|
||||
.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 {
|
||||
total_active_stake,
|
||||
total_current_stake,
|
||||
total_deliquent_stake,
|
||||
current_validators,
|
||||
delinquent_validators,
|
||||
stake_by_version,
|
||||
use_lamports_unit,
|
||||
};
|
||||
Ok(config.output_format.formatted_string(&cli_validators))
|
||||
@@ -1311,12 +1291,16 @@ pub fn process_transaction_history(
|
||||
config: &CliConfig,
|
||||
address: &Pubkey,
|
||||
before: Option<Signature>,
|
||||
until: Option<Signature>,
|
||||
limit: usize,
|
||||
) -> ProcessResult {
|
||||
let results = rpc_client.get_confirmed_signatures_for_address2_with_config(
|
||||
address,
|
||||
before,
|
||||
Some(limit),
|
||||
GetConfirmedSignaturesForAddress2Config {
|
||||
before,
|
||||
until,
|
||||
limit: Some(limit),
|
||||
},
|
||||
)?;
|
||||
|
||||
let transactions_found = format!("{} transactions found", results.len());
|
||||
@@ -1403,24 +1387,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
|
||||
.clone()
|
||||
.get_matches_from(vec!["test", "epoch-info"]);
|
||||
assert_eq!(
|
||||
parse_command(&test_get_epoch_info, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::GetEpochInfo,
|
||||
command: CliCommand::GetEpochInfo {
|
||||
commitment_config: CommitmentConfig::recent(),
|
||||
},
|
||||
signers: vec![],
|
||||
}
|
||||
);
|
||||
@@ -1440,7 +1415,22 @@ mod tests {
|
||||
assert_eq!(
|
||||
parse_command(&test_get_slot, &default_keypair_file, &mut None).unwrap(),
|
||||
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![],
|
||||
}
|
||||
);
|
||||
@@ -1451,7 +1441,9 @@ mod tests {
|
||||
assert_eq!(
|
||||
parse_command(&test_total_supply, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::TotalSupply,
|
||||
command: CliCommand::TotalSupply {
|
||||
commitment_config: CommitmentConfig::recent(),
|
||||
},
|
||||
signers: vec![],
|
||||
}
|
||||
);
|
||||
@@ -1462,7 +1454,9 @@ mod tests {
|
||||
assert_eq!(
|
||||
parse_command(&test_transaction_count, &default_keypair_file, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::GetTransactionCount,
|
||||
command: CliCommand::GetTransactionCount {
|
||||
commitment_config: CommitmentConfig::recent(),
|
||||
},
|
||||
signers: vec![],
|
||||
}
|
||||
);
|
||||
@@ -1487,6 +1481,7 @@ mod tests {
|
||||
interval: Duration::from_secs(1),
|
||||
count: Some(2),
|
||||
timeout: Duration::from_secs(3),
|
||||
commitment_config: CommitmentConfig::max(),
|
||||
},
|
||||
signers: vec![default_keypair.into()],
|
||||
}
|
||||
|
@@ -6,7 +6,7 @@ use solana_sdk::{
|
||||
transaction::Transaction,
|
||||
};
|
||||
use solana_transaction_status::UiTransactionStatusMeta;
|
||||
use std::{collections::HashMap, fmt, io};
|
||||
use std::{fmt, io};
|
||||
|
||||
// Pretty print a "name value"
|
||||
pub fn println_name_value(name: &str, value: &str) {
|
||||
@@ -27,19 +27,6 @@ pub fn writeln_name_value(f: &mut fmt::Formatter, name: &str, value: &str) -> fm
|
||||
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) {
|
||||
let description = match setting_type {
|
||||
SettingType::Explicit => "",
|
||||
@@ -223,32 +210,3 @@ pub fn new_spinner_progress_bar() -> ProgressBar {
|
||||
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)"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
126
cli/src/main.rs
126
cli/src/main.rs
@@ -1,43 +1,25 @@
|
||||
use clap::{
|
||||
crate_description, crate_name, value_t_or_exit, AppSettings, Arg, ArgGroup, ArgMatches,
|
||||
SubCommand,
|
||||
};
|
||||
use clap::{crate_description, crate_name, AppSettings, Arg, ArgGroup, ArgMatches, SubCommand};
|
||||
use console::style;
|
||||
|
||||
use solana_clap_utils::{
|
||||
commitment::COMMITMENT_ARG, input_parsers::commitment_of, input_validators::is_url,
|
||||
keypair::SKIP_SEED_PHRASE_VALIDATION_ARG, DisplayError,
|
||||
input_validators::is_url, keypair::SKIP_SEED_PHRASE_VALIDATION_ARG, DisplayError,
|
||||
};
|
||||
use solana_cli::{
|
||||
cli::{
|
||||
app, parse_command, process_command, CliCommandInfo, CliConfig, CliSigners,
|
||||
DEFAULT_RPC_TIMEOUT_SECONDS,
|
||||
},
|
||||
cli::{app, parse_command, process_command, CliCommandInfo, CliConfig, CliSigners},
|
||||
cli_output::OutputFormat,
|
||||
display::{println_name_value, println_name_value_or},
|
||||
};
|
||||
use solana_cli_config::{Config, CONFIG_FILE};
|
||||
use solana_client::rpc_config::RpcSendTransactionConfig;
|
||||
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>> {
|
||||
let parse_args = match matches.subcommand() {
|
||||
("config", Some(matches)) => {
|
||||
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();
|
||||
("config", Some(matches)) => match matches.subcommand() {
|
||||
("get", Some(subcommand_matches)) => {
|
||||
if let Some(config_file) = matches.value_of("config_file") {
|
||||
let config = Config::load(config_file).unwrap_or_default();
|
||||
|
||||
match matches.subcommand() {
|
||||
("get", Some(subcommand_matches)) => {
|
||||
let (url_setting_type, json_rpc_url) =
|
||||
CliConfig::compute_json_rpc_url_setting("", &config.json_rpc_url);
|
||||
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("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()
|
||||
);
|
||||
}
|
||||
("set", Some(subcommand_matches)) => {
|
||||
false
|
||||
}
|
||||
("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") {
|
||||
config.json_rpc_url = url.to_string();
|
||||
// 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") {
|
||||
config.keypair_path = keypair.to_string();
|
||||
}
|
||||
|
||||
config.save(config_file)?;
|
||||
|
||||
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("WebSocket URL:", &websocket_url, ws_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()
|
||||
);
|
||||
}
|
||||
("import-address-labels", Some(subcommand_matches)) => {
|
||||
let filename = value_t_or_exit!(subcommand_matches, "filename", PathBuf);
|
||||
config.import_address_labels(&filename)?;
|
||||
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,
|
||||
};
|
||||
Ok(parse_args)
|
||||
@@ -129,10 +113,6 @@ pub fn parse_args<'a>(
|
||||
matches.value_of("json_rpc_url").unwrap_or(""),
|
||||
&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(
|
||||
matches.value_of("websocket_url").unwrap_or(""),
|
||||
&config.websocket_url,
|
||||
@@ -156,18 +136,6 @@ pub fn parse_args<'a>(
|
||||
})
|
||||
.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((
|
||||
CliConfig {
|
||||
command,
|
||||
@@ -176,12 +144,8 @@ pub fn parse_args<'a>(
|
||||
signers: vec![],
|
||||
keypair_path: default_signer_path,
|
||||
rpc_client: None,
|
||||
rpc_timeout,
|
||||
verbose: matches.is_present("verbose"),
|
||||
output_format,
|
||||
commitment,
|
||||
send_transaction_config: RpcSendTransactionConfig::default(),
|
||||
address_labels,
|
||||
},
|
||||
signers,
|
||||
))
|
||||
@@ -243,12 +207,6 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
||||
.global(true)
|
||||
.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::with_name("output_format")
|
||||
.long("output")
|
||||
@@ -264,16 +222,6 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
||||
.global(true)
|
||||
.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::with_name("config")
|
||||
.about("Solana command-line tool configuration settings")
|
||||
@@ -300,28 +248,6 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
||||
.multiple(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();
|
||||
|
@@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
checks::{check_account_for_fee_with_commitment, check_unique_pubkeys},
|
||||
checks::{check_account_for_fee, check_unique_pubkeys},
|
||||
cli::{
|
||||
generate_unique_signers, log_instruction_custom_error, CliCommand, CliCommandInfo,
|
||||
CliConfig, CliError, ProcessResult, SignerIndex,
|
||||
@@ -16,7 +16,6 @@ use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||
use solana_sdk::{
|
||||
account::Account,
|
||||
account_utils::StateMut,
|
||||
commitment_config::CommitmentConfig,
|
||||
hash::Hash,
|
||||
message::Message,
|
||||
nonce::{
|
||||
@@ -223,23 +222,10 @@ impl NonceSubCommands for App<'_, '_> {
|
||||
pub fn get_account(
|
||||
rpc_client: &RpcClient,
|
||||
nonce_pubkey: &Pubkey,
|
||||
) -> Result<Account, CliNonceError> {
|
||||
get_account_with_commitment(rpc_client, nonce_pubkey, CommitmentConfig::default())
|
||||
}
|
||||
|
||||
pub fn get_account_with_commitment(
|
||||
rpc_client: &RpcClient,
|
||||
nonce_pubkey: &Pubkey,
|
||||
commitment: CommitmentConfig,
|
||||
) -> Result<Account, CliNonceError> {
|
||||
rpc_client
|
||||
.get_account_with_commitment(nonce_pubkey, commitment)
|
||||
.get_account(nonce_pubkey)
|
||||
.map_err(|e| CliNonceError::Client(format!("{}", e)))
|
||||
.and_then(|result| {
|
||||
result.value.ok_or_else(|| {
|
||||
CliNonceError::Client(format!("AccountNotFound: pubkey={}", nonce_pubkey))
|
||||
})
|
||||
})
|
||||
.and_then(|a| match account_identity_ok(&a) {
|
||||
Ok(()) => Ok(a),
|
||||
Err(e) => Err(e),
|
||||
@@ -447,9 +433,7 @@ pub fn process_authorize_nonce_account(
|
||||
nonce_authority: SignerIndex,
|
||||
new_authority: &Pubkey,
|
||||
) -> ProcessResult {
|
||||
let (recent_blockhash, fee_calculator, _) = rpc_client
|
||||
.get_recent_blockhash_with_commitment(config.commitment)?
|
||||
.value;
|
||||
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||
|
||||
let nonce_authority = config.signers[nonce_authority];
|
||||
let ix = authorize_nonce_account(nonce_account, &nonce_authority.pubkey(), new_authority);
|
||||
@@ -457,18 +441,13 @@ pub fn process_authorize_nonce_account(
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||
|
||||
check_account_for_fee_with_commitment(
|
||||
check_account_for_fee(
|
||||
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,
|
||||
);
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||
log_instruction_custom_error::<NonceError>(result, &config)
|
||||
}
|
||||
|
||||
@@ -515,9 +494,7 @@ pub fn process_create_nonce_account(
|
||||
Message::new(&ixs, Some(&config.signers[0].pubkey()))
|
||||
};
|
||||
|
||||
let (recent_blockhash, fee_calculator, _) = rpc_client
|
||||
.get_recent_blockhash_with_commitment(config.commitment)?
|
||||
.value;
|
||||
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||
|
||||
let (message, lamports) = resolve_spend_tx_and_check_account_balance(
|
||||
rpc_client,
|
||||
@@ -526,12 +503,9 @@ pub fn process_create_nonce_account(
|
||||
&fee_calculator,
|
||||
&config.signers[0].pubkey(),
|
||||
build_message,
|
||||
config.commitment,
|
||||
)?;
|
||||
|
||||
if let Ok(nonce_account) =
|
||||
get_account_with_commitment(rpc_client, &nonce_account_address, config.commitment)
|
||||
{
|
||||
if let Ok(nonce_account) = get_account(rpc_client, &nonce_account_address) {
|
||||
let err_msg = if state_from_account(&nonce_account).is_ok() {
|
||||
format!("Nonce account {} already exists", nonce_account_address)
|
||||
} else {
|
||||
@@ -554,22 +528,12 @@ pub fn process_create_nonce_account(
|
||||
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
|
||||
&tx,
|
||||
config.commitment,
|
||||
config.send_transaction_config,
|
||||
);
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||
log_instruction_custom_error::<SystemError>(result, &config)
|
||||
}
|
||||
|
||||
pub fn process_get_nonce(
|
||||
rpc_client: &RpcClient,
|
||||
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))?
|
||||
{
|
||||
pub fn process_get_nonce(rpc_client: &RpcClient, nonce_account_pubkey: &Pubkey) -> ProcessResult {
|
||||
match get_account(rpc_client, nonce_account_pubkey).and_then(|ref a| state_from_account(a))? {
|
||||
State::Uninitialized => Ok("Nonce account is uninitialized".to_string()),
|
||||
State::Initialized(ref data) => Ok(format!("{:?}", data.blockhash)),
|
||||
}
|
||||
@@ -586,9 +550,7 @@ pub fn process_new_nonce(
|
||||
(&nonce_account, "nonce_account_pubkey".to_string()),
|
||||
)?;
|
||||
|
||||
let nonce_account_check =
|
||||
rpc_client.get_account_with_commitment(&nonce_account, config.commitment);
|
||||
if nonce_account_check.is_err() || nonce_account_check.unwrap().value.is_none() {
|
||||
if rpc_client.get_account(&nonce_account).is_err() {
|
||||
return Err(CliError::BadParameter(
|
||||
"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 ix = advance_nonce_account(&nonce_account, &nonce_authority.pubkey());
|
||||
let (recent_blockhash, fee_calculator, _) = rpc_client
|
||||
.get_recent_blockhash_with_commitment(config.commitment)?
|
||||
.value;
|
||||
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||
let message = Message::new(&[ix], Some(&config.signers[0].pubkey()));
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||
check_account_for_fee_with_commitment(
|
||||
check_account_for_fee(
|
||||
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,
|
||||
);
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||
log_instruction_custom_error::<SystemError>(result, &config)
|
||||
}
|
||||
|
||||
@@ -624,8 +579,7 @@ pub fn process_show_nonce_account(
|
||||
nonce_account_pubkey: &Pubkey,
|
||||
use_lamports_unit: bool,
|
||||
) -> ProcessResult {
|
||||
let nonce_account =
|
||||
get_account_with_commitment(rpc_client, nonce_account_pubkey, config.commitment)?;
|
||||
let nonce_account = get_account(rpc_client, nonce_account_pubkey)?;
|
||||
let print_account = |data: Option<&nonce::state::Data>| {
|
||||
let mut nonce_account = CliNonceAccount {
|
||||
balance: nonce_account.lamports,
|
||||
@@ -656,9 +610,7 @@ pub fn process_withdraw_from_nonce_account(
|
||||
destination_account_pubkey: &Pubkey,
|
||||
lamports: u64,
|
||||
) -> ProcessResult {
|
||||
let (recent_blockhash, fee_calculator, _) = rpc_client
|
||||
.get_recent_blockhash_with_commitment(config.commitment)?
|
||||
.value;
|
||||
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||
|
||||
let nonce_authority = config.signers[nonce_authority];
|
||||
let ix = withdraw_nonce_account(
|
||||
@@ -670,18 +622,13 @@ pub fn process_withdraw_from_nonce_account(
|
||||
let message = Message::new(&[ix], Some(&config.signers[0].pubkey()));
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||
check_account_for_fee_with_commitment(
|
||||
check_account_for_fee(
|
||||
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,
|
||||
);
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||
log_instruction_custom_error::<NonceError>(result, &config)
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,4 @@
|
||||
use super::*;
|
||||
use solana_sdk::commitment_config::CommitmentConfig;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Source {
|
||||
@@ -11,17 +10,14 @@ impl Source {
|
||||
pub fn get_blockhash_and_fee_calculator(
|
||||
&self,
|
||||
rpc_client: &RpcClient,
|
||||
commitment: CommitmentConfig,
|
||||
) -> Result<(Hash, FeeCalculator), Box<dyn std::error::Error>> {
|
||||
match self {
|
||||
Self::Cluster => {
|
||||
let res = rpc_client
|
||||
.get_recent_blockhash_with_commitment(commitment)?
|
||||
.value;
|
||||
Ok((res.0, res.1))
|
||||
let res = rpc_client.get_recent_blockhash()?;
|
||||
Ok(res)
|
||||
}
|
||||
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))?;
|
||||
Ok((data.blockhash, data.fee_calculator))
|
||||
}
|
||||
@@ -32,21 +28,23 @@ impl Source {
|
||||
&self,
|
||||
rpc_client: &RpcClient,
|
||||
blockhash: &Hash,
|
||||
commitment: CommitmentConfig,
|
||||
) -> Result<Option<FeeCalculator>, Box<dyn std::error::Error>> {
|
||||
match self {
|
||||
Self::Cluster => {
|
||||
let res = rpc_client
|
||||
.get_fee_calculator_for_blockhash_with_commitment(blockhash, commitment)?
|
||||
.value;
|
||||
let res = rpc_client.get_fee_calculator_for_blockhash(blockhash)?;
|
||||
Ok(res)
|
||||
}
|
||||
Self::NonceAccount(ref pubkey) => {
|
||||
let res = nonce::get_account_with_commitment(rpc_client, pubkey, commitment)?;
|
||||
let res = nonce::data_from_account(&res)?;
|
||||
Ok(Some(res)
|
||||
.filter(|d| d.blockhash == *blockhash)
|
||||
.map(|d| d.fee_calculator))
|
||||
let res = nonce::get_account(rpc_client, pubkey)
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.and_then(|d| {
|
||||
if d.blockhash == *blockhash {
|
||||
Ok(Some(d.fee_calculator))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
})?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,19 +80,16 @@ impl BlockhashQuery {
|
||||
pub fn get_blockhash_and_fee_calculator(
|
||||
&self,
|
||||
rpc_client: &RpcClient,
|
||||
commitment: CommitmentConfig,
|
||||
) -> Result<(Hash, FeeCalculator), Box<dyn std::error::Error>> {
|
||||
match self {
|
||||
BlockhashQuery::None(hash) => Ok((*hash, FeeCalculator::default())),
|
||||
BlockhashQuery::FeeCalculator(source, hash) => {
|
||||
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((*hash, fee_calculator))
|
||||
}
|
||||
BlockhashQuery::All(source) => {
|
||||
source.get_blockhash_and_fee_calculator(rpc_client, commitment)
|
||||
}
|
||||
BlockhashQuery::All(source) => source.get_blockhash_and_fee_calculator(rpc_client),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -298,7 +293,7 @@ mod tests {
|
||||
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
|
||||
assert_eq!(
|
||||
BlockhashQuery::default()
|
||||
.get_blockhash_and_fee_calculator(&rpc_client, CommitmentConfig::default())
|
||||
.get_blockhash_and_fee_calculator(&rpc_client)
|
||||
.unwrap(),
|
||||
(rpc_blockhash, rpc_fee_calc.clone()),
|
||||
);
|
||||
@@ -314,7 +309,7 @@ mod tests {
|
||||
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
|
||||
assert_eq!(
|
||||
BlockhashQuery::FeeCalculator(Source::Cluster, test_blockhash)
|
||||
.get_blockhash_and_fee_calculator(&rpc_client, CommitmentConfig::default())
|
||||
.get_blockhash_and_fee_calculator(&rpc_client)
|
||||
.unwrap(),
|
||||
(test_blockhash, rpc_fee_calc),
|
||||
);
|
||||
@@ -326,13 +321,13 @@ mod tests {
|
||||
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
|
||||
assert_eq!(
|
||||
BlockhashQuery::None(test_blockhash)
|
||||
.get_blockhash_and_fee_calculator(&rpc_client, CommitmentConfig::default())
|
||||
.get_blockhash_and_fee_calculator(&rpc_client)
|
||||
.unwrap(),
|
||||
(test_blockhash, FeeCalculator::default()),
|
||||
);
|
||||
let rpc_client = RpcClient::new_mock("fails".to_string());
|
||||
assert!(BlockhashQuery::default()
|
||||
.get_blockhash_and_fee_calculator(&rpc_client, CommitmentConfig::default())
|
||||
.get_blockhash_and_fee_calculator(&rpc_client)
|
||||
.is_err());
|
||||
|
||||
let nonce_blockhash = Hash::new(&[2u8; 32]);
|
||||
@@ -350,7 +345,13 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
let nonce_pubkey = Pubkey::new(&[4u8; 32]);
|
||||
let rpc_nonce_account = UiAccount::encode(nonce_account, UiAccountEncoding::Binary, None);
|
||||
let rpc_nonce_account = UiAccount::encode(
|
||||
&nonce_pubkey,
|
||||
nonce_account,
|
||||
UiAccountEncoding::Base64,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
let get_account_response = json!(Response {
|
||||
context: RpcResponseContext { slot: 1 },
|
||||
value: json!(Some(rpc_nonce_account)),
|
||||
@@ -361,7 +362,7 @@ mod tests {
|
||||
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
|
||||
assert_eq!(
|
||||
BlockhashQuery::All(Source::NonceAccount(nonce_pubkey))
|
||||
.get_blockhash_and_fee_calculator(&rpc_client, CommitmentConfig::default())
|
||||
.get_blockhash_and_fee_calculator(&rpc_client)
|
||||
.unwrap(),
|
||||
(nonce_blockhash, nonce_fee_calc.clone()),
|
||||
);
|
||||
@@ -370,7 +371,7 @@ mod tests {
|
||||
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
|
||||
assert_eq!(
|
||||
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(),
|
||||
(nonce_blockhash, nonce_fee_calc),
|
||||
);
|
||||
@@ -379,7 +380,7 @@ mod tests {
|
||||
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
|
||||
assert!(
|
||||
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()
|
||||
);
|
||||
let mut mocks = HashMap::new();
|
||||
@@ -387,14 +388,14 @@ mod tests {
|
||||
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
|
||||
assert_eq!(
|
||||
BlockhashQuery::None(nonce_blockhash)
|
||||
.get_blockhash_and_fee_calculator(&rpc_client, CommitmentConfig::default())
|
||||
.get_blockhash_and_fee_calculator(&rpc_client)
|
||||
.unwrap(),
|
||||
(nonce_blockhash, FeeCalculator::default()),
|
||||
);
|
||||
|
||||
let rpc_client = RpcClient::new_mock("fails".to_string());
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
@@ -1,13 +1,12 @@
|
||||
use crate::{
|
||||
checks::{calculate_fee, check_account_for_balance_with_commitment},
|
||||
checks::{calculate_fee, check_account_for_balance},
|
||||
cli::CliError,
|
||||
};
|
||||
use clap::ArgMatches;
|
||||
use solana_clap_utils::{input_parsers::lamports_of_sol, offline::SIGN_ONLY_ARG};
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_sdk::{
|
||||
commitment_config::CommitmentConfig, fee_calculator::FeeCalculator, message::Message,
|
||||
native_token::lamports_to_sol, pubkey::Pubkey,
|
||||
fee_calculator::FeeCalculator, message::Message, native_token::lamports_to_sol, pubkey::Pubkey,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
@@ -50,7 +49,6 @@ pub fn resolve_spend_tx_and_check_account_balance<F>(
|
||||
fee_calculator: &FeeCalculator,
|
||||
from_pubkey: &Pubkey,
|
||||
build_message: F,
|
||||
commitment: CommitmentConfig,
|
||||
) -> Result<(Message, u64), CliError>
|
||||
where
|
||||
F: Fn(u64) -> Message,
|
||||
@@ -63,7 +61,6 @@ where
|
||||
from_pubkey,
|
||||
from_pubkey,
|
||||
build_message,
|
||||
commitment,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -75,7 +72,6 @@ pub fn resolve_spend_tx_and_check_account_balances<F>(
|
||||
from_pubkey: &Pubkey,
|
||||
fee_pubkey: &Pubkey,
|
||||
build_message: F,
|
||||
commitment: CommitmentConfig,
|
||||
) -> Result<(Message, u64), CliError>
|
||||
where
|
||||
F: Fn(u64) -> Message,
|
||||
@@ -91,9 +87,7 @@ where
|
||||
);
|
||||
Ok((message, spend))
|
||||
} else {
|
||||
let from_balance = rpc_client
|
||||
.get_balance_with_commitment(&from_pubkey, commitment)?
|
||||
.value;
|
||||
let from_balance = rpc_client.get_balance(&from_pubkey)?;
|
||||
let (message, SpendAndFee { spend, fee }) = resolve_spend_message(
|
||||
amount,
|
||||
fee_calculator,
|
||||
@@ -113,8 +107,7 @@ where
|
||||
if from_balance < 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)));
|
||||
}
|
||||
}
|
||||
|
170
cli/src/stake.rs
170
cli/src/stake.rs
@@ -1,12 +1,12 @@
|
||||
use crate::{
|
||||
checks::{check_account_for_fee_with_commitment, check_unique_pubkeys},
|
||||
checks::{check_account_for_fee, check_unique_pubkeys},
|
||||
cli::{
|
||||
fee_payer_arg, generate_unique_signers, log_instruction_custom_error, nonce_authority_arg,
|
||||
return_signers, CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult,
|
||||
SignerIndex, FEE_PAYER_ARG,
|
||||
},
|
||||
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, *},
|
||||
spend_utils::{resolve_spend_tx_and_check_account_balances, SpendAmount},
|
||||
};
|
||||
@@ -16,12 +16,10 @@ use solana_client::{rpc_client::RpcClient, rpc_request::DELINQUENT_VALIDATOR_SLO
|
||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||
use solana_sdk::{
|
||||
account_utils::StateMut,
|
||||
clock::Clock,
|
||||
message::Message,
|
||||
pubkey::Pubkey,
|
||||
system_instruction::SystemError,
|
||||
sysvar::{
|
||||
clock,
|
||||
stake_history::{self, StakeHistory},
|
||||
Sysvar,
|
||||
},
|
||||
@@ -896,7 +894,7 @@ pub fn process_create_stake_account(
|
||||
};
|
||||
|
||||
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(
|
||||
rpc_client,
|
||||
@@ -906,7 +904,6 @@ pub fn process_create_stake_account(
|
||||
&from.pubkey(),
|
||||
&fee_payer.pubkey(),
|
||||
build_message,
|
||||
config.commitment,
|
||||
)?;
|
||||
|
||||
if !sign_only {
|
||||
@@ -934,8 +931,7 @@ pub fn process_create_stake_account(
|
||||
}
|
||||
|
||||
if let Some(nonce_account) = &nonce_account {
|
||||
let nonce_account =
|
||||
nonce::get_account_with_commitment(rpc_client, nonce_account, config.commitment)?;
|
||||
let nonce_account = rpc_client.get_account(nonce_account)?;
|
||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
||||
}
|
||||
}
|
||||
@@ -946,11 +942,7 @@ pub fn process_create_stake_account(
|
||||
return_signers(&tx, &config)
|
||||
} else {
|
||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
|
||||
&tx,
|
||||
config.commitment,
|
||||
config.send_transaction_config,
|
||||
);
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||
log_instruction_custom_error::<SystemError>(result, &config)
|
||||
}
|
||||
}
|
||||
@@ -983,7 +975,7 @@ pub fn process_stake_authorize(
|
||||
}
|
||||
|
||||
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 fee_payer = config.signers[fee_payer];
|
||||
@@ -1006,22 +998,16 @@ pub fn process_stake_authorize(
|
||||
} 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)?;
|
||||
let nonce_account = rpc_client.get_account(nonce_account)?;
|
||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
||||
}
|
||||
check_account_for_fee_with_commitment(
|
||||
check_account_for_fee(
|
||||
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,
|
||||
);
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||
log_instruction_custom_error::<StakeError>(result, &config)
|
||||
}
|
||||
}
|
||||
@@ -1039,7 +1025,7 @@ pub fn process_deactivate_stake_account(
|
||||
fee_payer: SignerIndex,
|
||||
) -> ProcessResult {
|
||||
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 ixs = vec![stake_instruction::deactivate_stake(
|
||||
stake_account_pubkey,
|
||||
@@ -1066,22 +1052,16 @@ pub fn process_deactivate_stake_account(
|
||||
} 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)?;
|
||||
let nonce_account = rpc_client.get_account(nonce_account)?;
|
||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
||||
}
|
||||
check_account_for_fee_with_commitment(
|
||||
check_account_for_fee(
|
||||
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,
|
||||
);
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||
log_instruction_custom_error::<StakeError>(result, &config)
|
||||
}
|
||||
}
|
||||
@@ -1102,7 +1082,7 @@ pub fn process_withdraw_stake(
|
||||
fee_payer: SignerIndex,
|
||||
) -> ProcessResult {
|
||||
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 custodian = custodian.map(|index| config.signers[index]);
|
||||
|
||||
@@ -1135,22 +1115,16 @@ pub fn process_withdraw_stake(
|
||||
} 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)?;
|
||||
let nonce_account = rpc_client.get_account(nonce_account)?;
|
||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
||||
}
|
||||
check_account_for_fee_with_commitment(
|
||||
check_account_for_fee(
|
||||
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,
|
||||
);
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||
log_instruction_custom_error::<SystemError>(result, &config)
|
||||
}
|
||||
}
|
||||
@@ -1235,7 +1209,7 @@ pub fn process_split_stake(
|
||||
}
|
||||
|
||||
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 {
|
||||
stake_instruction::split_with_seed(
|
||||
@@ -1275,22 +1249,16 @@ pub fn process_split_stake(
|
||||
} 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)?;
|
||||
let nonce_account = rpc_client.get_account(nonce_account)?;
|
||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
||||
}
|
||||
check_account_for_fee_with_commitment(
|
||||
check_account_for_fee(
|
||||
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,
|
||||
);
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||
log_instruction_custom_error::<StakeError>(result, &config)
|
||||
}
|
||||
}
|
||||
@@ -1346,7 +1314,7 @@ pub fn process_merge_stake(
|
||||
}
|
||||
|
||||
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 = stake_instruction::merge(
|
||||
&stake_account_pubkey,
|
||||
@@ -1374,22 +1342,16 @@ pub fn process_merge_stake(
|
||||
} 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)?;
|
||||
let nonce_account = rpc_client.get_account(nonce_account)?;
|
||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
||||
}
|
||||
check_account_for_fee_with_commitment(
|
||||
check_account_for_fee(
|
||||
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,
|
||||
);
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||
log_instruction_custom_error::<StakeError>(result, &config)
|
||||
}
|
||||
}
|
||||
@@ -1408,7 +1370,7 @@ pub fn process_stake_set_lockup(
|
||||
fee_payer: SignerIndex,
|
||||
) -> ProcessResult {
|
||||
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 ixs = vec![stake_instruction::set_lockup(
|
||||
@@ -1437,22 +1399,16 @@ pub fn process_stake_set_lockup(
|
||||
} 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)?;
|
||||
let nonce_account = rpc_client.get_account(nonce_account)?;
|
||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
||||
}
|
||||
check_account_for_fee_with_commitment(
|
||||
check_account_for_fee(
|
||||
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,
|
||||
);
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||
log_instruction_custom_error::<StakeError>(result, &config)
|
||||
}
|
||||
}
|
||||
@@ -1470,7 +1426,6 @@ pub fn build_stake_state(
|
||||
stake_state: &StakeState,
|
||||
use_lamports_unit: bool,
|
||||
stake_history: &StakeHistory,
|
||||
clock: &Clock,
|
||||
) -> CliStakeState {
|
||||
match stake_state {
|
||||
StakeState::Stake(
|
||||
@@ -1481,15 +1436,11 @@ pub fn build_stake_state(
|
||||
},
|
||||
stake,
|
||||
) => {
|
||||
let current_epoch = clock.epoch;
|
||||
// The first entry in stake history is the previous epoch, so +1 for current
|
||||
let current_epoch = stake_history.iter().next().unwrap().0 + 1;
|
||||
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,
|
||||
account_balance,
|
||||
@@ -1512,7 +1463,7 @@ pub fn build_stake_state(
|
||||
None
|
||||
},
|
||||
authorized: Some(authorized.into()),
|
||||
lockup,
|
||||
lockup: Some(lockup.into()),
|
||||
use_lamports_unit,
|
||||
current_epoch,
|
||||
rent_exempt_reserve: Some(*rent_exempt_reserve),
|
||||
@@ -1534,22 +1485,15 @@ pub fn build_stake_state(
|
||||
rent_exempt_reserve,
|
||||
authorized,
|
||||
lockup,
|
||||
}) => {
|
||||
let lockup = if lockup.is_in_force(clock, None) {
|
||||
Some(lockup.into())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
CliStakeState {
|
||||
stake_type: CliStakeType::Initialized,
|
||||
account_balance,
|
||||
authorized: Some(authorized.into()),
|
||||
lockup,
|
||||
use_lamports_unit,
|
||||
rent_exempt_reserve: Some(*rent_exempt_reserve),
|
||||
..CliStakeState::default()
|
||||
}
|
||||
}
|
||||
}) => CliStakeState {
|
||||
stake_type: CliStakeType::Initialized,
|
||||
account_balance,
|
||||
authorized: Some(authorized.into()),
|
||||
lockup: Some(lockup.into()),
|
||||
use_lamports_unit,
|
||||
rent_exempt_reserve: Some(*rent_exempt_reserve),
|
||||
..CliStakeState::default()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1574,17 +1518,12 @@ pub fn process_show_stake_account(
|
||||
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))
|
||||
}
|
||||
@@ -1640,23 +1579,14 @@ pub fn process_delegate_stake(
|
||||
if !sign_only {
|
||||
// Sanity check the vote account to ensure it is attached to a validator that has recently
|
||||
// voted at the tip of the ledger
|
||||
let vote_account = rpc_client
|
||||
.get_account_with_commitment(vote_account_pubkey, config.commitment)
|
||||
let vote_account_data = rpc_client
|
||||
.get_account_data(vote_account_pubkey)
|
||||
.map_err(|_| {
|
||||
CliError::RpcRequestError(format!(
|
||||
"Vote account not found: {}",
|
||||
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(|_| {
|
||||
CliError::RpcRequestError(
|
||||
@@ -1694,7 +1624,7 @@ pub fn process_delegate_stake(
|
||||
}
|
||||
|
||||
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(
|
||||
stake_account_pubkey,
|
||||
@@ -1722,22 +1652,16 @@ pub fn process_delegate_stake(
|
||||
} 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)?;
|
||||
let nonce_account = rpc_client.get_account(nonce_account)?;
|
||||
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
||||
}
|
||||
check_account_for_fee_with_commitment(
|
||||
check_account_for_fee(
|
||||
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,
|
||||
);
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||
log_instruction_custom_error::<StakeError>(result, &config)
|
||||
}
|
||||
}
|
||||
|
@@ -1,13 +1,10 @@
|
||||
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};
|
||||
|
||||
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| {
|
||||
let balance = client
|
||||
.get_balance_with_commitment(pubkey, CommitmentConfig::recent())
|
||||
.unwrap()
|
||||
.value;
|
||||
let balance = client.get_balance(pubkey).unwrap();
|
||||
if balance == expected_balance {
|
||||
return;
|
||||
}
|
||||
@@ -17,13 +14,3 @@ pub fn check_recent_balance(expected_balance: u64, client: &RpcClient, pubkey: &
|
||||
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,9 +6,10 @@ use crate::{
|
||||
use bincode::deserialize;
|
||||
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
|
||||
use reqwest::blocking::Client;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
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::{
|
||||
input_parsers::pubkey_of,
|
||||
input_validators::{is_pubkey, is_url},
|
||||
@@ -27,23 +28,6 @@ use solana_sdk::{
|
||||
};
|
||||
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.
|
||||
pub fn check_details_length(string: String) -> Result<(), String> {
|
||||
if string.len() > MAX_LONG_FIELD_LENGTH {
|
||||
@@ -287,10 +271,12 @@ pub fn process_set_validator_info(
|
||||
let all_config = rpc_client.get_program_accounts(&solana_config_program::id())?;
|
||||
let existing_account = all_config
|
||||
.iter()
|
||||
.filter(|(_, account)| {
|
||||
let key_list: ConfigKeys = deserialize(&account.data).map_err(|_| false).unwrap();
|
||||
key_list.keys.contains(&(id(), false))
|
||||
})
|
||||
.filter(
|
||||
|(_, account)| match deserialize::<ConfigKeys>(&account.data) {
|
||||
Ok(key_list) => key_list.keys.contains(&(validator_info::id(), false)),
|
||||
Err(_) => false,
|
||||
},
|
||||
)
|
||||
.find(|(pubkey, account)| {
|
||||
let (validator_pubkey, _) = parse_validator_info(&pubkey, &account).unwrap();
|
||||
validator_pubkey == config.signers[0].pubkey()
|
||||
@@ -328,7 +314,10 @@ pub fn process_set_validator_info(
|
||||
};
|
||||
|
||||
let build_message = |lamports| {
|
||||
let keys = vec![(id(), false), (config.signers[0].pubkey(), true)];
|
||||
let keys = vec![
|
||||
(validator_info::id(), false),
|
||||
(config.signers[0].pubkey(), true),
|
||||
];
|
||||
if balance == 0 {
|
||||
println!(
|
||||
"Publishing info for Validator {:?}",
|
||||
@@ -372,7 +361,6 @@ pub fn process_set_validator_info(
|
||||
&fee_calculator,
|
||||
&config.signers[0].pubkey(),
|
||||
build_message,
|
||||
config.commitment,
|
||||
)?;
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
tx.try_sign(&signers, recent_blockhash)?;
|
||||
@@ -398,10 +386,10 @@ pub fn process_get_validator_info(
|
||||
all_config
|
||||
.into_iter()
|
||||
.filter(|(_, validator_info_account)| {
|
||||
let key_list: ConfigKeys = deserialize(&validator_info_account.data)
|
||||
.map_err(|_| false)
|
||||
.unwrap();
|
||||
key_list.keys.contains(&(id(), false))
|
||||
match deserialize::<ConfigKeys>(&validator_info_account.data) {
|
||||
Ok(key_list) => key_list.keys.contains(&(validator_info::id(), false)),
|
||||
Err(_) => false,
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
@@ -503,7 +491,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_parse_validator_info() {
|
||||
let pubkey = Pubkey::new_rand();
|
||||
let keys = vec![(id(), false), (pubkey, true)];
|
||||
let keys = vec![(validator_info::id(), false), (pubkey, true)];
|
||||
let config = ConfigKeys { keys };
|
||||
|
||||
let mut info = Map::new();
|
||||
|
104
cli/src/vote.rs
104
cli/src/vote.rs
@@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
checks::{check_account_for_fee_with_commitment, check_unique_pubkeys},
|
||||
checks::{check_account_for_fee, check_unique_pubkeys},
|
||||
cli::{
|
||||
generate_unique_signers, log_instruction_custom_error, CliCommand, CliCommandInfo,
|
||||
CliConfig, CliError, ProcessResult, SignerIndex,
|
||||
@@ -8,7 +8,11 @@ use crate::{
|
||||
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
|
||||
};
|
||||
use clap::{value_t_or_exit, App, Arg, ArgMatches, SubCommand};
|
||||
use solana_clap_utils::{commitment::commitment_arg, input_parsers::*, input_validators::*};
|
||||
use solana_clap_utils::{
|
||||
commitment::{commitment_arg, COMMITMENT_ARG},
|
||||
input_parsers::*,
|
||||
input_validators::*,
|
||||
};
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||
use solana_sdk::{
|
||||
@@ -374,10 +378,12 @@ pub fn parse_vote_get_account_command(
|
||||
let vote_account_pubkey =
|
||||
pubkey_of_signer(matches, "vote_account_pubkey", wallet_manager)?.unwrap();
|
||||
let use_lamports_unit = matches.is_present("lamports");
|
||||
let commitment_config = commitment_of(matches, COMMITMENT_ARG.long).unwrap();
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::ShowVoteAccount {
|
||||
pubkey: vote_account_pubkey,
|
||||
use_lamports_unit,
|
||||
commitment_config,
|
||||
},
|
||||
signers: vec![],
|
||||
})
|
||||
@@ -478,25 +484,19 @@ pub fn process_create_vote_account(
|
||||
Message::new(&ixs, Some(&config.signers[0].pubkey()))
|
||||
};
|
||||
|
||||
if let Ok(response) =
|
||||
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() {
|
||||
format!("Vote account {} already exists", vote_account_address)
|
||||
} else {
|
||||
format!(
|
||||
"Account {} already exists and is not a vote account",
|
||||
vote_account_address
|
||||
)
|
||||
};
|
||||
return Err(CliError::BadParameter(err_msg).into());
|
||||
}
|
||||
if let Ok(vote_account) = rpc_client.get_account(&vote_account_address) {
|
||||
let err_msg = if vote_account.owner == solana_vote_program::id() {
|
||||
format!("Vote account {} already exists", vote_account_address)
|
||||
} else {
|
||||
format!(
|
||||
"Account {} already exists and is not a vote account",
|
||||
vote_account_address
|
||||
)
|
||||
};
|
||||
return Err(CliError::BadParameter(err_msg).into());
|
||||
}
|
||||
|
||||
let (recent_blockhash, fee_calculator, _) = rpc_client
|
||||
.get_recent_blockhash_with_commitment(config.commitment)?
|
||||
.value;
|
||||
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||
|
||||
let (message, _) = resolve_spend_tx_and_check_account_balance(
|
||||
rpc_client,
|
||||
@@ -505,15 +505,10 @@ pub fn process_create_vote_account(
|
||||
&fee_calculator,
|
||||
&config.signers[0].pubkey(),
|
||||
build_message,
|
||||
config.commitment,
|
||||
)?;
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
|
||||
&tx,
|
||||
config.commitment,
|
||||
config.send_transaction_config,
|
||||
);
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||
log_instruction_custom_error::<SystemError>(result, &config)
|
||||
}
|
||||
|
||||
@@ -536,9 +531,7 @@ pub fn process_vote_authorize(
|
||||
(&authorized.pubkey(), "authorized_account".to_string()),
|
||||
(new_authorized_pubkey, "new_authorized_pubkey".to_string()),
|
||||
)?;
|
||||
let (recent_blockhash, fee_calculator, _) = rpc_client
|
||||
.get_recent_blockhash_with_commitment(config.commitment)?
|
||||
.value;
|
||||
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||
let ixs = vec![vote_instruction::authorize(
|
||||
vote_account_pubkey, // vote account to update
|
||||
&authorized.pubkey(), // current authorized
|
||||
@@ -549,18 +542,13 @@ pub fn process_vote_authorize(
|
||||
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(
|
||||
check_account_for_fee(
|
||||
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,
|
||||
);
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||
log_instruction_custom_error::<VoteError>(result, &config)
|
||||
}
|
||||
|
||||
@@ -578,9 +566,7 @@ pub fn process_vote_update_validator(
|
||||
(vote_account_pubkey, "vote_account_pubkey".to_string()),
|
||||
(&new_identity_pubkey, "new_identity_account".to_string()),
|
||||
)?;
|
||||
let (recent_blockhash, fee_calculator, _) = rpc_client
|
||||
.get_recent_blockhash_with_commitment(config.commitment)?
|
||||
.value;
|
||||
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||
let ixs = vec![vote_instruction::update_validator_identity(
|
||||
vote_account_pubkey,
|
||||
&authorized_withdrawer.pubkey(),
|
||||
@@ -590,18 +576,13 @@ pub fn process_vote_update_validator(
|
||||
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(
|
||||
check_account_for_fee(
|
||||
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,
|
||||
);
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||
log_instruction_custom_error::<VoteError>(result, &config)
|
||||
}
|
||||
|
||||
@@ -613,9 +594,7 @@ pub fn process_vote_update_commission(
|
||||
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 (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||
let ixs = vec![vote_instruction::update_commission(
|
||||
vote_account_pubkey,
|
||||
&authorized_withdrawer.pubkey(),
|
||||
@@ -625,18 +604,13 @@ pub fn process_vote_update_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(
|
||||
check_account_for_fee(
|
||||
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,
|
||||
);
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||
log_instruction_custom_error::<VoteError>(result, &config)
|
||||
}
|
||||
|
||||
@@ -673,9 +647,10 @@ pub fn process_show_vote_account(
|
||||
config: &CliConfig,
|
||||
vote_account_pubkey: &Pubkey,
|
||||
use_lamports_unit: bool,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> ProcessResult {
|
||||
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()?;
|
||||
|
||||
@@ -721,14 +696,10 @@ pub fn process_withdraw_from_vote_account(
|
||||
withdraw_amount: SpendAmount,
|
||||
destination_account_pubkey: &Pubkey,
|
||||
) -> ProcessResult {
|
||||
let (recent_blockhash, fee_calculator, _) = rpc_client
|
||||
.get_recent_blockhash_with_commitment(config.commitment)?
|
||||
.value;
|
||||
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||
let withdraw_authority = config.signers[withdraw_authority];
|
||||
|
||||
let current_balance = rpc_client
|
||||
.get_balance_with_commitment(&vote_account_pubkey, config.commitment)?
|
||||
.value;
|
||||
let current_balance = rpc_client.get_balance(&vote_account_pubkey)?;
|
||||
let minimum_balance = rpc_client.get_minimum_balance_for_rent_exemption(VoteState::size_of())?;
|
||||
|
||||
let lamports = match withdraw_amount {
|
||||
@@ -754,18 +725,13 @@ pub fn process_withdraw_from_vote_account(
|
||||
let message = Message::new(&[ix], Some(&config.signers[0].pubkey()));
|
||||
let mut transaction = Transaction::new_unsigned(message);
|
||||
transaction.try_sign(&config.signers, recent_blockhash)?;
|
||||
check_account_for_fee_with_commitment(
|
||||
check_account_for_fee(
|
||||
rpc_client,
|
||||
&config.signers[0].pubkey(),
|
||||
&fee_calculator,
|
||||
&transaction.message,
|
||||
config.commitment,
|
||||
)?;
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
|
||||
&transaction,
|
||||
config.commitment,
|
||||
config.send_transaction_config,
|
||||
);
|
||||
let result = rpc_client.send_and_confirm_transaction_with_spinner(&transaction);
|
||||
log_instruction_custom_error::<VoteError>(result, &config)
|
||||
}
|
||||
|
||||
|
@@ -3,12 +3,7 @@ use solana_cli::cli::{process_command, CliCommand, CliConfig};
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_core::validator::TestValidator;
|
||||
use solana_faucet::faucet::run_local_faucet;
|
||||
use solana_sdk::{
|
||||
bpf_loader,
|
||||
commitment_config::CommitmentConfig,
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, Signer},
|
||||
};
|
||||
use solana_sdk::{bpf_loader, pubkey::Pubkey, signature::Keypair};
|
||||
use std::{
|
||||
fs::{remove_dir_all, File},
|
||||
io::Read,
|
||||
@@ -48,22 +43,19 @@ fn test_cli_deploy_program() {
|
||||
.get_minimum_balance_for_rent_exemption(program_data.len())
|
||||
.unwrap();
|
||||
|
||||
let mut config = CliConfig::recent_for_tests();
|
||||
let mut config = CliConfig::default();
|
||||
let keypair = Keypair::new();
|
||||
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
config.command = CliCommand::Airdrop {
|
||||
faucet_host: None,
|
||||
faucet_port: faucet_addr.port(),
|
||||
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];
|
||||
process_command(&config).unwrap();
|
||||
|
||||
config.command = CliCommand::Deploy {
|
||||
program_location: pathbuf.to_str().unwrap().to_string(),
|
||||
address: None,
|
||||
};
|
||||
config.command = CliCommand::Deploy(pathbuf.to_str().unwrap().to_string());
|
||||
|
||||
let response = process_command(&config);
|
||||
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
|
||||
@@ -75,41 +67,16 @@ fn test_cli_deploy_program() {
|
||||
.as_str()
|
||||
.unwrap();
|
||||
let program_id = Pubkey::from_str(&program_id_str).unwrap();
|
||||
let account0 = rpc_client
|
||||
.get_account_with_commitment(&program_id, CommitmentConfig::recent())
|
||||
.unwrap()
|
||||
.value
|
||||
.unwrap();
|
||||
assert_eq!(account0.lamports, minimum_balance_for_rent_exemption);
|
||||
assert_eq!(account0.owner, bpf_loader::id());
|
||||
assert_eq!(account0.executable, true);
|
||||
let account = rpc_client.get_account(&program_id).unwrap();
|
||||
assert_eq!(account.lamports, minimum_balance_for_rent_exemption);
|
||||
assert_eq!(account.owner, bpf_loader::id());
|
||||
assert_eq!(account.executable, true);
|
||||
|
||||
let mut file = File::open(pathbuf.to_str().unwrap().to_string()).unwrap();
|
||||
let mut elf = Vec::new();
|
||||
file.read_to_end(&mut elf).unwrap();
|
||||
|
||||
assert_eq!(account0.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),
|
||||
};
|
||||
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();
|
||||
assert_eq!(account.data, elf);
|
||||
|
||||
server.close().unwrap();
|
||||
remove_dir_all(ledger_path).unwrap();
|
||||
|
@@ -1,3 +1,4 @@
|
||||
use solana_cli::test_utils::check_balance;
|
||||
use solana_cli::{
|
||||
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
|
||||
cli_output::OutputFormat,
|
||||
@@ -7,14 +8,12 @@ use solana_cli::{
|
||||
parse_sign_only_reply_string,
|
||||
},
|
||||
spend_utils::SpendAmount,
|
||||
test_utils::{check_ready, check_recent_balance},
|
||||
};
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_core::contact_info::ContactInfo;
|
||||
use solana_core::validator::{TestValidator, TestValidatorOptions};
|
||||
use solana_faucet::faucet::run_local_faucet;
|
||||
use solana_sdk::{
|
||||
commitment_config::CommitmentConfig,
|
||||
hash::Hash,
|
||||
pubkey::Pubkey,
|
||||
signature::{keypair_from_seed, Keypair, Signer},
|
||||
@@ -84,7 +83,7 @@ fn full_battery_tests(
|
||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||
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();
|
||||
let payer = Keypair::new();
|
||||
config_payer.signers = vec![&payer];
|
||||
@@ -97,9 +96,9 @@ fn full_battery_tests(
|
||||
&config_payer,
|
||||
)
|
||||
.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;
|
||||
let nonce_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
||||
config_nonce.signers = vec![&nonce_keypair];
|
||||
@@ -132,8 +131,8 @@ fn full_battery_tests(
|
||||
};
|
||||
|
||||
process_command(&config_payer).unwrap();
|
||||
check_recent_balance(1000, &rpc_client, &config_payer.signers[0].pubkey());
|
||||
check_recent_balance(1000, &rpc_client, &nonce_account);
|
||||
check_balance(1000, &rpc_client, &config_payer.signers[0].pubkey());
|
||||
check_balance(1000, &rpc_client, &nonce_account);
|
||||
|
||||
// Get nonce
|
||||
config_payer.signers.pop();
|
||||
@@ -182,9 +181,9 @@ fn full_battery_tests(
|
||||
lamports: 100,
|
||||
};
|
||||
process_command(&config_payer).unwrap();
|
||||
check_recent_balance(1000, &rpc_client, &config_payer.signers[0].pubkey());
|
||||
check_recent_balance(900, &rpc_client, &nonce_account);
|
||||
check_recent_balance(100, &rpc_client, &payee_pubkey);
|
||||
check_balance(1000, &rpc_client, &config_payer.signers[0].pubkey());
|
||||
check_balance(900, &rpc_client, &nonce_account);
|
||||
check_balance(100, &rpc_client, &payee_pubkey);
|
||||
|
||||
// Show nonce account
|
||||
config_payer.command = CliCommand::ShowNonceAccount {
|
||||
@@ -225,9 +224,9 @@ fn full_battery_tests(
|
||||
lamports: 100,
|
||||
};
|
||||
process_command(&config_payer).unwrap();
|
||||
check_recent_balance(1000, &rpc_client, &config_payer.signers[0].pubkey());
|
||||
check_recent_balance(800, &rpc_client, &nonce_account);
|
||||
check_recent_balance(200, &rpc_client, &payee_pubkey);
|
||||
check_balance(1000, &rpc_client, &config_payer.signers[0].pubkey());
|
||||
check_balance(800, &rpc_client, &nonce_account);
|
||||
check_balance(200, &rpc_client, &payee_pubkey);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -251,7 +250,7 @@ fn test_create_account_with_seed() {
|
||||
let offline_nonce_authority_signer = keypair_from_seed(&[1u8; 32]).unwrap();
|
||||
let online_nonce_creator_signer = keypair_from_seed(&[2u8; 32]).unwrap();
|
||||
let to_address = Pubkey::new(&[3u8; 32]);
|
||||
let config = CliConfig::recent_for_tests();
|
||||
let config = CliConfig::default();
|
||||
|
||||
// Setup accounts
|
||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||
@@ -271,11 +270,9 @@ fn test_create_account_with_seed() {
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
check_recent_balance(42, &rpc_client, &offline_nonce_authority_signer.pubkey());
|
||||
check_recent_balance(4242, &rpc_client, &online_nonce_creator_signer.pubkey());
|
||||
check_recent_balance(0, &rpc_client, &to_address);
|
||||
|
||||
check_ready(&rpc_client);
|
||||
check_balance(42, &rpc_client, &offline_nonce_authority_signer.pubkey());
|
||||
check_balance(4242, &rpc_client, &online_nonce_creator_signer.pubkey());
|
||||
check_balance(0, &rpc_client, &to_address);
|
||||
|
||||
// Create nonce account
|
||||
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 nonce_address =
|
||||
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 =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
creator_config.signers = vec![&online_nonce_creator_signer];
|
||||
@@ -296,20 +293,19 @@ fn test_create_account_with_seed() {
|
||||
amount: SpendAmount::Some(241),
|
||||
};
|
||||
process_command(&creator_config).unwrap();
|
||||
check_recent_balance(241, &rpc_client, &nonce_address);
|
||||
check_recent_balance(42, &rpc_client, &offline_nonce_authority_signer.pubkey());
|
||||
check_recent_balance(4000, &rpc_client, &online_nonce_creator_signer.pubkey());
|
||||
check_recent_balance(0, &rpc_client, &to_address);
|
||||
check_balance(241, &rpc_client, &nonce_address);
|
||||
check_balance(42, &rpc_client, &offline_nonce_authority_signer.pubkey());
|
||||
check_balance(4000, &rpc_client, &online_nonce_creator_signer.pubkey());
|
||||
check_balance(0, &rpc_client, &to_address);
|
||||
|
||||
// Fetch nonce hash
|
||||
let nonce_hash =
|
||||
nonce::get_account_with_commitment(&rpc_client, &nonce_address, CommitmentConfig::recent())
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
let nonce_hash = nonce::get_account(&rpc_client, &nonce_address)
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
|
||||
// Test by creating transfer TX with nonce, fully offline
|
||||
let mut authority_config = CliConfig::recent_for_tests();
|
||||
let mut authority_config = CliConfig::default();
|
||||
authority_config.json_rpc_url = String::default();
|
||||
authority_config.signers = vec![&offline_nonce_authority_signer];
|
||||
// Verify we cannot contact the cluster
|
||||
@@ -333,7 +329,7 @@ fn test_create_account_with_seed() {
|
||||
assert_eq!(sign_only.blockhash, nonce_hash);
|
||||
|
||||
// And submit it
|
||||
let mut submit_config = CliConfig::recent_for_tests();
|
||||
let mut submit_config = CliConfig::default();
|
||||
submit_config.json_rpc_url =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
submit_config.signers = vec![&authority_presigner];
|
||||
@@ -352,10 +348,10 @@ fn test_create_account_with_seed() {
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&submit_config).unwrap();
|
||||
check_recent_balance(241, &rpc_client, &nonce_address);
|
||||
check_recent_balance(31, &rpc_client, &offline_nonce_authority_signer.pubkey());
|
||||
check_recent_balance(4000, &rpc_client, &online_nonce_creator_signer.pubkey());
|
||||
check_recent_balance(10, &rpc_client, &to_address);
|
||||
check_balance(241, &rpc_client, &nonce_address);
|
||||
check_balance(31, &rpc_client, &offline_nonce_authority_signer.pubkey());
|
||||
check_balance(4000, &rpc_client, &online_nonce_creator_signer.pubkey());
|
||||
check_balance(10, &rpc_client, &to_address);
|
||||
|
||||
server.close().unwrap();
|
||||
remove_dir_all(ledger_path).unwrap();
|
||||
|
109
cli/tests/pay.rs
109
cli/tests/pay.rs
@@ -1,5 +1,6 @@
|
||||
use chrono::prelude::*;
|
||||
use serde_json::Value;
|
||||
use solana_cli::test_utils::check_balance;
|
||||
use solana_cli::{
|
||||
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig, PayCommand},
|
||||
cli_output::OutputFormat,
|
||||
@@ -9,13 +10,11 @@ use solana_cli::{
|
||||
parse_sign_only_reply_string,
|
||||
},
|
||||
spend_utils::SpendAmount,
|
||||
test_utils::check_recent_balance,
|
||||
};
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_core::validator::TestValidator;
|
||||
use solana_faucet::faucet::run_local_faucet;
|
||||
use solana_sdk::{
|
||||
commitment_config::CommitmentConfig,
|
||||
nonce::State as NonceState,
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, Signer},
|
||||
@@ -41,12 +40,12 @@ fn test_cli_timestamp_tx() {
|
||||
let default_signer0 = 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 =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
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.signers = vec![&default_signer1];
|
||||
|
||||
@@ -63,7 +62,7 @@ fn test_cli_timestamp_tx() {
|
||||
&config_witness,
|
||||
)
|
||||
.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(
|
||||
&rpc_client,
|
||||
@@ -93,17 +92,17 @@ fn test_cli_timestamp_tx() {
|
||||
.expect("base58-encoded public key");
|
||||
let process_id = Pubkey::new(&process_id_vec);
|
||||
|
||||
check_recent_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||
check_recent_balance(10, &rpc_client, &process_id); // contract balance
|
||||
check_recent_balance(0, &rpc_client, &bob_pubkey); // recipient balance
|
||||
check_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||
check_balance(10, &rpc_client, &process_id); // contract balance
|
||||
check_balance(0, &rpc_client, &bob_pubkey); // recipient balance
|
||||
|
||||
// Sign transaction by config_witness
|
||||
config_witness.command = CliCommand::TimeElapsed(bob_pubkey, process_id, dt);
|
||||
process_command(&config_witness).unwrap();
|
||||
|
||||
check_recent_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||
check_recent_balance(0, &rpc_client, &process_id); // contract balance
|
||||
check_recent_balance(10, &rpc_client, &bob_pubkey); // recipient balance
|
||||
check_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||
check_balance(0, &rpc_client, &process_id); // contract balance
|
||||
check_balance(10, &rpc_client, &bob_pubkey); // recipient balance
|
||||
|
||||
server.close().unwrap();
|
||||
remove_dir_all(ledger_path).unwrap();
|
||||
@@ -128,12 +127,12 @@ fn test_cli_witness_tx() {
|
||||
let default_signer0 = 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 =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
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.signers = vec![&default_signer1];
|
||||
|
||||
@@ -175,17 +174,17 @@ fn test_cli_witness_tx() {
|
||||
.expect("base58-encoded public key");
|
||||
let process_id = Pubkey::new(&process_id_vec);
|
||||
|
||||
check_recent_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||
check_recent_balance(10, &rpc_client, &process_id); // contract balance
|
||||
check_recent_balance(0, &rpc_client, &bob_pubkey); // recipient balance
|
||||
check_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||
check_balance(10, &rpc_client, &process_id); // contract balance
|
||||
check_balance(0, &rpc_client, &bob_pubkey); // recipient balance
|
||||
|
||||
// Sign transaction by config_witness
|
||||
config_witness.command = CliCommand::Witness(bob_pubkey, process_id);
|
||||
process_command(&config_witness).unwrap();
|
||||
|
||||
check_recent_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||
check_recent_balance(0, &rpc_client, &process_id); // contract balance
|
||||
check_recent_balance(10, &rpc_client, &bob_pubkey); // recipient balance
|
||||
check_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||
check_balance(0, &rpc_client, &process_id); // contract balance
|
||||
check_balance(10, &rpc_client, &bob_pubkey); // recipient balance
|
||||
|
||||
server.close().unwrap();
|
||||
remove_dir_all(ledger_path).unwrap();
|
||||
@@ -210,12 +209,12 @@ fn test_cli_cancel_tx() {
|
||||
let default_signer0 = 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 =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
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.signers = vec![&default_signer1];
|
||||
|
||||
@@ -250,17 +249,17 @@ fn test_cli_cancel_tx() {
|
||||
.expect("base58-encoded public key");
|
||||
let process_id = Pubkey::new(&process_id_vec);
|
||||
|
||||
check_recent_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||
check_recent_balance(10, &rpc_client, &process_id); // contract balance
|
||||
check_recent_balance(0, &rpc_client, &bob_pubkey); // recipient balance
|
||||
check_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||
check_balance(10, &rpc_client, &process_id); // contract balance
|
||||
check_balance(0, &rpc_client, &bob_pubkey); // recipient balance
|
||||
|
||||
// Sign transaction by config_witness
|
||||
config_payer.command = CliCommand::Cancel(process_id);
|
||||
process_command(&config_payer).unwrap();
|
||||
|
||||
check_recent_balance(50, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||
check_recent_balance(0, &rpc_client, &process_id); // contract balance
|
||||
check_recent_balance(0, &rpc_client, &bob_pubkey); // recipient balance
|
||||
check_balance(50, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance
|
||||
check_balance(0, &rpc_client, &process_id); // contract balance
|
||||
check_balance(0, &rpc_client, &bob_pubkey); // recipient balance
|
||||
|
||||
server.close().unwrap();
|
||||
remove_dir_all(ledger_path).unwrap();
|
||||
@@ -285,11 +284,11 @@ fn test_offline_pay_tx() {
|
||||
let default_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 =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
config_offline.signers = vec![&default_offline_signer];
|
||||
let mut config_online = CliConfig::recent_for_tests();
|
||||
let mut config_online = CliConfig::default();
|
||||
config_online.json_rpc_url =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
config_online.signers = vec![&default_signer];
|
||||
@@ -315,8 +314,8 @@ fn test_offline_pay_tx() {
|
||||
&config_offline,
|
||||
)
|
||||
.unwrap();
|
||||
check_recent_balance(50, &rpc_client, &config_offline.signers[0].pubkey());
|
||||
check_recent_balance(50, &rpc_client, &config_online.signers[0].pubkey());
|
||||
check_balance(50, &rpc_client, &config_offline.signers[0].pubkey());
|
||||
check_balance(50, &rpc_client, &config_online.signers[0].pubkey());
|
||||
|
||||
let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap();
|
||||
config_offline.command = CliCommand::Pay(PayCommand {
|
||||
@@ -329,9 +328,9 @@ fn test_offline_pay_tx() {
|
||||
config_offline.output_format = OutputFormat::JsonCompact;
|
||||
let sig_response = process_command(&config_offline).unwrap();
|
||||
|
||||
check_recent_balance(50, &rpc_client, &config_offline.signers[0].pubkey());
|
||||
check_recent_balance(50, &rpc_client, &config_online.signers[0].pubkey());
|
||||
check_recent_balance(0, &rpc_client, &bob_pubkey);
|
||||
check_balance(50, &rpc_client, &config_offline.signers[0].pubkey());
|
||||
check_balance(50, &rpc_client, &config_online.signers[0].pubkey());
|
||||
check_balance(0, &rpc_client, &bob_pubkey);
|
||||
|
||||
let sign_only = parse_sign_only_reply_string(&sig_response);
|
||||
assert!(sign_only.has_all_signers());
|
||||
@@ -348,9 +347,9 @@ fn test_offline_pay_tx() {
|
||||
});
|
||||
process_command(&config_online).unwrap();
|
||||
|
||||
check_recent_balance(40, &rpc_client, &config_offline.signers[0].pubkey());
|
||||
check_recent_balance(50, &rpc_client, &online_pubkey);
|
||||
check_recent_balance(10, &rpc_client, &bob_pubkey);
|
||||
check_balance(40, &rpc_client, &config_offline.signers[0].pubkey());
|
||||
check_balance(50, &rpc_client, &online_pubkey);
|
||||
check_balance(10, &rpc_client, &bob_pubkey);
|
||||
|
||||
server.close().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 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.signers = vec![&default_signer];
|
||||
|
||||
@@ -390,7 +389,7 @@ fn test_nonced_pay_tx() {
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
check_recent_balance(
|
||||
check_balance(
|
||||
50 + minimum_nonce_balance,
|
||||
&rpc_client,
|
||||
&config.signers[0].pubkey(),
|
||||
@@ -407,18 +406,14 @@ fn test_nonced_pay_tx() {
|
||||
config.signers.push(&nonce_account);
|
||||
process_command(&config).unwrap();
|
||||
|
||||
check_recent_balance(50, &rpc_client, &config.signers[0].pubkey());
|
||||
check_recent_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey());
|
||||
check_balance(50, &rpc_client, &config.signers[0].pubkey());
|
||||
check_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey());
|
||||
|
||||
// Fetch nonce hash
|
||||
let nonce_hash = nonce::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_account.pubkey(),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
|
||||
let bob_pubkey = Pubkey::new_rand();
|
||||
config.signers = vec![&default_signer];
|
||||
@@ -434,18 +429,14 @@ fn test_nonced_pay_tx() {
|
||||
});
|
||||
process_command(&config).expect("failed to process pay command");
|
||||
|
||||
check_recent_balance(40, &rpc_client, &config.signers[0].pubkey());
|
||||
check_recent_balance(10, &rpc_client, &bob_pubkey);
|
||||
check_balance(40, &rpc_client, &config.signers[0].pubkey());
|
||||
check_balance(10, &rpc_client, &bob_pubkey);
|
||||
|
||||
// Verify that nonce has been used
|
||||
let nonce_hash2 = nonce::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_account.pubkey(),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
let nonce_hash2 = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
assert_ne!(nonce_hash, nonce_hash2);
|
||||
|
||||
server.close().unwrap();
|
||||
|
@@ -2,7 +2,7 @@ use solana_cli::cli::{process_command, CliCommand, CliConfig};
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_core::validator::TestValidator;
|
||||
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};
|
||||
|
||||
#[test]
|
||||
@@ -18,7 +18,7 @@ fn test_cli_request_airdrop() {
|
||||
run_local_faucet(alice, sender, None);
|
||||
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.command = CliCommand::Airdrop {
|
||||
faucet_host: None,
|
||||
@@ -35,9 +35,8 @@ fn test_cli_request_airdrop() {
|
||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||
|
||||
let balance = rpc_client
|
||||
.get_balance_with_commitment(&bob_config.signers[0].pubkey(), CommitmentConfig::recent())
|
||||
.unwrap()
|
||||
.value;
|
||||
.get_balance(&bob_config.signers[0].pubkey())
|
||||
.unwrap();
|
||||
assert_eq!(balance, 50);
|
||||
|
||||
server.close().unwrap();
|
||||
|
@@ -1,3 +1,4 @@
|
||||
use solana_cli::test_utils::check_balance;
|
||||
use solana_cli::{
|
||||
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
|
||||
cli_output::OutputFormat,
|
||||
@@ -7,14 +8,12 @@ use solana_cli::{
|
||||
parse_sign_only_reply_string,
|
||||
},
|
||||
spend_utils::SpendAmount,
|
||||
test_utils::{check_ready, check_recent_balance},
|
||||
};
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_core::validator::{TestValidator, TestValidatorOptions};
|
||||
use solana_faucet::faucet::run_local_faucet;
|
||||
use solana_sdk::{
|
||||
account_utils::StateMut,
|
||||
commitment_config::CommitmentConfig,
|
||||
nonce::State as NonceState,
|
||||
pubkey::Pubkey,
|
||||
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 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.signers = vec![&default_signer];
|
||||
|
||||
@@ -138,7 +137,7 @@ fn test_seed_stake_delegation_and_deactivation() {
|
||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||
|
||||
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 =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
config_validator.signers = vec![&validator_keypair];
|
||||
@@ -151,7 +150,7 @@ fn test_seed_stake_delegation_and_deactivation() {
|
||||
&config_validator,
|
||||
)
|
||||
.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(
|
||||
&config_validator.signers[0].pubkey(),
|
||||
@@ -183,7 +182,7 @@ fn test_seed_stake_delegation_and_deactivation() {
|
||||
stake_account_pubkey: stake_address,
|
||||
vote_account_pubkey: vote_pubkey,
|
||||
stake_authority: 0,
|
||||
force: true,
|
||||
force: false,
|
||||
sign_only: false,
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
@@ -227,7 +226,7 @@ fn test_stake_delegation_and_deactivation() {
|
||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||
let validator_keypair = Keypair::new();
|
||||
|
||||
let mut config_validator = CliConfig::recent_for_tests();
|
||||
let mut config_validator = CliConfig::default();
|
||||
config_validator.json_rpc_url =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
config_validator.signers = vec![&validator_keypair];
|
||||
@@ -242,7 +241,7 @@ fn test_stake_delegation_and_deactivation() {
|
||||
&config_validator,
|
||||
)
|
||||
.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
|
||||
config_validator.signers.push(&stake_keypair);
|
||||
@@ -268,7 +267,7 @@ fn test_stake_delegation_and_deactivation() {
|
||||
stake_account_pubkey: stake_keypair.pubkey(),
|
||||
vote_account_pubkey: vote_pubkey,
|
||||
stake_authority: 0,
|
||||
force: true,
|
||||
force: false,
|
||||
sign_only: false,
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
@@ -311,19 +310,19 @@ fn test_offline_stake_delegation_and_deactivation() {
|
||||
|
||||
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 =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
let validator_keypair = Keypair::new();
|
||||
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 =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
|
||||
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.command = CliCommand::ClusterVersion;
|
||||
let offline_keypair = Keypair::new();
|
||||
@@ -339,7 +338,7 @@ fn test_offline_stake_delegation_and_deactivation() {
|
||||
&config_offline,
|
||||
)
|
||||
.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(
|
||||
&rpc_client,
|
||||
@@ -349,7 +348,7 @@ fn test_offline_stake_delegation_and_deactivation() {
|
||||
&config_validator,
|
||||
)
|
||||
.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
|
||||
config_validator.signers.push(&stake_keypair);
|
||||
@@ -375,7 +374,7 @@ fn test_offline_stake_delegation_and_deactivation() {
|
||||
stake_account_pubkey: stake_keypair.pubkey(),
|
||||
vote_account_pubkey: vote_pubkey,
|
||||
stake_authority: 0,
|
||||
force: true,
|
||||
force: false,
|
||||
sign_only: true,
|
||||
blockhash_query: BlockhashQuery::None(blockhash),
|
||||
nonce_account: None,
|
||||
@@ -394,7 +393,7 @@ fn test_offline_stake_delegation_and_deactivation() {
|
||||
stake_account_pubkey: stake_keypair.pubkey(),
|
||||
vote_account_pubkey: vote_pubkey,
|
||||
stake_authority: 0,
|
||||
force: true,
|
||||
force: false,
|
||||
sign_only: false,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
|
||||
nonce_account: None,
|
||||
@@ -455,7 +454,7 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||
|
||||
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.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
|
||||
@@ -503,14 +502,10 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
||||
process_command(&config).unwrap();
|
||||
|
||||
// Fetch nonce hash
|
||||
let nonce_hash = nonce::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_account.pubkey(),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
|
||||
// Delegate stake
|
||||
config.signers = vec![&config_keypair];
|
||||
@@ -518,7 +513,7 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
||||
stake_account_pubkey: stake_keypair.pubkey(),
|
||||
vote_account_pubkey: vote_pubkey,
|
||||
stake_authority: 0,
|
||||
force: true,
|
||||
force: false,
|
||||
sign_only: false,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(
|
||||
blockhash_query::Source::NonceAccount(nonce_account.pubkey()),
|
||||
@@ -531,14 +526,10 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
||||
process_command(&config).unwrap();
|
||||
|
||||
// Fetch nonce hash
|
||||
let nonce_hash = nonce::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_account.pubkey(),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
|
||||
// Deactivate stake
|
||||
config.command = CliCommand::DeactivateStake {
|
||||
@@ -577,7 +568,7 @@ fn test_stake_authorize() {
|
||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||
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.signers = vec![&default_signer];
|
||||
|
||||
@@ -591,7 +582,7 @@ fn test_stake_authorize() {
|
||||
.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.json_rpc_url = String::default();
|
||||
let offline_authority_pubkey = config_offline.signers[0].pubkey();
|
||||
@@ -642,11 +633,7 @@ fn test_stake_authorize() {
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let stake_account = rpc_client
|
||||
.get_account_with_commitment(&stake_account_pubkey, CommitmentConfig::recent())
|
||||
.unwrap()
|
||||
.value
|
||||
.unwrap();
|
||||
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||
let stake_state: StakeState = stake_account.state().unwrap();
|
||||
let current_authority = match stake_state {
|
||||
StakeState::Initialized(meta) => meta.authorized.staker,
|
||||
@@ -673,11 +660,7 @@ fn test_stake_authorize() {
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let stake_account = rpc_client
|
||||
.get_account_with_commitment(&stake_account_pubkey, CommitmentConfig::recent())
|
||||
.unwrap()
|
||||
.value
|
||||
.unwrap();
|
||||
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||
let stake_state: StakeState = stake_account.state().unwrap();
|
||||
let (current_staker, current_withdrawer) = match stake_state {
|
||||
StakeState::Initialized(meta) => (meta.authorized.staker, meta.authorized.withdrawer),
|
||||
@@ -699,11 +682,7 @@ fn test_stake_authorize() {
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let stake_account = rpc_client
|
||||
.get_account_with_commitment(&stake_account_pubkey, CommitmentConfig::recent())
|
||||
.unwrap()
|
||||
.value
|
||||
.unwrap();
|
||||
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||
let stake_state: StakeState = stake_account.state().unwrap();
|
||||
let current_authority = match stake_state {
|
||||
StakeState::Initialized(meta) => meta.authorized.staker,
|
||||
@@ -714,10 +693,7 @@ fn test_stake_authorize() {
|
||||
// Offline assignment of new nonced stake authority
|
||||
let nonced_authority = Keypair::new();
|
||||
let nonced_authority_pubkey = nonced_authority.pubkey();
|
||||
let (blockhash, _, _) = rpc_client
|
||||
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
|
||||
.unwrap()
|
||||
.value;
|
||||
let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap();
|
||||
config_offline.command = CliCommand::StakeAuthorize {
|
||||
stake_account_pubkey,
|
||||
new_authorizations: vec![(StakeAuthorize::Staker, nonced_authority_pubkey, 0)],
|
||||
@@ -743,11 +719,7 @@ fn test_stake_authorize() {
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let stake_account = rpc_client
|
||||
.get_account_with_commitment(&stake_account_pubkey, CommitmentConfig::recent())
|
||||
.unwrap()
|
||||
.value
|
||||
.unwrap();
|
||||
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||
let stake_state: StakeState = stake_account.state().unwrap();
|
||||
let current_authority = match stake_state {
|
||||
StakeState::Initialized(meta) => meta.authorized.staker,
|
||||
@@ -770,14 +742,10 @@ fn test_stake_authorize() {
|
||||
process_command(&config).unwrap();
|
||||
|
||||
// Fetch nonce hash
|
||||
let nonce_hash = nonce::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_account.pubkey(),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
|
||||
// Nonced assignment of new online stake authority
|
||||
let online_authority = Keypair::new();
|
||||
@@ -812,11 +780,7 @@ fn test_stake_authorize() {
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let stake_account = rpc_client
|
||||
.get_account_with_commitment(&stake_account_pubkey, CommitmentConfig::recent())
|
||||
.unwrap()
|
||||
.value
|
||||
.unwrap();
|
||||
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||
let stake_state: StakeState = stake_account.state().unwrap();
|
||||
let current_authority = match stake_state {
|
||||
StakeState::Initialized(meta) => meta.authorized.staker,
|
||||
@@ -824,14 +788,10 @@ fn test_stake_authorize() {
|
||||
};
|
||||
assert_eq!(current_authority, online_authority_pubkey);
|
||||
|
||||
let new_nonce_hash = nonce::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_account.pubkey(),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
let new_nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
assert_ne!(nonce_hash, new_nonce_hash);
|
||||
|
||||
server.close().unwrap();
|
||||
@@ -862,18 +822,18 @@ fn test_stake_authorize_with_fee_payer() {
|
||||
let default_signer = Keypair::new();
|
||||
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.signers = vec![&default_signer];
|
||||
|
||||
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.json_rpc_url =
|
||||
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||
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();
|
||||
config_offline.signers = vec![&offline_signer];
|
||||
config_offline.json_rpc_url = String::new();
|
||||
@@ -884,17 +844,15 @@ fn test_stake_authorize_with_fee_payer() {
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &default_pubkey, 100_000, &config)
|
||||
.unwrap();
|
||||
check_recent_balance(100_000, &rpc_client, &config.signers[0].pubkey());
|
||||
check_balance(100_000, &rpc_client, &config.signers[0].pubkey());
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &payer_pubkey, 100_000, &config)
|
||||
.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)
|
||||
.unwrap();
|
||||
check_recent_balance(100_000, &rpc_client, &offline_pubkey);
|
||||
|
||||
check_ready(&rpc_client);
|
||||
check_balance(100_000, &rpc_client, &offline_pubkey);
|
||||
|
||||
// Create stake account, identity is authority
|
||||
let stake_keypair = Keypair::new();
|
||||
@@ -916,7 +874,7 @@ fn test_stake_authorize_with_fee_payer() {
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
// `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
|
||||
config.signers = vec![&default_signer, &payer_keypair];
|
||||
@@ -931,16 +889,13 @@ fn test_stake_authorize_with_fee_payer() {
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
// `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
|
||||
// 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
|
||||
let (blockhash, _, _) = rpc_client
|
||||
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
|
||||
.unwrap()
|
||||
.value;
|
||||
let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap();
|
||||
config_offline.command = CliCommand::StakeAuthorize {
|
||||
stake_account_pubkey,
|
||||
new_authorizations: vec![(StakeAuthorize::Staker, payer_pubkey, 0)],
|
||||
@@ -967,10 +922,10 @@ fn test_stake_authorize_with_fee_payer() {
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
// `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
|
||||
// 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();
|
||||
remove_dir_all(ledger_path).unwrap();
|
||||
@@ -999,11 +954,11 @@ fn test_stake_split() {
|
||||
let default_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.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.signers = vec![&offline_signer];
|
||||
let offline_pubkey = config_offline.signers[0].pubkey();
|
||||
@@ -1019,11 +974,11 @@ fn test_stake_split() {
|
||||
&config,
|
||||
)
|
||||
.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)
|
||||
.unwrap();
|
||||
check_recent_balance(100_000, &rpc_client, &offline_pubkey);
|
||||
check_balance(100_000, &rpc_client, &offline_pubkey);
|
||||
|
||||
// Create stake account, identity is authority
|
||||
let minimum_stake_balance = rpc_client
|
||||
@@ -1047,7 +1002,7 @@ fn test_stake_split() {
|
||||
from: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_recent_balance(
|
||||
check_balance(
|
||||
10 * minimum_stake_balance,
|
||||
&rpc_client,
|
||||
&stake_account_pubkey,
|
||||
@@ -1066,21 +1021,17 @@ fn test_stake_split() {
|
||||
amount: SpendAmount::Some(minimum_nonce_balance),
|
||||
};
|
||||
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
|
||||
let nonce_hash = nonce::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_account.pubkey(),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
|
||||
// Nonced offline split
|
||||
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.command = CliCommand::SplitStake {
|
||||
stake_account_pubkey,
|
||||
@@ -1116,12 +1067,12 @@ fn test_stake_split() {
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_recent_balance(
|
||||
check_balance(
|
||||
8 * minimum_stake_balance,
|
||||
&rpc_client,
|
||||
&stake_account_pubkey,
|
||||
);
|
||||
check_recent_balance(
|
||||
check_balance(
|
||||
2 * minimum_stake_balance,
|
||||
&rpc_client,
|
||||
&split_account.pubkey(),
|
||||
@@ -1154,11 +1105,11 @@ fn test_stake_set_lockup() {
|
||||
let default_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.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.signers = vec![&offline_signer];
|
||||
let offline_pubkey = config_offline.signers[0].pubkey();
|
||||
@@ -1174,11 +1125,11 @@ fn test_stake_set_lockup() {
|
||||
&config,
|
||||
)
|
||||
.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)
|
||||
.unwrap();
|
||||
check_recent_balance(100_000, &rpc_client, &offline_pubkey);
|
||||
check_balance(100_000, &rpc_client, &offline_pubkey);
|
||||
|
||||
// Create stake account, identity is authority
|
||||
let minimum_stake_balance = rpc_client
|
||||
@@ -1207,7 +1158,7 @@ fn test_stake_set_lockup() {
|
||||
from: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_recent_balance(
|
||||
check_balance(
|
||||
10 * minimum_stake_balance,
|
||||
&rpc_client,
|
||||
&stake_account_pubkey,
|
||||
@@ -1231,11 +1182,7 @@ fn test_stake_set_lockup() {
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let stake_account = rpc_client
|
||||
.get_account_with_commitment(&stake_account_pubkey, CommitmentConfig::recent())
|
||||
.unwrap()
|
||||
.value
|
||||
.unwrap();
|
||||
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||
let stake_state: StakeState = stake_account.state().unwrap();
|
||||
let current_lockup = match stake_state {
|
||||
StakeState::Initialized(meta) => meta.lockup,
|
||||
@@ -1286,11 +1233,7 @@ fn test_stake_set_lockup() {
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let stake_account = rpc_client
|
||||
.get_account_with_commitment(&stake_account_pubkey, CommitmentConfig::recent())
|
||||
.unwrap()
|
||||
.value
|
||||
.unwrap();
|
||||
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||
let stake_state: StakeState = stake_account.state().unwrap();
|
||||
let current_lockup = match stake_state {
|
||||
StakeState::Initialized(meta) => meta.lockup,
|
||||
@@ -1335,17 +1278,13 @@ fn test_stake_set_lockup() {
|
||||
amount: SpendAmount::Some(minimum_nonce_balance),
|
||||
};
|
||||
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
|
||||
let nonce_hash = nonce::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_account.pubkey(),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
|
||||
// Nonced offline set lockup
|
||||
let lockup = LockupArgs {
|
||||
@@ -1383,11 +1322,7 @@ fn test_stake_set_lockup() {
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let stake_account = rpc_client
|
||||
.get_account_with_commitment(&stake_account_pubkey, CommitmentConfig::recent())
|
||||
.unwrap()
|
||||
.value
|
||||
.unwrap();
|
||||
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||
let stake_state: StakeState = stake_account.state().unwrap();
|
||||
let current_lockup = match stake_state {
|
||||
StakeState::Initialized(meta) => meta.lockup,
|
||||
@@ -1421,12 +1356,12 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
|
||||
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();
|
||||
config.signers = vec![&default_signer];
|
||||
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();
|
||||
config_offline.signers = vec![&offline_signer];
|
||||
let offline_pubkey = config_offline.signers[0].pubkey();
|
||||
@@ -1443,11 +1378,11 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
&config,
|
||||
)
|
||||
.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)
|
||||
.unwrap();
|
||||
check_recent_balance(100_000, &rpc_client, &offline_pubkey);
|
||||
check_balance(100_000, &rpc_client, &offline_pubkey);
|
||||
|
||||
// Create nonce account
|
||||
let minimum_nonce_balance = rpc_client
|
||||
@@ -1465,14 +1400,10 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
process_command(&config).unwrap();
|
||||
|
||||
// Fetch nonce hash
|
||||
let nonce_hash = nonce::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_account.pubkey(),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
|
||||
// Create stake account offline
|
||||
let stake_keypair = keypair_from_seed(&[4u8; 32]).unwrap();
|
||||
@@ -1517,17 +1448,13 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
from: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_recent_balance(50_000, &rpc_client, &stake_pubkey);
|
||||
check_balance(50_000, &rpc_client, &stake_pubkey);
|
||||
|
||||
// Fetch nonce hash
|
||||
let nonce_hash = nonce::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_account.pubkey(),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
|
||||
// Offline, nonced stake-withdraw
|
||||
let recipient = keypair_from_seed(&[5u8; 32]).unwrap();
|
||||
@@ -1565,17 +1492,13 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_recent_balance(42, &rpc_client, &recipient_pubkey);
|
||||
check_balance(42, &rpc_client, &recipient_pubkey);
|
||||
|
||||
// Fetch nonce hash
|
||||
let nonce_hash = nonce::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_account.pubkey(),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
|
||||
// Create another stake account. This time with seed
|
||||
let seed = "seedy";
|
||||
@@ -1619,7 +1542,7 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
process_command(&config).unwrap();
|
||||
let seed_address =
|
||||
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();
|
||||
remove_dir_all(ledger_path).unwrap();
|
||||
|
@@ -1,3 +1,4 @@
|
||||
use solana_cli::test_utils::check_balance;
|
||||
use solana_cli::{
|
||||
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
|
||||
cli_output::OutputFormat,
|
||||
@@ -7,13 +8,11 @@ use solana_cli::{
|
||||
parse_sign_only_reply_string,
|
||||
},
|
||||
spend_utils::SpendAmount,
|
||||
test_utils::{check_ready, check_recent_balance},
|
||||
};
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_core::validator::{TestValidator, TestValidatorOptions};
|
||||
use solana_faucet::faucet::run_local_faucet;
|
||||
use solana_sdk::{
|
||||
commitment_config::CommitmentConfig,
|
||||
nonce::State as NonceState,
|
||||
pubkey::Pubkey,
|
||||
signature::{keypair_from_seed, Keypair, NullSigner, Signer},
|
||||
@@ -43,7 +42,7 @@ fn test_transfer() {
|
||||
let default_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.signers = vec![&default_signer];
|
||||
|
||||
@@ -52,10 +51,8 @@ fn test_transfer() {
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &sender_pubkey, 50_000, &config)
|
||||
.unwrap();
|
||||
check_recent_balance(50_000, &rpc_client, &sender_pubkey);
|
||||
check_recent_balance(0, &rpc_client, &recipient_pubkey);
|
||||
|
||||
check_ready(&rpc_client);
|
||||
check_balance(50_000, &rpc_client, &sender_pubkey);
|
||||
check_balance(0, &rpc_client, &recipient_pubkey);
|
||||
|
||||
// Plain ole transfer
|
||||
config.command = CliCommand::Transfer {
|
||||
@@ -70,8 +67,8 @@ fn test_transfer() {
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_recent_balance(49_989, &rpc_client, &sender_pubkey);
|
||||
check_recent_balance(10, &rpc_client, &recipient_pubkey);
|
||||
check_balance(49_989, &rpc_client, &sender_pubkey);
|
||||
check_balance(10, &rpc_client, &recipient_pubkey);
|
||||
|
||||
// Plain ole transfer, failure due to InsufficientFundsForSpendAndFee
|
||||
config.command = CliCommand::Transfer {
|
||||
@@ -86,10 +83,10 @@ fn test_transfer() {
|
||||
fee_payer: 0,
|
||||
};
|
||||
assert!(process_command(&config).is_err());
|
||||
check_recent_balance(49_989, &rpc_client, &sender_pubkey);
|
||||
check_recent_balance(10, &rpc_client, &recipient_pubkey);
|
||||
check_balance(49_989, &rpc_client, &sender_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.signers = vec![&default_offline_signer];
|
||||
// Verify we cannot contact the cluster
|
||||
@@ -98,13 +95,10 @@ fn test_transfer() {
|
||||
|
||||
let offline_pubkey = offline.signers[0].pubkey();
|
||||
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
|
||||
let (blockhash, _, _) = rpc_client
|
||||
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
|
||||
.unwrap()
|
||||
.value;
|
||||
let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap();
|
||||
offline.command = CliCommand::Transfer {
|
||||
amount: SpendAmount::Some(10),
|
||||
to: recipient_pubkey,
|
||||
@@ -134,8 +128,8 @@ fn test_transfer() {
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_recent_balance(39, &rpc_client, &offline_pubkey);
|
||||
check_recent_balance(20, &rpc_client, &recipient_pubkey);
|
||||
check_balance(39, &rpc_client, &offline_pubkey);
|
||||
check_balance(20, &rpc_client, &recipient_pubkey);
|
||||
|
||||
// Create nonce account
|
||||
let nonce_account = keypair_from_seed(&[3u8; 32]).unwrap();
|
||||
@@ -150,17 +144,13 @@ fn test_transfer() {
|
||||
amount: SpendAmount::Some(minimum_nonce_balance),
|
||||
};
|
||||
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
|
||||
let nonce_hash = nonce::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_account.pubkey(),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
|
||||
// Nonced transfer
|
||||
config.signers = vec![&default_signer];
|
||||
@@ -179,16 +169,12 @@ fn test_transfer() {
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_recent_balance(49_976 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
|
||||
check_recent_balance(30, &rpc_client, &recipient_pubkey);
|
||||
let new_nonce_hash = nonce::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_account.pubkey(),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
check_balance(49_976 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
|
||||
check_balance(30, &rpc_client, &recipient_pubkey);
|
||||
let new_nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
assert_ne!(nonce_hash, new_nonce_hash);
|
||||
|
||||
// Assign nonce authority to offline
|
||||
@@ -199,17 +185,13 @@ fn test_transfer() {
|
||||
new_authority: offline_pubkey,
|
||||
};
|
||||
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
|
||||
let nonce_hash = nonce::get_account_with_commitment(
|
||||
&rpc_client,
|
||||
&nonce_account.pubkey(),
|
||||
CommitmentConfig::recent(),
|
||||
)
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
let nonce_hash = nonce::get_account(&rpc_client, &nonce_account.pubkey())
|
||||
.and_then(|ref a| nonce::data_from_account(a))
|
||||
.unwrap()
|
||||
.blockhash;
|
||||
|
||||
// Offline, nonced transfer
|
||||
offline.signers = vec![&default_offline_signer];
|
||||
@@ -244,8 +226,8 @@ fn test_transfer() {
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_recent_balance(28, &rpc_client, &offline_pubkey);
|
||||
check_recent_balance(40, &rpc_client, &recipient_pubkey);
|
||||
check_balance(28, &rpc_client, &offline_pubkey);
|
||||
check_balance(40, &rpc_client, &recipient_pubkey);
|
||||
|
||||
server.close().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_fee_payer_signer = keypair_from_seed(&[3u8; 32]).unwrap();
|
||||
let from_null_signer = NullSigner::new(&offline_from_signer.pubkey());
|
||||
let config = CliConfig::recent_for_tests();
|
||||
let config = CliConfig::default();
|
||||
|
||||
// Setup accounts
|
||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||
@@ -293,19 +275,14 @@ fn test_transfer_multisession_signing() {
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
check_recent_balance(43, &rpc_client, &offline_from_signer.pubkey());
|
||||
check_recent_balance(3, &rpc_client, &offline_fee_payer_signer.pubkey());
|
||||
check_recent_balance(0, &rpc_client, &to_pubkey);
|
||||
check_balance(43, &rpc_client, &offline_from_signer.pubkey());
|
||||
check_balance(3, &rpc_client, &offline_fee_payer_signer.pubkey());
|
||||
check_balance(0, &rpc_client, &to_pubkey);
|
||||
|
||||
check_ready(&rpc_client);
|
||||
|
||||
let (blockhash, _, _) = rpc_client
|
||||
.get_recent_blockhash_with_commitment(CommitmentConfig::recent())
|
||||
.unwrap()
|
||||
.value;
|
||||
let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap();
|
||||
|
||||
// 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.signers = vec![&offline_fee_payer_signer, &from_null_signer];
|
||||
// Verify we cannot contact the cluster
|
||||
@@ -331,7 +308,7 @@ fn test_transfer_multisession_signing() {
|
||||
.unwrap();
|
||||
|
||||
// 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.signers = vec![&fee_payer_presigner, &offline_from_signer];
|
||||
// Verify we cannot contact the cluster
|
||||
@@ -357,7 +334,7 @@ fn test_transfer_multisession_signing() {
|
||||
.unwrap();
|
||||
|
||||
// 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.signers = vec![&fee_payer_presigner, &from_presigner];
|
||||
config.command = CliCommand::Transfer {
|
||||
@@ -373,9 +350,9 @@ fn test_transfer_multisession_signing() {
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
|
||||
check_recent_balance(1, &rpc_client, &offline_from_signer.pubkey());
|
||||
check_recent_balance(1, &rpc_client, &offline_fee_payer_signer.pubkey());
|
||||
check_recent_balance(42, &rpc_client, &to_pubkey);
|
||||
check_balance(1, &rpc_client, &offline_from_signer.pubkey());
|
||||
check_balance(1, &rpc_client, &offline_fee_payer_signer.pubkey());
|
||||
check_balance(42, &rpc_client, &to_pubkey);
|
||||
|
||||
server.close().unwrap();
|
||||
remove_dir_all(ledger_path).unwrap();
|
||||
@@ -403,7 +380,7 @@ fn test_transfer_all() {
|
||||
|
||||
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.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)
|
||||
.unwrap();
|
||||
check_recent_balance(50_000, &rpc_client, &sender_pubkey);
|
||||
check_recent_balance(0, &rpc_client, &recipient_pubkey);
|
||||
|
||||
check_ready(&rpc_client);
|
||||
check_balance(50_000, &rpc_client, &sender_pubkey);
|
||||
check_balance(0, &rpc_client, &recipient_pubkey);
|
||||
|
||||
// Plain ole transfer
|
||||
config.command = CliCommand::Transfer {
|
||||
@@ -430,8 +405,8 @@ fn test_transfer_all() {
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_recent_balance(0, &rpc_client, &sender_pubkey);
|
||||
check_recent_balance(49_999, &rpc_client, &recipient_pubkey);
|
||||
check_balance(0, &rpc_client, &sender_pubkey);
|
||||
check_balance(49_999, &rpc_client, &recipient_pubkey);
|
||||
|
||||
server.close().unwrap();
|
||||
remove_dir_all(ledger_path).unwrap();
|
||||
|
@@ -2,14 +2,13 @@ use solana_cli::{
|
||||
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
|
||||
offline::{blockhash_query::BlockhashQuery, *},
|
||||
spend_utils::SpendAmount,
|
||||
test_utils::check_recent_balance,
|
||||
test_utils::check_balance,
|
||||
};
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_core::validator::TestValidator;
|
||||
use solana_faucet::faucet::run_local_faucet;
|
||||
use solana_sdk::{
|
||||
account_utils::StateMut,
|
||||
commitment_config::CommitmentConfig,
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, Signer},
|
||||
};
|
||||
@@ -32,7 +31,7 @@ fn test_vote_authorize_and_withdraw() {
|
||||
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||
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.signers = vec![&default_signer];
|
||||
|
||||
@@ -59,9 +58,7 @@ fn test_vote_authorize_and_withdraw() {
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let vote_account = rpc_client
|
||||
.get_account_with_commitment(&vote_account_keypair.pubkey(), CommitmentConfig::recent())
|
||||
.unwrap()
|
||||
.value
|
||||
.get_account(&vote_account_keypair.pubkey())
|
||||
.unwrap();
|
||||
let vote_state: VoteStateVersions = vote_account.state().unwrap();
|
||||
let authorized_withdrawer = vote_state.convert_to_current().authorized_withdrawer;
|
||||
@@ -70,7 +67,7 @@ fn test_vote_authorize_and_withdraw() {
|
||||
.get_minimum_balance_for_rent_exemption(VoteState::size_of())
|
||||
.unwrap()
|
||||
.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];
|
||||
@@ -87,7 +84,7 @@ fn test_vote_authorize_and_withdraw() {
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let expected_balance = expected_balance + 1_000;
|
||||
check_recent_balance(expected_balance, &rpc_client, &vote_account_pubkey);
|
||||
check_balance(expected_balance, &rpc_client, &vote_account_pubkey);
|
||||
|
||||
// Authorize vote account withdrawal to another signer
|
||||
let withdraw_authority = Keypair::new();
|
||||
@@ -99,9 +96,7 @@ fn test_vote_authorize_and_withdraw() {
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let vote_account = rpc_client
|
||||
.get_account_with_commitment(&vote_account_keypair.pubkey(), CommitmentConfig::recent())
|
||||
.unwrap()
|
||||
.value
|
||||
.get_account(&vote_account_keypair.pubkey())
|
||||
.unwrap();
|
||||
let vote_state: VoteStateVersions = vote_account.state().unwrap();
|
||||
let authorized_withdrawer = vote_state.convert_to_current().authorized_withdrawer;
|
||||
@@ -117,8 +112,8 @@ fn test_vote_authorize_and_withdraw() {
|
||||
destination_account_pubkey: destination_account,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_recent_balance(expected_balance - 100, &rpc_client, &vote_account_pubkey);
|
||||
check_recent_balance(100, &rpc_client, &destination_account);
|
||||
check_balance(expected_balance - 100, &rpc_client, &vote_account_pubkey);
|
||||
check_balance(100, &rpc_client, &destination_account);
|
||||
|
||||
// Re-assign validator identity
|
||||
let new_identity_keypair = Keypair::new();
|
||||
|
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "solana-client"
|
||||
version = "1.3.1"
|
||||
version = "1.2.29"
|
||||
description = "Solana Client"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
homepage = "https://solana.com/"
|
||||
license = "Apache-2.0"
|
||||
@@ -11,28 +11,28 @@ edition = "2018"
|
||||
[dependencies]
|
||||
bincode = "1.3.1"
|
||||
bs58 = "0.3.1"
|
||||
indicatif = "0.15.0"
|
||||
jsonrpc-core = "14.2.0"
|
||||
indicatif = "0.14.0"
|
||||
jsonrpc-core = "14.1.0"
|
||||
log = "0.4.8"
|
||||
rayon = "1.3.1"
|
||||
reqwest = { version = "0.10.6", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
serde = "1.0.112"
|
||||
rayon = "1.4.0"
|
||||
reqwest = { version = "0.10.4", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
serde = "1.0.110"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.56"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "1.3.1" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.3.1" }
|
||||
solana-sdk = { path = "../sdk", version = "1.3.1" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "1.3.1" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.3.1" }
|
||||
serde_json = "1.0.53"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "1.2.29" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.2.29" }
|
||||
solana-sdk = { path = "../sdk", version = "1.2.29" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "1.2.29" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.2.29" }
|
||||
thiserror = "1.0"
|
||||
tungstenite = "0.10.1"
|
||||
url = "2.1.1"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_matches = "1.3.0"
|
||||
jsonrpc-core = "14.2.0"
|
||||
jsonrpc-http-server = "14.2.0"
|
||||
solana-logger = { path = "../logger", version = "1.3.1" }
|
||||
jsonrpc-core = "14.1.0"
|
||||
jsonrpc-http-server = "14.1.0"
|
||||
solana-logger = { path = "../logger", version = "1.2.29" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -2,6 +2,7 @@ use crate::{
|
||||
client_error::{ClientError, ClientErrorKind, Result as ClientResult},
|
||||
http_sender::HttpSender,
|
||||
mock_sender::{MockSender, Mocks},
|
||||
rpc_config::RpcAccountInfoConfig,
|
||||
rpc_config::{
|
||||
RpcGetConfirmedSignaturesForAddress2Config, RpcLargestAccountsConfig,
|
||||
RpcSendTransactionConfig, RpcTokenAccountsFilter,
|
||||
@@ -14,26 +15,21 @@ use bincode::serialize;
|
||||
use indicatif::{ProgressBar, ProgressStyle};
|
||||
use log::*;
|
||||
use serde_json::{json, Value};
|
||||
use solana_account_decoder::{
|
||||
parse_token::{
|
||||
get_token_account_mint, parse_token, TokenAccountType, UiMint, UiMultisig, UiTokenAccount,
|
||||
UiTokenAmount,
|
||||
},
|
||||
UiAccount,
|
||||
};
|
||||
use solana_account_decoder::{parse_token::UiTokenAmount, UiAccount, UiAccountEncoding};
|
||||
use solana_sdk::{
|
||||
account::Account,
|
||||
clock::{
|
||||
Slot, UnixTimestamp, DEFAULT_TICKS_PER_SECOND, DEFAULT_TICKS_PER_SLOT,
|
||||
MAX_HASH_AGE_IN_SECONDS,
|
||||
},
|
||||
commitment_config::{CommitmentConfig, CommitmentLevel},
|
||||
commitment_config::CommitmentConfig,
|
||||
epoch_info::EpochInfo,
|
||||
epoch_schedule::EpochSchedule,
|
||||
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
||||
hash::Hash,
|
||||
pubkey::Pubkey,
|
||||
signature::Signature,
|
||||
signers::Signers,
|
||||
transaction::{self, Transaction},
|
||||
};
|
||||
use solana_transaction_status::{
|
||||
@@ -41,7 +37,6 @@ use solana_transaction_status::{
|
||||
};
|
||||
use solana_vote_program::vote_state::MAX_LOCKOUT_HISTORY;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
net::SocketAddr,
|
||||
thread::sleep,
|
||||
time::{Duration, Instant},
|
||||
@@ -62,10 +57,6 @@ impl RpcClient {
|
||||
Self::new_sender(HttpSender::new(url))
|
||||
}
|
||||
|
||||
pub fn new_with_timeout(url: String, timeout: Duration) -> Self {
|
||||
Self::new_sender(HttpSender::new_with_timeout(url, timeout))
|
||||
}
|
||||
|
||||
pub fn new_mock(url: String) -> Self {
|
||||
Self::new_sender(MockSender::new(url))
|
||||
}
|
||||
@@ -80,7 +71,7 @@ impl RpcClient {
|
||||
|
||||
pub fn new_socket_with_timeout(addr: SocketAddr, timeout: Duration) -> Self {
|
||||
let url = get_rpc_request_str(addr, false);
|
||||
Self::new_with_timeout(url, timeout)
|
||||
Self::new_sender(HttpSender::new_with_timeout(url, timeout))
|
||||
}
|
||||
|
||||
pub fn confirm_transaction(&self, signature: &Signature) -> ClientResult<bool> {
|
||||
@@ -300,18 +291,21 @@ impl RpcClient {
|
||||
&self,
|
||||
address: &Pubkey,
|
||||
) -> ClientResult<Vec<RpcConfirmedTransactionStatusWithSignature>> {
|
||||
self.get_confirmed_signatures_for_address2_with_config(address, None, None)
|
||||
self.get_confirmed_signatures_for_address2_with_config(
|
||||
address,
|
||||
GetConfirmedSignaturesForAddress2Config::default(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_confirmed_signatures_for_address2_with_config(
|
||||
&self,
|
||||
address: &Pubkey,
|
||||
before: Option<Signature>,
|
||||
limit: Option<usize>,
|
||||
config: GetConfirmedSignaturesForAddress2Config,
|
||||
) -> ClientResult<Vec<RpcConfirmedTransactionStatusWithSignature>> {
|
||||
let config = RpcGetConfirmedSignaturesForAddress2Config {
|
||||
before: before.map(|signature| signature.to_string()),
|
||||
limit,
|
||||
before: config.before.map(|signature| signature.to_string()),
|
||||
until: config.until.map(|signature| signature.to_string()),
|
||||
limit: config.limit,
|
||||
};
|
||||
|
||||
let result: Vec<RpcConfirmedTransactionStatusWithSignature> = self.send(
|
||||
@@ -414,47 +408,56 @@ impl RpcClient {
|
||||
&self,
|
||||
transaction: &Transaction,
|
||||
) -> ClientResult<Signature> {
|
||||
let mut send_retries = 20;
|
||||
loop {
|
||||
let mut status_retries = 15;
|
||||
let signature = self.send_transaction(transaction)?;
|
||||
let status = loop {
|
||||
let status = self.get_signature_status(&signature)?;
|
||||
if status.is_none() {
|
||||
status_retries -= 1;
|
||||
if status_retries == 0 {
|
||||
break status;
|
||||
}
|
||||
} else {
|
||||
let signature = self.send_transaction(transaction)?;
|
||||
let recent_blockhash = transaction.message.recent_blockhash;
|
||||
let status = loop {
|
||||
let status = self.get_signature_status(&signature)?;
|
||||
if status.is_none() {
|
||||
if self
|
||||
.get_fee_calculator_for_blockhash_with_commitment(
|
||||
&recent_blockhash,
|
||||
CommitmentConfig::recent(),
|
||||
)?
|
||||
.value
|
||||
.is_none()
|
||||
{
|
||||
break status;
|
||||
}
|
||||
if cfg!(not(test)) {
|
||||
// Retry twice a second
|
||||
sleep(Duration::from_millis(500));
|
||||
}
|
||||
};
|
||||
send_retries = if let Some(result) = status.clone() {
|
||||
match result {
|
||||
Ok(_) => return Ok(signature),
|
||||
Err(_) => 0,
|
||||
}
|
||||
} else {
|
||||
send_retries - 1
|
||||
};
|
||||
if send_retries == 0 {
|
||||
if let Some(err) = status {
|
||||
return Err(err.unwrap_err().into());
|
||||
} else {
|
||||
return Err(
|
||||
RpcError::ForUser("unable to confirm transaction. \
|
||||
This can happen in situations such as transaction expiration \
|
||||
and insufficient fee-payer funds".to_string()).into(),
|
||||
);
|
||||
}
|
||||
break status;
|
||||
}
|
||||
if cfg!(not(test)) {
|
||||
// Retry twice a second
|
||||
sleep(Duration::from_millis(500));
|
||||
}
|
||||
};
|
||||
if let Some(result) = status {
|
||||
match result {
|
||||
Ok(_) => Ok(signature),
|
||||
Err(err) => Err(err.into()),
|
||||
}
|
||||
} else {
|
||||
Err(RpcError::ForUser(
|
||||
"unable to confirm transaction. \
|
||||
This can happen in situations such as transaction expiration \
|
||||
and insufficient fee-payer funds"
|
||||
.to_string(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resign_transaction<T: Signers>(
|
||||
&self,
|
||||
tx: &mut Transaction,
|
||||
signer_keys: &T,
|
||||
) -> ClientResult<()> {
|
||||
let (blockhash, _fee_calculator) =
|
||||
self.get_new_blockhash(&tx.message().recent_blockhash)?;
|
||||
tx.try_sign(signer_keys, blockhash)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_account(&self, pubkey: &Pubkey) -> ClientResult<Account> {
|
||||
self.get_account_with_commitment(pubkey, CommitmentConfig::default())?
|
||||
.value
|
||||
@@ -466,9 +469,14 @@ impl RpcClient {
|
||||
pubkey: &Pubkey,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> RpcResult<Option<Account>> {
|
||||
let config = RpcAccountInfoConfig {
|
||||
encoding: Some(UiAccountEncoding::Base64),
|
||||
commitment: Some(commitment_config),
|
||||
data_slice: None,
|
||||
};
|
||||
let response = self.sender.send(
|
||||
RpcRequest::GetAccountInfo,
|
||||
json!([pubkey.to_string(), commitment_config]),
|
||||
json!([pubkey.to_string(), config]),
|
||||
);
|
||||
|
||||
response
|
||||
@@ -497,6 +505,38 @@ impl RpcClient {
|
||||
})?
|
||||
}
|
||||
|
||||
pub fn get_multiple_accounts(&self, pubkeys: &[Pubkey]) -> ClientResult<Vec<Option<Account>>> {
|
||||
Ok(self
|
||||
.get_multiple_accounts_with_commitment(pubkeys, CommitmentConfig::default())?
|
||||
.value)
|
||||
}
|
||||
|
||||
pub fn get_multiple_accounts_with_commitment(
|
||||
&self,
|
||||
pubkeys: &[Pubkey],
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> RpcResult<Vec<Option<Account>>> {
|
||||
let config = RpcAccountInfoConfig {
|
||||
encoding: Some(UiAccountEncoding::Base64),
|
||||
commitment: Some(commitment_config),
|
||||
data_slice: None,
|
||||
};
|
||||
let pubkeys: Vec<_> = pubkeys.iter().map(|pubkey| pubkey.to_string()).collect();
|
||||
let response = self.send(RpcRequest::GetMultipleAccounts, json!([[pubkeys], config]))?;
|
||||
let Response {
|
||||
context,
|
||||
value: accounts,
|
||||
} = serde_json::from_value::<Response<Option<UiAccount>>>(response)?;
|
||||
let accounts: Vec<Option<Account>> = accounts
|
||||
.iter()
|
||||
.map(|rpc_account| rpc_account.decode())
|
||||
.collect();
|
||||
Ok(Response {
|
||||
context,
|
||||
value: accounts,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_account_data(&self, pubkey: &Pubkey) -> ClientResult<Vec<u8>> {
|
||||
Ok(self.get_account(pubkey)?.data)
|
||||
}
|
||||
@@ -686,103 +726,6 @@ impl RpcClient {
|
||||
Ok(hash)
|
||||
}
|
||||
|
||||
pub fn get_token_account(&self, pubkey: &Pubkey) -> ClientResult<Option<UiTokenAccount>> {
|
||||
Ok(self
|
||||
.get_token_account_with_commitment(pubkey, CommitmentConfig::default())?
|
||||
.value)
|
||||
}
|
||||
|
||||
pub fn get_token_account_with_commitment(
|
||||
&self,
|
||||
pubkey: &Pubkey,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> RpcResult<Option<UiTokenAccount>> {
|
||||
let Response {
|
||||
context,
|
||||
value: account,
|
||||
} = self.get_account_with_commitment(pubkey, commitment_config)?;
|
||||
|
||||
if account.is_none() {
|
||||
return Err(RpcError::ForUser(format!("AccountNotFound: pubkey={}", pubkey)).into());
|
||||
}
|
||||
let account = account.unwrap();
|
||||
let mint = get_token_account_mint(&account.data)
|
||||
.and_then(|mint_pubkey| {
|
||||
self.get_token_mint_with_commitment(&mint_pubkey, commitment_config)
|
||||
.ok()
|
||||
.map(|response| response.value)
|
||||
.flatten()
|
||||
})
|
||||
.ok_or_else(|| {
|
||||
Into::<ClientError>::into(RpcError::ForUser(format!(
|
||||
"AccountNotFound: mint for token acccount pubkey={}",
|
||||
pubkey
|
||||
)))
|
||||
})?;
|
||||
|
||||
Ok(Response {
|
||||
context,
|
||||
value: match parse_token(&account.data, Some(mint.decimals)) {
|
||||
Ok(TokenAccountType::Account(ui_token_account)) => Some(ui_token_account),
|
||||
_ => None,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_token_mint(&self, pubkey: &Pubkey) -> ClientResult<Option<UiMint>> {
|
||||
Ok(self
|
||||
.get_token_mint_with_commitment(pubkey, CommitmentConfig::default())?
|
||||
.value)
|
||||
}
|
||||
|
||||
pub fn get_token_mint_with_commitment(
|
||||
&self,
|
||||
pubkey: &Pubkey,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> RpcResult<Option<UiMint>> {
|
||||
let Response {
|
||||
context,
|
||||
value: account,
|
||||
} = self.get_account_with_commitment(pubkey, commitment_config)?;
|
||||
|
||||
Ok(Response {
|
||||
context,
|
||||
value: account
|
||||
.map(|account| match parse_token(&account.data, None) {
|
||||
Ok(TokenAccountType::Mint(ui_token_mint)) => Some(ui_token_mint),
|
||||
_ => None,
|
||||
})
|
||||
.flatten(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_token_multisig(&self, pubkey: &Pubkey) -> ClientResult<Option<UiMultisig>> {
|
||||
Ok(self
|
||||
.get_token_multisig_with_commitment(pubkey, CommitmentConfig::default())?
|
||||
.value)
|
||||
}
|
||||
|
||||
pub fn get_token_multisig_with_commitment(
|
||||
&self,
|
||||
pubkey: &Pubkey,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> RpcResult<Option<UiMultisig>> {
|
||||
let Response {
|
||||
context,
|
||||
value: account,
|
||||
} = self.get_account_with_commitment(pubkey, commitment_config)?;
|
||||
|
||||
Ok(Response {
|
||||
context,
|
||||
value: account
|
||||
.map(|account| match parse_token(&account.data, None) {
|
||||
Ok(TokenAccountType::Multisig(ui_token_multisig)) => Some(ui_token_multisig),
|
||||
_ => None,
|
||||
})
|
||||
.flatten(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_token_account_balance(&self, pubkey: &Pubkey) -> ClientResult<UiTokenAmount> {
|
||||
Ok(self
|
||||
.get_token_account_balance_with_commitment(pubkey, CommitmentConfig::default())?
|
||||
@@ -804,7 +747,7 @@ impl RpcClient {
|
||||
&self,
|
||||
delegate: &Pubkey,
|
||||
token_account_filter: TokenAccountsFilter,
|
||||
) -> ClientResult<Vec<(Pubkey, UiTokenAccount)>> {
|
||||
) -> ClientResult<Vec<RpcKeyedAccount>> {
|
||||
Ok(self
|
||||
.get_token_accounts_by_delegate_with_commitment(
|
||||
delegate,
|
||||
@@ -819,39 +762,31 @@ impl RpcClient {
|
||||
delegate: &Pubkey,
|
||||
token_account_filter: TokenAccountsFilter,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> RpcResult<Vec<(Pubkey, UiTokenAccount)>> {
|
||||
) -> RpcResult<Vec<RpcKeyedAccount>> {
|
||||
let token_account_filter = match token_account_filter {
|
||||
TokenAccountsFilter::Mint(mint) => RpcTokenAccountsFilter::Mint(mint.to_string()),
|
||||
TokenAccountsFilter::ProgramId(program_id) => {
|
||||
RpcTokenAccountsFilter::ProgramId(program_id.to_string())
|
||||
}
|
||||
};
|
||||
let Response {
|
||||
context,
|
||||
value: accounts,
|
||||
} = self.send(
|
||||
RpcRequest::GetTokenAccountsByDelegate,
|
||||
json!([
|
||||
delegate.to_string(),
|
||||
token_account_filter,
|
||||
commitment_config
|
||||
]),
|
||||
)?;
|
||||
let pubkey_accounts = self.accounts_to_token_accounts(
|
||||
commitment_config,
|
||||
parse_keyed_accounts(accounts, RpcRequest::GetTokenAccountsByDelegate)?,
|
||||
);
|
||||
Ok(Response {
|
||||
context,
|
||||
value: pubkey_accounts,
|
||||
})
|
||||
|
||||
let config = RpcAccountInfoConfig {
|
||||
encoding: Some(UiAccountEncoding::JsonParsed),
|
||||
commitment: Some(commitment_config),
|
||||
data_slice: None,
|
||||
};
|
||||
|
||||
self.send(
|
||||
RpcRequest::GetTokenAccountsByOwner,
|
||||
json!([delegate.to_string(), token_account_filter, config]),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_token_accounts_by_owner(
|
||||
&self,
|
||||
owner: &Pubkey,
|
||||
token_account_filter: TokenAccountsFilter,
|
||||
) -> ClientResult<Vec<(Pubkey, UiTokenAccount)>> {
|
||||
) -> ClientResult<Vec<RpcKeyedAccount>> {
|
||||
Ok(self
|
||||
.get_token_accounts_by_owner_with_commitment(
|
||||
owner,
|
||||
@@ -866,28 +801,24 @@ impl RpcClient {
|
||||
owner: &Pubkey,
|
||||
token_account_filter: TokenAccountsFilter,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> RpcResult<Vec<(Pubkey, UiTokenAccount)>> {
|
||||
) -> RpcResult<Vec<RpcKeyedAccount>> {
|
||||
let token_account_filter = match token_account_filter {
|
||||
TokenAccountsFilter::Mint(mint) => RpcTokenAccountsFilter::Mint(mint.to_string()),
|
||||
TokenAccountsFilter::ProgramId(program_id) => {
|
||||
RpcTokenAccountsFilter::ProgramId(program_id.to_string())
|
||||
}
|
||||
};
|
||||
let Response {
|
||||
context,
|
||||
value: accounts,
|
||||
} = self.send(
|
||||
|
||||
let config = RpcAccountInfoConfig {
|
||||
encoding: Some(UiAccountEncoding::JsonParsed),
|
||||
commitment: Some(commitment_config),
|
||||
data_slice: None,
|
||||
};
|
||||
|
||||
self.send(
|
||||
RpcRequest::GetTokenAccountsByOwner,
|
||||
json!([owner.to_string(), token_account_filter, commitment_config]),
|
||||
)?;
|
||||
let pubkey_accounts = self.accounts_to_token_accounts(
|
||||
commitment_config,
|
||||
parse_keyed_accounts(accounts, RpcRequest::GetTokenAccountsByDelegate)?,
|
||||
);
|
||||
Ok(Response {
|
||||
context,
|
||||
value: pubkey_accounts,
|
||||
})
|
||||
json!([owner.to_string(), token_account_filter, config]),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_token_supply(&self, mint: &Pubkey) -> ClientResult<UiTokenAmount> {
|
||||
@@ -907,36 +838,7 @@ impl RpcClient {
|
||||
)
|
||||
}
|
||||
|
||||
fn accounts_to_token_accounts(
|
||||
&self,
|
||||
commitment_config: CommitmentConfig,
|
||||
pubkey_accounts: Vec<(Pubkey, Account)>,
|
||||
) -> Vec<(Pubkey, UiTokenAccount)> {
|
||||
let mut mint_decimals: HashMap<Pubkey, u8> = HashMap::new();
|
||||
pubkey_accounts
|
||||
.into_iter()
|
||||
.filter_map(|(pubkey, account)| {
|
||||
let mint_pubkey = get_token_account_mint(&account.data)?;
|
||||
let decimals = mint_decimals.get(&mint_pubkey).cloned().or_else(|| {
|
||||
let mint = self
|
||||
.get_token_mint_with_commitment(&mint_pubkey, commitment_config)
|
||||
.ok()
|
||||
.map(|response| response.value)
|
||||
.flatten()?;
|
||||
mint_decimals.insert(mint_pubkey, mint.decimals);
|
||||
Some(mint.decimals)
|
||||
})?;
|
||||
match parse_token(&account.data, Some(decimals)) {
|
||||
Ok(TokenAccountType::Account(ui_token_account)) => {
|
||||
Some((pubkey, ui_token_account))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn poll_balance_with_timeout_and_commitment(
|
||||
pub fn poll_balance_with_timeout_and_commitment(
|
||||
&self,
|
||||
pubkey: &Pubkey,
|
||||
polling_frequency: &Duration,
|
||||
@@ -945,7 +847,7 @@ impl RpcClient {
|
||||
) -> ClientResult<u64> {
|
||||
let now = Instant::now();
|
||||
loop {
|
||||
match self.get_balance_with_commitment(&pubkey, commitment_config) {
|
||||
match self.get_balance_with_commitment(&pubkey, commitment_config.clone()) {
|
||||
Ok(bal) => {
|
||||
return Ok(bal.value);
|
||||
}
|
||||
@@ -980,7 +882,8 @@ impl RpcClient {
|
||||
) -> Option<u64> {
|
||||
const LAST: usize = 30;
|
||||
for run in 0..LAST {
|
||||
let balance_result = self.poll_get_balance_with_commitment(pubkey, commitment_config);
|
||||
let balance_result =
|
||||
self.poll_get_balance_with_commitment(pubkey, commitment_config.clone());
|
||||
if expected_balance.is_none() {
|
||||
return balance_result.ok();
|
||||
}
|
||||
@@ -1014,7 +917,7 @@ impl RpcClient {
|
||||
let now = Instant::now();
|
||||
loop {
|
||||
if let Ok(Some(_)) =
|
||||
self.get_signature_status_with_commitment(&signature, commitment_config)
|
||||
self.get_signature_status_with_commitment(&signature, commitment_config.clone())
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -1030,6 +933,36 @@ impl RpcClient {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check a signature in the bank.
|
||||
pub fn check_signature(&self, signature: &Signature) -> bool {
|
||||
trace!("check_signature: {:?}", signature);
|
||||
|
||||
for _ in 0..30 {
|
||||
let response =
|
||||
self.confirm_transaction_with_commitment(signature, CommitmentConfig::recent());
|
||||
match response {
|
||||
Ok(Response {
|
||||
value: signature_status,
|
||||
..
|
||||
}) => {
|
||||
if signature_status {
|
||||
trace!("Response found signature");
|
||||
} else {
|
||||
trace!("Response signature not found");
|
||||
}
|
||||
|
||||
return signature_status;
|
||||
}
|
||||
Err(err) => {
|
||||
debug!("check_signature request failed: {:?}", err);
|
||||
}
|
||||
};
|
||||
sleep(Duration::from_millis(250));
|
||||
}
|
||||
|
||||
panic!("Couldn't check signature of {}", signature);
|
||||
}
|
||||
|
||||
/// Poll the server to confirm a transaction.
|
||||
pub fn poll_for_signature_confirmation(
|
||||
&self,
|
||||
@@ -1112,19 +1045,6 @@ impl RpcClient {
|
||||
) -> ClientResult<Signature> {
|
||||
self.send_and_confirm_transaction_with_spinner_and_config(
|
||||
transaction,
|
||||
CommitmentConfig::default(),
|
||||
RpcSendTransactionConfig::default(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn send_and_confirm_transaction_with_spinner_and_commitment(
|
||||
&self,
|
||||
transaction: &Transaction,
|
||||
commitment: CommitmentConfig,
|
||||
) -> ClientResult<Signature> {
|
||||
self.send_and_confirm_transaction_with_spinner_and_config(
|
||||
transaction,
|
||||
commitment,
|
||||
RpcSendTransactionConfig::default(),
|
||||
)
|
||||
}
|
||||
@@ -1132,99 +1052,69 @@ impl RpcClient {
|
||||
pub fn send_and_confirm_transaction_with_spinner_and_config(
|
||||
&self,
|
||||
transaction: &Transaction,
|
||||
commitment: CommitmentConfig,
|
||||
config: RpcSendTransactionConfig,
|
||||
) -> ClientResult<Signature> {
|
||||
let desired_confirmations = match commitment.commitment {
|
||||
CommitmentLevel::Max | CommitmentLevel::Root => MAX_LOCKOUT_HISTORY + 1,
|
||||
_ => 1,
|
||||
};
|
||||
let mut confirmations = 0;
|
||||
|
||||
let progress_bar = new_spinner_progress_bar();
|
||||
|
||||
let mut send_retries = 20;
|
||||
let signature = loop {
|
||||
progress_bar.set_message(&format!(
|
||||
"[{}/{}] Finalizing transaction {}",
|
||||
confirmations, desired_confirmations, transaction.signatures[0],
|
||||
));
|
||||
let mut status_retries = 15;
|
||||
let (signature, status) = loop {
|
||||
let signature = self.send_transaction_with_config(transaction, config)?;
|
||||
|
||||
// Get recent commitment in order to count confirmations for successful transactions
|
||||
let status = self
|
||||
.get_signature_status_with_commitment(&signature, CommitmentConfig::recent())?;
|
||||
if status.is_none() {
|
||||
status_retries -= 1;
|
||||
if status_retries == 0 {
|
||||
break (signature, status);
|
||||
}
|
||||
} else {
|
||||
progress_bar.set_message(&format!(
|
||||
"[{}/{}] Finalizing transaction {}",
|
||||
confirmations,
|
||||
MAX_LOCKOUT_HISTORY + 1,
|
||||
transaction.signatures[0],
|
||||
));
|
||||
let recent_blockhash = transaction.message.recent_blockhash;
|
||||
let signature = self.send_transaction_with_config(transaction, config)?;
|
||||
let (signature, status) = loop {
|
||||
// Get recent commitment in order to count confirmations for successful transactions
|
||||
let status =
|
||||
self.get_signature_status_with_commitment(&signature, CommitmentConfig::recent())?;
|
||||
if status.is_none() {
|
||||
if self
|
||||
.get_fee_calculator_for_blockhash_with_commitment(
|
||||
&recent_blockhash,
|
||||
CommitmentConfig::recent(),
|
||||
)?
|
||||
.value
|
||||
.is_none()
|
||||
{
|
||||
break (signature, status);
|
||||
}
|
||||
|
||||
if cfg!(not(test)) {
|
||||
sleep(Duration::from_millis(500));
|
||||
}
|
||||
};
|
||||
send_retries = if let Some(result) = status.clone() {
|
||||
match result {
|
||||
Ok(_) => 0,
|
||||
// If transaction errors, return right away; no point in counting confirmations
|
||||
Err(_) => 0,
|
||||
}
|
||||
} else {
|
||||
send_retries - 1
|
||||
};
|
||||
if send_retries == 0 {
|
||||
if let Some(result) = status {
|
||||
match result {
|
||||
Ok(_) => {
|
||||
break signature;
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(err.into());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(RpcError::ForUser(
|
||||
"unable to confirm transaction. \
|
||||
This can happen in situations such as transaction \
|
||||
expiration and insufficient fee-payer funds"
|
||||
.to_string(),
|
||||
)
|
||||
.into());
|
||||
}
|
||||
break (signature, status);
|
||||
}
|
||||
|
||||
if cfg!(not(test)) {
|
||||
sleep(Duration::from_millis(500));
|
||||
}
|
||||
};
|
||||
if let Some(result) = status {
|
||||
if let Err(err) = result {
|
||||
return Err(err.into());
|
||||
}
|
||||
} else {
|
||||
return Err(RpcError::ForUser(
|
||||
"unable to confirm transaction. \
|
||||
This can happen in situations such as transaction expiration \
|
||||
and insufficient fee-payer funds"
|
||||
.to_string(),
|
||||
)
|
||||
.into());
|
||||
}
|
||||
let now = Instant::now();
|
||||
loop {
|
||||
match commitment.commitment {
|
||||
CommitmentLevel::Max | CommitmentLevel::Root =>
|
||||
// Return when default (max) commitment is reached
|
||||
// Failed transactions have already been eliminated, `is_some` check is sufficient
|
||||
{
|
||||
if self.get_signature_status(&signature)?.is_some() {
|
||||
progress_bar.set_message("Transaction confirmed");
|
||||
progress_bar.finish_and_clear();
|
||||
return Ok(signature);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// Return when one confirmation has been reached
|
||||
if confirmations >= desired_confirmations {
|
||||
progress_bar.set_message("Transaction reached commitment");
|
||||
progress_bar.finish_and_clear();
|
||||
return Ok(signature);
|
||||
}
|
||||
}
|
||||
// Return when default (max) commitment is reached
|
||||
// Failed transactions have already been eliminated, `is_some` check is sufficient
|
||||
if self.get_signature_status(&signature)?.is_some() {
|
||||
progress_bar.set_message("Transaction confirmed");
|
||||
progress_bar.finish_and_clear();
|
||||
return Ok(signature);
|
||||
}
|
||||
progress_bar.set_message(&format!(
|
||||
"[{}/{}] Finalizing transaction {}",
|
||||
confirmations + 1,
|
||||
desired_confirmations,
|
||||
MAX_LOCKOUT_HISTORY + 1,
|
||||
signature,
|
||||
));
|
||||
sleep(Duration::from_millis(500));
|
||||
@@ -1259,6 +1149,13 @@ impl RpcClient {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct GetConfirmedSignaturesForAddress2Config {
|
||||
pub before: Option<Signature>,
|
||||
pub until: Option<Signature>,
|
||||
pub limit: Option<usize>,
|
||||
}
|
||||
|
||||
fn new_spinner_progress_bar() -> ProgressBar {
|
||||
let progress_bar = ProgressBar::new(42);
|
||||
progress_bar
|
||||
@@ -1453,6 +1350,28 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_resign_transaction() {
|
||||
let rpc_client = RpcClient::new_mock("succeeds".to_string());
|
||||
|
||||
let key = Keypair::new();
|
||||
let to = Pubkey::new_rand();
|
||||
let blockhash: Hash = "HUu3LwEzGRsUkuJS121jzkPJW39Kq62pXCTmTa1F9jDL"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let prev_tx = system_transaction::transfer(&key, &to, 50, blockhash);
|
||||
let mut tx = system_transaction::transfer(&key, &to, 50, blockhash);
|
||||
|
||||
rpc_client.resign_transaction(&mut tx, &[&key]).unwrap();
|
||||
|
||||
assert_ne!(prev_tx, tx);
|
||||
assert_ne!(prev_tx.signatures, tx.signatures);
|
||||
assert_ne!(
|
||||
prev_tx.message().recent_blockhash,
|
||||
tx.message().recent_blockhash
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rpc_client_thread() {
|
||||
let rpc_client = RpcClient::new_mock("succeeds".to_string());
|
||||
|
@@ -1,6 +1,9 @@
|
||||
use crate::rpc_filter::RpcFilterType;
|
||||
use solana_account_decoder::UiAccountEncoding;
|
||||
use solana_sdk::{clock::Epoch, commitment_config::CommitmentConfig};
|
||||
use solana_account_decoder::{UiAccountEncoding, UiDataSliceConfig};
|
||||
use solana_sdk::{
|
||||
clock::Epoch,
|
||||
commitment_config::{CommitmentConfig, CommitmentLevel},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -8,16 +11,21 @@ pub struct RpcSignatureStatusConfig {
|
||||
pub search_transaction_history: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcSendTransactionConfig {
|
||||
#[serde(default)]
|
||||
pub skip_preflight: bool,
|
||||
pub preflight_commitment: Option<CommitmentLevel>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcSimulateTransactionConfig {
|
||||
#[serde(default)]
|
||||
pub sig_verify: bool,
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
@@ -47,6 +55,7 @@ pub struct RpcStakeConfig {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcAccountInfoConfig {
|
||||
pub encoding: Option<UiAccountEncoding>,
|
||||
pub data_slice: Option<UiDataSliceConfig>,
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
}
|
||||
@@ -70,5 +79,6 @@ pub enum RpcTokenAccountsFilter {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcGetConfirmedSignaturesForAddress2Config {
|
||||
pub before: Option<String>, // Signature as base-58 string
|
||||
pub until: Option<String>, // Signature as base-58 string
|
||||
pub limit: Option<usize>,
|
||||
}
|
||||
|
@@ -28,6 +28,7 @@ pub enum RpcRequest {
|
||||
GetLargestAccounts,
|
||||
GetLeaderSchedule,
|
||||
GetMinimumBalanceForRentExemption,
|
||||
GetMultipleAccounts,
|
||||
GetProgramAccounts,
|
||||
GetRecentBlockhash,
|
||||
GetSignatureStatuses,
|
||||
@@ -80,6 +81,7 @@ impl fmt::Display for RpcRequest {
|
||||
RpcRequest::GetLargestAccounts => "getLargestAccounts",
|
||||
RpcRequest::GetLeaderSchedule => "getLeaderSchedule",
|
||||
RpcRequest::GetMinimumBalanceForRentExemption => "getMinimumBalanceForRentExemption",
|
||||
RpcRequest::GetMultipleAccounts => "getMultipleAccounts",
|
||||
RpcRequest::GetProgramAccounts => "getProgramAccounts",
|
||||
RpcRequest::GetRecentBlockhash => "getRecentBlockhash",
|
||||
RpcRequest::GetSignatureStatuses => "getSignatureStatuses",
|
||||
@@ -110,11 +112,12 @@ impl fmt::Display for RpcRequest {
|
||||
}
|
||||
}
|
||||
|
||||
pub const NUM_LARGEST_ACCOUNTS: usize = 20;
|
||||
pub const MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS: usize = 256;
|
||||
pub const MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS_SLOT_RANGE: u64 = 10_000;
|
||||
pub const MAX_GET_CONFIRMED_BLOCKS_RANGE: u64 = 500_000;
|
||||
pub const MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS2_LIMIT: usize = 1_000;
|
||||
pub const MAX_MULTIPLE_ACCOUNTS: usize = 100;
|
||||
pub const NUM_LARGEST_ACCOUNTS: usize = 20;
|
||||
|
||||
// Validators that are this number of slots behind are considered delinquent
|
||||
pub const DELINQUENT_VALIDATOR_SLOT_DISTANCE: u64 = 128;
|
||||
@@ -159,12 +162,12 @@ mod tests {
|
||||
fn test_build_request_json() {
|
||||
let test_request = RpcRequest::GetAccountInfo;
|
||||
let addr = json!("deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx");
|
||||
let request = test_request.build_request_json(1, json!([addr]));
|
||||
let request = test_request.build_request_json(1, json!([addr.clone()]));
|
||||
assert_eq!(request["method"], "getAccountInfo");
|
||||
assert_eq!(request["params"], json!([addr]));
|
||||
|
||||
let test_request = RpcRequest::GetBalance;
|
||||
let request = test_request.build_request_json(1, json!([addr]));
|
||||
let request = test_request.build_request_json(1, json!([addr.clone()]));
|
||||
assert_eq!(request["method"], "getBalance");
|
||||
|
||||
let test_request = RpcRequest::GetEpochInfo;
|
||||
@@ -209,12 +212,13 @@ mod tests {
|
||||
|
||||
// Test request with CommitmentConfig and no params
|
||||
let test_request = RpcRequest::GetRecentBlockhash;
|
||||
let request = test_request.build_request_json(1, json!([commitment_config]));
|
||||
let request = test_request.build_request_json(1, json!([commitment_config.clone()]));
|
||||
assert_eq!(request["params"], json!([commitment_config.clone()]));
|
||||
|
||||
// Test request with CommitmentConfig and params
|
||||
let test_request = RpcRequest::GetBalance;
|
||||
let request = test_request.build_request_json(1, json!([addr, commitment_config]));
|
||||
let request =
|
||||
test_request.build_request_json(1, json!([addr.clone(), commitment_config.clone()]));
|
||||
assert_eq!(request["params"], json!([addr, commitment_config]));
|
||||
|
||||
// Test request with CommitmentConfig and params
|
||||
|
@@ -254,6 +254,37 @@ impl ThinClient {
|
||||
.into())
|
||||
}
|
||||
|
||||
pub fn poll_balance_with_timeout_and_commitment(
|
||||
&self,
|
||||
pubkey: &Pubkey,
|
||||
polling_frequency: &Duration,
|
||||
timeout: &Duration,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> TransportResult<u64> {
|
||||
self.rpc_client()
|
||||
.poll_balance_with_timeout_and_commitment(
|
||||
pubkey,
|
||||
polling_frequency,
|
||||
timeout,
|
||||
commitment_config,
|
||||
)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
pub fn poll_balance_with_timeout(
|
||||
&self,
|
||||
pubkey: &Pubkey,
|
||||
polling_frequency: &Duration,
|
||||
timeout: &Duration,
|
||||
) -> TransportResult<u64> {
|
||||
self.poll_balance_with_timeout_and_commitment(
|
||||
pubkey,
|
||||
polling_frequency,
|
||||
timeout,
|
||||
CommitmentConfig::default(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn poll_get_balance(&self, pubkey: &Pubkey) -> TransportResult<u64> {
|
||||
self.poll_get_balance_with_commitment(pubkey, CommitmentConfig::default())
|
||||
}
|
||||
@@ -299,6 +330,12 @@ impl ThinClient {
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
/// Check a signature in the bank. This method blocks
|
||||
/// until the server sends a response.
|
||||
pub fn check_signature(&self, signature: &Signature) -> bool {
|
||||
self.rpc_client().check_signature(signature)
|
||||
}
|
||||
|
||||
pub fn validator_exit(&self) -> TransportResult<bool> {
|
||||
self.rpc_client().validator_exit().map_err(|e| e.into())
|
||||
}
|
||||
|
@@ -1,12 +1,12 @@
|
||||
[package]
|
||||
name = "solana-core"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.3.1"
|
||||
version = "1.2.29"
|
||||
documentation = "https://docs.rs/solana"
|
||||
homepage = "https://solana.com/"
|
||||
readme = "../README.md"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
|
||||
license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
|
||||
@@ -24,70 +24,66 @@ crossbeam-channel = "0.4"
|
||||
ed25519-dalek = "=1.0.0-pre.4"
|
||||
fs_extra = "1.1.0"
|
||||
flate2 = "1.0"
|
||||
indexmap = "1.4"
|
||||
indexmap = "1.3"
|
||||
itertools = "0.9.0"
|
||||
jsonrpc-core = "14.2.0"
|
||||
jsonrpc-core-client = { version = "14.2.0", features = ["ws"] }
|
||||
jsonrpc-derive = "14.2.1"
|
||||
jsonrpc-http-server = "14.2.0"
|
||||
jsonrpc-pubsub = "14.2.0"
|
||||
jsonrpc-ws-server = "14.2.0"
|
||||
jsonrpc-core = "14.1.0"
|
||||
jsonrpc-core-client = { version = "14.1.0", features = ["ws"] }
|
||||
jsonrpc-derive = "14.1.0"
|
||||
jsonrpc-http-server = "14.1.0"
|
||||
jsonrpc-pubsub = "14.1.0"
|
||||
jsonrpc-ws-server = "14.1.0"
|
||||
log = "0.4.8"
|
||||
num_cpus = "1.13.0"
|
||||
num-traits = "0.2"
|
||||
rand = "0.7.0"
|
||||
rand_chacha = "0.2.2"
|
||||
raptorq = "1.4.2"
|
||||
rayon = "1.3.1"
|
||||
regex = "1.3.9"
|
||||
serde = "1.0.112"
|
||||
rayon = "1.4.0"
|
||||
regex = "1.3.7"
|
||||
serde = "1.0.110"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.56"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "1.3.1" }
|
||||
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "1.3.1" }
|
||||
solana-budget-program = { path = "../programs/budget", version = "1.3.1" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.3.1" }
|
||||
solana-client = { path = "../client", version = "1.3.1" }
|
||||
solana-faucet = { path = "../faucet", version = "1.3.1" }
|
||||
solana-genesis-programs = { path = "../genesis-programs", version = "1.3.1" }
|
||||
solana-ledger = { path = "../ledger", version = "1.3.1" }
|
||||
solana-logger = { path = "../logger", version = "1.3.1" }
|
||||
solana-merkle-tree = { path = "../merkle-tree", version = "1.3.1" }
|
||||
solana-metrics = { path = "../metrics", version = "1.3.1" }
|
||||
solana-measure = { path = "../measure", version = "1.3.1" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.3.1" }
|
||||
solana-perf = { path = "../perf", version = "1.3.1" }
|
||||
solana-runtime = { path = "../runtime", version = "1.3.1" }
|
||||
solana-sdk = { path = "../sdk", version = "1.3.1" }
|
||||
solana-sdk-macro-frozen-abi = { path = "../sdk/macro-frozen-abi", version = "1.3.1" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "1.3.1" }
|
||||
solana-storage-bigtable = { path = "../storage-bigtable", version = "1.3.1" }
|
||||
solana-streamer = { path = "../streamer", version = "1.3.1" }
|
||||
solana-sys-tuner = { path = "../sys-tuner", version = "1.3.1" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "1.3.1" }
|
||||
solana-version = { path = "../version", version = "1.3.1" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.3.1" }
|
||||
solana-vote-signer = { path = "../vote-signer", version = "1.3.1" }
|
||||
spl-token-v1-0 = { package = "spl-token", version = "1.0.6", features = ["skip-no-mangle"] }
|
||||
serde_json = "1.0.53"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "1.2.29" }
|
||||
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "1.2.29" }
|
||||
solana-budget-program = { path = "../programs/budget", version = "1.2.29" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.2.29" }
|
||||
solana-client = { path = "../client", version = "1.2.29" }
|
||||
solana-faucet = { path = "../faucet", version = "1.2.29" }
|
||||
solana-genesis-programs = { path = "../genesis-programs", version = "1.2.29" }
|
||||
solana-ledger = { path = "../ledger", version = "1.2.29" }
|
||||
solana-logger = { path = "../logger", version = "1.2.29" }
|
||||
solana-merkle-tree = { path = "../merkle-tree", version = "1.2.29" }
|
||||
solana-metrics = { path = "../metrics", version = "1.2.29" }
|
||||
solana-measure = { path = "../measure", version = "1.2.29" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.2.29" }
|
||||
solana-perf = { path = "../perf", version = "1.2.29" }
|
||||
solana-runtime = { path = "../runtime", version = "1.2.29" }
|
||||
solana-sdk = { path = "../sdk", version = "1.2.29" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "1.2.29" }
|
||||
solana-storage-bigtable = { path = "../storage-bigtable", version = "1.2.29" }
|
||||
solana-streamer = { path = "../streamer", version = "1.2.29" }
|
||||
solana-sys-tuner = { path = "../sys-tuner", version = "1.2.29" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "1.2.29" }
|
||||
solana-version = { path = "../version", version = "1.2.29" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.2.29" }
|
||||
solana-vote-signer = { path = "../vote-signer", version = "1.2.29" }
|
||||
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.2.29" }
|
||||
spl-token-v2-0 = { package = "spl-token", version = "2.0.3", features = ["skip-no-mangle"] }
|
||||
tempfile = "3.1.0"
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "0.2.22", features = ["full"] }
|
||||
tokio_01 = { version = "0.1", package = "tokio" }
|
||||
tokio_fs_01 = { version = "0.1", package = "tokio-fs" }
|
||||
tokio_io_01 = { version = "0.1", package = "tokio-io" }
|
||||
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.3.1" }
|
||||
tokio = { version = "0.2.22", features = ["full"] }
|
||||
trees = "0.2.1"
|
||||
|
||||
[dev-dependencies]
|
||||
base64 = "0.12.3"
|
||||
matches = "0.1.6"
|
||||
reqwest = { version = "0.10.6", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
reqwest = { version = "0.10.4", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
serial_test = "0.4.0"
|
||||
serial_test_derive = "0.4.0"
|
||||
systemstat = "0.1.5"
|
||||
|
||||
[build-dependencies]
|
||||
rustc_version = "0.2"
|
||||
|
||||
[[bench]]
|
||||
name = "banking_stage"
|
||||
|
||||
|
@@ -73,7 +73,6 @@ fn bench_consume_buffered(bencher: &mut Bencher) {
|
||||
let batch_len = batch.packets.len();
|
||||
packets.push((batch, vec![0usize; batch_len]));
|
||||
}
|
||||
let (s, _r) = unbounded();
|
||||
// This tests the performance of buffering packets.
|
||||
// If the packet buffers are copied, performance will be poor.
|
||||
bencher.iter(move || {
|
||||
@@ -83,7 +82,6 @@ fn bench_consume_buffered(bencher: &mut Bencher) {
|
||||
&mut packets,
|
||||
10_000,
|
||||
None,
|
||||
&s,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -112,6 +110,7 @@ fn make_accounts_txs(txes: usize, mint_keypair: &Keypair, hash: Hash) -> Vec<Tra
|
||||
fn make_programs_txs(txes: usize, hash: Hash) -> Vec<Transaction> {
|
||||
let progs = 4;
|
||||
(0..txes)
|
||||
.into_iter()
|
||||
.map(|_| {
|
||||
let mut instructions = vec![];
|
||||
let from_key = Keypair::new();
|
||||
@@ -182,7 +181,7 @@ fn bench_banking(bencher: &mut Bencher, tx_type: TransactionType) {
|
||||
assert!(r.is_ok(), "sanity parallel execution");
|
||||
}
|
||||
bank.clear_signatures();
|
||||
let verified: Vec<_> = to_packets_chunked(&transactions, PACKETS_PER_BATCH);
|
||||
let verified: Vec<_> = to_packets_chunked(&transactions.clone(), PACKETS_PER_BATCH);
|
||||
let ledger_path = get_tmp_ledger_path!();
|
||||
{
|
||||
let blockstore = Arc::new(
|
||||
@@ -192,14 +191,12 @@ fn bench_banking(bencher: &mut Bencher, tx_type: TransactionType) {
|
||||
create_test_recorder(&bank, &blockstore, None);
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(Node::new_localhost().info);
|
||||
let cluster_info = Arc::new(cluster_info);
|
||||
let (s, _r) = unbounded();
|
||||
let _banking_stage = BankingStage::new(
|
||||
&cluster_info,
|
||||
&poh_recorder,
|
||||
verified_receiver,
|
||||
vote_receiver,
|
||||
None,
|
||||
s,
|
||||
);
|
||||
poh_recorder.lock().unwrap().set_bank(&bank);
|
||||
|
||||
@@ -210,7 +207,7 @@ fn bench_banking(bencher: &mut Bencher, tx_type: TransactionType) {
|
||||
// If it is dropped before poh_service, then poh_service will error when
|
||||
// calling send() on the channel.
|
||||
let signal_receiver = Arc::new(signal_receiver);
|
||||
let signal_receiver2 = signal_receiver;
|
||||
let signal_receiver2 = signal_receiver.clone();
|
||||
bencher.iter(move || {
|
||||
let now = Instant::now();
|
||||
let mut sent = 0;
|
||||
@@ -265,7 +262,7 @@ fn simulate_process_entries(
|
||||
mint_keypair: &Keypair,
|
||||
mut tx_vector: Vec<Transaction>,
|
||||
genesis_config: &GenesisConfig,
|
||||
keypairs: &[Keypair],
|
||||
keypairs: &Vec<Keypair>,
|
||||
initial_lamports: u64,
|
||||
num_accounts: usize,
|
||||
) {
|
||||
@@ -291,7 +288,7 @@ fn simulate_process_entries(
|
||||
hash: next_hash(&bank.last_blockhash(), 1, &tx_vector),
|
||||
transactions: tx_vector,
|
||||
};
|
||||
process_entries(&bank, &[entry], randomize_txs, None, None).unwrap();
|
||||
process_entries(&bank, &vec![entry], randomize_txs, None).unwrap();
|
||||
}
|
||||
|
||||
fn bench_process_entries(randomize_txs: bool, bencher: &mut Bencher) {
|
||||
|
@@ -1,4 +1,6 @@
|
||||
#![feature(test)]
|
||||
use rand;
|
||||
|
||||
extern crate solana_ledger;
|
||||
extern crate test;
|
||||
|
||||
|
@@ -22,7 +22,7 @@ fn broadcast_shreds_bench(bencher: &mut Bencher) {
|
||||
solana_logger::setup();
|
||||
let leader_pubkey = Pubkey::new_rand();
|
||||
let leader_info = Node::new_localhost_with_pubkey(&leader_pubkey);
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(leader_info.info);
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(leader_info.info.clone());
|
||||
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
|
||||
|
||||
const NUM_SHREDS: usize = 32;
|
||||
@@ -37,7 +37,7 @@ fn broadcast_shreds_bench(bencher: &mut Bencher) {
|
||||
}
|
||||
let stakes = Arc::new(stakes);
|
||||
let cluster_info = Arc::new(cluster_info);
|
||||
let (peers, peers_and_stakes) = get_broadcast_peers(&cluster_info, Some(stakes));
|
||||
let (peers, peers_and_stakes) = get_broadcast_peers(&cluster_info, Some(stakes.clone()));
|
||||
let shreds = Arc::new(shreds);
|
||||
let last_datapoint = Arc::new(AtomicU64::new(0));
|
||||
bencher.iter(move || {
|
||||
|
@@ -7,13 +7,13 @@ use log::*;
|
||||
use solana_core::cluster_info::{ClusterInfo, Node};
|
||||
use solana_core::contact_info::ContactInfo;
|
||||
use solana_core::retransmit_stage::retransmitter;
|
||||
use solana_ledger::bank_forks::BankForks;
|
||||
use solana_ledger::genesis_utils::{create_genesis_config, GenesisConfigInfo};
|
||||
use solana_ledger::leader_schedule_cache::LeaderScheduleCache;
|
||||
use solana_measure::measure::Measure;
|
||||
use solana_perf::packet::to_packets_chunked;
|
||||
use solana_perf::test_tx::test_tx;
|
||||
use solana_runtime::bank::Bank;
|
||||
use solana_runtime::bank_forks::BankForks;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::timing::timestamp;
|
||||
use std::net::UdpSocket;
|
||||
@@ -65,10 +65,7 @@ fn bench_retransmitter(bencher: &mut Bencher) {
|
||||
let tx = test_tx();
|
||||
const NUM_PACKETS: usize = 50;
|
||||
let chunk_size = NUM_PACKETS / (4 * NUM_THREADS);
|
||||
let batches = to_packets_chunked(
|
||||
&std::iter::repeat(tx).take(NUM_PACKETS).collect::<Vec<_>>(),
|
||||
chunk_size,
|
||||
);
|
||||
let batches = to_packets_chunked(&vec![tx; NUM_PACKETS], chunk_size);
|
||||
info!("batches: {}", batches.len());
|
||||
|
||||
let retransmitter_handles = retransmitter(
|
||||
@@ -83,6 +80,7 @@ fn bench_retransmitter(bencher: &mut Bencher) {
|
||||
bencher.iter(move || {
|
||||
let peer_sockets1 = peer_sockets.clone();
|
||||
let handles: Vec<_> = (0..NUM_PEERS)
|
||||
.into_iter()
|
||||
.map(|p| {
|
||||
let peer_sockets2 = peer_sockets1.clone();
|
||||
let total2 = total.clone();
|
||||
|
@@ -2,13 +2,10 @@
|
||||
|
||||
extern crate test;
|
||||
|
||||
use rand::seq::SliceRandom;
|
||||
use raptorq::{Decoder, Encoder};
|
||||
use solana_ledger::entry::{create_ticks, Entry};
|
||||
use solana_ledger::shred::{
|
||||
max_entries_per_n_shred, max_ticks_per_n_shreds, Shred, Shredder,
|
||||
MAX_DATA_SHREDS_PER_FEC_BLOCK, RECOMMENDED_FEC_RATE, SHRED_PAYLOAD_SIZE,
|
||||
SIZE_OF_DATA_SHRED_IGNORED_TAIL, SIZE_OF_DATA_SHRED_PAYLOAD,
|
||||
max_entries_per_n_shred, max_ticks_per_n_shreds, Shred, Shredder, RECOMMENDED_FEC_RATE,
|
||||
SIZE_OF_DATA_SHRED_PAYLOAD,
|
||||
};
|
||||
use solana_perf::test_tx;
|
||||
use solana_sdk::hash::Hash;
|
||||
@@ -29,34 +26,6 @@ fn make_large_unchained_entries(txs_per_entry: u64, num_entries: u64) -> Vec<Ent
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn make_shreds(num_shreds: usize) -> Vec<Shred> {
|
||||
let shred_size = SIZE_OF_DATA_SHRED_PAYLOAD;
|
||||
let txs_per_entry = 128;
|
||||
let num_entries = max_entries_per_n_shred(
|
||||
&make_test_entry(txs_per_entry),
|
||||
2 * num_shreds as u64,
|
||||
Some(shred_size),
|
||||
);
|
||||
let entries = make_large_unchained_entries(txs_per_entry, num_entries);
|
||||
let shredder =
|
||||
Shredder::new(1, 0, RECOMMENDED_FEC_RATE, Arc::new(Keypair::new()), 0, 0).unwrap();
|
||||
let data_shreds = shredder.entries_to_data_shreds(&entries, true, 0).0;
|
||||
assert!(data_shreds.len() >= num_shreds);
|
||||
data_shreds
|
||||
}
|
||||
|
||||
fn make_concatenated_shreds(num_shreds: usize) -> Vec<u8> {
|
||||
let data_shreds = make_shreds(num_shreds);
|
||||
let valid_shred_data_len = (SHRED_PAYLOAD_SIZE - SIZE_OF_DATA_SHRED_IGNORED_TAIL) as usize;
|
||||
let mut data: Vec<u8> = vec![0; num_shreds * valid_shred_data_len];
|
||||
for (i, shred) in (data_shreds[0..num_shreds]).iter().enumerate() {
|
||||
data[i * valid_shred_data_len..(i + 1) * valid_shred_data_len]
|
||||
.copy_from_slice(&shred.payload[..valid_shred_data_len]);
|
||||
}
|
||||
|
||||
data
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_shredder_ticks(bencher: &mut Bencher) {
|
||||
let kp = Arc::new(Keypair::new());
|
||||
@@ -117,68 +86,3 @@ fn bench_deserialize_hdr(bencher: &mut Bencher) {
|
||||
let _ = Shred::new_from_serialized_shred(payload).unwrap();
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_shredder_coding(bencher: &mut Bencher) {
|
||||
let symbol_count = MAX_DATA_SHREDS_PER_FEC_BLOCK as usize;
|
||||
let data_shreds = make_shreds(symbol_count);
|
||||
bencher.iter(|| {
|
||||
Shredder::generate_coding_shreds(0, RECOMMENDED_FEC_RATE, &data_shreds[..symbol_count], 0)
|
||||
.len();
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_shredder_decoding(bencher: &mut Bencher) {
|
||||
let symbol_count = MAX_DATA_SHREDS_PER_FEC_BLOCK as usize;
|
||||
let data_shreds = make_shreds(symbol_count);
|
||||
let coding_shreds =
|
||||
Shredder::generate_coding_shreds(0, RECOMMENDED_FEC_RATE, &data_shreds[..symbol_count], 0);
|
||||
bencher.iter(|| {
|
||||
Shredder::try_recovery(
|
||||
coding_shreds[..].to_vec(),
|
||||
symbol_count,
|
||||
symbol_count,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
)
|
||||
.unwrap();
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_shredder_coding_raptorq(bencher: &mut Bencher) {
|
||||
let symbol_count = MAX_DATA_SHREDS_PER_FEC_BLOCK;
|
||||
let data = make_concatenated_shreds(symbol_count as usize);
|
||||
let valid_shred_data_len = (SHRED_PAYLOAD_SIZE - SIZE_OF_DATA_SHRED_IGNORED_TAIL) as usize;
|
||||
bencher.iter(|| {
|
||||
let encoder = Encoder::with_defaults(&data, valid_shred_data_len as u16);
|
||||
encoder.get_encoded_packets(symbol_count);
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_shredder_decoding_raptorq(bencher: &mut Bencher) {
|
||||
let symbol_count = MAX_DATA_SHREDS_PER_FEC_BLOCK;
|
||||
let data = make_concatenated_shreds(symbol_count as usize);
|
||||
let valid_shred_data_len = (SHRED_PAYLOAD_SIZE - SIZE_OF_DATA_SHRED_IGNORED_TAIL) as usize;
|
||||
let encoder = Encoder::with_defaults(&data, valid_shred_data_len as u16);
|
||||
let mut packets = encoder.get_encoded_packets(symbol_count as u32);
|
||||
packets.shuffle(&mut rand::thread_rng());
|
||||
|
||||
// Here we simulate losing 1 less than 50% of the packets randomly
|
||||
packets.truncate(packets.len() - packets.len() / 2 + 1);
|
||||
|
||||
bencher.iter(|| {
|
||||
let mut decoder = Decoder::new(encoder.get_config());
|
||||
let mut result = None;
|
||||
for packet in &packets {
|
||||
result = decoder.decode(packet.clone());
|
||||
if result != None {
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert_eq!(result.unwrap(), data);
|
||||
})
|
||||
}
|
||||
|
@@ -37,14 +37,16 @@ fn bench_sigverify_stage(bencher: &mut Bencher) {
|
||||
let from_keypair = Keypair::new();
|
||||
let to_keypair = Keypair::new();
|
||||
let txs: Vec<_> = (0..len)
|
||||
.into_iter()
|
||||
.map(|_| {
|
||||
let amount = thread_rng().gen();
|
||||
system_transaction::transfer(
|
||||
let tx = system_transaction::transfer(
|
||||
&from_keypair,
|
||||
&to_keypair.pubkey(),
|
||||
amount,
|
||||
Hash::default(),
|
||||
)
|
||||
);
|
||||
tx
|
||||
})
|
||||
.collect();
|
||||
to_packets_chunked(&txs, chunk_size)
|
||||
|
@@ -1 +0,0 @@
|
||||
../sdk/build.rs
|
@@ -2,8 +2,7 @@
|
||||
//
|
||||
// This can be expensive since we have to walk the append vecs being cleaned up.
|
||||
|
||||
use rand::{thread_rng, Rng};
|
||||
use solana_runtime::bank_forks::BankForks;
|
||||
use solana_ledger::bank_forks::BankForks;
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc, RwLock,
|
||||
@@ -40,9 +39,7 @@ impl AccountsBackgroundService {
|
||||
consumed_budget = bank
|
||||
.process_stale_slot_with_budget(consumed_budget, SHRUNKEN_ACCOUNT_PER_INTERVAL);
|
||||
|
||||
if bank.block_height() - last_cleaned_slot
|
||||
> (CLEAN_INTERVAL_SLOTS + thread_rng().gen_range(0, 10))
|
||||
{
|
||||
if bank.block_height() - last_cleaned_slot > CLEAN_INTERVAL_SLOTS {
|
||||
bank.clean_accounts();
|
||||
last_cleaned_slot = bank.block_height();
|
||||
}
|
||||
|
@@ -5,8 +5,9 @@
|
||||
// set and halt the node if a mismatch is detected.
|
||||
|
||||
use crate::cluster_info::{ClusterInfo, MAX_SNAPSHOT_HASHES};
|
||||
use solana_runtime::snapshot_package::{
|
||||
AccountsPackage, AccountsPackageReceiver, AccountsPackageSender,
|
||||
use solana_ledger::{
|
||||
snapshot_package::AccountsPackage, snapshot_package::AccountsPackageReceiver,
|
||||
snapshot_package::AccountsPackageSender,
|
||||
};
|
||||
use solana_sdk::{clock::Slot, hash::Hash, pubkey::Pubkey};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
@@ -175,8 +176,8 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::cluster_info::make_accounts_hashes_message;
|
||||
use crate::contact_info::ContactInfo;
|
||||
use solana_runtime::bank_forks::CompressionType;
|
||||
use solana_runtime::snapshot_utils::SnapshotVersion;
|
||||
use solana_ledger::bank_forks::CompressionType;
|
||||
use solana_ledger::snapshot_utils::SnapshotVersion;
|
||||
use solana_sdk::{
|
||||
hash::hash,
|
||||
signature::{Keypair, Signer},
|
||||
|
@@ -3,7 +3,8 @@ use crate::{
|
||||
fork_choice::ForkChoice,
|
||||
progress_map::{ForkStats, ProgressMap},
|
||||
};
|
||||
use solana_runtime::{bank::Bank, bank_forks::BankForks};
|
||||
use solana_ledger::bank_forks::BankForks;
|
||||
use solana_runtime::bank::Bank;
|
||||
use solana_sdk::timing;
|
||||
use std::time::Instant;
|
||||
use std::{
|
||||
@@ -63,7 +64,7 @@ impl ForkChoice for BankWeightForkChoice {
|
||||
.filter(|b| b.slot() < tower.root().unwrap_or(0))
|
||||
.count();
|
||||
|
||||
let last_voted_slot = tower.last_voted_slot();
|
||||
let last_vote = tower.last_vote().slots.last().cloned();
|
||||
let mut heaviest_bank_on_same_fork = None;
|
||||
let mut heaviest_same_fork_weight = 0;
|
||||
let stats: Vec<&ForkStats> = frozen_banks
|
||||
@@ -76,18 +77,18 @@ impl ForkChoice for BankWeightForkChoice {
|
||||
.get_fork_stats(bank.slot())
|
||||
.expect("All frozen banks must exist in the Progress map");
|
||||
|
||||
if let Some(last_voted_slot) = last_voted_slot {
|
||||
if let Some(last_vote) = last_vote {
|
||||
if ancestors
|
||||
.get(&bank.slot())
|
||||
.expect("Entry in frozen banks must exist in ancestors")
|
||||
.contains(&last_voted_slot)
|
||||
.contains(&last_vote)
|
||||
{
|
||||
// Descendant of last vote cannot be locked out
|
||||
assert!(!stats.is_locked_out);
|
||||
|
||||
// ancestors(slot) should not contain the slot itself,
|
||||
// so we should never get the same bank as the last vote
|
||||
assert_ne!(bank.slot(), last_voted_slot);
|
||||
assert_ne!(bank.slot(), last_vote);
|
||||
// highest weight, lowest slot first. frozen_banks is sorted
|
||||
// from least slot to greatest slot, so if two banks have
|
||||
// the same fork weight, the lower slot will be picked
|
||||
|
@@ -24,14 +24,12 @@ use solana_perf::{
|
||||
use solana_runtime::{
|
||||
accounts_db::ErrorCounters,
|
||||
bank::{Bank, TransactionBalancesSet, TransactionProcessResult},
|
||||
bank_utils,
|
||||
transaction_batch::TransactionBatch,
|
||||
vote_sender_types::ReplayVoteSender,
|
||||
};
|
||||
use solana_sdk::{
|
||||
clock::{
|
||||
Slot, DEFAULT_TICKS_PER_SECOND, DEFAULT_TICKS_PER_SLOT, MAX_PROCESSING_AGE,
|
||||
MAX_TRANSACTION_FORWARDING_DELAY, MAX_TRANSACTION_FORWARDING_DELAY_GPU,
|
||||
Slot, DEFAULT_TICKS_PER_SLOT, MAX_PROCESSING_AGE, MAX_TRANSACTION_FORWARDING_DELAY,
|
||||
MAX_TRANSACTION_FORWARDING_DELAY_GPU,
|
||||
},
|
||||
poh_config::PohConfig,
|
||||
pubkey::Pubkey,
|
||||
@@ -83,7 +81,6 @@ impl BankingStage {
|
||||
verified_receiver: CrossbeamReceiver<Vec<Packets>>,
|
||||
verified_vote_receiver: CrossbeamReceiver<Vec<Packets>>,
|
||||
transaction_status_sender: Option<TransactionStatusSender>,
|
||||
gossip_vote_sender: ReplayVoteSender,
|
||||
) -> Self {
|
||||
Self::new_num_threads(
|
||||
cluster_info,
|
||||
@@ -92,7 +89,6 @@ impl BankingStage {
|
||||
verified_vote_receiver,
|
||||
Self::num_threads(),
|
||||
transaction_status_sender,
|
||||
gossip_vote_sender,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -103,7 +99,6 @@ impl BankingStage {
|
||||
verified_vote_receiver: CrossbeamReceiver<Vec<Packets>>,
|
||||
num_threads: u32,
|
||||
transaction_status_sender: Option<TransactionStatusSender>,
|
||||
gossip_vote_sender: ReplayVoteSender,
|
||||
) -> Self {
|
||||
let batch_limit = TOTAL_BUFFERED_PACKETS / ((num_threads - 1) as usize * PACKETS_PER_BATCH);
|
||||
// Single thread to generate entries from many banks.
|
||||
@@ -124,7 +119,6 @@ impl BankingStage {
|
||||
let cluster_info = cluster_info.clone();
|
||||
let mut recv_start = Instant::now();
|
||||
let transaction_status_sender = transaction_status_sender.clone();
|
||||
let gossip_vote_sender = gossip_vote_sender.clone();
|
||||
Builder::new()
|
||||
.name("solana-banking-stage-tx".to_string())
|
||||
.spawn(move || {
|
||||
@@ -138,8 +132,7 @@ impl BankingStage {
|
||||
enable_forwarding,
|
||||
i,
|
||||
batch_limit,
|
||||
transaction_status_sender,
|
||||
gossip_vote_sender,
|
||||
transaction_status_sender.clone(),
|
||||
);
|
||||
})
|
||||
.unwrap()
|
||||
@@ -175,7 +168,6 @@ impl BankingStage {
|
||||
buffered_packets: &mut Vec<PacketsAndOffsets>,
|
||||
batch_limit: usize,
|
||||
transaction_status_sender: Option<TransactionStatusSender>,
|
||||
gossip_vote_sender: &ReplayVoteSender,
|
||||
) -> UnprocessedPackets {
|
||||
let mut unprocessed_packets = vec![];
|
||||
let mut rebuffered_packets = 0;
|
||||
@@ -207,7 +199,6 @@ impl BankingStage {
|
||||
&msgs,
|
||||
unprocessed_indexes.to_owned(),
|
||||
transaction_status_sender.clone(),
|
||||
gossip_vote_sender,
|
||||
);
|
||||
|
||||
new_tx_count += processed;
|
||||
@@ -292,7 +283,6 @@ impl BankingStage {
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn process_buffered_packets(
|
||||
my_pubkey: &Pubkey,
|
||||
socket: &std::net::UdpSocket,
|
||||
@@ -302,7 +292,6 @@ impl BankingStage {
|
||||
enable_forwarding: bool,
|
||||
batch_limit: usize,
|
||||
transaction_status_sender: Option<TransactionStatusSender>,
|
||||
gossip_vote_sender: &ReplayVoteSender,
|
||||
) -> BufferedPacketsDecision {
|
||||
let (leader_at_slot_offset, poh_has_bank, would_be_leader) = {
|
||||
let poh = poh_recorder.lock().unwrap();
|
||||
@@ -330,7 +319,6 @@ impl BankingStage {
|
||||
buffered_packets,
|
||||
batch_limit,
|
||||
transaction_status_sender,
|
||||
gossip_vote_sender,
|
||||
);
|
||||
buffered_packets.append(&mut unprocessed);
|
||||
}
|
||||
@@ -364,7 +352,6 @@ impl BankingStage {
|
||||
decision
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn process_loop(
|
||||
my_pubkey: Pubkey,
|
||||
verified_receiver: &CrossbeamReceiver<Vec<Packets>>,
|
||||
@@ -375,7 +362,6 @@ impl BankingStage {
|
||||
id: u32,
|
||||
batch_limit: usize,
|
||||
transaction_status_sender: Option<TransactionStatusSender>,
|
||||
gossip_vote_sender: ReplayVoteSender,
|
||||
) {
|
||||
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
|
||||
let mut buffered_packets = vec![];
|
||||
@@ -390,7 +376,6 @@ impl BankingStage {
|
||||
enable_forwarding,
|
||||
batch_limit,
|
||||
transaction_status_sender.clone(),
|
||||
&gossip_vote_sender,
|
||||
);
|
||||
if decision == BufferedPacketsDecision::Hold {
|
||||
// If we are waiting on a new bank,
|
||||
@@ -418,7 +403,6 @@ impl BankingStage {
|
||||
id,
|
||||
batch_limit,
|
||||
transaction_status_sender.clone(),
|
||||
&gossip_vote_sender,
|
||||
) {
|
||||
Err(RecvTimeoutError::Timeout) => (),
|
||||
Err(RecvTimeoutError::Disconnected) => break,
|
||||
@@ -517,7 +501,6 @@ impl BankingStage {
|
||||
poh: &Arc<Mutex<PohRecorder>>,
|
||||
batch: &TransactionBatch,
|
||||
transaction_status_sender: Option<TransactionStatusSender>,
|
||||
gossip_vote_sender: &ReplayVoteSender,
|
||||
) -> (Result<usize, PohRecorderError>, Vec<usize>) {
|
||||
let mut load_execute_time = Measure::start("load_execute_time");
|
||||
// Use a shorter maximum age when adding transactions into the pipeline. This will reduce
|
||||
@@ -550,23 +533,24 @@ impl BankingStage {
|
||||
let num_to_commit = num_to_commit.unwrap();
|
||||
|
||||
if num_to_commit != 0 {
|
||||
let tx_results = bank.commit_transactions(
|
||||
txs,
|
||||
None,
|
||||
&mut loaded_accounts,
|
||||
&results,
|
||||
tx_count,
|
||||
signature_count,
|
||||
);
|
||||
let transaction_statuses = bank
|
||||
.commit_transactions(
|
||||
txs,
|
||||
None,
|
||||
&mut loaded_accounts,
|
||||
&results,
|
||||
tx_count,
|
||||
signature_count,
|
||||
)
|
||||
.processing_results;
|
||||
|
||||
bank_utils::find_and_send_votes(txs, &tx_results, Some(gossip_vote_sender));
|
||||
if let Some(sender) = transaction_status_sender {
|
||||
let post_balances = bank.collect_balances(batch);
|
||||
send_transaction_status_batch(
|
||||
bank.clone(),
|
||||
batch.transactions(),
|
||||
batch.iteration_order_vec(),
|
||||
tx_results.processing_results,
|
||||
transaction_statuses,
|
||||
TransactionBalancesSet::new(pre_balances, post_balances),
|
||||
sender,
|
||||
);
|
||||
@@ -594,7 +578,6 @@ impl BankingStage {
|
||||
poh: &Arc<Mutex<PohRecorder>>,
|
||||
chunk_offset: usize,
|
||||
transaction_status_sender: Option<TransactionStatusSender>,
|
||||
gossip_vote_sender: &ReplayVoteSender,
|
||||
) -> (Result<usize, PohRecorderError>, Vec<usize>) {
|
||||
let mut lock_time = Measure::start("lock_time");
|
||||
// Once accounts are locked, other threads cannot encode transactions that will modify the
|
||||
@@ -607,7 +590,6 @@ impl BankingStage {
|
||||
poh,
|
||||
&batch,
|
||||
transaction_status_sender,
|
||||
gossip_vote_sender,
|
||||
);
|
||||
retryable_txs.iter_mut().for_each(|x| *x += chunk_offset);
|
||||
|
||||
@@ -636,7 +618,6 @@ impl BankingStage {
|
||||
transactions: &[Transaction],
|
||||
poh: &Arc<Mutex<PohRecorder>>,
|
||||
transaction_status_sender: Option<TransactionStatusSender>,
|
||||
gossip_vote_sender: &ReplayVoteSender,
|
||||
) -> (usize, Vec<usize>) {
|
||||
let mut chunk_start = 0;
|
||||
let mut unprocessed_txs = vec![];
|
||||
@@ -652,7 +633,6 @@ impl BankingStage {
|
||||
poh,
|
||||
chunk_start,
|
||||
transaction_status_sender.clone(),
|
||||
gossip_vote_sender,
|
||||
);
|
||||
trace!("process_transactions result: {:?}", result);
|
||||
|
||||
@@ -768,10 +748,7 @@ impl BankingStage {
|
||||
&filter,
|
||||
(MAX_PROCESSING_AGE)
|
||||
.saturating_sub(max_tx_fwd_delay)
|
||||
.saturating_sub(
|
||||
(FORWARD_TRANSACTIONS_TO_LEADER_AT_SLOT_OFFSET * bank.ticks_per_slot()
|
||||
/ DEFAULT_TICKS_PER_SECOND) as usize,
|
||||
),
|
||||
.saturating_sub(FORWARD_TRANSACTIONS_TO_LEADER_AT_SLOT_OFFSET as usize),
|
||||
&mut error_counters,
|
||||
);
|
||||
|
||||
@@ -784,7 +761,6 @@ impl BankingStage {
|
||||
msgs: &Packets,
|
||||
packet_indexes: Vec<usize>,
|
||||
transaction_status_sender: Option<TransactionStatusSender>,
|
||||
gossip_vote_sender: &ReplayVoteSender,
|
||||
) -> (usize, usize, Vec<usize>) {
|
||||
let (transactions, transaction_to_packet_indexes) =
|
||||
Self::transactions_from_packets(msgs, &packet_indexes);
|
||||
@@ -796,13 +772,8 @@ impl BankingStage {
|
||||
|
||||
let tx_len = transactions.len();
|
||||
|
||||
let (processed, unprocessed_tx_indexes) = Self::process_transactions(
|
||||
bank,
|
||||
&transactions,
|
||||
poh,
|
||||
transaction_status_sender,
|
||||
gossip_vote_sender,
|
||||
);
|
||||
let (processed, unprocessed_tx_indexes) =
|
||||
Self::process_transactions(bank, &transactions, poh, transaction_status_sender);
|
||||
|
||||
let unprocessed_tx_count = unprocessed_tx_indexes.len();
|
||||
|
||||
@@ -872,7 +843,6 @@ impl BankingStage {
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
/// Process the incoming packets
|
||||
pub fn process_packets(
|
||||
my_pubkey: &Pubkey,
|
||||
@@ -883,7 +853,6 @@ impl BankingStage {
|
||||
id: u32,
|
||||
batch_limit: usize,
|
||||
transaction_status_sender: Option<TransactionStatusSender>,
|
||||
gossip_vote_sender: &ReplayVoteSender,
|
||||
) -> Result<UnprocessedPackets, RecvTimeoutError> {
|
||||
let mut recv_time = Measure::start("process_packets_recv");
|
||||
let mms = verified_receiver.recv_timeout(recv_timeout)?;
|
||||
@@ -926,7 +895,6 @@ impl BankingStage {
|
||||
&msgs,
|
||||
packet_indexes,
|
||||
transaction_status_sender.clone(),
|
||||
gossip_vote_sender,
|
||||
);
|
||||
|
||||
new_tx_count += processed;
|
||||
@@ -1073,7 +1041,6 @@ mod tests {
|
||||
let bank = Arc::new(Bank::new(&genesis_config));
|
||||
let (verified_sender, verified_receiver) = unbounded();
|
||||
let (vote_sender, vote_receiver) = unbounded();
|
||||
let (gossip_vote_sender, _gossip_vote_receiver) = unbounded();
|
||||
let ledger_path = get_tmp_ledger_path!();
|
||||
{
|
||||
let blockstore = Arc::new(
|
||||
@@ -1090,7 +1057,6 @@ mod tests {
|
||||
verified_receiver,
|
||||
vote_receiver,
|
||||
None,
|
||||
gossip_vote_sender,
|
||||
);
|
||||
drop(verified_sender);
|
||||
drop(vote_sender);
|
||||
@@ -1125,15 +1091,12 @@ mod tests {
|
||||
create_test_recorder(&bank, &blockstore, Some(poh_config));
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(Node::new_localhost().info);
|
||||
let cluster_info = Arc::new(cluster_info);
|
||||
let (gossip_vote_sender, _gossip_vote_receiver) = unbounded();
|
||||
|
||||
let banking_stage = BankingStage::new(
|
||||
&cluster_info,
|
||||
&poh_recorder,
|
||||
verified_receiver,
|
||||
vote_receiver,
|
||||
None,
|
||||
gossip_vote_sender,
|
||||
);
|
||||
trace!("sending bank");
|
||||
drop(verified_sender);
|
||||
@@ -1191,15 +1154,12 @@ mod tests {
|
||||
create_test_recorder(&bank, &blockstore, Some(poh_config));
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(Node::new_localhost().info);
|
||||
let cluster_info = Arc::new(cluster_info);
|
||||
let (gossip_vote_sender, _gossip_vote_receiver) = unbounded();
|
||||
|
||||
let banking_stage = BankingStage::new(
|
||||
&cluster_info,
|
||||
&poh_recorder,
|
||||
verified_receiver,
|
||||
vote_receiver,
|
||||
None,
|
||||
gossip_vote_sender,
|
||||
);
|
||||
|
||||
// fund another account so we can send 2 good transactions in a single batch.
|
||||
@@ -1320,8 +1280,6 @@ mod tests {
|
||||
let (vote_sender, vote_receiver) = unbounded();
|
||||
let ledger_path = get_tmp_ledger_path!();
|
||||
{
|
||||
let (gossip_vote_sender, _gossip_vote_receiver) = unbounded();
|
||||
|
||||
let entry_receiver = {
|
||||
// start a banking_stage to eat verified receiver
|
||||
let bank = Arc::new(Bank::new(&genesis_config));
|
||||
@@ -1344,7 +1302,6 @@ mod tests {
|
||||
vote_receiver,
|
||||
2,
|
||||
None,
|
||||
gossip_vote_sender,
|
||||
);
|
||||
|
||||
// wait for banking_stage to eat the packets
|
||||
@@ -1722,7 +1679,6 @@ mod tests {
|
||||
let poh_recorder = Arc::new(Mutex::new(poh_recorder));
|
||||
|
||||
poh_recorder.lock().unwrap().set_working_bank(working_bank);
|
||||
let (gossip_vote_sender, _gossip_vote_receiver) = unbounded();
|
||||
|
||||
BankingStage::process_and_record_transactions(
|
||||
&bank,
|
||||
@@ -1730,7 +1686,6 @@ mod tests {
|
||||
&poh_recorder,
|
||||
0,
|
||||
None,
|
||||
&gossip_vote_sender,
|
||||
)
|
||||
.0
|
||||
.unwrap();
|
||||
@@ -1767,7 +1722,6 @@ mod tests {
|
||||
&poh_recorder,
|
||||
0,
|
||||
None,
|
||||
&gossip_vote_sender,
|
||||
)
|
||||
.0,
|
||||
Err(PohRecorderError::MaxHeightReached)
|
||||
@@ -1819,15 +1773,12 @@ mod tests {
|
||||
|
||||
poh_recorder.lock().unwrap().set_working_bank(working_bank);
|
||||
|
||||
let (gossip_vote_sender, _gossip_vote_receiver) = unbounded();
|
||||
|
||||
let (result, unprocessed) = BankingStage::process_and_record_transactions(
|
||||
&bank,
|
||||
&transactions,
|
||||
&poh_recorder,
|
||||
0,
|
||||
None,
|
||||
&gossip_vote_sender,
|
||||
);
|
||||
|
||||
assert!(result.is_ok());
|
||||
@@ -1911,16 +1862,8 @@ mod tests {
|
||||
// record
|
||||
let poh_recorder = Arc::new(Mutex::new(poh_recorder));
|
||||
|
||||
let (gossip_vote_sender, _gossip_vote_receiver) = unbounded();
|
||||
|
||||
let (processed_transactions_count, mut retryable_txs) =
|
||||
BankingStage::process_transactions(
|
||||
&bank,
|
||||
&transactions,
|
||||
&poh_recorder,
|
||||
None,
|
||||
&gossip_vote_sender,
|
||||
);
|
||||
BankingStage::process_transactions(&bank, &transactions, &poh_recorder, None);
|
||||
|
||||
assert_eq!(processed_transactions_count, 0,);
|
||||
|
||||
@@ -1997,15 +1940,12 @@ mod tests {
|
||||
&Arc::new(AtomicBool::new(false)),
|
||||
);
|
||||
|
||||
let (gossip_vote_sender, _gossip_vote_receiver) = unbounded();
|
||||
|
||||
let _ = BankingStage::process_and_record_transactions(
|
||||
&bank,
|
||||
&transactions,
|
||||
&poh_recorder,
|
||||
0,
|
||||
Some(transaction_status_sender),
|
||||
&gossip_vote_sender,
|
||||
);
|
||||
|
||||
transaction_status_service.join().unwrap();
|
||||
|
93
core/src/bigtable_upload_service.rs
Normal file
93
core/src/bigtable_upload_service.rs
Normal file
@@ -0,0 +1,93 @@
|
||||
use crate::commitment::BlockCommitmentCache;
|
||||
use solana_ledger::blockstore::Blockstore;
|
||||
use std::{
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
sync::{Arc, RwLock},
|
||||
thread::{self, Builder, JoinHandle},
|
||||
};
|
||||
use tokio::runtime;
|
||||
|
||||
// Delay uploading the largest confirmed root for this many slots. This is done in an attempt to
|
||||
// ensure that the `CacheBlockTimeService` has had enough time to add the block time for the root
|
||||
// before it's uploaded to BigTable.
|
||||
//
|
||||
// A more direct connection between CacheBlockTimeService and BigTableUploadService would be
|
||||
// preferable...
|
||||
const LARGEST_CONFIRMED_ROOT_UPLOAD_DELAY: usize = 100;
|
||||
|
||||
pub struct BigTableUploadService {
|
||||
thread: JoinHandle<()>,
|
||||
}
|
||||
|
||||
impl BigTableUploadService {
|
||||
pub fn new(
|
||||
runtime_handle: runtime::Handle,
|
||||
bigtable_ledger_storage: solana_storage_bigtable::LedgerStorage,
|
||||
blockstore: Arc<Blockstore>,
|
||||
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
|
||||
exit: Arc<AtomicBool>,
|
||||
) -> Self {
|
||||
info!("Starting BigTable upload service");
|
||||
let thread = Builder::new()
|
||||
.name("bigtable-upload".to_string())
|
||||
.spawn(move || {
|
||||
Self::run(
|
||||
runtime_handle,
|
||||
bigtable_ledger_storage,
|
||||
blockstore,
|
||||
block_commitment_cache,
|
||||
exit,
|
||||
)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
Self { thread }
|
||||
}
|
||||
|
||||
fn run(
|
||||
runtime: runtime::Handle,
|
||||
bigtable_ledger_storage: solana_storage_bigtable::LedgerStorage,
|
||||
blockstore: Arc<Blockstore>,
|
||||
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
|
||||
exit: Arc<AtomicBool>,
|
||||
) {
|
||||
let mut start_slot = 0;
|
||||
loop {
|
||||
if exit.load(Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
|
||||
let end_slot = block_commitment_cache
|
||||
.read()
|
||||
.unwrap()
|
||||
.largest_confirmed_root()
|
||||
.saturating_sub(LARGEST_CONFIRMED_ROOT_UPLOAD_DELAY as u64);
|
||||
|
||||
if end_slot <= start_slot {
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
continue;
|
||||
}
|
||||
|
||||
let result = runtime.block_on(solana_ledger::bigtable_upload::upload_confirmed_blocks(
|
||||
blockstore.clone(),
|
||||
bigtable_ledger_storage.clone(),
|
||||
start_slot,
|
||||
Some(end_slot),
|
||||
true,
|
||||
exit.clone(),
|
||||
));
|
||||
|
||||
match result {
|
||||
Ok(()) => start_slot = end_slot,
|
||||
Err(err) => {
|
||||
warn!("bigtable: upload_confirmed_blocks: {}", err);
|
||||
std::thread::sleep(std::time::Duration::from_secs(2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn join(self) -> thread::Result<()> {
|
||||
self.thread.join()
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user