Compare commits
323 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
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 |
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
|
||||
|
1628
Cargo.lock
generated
1628
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",
|
||||
|
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.
|
||||
|
147
SECURITY.md
147
SECURITY.md
@@ -1,147 +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
|
||||
|
||||
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.0"
|
||||
version = "1.5.6"
|
||||
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.0" }
|
||||
solana-sdk = { path = "../sdk", version = "1.6.0" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "1.6.0" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.6.0" }
|
||||
spl-token-v2-0 = { package = "spl-token", version = "=3.1.0", features = ["no-entrypoint"] }
|
||||
solana-config-program = { path = "../programs/config", version = "1.5.6" }
|
||||
solana-sdk = { path = "../sdk", version = "1.5.6" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "1.5.6" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.5.6" }
|
||||
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)]
|
||||
@@ -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 {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
parse_account_data::{ParsableAccount, ParseAccountError},
|
||||
StringAmount, StringDecimals,
|
||||
StringAmount,
|
||||
};
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use spl_token_v2_0::{
|
||||
@@ -158,64 +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 s = real_number_string(amount, decimals);
|
||||
let zeros_trimmed = s.trim_end_matches('0');
|
||||
let decimal_trimmed = zeros_trimmed.trim_end_matches('.');
|
||||
decimal_trimmed.to_string()
|
||||
}
|
||||
|
||||
#[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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,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,
|
||||
@@ -355,51 +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(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);
|
||||
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.0"
|
||||
version = "1.5.6"
|
||||
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.0" }
|
||||
solana-runtime = { path = "../runtime", version = "1.6.0" }
|
||||
solana-measure = { path = "../measure", version = "1.6.0" }
|
||||
solana-sdk = { path = "../sdk", version = "1.6.0" }
|
||||
solana-version = { path = "../version", version = "1.6.0" }
|
||||
rayon = "1.4.0"
|
||||
solana-logger = { path = "../logger", version = "1.5.6" }
|
||||
solana-runtime = { path = "../runtime", version = "1.5.6" }
|
||||
solana-measure = { path = "../measure", version = "1.5.6" }
|
||||
solana-sdk = { path = "../sdk", version = "1.5.6" }
|
||||
solana-version = { path = "../version", version = "1.5.6" }
|
||||
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};
|
||||
@@ -103,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,34 +0,0 @@
|
||||
[package]
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-accounts-cluster-bench"
|
||||
version = "1.6.0"
|
||||
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.0" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.6.0" }
|
||||
solana-client = { path = "../client", version = "1.6.0" }
|
||||
solana-core = { path = "../core", version = "1.6.0" }
|
||||
solana-measure = { path = "../measure", version = "1.6.0" }
|
||||
solana-logger = { path = "../logger", version = "1.6.0" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.6.0" }
|
||||
solana-faucet = { path = "../faucet", version = "1.6.0" }
|
||||
solana-runtime = { path = "../runtime", version = "1.6.0" }
|
||||
solana-sdk = { path = "../sdk", version = "1.6.0" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "1.6.0" }
|
||||
solana-version = { path = "../version", version = "1.6.0" }
|
||||
spl-token-v2-0 = { package = "spl-token", version = "=3.1.0", features = ["no-entrypoint"] }
|
||||
|
||||
[dev-dependencies]
|
||||
solana-local-cluster = { path = "../local-cluster", version = "1.6.0" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
@@ -1,605 +0,0 @@
|
||||
#![allow(clippy::integer_arithmetic)]
|
||||
use clap::{crate_description, crate_name, value_t, value_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 spl_token_v2_0::solana_program::pubkey::Pubkey as SplPubkey;
|
||||
use std::{
|
||||
net::SocketAddr,
|
||||
process::exit,
|
||||
sync::{
|
||||
atomic::{AtomicBool, AtomicU64, Ordering},
|
||||
Arc, RwLock,
|
||||
},
|
||||
thread::{sleep, Builder, JoinHandle},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
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)
|
||||
.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
|
||||
})
|
||||
.flatten()
|
||||
.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()
|
||||
}
|
||||
}
|
||||
|
||||
fn make_message(
|
||||
keypair: &Keypair,
|
||||
num_instructions: usize,
|
||||
balance: u64,
|
||||
maybe_space: Option<u64>,
|
||||
mint: Option<Pubkey>,
|
||||
) -> (Message, Vec<Keypair>) {
|
||||
let space = maybe_space.unwrap_or_else(|| thread_rng().gen_range(0, 1000));
|
||||
|
||||
let (instructions, new_keypairs): (Vec<_>, Vec<_>) = (0..num_instructions)
|
||||
.into_iter()
|
||||
.map(|_| {
|
||||
let new_keypair = Keypair::new();
|
||||
|
||||
let program_id = if mint.is_some() {
|
||||
inline_spl_token_v2_0::id()
|
||||
} else {
|
||||
system_program::id()
|
||||
};
|
||||
let mut instructions = vec![system_instruction::create_account(
|
||||
&keypair.pubkey(),
|
||||
&new_keypair.pubkey(),
|
||||
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(&new_keypair.pubkey()),
|
||||
&spl_token_v2_0_pubkey(&mint_address),
|
||||
&SplPubkey::new_unique(),
|
||||
)
|
||||
.unwrap(),
|
||||
));
|
||||
}
|
||||
|
||||
(instructions, new_keypair)
|
||||
})
|
||||
.unzip();
|
||||
let instructions: Vec<_> = instructions.into_iter().flatten().collect();
|
||||
|
||||
(
|
||||
Message::new(&instructions, Some(&keypair.pubkey())),
|
||||
new_keypairs,
|
||||
)
|
||||
}
|
||||
|
||||
fn run_accounts_bench(
|
||||
entrypoint_addr: SocketAddr,
|
||||
faucet_addr: SocketAddr,
|
||||
keypair: &Keypair,
|
||||
iterations: usize,
|
||||
maybe_space: Option<u64>,
|
||||
batch_size: usize,
|
||||
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!("Targetting {}", 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_account_count = 0;
|
||||
let mut balance = client.get_balance(&keypair.pubkey()).unwrap_or(0);
|
||||
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")
|
||||
});
|
||||
|
||||
info!("Starting balance: {}", balance);
|
||||
|
||||
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 (message, _keypairs) =
|
||||
make_message(keypair, num_instructions, min_balance, maybe_space, mint);
|
||||
let fee = recent_blockhash.1.calculate_fee(&message);
|
||||
let lamports = min_balance + fee;
|
||||
|
||||
if balance < lamports || last_balance.elapsed().as_millis() > 2000 {
|
||||
if let Ok(b) = client.get_balance(&keypair.pubkey()) {
|
||||
balance = b;
|
||||
}
|
||||
last_balance = Instant::now();
|
||||
if balance < lamports {
|
||||
info!(
|
||||
"Balance {} is less than needed: {}, doing aidrop...",
|
||||
balance, lamports
|
||||
);
|
||||
if !airdrop_lamports(&client, &faucet_addr, keypair, 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;
|
||||
info!("creating {} new", num_to_create);
|
||||
let (txs, _new_keypairs): (Vec<_>, Vec<_>) = (0..num_to_create)
|
||||
.into_par_iter()
|
||||
.map(|_| {
|
||||
let (message, new_keypairs) =
|
||||
make_message(keypair, num_instructions, min_balance, maybe_space, mint);
|
||||
let signers: Vec<&Keypair> = new_keypairs
|
||||
.iter()
|
||||
.chain(std::iter::once(keypair))
|
||||
.collect();
|
||||
(
|
||||
Transaction::new(&signers, message, recent_blockhash.0),
|
||||
new_keypairs,
|
||||
)
|
||||
})
|
||||
.unzip();
|
||||
balance = balance.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_account_count += num_instructions * new_ids.len();
|
||||
} else {
|
||||
let _ = executor.drain_cleared();
|
||||
}
|
||||
|
||||
count += 1;
|
||||
if last_log.elapsed().as_millis() > 3000 {
|
||||
info!(
|
||||
"total_accounts: {} tx_sent_count: {} loop_count: {} balance: {}",
|
||||
total_account_count, tx_sent_count, count, balance
|
||||
);
|
||||
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)
|
||||
.value_name("FILE")
|
||||
.help("keypair file"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("batch_size")
|
||||
.long("batch_size")
|
||||
.takes_value(true)
|
||||
.value_name("BYTES")
|
||||
.help("Size of accounts to create"),
|
||||
)
|
||||
.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 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 keypair =
|
||||
read_keypair_file(&value_t_or_exit!(matches, "identity", String)).expect("bad keypair");
|
||||
|
||||
let rpc_addr = if !skip_gossip {
|
||||
info!("Finding cluster entry: {:?}", entrypoint_addr);
|
||||
let (gossip_nodes, _validators) = discover(
|
||||
None,
|
||||
Some(&entrypoint_addr),
|
||||
None,
|
||||
Some(60),
|
||||
None,
|
||||
Some(&entrypoint_addr),
|
||||
None,
|
||||
0,
|
||||
)
|
||||
.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,
|
||||
&keypair,
|
||||
iterations,
|
||||
space,
|
||||
batch_size,
|
||||
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 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,
|
||||
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.0"
|
||||
version = "1.5.6"
|
||||
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.0" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.6.0" }
|
||||
solana-streamer = { path = "../streamer", version = "1.6.0" }
|
||||
solana-perf = { path = "../perf", version = "1.6.0" }
|
||||
solana-ledger = { path = "../ledger", version = "1.6.0" }
|
||||
solana-logger = { path = "../logger", version = "1.6.0" }
|
||||
solana-runtime = { path = "../runtime", version = "1.6.0" }
|
||||
solana-measure = { path = "../measure", version = "1.6.0" }
|
||||
solana-sdk = { path = "../sdk", version = "1.6.0" }
|
||||
solana-version = { path = "../version", version = "1.6.0" }
|
||||
rayon = "1.4.0"
|
||||
solana-core = { path = "../core", version = "1.5.6" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.5.6" }
|
||||
solana-streamer = { path = "../streamer", version = "1.5.6" }
|
||||
solana-perf = { path = "../perf", version = "1.5.6" }
|
||||
solana-ledger = { path = "../ledger", version = "1.5.6" }
|
||||
solana-logger = { path = "../logger", version = "1.5.6" }
|
||||
solana-runtime = { path = "../runtime", version = "1.5.6" }
|
||||
solana-measure = { path = "../measure", version = "1.5.6" }
|
||||
solana-sdk = { path = "../sdk", version = "1.5.6" }
|
||||
solana-version = { path = "../version", version = "1.5.6" }
|
||||
|
||||
[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.0"
|
||||
version = "1.5.6"
|
||||
description = "Solana banks client"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
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.0" }
|
||||
solana-program = { path = "../sdk/program", version = "1.6.0" }
|
||||
solana-sdk = { path = "../sdk", version = "1.6.0" }
|
||||
tarpc = { version = "0.24.1", features = ["full"] }
|
||||
tokio = { version = "1.1", features = ["full"] }
|
||||
tokio-serde = { version = "0.8", features = ["bincode"] }
|
||||
solana-banks-interface = { path = "../banks-interface", version = "1.5.6" }
|
||||
solana-sdk = { path = "../sdk", version = "1.5.6" }
|
||||
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.0" }
|
||||
solana-banks-server = { path = "../banks-server", version = "1.6.0" }
|
||||
solana-runtime = { path = "../runtime", version = "1.5.6" }
|
||||
solana-banks-server = { path = "../banks-server", version = "1.5.6" }
|
||||
|
||||
[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.0"
|
||||
version = "1.5.6"
|
||||
description = "Solana banks RPC interface"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
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.0" }
|
||||
tarpc = { version = "0.24.1", features = ["full"] }
|
||||
serde = { version = "1.0.112", features = ["derive"] }
|
||||
solana-sdk = { path = "../sdk", version = "1.5.6" }
|
||||
tarpc = { version = "0.23.0", features = ["full"] }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1.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.0"
|
||||
version = "1.5.6"
|
||||
description = "Solana banks server"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
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.0" }
|
||||
solana-runtime = { path = "../runtime", version = "1.6.0" }
|
||||
solana-sdk = { path = "../sdk", version = "1.6.0" }
|
||||
solana-metrics = { path = "../metrics", version = "1.6.0" }
|
||||
tarpc = { version = "0.24.1", features = ["full"] }
|
||||
tokio = { version = "1.1", features = ["full"] }
|
||||
tokio-serde = { version = "0.8", features = ["bincode"] }
|
||||
tokio-stream = "0.1"
|
||||
solana-banks-interface = { path = "../banks-interface", version = "1.5.6" }
|
||||
solana-runtime = { path = "../runtime", version = "1.5.6" }
|
||||
solana-sdk = { path = "../sdk", version = "1.5.6" }
|
||||
solana-metrics = { path = "../metrics", version = "1.5.6" }
|
||||
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.0"
|
||||
version = "1.5.6"
|
||||
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.0" }
|
||||
solana-core = { path = "../core", version = "1.6.0" }
|
||||
solana-genesis = { path = "../genesis", version = "1.6.0" }
|
||||
solana-client = { path = "../client", version = "1.6.0" }
|
||||
solana-faucet = { path = "../faucet", version = "1.6.0" }
|
||||
solana-exchange-program = { path = "../programs/exchange", version = "1.6.0" }
|
||||
solana-logger = { path = "../logger", version = "1.6.0" }
|
||||
solana-metrics = { path = "../metrics", version = "1.6.0" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.6.0" }
|
||||
solana-runtime = { path = "../runtime", version = "1.6.0" }
|
||||
solana-sdk = { path = "../sdk", version = "1.6.0" }
|
||||
solana-version = { path = "../version", version = "1.6.0" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.5.6" }
|
||||
solana-core = { path = "../core", version = "1.5.6" }
|
||||
solana-genesis = { path = "../genesis", version = "1.5.6" }
|
||||
solana-client = { path = "../client", version = "1.5.6" }
|
||||
solana-faucet = { path = "../faucet", version = "1.5.6" }
|
||||
solana-exchange-program = { path = "../programs/exchange", version = "1.5.6" }
|
||||
solana-logger = { path = "../logger", version = "1.5.6" }
|
||||
solana-metrics = { path = "../metrics", version = "1.5.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.5.6" }
|
||||
solana-runtime = { path = "../runtime", version = "1.5.6" }
|
||||
solana-sdk = { path = "../sdk", version = "1.5.6" }
|
||||
solana-version = { path = "../version", version = "1.5.6" }
|
||||
|
||||
[dev-dependencies]
|
||||
solana-local-cluster = { path = "../local-cluster", version = "1.6.0" }
|
||||
solana-local-cluster = { path = "../local-cluster", version = "1.5.6" }
|
||||
|
||||
[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.0"
|
||||
version = "1.5.6"
|
||||
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.0" }
|
||||
solana-streamer = { path = "../streamer", version = "1.6.0" }
|
||||
solana-logger = { path = "../logger", version = "1.6.0" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.6.0" }
|
||||
solana-version = { path = "../version", version = "1.6.0" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.5.6" }
|
||||
solana-streamer = { path = "../streamer", version = "1.5.6" }
|
||||
solana-logger = { path = "../logger", version = "1.5.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.5.6" }
|
||||
solana-version = { path = "../version", version = "1.5.6" }
|
||||
|
||||
[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.0"
|
||||
version = "1.5.6"
|
||||
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.0" }
|
||||
solana-core = { path = "../core", version = "1.6.0" }
|
||||
solana-genesis = { path = "../genesis", version = "1.6.0" }
|
||||
solana-client = { path = "../client", version = "1.6.0" }
|
||||
solana-faucet = { path = "../faucet", version = "1.6.0" }
|
||||
solana-logger = { path = "../logger", version = "1.6.0" }
|
||||
solana-metrics = { path = "../metrics", version = "1.6.0" }
|
||||
solana-measure = { path = "../measure", version = "1.6.0" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.6.0" }
|
||||
solana-runtime = { path = "../runtime", version = "1.6.0" }
|
||||
solana-sdk = { path = "../sdk", version = "1.6.0" }
|
||||
solana-version = { path = "../version", version = "1.6.0" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.5.6" }
|
||||
solana-core = { path = "../core", version = "1.5.6" }
|
||||
solana-genesis = { path = "../genesis", version = "1.5.6" }
|
||||
solana-client = { path = "../client", version = "1.5.6" }
|
||||
solana-faucet = { path = "../faucet", version = "1.5.6" }
|
||||
solana-logger = { path = "../logger", version = "1.5.6" }
|
||||
solana-metrics = { path = "../metrics", version = "1.5.6" }
|
||||
solana-measure = { path = "../measure", version = "1.5.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.5.6" }
|
||||
solana-runtime = { path = "../runtime", version = "1.5.6" }
|
||||
solana-sdk = { path = "../sdk", version = "1.5.6" }
|
||||
solana-version = { path = "../version", version = "1.5.6" }
|
||||
|
||||
[dev-dependencies]
|
||||
serial_test = "0.4.0"
|
||||
solana-local-cluster = { path = "../local-cluster", version = "1.6.0" }
|
||||
serial_test_derive = "0.4.0"
|
||||
solana-local-cluster = { path = "../local-cluster", version = "1.5.6" }
|
||||
|
||||
[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()
|
||||
});
|
||||
|
@@ -1,45 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
here="$(dirname "$0")"
|
||||
src_root="$(readlink -f "${here}/..")"
|
||||
|
||||
cd "${src_root}"
|
||||
|
||||
source ci/rust-version.sh stable
|
||||
|
||||
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 +"$rust_stable" audit "${cargo_audit_ignores[@]}"
|
@@ -1,4 +1,4 @@
|
||||
FROM solanalabs/rust:1.50.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.50.0
|
||||
FROM rust:1.49.0
|
||||
|
||||
# Add Google Protocol Buffers for Libra's metrics library.
|
||||
ENV PROTOC_VERSION 3.8.0
|
||||
|
@@ -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
|
||||
|
@@ -25,8 +25,7 @@ snapshot_slot=1
|
||||
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.50.0
|
||||
stable_version=1.49.0
|
||||
fi
|
||||
|
||||
if [[ -n $RUST_NIGHTLY_VERSION ]]; then
|
||||
nightly_version="$RUST_NIGHTLY_VERSION"
|
||||
else
|
||||
nightly_version=2021-02-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
|
||||
@@ -62,24 +52,54 @@ else
|
||||
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
|
||||
}
|
||||
|
@@ -1,21 +1,20 @@
|
||||
[package]
|
||||
name = "solana-clap-utils"
|
||||
version = "1.6.0"
|
||||
version = "1.5.6"
|
||||
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.0" }
|
||||
solana-sdk = { path = "../sdk", version = "1.6.0" }
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "1.5.6" }
|
||||
solana-sdk = { path = "../sdk", version = "1.5.6" }
|
||||
thiserror = "1.0.21"
|
||||
tiny-bip39 = "0.8.0"
|
||||
tiny-bip39 = "0.7.0"
|
||||
url = "2.1.0"
|
||||
chrono = "0.4"
|
||||
|
||||
|
@@ -3,7 +3,7 @@ use chrono::DateTime;
|
||||
use solana_sdk::{
|
||||
clock::{Epoch, Slot},
|
||||
hash::Hash,
|
||||
pubkey::{Pubkey, MAX_SEED_LEN},
|
||||
pubkey::Pubkey,
|
||||
signature::{read_keypair_file, Signature},
|
||||
};
|
||||
use std::fmt::Display;
|
||||
@@ -291,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::*;
|
||||
|
@@ -3,16 +3,15 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-cli-config"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.6.0"
|
||||
version = "1.5.6"
|
||||
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()
|
||||
}
|
||||
|
@@ -3,11 +3,10 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
edition = "2018"
|
||||
name = "solana-cli-output"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.6.0"
|
||||
version = "1.5.6"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
documentation = "https://docs.rs/solana-cli-output"
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4.11", features = ["serde"] }
|
||||
@@ -15,16 +14,16 @@ 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.0" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.6.0" }
|
||||
solana-client = { path = "../client", version = "1.6.0" }
|
||||
solana-sdk = { path = "../sdk", version = "1.6.0" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "1.6.0" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "1.6.0" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.6.0" }
|
||||
solana-account-decoder = { path = "../account-decoder", version = "1.5.6" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.5.6" }
|
||||
solana-client = { path = "../client", version = "1.5.6" }
|
||||
solana-sdk = { path = "../sdk", version = "1.5.6" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "1.5.6" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "1.5.6" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.5.6" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -2,12 +2,10 @@ use {
|
||||
crate::{
|
||||
display::{
|
||||
build_balance_message, build_balance_message_with_config, format_labeled_address,
|
||||
unix_timestamp_to_string, writeln_name_value, writeln_transaction,
|
||||
BuildBalanceMessageConfig,
|
||||
unix_timestamp_to_string, writeln_name_value, BuildBalanceMessageConfig,
|
||||
},
|
||||
QuietDisplay, VerboseDisplay,
|
||||
},
|
||||
chrono::{Local, TimeZone},
|
||||
console::{style, Emoji},
|
||||
inflector::cases::titlecase::to_title_case,
|
||||
serde::{Deserialize, Serialize},
|
||||
@@ -26,16 +24,12 @@ use {
|
||||
pubkey::Pubkey,
|
||||
signature::Signature,
|
||||
stake_history::StakeHistoryEntry,
|
||||
transaction::{Transaction, TransactionError},
|
||||
transaction::Transaction,
|
||||
},
|
||||
solana_stake_program::stake_state::{Authorized, Lockup},
|
||||
solana_transaction_status::{
|
||||
EncodedConfirmedBlock, EncodedTransaction, TransactionConfirmationStatus,
|
||||
UiTransactionStatusMeta,
|
||||
},
|
||||
solana_vote_program::{
|
||||
authorized_voters::AuthorizedVoters,
|
||||
vote_state::{BlockTimestamp, Lockout, MAX_EPOCH_CREDITS_HISTORY, MAX_LOCKOUT_HISTORY},
|
||||
vote_state::{BlockTimestamp, Lockout},
|
||||
},
|
||||
std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
@@ -295,7 +289,10 @@ impl fmt::Display for CliEpochInfo {
|
||||
}
|
||||
|
||||
fn slot_to_human_time(slot: Slot) -> String {
|
||||
humantime::format_duration(Duration::from_millis(slot * clock::DEFAULT_MS_PER_SLOT)).to_string()
|
||||
humantime::format_duration(Duration::from_secs(
|
||||
slot * clock::DEFAULT_TICKS_PER_SLOT / clock::DEFAULT_TICKS_PER_SECOND,
|
||||
))
|
||||
.to_string()
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Default)]
|
||||
@@ -616,55 +613,17 @@ fn show_votes_and_credits(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Existence of this should guarantee the occurrence of vote truncation
|
||||
let newest_history_entry = epoch_voting_history.iter().rev().next();
|
||||
|
||||
writeln!(f, "Recent Votes:")?;
|
||||
for vote in votes {
|
||||
writeln!(f, "- slot: {}", vote.slot)?;
|
||||
writeln!(f, " confirmation count: {}", vote.confirmation_count)?;
|
||||
}
|
||||
writeln!(f, "Epoch Voting History:")?;
|
||||
writeln!(
|
||||
f,
|
||||
"{} Votes (using {}/{} entries):",
|
||||
(if newest_history_entry.is_none() {
|
||||
"All"
|
||||
} else {
|
||||
"Recent"
|
||||
}),
|
||||
votes.len(),
|
||||
MAX_LOCKOUT_HISTORY
|
||||
"* missed credits include slots unavailable to vote on due to delinquent leaders",
|
||||
)?;
|
||||
|
||||
for vote in votes.iter().rev() {
|
||||
writeln!(
|
||||
f,
|
||||
"- slot: {} (confirmation count: {})",
|
||||
vote.slot, vote.confirmation_count
|
||||
)?;
|
||||
}
|
||||
if let Some(newest) = newest_history_entry {
|
||||
writeln!(
|
||||
f,
|
||||
"- ... (truncated {} rooted votes, which have been credited)",
|
||||
newest.credits
|
||||
)?;
|
||||
}
|
||||
|
||||
if !epoch_voting_history.is_empty() {
|
||||
writeln!(
|
||||
f,
|
||||
"{} Epoch Voting History (using {}/{} entries):",
|
||||
(if epoch_voting_history.len() < MAX_EPOCH_CREDITS_HISTORY {
|
||||
"All"
|
||||
} else {
|
||||
"Recent"
|
||||
}),
|
||||
epoch_voting_history.len(),
|
||||
MAX_EPOCH_CREDITS_HISTORY
|
||||
)?;
|
||||
writeln!(
|
||||
f,
|
||||
"* missed credits include slots unavailable to vote on due to delinquent leaders",
|
||||
)?;
|
||||
}
|
||||
|
||||
for entry in epoch_voting_history.iter().rev() {
|
||||
for entry in epoch_voting_history {
|
||||
writeln!(
|
||||
f, // tame fmt so that this will be folded like following
|
||||
"- epoch: {}",
|
||||
@@ -672,7 +631,7 @@ fn show_votes_and_credits(
|
||||
)?;
|
||||
writeln!(
|
||||
f,
|
||||
" credits range: ({}..{}]",
|
||||
" credits range: [{}..{})",
|
||||
entry.prev_credits, entry.credits
|
||||
)?;
|
||||
writeln!(
|
||||
@@ -681,22 +640,6 @@ fn show_votes_and_credits(
|
||||
entry.credits_earned, entry.slots_in_epoch
|
||||
)?;
|
||||
}
|
||||
if let Some(oldest) = epoch_voting_history.iter().next() {
|
||||
if oldest.prev_credits > 0 {
|
||||
// Oldest entry doesn't start with 0. so history must be truncated...
|
||||
|
||||
// count of this combined pseudo credits range: (0..=oldest.prev_credits] like the above
|
||||
// (or this is just [1..=oldest.prev_credits] for human's simpler minds)
|
||||
let count = oldest.prev_credits;
|
||||
|
||||
writeln!(
|
||||
f,
|
||||
"- ... (omitting {} past rooted votes, which have already been credited)",
|
||||
count
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1457,37 +1400,11 @@ impl fmt::Display for CliSupply {
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliFeesInner {
|
||||
pub struct CliFees {
|
||||
pub slot: Slot,
|
||||
pub blockhash: String,
|
||||
pub lamports_per_signature: u64,
|
||||
pub last_valid_slot: Option<Slot>,
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliFeesInner {}
|
||||
impl VerboseDisplay for CliFeesInner {}
|
||||
|
||||
impl fmt::Display for CliFeesInner {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln_name_value(f, "Blockhash:", &self.blockhash)?;
|
||||
writeln_name_value(
|
||||
f,
|
||||
"Lamports per signature:",
|
||||
&self.lamports_per_signature.to_string(),
|
||||
)?;
|
||||
let last_valid_slot = self
|
||||
.last_valid_slot
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_default();
|
||||
writeln_name_value(f, "Last valid slot:", &last_valid_slot)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliFees {
|
||||
#[serde(flatten, skip_serializing_if = "Option::is_none")]
|
||||
pub inner: Option<CliFeesInner>,
|
||||
pub last_valid_slot: Slot,
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliFees {}
|
||||
@@ -1495,31 +1412,14 @@ impl VerboseDisplay for CliFees {}
|
||||
|
||||
impl fmt::Display for CliFees {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.inner.as_ref() {
|
||||
Some(inner) => write!(f, "{}", inner),
|
||||
None => write!(f, "Fees unavailable"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CliFees {
|
||||
pub fn some(
|
||||
slot: Slot,
|
||||
blockhash: Hash,
|
||||
lamports_per_signature: u64,
|
||||
last_valid_slot: Option<Slot>,
|
||||
) -> Self {
|
||||
Self {
|
||||
inner: Some(CliFeesInner {
|
||||
slot,
|
||||
blockhash: blockhash.to_string(),
|
||||
lamports_per_signature,
|
||||
last_valid_slot,
|
||||
}),
|
||||
}
|
||||
}
|
||||
pub fn none() -> Self {
|
||||
Self { inner: None }
|
||||
writeln_name_value(f, "Blockhash:", &self.blockhash)?;
|
||||
writeln_name_value(
|
||||
f,
|
||||
"Lamports per signature:",
|
||||
&self.lamports_per_signature.to_string(),
|
||||
)?;
|
||||
writeln_name_value(f, "Last valid slot:", &self.last_valid_slot.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1621,34 +1521,10 @@ impl fmt::Display for CliProgramAuthority {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliProgram {
|
||||
pub program_id: String,
|
||||
pub owner: String,
|
||||
pub data_len: usize,
|
||||
}
|
||||
impl QuietDisplay for CliProgram {}
|
||||
impl VerboseDisplay for CliProgram {}
|
||||
impl fmt::Display for CliProgram {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(f)?;
|
||||
writeln_name_value(f, "Program Id:", &self.program_id)?;
|
||||
writeln_name_value(f, "Owner:", &self.owner)?;
|
||||
writeln_name_value(
|
||||
f,
|
||||
"Data Length:",
|
||||
&format!("{:?} ({:#x?}) bytes", self.data_len, self.data_len),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliUpgradeableProgram {
|
||||
pub program_id: String,
|
||||
pub owner: String,
|
||||
pub programdata_address: String,
|
||||
pub authority: String,
|
||||
pub last_deploy_slot: u64,
|
||||
@@ -1660,7 +1536,6 @@ impl fmt::Display for CliUpgradeableProgram {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(f)?;
|
||||
writeln_name_value(f, "Program Id:", &self.program_id)?;
|
||||
writeln_name_value(f, "Owner:", &self.owner)?;
|
||||
writeln_name_value(f, "ProgramData Address:", &self.programdata_address)?;
|
||||
writeln_name_value(f, "Authority:", &self.authority)?;
|
||||
writeln_name_value(
|
||||
@@ -1786,8 +1661,7 @@ pub fn parse_sign_only_reply_string(reply: &str) -> SignOnly {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[derive(Debug)]
|
||||
pub enum CliSignatureVerificationStatus {
|
||||
None,
|
||||
Pass,
|
||||
@@ -1818,192 +1692,6 @@ impl fmt::Display for CliSignatureVerificationStatus {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliBlock {
|
||||
#[serde(flatten)]
|
||||
pub encoded_confirmed_block: EncodedConfirmedBlock,
|
||||
#[serde(skip_serializing)]
|
||||
pub slot: Slot,
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliBlock {}
|
||||
impl VerboseDisplay for CliBlock {}
|
||||
|
||||
impl fmt::Display for CliBlock {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(f, "Slot: {}", self.slot)?;
|
||||
writeln!(
|
||||
f,
|
||||
"Parent Slot: {}",
|
||||
self.encoded_confirmed_block.parent_slot
|
||||
)?;
|
||||
writeln!(f, "Blockhash: {}", self.encoded_confirmed_block.blockhash)?;
|
||||
writeln!(
|
||||
f,
|
||||
"Previous Blockhash: {}",
|
||||
self.encoded_confirmed_block.previous_blockhash
|
||||
)?;
|
||||
if let Some(block_time) = self.encoded_confirmed_block.block_time {
|
||||
writeln!(f, "Block Time: {:?}", Local.timestamp(block_time, 0))?;
|
||||
}
|
||||
if !self.encoded_confirmed_block.rewards.is_empty() {
|
||||
let mut rewards = self.encoded_confirmed_block.rewards.clone();
|
||||
rewards.sort_by(|a, b| a.pubkey.cmp(&b.pubkey));
|
||||
let mut total_rewards = 0;
|
||||
writeln!(f, "Rewards:")?;
|
||||
writeln!(
|
||||
f,
|
||||
" {:<44} {:^15} {:<15} {:<20} {:>14}",
|
||||
"Address", "Type", "Amount", "New Balance", "Percent Change"
|
||||
)?;
|
||||
for reward in rewards {
|
||||
let sign = if reward.lamports < 0 { "-" } else { "" };
|
||||
|
||||
total_rewards += reward.lamports;
|
||||
writeln!(
|
||||
f,
|
||||
" {:<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 { "" };
|
||||
writeln!(
|
||||
f,
|
||||
"Total Rewards: {}◎{:<12.9}",
|
||||
sign,
|
||||
lamports_to_sol(total_rewards.abs() as u64)
|
||||
)?;
|
||||
}
|
||||
for (index, transaction_with_meta) in
|
||||
self.encoded_confirmed_block.transactions.iter().enumerate()
|
||||
{
|
||||
writeln!(f, "Transaction {}:", index)?;
|
||||
writeln_transaction(
|
||||
f,
|
||||
&transaction_with_meta.transaction.decode().unwrap(),
|
||||
&transaction_with_meta.meta,
|
||||
" ",
|
||||
None,
|
||||
None,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliTransaction {
|
||||
pub transaction: EncodedTransaction,
|
||||
pub meta: Option<UiTransactionStatusMeta>,
|
||||
pub block_time: Option<UnixTimestamp>,
|
||||
#[serde(skip_serializing)]
|
||||
pub slot: Option<Slot>,
|
||||
#[serde(skip_serializing)]
|
||||
pub decoded_transaction: Transaction,
|
||||
#[serde(skip_serializing)]
|
||||
pub prefix: String,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
pub sigverify_status: Vec<CliSignatureVerificationStatus>,
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliTransaction {}
|
||||
impl VerboseDisplay for CliTransaction {}
|
||||
|
||||
impl fmt::Display for CliTransaction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln_transaction(
|
||||
f,
|
||||
&self.decoded_transaction,
|
||||
&self.meta,
|
||||
&self.prefix,
|
||||
if !self.sigverify_status.is_empty() {
|
||||
Some(&self.sigverify_status)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
self.block_time,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliTransactionConfirmation {
|
||||
pub confirmation_status: Option<TransactionConfirmationStatus>,
|
||||
#[serde(flatten, skip_serializing_if = "Option::is_none")]
|
||||
pub transaction: Option<CliTransaction>,
|
||||
#[serde(skip_serializing)]
|
||||
pub get_transaction_error: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub err: Option<TransactionError>,
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliTransactionConfirmation {}
|
||||
impl VerboseDisplay for CliTransactionConfirmation {
|
||||
fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result {
|
||||
if let Some(transaction) = &self.transaction {
|
||||
writeln!(
|
||||
w,
|
||||
"\nTransaction executed in slot {}:",
|
||||
transaction.slot.expect("slot should exist")
|
||||
)?;
|
||||
write!(w, "{}", transaction)?;
|
||||
} else if let Some(confirmation_status) = &self.confirmation_status {
|
||||
if confirmation_status != &TransactionConfirmationStatus::Finalized {
|
||||
writeln!(w)?;
|
||||
writeln!(
|
||||
w,
|
||||
"Unable to get finalized transaction details: not yet finalized"
|
||||
)?;
|
||||
} else if let Some(err) = &self.get_transaction_error {
|
||||
writeln!(w)?;
|
||||
writeln!(w, "Unable to get finalized transaction details: {}", err)?;
|
||||
}
|
||||
}
|
||||
writeln!(w)?;
|
||||
write!(w, "{}", self)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for CliTransactionConfirmation {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match &self.confirmation_status {
|
||||
None => write!(f, "Not found"),
|
||||
Some(confirmation_status) => {
|
||||
if let Some(err) = &self.err {
|
||||
write!(f, "Transaction failed: {}", err)
|
||||
} else {
|
||||
write!(f, "{:?}", confirmation_status)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
use {
|
||||
crate::cli_output::CliSignatureVerificationStatus,
|
||||
chrono::{DateTime, Local, NaiveDateTime, SecondsFormat, TimeZone, Utc},
|
||||
chrono::{DateTime, NaiveDateTime, SecondsFormat, Utc},
|
||||
console::style,
|
||||
indicatif::{ProgressBar, ProgressStyle},
|
||||
solana_sdk::{
|
||||
@@ -131,17 +131,8 @@ pub fn write_transaction<W: io::Write>(
|
||||
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: {:?}",
|
||||
@@ -286,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(
|
||||
@@ -295,7 +285,6 @@ pub fn println_transaction(
|
||||
transaction_status,
|
||||
prefix,
|
||||
sigverify_status,
|
||||
block_time,
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
@@ -305,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.0"
|
||||
version = "1.5.6"
|
||||
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"
|
||||
@@ -25,32 +24,32 @@ humantime = "2.0.1"
|
||||
num-traits = "0.2"
|
||||
pretty-hex = "0.2.1"
|
||||
reqwest = { version = "0.10.8", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
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.0" }
|
||||
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "1.6.0" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.6.0" }
|
||||
solana-cli-config = { path = "../cli-config", version = "1.6.0" }
|
||||
solana-cli-output = { path = "../cli-output", version = "1.6.0" }
|
||||
solana-client = { path = "../client", version = "1.6.0" }
|
||||
solana-config-program = { path = "../programs/config", version = "1.6.0" }
|
||||
solana-faucet = { path = "../faucet", version = "1.6.0" }
|
||||
solana-logger = { path = "../logger", version = "1.6.0" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.6.0" }
|
||||
solana_rbpf = "=0.2.5"
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "1.6.0" }
|
||||
solana-sdk = { path = "../sdk", version = "1.6.0" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "1.6.0" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "1.6.0" }
|
||||
solana-version = { path = "../version", version = "1.6.0" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.6.0" }
|
||||
solana-account-decoder = { path = "../account-decoder", version = "1.5.6" }
|
||||
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "1.5.6" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.5.6" }
|
||||
solana-cli-config = { path = "../cli-config", version = "1.5.6" }
|
||||
solana-cli-output = { path = "../cli-output", version = "1.5.6" }
|
||||
solana-client = { path = "../client", version = "1.5.6" }
|
||||
solana-config-program = { path = "../programs/config", version = "1.5.6" }
|
||||
solana-faucet = { path = "../faucet", version = "1.5.6" }
|
||||
solana-logger = { path = "../logger", version = "1.5.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.5.6" }
|
||||
solana_rbpf = "=0.2.4"
|
||||
solana-remote-wallet = { path = "../remote-wallet", version = "1.5.6" }
|
||||
solana-sdk = { path = "../sdk", version = "1.5.6" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "1.5.6" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "1.5.6" }
|
||||
solana-version = { path = "../version", version = "1.5.6" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.5.6" }
|
||||
thiserror = "1.0.21"
|
||||
tiny-bip39 = "0.7.0"
|
||||
url = "2.1.1"
|
||||
|
||||
[dev-dependencies]
|
||||
solana-core = { path = "../core", version = "1.6.0" }
|
||||
solana-core = { path = "../core", version = "1.5.6" }
|
||||
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(())
|
||||
|
264
cli/src/cli.rs
264
cli/src/cli.rs
@@ -17,9 +17,8 @@ use solana_clap_utils::{
|
||||
offline::*,
|
||||
};
|
||||
use solana_cli_output::{
|
||||
display::{build_balance_message, println_name_value},
|
||||
return_signers, CliAccount, CliSignature, CliSignatureVerificationStatus, CliTransaction,
|
||||
CliTransactionConfirmation, OutputFormat,
|
||||
display::{build_balance_message, println_name_value, println_transaction},
|
||||
return_signers, CliAccount, CliSignature, CliSignatureVerificationStatus, OutputFormat,
|
||||
};
|
||||
use solana_client::{
|
||||
blockhash_query::BlockhashQuery,
|
||||
@@ -41,7 +40,7 @@ use solana_sdk::{
|
||||
hash::Hash,
|
||||
instruction::InstructionError,
|
||||
message::Message,
|
||||
pubkey::Pubkey,
|
||||
pubkey::{Pubkey, MAX_SEED_LEN},
|
||||
signature::{Signature, Signer, SignerError},
|
||||
system_instruction::{self, SystemError},
|
||||
system_program,
|
||||
@@ -51,7 +50,9 @@ use solana_stake_program::{
|
||||
stake_instruction::LockupArgs,
|
||||
stake_state::{Lockup, StakeAuthorize},
|
||||
};
|
||||
use solana_transaction_status::{EncodedTransaction, UiTransactionEncoding};
|
||||
use solana_transaction_status::{
|
||||
EncodedTransaction, TransactionConfirmationStatus, UiTransactionEncoding,
|
||||
};
|
||||
use solana_vote_program::vote_state::VoteAuthorize;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
@@ -90,9 +91,7 @@ pub enum CliCommand {
|
||||
},
|
||||
Feature(FeatureCliCommand),
|
||||
Inflation(InflationCliCommand),
|
||||
Fees {
|
||||
blockhash: Option<Hash>,
|
||||
},
|
||||
Fees,
|
||||
FirstAvailableBlock,
|
||||
GetBlock {
|
||||
slot: Option<Slot>,
|
||||
@@ -356,8 +355,6 @@ pub enum CliCommand {
|
||||
nonce_account: Option<Pubkey>,
|
||||
nonce_authority: SignerIndex,
|
||||
fee_payer: SignerIndex,
|
||||
derived_address_seed: Option<String>,
|
||||
derived_address_program_id: Option<Pubkey>,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -369,25 +366,25 @@ pub struct CliCommandInfo {
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CliError {
|
||||
#[error("Bad parameter: {0}")]
|
||||
#[error("bad parameter: {0}")]
|
||||
BadParameter(String),
|
||||
#[error(transparent)]
|
||||
ClientError(#[from] ClientError),
|
||||
#[error("Command not recognized: {0}")]
|
||||
#[error("command not recognized: {0}")]
|
||||
CommandNotRecognized(String),
|
||||
#[error("Account {1} has insufficient funds for fee ({0} SOL)")]
|
||||
InsufficientFundsForFee(f64, Pubkey),
|
||||
#[error("Account {1} has insufficient funds for spend ({0} SOL)")]
|
||||
InsufficientFundsForSpend(f64, Pubkey),
|
||||
#[error("Account {2} has insufficient funds for spend ({0} SOL) + fee ({1} SOL)")]
|
||||
InsufficientFundsForSpendAndFee(f64, f64, Pubkey),
|
||||
#[error("insufficient funds for fee ({0} SOL)")]
|
||||
InsufficientFundsForFee(f64),
|
||||
#[error("insufficient funds for spend ({0} SOL)")]
|
||||
InsufficientFundsForSpend(f64),
|
||||
#[error("insufficient funds for spend ({0} SOL) and fee ({1} SOL)")]
|
||||
InsufficientFundsForSpendAndFee(f64, f64),
|
||||
#[error(transparent)]
|
||||
InvalidNonce(nonce_utils::Error),
|
||||
#[error("Dynamic program error: {0}")]
|
||||
#[error("dynamic program error: {0}")]
|
||||
DynamicProgramError(String),
|
||||
#[error("RPC request error: {0}")]
|
||||
#[error("rpc request error: {0}")]
|
||||
RpcRequestError(String),
|
||||
#[error("Keypair file not found: {0}")]
|
||||
#[error("keypair file not found: {0}")]
|
||||
KeypairFileNotFound(String),
|
||||
}
|
||||
|
||||
@@ -595,13 +592,10 @@ pub fn parse_command(
|
||||
("feature", Some(matches)) => {
|
||||
parse_feature_subcommand(matches, default_signer, wallet_manager)
|
||||
}
|
||||
("fees", Some(matches)) => {
|
||||
let blockhash = value_of::<Hash>(matches, "blockhash");
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::Fees { blockhash },
|
||||
signers: vec![],
|
||||
})
|
||||
}
|
||||
("fees", Some(_matches)) => Ok(CliCommandInfo {
|
||||
command: CliCommand::Fees,
|
||||
signers: vec![],
|
||||
}),
|
||||
("first-available-block", Some(_matches)) => Ok(CliCommandInfo {
|
||||
command: CliCommand::FirstAvailableBlock,
|
||||
signers: vec![],
|
||||
@@ -864,12 +858,6 @@ pub fn parse_command(
|
||||
let signer_info =
|
||||
default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
|
||||
|
||||
let derived_address_seed = matches
|
||||
.value_of("derived_address_seed")
|
||||
.map(|s| s.to_string());
|
||||
let derived_address_program_id =
|
||||
resolve_derived_address_program_id(matches, "derived_address_program_id");
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::Transfer {
|
||||
amount,
|
||||
@@ -881,8 +869,6 @@ pub fn parse_command(
|
||||
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
|
||||
fee_payer: signer_info.index_of(fee_payer_pubkey).unwrap(),
|
||||
from: signer_info.index_of(from_pubkey).unwrap(),
|
||||
derived_address_seed,
|
||||
derived_address_program_id,
|
||||
},
|
||||
signers: signer_info.signers,
|
||||
})
|
||||
@@ -912,15 +898,6 @@ pub fn parse_command(
|
||||
|
||||
pub type ProcessResult = Result<String, Box<dyn std::error::Error>>;
|
||||
|
||||
fn resolve_derived_address_program_id(matches: &ArgMatches<'_>, arg_name: &str) -> Option<Pubkey> {
|
||||
matches.value_of(arg_name).and_then(|v| match v {
|
||||
"NONCE" => Some(system_program::id()),
|
||||
"STAKE" => Some(solana_stake_program::id()),
|
||||
"VOTE" => Some(solana_vote_program::id()),
|
||||
_ => pubkey_of(matches, arg_name),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_create_address_with_seed(
|
||||
matches: &ArgMatches<'_>,
|
||||
default_signer: &DefaultSigner,
|
||||
@@ -933,10 +910,21 @@ pub fn parse_create_address_with_seed(
|
||||
vec![default_signer.signer_from_path(matches, wallet_manager)?]
|
||||
};
|
||||
|
||||
let program_id = resolve_derived_address_program_id(matches, "program_id").unwrap();
|
||||
let program_id = match matches.value_of("program_id").unwrap() {
|
||||
"NONCE" => system_program::id(),
|
||||
"STAKE" => solana_stake_program::id(),
|
||||
"VOTE" => solana_vote_program::id(),
|
||||
_ => pubkey_of(matches, "program_id").unwrap(),
|
||||
};
|
||||
|
||||
let seed = matches.value_of("seed").unwrap().to_string();
|
||||
|
||||
if seed.len() > MAX_SEED_LEN {
|
||||
return Err(CliError::BadParameter(
|
||||
"Address seed must not be longer than 32 bytes".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(CliCommandInfo {
|
||||
command: CliCommand::CreateAddressWithSeed {
|
||||
from_pubkey,
|
||||
@@ -1009,72 +997,60 @@ fn process_confirm(
|
||||
) -> ProcessResult {
|
||||
match rpc_client.get_signature_statuses_with_history(&[*signature]) {
|
||||
Ok(status) => {
|
||||
let cli_transaction = if let Some(transaction_status) = &status.value[0] {
|
||||
let mut transaction = None;
|
||||
let mut get_transaction_error = None;
|
||||
if let Some(transaction_status) = &status.value[0] {
|
||||
if config.verbose {
|
||||
match rpc_client
|
||||
.get_confirmed_transaction(signature, UiTransactionEncoding::Base64)
|
||||
{
|
||||
Ok(confirmed_transaction) => {
|
||||
let decoded_transaction = confirmed_transaction
|
||||
.transaction
|
||||
.transaction
|
||||
.decode()
|
||||
.expect("Successful decode");
|
||||
let json_transaction = EncodedTransaction::encode(
|
||||
decoded_transaction.clone(),
|
||||
UiTransactionEncoding::Json,
|
||||
println!(
|
||||
"\nTransaction executed in slot {}:",
|
||||
confirmed_transaction.slot
|
||||
);
|
||||
println_transaction(
|
||||
&confirmed_transaction
|
||||
.transaction
|
||||
.transaction
|
||||
.decode()
|
||||
.expect("Successful decode"),
|
||||
&confirmed_transaction.transaction.meta,
|
||||
" ",
|
||||
None,
|
||||
);
|
||||
|
||||
transaction = Some(CliTransaction {
|
||||
transaction: json_transaction,
|
||||
meta: confirmed_transaction.transaction.meta,
|
||||
block_time: confirmed_transaction.block_time,
|
||||
slot: Some(confirmed_transaction.slot),
|
||||
decoded_transaction,
|
||||
prefix: " ".to_string(),
|
||||
sigverify_status: vec![],
|
||||
});
|
||||
}
|
||||
Err(err) => {
|
||||
get_transaction_error = Some(format!("{:?}", err));
|
||||
if transaction_status.confirmation_status()
|
||||
!= TransactionConfirmationStatus::Finalized
|
||||
{
|
||||
println!();
|
||||
println!("Unable to get finalized transaction details: not yet finalized")
|
||||
} else {
|
||||
println!();
|
||||
println!("Unable to get finalized transaction details: {}", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
println!();
|
||||
}
|
||||
CliTransactionConfirmation {
|
||||
confirmation_status: Some(transaction_status.confirmation_status()),
|
||||
transaction,
|
||||
get_transaction_error,
|
||||
err: transaction_status.err.clone(),
|
||||
|
||||
if let Some(err) = &transaction_status.err {
|
||||
Ok(format!("Transaction failed: {}", err))
|
||||
} else {
|
||||
Ok(format!("{:?}", transaction_status.confirmation_status()))
|
||||
}
|
||||
} else {
|
||||
CliTransactionConfirmation {
|
||||
confirmation_status: None,
|
||||
transaction: None,
|
||||
get_transaction_error: None,
|
||||
err: None,
|
||||
}
|
||||
};
|
||||
Ok(config.output_format.formatted_string(&cli_transaction))
|
||||
Ok("Not found".to_string())
|
||||
}
|
||||
}
|
||||
Err(err) => Err(CliError::RpcRequestError(format!("Unable to confirm: {}", err)).into()),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
fn process_decode_transaction(config: &CliConfig, transaction: &Transaction) -> ProcessResult {
|
||||
let sigverify_status = CliSignatureVerificationStatus::verify_transaction(&transaction);
|
||||
let decode_transaction = CliTransaction {
|
||||
decoded_transaction: transaction.clone(),
|
||||
transaction: EncodedTransaction::encode(transaction.clone(), UiTransactionEncoding::Json),
|
||||
meta: None,
|
||||
block_time: None,
|
||||
slot: None,
|
||||
prefix: "".to_string(),
|
||||
sigverify_status,
|
||||
};
|
||||
Ok(config.output_format.formatted_string(&decode_transaction))
|
||||
fn process_decode_transaction(transaction: &Transaction) -> ProcessResult {
|
||||
let sig_stats = CliSignatureVerificationStatus::verify_transaction(&transaction);
|
||||
println_transaction(transaction, &None, "", Some(&sig_stats));
|
||||
Ok("".to_string())
|
||||
}
|
||||
|
||||
fn process_show_account(
|
||||
@@ -1132,11 +1108,8 @@ fn process_transfer(
|
||||
nonce_account: Option<&Pubkey>,
|
||||
nonce_authority: SignerIndex,
|
||||
fee_payer: SignerIndex,
|
||||
derived_address_seed: Option<String>,
|
||||
derived_address_program_id: Option<&Pubkey>,
|
||||
) -> ProcessResult {
|
||||
let from = config.signers[from];
|
||||
let mut from_pubkey = from.pubkey();
|
||||
|
||||
let (recent_blockhash, fee_calculator) =
|
||||
blockhash_query.get_blockhash_and_fee_calculator(rpc_client, config.commitment)?;
|
||||
@@ -1144,28 +1117,8 @@ fn process_transfer(
|
||||
let nonce_authority = config.signers[nonce_authority];
|
||||
let fee_payer = config.signers[fee_payer];
|
||||
|
||||
let derived_parts = derived_address_seed.zip(derived_address_program_id);
|
||||
let with_seed = if let Some((seed, program_id)) = derived_parts {
|
||||
let base_pubkey = from_pubkey;
|
||||
from_pubkey = Pubkey::create_with_seed(&base_pubkey, &seed, program_id)?;
|
||||
Some((base_pubkey, seed, program_id, from_pubkey))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let build_message = |lamports| {
|
||||
let ixs = if let Some((base_pubkey, seed, program_id, from_pubkey)) = with_seed.as_ref() {
|
||||
vec![system_instruction::transfer_with_seed(
|
||||
from_pubkey,
|
||||
base_pubkey,
|
||||
seed.clone(),
|
||||
program_id,
|
||||
to,
|
||||
lamports,
|
||||
)]
|
||||
} else {
|
||||
vec![system_instruction::transfer(&from_pubkey, to, lamports)]
|
||||
};
|
||||
let ixs = vec![system_instruction::transfer(&from.pubkey(), to, lamports)];
|
||||
|
||||
if let Some(nonce_account) = &nonce_account {
|
||||
Message::new_with_nonce(
|
||||
@@ -1184,7 +1137,7 @@ fn process_transfer(
|
||||
sign_only,
|
||||
amount,
|
||||
&fee_calculator,
|
||||
&from_pubkey,
|
||||
&from.pubkey(),
|
||||
&fee_payer.pubkey(),
|
||||
build_message,
|
||||
config.commitment,
|
||||
@@ -1268,7 +1221,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
||||
seed,
|
||||
program_id,
|
||||
} => process_create_address_with_seed(config, from_pubkey.as_ref(), &seed, &program_id),
|
||||
CliCommand::Fees { ref blockhash } => process_fees(&rpc_client, config, blockhash.as_ref()),
|
||||
CliCommand::Fees => process_fees(&rpc_client, config),
|
||||
CliCommand::Feature(feature_subcommand) => {
|
||||
process_feature_subcommand(&rpc_client, config, feature_subcommand)
|
||||
}
|
||||
@@ -1761,9 +1714,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
||||
} => process_balance(&rpc_client, config, &pubkey, *use_lamports_unit),
|
||||
// Confirm the last client transaction by signature
|
||||
CliCommand::Confirm(signature) => process_confirm(&rpc_client, config, signature),
|
||||
CliCommand::DecodeTransaction(transaction) => {
|
||||
process_decode_transaction(config, transaction)
|
||||
}
|
||||
CliCommand::DecodeTransaction(transaction) => process_decode_transaction(transaction),
|
||||
CliCommand::ResolveSigner(path) => {
|
||||
if let Some(path) = path {
|
||||
Ok(path.to_string())
|
||||
@@ -1792,8 +1743,6 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
||||
ref nonce_account,
|
||||
nonce_authority,
|
||||
fee_payer,
|
||||
derived_address_seed,
|
||||
ref derived_address_program_id,
|
||||
} => process_transfer(
|
||||
&rpc_client,
|
||||
config,
|
||||
@@ -1806,8 +1755,6 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
||||
nonce_account.as_ref(),
|
||||
*nonce_authority,
|
||||
*fee_payer,
|
||||
derived_address_seed.clone(),
|
||||
derived_address_program_id.as_ref(),
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -2020,7 +1967,6 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
|
||||
.value_name("SEED_STRING")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.validator(is_derived_address_seed)
|
||||
.help("The seed. Must not take more than 32 bytes to encode as utf-8"),
|
||||
)
|
||||
.arg(
|
||||
@@ -2141,23 +2087,6 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
|
||||
.takes_value(false)
|
||||
.help("Return signature immediately after submitting the transaction, instead of waiting for confirmations"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("derived_address_seed")
|
||||
.long("derived-address-seed")
|
||||
.takes_value(true)
|
||||
.value_name("SEED_STRING")
|
||||
.requires("derived_address_program_id")
|
||||
.validator(is_derived_address_seed)
|
||||
.hidden(true)
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("derived_address_program_id")
|
||||
.long("derived-address-program-id")
|
||||
.takes_value(true)
|
||||
.value_name("PROGRAM_ID")
|
||||
.requires("derived_address_seed")
|
||||
.hidden(true)
|
||||
)
|
||||
.offline_args()
|
||||
.nonce_args(false)
|
||||
.arg(fee_payer_arg()),
|
||||
@@ -2207,7 +2136,6 @@ mod tests {
|
||||
signature::{keypair_from_seed, read_keypair_file, write_keypair_file, Keypair, Presigner},
|
||||
transaction::TransactionError,
|
||||
};
|
||||
use solana_transaction_status::TransactionConfirmationStatus;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn make_tmp_path(name: &str) -> String {
|
||||
@@ -2802,7 +2730,6 @@ mod tests {
|
||||
use_deprecated_loader: false,
|
||||
allow_excessive_balance: false,
|
||||
};
|
||||
config.output_format = OutputFormat::JsonCompact;
|
||||
let result = process_command(&config);
|
||||
let json: Value = serde_json::from_str(&result.unwrap()).unwrap();
|
||||
let program_id = json
|
||||
@@ -2860,8 +2787,6 @@ mod tests {
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
},
|
||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
||||
}
|
||||
@@ -2884,8 +2809,6 @@ mod tests {
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
},
|
||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
||||
}
|
||||
@@ -2912,8 +2835,6 @@ mod tests {
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
},
|
||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
||||
}
|
||||
@@ -2944,8 +2865,6 @@ mod tests {
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
},
|
||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
||||
}
|
||||
@@ -2984,8 +2903,6 @@ mod tests {
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
},
|
||||
signers: vec![Presigner::new(&from_pubkey, &from_sig).into()],
|
||||
}
|
||||
@@ -3025,8 +2942,6 @@ mod tests {
|
||||
nonce_account: Some(nonce_address),
|
||||
nonce_authority: 1,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
},
|
||||
signers: vec![
|
||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||
@@ -3034,38 +2949,5 @@ mod tests {
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
//Test Transfer Subcommand, with seed
|
||||
let derived_address_seed = "seed".to_string();
|
||||
let derived_address_program_id = "STAKE";
|
||||
let test_transfer = test_commands.clone().get_matches_from(vec![
|
||||
"test",
|
||||
"transfer",
|
||||
&to_string,
|
||||
"42",
|
||||
"--derived-address-seed",
|
||||
&derived_address_seed,
|
||||
"--derived-address-program-id",
|
||||
derived_address_program_id,
|
||||
]);
|
||||
assert_eq!(
|
||||
parse_command(&test_transfer, &default_signer, &mut None).unwrap(),
|
||||
CliCommandInfo {
|
||||
command: CliCommand::Transfer {
|
||||
amount: SpendAmount::Some(42_000_000_000),
|
||||
to: to_pubkey,
|
||||
from: 0,
|
||||
sign_only: false,
|
||||
no_wait: false,
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: Some(derived_address_seed),
|
||||
derived_address_program_id: Some(solana_stake_program::id()),
|
||||
},
|
||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into(),],
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -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};
|
||||
@@ -134,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")
|
||||
@@ -742,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
|
||||
@@ -762,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!(
|
||||
@@ -817,7 +781,7 @@ pub fn process_catchup(
|
||||
"gaining"
|
||||
},
|
||||
slots_per_second,
|
||||
average_time_remaining
|
||||
time_remaining
|
||||
)
|
||||
},
|
||||
));
|
||||
@@ -828,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -858,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))
|
||||
}
|
||||
@@ -954,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 {
|
||||
@@ -963,13 +905,72 @@ pub fn process_get_block(
|
||||
rpc_client.get_slot_with_commitment(CommitmentConfig::finalized())?
|
||||
};
|
||||
|
||||
let encoded_confirmed_block =
|
||||
let mut block =
|
||||
rpc_client.get_confirmed_block_with_encoding(slot, UiTransactionEncoding::Base64)?;
|
||||
let cli_block = CliBlock {
|
||||
encoded_confirmed_block,
|
||||
slot,
|
||||
};
|
||||
Ok(config.output_format.formatted_string(&cli_block))
|
||||
|
||||
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(
|
||||
@@ -1338,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;
|
||||
@@ -1844,7 +1847,6 @@ pub fn process_transaction_history(
|
||||
&confirmed_transaction.transaction.meta,
|
||||
" ",
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
Err(err) => println!(" Unable to get confirmed transaction details: {}", err),
|
||||
@@ -1962,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,4 +1,3 @@
|
||||
#![allow(clippy::integer_arithmetic)]
|
||||
macro_rules! ACCOUNT_STRING {
|
||||
() => {
|
||||
r#", one of:
|
||||
|
@@ -1,4 +1,4 @@
|
||||
use crate::send_tpu::{get_leader_tpus, send_transaction_tpu};
|
||||
use crate::send_tpu::{get_leader_tpu, send_transaction_tpu};
|
||||
use crate::{
|
||||
checks::*,
|
||||
cli::{
|
||||
@@ -10,11 +10,11 @@ use bincode::serialize;
|
||||
use bip39::{Language, Mnemonic, MnemonicType, Seed};
|
||||
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
|
||||
use log::*;
|
||||
use solana_bpf_loader_program::{bpf_verifier, BpfError, ThisInstructionMeter};
|
||||
use serde_json::{self, json, Value};
|
||||
use solana_bpf_loader_program::{bpf_verifier, BPFError, ThisInstructionMeter};
|
||||
use solana_clap_utils::{self, input_parsers::*, input_validators::*, keypair::*};
|
||||
use solana_cli_output::{
|
||||
display::new_spinner_progress_bar, CliProgram, CliProgramAccountType, CliProgramAuthority,
|
||||
CliProgramBuffer, CliProgramId, CliUpgradeableBuffer, CliUpgradeableProgram,
|
||||
display::new_spinner_progress_bar, CliUpgradeableBuffer, CliUpgradeableProgram,
|
||||
};
|
||||
use solana_client::{
|
||||
rpc_client::RpcClient, rpc_config::RpcSendTransactionConfig,
|
||||
@@ -55,7 +55,6 @@ use std::{
|
||||
};
|
||||
|
||||
const DATA_CHUNK_SIZE: usize = 229; // Keep program chunks under PACKET_DATA_SIZE
|
||||
const NUM_TPU_LEADERS: u64 = 2;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ProgramCliCommand {
|
||||
@@ -122,6 +121,7 @@ impl ProgramSubCommands for App<'_, '_> {
|
||||
.value_name("BUFFER_SIGNER")
|
||||
.takes_value(true)
|
||||
.validator(is_valid_signer)
|
||||
.conflicts_with("program_location")
|
||||
.help("Intermediate buffer account to write data to, which can be used to resume a failed deploy \
|
||||
[default: random address]")
|
||||
)
|
||||
@@ -649,34 +649,24 @@ fn process_program_deploy(
|
||||
.get_account_with_commitment(&program_pubkey, config.commitment)?
|
||||
.value
|
||||
{
|
||||
if account.owner != bpf_loader_upgradeable::id() {
|
||||
return Err(format!(
|
||||
"Account {} is not an upgradeable program or already in use",
|
||||
program_pubkey
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
if !account.executable {
|
||||
// Continue an initial deploy
|
||||
true
|
||||
} else if let Ok(UpgradeableLoaderState::Program {
|
||||
} else if let UpgradeableLoaderState::Program {
|
||||
programdata_address,
|
||||
}) = account.state()
|
||||
} = account.state()?
|
||||
{
|
||||
if let Some(account) = rpc_client
|
||||
.get_account_with_commitment(&programdata_address, config.commitment)?
|
||||
.value
|
||||
{
|
||||
if let Ok(UpgradeableLoaderState::ProgramData {
|
||||
if let UpgradeableLoaderState::ProgramData {
|
||||
slot: _,
|
||||
upgrade_authority_address: program_authority_pubkey,
|
||||
}) = account.state()
|
||||
} = account.state()?
|
||||
{
|
||||
if program_authority_pubkey.is_none() {
|
||||
return Err(
|
||||
format!("Program {} is no longer upgradeable", program_pubkey).into(),
|
||||
);
|
||||
return Err("Program is no longer upgradeable".into());
|
||||
}
|
||||
if program_authority_pubkey != Some(upgrade_authority_signer.pubkey()) {
|
||||
return Err(format!(
|
||||
@@ -689,55 +679,46 @@ fn process_program_deploy(
|
||||
// Do upgrade
|
||||
false
|
||||
} else {
|
||||
return Err(format!(
|
||||
"{} is not an upgradeable loader ProgramData account",
|
||||
programdata_address
|
||||
)
|
||||
.into());
|
||||
return Err("Program account is corrupt".into());
|
||||
}
|
||||
} else {
|
||||
return Err(
|
||||
format!("ProgramData account {} does not exist", programdata_address).into(),
|
||||
);
|
||||
return Err("Program account is corrupt".into());
|
||||
}
|
||||
} else {
|
||||
return Err(format!("{} is not an upgradeable program", program_pubkey).into());
|
||||
return Err(
|
||||
format!("Program {:?} is not an upgradeable program", program_pubkey).into(),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// do new deploy
|
||||
true
|
||||
};
|
||||
|
||||
let (program_data, program_len) = if let Some(program_location) = program_location {
|
||||
let program_data = read_and_verify_elf(&program_location)?;
|
||||
let program_len = program_data.len();
|
||||
(program_data, program_len)
|
||||
} else if buffer_provided {
|
||||
let (program_data, program_len) = if buffer_provided {
|
||||
// Check supplied buffer account
|
||||
if let Some(account) = rpc_client
|
||||
.get_account_with_commitment(&buffer_pubkey, config.commitment)?
|
||||
.value
|
||||
{
|
||||
if let Ok(UpgradeableLoaderState::Buffer {
|
||||
if let UpgradeableLoaderState::Buffer {
|
||||
authority_address: _,
|
||||
}) = account.state()
|
||||
} = account.state()?
|
||||
{
|
||||
} else {
|
||||
return Err(format!("Buffer account {} is not initialized", buffer_pubkey).into());
|
||||
return Err("Buffer account is not initialized".into());
|
||||
}
|
||||
(vec![], account.data.len())
|
||||
} else {
|
||||
return Err(format!(
|
||||
"Buffer account {} not found, was it already consumed?",
|
||||
buffer_pubkey
|
||||
)
|
||||
.into());
|
||||
return Err("Buffer account not found, was it already consumed?".into());
|
||||
}
|
||||
} else if let Some(program_location) = program_location {
|
||||
let program_data = read_and_verify_elf(&program_location)?;
|
||||
let program_len = program_data.len();
|
||||
(program_data, program_len)
|
||||
} else {
|
||||
return Err("Program location required if buffer not supplied".into());
|
||||
};
|
||||
let buffer_data_len = program_len;
|
||||
let programdata_len = if let Some(len) = max_len {
|
||||
let buffer_data_len = if let Some(len) = max_len {
|
||||
if program_len > len {
|
||||
return Err("Max length specified not large enough".into());
|
||||
}
|
||||
@@ -757,7 +738,6 @@ fn process_program_deploy(
|
||||
config,
|
||||
&program_data,
|
||||
buffer_data_len,
|
||||
programdata_len,
|
||||
minimum_balance,
|
||||
&bpf_loader_upgradeable::id(),
|
||||
Some(&[program_signer.unwrap(), upgrade_authority_signer]),
|
||||
@@ -824,24 +804,20 @@ fn process_write_buffer(
|
||||
.get_account_with_commitment(&buffer_pubkey, config.commitment)?
|
||||
.value
|
||||
{
|
||||
if let Ok(UpgradeableLoaderState::Buffer { authority_address }) = account.state() {
|
||||
if let UpgradeableLoaderState::Buffer { authority_address } = account.state()? {
|
||||
if authority_address.is_none() {
|
||||
return Err(format!("Buffer {} is immutable", buffer_pubkey).into());
|
||||
return Err("Buffer is immutable".into());
|
||||
}
|
||||
if authority_address != Some(buffer_authority.pubkey()) {
|
||||
return Err(format!(
|
||||
"Buffer's authority {:?} does not match authority provided {}",
|
||||
"Buffer's authority {:?} does not match authority provided {:?}",
|
||||
authority_address,
|
||||
buffer_authority.pubkey()
|
||||
)
|
||||
.into());
|
||||
}
|
||||
} else {
|
||||
return Err(format!(
|
||||
"{} is not an upgradeable loader buffer account",
|
||||
buffer_pubkey
|
||||
)
|
||||
.into());
|
||||
return Err("Buffer account is corrupt".into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -849,7 +825,7 @@ fn process_write_buffer(
|
||||
let buffer_data_len = if let Some(len) = max_len {
|
||||
len
|
||||
} else {
|
||||
program_data.len()
|
||||
program_data.len() * 2
|
||||
};
|
||||
let minimum_balance = rpc_client.get_minimum_balance_for_rent_exemption(
|
||||
UpgradeableLoaderState::programdata_len(buffer_data_len)?,
|
||||
@@ -860,7 +836,6 @@ fn process_write_buffer(
|
||||
config,
|
||||
&program_data,
|
||||
program_data.len(),
|
||||
program_data.len(),
|
||||
minimum_balance,
|
||||
&bpf_loader_upgradeable::id(),
|
||||
None,
|
||||
@@ -932,17 +907,7 @@ fn process_set_authority(
|
||||
)
|
||||
.map_err(|e| format!("Setting authority failed: {}", e))?;
|
||||
|
||||
let authority = CliProgramAuthority {
|
||||
authority: new_authority
|
||||
.map(|pubkey| pubkey.to_string())
|
||||
.unwrap_or_else(|| "none".to_string()),
|
||||
account_type: if program_pubkey.is_some() {
|
||||
CliProgramAccountType::Program
|
||||
} else {
|
||||
CliProgramAccountType::Buffer
|
||||
},
|
||||
};
|
||||
Ok(config.output_format.formatted_string(&authority))
|
||||
Ok(option_pubkey_to_string("authority", new_authority).to_string())
|
||||
}
|
||||
|
||||
fn process_show(
|
||||
@@ -955,77 +920,57 @@ fn process_show(
|
||||
.get_account_with_commitment(&account_pubkey, config.commitment)?
|
||||
.value
|
||||
{
|
||||
if account.owner == bpf_loader::id() || account.owner == bpf_loader_deprecated::id() {
|
||||
Ok(config.output_format.formatted_string(&CliProgram {
|
||||
program_id: account_pubkey.to_string(),
|
||||
owner: account.owner.to_string(),
|
||||
data_len: account.data.len(),
|
||||
}))
|
||||
} else if account.owner == bpf_loader_upgradeable::id() {
|
||||
if let Ok(UpgradeableLoaderState::Program {
|
||||
programdata_address,
|
||||
}) = account.state()
|
||||
if let Ok(UpgradeableLoaderState::Program {
|
||||
programdata_address,
|
||||
}) = account.state()
|
||||
{
|
||||
if let Some(programdata_account) = rpc_client
|
||||
.get_account_with_commitment(&programdata_address, config.commitment)?
|
||||
.value
|
||||
{
|
||||
if let Some(programdata_account) = rpc_client
|
||||
.get_account_with_commitment(&programdata_address, config.commitment)?
|
||||
.value
|
||||
if let Ok(UpgradeableLoaderState::ProgramData {
|
||||
upgrade_authority_address,
|
||||
slot,
|
||||
}) = programdata_account.state()
|
||||
{
|
||||
if let Ok(UpgradeableLoaderState::ProgramData {
|
||||
upgrade_authority_address,
|
||||
slot,
|
||||
}) = programdata_account.state()
|
||||
{
|
||||
Ok(config
|
||||
.output_format
|
||||
.formatted_string(&CliUpgradeableProgram {
|
||||
program_id: account_pubkey.to_string(),
|
||||
owner: account.owner.to_string(),
|
||||
programdata_address: programdata_address.to_string(),
|
||||
authority: upgrade_authority_address
|
||||
.map(|pubkey| pubkey.to_string())
|
||||
.unwrap_or_else(|| "none".to_string()),
|
||||
last_deploy_slot: slot,
|
||||
data_len: programdata_account.data.len()
|
||||
- UpgradeableLoaderState::programdata_data_offset()?,
|
||||
}))
|
||||
} else {
|
||||
Err(format!("Invalid associated ProgramData account {} found for the program {}",
|
||||
programdata_address, account_pubkey)
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
Ok(config
|
||||
.output_format
|
||||
.formatted_string(&CliUpgradeableProgram {
|
||||
program_id: account_pubkey.to_string(),
|
||||
programdata_address: programdata_address.to_string(),
|
||||
authority: upgrade_authority_address
|
||||
.map(|pubkey| pubkey.to_string())
|
||||
.unwrap_or_else(|| "none".to_string()),
|
||||
last_deploy_slot: slot,
|
||||
data_len: programdata_account.data.len()
|
||||
- UpgradeableLoaderState::programdata_data_offset()?,
|
||||
}))
|
||||
} else {
|
||||
Err(format!(
|
||||
"Failed to find associated ProgramData account {} for the program {}",
|
||||
programdata_address, account_pubkey
|
||||
)
|
||||
.into())
|
||||
Err("Invalid associated ProgramData account found for the program".into())
|
||||
}
|
||||
} else if let Ok(UpgradeableLoaderState::Buffer { authority_address }) =
|
||||
account.state()
|
||||
{
|
||||
Ok(config
|
||||
.output_format
|
||||
.formatted_string(&CliUpgradeableBuffer {
|
||||
address: account_pubkey.to_string(),
|
||||
authority: authority_address
|
||||
.map(|pubkey| pubkey.to_string())
|
||||
.unwrap_or_else(|| "none".to_string()),
|
||||
data_len: account.data.len()
|
||||
- UpgradeableLoaderState::buffer_data_offset()?,
|
||||
}))
|
||||
} else {
|
||||
Err(format!(
|
||||
"{} is not an upgradeble loader buffer or program account",
|
||||
account_pubkey
|
||||
Err(
|
||||
"Failed to find associated ProgramData account for the provided program"
|
||||
.into(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
} else if let Ok(UpgradeableLoaderState::Buffer { authority_address }) = account.state()
|
||||
{
|
||||
Ok(config
|
||||
.output_format
|
||||
.formatted_string(&CliUpgradeableBuffer {
|
||||
address: account_pubkey.to_string(),
|
||||
authority: authority_address
|
||||
.map(|pubkey| pubkey.to_string())
|
||||
.unwrap_or_else(|| "none".to_string()),
|
||||
data_len: account.data.len()
|
||||
- UpgradeableLoaderState::buffer_data_offset()?,
|
||||
}))
|
||||
} else {
|
||||
Err(format!("{} is not a BPF program", account_pubkey).into())
|
||||
Err("Not a buffer or program account".into())
|
||||
}
|
||||
} else {
|
||||
Err(format!("Unable to find the account {}", account_pubkey).into())
|
||||
Err("Unable to find the account".into())
|
||||
}
|
||||
} else {
|
||||
Err("No account specified".into())
|
||||
@@ -1043,60 +988,42 @@ fn process_dump(
|
||||
.get_account_with_commitment(&account_pubkey, config.commitment)?
|
||||
.value
|
||||
{
|
||||
if account.owner == bpf_loader::id() || account.owner == bpf_loader_deprecated::id() {
|
||||
let mut f = File::create(output_location)?;
|
||||
f.write_all(&account.data)?;
|
||||
Ok(format!("Wrote program to {}", output_location))
|
||||
} else if account.owner == bpf_loader_upgradeable::id() {
|
||||
if let Ok(UpgradeableLoaderState::Program {
|
||||
programdata_address,
|
||||
}) = account.state()
|
||||
if let Ok(UpgradeableLoaderState::Program {
|
||||
programdata_address,
|
||||
}) = account.state()
|
||||
{
|
||||
if let Some(programdata_account) = rpc_client
|
||||
.get_account_with_commitment(&programdata_address, config.commitment)?
|
||||
.value
|
||||
{
|
||||
if let Some(programdata_account) = rpc_client
|
||||
.get_account_with_commitment(&programdata_address, config.commitment)?
|
||||
.value
|
||||
if let Ok(UpgradeableLoaderState::ProgramData { .. }) =
|
||||
programdata_account.state()
|
||||
{
|
||||
if let Ok(UpgradeableLoaderState::ProgramData { .. }) =
|
||||
programdata_account.state()
|
||||
{
|
||||
let offset =
|
||||
UpgradeableLoaderState::programdata_data_offset().unwrap_or(0);
|
||||
let program_data = &programdata_account.data[offset..];
|
||||
let mut f = File::create(output_location)?;
|
||||
f.write_all(&program_data)?;
|
||||
Ok(format!("Wrote program to {}", output_location))
|
||||
} else {
|
||||
Err(
|
||||
format!("Invalid associated ProgramData account {} found for the program {}",
|
||||
programdata_address, account_pubkey)
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
let offset = UpgradeableLoaderState::programdata_data_offset().unwrap_or(0);
|
||||
let program_data = &programdata_account.data[offset..];
|
||||
let mut f = File::create(output_location)?;
|
||||
f.write_all(&program_data)?;
|
||||
Ok(format!("Wrote program to {}", output_location))
|
||||
} else {
|
||||
Err(format!(
|
||||
"Failed to find associated ProgramData account {} for the program {}",
|
||||
programdata_address, account_pubkey
|
||||
)
|
||||
.into())
|
||||
Err("Invalid associated ProgramData account found for the program".into())
|
||||
}
|
||||
} else if let Ok(UpgradeableLoaderState::Buffer { .. }) = account.state() {
|
||||
let offset = UpgradeableLoaderState::buffer_data_offset().unwrap_or(0);
|
||||
let program_data = &account.data[offset..];
|
||||
let mut f = File::create(output_location)?;
|
||||
f.write_all(&program_data)?;
|
||||
Ok(format!("Wrote program to {}", output_location))
|
||||
} else {
|
||||
Err(format!(
|
||||
"{} is not an upgradeble loader buffer or program account",
|
||||
account_pubkey
|
||||
Err(
|
||||
"Failed to find associated ProgramData account for the provided program"
|
||||
.into(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
} else if let Ok(UpgradeableLoaderState::Buffer { .. }) = account.state() {
|
||||
let offset = UpgradeableLoaderState::buffer_data_offset().unwrap_or(0);
|
||||
let program_data = &account.data[offset..];
|
||||
let mut f = File::create(output_location)?;
|
||||
f.write_all(&program_data)?;
|
||||
Ok(format!("Wrote program to {}", output_location))
|
||||
} else {
|
||||
Err(format!("{} is not a BPF program", account_pubkey).into())
|
||||
Err("Not a buffer or program account".into())
|
||||
}
|
||||
} else {
|
||||
Err(format!("Unable to find the account {}", account_pubkey).into())
|
||||
Err("Unable to find the account".into())
|
||||
}
|
||||
} else {
|
||||
Err("No account specified".into())
|
||||
@@ -1133,7 +1060,6 @@ pub fn process_deploy(
|
||||
config,
|
||||
&program_data,
|
||||
program_data.len(),
|
||||
program_data.len(),
|
||||
minimum_balance,
|
||||
&loader_id,
|
||||
Some(&[buffer_signer]),
|
||||
@@ -1154,7 +1080,6 @@ fn do_process_program_write_and_deploy(
|
||||
config: &CliConfig,
|
||||
program_data: &[u8],
|
||||
buffer_data_len: usize,
|
||||
programdata_len: usize,
|
||||
minimum_balance: u64,
|
||||
loader_id: &Pubkey,
|
||||
program_signers: Option<&[&dyn Signer]>,
|
||||
@@ -1270,7 +1195,7 @@ fn do_process_program_write_and_deploy(
|
||||
rpc_client.get_minimum_balance_for_rent_exemption(
|
||||
UpgradeableLoaderState::program_len()?,
|
||||
)?,
|
||||
programdata_len,
|
||||
buffer_data_len,
|
||||
)?,
|
||||
Some(&config.signers[0].pubkey()),
|
||||
)
|
||||
@@ -1302,15 +1227,15 @@ fn do_process_program_write_and_deploy(
|
||||
)?;
|
||||
|
||||
if let Some(program_signers) = program_signers {
|
||||
let program_id = CliProgramId {
|
||||
program_id: program_signers[0].pubkey().to_string(),
|
||||
};
|
||||
Ok(config.output_format.formatted_string(&program_id))
|
||||
Ok(json!({
|
||||
"programId": format!("{}", program_signers[0].pubkey()),
|
||||
})
|
||||
.to_string())
|
||||
} else {
|
||||
let buffer = CliProgramBuffer {
|
||||
buffer: buffer_pubkey.to_string(),
|
||||
};
|
||||
Ok(config.output_format.formatted_string(&buffer))
|
||||
Ok(json!({
|
||||
"buffer": format!("{}", buffer_pubkey),
|
||||
})
|
||||
.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1423,10 +1348,10 @@ fn do_process_program_upgrade(
|
||||
Some(&[upgrade_authority]),
|
||||
)?;
|
||||
|
||||
let program_id = CliProgramId {
|
||||
program_id: program_id.to_string(),
|
||||
};
|
||||
Ok(config.output_format.formatted_string(&program_id))
|
||||
Ok(json!({
|
||||
"programId": format!("{}", program_id),
|
||||
})
|
||||
.to_string())
|
||||
}
|
||||
|
||||
fn read_and_verify_elf(program_location: &str) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
|
||||
@@ -1437,9 +1362,9 @@ fn read_and_verify_elf(program_location: &str) -> Result<Vec<u8>, Box<dyn std::e
|
||||
.map_err(|err| format!("Unable to read program file: {}", err))?;
|
||||
|
||||
// Verify the program
|
||||
Executable::<BpfError, ThisInstructionMeter>::from_elf(
|
||||
Executable::<BPFError, ThisInstructionMeter>::from_elf(
|
||||
&program_data,
|
||||
Some(|x| bpf_verifier::check(x)),
|
||||
Some(|x| bpf_verifier::check(x, false)),
|
||||
Config::default(),
|
||||
)
|
||||
.map_err(|err| format!("ELF error: {}", err))?;
|
||||
@@ -1622,11 +1547,22 @@ fn report_ephemeral_mnemonic(words: usize, mnemonic: bip39::Mnemonic) {
|
||||
words
|
||||
);
|
||||
eprintln!(
|
||||
"then pass it as the [BUFFER_SIGNER] argument to `solana deploy` or `solana write-buffer`\n{}\n{}\n{}",
|
||||
"then pass it as the [BUFFER_SIGNER] argument to `solana upgrade ...`\n{}\n{}\n{}",
|
||||
divider, phrase, divider
|
||||
);
|
||||
}
|
||||
|
||||
fn option_pubkey_to_string(tag: &str, option: Option<Pubkey>) -> Value {
|
||||
match option {
|
||||
Some(pubkey) => json!({
|
||||
tag: format!("{:?}", pubkey),
|
||||
}),
|
||||
None => json!({
|
||||
tag: "none",
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn send_and_confirm_transactions_with_spinner<T: Signers>(
|
||||
rpc_client: &RpcClient,
|
||||
mut transactions: Vec<Transaction>,
|
||||
@@ -1642,7 +1578,7 @@ fn send_and_confirm_transactions_with_spinner<T: Signers>(
|
||||
let cluster_nodes = rpc_client.get_cluster_nodes().ok();
|
||||
|
||||
loop {
|
||||
progress_bar.set_message("Finding leader nodes...");
|
||||
progress_bar.set_message("Finding leader node...");
|
||||
let epoch_info = rpc_client.get_epoch_info()?;
|
||||
let mut slot = epoch_info.absolute_slot;
|
||||
let mut last_epoch_fetch = Instant::now();
|
||||
@@ -1651,9 +1587,8 @@ fn send_and_confirm_transactions_with_spinner<T: Signers>(
|
||||
leader_schedule_epoch = epoch_info.epoch;
|
||||
}
|
||||
|
||||
let mut tpu_addresses = get_leader_tpus(
|
||||
let mut tpu_address = get_leader_tpu(
|
||||
min(epoch_info.slot_index + 1, epoch_info.slots_in_epoch),
|
||||
NUM_TPU_LEADERS,
|
||||
leader_schedule.as_ref(),
|
||||
cluster_nodes.as_ref(),
|
||||
);
|
||||
@@ -1662,12 +1597,10 @@ fn send_and_confirm_transactions_with_spinner<T: Signers>(
|
||||
let mut pending_transactions = HashMap::new();
|
||||
let num_transactions = transactions.len();
|
||||
for transaction in transactions {
|
||||
if !tpu_addresses.is_empty() {
|
||||
if let Some(tpu_address) = tpu_address {
|
||||
let wire_transaction =
|
||||
serialize(&transaction).expect("serialization should succeed");
|
||||
for tpu_address in &tpu_addresses {
|
||||
send_transaction_tpu(&send_socket, &tpu_address, &wire_transaction);
|
||||
}
|
||||
send_transaction_tpu(&send_socket, &tpu_address, &wire_transaction);
|
||||
} else {
|
||||
let _result = rpc_client
|
||||
.send_transaction_with_config(
|
||||
@@ -1693,9 +1626,8 @@ fn send_and_confirm_transactions_with_spinner<T: Signers>(
|
||||
if last_epoch_fetch.elapsed() > Duration::from_millis(400) {
|
||||
let epoch_info = rpc_client.get_epoch_info()?;
|
||||
last_epoch_fetch = Instant::now();
|
||||
tpu_addresses = get_leader_tpus(
|
||||
tpu_address = get_leader_tpu(
|
||||
min(epoch_info.slot_index + 1, epoch_info.slots_in_epoch),
|
||||
NUM_TPU_LEADERS,
|
||||
leader_schedule.as_ref(),
|
||||
cluster_nodes.as_ref(),
|
||||
);
|
||||
@@ -1708,7 +1640,9 @@ fn send_and_confirm_transactions_with_spinner<T: Signers>(
|
||||
for pending_signatures_chunk in
|
||||
pending_signatures.chunks(MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS)
|
||||
{
|
||||
if let Ok(result) = rpc_client.get_signature_statuses(pending_signatures_chunk) {
|
||||
if let Ok(result) =
|
||||
rpc_client.get_signature_statuses_with_history(pending_signatures_chunk)
|
||||
{
|
||||
let statuses = result.value;
|
||||
for (signature, status) in
|
||||
pending_signatures_chunk.iter().zip(statuses.into_iter())
|
||||
@@ -1746,20 +1680,17 @@ fn send_and_confirm_transactions_with_spinner<T: Signers>(
|
||||
}
|
||||
|
||||
let epoch_info = rpc_client.get_epoch_info()?;
|
||||
tpu_addresses = get_leader_tpus(
|
||||
tpu_address = get_leader_tpu(
|
||||
min(epoch_info.slot_index + 1, epoch_info.slots_in_epoch),
|
||||
NUM_TPU_LEADERS,
|
||||
leader_schedule.as_ref(),
|
||||
cluster_nodes.as_ref(),
|
||||
);
|
||||
|
||||
for transaction in pending_transactions.values() {
|
||||
if !tpu_addresses.is_empty() {
|
||||
if let Some(tpu_address) = tpu_address {
|
||||
let wire_transaction =
|
||||
serialize(&transaction).expect("serialization should succeed");
|
||||
for tpu_address in &tpu_addresses {
|
||||
send_transaction_tpu(&send_socket, &tpu_address, &wire_transaction);
|
||||
}
|
||||
serialize(transaction).expect("serialization should succeed");
|
||||
send_transaction_tpu(&send_socket, &tpu_address, &wire_transaction);
|
||||
} else {
|
||||
let _result = rpc_client
|
||||
.send_transaction_with_config(
|
||||
@@ -1802,7 +1733,6 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::cli::{app, parse_command, process_command};
|
||||
use serde_json::Value;
|
||||
use solana_cli_output::OutputFormat;
|
||||
use solana_sdk::signature::write_keypair_file;
|
||||
|
||||
fn make_tmp_path(name: &str) -> String {
|
||||
@@ -2389,7 +2319,6 @@ mod tests {
|
||||
allow_excessive_balance: false,
|
||||
}),
|
||||
signers: vec![&default_keypair],
|
||||
output_format: OutputFormat::JsonCompact,
|
||||
..CliConfig::default()
|
||||
};
|
||||
|
||||
|
@@ -1,38 +1,21 @@
|
||||
use log::*;
|
||||
use solana_client::rpc_response::{RpcContactInfo, RpcLeaderSchedule};
|
||||
use solana_sdk::clock::NUM_CONSECUTIVE_LEADER_SLOTS;
|
||||
use std::net::{SocketAddr, UdpSocket};
|
||||
|
||||
pub fn get_leader_tpus(
|
||||
pub fn get_leader_tpu(
|
||||
slot_index: u64,
|
||||
num_leaders: u64,
|
||||
leader_schedule: Option<&RpcLeaderSchedule>,
|
||||
cluster_nodes: Option<&Vec<RpcContactInfo>>,
|
||||
) -> Vec<SocketAddr> {
|
||||
let leaders: Vec<_> = (0..num_leaders)
|
||||
.filter_map(|i| {
|
||||
leader_schedule?
|
||||
) -> Option<SocketAddr> {
|
||||
leader_schedule?
|
||||
.iter()
|
||||
.find(|(_pubkey, slots)| slots.iter().any(|slot| *slot as u64 == slot_index))
|
||||
.and_then(|(pubkey, _)| {
|
||||
cluster_nodes?
|
||||
.iter()
|
||||
.find(|(_pubkey, slots)| {
|
||||
slots.iter().any(|slot| {
|
||||
*slot as u64 == (slot_index + (i * NUM_CONSECUTIVE_LEADER_SLOTS))
|
||||
})
|
||||
})
|
||||
.and_then(|(pubkey, _)| {
|
||||
cluster_nodes?
|
||||
.iter()
|
||||
.find(|contact_info| contact_info.pubkey == *pubkey)
|
||||
.and_then(|contact_info| contact_info.tpu)
|
||||
})
|
||||
.find(|contact_info| contact_info.pubkey == *pubkey)
|
||||
.and_then(|contact_info| contact_info.tpu)
|
||||
})
|
||||
.collect();
|
||||
let mut unique_leaders = vec![];
|
||||
for leader in leaders.into_iter() {
|
||||
if !unique_leaders.contains(&leader) {
|
||||
unique_leaders.push(leader);
|
||||
}
|
||||
}
|
||||
unique_leaders
|
||||
}
|
||||
|
||||
pub fn send_transaction_tpu(
|
||||
|
@@ -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))
|
||||
|
@@ -268,7 +268,7 @@ impl StakeSubCommands for App<'_, '_> {
|
||||
.arg(
|
||||
Arg::with_name("split_stake_account")
|
||||
.index(2)
|
||||
.value_name("SPLIT_STAKE_ACCOUNT")
|
||||
.value_name("ACCOUNT_KEYPAIR")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.validator(is_valid_signer)
|
||||
@@ -288,7 +288,7 @@ impl StakeSubCommands for App<'_, '_> {
|
||||
.long("seed")
|
||||
.value_name("STRING")
|
||||
.takes_value(true)
|
||||
.help("Seed for address generation; if specified, the resulting account will be at a derived address of the SPLIT_STAKE_ACCOUNT pubkey")
|
||||
.help("Seed for address generation; if specified, the resulting account will be at a derived address of the SPLIT STAKE ACCOUNT pubkey")
|
||||
)
|
||||
.arg(stake_authority_arg())
|
||||
.offline_args()
|
||||
@@ -1747,10 +1747,9 @@ pub fn process_show_stake_history(
|
||||
use_lamports_unit: bool,
|
||||
) -> ProcessResult {
|
||||
let stake_history_account = rpc_client.get_account(&stake_history::id())?;
|
||||
let stake_history =
|
||||
from_account::<StakeHistory, _>(&stake_history_account).ok_or_else(|| {
|
||||
CliError::RpcRequestError("Failed to deserialize stake history".to_string())
|
||||
})?;
|
||||
let stake_history = from_account::<StakeHistory>(&stake_history_account).ok_or_else(|| {
|
||||
CliError::RpcRequestError("Failed to deserialize stake history".to_string())
|
||||
})?;
|
||||
|
||||
let mut entries: Vec<CliStakeHistoryEntry> = vec![];
|
||||
for entry in stake_history.deref() {
|
||||
|
@@ -298,8 +298,6 @@ fn test_create_account_with_seed() {
|
||||
nonce_account: Some(nonce_address),
|
||||
nonce_authority: 0,
|
||||
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();
|
||||
@@ -324,8 +322,6 @@ fn test_create_account_with_seed() {
|
||||
nonce_account: Some(nonce_address),
|
||||
nonce_authority: 0,
|
||||
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);
|
||||
|
@@ -59,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
|
||||
@@ -192,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
|
||||
@@ -571,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();
|
||||
|
||||
@@ -831,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
|
||||
@@ -1078,9 +1075,8 @@ 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];
|
||||
@@ -1133,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()
|
||||
|
@@ -286,7 +286,7 @@ 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(
|
||||
@@ -535,7 +535,7 @@ 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(
|
||||
@@ -1295,7 +1295,7 @@ 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(
|
||||
|
@@ -56,8 +56,6 @@ fn test_transfer() {
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
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);
|
||||
@@ -74,8 +72,6 @@ fn test_transfer() {
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
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);
|
||||
@@ -104,8 +100,6 @@ fn test_transfer() {
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
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();
|
||||
@@ -123,8 +117,6 @@ fn test_transfer() {
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_recent_balance(39, &rpc_client, &offline_pubkey);
|
||||
@@ -170,8 +162,6 @@ fn test_transfer() {
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
nonce_authority: 0,
|
||||
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);
|
||||
@@ -218,8 +208,6 @@ fn test_transfer() {
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
nonce_authority: 0,
|
||||
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);
|
||||
@@ -239,8 +227,6 @@ fn test_transfer() {
|
||||
nonce_account: Some(nonce_account.pubkey()),
|
||||
nonce_authority: 0,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
check_recent_balance(28, &rpc_client, &offline_pubkey);
|
||||
@@ -304,8 +290,6 @@ fn test_transfer_multisession_signing() {
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
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();
|
||||
@@ -332,8 +316,6 @@ fn test_transfer_multisession_signing() {
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
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();
|
||||
@@ -357,8 +339,6 @@ fn test_transfer_multisession_signing() {
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
|
||||
@@ -404,66 +384,8 @@ fn test_transfer_all() {
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
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_with_seed() {
|
||||
solana_logger::setup();
|
||||
let mint_keypair = Keypair::new();
|
||||
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
|
||||
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, &faucet_addr, &sender_pubkey, 1, &config).unwrap();
|
||||
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &derived_address, 50_000, &config)
|
||||
.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,
|
||||
no_wait: false,
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
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);
|
||||
}
|
||||
|
@@ -76,8 +76,6 @@ fn test_vote_authorize_and_withdraw() {
|
||||
nonce_account: None,
|
||||
nonce_authority: 0,
|
||||
fee_payer: 0,
|
||||
derived_address_seed: None,
|
||||
derived_address_program_id: None,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let expected_balance = expected_balance + 1_000;
|
||||
|
@@ -1,11 +1,10 @@
|
||||
[package]
|
||||
name = "solana-client"
|
||||
version = "1.6.0"
|
||||
version = "1.5.6"
|
||||
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,30 +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"
|
||||
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.0" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.6.0" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.6.0" }
|
||||
solana-sdk = { path = "../sdk", version = "1.6.0" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "1.6.0" }
|
||||
solana-version = { path = "../version", version = "1.6.0" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.6.0" }
|
||||
solana-account-decoder = { path = "../account-decoder", version = "1.5.6" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.5.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.5.6" }
|
||||
solana-sdk = { path = "../sdk", version = "1.5.6" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "1.5.6" }
|
||||
solana-version = { path = "../version", version = "1.5.6" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.5.6" }
|
||||
thiserror = "1.0"
|
||||
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.0" }
|
||||
jsonrpc-core = "15.0.0"
|
||||
jsonrpc-http-server = "15.0.0"
|
||||
solana-logger = { path = "../logger", version = "1.5.6" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
@@ -85,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) => {
|
||||
|
@@ -1,4 +1,3 @@
|
||||
#![allow(clippy::integer_arithmetic)]
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
@@ -9,7 +8,6 @@ pub mod mock_sender;
|
||||
pub mod nonce_utils;
|
||||
pub mod perf_utils;
|
||||
pub mod pubsub_client;
|
||||
pub mod rpc_cache;
|
||||
pub mod rpc_client;
|
||||
pub mod rpc_config;
|
||||
pub mod rpc_custom_error;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
use crate::rpc_client::RpcClient;
|
||||
use solana_sdk::{
|
||||
account::{Account, ReadableAccount},
|
||||
account::Account,
|
||||
account_utils::StateMut,
|
||||
commitment_config::CommitmentConfig,
|
||||
nonce::{
|
||||
@@ -52,28 +52,24 @@ pub fn get_account_with_commitment(
|
||||
})
|
||||
}
|
||||
|
||||
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()))
|
||||
}
|
||||
|
@@ -98,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()
|
||||
|
@@ -1,75 +0,0 @@
|
||||
use crate::{rpc_config::RpcLargestAccountsFilter, rpc_response::RpcAccountBalance};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
time::{Duration, SystemTime},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LargestAccountsCache {
|
||||
duration: u64,
|
||||
cache: HashMap<Option<RpcLargestAccountsFilter>, LargestAccountsCacheValue>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct LargestAccountsCacheValue {
|
||||
accounts: Vec<RpcAccountBalance>,
|
||||
slot: u64,
|
||||
cached_time: SystemTime,
|
||||
}
|
||||
|
||||
impl LargestAccountsCache {
|
||||
pub fn new(duration: u64) -> Self {
|
||||
Self {
|
||||
duration,
|
||||
cache: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_largest_accounts(
|
||||
&self,
|
||||
filter: &Option<RpcLargestAccountsFilter>,
|
||||
) -> Option<(u64, Vec<RpcAccountBalance>)> {
|
||||
self.cache.get(&filter).and_then(|value| {
|
||||
if let Ok(elapsed) = value.cached_time.elapsed() {
|
||||
if elapsed < Duration::from_secs(self.duration) {
|
||||
return Some((value.slot, value.accounts.clone()));
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_largest_accounts(
|
||||
&mut self,
|
||||
filter: &Option<RpcLargestAccountsFilter>,
|
||||
slot: u64,
|
||||
accounts: &[RpcAccountBalance],
|
||||
) {
|
||||
self.cache.insert(
|
||||
filter.clone(),
|
||||
LargestAccountsCacheValue {
|
||||
accounts: accounts.to_owned(),
|
||||
slot,
|
||||
cached_time: SystemTime::now(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_old_entries_expire() {
|
||||
let mut cache = LargestAccountsCache::new(1);
|
||||
|
||||
let filter = Some(RpcLargestAccountsFilter::Circulating);
|
||||
|
||||
let accounts: Vec<RpcAccountBalance> = Vec::new();
|
||||
|
||||
cache.set_largest_accounts(&filter, 1000, &accounts);
|
||||
std::thread::sleep(Duration::from_secs(1));
|
||||
assert_eq!(cache.get_largest_accounts(&filter), None);
|
||||
}
|
||||
}
|
@@ -22,7 +22,10 @@ use solana_account_decoder::{
|
||||
};
|
||||
use solana_sdk::{
|
||||
account::Account,
|
||||
clock::{Slot, UnixTimestamp, DEFAULT_MS_PER_SLOT, MAX_HASH_AGE_IN_SECONDS},
|
||||
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,
|
||||
@@ -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)
|
||||
@@ -1003,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!(
|
||||
@@ -1505,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,
|
||||
@@ -1572,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::{
|
||||
@@ -1589,14 +1591,14 @@ mod tests {
|
||||
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(),
|
||||
))
|
||||
}
|
||||
|
@@ -31,7 +31,7 @@ pub struct RpcSimulateTransactionConfig {
|
||||
pub encoding: Option<UiTransactionEncoding>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum RpcLargestAccountsFilter {
|
||||
Circulating,
|
||||
@@ -108,51 +108,3 @@ pub struct RpcGetConfirmedSignaturesForAddress2Config {
|
||||
pub until: Option<String>, // Signature as base-58 string
|
||||
pub limit: Option<usize>,
|
||||
}
|
||||
|
||||
#[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>,
|
||||
}
|
||||
|
||||
impl EncodingConfig for RpcConfirmedBlockConfig {
|
||||
fn new_with_encoding(encoding: &Option<UiTransactionEncoding>) -> Self {
|
||||
Self {
|
||||
encoding: *encoding,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcConfirmedTransactionConfig {
|
||||
pub encoding: Option<UiTransactionEncoding>,
|
||||
}
|
||||
|
||||
impl EncodingConfig for RpcConfirmedTransactionConfig {
|
||||
fn new_with_encoding(encoding: &Option<UiTransactionEncoding>) -> Self {
|
||||
Self {
|
||||
encoding: *encoding,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ 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;
|
||||
@@ -80,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 {
|
||||
|
@@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@ use thiserror::Error;
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||
pub enum RpcRequest {
|
||||
DeregisterNode,
|
||||
ValidatorExit,
|
||||
GetAccountInfo,
|
||||
GetBalance,
|
||||
GetBlockTime,
|
||||
@@ -63,6 +64,7 @@ impl fmt::Display for RpcRequest {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let method = match self {
|
||||
RpcRequest::DeregisterNode => "deregisterNode",
|
||||
RpcRequest::ValidatorExit => "validatorExit",
|
||||
RpcRequest::GetAccountInfo => "getAccountInfo",
|
||||
RpcRequest::GetBalance => "getBalance",
|
||||
RpcRequest::GetBlockTime => "getBlockTime",
|
||||
@@ -125,7 +127,6 @@ pub const MAX_GET_CONFIRMED_BLOCKS_RANGE: u64 = 500_000;
|
||||
pub const MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS2_LIMIT: usize = 1_000;
|
||||
pub const MAX_MULTIPLE_ACCOUNTS: usize = 100;
|
||||
pub const NUM_LARGEST_ACCOUNTS: usize = 20;
|
||||
pub const MAX_GET_PROGRAM_ACCOUNT_FILTERS: usize = 4;
|
||||
|
||||
// Validators that are this number of slots behind are considered delinquent
|
||||
pub const DELINQUENT_VALIDATOR_SLOT_DISTANCE: u64 = 128;
|
||||
|
@@ -101,65 +101,6 @@ pub struct SlotInfo {
|
||||
pub root: Slot,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SlotTransactionStats {
|
||||
pub num_transaction_entries: u64,
|
||||
pub num_successful_transactions: u64,
|
||||
pub num_failed_transactions: u64,
|
||||
pub max_transactions_per_entry: u64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase", tag = "type")]
|
||||
pub enum SlotUpdate {
|
||||
FirstShredReceived {
|
||||
slot: Slot,
|
||||
timestamp: u64,
|
||||
},
|
||||
Completed {
|
||||
slot: Slot,
|
||||
timestamp: u64,
|
||||
},
|
||||
CreatedBank {
|
||||
slot: Slot,
|
||||
parent: Slot,
|
||||
timestamp: u64,
|
||||
},
|
||||
Frozen {
|
||||
slot: Slot,
|
||||
timestamp: u64,
|
||||
stats: SlotTransactionStats,
|
||||
},
|
||||
Dead {
|
||||
slot: Slot,
|
||||
timestamp: u64,
|
||||
err: String,
|
||||
},
|
||||
OptimisticConfirmation {
|
||||
slot: Slot,
|
||||
timestamp: u64,
|
||||
},
|
||||
Root {
|
||||
slot: Slot,
|
||||
timestamp: u64,
|
||||
},
|
||||
}
|
||||
|
||||
impl SlotUpdate {
|
||||
pub fn slot(&self) -> Slot {
|
||||
match self {
|
||||
Self::FirstShredReceived { slot, .. } => *slot,
|
||||
Self::Completed { slot, .. } => *slot,
|
||||
Self::CreatedBank { slot, .. } => *slot,
|
||||
Self::Frozen { slot, .. } => *slot,
|
||||
Self::Dead { slot, .. } => *slot,
|
||||
Self::OptimisticConfirmation { slot, .. } => *slot,
|
||||
Self::Root { slot, .. } => *slot,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(rename_all = "camelCase", untagged)]
|
||||
pub enum RpcSignatureResult {
|
||||
|
@@ -309,6 +309,10 @@ impl ThinClient {
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
pub fn validator_exit(&self) -> TransportResult<bool> {
|
||||
self.rpc_client().validator_exit().map_err(|e| e.into())
|
||||
}
|
||||
|
||||
pub fn get_num_blocks_since_signature_confirmation(
|
||||
&mut self,
|
||||
sig: &Signature,
|
||||
|
@@ -1,9 +1,9 @@
|
||||
[package]
|
||||
name = "solana-core"
|
||||
description = "Blockchain, Rebuilt for Scale"
|
||||
version = "1.6.0"
|
||||
version = "1.5.6"
|
||||
documentation = "https://docs.rs/solana"
|
||||
homepage = "https://solana.com/"
|
||||
documentation = "https://docs.rs/solana-core"
|
||||
readme = "../README.md"
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
@@ -23,18 +23,17 @@ byteorder = "1.3.4"
|
||||
chrono = { version = "0.4.11", features = ["serde"] }
|
||||
core_affinity = "0.5.10"
|
||||
crossbeam-channel = "0.4"
|
||||
ed25519-dalek = "=1.0.1"
|
||||
fs_extra = "1.2.0"
|
||||
ed25519-dalek = "=1.0.0-pre.4"
|
||||
fs_extra = "1.1.0"
|
||||
flate2 = "1.0"
|
||||
indexmap = { version = "1.5", features = ["rayon"] }
|
||||
itertools = "0.9.0"
|
||||
jsonrpc-core = "17.0.0"
|
||||
jsonrpc-core-client = { version = "17.0.0", features = ["ipc", "ws"] }
|
||||
jsonrpc-derive = "17.0.0"
|
||||
jsonrpc-http-server = "17.0.0"
|
||||
jsonrpc-pubsub = "17.0.0"
|
||||
jsonrpc-ws-server = "17.0.0"
|
||||
libc = "0.2.81"
|
||||
jsonrpc-core = "15.0.0"
|
||||
jsonrpc-core-client = { version = "15.0.0", features = ["ws"] }
|
||||
jsonrpc-derive = "15.0.0"
|
||||
jsonrpc-http-server = "15.0.0"
|
||||
jsonrpc-pubsub = "15.0.0"
|
||||
jsonrpc-ws-server = "15.0.0"
|
||||
log = "0.4.11"
|
||||
lru = "0.6.1"
|
||||
miow = "0.2.2"
|
||||
@@ -42,47 +41,48 @@ net2 = "0.2.37"
|
||||
num-traits = "0.2"
|
||||
rand = "0.7.0"
|
||||
rand_chacha = "0.2.2"
|
||||
rand_core = "0.6.2"
|
||||
raptorq = "1.4.2"
|
||||
rayon = "1.5.0"
|
||||
rayon = "1.4.1"
|
||||
regex = "1.3.9"
|
||||
retain_mut = "0.1.2"
|
||||
rustversion = "1.0.4"
|
||||
serde = "1.0.122"
|
||||
serde = "1.0.112"
|
||||
serde_bytes = "0.11"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.56"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "1.6.0" }
|
||||
solana-banks-server = { path = "../banks-server", version = "1.6.0" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.6.0" }
|
||||
solana-client = { path = "../client", version = "1.6.0" }
|
||||
solana-faucet = { path = "../faucet", version = "1.6.0" }
|
||||
solana-ledger = { path = "../ledger", version = "1.6.0" }
|
||||
solana-logger = { path = "../logger", version = "1.6.0" }
|
||||
solana-merkle-tree = { path = "../merkle-tree", version = "1.6.0" }
|
||||
solana-metrics = { path = "../metrics", version = "1.6.0" }
|
||||
solana-measure = { path = "../measure", version = "1.6.0" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.6.0" }
|
||||
solana-perf = { path = "../perf", version = "1.6.0" }
|
||||
solana-program-test = { path = "../program-test", version = "1.6.0" }
|
||||
solana-runtime = { path = "../runtime", version = "1.6.0" }
|
||||
solana-sdk = { path = "../sdk", version = "1.6.0" }
|
||||
solana-frozen-abi = { path = "../frozen-abi", version = "1.6.0" }
|
||||
solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "1.6.0" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "1.6.0" }
|
||||
solana-storage-bigtable = { path = "../storage-bigtable", version = "1.6.0" }
|
||||
solana-streamer = { path = "../streamer", version = "1.6.0" }
|
||||
solana-sys-tuner = { path = "../sys-tuner", version = "1.6.0" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "1.6.0" }
|
||||
solana-version = { path = "../version", version = "1.6.0" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.6.0" }
|
||||
spl-token-v2-0 = { package = "spl-token", version = "=3.1.0", features = ["no-entrypoint"] }
|
||||
solana-account-decoder = { path = "../account-decoder", version = "1.5.6" }
|
||||
solana-banks-server = { path = "../banks-server", version = "1.5.6" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.5.6" }
|
||||
solana-client = { path = "../client", version = "1.5.6" }
|
||||
solana-faucet = { path = "../faucet", version = "1.5.6" }
|
||||
solana-ledger = { path = "../ledger", version = "1.5.6" }
|
||||
solana-logger = { path = "../logger", version = "1.5.6" }
|
||||
solana-merkle-tree = { path = "../merkle-tree", version = "1.5.6" }
|
||||
solana-metrics = { path = "../metrics", version = "1.5.6" }
|
||||
solana-measure = { path = "../measure", version = "1.5.6" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.5.6" }
|
||||
solana-perf = { path = "../perf", version = "1.5.6" }
|
||||
solana-program-test = { path = "../program-test", version = "1.5.6" }
|
||||
solana-runtime = { path = "../runtime", version = "1.5.6" }
|
||||
solana-sdk = { path = "../sdk", version = "1.5.6" }
|
||||
solana-frozen-abi = { path = "../frozen-abi", version = "1.5.6" }
|
||||
solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "1.5.6" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "1.5.6" }
|
||||
solana-storage-bigtable = { path = "../storage-bigtable", version = "1.5.6" }
|
||||
solana-streamer = { path = "../streamer", version = "1.5.6" }
|
||||
solana-sys-tuner = { path = "../sys-tuner", version = "1.5.6" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "1.5.6" }
|
||||
solana-version = { path = "../version", version = "1.5.6" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.5.6" }
|
||||
spl-token-v2-0 = { package = "spl-token", version = "=3.0.1", features = ["no-entrypoint"] }
|
||||
tempfile = "3.1.0"
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "1.1", features = ["full"] }
|
||||
tokio_02 = { version = "0.2", package = "tokio", features = ["full"] }
|
||||
tokio-util = { version = "0.3", features = ["codec"] } # This crate needs to stay in sync with tokio_02, until that dependency can be removed
|
||||
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.6.0" }
|
||||
tokio = { version = "0.2", features = ["full"] }
|
||||
tokio_01 = { version = "0.1", package = "tokio" }
|
||||
tokio_01_bytes = { version = "0.4.7", package = "bytes" }
|
||||
tokio_fs_01 = { version = "0.1", package = "tokio-fs" }
|
||||
tokio_io_01 = { version = "0.1", package = "tokio-io" }
|
||||
tokio_codec_01 = { version = "0.1", package = "tokio-codec" }
|
||||
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.5.6" }
|
||||
trees = "0.2.1"
|
||||
|
||||
[dev-dependencies]
|
||||
@@ -90,7 +90,7 @@ matches = "0.1.6"
|
||||
num_cpus = "1.13.0"
|
||||
reqwest = { version = "0.10.8", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
serial_test = "0.4.0"
|
||||
symlink = "0.1.0"
|
||||
serial_test_derive = "0.4.0"
|
||||
systemstat = "0.1.5"
|
||||
|
||||
[build-dependencies]
|
||||
|
@@ -1,4 +1,3 @@
|
||||
#![allow(clippy::integer_arithmetic)]
|
||||
#![feature(test)]
|
||||
|
||||
extern crate test;
|
||||
@@ -29,7 +28,6 @@ use solana_sdk::system_instruction;
|
||||
use solana_sdk::system_transaction;
|
||||
use solana_sdk::timing::{duration_as_us, timestamp};
|
||||
use solana_sdk::transaction::Transaction;
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::mpsc::Receiver;
|
||||
use std::sync::Arc;
|
||||
@@ -70,10 +68,10 @@ fn bench_consume_buffered(bencher: &mut Bencher) {
|
||||
let len = 4096;
|
||||
let chunk_size = 1024;
|
||||
let batches = to_packets_chunked(&vec![tx; len], chunk_size);
|
||||
let mut packets = VecDeque::new();
|
||||
let mut packets = vec![];
|
||||
for batch in batches {
|
||||
let batch_len = batch.packets.len();
|
||||
packets.push_back((batch, vec![0usize; batch_len], false));
|
||||
packets.push((batch, vec![0usize; batch_len]));
|
||||
}
|
||||
let (s, _r) = unbounded();
|
||||
// This tests the performance of buffering packets.
|
||||
@@ -83,10 +81,9 @@ fn bench_consume_buffered(bencher: &mut Bencher) {
|
||||
&my_pubkey,
|
||||
&poh_recorder,
|
||||
&mut packets,
|
||||
10_000,
|
||||
None,
|
||||
&s,
|
||||
None::<Box<dyn Fn()>>,
|
||||
None,
|
||||
);
|
||||
});
|
||||
|
||||
|
@@ -1,4 +1,3 @@
|
||||
#![allow(clippy::integer_arithmetic)]
|
||||
#![feature(test)]
|
||||
extern crate solana_ledger;
|
||||
extern crate test;
|
||||
|
@@ -3,7 +3,7 @@
|
||||
#![feature(test)]
|
||||
extern crate test;
|
||||
|
||||
use solana_core::poh_service::DEFAULT_HASHES_PER_BATCH;
|
||||
use solana_core::poh_service::NUM_HASHES_PER_BATCH;
|
||||
use solana_ledger::poh::Poh;
|
||||
use solana_sdk::hash::Hash;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
@@ -43,7 +43,7 @@ fn bench_arc_mutex_poh_batched_hash(bencher: &mut Bencher) {
|
||||
bencher.iter(|| {
|
||||
// NOTE: This block attempts to look as close as possible to `PohService::tick_producer()`
|
||||
loop {
|
||||
if poh.lock().unwrap().hash(DEFAULT_HASHES_PER_BATCH) {
|
||||
if poh.lock().unwrap().hash(NUM_HASHES_PER_BATCH) {
|
||||
poh.lock().unwrap().tick().unwrap();
|
||||
if exit.load(Ordering::Relaxed) {
|
||||
break;
|
||||
@@ -58,6 +58,6 @@ fn bench_arc_mutex_poh_batched_hash(bencher: &mut Bencher) {
|
||||
fn bench_poh_lock_time_per_batch(bencher: &mut Bencher) {
|
||||
let mut poh = Poh::new(Hash::default(), None);
|
||||
bencher.iter(|| {
|
||||
poh.hash(DEFAULT_HASHES_PER_BATCH);
|
||||
poh.hash(NUM_HASHES_PER_BATCH);
|
||||
})
|
||||
}
|
||||
|
@@ -6,7 +6,6 @@ extern crate test;
|
||||
use log::*;
|
||||
use solana_core::cluster_info::{ClusterInfo, Node};
|
||||
use solana_core::contact_info::ContactInfo;
|
||||
use solana_core::max_slots::MaxSlots;
|
||||
use solana_core::retransmit_stage::retransmitter;
|
||||
use solana_ledger::entry::Entry;
|
||||
use solana_ledger::genesis_utils::{create_genesis_config, GenesisConfigInfo};
|
||||
@@ -93,8 +92,6 @@ fn bench_retransmitter(bencher: &mut Bencher) {
|
||||
&leader_schedule_cache,
|
||||
cluster_info,
|
||||
packet_receiver,
|
||||
&Arc::new(MaxSlots::default()),
|
||||
None,
|
||||
);
|
||||
|
||||
let mut index = 0;
|
||||
|
@@ -1,4 +1,3 @@
|
||||
#![allow(clippy::integer_arithmetic)]
|
||||
#![feature(test)]
|
||||
|
||||
extern crate test;
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@ use std::{
|
||||
sync::{Arc, RwLock},
|
||||
thread::{self, Builder, JoinHandle},
|
||||
};
|
||||
use tokio::runtime::Runtime;
|
||||
use tokio::runtime;
|
||||
|
||||
// Delay uploading the largest confirmed root for this many slots. This is done in an attempt to
|
||||
// ensure that the `CacheBlockTimeService` has had enough time to add the block time for the root
|
||||
@@ -21,7 +21,7 @@ pub struct BigTableUploadService {
|
||||
|
||||
impl BigTableUploadService {
|
||||
pub fn new(
|
||||
runtime: Arc<Runtime>,
|
||||
runtime_handle: runtime::Handle,
|
||||
bigtable_ledger_storage: solana_storage_bigtable::LedgerStorage,
|
||||
blockstore: Arc<Blockstore>,
|
||||
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
|
||||
@@ -32,7 +32,7 @@ impl BigTableUploadService {
|
||||
.name("bigtable-upload".to_string())
|
||||
.spawn(move || {
|
||||
Self::run(
|
||||
runtime,
|
||||
runtime_handle,
|
||||
bigtable_ledger_storage,
|
||||
blockstore,
|
||||
block_commitment_cache,
|
||||
@@ -45,7 +45,7 @@ impl BigTableUploadService {
|
||||
}
|
||||
|
||||
fn run(
|
||||
runtime: Arc<Runtime>,
|
||||
runtime: runtime::Handle,
|
||||
bigtable_ledger_storage: solana_storage_bigtable::LedgerStorage,
|
||||
blockstore: Arc<Blockstore>,
|
||||
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
|
||||
@@ -74,7 +74,6 @@ impl BigTableUploadService {
|
||||
start_slot,
|
||||
Some(end_slot),
|
||||
true,
|
||||
false,
|
||||
exit.clone(),
|
||||
));
|
||||
|
||||
|
@@ -402,7 +402,7 @@ pub fn broadcast_shreds(
|
||||
match send_mmsg(s, &packets[sent..]) {
|
||||
Ok(n) => sent += n,
|
||||
Err(e) => {
|
||||
return Err(Error::Io(e));
|
||||
return Err(Error::IO(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -667,6 +667,8 @@ pub mod test {
|
||||
}
|
||||
}
|
||||
|
||||
sleep(Duration::from_millis(2000));
|
||||
|
||||
trace!(
|
||||
"[broadcast_ledger] max_tick_height: {}, start_tick_height: {}, ticks_per_slot: {}",
|
||||
max_tick_height,
|
||||
@@ -674,17 +676,10 @@ pub mod test {
|
||||
ticks_per_slot,
|
||||
);
|
||||
|
||||
let mut entries = vec![];
|
||||
for _ in 0..10 {
|
||||
entries = broadcast_service
|
||||
.blockstore
|
||||
.get_slot_entries(slot, 0)
|
||||
.expect("Expect entries to be present");
|
||||
if entries.len() >= max_tick_height as usize {
|
||||
break;
|
||||
}
|
||||
sleep(Duration::from_millis(1000));
|
||||
}
|
||||
let blockstore = broadcast_service.blockstore;
|
||||
let entries = blockstore
|
||||
.get_slot_entries(slot, 0)
|
||||
.expect("Expect entries to be present");
|
||||
assert_eq!(entries.len(), max_tick_height as usize);
|
||||
|
||||
drop(entry_sender);
|
||||
|
@@ -113,14 +113,7 @@ const GOSSIP_PING_CACHE_TTL: Duration = Duration::from_secs(640);
|
||||
pub const DEFAULT_CONTACT_DEBUG_INTERVAL_MILLIS: u64 = 10_000;
|
||||
pub const DEFAULT_CONTACT_SAVE_INTERVAL_MILLIS: u64 = 60_000;
|
||||
/// Minimum serialized size of a Protocol::PullResponse packet.
|
||||
const PULL_RESPONSE_MIN_SERIALIZED_SIZE: usize = 161;
|
||||
// Limit number of unique pubkeys in the crds table.
|
||||
const CRDS_UNIQUE_PUBKEY_CAPACITY: usize = 4096;
|
||||
/// Minimum stake that a node should have so that its CRDS values are
|
||||
/// propagated through gossip (few types are exempted).
|
||||
const MIN_STAKE_FOR_GOSSIP: u64 = solana_sdk::native_token::LAMPORTS_PER_SOL;
|
||||
/// Minimum number of staked nodes for enforcing stakes in gossip.
|
||||
const MIN_NUM_STAKED_NODES: usize = 500;
|
||||
const PULL_RESPONSE_MIN_SERIALIZED_SIZE: usize = 167;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum ClusterInfoError {
|
||||
@@ -264,17 +257,6 @@ struct GossipStats {
|
||||
handle_batch_pull_requests_time: Counter,
|
||||
handle_batch_pull_responses_time: Counter,
|
||||
handle_batch_push_messages_time: Counter,
|
||||
packets_received_count: Counter,
|
||||
packets_received_prune_messages_count: Counter,
|
||||
packets_received_pull_requests_count: Counter,
|
||||
packets_received_pull_responses_count: Counter,
|
||||
packets_received_push_messages_count: Counter,
|
||||
packets_received_verified_count: Counter,
|
||||
packets_sent_gossip_requests_count: Counter,
|
||||
packets_sent_prune_messages_count: Counter,
|
||||
packets_sent_pull_requests_count: Counter,
|
||||
packets_sent_pull_responses_count: Counter,
|
||||
packets_sent_push_messages_count: Counter,
|
||||
process_gossip_packets_time: Counter,
|
||||
process_pull_response: Counter,
|
||||
process_pull_response_count: Counter,
|
||||
@@ -292,10 +274,6 @@ struct GossipStats {
|
||||
prune_message_len: Counter,
|
||||
pull_request_ping_pong_check_failed_count: Counter,
|
||||
purge: Counter,
|
||||
require_stake_for_gossip_unknown_feature_set: Counter,
|
||||
require_stake_for_gossip_unknown_stakes: Counter,
|
||||
trim_crds_table_failed: Counter,
|
||||
trim_crds_table_purged_values_count: Counter,
|
||||
epoch_slots_lookup: Counter,
|
||||
new_pull_requests: Counter,
|
||||
new_pull_requests_count: Counter,
|
||||
@@ -335,7 +313,7 @@ impl Default for ClusterInfo {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize, AbiExample)]
|
||||
#[derive(Debug, Default, Deserialize, Serialize, AbiExample)]
|
||||
pub struct PruneData {
|
||||
/// Pubkey of the node that sent this prune data
|
||||
pub pubkey: Pubkey,
|
||||
@@ -427,7 +405,7 @@ pub fn make_accounts_hashes_message(
|
||||
type Ping = ping_pong::Ping<[u8; GOSSIP_PING_TOKEN_SIZE]>;
|
||||
|
||||
// TODO These messages should go through the gpu pipeline for spam filtering
|
||||
#[frozen_abi(digest = "CH5BWuhAyvUiUQYgu2Lcwu7eoiW6bQitvtLS1yFsdmrE")]
|
||||
#[frozen_abi(digest = "DdTxrwwnbe571Di4rLtrAQorFDE58vYnmzzbaeQ7sQMC")]
|
||||
#[derive(Serialize, Deserialize, Debug, AbiEnumVisitor, AbiExample)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
enum Protocol {
|
||||
@@ -435,8 +413,6 @@ enum Protocol {
|
||||
PullRequest(CrdsFilter, CrdsValue),
|
||||
PullResponse(Pubkey, Vec<CrdsValue>),
|
||||
PushMessage(Pubkey, Vec<CrdsValue>),
|
||||
// TODO: Remove the redundant outer pubkey here,
|
||||
// and use the inner PruneData.pubkey instead.
|
||||
PruneMessage(Pubkey, PruneData),
|
||||
PingMessage(Ping),
|
||||
PongMessage(Pong),
|
||||
@@ -520,13 +496,7 @@ impl Sanitize for Protocol {
|
||||
}
|
||||
Protocol::PullResponse(_, val) => val.sanitize(),
|
||||
Protocol::PushMessage(_, val) => val.sanitize(),
|
||||
Protocol::PruneMessage(from, val) => {
|
||||
if *from != val.pubkey {
|
||||
Err(SanitizeError::InvalidValue)
|
||||
} else {
|
||||
val.sanitize()
|
||||
}
|
||||
}
|
||||
Protocol::PruneMessage(_, val) => val.sanitize(),
|
||||
Protocol::PingMessage(ping) => ping.sanitize(),
|
||||
Protocol::PongMessage(pong) => pong.sanitize(),
|
||||
}
|
||||
@@ -544,33 +514,6 @@ struct ResponseScore {
|
||||
score: u64, // Relative score of the response
|
||||
}
|
||||
|
||||
// Retains only CRDS values associated with nodes with enough stake.
|
||||
// (some crds types are exempted)
|
||||
fn retain_staked(values: &mut Vec<CrdsValue>, stakes: &HashMap<Pubkey, u64>) {
|
||||
values.retain(|value| {
|
||||
match value.data {
|
||||
CrdsData::ContactInfo(_) => true,
|
||||
// May Impact new validators starting up without any stake yet.
|
||||
CrdsData::Vote(_, _) => true,
|
||||
// Unstaked nodes can still help repair.
|
||||
CrdsData::EpochSlots(_, _) => true,
|
||||
// Unstaked nodes can still serve snapshots.
|
||||
CrdsData::SnapshotHashes(_) => true,
|
||||
// Otherwise unstaked voting nodes will show up with no version in
|
||||
// the various dashboards.
|
||||
CrdsData::Version(_) => true,
|
||||
CrdsData::LowestSlot(_, _)
|
||||
| CrdsData::AccountsHashes(_)
|
||||
| CrdsData::LegacyVersion(_)
|
||||
| CrdsData::NodeInstance(_)
|
||||
| CrdsData::DuplicateShred(_, _) => {
|
||||
let stake = stakes.get(&value.pubkey()).copied();
|
||||
stake.unwrap_or_default() >= MIN_STAKE_FOR_GOSSIP
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
impl ClusterInfo {
|
||||
/// Without a valid keypair gossip will not function. Only useful for tests.
|
||||
pub fn new_with_invalid_keypair(contact_info: ContactInfo) -> Self {
|
||||
@@ -641,6 +584,16 @@ impl ClusterInfo {
|
||||
self.contact_debug_interval = new;
|
||||
}
|
||||
|
||||
pub fn update_contact_info<F>(&self, modify: F)
|
||||
where
|
||||
F: FnOnce(&mut ContactInfo),
|
||||
{
|
||||
let my_id = self.id();
|
||||
modify(&mut self.my_contact_info.write().unwrap());
|
||||
assert_eq!(self.my_contact_info.read().unwrap().id, my_id);
|
||||
self.insert_self()
|
||||
}
|
||||
|
||||
fn push_self(
|
||||
&self,
|
||||
stakes: &HashMap<Pubkey, u64>,
|
||||
@@ -1270,30 +1223,23 @@ impl ClusterInfo {
|
||||
.map(|x| map(x.value.lowest_slot().unwrap(), x.insert_timestamp))
|
||||
}
|
||||
|
||||
pub fn get_epoch_slots_since(
|
||||
&self,
|
||||
timestamp: u64,
|
||||
) -> (
|
||||
Vec<EpochSlots>,
|
||||
Option<u64>, // Most recent insert timestmap.
|
||||
) {
|
||||
let mut max_ts = 0;
|
||||
pub fn get_epoch_slots_since(&self, since: Option<u64>) -> (Vec<EpochSlots>, Option<u64>) {
|
||||
let vals: Vec<_> = self
|
||||
.gossip
|
||||
.read()
|
||||
.unwrap()
|
||||
.crds
|
||||
.get_epoch_slots_since(timestamp)
|
||||
.map(|value| {
|
||||
max_ts = std::cmp::max(max_ts, value.insert_timestamp);
|
||||
match &value.value.data {
|
||||
CrdsData::EpochSlots(_, slots) => slots.clone(),
|
||||
_ => panic!("this should not happen!"),
|
||||
}
|
||||
.values()
|
||||
.filter(|x| {
|
||||
since
|
||||
.map(|since| x.insert_timestamp > since)
|
||||
.unwrap_or(true)
|
||||
})
|
||||
.filter_map(|x| Some((x.value.epoch_slots()?.clone(), x.insert_timestamp)))
|
||||
.collect();
|
||||
let max_ts = if vals.is_empty() { None } else { Some(max_ts) };
|
||||
(vals, max_ts)
|
||||
let max = vals.iter().map(|x| x.1).max().or(since);
|
||||
let vec = vals.into_iter().map(|x| x.0).collect();
|
||||
(vec, max)
|
||||
}
|
||||
|
||||
pub fn get_node_version(&self, pubkey: &Pubkey) -> Option<solana_version::Version> {
|
||||
@@ -1542,7 +1488,7 @@ impl ClusterInfo {
|
||||
1
|
||||
);
|
||||
error!("retransmit result {:?}", e);
|
||||
return Err(Error::Io(e));
|
||||
return Err(Error::IO(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1717,21 +1663,11 @@ impl ClusterInfo {
|
||||
let mut gossip = self.gossip.write().unwrap();
|
||||
gossip.process_push_messages(pending_push_messages);
|
||||
}
|
||||
fn new_push_requests(
|
||||
&self,
|
||||
stakes: &HashMap<Pubkey, u64>,
|
||||
require_stake_for_gossip: bool,
|
||||
) -> Vec<(SocketAddr, Protocol)> {
|
||||
fn new_push_requests(&self) -> Vec<(SocketAddr, Protocol)> {
|
||||
let self_id = self.id();
|
||||
let mut push_messages = self
|
||||
let (_, push_messages) = self
|
||||
.time_gossip_write_lock("new_push_requests", &self.stats.new_push_requests)
|
||||
.new_push_messages(self.drain_push_queue(), timestamp());
|
||||
if require_stake_for_gossip {
|
||||
push_messages.retain(|_, data| {
|
||||
retain_staked(data, stakes);
|
||||
!data.is_empty()
|
||||
})
|
||||
}
|
||||
let push_messages: Vec<_> = {
|
||||
let gossip =
|
||||
self.time_gossip_read_lock("push_req_lookup", &self.stats.new_push_requests2);
|
||||
@@ -1763,21 +1699,14 @@ impl ClusterInfo {
|
||||
gossip_validators: Option<&HashSet<Pubkey>>,
|
||||
stakes: &HashMap<Pubkey, u64>,
|
||||
generate_pull_requests: bool,
|
||||
require_stake_for_gossip: bool,
|
||||
) -> Vec<(SocketAddr, Protocol)> {
|
||||
self.trim_crds_table(CRDS_UNIQUE_PUBKEY_CAPACITY, &stakes);
|
||||
let mut pulls: Vec<_> = if generate_pull_requests {
|
||||
self.new_pull_requests(&thread_pool, gossip_validators, stakes)
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
let mut pushes: Vec<_> = self.new_push_requests(stakes, require_stake_for_gossip);
|
||||
self.stats
|
||||
.packets_sent_pull_requests_count
|
||||
.add_relaxed(pulls.len() as u64);
|
||||
self.stats
|
||||
.packets_sent_push_messages_count
|
||||
.add_relaxed(pushes.len() as u64);
|
||||
let mut pushes: Vec<_> = self.new_push_requests();
|
||||
|
||||
pulls.append(&mut pushes);
|
||||
pulls
|
||||
}
|
||||
@@ -1791,20 +1720,15 @@ impl ClusterInfo {
|
||||
stakes: &HashMap<Pubkey, u64>,
|
||||
sender: &PacketSender,
|
||||
generate_pull_requests: bool,
|
||||
require_stake_for_gossip: bool,
|
||||
) -> Result<()> {
|
||||
let reqs = self.generate_new_gossip_requests(
|
||||
thread_pool,
|
||||
gossip_validators,
|
||||
stakes,
|
||||
&stakes,
|
||||
generate_pull_requests,
|
||||
require_stake_for_gossip,
|
||||
);
|
||||
if !reqs.is_empty() {
|
||||
let packets = to_packets_with_destination(recycler.clone(), &reqs);
|
||||
self.stats
|
||||
.packets_sent_gossip_requests_count
|
||||
.add_relaxed(packets.packets.len() as u64);
|
||||
sender.send(packets)?;
|
||||
}
|
||||
Ok(())
|
||||
@@ -1881,39 +1805,6 @@ impl ClusterInfo {
|
||||
inc_new_counter_info!("cluster_info-purge-count", num_purged);
|
||||
}
|
||||
|
||||
// Trims the CRDS table by dropping all values associated with the pubkeys
|
||||
// with the lowest stake, so that the number of unique pubkeys are bounded.
|
||||
fn trim_crds_table(&self, cap: usize, stakes: &HashMap<Pubkey, u64>) {
|
||||
if !self.gossip.read().unwrap().crds.should_trim(cap) {
|
||||
return;
|
||||
}
|
||||
let keep: Vec<_> = self
|
||||
.entrypoints
|
||||
.read()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|k| k.id)
|
||||
.chain(std::iter::once(self.id))
|
||||
.collect();
|
||||
let mut gossip = self.gossip.write().unwrap();
|
||||
match gossip.crds.trim(cap, &keep, stakes) {
|
||||
Err(err) => {
|
||||
self.stats.trim_crds_table_failed.add_relaxed(1);
|
||||
error!("crds table trim failed: {:?}", err);
|
||||
}
|
||||
Ok(purged_values) => {
|
||||
self.stats
|
||||
.trim_crds_table_purged_values_count
|
||||
.add_relaxed(purged_values.len() as u64);
|
||||
gossip.pull.purged_values.extend(
|
||||
purged_values
|
||||
.into_iter()
|
||||
.map(|v| (v.value_hash, v.local_timestamp)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// randomly pick a node and ask them for updates asynchronously
|
||||
pub fn gossip(
|
||||
self: Arc<Self>,
|
||||
@@ -1935,7 +1826,7 @@ impl ClusterInfo {
|
||||
let mut last_contact_info_trace = timestamp();
|
||||
let mut last_contact_info_save = timestamp();
|
||||
let mut entrypoints_processed = false;
|
||||
let recycler = PacketsRecycler::new_without_limit("gossip-recycler-shrink-stats");
|
||||
let recycler = PacketsRecycler::default();
|
||||
let crds_data = vec![
|
||||
CrdsData::Version(Version::new(self.id())),
|
||||
CrdsData::NodeInstance(self.instance.with_wallclock(timestamp())),
|
||||
@@ -1967,18 +1858,13 @@ impl ClusterInfo {
|
||||
last_contact_info_save = start;
|
||||
}
|
||||
|
||||
let (stakes, feature_set) = match bank_forks {
|
||||
let stakes: HashMap<_, _> = match bank_forks {
|
||||
Some(ref bank_forks) => {
|
||||
let root_bank = bank_forks.read().unwrap().root_bank();
|
||||
(
|
||||
root_bank.staked_nodes(),
|
||||
Some(root_bank.feature_set.clone()),
|
||||
)
|
||||
bank_forks.read().unwrap().root_bank().staked_nodes()
|
||||
}
|
||||
None => (HashMap::new(), None),
|
||||
None => HashMap::new(),
|
||||
};
|
||||
let require_stake_for_gossip =
|
||||
self.require_stake_for_gossip(feature_set.as_deref(), &stakes);
|
||||
|
||||
let _ = self.run_gossip(
|
||||
&thread_pool,
|
||||
gossip_validators.as_ref(),
|
||||
@@ -1986,7 +1872,6 @@ impl ClusterInfo {
|
||||
&stakes,
|
||||
&sender,
|
||||
generate_pull_requests,
|
||||
require_stake_for_gossip,
|
||||
);
|
||||
if exit.load(Ordering::Relaxed) {
|
||||
return;
|
||||
@@ -2067,7 +1952,6 @@ impl ClusterInfo {
|
||||
stakes: &HashMap<Pubkey, u64>,
|
||||
response_sender: &PacketSender,
|
||||
feature_set: Option<&FeatureSet>,
|
||||
require_stake_for_gossip: bool,
|
||||
) {
|
||||
let _st = ScopedTimer::from(&self.stats.handle_batch_pull_requests_time);
|
||||
if requests.is_empty() {
|
||||
@@ -2109,17 +1993,8 @@ impl ClusterInfo {
|
||||
self.stats
|
||||
.pull_requests_count
|
||||
.add_relaxed(requests.len() as u64);
|
||||
let response = self.handle_pull_requests(
|
||||
recycler,
|
||||
requests,
|
||||
stakes,
|
||||
feature_set,
|
||||
require_stake_for_gossip,
|
||||
);
|
||||
let response = self.handle_pull_requests(recycler, requests, stakes, feature_set);
|
||||
if !response.is_empty() {
|
||||
self.stats
|
||||
.packets_sent_pull_responses_count
|
||||
.add_relaxed(response.packets.len() as u64);
|
||||
let _ = response_sender.send(response);
|
||||
}
|
||||
}
|
||||
@@ -2192,7 +2067,6 @@ impl ClusterInfo {
|
||||
requests: Vec<PullData>,
|
||||
stakes: &HashMap<Pubkey, u64>,
|
||||
feature_set: Option<&FeatureSet>,
|
||||
require_stake_for_gossip: bool,
|
||||
) -> Packets {
|
||||
let mut time = Measure::start("handle_pull_requests");
|
||||
let callers = crds_value::filter_current(requests.iter().map(|r| &r.caller));
|
||||
@@ -2200,7 +2074,7 @@ impl ClusterInfo {
|
||||
.process_pull_requests(callers.cloned(), timestamp());
|
||||
let output_size_limit =
|
||||
self.update_data_budget(stakes.len()) / PULL_RESPONSE_MIN_SERIALIZED_SIZE;
|
||||
let mut packets = Packets::new_with_recycler(recycler.clone(), 64).unwrap();
|
||||
let mut packets = Packets::new_with_recycler(recycler.clone(), 64, "handle_pull_requests");
|
||||
let (caller_and_filters, addrs): (Vec<_>, Vec<_>) = {
|
||||
let mut rng = rand::thread_rng();
|
||||
let check_pull_request =
|
||||
@@ -2214,17 +2088,13 @@ impl ClusterInfo {
|
||||
let now = timestamp();
|
||||
let self_id = self.id();
|
||||
|
||||
let mut pull_responses = self
|
||||
let pull_responses = self
|
||||
.time_gossip_read_lock(
|
||||
"generate_pull_responses",
|
||||
&self.stats.generate_pull_responses,
|
||||
)
|
||||
.generate_pull_responses(&caller_and_filters, output_size_limit, now);
|
||||
if require_stake_for_gossip {
|
||||
for resp in &mut pull_responses {
|
||||
retain_staked(resp, stakes);
|
||||
}
|
||||
}
|
||||
|
||||
let pull_responses: Vec<_> = pull_responses
|
||||
.into_iter()
|
||||
.zip(addrs.into_iter())
|
||||
@@ -2489,7 +2359,8 @@ impl ClusterInfo {
|
||||
if packets.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let packets = Packets::new_with_recycler_data(recycler, packets).unwrap();
|
||||
let packets =
|
||||
Packets::new_with_recycler_data(recycler, "handle_ping_messages", packets);
|
||||
Some(packets)
|
||||
}
|
||||
}
|
||||
@@ -2515,7 +2386,6 @@ impl ClusterInfo {
|
||||
recycler: &PacketsRecycler,
|
||||
stakes: &HashMap<Pubkey, u64>,
|
||||
response_sender: &PacketSender,
|
||||
require_stake_for_gossip: bool,
|
||||
) {
|
||||
let _st = ScopedTimer::from(&self.stats.handle_batch_push_messages_time);
|
||||
if messages.is_empty() {
|
||||
@@ -2621,11 +2491,10 @@ impl ClusterInfo {
|
||||
return;
|
||||
}
|
||||
let mut packets = to_packets_with_destination(recycler.clone(), &prune_messages);
|
||||
let num_prune_packets = packets.packets.len();
|
||||
self.stats
|
||||
.push_response_count
|
||||
.add_relaxed(packets.packets.len() as u64);
|
||||
let new_push_requests = self.new_push_requests(stakes, require_stake_for_gossip);
|
||||
let new_push_requests = self.new_push_requests();
|
||||
inc_new_counter_debug!("cluster_info-push_message-pushes", new_push_requests.len());
|
||||
for (address, request) in new_push_requests {
|
||||
if ContactInfo::is_valid_address(&address) {
|
||||
@@ -2637,12 +2506,6 @@ impl ClusterInfo {
|
||||
trace!("Dropping Gossip push response, as destination is unknown");
|
||||
}
|
||||
}
|
||||
self.stats
|
||||
.packets_sent_prune_messages_count
|
||||
.add_relaxed(num_prune_packets as u64);
|
||||
self.stats
|
||||
.packets_sent_push_messages_count
|
||||
.add_relaxed((packets.packets.len() - num_prune_packets) as u64);
|
||||
let _ = response_sender.send(packets);
|
||||
}
|
||||
|
||||
@@ -2668,33 +2531,6 @@ impl ClusterInfo {
|
||||
}
|
||||
}
|
||||
|
||||
fn require_stake_for_gossip(
|
||||
&self,
|
||||
feature_set: Option<&FeatureSet>,
|
||||
stakes: &HashMap<Pubkey, u64>,
|
||||
) -> bool {
|
||||
match feature_set {
|
||||
None => {
|
||||
self.stats
|
||||
.require_stake_for_gossip_unknown_feature_set
|
||||
.add_relaxed(1);
|
||||
false
|
||||
}
|
||||
Some(feature_set) => {
|
||||
if !feature_set.is_active(&feature_set::require_stake_for_gossip::id()) {
|
||||
false
|
||||
} else if stakes.len() < MIN_NUM_STAKED_NODES {
|
||||
self.stats
|
||||
.require_stake_for_gossip_unknown_stakes
|
||||
.add_relaxed(1);
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn process_packets(
|
||||
&self,
|
||||
packets: VecDeque<Packet>,
|
||||
@@ -2707,9 +2543,6 @@ impl ClusterInfo {
|
||||
should_check_duplicate_instance: bool,
|
||||
) -> Result<()> {
|
||||
let _st = ScopedTimer::from(&self.stats.process_gossip_packets_time);
|
||||
self.stats
|
||||
.packets_received_count
|
||||
.add_relaxed(packets.len() as u64);
|
||||
let packets: Vec<_> = thread_pool.install(|| {
|
||||
packets
|
||||
.into_par_iter()
|
||||
@@ -2722,9 +2555,6 @@ impl ClusterInfo {
|
||||
})
|
||||
.collect()
|
||||
});
|
||||
self.stats
|
||||
.packets_received_verified_count
|
||||
.add_relaxed(packets.len() as u64);
|
||||
// Check if there is a duplicate instance of
|
||||
// this node with more recent timestamp.
|
||||
let check_duplicate_instance = |values: &[CrdsValue]| {
|
||||
@@ -2762,29 +2592,6 @@ impl ClusterInfo {
|
||||
Protocol::PongMessage(pong) => pong_messages.push((from_addr, pong)),
|
||||
}
|
||||
}
|
||||
self.stats
|
||||
.packets_received_pull_requests_count
|
||||
.add_relaxed(pull_requests.len() as u64);
|
||||
self.stats
|
||||
.packets_received_pull_responses_count
|
||||
.add_relaxed(pull_responses.len() as u64);
|
||||
self.stats
|
||||
.packets_received_push_messages_count
|
||||
.add_relaxed(push_messages.len() as u64);
|
||||
self.stats
|
||||
.packets_received_prune_messages_count
|
||||
.add_relaxed(prune_messages.len() as u64);
|
||||
let require_stake_for_gossip = self.require_stake_for_gossip(feature_set, &stakes);
|
||||
if require_stake_for_gossip {
|
||||
for (_, data) in &mut pull_responses {
|
||||
retain_staked(data, &stakes);
|
||||
}
|
||||
for (_, data) in &mut push_messages {
|
||||
retain_staked(data, &stakes);
|
||||
}
|
||||
pull_responses.retain(|(_, data)| !data.is_empty());
|
||||
push_messages.retain(|(_, data)| !data.is_empty());
|
||||
}
|
||||
self.handle_batch_ping_messages(ping_messages, recycler, response_sender);
|
||||
self.handle_batch_prune_messages(prune_messages);
|
||||
self.handle_batch_push_messages(
|
||||
@@ -2793,10 +2600,8 @@ impl ClusterInfo {
|
||||
recycler,
|
||||
&stakes,
|
||||
response_sender,
|
||||
require_stake_for_gossip,
|
||||
);
|
||||
self.handle_batch_pull_responses(pull_responses, thread_pool, &stakes, epoch_time_ms);
|
||||
self.trim_crds_table(CRDS_UNIQUE_PUBKEY_CAPACITY, &stakes);
|
||||
self.handle_batch_pong_messages(pong_messages, Instant::now());
|
||||
self.handle_batch_pull_requests(
|
||||
pull_requests,
|
||||
@@ -2805,7 +2610,6 @@ impl ClusterInfo {
|
||||
&stakes,
|
||||
response_sender,
|
||||
feature_set,
|
||||
require_stake_for_gossip,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
@@ -3090,83 +2894,6 @@ impl ClusterInfo {
|
||||
self.stats.pull_requests_count.clear(),
|
||||
i64
|
||||
),
|
||||
(
|
||||
"packets_received_count",
|
||||
self.stats.packets_received_count.clear(),
|
||||
i64
|
||||
),
|
||||
(
|
||||
"packets_received_prune_messages_count",
|
||||
self.stats.packets_received_prune_messages_count.clear(),
|
||||
i64
|
||||
),
|
||||
(
|
||||
"packets_received_pull_requests_count",
|
||||
self.stats.packets_received_pull_requests_count.clear(),
|
||||
i64
|
||||
),
|
||||
(
|
||||
"packets_received_pull_responses_count",
|
||||
self.stats.packets_received_pull_responses_count.clear(),
|
||||
i64
|
||||
),
|
||||
(
|
||||
"packets_received_push_messages_count",
|
||||
self.stats.packets_received_push_messages_count.clear(),
|
||||
i64
|
||||
),
|
||||
(
|
||||
"packets_received_verified_count",
|
||||
self.stats.packets_received_verified_count.clear(),
|
||||
i64
|
||||
),
|
||||
(
|
||||
"packets_sent_gossip_requests_count",
|
||||
self.stats.packets_sent_gossip_requests_count.clear(),
|
||||
i64
|
||||
),
|
||||
(
|
||||
"packets_sent_prune_messages_count",
|
||||
self.stats.packets_sent_prune_messages_count.clear(),
|
||||
i64
|
||||
),
|
||||
(
|
||||
"packets_sent_pull_requests_count",
|
||||
self.stats.packets_sent_pull_requests_count.clear(),
|
||||
i64
|
||||
),
|
||||
(
|
||||
"packets_sent_pull_responses_count",
|
||||
self.stats.packets_sent_pull_responses_count.clear(),
|
||||
i64
|
||||
),
|
||||
(
|
||||
"packets_sent_push_messages_count",
|
||||
self.stats.packets_sent_push_messages_count.clear(),
|
||||
i64
|
||||
),
|
||||
(
|
||||
"require_stake_for_gossip_unknown_feature_set",
|
||||
self.stats
|
||||
.require_stake_for_gossip_unknown_feature_set
|
||||
.clear(),
|
||||
i64
|
||||
),
|
||||
(
|
||||
"require_stake_for_gossip_unknown_stakes",
|
||||
self.stats.require_stake_for_gossip_unknown_stakes.clear(),
|
||||
i64
|
||||
),
|
||||
(
|
||||
"trim_crds_table_failed",
|
||||
self.stats.trim_crds_table_failed.clear(),
|
||||
i64
|
||||
),
|
||||
(
|
||||
"trim_crds_table_purged_values_count",
|
||||
self.stats.trim_crds_table_purged_values_count.clear(),
|
||||
i64
|
||||
),
|
||||
);
|
||||
|
||||
*last_print = Instant::now();
|
||||
@@ -3182,8 +2909,7 @@ impl ClusterInfo {
|
||||
exit: &Arc<AtomicBool>,
|
||||
) -> JoinHandle<()> {
|
||||
let exit = exit.clone();
|
||||
let recycler =
|
||||
PacketsRecycler::new_without_limit("cluster-info-listen-recycler-shrink-stats");
|
||||
let recycler = PacketsRecycler::default();
|
||||
Builder::new()
|
||||
.name("solana-listen".to_string())
|
||||
.spawn(move || {
|
||||
@@ -3628,7 +3354,7 @@ mod tests {
|
||||
.iter()
|
||||
.map(|ping| Pong::new(ping, &this_node).unwrap())
|
||||
.collect();
|
||||
let recycler = PacketsRecycler::new_without_limit("");
|
||||
let recycler = PacketsRecycler::default();
|
||||
let packets = cluster_info
|
||||
.handle_ping_messages(
|
||||
remote_nodes
|
||||
@@ -3822,11 +3548,7 @@ mod tests {
|
||||
let crds_values = vec![CrdsValue::new_rand(&mut rng, None)];
|
||||
let pull_response = Protocol::PullResponse(Pubkey::new_unique(), crds_values);
|
||||
let size = serialized_size(&pull_response).unwrap();
|
||||
assert!(
|
||||
PULL_RESPONSE_MIN_SERIALIZED_SIZE as u64 <= size,
|
||||
"pull-response serialized size: {}",
|
||||
size
|
||||
);
|
||||
assert!(PULL_RESPONSE_MIN_SERIALIZED_SIZE as u64 <= size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3843,13 +3565,8 @@ mod tests {
|
||||
.write()
|
||||
.unwrap()
|
||||
.refresh_push_active_set(&HashMap::new(), None);
|
||||
let reqs = cluster_info.generate_new_gossip_requests(
|
||||
&thread_pool,
|
||||
None, // gossip_validators
|
||||
&HashMap::new(),
|
||||
true, // generate_pull_requests
|
||||
false, // require_stake_for_gossip
|
||||
);
|
||||
let reqs =
|
||||
cluster_info.generate_new_gossip_requests(&thread_pool, None, &HashMap::new(), true);
|
||||
//assert none of the addrs are invalid.
|
||||
reqs.iter().all(|(addr, _)| {
|
||||
let res = ContactInfo::is_valid_address(addr);
|
||||
@@ -3880,6 +3597,40 @@ mod tests {
|
||||
.lookup(&label)
|
||||
.is_some());
|
||||
}
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_update_contact_info() {
|
||||
let d = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), timestamp());
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(d);
|
||||
let entry_label = CrdsValueLabel::ContactInfo(cluster_info.id());
|
||||
assert!(cluster_info
|
||||
.gossip
|
||||
.read()
|
||||
.unwrap()
|
||||
.crds
|
||||
.lookup(&entry_label)
|
||||
.is_some());
|
||||
|
||||
let now = timestamp();
|
||||
cluster_info.update_contact_info(|ci| ci.wallclock = now);
|
||||
assert_eq!(
|
||||
cluster_info
|
||||
.gossip
|
||||
.read()
|
||||
.unwrap()
|
||||
.crds
|
||||
.lookup(&entry_label)
|
||||
.unwrap()
|
||||
.contact_info()
|
||||
.unwrap()
|
||||
.wallclock,
|
||||
now
|
||||
);
|
||||
|
||||
// Inserting Contactinfo with different pubkey should panic,
|
||||
// and update should fail
|
||||
cluster_info.update_contact_info(|ci| ci.id = solana_sdk::pubkey::new_rand())
|
||||
}
|
||||
|
||||
fn assert_in_range(x: u16, range: (u16, u16)) {
|
||||
assert!(x >= range.0);
|
||||
@@ -3960,7 +3711,7 @@ mod tests {
|
||||
.unwrap()
|
||||
.refresh_push_active_set(&HashMap::new(), None);
|
||||
//check that all types of gossip messages are signed correctly
|
||||
let push_messages = cluster_info
|
||||
let (_, push_messages) = cluster_info
|
||||
.gossip
|
||||
.write()
|
||||
.unwrap()
|
||||
@@ -4117,23 +3868,23 @@ mod tests {
|
||||
let keys = Keypair::new();
|
||||
let contact_info = ContactInfo::new_localhost(&keys.pubkey(), 0);
|
||||
let cluster_info = ClusterInfo::new_with_invalid_keypair(contact_info);
|
||||
let (slots, since) = cluster_info.get_epoch_slots_since(0);
|
||||
let (slots, since) = cluster_info.get_epoch_slots_since(None);
|
||||
assert!(slots.is_empty());
|
||||
assert!(since.is_none());
|
||||
cluster_info.push_epoch_slots(&[0]);
|
||||
cluster_info.flush_push_queue();
|
||||
|
||||
let (slots, since) = cluster_info.get_epoch_slots_since(std::u64::MAX);
|
||||
let (slots, since) = cluster_info.get_epoch_slots_since(Some(std::u64::MAX));
|
||||
assert!(slots.is_empty());
|
||||
assert_eq!(since, None);
|
||||
assert_eq!(since, Some(std::u64::MAX));
|
||||
|
||||
let (slots, since) = cluster_info.get_epoch_slots_since(0);
|
||||
let (slots, since) = cluster_info.get_epoch_slots_since(None);
|
||||
assert_eq!(slots.len(), 1);
|
||||
assert!(since.is_some());
|
||||
|
||||
let (slots, since2) = cluster_info.get_epoch_slots_since(since.unwrap() + 1);
|
||||
let (slots, since2) = cluster_info.get_epoch_slots_since(since);
|
||||
assert!(slots.is_empty());
|
||||
assert_eq!(since2, None);
|
||||
assert_eq!(since2, since);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -4418,23 +4169,6 @@ mod tests {
|
||||
assert_eq!(msg.sanitize(), Err(SanitizeError::ValueOutOfBounds));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_protocol_prune_message_sanitize() {
|
||||
let keypair = Keypair::new();
|
||||
let mut prune_data = PruneData {
|
||||
pubkey: keypair.pubkey(),
|
||||
prunes: vec![],
|
||||
signature: Signature::default(),
|
||||
destination: Pubkey::new_unique(),
|
||||
wallclock: timestamp(),
|
||||
};
|
||||
prune_data.sign(&keypair);
|
||||
let prune_message = Protocol::PruneMessage(keypair.pubkey(), prune_data.clone());
|
||||
assert_eq!(prune_message.sanitize(), Ok(()));
|
||||
let prune_message = Protocol::PruneMessage(Pubkey::new_unique(), prune_data);
|
||||
assert_eq!(prune_message.sanitize(), Err(SanitizeError::InvalidValue));
|
||||
}
|
||||
|
||||
// computes the maximum size for pull request blooms
|
||||
fn max_bloom_size() -> usize {
|
||||
let filter_size = serialized_size(&CrdsFilter::default())
|
||||
@@ -4466,7 +4200,7 @@ mod tests {
|
||||
cluster_info.flush_push_queue();
|
||||
cluster_info.push_epoch_slots(&range[16000..]);
|
||||
cluster_info.flush_push_queue();
|
||||
let (slots, since) = cluster_info.get_epoch_slots_since(0);
|
||||
let (slots, since) = cluster_info.get_epoch_slots_since(None);
|
||||
let slots: Vec<_> = slots.iter().flat_map(|x| x.to_slots(0)).collect();
|
||||
assert_eq!(slots, range);
|
||||
assert!(since.is_some());
|
||||
|
@@ -44,8 +44,8 @@ use std::{
|
||||
};
|
||||
|
||||
// Map from a vote account to the authorized voter for an epoch
|
||||
pub type VerifiedLabelVotePacketsSender = CrossbeamSender<Vec<(CrdsValueLabel, Slot, Packets)>>;
|
||||
pub type VerifiedLabelVotePacketsReceiver = CrossbeamReceiver<Vec<(CrdsValueLabel, Slot, Packets)>>;
|
||||
pub type VerifiedLabelVotePacketsSender = CrossbeamSender<Vec<(CrdsValueLabel, Packets)>>;
|
||||
pub type VerifiedLabelVotePacketsReceiver = CrossbeamReceiver<Vec<(CrdsValueLabel, Packets)>>;
|
||||
pub type VerifiedVoteTransactionsSender = CrossbeamSender<Vec<Transaction>>;
|
||||
pub type VerifiedVoteTransactionsReceiver = CrossbeamReceiver<Vec<Transaction>>;
|
||||
pub type VerifiedVoteSender = CrossbeamSender<(Pubkey, Vec<Slot>)>;
|
||||
@@ -332,28 +332,34 @@ impl ClusterInfoVoteListener {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn verify_votes(
|
||||
votes: Vec<Transaction>,
|
||||
labels: Vec<CrdsValueLabel>,
|
||||
) -> (Vec<Transaction>, Vec<(CrdsValueLabel, Slot, Packets)>) {
|
||||
let mut msgs = packet::to_packets_chunked(&votes, 1);
|
||||
sigverify::ed25519_verify_cpu(&mut msgs);
|
||||
) -> (Vec<Transaction>, Vec<(CrdsValueLabel, Packets)>) {
|
||||
let msgs = packet::to_packets_chunked(&votes, 1);
|
||||
let r = sigverify::ed25519_verify_cpu(&msgs);
|
||||
|
||||
let (vote_txs, packets) = izip!(labels.into_iter(), votes.into_iter(), msgs,)
|
||||
.filter_map(|(label, vote, packet)| {
|
||||
let slot = vote_transaction::parse_vote_transaction(&vote)
|
||||
.and_then(|(_, vote, _)| vote.slots.last().copied())?;
|
||||
assert_eq!(
|
||||
r.iter()
|
||||
.map(|packets_results| packets_results.len())
|
||||
.sum::<usize>(),
|
||||
votes.len()
|
||||
);
|
||||
|
||||
// to_packets_chunked() above split into 1 packet long chunks
|
||||
assert_eq!(packet.packets.len(), 1);
|
||||
if !packet.packets[0].meta.discard {
|
||||
Some((vote, (label, slot, packet)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unzip();
|
||||
let (vote_txs, packets) = izip!(
|
||||
labels.into_iter(),
|
||||
votes.into_iter(),
|
||||
r.iter().flatten(),
|
||||
msgs,
|
||||
)
|
||||
.filter_map(|(label, vote, verify_result, packet)| {
|
||||
if *verify_result != 0 {
|
||||
Some((vote, (label, packet)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unzip();
|
||||
(vote_txs, packets)
|
||||
}
|
||||
|
||||
@@ -391,9 +397,7 @@ impl ClusterInfoVoteListener {
|
||||
if let Some(bank) = bank {
|
||||
let last_version = bank.last_vote_sync.load(Ordering::Relaxed);
|
||||
let (new_version, msgs) = verified_vote_packets.get_latest_votes(last_version);
|
||||
inc_new_counter_info!("bank_send_loop_batch_size", msgs.packets.len());
|
||||
inc_new_counter_info!("bank_send_loop_num_batches", 1);
|
||||
verified_packets_sender.send(vec![msgs])?;
|
||||
verified_packets_sender.send(msgs)?;
|
||||
#[allow(deprecated)]
|
||||
bank.last_vote_sync.compare_and_swap(
|
||||
last_version,
|
||||
@@ -1596,8 +1600,8 @@ mod tests {
|
||||
assert!(packets.is_empty());
|
||||
}
|
||||
|
||||
fn verify_packets_len(packets: &[(CrdsValueLabel, Slot, Packets)], ref_value: usize) {
|
||||
let num_packets: usize = packets.iter().map(|(_, _, p)| p.packets.len()).sum();
|
||||
fn verify_packets_len(packets: &[(CrdsValueLabel, Packets)], ref_value: usize) {
|
||||
let num_packets: usize = packets.iter().map(|p| p.1.packets.len()).sum();
|
||||
assert_eq!(num_packets, ref_value);
|
||||
}
|
||||
|
||||
|
@@ -2,29 +2,22 @@ use crate::{
|
||||
cluster_info::ClusterInfo, contact_info::ContactInfo, epoch_slots::EpochSlots,
|
||||
serve_repair::RepairType,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use solana_runtime::{bank_forks::BankForks, epoch_stakes::NodeIdToVoteAccounts};
|
||||
use solana_sdk::{clock::Slot, pubkey::Pubkey};
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap, HashSet},
|
||||
sync::{
|
||||
atomic::{AtomicU64, Ordering},
|
||||
Arc, RwLock,
|
||||
},
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
// Limit the size of cluster-slots map in case
|
||||
// of receiving bogus epoch slots values.
|
||||
const CLUSTER_SLOTS_TRIM_SIZE: usize = 524_288; // 512K
|
||||
|
||||
pub type SlotPubkeys = HashMap<Pubkey, u64>;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ClusterSlots {
|
||||
cluster_slots: RwLock<BTreeMap<Slot, Arc<RwLock<SlotPubkeys>>>>,
|
||||
since: AtomicU64,
|
||||
since: RwLock<Option<u64>>,
|
||||
validator_stakes: RwLock<Arc<NodeIdToVoteAccounts>>,
|
||||
epoch: RwLock<Option<u64>>,
|
||||
self_id: RwLock<Pubkey>,
|
||||
}
|
||||
|
||||
impl ClusterSlots {
|
||||
@@ -32,63 +25,26 @@ impl ClusterSlots {
|
||||
self.cluster_slots.read().unwrap().get(&slot).cloned()
|
||||
}
|
||||
pub fn update(&self, root: Slot, cluster_info: &ClusterInfo, bank_forks: &RwLock<BankForks>) {
|
||||
self.update_peers(bank_forks);
|
||||
let since = self.since.load(Ordering::Relaxed);
|
||||
self.update_peers(cluster_info, bank_forks);
|
||||
let since = *self.since.read().unwrap();
|
||||
let (epoch_slots, since) = cluster_info.get_epoch_slots_since(since);
|
||||
self.update_internal(root, epoch_slots, since);
|
||||
}
|
||||
fn update_internal(&self, root: Slot, epoch_slots_list: Vec<EpochSlots>, since: Option<u64>) {
|
||||
// Attach validator's total stake.
|
||||
let epoch_slots_list: Vec<_> = {
|
||||
let validator_stakes = self.validator_stakes.read().unwrap();
|
||||
epoch_slots_list
|
||||
.into_iter()
|
||||
.map(|epoch_slots| {
|
||||
let stake = match validator_stakes.get(&epoch_slots.from) {
|
||||
Some(v) => v.total_stake,
|
||||
None => 0,
|
||||
};
|
||||
(epoch_slots, stake)
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
let slot_nodes_stakes = epoch_slots_list
|
||||
.into_iter()
|
||||
.flat_map(|(epoch_slots, stake)| {
|
||||
epoch_slots
|
||||
.to_slots(root)
|
||||
.into_iter()
|
||||
.filter(|slot| *slot > root)
|
||||
.zip(std::iter::repeat((epoch_slots.from, stake)))
|
||||
})
|
||||
.into_group_map();
|
||||
let slot_nodes_stakes: Vec<_> = {
|
||||
let mut cluster_slots = self.cluster_slots.write().unwrap();
|
||||
slot_nodes_stakes
|
||||
.into_iter()
|
||||
.map(|(slot, nodes_stakes)| {
|
||||
let slot_nodes = cluster_slots.entry(slot).or_default().clone();
|
||||
(slot_nodes, nodes_stakes)
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
for (slot_nodes, nodes_stakes) in slot_nodes_stakes {
|
||||
slot_nodes.write().unwrap().extend(nodes_stakes);
|
||||
for epoch_slots in epoch_slots_list {
|
||||
let slots = epoch_slots.to_slots(root);
|
||||
for slot in &slots {
|
||||
if *slot <= root {
|
||||
continue;
|
||||
}
|
||||
self.insert_node_id(*slot, epoch_slots.from);
|
||||
}
|
||||
}
|
||||
{
|
||||
let mut cluster_slots = self.cluster_slots.write().unwrap();
|
||||
*cluster_slots = cluster_slots.split_off(&(root + 1));
|
||||
// Allow 10% overshoot so that the computation cost is amortized
|
||||
// down. The slots furthest away from the root are discarded.
|
||||
if 10 * cluster_slots.len() > 11 * CLUSTER_SLOTS_TRIM_SIZE {
|
||||
warn!("trimming cluster slots");
|
||||
let key = *cluster_slots.keys().nth(CLUSTER_SLOTS_TRIM_SIZE).unwrap();
|
||||
cluster_slots.split_off(&key);
|
||||
}
|
||||
}
|
||||
if let Some(since) = since {
|
||||
self.since.store(since + 1, Ordering::Relaxed);
|
||||
}
|
||||
*self.since.write().unwrap() = since;
|
||||
}
|
||||
|
||||
pub fn collect(&self, id: &Pubkey) -> HashSet<Slot> {
|
||||
@@ -101,8 +57,7 @@ impl ClusterSlots {
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn insert_node_id(&self, slot: Slot, node_id: Pubkey) {
|
||||
pub fn insert_node_id(&self, slot: Slot, node_id: Pubkey) {
|
||||
let balance = self
|
||||
.validator_stakes
|
||||
.read()
|
||||
@@ -120,7 +75,7 @@ impl ClusterSlots {
|
||||
slot_pubkeys.write().unwrap().insert(node_id, balance);
|
||||
}
|
||||
|
||||
fn update_peers(&self, bank_forks: &RwLock<BankForks>) {
|
||||
fn update_peers(&self, cluster_info: &ClusterInfo, bank_forks: &RwLock<BankForks>) {
|
||||
let root_bank = bank_forks.read().unwrap().root_bank();
|
||||
let root_epoch = root_bank.epoch();
|
||||
let my_epoch = *self.epoch.read().unwrap();
|
||||
@@ -128,11 +83,16 @@ impl ClusterSlots {
|
||||
if Some(root_epoch) != my_epoch {
|
||||
let validator_stakes = root_bank
|
||||
.epoch_stakes(root_epoch)
|
||||
.expect("Bank must have epoch stakes for its own epoch")
|
||||
.expect(
|
||||
"Bank must have epoch stakes
|
||||
for its own epoch",
|
||||
)
|
||||
.node_id_to_vote_accounts()
|
||||
.clone();
|
||||
|
||||
*self.validator_stakes.write().unwrap() = validator_stakes;
|
||||
let id = cluster_info.id();
|
||||
*self.self_id.write().unwrap() = id;
|
||||
*self.epoch.write().unwrap() = Some(root_epoch);
|
||||
}
|
||||
}
|
||||
@@ -207,7 +167,7 @@ mod tests {
|
||||
fn test_default() {
|
||||
let cs = ClusterSlots::default();
|
||||
assert!(cs.cluster_slots.read().unwrap().is_empty());
|
||||
assert_eq!(cs.since.load(Ordering::Relaxed), 0);
|
||||
assert!(cs.since.read().unwrap().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -215,7 +175,7 @@ mod tests {
|
||||
let cs = ClusterSlots::default();
|
||||
cs.update_internal(0, vec![], None);
|
||||
assert!(cs.cluster_slots.read().unwrap().is_empty());
|
||||
assert_eq!(cs.since.load(Ordering::Relaxed), 0);
|
||||
assert!(cs.since.read().unwrap().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -223,7 +183,7 @@ mod tests {
|
||||
let cs = ClusterSlots::default();
|
||||
let epoch_slot = EpochSlots::default();
|
||||
cs.update_internal(0, vec![epoch_slot], Some(0));
|
||||
assert_eq!(cs.since.load(Ordering::Relaxed), 1);
|
||||
assert_eq!(*cs.since.read().unwrap(), Some(0));
|
||||
assert!(cs.lookup(0).is_none());
|
||||
}
|
||||
|
||||
@@ -234,7 +194,7 @@ mod tests {
|
||||
let mut epoch_slot = EpochSlots::default();
|
||||
epoch_slot.fill(&[0], 0);
|
||||
cs.update_internal(0, vec![epoch_slot], Some(0));
|
||||
assert_eq!(cs.since.load(Ordering::Relaxed), 1);
|
||||
assert_eq!(*cs.since.read().unwrap(), Some(0));
|
||||
assert!(cs.lookup(0).is_none());
|
||||
}
|
||||
|
||||
@@ -244,7 +204,7 @@ mod tests {
|
||||
let mut epoch_slot = EpochSlots::default();
|
||||
epoch_slot.fill(&[1], 0);
|
||||
cs.update_internal(0, vec![epoch_slot], Some(0));
|
||||
assert_eq!(cs.since.load(Ordering::Relaxed), 1);
|
||||
assert_eq!(*cs.since.read().unwrap(), Some(0));
|
||||
assert!(cs.lookup(0).is_none());
|
||||
assert!(cs.lookup(1).is_some());
|
||||
assert_eq!(
|
||||
|
@@ -6,9 +6,9 @@ use solana_sdk::{clock::Slot, pubkey::Pubkey};
|
||||
use std::{
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
mpsc::RecvTimeoutError,
|
||||
{Arc, RwLock},
|
||||
},
|
||||
thread::sleep,
|
||||
thread::{self, Builder, JoinHandle},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
@@ -79,14 +79,6 @@ impl ClusterSlotsService {
|
||||
if exit.load(Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
let slots = match completed_slots_receiver.recv_timeout(Duration::from_millis(200)) {
|
||||
Ok(slots) => Some(slots),
|
||||
Err(RecvTimeoutError::Timeout) => None,
|
||||
Err(RecvTimeoutError::Disconnected) => {
|
||||
warn!("Cluster slots service - sender disconnected");
|
||||
break;
|
||||
}
|
||||
};
|
||||
let new_root = bank_forks.read().unwrap().root();
|
||||
let id = cluster_info.id();
|
||||
let mut lowest_slot_elapsed = Measure::start("lowest_slot_elapsed");
|
||||
@@ -95,9 +87,7 @@ impl ClusterSlotsService {
|
||||
lowest_slot_elapsed.stop();
|
||||
let mut update_completed_slots_elapsed =
|
||||
Measure::start("update_completed_slots_elapsed");
|
||||
if let Some(slots) = slots {
|
||||
Self::update_completed_slots(slots, &completed_slots_receiver, &cluster_info);
|
||||
}
|
||||
Self::update_completed_slots(&completed_slots_receiver, &cluster_info);
|
||||
cluster_slots.update(new_root, &cluster_info, &bank_forks);
|
||||
update_completed_slots_elapsed.stop();
|
||||
|
||||
@@ -123,20 +113,20 @@ impl ClusterSlotsService {
|
||||
cluster_slots_service_timing = ClusterSlotsServiceTiming::default();
|
||||
last_stats = Instant::now();
|
||||
}
|
||||
sleep(Duration::from_millis(200));
|
||||
}
|
||||
}
|
||||
|
||||
fn update_completed_slots(
|
||||
mut slots: Vec<Slot>,
|
||||
completed_slots_receiver: &CompletedSlotsReceiver,
|
||||
cluster_info: &ClusterInfo,
|
||||
) {
|
||||
let mut slots: Vec<Slot> = vec![];
|
||||
while let Ok(mut more) = completed_slots_receiver.try_recv() {
|
||||
slots.append(&mut more);
|
||||
}
|
||||
#[allow(clippy::stable_sort_primitive)]
|
||||
slots.sort();
|
||||
|
||||
if !slots.is_empty() {
|
||||
cluster_info.push_epoch_slots(&slots);
|
||||
}
|
||||
|
@@ -249,11 +249,11 @@ mod tests {
|
||||
use super::*;
|
||||
use solana_ledger::genesis_utils::{create_genesis_config, GenesisConfigInfo};
|
||||
use solana_runtime::{
|
||||
accounts_background_service::AbsRequestSender,
|
||||
accounts_background_service::ABSRequestSender,
|
||||
bank_forks::BankForks,
|
||||
genesis_utils::{create_genesis_config_with_vote_accounts, ValidatorVoteKeypairs},
|
||||
};
|
||||
use solana_sdk::{account::Account, pubkey::Pubkey, signature::Signer};
|
||||
use solana_sdk::{pubkey::Pubkey, signature::Signer};
|
||||
use solana_stake_program::stake_state;
|
||||
use solana_vote_program::{
|
||||
vote_state::{self, VoteStateVersions},
|
||||
@@ -411,20 +411,16 @@ mod tests {
|
||||
rooted_stake_amount,
|
||||
);
|
||||
|
||||
genesis_config.accounts.extend(
|
||||
vec![
|
||||
(pk1, vote_account1.clone()),
|
||||
(sk1, stake_account1),
|
||||
(pk2, vote_account2.clone()),
|
||||
(sk2, stake_account2),
|
||||
(pk3, vote_account3.clone()),
|
||||
(sk3, stake_account3),
|
||||
(pk4, vote_account4.clone()),
|
||||
(sk4, stake_account4),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|(key, account)| (key, Account::from(account))),
|
||||
);
|
||||
genesis_config.accounts.extend(vec![
|
||||
(pk1, vote_account1.clone()),
|
||||
(sk1, stake_account1),
|
||||
(pk2, vote_account2.clone()),
|
||||
(sk2, stake_account2),
|
||||
(pk3, vote_account3.clone()),
|
||||
(sk3, stake_account3),
|
||||
(pk4, vote_account4.clone()),
|
||||
(sk4, stake_account4),
|
||||
]);
|
||||
|
||||
// Create bank
|
||||
let bank = Arc::new(Bank::new(&genesis_config));
|
||||
@@ -538,7 +534,7 @@ mod tests {
|
||||
&working_bank,
|
||||
);
|
||||
for x in 0..root {
|
||||
bank_forks.set_root(x, &AbsRequestSender::default(), None);
|
||||
bank_forks.set_root(x, &ABSRequestSender::default(), None);
|
||||
}
|
||||
|
||||
// Add an additional bank/vote that will root slot 2
|
||||
@@ -577,7 +573,7 @@ mod tests {
|
||||
.highest_confirmed_root();
|
||||
bank_forks.set_root(
|
||||
root,
|
||||
&AbsRequestSender::default(),
|
||||
&ABSRequestSender::default(),
|
||||
Some(highest_confirmed_root),
|
||||
);
|
||||
let highest_confirmed_root_bank = bank_forks.get(highest_confirmed_root);
|
||||
@@ -646,7 +642,7 @@ mod tests {
|
||||
.highest_confirmed_root();
|
||||
bank_forks.set_root(
|
||||
root,
|
||||
&AbsRequestSender::default(),
|
||||
&ABSRequestSender::default(),
|
||||
Some(highest_confirmed_root),
|
||||
);
|
||||
let highest_confirmed_root_bank = bank_forks.get(highest_confirmed_root);
|
||||
|
@@ -1,4 +1,4 @@
|
||||
use crate::{max_slots::MaxSlots, rpc_subscriptions::RpcSubscriptions};
|
||||
use crate::rpc_subscriptions::RpcSubscriptions;
|
||||
use crossbeam_channel::{Receiver, RecvTimeoutError, Sender};
|
||||
use solana_ledger::blockstore::{Blockstore, CompletedDataSetInfo};
|
||||
use solana_ledger::entry::Entry;
|
||||
@@ -25,7 +25,6 @@ impl CompletedDataSetsService {
|
||||
blockstore: Arc<Blockstore>,
|
||||
rpc_subscriptions: Arc<RpcSubscriptions>,
|
||||
exit: &Arc<AtomicBool>,
|
||||
max_slots: Arc<MaxSlots>,
|
||||
) -> Self {
|
||||
let exit = exit.clone();
|
||||
let thread_hdl = Builder::new()
|
||||
@@ -38,7 +37,6 @@ impl CompletedDataSetsService {
|
||||
&completed_sets_receiver,
|
||||
&blockstore,
|
||||
&rpc_subscriptions,
|
||||
&max_slots,
|
||||
) {
|
||||
break;
|
||||
}
|
||||
@@ -51,10 +49,8 @@ impl CompletedDataSetsService {
|
||||
completed_sets_receiver: &CompletedDataSetsReceiver,
|
||||
blockstore: &Blockstore,
|
||||
rpc_subscriptions: &RpcSubscriptions,
|
||||
max_slots: &Arc<MaxSlots>,
|
||||
) -> Result<(), RecvTimeoutError> {
|
||||
let completed_data_sets = completed_sets_receiver.recv_timeout(Duration::from_secs(1))?;
|
||||
let mut max_slot = 0;
|
||||
for completed_set_info in std::iter::once(completed_data_sets)
|
||||
.chain(completed_sets_receiver.try_iter())
|
||||
.flatten()
|
||||
@@ -64,7 +60,6 @@ impl CompletedDataSetsService {
|
||||
start_index,
|
||||
end_index,
|
||||
} = completed_set_info;
|
||||
max_slot = max_slot.max(slot);
|
||||
match blockstore.get_entries_in_data_block(slot, start_index, end_index, None) {
|
||||
Ok(entries) => {
|
||||
let transactions = Self::get_transaction_signatures(entries);
|
||||
@@ -75,9 +70,6 @@ impl CompletedDataSetsService {
|
||||
Err(e) => warn!("completed-data-set-service deserialize error: {:?}", e),
|
||||
}
|
||||
}
|
||||
max_slots
|
||||
.shred_insert
|
||||
.fetch_max(max_slot, Ordering::Relaxed);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@@ -845,9 +845,10 @@ impl Tower {
|
||||
assert!(
|
||||
self.last_vote == Vote::default() && self.lockouts.votes.is_empty()
|
||||
|| self.last_vote != Vote::default() && !self.lockouts.votes.is_empty(),
|
||||
"last vote: {:?} lockouts.votes: {:?}",
|
||||
self.last_vote,
|
||||
self.lockouts.votes
|
||||
format!(
|
||||
"last vote: {:?} lockouts.votes: {:?}",
|
||||
self.last_vote, self.lockouts.votes
|
||||
)
|
||||
);
|
||||
|
||||
if let Some(last_voted_slot) = self.last_voted_slot() {
|
||||
@@ -1130,7 +1131,7 @@ impl Tower {
|
||||
#[derive(Error, Debug)]
|
||||
pub enum TowerError {
|
||||
#[error("IO Error: {0}")]
|
||||
IoError(#[from] std::io::Error),
|
||||
IOError(#[from] std::io::Error),
|
||||
|
||||
#[error("Serialization Error: {0}")]
|
||||
SerializeError(#[from] bincode::Error),
|
||||
@@ -1156,7 +1157,7 @@ pub enum TowerError {
|
||||
|
||||
impl TowerError {
|
||||
pub fn is_file_missing(&self) -> bool {
|
||||
if let TowerError::IoError(io_err) = &self {
|
||||
if let TowerError::IOError(io_err) = &self {
|
||||
io_err.kind() == std::io::ErrorKind::NotFound
|
||||
} else {
|
||||
false
|
||||
@@ -1245,7 +1246,7 @@ pub mod test {
|
||||
};
|
||||
use solana_ledger::{blockstore::make_slot_entries, get_tmp_ledger_path};
|
||||
use solana_runtime::{
|
||||
accounts_background_service::AbsRequestSender,
|
||||
accounts_background_service::ABSRequestSender,
|
||||
bank::Bank,
|
||||
bank_forks::BankForks,
|
||||
genesis_utils::{
|
||||
@@ -1253,11 +1254,7 @@ pub mod test {
|
||||
},
|
||||
};
|
||||
use solana_sdk::{
|
||||
account::{Account, AccountSharedData, WritableAccount},
|
||||
clock::Slot,
|
||||
hash::Hash,
|
||||
pubkey::Pubkey,
|
||||
signature::Signer,
|
||||
account::Account, clock::Slot, hash::Hash, pubkey::Pubkey, signature::Signer,
|
||||
slot_history::SlotHistory,
|
||||
};
|
||||
use solana_vote_program::{
|
||||
@@ -1420,7 +1417,7 @@ pub mod test {
|
||||
new_root,
|
||||
&self.bank_forks,
|
||||
&mut self.progress,
|
||||
&AbsRequestSender::default(),
|
||||
&ABSRequestSender::default(),
|
||||
None,
|
||||
&mut self.heaviest_subtree_fork_choice,
|
||||
)
|
||||
@@ -1575,10 +1572,10 @@ pub mod test {
|
||||
fn gen_stakes(stake_votes: &[(u64, &[u64])]) -> Vec<(Pubkey, (u64, ArcVoteAccount))> {
|
||||
let mut stakes = vec![];
|
||||
for (lamports, votes) in stake_votes {
|
||||
let mut account = AccountSharedData {
|
||||
let mut account = Account {
|
||||
data: vec![0; VoteState::size_of()],
|
||||
lamports: *lamports,
|
||||
..AccountSharedData::default()
|
||||
..Account::default()
|
||||
};
|
||||
let mut vote_state = VoteState::default();
|
||||
for slot in *votes {
|
||||
@@ -1586,7 +1583,7 @@ pub mod test {
|
||||
}
|
||||
VoteState::serialize(
|
||||
&VoteStateVersions::new_current(vote_state),
|
||||
&mut account.data_as_mut_slice(),
|
||||
&mut account.data,
|
||||
)
|
||||
.expect("serialize state");
|
||||
stakes.push((
|
||||
@@ -2255,10 +2252,10 @@ pub mod test {
|
||||
#[test]
|
||||
fn test_stake_is_updated_for_entire_branch() {
|
||||
let mut voted_stakes = HashMap::new();
|
||||
let account = AccountSharedData::from(Account {
|
||||
let account = Account {
|
||||
lamports: 1,
|
||||
..Account::default()
|
||||
});
|
||||
};
|
||||
let set: HashSet<u64> = vec![0u64, 1u64].into_iter().collect();
|
||||
let ancestors: HashMap<u64, HashSet<u64>> = [(2u64, set)].iter().cloned().collect();
|
||||
Tower::update_ancestor_voted_stakes(&mut voted_stakes, 2, account.lamports, &ancestors);
|
||||
@@ -2707,7 +2704,7 @@ pub mod test {
|
||||
remove_file(path).unwrap();
|
||||
},
|
||||
);
|
||||
assert_matches!(loaded, Err(TowerError::IoError(_)))
|
||||
assert_matches!(loaded, Err(TowerError::IOError(_)))
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
201
core/src/crds.rs
201
core/src/crds.rs
@@ -28,7 +28,7 @@ use crate::contact_info::ContactInfo;
|
||||
use crate::crds_shards::CrdsShards;
|
||||
use crate::crds_value::{CrdsData, CrdsValue, CrdsValueLabel, LowestSlot};
|
||||
use bincode::serialize;
|
||||
use indexmap::map::{rayon::ParValues, Entry, IndexMap};
|
||||
use indexmap::map::{rayon::ParValues, Entry, IndexMap, Values};
|
||||
use indexmap::set::IndexSet;
|
||||
use rayon::{prelude::*, ThreadPool};
|
||||
use solana_sdk::hash::{hash, Hash};
|
||||
@@ -36,8 +36,8 @@ use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::signature::Keypair;
|
||||
use solana_sdk::timing::timestamp;
|
||||
use std::cmp;
|
||||
use std::collections::{hash_map, BTreeSet, HashMap};
|
||||
use std::ops::{Bound, Index, IndexMut};
|
||||
use std::collections::{hash_map, HashMap};
|
||||
use std::ops::{Index, IndexMut};
|
||||
|
||||
const CRDS_SHARDS_BITS: u32 = 8;
|
||||
// Limit number of crds values associated with each unique pubkey. This
|
||||
@@ -52,8 +52,6 @@ pub struct Crds {
|
||||
shards: CrdsShards,
|
||||
nodes: IndexSet<usize>, // Indices of nodes' ContactInfo.
|
||||
votes: IndexSet<usize>, // Indices of Vote crds values.
|
||||
// Indices of EpochSlots crds values ordered by insert timestamp.
|
||||
epoch_slots: BTreeSet<(u64 /*insert timestamp*/, usize)>,
|
||||
// Indices of all crds values associated with a node.
|
||||
records: HashMap<Pubkey, IndexSet<usize>>,
|
||||
}
|
||||
@@ -61,7 +59,6 @@ pub struct Crds {
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum CrdsError {
|
||||
InsertFailed,
|
||||
UnknownStakes,
|
||||
}
|
||||
|
||||
/// This structure stores some local metadata associated with the CrdsValue
|
||||
@@ -116,7 +113,6 @@ impl Default for Crds {
|
||||
shards: CrdsShards::new(CRDS_SHARDS_BITS),
|
||||
nodes: IndexSet::default(),
|
||||
votes: IndexSet::default(),
|
||||
epoch_slots: BTreeSet::default(),
|
||||
records: HashMap::default(),
|
||||
}
|
||||
}
|
||||
@@ -156,10 +152,6 @@ impl Crds {
|
||||
CrdsData::Vote(_, _) => {
|
||||
self.votes.insert(entry_index);
|
||||
}
|
||||
CrdsData::EpochSlots(_, _) => {
|
||||
self.epoch_slots
|
||||
.insert((new_value.insert_timestamp, entry_index));
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
self.records
|
||||
@@ -171,15 +163,9 @@ impl Crds {
|
||||
Ok(None)
|
||||
}
|
||||
Entry::Occupied(mut entry) if *entry.get() < new_value => {
|
||||
let entry_index = entry.index();
|
||||
self.shards.remove(entry_index, entry.get());
|
||||
self.shards.insert(entry_index, &new_value);
|
||||
if let CrdsData::EpochSlots(_, _) = new_value.value.data {
|
||||
self.epoch_slots
|
||||
.remove(&(entry.get().insert_timestamp, entry_index));
|
||||
self.epoch_slots
|
||||
.insert((new_value.insert_timestamp, entry_index));
|
||||
}
|
||||
let index = entry.index();
|
||||
self.shards.remove(index, entry.get());
|
||||
self.shards.insert(index, &new_value);
|
||||
self.num_inserts += 1;
|
||||
// As long as the pubkey does not change, self.records
|
||||
// does not need to be updated.
|
||||
@@ -244,17 +230,6 @@ impl Crds {
|
||||
self.votes.iter().map(move |i| self.table.index(*i))
|
||||
}
|
||||
|
||||
/// Returns epoch-slots inserted since (or at) the given timestamp.
|
||||
pub(crate) fn get_epoch_slots_since(
|
||||
&self,
|
||||
timestamp: u64,
|
||||
) -> impl Iterator<Item = &VersionedCrdsValue> {
|
||||
let range = (Bound::Included((timestamp, 0)), Bound::Unbounded);
|
||||
self.epoch_slots
|
||||
.range(range)
|
||||
.map(move |(_, i)| self.table.index(*i))
|
||||
}
|
||||
|
||||
/// Returns all records associated with a pubkey.
|
||||
pub(crate) fn get_records(&self, pubkey: &Pubkey) -> impl Iterator<Item = &VersionedCrdsValue> {
|
||||
self.records
|
||||
@@ -272,8 +247,7 @@ impl Crds {
|
||||
self.table.is_empty()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn values(&self) -> impl Iterator<Item = &VersionedCrdsValue> {
|
||||
pub fn values(&self) -> Values<'_, CrdsValueLabel, VersionedCrdsValue> {
|
||||
self.table.values()
|
||||
}
|
||||
|
||||
@@ -380,9 +354,6 @@ impl Crds {
|
||||
CrdsData::Vote(_, _) => {
|
||||
self.votes.swap_remove(&index);
|
||||
}
|
||||
CrdsData::EpochSlots(_, _) => {
|
||||
self.epoch_slots.remove(&(value.insert_timestamp, index));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
// Remove the index from records associated with the value's pubkey.
|
||||
@@ -414,10 +385,6 @@ impl Crds {
|
||||
self.votes.swap_remove(&size);
|
||||
self.votes.insert(index);
|
||||
}
|
||||
CrdsData::EpochSlots(_, _) => {
|
||||
self.epoch_slots.remove(&(value.insert_timestamp, size));
|
||||
self.epoch_slots.insert((value.insert_timestamp, index));
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
let pubkey = value.value.pubkey();
|
||||
@@ -427,62 +394,6 @@ impl Crds {
|
||||
}
|
||||
Some(value)
|
||||
}
|
||||
|
||||
/// Returns true if the number of unique pubkeys in the table exceeds the
|
||||
/// given capacity (plus some margin).
|
||||
/// Allows skipping unnecessary calls to trim without obtaining a write
|
||||
/// lock on gossip.
|
||||
pub(crate) fn should_trim(&self, cap: usize) -> bool {
|
||||
// Allow 10% overshoot so that the computation cost is amortized down.
|
||||
10 * self.records.len() > 11 * cap
|
||||
}
|
||||
|
||||
/// Trims the table by dropping all values associated with the pubkeys with
|
||||
/// the lowest stake, so that the number of unique pubkeys are bounded.
|
||||
pub(crate) fn trim(
|
||||
&mut self,
|
||||
cap: usize, // Capacity hint for number of unique pubkeys.
|
||||
// Set of pubkeys to never drop.
|
||||
// e.g. trusted validators, self pubkey, ...
|
||||
keep: &[Pubkey],
|
||||
stakes: &HashMap<Pubkey, u64>,
|
||||
) -> Result<Vec<VersionedCrdsValue>, CrdsError> {
|
||||
if self.should_trim(cap) {
|
||||
let size = self.records.len().saturating_sub(cap);
|
||||
self.drop(size, keep, stakes)
|
||||
} else {
|
||||
Ok(Vec::default())
|
||||
}
|
||||
}
|
||||
|
||||
// Drops 'size' many pubkeys with the lowest stake.
|
||||
fn drop(
|
||||
&mut self,
|
||||
size: usize,
|
||||
keep: &[Pubkey],
|
||||
stakes: &HashMap<Pubkey, u64>,
|
||||
) -> Result<Vec<VersionedCrdsValue>, CrdsError> {
|
||||
if stakes.is_empty() {
|
||||
return Err(CrdsError::UnknownStakes);
|
||||
}
|
||||
let mut keys: Vec<_> = self
|
||||
.records
|
||||
.keys()
|
||||
.map(|k| (stakes.get(k).copied().unwrap_or_default(), *k))
|
||||
.collect();
|
||||
if size < keys.len() {
|
||||
keys.select_nth_unstable(size);
|
||||
}
|
||||
let keys: Vec<_> = keys
|
||||
.into_iter()
|
||||
.take(size)
|
||||
.map(|(_, k)| k)
|
||||
.filter(|k| !keep.contains(k))
|
||||
.flat_map(|k| &self.records[&k])
|
||||
.map(|k| self.table.get_index(*k).unwrap().0.clone())
|
||||
.collect();
|
||||
Ok(keys.iter().map(|k| self.remove(k).unwrap()).collect())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -491,7 +402,6 @@ mod test {
|
||||
use crate::{contact_info::ContactInfo, crds_value::NodeInstance};
|
||||
use rand::{thread_rng, Rng};
|
||||
use rayon::ThreadPoolBuilder;
|
||||
use solana_sdk::signature::Signer;
|
||||
use std::{collections::HashSet, iter::repeat_with};
|
||||
|
||||
#[test]
|
||||
@@ -733,27 +643,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_crds_value_indices() {
|
||||
fn check_crds_value_indices<R: rand::Rng>(
|
||||
rng: &mut R,
|
||||
crds: &Crds,
|
||||
) -> (usize, usize, usize) {
|
||||
if !crds.table.is_empty() {
|
||||
let since = crds.table[rng.gen_range(0, crds.table.len())].insert_timestamp;
|
||||
let num_epoch_slots = crds
|
||||
.table
|
||||
.values()
|
||||
.filter(|value| value.insert_timestamp >= since)
|
||||
.filter(|value| matches!(value.value.data, CrdsData::EpochSlots(_, _)))
|
||||
.count();
|
||||
assert_eq!(num_epoch_slots, crds.get_epoch_slots_since(since).count());
|
||||
for value in crds.get_epoch_slots_since(since) {
|
||||
assert!(value.insert_timestamp >= since);
|
||||
match value.value.data {
|
||||
CrdsData::EpochSlots(_, _) => (),
|
||||
_ => panic!("not an epoch-slot!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
fn check_crds_value_indices(crds: &Crds) -> (usize, usize) {
|
||||
let num_nodes = crds
|
||||
.table
|
||||
.values()
|
||||
@@ -764,27 +654,15 @@ mod test {
|
||||
.values()
|
||||
.filter(|value| matches!(value.value.data, CrdsData::Vote(_, _)))
|
||||
.count();
|
||||
let num_epoch_slots = crds
|
||||
.table
|
||||
.values()
|
||||
.filter(|value| matches!(value.value.data, CrdsData::EpochSlots(_, _)))
|
||||
.count();
|
||||
assert_eq!(num_nodes, crds.get_nodes_contact_info().count());
|
||||
assert_eq!(num_votes, crds.get_votes().count());
|
||||
assert_eq!(num_epoch_slots, crds.get_epoch_slots_since(0).count());
|
||||
for vote in crds.get_votes() {
|
||||
match vote.value.data {
|
||||
CrdsData::Vote(_, _) => (),
|
||||
_ => panic!("not a vote!"),
|
||||
}
|
||||
}
|
||||
for epoch_slots in crds.get_epoch_slots_since(0) {
|
||||
match epoch_slots.value.data {
|
||||
CrdsData::EpochSlots(_, _) => (),
|
||||
_ => panic!("not an epoch-slot!"),
|
||||
}
|
||||
}
|
||||
(num_nodes, num_votes, num_epoch_slots)
|
||||
(num_nodes, num_votes)
|
||||
}
|
||||
let mut rng = thread_rng();
|
||||
let keypairs: Vec<_> = repeat_with(Keypair::new).take(128).collect();
|
||||
@@ -805,7 +683,7 @@ mod test {
|
||||
Err(_) => (),
|
||||
}
|
||||
if k % 64 == 0 {
|
||||
check_crds_value_indices(&mut rng, &crds);
|
||||
check_crds_value_indices(&crds);
|
||||
}
|
||||
}
|
||||
assert_eq!(num_inserts, crds.num_inserts);
|
||||
@@ -813,22 +691,17 @@ mod test {
|
||||
assert!(num_overrides > 500);
|
||||
assert!(crds.table.len() > 200);
|
||||
assert!(num_inserts > crds.table.len());
|
||||
let (num_nodes, num_votes, num_epoch_slots) = check_crds_value_indices(&mut rng, &crds);
|
||||
let (num_nodes, num_votes) = check_crds_value_indices(&crds);
|
||||
assert!(num_nodes * 3 < crds.table.len());
|
||||
assert!(num_nodes > 100, "num nodes: {}", num_nodes);
|
||||
assert!(num_votes > 100, "num votes: {}", num_votes);
|
||||
assert!(
|
||||
num_epoch_slots > 100,
|
||||
"num epoch slots: {}",
|
||||
num_epoch_slots
|
||||
);
|
||||
// Remove values one by one and assert that nodes indices stay valid.
|
||||
while !crds.table.is_empty() {
|
||||
let index = rng.gen_range(0, crds.table.len());
|
||||
let key = crds.table.get_index(index).unwrap().0.clone();
|
||||
crds.remove(&key);
|
||||
if crds.table.len() % 64 == 0 {
|
||||
check_crds_value_indices(&mut rng, &crds);
|
||||
check_crds_value_indices(&crds);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -872,56 +745,6 @@ mod test {
|
||||
assert!(crds.records.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_drop() {
|
||||
fn num_unique_pubkeys<'a, I>(values: I) -> usize
|
||||
where
|
||||
I: IntoIterator<Item = &'a VersionedCrdsValue>,
|
||||
{
|
||||
values
|
||||
.into_iter()
|
||||
.map(|v| v.value.pubkey())
|
||||
.collect::<HashSet<_>>()
|
||||
.len()
|
||||
}
|
||||
let mut rng = thread_rng();
|
||||
let keypairs: Vec<_> = repeat_with(Keypair::new).take(64).collect();
|
||||
let stakes = keypairs
|
||||
.iter()
|
||||
.map(|k| (k.pubkey(), rng.gen_range(0, 1000)))
|
||||
.collect();
|
||||
let mut crds = Crds::default();
|
||||
for _ in 0..2048 {
|
||||
let keypair = &keypairs[rng.gen_range(0, keypairs.len())];
|
||||
let value = VersionedCrdsValue::new_rand(&mut rng, Some(keypair));
|
||||
let _ = crds.insert_versioned(value);
|
||||
}
|
||||
let num_values = crds.table.len();
|
||||
let num_pubkeys = num_unique_pubkeys(crds.table.values());
|
||||
assert!(!crds.should_trim(num_pubkeys));
|
||||
assert!(crds.should_trim(num_pubkeys * 5 / 6));
|
||||
let purged = crds.drop(16, &[], &stakes).unwrap();
|
||||
assert_eq!(purged.len() + crds.table.len(), num_values);
|
||||
assert_eq!(num_unique_pubkeys(&purged), 16);
|
||||
assert_eq!(num_unique_pubkeys(crds.table.values()), num_pubkeys - 16);
|
||||
let attach_stake = |v: &VersionedCrdsValue| {
|
||||
let pk = v.value.pubkey();
|
||||
(stakes[&pk], pk)
|
||||
};
|
||||
assert!(
|
||||
purged.iter().map(attach_stake).max().unwrap()
|
||||
< crds.table.values().map(attach_stake).min().unwrap()
|
||||
);
|
||||
let purged = purged
|
||||
.into_iter()
|
||||
.map(|v| v.value.pubkey())
|
||||
.collect::<HashSet<_>>();
|
||||
for (k, v) in crds.table {
|
||||
assert!(!purged.contains(&k.pubkey()));
|
||||
assert!(!purged.contains(&v.value.pubkey()));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove_staked() {
|
||||
let thread_pool = ThreadPoolBuilder::new().build().unwrap();
|
||||
|
@@ -106,9 +106,10 @@ impl CrdsGossip {
|
||||
&mut self,
|
||||
pending_push_messages: Vec<(CrdsValue, u64)>,
|
||||
now: u64,
|
||||
) -> HashMap<Pubkey, Vec<CrdsValue>> {
|
||||
) -> (Pubkey, HashMap<Pubkey, Vec<CrdsValue>>) {
|
||||
self.process_push_messages(pending_push_messages);
|
||||
self.push.new_push_messages(&self.crds, now)
|
||||
let push_messages = self.push.new_push_messages(&self.crds, now);
|
||||
(self.id, push_messages)
|
||||
}
|
||||
|
||||
pub(crate) fn push_duplicate_shred(
|
||||
|
@@ -137,7 +137,7 @@ pub(crate) fn new_rand_timestamp<R: Rng>(rng: &mut R) -> u64 {
|
||||
impl CrdsData {
|
||||
/// New random CrdsData for tests and benchmarks.
|
||||
fn new_rand<R: Rng>(rng: &mut R, pubkey: Option<Pubkey>) -> CrdsData {
|
||||
let kind = rng.gen_range(0, 7);
|
||||
let kind = rng.gen_range(0, 6);
|
||||
// TODO: Implement other kinds of CrdsData here.
|
||||
// TODO: Assign ranges to each arm proportional to their frequency in
|
||||
// the mainnet crds table.
|
||||
@@ -147,11 +147,7 @@ impl CrdsData {
|
||||
2 => CrdsData::SnapshotHashes(SnapshotHash::new_rand(rng, pubkey)),
|
||||
3 => CrdsData::AccountsHashes(SnapshotHash::new_rand(rng, pubkey)),
|
||||
4 => CrdsData::Version(Version::new_rand(rng, pubkey)),
|
||||
5 => CrdsData::Vote(rng.gen_range(0, MAX_VOTES), Vote::new_rand(rng, pubkey)),
|
||||
_ => CrdsData::EpochSlots(
|
||||
rng.gen_range(0, MAX_EPOCH_SLOTS),
|
||||
EpochSlots::new_rand(rng, pubkey),
|
||||
),
|
||||
_ => CrdsData::Vote(rng.gen_range(0, MAX_VOTES), Vote::new_rand(rng, pubkey)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
use crate::cluster_info::MAX_CRDS_OBJECT_SIZE;
|
||||
use crate::crds_value::{self, MAX_SLOT, MAX_WALLCLOCK};
|
||||
use crate::crds_value::MAX_SLOT;
|
||||
use crate::crds_value::MAX_WALLCLOCK;
|
||||
use bincode::serialized_size;
|
||||
use bv::BitVec;
|
||||
use flate2::{Compress, Compression, Decompress, FlushCompress, FlushDecompress};
|
||||
@@ -315,19 +316,6 @@ impl EpochSlots {
|
||||
.flatten()
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// New random EpochSlots for tests and simulations.
|
||||
pub(crate) fn new_rand<R: rand::Rng>(rng: &mut R, pubkey: Option<Pubkey>) -> Self {
|
||||
let now = crds_value::new_rand_timestamp(rng);
|
||||
let pubkey = pubkey.unwrap_or_else(solana_sdk::pubkey::new_rand);
|
||||
let mut epoch_slots = Self::new(pubkey, now);
|
||||
let num_slots = rng.gen_range(0, 20);
|
||||
let slots: Vec<_> = std::iter::repeat_with(|| 47825632 + rng.gen_range(0, 512))
|
||||
.take(num_slots)
|
||||
.collect();
|
||||
epoch_slots.add(&slots);
|
||||
epoch_slots
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@@ -1,6 +1,6 @@
|
||||
//! The `fetch_stage` batches input from a UDP socket and sends it to a channel.
|
||||
|
||||
use crate::banking_stage::HOLD_TRANSACTIONS_SLOT_OFFSET;
|
||||
use crate::banking_stage::FORWARD_TRANSACTIONS_TO_LEADER_AT_SLOT_OFFSET;
|
||||
use crate::poh_recorder::PohRecorder;
|
||||
use crate::result::{Error, Result};
|
||||
use solana_measure::thread_mem_usage;
|
||||
@@ -26,19 +26,10 @@ impl FetchStage {
|
||||
tpu_forwards_sockets: Vec<UdpSocket>,
|
||||
exit: &Arc<AtomicBool>,
|
||||
poh_recorder: &Arc<Mutex<PohRecorder>>,
|
||||
coalesce_ms: u64,
|
||||
) -> (Self, PacketReceiver) {
|
||||
let (sender, receiver) = channel();
|
||||
(
|
||||
Self::new_with_sender(
|
||||
sockets,
|
||||
tpu_forwards_sockets,
|
||||
exit,
|
||||
&sender,
|
||||
&poh_recorder,
|
||||
None,
|
||||
coalesce_ms,
|
||||
),
|
||||
Self::new_with_sender(sockets, tpu_forwards_sockets, exit, &sender, &poh_recorder),
|
||||
receiver,
|
||||
)
|
||||
}
|
||||
@@ -48,8 +39,6 @@ impl FetchStage {
|
||||
exit: &Arc<AtomicBool>,
|
||||
sender: &PacketSender,
|
||||
poh_recorder: &Arc<Mutex<PohRecorder>>,
|
||||
allocated_packet_limit: Option<u32>,
|
||||
coalesce_ms: u64,
|
||||
) -> Self {
|
||||
let tx_sockets = sockets.into_iter().map(Arc::new).collect();
|
||||
let tpu_forwards_sockets = tpu_forwards_sockets.into_iter().map(Arc::new).collect();
|
||||
@@ -59,8 +48,6 @@ impl FetchStage {
|
||||
exit,
|
||||
&sender,
|
||||
&poh_recorder,
|
||||
allocated_packet_limit,
|
||||
coalesce_ms,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -81,11 +68,11 @@ impl FetchStage {
|
||||
}
|
||||
}
|
||||
|
||||
if poh_recorder
|
||||
.lock()
|
||||
.unwrap()
|
||||
.would_be_leader(HOLD_TRANSACTIONS_SLOT_OFFSET.saturating_mul(DEFAULT_TICKS_PER_SLOT))
|
||||
{
|
||||
if poh_recorder.lock().unwrap().would_be_leader(
|
||||
FORWARD_TRANSACTIONS_TO_LEADER_AT_SLOT_OFFSET
|
||||
.saturating_add(1)
|
||||
.saturating_mul(DEFAULT_TICKS_PER_SLOT),
|
||||
) {
|
||||
inc_new_counter_debug!("fetch_stage-honor_forwards", len);
|
||||
for packets in batch {
|
||||
if sendr.send(packets).is_err() {
|
||||
@@ -105,11 +92,8 @@ impl FetchStage {
|
||||
exit: &Arc<AtomicBool>,
|
||||
sender: &PacketSender,
|
||||
poh_recorder: &Arc<Mutex<PohRecorder>>,
|
||||
limit: Option<u32>,
|
||||
coalesce_ms: u64,
|
||||
) -> Self {
|
||||
let recycler: PacketsRecycler =
|
||||
Recycler::warmed(1000, 1024, limit, "fetch_stage_recycler_shrink");
|
||||
let recycler: PacketsRecycler = Recycler::warmed(1000, 1024);
|
||||
|
||||
let tpu_threads = sockets.into_iter().map(|socket| {
|
||||
streamer::receiver(
|
||||
@@ -118,7 +102,6 @@ impl FetchStage {
|
||||
sender.clone(),
|
||||
recycler.clone(),
|
||||
"fetch_stage",
|
||||
coalesce_ms,
|
||||
)
|
||||
});
|
||||
|
||||
@@ -130,7 +113,6 @@ impl FetchStage {
|
||||
forward_sender.clone(),
|
||||
recycler.clone(),
|
||||
"fetch_forward_stage",
|
||||
coalesce_ms,
|
||||
)
|
||||
});
|
||||
|
||||
|
@@ -47,9 +47,8 @@ impl GossipService {
|
||||
gossip_socket.clone(),
|
||||
&exit,
|
||||
request_sender,
|
||||
Recycler::new_without_limit("gossip-receiver-recycler-shrink-stats"),
|
||||
Recycler::default(),
|
||||
"gossip_receiver",
|
||||
1,
|
||||
);
|
||||
let (response_sender, response_receiver) = channel();
|
||||
let t_responder = streamer::responder("gossip", gossip_socket, response_receiver);
|
||||
|
@@ -1,16 +1,15 @@
|
||||
//! The `ledger_cleanup_service` drops older ledger data to limit disk space usage
|
||||
|
||||
use rand::{thread_rng, Rng};
|
||||
use solana_ledger::blockstore::{Blockstore, PurgeType};
|
||||
use solana_ledger::blockstore_db::Result as BlockstoreResult;
|
||||
use solana_measure::measure::Measure;
|
||||
use solana_sdk::clock::{Slot, DEFAULT_TICKS_PER_SLOT, TICKS_PER_DAY};
|
||||
use std::string::ToString;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::mpsc::{Receiver, RecvTimeoutError};
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::thread::{sleep, Builder, JoinHandle};
|
||||
use std::thread::{Builder, JoinHandle};
|
||||
use std::time::Duration;
|
||||
|
||||
// - To try and keep the RocksDB size under 400GB:
|
||||
@@ -36,7 +35,6 @@ const DEFAULT_COMPACTION_SLOT_INTERVAL: u64 = TICKS_PER_DAY / DEFAULT_TICKS_PER_
|
||||
|
||||
pub struct LedgerCleanupService {
|
||||
t_cleanup: JoinHandle<()>,
|
||||
t_compact: JoinHandle<()>,
|
||||
}
|
||||
|
||||
impl LedgerCleanupService {
|
||||
@@ -45,8 +43,6 @@ impl LedgerCleanupService {
|
||||
blockstore: Arc<Blockstore>,
|
||||
max_ledger_shreds: u64,
|
||||
exit: &Arc<AtomicBool>,
|
||||
compaction_interval: Option<u64>,
|
||||
max_compaction_jitter: Option<u64>,
|
||||
) -> Self {
|
||||
info!(
|
||||
"LedgerCleanupService active. Max Ledger Slots {}",
|
||||
@@ -55,16 +51,9 @@ impl LedgerCleanupService {
|
||||
let exit = exit.clone();
|
||||
let mut last_purge_slot = 0;
|
||||
let mut last_compaction_slot = 0;
|
||||
let mut compaction_jitter = 0;
|
||||
let compaction_interval = compaction_interval.unwrap_or(DEFAULT_COMPACTION_SLOT_INTERVAL);
|
||||
let last_compact_slot = Arc::new(AtomicU64::new(0));
|
||||
let last_compact_slot2 = last_compact_slot.clone();
|
||||
|
||||
let exit_compact = exit.clone();
|
||||
let blockstore_compact = blockstore.clone();
|
||||
|
||||
let t_cleanup = Builder::new()
|
||||
.name("sol-led-cleanup".to_string())
|
||||
.name("solana-ledger-cleanup".to_string())
|
||||
.spawn(move || loop {
|
||||
if exit.load(Ordering::Relaxed) {
|
||||
break;
|
||||
@@ -75,7 +64,8 @@ impl LedgerCleanupService {
|
||||
max_ledger_shreds,
|
||||
&mut last_purge_slot,
|
||||
DEFAULT_PURGE_SLOT_INTERVAL,
|
||||
&last_compact_slot,
|
||||
&mut last_compaction_slot,
|
||||
DEFAULT_COMPACTION_SLOT_INTERVAL,
|
||||
) {
|
||||
match e {
|
||||
RecvTimeoutError::Disconnected => break,
|
||||
@@ -84,29 +74,7 @@ impl LedgerCleanupService {
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let t_compact = Builder::new()
|
||||
.name("sol-led-compact".to_string())
|
||||
.spawn(move || loop {
|
||||
if exit_compact.load(Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
Self::compact_ledger(
|
||||
&blockstore_compact,
|
||||
&mut last_compaction_slot,
|
||||
compaction_interval,
|
||||
&last_compact_slot2,
|
||||
&mut compaction_jitter,
|
||||
max_compaction_jitter,
|
||||
);
|
||||
sleep(Duration::from_secs(1));
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
Self {
|
||||
t_cleanup,
|
||||
t_compact,
|
||||
}
|
||||
Self { t_cleanup }
|
||||
}
|
||||
|
||||
fn find_slots_to_clean(
|
||||
@@ -170,7 +138,8 @@ impl LedgerCleanupService {
|
||||
max_ledger_shreds: u64,
|
||||
last_purge_slot: &mut u64,
|
||||
purge_interval: u64,
|
||||
last_compact_slot: &Arc<AtomicU64>,
|
||||
last_compaction_slot: &mut u64,
|
||||
compaction_interval: u64,
|
||||
) -> Result<(), RecvTimeoutError> {
|
||||
let root = Self::receive_new_roots(new_root_receiver)?;
|
||||
if root - *last_purge_slot <= purge_interval {
|
||||
@@ -179,8 +148,8 @@ impl LedgerCleanupService {
|
||||
|
||||
let disk_utilization_pre = blockstore.storage_size();
|
||||
info!(
|
||||
"purge: last_root={}, last_purge_slot={}, purge_interval={}, disk_utilization={:?}",
|
||||
root, last_purge_slot, purge_interval, disk_utilization_pre
|
||||
"purge: last_root={}, last_purge_slot={}, purge_interval={}, last_compaction_slot={}, disk_utilization={:?}",
|
||||
root, last_purge_slot, purge_interval, last_compaction_slot, disk_utilization_pre
|
||||
);
|
||||
|
||||
*last_purge_slot = root;
|
||||
@@ -189,10 +158,15 @@ impl LedgerCleanupService {
|
||||
Self::find_slots_to_clean(&blockstore, root, max_ledger_shreds);
|
||||
|
||||
if slots_to_clean {
|
||||
let mut compact_first_slot = std::u64::MAX;
|
||||
if lowest_cleanup_slot.saturating_sub(*last_compaction_slot) > compaction_interval {
|
||||
compact_first_slot = *last_compaction_slot;
|
||||
*last_compaction_slot = lowest_cleanup_slot;
|
||||
}
|
||||
|
||||
let purge_complete = Arc::new(AtomicBool::new(false));
|
||||
let blockstore = blockstore.clone();
|
||||
let purge_complete1 = purge_complete.clone();
|
||||
let last_compact_slot1 = last_compact_slot.clone();
|
||||
let _t_purge = Builder::new()
|
||||
.name("solana-ledger-purge".to_string())
|
||||
.spawn(move || {
|
||||
@@ -214,7 +188,21 @@ impl LedgerCleanupService {
|
||||
purge_time.stop();
|
||||
info!("{}", purge_time);
|
||||
|
||||
last_compact_slot1.store(lowest_cleanup_slot, Ordering::Relaxed);
|
||||
if compact_first_slot < lowest_cleanup_slot {
|
||||
info!(
|
||||
"compacting data from slots {} to {}",
|
||||
compact_first_slot, lowest_cleanup_slot
|
||||
);
|
||||
if let Err(err) =
|
||||
blockstore.compact_storage(compact_first_slot, lowest_cleanup_slot)
|
||||
{
|
||||
// This error is not fatal and indicates an internal error?
|
||||
error!(
|
||||
"Error: {:?}; Couldn't compact storage from {:?} to {:?}",
|
||||
err, compact_first_slot, lowest_cleanup_slot
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
purge_complete1.store(true, Ordering::Relaxed);
|
||||
})
|
||||
@@ -235,39 +223,6 @@ impl LedgerCleanupService {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compact_ledger(
|
||||
blockstore: &Arc<Blockstore>,
|
||||
last_compaction_slot: &mut u64,
|
||||
compaction_interval: u64,
|
||||
highest_compact_slot: &Arc<AtomicU64>,
|
||||
compaction_jitter: &mut u64,
|
||||
max_jitter: Option<u64>,
|
||||
) {
|
||||
let highest_compaction_slot = highest_compact_slot.load(Ordering::Relaxed);
|
||||
if highest_compaction_slot.saturating_sub(*last_compaction_slot)
|
||||
> (compaction_interval + *compaction_jitter)
|
||||
{
|
||||
info!(
|
||||
"compacting data from slots {} to {}",
|
||||
*last_compaction_slot, highest_compaction_slot,
|
||||
);
|
||||
if let Err(err) =
|
||||
blockstore.compact_storage(*last_compaction_slot, highest_compaction_slot)
|
||||
{
|
||||
// This error is not fatal and indicates an internal error?
|
||||
error!(
|
||||
"Error: {:?}; Couldn't compact storage from {:?} to {:?}",
|
||||
err, last_compaction_slot, highest_compaction_slot,
|
||||
);
|
||||
}
|
||||
*last_compaction_slot = highest_compaction_slot;
|
||||
let jitter = max_jitter.unwrap_or(0);
|
||||
if jitter > 0 {
|
||||
*compaction_jitter = thread_rng().gen_range(0, jitter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn report_disk_metrics(
|
||||
pre: BlockstoreResult<u64>,
|
||||
post: BlockstoreResult<u64>,
|
||||
@@ -285,8 +240,7 @@ impl LedgerCleanupService {
|
||||
}
|
||||
|
||||
pub fn join(self) -> thread::Result<()> {
|
||||
self.t_cleanup.join()?;
|
||||
self.t_compact.join()
|
||||
self.t_cleanup.join()
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
@@ -297,7 +251,7 @@ mod tests {
|
||||
use std::sync::mpsc::channel;
|
||||
|
||||
#[test]
|
||||
fn test_cleanup1() {
|
||||
fn test_cleanup() {
|
||||
solana_logger::setup();
|
||||
let blockstore_path = get_tmp_ledger_path!();
|
||||
let blockstore = Blockstore::open(&blockstore_path).unwrap();
|
||||
@@ -308,7 +262,7 @@ mod tests {
|
||||
|
||||
//send a signal to kill all but 5 shreds, which will be in the newest slots
|
||||
let mut last_purge_slot = 0;
|
||||
let highest_compaction_slot = Arc::new(AtomicU64::new(0));
|
||||
let mut last_compaction_slot = 0;
|
||||
sender.send(50).unwrap();
|
||||
LedgerCleanupService::cleanup_ledger(
|
||||
&receiver,
|
||||
@@ -316,11 +270,10 @@ mod tests {
|
||||
5,
|
||||
&mut last_purge_slot,
|
||||
10,
|
||||
&highest_compaction_slot,
|
||||
&mut last_compaction_slot,
|
||||
10,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(last_purge_slot, 50);
|
||||
assert_eq!(highest_compaction_slot.load(Ordering::Relaxed), 44);
|
||||
|
||||
//check that 0-40 don't exist
|
||||
blockstore
|
||||
@@ -328,18 +281,6 @@ mod tests {
|
||||
.unwrap()
|
||||
.for_each(|(slot, _)| assert!(slot > 40));
|
||||
|
||||
let mut last_compaction_slot = 0;
|
||||
let mut jitter = 0;
|
||||
LedgerCleanupService::compact_ledger(
|
||||
&blockstore,
|
||||
&mut last_compaction_slot,
|
||||
10,
|
||||
&highest_compaction_slot,
|
||||
&mut jitter,
|
||||
None,
|
||||
);
|
||||
assert_eq!(jitter, 0);
|
||||
|
||||
drop(blockstore);
|
||||
Blockstore::destroy(&blockstore_path).expect("Expected successful database destruction");
|
||||
}
|
||||
@@ -362,7 +303,7 @@ mod tests {
|
||||
info!("{}", first_insert);
|
||||
|
||||
let mut last_purge_slot = 0;
|
||||
let last_compaction_slot = Arc::new(AtomicU64::new(0));
|
||||
let mut last_compaction_slot = 0;
|
||||
let mut slot = initial_slots;
|
||||
let mut num_slots = 6;
|
||||
for _ in 0..5 {
|
||||
@@ -386,7 +327,8 @@ mod tests {
|
||||
initial_slots,
|
||||
&mut last_purge_slot,
|
||||
10,
|
||||
&last_compaction_slot,
|
||||
&mut last_compaction_slot,
|
||||
10,
|
||||
)
|
||||
.unwrap();
|
||||
time.stop();
|
||||
|
@@ -1,5 +1,4 @@
|
||||
#![cfg_attr(RUSTC_WITH_SPECIALIZATION, feature(specialization))]
|
||||
#![allow(clippy::integer_arithmetic)]
|
||||
//! The `solana` library implements the Solana high-performance blockchain architecture.
|
||||
//! It includes a full Rust implementation of the architecture (see
|
||||
//! [Validator](server/struct.Validator.html)) as well as hooks to GPU implementations of its most
|
||||
@@ -16,7 +15,6 @@ pub mod cluster_info_vote_listener;
|
||||
pub mod commitment_service;
|
||||
pub mod completed_data_sets_service;
|
||||
mod deprecated;
|
||||
pub mod max_slots;
|
||||
pub mod sample_performance_service;
|
||||
pub mod shred_fetch_stage;
|
||||
#[macro_use]
|
||||
@@ -58,7 +56,6 @@ mod result;
|
||||
pub mod retransmit_stage;
|
||||
pub mod rewards_recorder_service;
|
||||
pub mod rpc;
|
||||
pub mod rpc_completed_slots_service;
|
||||
pub mod rpc_health;
|
||||
pub mod rpc_pubsub;
|
||||
pub mod rpc_pubsub_service;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user