Compare commits
335 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
563231132f | ||
|
32ec9147bb | ||
|
4be8842925 | ||
|
b1ef9045ec | ||
|
dbe4d87e60 | ||
|
d2efa3aa15 | ||
|
ccd2c6cc13 | ||
|
e976b1547a | ||
|
03ac807756 | ||
|
067871cc39 | ||
|
e9ceb99460 | ||
|
cd994f0162 | ||
|
01e4d0a1e9 | ||
|
7f0f43cb28 | ||
|
d0bf97d25d | ||
|
c3056734e3 | ||
|
5e2b9e595d | ||
|
9be3e00546 | ||
|
0c2dcd759c | ||
|
b711476811 | ||
|
e4fe7dfbbd | ||
|
40e62c60d3 | ||
|
a93d37b804 | ||
|
65e6df2b0d | ||
|
f02bd10d4a | ||
|
d7d8a751d9 | ||
|
f6f4193d4d | ||
|
fe0077d88a | ||
|
8016f61ce8 | ||
|
0b6366da9c | ||
|
d567a62cc7 | ||
|
eccea2b1ea | ||
|
bfd93cc13f | ||
|
b21c89e494 | ||
|
253b68abf1 | ||
|
49034b8016 | ||
|
3838fb62d4 | ||
|
9f267fc5e7 | ||
|
5a82265650 | ||
|
77d2ed95ff | ||
|
858ca752e2 | ||
|
fea0bd234c | ||
|
7af7d5f22c | ||
|
de4cccd977 | ||
|
9f74136632 | ||
|
21484acc20 | ||
|
36ad03af1f | ||
|
e255ee52b1 | ||
|
972540907b | ||
|
cfeed09f1f | ||
|
dadebb2bba | ||
|
169403a15e | ||
|
e2a874370b | ||
|
baf7713744 | ||
|
573304cf73 | ||
|
ec6d5933de | ||
|
30b815e7bc | ||
|
f463ebfde2 | ||
|
ba0aa706e4 | ||
|
eacf9209f7 | ||
|
ba12a14494 | ||
|
4e60f95854 | ||
|
a7193ce834 | ||
|
f12a467177 | ||
|
45a92337f4 | ||
|
bfa6e9bdf6 | ||
|
a7d9a52690 | ||
|
4b3391f1d8 | ||
|
19b203f344 | ||
|
6150faafb6 | ||
|
49e608295e | ||
|
636be95e2a | ||
|
0db5a74bb9 | ||
|
08fd4905db | ||
|
3610b6f31c | ||
|
b35f35a7e8 | ||
|
790a6b7550 | ||
|
30d2e15945 | ||
|
7b3c7a075a | ||
|
f534698618 | ||
|
fe1347b441 | ||
|
8f6c01f0f8 | ||
|
066ff36175 | ||
|
5da9e7cb8a | ||
|
09b68f2fbb | ||
|
d9fcd84bc2 | ||
|
86703384dc | ||
|
580b0859e8 | ||
|
5d8c5254b0 | ||
|
ea83292daa | ||
|
f1c3e6dc36 | ||
|
bdd19c09d1 | ||
|
95cbfce900 | ||
|
a404a9d802 | ||
|
15cd1283e8 | ||
|
512a193674 | ||
|
de37795ca1 | ||
|
cb7871347d | ||
|
b4b9ea7771 | ||
|
62b7bf5365 | ||
|
91c57cd70d | ||
|
cb35fc185b | ||
|
cca9009f23 | ||
|
d8d73ff56c | ||
|
34504797b4 | ||
|
1767e4fbde | ||
|
39515cae5e | ||
|
116d67e1e3 | ||
|
08bda35fd6 | ||
|
ba1d0927e6 | ||
|
555df9f96c | ||
|
99166a4a59 | ||
|
92534849db | ||
|
5ba8b4884b | ||
|
cb878f2ea8 | ||
|
b1d5bf30d2 | ||
|
481c60e287 | ||
|
86242dc3ba | ||
|
71899deb53 | ||
|
7e2e0d4a86 | ||
|
d4cc7c6b66 | ||
|
b62349f081 | ||
|
d16638dc90 | ||
|
b97fc31fcd | ||
|
4378634970 | ||
|
10e12d14e1 | ||
|
c5cfbee853 | ||
|
c276670a94 | ||
|
676da0a836 | ||
|
0021cf924f | ||
|
d593ee187c | ||
|
a07bfc2d76 | ||
|
f0e9843dd4 | ||
|
a2f643e7c7 | ||
|
7ebaf1c192 | ||
|
6a61e7a01e | ||
|
1c23f135bf | ||
|
3c67f71695 | ||
|
d380b9cef7 | ||
|
7415821156 | ||
|
4a6c3a9331 | ||
|
0cd1cce588 | ||
|
f762b4a730 | ||
|
08f7f2546e | ||
|
cb701fbbd9 | ||
|
231ff7d70d | ||
|
a154414e65 | ||
|
673c679523 | ||
|
65a7621b17 | ||
|
c9da25836a | ||
|
b48dd58fda | ||
|
40f32fd37d | ||
|
fef4100f5f | ||
|
68bf58aac0 | ||
|
5677662d61 | ||
|
6755fd0c96 | ||
|
dfbe38b859 | ||
|
480a35d678 | ||
|
e6b53c262b | ||
|
e127631f8d | ||
|
2d246581ed | ||
|
952a5b11c1 | ||
|
c49a8cb67c | ||
|
733a1c85cf | ||
|
c8f7719c9e | ||
|
c89f5e28b7 | ||
|
38e4c34d18 | ||
|
afa7343bc2 | ||
|
8ea584e01f | ||
|
239dc9b0b7 | ||
|
6e6a55b7d6 | ||
|
815bad8a6c | ||
|
74f813574f | ||
|
07648f43db | ||
|
99f0d29e65 | ||
|
87825f3beb | ||
|
8e38f90e54 | ||
|
d72c90e475 | ||
|
459ae81655 | ||
|
a2ce22f11b | ||
|
44ad4a1ecd | ||
|
fcd8dd75c5 | ||
|
ca262fdeb9 | ||
|
3d6bb95932 | ||
|
e5d36fcfb3 | ||
|
061965e291 | ||
|
e56681a2f6 | ||
|
8cf23903ce | ||
|
7ac2aae730 | ||
|
6f5b9331bd | ||
|
a04375e204 | ||
|
7b19d26a6e | ||
|
3c3a3f0b50 | ||
|
0367b32533 | ||
|
09392ee562 | ||
|
1a848e22ea | ||
|
3d8cadebc0 | ||
|
47b8d518c5 | ||
|
011d2dd41d | ||
|
bdfffd0151 | ||
|
722cebc4c3 | ||
|
771b98a168 | ||
|
1b02ec4f6e | ||
|
aae51925c1 | ||
|
00626fbf4c | ||
|
14ffc05fd4 | ||
|
6e5c9a1b1c | ||
|
1276b92462 | ||
|
89241cedba | ||
|
0e3e3a03cc | ||
|
25fe93e9fb | ||
|
4440a8d9fa | ||
|
b737e562ab | ||
|
9cb32b2105 | ||
|
f46ad1b7b7 | ||
|
b15603b4eb | ||
|
c7267799ce | ||
|
ed0a083cd9 | ||
|
484bd48b35 | ||
|
ae73cc8d05 | ||
|
fc59a08f0e | ||
|
15da7968c5 | ||
|
b58a6e2b6e | ||
|
ec15ea079f | ||
|
4b5a05bf38 | ||
|
7dd7141307 | ||
|
e5175c843d | ||
|
d5ff64b0d7 | ||
|
0fbdc7e152 | ||
|
49aca9ecd8 | ||
|
fcc147b4f2 | ||
|
c455d1b1c5 | ||
|
e9b29fc697 | ||
|
fdea6fad26 | ||
|
a4bc31341a | ||
|
4af797c0a2 | ||
|
a1e06df4a8 | ||
|
1f2480fd9f | ||
|
8587bd0d69 | ||
|
0063a58e95 | ||
|
9aeb3bc5d6 | ||
|
97665b977e | ||
|
c45ed29cf4 | ||
|
635afbabff | ||
|
98afdad1dd | ||
|
c085b94b43 | ||
|
a53946c485 | ||
|
f6de92c346 | ||
|
46f9822d62 | ||
|
6dad84d228 | ||
|
3582607aa0 | ||
|
f051077350 | ||
|
ffd6f3e6bf | ||
|
c6b2eb07ee | ||
|
7a3e1f9826 | ||
|
8a690b6cf7 | ||
|
8688efa89b | ||
|
3b047e5b99 | ||
|
3cddc731b2 | ||
|
1d29a583c6 | ||
|
b5335edb35 | ||
|
abee1e83eb | ||
|
6c5be574c8 | ||
|
c1f993d2fc | ||
|
e2ddb2f0ea | ||
|
f3faba5ca9 | ||
|
3a6fd91739 | ||
|
2d2b3d8287 | ||
|
6e47b88399 | ||
|
941e56c6c7 | ||
|
d1adc2a446 | ||
|
02da7dfedf | ||
|
eb0fd3625a | ||
|
b87e606626 | ||
|
1c91376f78 | ||
|
10067ad07b | ||
|
eb76289107 | ||
|
8926736e1c | ||
|
bf4c169703 | ||
|
0020e43476 | ||
|
a9a2c76221 | ||
|
4754b4e871 | ||
|
52ffb9a64a | ||
|
bd0b1503c6 | ||
|
10e7fa40ac | ||
|
198ed407b7 | ||
|
d96af2dd23 | ||
|
192cca8f98 | ||
|
ee716e1c55 | ||
|
6dd3c7c2dd | ||
|
582b4c9edf | ||
|
f15add2a74 | ||
|
74d48910e2 | ||
|
c53e8ee3ad | ||
|
c5e5fedc47 | ||
|
b9929dcd67 | ||
|
554a158443 | ||
|
b7fa4b7ee1 | ||
|
fd44cee8cc | ||
|
c6a362cce2 | ||
|
252180c244 | ||
|
e02b4e698e | ||
|
4811afe8eb | ||
|
bc4568b10f | ||
|
d59c131e90 | ||
|
825027f9f7 | ||
|
9b8f0bee99 | ||
|
fc13c1d654 | ||
|
a57758e9c9 | ||
|
564590462a | ||
|
269f6af97e | ||
|
57b8a59632 | ||
|
4289f52d2b | ||
|
573f68620b | ||
|
4bfe64688b | ||
|
50034848a5 | ||
|
981294cbc6 | ||
|
ff728e5e56 | ||
|
9aaf41bef2 | ||
|
271eec656c | ||
|
13d071607f | ||
|
ffe35d9a10 | ||
|
bb2fb07b39 | ||
|
85fc51dc61 | ||
|
0276b6c4c2 | ||
|
c481e4fe7f | ||
|
76a3b3ad11 | ||
|
356c663e88 | ||
|
015bbc1e12 | ||
|
454a9f3175 | ||
|
485b3d64a1 | ||
|
5d170d83c0 | ||
|
f54d8ea3ab | ||
|
ef9f54b3d4 | ||
|
8d0b102b44 |
2
.buildkite/env/secrets.ejson
vendored
2
.buildkite/env/secrets.ejson
vendored
@@ -2,6 +2,6 @@
|
||||
"_public_key": "ae29f4f7ad2fc92de70d470e411c8426d5d48db8817c9e3dae574b122192335f",
|
||||
"_comment": "These credentials are encrypted and pose no risk",
|
||||
"environment": {
|
||||
"CODECOV_TOKEN": "EJ[1:KToenD1Sr3w82lHGxz1n+j3hwNlLk/1pYrjZHlvY6kE=:hN1Q25omtJ+4yYVn+qzIsPLKT3O6J9XN:DMLNLXi/pkWgvwF6gNIcNF222sgsRR9LnwLZYj0P0wGj7q6w8YQnd1Rskj+sRroI/z5pQg==]"
|
||||
"CODECOV_TOKEN": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:3K68mE38LJ2RB98VWmjuNLFBNn1XTGR4:cR4r05/TOZQKmEZp1v4CSgUJtC6QJiOaL85QjXW0qZ061fMnsBA8AtAPMDoDq4WCGOZM1A==]"
|
||||
}
|
||||
}
|
||||
|
@@ -36,7 +36,4 @@ export CARGO_TARGET_CACHE=$HOME/cargo-target-cache/"$CHANNEL"-"$BUILDKITE_LABEL"
|
||||
# `std:
|
||||
# "found possibly newer version of crate `std` which `xyz` depends on
|
||||
rm -rf target/bpfel-unknown-unknown
|
||||
if [[ $BUILDKITE_LABEL = "stable-perf" ]]; then
|
||||
rm -rf target/release
|
||||
fi
|
||||
)
|
||||
|
16
.mergify.yml
16
.mergify.yml
@@ -50,6 +50,14 @@ pull_request_rules:
|
||||
label:
|
||||
add:
|
||||
- automerge
|
||||
- name: v1.3 backport
|
||||
conditions:
|
||||
- label=v1.3
|
||||
actions:
|
||||
backport:
|
||||
ignore_conflicts: true
|
||||
branches:
|
||||
- v1.3
|
||||
- name: v1.4 backport
|
||||
conditions:
|
||||
- label=v1.4
|
||||
@@ -74,11 +82,3 @@ pull_request_rules:
|
||||
ignore_conflicts: true
|
||||
branches:
|
||||
- v1.6
|
||||
- name: v1.7 backport
|
||||
conditions:
|
||||
- label=v1.7
|
||||
actions:
|
||||
backport:
|
||||
ignore_conflicts: true
|
||||
branches:
|
||||
- v1.7
|
||||
|
1893
Cargo.lock
generated
1893
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,5 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"accounts-cluster-bench",
|
||||
"bench-exchange",
|
||||
"bench-streamer",
|
||||
"bench-tps",
|
||||
@@ -77,6 +76,3 @@ members = [
|
||||
exclude = [
|
||||
"programs/bpf",
|
||||
]
|
||||
|
||||
[profile.dev]
|
||||
split-debuginfo = "unpacked"
|
||||
|
39
README.md
39
README.md
@@ -107,41 +107,6 @@ send us that patch!
|
||||
|
||||
# Disclaimer
|
||||
|
||||
All claims, content, designs, algorithms, estimates, roadmaps,
|
||||
specifications, and performance measurements described in this project
|
||||
are done with the Solana Foundation's ("SF") best efforts. It is up to
|
||||
the reader to check and validate their accuracy and truthfulness.
|
||||
Furthermore nothing in this project constitutes a solicitation for
|
||||
investment.
|
||||
All claims, content, designs, algorithms, estimates, roadmaps, specifications, and performance measurements described in this project are done with the author's best effort. It is up to the reader to check and validate their accuracy and truthfulness. Furthermore nothing in this project constitutes a solicitation for investment.
|
||||
|
||||
Any content produced by SF or developer resources that SF provides, are
|
||||
for educational and inspiration purposes only. SF does not encourage,
|
||||
induce or sanction the deployment, integration or use of any such
|
||||
applications (including the code comprising the Solana blockchain
|
||||
protocol) in violation of applicable laws or regulations and hereby
|
||||
prohibits any such deployment, integration or use. This includes use of
|
||||
any such applications by the reader (a) in violation of export control
|
||||
or sanctions laws of the United States or any other applicable
|
||||
jurisdiction, (b) if the reader is located in or ordinarily resident in
|
||||
a country or territory subject to comprehensive sanctions administered
|
||||
by the U.S. Office of Foreign Assets Control (OFAC), or (c) if the
|
||||
reader is or is working on behalf of a Specially Designated National
|
||||
(SDN) or a person subject to similar blocking or denied party
|
||||
prohibitions.
|
||||
|
||||
The reader should be aware that U.S. export control and sanctions laws
|
||||
prohibit U.S. persons (and other persons that are subject to such laws)
|
||||
from transacting with persons in certain countries and territories or
|
||||
that are on the SDN list. As a project based primarily on open-source
|
||||
software, it is possible that such sanctioned persons may nevertheless
|
||||
bypass prohibitions, obtain the code comprising the Solana blockchain
|
||||
protocol (or other project code or applications) and deploy, integrate,
|
||||
or otherwise use it. Accordingly, there is a risk to individuals that
|
||||
other persons using the Solana blockchain protocol may be sanctioned
|
||||
persons and that transactions with such persons would be a violation of
|
||||
U.S. export controls and sanctions law. This risk applies to
|
||||
individuals, organizations, and other ecosystem participants that
|
||||
deploy, integrate, or use the Solana blockchain protocol code directly
|
||||
(e.g., as a node operator), and individuals that transact on the Solana
|
||||
blockchain through light clients, third party interfaces, and/or wallet
|
||||
software.
|
||||
Any content produced by Solana, or developer resources that Solana provides, are for educational and inspiration purposes only. Solana does not encourage, induce or sanction the deployment of any such applications in violation of applicable laws or regulations.
|
||||
|
155
SECURITY.md
155
SECURITY.md
@@ -1,155 +0,0 @@
|
||||
# Security Policy
|
||||
|
||||
1. [Reporting security problems](#reporting)
|
||||
4. [Security Bug Bounties](#bounty)
|
||||
2. [Incident Response Process](#process)
|
||||
|
||||
<a name="reporting"></a>
|
||||
## Reporting security problems to Solana
|
||||
|
||||
**DO NOT CREATE AN ISSUE** to report a security problem. Instead, please send an
|
||||
email to security@solana.com and provide your github username so we can add you
|
||||
to a new draft security advisory for further discussion.
|
||||
|
||||
Expect a response as fast as possible, within one business day at the latest.
|
||||
|
||||
<a name="bounty"></a>
|
||||
## Security Bug Bounties
|
||||
We offer bounties for critical security issues. Please see below for more details.
|
||||
|
||||
Loss of Funds:
|
||||
$500,000 USD in locked SOL tokens (locked for 12 months)
|
||||
* Theft of funds without users signature from any account
|
||||
* Theft of funds without users interaction in system, token, stake, vote programs
|
||||
* Theft of funds that requires users signature - creating a vote program that drains the delegated stakes.
|
||||
|
||||
Consensus/Safety Violations:
|
||||
$250,000 USD in locked SOL tokens (locked for 12 months)
|
||||
* Consensus safety violation
|
||||
* Tricking a validator to accept an optimistic confirmation or rooted slot without a double vote, etc..
|
||||
|
||||
Other Attacks:
|
||||
$100,000 USD in locked SOL tokens (locked for 12 months)
|
||||
* Protocol liveness attacks,
|
||||
* Eclipse attacks,
|
||||
* Remote attacks that partition the network,
|
||||
|
||||
DoS Attacks:
|
||||
$25,000 USD in locked SOL tokens (locked for 12 months)
|
||||
* Remote resource exaustion via Non-RPC protocols
|
||||
|
||||
RPC DoS/Crashes:
|
||||
$5,000 USD in locked SOL tokens (locked for 12 months)
|
||||
* RPC attacks
|
||||
|
||||
Out of Scope:
|
||||
The following components are out of scope for the bounty program
|
||||
* Metrics: `/metrics` in the monorepo as well as https://metrics.solana.com
|
||||
* Explorer: `/explorer` in the monorepo as well as https://explorer.solana.com
|
||||
* Any encrypted credentials, auth tokens, etc. checked into the repo
|
||||
* Bugs in dependencies. Please take them upstream!
|
||||
* Attacks that require social engineering
|
||||
|
||||
Eligibility:
|
||||
* The participant submitting the bug bounty shall follow the process outlined within this document
|
||||
* Valid exploits can be eligible even if they are not successfully executed on the cluster
|
||||
* Multiple submissions for the same class of exploit are still eligible for compensation, though may be compensated at a lower rate, however these will be assessed on a case-by-case basis
|
||||
* Participants must complete KYC and sign the participation agreement here when the registrations are open https://solana.com/validator-registration. Security exploits will still be assessed and open for submission at all times. This needs only be done prior to distribution of tokens.
|
||||
|
||||
Notes:
|
||||
* All locked tokens can be staked during the lockup period
|
||||
|
||||
<a name="process"></a>
|
||||
## Incident Response Process
|
||||
|
||||
In case an incident is discovered or reported, the following process will be
|
||||
followed to contain, respond and remediate:
|
||||
|
||||
### 1. Establish a new draft security advisory
|
||||
In response to an email to security@solana.com, a member of the `solana-labs/admins` group will
|
||||
1. Create a new draft security advisory for the incident at https://github.com/solana-labs/solana/security/advisories
|
||||
1. Add the reporter's github user and the `solana-labs/security-incident-response` group to the draft security advisory
|
||||
1. Create a private fork of the repository (grey button towards the bottom of the page)
|
||||
1. Respond to the reporter by email, sharing a link to the draft security advisory
|
||||
|
||||
### 2. Triage
|
||||
Within the draft security advisory, discuss and determine the severity of the
|
||||
issue. If necessary, members of the `solana-labs/security-incident-response`
|
||||
group may add other github users to the advisory to assist.
|
||||
|
||||
If it is determined that this not a critical network issue then the advisory
|
||||
should be closed and if more follow-up is required a normal Solana public github
|
||||
issue should be created.
|
||||
|
||||
### 3. Prepare Fixes
|
||||
For the affected branches, typically all three (edge, beta and stable), prepare
|
||||
a fix for the issue and push them to the corresponding branch in the private
|
||||
repository associated with the draft security advisory.
|
||||
|
||||
There is no CI available in the private repository so you must build from source
|
||||
and manually verify fixes.
|
||||
|
||||
Code review from the reporter is ideal, as well as from multiple members of the
|
||||
core development team.
|
||||
|
||||
### 4. Notify Security Group Validators
|
||||
Once an ETA is available for the fix, a member of the
|
||||
`solana-labs/security-incident-response` group should notify the validators so
|
||||
they can prepare for an update using the "Solana Red Alert" notification system.
|
||||
|
||||
The teams are all over the world and it's critical to provide actionable
|
||||
information at the right time. Don't be the person that wakes everybody up at
|
||||
2am when a fix won't be available for hours.
|
||||
|
||||
### 5. Ship the patch
|
||||
Once the fix is accepted, a member of the
|
||||
`solana-labs/security-incident-response` group should prepare a single patch
|
||||
file for each affected branch. The commit title for the patch should only
|
||||
contain the advisory id, and not disclose any further details about the
|
||||
incident.
|
||||
|
||||
Copy the patches to https://release.solana.com/ under a subdirectory named after
|
||||
the advisory id (example:
|
||||
https://release.solana.com/GHSA-hx59-f5g4-jghh/v1.4.patch). Contact a member of
|
||||
the `solana-labs/admins` group if you require access to release.solana.com
|
||||
|
||||
Using the "Solana Red Alert" channel:
|
||||
1. Notify validators that there's an issue and a patch will be provided in X minutes
|
||||
2. If X minutes expires and there's no patch, notify of the delay and provide a
|
||||
new ETA
|
||||
3. Provide links to patches of https://release.solana.com/ for each affected branch
|
||||
|
||||
Validators can be expected to build the patch from source against the latest
|
||||
release for the affected branch.
|
||||
|
||||
Since the software version will not change after the patch is applied, request
|
||||
that each validator notify in the existing channel once they've updated. Manually
|
||||
monitor the roll out until a sufficient amount of stake has updated - typically
|
||||
at least 33.3% or 66.6% depending on the issue.
|
||||
|
||||
### 6. Public Disclosure and Release
|
||||
Once the fix has been deployed to the security group validators, the patches from the security
|
||||
advisory may be merged into the main source repository. A new official release
|
||||
for each affected branch should be shipped and all validators requested to
|
||||
upgrade as quickly as possible.
|
||||
|
||||
### 7. Security Advisory Bounty Accounting and Cleanup
|
||||
|
||||
If this issue is eligible for a bounty, prefix the title of the security
|
||||
advisory with one of the following, depending on the severity:
|
||||
* `[Bounty Category: Critical: Loss of Funds]`
|
||||
* `[Bounty Category: Critical: Loss of Availability]`
|
||||
* `[Bounty Category: Critical: DoS]`
|
||||
* `[Bounty Category: Critical: Other]`
|
||||
* `[Bounty Category: Non-critical]`
|
||||
* `[Bounty Category: RPC]`
|
||||
|
||||
Confirm with the reporter that they agree with the severity assessment, and
|
||||
discuss as required to reach a conclusion.
|
||||
|
||||
We currently do not use the Github workflow to publish security advisories.
|
||||
Once the issue and fix have been disclosed, and a bounty category is assessed if
|
||||
appropriate, the GitHub security advisory is no longer needed and can be closed.
|
||||
|
||||
Bounties are currently awarded once a quarter (TODO: link to this process, or
|
||||
inline the workflow)
|
@@ -1,11 +1,10 @@
|
||||
[package]
|
||||
name = "solana-account-decoder"
|
||||
version = "1.6.13"
|
||||
version = "1.5.7"
|
||||
description = "Solana account decoder"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
homepage = "https://solana.com/"
|
||||
documentation = "https://docs.rs/solana-account-decoder"
|
||||
license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
|
||||
@@ -16,14 +15,14 @@ bs58 = "0.3.1"
|
||||
bv = "0.11.1"
|
||||
Inflector = "0.11.4"
|
||||
lazy_static = "1.4.0"
|
||||
serde = "1.0.122"
|
||||
serde = "1.0.112"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.56"
|
||||
solana-config-program = { path = "../programs/config", version = "=1.6.13" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.13" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "=1.6.13" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.6.13" }
|
||||
spl-token-v2-0 = { package = "spl-token", version = "=3.1.1", features = ["no-entrypoint"] }
|
||||
solana-config-program = { path = "../programs/config", version = "1.5.7" }
|
||||
solana-sdk = { path = "../sdk", version = "1.5.7" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "1.5.7" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.5.7" }
|
||||
spl-token-v2-0 = { package = "spl-token", version = "=3.0.1", features = ["no-entrypoint"] }
|
||||
thiserror = "1.0"
|
||||
zstd = "0.5.1"
|
||||
|
||||
|
@@ -1,4 +1,3 @@
|
||||
#![allow(clippy::integer_arithmetic)]
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
#[macro_use]
|
||||
@@ -16,10 +15,7 @@ pub mod validator_info;
|
||||
|
||||
use {
|
||||
crate::parse_account_data::{parse_account_data, AccountAdditionalData, ParsedAccount},
|
||||
solana_sdk::{
|
||||
account::ReadableAccount, account::WritableAccount, clock::Epoch,
|
||||
fee_calculator::FeeCalculator, pubkey::Pubkey,
|
||||
},
|
||||
solana_sdk::{account::Account, clock::Epoch, fee_calculator::FeeCalculator, pubkey::Pubkey},
|
||||
std::{
|
||||
io::{Read, Write},
|
||||
str::FromStr,
|
||||
@@ -27,7 +23,6 @@ use {
|
||||
};
|
||||
|
||||
pub type StringAmount = String;
|
||||
pub type StringDecimals = String;
|
||||
|
||||
/// A duplicate representation of an Account for pretty JSON serialization
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
@@ -48,7 +43,7 @@ pub enum UiAccountData {
|
||||
Binary(String, UiAccountEncoding),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum UiAccountEncoding {
|
||||
Binary, // Legacy. Retained for RPC backwards compatibility
|
||||
@@ -60,61 +55,58 @@ pub enum UiAccountEncoding {
|
||||
}
|
||||
|
||||
impl UiAccount {
|
||||
pub fn encode<T: ReadableAccount>(
|
||||
pub fn encode(
|
||||
pubkey: &Pubkey,
|
||||
account: &T,
|
||||
account: Account,
|
||||
encoding: UiAccountEncoding,
|
||||
additional_data: Option<AccountAdditionalData>,
|
||||
data_slice_config: Option<UiDataSliceConfig>,
|
||||
) -> Self {
|
||||
let data = match encoding {
|
||||
UiAccountEncoding::Binary => UiAccountData::LegacyBinary(
|
||||
bs58::encode(slice_data(&account.data(), data_slice_config)).into_string(),
|
||||
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(),
|
||||
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)),
|
||||
base64::encode(slice_data(&account.data, data_slice_config)),
|
||||
encoding,
|
||||
),
|
||||
UiAccountEncoding::Base64Zstd => {
|
||||
let mut encoder = zstd::stream::write::Encoder::new(Vec::new(), 0).unwrap();
|
||||
match encoder
|
||||
.write_all(slice_data(&account.data(), data_slice_config))
|
||||
.write_all(slice_data(&account.data, data_slice_config))
|
||||
.and_then(|()| encoder.finish())
|
||||
{
|
||||
Ok(zstd_data) => UiAccountData::Binary(base64::encode(zstd_data), encoding),
|
||||
Err(_) => UiAccountData::Binary(
|
||||
base64::encode(slice_data(&account.data(), data_slice_config)),
|
||||
base64::encode(slice_data(&account.data, data_slice_config)),
|
||||
UiAccountEncoding::Base64,
|
||||
),
|
||||
}
|
||||
}
|
||||
UiAccountEncoding::JsonParsed => {
|
||||
if let Ok(parsed_data) =
|
||||
parse_account_data(pubkey, &account.owner(), &account.data(), additional_data)
|
||||
parse_account_data(pubkey, &account.owner, &account.data, additional_data)
|
||||
{
|
||||
UiAccountData::Json(parsed_data)
|
||||
} else {
|
||||
UiAccountData::Binary(
|
||||
base64::encode(&account.data()),
|
||||
UiAccountEncoding::Base64,
|
||||
)
|
||||
UiAccountData::Binary(base64::encode(&account.data), UiAccountEncoding::Base64)
|
||||
}
|
||||
}
|
||||
};
|
||||
UiAccount {
|
||||
lamports: account.lamports(),
|
||||
lamports: account.lamports,
|
||||
data,
|
||||
owner: account.owner().to_string(),
|
||||
executable: account.executable(),
|
||||
rent_epoch: account.rent_epoch(),
|
||||
owner: account.owner.to_string(),
|
||||
executable: account.executable,
|
||||
rent_epoch: account.rent_epoch,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decode<T: WritableAccount>(&self) -> Option<T> {
|
||||
pub fn decode(&self) -> Option<Account> {
|
||||
let data = match &self.data {
|
||||
UiAccountData::Json(_) => None,
|
||||
UiAccountData::LegacyBinary(blob) => bs58::decode(blob).into_vec().ok(),
|
||||
@@ -134,13 +126,13 @@ impl UiAccount {
|
||||
UiAccountEncoding::Binary | UiAccountEncoding::JsonParsed => None,
|
||||
},
|
||||
}?;
|
||||
Some(T::create(
|
||||
self.lamports,
|
||||
Some(Account {
|
||||
lamports: self.lamports,
|
||||
data,
|
||||
Pubkey::from_str(&self.owner).ok()?,
|
||||
self.executable,
|
||||
self.rent_epoch,
|
||||
))
|
||||
owner: Pubkey::from_str(&self.owner).ok()?,
|
||||
executable: self.executable,
|
||||
rent_epoch: self.rent_epoch,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +182,6 @@ fn slice_data(data: &[u8], data_slice_config: Option<UiDataSliceConfig>) -> &[u8
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use solana_sdk::account::{Account, AccountSharedData};
|
||||
|
||||
#[test]
|
||||
fn test_slice_data() {
|
||||
@@ -224,10 +215,10 @@ mod test {
|
||||
fn test_base64_zstd() {
|
||||
let encoded_account = UiAccount::encode(
|
||||
&Pubkey::default(),
|
||||
&AccountSharedData::from(Account {
|
||||
Account {
|
||||
data: vec![0; 1024],
|
||||
..Account::default()
|
||||
}),
|
||||
},
|
||||
UiAccountEncoding::Base64Zstd,
|
||||
None,
|
||||
None,
|
||||
@@ -237,9 +228,7 @@ mod test {
|
||||
UiAccountData::Binary(_, UiAccountEncoding::Base64Zstd)
|
||||
));
|
||||
|
||||
let decoded_account = encoded_account.decode::<Account>().unwrap();
|
||||
assert_eq!(decoded_account.data(), &vec![0; 1024]);
|
||||
let decoded_account = encoded_account.decode::<AccountSharedData>().unwrap();
|
||||
assert_eq!(decoded_account.data(), &vec![0; 1024]);
|
||||
let decoded_account = encoded_account.decode().unwrap();
|
||||
assert_eq!(decoded_account.data, vec![0; 1024]);
|
||||
}
|
||||
}
|
||||
|
@@ -91,7 +91,6 @@ mod test {
|
||||
use crate::validator_info::ValidatorInfo;
|
||||
use serde_json::json;
|
||||
use solana_config_program::create_config_account;
|
||||
use solana_sdk::account::ReadableAccount;
|
||||
|
||||
#[test]
|
||||
fn test_parse_config() {
|
||||
@@ -102,7 +101,7 @@ mod test {
|
||||
let stake_config_account = create_config_account(vec![], &stake_config, 10);
|
||||
assert_eq!(
|
||||
parse_config(
|
||||
&stake_config_account.data(),
|
||||
&stake_config_account.data,
|
||||
&solana_stake_program::config::id()
|
||||
)
|
||||
.unwrap(),
|
||||
@@ -125,7 +124,7 @@ mod test {
|
||||
10,
|
||||
);
|
||||
assert_eq!(
|
||||
parse_config(&validator_info_config_account.data(), &info_pubkey).unwrap(),
|
||||
parse_config(&validator_info_config_account.data, &info_pubkey).unwrap(),
|
||||
ConfigAccountType::ValidatorInfo(UiConfig {
|
||||
keys: vec![
|
||||
UiConfigKey {
|
||||
|
@@ -9,13 +9,7 @@ pub fn parse_nonce(data: &[u8]) -> Result<UiNonceState, ParseAccountError> {
|
||||
.map_err(|_| ParseAccountError::from(InstructionError::InvalidAccountData))?;
|
||||
let nonce_state = nonce_state.convert_to_current();
|
||||
match nonce_state {
|
||||
// This prevents parsing an allocated System-owned account with empty data of any non-zero
|
||||
// length as `uninitialized` nonce. An empty account of the wrong length can never be
|
||||
// initialized as a nonce account, and an empty account of the correct length may not be an
|
||||
// uninitialized nonce account, since it can be assigned to another program.
|
||||
State::Uninitialized => Err(ParseAccountError::from(
|
||||
InstructionError::InvalidAccountData,
|
||||
)),
|
||||
State::Uninitialized => Ok(UiNonceState::Uninitialized),
|
||||
State::Initialized(data) => Ok(UiNonceState::Initialized(UiNonceData {
|
||||
authority: data.authority.to_string(),
|
||||
blockhash: data.blockhash.to_string(),
|
||||
|
@@ -214,13 +214,13 @@ pub struct UiStakeHistoryEntry {
|
||||
mod test {
|
||||
use super::*;
|
||||
use solana_sdk::{
|
||||
account::create_account_for_test, fee_calculator::FeeCalculator, hash::Hash,
|
||||
account::create_account, fee_calculator::FeeCalculator, hash::Hash,
|
||||
sysvar::recent_blockhashes::IterItem,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_parse_sysvars() {
|
||||
let clock_sysvar = create_account_for_test(&Clock::default());
|
||||
let clock_sysvar = create_account(&Clock::default(), 1);
|
||||
assert_eq!(
|
||||
parse_sysvar(&clock_sysvar.data, &sysvar::clock::id()).unwrap(),
|
||||
SysvarAccountType::Clock(UiClock::default()),
|
||||
@@ -233,13 +233,13 @@ mod test {
|
||||
first_normal_epoch: 1,
|
||||
first_normal_slot: 12,
|
||||
};
|
||||
let epoch_schedule_sysvar = create_account_for_test(&epoch_schedule);
|
||||
let epoch_schedule_sysvar = create_account(&epoch_schedule, 1);
|
||||
assert_eq!(
|
||||
parse_sysvar(&epoch_schedule_sysvar.data, &sysvar::epoch_schedule::id()).unwrap(),
|
||||
SysvarAccountType::EpochSchedule(epoch_schedule),
|
||||
);
|
||||
|
||||
let fees_sysvar = create_account_for_test(&Fees::default());
|
||||
let fees_sysvar = create_account(&Fees::default(), 1);
|
||||
assert_eq!(
|
||||
parse_sysvar(&fees_sysvar.data, &sysvar::fees::id()).unwrap(),
|
||||
SysvarAccountType::Fees(UiFees::default()),
|
||||
@@ -252,7 +252,7 @@ mod test {
|
||||
let recent_blockhashes: RecentBlockhashes = vec![IterItem(0, &hash, &fee_calculator)]
|
||||
.into_iter()
|
||||
.collect();
|
||||
let recent_blockhashes_sysvar = create_account_for_test(&recent_blockhashes);
|
||||
let recent_blockhashes_sysvar = create_account(&recent_blockhashes, 1);
|
||||
assert_eq!(
|
||||
parse_sysvar(
|
||||
&recent_blockhashes_sysvar.data,
|
||||
@@ -270,13 +270,13 @@ mod test {
|
||||
exemption_threshold: 2.0,
|
||||
burn_percent: 5,
|
||||
};
|
||||
let rent_sysvar = create_account_for_test(&rent);
|
||||
let rent_sysvar = create_account(&rent, 1);
|
||||
assert_eq!(
|
||||
parse_sysvar(&rent_sysvar.data, &sysvar::rent::id()).unwrap(),
|
||||
SysvarAccountType::Rent(rent.into()),
|
||||
);
|
||||
|
||||
let rewards_sysvar = create_account_for_test(&Rewards::default());
|
||||
let rewards_sysvar = create_account(&Rewards::default(), 1);
|
||||
assert_eq!(
|
||||
parse_sysvar(&rewards_sysvar.data, &sysvar::rewards::id()).unwrap(),
|
||||
SysvarAccountType::Rewards(UiRewards::default()),
|
||||
@@ -284,7 +284,7 @@ mod test {
|
||||
|
||||
let mut slot_hashes = SlotHashes::default();
|
||||
slot_hashes.add(1, hash);
|
||||
let slot_hashes_sysvar = create_account_for_test(&slot_hashes);
|
||||
let slot_hashes_sysvar = create_account(&slot_hashes, 1);
|
||||
assert_eq!(
|
||||
parse_sysvar(&slot_hashes_sysvar.data, &sysvar::slot_hashes::id()).unwrap(),
|
||||
SysvarAccountType::SlotHashes(vec![UiSlotHashEntry {
|
||||
@@ -295,7 +295,7 @@ mod test {
|
||||
|
||||
let mut slot_history = SlotHistory::default();
|
||||
slot_history.add(42);
|
||||
let slot_history_sysvar = create_account_for_test(&slot_history);
|
||||
let slot_history_sysvar = create_account(&slot_history, 1);
|
||||
assert_eq!(
|
||||
parse_sysvar(&slot_history_sysvar.data, &sysvar::slot_history::id()).unwrap(),
|
||||
SysvarAccountType::SlotHistory(UiSlotHistory {
|
||||
@@ -311,7 +311,7 @@ mod test {
|
||||
deactivating: 3,
|
||||
};
|
||||
stake_history.add(1, stake_history_entry.clone());
|
||||
let stake_history_sysvar = create_account_for_test(&stake_history);
|
||||
let stake_history_sysvar = create_account(&stake_history, 1);
|
||||
assert_eq!(
|
||||
parse_sysvar(&stake_history_sysvar.data, &sysvar::stake_history::id()).unwrap(),
|
||||
SysvarAccountType::StakeHistory(vec![UiStakeHistoryEntry {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
parse_account_data::{ParsableAccount, ParseAccountError},
|
||||
StringAmount, StringDecimals,
|
||||
StringAmount,
|
||||
};
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use spl_token_v2_0::{
|
||||
@@ -14,23 +14,23 @@ use std::str::FromStr;
|
||||
// A helper function to convert spl_token_v2_0::id() as spl_sdk::pubkey::Pubkey to
|
||||
// solana_sdk::pubkey::Pubkey
|
||||
pub fn spl_token_id_v2_0() -> Pubkey {
|
||||
Pubkey::new_from_array(spl_token_v2_0::id().to_bytes())
|
||||
Pubkey::from_str(&spl_token_v2_0::id().to_string()).unwrap()
|
||||
}
|
||||
|
||||
// A helper function to convert spl_token_v2_0::native_mint::id() as spl_sdk::pubkey::Pubkey to
|
||||
// solana_sdk::pubkey::Pubkey
|
||||
pub fn spl_token_v2_0_native_mint() -> Pubkey {
|
||||
Pubkey::new_from_array(spl_token_v2_0::native_mint::id().to_bytes())
|
||||
Pubkey::from_str(&spl_token_v2_0::native_mint::id().to_string()).unwrap()
|
||||
}
|
||||
|
||||
// A helper function to convert a solana_sdk::pubkey::Pubkey to spl_sdk::pubkey::Pubkey
|
||||
pub fn spl_token_v2_0_pubkey(pubkey: &Pubkey) -> SplTokenPubkey {
|
||||
SplTokenPubkey::new_from_array(pubkey.to_bytes())
|
||||
SplTokenPubkey::from_str(&pubkey.to_string()).unwrap()
|
||||
}
|
||||
|
||||
// A helper function to convert a spl_sdk::pubkey::Pubkey to solana_sdk::pubkey::Pubkey
|
||||
pub fn pubkey_from_spl_token_v2_0(pubkey: &SplTokenPubkey) -> Pubkey {
|
||||
Pubkey::new_from_array(pubkey.to_bytes())
|
||||
Pubkey::from_str(&pubkey.to_string()).unwrap()
|
||||
}
|
||||
|
||||
pub fn parse_token(
|
||||
@@ -158,66 +158,46 @@ impl From<AccountState> for UiAccountState {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn real_number_string(amount: u64, decimals: u8) -> StringDecimals {
|
||||
let decimals = decimals as usize;
|
||||
if decimals > 0 {
|
||||
// Left-pad zeros to decimals + 1, so we at least have an integer zero
|
||||
let mut s = format!("{:01$}", amount, decimals + 1);
|
||||
// Add the decimal point (Sorry, "," locales!)
|
||||
s.insert(s.len() - decimals, '.');
|
||||
s
|
||||
} else {
|
||||
amount.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn real_number_string_trimmed(amount: u64, decimals: u8) -> StringDecimals {
|
||||
let mut s = real_number_string(amount, decimals);
|
||||
if decimals > 0 {
|
||||
let zeros_trimmed = s.trim_end_matches('0');
|
||||
s = zeros_trimmed.trim_end_matches('.').to_string();
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UiTokenAmount {
|
||||
pub ui_amount: Option<f64>,
|
||||
pub ui_amount: f64,
|
||||
pub decimals: u8,
|
||||
pub amount: StringAmount,
|
||||
pub ui_amount_string: StringDecimals,
|
||||
}
|
||||
|
||||
impl UiTokenAmount {
|
||||
pub fn real_number_string(&self) -> String {
|
||||
real_number_string(
|
||||
u64::from_str(&self.amount).unwrap_or_default(),
|
||||
self.decimals as u8,
|
||||
)
|
||||
let decimals = self.decimals as usize;
|
||||
if decimals > 0 {
|
||||
let amount = u64::from_str(&self.amount).unwrap_or(0);
|
||||
|
||||
// Left-pad zeros to decimals + 1, so we at least have an integer zero
|
||||
let mut s = format!("{:01$}", amount, decimals + 1);
|
||||
|
||||
// Add the decimal point (Sorry, "," locales!)
|
||||
s.insert(s.len() - decimals, '.');
|
||||
s
|
||||
} else {
|
||||
self.amount.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn real_number_string_trimmed(&self) -> String {
|
||||
if !self.ui_amount_string.is_empty() {
|
||||
self.ui_amount_string.clone()
|
||||
} else {
|
||||
real_number_string_trimmed(
|
||||
u64::from_str(&self.amount).unwrap_or_default(),
|
||||
self.decimals as u8,
|
||||
)
|
||||
}
|
||||
let s = self.real_number_string();
|
||||
let zeros_trimmed = s.trim_end_matches('0');
|
||||
let decimal_trimmed = zeros_trimmed.trim_end_matches('.');
|
||||
decimal_trimmed.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn token_amount_to_ui_amount(amount: u64, decimals: u8) -> UiTokenAmount {
|
||||
let amount_decimals = 10_usize
|
||||
.checked_pow(decimals as u32)
|
||||
.map(|dividend| amount as f64 / dividend as f64);
|
||||
// Use `amount_to_ui_amount()` once spl_token is bumped to a version that supports it: https://github.com/solana-labs/solana-program-library/pull/211
|
||||
let amount_decimals = amount as f64 / 10_usize.pow(decimals as u32) as f64;
|
||||
UiTokenAmount {
|
||||
ui_amount: amount_decimals,
|
||||
decimals,
|
||||
amount: amount.to_string(),
|
||||
ui_amount_string: real_number_string_trimmed(amount, decimals),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,10 +253,9 @@ mod test {
|
||||
mint: mint_pubkey.to_string(),
|
||||
owner: owner_pubkey.to_string(),
|
||||
token_amount: UiTokenAmount {
|
||||
ui_amount: Some(0.42),
|
||||
ui_amount: 0.42,
|
||||
decimals: 2,
|
||||
amount: "42".to_string(),
|
||||
ui_amount_string: "0.42".to_string()
|
||||
amount: "42".to_string()
|
||||
},
|
||||
delegate: None,
|
||||
state: UiAccountState::Initialized,
|
||||
@@ -357,87 +336,17 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_ui_token_amount_real_string() {
|
||||
assert_eq!(&real_number_string(1, 0), "1");
|
||||
assert_eq!(&real_number_string_trimmed(1, 0), "1");
|
||||
let token_amount = token_amount_to_ui_amount(1, 0);
|
||||
assert_eq!(
|
||||
token_amount.ui_amount_string,
|
||||
real_number_string_trimmed(1, 0)
|
||||
);
|
||||
assert_eq!(token_amount.ui_amount, Some(1.0));
|
||||
assert_eq!(&real_number_string(10, 0), "10");
|
||||
assert_eq!(&real_number_string_trimmed(10, 0), "10");
|
||||
let token_amount = token_amount_to_ui_amount(10, 0);
|
||||
assert_eq!(
|
||||
token_amount.ui_amount_string,
|
||||
real_number_string_trimmed(10, 0)
|
||||
);
|
||||
assert_eq!(token_amount.ui_amount, Some(10.0));
|
||||
assert_eq!(&real_number_string(1, 9), "0.000000001");
|
||||
assert_eq!(&real_number_string_trimmed(1, 9), "0.000000001");
|
||||
assert_eq!(&token_amount.real_number_string(), "1");
|
||||
assert_eq!(&token_amount.real_number_string_trimmed(), "1");
|
||||
let token_amount = token_amount_to_ui_amount(1, 9);
|
||||
assert_eq!(
|
||||
token_amount.ui_amount_string,
|
||||
real_number_string_trimmed(1, 9)
|
||||
);
|
||||
assert_eq!(token_amount.ui_amount, Some(0.000000001));
|
||||
assert_eq!(&real_number_string(1_000_000_000, 9), "1.000000000");
|
||||
assert_eq!(&real_number_string_trimmed(1_000_000_000, 9), "1");
|
||||
assert_eq!(&token_amount.real_number_string(), "0.000000001");
|
||||
assert_eq!(&token_amount.real_number_string_trimmed(), "0.000000001");
|
||||
let token_amount = token_amount_to_ui_amount(1_000_000_000, 9);
|
||||
assert_eq!(
|
||||
token_amount.ui_amount_string,
|
||||
real_number_string_trimmed(1_000_000_000, 9)
|
||||
);
|
||||
assert_eq!(token_amount.ui_amount, Some(1.0));
|
||||
assert_eq!(&real_number_string(1_234_567_890, 3), "1234567.890");
|
||||
assert_eq!(&real_number_string_trimmed(1_234_567_890, 3), "1234567.89");
|
||||
assert_eq!(&token_amount.real_number_string(), "1.000000000");
|
||||
assert_eq!(&token_amount.real_number_string_trimmed(), "1");
|
||||
let token_amount = token_amount_to_ui_amount(1_234_567_890, 3);
|
||||
assert_eq!(
|
||||
token_amount.ui_amount_string,
|
||||
real_number_string_trimmed(1_234_567_890, 3)
|
||||
);
|
||||
assert_eq!(token_amount.ui_amount, Some(1234567.89));
|
||||
assert_eq!(
|
||||
&real_number_string(1_234_567_890, 25),
|
||||
"0.0000000000000001234567890"
|
||||
);
|
||||
assert_eq!(
|
||||
&real_number_string_trimmed(1_234_567_890, 25),
|
||||
"0.000000000000000123456789"
|
||||
);
|
||||
let token_amount = token_amount_to_ui_amount(1_234_567_890, 20);
|
||||
assert_eq!(
|
||||
token_amount.ui_amount_string,
|
||||
real_number_string_trimmed(1_234_567_890, 20)
|
||||
);
|
||||
assert_eq!(token_amount.ui_amount, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ui_token_amount_real_string_zero() {
|
||||
assert_eq!(&real_number_string(0, 0), "0");
|
||||
assert_eq!(&real_number_string_trimmed(0, 0), "0");
|
||||
let token_amount = token_amount_to_ui_amount(0, 0);
|
||||
assert_eq!(
|
||||
token_amount.ui_amount_string,
|
||||
real_number_string_trimmed(0, 0)
|
||||
);
|
||||
assert_eq!(token_amount.ui_amount, Some(0.0));
|
||||
assert_eq!(&real_number_string(0, 9), "0.000000000");
|
||||
assert_eq!(&real_number_string_trimmed(0, 9), "0");
|
||||
let token_amount = token_amount_to_ui_amount(0, 9);
|
||||
assert_eq!(
|
||||
token_amount.ui_amount_string,
|
||||
real_number_string_trimmed(0, 9)
|
||||
);
|
||||
assert_eq!(token_amount.ui_amount, Some(0.0));
|
||||
assert_eq!(&real_number_string(0, 25), "0.0000000000000000000000000");
|
||||
assert_eq!(&real_number_string_trimmed(0, 25), "0");
|
||||
let token_amount = token_amount_to_ui_amount(0, 20);
|
||||
assert_eq!(
|
||||
token_amount.ui_amount_string,
|
||||
real_number_string_trimmed(0, 20)
|
||||
);
|
||||
assert_eq!(token_amount.ui_amount, None);
|
||||
assert_eq!(&token_amount.real_number_string(), "1234567.890");
|
||||
assert_eq!(&token_amount.real_number_string_trimmed(), "1234567.89");
|
||||
}
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-accounts-bench"
|
||||
version = "1.6.13"
|
||||
version = "1.5.7"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -10,12 +10,12 @@ publish = false
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.11"
|
||||
rayon = "1.5.0"
|
||||
solana-logger = { path = "../logger", version = "=1.6.13" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.6.13" }
|
||||
solana-measure = { path = "../measure", version = "=1.6.13" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.13" }
|
||||
solana-version = { path = "../version", version = "=1.6.13" }
|
||||
rayon = "1.4.0"
|
||||
solana-logger = { path = "../logger", version = "1.5.7" }
|
||||
solana-runtime = { path = "../runtime", version = "1.5.7" }
|
||||
solana-measure = { path = "../measure", version = "1.5.7" }
|
||||
solana-sdk = { path = "../sdk", version = "1.5.7" }
|
||||
solana-version = { path = "../version", version = "1.5.7" }
|
||||
rand = "0.7.0"
|
||||
clap = "2.33.1"
|
||||
crossbeam-channel = "0.4"
|
||||
|
@@ -1,4 +1,3 @@
|
||||
#![allow(clippy::integer_arithmetic)]
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
use clap::{crate_description, crate_name, value_t, App, Arg};
|
||||
@@ -6,10 +5,10 @@ use rayon::prelude::*;
|
||||
use solana_measure::measure::Measure;
|
||||
use solana_runtime::{
|
||||
accounts::{create_test_accounts, update_accounts_bench, Accounts},
|
||||
accounts_index::{AccountSecondaryIndexes, Ancestors},
|
||||
accounts_index::Ancestors,
|
||||
};
|
||||
use solana_sdk::{genesis_config::ClusterType, pubkey::Pubkey};
|
||||
use std::{env, fs, path::PathBuf};
|
||||
use std::{collections::HashSet, env, fs, path::PathBuf};
|
||||
|
||||
fn main() {
|
||||
solana_logger::setup();
|
||||
@@ -58,12 +57,8 @@ fn main() {
|
||||
if fs::remove_dir_all(path.clone()).is_err() {
|
||||
println!("Warning: Couldn't remove {:?}", path);
|
||||
}
|
||||
let accounts = Accounts::new_with_config(
|
||||
vec![path],
|
||||
&ClusterType::Testnet,
|
||||
AccountSecondaryIndexes::default(),
|
||||
false,
|
||||
);
|
||||
let accounts =
|
||||
Accounts::new_with_config(vec![path], &ClusterType::Testnet, HashSet::new(), false);
|
||||
println!("Creating {} accounts", num_accounts);
|
||||
let mut create_time = Measure::start("create accounts");
|
||||
let pubkeys: Vec<_> = (0..num_slots)
|
||||
@@ -107,15 +102,17 @@ fn main() {
|
||||
} else {
|
||||
let mut pubkeys: Vec<Pubkey> = vec![];
|
||||
let mut time = Measure::start("hash");
|
||||
let results = accounts.accounts_db.update_accounts_hash(0, &ancestors);
|
||||
let results = accounts
|
||||
.accounts_db
|
||||
.update_accounts_hash(0, &ancestors, true);
|
||||
time.stop();
|
||||
let mut time_store = Measure::start("hash using store");
|
||||
let results_store = accounts.accounts_db.update_accounts_hash_with_index_option(
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
solana_sdk::clock::Slot::default(),
|
||||
&ancestors,
|
||||
None,
|
||||
true,
|
||||
);
|
||||
time_store.stop();
|
||||
if results != results_store {
|
||||
|
1
accounts-cluster-bench/.gitignore
vendored
1
accounts-cluster-bench/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
/farf/
|
@@ -1,34 +0,0 @@
|
||||
[package]
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-accounts-cluster-bench"
|
||||
version = "1.6.13"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33.1"
|
||||
log = "0.4.11"
|
||||
rand = "0.7.0"
|
||||
rayon = "1.4.1"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.6.13" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.6.13" }
|
||||
solana-client = { path = "../client", version = "=1.6.13" }
|
||||
solana-core = { path = "../core", version = "=1.6.13" }
|
||||
solana-measure = { path = "../measure", version = "=1.6.13" }
|
||||
solana-logger = { path = "../logger", version = "=1.6.13" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.6.13" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.6.13" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.6.13" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.13" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.6.13" }
|
||||
solana-version = { path = "../version", version = "=1.6.13" }
|
||||
spl-token-v2-0 = { package = "spl-token", version = "=3.1.1", features = ["no-entrypoint"] }
|
||||
|
||||
[dev-dependencies]
|
||||
solana-local-cluster = { path = "../local-cluster", version = "=1.6.13" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
@@ -1,742 +0,0 @@
|
||||
#![allow(clippy::integer_arithmetic)]
|
||||
use clap::{crate_description, crate_name, value_t, values_t_or_exit, App, Arg};
|
||||
use log::*;
|
||||
use rand::{thread_rng, Rng};
|
||||
use rayon::prelude::*;
|
||||
use solana_account_decoder::parse_token::spl_token_v2_0_pubkey;
|
||||
use solana_clap_utils::input_parsers::pubkey_of;
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_core::gossip_service::discover;
|
||||
use solana_faucet::faucet::{request_airdrop_transaction, FAUCET_PORT};
|
||||
use solana_measure::measure::Measure;
|
||||
use solana_runtime::inline_spl_token_v2_0;
|
||||
use solana_sdk::{
|
||||
commitment_config::CommitmentConfig,
|
||||
message::Message,
|
||||
pubkey::Pubkey,
|
||||
rpc_port::DEFAULT_RPC_PORT,
|
||||
signature::{read_keypair_file, Keypair, Signature, Signer},
|
||||
system_instruction, system_program,
|
||||
timing::timestamp,
|
||||
transaction::Transaction,
|
||||
};
|
||||
use solana_transaction_status::parse_token::spl_token_v2_0_instruction;
|
||||
use std::{
|
||||
net::SocketAddr,
|
||||
process::exit,
|
||||
sync::{
|
||||
atomic::{AtomicBool, AtomicU64, Ordering},
|
||||
Arc, RwLock,
|
||||
},
|
||||
thread::{sleep, Builder, JoinHandle},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
// Create and close messages both require 2 signatures; if transaction construction changes, update
|
||||
// this magic number
|
||||
const NUM_SIGNATURES: u64 = 2;
|
||||
|
||||
pub fn airdrop_lamports(
|
||||
client: &RpcClient,
|
||||
faucet_addr: &SocketAddr,
|
||||
id: &Keypair,
|
||||
desired_balance: u64,
|
||||
) -> bool {
|
||||
let starting_balance = client.get_balance(&id.pubkey()).unwrap_or(0);
|
||||
info!("starting balance {}", starting_balance);
|
||||
|
||||
if starting_balance < desired_balance {
|
||||
let airdrop_amount = desired_balance - starting_balance;
|
||||
info!(
|
||||
"Airdropping {:?} lamports from {} for {}",
|
||||
airdrop_amount,
|
||||
faucet_addr,
|
||||
id.pubkey(),
|
||||
);
|
||||
|
||||
let (blockhash, _fee_calculator) = client.get_recent_blockhash().unwrap();
|
||||
match request_airdrop_transaction(&faucet_addr, &id.pubkey(), airdrop_amount, blockhash) {
|
||||
Ok(transaction) => {
|
||||
let mut tries = 0;
|
||||
loop {
|
||||
tries += 1;
|
||||
let result = client.send_and_confirm_transaction(&transaction);
|
||||
|
||||
if result.is_ok() {
|
||||
break;
|
||||
}
|
||||
if tries >= 5 {
|
||||
panic!(
|
||||
"Error requesting airdrop: to addr: {:?} amount: {} {:?}",
|
||||
faucet_addr, airdrop_amount, result
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
panic!(
|
||||
"Error requesting airdrop: {:?} to addr: {:?} amount: {}",
|
||||
err, faucet_addr, airdrop_amount
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let current_balance = client.get_balance(&id.pubkey()).unwrap_or_else(|e| {
|
||||
panic!("airdrop error {}", e);
|
||||
});
|
||||
info!("current balance {}...", current_balance);
|
||||
|
||||
if current_balance - starting_balance != airdrop_amount {
|
||||
info!(
|
||||
"Airdrop failed? {} {} {} {}",
|
||||
id.pubkey(),
|
||||
current_balance,
|
||||
starting_balance,
|
||||
airdrop_amount,
|
||||
);
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
// signature, timestamp, id
|
||||
type PendingQueue = Vec<(Signature, u64, u64)>;
|
||||
|
||||
struct TransactionExecutor {
|
||||
sig_clear_t: JoinHandle<()>,
|
||||
sigs: Arc<RwLock<PendingQueue>>,
|
||||
cleared: Arc<RwLock<Vec<u64>>>,
|
||||
exit: Arc<AtomicBool>,
|
||||
counter: AtomicU64,
|
||||
client: RpcClient,
|
||||
}
|
||||
|
||||
impl TransactionExecutor {
|
||||
fn new(entrypoint_addr: SocketAddr) -> Self {
|
||||
let sigs = Arc::new(RwLock::new(Vec::new()));
|
||||
let cleared = Arc::new(RwLock::new(Vec::new()));
|
||||
let exit = Arc::new(AtomicBool::new(false));
|
||||
let sig_clear_t = Self::start_sig_clear_thread(&exit, &sigs, &cleared, entrypoint_addr);
|
||||
let client =
|
||||
RpcClient::new_socket_with_commitment(entrypoint_addr, CommitmentConfig::confirmed());
|
||||
Self {
|
||||
sigs,
|
||||
cleared,
|
||||
sig_clear_t,
|
||||
exit,
|
||||
counter: AtomicU64::new(0),
|
||||
client,
|
||||
}
|
||||
}
|
||||
|
||||
fn num_outstanding(&self) -> usize {
|
||||
self.sigs.read().unwrap().len()
|
||||
}
|
||||
|
||||
fn push_transactions(&self, txs: Vec<Transaction>) -> Vec<u64> {
|
||||
let mut ids = vec![];
|
||||
let new_sigs = txs.into_iter().filter_map(|tx| {
|
||||
let id = self.counter.fetch_add(1, Ordering::Relaxed);
|
||||
ids.push(id);
|
||||
match self.client.send_transaction(&tx) {
|
||||
Ok(sig) => {
|
||||
return Some((sig, timestamp(), id));
|
||||
}
|
||||
Err(e) => {
|
||||
info!("error: {:#?}", e);
|
||||
}
|
||||
}
|
||||
None
|
||||
});
|
||||
let mut sigs_w = self.sigs.write().unwrap();
|
||||
sigs_w.extend(new_sigs);
|
||||
ids
|
||||
}
|
||||
|
||||
fn drain_cleared(&self) -> Vec<u64> {
|
||||
std::mem::take(&mut *self.cleared.write().unwrap())
|
||||
}
|
||||
|
||||
fn close(self) {
|
||||
self.exit.store(true, Ordering::Relaxed);
|
||||
self.sig_clear_t.join().unwrap();
|
||||
}
|
||||
|
||||
fn start_sig_clear_thread(
|
||||
exit: &Arc<AtomicBool>,
|
||||
sigs: &Arc<RwLock<PendingQueue>>,
|
||||
cleared: &Arc<RwLock<Vec<u64>>>,
|
||||
entrypoint_addr: SocketAddr,
|
||||
) -> JoinHandle<()> {
|
||||
let sigs = sigs.clone();
|
||||
let exit = exit.clone();
|
||||
let cleared = cleared.clone();
|
||||
Builder::new()
|
||||
.name("sig_clear".to_string())
|
||||
.spawn(move || {
|
||||
let client = RpcClient::new_socket_with_commitment(
|
||||
entrypoint_addr,
|
||||
CommitmentConfig::confirmed(),
|
||||
);
|
||||
let mut success = 0;
|
||||
let mut error_count = 0;
|
||||
let mut timed_out = 0;
|
||||
let mut last_log = Instant::now();
|
||||
while !exit.load(Ordering::Relaxed) {
|
||||
let sigs_len = sigs.read().unwrap().len();
|
||||
if sigs_len > 0 {
|
||||
let mut sigs_w = sigs.write().unwrap();
|
||||
let mut start = Measure::start("sig_status");
|
||||
let statuses: Vec<_> = sigs_w
|
||||
.chunks(200)
|
||||
.flat_map(|sig_chunk| {
|
||||
let only_sigs: Vec<_> = sig_chunk.iter().map(|s| s.0).collect();
|
||||
client
|
||||
.get_signature_statuses(&only_sigs)
|
||||
.expect("status fail")
|
||||
.value
|
||||
})
|
||||
.collect();
|
||||
let mut num_cleared = 0;
|
||||
let start_len = sigs_w.len();
|
||||
let now = timestamp();
|
||||
let mut new_ids = vec![];
|
||||
let mut i = 0;
|
||||
let mut j = 0;
|
||||
while i != sigs_w.len() {
|
||||
let mut retain = true;
|
||||
let sent_ts = sigs_w[i].1;
|
||||
if let Some(e) = &statuses[j] {
|
||||
debug!("error: {:?}", e);
|
||||
if e.status.is_ok() {
|
||||
success += 1;
|
||||
} else {
|
||||
error_count += 1;
|
||||
}
|
||||
num_cleared += 1;
|
||||
retain = false;
|
||||
} else if now - sent_ts > 30_000 {
|
||||
retain = false;
|
||||
timed_out += 1;
|
||||
}
|
||||
if !retain {
|
||||
new_ids.push(sigs_w.remove(i).2);
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
j += 1;
|
||||
}
|
||||
let final_sigs_len = sigs_w.len();
|
||||
drop(sigs_w);
|
||||
cleared.write().unwrap().extend(new_ids);
|
||||
start.stop();
|
||||
debug!(
|
||||
"sigs len: {:?} success: {} took: {}ms cleared: {}/{}",
|
||||
final_sigs_len,
|
||||
success,
|
||||
start.as_ms(),
|
||||
num_cleared,
|
||||
start_len,
|
||||
);
|
||||
if last_log.elapsed().as_millis() > 5000 {
|
||||
info!(
|
||||
"success: {} error: {} timed_out: {}",
|
||||
success, error_count, timed_out,
|
||||
);
|
||||
last_log = Instant::now();
|
||||
}
|
||||
}
|
||||
sleep(Duration::from_millis(200));
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
struct SeedTracker {
|
||||
max_created: Arc<AtomicU64>,
|
||||
max_closed: Arc<AtomicU64>,
|
||||
}
|
||||
|
||||
fn make_create_message(
|
||||
keypair: &Keypair,
|
||||
base_keypair: &Keypair,
|
||||
max_created_seed: Arc<AtomicU64>,
|
||||
num_instructions: usize,
|
||||
balance: u64,
|
||||
maybe_space: Option<u64>,
|
||||
mint: Option<Pubkey>,
|
||||
) -> Message {
|
||||
let space = maybe_space.unwrap_or_else(|| thread_rng().gen_range(0, 1000));
|
||||
|
||||
let instructions: Vec<_> = (0..num_instructions)
|
||||
.into_iter()
|
||||
.map(|_| {
|
||||
let program_id = if mint.is_some() {
|
||||
inline_spl_token_v2_0::id()
|
||||
} else {
|
||||
system_program::id()
|
||||
};
|
||||
let seed = max_created_seed.fetch_add(1, Ordering::Relaxed).to_string();
|
||||
let to_pubkey =
|
||||
Pubkey::create_with_seed(&base_keypair.pubkey(), &seed, &program_id).unwrap();
|
||||
let mut instructions = vec![system_instruction::create_account_with_seed(
|
||||
&keypair.pubkey(),
|
||||
&to_pubkey,
|
||||
&base_keypair.pubkey(),
|
||||
&seed,
|
||||
balance,
|
||||
space,
|
||||
&program_id,
|
||||
)];
|
||||
if let Some(mint_address) = mint {
|
||||
instructions.push(spl_token_v2_0_instruction(
|
||||
spl_token_v2_0::instruction::initialize_account(
|
||||
&spl_token_v2_0::id(),
|
||||
&spl_token_v2_0_pubkey(&to_pubkey),
|
||||
&spl_token_v2_0_pubkey(&mint_address),
|
||||
&spl_token_v2_0_pubkey(&base_keypair.pubkey()),
|
||||
)
|
||||
.unwrap(),
|
||||
));
|
||||
}
|
||||
|
||||
instructions
|
||||
})
|
||||
.collect();
|
||||
let instructions: Vec<_> = instructions.into_iter().flatten().collect();
|
||||
|
||||
Message::new(&instructions, Some(&keypair.pubkey()))
|
||||
}
|
||||
|
||||
fn make_close_message(
|
||||
keypair: &Keypair,
|
||||
base_keypair: &Keypair,
|
||||
max_closed_seed: Arc<AtomicU64>,
|
||||
num_instructions: usize,
|
||||
balance: u64,
|
||||
spl_token: bool,
|
||||
) -> Message {
|
||||
let instructions: Vec<_> = (0..num_instructions)
|
||||
.into_iter()
|
||||
.map(|_| {
|
||||
let program_id = if spl_token {
|
||||
inline_spl_token_v2_0::id()
|
||||
} else {
|
||||
system_program::id()
|
||||
};
|
||||
let seed = max_closed_seed.fetch_add(1, Ordering::Relaxed).to_string();
|
||||
let address =
|
||||
Pubkey::create_with_seed(&base_keypair.pubkey(), &seed, &program_id).unwrap();
|
||||
if spl_token {
|
||||
spl_token_v2_0_instruction(
|
||||
spl_token_v2_0::instruction::close_account(
|
||||
&spl_token_v2_0::id(),
|
||||
&spl_token_v2_0_pubkey(&address),
|
||||
&spl_token_v2_0_pubkey(&keypair.pubkey()),
|
||||
&spl_token_v2_0_pubkey(&base_keypair.pubkey()),
|
||||
&[],
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
} else {
|
||||
system_instruction::transfer_with_seed(
|
||||
&address,
|
||||
&base_keypair.pubkey(),
|
||||
seed,
|
||||
&program_id,
|
||||
&keypair.pubkey(),
|
||||
balance,
|
||||
)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Message::new(&instructions, Some(&keypair.pubkey()))
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn run_accounts_bench(
|
||||
entrypoint_addr: SocketAddr,
|
||||
faucet_addr: SocketAddr,
|
||||
payer_keypairs: &[&Keypair],
|
||||
iterations: usize,
|
||||
maybe_space: Option<u64>,
|
||||
batch_size: usize,
|
||||
close_nth: u64,
|
||||
maybe_lamports: Option<u64>,
|
||||
num_instructions: usize,
|
||||
mint: Option<Pubkey>,
|
||||
) {
|
||||
assert!(num_instructions > 0);
|
||||
let client =
|
||||
RpcClient::new_socket_with_commitment(entrypoint_addr, CommitmentConfig::confirmed());
|
||||
|
||||
info!("Targeting {}", entrypoint_addr);
|
||||
|
||||
let mut last_blockhash = Instant::now();
|
||||
let mut last_log = Instant::now();
|
||||
let mut count = 0;
|
||||
let mut recent_blockhash = client.get_recent_blockhash().expect("blockhash");
|
||||
let mut tx_sent_count = 0;
|
||||
let mut total_accounts_created = 0;
|
||||
let mut total_accounts_closed = 0;
|
||||
let mut balances: Vec<_> = payer_keypairs
|
||||
.iter()
|
||||
.map(|keypair| client.get_balance(&keypair.pubkey()).unwrap_or(0))
|
||||
.collect();
|
||||
let mut last_balance = Instant::now();
|
||||
|
||||
let default_max_lamports = 1000;
|
||||
let min_balance = maybe_lamports.unwrap_or_else(|| {
|
||||
let space = maybe_space.unwrap_or(default_max_lamports);
|
||||
client
|
||||
.get_minimum_balance_for_rent_exemption(space as usize)
|
||||
.expect("min balance")
|
||||
});
|
||||
|
||||
let base_keypair = Keypair::new();
|
||||
let seed_tracker = SeedTracker {
|
||||
max_created: Arc::new(AtomicU64::default()),
|
||||
max_closed: Arc::new(AtomicU64::default()),
|
||||
};
|
||||
|
||||
info!("Starting balance(s): {:?}", balances);
|
||||
|
||||
let executor = TransactionExecutor::new(entrypoint_addr);
|
||||
|
||||
loop {
|
||||
if last_blockhash.elapsed().as_millis() > 10_000 {
|
||||
recent_blockhash = client.get_recent_blockhash().expect("blockhash");
|
||||
last_blockhash = Instant::now();
|
||||
}
|
||||
|
||||
let fee = recent_blockhash
|
||||
.1
|
||||
.lamports_per_signature
|
||||
.saturating_mul(NUM_SIGNATURES);
|
||||
let lamports = min_balance + fee;
|
||||
|
||||
for (i, balance) in balances.iter_mut().enumerate() {
|
||||
if *balance < lamports || last_balance.elapsed().as_millis() > 2000 {
|
||||
if let Ok(b) = client.get_balance(&payer_keypairs[i].pubkey()) {
|
||||
*balance = b;
|
||||
}
|
||||
last_balance = Instant::now();
|
||||
if *balance < lamports * 2 {
|
||||
info!(
|
||||
"Balance {} is less than needed: {}, doing aidrop...",
|
||||
balance, lamports
|
||||
);
|
||||
if !airdrop_lamports(
|
||||
&client,
|
||||
&faucet_addr,
|
||||
&payer_keypairs[i],
|
||||
lamports * 100_000,
|
||||
) {
|
||||
warn!("failed airdrop, exiting");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let sigs_len = executor.num_outstanding();
|
||||
if sigs_len < batch_size {
|
||||
let num_to_create = batch_size - sigs_len;
|
||||
if num_to_create >= payer_keypairs.len() {
|
||||
info!("creating {} new", num_to_create);
|
||||
let chunk_size = num_to_create / payer_keypairs.len();
|
||||
if chunk_size > 0 {
|
||||
for (i, keypair) in payer_keypairs.iter().enumerate() {
|
||||
let txs: Vec<_> = (0..chunk_size)
|
||||
.into_par_iter()
|
||||
.map(|_| {
|
||||
let message = make_create_message(
|
||||
keypair,
|
||||
&base_keypair,
|
||||
seed_tracker.max_created.clone(),
|
||||
num_instructions,
|
||||
min_balance,
|
||||
maybe_space,
|
||||
mint,
|
||||
);
|
||||
let signers: Vec<&Keypair> = vec![keypair, &base_keypair];
|
||||
Transaction::new(&signers, message, recent_blockhash.0)
|
||||
})
|
||||
.collect();
|
||||
balances[i] = balances[i].saturating_sub(lamports * txs.len() as u64);
|
||||
info!("txs: {}", txs.len());
|
||||
let new_ids = executor.push_transactions(txs);
|
||||
info!("ids: {}", new_ids.len());
|
||||
tx_sent_count += new_ids.len();
|
||||
total_accounts_created += num_instructions * new_ids.len();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if close_nth > 0 {
|
||||
let expected_closed = total_accounts_created as u64 / close_nth;
|
||||
if expected_closed > total_accounts_closed {
|
||||
let txs: Vec<_> = (0..expected_closed - total_accounts_closed)
|
||||
.into_par_iter()
|
||||
.map(|_| {
|
||||
let message = make_close_message(
|
||||
&payer_keypairs[0],
|
||||
&base_keypair,
|
||||
seed_tracker.max_closed.clone(),
|
||||
1,
|
||||
min_balance,
|
||||
mint.is_some(),
|
||||
);
|
||||
let signers: Vec<&Keypair> = vec![&payer_keypairs[0], &base_keypair];
|
||||
Transaction::new(&signers, message, recent_blockhash.0)
|
||||
})
|
||||
.collect();
|
||||
balances[0] = balances[0].saturating_sub(fee * txs.len() as u64);
|
||||
info!("close txs: {}", txs.len());
|
||||
let new_ids = executor.push_transactions(txs);
|
||||
info!("close ids: {}", new_ids.len());
|
||||
tx_sent_count += new_ids.len();
|
||||
total_accounts_closed += new_ids.len() as u64;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let _ = executor.drain_cleared();
|
||||
}
|
||||
|
||||
count += 1;
|
||||
if last_log.elapsed().as_millis() > 3000 {
|
||||
info!(
|
||||
"total_accounts_created: {} total_accounts_closed: {} tx_sent_count: {} loop_count: {} balance(s): {:?}",
|
||||
total_accounts_created, total_accounts_closed, tx_sent_count, count, balances
|
||||
);
|
||||
last_log = Instant::now();
|
||||
}
|
||||
if iterations != 0 && count >= iterations {
|
||||
break;
|
||||
}
|
||||
if executor.num_outstanding() >= batch_size {
|
||||
sleep(Duration::from_millis(500));
|
||||
}
|
||||
}
|
||||
executor.close();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
solana_logger::setup_with_default("solana=info");
|
||||
let matches = App::new(crate_name!())
|
||||
.about(crate_description!())
|
||||
.version(solana_version::version!())
|
||||
.arg(
|
||||
Arg::with_name("entrypoint")
|
||||
.long("entrypoint")
|
||||
.takes_value(true)
|
||||
.value_name("HOST:PORT")
|
||||
.help("RPC entrypoint address. Usually <ip>:8899"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("faucet_addr")
|
||||
.long("faucet")
|
||||
.takes_value(true)
|
||||
.value_name("HOST:PORT")
|
||||
.help("Faucet entrypoint address. Usually <ip>:9900"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("space")
|
||||
.long("space")
|
||||
.takes_value(true)
|
||||
.value_name("BYTES")
|
||||
.help("Size of accounts to create"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("lamports")
|
||||
.long("lamports")
|
||||
.takes_value(true)
|
||||
.value_name("LAMPORTS")
|
||||
.help("How many lamports to fund each account"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("identity")
|
||||
.long("identity")
|
||||
.takes_value(true)
|
||||
.multiple(true)
|
||||
.value_name("FILE")
|
||||
.help("keypair file"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("batch_size")
|
||||
.long("batch-size")
|
||||
.takes_value(true)
|
||||
.value_name("BYTES")
|
||||
.help("Number of transactions to send per batch"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("close_nth")
|
||||
.long("close-frequency")
|
||||
.takes_value(true)
|
||||
.value_name("BYTES")
|
||||
.help(
|
||||
"Send close transactions after this many accounts created. \
|
||||
Note: a `close-frequency` value near or below `batch-size` \
|
||||
may result in transaction-simulation errors, as the close \
|
||||
transactions will be submitted before the corresponding \
|
||||
create transactions have been confirmed",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("num_instructions")
|
||||
.long("num-instructions")
|
||||
.takes_value(true)
|
||||
.value_name("NUM")
|
||||
.help("Number of accounts to create on each transaction"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("iterations")
|
||||
.long("iterations")
|
||||
.takes_value(true)
|
||||
.value_name("NUM")
|
||||
.help("Number of iterations to make"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("check_gossip")
|
||||
.long("check-gossip")
|
||||
.help("Just use entrypoint address directly"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("mint")
|
||||
.long("mint")
|
||||
.takes_value(true)
|
||||
.help("Mint address to initialize account"),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
let skip_gossip = !matches.is_present("check_gossip");
|
||||
|
||||
let port = if skip_gossip { DEFAULT_RPC_PORT } else { 8001 };
|
||||
let mut entrypoint_addr = SocketAddr::from(([127, 0, 0, 1], port));
|
||||
if let Some(addr) = matches.value_of("entrypoint") {
|
||||
entrypoint_addr = solana_net_utils::parse_host_port(addr).unwrap_or_else(|e| {
|
||||
eprintln!("failed to parse entrypoint address: {}", e);
|
||||
exit(1)
|
||||
});
|
||||
}
|
||||
let mut faucet_addr = SocketAddr::from(([127, 0, 0, 1], FAUCET_PORT));
|
||||
if let Some(addr) = matches.value_of("faucet_addr") {
|
||||
faucet_addr = solana_net_utils::parse_host_port(addr).unwrap_or_else(|e| {
|
||||
eprintln!("failed to parse entrypoint address: {}", e);
|
||||
exit(1)
|
||||
});
|
||||
}
|
||||
|
||||
let space = value_t!(matches, "space", u64).ok();
|
||||
let lamports = value_t!(matches, "lamports", u64).ok();
|
||||
let batch_size = value_t!(matches, "batch_size", usize).unwrap_or(4);
|
||||
let close_nth = value_t!(matches, "close_nth", u64).unwrap_or(0);
|
||||
let iterations = value_t!(matches, "iterations", usize).unwrap_or(10);
|
||||
let num_instructions = value_t!(matches, "num_instructions", usize).unwrap_or(1);
|
||||
if num_instructions == 0 || num_instructions > 500 {
|
||||
eprintln!("bad num_instructions: {}", num_instructions);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
let mint = pubkey_of(&matches, "mint");
|
||||
|
||||
let payer_keypairs: Vec<_> = values_t_or_exit!(matches, "identity", String)
|
||||
.iter()
|
||||
.map(|keypair_string| {
|
||||
read_keypair_file(keypair_string)
|
||||
.unwrap_or_else(|_| panic!("bad keypair {:?}", keypair_string))
|
||||
})
|
||||
.collect();
|
||||
let mut payer_keypair_refs: Vec<&Keypair> = vec![];
|
||||
for keypair in payer_keypairs.iter() {
|
||||
payer_keypair_refs.push(keypair);
|
||||
}
|
||||
|
||||
let rpc_addr = if !skip_gossip {
|
||||
info!("Finding cluster entry: {:?}", entrypoint_addr);
|
||||
let (gossip_nodes, _validators) = discover(
|
||||
None, // keypair
|
||||
Some(&entrypoint_addr),
|
||||
None, // num_nodes
|
||||
Duration::from_secs(60), // timeout
|
||||
None, // find_node_by_pubkey
|
||||
Some(&entrypoint_addr), // find_node_by_gossip_addr
|
||||
None, // my_gossip_addr
|
||||
0, // my_shred_version
|
||||
)
|
||||
.unwrap_or_else(|err| {
|
||||
eprintln!("Failed to discover {} node: {:?}", entrypoint_addr, err);
|
||||
exit(1);
|
||||
});
|
||||
|
||||
info!("done found {} nodes", gossip_nodes.len());
|
||||
gossip_nodes[0].rpc
|
||||
} else {
|
||||
info!("Using {:?} as the RPC address", entrypoint_addr);
|
||||
entrypoint_addr
|
||||
};
|
||||
|
||||
run_accounts_bench(
|
||||
rpc_addr,
|
||||
faucet_addr,
|
||||
&payer_keypair_refs,
|
||||
iterations,
|
||||
space,
|
||||
batch_size,
|
||||
close_nth,
|
||||
lamports,
|
||||
num_instructions,
|
||||
mint,
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use super::*;
|
||||
use solana_core::validator::ValidatorConfig;
|
||||
use solana_local_cluster::{
|
||||
local_cluster::{ClusterConfig, LocalCluster},
|
||||
validator_configs::make_identical_validator_configs,
|
||||
};
|
||||
use solana_sdk::poh_config::PohConfig;
|
||||
|
||||
#[test]
|
||||
fn test_accounts_cluster_bench() {
|
||||
solana_logger::setup();
|
||||
let validator_config = ValidatorConfig::default();
|
||||
let num_nodes = 1;
|
||||
let mut config = ClusterConfig {
|
||||
cluster_lamports: 10_000_000,
|
||||
poh_config: PohConfig::new_sleep(Duration::from_millis(50)),
|
||||
node_stakes: vec![100; num_nodes],
|
||||
validator_configs: make_identical_validator_configs(&validator_config, num_nodes),
|
||||
..ClusterConfig::default()
|
||||
};
|
||||
|
||||
let faucet_addr = SocketAddr::from(([127, 0, 0, 1], 9900));
|
||||
let cluster = LocalCluster::new(&mut config);
|
||||
let iterations = 10;
|
||||
let maybe_space = None;
|
||||
let batch_size = 100;
|
||||
let close_nth = 100;
|
||||
let maybe_lamports = None;
|
||||
let num_instructions = 2;
|
||||
let mut start = Measure::start("total accounts run");
|
||||
run_accounts_bench(
|
||||
cluster.entry_point_info.rpc,
|
||||
faucet_addr,
|
||||
&[&cluster.funding_keypair],
|
||||
iterations,
|
||||
maybe_space,
|
||||
batch_size,
|
||||
close_nth,
|
||||
maybe_lamports,
|
||||
num_instructions,
|
||||
None,
|
||||
);
|
||||
start.stop();
|
||||
info!("{}", start);
|
||||
}
|
||||
}
|
@@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-banking-bench"
|
||||
version = "1.6.13"
|
||||
version = "1.5.7"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -13,17 +13,17 @@ clap = "2.33.1"
|
||||
crossbeam-channel = "0.4"
|
||||
log = "0.4.11"
|
||||
rand = "0.7.0"
|
||||
rayon = "1.5.0"
|
||||
solana-core = { path = "../core", version = "=1.6.13" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.6.13" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.6.13" }
|
||||
solana-perf = { path = "../perf", version = "=1.6.13" }
|
||||
solana-ledger = { path = "../ledger", version = "=1.6.13" }
|
||||
solana-logger = { path = "../logger", version = "=1.6.13" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.6.13" }
|
||||
solana-measure = { path = "../measure", version = "=1.6.13" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.13" }
|
||||
solana-version = { path = "../version", version = "=1.6.13" }
|
||||
rayon = "1.4.0"
|
||||
solana-core = { path = "../core", version = "1.5.7" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.5.7" }
|
||||
solana-streamer = { path = "../streamer", version = "1.5.7" }
|
||||
solana-perf = { path = "../perf", version = "1.5.7" }
|
||||
solana-ledger = { path = "../ledger", version = "1.5.7" }
|
||||
solana-logger = { path = "../logger", version = "1.5.7" }
|
||||
solana-runtime = { path = "../runtime", version = "1.5.7" }
|
||||
solana-measure = { path = "../measure", version = "1.5.7" }
|
||||
solana-sdk = { path = "../sdk", version = "1.5.7" }
|
||||
solana-version = { path = "../version", version = "1.5.7" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -1,4 +1,3 @@
|
||||
#![allow(clippy::integer_arithmetic)]
|
||||
use clap::{crate_description, crate_name, value_t, App, Arg};
|
||||
use crossbeam_channel::unbounded;
|
||||
use log::*;
|
||||
@@ -19,7 +18,7 @@ use solana_ledger::{
|
||||
use solana_measure::measure::Measure;
|
||||
use solana_perf::packet::to_packets_chunked;
|
||||
use solana_runtime::{
|
||||
accounts_background_service::AbsRequestSender, bank::Bank, bank_forks::BankForks,
|
||||
accounts_background_service::ABSRequestSender, bank::Bank, bank_forks::BankForks,
|
||||
};
|
||||
use solana_sdk::{
|
||||
hash::Hash,
|
||||
@@ -326,7 +325,7 @@ fn main() {
|
||||
poh_recorder.lock().unwrap().set_bank(&bank);
|
||||
assert!(poh_recorder.lock().unwrap().bank().is_some());
|
||||
if bank.slot() > 32 {
|
||||
bank_forks.set_root(root, &AbsRequestSender::default(), None);
|
||||
bank_forks.set_root(root, &ABSRequestSender::default(), None);
|
||||
root += 1;
|
||||
}
|
||||
debug!(
|
||||
|
@@ -1,30 +1,26 @@
|
||||
[package]
|
||||
name = "solana-banks-client"
|
||||
version = "1.6.13"
|
||||
version = "1.5.7"
|
||||
description = "Solana banks client"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
documentation = "https://docs.rs/solana-banks-client"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
bincode = "1.3.1"
|
||||
borsh = "0.8.1"
|
||||
borsh-derive = "0.8.1"
|
||||
futures = "0.3"
|
||||
mio = "0.7.6"
|
||||
solana-banks-interface = { path = "../banks-interface", version = "=1.6.13" }
|
||||
solana-program = { path = "../sdk/program", version = "=1.6.13" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.13" }
|
||||
tarpc = { version = "0.24.1", features = ["full"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-serde = { version = "0.8", features = ["bincode"] }
|
||||
solana-banks-interface = { path = "../banks-interface", version = "1.5.7" }
|
||||
solana-sdk = { path = "../sdk", version = "1.5.7" }
|
||||
tarpc = { version = "0.23.0", features = ["full"] }
|
||||
tokio = { version = "0.3.5", features = ["full"] }
|
||||
tokio-serde = { version = "0.6", features = ["bincode"] }
|
||||
|
||||
[dev-dependencies]
|
||||
solana-runtime = { path = "../runtime", version = "=1.6.13" }
|
||||
solana-banks-server = { path = "../banks-server", version = "=1.6.13" }
|
||||
solana-runtime = { path = "../runtime", version = "1.5.7" }
|
||||
solana-banks-server = { path = "../banks-server", version = "1.5.7" }
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib"]
|
||||
|
@@ -5,18 +5,19 @@
|
||||
//! but they are undocumented, may change over time, and are generally more
|
||||
//! cumbersome to use.
|
||||
|
||||
use borsh::BorshDeserialize;
|
||||
use futures::{future::join_all, Future, FutureExt};
|
||||
pub use solana_banks_interface::{BanksClient as TarpcClient, TransactionStatus};
|
||||
use solana_banks_interface::{BanksRequest, BanksResponse};
|
||||
use solana_program::{
|
||||
clock::Slot, fee_calculator::FeeCalculator, hash::Hash, program_pack::Pack, pubkey::Pubkey,
|
||||
rent::Rent, sysvar,
|
||||
};
|
||||
use solana_sdk::{
|
||||
account::{from_account, Account},
|
||||
clock::Slot,
|
||||
commitment_config::CommitmentLevel,
|
||||
fee_calculator::FeeCalculator,
|
||||
hash::Hash,
|
||||
pubkey::Pubkey,
|
||||
rent::Rent,
|
||||
signature::Signature,
|
||||
sysvar,
|
||||
transaction::{self, Transaction},
|
||||
transport,
|
||||
};
|
||||
@@ -129,7 +130,7 @@ impl BanksClient {
|
||||
self.get_account(sysvar::rent::id()).map(|result| {
|
||||
let rent_sysvar = result?
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Rent sysvar not present"))?;
|
||||
from_account::<Rent, _>(&rent_sysvar).ok_or_else(|| {
|
||||
from_account::<Rent>(&rent_sysvar).ok_or_else(|| {
|
||||
io::Error::new(io::ErrorKind::Other, "Failed to deserialize Rent sysvar")
|
||||
})
|
||||
})
|
||||
@@ -217,33 +218,6 @@ impl BanksClient {
|
||||
self.get_account_with_commitment(address, CommitmentLevel::default())
|
||||
}
|
||||
|
||||
/// Return the unpacked account data at the given address
|
||||
/// If the account is not found, an error is returned
|
||||
pub fn get_packed_account_data<T: Pack>(
|
||||
&mut self,
|
||||
address: Pubkey,
|
||||
) -> impl Future<Output = io::Result<T>> + '_ {
|
||||
self.get_account(address).map(|result| {
|
||||
let account =
|
||||
result?.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Account not found"))?;
|
||||
T::unpack_from_slice(&account.data)
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::Other, "Failed to deserialize account"))
|
||||
})
|
||||
}
|
||||
|
||||
/// Return the unpacked account data at the given address
|
||||
/// If the account is not found, an error is returned
|
||||
pub fn get_account_data_with_borsh<T: BorshDeserialize>(
|
||||
&mut self,
|
||||
address: Pubkey,
|
||||
) -> impl Future<Output = io::Result<T>> + '_ {
|
||||
self.get_account(address).map(|result| {
|
||||
let account =
|
||||
result?.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "account not found"))?;
|
||||
T::try_from_slice(&account.data)
|
||||
})
|
||||
}
|
||||
|
||||
/// Return the balance in lamports of an account at the given address at the slot
|
||||
/// corresponding to the given commitment level.
|
||||
pub fn get_balance_with_commitment(
|
||||
|
@@ -1,22 +1,21 @@
|
||||
[package]
|
||||
name = "solana-banks-interface"
|
||||
version = "1.6.13"
|
||||
version = "1.5.7"
|
||||
description = "Solana banks RPC interface"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
documentation = "https://docs.rs/solana-banks-interface"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
mio = "0.7.6"
|
||||
serde = { version = "1.0.122", features = ["derive"] }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.13" }
|
||||
tarpc = { version = "0.24.1", features = ["full"] }
|
||||
serde = { version = "1.0.112", features = ["derive"] }
|
||||
solana-sdk = { path = "../sdk", version = "1.5.7" }
|
||||
tarpc = { version = "0.23.0", features = ["full"] }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio = { version = "0.3.5", features = ["full"] }
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib"]
|
||||
|
@@ -1,12 +1,11 @@
|
||||
[package]
|
||||
name = "solana-banks-server"
|
||||
version = "1.6.13"
|
||||
version = "1.5.7"
|
||||
description = "Solana banks server"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
documentation = "https://docs.rs/solana-banks-server"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
@@ -14,14 +13,13 @@ bincode = "1.3.1"
|
||||
futures = "0.3"
|
||||
log = "0.4.11"
|
||||
mio = "0.7.6"
|
||||
solana-banks-interface = { path = "../banks-interface", version = "=1.6.13" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.6.13" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.13" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.6.13" }
|
||||
tarpc = { version = "0.24.1", features = ["full"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-serde = { version = "0.8", features = ["bincode"] }
|
||||
tokio-stream = "0.1"
|
||||
solana-banks-interface = { path = "../banks-interface", version = "1.5.7" }
|
||||
solana-runtime = { path = "../runtime", version = "1.5.7" }
|
||||
solana-sdk = { path = "../sdk", version = "1.5.7" }
|
||||
solana-metrics = { path = "../metrics", version = "1.5.7" }
|
||||
tarpc = { version = "0.23.0", features = ["full"] }
|
||||
tokio = { version = "0.3", features = ["full"] }
|
||||
tokio-serde = { version = "0.6", features = ["bincode"] }
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib"]
|
||||
|
@@ -242,7 +242,7 @@ impl Banks for BanksServer {
|
||||
commitment: CommitmentLevel,
|
||||
) -> Option<Account> {
|
||||
let bank = self.bank(commitment);
|
||||
bank.get_account(&address).map(Account::from)
|
||||
bank.get_account(&address)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,3 @@
|
||||
#![allow(clippy::integer_arithmetic)]
|
||||
pub mod banks_server;
|
||||
pub mod rpc_banks_service;
|
||||
pub mod send_transaction_service;
|
||||
|
@@ -15,7 +15,6 @@ use tokio::{
|
||||
runtime::Runtime,
|
||||
time::{self, Duration},
|
||||
};
|
||||
use tokio_stream::wrappers::IntervalStream;
|
||||
|
||||
pub struct RpcBanksService {
|
||||
thread_hdl: JoinHandle<()>,
|
||||
@@ -36,7 +35,7 @@ async fn start_abortable_tcp_server(
|
||||
block_commitment_cache.clone(),
|
||||
)
|
||||
.fuse();
|
||||
let interval = IntervalStream::new(time::interval(Duration::from_millis(100))).fuse();
|
||||
let interval = time::interval(Duration::from_millis(100)).fuse();
|
||||
pin_mut!(server, interval);
|
||||
loop {
|
||||
select! {
|
||||
|
@@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-bench-exchange"
|
||||
version = "1.6.13"
|
||||
version = "1.5.7"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -15,24 +15,24 @@ log = "0.4.11"
|
||||
num-derive = "0.3"
|
||||
num-traits = "0.2"
|
||||
rand = "0.7.0"
|
||||
rayon = "1.5.0"
|
||||
rayon = "1.4.0"
|
||||
serde_json = "1.0.56"
|
||||
serde_yaml = "0.8.13"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.6.13" }
|
||||
solana-core = { path = "../core", version = "=1.6.13" }
|
||||
solana-genesis = { path = "../genesis", version = "=1.6.13" }
|
||||
solana-client = { path = "../client", version = "=1.6.13" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.6.13" }
|
||||
solana-exchange-program = { path = "../programs/exchange", version = "=1.6.13" }
|
||||
solana-logger = { path = "../logger", version = "=1.6.13" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.6.13" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.6.13" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.6.13" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.13" }
|
||||
solana-version = { path = "../version", version = "=1.6.13" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.5.7" }
|
||||
solana-core = { path = "../core", version = "1.5.7" }
|
||||
solana-genesis = { path = "../genesis", version = "1.5.7" }
|
||||
solana-client = { path = "../client", version = "1.5.7" }
|
||||
solana-faucet = { path = "../faucet", version = "1.5.7" }
|
||||
solana-exchange-program = { path = "../programs/exchange", version = "1.5.7" }
|
||||
solana-logger = { path = "../logger", version = "1.5.7" }
|
||||
solana-metrics = { path = "../metrics", version = "1.5.7" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.5.7" }
|
||||
solana-runtime = { path = "../runtime", version = "1.5.7" }
|
||||
solana-sdk = { path = "../sdk", version = "1.5.7" }
|
||||
solana-version = { path = "../version", version = "1.5.7" }
|
||||
|
||||
[dev-dependencies]
|
||||
solana-local-cluster = { path = "../local-cluster", version = "=1.6.13" }
|
||||
solana-local-cluster = { path = "../local-cluster", version = "1.5.7" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -1,5 +1,4 @@
|
||||
#![allow(clippy::useless_attribute)]
|
||||
#![allow(clippy::integer_arithmetic)]
|
||||
|
||||
use crate::order_book::*;
|
||||
use itertools::izip;
|
||||
|
@@ -1,4 +1,3 @@
|
||||
#![allow(clippy::integer_arithmetic)]
|
||||
pub mod bench;
|
||||
mod cli;
|
||||
pub mod order_book;
|
||||
|
@@ -1,23 +1,19 @@
|
||||
use log::*;
|
||||
use solana_bench_exchange::bench::{airdrop_lamports, do_bench_exchange, Config};
|
||||
use solana_core::{
|
||||
gossip_service::{discover_cluster, get_multi_client},
|
||||
validator::ValidatorConfig,
|
||||
};
|
||||
use solana_exchange_program::{
|
||||
exchange_processor::process_instruction, id, solana_exchange_program,
|
||||
};
|
||||
use solana_core::gossip_service::{discover_cluster, get_multi_client};
|
||||
use solana_core::validator::ValidatorConfig;
|
||||
use solana_exchange_program::exchange_processor::process_instruction;
|
||||
use solana_exchange_program::id;
|
||||
use solana_exchange_program::solana_exchange_program;
|
||||
use solana_faucet::faucet::run_local_faucet_with_port;
|
||||
use solana_local_cluster::{
|
||||
local_cluster::{ClusterConfig, LocalCluster},
|
||||
validator_configs::make_identical_validator_configs,
|
||||
};
|
||||
use solana_runtime::{bank::Bank, bank_client::BankClient};
|
||||
use solana_sdk::{
|
||||
genesis_config::create_genesis_config,
|
||||
signature::{Keypair, Signer},
|
||||
};
|
||||
use std::{process::exit, sync::mpsc::channel, time::Duration};
|
||||
use solana_local_cluster::local_cluster::{ClusterConfig, LocalCluster};
|
||||
use solana_runtime::bank::Bank;
|
||||
use solana_runtime::bank_client::BankClient;
|
||||
use solana_sdk::genesis_config::create_genesis_config;
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use std::process::exit;
|
||||
use std::sync::mpsc::channel;
|
||||
use std::time::Duration;
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
@@ -48,7 +44,7 @@ fn test_exchange_local_cluster() {
|
||||
let cluster = LocalCluster::new(&mut ClusterConfig {
|
||||
node_stakes: vec![100_000; NUM_NODES],
|
||||
cluster_lamports: 100_000_000_000_000,
|
||||
validator_configs: make_identical_validator_configs(&ValidatorConfig::default(), NUM_NODES),
|
||||
validator_configs: vec![ValidatorConfig::default(); NUM_NODES],
|
||||
native_instruction_processors: [solana_exchange_program!()].to_vec(),
|
||||
..ClusterConfig::default()
|
||||
});
|
||||
|
@@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-bench-streamer"
|
||||
version = "1.6.13"
|
||||
version = "1.5.7"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -10,11 +10,11 @@ publish = false
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33.1"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.6.13" }
|
||||
solana-streamer = { path = "../streamer", version = "=1.6.13" }
|
||||
solana-logger = { path = "../logger", version = "=1.6.13" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.6.13" }
|
||||
solana-version = { path = "../version", version = "=1.6.13" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.5.7" }
|
||||
solana-streamer = { path = "../streamer", version = "1.5.7" }
|
||||
solana-logger = { path = "../logger", version = "1.5.7" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.5.7" }
|
||||
solana-version = { path = "../version", version = "1.5.7" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -1,4 +1,3 @@
|
||||
#![allow(clippy::integer_arithmetic)]
|
||||
use clap::{crate_description, crate_name, App, Arg};
|
||||
use solana_streamer::packet::{Packet, Packets, PacketsRecycler, PACKET_DATA_SIZE};
|
||||
use solana_streamer::streamer::{receiver, PacketReceiver};
|
||||
@@ -75,7 +74,7 @@ fn main() -> Result<()> {
|
||||
|
||||
let mut read_channels = Vec::new();
|
||||
let mut read_threads = Vec::new();
|
||||
let recycler = PacketsRecycler::new_without_limit("bench-streamer-recycler-shrink-stats");
|
||||
let recycler = PacketsRecycler::default();
|
||||
for _ in 0..num_sockets {
|
||||
let read = solana_net_utils::bind_to(ip_addr, port, false).unwrap();
|
||||
read.set_read_timeout(Some(Duration::new(1, 0))).unwrap();
|
||||
@@ -91,7 +90,6 @@ fn main() -> Result<()> {
|
||||
s_reader,
|
||||
recycler.clone(),
|
||||
"bench-streamer-test",
|
||||
1,
|
||||
));
|
||||
}
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-bench-tps"
|
||||
version = "1.6.13"
|
||||
version = "1.5.7"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
@@ -12,25 +12,26 @@ publish = false
|
||||
bincode = "1.3.1"
|
||||
clap = "2.33.1"
|
||||
log = "0.4.11"
|
||||
rayon = "1.5.0"
|
||||
rayon = "1.4.0"
|
||||
serde_json = "1.0.56"
|
||||
serde_yaml = "0.8.13"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.6.13" }
|
||||
solana-core = { path = "../core", version = "=1.6.13" }
|
||||
solana-genesis = { path = "../genesis", version = "=1.6.13" }
|
||||
solana-client = { path = "../client", version = "=1.6.13" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.6.13" }
|
||||
solana-logger = { path = "../logger", version = "=1.6.13" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.6.13" }
|
||||
solana-measure = { path = "../measure", version = "=1.6.13" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.6.13" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.6.13" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.13" }
|
||||
solana-version = { path = "../version", version = "=1.6.13" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.5.7" }
|
||||
solana-core = { path = "../core", version = "1.5.7" }
|
||||
solana-genesis = { path = "../genesis", version = "1.5.7" }
|
||||
solana-client = { path = "../client", version = "1.5.7" }
|
||||
solana-faucet = { path = "../faucet", version = "1.5.7" }
|
||||
solana-logger = { path = "../logger", version = "1.5.7" }
|
||||
solana-metrics = { path = "../metrics", version = "1.5.7" }
|
||||
solana-measure = { path = "../measure", version = "1.5.7" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.5.7" }
|
||||
solana-runtime = { path = "../runtime", version = "1.5.7" }
|
||||
solana-sdk = { path = "../sdk", version = "1.5.7" }
|
||||
solana-version = { path = "../version", version = "1.5.7" }
|
||||
|
||||
[dev-dependencies]
|
||||
serial_test = "0.4.0"
|
||||
solana-local-cluster = { path = "../local-cluster", version = "=1.6.13" }
|
||||
serial_test_derive = "0.4.0"
|
||||
solana-local-cluster = { path = "../local-cluster", version = "1.5.7" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -8,7 +8,7 @@ use solana_measure::measure::Measure;
|
||||
use solana_metrics::{self, datapoint_info};
|
||||
use solana_sdk::{
|
||||
client::Client,
|
||||
clock::{DEFAULT_S_PER_SLOT, MAX_PROCESSING_AGE},
|
||||
clock::{DEFAULT_TICKS_PER_SECOND, DEFAULT_TICKS_PER_SLOT, MAX_PROCESSING_AGE},
|
||||
commitment_config::CommitmentConfig,
|
||||
fee_calculator::FeeCalculator,
|
||||
hash::Hash,
|
||||
@@ -32,7 +32,8 @@ use std::{
|
||||
};
|
||||
|
||||
// The point at which transactions become "too old", in seconds.
|
||||
const MAX_TX_QUEUE_AGE: u64 = (MAX_PROCESSING_AGE as f64 * DEFAULT_S_PER_SLOT) as u64;
|
||||
const MAX_TX_QUEUE_AGE: u64 =
|
||||
MAX_PROCESSING_AGE as u64 * DEFAULT_TICKS_PER_SLOT / DEFAULT_TICKS_PER_SECOND;
|
||||
|
||||
pub const MAX_SPENDS_PER_TX: u64 = 4;
|
||||
|
||||
|
@@ -1,3 +1,2 @@
|
||||
#![allow(clippy::integer_arithmetic)]
|
||||
pub mod bench;
|
||||
pub mod cli;
|
||||
|
@@ -1,4 +1,3 @@
|
||||
#![allow(clippy::integer_arithmetic)]
|
||||
use log::*;
|
||||
use solana_bench_tps::bench::{do_bench_tps, generate_and_fund_keypairs, generate_keypairs};
|
||||
use solana_bench_tps::cli;
|
||||
|
@@ -1,21 +1,14 @@
|
||||
#![allow(clippy::integer_arithmetic)]
|
||||
use serial_test::serial;
|
||||
use solana_bench_tps::{
|
||||
bench::{do_bench_tps, generate_and_fund_keypairs},
|
||||
cli::Config,
|
||||
};
|
||||
use serial_test_derive::serial;
|
||||
use solana_bench_tps::bench::{do_bench_tps, generate_and_fund_keypairs};
|
||||
use solana_bench_tps::cli::Config;
|
||||
use solana_client::thin_client::create_client;
|
||||
use solana_core::{cluster_info::VALIDATOR_PORT_RANGE, validator::ValidatorConfig};
|
||||
use solana_core::cluster_info::VALIDATOR_PORT_RANGE;
|
||||
use solana_core::validator::ValidatorConfig;
|
||||
use solana_faucet::faucet::run_local_faucet_with_port;
|
||||
use solana_local_cluster::{
|
||||
local_cluster::{ClusterConfig, LocalCluster},
|
||||
validator_configs::make_identical_validator_configs,
|
||||
};
|
||||
use solana_local_cluster::local_cluster::{ClusterConfig, LocalCluster};
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use std::{
|
||||
sync::{mpsc::channel, Arc},
|
||||
time::Duration,
|
||||
};
|
||||
use std::sync::{mpsc::channel, Arc};
|
||||
use std::time::Duration;
|
||||
|
||||
fn test_bench_tps_local_cluster(config: Config) {
|
||||
let native_instruction_processors = vec![];
|
||||
@@ -25,7 +18,7 @@ fn test_bench_tps_local_cluster(config: Config) {
|
||||
let cluster = LocalCluster::new(&mut ClusterConfig {
|
||||
node_stakes: vec![999_990; NUM_NODES],
|
||||
cluster_lamports: 200_000_000,
|
||||
validator_configs: make_identical_validator_configs(&ValidatorConfig::default(), NUM_NODES),
|
||||
validator_configs: vec![ValidatorConfig::default(); NUM_NODES],
|
||||
native_instruction_processors,
|
||||
..ClusterConfig::default()
|
||||
});
|
||||
|
9
cargo
9
cargo
@@ -3,22 +3,25 @@
|
||||
# shellcheck source=ci/rust-version.sh
|
||||
here=$(dirname "$0")
|
||||
|
||||
source "${here}"/ci/rust-version.sh all
|
||||
|
||||
toolchain=
|
||||
case "$1" in
|
||||
stable)
|
||||
source "${here}"/ci/rust-version.sh stable
|
||||
# shellcheck disable=SC2054 # rust_stable is sourced from rust-version.sh
|
||||
toolchain="$rust_stable"
|
||||
shift
|
||||
;;
|
||||
nightly)
|
||||
source "${here}"/ci/rust-version.sh nightly
|
||||
# shellcheck disable=SC2054 # rust_nightly is sourced from rust-version.sh
|
||||
toolchain="$rust_nightly"
|
||||
shift
|
||||
;;
|
||||
+*)
|
||||
toolchain="${1#+}"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
source "${here}"/ci/rust-version.sh stable
|
||||
# shellcheck disable=SC2054 # rust_stable is sourced from rust-version.sh
|
||||
toolchain="$rust_stable"
|
||||
;;
|
||||
|
@@ -105,18 +105,11 @@ if [[ -z "$CHANNEL" ]]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $CHANNEL = beta ]]; then
|
||||
CHANNEL_LATEST_TAG="$BETA_CHANNEL_LATEST_TAG"
|
||||
elif [[ $CHANNEL = stable ]]; then
|
||||
CHANNEL_LATEST_TAG="$STABLE_CHANNEL_LATEST_TAG"
|
||||
fi
|
||||
|
||||
echo EDGE_CHANNEL="$EDGE_CHANNEL"
|
||||
echo BETA_CHANNEL="$BETA_CHANNEL"
|
||||
echo BETA_CHANNEL_LATEST_TAG="$BETA_CHANNEL_LATEST_TAG"
|
||||
echo STABLE_CHANNEL="$STABLE_CHANNEL"
|
||||
echo STABLE_CHANNEL_LATEST_TAG="$STABLE_CHANNEL_LATEST_TAG"
|
||||
echo CHANNEL="$CHANNEL"
|
||||
echo CHANNEL_LATEST_TAG="$CHANNEL_LATEST_TAG"
|
||||
|
||||
exit 0
|
||||
|
@@ -1,43 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
here="$(dirname "$0")"
|
||||
src_root="$(readlink -f "${here}/..")"
|
||||
|
||||
cd "${src_root}"
|
||||
|
||||
cargo_audit_ignores=(
|
||||
# failure is officially deprecated/unmaintained
|
||||
#
|
||||
# Blocked on multiple upstream crates removing their `failure` dependency.
|
||||
--ignore RUSTSEC-2020-0036
|
||||
|
||||
# `net2` crate has been deprecated; use `socket2` instead
|
||||
#
|
||||
# Blocked on https://github.com/paritytech/jsonrpc/issues/575
|
||||
--ignore RUSTSEC-2020-0016
|
||||
|
||||
# stdweb is unmaintained
|
||||
#
|
||||
# Blocked on multiple upstream crates removing their `stdweb` dependency.
|
||||
--ignore RUSTSEC-2020-0056
|
||||
|
||||
# Potential segfault in the time crate
|
||||
#
|
||||
# Blocked on multiple crates updating `time` to >= 0.2.23
|
||||
--ignore RUSTSEC-2020-0071
|
||||
|
||||
# difference is unmaintained
|
||||
#
|
||||
# Blocked on predicates v1.0.6 removing its dependency on `difference`
|
||||
--ignore RUSTSEC-2020-0095
|
||||
|
||||
# generic-array: arr! macro erases lifetimes
|
||||
#
|
||||
# Blocked on libsecp256k1 releasing with upgraded dependencies
|
||||
# https://github.com/paritytech/libsecp256k1/issues/66
|
||||
--ignore RUSTSEC-2020-0146
|
||||
|
||||
)
|
||||
scripts/cargo-for-all-lock-files.sh stable audit "${cargo_audit_ignores[@]}"
|
@@ -1,4 +1,4 @@
|
||||
FROM solanalabs/rust:1.51.0
|
||||
FROM solanalabs/rust:1.49.0
|
||||
ARG date
|
||||
|
||||
RUN set -x \
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# Note: when the rust version is changed also modify
|
||||
# ci/rust-version.sh to pick up the new image tag
|
||||
FROM rust:1.51.0
|
||||
FROM rust:1.49.0
|
||||
|
||||
# Add Google Protocol Buffers for Libra's metrics library.
|
||||
ENV PROTOC_VERSION 3.8.0
|
||||
|
@@ -70,7 +70,7 @@ done
|
||||
|
||||
source ci/upload-ci-artifact.sh
|
||||
source scripts/configure-metrics.sh
|
||||
source multinode-demo/common.sh --prebuild
|
||||
source multinode-demo/common.sh
|
||||
|
||||
nodes=(
|
||||
"multinode-demo/bootstrap-validator.sh \
|
||||
@@ -78,8 +78,9 @@ nodes=(
|
||||
--init-complete-file init-complete-node0.log \
|
||||
--dynamic-port-range 8000-8050"
|
||||
"multinode-demo/validator.sh \
|
||||
--enable-rpc-exit \
|
||||
--no-restart \
|
||||
--dynamic-port-range 8050-8100
|
||||
--dynamic-port-range 8050-8100 \
|
||||
--init-complete-file init-complete-node1.log \
|
||||
--rpc-port 18899"
|
||||
)
|
||||
@@ -200,10 +201,17 @@ killNodes() {
|
||||
[[ ${#pids[@]} -gt 0 ]] || return
|
||||
|
||||
# Try to use the RPC exit API to cleanly exit the first two nodes
|
||||
# (dynamic nodes, -x, are just killed)
|
||||
# (dynamic nodes, -x, are just killed since their RPC port is not known)
|
||||
echo "--- RPC exit"
|
||||
$solana_validator --ledger "$SOLANA_CONFIG_DIR"/bootstrap-validator exit --force || true
|
||||
$solana_validator --ledger "$SOLANA_CONFIG_DIR"/validator exit --force || true
|
||||
for port in 8899 18899; do
|
||||
(
|
||||
set -x
|
||||
curl --retry 5 --retry-delay 2 --retry-connrefused \
|
||||
-X POST -H 'Content-Type: application/json' \
|
||||
-d '{"jsonrpc":"2.0","id":1, "method":"validatorExit"}' \
|
||||
http://localhost:$port
|
||||
)
|
||||
done
|
||||
|
||||
# Give the nodes a splash of time to cleanly exit before killing them
|
||||
sleep 2
|
||||
|
@@ -83,7 +83,7 @@ echo --- Creating release tarball
|
||||
export CHANNEL
|
||||
|
||||
source ci/rust-version.sh stable
|
||||
scripts/cargo-install-all.sh stable "${RELEASE_BASENAME}"
|
||||
scripts/cargo-install-all.sh +"$rust_stable" "${RELEASE_BASENAME}"
|
||||
|
||||
tar cvf "${TARBALL_BASENAME}"-$TARGET.tar "${RELEASE_BASENAME}"
|
||||
bzip2 "${TARBALL_BASENAME}"-$TARGET.tar
|
||||
|
@@ -22,11 +22,10 @@ done
|
||||
snapshot_slot=1
|
||||
|
||||
# wait a bit longer than snapshot_slot
|
||||
while [[ $($solana_cli --url http://localhost:8899 slot --commitment processed) -le $((snapshot_slot + 1)) ]]; do
|
||||
while [[ $($solana_cli --url http://localhost:8899 slot --commitment recent) -le $((snapshot_slot + 1)) ]]; do
|
||||
sleep 1
|
||||
done
|
||||
|
||||
$solana_validator --ledger config/ledger exit --force || true
|
||||
curl -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1, "method":"validatorExit"}' http://localhost:8899
|
||||
|
||||
wait $pid
|
||||
|
||||
|
@@ -18,13 +18,13 @@
|
||||
if [[ -n $RUST_STABLE_VERSION ]]; then
|
||||
stable_version="$RUST_STABLE_VERSION"
|
||||
else
|
||||
stable_version=1.51.0
|
||||
stable_version=1.49.0
|
||||
fi
|
||||
|
||||
if [[ -n $RUST_NIGHTLY_VERSION ]]; then
|
||||
nightly_version="$RUST_NIGHTLY_VERSION"
|
||||
else
|
||||
nightly_version=2021-04-18
|
||||
nightly_version=2021-01-23
|
||||
fi
|
||||
|
||||
|
||||
|
@@ -12,16 +12,6 @@ cargo="$(readlink -f "./cargo")"
|
||||
|
||||
scripts/increment-cargo-version.sh check
|
||||
|
||||
# Disallow uncommitted Cargo.lock changes
|
||||
(
|
||||
_ scripts/cargo-for-all-lock-files.sh tree
|
||||
set +e
|
||||
if ! _ git diff --exit-code; then
|
||||
echo -e "\nError: Uncommitted Cargo.lock changes" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
)
|
||||
|
||||
echo --- build environment
|
||||
(
|
||||
set -x
|
||||
@@ -45,7 +35,7 @@ export RUSTFLAGS="-D warnings -A incomplete_features"
|
||||
# Only force up-to-date lock files on edge
|
||||
if [[ $CI_BASE_BRANCH = "$EDGE_CHANNEL" ]]; then
|
||||
# Exclude --benches as it's not available in rust stable yet
|
||||
if _ scripts/cargo-for-all-lock-files.sh stable check --locked --tests --bins --examples; then
|
||||
if _ scripts/cargo-for-all-lock-files.sh +"$rust_stable" check --locked --tests --bins --examples; then
|
||||
true
|
||||
else
|
||||
check_status=$?
|
||||
@@ -56,29 +46,60 @@ if [[ $CI_BASE_BRANCH = "$EDGE_CHANNEL" ]]; then
|
||||
fi
|
||||
|
||||
# Ensure nightly and --benches
|
||||
_ scripts/cargo-for-all-lock-files.sh nightly check --locked --all-targets
|
||||
_ scripts/cargo-for-all-lock-files.sh +"$rust_nightly" check --locked --all-targets
|
||||
else
|
||||
echo "Note: cargo-for-all-lock-files.sh skipped because $CI_BASE_BRANCH != $EDGE_CHANNEL"
|
||||
fi
|
||||
|
||||
_ ci/order-crates-for-publishing.py
|
||||
_ "$cargo" 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" nightly clippy -Zunstable-options --workspace --all-targets -- --deny=warnings --deny=clippy::integer_arithmetic
|
||||
_ "$cargo" nightly clippy -Zunstable-options --workspace --all-targets -- --deny=warnings
|
||||
|
||||
_ "$cargo" stable fmt --all -- --check
|
||||
cargo_audit_ignores=(
|
||||
# failure is officially deprecated/unmaintained
|
||||
#
|
||||
# Blocked on multiple upstream crates removing their `failure` dependency.
|
||||
--ignore RUSTSEC-2020-0036
|
||||
|
||||
_ ci/do-audit.sh
|
||||
# `net2` crate has been deprecated; use `socket2` instead
|
||||
#
|
||||
# Blocked on https://github.com/paritytech/jsonrpc/issues/575
|
||||
--ignore RUSTSEC-2020-0016
|
||||
|
||||
# stdweb is unmaintained
|
||||
#
|
||||
# Blocked on multiple upstream crates removing their `stdweb` dependency.
|
||||
--ignore RUSTSEC-2020-0056
|
||||
|
||||
# Potential segfault in the time crate
|
||||
#
|
||||
# Blocked on multiple crates updating `time` to >= 0.2.23
|
||||
--ignore RUSTSEC-2020-0071
|
||||
|
||||
# difference is unmaintained
|
||||
#
|
||||
# Blocked on predicates v1.0.6 removing its dependency on `difference`
|
||||
--ignore RUSTSEC-2020-0095
|
||||
|
||||
# hyper is upgraded on master/v1.6 but not for v1.5
|
||||
--ignore RUSTSEC-2021-0020
|
||||
|
||||
)
|
||||
_ scripts/cargo-for-all-lock-files.sh +"$rust_stable" audit "${cargo_audit_ignores[@]}"
|
||||
|
||||
{
|
||||
cd programs/bpf
|
||||
_ "$cargo" stable audit
|
||||
for project in rust/*/ ; do
|
||||
echo "+++ do_bpf_checks $project"
|
||||
(
|
||||
cd "$project"
|
||||
_ "$cargo" nightly clippy -- --deny=warnings --allow=clippy::missing_safety_doc
|
||||
_ "$cargo" stable fmt -- --check
|
||||
_ "$cargo" nightly test
|
||||
_ "$cargo" nightly clippy -- --deny=warnings --allow=clippy::missing_safety_doc
|
||||
)
|
||||
done
|
||||
}
|
||||
|
@@ -25,29 +25,4 @@ echo
|
||||
_ ci/nits.sh
|
||||
_ ci/check-ssh-keys.sh
|
||||
|
||||
|
||||
# Ensure the current channel version is not equal ("greater") than
|
||||
# the version of the latest tag
|
||||
if [[ -z $CI_TAG ]]; then
|
||||
echo "--- channel version check"
|
||||
(
|
||||
eval "$(ci/channel-info.sh)"
|
||||
|
||||
if [[ -n $CHANNEL_LATEST_TAG ]]; then
|
||||
source scripts/read-cargo-variable.sh
|
||||
|
||||
version=$(readCargoVariable version "version/Cargo.toml")
|
||||
echo "version: v$version"
|
||||
echo "latest channel tag: $CHANNEL_LATEST_TAG"
|
||||
|
||||
if [[ $CHANNEL_LATEST_TAG = v$version ]]; then
|
||||
echo "Error: please run ./scripts/increment-cargo-version.sh"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "Skipped. CHANNEL_LATEST_TAG (CHANNEL=$CHANNEL) unset"
|
||||
fi
|
||||
)
|
||||
fi
|
||||
|
||||
echo --- ok
|
||||
|
@@ -25,6 +25,9 @@ source scripts/ulimit-n.sh
|
||||
test -d target/debug/bpf && find target/debug/bpf -name '*.d' -delete
|
||||
test -d target/release/bpf && find target/release/bpf -name '*.d' -delete
|
||||
|
||||
# Clear the BPF sysroot files, they are not automatically rebuilt
|
||||
rm -rf target/xargo # Issue #3105
|
||||
|
||||
# Limit compiler jobs to reduce memory usage
|
||||
# on machines with 2gb/thread of memory
|
||||
NPROC=$(nproc)
|
||||
@@ -43,11 +46,7 @@ test-stable-perf)
|
||||
# BPF solana-sdk legacy compile test
|
||||
./cargo-build-bpf --manifest-path sdk/Cargo.toml
|
||||
|
||||
# BPF Program unit tests
|
||||
"$cargo" stable test --manifest-path programs/bpf/Cargo.toml
|
||||
cargo-build-bpf --manifest-path programs/bpf/Cargo.toml --bpf-sdk sdk/bpf
|
||||
|
||||
# BPF program system tests
|
||||
# BPF program tests
|
||||
_ make -C programs/bpf/c tests
|
||||
_ "$cargo" stable test \
|
||||
--manifest-path programs/bpf/Cargo.toml \
|
||||
|
@@ -1,28 +1,23 @@
|
||||
[package]
|
||||
name = "solana-clap-utils"
|
||||
version = "1.6.13"
|
||||
version = "1.5.7"
|
||||
description = "Solana utilities for the clap"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
documentation = "https://docs.rs/solana-clap-utils"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33.0"
|
||||
rpassword = "4.0"
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "=1.6.13" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.13" }
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "1.5.7" }
|
||||
solana-sdk = { path = "../sdk", version = "1.5.7" }
|
||||
thiserror = "1.0.21"
|
||||
tiny-bip39 = "0.8.0"
|
||||
uriparse = "0.6.3"
|
||||
tiny-bip39 = "0.7.0"
|
||||
url = "2.1.0"
|
||||
chrono = "0.4"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.1.0"
|
||||
|
||||
[lib]
|
||||
name = "solana_clap_utils"
|
||||
|
||||
|
@@ -1,7 +1,5 @@
|
||||
use {
|
||||
crate::{input_validators, ArgConstant},
|
||||
clap::Arg,
|
||||
};
|
||||
use crate::{input_validators, ArgConstant};
|
||||
use clap::Arg;
|
||||
|
||||
pub const FEE_PAYER_ARG: ArgConstant<'static> = ArgConstant {
|
||||
name: "fee_payer",
|
||||
|
@@ -1,21 +1,19 @@
|
||||
use {
|
||||
crate::keypair::{
|
||||
keypair_from_seed_phrase, pubkey_from_path, resolve_signer_from_path, signer_from_path,
|
||||
ASK_KEYWORD, SKIP_SEED_PHRASE_VALIDATION_ARG,
|
||||
},
|
||||
chrono::DateTime,
|
||||
clap::ArgMatches,
|
||||
solana_remote_wallet::remote_wallet::RemoteWalletManager,
|
||||
solana_sdk::{
|
||||
clock::UnixTimestamp,
|
||||
commitment_config::CommitmentConfig,
|
||||
genesis_config::ClusterType,
|
||||
native_token::sol_to_lamports,
|
||||
pubkey::Pubkey,
|
||||
signature::{read_keypair_file, Keypair, Signature, Signer},
|
||||
},
|
||||
std::{str::FromStr, sync::Arc},
|
||||
use crate::keypair::{
|
||||
keypair_from_seed_phrase, pubkey_from_path, resolve_signer_from_path, signer_from_path,
|
||||
ASK_KEYWORD, SKIP_SEED_PHRASE_VALIDATION_ARG,
|
||||
};
|
||||
use chrono::DateTime;
|
||||
use clap::ArgMatches;
|
||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||
use solana_sdk::{
|
||||
clock::UnixTimestamp,
|
||||
commitment_config::CommitmentConfig,
|
||||
genesis_config::ClusterType,
|
||||
native_token::sol_to_lamports,
|
||||
pubkey::Pubkey,
|
||||
signature::{read_keypair_file, Keypair, Signature, Signer},
|
||||
};
|
||||
use std::{str::FromStr, sync::Arc};
|
||||
|
||||
// Return parsed values from matches at `name`
|
||||
pub fn values_of<T>(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<T>>
|
||||
@@ -57,7 +55,7 @@ pub fn keypair_of(matches: &ArgMatches<'_>, name: &str) -> Option<Keypair> {
|
||||
if let Some(value) = matches.value_of(name) {
|
||||
if value == ASK_KEYWORD {
|
||||
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
||||
keypair_from_seed_phrase(name, skip_validation, true, None, true).ok()
|
||||
keypair_from_seed_phrase(name, skip_validation, true).ok()
|
||||
} else {
|
||||
read_keypair_file(value).ok()
|
||||
}
|
||||
@@ -72,7 +70,7 @@ pub fn keypairs_of(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<Keypair>>
|
||||
.filter_map(|value| {
|
||||
if value == ASK_KEYWORD {
|
||||
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
||||
keypair_from_seed_phrase(name, skip_validation, true, None, true).ok()
|
||||
keypair_from_seed_phrase(name, skip_validation, true).ok()
|
||||
} else {
|
||||
read_keypair_file(value).ok()
|
||||
}
|
||||
|
@@ -1,15 +1,13 @@
|
||||
use {
|
||||
crate::keypair::{parse_signer_source, SignerSourceKind, ASK_KEYWORD},
|
||||
chrono::DateTime,
|
||||
solana_sdk::{
|
||||
clock::{Epoch, Slot},
|
||||
hash::Hash,
|
||||
pubkey::{Pubkey, MAX_SEED_LEN},
|
||||
signature::{read_keypair_file, Signature},
|
||||
},
|
||||
std::fmt::Display,
|
||||
std::str::FromStr,
|
||||
use crate::keypair::{parse_keypair_path, KeypairUrl, ASK_KEYWORD};
|
||||
use chrono::DateTime;
|
||||
use solana_sdk::{
|
||||
clock::{Epoch, Slot},
|
||||
hash::Hash,
|
||||
pubkey::Pubkey,
|
||||
signature::{read_keypair_file, Signature},
|
||||
};
|
||||
use std::fmt::Display;
|
||||
use std::str::FromStr;
|
||||
|
||||
fn is_parsable_generic<U, T>(string: T) -> Result<(), String>
|
||||
where
|
||||
@@ -34,29 +32,6 @@ where
|
||||
is_parsable_generic::<T, String>(string)
|
||||
}
|
||||
|
||||
// Return an error if string cannot be parsed as numeric type T, and value not within specified
|
||||
// range
|
||||
pub fn is_within_range<T>(string: String, range_min: T, range_max: T) -> Result<(), String>
|
||||
where
|
||||
T: FromStr + Copy + std::fmt::Debug + PartialOrd + std::ops::Add<Output = T> + From<usize>,
|
||||
T::Err: Display,
|
||||
{
|
||||
match string.parse::<T>() {
|
||||
Ok(input) => {
|
||||
let range = range_min..range_max + 1.into();
|
||||
if !range.contains(&input) {
|
||||
Err(format!(
|
||||
"input '{:?}' out of range ({:?}..{:?}]",
|
||||
input, range_min, range_max
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Err(err) => Err(format!("error parsing '{}': {}", string, err)),
|
||||
}
|
||||
}
|
||||
|
||||
// Return an error if a pubkey cannot be parsed.
|
||||
pub fn is_pubkey<T>(string: T) -> Result<(), String>
|
||||
where
|
||||
@@ -96,26 +71,6 @@ where
|
||||
.map_err(|err| format!("{}", err))
|
||||
}
|
||||
|
||||
// Return an error if a `SignerSourceKind::Prompt` cannot be parsed
|
||||
pub fn is_prompt_signer_source<T>(string: T) -> Result<(), String>
|
||||
where
|
||||
T: AsRef<str> + Display,
|
||||
{
|
||||
if string.as_ref() == ASK_KEYWORD {
|
||||
return Ok(());
|
||||
}
|
||||
match parse_signer_source(string.as_ref())
|
||||
.map_err(|err| format!("{}", err))?
|
||||
.kind
|
||||
{
|
||||
SignerSourceKind::Prompt => Ok(()),
|
||||
_ => Err(format!(
|
||||
"Unable to parse input as `prompt:` URI scheme or `ASK` keyword: {}",
|
||||
string
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
// Return an error if string cannot be parsed as pubkey string or keypair file location
|
||||
pub fn is_pubkey_or_keypair<T>(string: T) -> Result<(), String>
|
||||
where
|
||||
@@ -130,11 +85,8 @@ pub fn is_valid_pubkey<T>(string: T) -> Result<(), String>
|
||||
where
|
||||
T: AsRef<str> + Display,
|
||||
{
|
||||
match parse_signer_source(string.as_ref())
|
||||
.map_err(|err| format!("{}", err))?
|
||||
.kind
|
||||
{
|
||||
SignerSourceKind::Filepath(path) => is_keypair(path),
|
||||
match parse_keypair_path(string.as_ref()) {
|
||||
KeypairUrl::Filepath(path) => is_keypair(path),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
@@ -215,8 +167,8 @@ where
|
||||
pub fn normalize_to_url_if_moniker<T: AsRef<str>>(url_or_moniker: T) -> String {
|
||||
match url_or_moniker.as_ref() {
|
||||
"m" | "mainnet-beta" => "https://api.mainnet-beta.solana.com",
|
||||
"t" | "testnet" => "https://api.testnet.solana.com",
|
||||
"d" | "devnet" => "https://api.devnet.solana.com",
|
||||
"t" | "testnet" => "https://testnet.solana.com",
|
||||
"d" | "devnet" => "https://devnet.solana.com",
|
||||
"l" | "localhost" => "http://localhost:8899",
|
||||
url => url,
|
||||
}
|
||||
@@ -339,21 +291,6 @@ where
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
pub fn is_derived_address_seed<T>(value: T) -> Result<(), String>
|
||||
where
|
||||
T: AsRef<str> + Display,
|
||||
{
|
||||
let value = value.as_ref();
|
||||
if value.len() > MAX_SEED_LEN {
|
||||
Err(format!(
|
||||
"Address seed must not be longer than {} bytes",
|
||||
MAX_SEED_LEN
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@@ -1,44 +1,33 @@
|
||||
use {
|
||||
crate::{
|
||||
input_parsers::pubkeys_sigs_of,
|
||||
offline::{SIGNER_ARG, SIGN_ONLY_ARG},
|
||||
ArgConstant,
|
||||
use crate::{
|
||||
input_parsers::pubkeys_sigs_of,
|
||||
offline::{SIGNER_ARG, SIGN_ONLY_ARG},
|
||||
ArgConstant,
|
||||
};
|
||||
use bip39::{Language, Mnemonic, Seed};
|
||||
use clap::ArgMatches;
|
||||
use rpassword::prompt_password_stderr;
|
||||
use solana_remote_wallet::{
|
||||
remote_keypair::generate_remote_keypair,
|
||||
remote_wallet::{maybe_wallet_manager, RemoteWalletError, RemoteWalletManager},
|
||||
};
|
||||
use solana_sdk::{
|
||||
hash::Hash,
|
||||
pubkey::Pubkey,
|
||||
signature::{
|
||||
keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair,
|
||||
read_keypair_file, Keypair, NullSigner, Presigner, Signature, Signer,
|
||||
},
|
||||
bip39::{Language, Mnemonic, Seed},
|
||||
clap::ArgMatches,
|
||||
rpassword::prompt_password_stderr,
|
||||
solana_remote_wallet::{
|
||||
locator::{Locator as RemoteWalletLocator, LocatorError as RemoteWalletLocatorError},
|
||||
remote_keypair::generate_remote_keypair,
|
||||
remote_wallet::{maybe_wallet_manager, RemoteWalletError, RemoteWalletManager},
|
||||
},
|
||||
solana_sdk::{
|
||||
derivation_path::{DerivationPath, DerivationPathError},
|
||||
hash::Hash,
|
||||
message::Message,
|
||||
pubkey::Pubkey,
|
||||
signature::{
|
||||
generate_seed_from_seed_phrase_and_passphrase, keypair_from_seed,
|
||||
keypair_from_seed_and_derivation_path, keypair_from_seed_phrase_and_passphrase,
|
||||
read_keypair, read_keypair_file, Keypair, NullSigner, Presigner, Signature, Signer,
|
||||
},
|
||||
},
|
||||
std::{
|
||||
cell::RefCell,
|
||||
convert::TryFrom,
|
||||
error,
|
||||
io::{stdin, stdout, Write},
|
||||
ops::Deref,
|
||||
process::exit,
|
||||
str::FromStr,
|
||||
sync::Arc,
|
||||
},
|
||||
thiserror::Error,
|
||||
};
|
||||
use std::{
|
||||
error,
|
||||
io::{stdin, stdout, Write},
|
||||
process::exit,
|
||||
str::FromStr,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
pub struct SignOnly {
|
||||
pub blockhash: Hash,
|
||||
pub message: Option<String>,
|
||||
pub present_signers: Vec<(Pubkey, Signature)>,
|
||||
pub absent_signers: Vec<Pubkey>,
|
||||
pub bad_signers: Vec<Pubkey>,
|
||||
@@ -78,62 +67,14 @@ impl CliSignerInfo {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn signers_for_message(&self, message: &Message) -> Vec<&dyn Signer> {
|
||||
self.signers
|
||||
.iter()
|
||||
.filter_map(|k| {
|
||||
if message.signer_keys().contains(&&k.pubkey()) {
|
||||
Some(k.as_ref())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct DefaultSigner {
|
||||
pub arg_name: String,
|
||||
pub path: String,
|
||||
is_path_checked: RefCell<bool>,
|
||||
}
|
||||
|
||||
impl DefaultSigner {
|
||||
pub fn new<AN: AsRef<str>, P: AsRef<str>>(arg_name: AN, path: P) -> Self {
|
||||
let arg_name = arg_name.as_ref().to_string();
|
||||
let path = path.as_ref().to_string();
|
||||
Self {
|
||||
arg_name,
|
||||
path,
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn path(&self) -> Result<&str, Box<dyn std::error::Error>> {
|
||||
if !self.is_path_checked.borrow().deref() {
|
||||
parse_signer_source(&self.path)
|
||||
.and_then(|s| {
|
||||
if let SignerSourceKind::Filepath(path) = &s.kind {
|
||||
std::fs::metadata(path).map(|_| ()).map_err(|e| e.into())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
.map_err(|_| {
|
||||
std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!(
|
||||
"No default signer found, run \"solana-keygen new -o {}\" to create a new one",
|
||||
self.path
|
||||
),
|
||||
)
|
||||
})?;
|
||||
*self.is_path_checked.borrow_mut() = true;
|
||||
}
|
||||
Ok(&self.path)
|
||||
}
|
||||
|
||||
pub fn generate_unique_signers(
|
||||
&self,
|
||||
bulk_signers: Vec<Option<Box<dyn Signer>>>,
|
||||
@@ -148,9 +89,11 @@ impl DefaultSigner {
|
||||
unique_signers.push(default_signer);
|
||||
}
|
||||
|
||||
for signer in bulk_signers.into_iter().flatten() {
|
||||
if !unique_signers.iter().any(|s| s == &signer) {
|
||||
unique_signers.push(signer);
|
||||
for signer in bulk_signers.into_iter() {
|
||||
if let Some(signer) = signer {
|
||||
if !unique_signers.iter().any(|s| s == &signer) {
|
||||
unique_signers.push(signer);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(CliSignerInfo {
|
||||
@@ -163,151 +106,29 @@ impl DefaultSigner {
|
||||
matches: &ArgMatches,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<Box<dyn Signer>, Box<dyn std::error::Error>> {
|
||||
signer_from_path(matches, self.path()?, &self.arg_name, wallet_manager)
|
||||
}
|
||||
|
||||
pub fn signer_from_path_with_config(
|
||||
&self,
|
||||
matches: &ArgMatches,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
config: &SignerFromPathConfig,
|
||||
) -> Result<Box<dyn Signer>, Box<dyn std::error::Error>> {
|
||||
signer_from_path_with_config(
|
||||
matches,
|
||||
self.path()?,
|
||||
&self.arg_name,
|
||||
wallet_manager,
|
||||
config,
|
||||
)
|
||||
signer_from_path(matches, &self.path, &self.arg_name, wallet_manager)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct SignerSource {
|
||||
pub kind: SignerSourceKind,
|
||||
pub derivation_path: Option<DerivationPath>,
|
||||
pub legacy: bool,
|
||||
}
|
||||
|
||||
impl SignerSource {
|
||||
fn new(kind: SignerSourceKind) -> Self {
|
||||
Self {
|
||||
kind,
|
||||
derivation_path: None,
|
||||
legacy: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn new_legacy(kind: SignerSourceKind) -> Self {
|
||||
Self {
|
||||
kind,
|
||||
derivation_path: None,
|
||||
legacy: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const SIGNER_SOURCE_PROMPT: &str = "prompt";
|
||||
const SIGNER_SOURCE_FILEPATH: &str = "file";
|
||||
const SIGNER_SOURCE_USB: &str = "usb";
|
||||
const SIGNER_SOURCE_STDIN: &str = "stdin";
|
||||
const SIGNER_SOURCE_PUBKEY: &str = "pubkey";
|
||||
|
||||
pub(crate) enum SignerSourceKind {
|
||||
Prompt,
|
||||
pub enum KeypairUrl {
|
||||
Ask,
|
||||
Filepath(String),
|
||||
Usb(RemoteWalletLocator),
|
||||
Usb(String),
|
||||
Stdin,
|
||||
Pubkey(Pubkey),
|
||||
}
|
||||
|
||||
impl AsRef<str> for SignerSourceKind {
|
||||
fn as_ref(&self) -> &str {
|
||||
match self {
|
||||
Self::Prompt => SIGNER_SOURCE_PROMPT,
|
||||
Self::Filepath(_) => SIGNER_SOURCE_FILEPATH,
|
||||
Self::Usb(_) => SIGNER_SOURCE_USB,
|
||||
Self::Stdin => SIGNER_SOURCE_STDIN,
|
||||
Self::Pubkey(_) => SIGNER_SOURCE_PUBKEY,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for SignerSourceKind {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let s: &str = self.as_ref();
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub(crate) enum SignerSourceError {
|
||||
#[error("unrecognized signer source")]
|
||||
UnrecognizedSource,
|
||||
#[error(transparent)]
|
||||
RemoteWalletLocatorError(#[from] RemoteWalletLocatorError),
|
||||
#[error(transparent)]
|
||||
DerivationPathError(#[from] DerivationPathError),
|
||||
#[error(transparent)]
|
||||
IoError(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
pub(crate) fn parse_signer_source<S: AsRef<str>>(
|
||||
source: S,
|
||||
) -> Result<SignerSource, SignerSourceError> {
|
||||
let source = source.as_ref();
|
||||
let source = {
|
||||
#[cfg(target_family = "windows")]
|
||||
{
|
||||
source.replace("\\", "/")
|
||||
}
|
||||
#[cfg(not(target_family = "windows"))]
|
||||
{
|
||||
source.to_string()
|
||||
}
|
||||
};
|
||||
match uriparse::URIReference::try_from(source.as_str()) {
|
||||
Err(_) => Err(SignerSourceError::UnrecognizedSource),
|
||||
Ok(uri) => {
|
||||
if let Some(scheme) = uri.scheme() {
|
||||
let scheme = scheme.as_str().to_ascii_lowercase();
|
||||
match scheme.as_str() {
|
||||
SIGNER_SOURCE_PROMPT => Ok(SignerSource {
|
||||
kind: SignerSourceKind::Prompt,
|
||||
derivation_path: DerivationPath::from_uri_any_query(&uri)?,
|
||||
legacy: false,
|
||||
}),
|
||||
SIGNER_SOURCE_FILEPATH => Ok(SignerSource::new(SignerSourceKind::Filepath(
|
||||
uri.path().to_string(),
|
||||
))),
|
||||
SIGNER_SOURCE_USB => Ok(SignerSource {
|
||||
kind: SignerSourceKind::Usb(RemoteWalletLocator::new_from_uri(&uri)?),
|
||||
derivation_path: DerivationPath::from_uri_key_query(&uri)?,
|
||||
legacy: false,
|
||||
}),
|
||||
SIGNER_SOURCE_STDIN => Ok(SignerSource::new(SignerSourceKind::Stdin)),
|
||||
_ => {
|
||||
#[cfg(target_family = "windows")]
|
||||
// On Windows, an absolute path's drive letter will be parsed as the URI
|
||||
// scheme. Assume a filepath source in case of a single character shceme.
|
||||
if scheme.len() == 1 {
|
||||
return Ok(SignerSource::new(SignerSourceKind::Filepath(source)));
|
||||
}
|
||||
Err(SignerSourceError::UnrecognizedSource)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match source.as_str() {
|
||||
"-" => Ok(SignerSource::new(SignerSourceKind::Stdin)),
|
||||
ASK_KEYWORD => Ok(SignerSource::new_legacy(SignerSourceKind::Prompt)),
|
||||
_ => match Pubkey::from_str(source.as_str()) {
|
||||
Ok(pubkey) => Ok(SignerSource::new(SignerSourceKind::Pubkey(pubkey))),
|
||||
Err(_) => std::fs::metadata(source.as_str())
|
||||
.map(|_| SignerSource::new(SignerSourceKind::Filepath(source)))
|
||||
.map_err(|err| err.into()),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn parse_keypair_path(path: &str) -> KeypairUrl {
|
||||
if path == "-" {
|
||||
KeypairUrl::Stdin
|
||||
} else if path == ASK_KEYWORD {
|
||||
KeypairUrl::Ask
|
||||
} else if path.starts_with("usb://") {
|
||||
KeypairUrl::Usb(path.to_string())
|
||||
} else if let Ok(pubkey) = Pubkey::from_str(path) {
|
||||
KeypairUrl::Pubkey(pubkey)
|
||||
} else {
|
||||
KeypairUrl::Filepath(path.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,53 +145,22 @@ pub fn presigner_from_pubkey_sigs(
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SignerFromPathConfig {
|
||||
pub allow_null_signer: bool,
|
||||
}
|
||||
|
||||
impl Default for SignerFromPathConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
allow_null_signer: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn signer_from_path(
|
||||
matches: &ArgMatches,
|
||||
path: &str,
|
||||
keypair_name: &str,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<Box<dyn Signer>, Box<dyn error::Error>> {
|
||||
let config = SignerFromPathConfig::default();
|
||||
signer_from_path_with_config(matches, path, keypair_name, wallet_manager, &config)
|
||||
}
|
||||
|
||||
pub fn signer_from_path_with_config(
|
||||
matches: &ArgMatches,
|
||||
path: &str,
|
||||
keypair_name: &str,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
config: &SignerFromPathConfig,
|
||||
) -> Result<Box<dyn Signer>, Box<dyn error::Error>> {
|
||||
let SignerSource {
|
||||
kind,
|
||||
derivation_path,
|
||||
legacy,
|
||||
} = parse_signer_source(path)?;
|
||||
match kind {
|
||||
SignerSourceKind::Prompt => {
|
||||
match parse_keypair_path(path) {
|
||||
KeypairUrl::Ask => {
|
||||
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
||||
Ok(Box::new(keypair_from_seed_phrase(
|
||||
keypair_name,
|
||||
skip_validation,
|
||||
false,
|
||||
derivation_path,
|
||||
legacy,
|
||||
)?))
|
||||
}
|
||||
SignerSourceKind::Filepath(path) => match read_keypair_file(&path) {
|
||||
KeypairUrl::Filepath(path) => match read_keypair_file(&path) {
|
||||
Err(e) => Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("could not read keypair file \"{}\". Run \"solana-keygen new\" to create a keypair file: {}", path, e),
|
||||
@@ -378,18 +168,17 @@ pub fn signer_from_path_with_config(
|
||||
.into()),
|
||||
Ok(file) => Ok(Box::new(file)),
|
||||
},
|
||||
SignerSourceKind::Stdin => {
|
||||
KeypairUrl::Stdin => {
|
||||
let mut stdin = std::io::stdin();
|
||||
Ok(Box::new(read_keypair(&mut stdin)?))
|
||||
}
|
||||
SignerSourceKind::Usb(locator) => {
|
||||
KeypairUrl::Usb(path) => {
|
||||
if wallet_manager.is_none() {
|
||||
*wallet_manager = maybe_wallet_manager()?;
|
||||
}
|
||||
if let Some(wallet_manager) = wallet_manager {
|
||||
Ok(Box::new(generate_remote_keypair(
|
||||
locator,
|
||||
derivation_path.unwrap_or_default(),
|
||||
path,
|
||||
wallet_manager,
|
||||
matches.is_present("confirm_key"),
|
||||
keypair_name,
|
||||
@@ -398,13 +187,13 @@ pub fn signer_from_path_with_config(
|
||||
Err(RemoteWalletError::NoDeviceFound.into())
|
||||
}
|
||||
}
|
||||
SignerSourceKind::Pubkey(pubkey) => {
|
||||
KeypairUrl::Pubkey(pubkey) => {
|
||||
let presigner = pubkeys_sigs_of(matches, SIGNER_ARG.name)
|
||||
.as_ref()
|
||||
.and_then(|presigners| presigner_from_pubkey_sigs(&pubkey, presigners));
|
||||
if let Some(presigner) = presigner {
|
||||
Ok(Box::new(presigner))
|
||||
} else if config.allow_null_signer || matches.is_present(SIGN_ONLY_ARG.name) {
|
||||
} else if matches.is_present(SIGN_ONLY_ARG.name) {
|
||||
Ok(Box::new(NullSigner::new(&pubkey)))
|
||||
} else {
|
||||
Err(std::io::Error::new(
|
||||
@@ -423,9 +212,8 @@ pub fn pubkey_from_path(
|
||||
keypair_name: &str,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<Pubkey, Box<dyn error::Error>> {
|
||||
let SignerSource { kind, .. } = parse_signer_source(path)?;
|
||||
match kind {
|
||||
SignerSourceKind::Pubkey(pubkey) => Ok(pubkey),
|
||||
match parse_keypair_path(path) {
|
||||
KeypairUrl::Pubkey(pubkey) => Ok(pubkey),
|
||||
_ => Ok(signer_from_path(matches, path, keypair_name, wallet_manager)?.pubkey()),
|
||||
}
|
||||
}
|
||||
@@ -436,51 +224,34 @@ pub fn resolve_signer_from_path(
|
||||
keypair_name: &str,
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<Option<String>, Box<dyn error::Error>> {
|
||||
let SignerSource {
|
||||
kind,
|
||||
derivation_path,
|
||||
legacy,
|
||||
} = parse_signer_source(path)?;
|
||||
match kind {
|
||||
SignerSourceKind::Prompt => {
|
||||
match parse_keypair_path(path) {
|
||||
KeypairUrl::Ask => {
|
||||
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
||||
// This method validates the seed phrase, but returns `None` because there is no path
|
||||
// on disk or to a device
|
||||
keypair_from_seed_phrase(
|
||||
keypair_name,
|
||||
skip_validation,
|
||||
false,
|
||||
derivation_path,
|
||||
legacy,
|
||||
)
|
||||
.map(|_| None)
|
||||
keypair_from_seed_phrase(keypair_name, skip_validation, false).map(|_| None)
|
||||
}
|
||||
SignerSourceKind::Filepath(path) => match read_keypair_file(&path) {
|
||||
KeypairUrl::Filepath(path) => match read_keypair_file(&path) {
|
||||
Err(e) => Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!(
|
||||
"could not read keypair file \"{}\". \
|
||||
Run \"solana-keygen new\" to create a keypair file: {}",
|
||||
path, e
|
||||
),
|
||||
format!("could not read keypair file \"{}\". Run \"solana-keygen new\" to create a keypair file: {}", path, e),
|
||||
)
|
||||
.into()),
|
||||
Ok(_) => Ok(Some(path.to_string())),
|
||||
},
|
||||
SignerSourceKind::Stdin => {
|
||||
KeypairUrl::Stdin => {
|
||||
let mut stdin = std::io::stdin();
|
||||
// This method validates the keypair from stdin, but returns `None` because there is no
|
||||
// path on disk or to a device
|
||||
read_keypair(&mut stdin).map(|_| None)
|
||||
}
|
||||
SignerSourceKind::Usb(locator) => {
|
||||
KeypairUrl::Usb(path) => {
|
||||
if wallet_manager.is_none() {
|
||||
*wallet_manager = maybe_wallet_manager()?;
|
||||
}
|
||||
if let Some(wallet_manager) = wallet_manager {
|
||||
let path = generate_remote_keypair(
|
||||
locator,
|
||||
derivation_path.unwrap_or_default(),
|
||||
path,
|
||||
wallet_manager,
|
||||
matches.is_present("confirm_key"),
|
||||
keypair_name,
|
||||
@@ -495,7 +266,7 @@ pub fn resolve_signer_from_path(
|
||||
}
|
||||
}
|
||||
|
||||
// Keyword used to indicate that the user should be prompted for a keypair seed phrase
|
||||
// Keyword used to indicate that the user should be asked for a keypair seed phrase
|
||||
pub const ASK_KEYWORD: &str = "ASK";
|
||||
|
||||
pub const SKIP_SEED_PHRASE_VALIDATION_ARG: ArgConstant<'static> = ArgConstant {
|
||||
@@ -516,56 +287,6 @@ pub fn prompt_passphrase(prompt: &str) -> Result<String, Box<dyn error::Error>>
|
||||
Ok(passphrase)
|
||||
}
|
||||
|
||||
/// Parses a path into a SignerSource and returns a Keypair for supporting SignerSourceKinds
|
||||
pub fn keypair_from_path(
|
||||
matches: &ArgMatches,
|
||||
path: &str,
|
||||
keypair_name: &str,
|
||||
confirm_pubkey: bool,
|
||||
) -> Result<Keypair, Box<dyn error::Error>> {
|
||||
let SignerSource {
|
||||
kind,
|
||||
derivation_path,
|
||||
legacy,
|
||||
} = parse_signer_source(path)?;
|
||||
match kind {
|
||||
SignerSourceKind::Prompt => {
|
||||
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
||||
Ok(keypair_from_seed_phrase(
|
||||
keypair_name,
|
||||
skip_validation,
|
||||
confirm_pubkey,
|
||||
derivation_path,
|
||||
legacy,
|
||||
)?)
|
||||
}
|
||||
SignerSourceKind::Filepath(path) => match read_keypair_file(&path) {
|
||||
Err(e) => Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!(
|
||||
"could not read keypair file \"{}\". \
|
||||
Run \"solana-keygen new\" to create a keypair file: {}",
|
||||
path, e
|
||||
),
|
||||
)
|
||||
.into()),
|
||||
Ok(file) => Ok(file),
|
||||
},
|
||||
SignerSourceKind::Stdin => {
|
||||
let mut stdin = std::io::stdin();
|
||||
Ok(read_keypair(&mut stdin)?)
|
||||
}
|
||||
_ => Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!(
|
||||
"signer of type `{:?}` does not support Keypair output",
|
||||
kind
|
||||
),
|
||||
)
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads user input from stdin to retrieve a seed phrase and passphrase for keypair derivation
|
||||
/// Optionally skips validation of seed phrase
|
||||
/// Optionally confirms recovered public key
|
||||
@@ -573,8 +294,6 @@ pub fn keypair_from_seed_phrase(
|
||||
keypair_name: &str,
|
||||
skip_validation: bool,
|
||||
confirm_pubkey: bool,
|
||||
derivation_path: Option<DerivationPath>,
|
||||
legacy: bool,
|
||||
) -> Result<Keypair, Box<dyn error::Error>> {
|
||||
let seed_phrase = prompt_password_stderr(&format!("[{}] seed phrase: ", keypair_name))?;
|
||||
let seed_phrase = seed_phrase.trim();
|
||||
@@ -585,12 +304,7 @@ pub fn keypair_from_seed_phrase(
|
||||
|
||||
let keypair = if skip_validation {
|
||||
let passphrase = prompt_passphrase(&passphrase_prompt)?;
|
||||
if legacy {
|
||||
keypair_from_seed_phrase_and_passphrase(&seed_phrase, &passphrase)?
|
||||
} else {
|
||||
let seed = generate_seed_from_seed_phrase_and_passphrase(&seed_phrase, &passphrase);
|
||||
keypair_from_seed_and_derivation_path(&seed, derivation_path)?
|
||||
}
|
||||
keypair_from_seed_phrase_and_passphrase(&seed_phrase, &passphrase)?
|
||||
} else {
|
||||
let sanitized = sanitize_seed_phrase(seed_phrase);
|
||||
let parse_language_fn = || {
|
||||
@@ -613,11 +327,7 @@ pub fn keypair_from_seed_phrase(
|
||||
let mnemonic = parse_language_fn()?;
|
||||
let passphrase = prompt_passphrase(&passphrase_prompt)?;
|
||||
let seed = Seed::new(&mnemonic, &passphrase);
|
||||
if legacy {
|
||||
keypair_from_seed(seed.as_bytes())?
|
||||
} else {
|
||||
keypair_from_seed_and_derivation_path(&seed.as_bytes(), derivation_path)?
|
||||
}
|
||||
keypair_from_seed(seed.as_bytes())?
|
||||
};
|
||||
|
||||
if confirm_pubkey {
|
||||
@@ -645,9 +355,6 @@ fn sanitize_seed_phrase(seed_phrase: &str) -> String {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use solana_remote_wallet::locator::Manufacturer;
|
||||
use solana_sdk::system_instruction;
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
#[test]
|
||||
fn test_sanitize_seed_phrase() {
|
||||
@@ -657,153 +364,4 @@ mod tests {
|
||||
sanitize_seed_phrase(seed_phrase)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signer_info_signers_for_message() {
|
||||
let source = Keypair::new();
|
||||
let fee_payer = Keypair::new();
|
||||
let nonsigner1 = Keypair::new();
|
||||
let nonsigner2 = Keypair::new();
|
||||
let recipient = Pubkey::new_unique();
|
||||
let message = Message::new(
|
||||
&[system_instruction::transfer(
|
||||
&source.pubkey(),
|
||||
&recipient,
|
||||
42,
|
||||
)],
|
||||
Some(&fee_payer.pubkey()),
|
||||
);
|
||||
let signers = vec![
|
||||
Box::new(fee_payer) as Box<dyn Signer>,
|
||||
Box::new(source) as Box<dyn Signer>,
|
||||
Box::new(nonsigner1) as Box<dyn Signer>,
|
||||
Box::new(nonsigner2) as Box<dyn Signer>,
|
||||
];
|
||||
let signer_info = CliSignerInfo { signers };
|
||||
let msg_signers = signer_info.signers_for_message(&message);
|
||||
let signer_pubkeys = msg_signers.iter().map(|s| s.pubkey()).collect::<Vec<_>>();
|
||||
let expect = vec![
|
||||
signer_info.signers[0].pubkey(),
|
||||
signer_info.signers[1].pubkey(),
|
||||
];
|
||||
assert_eq!(signer_pubkeys, expect);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_signer_source() {
|
||||
assert!(matches!(
|
||||
parse_signer_source("-").unwrap(),
|
||||
SignerSource {
|
||||
kind: SignerSourceKind::Stdin,
|
||||
derivation_path: None,
|
||||
legacy: false,
|
||||
}
|
||||
));
|
||||
let stdin = "stdin:".to_string();
|
||||
assert!(matches!(
|
||||
parse_signer_source(&stdin).unwrap(),
|
||||
SignerSource {
|
||||
kind: SignerSourceKind::Stdin,
|
||||
derivation_path: None,
|
||||
legacy: false,
|
||||
}
|
||||
));
|
||||
assert!(matches!(
|
||||
parse_signer_source(ASK_KEYWORD).unwrap(),
|
||||
SignerSource {
|
||||
kind: SignerSourceKind::Prompt,
|
||||
derivation_path: None,
|
||||
legacy: true,
|
||||
}
|
||||
));
|
||||
let pubkey = Pubkey::new_unique();
|
||||
assert!(
|
||||
matches!(parse_signer_source(&pubkey.to_string()).unwrap(), SignerSource {
|
||||
kind: SignerSourceKind::Pubkey(p),
|
||||
derivation_path: None,
|
||||
legacy: false,
|
||||
}
|
||||
if p == pubkey)
|
||||
);
|
||||
|
||||
// Set up absolute and relative path strs
|
||||
let file0 = NamedTempFile::new().unwrap();
|
||||
let path = file0.path();
|
||||
assert!(path.is_absolute());
|
||||
let absolute_path_str = path.to_str().unwrap();
|
||||
|
||||
let file1 = NamedTempFile::new_in(std::env::current_dir().unwrap()).unwrap();
|
||||
let path = file1.path().file_name().unwrap().to_str().unwrap();
|
||||
let path = std::path::Path::new(path);
|
||||
assert!(path.is_relative());
|
||||
let relative_path_str = path.to_str().unwrap();
|
||||
|
||||
assert!(
|
||||
matches!(parse_signer_source(absolute_path_str).unwrap(), SignerSource {
|
||||
kind: SignerSourceKind::Filepath(p),
|
||||
derivation_path: None,
|
||||
legacy: false,
|
||||
} if p == absolute_path_str)
|
||||
);
|
||||
assert!(
|
||||
matches!(parse_signer_source(&relative_path_str).unwrap(), SignerSource {
|
||||
kind: SignerSourceKind::Filepath(p),
|
||||
derivation_path: None,
|
||||
legacy: false,
|
||||
} if p == relative_path_str)
|
||||
);
|
||||
|
||||
let usb = "usb://ledger".to_string();
|
||||
let expected_locator = RemoteWalletLocator {
|
||||
manufacturer: Manufacturer::Ledger,
|
||||
pubkey: None,
|
||||
};
|
||||
assert!(matches!(parse_signer_source(&usb).unwrap(), SignerSource {
|
||||
kind: SignerSourceKind::Usb(u),
|
||||
derivation_path: None,
|
||||
legacy: false,
|
||||
} if u == expected_locator));
|
||||
let usb = "usb://ledger?key=0/0".to_string();
|
||||
let expected_locator = RemoteWalletLocator {
|
||||
manufacturer: Manufacturer::Ledger,
|
||||
pubkey: None,
|
||||
};
|
||||
let expected_derivation_path = Some(DerivationPath::new_bip44(Some(0), Some(0)));
|
||||
assert!(matches!(parse_signer_source(&usb).unwrap(), SignerSource {
|
||||
kind: SignerSourceKind::Usb(u),
|
||||
derivation_path: d,
|
||||
legacy: false,
|
||||
} if u == expected_locator && d == expected_derivation_path));
|
||||
// Catchall into SignerSource::Filepath fails
|
||||
let junk = "sometextthatisnotapubkeyorfile".to_string();
|
||||
assert!(Pubkey::from_str(&junk).is_err());
|
||||
assert!(matches!(
|
||||
parse_signer_source(&junk),
|
||||
Err(SignerSourceError::IoError(_))
|
||||
));
|
||||
|
||||
let prompt = "prompt:".to_string();
|
||||
assert!(matches!(
|
||||
parse_signer_source(&prompt).unwrap(),
|
||||
SignerSource {
|
||||
kind: SignerSourceKind::Prompt,
|
||||
derivation_path: None,
|
||||
legacy: false,
|
||||
}
|
||||
));
|
||||
assert!(
|
||||
matches!(parse_signer_source(&format!("file:{}", absolute_path_str)).unwrap(), SignerSource {
|
||||
kind: SignerSourceKind::Filepath(p),
|
||||
derivation_path: None,
|
||||
legacy: false,
|
||||
} if p == absolute_path_str)
|
||||
);
|
||||
assert!(
|
||||
matches!(parse_signer_source(&format!("file:{}", relative_path_str)).unwrap(), SignerSource {
|
||||
kind: SignerSourceKind::Filepath(p),
|
||||
derivation_path: None,
|
||||
legacy: false,
|
||||
} if p == relative_path_str)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -27,6 +27,5 @@ pub mod fee_payer;
|
||||
pub mod input_parsers;
|
||||
pub mod input_validators;
|
||||
pub mod keypair;
|
||||
pub mod memo;
|
||||
pub mod nonce;
|
||||
pub mod offline;
|
||||
|
@@ -1,15 +0,0 @@
|
||||
use {crate::ArgConstant, clap::Arg};
|
||||
|
||||
pub const MEMO_ARG: ArgConstant<'static> = ArgConstant {
|
||||
name: "memo",
|
||||
long: "--with-memo",
|
||||
help: "Specify a memo string to include in the transaction.",
|
||||
};
|
||||
|
||||
pub fn memo_arg<'a, 'b>() -> Arg<'a, 'b> {
|
||||
Arg::with_name(MEMO_ARG.name)
|
||||
.long(MEMO_ARG.long)
|
||||
.takes_value(true)
|
||||
.value_name("MEMO")
|
||||
.help(MEMO_ARG.help)
|
||||
}
|
@@ -1,7 +1,5 @@
|
||||
use {
|
||||
crate::{input_validators::*, offline::BLOCKHASH_ARG, ArgConstant},
|
||||
clap::{App, Arg},
|
||||
};
|
||||
use crate::{input_validators::*, offline::BLOCKHASH_ARG, ArgConstant};
|
||||
use clap::{App, Arg};
|
||||
|
||||
pub const NONCE_ARG: ArgConstant<'static> = ArgConstant {
|
||||
name: "nonce",
|
||||
|
@@ -1,7 +1,5 @@
|
||||
use {
|
||||
crate::{input_validators::*, ArgConstant},
|
||||
clap::{App, Arg},
|
||||
};
|
||||
use crate::{input_validators::*, ArgConstant};
|
||||
use clap::{App, Arg};
|
||||
|
||||
pub const BLOCKHASH_ARG: ArgConstant<'static> = ArgConstant {
|
||||
name: "blockhash",
|
||||
@@ -21,12 +19,6 @@ pub const SIGNER_ARG: ArgConstant<'static> = ArgConstant {
|
||||
help: "Provide a public-key/signature pair for the transaction",
|
||||
};
|
||||
|
||||
pub const DUMP_TRANSACTION_MESSAGE: ArgConstant<'static> = ArgConstant {
|
||||
name: "dump_transaction_message",
|
||||
long: "dump-transaction-message",
|
||||
help: "Display the base64 encoded binary transaction message in sign-only mode",
|
||||
};
|
||||
|
||||
pub fn blockhash_arg<'a, 'b>() -> Arg<'a, 'b> {
|
||||
Arg::with_name(BLOCKHASH_ARG.name)
|
||||
.long(BLOCKHASH_ARG.long)
|
||||
@@ -55,14 +47,6 @@ fn signer_arg<'a, 'b>() -> Arg<'a, 'b> {
|
||||
.help(SIGNER_ARG.help)
|
||||
}
|
||||
|
||||
pub fn dump_transaction_message<'a, 'b>() -> Arg<'a, 'b> {
|
||||
Arg::with_name(DUMP_TRANSACTION_MESSAGE.name)
|
||||
.long(DUMP_TRANSACTION_MESSAGE.long)
|
||||
.takes_value(false)
|
||||
.requires(SIGN_ONLY_ARG.name)
|
||||
.help(DUMP_TRANSACTION_MESSAGE.help)
|
||||
}
|
||||
|
||||
pub trait ArgsConfig {
|
||||
fn blockhash_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> {
|
||||
arg
|
||||
@@ -73,9 +57,6 @@ pub trait ArgsConfig {
|
||||
fn signer_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> {
|
||||
arg
|
||||
}
|
||||
fn dump_transaction_message_arg<'a, 'b>(&self, arg: Arg<'a, 'b>) -> Arg<'a, 'b> {
|
||||
arg
|
||||
}
|
||||
}
|
||||
|
||||
pub trait OfflineArgs {
|
||||
@@ -88,7 +69,6 @@ impl OfflineArgs for App<'_, '_> {
|
||||
self.arg(config.blockhash_arg(blockhash_arg()))
|
||||
.arg(config.sign_only_arg(sign_only_arg()))
|
||||
.arg(config.signer_arg(signer_arg()))
|
||||
.arg(config.dump_transaction_message_arg(dump_transaction_message()))
|
||||
}
|
||||
fn offline_args(self) -> Self {
|
||||
struct NullArgsConfig {}
|
||||
|
@@ -3,16 +3,15 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-cli-config"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.6.13"
|
||||
version = "1.5.7"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
documentation = "https://docs.rs/solana-cli-config"
|
||||
|
||||
[dependencies]
|
||||
dirs-next = "2.0.0"
|
||||
lazy_static = "1.4.0"
|
||||
serde = "1.0.122"
|
||||
serde = "1.0.112"
|
||||
serde_derive = "1.0.103"
|
||||
serde_yaml = "0.8.13"
|
||||
url = "2.1.1"
|
||||
|
@@ -75,8 +75,7 @@ impl Config {
|
||||
.set_scheme(if is_secure { "wss" } else { "ws" })
|
||||
.expect("unable to set scheme");
|
||||
if let Some(port) = json_rpc_url.port() {
|
||||
let port = port.checked_add(1).expect("port out of range");
|
||||
ws_url.set_port(Some(port)).expect("unable to set port");
|
||||
ws_url.set_port(Some(port + 1)).expect("unable to set port");
|
||||
}
|
||||
ws_url.to_string()
|
||||
}
|
||||
@@ -107,13 +106,13 @@ mod test {
|
||||
#[test]
|
||||
fn compute_websocket_url() {
|
||||
assert_eq!(
|
||||
Config::compute_websocket_url(&"http://api.devnet.solana.com"),
|
||||
"ws://api.devnet.solana.com/".to_string()
|
||||
Config::compute_websocket_url(&"http://devnet.solana.com"),
|
||||
"ws://devnet.solana.com/".to_string()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Config::compute_websocket_url(&"https://api.devnet.solana.com"),
|
||||
"wss://api.devnet.solana.com/".to_string()
|
||||
Config::compute_websocket_url(&"https://devnet.solana.com"),
|
||||
"wss://devnet.solana.com/".to_string()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
|
@@ -3,30 +3,27 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-cli-output"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.6.13"
|
||||
version = "1.5.7"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
documentation = "https://docs.rs/solana-cli-output"
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.13.0"
|
||||
chrono = { version = "0.4.11", features = ["serde"] }
|
||||
console = "0.11.3"
|
||||
humantime = "2.0.1"
|
||||
Inflector = "0.11.4"
|
||||
indicatif = "0.15.0"
|
||||
serde = "1.0.122"
|
||||
serde = "1.0.112"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.56"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.6.13" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.6.13" }
|
||||
solana-client = { path = "../client", version = "=1.6.13" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.13" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "=1.6.13" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.6.13" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.6.13" }
|
||||
spl-memo = { version = "=3.0.1", features = ["no-entrypoint"] }
|
||||
solana-account-decoder = { path = "../account-decoder", version = "1.5.7" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.5.7" }
|
||||
solana-client = { path = "../client", version = "1.5.7" }
|
||||
solana-sdk = { path = "../sdk", version = "1.5.7" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "1.5.7" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "1.5.7" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.5.7" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,13 @@
|
||||
use {
|
||||
crate::cli_output::CliSignatureVerificationStatus,
|
||||
chrono::{DateTime, Local, NaiveDateTime, SecondsFormat, TimeZone, Utc},
|
||||
chrono::{DateTime, NaiveDateTime, SecondsFormat, Utc},
|
||||
console::style,
|
||||
indicatif::{ProgressBar, ProgressStyle},
|
||||
solana_sdk::{
|
||||
clock::UnixTimestamp, hash::Hash, message::Message, native_token::lamports_to_sol,
|
||||
program_utils::limited_deserialize, pubkey::Pubkey, transaction::Transaction,
|
||||
clock::UnixTimestamp, hash::Hash, native_token::lamports_to_sol,
|
||||
program_utils::limited_deserialize, transaction::Transaction,
|
||||
},
|
||||
solana_transaction_status::UiTransactionStatusMeta,
|
||||
spl_memo::id as spl_memo_id,
|
||||
spl_memo::v1::id as spl_memo_v1_id,
|
||||
std::{collections::HashMap, fmt, io},
|
||||
};
|
||||
|
||||
@@ -30,11 +28,6 @@ impl Default for BuildBalanceMessageConfig {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_memo_program(k: &Pubkey) -> bool {
|
||||
let k_str = k.to_string();
|
||||
(k_str == spl_memo_v1_id().to_string()) || (k_str == spl_memo_id().to_string())
|
||||
}
|
||||
|
||||
pub fn build_balance_message_with_config(
|
||||
lamports: u64,
|
||||
config: &BuildBalanceMessageConfig,
|
||||
@@ -132,48 +125,14 @@ pub fn println_signers(
|
||||
println!();
|
||||
}
|
||||
|
||||
fn format_account_mode(message: &Message, index: usize) -> String {
|
||||
format!(
|
||||
"{}r{}{}", // accounts are always readable...
|
||||
if message.is_signer(index) {
|
||||
"s" // stands for signer
|
||||
} else {
|
||||
"-"
|
||||
},
|
||||
if message.is_writable(index, /*demote_sysvar_write_locks=*/ true) {
|
||||
"w" // comment for consistent rust fmt (no joking; lol)
|
||||
} else {
|
||||
"-"
|
||||
},
|
||||
// account may be executable on-chain while not being
|
||||
// designated as a program-id in the message
|
||||
if message.maybe_executable(index) {
|
||||
"x"
|
||||
} else {
|
||||
// programs to be executed via CPI cannot be identified as
|
||||
// executable from the message
|
||||
"-"
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn write_transaction<W: io::Write>(
|
||||
w: &mut W,
|
||||
transaction: &Transaction,
|
||||
transaction_status: &Option<UiTransactionStatusMeta>,
|
||||
prefix: &str,
|
||||
sigverify_status: Option<&[CliSignatureVerificationStatus]>,
|
||||
block_time: Option<UnixTimestamp>,
|
||||
) -> io::Result<()> {
|
||||
let message = &transaction.message;
|
||||
if let Some(block_time) = block_time {
|
||||
writeln!(
|
||||
w,
|
||||
"{}Block Time: {:?}",
|
||||
prefix,
|
||||
Local.timestamp(block_time, 0)
|
||||
)?;
|
||||
}
|
||||
writeln!(
|
||||
w,
|
||||
"{}Recent Blockhash: {:?}",
|
||||
@@ -199,31 +158,16 @@ pub fn write_transaction<W: io::Write>(
|
||||
prefix, signature_index, signature, sigverify_status,
|
||||
)?;
|
||||
}
|
||||
let mut fee_payer_index = None;
|
||||
writeln!(w, "{}{:?}", prefix, message.header)?;
|
||||
for (account_index, account) in message.account_keys.iter().enumerate() {
|
||||
if fee_payer_index.is_none() && message.is_non_loader_key(account, account_index) {
|
||||
fee_payer_index = Some(account_index)
|
||||
}
|
||||
writeln!(
|
||||
w,
|
||||
"{}Account {}: {} {}{}",
|
||||
prefix,
|
||||
account_index,
|
||||
format_account_mode(message, account_index),
|
||||
account,
|
||||
if Some(account_index) == fee_payer_index {
|
||||
" (fee payer)"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
)?;
|
||||
writeln!(w, "{}Account {}: {:?}", prefix, account_index, account)?;
|
||||
}
|
||||
for (instruction_index, instruction) in message.instructions.iter().enumerate() {
|
||||
let program_pubkey = message.account_keys[instruction.program_id_index as usize];
|
||||
writeln!(w, "{}Instruction {}", prefix, instruction_index)?;
|
||||
writeln!(
|
||||
w,
|
||||
"{} Program: {} ({})",
|
||||
"{} Program: {} ({})",
|
||||
prefix, program_pubkey, instruction.program_id_index
|
||||
)?;
|
||||
for (account_index, account) in instruction.accounts.iter().enumerate() {
|
||||
@@ -260,11 +204,6 @@ pub fn write_transaction<W: io::Write>(
|
||||
writeln!(w, "{} {:?}", prefix, system_instruction)?;
|
||||
raw = false;
|
||||
}
|
||||
} else if is_memo_program(&program_pubkey) {
|
||||
if let Ok(s) = std::str::from_utf8(&instruction.data) {
|
||||
writeln!(w, "{} Data: \"{}\"", prefix, s)?;
|
||||
raw = false;
|
||||
}
|
||||
}
|
||||
|
||||
if raw {
|
||||
@@ -322,38 +261,7 @@ pub fn write_transaction<W: io::Write>(
|
||||
if !log_messages.is_empty() {
|
||||
writeln!(w, "{}Log Messages:", prefix,)?;
|
||||
for log_message in log_messages {
|
||||
writeln!(w, "{} {}", prefix, log_message)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(rewards) = &transaction_status.rewards {
|
||||
if !rewards.is_empty() {
|
||||
writeln!(w, "{}Rewards:", prefix,)?;
|
||||
writeln!(
|
||||
w,
|
||||
"{} {:<44} {:^15} {:<15} {:<20}",
|
||||
prefix, "Address", "Type", "Amount", "New Balance"
|
||||
)?;
|
||||
for reward in rewards {
|
||||
let sign = if reward.lamports < 0 { "-" } else { "" };
|
||||
writeln!(
|
||||
w,
|
||||
"{} {:<44} {:^15} {:<15} {}",
|
||||
prefix,
|
||||
reward.pubkey,
|
||||
if let Some(reward_type) = reward.reward_type {
|
||||
format!("{}", reward_type)
|
||||
} else {
|
||||
"-".to_string()
|
||||
},
|
||||
format!(
|
||||
"{}◎{:<14.9}",
|
||||
sign,
|
||||
lamports_to_sol(reward.lamports.abs() as u64)
|
||||
),
|
||||
format!("◎{:<18.9}", lamports_to_sol(reward.post_balance),)
|
||||
)?;
|
||||
writeln!(w, "{} {}", prefix, log_message,)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -369,7 +277,6 @@ pub fn println_transaction(
|
||||
transaction_status: &Option<UiTransactionStatusMeta>,
|
||||
prefix: &str,
|
||||
sigverify_status: Option<&[CliSignatureVerificationStatus]>,
|
||||
block_time: Option<UnixTimestamp>,
|
||||
) {
|
||||
let mut w = Vec::new();
|
||||
if write_transaction(
|
||||
@@ -378,7 +285,6 @@ pub fn println_transaction(
|
||||
transaction_status,
|
||||
prefix,
|
||||
sigverify_status,
|
||||
block_time,
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
@@ -388,32 +294,6 @@ pub fn println_transaction(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writeln_transaction(
|
||||
f: &mut dyn fmt::Write,
|
||||
transaction: &Transaction,
|
||||
transaction_status: &Option<UiTransactionStatusMeta>,
|
||||
prefix: &str,
|
||||
sigverify_status: Option<&[CliSignatureVerificationStatus]>,
|
||||
block_time: Option<UnixTimestamp>,
|
||||
) -> fmt::Result {
|
||||
let mut w = Vec::new();
|
||||
if write_transaction(
|
||||
&mut w,
|
||||
transaction,
|
||||
transaction_status,
|
||||
prefix,
|
||||
sigverify_status,
|
||||
block_time,
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
if let Ok(s) = String::from_utf8(w) {
|
||||
write!(f, "{}", s)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Creates a new process bar for processing that will take an unknown amount of time
|
||||
pub fn new_spinner_progress_bar() -> ProgressBar {
|
||||
let progress_bar = ProgressBar::new(42);
|
||||
|
@@ -1,4 +1,3 @@
|
||||
#![allow(clippy::integer_arithmetic)]
|
||||
mod cli_output;
|
||||
pub mod display;
|
||||
pub use cli_output::*;
|
||||
|
@@ -3,11 +3,10 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-cli"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.6.13"
|
||||
version = "1.5.7"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
documentation = "https://docs.rs/solana-cli"
|
||||
|
||||
[dependencies]
|
||||
bincode = "1.3.1"
|
||||
@@ -24,34 +23,33 @@ indicatif = "0.15.0"
|
||||
humantime = "2.0.1"
|
||||
num-traits = "0.2"
|
||||
pretty-hex = "0.2.1"
|
||||
reqwest = { version = "0.11.2", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
serde = "1.0.122"
|
||||
reqwest = { version = "0.10.8", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
serde = "1.0.112"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.56"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.6.13" }
|
||||
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.6.13" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.6.13" }
|
||||
solana-cli-config = { path = "../cli-config", version = "=1.6.13" }
|
||||
solana-cli-output = { path = "../cli-output", version = "=1.6.13" }
|
||||
solana-client = { path = "../client", version = "=1.6.13" }
|
||||
solana-config-program = { path = "../programs/config", version = "=1.6.13" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.6.13" }
|
||||
solana-logger = { path = "../logger", version = "=1.6.13" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.6.13" }
|
||||
solana_rbpf = "=0.2.9"
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "=1.6.13" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.13" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "=1.6.13" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.6.13" }
|
||||
solana-version = { path = "../version", version = "=1.6.13" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.6.13" }
|
||||
spl-memo = { version = "=3.0.1", features = ["no-entrypoint"] }
|
||||
solana-account-decoder = { path = "../account-decoder", version = "1.5.7" }
|
||||
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "1.5.7" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.5.7" }
|
||||
solana-cli-config = { path = "../cli-config", version = "1.5.7" }
|
||||
solana-cli-output = { path = "../cli-output", version = "1.5.7" }
|
||||
solana-client = { path = "../client", version = "1.5.7" }
|
||||
solana-config-program = { path = "../programs/config", version = "1.5.7" }
|
||||
solana-faucet = { path = "../faucet", version = "1.5.7" }
|
||||
solana-logger = { path = "../logger", version = "1.5.7" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.5.7" }
|
||||
solana_rbpf = "=0.2.4"
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "1.5.7" }
|
||||
solana-sdk = { path = "../sdk", version = "1.5.7" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "1.5.7" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "1.5.7" }
|
||||
solana-version = { path = "../version", version = "1.5.7" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.5.7" }
|
||||
thiserror = "1.0.21"
|
||||
tiny-bip39 = "0.7.0"
|
||||
url = "2.1.1"
|
||||
|
||||
[dev-dependencies]
|
||||
solana-core = { path = "../core", version = "=1.6.13" }
|
||||
solana-core = { path = "../core", version = "1.5.7" }
|
||||
tempfile = "3.1.0"
|
||||
|
||||
[[bin]]
|
||||
|
@@ -86,13 +86,9 @@ pub fn check_account_for_spend_multiple_fees_with_commitment(
|
||||
return Err(CliError::InsufficientFundsForSpendAndFee(
|
||||
lamports_to_sol(balance),
|
||||
lamports_to_sol(fee),
|
||||
*account_pubkey,
|
||||
));
|
||||
} else {
|
||||
return Err(CliError::InsufficientFundsForFee(
|
||||
lamports_to_sol(fee),
|
||||
*account_pubkey,
|
||||
));
|
||||
return Err(CliError::InsufficientFundsForFee(lamports_to_sol(fee)));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
765
cli/src/cli.rs
765
cli/src/cli.rs
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,7 @@ use crate::{
|
||||
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
|
||||
stake::is_stake_program_v2_enabled,
|
||||
};
|
||||
use chrono::{Local, TimeZone};
|
||||
use clap::{value_t, value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand};
|
||||
use console::{style, Emoji};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -24,9 +25,8 @@ use solana_client::{
|
||||
pubsub_client::PubsubClient,
|
||||
rpc_client::{GetConfirmedSignaturesForAddress2Config, RpcClient},
|
||||
rpc_config::{
|
||||
RpcAccountInfoConfig, RpcConfirmedBlockConfig, RpcConfirmedTransactionConfig,
|
||||
RpcLargestAccountsConfig, RpcLargestAccountsFilter, RpcProgramAccountsConfig,
|
||||
RpcTransactionLogsConfig, RpcTransactionLogsFilter,
|
||||
RpcAccountInfoConfig, RpcLargestAccountsConfig, RpcLargestAccountsFilter,
|
||||
RpcProgramAccountsConfig, RpcTransactionLogsConfig, RpcTransactionLogsFilter,
|
||||
},
|
||||
rpc_filter,
|
||||
rpc_response::SlotInfo,
|
||||
@@ -41,27 +41,23 @@ use solana_sdk::{
|
||||
hash::Hash,
|
||||
message::Message,
|
||||
native_token::lamports_to_sol,
|
||||
nonce::State as NonceState,
|
||||
pubkey::{self, Pubkey},
|
||||
rent::Rent,
|
||||
rpc_port::DEFAULT_RPC_PORT_STR,
|
||||
signature::Signature,
|
||||
slot_history, system_instruction, system_program,
|
||||
system_instruction, system_program,
|
||||
sysvar::{
|
||||
self,
|
||||
slot_history::SlotHistory,
|
||||
stake_history::{self},
|
||||
},
|
||||
timing,
|
||||
transaction::Transaction,
|
||||
};
|
||||
use solana_stake_program::stake_state::StakeState;
|
||||
use solana_transaction_status::UiTransactionEncoding;
|
||||
use solana_vote_program::vote_state::VoteState;
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap, VecDeque},
|
||||
fmt,
|
||||
str::FromStr,
|
||||
net::SocketAddr,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
@@ -69,7 +65,6 @@ use std::{
|
||||
thread::sleep,
|
||||
time::{Duration, Instant, SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
static CHECK_MARK: Emoji = Emoji("✅ ", "");
|
||||
static CROSS_MARK: Emoji = Emoji("❌ ", "");
|
||||
@@ -140,17 +135,7 @@ impl ClusterQuerySubCommands for App<'_, '_> {
|
||||
SubCommand::with_name("cluster-version")
|
||||
.about("Get the version of the cluster entrypoint"),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("fees")
|
||||
.about("Display current cluster fees")
|
||||
.arg(
|
||||
Arg::with_name("blockhash")
|
||||
.long("blockhash")
|
||||
.takes_value(true)
|
||||
.value_name("BLOCKHASH")
|
||||
.validator(is_hash)
|
||||
.help("Query fees for BLOCKHASH instead of the the most recent blockhash")
|
||||
),
|
||||
.subcommand(SubCommand::with_name("fees").about("Display current cluster fees"),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("first-available-block")
|
||||
@@ -349,38 +334,6 @@ impl ClusterQuerySubCommands for App<'_, '_> {
|
||||
.long("lamports")
|
||||
.takes_value(false)
|
||||
.help("Display balance in lamports instead of SOL"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("number")
|
||||
.long("number")
|
||||
.short("n")
|
||||
.takes_value(false)
|
||||
.help("Number the validators"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("reverse")
|
||||
.long("reverse")
|
||||
.short("r")
|
||||
.takes_value(false)
|
||||
.help("Reverse order while sorting"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("sort")
|
||||
.long("sort")
|
||||
.takes_value(true)
|
||||
.possible_values(&[
|
||||
"delinquent",
|
||||
"commission",
|
||||
"credits",
|
||||
"identity",
|
||||
"last-vote",
|
||||
"root",
|
||||
"skip-rate",
|
||||
"stake",
|
||||
"vote-account",
|
||||
])
|
||||
.default_value("stake")
|
||||
.help("Sort order (does not affect JSON output)"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
@@ -434,14 +387,9 @@ impl ClusterQuerySubCommands for App<'_, '_> {
|
||||
.arg(
|
||||
Arg::with_name("data_length")
|
||||
.index(1)
|
||||
.value_name("DATA_LENGTH_OR_MONIKER")
|
||||
.value_name("DATA_LENGTH")
|
||||
.required(true)
|
||||
.validator(|s| {
|
||||
RentLengthValue::from_str(&s)
|
||||
.map(|_| ())
|
||||
.map_err(|e| e.to_string())
|
||||
})
|
||||
.help("Length of data in the account to calculate rent for, or moniker: [nonce, stake, system, vote]"),
|
||||
.help("Length of data in the account to calculate rent for"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("lamports")
|
||||
@@ -614,29 +562,9 @@ pub fn parse_show_stakes(
|
||||
|
||||
pub fn parse_show_validators(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||
let use_lamports_unit = matches.is_present("lamports");
|
||||
let number_validators = matches.is_present("number");
|
||||
let reverse_sort = matches.is_present("reverse");
|
||||
|
||||
let sort_order = match value_t_or_exit!(matches, "sort", String).as_str() {
|
||||
"delinquent" => CliValidatorsSortOrder::Delinquent,
|
||||
"commission" => CliValidatorsSortOrder::Commission,
|
||||
"credits" => CliValidatorsSortOrder::EpochCredits,
|
||||
"identity" => CliValidatorsSortOrder::Identity,
|
||||
"last-vote" => CliValidatorsSortOrder::LastVote,
|
||||
"root" => CliValidatorsSortOrder::Root,
|
||||
"skip-rate" => CliValidatorsSortOrder::SkipRate,
|
||||
"stake" => CliValidatorsSortOrder::Stake,
|
||||
"vote-account" => CliValidatorsSortOrder::VoteAccount,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::ShowValidators {
|
||||
use_lamports_unit,
|
||||
sort_order,
|
||||
reverse_sort,
|
||||
number_validators,
|
||||
},
|
||||
command: CliCommand::ShowValidators { use_lamports_unit },
|
||||
signers: vec![],
|
||||
})
|
||||
}
|
||||
@@ -805,10 +733,6 @@ pub fn process_catchup(
|
||||
}
|
||||
};
|
||||
|
||||
let start_node_slot = get_slot_while_retrying(&node_client)?;
|
||||
let start_rpc_slot = get_slot_while_retrying(rpc_client)?;
|
||||
let start_slot_distance = start_rpc_slot as i64 - start_node_slot as i64;
|
||||
let mut total_sleep_interval = 0;
|
||||
loop {
|
||||
// humbly retry; the reference node (rpc_client) could be spotty,
|
||||
// especially if pointing to api.meinnet-beta.solana.com at times
|
||||
@@ -825,37 +749,14 @@ pub fn process_catchup(
|
||||
let slot_distance = rpc_slot as i64 - node_slot as i64;
|
||||
let slots_per_second =
|
||||
(previous_slot_distance - slot_distance) as f64 / f64::from(sleep_interval);
|
||||
|
||||
let average_time_remaining = if slot_distance == 0 || total_sleep_interval == 0 {
|
||||
let time_remaining = (slot_distance as f64 / slots_per_second).round();
|
||||
let time_remaining = if !time_remaining.is_normal() || time_remaining <= 0.0 {
|
||||
"".to_string()
|
||||
} else {
|
||||
let distance_delta = start_slot_distance as i64 - slot_distance as i64;
|
||||
let average_catchup_slots_per_second =
|
||||
distance_delta as f64 / f64::from(total_sleep_interval);
|
||||
let average_time_remaining =
|
||||
(slot_distance as f64 / average_catchup_slots_per_second).round();
|
||||
if !average_time_remaining.is_normal() {
|
||||
"".to_string()
|
||||
} else if average_time_remaining < 0.0 {
|
||||
format!(
|
||||
" (AVG: {:.1} slots/second (falling))",
|
||||
average_catchup_slots_per_second
|
||||
)
|
||||
} else {
|
||||
// important not to miss next scheduled lead slots
|
||||
let total_node_slot_delta = node_slot as i64 - start_node_slot as i64;
|
||||
let average_node_slots_per_second =
|
||||
total_node_slot_delta as f64 / f64::from(total_sleep_interval);
|
||||
let expected_finish_slot = (node_slot as f64
|
||||
+ average_time_remaining as f64 * average_node_slots_per_second as f64)
|
||||
.round();
|
||||
format!(
|
||||
" (AVG: {:.1} slots/second, ETA: slot {} in {})",
|
||||
average_catchup_slots_per_second,
|
||||
expected_finish_slot,
|
||||
humantime::format_duration(Duration::from_secs_f64(average_time_remaining))
|
||||
)
|
||||
}
|
||||
format!(
|
||||
". Time remaining: {}",
|
||||
humantime::format_duration(Duration::from_secs_f64(time_remaining))
|
||||
)
|
||||
};
|
||||
|
||||
progress_bar.set_message(&format!(
|
||||
@@ -880,7 +781,7 @@ pub fn process_catchup(
|
||||
"gaining"
|
||||
},
|
||||
slots_per_second,
|
||||
average_time_remaining
|
||||
time_remaining
|
||||
)
|
||||
},
|
||||
));
|
||||
@@ -891,7 +792,6 @@ pub fn process_catchup(
|
||||
sleep(Duration::from_secs(sleep_interval as u64));
|
||||
previous_rpc_slot = rpc_slot;
|
||||
previous_slot_distance = slot_distance;
|
||||
total_sleep_interval += sleep_interval;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -921,35 +821,14 @@ pub fn process_cluster_version(rpc_client: &RpcClient, config: &CliConfig) -> Pr
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_fees(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
blockhash: Option<&Hash>,
|
||||
) -> ProcessResult {
|
||||
let fees = if let Some(recent_blockhash) = blockhash {
|
||||
let result = rpc_client.get_fee_calculator_for_blockhash_with_commitment(
|
||||
recent_blockhash,
|
||||
config.commitment,
|
||||
)?;
|
||||
if let Some(fee_calculator) = result.value {
|
||||
CliFees::some(
|
||||
result.context.slot,
|
||||
*recent_blockhash,
|
||||
fee_calculator.lamports_per_signature,
|
||||
None,
|
||||
)
|
||||
} else {
|
||||
CliFees::none()
|
||||
}
|
||||
} else {
|
||||
let result = rpc_client.get_recent_blockhash_with_commitment(config.commitment)?;
|
||||
let (recent_blockhash, fee_calculator, last_valid_slot) = result.value;
|
||||
CliFees::some(
|
||||
result.context.slot,
|
||||
recent_blockhash,
|
||||
fee_calculator.lamports_per_signature,
|
||||
Some(last_valid_slot),
|
||||
)
|
||||
pub fn process_fees(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
|
||||
let result = rpc_client.get_recent_blockhash_with_commitment(config.commitment)?;
|
||||
let (recent_blockhash, fee_calculator, last_valid_slot) = result.value;
|
||||
let fees = CliFees {
|
||||
slot: result.context.slot,
|
||||
blockhash: recent_blockhash.to_string(),
|
||||
lamports_per_signature: fee_calculator.lamports_per_signature,
|
||||
last_valid_slot,
|
||||
};
|
||||
Ok(config.output_format.formatted_string(&fees))
|
||||
}
|
||||
@@ -1017,7 +896,7 @@ pub fn process_leader_schedule(
|
||||
|
||||
pub fn process_get_block(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
_config: &CliConfig,
|
||||
slot: Option<Slot>,
|
||||
) -> ProcessResult {
|
||||
let slot = if let Some(slot) = slot {
|
||||
@@ -1026,21 +905,72 @@ pub fn process_get_block(
|
||||
rpc_client.get_slot_with_commitment(CommitmentConfig::finalized())?
|
||||
};
|
||||
|
||||
let encoded_confirmed_block = rpc_client
|
||||
.get_confirmed_block_with_config(
|
||||
slot,
|
||||
RpcConfirmedBlockConfig {
|
||||
encoding: Some(UiTransactionEncoding::Base64),
|
||||
commitment: Some(CommitmentConfig::confirmed()),
|
||||
..RpcConfirmedBlockConfig::default()
|
||||
},
|
||||
)?
|
||||
.into();
|
||||
let cli_block = CliBlock {
|
||||
encoded_confirmed_block,
|
||||
slot,
|
||||
};
|
||||
Ok(config.output_format.formatted_string(&cli_block))
|
||||
let mut block =
|
||||
rpc_client.get_confirmed_block_with_encoding(slot, UiTransactionEncoding::Base64)?;
|
||||
|
||||
println!("Slot: {}", slot);
|
||||
println!("Parent Slot: {}", block.parent_slot);
|
||||
println!("Blockhash: {}", block.blockhash);
|
||||
println!("Previous Blockhash: {}", block.previous_blockhash);
|
||||
if let Some(block_time) = block.block_time {
|
||||
println!("Block Time: {:?}", Local.timestamp(block_time, 0));
|
||||
}
|
||||
if !block.rewards.is_empty() {
|
||||
block.rewards.sort_by(|a, b| a.pubkey.cmp(&b.pubkey));
|
||||
let mut total_rewards = 0;
|
||||
println!("Rewards:",);
|
||||
println!(
|
||||
" {:<44} {:^15} {:<15} {:<20} {:>14}",
|
||||
"Address", "Type", "Amount", "New Balance", "Percent Change"
|
||||
);
|
||||
for reward in block.rewards {
|
||||
let sign = if reward.lamports < 0 { "-" } else { "" };
|
||||
|
||||
total_rewards += reward.lamports;
|
||||
println!(
|
||||
" {:<44} {:^15} {:>15} {}",
|
||||
reward.pubkey,
|
||||
if let Some(reward_type) = reward.reward_type {
|
||||
format!("{}", reward_type)
|
||||
} else {
|
||||
"-".to_string()
|
||||
},
|
||||
format!(
|
||||
"{}◎{:<14.9}",
|
||||
sign,
|
||||
lamports_to_sol(reward.lamports.abs() as u64)
|
||||
),
|
||||
if reward.post_balance == 0 {
|
||||
" - -".to_string()
|
||||
} else {
|
||||
format!(
|
||||
"◎{:<19.9} {:>13.9}%",
|
||||
lamports_to_sol(reward.post_balance),
|
||||
(reward.lamports.abs() as f64
|
||||
/ (reward.post_balance as f64 - reward.lamports as f64))
|
||||
* 100.0
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
let sign = if total_rewards < 0 { "-" } else { "" };
|
||||
println!(
|
||||
"Total Rewards: {}◎{:<12.9}",
|
||||
sign,
|
||||
lamports_to_sol(total_rewards.abs() as u64)
|
||||
);
|
||||
}
|
||||
for (index, transaction_with_meta) in block.transactions.iter().enumerate() {
|
||||
println!("Transaction {}:", index);
|
||||
println_transaction(
|
||||
&transaction_with_meta.transaction.decode().unwrap(),
|
||||
&transaction_with_meta.meta,
|
||||
" ",
|
||||
None,
|
||||
);
|
||||
}
|
||||
Ok("".to_string())
|
||||
}
|
||||
|
||||
pub fn process_get_block_time(
|
||||
@@ -1064,21 +994,7 @@ pub fn process_get_epoch(rpc_client: &RpcClient, _config: &CliConfig) -> Process
|
||||
}
|
||||
|
||||
pub fn process_get_epoch_info(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
|
||||
let epoch_info = rpc_client.get_epoch_info()?;
|
||||
let average_slot_time_ms = rpc_client
|
||||
.get_recent_performance_samples(Some(60))
|
||||
.ok()
|
||||
.and_then(|samples| {
|
||||
let (slots, secs) = samples.iter().fold((0, 0), |(slots, secs), sample| {
|
||||
(slots + sample.num_slots, secs + sample.sample_period_secs)
|
||||
});
|
||||
(secs as u64).saturating_mul(1000).checked_div(slots)
|
||||
})
|
||||
.unwrap_or(clock::DEFAULT_MS_PER_SLOT);
|
||||
let epoch_info = CliEpochInfo {
|
||||
epoch_info,
|
||||
average_slot_time_ms,
|
||||
};
|
||||
let epoch_info: CliEpochInfo = rpc_client.get_epoch_info()?.into();
|
||||
Ok(config.output_format.formatted_string(&epoch_info))
|
||||
}
|
||||
|
||||
@@ -1093,8 +1009,8 @@ pub fn process_get_slot(rpc_client: &RpcClient, _config: &CliConfig) -> ProcessR
|
||||
}
|
||||
|
||||
pub fn process_get_block_height(rpc_client: &RpcClient, _config: &CliConfig) -> ProcessResult {
|
||||
let block_height = rpc_client.get_block_height()?;
|
||||
Ok(block_height.to_string())
|
||||
let epoch_info: CliEpochInfo = rpc_client.get_epoch_info()?.into();
|
||||
Ok(epoch_info.epoch_info.block_height.to_string())
|
||||
}
|
||||
|
||||
pub fn parse_show_block_production(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||
@@ -1121,6 +1037,8 @@ pub fn process_show_block_production(
|
||||
return Err(format!("Epoch {} is in the future", epoch).into());
|
||||
}
|
||||
|
||||
let minimum_ledger_slot = rpc_client.minimum_ledger_slot()?;
|
||||
|
||||
let first_slot_in_epoch = epoch_schedule.get_first_slot_in_epoch(epoch);
|
||||
let end_slot = std::cmp::min(
|
||||
epoch_info.absolute_slot,
|
||||
@@ -1133,60 +1051,32 @@ pub fn process_show_block_production(
|
||||
first_slot_in_epoch
|
||||
};
|
||||
|
||||
if minimum_ledger_slot > end_slot {
|
||||
return Err(format!(
|
||||
"Ledger data not available for slots {} to {} (minimum ledger slot is {})",
|
||||
start_slot, end_slot, minimum_ledger_slot
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
if minimum_ledger_slot > start_slot {
|
||||
println!(
|
||||
"\n{}",
|
||||
style(format!(
|
||||
"Note: Requested start slot was {} but minimum ledger slot is {}",
|
||||
start_slot, minimum_ledger_slot
|
||||
))
|
||||
.italic(),
|
||||
);
|
||||
start_slot = minimum_ledger_slot;
|
||||
}
|
||||
|
||||
let progress_bar = new_spinner_progress_bar();
|
||||
progress_bar.set_message(&format!(
|
||||
"Fetching confirmed blocks between slots {} and {}...",
|
||||
start_slot, end_slot
|
||||
));
|
||||
|
||||
let slot_history_account = rpc_client
|
||||
.get_account_with_commitment(&sysvar::slot_history::id(), CommitmentConfig::finalized())?
|
||||
.value
|
||||
.unwrap();
|
||||
|
||||
let slot_history: SlotHistory = from_account(&slot_history_account).ok_or_else(|| {
|
||||
CliError::RpcRequestError("Failed to deserialize slot history".to_string())
|
||||
})?;
|
||||
|
||||
let (confirmed_blocks, start_slot) =
|
||||
if start_slot >= slot_history.oldest() && end_slot <= slot_history.newest() {
|
||||
// Fast, more reliable path using the SlotHistory sysvar
|
||||
|
||||
let confirmed_blocks: Vec<_> = (start_slot..=end_slot)
|
||||
.filter(|slot| slot_history.check(*slot) == slot_history::Check::Found)
|
||||
.collect();
|
||||
(confirmed_blocks, start_slot)
|
||||
} else {
|
||||
// Slow, less reliable path using `getBlocks`.
|
||||
//
|
||||
// "less reliable" because if the RPC node has holds in its ledger then the block production data will be
|
||||
// incorrect. This condition currently can't be detected over RPC
|
||||
//
|
||||
|
||||
let minimum_ledger_slot = rpc_client.minimum_ledger_slot()?;
|
||||
if minimum_ledger_slot > end_slot {
|
||||
return Err(format!(
|
||||
"Ledger data not available for slots {} to {} (minimum ledger slot is {})",
|
||||
start_slot, end_slot, minimum_ledger_slot
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
if minimum_ledger_slot > start_slot {
|
||||
progress_bar.println(format!(
|
||||
"{}",
|
||||
style(format!(
|
||||
"Note: Requested start slot was {} but minimum ledger slot is {}",
|
||||
start_slot, minimum_ledger_slot
|
||||
))
|
||||
.italic(),
|
||||
));
|
||||
start_slot = minimum_ledger_slot;
|
||||
}
|
||||
|
||||
let confirmed_blocks = rpc_client.get_confirmed_blocks(start_slot, Some(end_slot))?;
|
||||
(confirmed_blocks, start_slot)
|
||||
};
|
||||
let confirmed_blocks = rpc_client.get_confirmed_blocks(start_slot, Some(end_slot))?;
|
||||
|
||||
let start_slot_index = (start_slot - first_slot_in_epoch) as usize;
|
||||
let end_slot_index = (end_slot - first_slot_in_epoch) as usize;
|
||||
@@ -1312,8 +1202,8 @@ pub fn process_supply(
|
||||
}
|
||||
|
||||
pub fn process_total_supply(rpc_client: &RpcClient, _config: &CliConfig) -> ProcessResult {
|
||||
let supply = rpc_client.supply()?.value;
|
||||
Ok(format!("{} SOL", lamports_to_sol(supply.total)))
|
||||
let total_supply = rpc_client.total_supply()?;
|
||||
Ok(format!("{} SOL", lamports_to_sol(total_supply)))
|
||||
}
|
||||
|
||||
pub fn process_get_transaction_count(rpc_client: &RpcClient, _config: &CliConfig) -> ProcessResult {
|
||||
@@ -1449,7 +1339,9 @@ pub fn process_ping(
|
||||
|
||||
// Sleep for half a slot
|
||||
if signal_receiver
|
||||
.recv_timeout(Duration::from_millis(clock::DEFAULT_MS_PER_SLOT / 2))
|
||||
.recv_timeout(Duration::from_millis(
|
||||
500 * clock::DEFAULT_TICKS_PER_SLOT / clock::DEFAULT_TICKS_PER_SECOND,
|
||||
))
|
||||
.is_ok()
|
||||
{
|
||||
break 'mainloop;
|
||||
@@ -1654,14 +1546,40 @@ pub fn process_live_slots(config: &CliConfig) -> ProcessResult {
|
||||
pub fn process_show_gossip(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
|
||||
let cluster_nodes = rpc_client.get_cluster_nodes()?;
|
||||
|
||||
let nodes: Vec<_> = cluster_nodes
|
||||
fn format_port(addr: Option<SocketAddr>) -> String {
|
||||
addr.map(|addr| addr.port().to_string())
|
||||
.unwrap_or_else(|| "none".to_string())
|
||||
}
|
||||
|
||||
let s: Vec<_> = cluster_nodes
|
||||
.into_iter()
|
||||
.map(|node| CliGossipNode::new(node, &config.address_labels))
|
||||
.map(|node| {
|
||||
format!(
|
||||
"{:15} | {:44} | {:6} | {:5} | {:21} | {}",
|
||||
node.gossip
|
||||
.map(|addr| addr.ip().to_string())
|
||||
.unwrap_or_else(|| "none".to_string()),
|
||||
format_labeled_address(&node.pubkey, &config.address_labels),
|
||||
format_port(node.gossip),
|
||||
format_port(node.tpu),
|
||||
node.rpc
|
||||
.map(|addr| addr.to_string())
|
||||
.unwrap_or_else(|| "none".to_string()),
|
||||
node.version.unwrap_or_else(|| "unknown".to_string()),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(config
|
||||
.output_format
|
||||
.formatted_string(&CliGossipNodes(nodes)))
|
||||
Ok(format!(
|
||||
"IP Address | Node identifier \
|
||||
| Gossip | TPU | RPC Address | Version\n\
|
||||
----------------+----------------------------------------------+\
|
||||
--------+-------+-----------------------+----------------\n\
|
||||
{}\n\
|
||||
Nodes: {}",
|
||||
s.join("\n"),
|
||||
s.len(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn process_show_stakes(
|
||||
@@ -1671,16 +1589,17 @@ pub fn process_show_stakes(
|
||||
vote_account_pubkeys: Option<&[Pubkey]>,
|
||||
) -> ProcessResult {
|
||||
use crate::stake::build_stake_state;
|
||||
use solana_stake_program::stake_state::StakeState;
|
||||
|
||||
let progress_bar = new_spinner_progress_bar();
|
||||
progress_bar.set_message("Fetching stake accounts...");
|
||||
|
||||
let mut program_accounts_config = RpcProgramAccountsConfig {
|
||||
filters: None,
|
||||
account_config: RpcAccountInfoConfig {
|
||||
encoding: Some(solana_account_decoder::UiAccountEncoding::Base64),
|
||||
..RpcAccountInfoConfig::default()
|
||||
},
|
||||
..RpcProgramAccountsConfig::default()
|
||||
};
|
||||
|
||||
if let Some(vote_account_pubkeys) = vote_account_pubkeys {
|
||||
@@ -1782,36 +1701,10 @@ pub fn process_show_validators(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
use_lamports_unit: bool,
|
||||
validators_sort_order: CliValidatorsSortOrder,
|
||||
validators_reverse_sort: bool,
|
||||
number_validators: bool,
|
||||
) -> ProcessResult {
|
||||
let progress_bar = new_spinner_progress_bar();
|
||||
progress_bar.set_message("Fetching vote accounts...");
|
||||
let epoch_info = rpc_client.get_epoch_info()?;
|
||||
let vote_accounts = rpc_client.get_vote_accounts()?;
|
||||
|
||||
progress_bar.set_message("Fetching block production...");
|
||||
let skip_rate: HashMap<_, _> = rpc_client
|
||||
.get_block_production()
|
||||
.ok()
|
||||
.map(|result| {
|
||||
result
|
||||
.value
|
||||
.by_identity
|
||||
.into_iter()
|
||||
.map(|(identity, (leader_slots, blocks_produced))| {
|
||||
(
|
||||
identity,
|
||||
100. * (leader_slots.saturating_sub(blocks_produced)) as f64
|
||||
/ leader_slots as f64,
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
progress_bar.set_message("Fetching version information...");
|
||||
let mut node_version = HashMap::new();
|
||||
let unknown_version = "unknown".to_string();
|
||||
for contact_info in rpc_client.get_cluster_nodes()? {
|
||||
@@ -1823,8 +1716,6 @@ pub fn process_show_validators(
|
||||
);
|
||||
}
|
||||
|
||||
progress_bar.finish_and_clear();
|
||||
|
||||
let total_active_stake = vote_accounts
|
||||
.current
|
||||
.iter()
|
||||
@@ -1839,8 +1730,9 @@ pub fn process_show_validators(
|
||||
.sum();
|
||||
let total_current_stake = total_active_stake - total_delinquent_stake;
|
||||
|
||||
let current_validators: Vec<CliValidator> = vote_accounts
|
||||
.current
|
||||
let mut current = vote_accounts.current;
|
||||
current.sort_by(|a, b| b.activated_stake.cmp(&a.activated_stake));
|
||||
let current_validators: Vec<CliValidator> = current
|
||||
.iter()
|
||||
.map(|vote_account| {
|
||||
CliValidator::new(
|
||||
@@ -1850,23 +1742,22 @@ pub fn process_show_validators(
|
||||
.get(&vote_account.node_pubkey)
|
||||
.unwrap_or(&unknown_version)
|
||||
.clone(),
|
||||
skip_rate.get(&vote_account.node_pubkey).cloned(),
|
||||
&config.address_labels,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let delinquent_validators: Vec<CliValidator> = vote_accounts
|
||||
.delinquent
|
||||
let mut delinquent = vote_accounts.delinquent;
|
||||
delinquent.sort_by(|a, b| b.activated_stake.cmp(&a.activated_stake));
|
||||
let delinquent_validators: Vec<CliValidator> = delinquent
|
||||
.iter()
|
||||
.map(|vote_account| {
|
||||
CliValidator::new_delinquent(
|
||||
CliValidator::new(
|
||||
vote_account,
|
||||
epoch_info.epoch,
|
||||
node_version
|
||||
.get(&vote_account.node_pubkey)
|
||||
.unwrap_or(&unknown_version)
|
||||
.clone(),
|
||||
skip_rate.get(&vote_account.node_pubkey).cloned(),
|
||||
&config.address_labels,
|
||||
)
|
||||
})
|
||||
@@ -1892,13 +1783,8 @@ pub fn process_show_validators(
|
||||
total_active_stake,
|
||||
total_current_stake,
|
||||
total_delinquent_stake,
|
||||
validators: current_validators
|
||||
.into_iter()
|
||||
.chain(delinquent_validators.into_iter())
|
||||
.collect(),
|
||||
validators_sort_order,
|
||||
validators_reverse_sort,
|
||||
number_validators,
|
||||
current_validators,
|
||||
delinquent_validators,
|
||||
stake_by_version,
|
||||
use_lamports_unit,
|
||||
};
|
||||
@@ -1920,7 +1806,6 @@ pub fn process_transaction_history(
|
||||
before,
|
||||
until,
|
||||
limit: Some(limit),
|
||||
commitment: Some(CommitmentConfig::confirmed()),
|
||||
},
|
||||
)?;
|
||||
|
||||
@@ -1937,13 +1822,9 @@ pub fn process_transaction_history(
|
||||
Some(block_time) =>
|
||||
format!("timestamp={} ", unix_timestamp_to_string(block_time)),
|
||||
},
|
||||
if let Some(err) = result.err {
|
||||
format!("Failed: {:?}", err)
|
||||
} else {
|
||||
match result.confirmation_status {
|
||||
None => "Finalized".to_string(),
|
||||
Some(status) => format!("{:?}", status),
|
||||
}
|
||||
match result.err {
|
||||
None => "Confirmed".to_string(),
|
||||
Some(err) => format!("Failed: {:?}", err),
|
||||
},
|
||||
result.memo.unwrap_or_else(|| "".to_string()),
|
||||
);
|
||||
@@ -1953,13 +1834,9 @@ pub fn process_transaction_history(
|
||||
|
||||
if show_transactions {
|
||||
if let Ok(signature) = result.signature.parse::<Signature>() {
|
||||
match rpc_client.get_confirmed_transaction_with_config(
|
||||
&signature,
|
||||
RpcConfirmedTransactionConfig {
|
||||
encoding: Some(UiTransactionEncoding::Base64),
|
||||
commitment: Some(CommitmentConfig::confirmed()),
|
||||
},
|
||||
) {
|
||||
match rpc_client
|
||||
.get_confirmed_transaction(&signature, UiTransactionEncoding::Base64)
|
||||
{
|
||||
Ok(confirmed_transaction) => {
|
||||
println_transaction(
|
||||
&confirmed_transaction
|
||||
@@ -1970,7 +1847,6 @@ pub fn process_transaction_history(
|
||||
&confirmed_transaction.transaction.meta,
|
||||
" ",
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
Err(err) => println!(" Unable to get confirmed transaction details: {}", err),
|
||||
@@ -2012,47 +1888,6 @@ impl fmt::Display for CliRentCalculation {
|
||||
impl QuietDisplay for CliRentCalculation {}
|
||||
impl VerboseDisplay for CliRentCalculation {}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum RentLengthValue {
|
||||
Nonce,
|
||||
Stake,
|
||||
System,
|
||||
Vote,
|
||||
Bytes(usize),
|
||||
}
|
||||
|
||||
impl RentLengthValue {
|
||||
pub fn length(&self) -> usize {
|
||||
match self {
|
||||
Self::Nonce => NonceState::size(),
|
||||
Self::Stake => std::mem::size_of::<StakeState>(),
|
||||
Self::System => 0,
|
||||
Self::Vote => VoteState::size_of(),
|
||||
Self::Bytes(l) => *l,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[error("expected number or moniker, got \"{0}\"")]
|
||||
pub struct RentLengthValueError(pub String);
|
||||
|
||||
impl FromStr for RentLengthValue {
|
||||
type Err = RentLengthValueError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let s = s.to_ascii_lowercase();
|
||||
match s.as_str() {
|
||||
"nonce" => Ok(Self::Nonce),
|
||||
"stake" => Ok(Self::Stake),
|
||||
"system" => Ok(Self::System),
|
||||
"vote" => Ok(Self::Vote),
|
||||
_ => usize::from_str(&s)
|
||||
.map(Self::Bytes)
|
||||
.map_err(|_| RentLengthValueError(s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_calculate_rent(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
@@ -2098,7 +1933,10 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let (default_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
let default_signer = DefaultSigner::new("", &default_keypair_file);
|
||||
let default_signer = DefaultSigner {
|
||||
path: default_keypair_file,
|
||||
arg_name: String::new(),
|
||||
};
|
||||
|
||||
let test_cluster_version = test_commands
|
||||
.clone()
|
||||
@@ -2126,24 +1964,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
parse_command(&test_fees, &default_signer, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Fees { blockhash: None },
|
||||
signers: vec![],
|
||||
}
|
||||
);
|
||||
|
||||
let blockhash = Hash::new_unique();
|
||||
let test_fees = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
"fees",
|
||||
"--blockhash",
|
||||
&blockhash.to_string(),
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_fees, &default_signer, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Fees {
|
||||
blockhash: Some(blockhash)
|
||||
},
|
||||
command: CliCommand::Fees,
|
||||
signers: vec![],
|
||||
}
|
||||
);
|
||||
|
@@ -70,7 +70,7 @@ impl fmt::Display for CliFeatures {
|
||||
f,
|
||||
"{}",
|
||||
style(format!(
|
||||
"{:<44} | {:<27} | {}",
|
||||
"{:<44} {:<28} {}",
|
||||
"Feature", "Status", "Description"
|
||||
))
|
||||
.bold()
|
||||
@@ -79,7 +79,7 @@ impl fmt::Display for CliFeatures {
|
||||
for feature in &self.features {
|
||||
writeln!(
|
||||
f,
|
||||
"{:<44} | {:<27} | {}",
|
||||
"{:<44} {:<28} {}",
|
||||
feature.id,
|
||||
match feature.status {
|
||||
CliFeatureStatus::Inactive => style("inactive".to_string()).red(),
|
||||
@@ -221,7 +221,7 @@ pub fn process_feature_subcommand(
|
||||
}
|
||||
}
|
||||
|
||||
fn active_stake_by_feature_set(rpc_client: &RpcClient) -> Result<HashMap<u32, f64>, ClientError> {
|
||||
fn active_stake_by_feature_set(rpc_client: &RpcClient) -> Result<HashMap<u32, u64>, ClientError> {
|
||||
// Validator identity -> feature set
|
||||
let feature_set_map = rpc_client
|
||||
.get_cluster_nodes()?
|
||||
@@ -239,7 +239,7 @@ fn active_stake_by_feature_set(rpc_client: &RpcClient) -> Result<HashMap<u32, f6
|
||||
.sum();
|
||||
|
||||
// Sum all active stake by feature set
|
||||
let mut active_stake_by_feature_set: HashMap<u32, u64> = HashMap::new();
|
||||
let mut active_stake_by_feature_set = HashMap::new();
|
||||
for vote_account in vote_accounts.current {
|
||||
if let Some(Some(feature_set)) = feature_set_map.get(&vote_account.node_pubkey) {
|
||||
*active_stake_by_feature_set.entry(*feature_set).or_default() +=
|
||||
@@ -251,15 +251,11 @@ fn active_stake_by_feature_set(rpc_client: &RpcClient) -> Result<HashMap<u32, f6
|
||||
}
|
||||
}
|
||||
|
||||
Ok(active_stake_by_feature_set
|
||||
.into_iter()
|
||||
.map(|(feature_set, active_stake)| {
|
||||
(
|
||||
feature_set,
|
||||
active_stake as f64 * 100. / total_active_stake as f64,
|
||||
)
|
||||
})
|
||||
.collect())
|
||||
// Convert active stake to a percentage so the caller doesn't need `total_active_stake`
|
||||
for (_, val) in active_stake_by_feature_set.iter_mut() {
|
||||
*val = *val * 100 / total_active_stake;
|
||||
}
|
||||
Ok(active_stake_by_feature_set)
|
||||
}
|
||||
|
||||
// Feature activation is only allowed when 95% of the active stake is on the current feature set
|
||||
@@ -270,7 +266,7 @@ fn feature_activation_allowed(rpc_client: &RpcClient, quiet: bool) -> Result<boo
|
||||
|
||||
let feature_activation_allowed = active_stake_by_feature_set
|
||||
.get(&my_feature_set)
|
||||
.map(|percentage| *percentage >= 95.)
|
||||
.map(|percentage| *percentage >= 95)
|
||||
.unwrap_or(false);
|
||||
|
||||
if !feature_activation_allowed && !quiet {
|
||||
@@ -287,15 +283,15 @@ fn feature_activation_allowed(rpc_client: &RpcClient, quiet: bool) -> Result<boo
|
||||
}
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("Tool Feature Set: {}", my_feature_set)).bold()
|
||||
style(format!("Tool Feture Set: {}", my_feature_set)).bold()
|
||||
);
|
||||
println!("{}", style("Cluster Feature Sets and Stakes:").bold());
|
||||
for (feature_set, percentage) in active_stake_by_feature_set.iter() {
|
||||
if *feature_set == 0 {
|
||||
println!(" unknown - {:.2}%", percentage);
|
||||
println!("unknown - {}%", percentage);
|
||||
} else {
|
||||
println!(
|
||||
" {:<10} - {:.2}% {}",
|
||||
"{} - {}% {}",
|
||||
feature_set,
|
||||
percentage,
|
||||
if *feature_set == my_feature_set {
|
||||
|
@@ -1,22 +1,14 @@
|
||||
use crate::cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult};
|
||||
use clap::{App, Arg, ArgMatches, SubCommand};
|
||||
use solana_clap_utils::{
|
||||
input_parsers::{pubkeys_of, value_of},
|
||||
input_validators::is_valid_pubkey,
|
||||
keypair::*,
|
||||
};
|
||||
use solana_cli_output::{
|
||||
CliEpochRewardshMetadata, CliInflation, CliKeyedEpochReward, CliKeyedEpochRewards,
|
||||
};
|
||||
use clap::{App, ArgMatches, SubCommand};
|
||||
use solana_clap_utils::keypair::*;
|
||||
use solana_cli_output::CliInflation;
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||
use solana_sdk::{clock::Epoch, pubkey::Pubkey};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum InflationCliCommand {
|
||||
Show,
|
||||
Rewards(Vec<Pubkey>, Option<Epoch>),
|
||||
}
|
||||
|
||||
pub trait InflationSubCommands {
|
||||
@@ -25,47 +17,17 @@ pub trait InflationSubCommands {
|
||||
|
||||
impl InflationSubCommands for App<'_, '_> {
|
||||
fn inflation_subcommands(self) -> Self {
|
||||
self.subcommand(
|
||||
SubCommand::with_name("inflation")
|
||||
.about("Show inflation information")
|
||||
.subcommand(
|
||||
SubCommand::with_name("rewards")
|
||||
.about("Show inflation rewards for a set of addresses")
|
||||
.arg(pubkey!(
|
||||
Arg::with_name("addresses")
|
||||
.value_name("ADDRESS")
|
||||
.index(1)
|
||||
.multiple(true)
|
||||
.required(true),
|
||||
"Address of account to query for rewards. "
|
||||
))
|
||||
.arg(
|
||||
Arg::with_name("rewards_epoch")
|
||||
.long("rewards-epoch")
|
||||
.takes_value(true)
|
||||
.value_name("EPOCH")
|
||||
.help("Display rewards for specific epoch [default: latest epoch]"),
|
||||
),
|
||||
),
|
||||
)
|
||||
self.subcommand(SubCommand::with_name("inflation").about("Show inflation information"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_inflation_subcommand(
|
||||
matches: &ArgMatches<'_>,
|
||||
_matches: &ArgMatches<'_>,
|
||||
_default_signer: &DefaultSigner,
|
||||
_wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let command = match matches.subcommand() {
|
||||
("rewards", Some(matches)) => {
|
||||
let addresses = pubkeys_of(matches, "addresses").unwrap();
|
||||
let rewards_epoch = value_of(matches, "rewards_epoch");
|
||||
InflationCliCommand::Rewards(addresses, rewards_epoch)
|
||||
}
|
||||
_ => InflationCliCommand::Show,
|
||||
};
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::Inflation(command),
|
||||
command: CliCommand::Inflation(InflationCliCommand::Show),
|
||||
signers: vec![],
|
||||
})
|
||||
}
|
||||
@@ -75,15 +37,8 @@ pub fn process_inflation_subcommand(
|
||||
config: &CliConfig,
|
||||
inflation_subcommand: &InflationCliCommand,
|
||||
) -> ProcessResult {
|
||||
match inflation_subcommand {
|
||||
InflationCliCommand::Show => process_show(rpc_client, config),
|
||||
InflationCliCommand::Rewards(ref addresses, rewards_epoch) => {
|
||||
process_rewards(rpc_client, config, addresses, *rewards_epoch)
|
||||
}
|
||||
}
|
||||
}
|
||||
assert_eq!(*inflation_subcommand, InflationCliCommand::Show);
|
||||
|
||||
fn process_show(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
|
||||
let governor = rpc_client.get_inflation_governor()?;
|
||||
let current_rate = rpc_client.get_inflation_rate()?;
|
||||
|
||||
@@ -94,49 +49,3 @@ fn process_show(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
|
||||
|
||||
Ok(config.output_format.formatted_string(&inflation))
|
||||
}
|
||||
|
||||
fn process_rewards(
|
||||
rpc_client: &RpcClient,
|
||||
config: &CliConfig,
|
||||
addresses: &[Pubkey],
|
||||
rewards_epoch: Option<Epoch>,
|
||||
) -> ProcessResult {
|
||||
let rewards = rpc_client
|
||||
.get_inflation_reward(&addresses, rewards_epoch)
|
||||
.map_err(|err| {
|
||||
if let Some(epoch) = rewards_epoch {
|
||||
format!("Rewards not available for epoch {}", epoch)
|
||||
} else {
|
||||
format!("Rewards not available {}", err)
|
||||
}
|
||||
})?;
|
||||
let epoch_schedule = rpc_client.get_epoch_schedule()?;
|
||||
|
||||
let mut epoch_rewards: Vec<CliKeyedEpochReward> = vec![];
|
||||
let epoch_metadata = if let Some(Some(first_reward)) = rewards.iter().find(|&v| v.is_some()) {
|
||||
let (epoch_start_time, epoch_end_time) =
|
||||
crate::stake::get_epoch_boundary_timestamps(rpc_client, first_reward, &epoch_schedule)?;
|
||||
for (reward, address) in rewards.iter().zip(addresses) {
|
||||
let cli_reward = reward.as_ref().and_then(|reward| {
|
||||
crate::stake::make_cli_reward(reward, epoch_start_time, epoch_end_time)
|
||||
});
|
||||
epoch_rewards.push(CliKeyedEpochReward {
|
||||
address: address.to_string(),
|
||||
reward: cli_reward,
|
||||
});
|
||||
}
|
||||
let block_time = rpc_client.get_block_time(first_reward.effective_slot)?;
|
||||
Some(CliEpochRewardshMetadata {
|
||||
epoch: first_reward.epoch,
|
||||
effective_slot: first_reward.effective_slot,
|
||||
block_time,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let cli_rewards = CliKeyedEpochRewards {
|
||||
epoch_metadata,
|
||||
rewards: epoch_rewards,
|
||||
};
|
||||
Ok(config.output_format.formatted_string(&cli_rewards))
|
||||
}
|
||||
|
@@ -1,4 +1,3 @@
|
||||
#![allow(clippy::integer_arithmetic)]
|
||||
macro_rules! ACCOUNT_STRING {
|
||||
() => {
|
||||
r#", one of:
|
||||
@@ -26,9 +25,9 @@ pub mod cli;
|
||||
pub mod cluster_query;
|
||||
pub mod feature;
|
||||
pub mod inflation;
|
||||
pub mod memo;
|
||||
pub mod nonce;
|
||||
pub mod program;
|
||||
pub mod send_tpu;
|
||||
pub mod spend_utils;
|
||||
pub mod stake;
|
||||
pub mod test_utils;
|
||||
|
@@ -179,7 +179,10 @@ pub fn parse_args<'a>(
|
||||
&config.keypair_path,
|
||||
);
|
||||
|
||||
let default_signer = DefaultSigner::new(default_signer_arg_name, &default_signer_path);
|
||||
let default_signer = DefaultSigner {
|
||||
arg_name: default_signer_arg_name,
|
||||
path: default_signer_path.clone(),
|
||||
};
|
||||
|
||||
let CliCommandInfo {
|
||||
command,
|
||||
|
@@ -1,22 +0,0 @@
|
||||
use solana_sdk::instruction::Instruction;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use spl_memo::id;
|
||||
|
||||
pub trait WithMemo {
|
||||
fn with_memo<T: AsRef<str>>(self, memo: Option<T>) -> Self;
|
||||
}
|
||||
|
||||
impl WithMemo for Vec<Instruction> {
|
||||
fn with_memo<T: AsRef<str>>(mut self, memo: Option<T>) -> Self {
|
||||
if let Some(memo) = &memo {
|
||||
let memo = memo.as_ref();
|
||||
let memo_ix = Instruction {
|
||||
program_id: Pubkey::new(&id().to_bytes()),
|
||||
accounts: vec![],
|
||||
data: memo.as_bytes().to_vec(),
|
||||
};
|
||||
self.push(memo_ix);
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
@@ -4,7 +4,6 @@ use crate::{
|
||||
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
|
||||
ProcessResult,
|
||||
},
|
||||
memo::WithMemo,
|
||||
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
|
||||
};
|
||||
use clap::{App, Arg, ArgMatches, SubCommand};
|
||||
@@ -12,7 +11,6 @@ use solana_clap_utils::{
|
||||
input_parsers::*,
|
||||
input_validators::*,
|
||||
keypair::{DefaultSigner, SignerIndex},
|
||||
memo::MEMO_ARG,
|
||||
nonce::*,
|
||||
};
|
||||
use solana_cli_output::CliNonceAccount;
|
||||
@@ -173,7 +171,6 @@ pub fn parse_authorize_nonce_account(
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap();
|
||||
let new_authority = pubkey_of_signer(matches, "new_authority", wallet_manager)?.unwrap();
|
||||
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
|
||||
let (nonce_authority, nonce_authority_pubkey) =
|
||||
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
|
||||
|
||||
@@ -188,7 +185,6 @@ pub fn parse_authorize_nonce_account(
|
||||
command: CliCommand::AuthorizeNonceAccount {
|
||||
nonce_account,
|
||||
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
|
||||
memo,
|
||||
new_authority,
|
||||
},
|
||||
signers: signer_info.signers,
|
||||
@@ -205,7 +201,6 @@ pub fn parse_nonce_create_account(
|
||||
let seed = matches.value_of("seed").map(|s| s.to_string());
|
||||
let amount = SpendAmount::new_from_matches(matches, "amount");
|
||||
let nonce_authority = pubkey_of_signer(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
|
||||
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
|
||||
|
||||
let payer_provided = None;
|
||||
let signer_info = default_signer.generate_unique_signers(
|
||||
@@ -219,7 +214,6 @@ pub fn parse_nonce_create_account(
|
||||
nonce_account: signer_info.index_of(nonce_account_pubkey).unwrap(),
|
||||
seed,
|
||||
nonce_authority,
|
||||
memo,
|
||||
amount,
|
||||
},
|
||||
signers: signer_info.signers,
|
||||
@@ -245,7 +239,6 @@ pub fn parse_new_nonce(
|
||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||
) -> Result<CliCommandInfo, CliError> {
|
||||
let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap();
|
||||
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
|
||||
let (nonce_authority, nonce_authority_pubkey) =
|
||||
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
|
||||
|
||||
@@ -260,7 +253,6 @@ pub fn parse_new_nonce(
|
||||
command: CliCommand::NewNonce {
|
||||
nonce_account,
|
||||
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
|
||||
memo,
|
||||
},
|
||||
signers: signer_info.signers,
|
||||
})
|
||||
@@ -292,7 +284,6 @@ pub fn parse_withdraw_from_nonce_account(
|
||||
let destination_account_pubkey =
|
||||
pubkey_of_signer(matches, "destination_account_pubkey", wallet_manager)?.unwrap();
|
||||
let lamports = lamports_of_sol(matches, "amount").unwrap();
|
||||
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
|
||||
let (nonce_authority, nonce_authority_pubkey) =
|
||||
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
|
||||
|
||||
@@ -307,7 +298,6 @@ pub fn parse_withdraw_from_nonce_account(
|
||||
command: CliCommand::WithdrawFromNonceAccount {
|
||||
nonce_account,
|
||||
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
|
||||
memo,
|
||||
destination_account_pubkey,
|
||||
lamports,
|
||||
},
|
||||
@@ -340,19 +330,13 @@ pub fn process_authorize_nonce_account(
|
||||
config: &CliConfig,
|
||||
nonce_account: &Pubkey,
|
||||
nonce_authority: SignerIndex,
|
||||
memo: Option<&String>,
|
||||
new_authority: &Pubkey,
|
||||
) -> ProcessResult {
|
||||
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||
|
||||
let nonce_authority = config.signers[nonce_authority];
|
||||
let ixs = vec![authorize_nonce_account(
|
||||
nonce_account,
|
||||
&nonce_authority.pubkey(),
|
||||
new_authority,
|
||||
)]
|
||||
.with_memo(memo);
|
||||
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
|
||||
let ix = authorize_nonce_account(nonce_account, &nonce_authority.pubkey(), new_authority);
|
||||
let message = Message::new(&[ix], Some(&config.signers[0].pubkey()));
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||
|
||||
@@ -373,7 +357,6 @@ pub fn process_create_nonce_account(
|
||||
nonce_account: SignerIndex,
|
||||
seed: Option<String>,
|
||||
nonce_authority: Option<Pubkey>,
|
||||
memo: Option<&String>,
|
||||
amount: SpendAmount,
|
||||
) -> ProcessResult {
|
||||
let nonce_account_pubkey = config.signers[nonce_account].pubkey();
|
||||
@@ -400,7 +383,6 @@ pub fn process_create_nonce_account(
|
||||
&nonce_authority,
|
||||
lamports,
|
||||
)
|
||||
.with_memo(memo)
|
||||
} else {
|
||||
create_nonce_account(
|
||||
&config.signers[0].pubkey(),
|
||||
@@ -408,7 +390,6 @@ pub fn process_create_nonce_account(
|
||||
&nonce_authority,
|
||||
lamports,
|
||||
)
|
||||
.with_memo(memo)
|
||||
};
|
||||
Message::new(&ixs, Some(&config.signers[0].pubkey()))
|
||||
};
|
||||
@@ -470,7 +451,6 @@ pub fn process_new_nonce(
|
||||
config: &CliConfig,
|
||||
nonce_account: &Pubkey,
|
||||
nonce_authority: SignerIndex,
|
||||
memo: Option<&String>,
|
||||
) -> ProcessResult {
|
||||
check_unique_pubkeys(
|
||||
(&config.signers[0].pubkey(), "cli keypair".to_string()),
|
||||
@@ -486,13 +466,9 @@ pub fn process_new_nonce(
|
||||
}
|
||||
|
||||
let nonce_authority = config.signers[nonce_authority];
|
||||
let ixs = vec![advance_nonce_account(
|
||||
&nonce_account,
|
||||
&nonce_authority.pubkey(),
|
||||
)]
|
||||
.with_memo(memo);
|
||||
let ix = advance_nonce_account(&nonce_account, &nonce_authority.pubkey());
|
||||
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
|
||||
let message = Message::new(&[ix], Some(&config.signers[0].pubkey()));
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||
check_account_for_fee_with_commitment(
|
||||
@@ -541,21 +517,19 @@ pub fn process_withdraw_from_nonce_account(
|
||||
config: &CliConfig,
|
||||
nonce_account: &Pubkey,
|
||||
nonce_authority: SignerIndex,
|
||||
memo: Option<&String>,
|
||||
destination_account_pubkey: &Pubkey,
|
||||
lamports: u64,
|
||||
) -> ProcessResult {
|
||||
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||
|
||||
let nonce_authority = config.signers[nonce_authority];
|
||||
let ixs = vec![withdraw_nonce_account(
|
||||
let ix = withdraw_nonce_account(
|
||||
nonce_account,
|
||||
&nonce_authority.pubkey(),
|
||||
destination_account_pubkey,
|
||||
lamports,
|
||||
)]
|
||||
.with_memo(memo);
|
||||
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
|
||||
);
|
||||
let message = Message::new(&[ix], Some(&config.signers[0].pubkey()));
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||
check_account_for_fee_with_commitment(
|
||||
@@ -596,7 +570,10 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let (default_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
let default_signer = DefaultSigner::new("", &default_keypair_file);
|
||||
let default_signer = DefaultSigner {
|
||||
path: default_keypair_file.clone(),
|
||||
arg_name: String::new(),
|
||||
};
|
||||
let (keypair_file, mut tmp_file) = make_tmp_file();
|
||||
let nonce_account_keypair = Keypair::new();
|
||||
write_keypair(&nonce_account_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
@@ -620,7 +597,6 @@ mod tests {
|
||||
command: CliCommand::AuthorizeNonceAccount {
|
||||
nonce_account: nonce_account_pubkey,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
new_authority: Pubkey::default(),
|
||||
},
|
||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
||||
@@ -642,7 +618,6 @@ mod tests {
|
||||
command: CliCommand::AuthorizeNonceAccount {
|
||||
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
|
||||
nonce_authority: 1,
|
||||
memo: None,
|
||||
new_authority: Pubkey::default(),
|
||||
},
|
||||
signers: vec![
|
||||
@@ -666,7 +641,6 @@ mod tests {
|
||||
nonce_account: 1,
|
||||
seed: None,
|
||||
nonce_authority: None,
|
||||
memo: None,
|
||||
amount: SpendAmount::Some(50_000_000_000),
|
||||
},
|
||||
signers: vec![
|
||||
@@ -692,7 +666,6 @@ mod tests {
|
||||
nonce_account: 1,
|
||||
seed: None,
|
||||
nonce_authority: Some(nonce_authority_keypair.pubkey()),
|
||||
memo: None,
|
||||
amount: SpendAmount::Some(50_000_000_000),
|
||||
},
|
||||
signers: vec![
|
||||
@@ -728,7 +701,6 @@ mod tests {
|
||||
command: CliCommand::NewNonce {
|
||||
nonce_account: nonce_account.pubkey(),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
},
|
||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
||||
}
|
||||
@@ -749,7 +721,6 @@ mod tests {
|
||||
command: CliCommand::NewNonce {
|
||||
nonce_account: nonce_account.pubkey(),
|
||||
nonce_authority: 1,
|
||||
memo: None,
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
@@ -794,7 +765,6 @@ mod tests {
|
||||
command: CliCommand::WithdrawFromNonceAccount {
|
||||
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
destination_account_pubkey: nonce_account_pubkey,
|
||||
lamports: 42_000_000_000
|
||||
},
|
||||
@@ -823,7 +793,6 @@ mod tests {
|
||||
command: CliCommand::WithdrawFromNonceAccount {
|
||||
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
|
||||
nonce_authority: 1,
|
||||
memo: None,
|
||||
destination_account_pubkey: nonce_account_pubkey,
|
||||
lamports: 42_000_000_000
|
||||
},
|
||||
|
1170
cli/src/program.rs
1170
cli/src/program.rs
File diff suppressed because it is too large
Load Diff
29
cli/src/send_tpu.rs
Normal file
29
cli/src/send_tpu.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
use log::*;
|
||||
use solana_client::rpc_response::{RpcContactInfo, RpcLeaderSchedule};
|
||||
use std::net::{SocketAddr, UdpSocket};
|
||||
|
||||
pub fn get_leader_tpu(
|
||||
slot_index: u64,
|
||||
leader_schedule: Option<&RpcLeaderSchedule>,
|
||||
cluster_nodes: Option<&Vec<RpcContactInfo>>,
|
||||
) -> Option<SocketAddr> {
|
||||
leader_schedule?
|
||||
.iter()
|
||||
.find(|(_pubkey, slots)| slots.iter().any(|slot| *slot as u64 == slot_index))
|
||||
.and_then(|(pubkey, _)| {
|
||||
cluster_nodes?
|
||||
.iter()
|
||||
.find(|contact_info| contact_info.pubkey == *pubkey)
|
||||
.and_then(|contact_info| contact_info.tpu)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn send_transaction_tpu(
|
||||
send_socket: &UdpSocket,
|
||||
tpu_address: &SocketAddr,
|
||||
wire_transaction: &[u8],
|
||||
) {
|
||||
if let Err(err) = send_socket.send_to(wire_transaction, tpu_address) {
|
||||
warn!("Failed to send transaction to {}: {:?}", tpu_address, err);
|
||||
}
|
||||
}
|
@@ -107,22 +107,15 @@ where
|
||||
return Err(CliError::InsufficientFundsForSpendAndFee(
|
||||
lamports_to_sol(spend),
|
||||
lamports_to_sol(fee),
|
||||
*from_pubkey,
|
||||
));
|
||||
}
|
||||
} else {
|
||||
if from_balance < spend {
|
||||
return Err(CliError::InsufficientFundsForSpend(
|
||||
lamports_to_sol(spend),
|
||||
*from_pubkey,
|
||||
));
|
||||
return Err(CliError::InsufficientFundsForSpend(lamports_to_sol(spend)));
|
||||
}
|
||||
if !check_account_for_balance_with_commitment(rpc_client, fee_pubkey, fee, commitment)?
|
||||
{
|
||||
return Err(CliError::InsufficientFundsForFee(
|
||||
lamports_to_sol(fee),
|
||||
*fee_pubkey,
|
||||
));
|
||||
return Err(CliError::InsufficientFundsForFee(lamports_to_sol(fee)));
|
||||
}
|
||||
}
|
||||
Ok((message, spend))
|
||||
|
614
cli/src/stake.rs
614
cli/src/stake.rs
File diff suppressed because it is too large
Load Diff
112
cli/src/vote.rs
112
cli/src/vote.rs
@@ -4,7 +4,6 @@ use crate::{
|
||||
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
|
||||
ProcessResult,
|
||||
},
|
||||
memo::WithMemo,
|
||||
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
|
||||
};
|
||||
use clap::{value_t_or_exit, App, Arg, ArgMatches, SubCommand};
|
||||
@@ -12,7 +11,6 @@ use solana_clap_utils::{
|
||||
input_parsers::*,
|
||||
input_validators::*,
|
||||
keypair::{DefaultSigner, SignerIndex},
|
||||
memo::{memo_arg, MEMO_ARG},
|
||||
};
|
||||
use solana_cli_output::{CliEpochVotingHistory, CliLockout, CliVoteAccount};
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
@@ -81,8 +79,7 @@ impl VoteSubCommands for App<'_, '_> {
|
||||
.value_name("STRING")
|
||||
.takes_value(true)
|
||||
.help("Seed for address generation; if specified, the resulting account will be at a derived address of the VOTE ACCOUNT pubkey")
|
||||
)
|
||||
.arg(memo_arg())
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("vote-authorize-voter")
|
||||
@@ -108,8 +105,7 @@ impl VoteSubCommands for App<'_, '_> {
|
||||
.value_name("NEW_AUTHORIZED_PUBKEY")
|
||||
.required(true),
|
||||
"New authorized vote signer. "),
|
||||
)
|
||||
.arg(memo_arg())
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("vote-authorize-withdrawer")
|
||||
@@ -135,8 +131,7 @@ impl VoteSubCommands for App<'_, '_> {
|
||||
.value_name("AUTHORIZED_PUBKEY")
|
||||
.required(true),
|
||||
"New authorized withdrawer. "),
|
||||
)
|
||||
.arg(memo_arg())
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("vote-update-validator")
|
||||
@@ -166,7 +161,6 @@ impl VoteSubCommands for App<'_, '_> {
|
||||
.validator(is_valid_signer)
|
||||
.help("Authorized withdrawer keypair"),
|
||||
)
|
||||
.arg(memo_arg())
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("vote-update-commission")
|
||||
@@ -196,7 +190,6 @@ impl VoteSubCommands for App<'_, '_> {
|
||||
.validator(is_valid_signer)
|
||||
.help("Authorized withdrawer keypair"),
|
||||
)
|
||||
.arg(memo_arg())
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("vote-account")
|
||||
@@ -214,22 +207,6 @@ impl VoteSubCommands for App<'_, '_> {
|
||||
.long("lamports")
|
||||
.takes_value(false)
|
||||
.help("Display balance in lamports instead of SOL"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("with_rewards")
|
||||
.long("with-rewards")
|
||||
.takes_value(false)
|
||||
.help("Display inflation rewards"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("num_rewards_epochs")
|
||||
.long("num-rewards-epochs")
|
||||
.takes_value(true)
|
||||
.value_name("NUM")
|
||||
.validator(|s| is_within_range(s, 1, 10))
|
||||
.default_value_if("with_rewards", None, "1")
|
||||
.requires("with_rewards")
|
||||
.help("Display rewards for NUM recent epochs, max 10 [default: latest epoch only]"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
@@ -266,7 +243,6 @@ impl VoteSubCommands for App<'_, '_> {
|
||||
.validator(is_valid_signer)
|
||||
.help("Authorized withdrawer [default: cli config keypair]"),
|
||||
)
|
||||
.arg(memo_arg())
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -283,7 +259,6 @@ pub fn parse_create_vote_account(
|
||||
let commission = value_t_or_exit!(matches, "commission", u8);
|
||||
let authorized_voter = pubkey_of_signer(matches, "authorized_voter", wallet_manager)?;
|
||||
let authorized_withdrawer = pubkey_of_signer(matches, "authorized_withdrawer", wallet_manager)?;
|
||||
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
|
||||
|
||||
let payer_provided = None;
|
||||
let signer_info = default_signer.generate_unique_signers(
|
||||
@@ -300,7 +275,6 @@ pub fn parse_create_vote_account(
|
||||
authorized_voter,
|
||||
authorized_withdrawer,
|
||||
commission,
|
||||
memo,
|
||||
},
|
||||
signers: signer_info.signers,
|
||||
})
|
||||
@@ -324,14 +298,12 @@ pub fn parse_vote_authorize(
|
||||
matches,
|
||||
wallet_manager,
|
||||
)?;
|
||||
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::VoteAuthorize {
|
||||
vote_account_pubkey,
|
||||
new_authorized_pubkey,
|
||||
vote_authorize,
|
||||
memo,
|
||||
},
|
||||
signers: signer_info.signers,
|
||||
})
|
||||
@@ -355,14 +327,12 @@ pub fn parse_vote_update_validator(
|
||||
matches,
|
||||
wallet_manager,
|
||||
)?;
|
||||
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::VoteUpdateValidator {
|
||||
vote_account_pubkey,
|
||||
new_identity_account: signer_info.index_of(new_identity_pubkey).unwrap(),
|
||||
withdraw_authority: signer_info.index_of(authorized_withdrawer_pubkey).unwrap(),
|
||||
memo,
|
||||
},
|
||||
signers: signer_info.signers,
|
||||
})
|
||||
@@ -385,14 +355,12 @@ pub fn parse_vote_update_commission(
|
||||
matches,
|
||||
wallet_manager,
|
||||
)?;
|
||||
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::VoteUpdateCommission {
|
||||
vote_account_pubkey,
|
||||
commission,
|
||||
withdraw_authority: signer_info.index_of(authorized_withdrawer_pubkey).unwrap(),
|
||||
memo,
|
||||
},
|
||||
signers: signer_info.signers,
|
||||
})
|
||||
@@ -405,16 +373,10 @@ pub fn parse_vote_get_account_command(
|
||||
let vote_account_pubkey =
|
||||
pubkey_of_signer(matches, "vote_account_pubkey", wallet_manager)?.unwrap();
|
||||
let use_lamports_unit = matches.is_present("lamports");
|
||||
let with_rewards = if matches.is_present("with_rewards") {
|
||||
Some(value_of(matches, "num_rewards_epochs").unwrap())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::ShowVoteAccount {
|
||||
pubkey: vote_account_pubkey,
|
||||
use_lamports_unit,
|
||||
with_rewards,
|
||||
},
|
||||
signers: vec![],
|
||||
})
|
||||
@@ -440,7 +402,6 @@ pub fn parse_withdraw_from_vote_account(
|
||||
matches,
|
||||
wallet_manager,
|
||||
)?;
|
||||
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::WithdrawFromVoteAccount {
|
||||
@@ -448,7 +409,6 @@ pub fn parse_withdraw_from_vote_account(
|
||||
destination_account_pubkey,
|
||||
withdraw_authority: signer_info.index_of(withdraw_authority_pubkey).unwrap(),
|
||||
withdraw_amount,
|
||||
memo,
|
||||
},
|
||||
signers: signer_info.signers,
|
||||
})
|
||||
@@ -463,7 +423,6 @@ pub fn process_create_vote_account(
|
||||
authorized_voter: &Option<Pubkey>,
|
||||
authorized_withdrawer: &Option<Pubkey>,
|
||||
commission: u8,
|
||||
memo: Option<&String>,
|
||||
) -> ProcessResult {
|
||||
let vote_account = config.signers[vote_account];
|
||||
let vote_account_pubkey = vote_account.pubkey();
|
||||
@@ -506,7 +465,6 @@ pub fn process_create_vote_account(
|
||||
&vote_init,
|
||||
lamports,
|
||||
)
|
||||
.with_memo(memo)
|
||||
} else {
|
||||
vote_instruction::create_account(
|
||||
&config.signers[0].pubkey(),
|
||||
@@ -514,7 +472,6 @@ pub fn process_create_vote_account(
|
||||
&vote_init,
|
||||
lamports,
|
||||
)
|
||||
.with_memo(memo)
|
||||
};
|
||||
Message::new(&ixs, Some(&config.signers[0].pubkey()))
|
||||
};
|
||||
@@ -558,7 +515,6 @@ pub fn process_vote_authorize(
|
||||
vote_account_pubkey: &Pubkey,
|
||||
new_authorized_pubkey: &Pubkey,
|
||||
vote_authorize: VoteAuthorize,
|
||||
memo: Option<&String>,
|
||||
) -> ProcessResult {
|
||||
// If the `authorized_account` is also the fee payer, `config.signers` will only have one
|
||||
// keypair in it
|
||||
@@ -578,8 +534,7 @@ pub fn process_vote_authorize(
|
||||
&authorized.pubkey(), // current authorized
|
||||
new_authorized_pubkey, // new vote signer/withdrawer
|
||||
vote_authorize, // vote or withdraw
|
||||
)]
|
||||
.with_memo(memo);
|
||||
)];
|
||||
|
||||
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
@@ -601,7 +556,6 @@ pub fn process_vote_update_validator(
|
||||
vote_account_pubkey: &Pubkey,
|
||||
new_identity_account: SignerIndex,
|
||||
withdraw_authority: SignerIndex,
|
||||
memo: Option<&String>,
|
||||
) -> ProcessResult {
|
||||
let authorized_withdrawer = config.signers[withdraw_authority];
|
||||
let new_identity_account = config.signers[new_identity_account];
|
||||
@@ -615,8 +569,7 @@ pub fn process_vote_update_validator(
|
||||
vote_account_pubkey,
|
||||
&authorized_withdrawer.pubkey(),
|
||||
&new_identity_pubkey,
|
||||
)]
|
||||
.with_memo(memo);
|
||||
)];
|
||||
|
||||
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
@@ -638,7 +591,6 @@ pub fn process_vote_update_commission(
|
||||
vote_account_pubkey: &Pubkey,
|
||||
commission: u8,
|
||||
withdraw_authority: SignerIndex,
|
||||
memo: Option<&String>,
|
||||
) -> ProcessResult {
|
||||
let authorized_withdrawer = config.signers[withdraw_authority];
|
||||
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||
@@ -646,8 +598,7 @@ pub fn process_vote_update_commission(
|
||||
vote_account_pubkey,
|
||||
&authorized_withdrawer.pubkey(),
|
||||
commission,
|
||||
)]
|
||||
.with_memo(memo);
|
||||
)];
|
||||
|
||||
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
|
||||
let mut tx = Transaction::new_unsigned(message);
|
||||
@@ -696,7 +647,6 @@ pub fn process_show_vote_account(
|
||||
config: &CliConfig,
|
||||
vote_account_address: &Pubkey,
|
||||
use_lamports_unit: bool,
|
||||
with_rewards: Option<usize>,
|
||||
) -> ProcessResult {
|
||||
let (vote_account, vote_state) =
|
||||
get_vote_account(rpc_client, vote_account_address, config.commitment)?;
|
||||
@@ -722,16 +672,14 @@ pub fn process_show_vote_account(
|
||||
}
|
||||
}
|
||||
|
||||
let epoch_rewards =
|
||||
with_rewards.and_then(|num_epochs| {
|
||||
match crate::stake::fetch_epoch_rewards(rpc_client, vote_account_address, num_epochs) {
|
||||
Ok(rewards) => Some(rewards),
|
||||
Err(error) => {
|
||||
eprintln!("Failed to fetch epoch rewards: {:?}", error);
|
||||
None
|
||||
}
|
||||
}
|
||||
});
|
||||
let epoch_rewards = match crate::stake::fetch_epoch_rewards(rpc_client, vote_account_address, 1)
|
||||
{
|
||||
Ok(rewards) => Some(rewards),
|
||||
Err(error) => {
|
||||
eprintln!("Failed to fetch epoch rewards: {:?}", error);
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let vote_account_data = CliVoteAccount {
|
||||
account_balance: vote_account.lamports,
|
||||
@@ -758,7 +706,6 @@ pub fn process_withdraw_from_vote_account(
|
||||
withdraw_authority: SignerIndex,
|
||||
withdraw_amount: SpendAmount,
|
||||
destination_account_pubkey: &Pubkey,
|
||||
memo: Option<&String>,
|
||||
) -> ProcessResult {
|
||||
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||
let withdraw_authority = config.signers[withdraw_authority];
|
||||
@@ -779,15 +726,14 @@ pub fn process_withdraw_from_vote_account(
|
||||
}
|
||||
};
|
||||
|
||||
let ixs = vec![withdraw(
|
||||
let ix = withdraw(
|
||||
vote_account_pubkey,
|
||||
&withdraw_authority.pubkey(),
|
||||
lamports,
|
||||
destination_account_pubkey,
|
||||
)]
|
||||
.with_memo(memo);
|
||||
);
|
||||
|
||||
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
|
||||
let message = Message::new(&[ix], Some(&config.signers[0].pubkey()));
|
||||
let mut transaction = Transaction::new_unsigned(message);
|
||||
transaction.try_sign(&config.signers, recent_blockhash)?;
|
||||
check_account_for_fee_with_commitment(
|
||||
@@ -826,7 +772,10 @@ mod tests {
|
||||
let default_keypair = Keypair::new();
|
||||
let (default_keypair_file, mut tmp_file) = make_tmp_file();
|
||||
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
|
||||
let default_signer = DefaultSigner::new("", &default_keypair_file);
|
||||
let default_signer = DefaultSigner {
|
||||
path: default_keypair_file.clone(),
|
||||
arg_name: String::new(),
|
||||
};
|
||||
|
||||
let test_authorize_voter = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
@@ -841,8 +790,7 @@ mod tests {
|
||||
command: CliCommand::VoteAuthorize {
|
||||
vote_account_pubkey: pubkey,
|
||||
new_authorized_pubkey: pubkey2,
|
||||
vote_authorize: VoteAuthorize::Voter,
|
||||
memo: None,
|
||||
vote_authorize: VoteAuthorize::Voter
|
||||
},
|
||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
||||
}
|
||||
@@ -865,8 +813,7 @@ mod tests {
|
||||
command: CliCommand::VoteAuthorize {
|
||||
vote_account_pubkey: pubkey,
|
||||
new_authorized_pubkey: pubkey2,
|
||||
vote_authorize: VoteAuthorize::Voter,
|
||||
memo: None,
|
||||
vote_authorize: VoteAuthorize::Voter
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
@@ -900,7 +847,6 @@ mod tests {
|
||||
authorized_voter: None,
|
||||
authorized_withdrawer: None,
|
||||
commission: 10,
|
||||
memo: None,
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
@@ -930,7 +876,6 @@ mod tests {
|
||||
authorized_voter: None,
|
||||
authorized_withdrawer: None,
|
||||
commission: 100,
|
||||
memo: None,
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
@@ -963,8 +908,7 @@ mod tests {
|
||||
identity_account: 2,
|
||||
authorized_voter: Some(authed),
|
||||
authorized_withdrawer: None,
|
||||
commission: 100,
|
||||
memo: None,
|
||||
commission: 100
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
@@ -995,8 +939,7 @@ mod tests {
|
||||
identity_account: 2,
|
||||
authorized_voter: None,
|
||||
authorized_withdrawer: Some(authed),
|
||||
commission: 100,
|
||||
memo: None,
|
||||
commission: 100
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
@@ -1020,7 +963,6 @@ mod tests {
|
||||
vote_account_pubkey: pubkey,
|
||||
new_identity_account: 2,
|
||||
withdraw_authority: 1,
|
||||
memo: None,
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
@@ -1044,7 +986,6 @@ mod tests {
|
||||
vote_account_pubkey: pubkey,
|
||||
commission: 42,
|
||||
withdraw_authority: 1,
|
||||
memo: None,
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
@@ -1069,7 +1010,6 @@ mod tests {
|
||||
destination_account_pubkey: pubkey,
|
||||
withdraw_authority: 0,
|
||||
withdraw_amount: SpendAmount::Some(42_000_000_000),
|
||||
memo: None,
|
||||
},
|
||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
||||
}
|
||||
@@ -1091,7 +1031,6 @@ mod tests {
|
||||
destination_account_pubkey: pubkey,
|
||||
withdraw_authority: 0,
|
||||
withdraw_amount: SpendAmount::All,
|
||||
memo: None,
|
||||
},
|
||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
||||
}
|
||||
@@ -1118,7 +1057,6 @@ mod tests {
|
||||
destination_account_pubkey: pubkey,
|
||||
withdraw_authority: 1,
|
||||
withdraw_amount: SpendAmount::Some(42_000_000_000),
|
||||
memo: None,
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
|
@@ -22,38 +22,44 @@ use solana_sdk::{
|
||||
#[test]
|
||||
fn test_nonce() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
full_battery_tests(test_validator, None, false);
|
||||
full_battery_tests(
|
||||
TestValidator::with_no_fees(mint_keypair.pubkey()),
|
||||
mint_keypair,
|
||||
None,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nonce_with_seed() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
full_battery_tests(test_validator, Some(String::from("seed")), false);
|
||||
full_battery_tests(
|
||||
TestValidator::with_no_fees(mint_keypair.pubkey()),
|
||||
mint_keypair,
|
||||
Some(String::from("seed")),
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nonce_with_authority() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
full_battery_tests(test_validator, None, true);
|
||||
full_battery_tests(
|
||||
TestValidator::with_no_fees(mint_keypair.pubkey()),
|
||||
mint_keypair,
|
||||
None,
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
fn full_battery_tests(
|
||||
test_validator: TestValidator,
|
||||
mint_keypair: Keypair,
|
||||
seed: Option<String>,
|
||||
use_nonce_authority: bool,
|
||||
) {
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
let json_rpc_url = test_validator.rpc_url();
|
||||
@@ -65,9 +71,10 @@ fn full_battery_tests(
|
||||
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&config_payer,
|
||||
&faucet_addr,
|
||||
&config_payer.signers[0].pubkey(),
|
||||
2000,
|
||||
&config_payer,
|
||||
)
|
||||
.unwrap();
|
||||
check_recent_balance(2000, &rpc_client, &config_payer.signers[0].pubkey());
|
||||
@@ -101,7 +108,6 @@ fn full_battery_tests(
|
||||
nonce_account: 1,
|
||||
seed,
|
||||
nonce_authority: optional_authority,
|
||||
memo: None,
|
||||
amount: SpendAmount::Some(1000),
|
||||
};
|
||||
|
||||
@@ -135,7 +141,6 @@ fn full_battery_tests(
|
||||
config_payer.command = CliCommand::NewNonce {
|
||||
nonce_account,
|
||||
nonce_authority: index,
|
||||
memo: None,
|
||||
};
|
||||
process_command(&config_payer).unwrap();
|
||||
|
||||
@@ -153,7 +158,6 @@ fn full_battery_tests(
|
||||
config_payer.command = CliCommand::WithdrawFromNonceAccount {
|
||||
nonce_account,
|
||||
nonce_authority: index,
|
||||
memo: None,
|
||||
destination_account_pubkey: payee_pubkey,
|
||||
lamports: 100,
|
||||
};
|
||||
@@ -174,7 +178,6 @@ fn full_battery_tests(
|
||||
config_payer.command = CliCommand::AuthorizeNonceAccount {
|
||||
nonce_account,
|
||||
nonce_authority: index,
|
||||
memo: None,
|
||||
new_authority: new_authority.pubkey(),
|
||||
};
|
||||
process_command(&config_payer).unwrap();
|
||||
@@ -183,7 +186,6 @@ fn full_battery_tests(
|
||||
config_payer.command = CliCommand::NewNonce {
|
||||
nonce_account,
|
||||
nonce_authority: index,
|
||||
memo: None,
|
||||
};
|
||||
process_command(&config_payer).unwrap_err();
|
||||
|
||||
@@ -192,7 +194,6 @@ fn full_battery_tests(
|
||||
config_payer.command = CliCommand::NewNonce {
|
||||
nonce_account,
|
||||
nonce_authority: 1,
|
||||
memo: None,
|
||||
};
|
||||
process_command(&config_payer).unwrap();
|
||||
|
||||
@@ -200,7 +201,6 @@ fn full_battery_tests(
|
||||
config_payer.command = CliCommand::WithdrawFromNonceAccount {
|
||||
nonce_account,
|
||||
nonce_authority: 1,
|
||||
memo: None,
|
||||
destination_account_pubkey: payee_pubkey,
|
||||
lamports: 100,
|
||||
};
|
||||
@@ -214,29 +214,32 @@ fn full_battery_tests(
|
||||
fn test_create_account_with_seed() {
|
||||
solana_logger::setup();
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
|
||||
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
|
||||
|
||||
let offline_nonce_authority_signer = keypair_from_seed(&[1u8; 32]).unwrap();
|
||||
let online_nonce_creator_signer = keypair_from_seed(&[2u8; 32]).unwrap();
|
||||
let to_address = Pubkey::new(&[3u8; 32]);
|
||||
let config = CliConfig::recent_for_tests();
|
||||
|
||||
// Setup accounts
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&CliConfig::recent_for_tests(),
|
||||
&faucet_addr,
|
||||
&offline_nonce_authority_signer.pubkey(),
|
||||
42,
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&CliConfig::recent_for_tests(),
|
||||
&faucet_addr,
|
||||
&online_nonce_creator_signer.pubkey(),
|
||||
4242,
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
check_recent_balance(42, &rpc_client, &offline_nonce_authority_signer.pubkey());
|
||||
@@ -260,7 +263,6 @@ fn test_create_account_with_seed() {
|
||||
nonce_account: 0,
|
||||
seed: Some(seed),
|
||||
nonce_authority: Some(authority_pubkey),
|
||||
memo: None,
|
||||
amount: SpendAmount::Some(241),
|
||||
};
|
||||
process_command(&creator_config).unwrap();
|
||||
@@ -291,16 +293,11 @@ fn test_create_account_with_seed() {
|
||||
to: to_address,
|
||||
from: 0,
|
||||
sign_only: true,
|
||||
dump_transaction_message: true,
|
||||
allow_unfunded_recipient: true,
|
||||
no_wait: false,
|
||||
blockhash_query: BlockhashQuery::None(nonce_hash),
|
||||
nonce_account: Some(nonce_address),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
};
|
||||
authority_config.output_format = OutputFormat::JsonCompact;
|
||||
let sign_only_reply = process_command(&authority_config).unwrap();
|
||||
@@ -317,8 +314,6 @@ fn test_create_account_with_seed() {
|
||||
to: to_address,
|
||||
from: 0,
|
||||
sign_only: false,
|
||||
dump_transaction_message: true,
|
||||
allow_unfunded_recipient: true,
|
||||
no_wait: false,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(
|
||||
blockhash_query::Source::NonceAccount(nonce_address),
|
||||
@@ -326,10 +321,7 @@ fn test_create_account_with_seed() {
|
||||
),
|
||||
nonce_account: Some(nonce_address),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
};
|
||||
process_command(&submit_config).unwrap();
|
||||
check_recent_balance(241, &rpc_client, &nonce_address);
|
||||
|
@@ -28,9 +28,8 @@ fn test_cli_program_deploy_non_upgradeable() {
|
||||
pathbuf.set_extension("so");
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -47,6 +46,8 @@ fn test_cli_program_deploy_non_upgradeable() {
|
||||
config.json_rpc_url = test_validator.rpc_url();
|
||||
config.signers = vec![&keypair];
|
||||
config.command = CliCommand::Airdrop {
|
||||
faucet_host: None,
|
||||
faucet_port: faucet_addr.port(),
|
||||
pubkey: None,
|
||||
lamports: 4 * minimum_balance_for_rent_exemption, // min balance for rent exemption for three programs + leftover for tx processing
|
||||
};
|
||||
@@ -58,7 +59,6 @@ fn test_cli_program_deploy_non_upgradeable() {
|
||||
use_deprecated_loader: false,
|
||||
allow_excessive_balance: false,
|
||||
};
|
||||
config.output_format = OutputFormat::JsonCompact;
|
||||
let response = process_command(&config);
|
||||
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
|
||||
let program_id_str = json
|
||||
@@ -103,6 +103,8 @@ fn test_cli_program_deploy_non_upgradeable() {
|
||||
let custom_address_keypair = Keypair::new();
|
||||
config.signers = vec![&custom_address_keypair];
|
||||
config.command = CliCommand::Airdrop {
|
||||
faucet_host: None,
|
||||
faucet_port: faucet_addr.port(),
|
||||
pubkey: None,
|
||||
lamports: 2 * minimum_balance_for_rent_exemption, // Anything over minimum_balance_for_rent_exemption should trigger err
|
||||
};
|
||||
@@ -144,9 +146,8 @@ fn test_cli_program_deploy_no_authority() {
|
||||
pathbuf.set_extension("so");
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -169,6 +170,8 @@ fn test_cli_program_deploy_no_authority() {
|
||||
let keypair = Keypair::new();
|
||||
config.json_rpc_url = test_validator.rpc_url();
|
||||
config.command = CliCommand::Airdrop {
|
||||
faucet_host: None,
|
||||
faucet_port: faucet_addr.port(),
|
||||
pubkey: None,
|
||||
lamports: 100 * minimum_balance_for_programdata + minimum_balance_for_program,
|
||||
};
|
||||
@@ -188,7 +191,6 @@ fn test_cli_program_deploy_no_authority() {
|
||||
is_final: true,
|
||||
max_len: None,
|
||||
});
|
||||
config.output_format = OutputFormat::JsonCompact;
|
||||
let response = process_command(&config);
|
||||
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
|
||||
let program_id_str = json
|
||||
@@ -227,9 +229,8 @@ fn test_cli_program_deploy_with_authority() {
|
||||
pathbuf.set_extension("so");
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -253,6 +254,8 @@ fn test_cli_program_deploy_with_authority() {
|
||||
config.json_rpc_url = test_validator.rpc_url();
|
||||
config.signers = vec![&keypair];
|
||||
config.command = CliCommand::Airdrop {
|
||||
faucet_host: None,
|
||||
faucet_port: faucet_addr.port(),
|
||||
pubkey: None,
|
||||
lamports: 100 * minimum_balance_for_programdata + minimum_balance_for_program,
|
||||
};
|
||||
@@ -437,9 +440,6 @@ fn test_cli_program_deploy_with_authority() {
|
||||
config.signers = vec![&keypair];
|
||||
config.command = CliCommand::Program(ProgramCliCommand::Show {
|
||||
account_pubkey: Some(program_pubkey),
|
||||
authority_pubkey: keypair.pubkey(),
|
||||
all: false,
|
||||
use_lamports_unit: false,
|
||||
});
|
||||
let response = process_command(&config);
|
||||
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
|
||||
@@ -528,9 +528,6 @@ fn test_cli_program_deploy_with_authority() {
|
||||
config.signers = vec![&keypair];
|
||||
config.command = CliCommand::Program(ProgramCliCommand::Show {
|
||||
account_pubkey: Some(program_pubkey),
|
||||
authority_pubkey: keypair.pubkey(),
|
||||
all: false,
|
||||
use_lamports_unit: false,
|
||||
});
|
||||
let response = process_command(&config);
|
||||
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
|
||||
@@ -555,9 +552,8 @@ fn test_cli_program_write_buffer() {
|
||||
pathbuf.set_extension("so");
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -573,7 +569,7 @@ fn test_cli_program_write_buffer() {
|
||||
.unwrap();
|
||||
let minimum_balance_for_buffer_default = rpc_client
|
||||
.get_minimum_balance_for_rent_exemption(
|
||||
UpgradeableLoaderState::programdata_len(max_len).unwrap(),
|
||||
UpgradeableLoaderState::programdata_len(max_len * 2).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -582,6 +578,8 @@ fn test_cli_program_write_buffer() {
|
||||
config.json_rpc_url = test_validator.rpc_url();
|
||||
config.signers = vec![&keypair];
|
||||
config.command = CliCommand::Airdrop {
|
||||
faucet_host: None,
|
||||
faucet_port: faucet_addr.port(),
|
||||
pubkey: None,
|
||||
lamports: 100 * minimum_balance_for_buffer,
|
||||
};
|
||||
@@ -657,12 +655,9 @@ fn test_cli_program_write_buffer() {
|
||||
);
|
||||
|
||||
// Get buffer authority
|
||||
config.signers = vec![];
|
||||
config.signers = vec![&keypair];
|
||||
config.command = CliCommand::Program(ProgramCliCommand::Show {
|
||||
account_pubkey: Some(buffer_keypair.pubkey()),
|
||||
authority_pubkey: keypair.pubkey(),
|
||||
all: false,
|
||||
use_lamports_unit: false,
|
||||
});
|
||||
let response = process_command(&config);
|
||||
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
|
||||
@@ -750,12 +745,9 @@ fn test_cli_program_write_buffer() {
|
||||
);
|
||||
|
||||
// Get buffer authority
|
||||
config.signers = vec![];
|
||||
config.signers = vec![&keypair];
|
||||
config.command = CliCommand::Program(ProgramCliCommand::Show {
|
||||
account_pubkey: Some(buffer_pubkey),
|
||||
authority_pubkey: keypair.pubkey(),
|
||||
all: false,
|
||||
use_lamports_unit: false,
|
||||
});
|
||||
let response = process_command(&config);
|
||||
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
|
||||
@@ -770,60 +762,6 @@ fn test_cli_program_write_buffer() {
|
||||
authority_keypair.pubkey(),
|
||||
Pubkey::from_str(&authority_pubkey_str).unwrap()
|
||||
);
|
||||
|
||||
// Close buffer
|
||||
let close_account = rpc_client.get_account(&buffer_pubkey).unwrap();
|
||||
assert_eq!(minimum_balance_for_buffer, close_account.lamports);
|
||||
let recipient_pubkey = Pubkey::new_unique();
|
||||
config.signers = vec![&keypair, &authority_keypair];
|
||||
config.command = CliCommand::Program(ProgramCliCommand::Close {
|
||||
account_pubkey: Some(buffer_pubkey),
|
||||
recipient_pubkey,
|
||||
authority_index: 1,
|
||||
use_lamports_unit: false,
|
||||
});
|
||||
process_command(&config).unwrap();
|
||||
rpc_client.get_account(&buffer_pubkey).unwrap_err();
|
||||
let recipient_account = rpc_client.get_account(&recipient_pubkey).unwrap();
|
||||
assert_eq!(minimum_balance_for_buffer, recipient_account.lamports);
|
||||
|
||||
// Write a buffer with default params
|
||||
config.signers = vec![&keypair];
|
||||
config.command = CliCommand::Program(ProgramCliCommand::WriteBuffer {
|
||||
program_location: pathbuf.to_str().unwrap().to_string(),
|
||||
buffer_signer_index: None,
|
||||
buffer_pubkey: None,
|
||||
buffer_authority_signer_index: None,
|
||||
max_len: None,
|
||||
});
|
||||
config.output_format = OutputFormat::JsonCompact;
|
||||
let response = process_command(&config);
|
||||
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
|
||||
let buffer_pubkey_str = json
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.get("buffer")
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.unwrap();
|
||||
let new_buffer_pubkey = Pubkey::from_str(&buffer_pubkey_str).unwrap();
|
||||
|
||||
// Close buffers and deposit default keypair
|
||||
let pre_lamports = rpc_client.get_account(&keypair.pubkey()).unwrap().lamports;
|
||||
config.signers = vec![&keypair];
|
||||
config.command = CliCommand::Program(ProgramCliCommand::Close {
|
||||
account_pubkey: Some(new_buffer_pubkey),
|
||||
recipient_pubkey: keypair.pubkey(),
|
||||
authority_index: 0,
|
||||
use_lamports_unit: false,
|
||||
});
|
||||
process_command(&config).unwrap();
|
||||
rpc_client.get_account(&new_buffer_pubkey).unwrap_err();
|
||||
let recipient_account = rpc_client.get_account(&keypair.pubkey()).unwrap();
|
||||
assert_eq!(
|
||||
pre_lamports + minimum_balance_for_buffer,
|
||||
recipient_account.lamports
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -837,9 +775,8 @@ fn test_cli_program_set_buffer_authority() {
|
||||
pathbuf.set_extension("so");
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -859,6 +796,8 @@ fn test_cli_program_set_buffer_authority() {
|
||||
config.json_rpc_url = test_validator.rpc_url();
|
||||
config.signers = vec![&keypair];
|
||||
config.command = CliCommand::Airdrop {
|
||||
faucet_host: None,
|
||||
faucet_port: faucet_addr.port(),
|
||||
pubkey: None,
|
||||
lamports: 100 * minimum_balance_for_buffer,
|
||||
};
|
||||
@@ -890,7 +829,6 @@ fn test_cli_program_set_buffer_authority() {
|
||||
buffer_authority_index: Some(0),
|
||||
new_buffer_authority: new_buffer_authority.pubkey(),
|
||||
});
|
||||
config.output_format = OutputFormat::JsonCompact;
|
||||
let response = process_command(&config);
|
||||
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
|
||||
let new_buffer_authority_str = json
|
||||
@@ -950,9 +888,8 @@ fn test_cli_program_mismatch_buffer_authority() {
|
||||
pathbuf.set_extension("so");
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -972,6 +909,8 @@ fn test_cli_program_mismatch_buffer_authority() {
|
||||
config.json_rpc_url = test_validator.rpc_url();
|
||||
config.signers = vec![&keypair];
|
||||
config.command = CliCommand::Airdrop {
|
||||
faucet_host: None,
|
||||
faucet_port: faucet_addr.port(),
|
||||
pubkey: None,
|
||||
lamports: 100 * minimum_balance_for_buffer,
|
||||
};
|
||||
@@ -1039,9 +978,8 @@ fn test_cli_program_show() {
|
||||
pathbuf.set_extension("so");
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -1064,6 +1002,8 @@ fn test_cli_program_show() {
|
||||
// Airdrop
|
||||
config.signers = vec![&keypair];
|
||||
config.command = CliCommand::Airdrop {
|
||||
faucet_host: None,
|
||||
faucet_port: faucet_addr.port(),
|
||||
pubkey: None,
|
||||
lamports: 100 * minimum_balance_for_buffer,
|
||||
};
|
||||
@@ -1086,9 +1026,6 @@ fn test_cli_program_show() {
|
||||
config.signers = vec![&keypair];
|
||||
config.command = CliCommand::Program(ProgramCliCommand::Show {
|
||||
account_pubkey: Some(buffer_keypair.pubkey()),
|
||||
authority_pubkey: keypair.pubkey(),
|
||||
all: false,
|
||||
use_lamports_unit: false,
|
||||
});
|
||||
let response = process_command(&config);
|
||||
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
|
||||
@@ -1138,17 +1075,13 @@ fn test_cli_program_show() {
|
||||
max_len: Some(max_len),
|
||||
});
|
||||
config.output_format = OutputFormat::JsonCompact;
|
||||
let min_slot = rpc_client.get_slot().unwrap();
|
||||
process_command(&config).unwrap();
|
||||
let max_slot = rpc_client.get_slot().unwrap();
|
||||
let slot = rpc_client.get_slot().unwrap();
|
||||
|
||||
// Verify show
|
||||
config.signers = vec![&keypair];
|
||||
config.command = CliCommand::Program(ProgramCliCommand::Show {
|
||||
account_pubkey: Some(program_keypair.pubkey()),
|
||||
authority_pubkey: keypair.pubkey(),
|
||||
all: false,
|
||||
use_lamports_unit: false,
|
||||
});
|
||||
let response = process_command(&config);
|
||||
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
|
||||
@@ -1196,8 +1129,7 @@ fn test_cli_program_show() {
|
||||
.unwrap()
|
||||
.as_u64()
|
||||
.unwrap();
|
||||
assert!(deployed_slot >= min_slot);
|
||||
assert!(deployed_slot <= max_slot);
|
||||
assert_eq!(slot, deployed_slot);
|
||||
let data_len = json
|
||||
.as_object()
|
||||
.unwrap()
|
||||
@@ -1219,9 +1151,8 @@ fn test_cli_program_dump() {
|
||||
pathbuf.set_extension("so");
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -1244,6 +1175,8 @@ fn test_cli_program_dump() {
|
||||
// Airdrop
|
||||
config.signers = vec![&keypair];
|
||||
config.command = CliCommand::Airdrop {
|
||||
faucet_host: None,
|
||||
faucet_port: faucet_addr.port(),
|
||||
pubkey: None,
|
||||
lamports: 100 * minimum_balance_for_buffer,
|
||||
};
|
||||
|
@@ -10,13 +10,15 @@ use solana_sdk::{
|
||||
#[test]
|
||||
fn test_cli_request_airdrop() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let mut bob_config = CliConfig::recent_for_tests();
|
||||
bob_config.json_rpc_url = test_validator.rpc_url();
|
||||
bob_config.command = CliCommand::Airdrop {
|
||||
faucet_host: None,
|
||||
faucet_port: faucet_addr.port(),
|
||||
pubkey: None,
|
||||
lamports: 50,
|
||||
};
|
||||
|
@@ -26,9 +26,8 @@ use solana_stake_program::{
|
||||
#[test]
|
||||
fn test_stake_delegation_force() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -38,8 +37,14 @@ fn test_stake_delegation_force() {
|
||||
config.json_rpc_url = test_validator.rpc_url();
|
||||
config.signers = vec![&default_signer];
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &config.signers[0].pubkey(), 100_000)
|
||||
.unwrap();
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&faucet_addr,
|
||||
&config.signers[0].pubkey(),
|
||||
100_000,
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Create vote account
|
||||
let vote_keypair = Keypair::new();
|
||||
@@ -51,7 +56,6 @@ fn test_stake_delegation_force() {
|
||||
authorized_voter: None,
|
||||
authorized_withdrawer: None,
|
||||
commission: 0,
|
||||
memo: None,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
|
||||
@@ -66,11 +70,9 @@ fn test_stake_delegation_force() {
|
||||
lockup: Lockup::default(),
|
||||
amount: SpendAmount::Some(50_000),
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
from: 0,
|
||||
};
|
||||
@@ -84,11 +86,9 @@ fn test_stake_delegation_force() {
|
||||
stake_authority: 0,
|
||||
force: false,
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap_err();
|
||||
@@ -100,11 +100,9 @@ fn test_stake_delegation_force() {
|
||||
stake_authority: 0,
|
||||
force: true,
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -115,9 +113,8 @@ fn test_seed_stake_delegation_and_deactivation() {
|
||||
solana_logger::setup();
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -129,9 +126,10 @@ fn test_seed_stake_delegation_and_deactivation() {
|
||||
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&config_validator,
|
||||
&faucet_addr,
|
||||
&config_validator.signers[0].pubkey(),
|
||||
100_000,
|
||||
&config_validator,
|
||||
)
|
||||
.unwrap();
|
||||
check_recent_balance(100_000, &rpc_client, &config_validator.signers[0].pubkey());
|
||||
@@ -153,11 +151,9 @@ fn test_seed_stake_delegation_and_deactivation() {
|
||||
lockup: Lockup::default(),
|
||||
amount: SpendAmount::Some(50_000),
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
from: 0,
|
||||
};
|
||||
@@ -170,11 +166,9 @@ fn test_seed_stake_delegation_and_deactivation() {
|
||||
stake_authority: 0,
|
||||
force: true,
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config_validator).unwrap();
|
||||
@@ -184,12 +178,9 @@ fn test_seed_stake_delegation_and_deactivation() {
|
||||
stake_account_pubkey: stake_address,
|
||||
stake_authority: 0,
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
seed: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config_validator).unwrap();
|
||||
@@ -200,9 +191,8 @@ fn test_stake_delegation_and_deactivation() {
|
||||
solana_logger::setup();
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -216,9 +206,10 @@ fn test_stake_delegation_and_deactivation() {
|
||||
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&config_validator,
|
||||
&faucet_addr,
|
||||
&config_validator.signers[0].pubkey(),
|
||||
100_000,
|
||||
&config_validator,
|
||||
)
|
||||
.unwrap();
|
||||
check_recent_balance(100_000, &rpc_client, &config_validator.signers[0].pubkey());
|
||||
@@ -233,11 +224,9 @@ fn test_stake_delegation_and_deactivation() {
|
||||
lockup: Lockup::default(),
|
||||
amount: SpendAmount::Some(50_000),
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
from: 0,
|
||||
};
|
||||
@@ -251,11 +240,9 @@ fn test_stake_delegation_and_deactivation() {
|
||||
stake_authority: 0,
|
||||
force: true,
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config_validator).unwrap();
|
||||
@@ -265,12 +252,9 @@ fn test_stake_delegation_and_deactivation() {
|
||||
stake_account_pubkey: stake_keypair.pubkey(),
|
||||
stake_authority: 0,
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
seed: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config_validator).unwrap();
|
||||
@@ -281,9 +265,8 @@ fn test_offline_stake_delegation_and_deactivation() {
|
||||
solana_logger::setup();
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -303,23 +286,25 @@ fn test_offline_stake_delegation_and_deactivation() {
|
||||
config_offline.command = CliCommand::ClusterVersion;
|
||||
let offline_keypair = Keypair::new();
|
||||
config_offline.signers = vec![&offline_keypair];
|
||||
// Verify that we cannot reach the cluster
|
||||
// Verfiy that we cannot reach the cluster
|
||||
process_command(&config_offline).unwrap_err();
|
||||
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&config_validator,
|
||||
&faucet_addr,
|
||||
&config_validator.signers[0].pubkey(),
|
||||
100_000,
|
||||
&config_offline,
|
||||
)
|
||||
.unwrap();
|
||||
check_recent_balance(100_000, &rpc_client, &config_validator.signers[0].pubkey());
|
||||
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&config_offline,
|
||||
&faucet_addr,
|
||||
&config_offline.signers[0].pubkey(),
|
||||
100_000,
|
||||
&config_validator,
|
||||
)
|
||||
.unwrap();
|
||||
check_recent_balance(100_000, &rpc_client, &config_offline.signers[0].pubkey());
|
||||
@@ -334,11 +319,9 @@ fn test_offline_stake_delegation_and_deactivation() {
|
||||
lockup: Lockup::default(),
|
||||
amount: SpendAmount::Some(50_000),
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
from: 0,
|
||||
};
|
||||
@@ -352,11 +335,9 @@ fn test_offline_stake_delegation_and_deactivation() {
|
||||
stake_authority: 0,
|
||||
force: true,
|
||||
sign_only: true,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::None(blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
config_offline.output_format = OutputFormat::JsonCompact;
|
||||
@@ -373,11 +354,9 @@ fn test_offline_stake_delegation_and_deactivation() {
|
||||
stake_authority: 0,
|
||||
force: true,
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config_payer).unwrap();
|
||||
@@ -388,12 +367,9 @@ fn test_offline_stake_delegation_and_deactivation() {
|
||||
stake_account_pubkey: stake_keypair.pubkey(),
|
||||
stake_authority: 0,
|
||||
sign_only: true,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::None(blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
seed: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
let sig_response = process_command(&config_offline).unwrap();
|
||||
@@ -407,12 +383,9 @@ fn test_offline_stake_delegation_and_deactivation() {
|
||||
stake_account_pubkey: stake_keypair.pubkey(),
|
||||
stake_authority: 0,
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
seed: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config_payer).unwrap();
|
||||
@@ -423,9 +396,8 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
||||
solana_logger::setup();
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -439,8 +411,14 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
||||
.get_minimum_balance_for_rent_exemption(NonceState::size())
|
||||
.unwrap();
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &config.signers[0].pubkey(), 100_000)
|
||||
.unwrap();
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&faucet_addr,
|
||||
&config.signers[0].pubkey(),
|
||||
100_000,
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Create stake account
|
||||
let stake_keypair = Keypair::new();
|
||||
@@ -453,11 +431,9 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
||||
lockup: Lockup::default(),
|
||||
amount: SpendAmount::Some(50_000),
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
from: 0,
|
||||
};
|
||||
@@ -470,7 +446,6 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
||||
nonce_account: 1,
|
||||
seed: None,
|
||||
nonce_authority: Some(config.signers[0].pubkey()),
|
||||
memo: None,
|
||||
amount: SpendAmount::Some(minimum_nonce_balance),
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -493,14 +468,12 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
||||
stake_authority: 0,
|
||||
force: true,
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(
|
||||
blockhash_query::Source::NonceAccount(nonce_account.pubkey()),
|
||||
nonce_hash,
|
||||
),
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -520,15 +493,12 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
||||
stake_account_pubkey: stake_keypair.pubkey(),
|
||||
stake_authority: 0,
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(
|
||||
blockhash_query::Source::NonceAccount(nonce_account.pubkey()),
|
||||
nonce_hash,
|
||||
),
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
seed: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -539,9 +509,8 @@ fn test_stake_authorize() {
|
||||
solana_logger::setup();
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -551,8 +520,14 @@ fn test_stake_authorize() {
|
||||
config.json_rpc_url = test_validator.rpc_url();
|
||||
config.signers = vec![&default_signer];
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &config.signers[0].pubkey(), 100_000)
|
||||
.unwrap();
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&faucet_addr,
|
||||
&config.signers[0].pubkey(),
|
||||
100_000,
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let offline_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
||||
let mut config_offline = CliConfig::recent_for_tests();
|
||||
@@ -560,14 +535,15 @@ fn test_stake_authorize() {
|
||||
config_offline.json_rpc_url = String::default();
|
||||
let offline_authority_pubkey = config_offline.signers[0].pubkey();
|
||||
config_offline.command = CliCommand::ClusterVersion;
|
||||
// Verify that we cannot reach the cluster
|
||||
// Verfiy that we cannot reach the cluster
|
||||
process_command(&config_offline).unwrap_err();
|
||||
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&config_offline,
|
||||
&faucet_addr,
|
||||
&config_offline.signers[0].pubkey(),
|
||||
100_000,
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -583,11 +559,9 @@ fn test_stake_authorize() {
|
||||
lockup: Lockup::default(),
|
||||
amount: SpendAmount::Some(50_000),
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
from: 0,
|
||||
};
|
||||
@@ -601,11 +575,9 @@ fn test_stake_authorize() {
|
||||
stake_account_pubkey,
|
||||
new_authorizations: vec![(StakeAuthorize::Staker, online_authority_pubkey, 0)],
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
};
|
||||
@@ -631,11 +603,9 @@ fn test_stake_authorize() {
|
||||
(StakeAuthorize::Withdrawer, withdraw_authority_pubkey, 0),
|
||||
],
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
};
|
||||
@@ -656,11 +626,9 @@ fn test_stake_authorize() {
|
||||
stake_account_pubkey,
|
||||
new_authorizations: vec![(StakeAuthorize::Staker, offline_authority_pubkey, 1)],
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
};
|
||||
@@ -681,11 +649,9 @@ fn test_stake_authorize() {
|
||||
stake_account_pubkey,
|
||||
new_authorizations: vec![(StakeAuthorize::Staker, nonced_authority_pubkey, 0)],
|
||||
sign_only: true,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::None(blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
};
|
||||
@@ -699,11 +665,9 @@ fn test_stake_authorize() {
|
||||
stake_account_pubkey,
|
||||
new_authorizations: vec![(StakeAuthorize::Staker, nonced_authority_pubkey, 0)],
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
};
|
||||
@@ -726,7 +690,6 @@ fn test_stake_authorize() {
|
||||
nonce_account: 1,
|
||||
seed: None,
|
||||
nonce_authority: Some(offline_authority_pubkey),
|
||||
memo: None,
|
||||
amount: SpendAmount::Some(minimum_nonce_balance),
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -749,11 +712,9 @@ fn test_stake_authorize() {
|
||||
stake_account_pubkey,
|
||||
new_authorizations: vec![(StakeAuthorize::Staker, online_authority_pubkey, 1)],
|
||||
sign_only: true,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::None(nonce_hash),
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
};
|
||||
@@ -768,14 +729,12 @@ fn test_stake_authorize() {
|
||||
stake_account_pubkey,
|
||||
new_authorizations: vec![(StakeAuthorize::Staker, online_authority_pubkey, 1)],
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(
|
||||
blockhash_query::Source::NonceAccount(nonce_account.pubkey()),
|
||||
sign_only.blockhash,
|
||||
),
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
};
|
||||
@@ -805,9 +764,8 @@ fn test_stake_authorize_with_fee_payer() {
|
||||
const SIG_FEE: u64 = 42;
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), SIG_FEE);
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(mint_pubkey, SIG_FEE, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -833,13 +791,16 @@ fn test_stake_authorize_with_fee_payer() {
|
||||
config_offline.command = CliCommand::ClusterVersion;
|
||||
process_command(&config_offline).unwrap_err();
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &default_pubkey, 100_000).unwrap();
|
||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &default_pubkey, 100_000, &config)
|
||||
.unwrap();
|
||||
check_recent_balance(100_000, &rpc_client, &config.signers[0].pubkey());
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config_payer, &payer_pubkey, 100_000).unwrap();
|
||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &payer_pubkey, 100_000, &config)
|
||||
.unwrap();
|
||||
check_recent_balance(100_000, &rpc_client, &payer_pubkey);
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config_offline, &offline_pubkey, 100_000).unwrap();
|
||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 100_000, &config)
|
||||
.unwrap();
|
||||
check_recent_balance(100_000, &rpc_client, &offline_pubkey);
|
||||
|
||||
check_ready(&rpc_client);
|
||||
@@ -856,11 +817,9 @@ fn test_stake_authorize_with_fee_payer() {
|
||||
lockup: Lockup::default(),
|
||||
amount: SpendAmount::Some(50_000),
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
from: 0,
|
||||
};
|
||||
@@ -874,11 +833,9 @@ fn test_stake_authorize_with_fee_payer() {
|
||||
stake_account_pubkey,
|
||||
new_authorizations: vec![(StakeAuthorize::Staker, offline_pubkey, 0)],
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 1,
|
||||
custodian: None,
|
||||
};
|
||||
@@ -895,11 +852,9 @@ fn test_stake_authorize_with_fee_payer() {
|
||||
stake_account_pubkey,
|
||||
new_authorizations: vec![(StakeAuthorize::Staker, payer_pubkey, 0)],
|
||||
sign_only: true,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::None(blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
};
|
||||
@@ -913,11 +868,9 @@ fn test_stake_authorize_with_fee_payer() {
|
||||
stake_account_pubkey,
|
||||
new_authorizations: vec![(StakeAuthorize::Staker, payer_pubkey, 0)],
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
custodian: None,
|
||||
};
|
||||
@@ -934,9 +887,8 @@ fn test_stake_split() {
|
||||
solana_logger::setup();
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -955,11 +907,18 @@ fn test_stake_split() {
|
||||
config_offline.command = CliCommand::ClusterVersion;
|
||||
process_command(&config_offline).unwrap_err();
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &config.signers[0].pubkey(), 500_000)
|
||||
.unwrap();
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&faucet_addr,
|
||||
&config.signers[0].pubkey(),
|
||||
500_000,
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
check_recent_balance(500_000, &rpc_client, &config.signers[0].pubkey());
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config_offline, &offline_pubkey, 100_000).unwrap();
|
||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 100_000, &config)
|
||||
.unwrap();
|
||||
check_recent_balance(100_000, &rpc_client, &offline_pubkey);
|
||||
|
||||
// Create stake account, identity is authority
|
||||
@@ -977,11 +936,9 @@ fn test_stake_split() {
|
||||
lockup: Lockup::default(),
|
||||
amount: SpendAmount::Some(10 * minimum_stake_balance),
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
from: 0,
|
||||
};
|
||||
@@ -1002,7 +959,6 @@ fn test_stake_split() {
|
||||
nonce_account: 1,
|
||||
seed: None,
|
||||
nonce_authority: Some(offline_pubkey),
|
||||
memo: None,
|
||||
amount: SpendAmount::Some(minimum_nonce_balance),
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -1026,11 +982,9 @@ fn test_stake_split() {
|
||||
stake_account_pubkey,
|
||||
stake_authority: 0,
|
||||
sign_only: true,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::None(nonce_hash),
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
split_stake_account: 1,
|
||||
seed: None,
|
||||
lamports: 2 * minimum_stake_balance,
|
||||
@@ -1046,14 +1000,12 @@ fn test_stake_split() {
|
||||
stake_account_pubkey,
|
||||
stake_authority: 0,
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(
|
||||
blockhash_query::Source::NonceAccount(nonce_account.pubkey()),
|
||||
sign_only.blockhash,
|
||||
),
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
split_stake_account: 1,
|
||||
seed: None,
|
||||
lamports: 2 * minimum_stake_balance,
|
||||
@@ -1077,9 +1029,8 @@ fn test_stake_set_lockup() {
|
||||
solana_logger::setup();
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -1098,11 +1049,18 @@ fn test_stake_set_lockup() {
|
||||
config_offline.command = CliCommand::ClusterVersion;
|
||||
process_command(&config_offline).unwrap_err();
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &config.signers[0].pubkey(), 500_000)
|
||||
.unwrap();
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&faucet_addr,
|
||||
&config.signers[0].pubkey(),
|
||||
500_000,
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
check_recent_balance(500_000, &rpc_client, &config.signers[0].pubkey());
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config_offline, &offline_pubkey, 100_000).unwrap();
|
||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 100_000, &config)
|
||||
.unwrap();
|
||||
check_recent_balance(100_000, &rpc_client, &offline_pubkey);
|
||||
|
||||
// Create stake account, identity is authority
|
||||
@@ -1123,15 +1081,13 @@ fn test_stake_set_lockup() {
|
||||
stake_account: 1,
|
||||
seed: None,
|
||||
staker: Some(offline_pubkey),
|
||||
withdrawer: Some(config.signers[0].pubkey()),
|
||||
withdrawer: Some(offline_pubkey),
|
||||
lockup,
|
||||
amount: SpendAmount::Some(10 * minimum_stake_balance),
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
from: 0,
|
||||
};
|
||||
@@ -1154,11 +1110,9 @@ fn test_stake_set_lockup() {
|
||||
lockup,
|
||||
custodian: 0,
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -1189,11 +1143,9 @@ fn test_stake_set_lockup() {
|
||||
lockup,
|
||||
custodian: 0,
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -1209,11 +1161,9 @@ fn test_stake_set_lockup() {
|
||||
lockup,
|
||||
custodian: 1,
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -1241,11 +1191,9 @@ fn test_stake_set_lockup() {
|
||||
lockup,
|
||||
custodian: 1,
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::default(),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -1261,7 +1209,6 @@ fn test_stake_set_lockup() {
|
||||
nonce_account: 1,
|
||||
seed: None,
|
||||
nonce_authority: Some(offline_pubkey),
|
||||
memo: None,
|
||||
amount: SpendAmount::Some(minimum_nonce_balance),
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -1288,11 +1235,9 @@ fn test_stake_set_lockup() {
|
||||
lockup,
|
||||
custodian: 0,
|
||||
sign_only: true,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::None(nonce_hash),
|
||||
nonce_account: Some(nonce_account_pubkey),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
config_offline.output_format = OutputFormat::JsonCompact;
|
||||
@@ -1306,14 +1251,12 @@ fn test_stake_set_lockup() {
|
||||
lockup,
|
||||
custodian: 0,
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(
|
||||
blockhash_query::Source::NonceAccount(nonce_account_pubkey),
|
||||
sign_only.blockhash,
|
||||
),
|
||||
nonce_account: Some(nonce_account_pubkey),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -1336,9 +1279,8 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
solana_logger::setup();
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -1353,14 +1295,21 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
let offline_pubkey = config_offline.signers[0].pubkey();
|
||||
config_offline.json_rpc_url = String::default();
|
||||
config_offline.command = CliCommand::ClusterVersion;
|
||||
// Verify that we cannot reach the cluster
|
||||
// Verfiy that we cannot reach the cluster
|
||||
process_command(&config_offline).unwrap_err();
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &config.signers[0].pubkey(), 200_000)
|
||||
.unwrap();
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&faucet_addr,
|
||||
&config.signers[0].pubkey(),
|
||||
200_000,
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
check_recent_balance(200_000, &rpc_client, &config.signers[0].pubkey());
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config_offline, &offline_pubkey, 100_000).unwrap();
|
||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 100_000, &config)
|
||||
.unwrap();
|
||||
check_recent_balance(100_000, &rpc_client, &offline_pubkey);
|
||||
|
||||
// Create nonce account
|
||||
@@ -1374,7 +1323,6 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
nonce_account: 1,
|
||||
seed: None,
|
||||
nonce_authority: Some(offline_pubkey),
|
||||
memo: None,
|
||||
amount: SpendAmount::Some(minimum_nonce_balance),
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -1401,11 +1349,9 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
lockup: Lockup::default(),
|
||||
amount: SpendAmount::Some(50_000),
|
||||
sign_only: true,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::None(nonce_hash),
|
||||
nonce_account: Some(nonce_pubkey),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
from: 0,
|
||||
};
|
||||
@@ -1424,14 +1370,12 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
lockup: Lockup::default(),
|
||||
amount: SpendAmount::Some(50_000),
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(
|
||||
blockhash_query::Source::NonceAccount(nonce_pubkey),
|
||||
sign_only.blockhash,
|
||||
),
|
||||
nonce_account: Some(nonce_pubkey),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
from: 0,
|
||||
};
|
||||
@@ -1455,16 +1399,13 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
config_offline.command = CliCommand::WithdrawStake {
|
||||
stake_account_pubkey: stake_pubkey,
|
||||
destination_account_pubkey: recipient_pubkey,
|
||||
amount: SpendAmount::Some(42),
|
||||
lamports: 42,
|
||||
withdraw_authority: 0,
|
||||
custodian: None,
|
||||
sign_only: true,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::None(nonce_hash),
|
||||
nonce_account: Some(nonce_pubkey),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
seed: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
let sig_response = process_command(&config_offline).unwrap();
|
||||
@@ -1474,19 +1415,16 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
config.command = CliCommand::WithdrawStake {
|
||||
stake_account_pubkey: stake_pubkey,
|
||||
destination_account_pubkey: recipient_pubkey,
|
||||
amount: SpendAmount::Some(42),
|
||||
lamports: 42,
|
||||
withdraw_authority: 0,
|
||||
custodian: None,
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(
|
||||
blockhash_query::Source::NonceAccount(nonce_pubkey),
|
||||
sign_only.blockhash,
|
||||
),
|
||||
nonce_account: Some(nonce_pubkey),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
seed: None,
|
||||
fee_payer: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -1513,11 +1451,9 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
lockup: Lockup::default(),
|
||||
amount: SpendAmount::Some(50_000),
|
||||
sign_only: true,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::None(nonce_hash),
|
||||
nonce_account: Some(nonce_pubkey),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
from: 0,
|
||||
};
|
||||
@@ -1534,14 +1470,12 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
lockup: Lockup::default(),
|
||||
amount: SpendAmount::Some(50_000),
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(
|
||||
blockhash_query::Source::NonceAccount(nonce_pubkey),
|
||||
sign_only.blockhash,
|
||||
),
|
||||
nonce_account: Some(nonce_pubkey),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
from: 0,
|
||||
};
|
||||
|
@@ -22,9 +22,8 @@ use solana_sdk::{
|
||||
fn test_transfer() {
|
||||
solana_logger::setup();
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -39,7 +38,8 @@ fn test_transfer() {
|
||||
let sender_pubkey = config.signers[0].pubkey();
|
||||
let recipient_pubkey = Pubkey::new(&[1u8; 32]);
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 50_000).unwrap();
|
||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &sender_pubkey, 50_000, &config)
|
||||
.unwrap();
|
||||
check_recent_balance(50_000, &rpc_client, &sender_pubkey);
|
||||
check_recent_balance(0, &rpc_client, &recipient_pubkey);
|
||||
|
||||
@@ -51,16 +51,11 @@ fn test_transfer() {
|
||||
to: recipient_pubkey,
|
||||
from: 0,
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
allow_unfunded_recipient: true,
|
||||
no_wait: false,
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_recent_balance(49_989, &rpc_client, &sender_pubkey);
|
||||
@@ -72,16 +67,11 @@ fn test_transfer() {
|
||||
to: recipient_pubkey,
|
||||
from: 0,
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
allow_unfunded_recipient: true,
|
||||
no_wait: false,
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
};
|
||||
assert!(process_command(&config).is_err());
|
||||
check_recent_balance(49_989, &rpc_client, &sender_pubkey);
|
||||
@@ -95,7 +85,7 @@ fn test_transfer() {
|
||||
process_command(&offline).unwrap_err();
|
||||
|
||||
let offline_pubkey = offline.signers[0].pubkey();
|
||||
request_and_confirm_airdrop(&rpc_client, &offline, &offline_pubkey, 50).unwrap();
|
||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 50, &config).unwrap();
|
||||
check_recent_balance(50, &rpc_client, &offline_pubkey);
|
||||
|
||||
// Offline transfer
|
||||
@@ -105,16 +95,11 @@ fn test_transfer() {
|
||||
to: recipient_pubkey,
|
||||
from: 0,
|
||||
sign_only: true,
|
||||
dump_transaction_message: false,
|
||||
allow_unfunded_recipient: true,
|
||||
no_wait: false,
|
||||
blockhash_query: BlockhashQuery::None(blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
};
|
||||
offline.output_format = OutputFormat::JsonCompact;
|
||||
let sign_only_reply = process_command(&offline).unwrap();
|
||||
@@ -127,16 +112,11 @@ fn test_transfer() {
|
||||
to: recipient_pubkey,
|
||||
from: 0,
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
allow_unfunded_recipient: true,
|
||||
no_wait: false,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_recent_balance(39, &rpc_client, &offline_pubkey);
|
||||
@@ -152,7 +132,6 @@ fn test_transfer() {
|
||||
nonce_account: 1,
|
||||
seed: None,
|
||||
nonce_authority: None,
|
||||
memo: None,
|
||||
amount: SpendAmount::Some(minimum_nonce_balance),
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -175,8 +154,6 @@ fn test_transfer() {
|
||||
to: recipient_pubkey,
|
||||
from: 0,
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
allow_unfunded_recipient: true,
|
||||
no_wait: false,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(
|
||||
blockhash_query::Source::NonceAccount(nonce_account.pubkey()),
|
||||
@@ -184,10 +161,7 @@ fn test_transfer() {
|
||||
),
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_recent_balance(49_976 - minimum_nonce_balance, &rpc_client, &sender_pubkey);
|
||||
@@ -207,7 +181,6 @@ fn test_transfer() {
|
||||
config.command = CliCommand::AuthorizeNonceAccount {
|
||||
nonce_account: nonce_account.pubkey(),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
new_authority: offline_pubkey,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
@@ -230,16 +203,11 @@ fn test_transfer() {
|
||||
to: recipient_pubkey,
|
||||
from: 0,
|
||||
sign_only: true,
|
||||
dump_transaction_message: false,
|
||||
allow_unfunded_recipient: true,
|
||||
no_wait: false,
|
||||
blockhash_query: BlockhashQuery::None(nonce_hash),
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
};
|
||||
let sign_only_reply = process_command(&offline).unwrap();
|
||||
let sign_only = parse_sign_only_reply_string(&sign_only_reply);
|
||||
@@ -251,8 +219,6 @@ fn test_transfer() {
|
||||
to: recipient_pubkey,
|
||||
from: 0,
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
allow_unfunded_recipient: true,
|
||||
no_wait: false,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(
|
||||
blockhash_query::Source::NonceAccount(nonce_account.pubkey()),
|
||||
@@ -260,10 +226,7 @@ fn test_transfer() {
|
||||
),
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_recent_balance(28, &rpc_client, &offline_pubkey);
|
||||
@@ -274,30 +237,32 @@ fn test_transfer() {
|
||||
fn test_transfer_multisession_signing() {
|
||||
solana_logger::setup();
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
|
||||
|
||||
let to_pubkey = Pubkey::new(&[1u8; 32]);
|
||||
let offline_from_signer = keypair_from_seed(&[2u8; 32]).unwrap();
|
||||
let offline_fee_payer_signer = keypair_from_seed(&[3u8; 32]).unwrap();
|
||||
let from_null_signer = NullSigner::new(&offline_from_signer.pubkey());
|
||||
let config = CliConfig::recent_for_tests();
|
||||
|
||||
// Setup accounts
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&CliConfig::recent_for_tests(),
|
||||
&faucet_addr,
|
||||
&offline_from_signer.pubkey(),
|
||||
43,
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&CliConfig::recent_for_tests(),
|
||||
&faucet_addr,
|
||||
&offline_fee_payer_signer.pubkey(),
|
||||
3,
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
check_recent_balance(43, &rpc_client, &offline_from_signer.pubkey());
|
||||
@@ -320,16 +285,11 @@ fn test_transfer_multisession_signing() {
|
||||
to: to_pubkey,
|
||||
from: 1,
|
||||
sign_only: true,
|
||||
dump_transaction_message: false,
|
||||
allow_unfunded_recipient: true,
|
||||
no_wait: false,
|
||||
blockhash_query: BlockhashQuery::None(blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
};
|
||||
fee_payer_config.output_format = OutputFormat::JsonCompact;
|
||||
let sign_only_reply = process_command(&fee_payer_config).unwrap();
|
||||
@@ -351,16 +311,11 @@ fn test_transfer_multisession_signing() {
|
||||
to: to_pubkey,
|
||||
from: 1,
|
||||
sign_only: true,
|
||||
dump_transaction_message: false,
|
||||
allow_unfunded_recipient: true,
|
||||
no_wait: false,
|
||||
blockhash_query: BlockhashQuery::None(blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
};
|
||||
from_config.output_format = OutputFormat::JsonCompact;
|
||||
let sign_only_reply = process_command(&from_config).unwrap();
|
||||
@@ -379,16 +334,11 @@ fn test_transfer_multisession_signing() {
|
||||
to: to_pubkey,
|
||||
from: 1,
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
allow_unfunded_recipient: true,
|
||||
no_wait: false,
|
||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
|
||||
@@ -401,9 +351,8 @@ fn test_transfer_multisession_signing() {
|
||||
fn test_transfer_all() {
|
||||
solana_logger::setup();
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -417,7 +366,8 @@ fn test_transfer_all() {
|
||||
let sender_pubkey = config.signers[0].pubkey();
|
||||
let recipient_pubkey = Pubkey::new(&[1u8; 32]);
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 50_000).unwrap();
|
||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &sender_pubkey, 50_000, &config)
|
||||
.unwrap();
|
||||
check_recent_balance(50_000, &rpc_client, &sender_pubkey);
|
||||
check_recent_balance(0, &rpc_client, &recipient_pubkey);
|
||||
|
||||
@@ -429,125 +379,13 @@ fn test_transfer_all() {
|
||||
to: recipient_pubkey,
|
||||
from: 0,
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
allow_unfunded_recipient: true,
|
||||
no_wait: false,
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_recent_balance(0, &rpc_client, &sender_pubkey);
|
||||
check_recent_balance(49_999, &rpc_client, &recipient_pubkey);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transfer_unfunded_recipient() {
|
||||
solana_logger::setup();
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
|
||||
let default_signer = Keypair::new();
|
||||
|
||||
let mut config = CliConfig::recent_for_tests();
|
||||
config.json_rpc_url = test_validator.rpc_url();
|
||||
config.signers = vec![&default_signer];
|
||||
|
||||
let sender_pubkey = config.signers[0].pubkey();
|
||||
let recipient_pubkey = Pubkey::new(&[1u8; 32]);
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 50_000).unwrap();
|
||||
check_recent_balance(50_000, &rpc_client, &sender_pubkey);
|
||||
check_recent_balance(0, &rpc_client, &recipient_pubkey);
|
||||
|
||||
check_ready(&rpc_client);
|
||||
|
||||
// Plain ole transfer
|
||||
config.command = CliCommand::Transfer {
|
||||
amount: SpendAmount::All,
|
||||
to: recipient_pubkey,
|
||||
from: 0,
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
allow_unfunded_recipient: false,
|
||||
no_wait: false,
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
};
|
||||
|
||||
// Expect failure due to unfunded recipient and the lack of the `allow_unfunded_recipient` flag
|
||||
process_command(&config).unwrap_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transfer_with_seed() {
|
||||
solana_logger::setup();
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(mint_pubkey, 1, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
|
||||
let default_signer = Keypair::new();
|
||||
|
||||
let mut config = CliConfig::recent_for_tests();
|
||||
config.json_rpc_url = test_validator.rpc_url();
|
||||
config.signers = vec![&default_signer];
|
||||
|
||||
let sender_pubkey = config.signers[0].pubkey();
|
||||
let recipient_pubkey = Pubkey::new(&[1u8; 32]);
|
||||
let derived_address_seed = "seed".to_string();
|
||||
let derived_address_program_id = solana_stake_program::id();
|
||||
let derived_address = Pubkey::create_with_seed(
|
||||
&sender_pubkey,
|
||||
&derived_address_seed,
|
||||
&derived_address_program_id,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 1).unwrap();
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &derived_address, 50_000).unwrap();
|
||||
check_recent_balance(1, &rpc_client, &sender_pubkey);
|
||||
check_recent_balance(50_000, &rpc_client, &derived_address);
|
||||
check_recent_balance(0, &rpc_client, &recipient_pubkey);
|
||||
|
||||
check_ready(&rpc_client);
|
||||
|
||||
// Transfer with seed
|
||||
config.command = CliCommand::Transfer {
|
||||
amount: SpendAmount::Some(50_000),
|
||||
to: recipient_pubkey,
|
||||
from: 0,
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
allow_unfunded_recipient: true,
|
||||
no_wait: false,
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: Some(derived_address_seed),
|
||||
derived_address_program_id: Some(derived_address_program_id),
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_recent_balance(0, &rpc_client, &sender_pubkey);
|
||||
check_recent_balance(50_000, &rpc_client, &recipient_pubkey);
|
||||
check_recent_balance(0, &rpc_client, &derived_address);
|
||||
}
|
||||
|
@@ -19,9 +19,8 @@ use solana_vote_program::vote_state::{VoteAuthorize, VoteState, VoteStateVersion
|
||||
#[test]
|
||||
fn test_vote_authorize_and_withdraw() {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
|
||||
|
||||
let rpc_client =
|
||||
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
|
||||
@@ -31,8 +30,14 @@ fn test_vote_authorize_and_withdraw() {
|
||||
config.json_rpc_url = test_validator.rpc_url();
|
||||
config.signers = vec![&default_signer];
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &config.signers[0].pubkey(), 100_000)
|
||||
.unwrap();
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&faucet_addr,
|
||||
&config.signers[0].pubkey(),
|
||||
100_000,
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Create vote account
|
||||
let vote_account_keypair = Keypair::new();
|
||||
@@ -45,7 +50,6 @@ fn test_vote_authorize_and_withdraw() {
|
||||
authorized_voter: None,
|
||||
authorized_withdrawer: Some(config.signers[0].pubkey()),
|
||||
commission: 0,
|
||||
memo: None,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let vote_account = rpc_client
|
||||
@@ -67,16 +71,11 @@ fn test_vote_authorize_and_withdraw() {
|
||||
to: vote_account_pubkey,
|
||||
from: 0,
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
allow_unfunded_recipient: true,
|
||||
no_wait: false,
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
memo: None,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let expected_balance = expected_balance + 1_000;
|
||||
@@ -89,7 +88,6 @@ fn test_vote_authorize_and_withdraw() {
|
||||
vote_account_pubkey,
|
||||
new_authorized_pubkey: withdraw_authority.pubkey(),
|
||||
vote_authorize: VoteAuthorize::Withdrawer,
|
||||
memo: None,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let vote_account = rpc_client
|
||||
@@ -107,7 +105,6 @@ fn test_vote_authorize_and_withdraw() {
|
||||
withdraw_authority: 1,
|
||||
withdraw_amount: SpendAmount::Some(100),
|
||||
destination_account_pubkey: destination_account,
|
||||
memo: None,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_recent_balance(expected_balance - 100, &rpc_client, &vote_account_pubkey);
|
||||
@@ -120,7 +117,6 @@ fn test_vote_authorize_and_withdraw() {
|
||||
vote_account_pubkey,
|
||||
new_identity_account: 2,
|
||||
withdraw_authority: 1,
|
||||
memo: None,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
}
|
||||
|
@@ -1,11 +1,10 @@
|
||||
[package]
|
||||
name = "solana-client"
|
||||
version = "1.6.13"
|
||||
version = "1.5.7"
|
||||
description = "Solana Client"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
homepage = "https://solana.com/"
|
||||
documentation = "https://docs.rs/solana-client"
|
||||
license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
|
||||
@@ -15,32 +14,31 @@ bincode = "1.3.1"
|
||||
bs58 = "0.3.1"
|
||||
clap = "2.33.0"
|
||||
indicatif = "0.15.0"
|
||||
jsonrpc-core = "17.0.0"
|
||||
jsonrpc-core = "15.0.0"
|
||||
log = "0.4.11"
|
||||
net2 = "0.2.37"
|
||||
rayon = "1.5.0"
|
||||
reqwest = { version = "0.11.2", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
rayon = "1.4.0"
|
||||
reqwest = { version = "0.10.8", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
semver = "0.11.0"
|
||||
serde = "1.0.122"
|
||||
serde = "1.0.112"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.56"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.6.13" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "=1.6.13" }
|
||||
solana-faucet = { path = "../faucet", version = "=1.6.13" }
|
||||
solana-net-utils = { path = "../net-utils", version = "=1.6.13" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.6.13" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.6.13" }
|
||||
solana-version = { path = "../version", version = "=1.6.13" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.6.13" }
|
||||
solana-account-decoder = { path = "../account-decoder", version = "1.5.7" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.5.7" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.5.7" }
|
||||
solana-sdk = { path = "../sdk", version = "1.5.7" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "1.5.7" }
|
||||
solana-version = { path = "../version", version = "1.5.7" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.5.7" }
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tungstenite = "0.10.1"
|
||||
url = "2.1.1"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_matches = "1.3.0"
|
||||
jsonrpc-http-server = "17.0.0"
|
||||
solana-logger = { path = "../logger", version = "=1.6.13" }
|
||||
jsonrpc-core = "15.0.0"
|
||||
jsonrpc-http-server = "15.0.0"
|
||||
solana-logger = { path = "../logger", version = "1.5.7" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -1,15 +1,12 @@
|
||||
use {
|
||||
crate::{nonce_utils, rpc_client::RpcClient},
|
||||
clap::ArgMatches,
|
||||
solana_clap_utils::{
|
||||
input_parsers::{pubkey_of, value_of},
|
||||
nonce::*,
|
||||
offline::*,
|
||||
},
|
||||
solana_sdk::{
|
||||
commitment_config::CommitmentConfig, fee_calculator::FeeCalculator, hash::Hash,
|
||||
pubkey::Pubkey,
|
||||
},
|
||||
use crate::{nonce_utils, rpc_client::RpcClient};
|
||||
use clap::ArgMatches;
|
||||
use solana_clap_utils::{
|
||||
input_parsers::{pubkey_of, value_of},
|
||||
nonce::*,
|
||||
offline::*,
|
||||
};
|
||||
use solana_sdk::{
|
||||
commitment_config::CommitmentConfig, fee_calculator::FeeCalculator, hash::Hash, pubkey::Pubkey,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
@@ -361,7 +358,7 @@ mod tests {
|
||||
let nonce_pubkey = Pubkey::new(&[4u8; 32]);
|
||||
let rpc_nonce_account = UiAccount::encode(
|
||||
&nonce_pubkey,
|
||||
&nonce_account,
|
||||
nonce_account,
|
||||
UiAccountEncoding::Base64,
|
||||
None,
|
||||
None,
|
||||
|
@@ -1,12 +1,9 @@
|
||||
use {
|
||||
crate::rpc_request,
|
||||
solana_faucet::faucet::FaucetError,
|
||||
solana_sdk::{
|
||||
signature::SignerError, transaction::TransactionError, transport::TransportError,
|
||||
},
|
||||
std::io,
|
||||
thiserror::Error,
|
||||
use crate::rpc_request;
|
||||
use solana_sdk::{
|
||||
signature::SignerError, transaction::TransactionError, transport::TransportError,
|
||||
};
|
||||
use std::io;
|
||||
use thiserror::Error;
|
||||
|
||||
pub use reqwest; // export `reqwest` for clients
|
||||
|
||||
@@ -24,8 +21,6 @@ pub enum ClientErrorKind {
|
||||
SigningError(#[from] SignerError),
|
||||
#[error(transparent)]
|
||||
TransactionError(#[from] TransactionError),
|
||||
#[error(transparent)]
|
||||
FaucetError(#[from] FaucetError),
|
||||
#[error("Custom: {0}")]
|
||||
Custom(String),
|
||||
}
|
||||
@@ -49,7 +44,6 @@ impl From<ClientErrorKind> for TransportError {
|
||||
ClientErrorKind::RpcError(err) => Self::Custom(format!("{:?}", err)),
|
||||
ClientErrorKind::SerdeJson(err) => Self::Custom(format!("{:?}", err)),
|
||||
ClientErrorKind::SigningError(err) => Self::Custom(format!("{:?}", err)),
|
||||
ClientErrorKind::FaucetError(err) => Self::Custom(format!("{:?}", err)),
|
||||
ClientErrorKind::Custom(err) => Self::Custom(format!("{:?}", err)),
|
||||
}
|
||||
}
|
||||
@@ -166,13 +160,4 @@ impl From<TransactionError> for ClientError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FaucetError> for ClientError {
|
||||
fn from(err: FaucetError) -> Self {
|
||||
Self {
|
||||
request: None,
|
||||
kind: err.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, ClientError>;
|
||||
|
@@ -1,31 +1,17 @@
|
||||
use {
|
||||
crate::{
|
||||
client_error::Result,
|
||||
rpc_custom_error,
|
||||
rpc_request::{RpcError, RpcRequest, RpcResponseErrorData},
|
||||
rpc_response::RpcSimulateTransactionResult,
|
||||
rpc_sender::RpcSender,
|
||||
},
|
||||
log::*,
|
||||
reqwest::{
|
||||
self,
|
||||
header::{CONTENT_TYPE, RETRY_AFTER},
|
||||
StatusCode,
|
||||
},
|
||||
std::{
|
||||
sync::{
|
||||
atomic::{AtomicU64, Ordering},
|
||||
Arc,
|
||||
},
|
||||
thread::sleep,
|
||||
time::Duration,
|
||||
},
|
||||
use crate::{
|
||||
client_error::Result,
|
||||
rpc_custom_error,
|
||||
rpc_request::{RpcError, RpcRequest, RpcResponseErrorData},
|
||||
rpc_response::RpcSimulateTransactionResult,
|
||||
rpc_sender::RpcSender,
|
||||
};
|
||||
use log::*;
|
||||
use reqwest::{self, header::CONTENT_TYPE, StatusCode};
|
||||
use std::{thread::sleep, time::Duration};
|
||||
|
||||
pub struct HttpSender {
|
||||
client: Arc<reqwest::blocking::Client>,
|
||||
client: reqwest::blocking::Client,
|
||||
url: String,
|
||||
request_id: AtomicU64,
|
||||
}
|
||||
|
||||
impl HttpSender {
|
||||
@@ -34,22 +20,12 @@ impl HttpSender {
|
||||
}
|
||||
|
||||
pub fn new_with_timeout(url: String, timeout: Duration) -> Self {
|
||||
// `reqwest::blocking::Client` panics if run in a tokio async context. Shuttle the
|
||||
// request to a different tokio thread to avoid this
|
||||
let client = Arc::new(
|
||||
tokio::task::block_in_place(move || {
|
||||
reqwest::blocking::Client::builder()
|
||||
.timeout(timeout)
|
||||
.build()
|
||||
})
|
||||
.expect("build rpc client"),
|
||||
);
|
||||
let client = reqwest::blocking::Client::builder()
|
||||
.timeout(timeout)
|
||||
.build()
|
||||
.expect("build rpc client");
|
||||
|
||||
Self {
|
||||
client,
|
||||
url,
|
||||
request_id: AtomicU64::new(0),
|
||||
}
|
||||
Self { client, url }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,57 +38,39 @@ struct RpcErrorObject {
|
||||
|
||||
impl RpcSender for HttpSender {
|
||||
fn send(&self, request: RpcRequest, params: serde_json::Value) -> Result<serde_json::Value> {
|
||||
let request_id = self.request_id.fetch_add(1, Ordering::Relaxed);
|
||||
let request_json = request.build_request_json(request_id, params).to_string();
|
||||
// Concurrent requests are not supported so reuse the same request id for all requests
|
||||
let request_id = 1;
|
||||
|
||||
let request_json = request.build_request_json(request_id, params);
|
||||
|
||||
let mut too_many_requests_retries = 5;
|
||||
loop {
|
||||
// `reqwest::blocking::Client` panics if run in a tokio async context. Shuttle the
|
||||
// request to a different tokio thread to avoid this
|
||||
let response = {
|
||||
let client = self.client.clone();
|
||||
let request_json = request_json.clone();
|
||||
tokio::task::block_in_place(move || {
|
||||
client
|
||||
.post(&self.url)
|
||||
.header(CONTENT_TYPE, "application/json")
|
||||
.body(request_json)
|
||||
.send()
|
||||
})
|
||||
};
|
||||
|
||||
match response {
|
||||
match self
|
||||
.client
|
||||
.post(&self.url)
|
||||
.header(CONTENT_TYPE, "application/json")
|
||||
.body(request_json.to_string())
|
||||
.send()
|
||||
{
|
||||
Ok(response) => {
|
||||
if !response.status().is_success() {
|
||||
if response.status() == StatusCode::TOO_MANY_REQUESTS
|
||||
&& too_many_requests_retries > 0
|
||||
{
|
||||
let mut duration = Duration::from_millis(500);
|
||||
if let Some(retry_after) = response.headers().get(RETRY_AFTER) {
|
||||
if let Ok(retry_after) = retry_after.to_str() {
|
||||
if let Ok(retry_after) = retry_after.parse::<u64>() {
|
||||
if retry_after < 120 {
|
||||
duration = Duration::from_secs(retry_after);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
too_many_requests_retries -= 1;
|
||||
debug!(
|
||||
"Too many requests: server responded with {:?}, {} retries left, pausing for {:?}",
|
||||
response, too_many_requests_retries, duration
|
||||
"Server responded with {:?}, {} retries left",
|
||||
response, too_many_requests_retries
|
||||
);
|
||||
|
||||
sleep(duration);
|
||||
// Sleep for 500ms to give the server a break
|
||||
sleep(Duration::from_millis(500));
|
||||
continue;
|
||||
}
|
||||
return Err(response.error_for_status().unwrap_err().into());
|
||||
}
|
||||
|
||||
let response_text = tokio::task::block_in_place(move || response.text())?;
|
||||
|
||||
let json: serde_json::Value = serde_json::from_str(&response_text)?;
|
||||
let json: serde_json::Value = serde_json::from_str(&response.text()?)?;
|
||||
if json["error"].is_object() {
|
||||
return match serde_json::from_value::<RpcErrorObject>(json["error"].clone())
|
||||
{
|
||||
@@ -127,7 +85,7 @@ impl RpcSender for HttpSender {
|
||||
}
|
||||
}
|
||||
},
|
||||
rpc_custom_error::JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY => {
|
||||
rpc_custom_error::JSON_RPC_SERVER_ERROR_NODE_UNHEALTHLY => {
|
||||
match serde_json::from_value::<rpc_custom_error::NodeUnhealthyErrorData>(json["error"]["data"].clone()) {
|
||||
Ok(rpc_custom_error::NodeUnhealthyErrorData {num_slots_behind}) => RpcResponseErrorData::NodeUnhealthy {num_slots_behind},
|
||||
Err(_err) => {
|
||||
@@ -162,22 +120,3 @@ impl RpcSender for HttpSender {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn http_sender_on_tokio_multi_thread() {
|
||||
let http_sender = HttpSender::new("http://localhost:1234".to_string());
|
||||
let _ = http_sender.send(RpcRequest::GetVersion, serde_json::Value::Null);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
#[should_panic(expected = "can call blocking only when running on the multi-threaded runtime")]
|
||||
async fn http_sender_ontokio_current_thread_should_panic() {
|
||||
// RpcClient::new() will panic in the tokio current-thread runtime due to `tokio::task::block_in_place()` usage, and there
|
||||
// doesn't seem to be a way to detect whether the tokio runtime is multi_thread or current_thread...
|
||||
let _ = HttpSender::new("http://localhost:1234".to_string());
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,3 @@
|
||||
#![allow(clippy::integer_arithmetic)]
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
@@ -18,4 +17,3 @@ pub mod rpc_request;
|
||||
pub mod rpc_response;
|
||||
pub mod rpc_sender;
|
||||
pub mod thin_client;
|
||||
pub mod tpu_client;
|
||||
|
@@ -1,22 +1,20 @@
|
||||
use {
|
||||
crate::{
|
||||
client_error::Result,
|
||||
rpc_request::RpcRequest,
|
||||
rpc_response::{Response, RpcResponseContext, RpcVersionInfo},
|
||||
rpc_sender::RpcSender,
|
||||
},
|
||||
serde_json::{json, Number, Value},
|
||||
solana_sdk::{
|
||||
epoch_info::EpochInfo,
|
||||
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
||||
instruction::InstructionError,
|
||||
signature::Signature,
|
||||
transaction::{self, Transaction, TransactionError},
|
||||
},
|
||||
solana_transaction_status::{TransactionConfirmationStatus, TransactionStatus},
|
||||
solana_version::Version,
|
||||
std::{collections::HashMap, sync::RwLock},
|
||||
use crate::{
|
||||
client_error::Result,
|
||||
rpc_request::RpcRequest,
|
||||
rpc_response::{Response, RpcResponseContext, RpcVersionInfo},
|
||||
rpc_sender::RpcSender,
|
||||
};
|
||||
use serde_json::{json, Number, Value};
|
||||
use solana_sdk::{
|
||||
epoch_info::EpochInfo,
|
||||
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
||||
instruction::InstructionError,
|
||||
signature::Signature,
|
||||
transaction::{self, Transaction, TransactionError},
|
||||
};
|
||||
use solana_transaction_status::{TransactionConfirmationStatus, TransactionStatus};
|
||||
use solana_version::Version;
|
||||
use std::{collections::HashMap, sync::RwLock};
|
||||
|
||||
pub const PUBKEY: &str = "7RoSF9fUmdphVCpabEoefH81WwrW7orsWonXWqTXkKV8";
|
||||
pub const SIGNATURE: &str =
|
||||
@@ -124,8 +122,6 @@ impl RpcSender for MockSender {
|
||||
}
|
||||
RpcRequest::GetTransactionCount => Value::Number(Number::from(1234)),
|
||||
RpcRequest::GetSlot => Value::Number(Number::from(0)),
|
||||
RpcRequest::GetMaxShredInsertSlot => Value::Number(Number::from(0)),
|
||||
RpcRequest::RequestAirdrop => Value::String(Signature::new(&[8; 64]).to_string()),
|
||||
RpcRequest::SendTransaction => {
|
||||
let signature = if self.url == "malicious" {
|
||||
Signature::new(&[8; 64]).to_string()
|
||||
|
@@ -1,16 +1,14 @@
|
||||
use {
|
||||
crate::rpc_client::RpcClient,
|
||||
solana_sdk::{
|
||||
account::{Account, ReadableAccount},
|
||||
account_utils::StateMut,
|
||||
commitment_config::CommitmentConfig,
|
||||
nonce::{
|
||||
state::{Data, Versions},
|
||||
State,
|
||||
},
|
||||
pubkey::Pubkey,
|
||||
system_program,
|
||||
use crate::rpc_client::RpcClient;
|
||||
use solana_sdk::{
|
||||
account::Account,
|
||||
account_utils::StateMut,
|
||||
commitment_config::CommitmentConfig,
|
||||
nonce::{
|
||||
state::{Data, Versions},
|
||||
State,
|
||||
},
|
||||
pubkey::Pubkey,
|
||||
system_program,
|
||||
};
|
||||
|
||||
#[derive(Debug, thiserror::Error, PartialEq)]
|
||||
@@ -48,31 +46,30 @@ pub fn get_account_with_commitment(
|
||||
.value
|
||||
.ok_or_else(|| Error::Client(format!("AccountNotFound: pubkey={}", nonce_pubkey)))
|
||||
})
|
||||
.and_then(|a| account_identity_ok(&a).map(|()| a))
|
||||
.and_then(|a| match account_identity_ok(&a) {
|
||||
Ok(()) => Ok(a),
|
||||
Err(e) => Err(e),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn account_identity_ok<T: ReadableAccount>(account: &T) -> Result<(), Error> {
|
||||
if account.owner() != &system_program::id() {
|
||||
pub fn account_identity_ok(account: &Account) -> Result<(), Error> {
|
||||
if account.owner != system_program::id() {
|
||||
Err(Error::InvalidAccountOwner)
|
||||
} else if account.data().is_empty() {
|
||||
} else if account.data.is_empty() {
|
||||
Err(Error::UnexpectedDataSize)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn state_from_account<T: ReadableAccount + StateMut<Versions>>(
|
||||
account: &T,
|
||||
) -> Result<State, Error> {
|
||||
pub fn state_from_account(account: &Account) -> Result<State, Error> {
|
||||
account_identity_ok(account)?;
|
||||
StateMut::<Versions>::state(account)
|
||||
.map_err(|_| Error::InvalidAccountData)
|
||||
.map(|v| v.convert_to_current())
|
||||
}
|
||||
|
||||
pub fn data_from_account<T: ReadableAccount + StateMut<Versions>>(
|
||||
account: &T,
|
||||
) -> Result<Data, Error> {
|
||||
pub fn data_from_account(account: &Account) -> Result<Data, Error> {
|
||||
account_identity_ok(account)?;
|
||||
state_from_account(account).and_then(|ref s| data_from_state(s).map(|d| d.clone()))
|
||||
}
|
||||
|
@@ -1,14 +1,12 @@
|
||||
use {
|
||||
log::*,
|
||||
solana_sdk::{client::Client, commitment_config::CommitmentConfig, timing::duration_as_s},
|
||||
std::{
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc, RwLock,
|
||||
},
|
||||
thread::sleep,
|
||||
time::{Duration, Instant},
|
||||
use log::*;
|
||||
use solana_sdk::{client::Client, commitment_config::CommitmentConfig, timing::duration_as_s};
|
||||
use std::{
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc, RwLock,
|
||||
},
|
||||
thread::sleep,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
|
@@ -1,33 +1,27 @@
|
||||
use {
|
||||
crate::{
|
||||
rpc_config::{
|
||||
RpcSignatureSubscribeConfig, RpcTransactionLogsConfig, RpcTransactionLogsFilter,
|
||||
},
|
||||
rpc_response::{
|
||||
Response as RpcResponse, RpcLogsResponse, RpcSignatureResult, SlotInfo, SlotUpdate,
|
||||
},
|
||||
},
|
||||
log::*,
|
||||
serde::de::DeserializeOwned,
|
||||
serde_json::{
|
||||
json,
|
||||
value::Value::{Number, Object},
|
||||
Map, Value,
|
||||
},
|
||||
solana_sdk::signature::Signature,
|
||||
std::{
|
||||
marker::PhantomData,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
mpsc::{channel, Receiver},
|
||||
Arc, RwLock,
|
||||
},
|
||||
thread::JoinHandle,
|
||||
},
|
||||
thiserror::Error,
|
||||
tungstenite::{client::AutoStream, connect, Message, WebSocket},
|
||||
url::{ParseError, Url},
|
||||
use crate::{
|
||||
rpc_config::{RpcSignatureSubscribeConfig, RpcTransactionLogsConfig, RpcTransactionLogsFilter},
|
||||
rpc_response::{Response as RpcResponse, RpcLogsResponse, RpcSignatureResult, SlotInfo},
|
||||
};
|
||||
use log::*;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde_json::{
|
||||
json,
|
||||
value::Value::{Number, Object},
|
||||
Map, Value,
|
||||
};
|
||||
use solana_sdk::signature::Signature;
|
||||
use std::{
|
||||
marker::PhantomData,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
mpsc::{channel, Receiver},
|
||||
Arc, RwLock,
|
||||
},
|
||||
thread::JoinHandle,
|
||||
};
|
||||
use thiserror::Error;
|
||||
use tungstenite::{client::AutoStream, connect, Message, WebSocket};
|
||||
use url::{ParseError, Url};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum PubsubClientError {
|
||||
@@ -104,7 +98,7 @@ where
|
||||
}
|
||||
|
||||
pub fn send_unsubscribe(&self) -> Result<(), PubsubClientError> {
|
||||
let method = format!("{}Unsubscribe", self.operation);
|
||||
let method = format!("{}Unubscribe", self.operation);
|
||||
self.socket
|
||||
.write()
|
||||
.unwrap()
|
||||
@@ -342,54 +336,6 @@ impl PubsubClient {
|
||||
|
||||
Ok((result, receiver))
|
||||
}
|
||||
|
||||
pub fn slot_updates_subscribe(
|
||||
url: &str,
|
||||
handler: impl Fn(SlotUpdate) + Send + 'static,
|
||||
) -> Result<PubsubClientSubscription<SlotUpdate>, PubsubClientError> {
|
||||
let url = Url::parse(url)?;
|
||||
let (socket, _response) = connect(url)?;
|
||||
|
||||
let socket = Arc::new(RwLock::new(socket));
|
||||
let exit = Arc::new(AtomicBool::new(false));
|
||||
let exit_clone = exit.clone();
|
||||
let subscription_id = PubsubClientSubscription::<SlotUpdate>::send_subscribe(
|
||||
&socket,
|
||||
json!({
|
||||
"jsonrpc":"2.0","id":1,"method":"slotsUpdatesSubscribe","params":[]
|
||||
})
|
||||
.to_string(),
|
||||
)?;
|
||||
|
||||
let t_cleanup = {
|
||||
let socket = socket.clone();
|
||||
std::thread::spawn(move || {
|
||||
loop {
|
||||
if exit_clone.load(Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
match PubsubClientSubscription::read_message(&socket) {
|
||||
Ok(message) => handler(message),
|
||||
Err(err) => {
|
||||
info!("receive error: {:?}", err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info!("websocket - exited receive loop");
|
||||
})
|
||||
};
|
||||
|
||||
Ok(PubsubClientSubscription {
|
||||
message_type: PhantomData,
|
||||
operation: "slotsUpdates",
|
||||
socket,
|
||||
subscription_id,
|
||||
t_cleanup: Some(t_cleanup),
|
||||
exit,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@@ -1,9 +1,7 @@
|
||||
use {
|
||||
crate::{rpc_config::RpcLargestAccountsFilter, rpc_response::RpcAccountBalance},
|
||||
std::{
|
||||
collections::HashMap,
|
||||
time::{Duration, SystemTime},
|
||||
},
|
||||
use crate::{rpc_config::RpcLargestAccountsFilter, rpc_response::RpcAccountBalance};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
time::{Duration, SystemTime},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@@ -1,47 +1,50 @@
|
||||
use {
|
||||
crate::{
|
||||
client_error::{ClientError, ClientErrorKind, Result as ClientResult},
|
||||
http_sender::HttpSender,
|
||||
mock_sender::{MockSender, Mocks},
|
||||
rpc_config::RpcAccountInfoConfig,
|
||||
rpc_config::*,
|
||||
rpc_request::{RpcError, RpcRequest, RpcResponseErrorData, TokenAccountsFilter},
|
||||
rpc_response::*,
|
||||
rpc_sender::RpcSender,
|
||||
use crate::{
|
||||
client_error::{ClientError, ClientErrorKind, Result as ClientResult},
|
||||
http_sender::HttpSender,
|
||||
mock_sender::{MockSender, Mocks},
|
||||
rpc_config::RpcAccountInfoConfig,
|
||||
rpc_config::{
|
||||
RpcGetConfirmedSignaturesForAddress2Config, RpcLargestAccountsConfig,
|
||||
RpcProgramAccountsConfig, RpcSendTransactionConfig, RpcSimulateTransactionConfig,
|
||||
RpcTokenAccountsFilter,
|
||||
},
|
||||
bincode::serialize,
|
||||
indicatif::{ProgressBar, ProgressStyle},
|
||||
log::*,
|
||||
serde_json::{json, Value},
|
||||
solana_account_decoder::{
|
||||
parse_token::{TokenAccountType, UiTokenAccount, UiTokenAmount},
|
||||
UiAccount, UiAccountData, UiAccountEncoding,
|
||||
},
|
||||
solana_sdk::{
|
||||
account::Account,
|
||||
clock::{Epoch, Slot, UnixTimestamp, DEFAULT_MS_PER_SLOT, MAX_HASH_AGE_IN_SECONDS},
|
||||
commitment_config::{CommitmentConfig, CommitmentLevel},
|
||||
epoch_info::EpochInfo,
|
||||
epoch_schedule::EpochSchedule,
|
||||
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
||||
hash::Hash,
|
||||
pubkey::Pubkey,
|
||||
signature::Signature,
|
||||
transaction::{self, uses_durable_nonce, Transaction},
|
||||
},
|
||||
solana_transaction_status::{
|
||||
EncodedConfirmedBlock, EncodedConfirmedTransaction, TransactionStatus, UiConfirmedBlock,
|
||||
UiTransactionEncoding,
|
||||
},
|
||||
solana_vote_program::vote_state::MAX_LOCKOUT_HISTORY,
|
||||
std::{
|
||||
cmp::min,
|
||||
net::SocketAddr,
|
||||
str::FromStr,
|
||||
sync::RwLock,
|
||||
thread::sleep,
|
||||
time::{Duration, Instant},
|
||||
rpc_request::{RpcError, RpcRequest, RpcResponseErrorData, TokenAccountsFilter},
|
||||
rpc_response::*,
|
||||
rpc_sender::RpcSender,
|
||||
};
|
||||
use bincode::serialize;
|
||||
use indicatif::{ProgressBar, ProgressStyle};
|
||||
use log::*;
|
||||
use serde_json::{json, Value};
|
||||
use solana_account_decoder::{
|
||||
parse_token::{TokenAccountType, UiTokenAccount, UiTokenAmount},
|
||||
UiAccount, UiAccountData, UiAccountEncoding,
|
||||
};
|
||||
use solana_sdk::{
|
||||
account::Account,
|
||||
clock::{
|
||||
Slot, UnixTimestamp, DEFAULT_TICKS_PER_SECOND, DEFAULT_TICKS_PER_SLOT,
|
||||
MAX_HASH_AGE_IN_SECONDS,
|
||||
},
|
||||
commitment_config::{CommitmentConfig, CommitmentLevel},
|
||||
epoch_info::EpochInfo,
|
||||
epoch_schedule::EpochSchedule,
|
||||
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
||||
hash::Hash,
|
||||
pubkey::Pubkey,
|
||||
signature::Signature,
|
||||
transaction::{self, uses_durable_nonce, Transaction},
|
||||
};
|
||||
use solana_transaction_status::{
|
||||
EncodedConfirmedBlock, EncodedConfirmedTransaction, TransactionStatus, UiTransactionEncoding,
|
||||
};
|
||||
use solana_vote_program::vote_state::MAX_LOCKOUT_HISTORY;
|
||||
use std::{
|
||||
cmp::min,
|
||||
net::SocketAddr,
|
||||
sync::RwLock,
|
||||
thread::sleep,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
pub struct RpcClient {
|
||||
@@ -123,13 +126,6 @@ impl RpcClient {
|
||||
Self::new(get_rpc_request_str(addr, false))
|
||||
}
|
||||
|
||||
pub fn new_socket_with_commitment(
|
||||
addr: SocketAddr,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> Self {
|
||||
Self::new_with_commitment(get_rpc_request_str(addr, false), commitment_config)
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -404,67 +400,6 @@ impl RpcClient {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_block_height(&self) -> ClientResult<u64> {
|
||||
self.get_block_height_with_commitment(self.commitment_config)
|
||||
}
|
||||
|
||||
pub fn get_block_height_with_commitment(
|
||||
&self,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> ClientResult<u64> {
|
||||
self.send(
|
||||
RpcRequest::GetBlockHeight,
|
||||
json!([self.maybe_map_commitment(commitment_config)?]),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_slot_leaders(&self, start_slot: Slot, limit: u64) -> ClientResult<Vec<Pubkey>> {
|
||||
self.send(RpcRequest::GetSlotLeaders, json!([start_slot, limit]))
|
||||
.and_then(|slot_leaders: Vec<String>| {
|
||||
slot_leaders
|
||||
.iter()
|
||||
.map(|slot_leader| {
|
||||
Pubkey::from_str(slot_leader).map_err(|err| {
|
||||
ClientErrorKind::Custom(format!(
|
||||
"pubkey deserialization failed: {}",
|
||||
err
|
||||
))
|
||||
.into()
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
/// Get block production for the current epoch
|
||||
pub fn get_block_production(&self) -> RpcResult<RpcBlockProduction> {
|
||||
self.send(RpcRequest::GetBlockProduction, Value::Null)
|
||||
}
|
||||
|
||||
pub fn get_block_production_with_config(
|
||||
&self,
|
||||
config: RpcBlockProductionConfig,
|
||||
) -> RpcResult<RpcBlockProduction> {
|
||||
self.send(RpcRequest::GetBlockProduction, json!(config))
|
||||
}
|
||||
|
||||
pub fn get_stake_activation(
|
||||
&self,
|
||||
stake_account: Pubkey,
|
||||
epoch: Option<Epoch>,
|
||||
) -> ClientResult<RpcStakeActivation> {
|
||||
self.send(
|
||||
RpcRequest::GetStakeActivation,
|
||||
json!([
|
||||
stake_account.to_string(),
|
||||
RpcEpochConfig {
|
||||
epoch,
|
||||
commitment: Some(self.commitment_config),
|
||||
}
|
||||
]),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn supply(&self) -> RpcResult<RpcSupply> {
|
||||
self.supply_with_commitment(self.commitment_config)
|
||||
}
|
||||
@@ -479,17 +414,10 @@ impl RpcClient {
|
||||
)
|
||||
}
|
||||
|
||||
#[deprecated(since = "1.5.19", note = "Please use RpcClient::supply() instead")]
|
||||
#[allow(deprecated)]
|
||||
pub fn total_supply(&self) -> ClientResult<u64> {
|
||||
self.total_supply_with_commitment(self.commitment_config)
|
||||
}
|
||||
|
||||
#[deprecated(
|
||||
since = "1.5.19",
|
||||
note = "Please use RpcClient::supply_with_commitment() instead"
|
||||
)]
|
||||
#[allow(deprecated)]
|
||||
pub fn total_supply_with_commitment(
|
||||
&self,
|
||||
commitment_config: CommitmentConfig,
|
||||
@@ -521,17 +449,10 @@ impl RpcClient {
|
||||
&self,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> ClientResult<RpcVoteAccountStatus> {
|
||||
self.get_vote_accounts_with_config(RpcGetVoteAccountsConfig {
|
||||
commitment: Some(self.maybe_map_commitment(commitment_config)?),
|
||||
..RpcGetVoteAccountsConfig::default()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_vote_accounts_with_config(
|
||||
&self,
|
||||
config: RpcGetVoteAccountsConfig,
|
||||
) -> ClientResult<RpcVoteAccountStatus> {
|
||||
self.send(RpcRequest::GetVoteAccounts, json!([config]))
|
||||
self.send(
|
||||
RpcRequest::GetVoteAccounts,
|
||||
json!([self.maybe_map_commitment(commitment_config)?]),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn wait_for_max_stake(
|
||||
@@ -582,14 +503,6 @@ impl RpcClient {
|
||||
self.send(RpcRequest::GetConfirmedBlock, json!([slot, encoding]))
|
||||
}
|
||||
|
||||
pub fn get_confirmed_block_with_config(
|
||||
&self,
|
||||
slot: Slot,
|
||||
config: RpcConfirmedBlockConfig,
|
||||
) -> ClientResult<UiConfirmedBlock> {
|
||||
self.send(RpcRequest::GetConfirmedBlock, json!([slot, config]))
|
||||
}
|
||||
|
||||
pub fn get_confirmed_blocks(
|
||||
&self,
|
||||
start_slot: Slot,
|
||||
@@ -601,24 +514,6 @@ impl RpcClient {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_confirmed_blocks_with_commitment(
|
||||
&self,
|
||||
start_slot: Slot,
|
||||
end_slot: Option<Slot>,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> ClientResult<Vec<Slot>> {
|
||||
let json = if end_slot.is_some() {
|
||||
json!([
|
||||
start_slot,
|
||||
end_slot,
|
||||
self.maybe_map_commitment(commitment_config)?
|
||||
])
|
||||
} else {
|
||||
json!([start_slot, self.maybe_map_commitment(commitment_config)?])
|
||||
};
|
||||
self.send(RpcRequest::GetConfirmedBlocks, json)
|
||||
}
|
||||
|
||||
pub fn get_confirmed_blocks_with_limit(
|
||||
&self,
|
||||
start_slot: Slot,
|
||||
@@ -630,27 +525,6 @@ impl RpcClient {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_confirmed_blocks_with_limit_and_commitment(
|
||||
&self,
|
||||
start_slot: Slot,
|
||||
limit: usize,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> ClientResult<Vec<Slot>> {
|
||||
self.send(
|
||||
RpcRequest::GetConfirmedBlocksWithLimit,
|
||||
json!([
|
||||
start_slot,
|
||||
limit,
|
||||
self.maybe_map_commitment(commitment_config)?
|
||||
]),
|
||||
)
|
||||
}
|
||||
|
||||
#[deprecated(
|
||||
since = "1.5.19",
|
||||
note = "Please use RpcClient::get_confirmed_signatures_for_address2() instead"
|
||||
)]
|
||||
#[allow(deprecated)]
|
||||
pub fn get_confirmed_signatures_for_address(
|
||||
&self,
|
||||
address: &Pubkey,
|
||||
@@ -692,7 +566,6 @@ impl RpcClient {
|
||||
before: config.before.map(|signature| signature.to_string()),
|
||||
until: config.until.map(|signature| signature.to_string()),
|
||||
limit: config.limit,
|
||||
commitment: config.commitment,
|
||||
};
|
||||
|
||||
let result: Vec<RpcConfirmedTransactionStatusWithSignature> = self.send(
|
||||
@@ -714,17 +587,6 @@ impl RpcClient {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_confirmed_transaction_with_config(
|
||||
&self,
|
||||
signature: &Signature,
|
||||
config: RpcConfirmedTransactionConfig,
|
||||
) -> ClientResult<EncodedConfirmedTransaction> {
|
||||
self.send(
|
||||
RpcRequest::GetConfirmedTransaction,
|
||||
json!([signature.to_string(), config]),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_block_time(&self, slot: Slot) -> ClientResult<UnixTimestamp> {
|
||||
let request = RpcRequest::GetBlockTime;
|
||||
let response = self.sender.send(request, json!([slot]));
|
||||
@@ -768,34 +630,16 @@ impl RpcClient {
|
||||
slot: Option<Slot>,
|
||||
commitment_config: CommitmentConfig,
|
||||
) -> ClientResult<Option<RpcLeaderSchedule>> {
|
||||
self.get_leader_schedule_with_config(
|
||||
slot,
|
||||
RpcLeaderScheduleConfig {
|
||||
commitment: Some(self.maybe_map_commitment(commitment_config)?),
|
||||
..RpcLeaderScheduleConfig::default()
|
||||
},
|
||||
self.send(
|
||||
RpcRequest::GetLeaderSchedule,
|
||||
json!([slot, self.maybe_map_commitment(commitment_config)?]),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_leader_schedule_with_config(
|
||||
&self,
|
||||
slot: Option<Slot>,
|
||||
config: RpcLeaderScheduleConfig,
|
||||
) -> ClientResult<Option<RpcLeaderSchedule>> {
|
||||
self.send(RpcRequest::GetLeaderSchedule, json!([slot, config]))
|
||||
}
|
||||
|
||||
pub fn get_epoch_schedule(&self) -> ClientResult<EpochSchedule> {
|
||||
self.send(RpcRequest::GetEpochSchedule, Value::Null)
|
||||
}
|
||||
|
||||
pub fn get_recent_performance_samples(
|
||||
&self,
|
||||
limit: Option<usize>,
|
||||
) -> ClientResult<Vec<RpcPerfSample>> {
|
||||
self.send(RpcRequest::GetRecentPerformanceSamples, json!([limit]))
|
||||
}
|
||||
|
||||
pub fn get_identity(&self) -> ClientResult<Pubkey> {
|
||||
let rpc_identity: RpcIdentity = self.send(RpcRequest::GetIdentity, Value::Null)?;
|
||||
|
||||
@@ -815,27 +659,6 @@ impl RpcClient {
|
||||
self.send(RpcRequest::GetInflationRate, Value::Null)
|
||||
}
|
||||
|
||||
pub fn get_inflation_reward(
|
||||
&self,
|
||||
addresses: &[Pubkey],
|
||||
epoch: Option<Epoch>,
|
||||
) -> ClientResult<Vec<Option<RpcInflationReward>>> {
|
||||
let addresses: Vec<_> = addresses
|
||||
.iter()
|
||||
.map(|address| address.to_string())
|
||||
.collect();
|
||||
self.send(
|
||||
RpcRequest::GetInflationReward,
|
||||
json!([
|
||||
addresses,
|
||||
RpcEpochConfig {
|
||||
epoch,
|
||||
commitment: Some(self.commitment_config),
|
||||
}
|
||||
]),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_version(&self) -> ClientResult<RpcVersionInfo> {
|
||||
self.send(RpcRequest::GetVersion, Value::Null)
|
||||
}
|
||||
@@ -942,14 +765,6 @@ impl RpcClient {
|
||||
})?
|
||||
}
|
||||
|
||||
pub fn get_max_retransmit_slot(&self) -> ClientResult<Slot> {
|
||||
self.send(RpcRequest::GetMaxRetransmitSlot, Value::Null)
|
||||
}
|
||||
|
||||
pub fn get_max_shred_insert_slot(&self) -> ClientResult<Slot> {
|
||||
self.send(RpcRequest::GetMaxShredInsertSlot, Value::Null)
|
||||
}
|
||||
|
||||
pub fn get_multiple_accounts(&self, pubkeys: &[Pubkey]) -> ClientResult<Vec<Option<Account>>> {
|
||||
Ok(self
|
||||
.get_multiple_accounts_with_commitment(pubkeys, self.commitment_config)?
|
||||
@@ -1028,11 +843,12 @@ impl RpcClient {
|
||||
self.get_program_accounts_with_config(
|
||||
pubkey,
|
||||
RpcProgramAccountsConfig {
|
||||
filters: None,
|
||||
account_config: RpcAccountInfoConfig {
|
||||
encoding: Some(UiAccountEncoding::Base64Zstd),
|
||||
encoding: Some(UiAccountEncoding::Base64),
|
||||
commitment: Some(self.commitment_config),
|
||||
..RpcAccountInfoConfig::default()
|
||||
},
|
||||
..RpcProgramAccountsConfig::default()
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -1042,10 +858,7 @@ impl RpcClient {
|
||||
pubkey: &Pubkey,
|
||||
config: RpcProgramAccountsConfig,
|
||||
) -> ClientResult<Vec<(Pubkey, Account)>> {
|
||||
let commitment = config
|
||||
.account_config
|
||||
.commitment
|
||||
.unwrap_or_else(|| self.commitment());
|
||||
let commitment = config.account_config.commitment.unwrap_or_default();
|
||||
let commitment = self.maybe_map_commitment(commitment)?;
|
||||
let account_config = RpcAccountInfoConfig {
|
||||
commitment: Some(commitment),
|
||||
@@ -1095,7 +908,6 @@ impl RpcClient {
|
||||
blockhash,
|
||||
fee_calculator,
|
||||
last_valid_slot,
|
||||
..
|
||||
},
|
||||
}) = self
|
||||
.send::<Response<RpcFees>>(
|
||||
@@ -1103,19 +915,6 @@ impl RpcClient {
|
||||
json!([self.maybe_map_commitment(commitment_config)?]),
|
||||
) {
|
||||
(context, blockhash, fee_calculator, last_valid_slot)
|
||||
} else if let Ok(Response {
|
||||
context,
|
||||
value:
|
||||
DeprecatedRpcFees {
|
||||
blockhash,
|
||||
fee_calculator,
|
||||
last_valid_slot,
|
||||
},
|
||||
}) = self.send::<Response<DeprecatedRpcFees>>(
|
||||
RpcRequest::GetFees,
|
||||
json!([self.maybe_map_commitment(commitment_config)?]),
|
||||
) {
|
||||
(context, blockhash, fee_calculator, last_valid_slot)
|
||||
} else if let Ok(Response {
|
||||
context,
|
||||
value:
|
||||
@@ -1200,7 +999,9 @@ impl RpcClient {
|
||||
debug!("Got same blockhash ({:?}), will retry...", blockhash);
|
||||
|
||||
// Retry ~twice during a slot
|
||||
sleep(Duration::from_millis(DEFAULT_MS_PER_SLOT / 2));
|
||||
sleep(Duration::from_millis(
|
||||
500 * DEFAULT_TICKS_PER_SLOT / DEFAULT_TICKS_PER_SECOND,
|
||||
));
|
||||
num_retries += 1;
|
||||
}
|
||||
Err(RpcError::ForUser(format!(
|
||||
@@ -1411,64 +1212,6 @@ impl RpcClient {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn request_airdrop(&self, pubkey: &Pubkey, lamports: u64) -> ClientResult<Signature> {
|
||||
self.request_airdrop_with_config(
|
||||
pubkey,
|
||||
lamports,
|
||||
RpcRequestAirdropConfig {
|
||||
commitment: Some(self.commitment_config),
|
||||
..RpcRequestAirdropConfig::default()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn request_airdrop_with_blockhash(
|
||||
&self,
|
||||
pubkey: &Pubkey,
|
||||
lamports: u64,
|
||||
recent_blockhash: &Hash,
|
||||
) -> ClientResult<Signature> {
|
||||
self.request_airdrop_with_config(
|
||||
pubkey,
|
||||
lamports,
|
||||
RpcRequestAirdropConfig {
|
||||
commitment: Some(self.commitment_config),
|
||||
recent_blockhash: Some(recent_blockhash.to_string()),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn request_airdrop_with_config(
|
||||
&self,
|
||||
pubkey: &Pubkey,
|
||||
lamports: u64,
|
||||
config: RpcRequestAirdropConfig,
|
||||
) -> ClientResult<Signature> {
|
||||
let commitment = config.commitment.unwrap_or_default();
|
||||
let commitment = self.maybe_map_commitment(commitment)?;
|
||||
let config = RpcRequestAirdropConfig {
|
||||
commitment: Some(commitment),
|
||||
..config
|
||||
};
|
||||
self.send(
|
||||
RpcRequest::RequestAirdrop,
|
||||
json!([pubkey.to_string(), lamports, config]),
|
||||
)
|
||||
.and_then(|signature: String| {
|
||||
Signature::from_str(&signature).map_err(|err| {
|
||||
ClientErrorKind::Custom(format!("signature deserialization failed: {}", err)).into()
|
||||
})
|
||||
})
|
||||
.map_err(|_| {
|
||||
RpcError::ForUser(
|
||||
"airdrop request failed. \
|
||||
This can happen when the rate limit is reached."
|
||||
.to_string(),
|
||||
)
|
||||
.into()
|
||||
})
|
||||
}
|
||||
|
||||
fn poll_balance_with_timeout_and_commitment(
|
||||
&self,
|
||||
pubkey: &Pubkey,
|
||||
@@ -1670,24 +1413,6 @@ impl RpcClient {
|
||||
commitment: CommitmentConfig,
|
||||
config: RpcSendTransactionConfig,
|
||||
) -> ClientResult<Signature> {
|
||||
let recent_blockhash = if uses_durable_nonce(transaction).is_some() {
|
||||
self.get_recent_blockhash_with_commitment(CommitmentConfig::processed())?
|
||||
.value
|
||||
.0
|
||||
} else {
|
||||
transaction.message.recent_blockhash
|
||||
};
|
||||
let signature = self.send_transaction_with_config(transaction, config)?;
|
||||
self.confirm_transaction_with_spinner(&signature, &recent_blockhash, commitment)?;
|
||||
Ok(signature)
|
||||
}
|
||||
|
||||
pub fn confirm_transaction_with_spinner(
|
||||
&self,
|
||||
signature: &Signature,
|
||||
recent_blockhash: &Hash,
|
||||
commitment: CommitmentConfig,
|
||||
) -> ClientResult<()> {
|
||||
let desired_confirmations = if commitment.is_finalized() {
|
||||
MAX_LOCKOUT_HISTORY + 1
|
||||
} else {
|
||||
@@ -1699,8 +1424,16 @@ impl RpcClient {
|
||||
|
||||
progress_bar.set_message(&format!(
|
||||
"[{}/{}] Finalizing transaction {}",
|
||||
confirmations, desired_confirmations, signature,
|
||||
confirmations, desired_confirmations, transaction.signatures[0],
|
||||
));
|
||||
let recent_blockhash = if uses_durable_nonce(transaction).is_some() {
|
||||
self.get_recent_blockhash_with_commitment(CommitmentConfig::processed())?
|
||||
.value
|
||||
.0
|
||||
} else {
|
||||
transaction.message.recent_blockhash
|
||||
};
|
||||
let signature = self.send_transaction_with_config(transaction, config)?;
|
||||
let (signature, status) = loop {
|
||||
// Get recent commitment in order to count confirmations for successful transactions
|
||||
let status = self
|
||||
@@ -1747,7 +1480,7 @@ impl RpcClient {
|
||||
{
|
||||
progress_bar.set_message("Transaction confirmed");
|
||||
progress_bar.finish_and_clear();
|
||||
return Ok(());
|
||||
return Ok(signature);
|
||||
}
|
||||
|
||||
progress_bar.set_message(&format!(
|
||||
@@ -1770,6 +1503,10 @@ impl RpcClient {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validator_exit(&self) -> ClientResult<bool> {
|
||||
self.send(RpcRequest::ValidatorExit, Value::Null)
|
||||
}
|
||||
|
||||
pub fn send<T>(&self, request: RpcRequest, params: Value) -> ClientResult<T>
|
||||
where
|
||||
T: serde::de::DeserializeOwned,
|
||||
@@ -1789,7 +1526,6 @@ pub struct GetConfirmedSignaturesForAddress2Config {
|
||||
pub before: Option<Signature>,
|
||||
pub until: Option<Signature>,
|
||||
pub limit: Option<usize>,
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
}
|
||||
|
||||
fn new_spinner_progress_bar() -> ProgressBar {
|
||||
@@ -1838,7 +1574,7 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::{client_error::ClientErrorKind, mock_sender::PUBKEY};
|
||||
use assert_matches::assert_matches;
|
||||
use jsonrpc_core::{futures::prelude::*, Error, IoHandler, Params};
|
||||
use jsonrpc_core::{Error, IoHandler, Params};
|
||||
use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBuilder};
|
||||
use serde_json::Number;
|
||||
use solana_sdk::{
|
||||
@@ -1849,35 +1585,20 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_send() {
|
||||
_test_send();
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
#[should_panic(expected = "can call blocking only when running on the multi-threaded runtime")]
|
||||
async fn test_send_async_current_thread_should_panic() {
|
||||
_test_send();
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_send_async_multi_thread() {
|
||||
_test_send();
|
||||
}
|
||||
|
||||
fn _test_send() {
|
||||
let (sender, receiver) = channel();
|
||||
thread::spawn(move || {
|
||||
let rpc_addr = "0.0.0.0:0".parse().unwrap();
|
||||
let mut io = IoHandler::default();
|
||||
// Successful request
|
||||
io.add_method("getBalance", |_params: Params| {
|
||||
future::ok(Value::Number(Number::from(50)))
|
||||
Ok(Value::Number(Number::from(50)))
|
||||
});
|
||||
// Failed request
|
||||
io.add_method("getRecentBlockhash", |params: Params| {
|
||||
if params != Params::None {
|
||||
future::err(Error::invalid_request())
|
||||
Err(Error::invalid_request())
|
||||
} else {
|
||||
future::ok(Value::String(
|
||||
Ok(Value::String(
|
||||
"deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx".to_string(),
|
||||
))
|
||||
}
|
||||
|
@@ -1,12 +1,10 @@
|
||||
use {
|
||||
crate::rpc_filter::RpcFilterType,
|
||||
solana_account_decoder::{UiAccountEncoding, UiDataSliceConfig},
|
||||
solana_sdk::{
|
||||
clock::{Epoch, Slot},
|
||||
commitment_config::{CommitmentConfig, CommitmentLevel},
|
||||
},
|
||||
solana_transaction_status::{TransactionDetails, UiTransactionEncoding},
|
||||
use crate::rpc_filter::RpcFilterType;
|
||||
use solana_account_decoder::{UiAccountEncoding, UiDataSliceConfig};
|
||||
use solana_sdk::{
|
||||
clock::Epoch,
|
||||
commitment_config::{CommitmentConfig, CommitmentLevel},
|
||||
};
|
||||
use solana_transaction_status::UiTransactionEncoding;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -23,80 +21,14 @@ pub struct RpcSendTransactionConfig {
|
||||
pub encoding: Option<UiTransactionEncoding>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcSimulateTransactionAccountsConfig {
|
||||
pub encoding: Option<UiAccountEncoding>,
|
||||
pub addresses: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcSimulateTransactionConfig {
|
||||
#[serde(default)]
|
||||
pub sig_verify: bool,
|
||||
#[serde(default)]
|
||||
pub replace_recent_blockhash: bool,
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
pub encoding: Option<UiTransactionEncoding>,
|
||||
pub accounts: Option<RpcSimulateTransactionAccountsConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcRequestAirdropConfig {
|
||||
pub recent_blockhash: Option<String>, // base-58 encoded blockhash
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcLeaderScheduleConfig {
|
||||
pub identity: Option<String>, // validator identity, as a base-58 encoded string
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcBlockProductionConfigRange {
|
||||
pub first_slot: Slot,
|
||||
pub last_slot: Option<Slot>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcBlockProductionConfig {
|
||||
pub identity: Option<String>, // validator identity, as a base-58 encoded string
|
||||
pub range: Option<RpcBlockProductionConfigRange>, // current epoch if `None`
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcGetVoteAccountsConfig {
|
||||
pub vote_pubkey: Option<String>, // validator vote address, as a base-58 encoded string
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum RpcLeaderScheduleConfigWrapper {
|
||||
SlotOnly(Option<Slot>),
|
||||
ConfigOnly(Option<RpcLeaderScheduleConfig>),
|
||||
}
|
||||
|
||||
impl RpcLeaderScheduleConfigWrapper {
|
||||
pub fn unzip(&self) -> (Option<Slot>, Option<RpcLeaderScheduleConfig>) {
|
||||
match &self {
|
||||
RpcLeaderScheduleConfigWrapper::SlotOnly(slot) => (*slot, None),
|
||||
RpcLeaderScheduleConfigWrapper::ConfigOnly(config) => (None, config.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
@@ -116,7 +48,7 @@ pub struct RpcLargestAccountsConfig {
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcEpochConfig {
|
||||
pub struct RpcStakeConfig {
|
||||
pub epoch: Option<Epoch>,
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
@@ -137,7 +69,6 @@ pub struct RpcProgramAccountsConfig {
|
||||
pub filters: Option<Vec<RpcFilterType>>,
|
||||
#[serde(flatten)]
|
||||
pub account_config: RpcAccountInfoConfig,
|
||||
pub with_context: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
@@ -176,101 +107,4 @@ pub struct RpcGetConfirmedSignaturesForAddress2Config {
|
||||
pub before: Option<String>, // Signature as base-58 string
|
||||
pub until: Option<String>, // Signature as base-58 string
|
||||
pub limit: Option<usize>,
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum RpcEncodingConfigWrapper<T> {
|
||||
Deprecated(Option<UiTransactionEncoding>),
|
||||
Current(Option<T>),
|
||||
}
|
||||
|
||||
impl<T: EncodingConfig + Default + Copy> RpcEncodingConfigWrapper<T> {
|
||||
pub fn convert_to_current(&self) -> T {
|
||||
match self {
|
||||
RpcEncodingConfigWrapper::Deprecated(encoding) => T::new_with_encoding(encoding),
|
||||
RpcEncodingConfigWrapper::Current(config) => config.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait EncodingConfig {
|
||||
fn new_with_encoding(encoding: &Option<UiTransactionEncoding>) -> Self;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcConfirmedBlockConfig {
|
||||
pub encoding: Option<UiTransactionEncoding>,
|
||||
pub transaction_details: Option<TransactionDetails>,
|
||||
pub rewards: Option<bool>,
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
}
|
||||
|
||||
impl EncodingConfig for RpcConfirmedBlockConfig {
|
||||
fn new_with_encoding(encoding: &Option<UiTransactionEncoding>) -> Self {
|
||||
Self {
|
||||
encoding: *encoding,
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RpcConfirmedBlockConfig {
|
||||
pub fn rewards_only() -> Self {
|
||||
Self {
|
||||
transaction_details: Some(TransactionDetails::None),
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rewards_with_commitment(commitment: Option<CommitmentConfig>) -> Self {
|
||||
Self {
|
||||
transaction_details: Some(TransactionDetails::None),
|
||||
commitment,
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RpcConfirmedBlockConfig> for RpcEncodingConfigWrapper<RpcConfirmedBlockConfig> {
|
||||
fn from(config: RpcConfirmedBlockConfig) -> Self {
|
||||
RpcEncodingConfigWrapper::Current(Some(config))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcConfirmedTransactionConfig {
|
||||
pub encoding: Option<UiTransactionEncoding>,
|
||||
#[serde(flatten)]
|
||||
pub commitment: Option<CommitmentConfig>,
|
||||
}
|
||||
|
||||
impl EncodingConfig for RpcConfirmedTransactionConfig {
|
||||
fn new_with_encoding(encoding: &Option<UiTransactionEncoding>) -> Self {
|
||||
Self {
|
||||
encoding: *encoding,
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum RpcConfirmedBlocksConfigWrapper {
|
||||
EndSlotOnly(Option<Slot>),
|
||||
CommitmentOnly(Option<CommitmentConfig>),
|
||||
}
|
||||
|
||||
impl RpcConfirmedBlocksConfigWrapper {
|
||||
pub fn unzip(&self) -> (Option<Slot>, Option<CommitmentConfig>) {
|
||||
match &self {
|
||||
RpcConfirmedBlocksConfigWrapper::EndSlotOnly(end_slot) => (*end_slot, None),
|
||||
RpcConfirmedBlocksConfigWrapper::CommitmentOnly(commitment) => (None, *commitment),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,22 +1,18 @@
|
||||
//! Implementation defined RPC server errors
|
||||
|
||||
use {
|
||||
crate::rpc_response::RpcSimulateTransactionResult,
|
||||
jsonrpc_core::{Error, ErrorCode},
|
||||
solana_sdk::clock::Slot,
|
||||
};
|
||||
use crate::rpc_response::RpcSimulateTransactionResult;
|
||||
use jsonrpc_core::{Error, ErrorCode};
|
||||
use solana_sdk::clock::Slot;
|
||||
|
||||
pub const JSON_RPC_SERVER_ERROR_BLOCK_CLEANED_UP: i64 = -32001;
|
||||
pub const JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE: i64 = -32002;
|
||||
pub const JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE: i64 = -32003;
|
||||
pub const JSON_RPC_SERVER_ERROR_BLOCK_NOT_AVAILABLE: i64 = -32004;
|
||||
pub const JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY: i64 = -32005;
|
||||
pub const JSON_RPC_SERVER_ERROR_NODE_UNHEALTHLY: i64 = -32005;
|
||||
pub const JSON_RPC_SERVER_ERROR_TRANSACTION_PRECOMPILE_VERIFICATION_FAILURE: i64 = -32006;
|
||||
pub const JSON_RPC_SERVER_ERROR_SLOT_SKIPPED: i64 = -32007;
|
||||
pub const JSON_RPC_SERVER_ERROR_NO_SNAPSHOT: i64 = -32008;
|
||||
pub const JSON_RPC_SERVER_ERROR_LONG_TERM_STORAGE_SLOT_SKIPPED: i64 = -32009;
|
||||
pub const JSON_RPC_SERVER_ERROR_KEY_EXCLUDED_FROM_SECONDARY_INDEX: i64 = -32010;
|
||||
pub const JSON_RPC_SERVER_ERROR_TRANSACTION_HISTORY_NOT_AVAILABLE: i64 = -32011;
|
||||
|
||||
pub enum RpcCustomError {
|
||||
BlockCleanedUp {
|
||||
@@ -42,10 +38,6 @@ pub enum RpcCustomError {
|
||||
LongTermStorageSlotSkipped {
|
||||
slot: Slot,
|
||||
},
|
||||
KeyExcludedFromSecondaryIndex {
|
||||
index_key: String,
|
||||
},
|
||||
TransactionHistoryNotAvailable,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
@@ -88,7 +80,7 @@ impl From<RpcCustomError> for Error {
|
||||
data: None,
|
||||
},
|
||||
RpcCustomError::NodeUnhealthy { num_slots_behind } => Self {
|
||||
code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY),
|
||||
code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_NODE_UNHEALTHLY),
|
||||
message: if let Some(num_slots_behind) = num_slots_behind {
|
||||
format!("Node is behind by {} slots", num_slots_behind)
|
||||
} else {
|
||||
@@ -123,24 +115,6 @@ impl From<RpcCustomError> for Error {
|
||||
message: format!("Slot {} was skipped, or missing in long-term storage", slot),
|
||||
data: None,
|
||||
},
|
||||
RpcCustomError::KeyExcludedFromSecondaryIndex { index_key } => Self {
|
||||
code: ErrorCode::ServerError(
|
||||
JSON_RPC_SERVER_ERROR_KEY_EXCLUDED_FROM_SECONDARY_INDEX,
|
||||
),
|
||||
message: format!(
|
||||
"{} excluded from account secondary indexes; \
|
||||
this RPC method unavailable for key",
|
||||
index_key
|
||||
),
|
||||
data: None,
|
||||
},
|
||||
RpcCustomError::TransactionHistoryNotAvailable => Self {
|
||||
code: ErrorCode::ServerError(
|
||||
JSON_RPC_SERVER_ERROR_TRANSACTION_HISTORY_NOT_AVAILABLE,
|
||||
),
|
||||
message: "Transaction history is not available from this node".to_string(),
|
||||
data: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -16,15 +16,10 @@ impl RpcFilterType {
|
||||
match encoding {
|
||||
MemcmpEncoding::Binary => {
|
||||
let MemcmpEncodedBytes::Binary(bytes) = &compare.bytes;
|
||||
|
||||
if bytes.len() > 128 {
|
||||
Err(RpcFilterError::Base58DataTooLarge)
|
||||
} else {
|
||||
bs58::decode(&bytes)
|
||||
.into_vec()
|
||||
.map(|_| ())
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
bs58::decode(&bytes)
|
||||
.into_vec()
|
||||
.map(|_| ())
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,12 +27,10 @@ impl RpcFilterType {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, PartialEq, Debug)]
|
||||
#[derive(Error, Debug)]
|
||||
pub enum RpcFilterError {
|
||||
#[error("bs58 decode error")]
|
||||
DecodeError(#[from] bs58::decode::Error),
|
||||
#[error("encoded binary (base 58) data should be less than 129 bytes")]
|
||||
Base58DataTooLarge,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
@@ -147,36 +140,4 @@ mod tests {
|
||||
}
|
||||
.bytes_match(&data));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_memcmp() {
|
||||
let base58_bytes = "\
|
||||
1111111111111111111111111111111111111111111111111111111111111111\
|
||||
1111111111111111111111111111111111111111111111111111111111111111";
|
||||
assert_eq!(base58_bytes.len(), 128);
|
||||
assert_eq!(
|
||||
RpcFilterType::Memcmp(Memcmp {
|
||||
offset: 0,
|
||||
bytes: MemcmpEncodedBytes::Binary(base58_bytes.to_string()),
|
||||
encoding: None,
|
||||
})
|
||||
.verify(),
|
||||
Ok(())
|
||||
);
|
||||
|
||||
let base58_bytes = "\
|
||||
1111111111111111111111111111111111111111111111111111111111111111\
|
||||
1111111111111111111111111111111111111111111111111111111111111111\
|
||||
1";
|
||||
assert_eq!(base58_bytes.len(), 129);
|
||||
assert_eq!(
|
||||
RpcFilterType::Memcmp(Memcmp {
|
||||
offset: 0,
|
||||
bytes: MemcmpEncodedBytes::Binary(base58_bytes.to_string()),
|
||||
encoding: None,
|
||||
})
|
||||
.verify(),
|
||||
Err(RpcFilterError::Base58DataTooLarge)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user