Compare commits

...

254 Commits

Author SHA1 Message Date
mergify[bot]
85bbcdad9a Cli: Support checked stake and vote operations (#18449) (#18705)
* Refactor VoteAuthorize to use SignerIndex and support vote-authorize-*-checked

* Add checked bool const and use in command parsing

* Add create-stake-account-checked handling

* Add stake-set-lockup-checked handling

* Remove unnecessary mut

* Add stake-authorized-checked handling

(cherry picked from commit aeb30fa873)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-07-16 09:42:29 -06:00
Trent Nelson
336c1c1d37 nonce: Unify NonceError with SystemError 2021-07-16 04:41:51 -06:00
mergify[bot]
1570afe493 Cli configurable validators (backport #18630) (#18666)
* rpc: more params for `GetVoteAccountsConfig`

(cherry picked from commit bf90ea282a)

* cli: allow returning more `solana validators`

(cherry picked from commit a4a24b6531)

# Conflicts:
#	Cargo.lock

Co-authored-by: Trent Nelson <trent@solana.com>
2021-07-16 09:49:13 +00:00
mergify[bot]
c7c650fccc Gate libsecp256k1 update (backport #18656) (#18701)
* hijack secp256k1 enablement feature plumbing for libsecp256k1 upgrade

* bump libsecp256k1 to v0.5.0

* gate libsecp256k1 upgrade to v0.5.0

* ci: allow clippy::inconsistent_struct_constructor

Co-authored-by: Trent Nelson <trent@solana.com>
2021-07-16 07:38:45 +00:00
Michael Vines
9b7fba69f4 Remove tour-de-sol.md link 2021-07-15 21:55:04 -07:00
Michael Vines
0bd355c166 Remove zh tds pages 2021-07-15 21:08:35 -07:00
Michael Vines
0d8c8d013d Remove Tour de SOL more
(cherry picked from commit c03490b24a)
2021-07-15 21:07:11 -07:00
Michael Vines
7a57f7b8cc Remove tour-de-sol/
(cherry picked from commit 5fe0350c2e)
2021-07-15 20:17:54 -07:00
Michael Vines
940c4731c4 Align vote-account output with stake-account output 2021-07-15 08:16:17 -07:00
Kirill Fomichev
4332f0ca05 Replace get_clock by get_sysvar in BanksClient 2021-07-14 18:06:27 -07:00
Kirill Fomichev
e0e6e20e02 Add method id to SysbarId trait (#18604)
(cherry picked from commit 5cea25ac3e)
2021-07-14 17:51:54 -07:00
Michael Vines
0fdaa1438e Drop default_on_eof attribute from Reward struct
(cherry picked from commit 33718e5fb4)
2021-07-14 12:37:38 -07:00
mergify[bot]
8a111229f7 Cleanup secp256k1 deps (backport #18618) (#18625)
* Cleanup secp256k1 deps (#18618)

(cherry picked from commit 0d3e8ada94)

# Conflicts:
#	Cargo.lock
#	programs/bpf/Cargo.lock
#	programs/secp256k1/Cargo.toml
#	runtime/Cargo.toml

* resolve conflicts

Co-authored-by: Jack May <jack@solana.com>
2021-07-13 08:35:50 +00:00
mergify[bot]
e3eb9c195a Cli: expose last valid block height (#18620) (#18627)
* Add Fees struct to client

* Add complete RpcClient::get_fees methods

* Switch cli to last_valid_block_height

(cherry picked from commit 8ad4ffdee5)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-07-13 06:34:31 +00:00
mergify[bot]
8dd5ec6fbd refactor(rpc_client): const number of retries for send and confirm transaction (#18390) (#18427)
* refactor(rpc_client): simplfy send and confirm transaction flow

* chore: configurable num of retries for send and confirm

(cherry picked from commit 70234dfdf4)

Co-authored-by: hrls <viktor.kharitonovich@gmail.com>
2021-07-13 06:28:11 +00:00
Michael Vines
e5d60bc56d Record vote account commission with voting/staking rewards and surface in RPC
(cherry picked from commit 4098af3b5b)
2021-07-12 17:07:07 -07:00
mergify[bot]
cba97d576a serial insertion of bins into accounts index (#18469) (#18617)
(cherry picked from commit f5ff4b2058)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-07-12 22:26:29 +00:00
mergify[bot]
0670213365 Unignore spl downstream build (#18218) (#18611)
(cherry picked from commit 072a7761e1)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-07-12 20:12:39 +00:00
mergify[bot]
ed5c11b3aa Update neon program id (backport #18607) (#18608)
* Update neon program id (#18607)

(cherry picked from commit 00f7e514b8)

* fix test

Co-authored-by: Jack May <jack@solana.com>
2021-07-12 11:18:29 -07:00
mergify[bot]
4b8c4704b0 Fix typo (#18595) (#18596)
(cherry picked from commit ab45532b52)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-07-12 08:02:06 +00:00
mergify[bot]
c5374095e6 storage-proto: Rework source generation (#18584)
(cherry picked from commit 899b09872b)

Co-authored-by: Trent Nelson <trent@solana.com>
2021-07-11 03:33:56 +00:00
mergify[bot]
6abd5fbc3e removes redundant (mutable) self receivers (backport #18574) (#18580)
* removes redundant (mutable) self receivers (#18574)

(cherry picked from commit 918b5c28b2)

# Conflicts:
#	gossip/src/cluster_info.rs

* removes backport merge conflicts

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2021-07-11 01:02:20 +00:00
mergify[bot]
a6a302f41f chore: bump ouroboros from 0.5.1 to 0.9.3 (#18189) (#18573)
* chore: bump ouroboros from 0.5.1 to 0.9.3

Bumps [ouroboros](https://github.com/joshua-maros/ouroboros) from 0.5.1 to 0.9.3.
- [Release notes](https://github.com/joshua-maros/ouroboros/releases)
- [Commits](https://github.com/joshua-maros/ouroboros/commits)

---
updated-dependencies:
- dependency-name: ouroboros
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* [auto-commit] Update all Cargo lock files

* Api changes

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: dependabot-buildkite <dependabot-buildkite@noreply.solana.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: dependabot-buildkite <dependabot-buildkite@noreply.solana.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2021-07-10 20:44:54 +00:00
mergify[bot]
4c764829da CI: Make BPF test suite a first-class citizen (backport #18535) (#18571)
* CI: Extricate BPF tests from stable-perf

(cherry picked from commit 1eab0773af)

* CI: Dump BPF assembly listings and upload as artifact

(cherry picked from commit f1996ca0f3)

Co-authored-by: Trent Nelson <trent@solana.com>
2021-07-10 19:24:23 +00:00
mergify[bot]
9900c1ad8a adds a generic implementation of Gossip{Read,Write}Lock (#18559) (#18569)
(cherry picked from commit fd9c10c2e2)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2021-07-10 15:51:29 +00:00
mergify[bot]
3fabbab417 Add storage-proto build.rs and readme (backport #18353) (#18562)
* Add storage-proto build.rs and readme (#18353)

* Use build.rs for storage-proto generation

* Add readme

* Single use statements

(cherry picked from commit c2e7d39154)

# Conflicts:
#	Cargo.lock
#	storage-proto/build-proto/Cargo.lock
#	storage-proto/build-proto/Cargo.toml

* Fix conflicts

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2021-07-09 22:21:08 +00:00
mergify[bot]
6c99c1ae13 Bump prost, prost-types, and tonic (backport #18537) (#18558)
* Bump prost, prost-types, and tonic (#18537)

* Bump prost+tonic and accommodate generated service changes

* Unignore advisory

* Fixup .proto error list

(cherry picked from commit 761de8b1a3)

# Conflicts:
#	Cargo.lock
#	storage-bigtable/Cargo.toml
#	storage-proto/Cargo.toml

* Fix conflicts

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2021-07-09 19:54:29 +00:00
Ryo Onodera
c2320fceab Remove sysvar special cases for rent and assign 2021-07-10 01:24:44 +09:00
mergify[bot]
8b87d86358 Bump compute budget for neon evm (#17700) (#18522)
(cherry picked from commit 2867584985)

Co-authored-by: Jack May <jack@solana.com>
2021-07-09 09:12:31 -07:00
mergify[bot]
5d0e1d62d5 removes id and shred_version from CrdsGossip (backport #18505) (#18554)
* removes id and shred_version from CrdsGossip (#18505)

ClusterInfo is the gateway to CrdsGossip function calls, and it already
has node's pubkey and shred version (full ContactInfo and Keypair in
fact).
Duplicating these data in CrdsGossip adds redundancy and possibility for
bugs should they not be consistent with ClusterInfo.

(cherry picked from commit 4e1333fbe6)

# Conflicts:
#	gossip/src/cluster_info.rs

* removes backport merge conflicts

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2021-07-09 15:37:05 +00:00
mergify[bot]
7e613c7a78 skips process_push_message for local messages (backport #18493) (#18532)
* skips process_push_message for local messages (#18493)

received_cache is not relevant for local messages, and does not need to
be updated:
https://github.com/solana-labs/solana/blob/92c5cdab6/gossip/src/crds_gossip_push.rs#L166-L189

(cherry picked from commit 27cc7577a1)

# Conflicts:
#	gossip/src/cluster_info.rs

* removes backport merge conflicts

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2021-07-09 13:33:40 +00:00
mergify[bot]
2a30436e45 Show grcov version as well (#18549) (#18551)
(cherry picked from commit a5b91ef4c3)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2021-07-09 12:41:44 +00:00
mergify[bot]
5315a89e7d Remove dead solana airdrop parameters (#18520) (#18524)
(cherry picked from commit f39ffa69f6)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-07-08 21:56:25 +00:00
mergify[bot]
8c328316ae Add ecrecover syscall (backport #17720) (#18500)
* Add ecrecover syscall (#17720)

Co-authored-by: Anton Lisanin <lisanin.anton@gmail.com>
(cherry picked from commit 1f288ce527)

# Conflicts:
#	Cargo.lock
#	programs/bpf/Cargo.lock
#	programs/bpf/tests/programs.rs
#	programs/bpf_loader/Cargo.toml
#	programs/bpf_loader/src/syscalls.rs
#	sdk/program/Cargo.toml

* resolve conflicts

Co-authored-by: s-medvedev <40623263+s-medvedev@users.noreply.github.com>
Co-authored-by: Jack May <jack@solana.com>
2021-07-08 21:56:19 +00:00
mergify[bot]
030a97d098 Temporarily ignore prost-types advisory (backport #18525) (#18527)
* Temporarily ignore prost-types audit (#18525)

(cherry picked from commit 6188283ba6)

* Bump tokio

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2021-07-08 13:49:33 -06:00
mergify[bot]
c40e71dc03 featurize_policy_update (#18492) (#18502)
(cherry picked from commit ccdf93e2b8)

Co-authored-by: Jack May <jack@solana.com>
2021-07-07 22:31:05 +00:00
mergify[bot]
2f633cdfb7 Refactor verify_and_update write privileges check (#18468) (#18484)
Co-authored-by: Justin Starry <justin@solana.com>
2021-07-07 21:46:07 +00:00
mergify[bot]
edd6ae588a Update verify policy (backport #18459) (#18491)
* Update verify policy (#18459)

(cherry picked from commit 44289e6728)

* resolve conflicts

Co-authored-by: Jack May <jack@solana.com>
2021-07-07 19:02:52 +00:00
Justin Starry
2a73ba2fbb Fix cargo check (#18497) 2021-07-07 14:01:19 -05:00
mergify[bot]
5eadb86500 Fix example in docs for getInflationReward (#18494) (#18496)
(cherry picked from commit ebc7df62f3)

Co-authored-by: Max Ogden <maxogden@users.noreply.github.com>
2021-07-07 18:43:24 +00:00
mergify[bot]
c98ab6c6dc Record parent slot to reconstruct fork tree from influxdb (#18482) (#18488)
(cherry picked from commit d69f469b83)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2021-07-07 17:42:03 +00:00
mergify[bot]
5321463892 Refactoring: Unify account_deps and accounts (backport #17898) (#18486)
* Refactoring: Unify account_deps and accounts (#17898)

* Changes ThisInvokeContext::get_account() to use accounts instead of pre_accounts.

* Adds explicit keys to accounts to make them symmetric to account_deps.

* Appends account_deps to accounts in transaction loading and removes account_deps everywhere else.

(cherry picked from commit 7462c27d07)

# Conflicts:
#	program-test/src/lib.rs
#	runtime/src/bank.rs
#	runtime/src/message_processor.rs

* fix conflicts

Co-authored-by: Alexander Meißner <AlexanderMeissner@gmx.net>
Co-authored-by: Justin Starry <justin@solana.com>
2021-07-07 17:14:39 +00:00
mergify[bot]
d668a7694f implements an unbiased weighted shuffle using binary indexed tree (#18343) (#18485)
Current implementation of weighted_shuffle:
https://github.com/solana-labs/solana/blob/b08f8bd1b/gossip/src/weighted_shuffle.rs#L11-L37
uses a heuristic which results in biased samples.

For example, if the weights are [1, 10, 100], then the 3rd index should
come first 100 times more often than the 1st index. However,
weighted_shuffle is picking the 3rd index 200+ times more often than the
1st index, showing a disproportional bias in favor of higher weights.

This commit implements weighted shuffle using binary indexed tree to
maintain cumulative sum of weights while sampling. The resulting samples
are demonstrably unbiased and precisely proportional to the weights.

Additionally the iterator interface allows to skip computations when
not all indices are processed.

Of the use cases of weighted_shuffle, changing turbine code requires
feature-gating to keep the cluster in sync. That is not updated in
this commit, but can be done together with future updates to turbine.

(cherry picked from commit dba42c57b4)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2021-07-07 16:07:41 +00:00
mergify[bot]
9bb482e46f persists repair-peers cache across repair service loops (#18400) (#18483)
The repair-peers cache is reset each time repair service loop runs,
and so computed repeatedly for the same slots:
https://github.com/solana-labs/solana/blob/d2b07dca9/core/src/repair_service.rs#L275

This commit uses an LRU cache to persists repair-peers for each slot.
In addition to LRU eviction rules, in order to avoid re-using outdated
data, each entry also has 10 seconds TTL.

(cherry picked from commit a0551b4054)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2021-07-07 15:43:15 +00:00
mergify[bot]
c534c928a7 encapsulates turbine peers computations of broadcast & retransmit stages (#18238) (#18464)
Broadcast stage and retransmit stage should arrange nodes on turbine
broadcast tree in exactly same order. Additionally any changes to this
ordering (e.g. updating how unstaked nodes are handled) requires feature
gating to keep the cluster in sync.

Current implementation is scattered out over several public methods and
exposes too much of implementation details (e.g. usize indices into
peers vector) which makes code changes and checking for feature
activations more difficult.

This commit encapsulates turbine peer computations into a new struct,
and only exposes two public methods, get_broadcast_peer and
get_retransmit_peers, for call-sites.

(cherry picked from commit 04787be8b1)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2021-07-07 14:30:55 +00:00
mergify[bot]
0d0478c4a4 Add vote/stake checked instructions (backport #18345) (#18457)
* Add vote/stake checked instructions

(cherry picked from commit ee219ffa47)

* Fix set-lockup custodian index

(cherry picked from commit 544f62c92f)

* Add parsing for new stake instructions; clean up confusing test args

(cherry picked from commit 9b302ac0b5)

* Add parsing for new vote instructions

(cherry picked from commit 39bac256ab)

* Add VoteInstruction::AuthorizeChecked test

(cherry picked from commit b8ca2250fd)

* Add Stake checked tests

(cherry picked from commit 74e89a3e3e)

Co-authored-by: Michael Vines <mvines@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2021-07-07 03:17:21 +00:00
mergify[bot]
cda681a2f0 Remove obsolete stake-monitor command (backport #18020) (#18462)
* Remove obsolete stake-monitor command

(cherry picked from commit f859a39b86)

# Conflicts:
#	Cargo.lock
#	stake-monitor/Cargo.toml

* Fix conflict

Co-authored-by: Michael Vines <mvines@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2021-07-07 00:55:13 +00:00
mergify[bot]
72ed4f28b1 Remove feature switch for demoting sysvar write locks (#18373) (#18454)
Co-authored-by: Justin Starry <justin@solana.com>
2021-07-06 23:25:28 +00:00
mergify[bot]
6afeaac7a5 Fix transaction logs and inner ixs for leader nodes (backport #18395) (#18448)
* Fix transaction logs and inner ixs for leader nodes (#18395)

* Fix transaction logs and inner ixs for leader nodes

* Fix cpi log storage flag

(cherry picked from commit 5dd399dafa)

# Conflicts:
#	runtime/src/bank.rs

* resolve conflict

Co-authored-by: Justin Starry <justin@solana.com>
2021-07-06 23:04:19 +00:00
mergify[bot]
75a2b66206 docs: Correct integration validator cli reference (#18458)
(cherry picked from commit 77f61a5e2e)

Co-authored-by: Trent Nelson <trent@solana.com>
2021-07-06 22:26:38 +00:00
mergify[bot]
5966053a6d Improve account unlock performance (#18442) (#18447)
* Improve account unlock performance

* fix clippy

(cherry picked from commit 6319e8811a)

Co-authored-by: Justin Starry <justin@solana.com>
2021-07-06 21:40:04 +00:00
mergify[bot]
e500b79858 Update terminology.md (#18430) (#18431)
* Update terminology.md

Fix duplicate "holders"

* Update docs/src/inflation/terminology.md

Co-authored-by: Michael Vines <mvines@gmail.com>
(cherry picked from commit b887e89972)

Co-authored-by: Sean Michael <seankmichael@gmail.com>
2021-07-06 03:13:50 +00:00
mergify[bot]
428c20c79f borsh: add bool type (#18429)
(cherry picked from commit e7b36c8484)

Co-authored-by: Michael Vines <mvines@gmail.com>
2021-07-05 21:48:10 +00:00
mergify[bot]
bf84bc17ea Cli: expose --with-memo to nonce and stake commands (#18404) (#18410)
* Fmt memo_arg

* Add --with-memo to nonce and stake cli commands

(cherry picked from commit 1dd730d685)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-07-05 01:19:17 +00:00
mergify[bot]
30fa9cbee7 sdk: add is_interactive() method Signer trait (#18407)
(cherry picked from commit 2af5ec4f57)

Co-authored-by: Trent Nelson <trent@solana.com>
2021-07-03 09:09:39 +00:00
Michael Vines
eefca613ad Rename Tower::lockouts to Tower::vote_state
(cherry picked from commit d5c2c72360)
2021-07-02 20:35:02 -07:00
mergify[bot]
f6d943aec7 SDK: Add test for illegal Pubkey::create_with_seed owners (#18402)
(cherry picked from commit 216983c50e)

Co-authored-by: Trent Nelson <trent@solana.com>
2021-07-03 01:27:53 +00:00
mergify[bot]
88462e67b5 Allow a solana-validator to join a cluster established by solana-test-validator (backport #18394) (#18399)
* Report 404 instead of 502 when a snapshot file is not found

This provides the client with more useful information about why their
request failed

(cherry picked from commit 40d696fcbc)

* As a last resort try to download an uncompressed snapshot

`solana-test-validator` creates uncompressed snapshots and it can be
useful to attach another validator to a `solana-test-validator` cluster
from time to time

(cherry picked from commit e36247d187)

Co-authored-by: Michael Vines <mvines@gmail.com>
2021-07-03 00:27:42 +00:00
mergify[bot]
eb683dd402 Document order of recent blockhashes sysvar (#18379)
I wanted to use this sysvar to get a recent block hash, but I didn't
know whether the first or the last entry contains the most recent block
hash.

By calling it for mainnet, printing the results, and comparing that to
the recent blocks on solanabeach.io/blocks, I discovered that the
entries are ordered from most recent to least recent. Document this to
save future readers the trouble.

(cherry picked from commit 94ab0eb49f)

Co-authored-by: Ruud van Asseldonk <ruud@chorus.one>
2021-07-02 07:03:04 +00:00
mergify[bot]
c7d0aea5f4 Make set roots an iterator (#18357) (#18377)
(cherry picked from commit 0eca92de18)

Co-authored-by: carllin <carl@solana.com>
2021-07-02 04:44:38 +00:00
Tiago Carvalho
8f08953100 Added formats to notifications in WebSocket RPC client. (#18231)
(cherry picked from commit 8e7d393b70)
2021-07-01 21:14:49 -07:00
Lijun Wang
09b009abd9 Removed unused purged_slots in purge_older_root_entries (#18102)
Removed unused purged_slots in purge_older_root_entries

(cherry picked from commit ccf6b21bf8)
2021-07-01 21:13:02 -07:00
mergify[bot]
c37e481a43 test (#18159) (#18289)
(cherry picked from commit 47cafb70da)

Co-authored-by: carllin <carl@solana.com>
2021-07-02 04:12:53 +00:00
sakridge
72cf55b8c3 More detailed voting timings in replay stage (#18229)
(cherry picked from commit 5d08bf9aa3)
2021-07-01 21:12:11 -07:00
mergify[bot]
6cb24ae7b6 Docs expose Transaction v2 proposal (backport #18283) (#18286)
* docs: expose TX v2 proposal

(cherry picked from commit 0972d12c1c)

* docs: A-Z sort accepted proposals

(cherry picked from commit 70d75ce4e8)

Co-authored-by: Trent Nelson <trent@solana.com>
2021-07-02 03:07:26 +00:00
mergify[bot]
eeaf0234f0 Add proposal for transactions v2 and address map program (#17103) (#18376)
* Add proposal for supporting big transactions

* account index program

* fix formatting

* review feedback

* Add cost changes section

* Add cost section and more attack details

* fix lint

* document metadata changes

* nit

* rpc details

* add index meta struct

* add additional proposal and chagne title

* rename proposal file

* rename to address map and rewrite tx format

* no more appends, limit mapping size to 256

* update dos section

* add note about readonly

* restructure message to use enum

* cleanup

(cherry picked from commit 191519188d)

Co-authored-by: Justin Starry <justin@solana.com>
2021-07-02 02:39:44 +00:00
Jack May
0200740d70 Add sdk and program READMEs (#18154)
(cherry picked from commit 94905dae52)
2021-07-01 18:25:04 -07:00
Trent Nelson
1268eef3b2 Revert "Clean up build warning"
This reverts commit 17a173ebb5.

(cherry picked from commit d269975784)
2021-07-01 18:23:23 -07:00
mergify[bot]
b433048003 report on min/max bin stats for accounts index (#18338) (#18347)
(cherry picked from commit 531f1bce78)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-07-01 23:55:43 +00:00
mergify[bot]
daf2c3c155 Consider all peers as potential candidates during pull-request in case of offline nodes (#18333) (#18372)
* Try all peers during pull-request in case of offline nodes

* fix clippy err

(cherry picked from commit f4fb5de545)

Co-authored-by: Ashwin Sekar <ashwin@solana.com>
2021-07-01 22:56:20 +00:00
mergify[bot]
03d213d764 Reject transactions with extra signatures (#18306) (#18370)
* Reject transactions with extra signatures

* fix tests

* fix check

* fix check

* tx method

* fix checks

(cherry picked from commit d5961e9d9f)

Co-authored-by: Justin Starry <justin@solana.com>
2021-07-01 20:07:43 +00:00
rmshea
99f9481b5d Remove hackathon banner 2021-07-01 13:01:48 -07:00
mergify[bot]
10bd14bca6 Remove feature switch for using message hash for already processed check (#18340) (#18367)
(cherry picked from commit 5ca975383c)

Co-authored-by: Justin Starry <justin@solana.com>
2021-07-01 17:39:22 +00:00
mergify[bot]
d26533e370 Cleanup ReplayStage tests (#18241) (#18292)
(cherry picked from commit 68c87469c3)

Co-authored-by: carllin <carl@solana.com>
2021-07-01 07:47:02 +00:00
mergify[bot]
820abacf49 Update notification format info to be consistent (#18354) (#18356)
(cherry picked from commit cc80197349)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-07-01 06:14:03 +00:00
Trent Nelson
4466aa39c4 Bump version to v1.7.5 2021-06-30 22:55:01 -06:00
Michael Vines
f4e43731ef solana validators output now includes average skip rate
(cherry picked from commit 52290dbd35)
2021-06-30 17:47:49 -07:00
mergify[bot]
884ef211f7 Document slotsUpdates (#18335) (#18339)
* Add slotsUpdates to jsonrpc docs

* Re-add unstable section

(cherry picked from commit dfb6296499)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-07-01 00:25:24 +00:00
mergify[bot]
896ef5a15f Fixed an issue doing the set_roots repeatedly for the same set. Instead doing the per chunks. (#18314) (#18337)
(cherry picked from commit a67d26a1e8)

Co-authored-by: Lijun Wang <83639177+lijunwangs@users.noreply.github.com>
2021-07-01 00:12:33 +00:00
mergify[bot]
4ff482cd47 Fail simulation if transaction contains duplicate accounts (#18304) (#18334)
(cherry picked from commit b08f8bd1b0)

Co-authored-by: Justin Starry <justin@solana.com>
2021-06-30 21:35:56 +00:00
mergify[bot]
6e27360fbc Add repair number per slot (#18082) (#18329)
(cherry picked from commit 8d9a6deda4)

Co-authored-by: sakridge <sakridge@gmail.com>
2021-06-30 20:39:05 +00:00
mergify[bot]
a10a0824eb refactor untar_snapshot_in to push parallelism deeper for further refactoring (#18310) (#18312)
(cherry picked from commit ce53b84cdc)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-30 17:59:57 +00:00
mergify[bot]
2fdda2ec1b test-validator: hold rent constant with --slots-per-epoch (#18318)
(cherry picked from commit 02b14caa5f)

Co-authored-by: Trent Nelson <trent@solana.com>
2021-06-30 08:37:54 +00:00
mergify[bot]
57f76a2111 Use timeout to allow RpcClient to retry initial transaction confirmation (#18311) (#18316)
* Tidying: relocate function

* Use proper helper method for RpcClient commitment

* Add RpcClientConfig

* Add configurable confirm_transaction_initial_timeout

* Use default 5s timeout for initial tx confirmation

(cherry picked from commit 9d4428d3d8)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-06-30 03:09:47 +00:00
mergify[bot]
287daa9248 debug logs when crds table trim failed (#18307) (#18309)
reports of this error being possibly spammy:
https://discord.com/channels/428295358100013066/689412830075551748/859441080054710293

The commit changes the log level to debug.
Additionally adding a new metric to understand the frequency of this error.

(cherry picked from commit 9d983a34a0)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2021-06-29 22:43:33 +00:00
mergify[bot]
7e40a051a4 bin accounts index (#18111) (#18280)
Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-29 09:14:10 -05:00
mergify[bot]
bd0a7730b6 tx-status: Don't assume a memo instruction succeeded (#18288)
(cherry picked from commit 7babf28ef7)

Co-authored-by: Trent Nelson <trent@solana.com>
2021-06-29 03:37:44 +00:00
mergify[bot]
0d1c87e650 Cli epoch-info: generate epoch-completed time from block times (#18258) (#18285)
* Generate epoch-completed time from block times

* Add annotation when block times not available

(cherry picked from commit f2b0d562b0)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-06-28 17:54:20 -06:00
mergify[bot]
56cd963fd7 Ignore flaky tests (#18278) (#18282)
(cherry picked from commit 639a61d639)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-06-28 23:13:25 +00:00
mergify[bot]
d1b26cb267 reduce pubkey copies on insert path (#18240) (#18276)
(cherry picked from commit 1f1e54b9d8)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-28 16:31:48 -05:00
Jack May
6a0a03415c Don't update if already an executable
(cherry picked from commit 2fbedd834f)
2021-06-27 12:24:00 -06:00
Michael Vines
41d50adbf3 Only print Foundation inflation rate if it is greater than 0
(cherry picked from commit b44af11511)
2021-06-26 14:57:03 -07:00
mergify[bot]
328b52c4d6 Zelcore wallet support for SOL/SPLs info added (#17580) (#18249)
* Zelcore wallet support for SOL/SPLs info added

I have added a quick infodump about Zelcore wallet supporting various features of the Solana ecosystem. I looked at previous commits and it seemed adding to the top of the file was the norm, but please feel free to move around if needed.

* Zelcore wallet info moved to bottom of file.

(cherry picked from commit 8fc4c2f375)

Co-authored-by: trial123Zel <38964930+trial123Zel@users.noreply.github.com>
2021-06-26 17:22:55 +00:00
mergify[bot]
b7d04cf7b9 replace account index bulk insert with iterator (#18198) (#18235)
(cherry picked from commit e06376664b)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-25 17:55:42 -05:00
Tyera Eulberg
7ed2cf30a5 Bump borsh from 0.8.1 to 0.9.0 (#18230) 2021-06-25 14:43:04 -06:00
mergify[bot]
78fe5576a9 Added notes to running validator documentation to ensure that 'exec' is used from scripts when starting the validator to prevent logrotate signals from killing the validator. (#18234) (#18236)
(cherry picked from commit c045f1dfb5)

Co-authored-by: bji <bryan@ischo.com>
2021-06-25 20:36:41 +00:00
mergify[bot]
41179b1282 metric for accounts index insertion time (#18202) (#18226)
(cherry picked from commit f2a2581259)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-25 13:36:29 -05:00
Trent Nelson
f82d99b4c2 ci: add wrapper script for running ci locally
Linux only for now

(cherry picked from commit 0bc38153ca)
2021-06-25 10:17:17 -06:00
Trent Nelson
967f0d07f2 ci: add downstream build wrapper
(cherry picked from commit 761e324982)
2021-06-25 10:17:17 -06:00
Trent Nelson
940dbe99e9 ci: give localnet nodes a more time to startup
(cherry picked from commit 278a241db3)
2021-06-25 10:17:17 -06:00
Trent Nelson
98e1c68a70 sdk: ensure ld can find criterion when running BPF tests
(cherry picked from commit 7ee39fcb0f)
2021-06-25 10:17:17 -06:00
Trent Nelson
d8e250e9b0 ci: nvidia persistence mode isn't a hard requirement
(cherry picked from commit f213e48067)
2021-06-25 10:17:17 -06:00
Trent Nelson
c8be8510ba ci: use versioned cargo wrapper for crate ordering
(cherry picked from commit 554002b73c)
2021-06-25 10:17:17 -06:00
mergify[bot]
c99460ba15 remove unnecessary copies from accounts index code paths (#18196) (#18212)
(cherry picked from commit f9fccdee85)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-25 02:23:23 +00:00
mergify[bot]
769fcb7f50 untar in parallel (#18184) (#18210)
* untar in parallel

* make enum for 'ignore' return value

(cherry picked from commit 31ec986ea1)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-24 19:35:21 -05:00
mergify[bot]
60812790e1 rework dirty_pubkeys from insert_new_if_missing_into_primary_index (#18200) (#18208)
(cherry picked from commit 4b314be5bd)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-24 17:55:45 -05:00
mergify[bot]
a01e44f3b9 Update README.md (#18191) (#18195)
(cherry picked from commit 3362ac06b5)

Co-authored-by: Hao-Cher Hong <rax333j@gmail.com>
2021-06-24 15:47:12 +00:00
mergify[bot]
0917370bd5 Update leader-rotation.md (#18192) (#18193)
Confusing wording regarding epoch length

(cherry picked from commit 6b62ba045d)

Co-authored-by: Marcel Jackisch <jackisch@protonmail.com>
2021-06-24 14:49:22 +00:00
mergify[bot]
09b612b130 add min/max bin size metrics to hash calculation (#18155) (#18181)
(cherry picked from commit 77c3ffe4cf)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-23 20:04:15 +00:00
mergify[bot]
26fdf3fb07 Add batch stats (#18096) (#18142)
(cherry picked from commit e808f34b0b)

Co-authored-by: sakridge <sakridge@gmail.com>
2021-06-23 12:43:37 -05:00
mergify[bot]
f41f3f6b51 apply 'debug_do_not_add_builtins' to feature activations (#18110) (#18150)
(cherry picked from commit fbc94d84c8)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-23 17:34:19 +00:00
mergify[bot]
677664e71d filters crds values obtained through gossip by their shred version (backport #18072) (#18175)
* filters crds values obtained through gossip by their shred version (#18072)

filter_by_shred_version does not check the shred-version of the owner of
the crds-value. It only checks the shred-version of the node which is
relaying the value:
https://github.com/solana-labs/solana/blob/5cc073420/gossip/src/cluster_info.rs#L2274-L2289

So crds-values with different shred versions can still pass through this
function as long as they are relayed by a node with matching shred
version; and so, a single node can bridge different shred values
through-out the cluster.

(cherry picked from commit 69a5f0e6cd)

# Conflicts:
#	gossip/src/cluster_info.rs

* removes backport merge conflicts

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2021-06-23 17:16:33 +00:00
mergify[bot]
a8071f1039 chore(pubkey): remove dead code (#18162)
(cherry picked from commit 755b7c7aee)

Co-authored-by: hrls <viktor.kharitonovich@gmail.com>
2021-06-23 17:07:21 +00:00
mergify[bot]
bc08351a0a capture sort time in hash calculation (#18118) (#18153)
(cherry picked from commit d3ee73e151)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-23 15:49:28 +00:00
mergify[bot]
088b3893c3 Keep track of dirty stores on remove accounts to clean (#17601) (#18173)
* Keep track of dirty stores on remove accounts to clean

and not zero_lamport key set

* Only dirty when count==0?

* Add another clean

(cherry picked from commit 3b1738c000)

Co-authored-by: sakridge <sakridge@gmail.com>
2021-06-23 10:23:44 +00:00
mergify[bot]
d9d6dd9ba6 Add metrics for rpc send-tx failures (#18156) (#18164)
(cherry picked from commit 64cff8c5a1)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-06-22 22:28:22 -06:00
mergify[bot]
aca66674d3 correctly calculate hash_total for hash/lamport calculation (#18144) (#18146)
(cherry picked from commit d5f9f3b7ce)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-22 21:21:10 +00:00
Trent Nelson
597429ab3e Bump version to v1.7.4 2021-06-22 19:57:34 +00:00
Trent Nelson
715360c1e7 ci: fix release build agent targeting 2021-06-22 13:36:34 -06:00
Trent Nelson
fcabaa7eff sdk: refactor pda generation 2021-06-22 11:36:05 -06:00
mergify[bot]
b14af989b8 Update sysvar docs (#18125) (#18147)
(cherry picked from commit 8a136736ad)

Co-authored-by: Jack May <jack@solana.com>
2021-06-22 17:28:15 +00:00
mergify[bot]
1a919e0c3e ci: isolate release builds (#18133)
(cherry picked from commit d43c6eafaf)

Co-authored-by: Trent Nelson <trent@solana.com>
2021-06-22 06:14:59 +00:00
mergify[bot]
9c1a6bed7b Fix flaky optimistic violation detection cluster test (#18027) (#18126)
* Fix flaky optimistic violation detection cluster test

* Add small sleep to avoid tight loop

(cherry picked from commit 423e0d90ee)

Co-authored-by: Ashwin Sekar <ashwin@solana.com>
2021-06-22 05:07:33 +00:00
mergify[bot]
c48ec02f42 Docs val net reqs (backport #18122) (#18130)
* docs: don't suggest cloud instances for validators

(cherry picked from commit cf187dcb0b)

* docs: flesh out validator network requirements

(cherry picked from commit 93dd965947)

Co-authored-by: Trent Nelson <trent@solana.com>
2021-06-22 02:12:49 +00:00
mergify[bot]
6f376489a5 Handle removing slots during account scans (#17471) (#17953)
(cherry picked from commit ccc013e134)

Co-authored-by: carllin <carl@solana.com>
2021-06-22 00:59:12 +00:00
mergify[bot]
0cbf7bef1e Move stake_weighted_timestamp module (#18114) (#18120)
* Move timestamp module into runtime

* Less public

* Remove unused enum

(cherry picked from commit 19fe1dd463)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-06-22 00:11:12 +00:00
mergify[bot]
0f87e598f6 refactor generate_index_for_slot (#17984) (#18116)
(cherry picked from commit 2087f5da94)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-21 18:25:56 -05:00
mergify[bot]
363b75619f adds shred-version to ip-echo-server response (backport #18066) (#18113)
* adds shred-version to ip-echo-server response

When starting a validator, the node initially joins gossip with
shred_verison = 0, until it adopts the entrypoint's shred-version:
https://github.com/solana-labs/solana/blob/9b182f408/validator/src/main.rs#L417

Depending on the load on the entrypoint, this adopting entrypoint
shred-version through gossip sometimes becomes very slow, and causes
several problems in gossip because we have to partially support
shred_version == 0 which is a source of leaking crds values from one
cluster to another. e.g. see
https://github.com/solana-labs/solana/pull/17899
and the other linked issues there.

In order to remove shred_version == 0 from gossip, this commit adds
shred-version to ip-echo-server response. Once the entrypoints are
updated, on validator start-up, if --expected_shred_version is not
specified we will obtain shred-version from the entrypoint using
ip-echo-server.

(cherry picked from commit 598093b5db)

# Conflicts:
#	Cargo.lock
#	net-utils/Cargo.toml
#	programs/bpf/Cargo.lock

* removes backport merge conflicts

* obtains shred-version from entrypoint's ip-echo-server in validator-main

(cherry picked from commit 58e115275a)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2021-06-21 23:04:08 +00:00
mergify[bot]
090c801cc6 eliminate flatten and sort in hash calculation (#17802) (#18115)
* eliminate flatten and sort in hash calculation

* reduce critical section time

* remove now no-longer necessary test code

* conflict with reset bins to 0 pr

(cherry picked from commit bf97627021)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-21 17:47:47 -05:00
mergify[bot]
b711778d4a More nightly clippy fixes (#18081)
(cherry picked from commit 1b7f6d2bc0)

Co-authored-by: Michael Vines <mvines@gmail.com>
2021-06-21 21:55:42 +00:00
mergify[bot]
d52569d66f user process.accounts_db_test_hash_calculation for debug_verify hash (#18053) (#18109)
(cherry picked from commit ec2f930475)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-21 16:15:13 -05:00
mergify[bot]
c676b7a473 refactor reconstruct_accountsdb_from_fields (#17987) (#18100)
(cherry picked from commit ae82e4e25b)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-21 15:14:45 -05:00
mergify[bot]
71c49bc8cd Add logging when RpcHealthStatus::Unknown (#18098) (#18107)
(cherry picked from commit faecc41603)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-06-21 13:53:25 -06:00
mergify[bot]
97a7f747fb remove unused code (#17981) (#18048)
(cherry picked from commit 20e714b3d0)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-21 10:12:50 -05:00
mergify[bot]
cef9e0de0c generate_index timings (#17983) (#18049)
(cherry picked from commit 1feaaf009d)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-21 10:12:27 -05:00
mergify[bot]
5637acb799 Add additional subscription metrics (#18071) (#18076)
(cherry picked from commit 83a6c669a5)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-06-19 04:51:40 +00:00
Michael Vines
3d3bdcb966 Drop Error suffix from enum values to avoid the enum_variant_names clippy lint
(cherry picked from commit 4a12c715a3)
2021-06-18 19:59:20 -07:00
mergify[bot]
c9f02ae020 fix loader instruction checker (#18047) (#18074)
(cherry picked from commit d18e02ef44)

Co-authored-by: Jack May <jack@solana.com>
2021-06-18 21:28:29 +00:00
mergify[bot]
0e7512a225 Fix Nightly Clippy Warnings (backport #18065) (#18070)
* chore: cargo +nightly clippy --fix -Z unstable-options

(cherry picked from commit 6514096a67)

# Conflicts:
#	core/src/banking_stage.rs
#	core/src/cost_model.rs
#	core/src/cost_tracker.rs
#	core/src/execute_cost_table.rs
#	core/src/replay_stage.rs
#	core/src/tvu.rs
#	ledger-tool/src/main.rs
#	programs/bpf_loader/build.rs
#	rbpf-cli/src/main.rs
#	sdk/cargo-build-bpf/src/main.rs
#	sdk/cargo-test-bpf/src/main.rs
#	sdk/src/secp256k1_instruction.rs

* chore: cargo fmt

(cherry picked from commit 789f33e8db)

* Updates BPF program assert_instruction_count tests.

(cherry picked from commit c1e03f3410)

# Conflicts:
#	programs/bpf/tests/programs.rs

* Resolve conflicts

Co-authored-by: Alexander Meißner <AlexanderMeissner@gmx.net>
Co-authored-by: Michael Vines <mvines@gmail.com>
2021-06-18 20:02:48 +00:00
mergify[bot]
c330016109 adds mapping from nodes pubkeys to their shred-version (#17940) (#18067)
Crds values of nodes with different shred versions are creeping into
gossip table resulting in runtime issues as the one addressed in:
https://github.com/solana-labs/solana/pull/17899

This commit works towards enforcing more checks and filtering based on
shred version by adding necessary mapping and api to gossip table.
Once populated, pubkey->shred-version mapping persists as long as there
are any values associated with the pubkey.

(cherry picked from commit 5a99fa3790)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2021-06-18 18:02:23 +00:00
Michael Vines
cb13cdec85 Fix build issues downstream due to stake state shuffle
(cherry picked from commit 810fce1f3d)
2021-06-18 07:13:40 -07:00
mergify[bot]
c65c580b20 introduce LockMapType for accounts_index (#18021) (#18051)
(cherry picked from commit 0a81c37fce)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-18 01:39:27 +00:00
Stephen Akridge
d159ae9342 Bump version to v1.7.3 2021-06-17 15:34:50 -06:00
mergify[bot]
a540af1ca7 refactor rebuild_bank_from_snapshots (backport #17988) (#18006)
* refactor rebuild_bank_from_snapshots (#17988)

(cherry picked from commit bb7413c9f9)

# Conflicts:
#	runtime/src/snapshot_utils.rs

* merge errors

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
Co-authored-by: Jeff Washington (jwash) <wash678@gmail.com>
2021-06-17 19:26:10 +00:00
mergify[bot]
e9c234d89f Make account shrink configurable #17544 (backport #17778) (#18013)
* Make account shrink configurable #17544 (#17778)

1. Added both options for measuring space usage using total accounts usage and for individual store shrink ratio using an enum. Validator CLI options: --accounts-shrink-optimize-total-space and --accounts-shrink-ratio
2. Added code for selecting candidates based on total usage in a separate function select_candidates_by_total_usage
3. Added unit tests for the new functions added
4. The default implementations is kept at 0.8 shrink ratio with --accounts-shrink-optimize-total-space set to true

Fixes #17544

(cherry picked from commit 269d995832)

# Conflicts:
#	core/tests/snapshots.rs
#	ledger/src/bank_forks_utils.rs
#	runtime/src/accounts_db.rs
#	runtime/src/snapshot_utils.rs

* fix some merge errors

Co-authored-by: Lijun Wang <83639177+lijunwangs@users.noreply.github.com>
Co-authored-by: Jeff Washington (jwash) <wash678@gmail.com>
2021-06-17 17:32:03 +00:00
mergify[bot]
b472dac6b3 validator: expose max active pubsub subscriptions to CLI (#18036)
(cherry picked from commit 5efc48fc69)

Co-authored-by: Trent Nelson <trent@solana.com>
2021-06-17 08:02:20 +00:00
mergify[bot]
523dac1be3 metrics: Don't unwrap client instantiation errors (#18019)
(cherry picked from commit 5cc073420a)

Co-authored-by: Trent Nelson <trent@solana.com>
2021-06-17 07:08:36 +00:00
mergify[bot]
962a2126b5 Log more info on runtime account verification errors (#17861) (#17879)
(cherry picked from commit a1fab0c5ca)

Co-authored-by: Jack May <jack@solana.com>
2021-06-17 01:55:37 +00:00
mergify[bot]
5c495ad1b0 ledger tool limit_load_slot_count_from_snapshot avoids assert failures (backport #17974) (#18008)
* ledger tool limit_load_slot_count_from_snapshot avoids assert failures (#17974)

(cherry picked from commit dbd4dc04b0)

# Conflicts:
#	core/tests/snapshots.rs
#	ledger/src/blockstore_processor.rs
#	runtime/benches/accounts.rs
#	runtime/src/bank.rs

* fix merge errors

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
Co-authored-by: Jeff Washington (jwash) <wash678@gmail.com>
2021-06-17 01:32:50 +00:00
mergify[bot]
f633f34e43 Clean up remove_all_authorized_voters() (#18029)
(cherry picked from commit f1ebbbab8f)

Co-authored-by: Michael Vines <mvines@gmail.com>
2021-06-17 00:28:41 +00:00
mergify[bot]
bacf1b9acc accounts-cluster-bench, close all accounts in a sliding window (#17993) (#18026)
(cherry picked from commit ed18add7d3)

Co-authored-by: carllin <carl@solana.com>
2021-06-16 23:53:31 +00:00
mergify[bot]
4df9da5c48 validator: run poh speed test earlier in start up (#18024)
(cherry picked from commit 5bc6c89adc)

Co-authored-by: Trent Nelson <trent@solana.com>
2021-06-16 23:28:15 +00:00
mergify[bot]
30bbc1350d Startup optimization in shrink - don't shrink non-shrinkable slots (backport #17405) (#17792)
* Skip shrink when it doesn't save anything (#17405)

(cherry picked from commit 14c52ab018)

# Conflicts:
#	runtime/src/accounts_db.rs

* fix merge error

Co-authored-by: sakridge <sakridge@gmail.com>
Co-authored-by: Jeff Washington (jwash) <wash678@gmail.com>
2021-06-16 23:28:03 +00:00
mergify[bot]
2f0f1fd5f5 break out generate index reporting (#17980) (#18015)
(cherry picked from commit 71796f4951)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-16 22:02:19 +00:00
mergify[bot]
c28e6ebc4c do not use index for verify_bank_hash_and_lamports (#17812) (#18010)
(cherry picked from commit adc683956f)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-16 21:33:30 +00:00
mergify[bot]
6479c11e9a Don't store votes unless we are leader soon (#17803) (#17894)
(cherry picked from commit 0feac57cb0)

Co-authored-by: sakridge <sakridge@gmail.com>
2021-06-16 21:28:06 +00:00
mergify[bot]
4ed0fcdde6 avoid unnecessary empty arrays when binning (#17944) (#18011)
(cherry picked from commit eee5414c64)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-16 20:48:18 +00:00
mergify[bot]
296a8ade63 set hash bins to 65k (#17912) (#18009)
(cherry picked from commit 55ee3b5f2f)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-16 20:31:26 +00:00
mergify[bot]
a84953ccfd Improve program deployment error message (#17717) (#17725)
(cherry picked from commit 39654d3fa5)

Co-authored-by: Jack May <jack@solana.com>
2021-06-16 20:16:00 +00:00
mergify[bot]
8492031fd0 refactor generate_index process_storage_slot (#17982) (#18007)
(cherry picked from commit 7ca04d6a86)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-16 20:14:49 +00:00
mergify[bot]
bff7259111 parallel storage -> accounts folder (#17955) (#18004)
(cherry picked from commit 7de79425ce)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-16 15:51:00 +00:00
mergify[bot]
67e1814581 nit: rename measure (#17946) (#17995)
(cherry picked from commit 5ecb30ff58)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-16 14:51:38 +00:00
mergify[bot]
12e92dd59d verify bank hash on startup with ledger tool option (backport #17939) (#17996)
* verify bank hash on startup with ledger tool option (#17939)

(cherry picked from commit f558b9b6bf)

# Conflicts:
#	core/tests/snapshots.rs
#	ledger/src/bank_forks_utils.rs
#	runtime/src/snapshot_utils.rs

* fix merge errors

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
Co-authored-by: Jeff Washington (jwash) <wash678@gmail.com>
2021-06-16 14:46:34 +00:00
mergify[bot]
4ee366edfa Properly handle block_height in Bigtable bincode deserialization (#17990) (#17994)
* Default block_height on eof

* Add comment to prevent future errors

(cherry picked from commit c57d1b44ef)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-06-16 05:41:38 +00:00
mergify[bot]
61573756f8 add metrics for startup (#17913) (#17975)
* add metrics for startup

    * roll timings up higher

    * fix test

    * fix duplicate

    (cherry picked from commit 471b34132e)

    # Conflicts:
    #       ledger/src/bank_forks_utils.rs
    #       runtime/src/snapshot_utils.rs
conflicts because #17778 is not present.

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-16 01:40:56 +00:00
mergify[bot]
fe5fed1182 Refactor bank_forks_utils::load() and some snapshot logic (#17492) (#17979)
Refactor a few functions that are on the load-from-snapshot path, to facilitate
adding in incremental snapshots more easily.

Additionally, add some tests and doc comments.

(cherry picked from commit 1953543274)

Co-authored-by: Brooks Prumo <brooks@solana.com>
2021-06-15 22:03:49 +00:00
Jon Cinque
0c90307677 v1.7: Stake refactor (Manual backport of #17906) (#17978)
* Refactor stake program into solana_program (#17906)

* Move stake state / instructions into solana_program

* Update account-decoder

* Update cli and runtime

* Update all other parts

* Commit Cargo.lock changes in programs/bpf

* Update cli stake instruction import

* Allow integer arithmetic

* Update ABI digest

* Bump rust mem instruction count

* Remove useless structs

* Move stake::id() -> stake::program::id()

* Re-export from solana_sdk and mark deprecated

* Address feedback

* Run cargo fmt

* Run cargo fmt post cherry-pick
2021-06-15 23:43:22 +02:00
mergify[bot]
cdd2a51f1f remove unused parameters from CalculateHashIntermediate (#17949) (#17976)
(cherry picked from commit 4d8ffdcc11)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-15 19:15:07 +00:00
mergify[bot]
0dbe3434f0 refactor so hash verify can be done by more callers (#17941) (#17947)
Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-15 16:49:32 +00:00
mergify[bot]
ef205593c5 removes port-based forwarding logic from turbine retransmit (#17716) (#17973)
Turbine retransmit logic is based on which socket it received the packet
from (i.e `packet.meta.forward`):
https://github.com/solana-labs/solana/blob/708bbcb00/core/src/retransmit_stage.rs#L467-L470

This can leave the cluster vulnerable to spoofing and selective
propagation of packets; see
https://github.com/solana-labs/solana/issues/6672
https://github.com/solana-labs/solana/pull/7774

This commit identifies if the node is on the "critical path" based on
its index in the shuffled cluster. If so, it forwards the packet to both
neighbors and children; otherwise, the packet is only forwarded to the
children.

The metrics added in
https://github.com/solana-labs/solana/pull/17351
shows that the number of times the index does not match the port is very
rare, and therefore this change should be safe.

(cherry picked from commit 161838655c)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2021-06-15 15:16:20 +00:00
mergify[bot]
8b5ba771ad name arguments to help with confusion (#17942) (#17948)
(cherry picked from commit 7fde9b6ff0)

    # Conflicts:
    #       runtime/src/accounts_db.rs

Co-authored-by: Jeff Washington (jwash) <wash678@gmail.com>
2021-06-15 14:46:07 +00:00
mergify[bot]
991f99b239 Bump spl-token to v3.1.1 (backport #17951) (#17958)
* Bump spl-token to v3.1.1 (#17951)

(cherry picked from commit b7de369992)

# Conflicts:
#	Cargo.lock
#	account-decoder/Cargo.toml
#	accounts-cluster-bench/Cargo.toml
#	programs/bpf/Cargo.lock
#	rpc/Cargo.toml
#	tokens/Cargo.toml

* Fix conflicts

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2021-06-15 08:29:54 +00:00
mergify[bot]
d6f17517cb calculate_capitalization uses hash calculation (#17443) (#17932)
* calculate_capitalization uses hash calculation

* feedback

* remove debugging code, clean up slot math

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-15 05:03:06 +00:00
mergify[bot]
15b2f280e3 use trait to simplify and consolidate cumulative code (#17852) (#17931)
(cherry picked from commit 2dc6969858)

    # Conflicts:
    #       runtime/src/accounts_hash.rs

Co-authored-by: Jeff Washington (jwash) <wash678@gmail.com>
2021-06-15 04:29:13 +00:00
mergify[bot]
60b43a1ddf Pass iterator for SortedStorages::new_with_slots (#17811) (#17847)
Co-authored-by: Carl Lin <carl@solana.com>
(cherry picked from commit 2a9b127029)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-15 00:01:53 +00:00
mergify[bot]
d6b83e3b0a add metrics to handle_snapshot_requests (#17937) (#17945)
(cherry picked from commit e6bbd4b3f0)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-14 22:56:14 +00:00
mergify[bot]
0446f89d22 binary search in slices for hashing (#17755) (#17804)
(cherry picked from commit 817d48be21)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-14 17:32:05 -05:00
mergify[bot]
54bc3e606e Fix typo in docs (#17920) (#17938)
(cherry picked from commit 3657469826)

Co-authored-by: Sarat Limawongpranee <innneang@users.noreply.github.com>
2021-06-14 12:04:02 -06:00
mergify[bot]
fed90cfbe8 Fix accounts index panic in purge_exact (#17757) (#17935)
(cherry picked from commit c2191d885d)

Co-authored-by: sakridge <sakridge@gmail.com>
2021-06-14 17:41:59 +00:00
mergify[bot]
e2e41a29eb Don't use pinned memory when unnecessary (#17832) (#17934)
Reports of excessive GPU memory usage and errors
from cudaHostRegister. There are some cases where pinning is
not required.

(cherry picked from commit eeee75c5be)

Co-authored-by: sakridge <sakridge@gmail.com>
2021-06-14 16:30:51 +00:00
mergify[bot]
3b813db42f minor fixes to punctuation and typos (#17881) (#17923)
* fix minor typos and punctuation

* fix minor typos and punctuation

* rewording for clarity and typo corrections

* rewording for clarity and typo corrections

* rewording for clarity and typo corrections

Co-authored-by: Gregg Dourgarian <greggd@aidacreative.com>
(cherry picked from commit 54155f875a)

Co-authored-by: Haik Dulgarian <greggd@tempworks.com>
2021-06-14 06:56:39 +00:00
mergify[bot]
16b1a4d003 short cuts expiration check if origin's contact-info is still valid (#17918) (#17921)
Crds::find_old_labels can skip checking values timestamps if the
origin's contact info hasn't expired yet:
https://github.com/solana-labs/solana/blob/985280ec0/gossip/src/crds.rs#L394-L408

(cherry picked from commit cca46308bc)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2021-06-13 21:12:24 +00:00
mergify[bot]
b51ea3ca0c excludes epoch-slots from nodes with unknown or different shred version (#17899) (#17916)
Inspecting TDS gossip table shows that crds values of nodes with
different shred-versions are creeping in. Their epoch-slots are
accumulated in ClusterSlots causing bogus slots very far from current
root which are not purged and so cause ClusterSlots keep consuming more
memory:
https://github.com/solana-labs/solana/issues/17789
https://github.com/solana-labs/solana/issues/14366#issuecomment-769896036
https://github.com/solana-labs/solana/issues/14366#issuecomment-832754654

This commit updates ClusterInfo::get_epoch_slots, and discards entries
from nodes with unknown or different shred-version.

Follow up commits will patch gossip not to waste bandwidth and memory
over crds values of nodes with different shred-version.

(cherry picked from commit 985280ec0b)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2021-06-13 15:56:05 +00:00
mergify[bot]
dc76675644 Don't require non-existent param for keygen new (#17896) (#17897)
(cherry picked from commit 22c356d24c)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-06-11 22:08:46 +00:00
mergify[bot]
274a238a00 Port unconfirmed duplicate tracking logic from ProgressMap to ForkChoice (#17779) (#17889)
(cherry picked from commit c8535be0e1)

Co-authored-by: carllin <carl@solana.com>
2021-06-11 11:41:59 +00:00
mergify[bot]
10507f0ade Account for duplicate before a bank is frozen or replayed (#17866) (#17883)
(cherry picked from commit afafa624a3)

Co-authored-by: carllin <carl@solana.com>
2021-06-11 07:06:42 +00:00
mergify[bot]
af2a6106da Check for undefined symbols in .so and warn about run-time errors (#17850) (#17880)
(cherry picked from commit c684e2bdc0)

Co-authored-by: Dmitri Makarov <dmakarov@users.noreply.github.com>
2021-06-11 00:03:17 +00:00
mergify[bot]
120a7e433f Warn about InstructionError meta (#17864) (#17878)
(cherry picked from commit fa6bdd2d12)

Co-authored-by: Jack May <jack@solana.com>
2021-06-10 23:49:03 +00:00
Trent Nelson
98e34f07df programs/config: Disallow duplicate signers 2021-06-10 06:54:18 +00:00
mergify[bot]
738df79394 Add local cluster tests that broadcast duplicate slots (#13995) (#17863)
* Add duplicate node local cluster test

* fix clippy

* remove dupe test

(cherry picked from commit 050bb5446d)

Co-authored-by: Justin Starry <justin@solana.com>
2021-06-10 03:17:44 +00:00
mergify[bot]
98e9b6e70b refactor: create type (#17818) (#17848)
(cherry picked from commit 576e3d95f7)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-09 08:38:04 +00:00
mergify[bot]
c9318f8dc2 Wrap long lines (#17842)
(cherry picked from commit e5e7390d44)

Co-authored-by: Michael Vines <mvines@gmail.com>
2021-06-09 08:10:34 +00:00
mergify[bot]
78f3606e30 test: simple CalculateHashIntermediate refactoring (#17813) (#17846)
(cherry picked from commit 01ef2a5c4a)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-08 21:47:40 +00:00
mergify[bot]
97f4d098e1 Add more info about how to safely change the identity of a staked validator (#17843)
(cherry picked from commit 193813d49a)

Co-authored-by: Michael Vines <mvines@gmail.com>
2021-06-08 19:25:41 +00:00
mergify[bot]
144a13b096 Update a dangling devnet endpoint doc (#17836) (#17839)
(cherry picked from commit 2dfb5b7579)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-06-08 16:20:43 +00:00
mergify[bot]
af0869c66c add min to roots_tracker (#17806) (#17835)
(cherry picked from commit 58fe1d0764)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-08 16:14:59 +00:00
Ryo Onodera
48e565038a Bump version to v1.7.2 (#17831) 2021-06-08 10:29:39 +00:00
mergify[bot]
cd6e1d921c update dependence version for gag to latest support windows (#17801) (#17825)
* update dependence version for gag to leatest support windows

* fix compile on windows

* add Cargo.lock

(cherry picked from commit e0ab5ee4f8)

Co-authored-by: Govlzkoy <gotope@users.noreply.github.com>
2021-06-08 07:48:39 +00:00
mergify[bot]
fb767f4612 Remove budget program (backport #17816) (#17822)
* Remove budget program (#17816)

(cherry picked from commit a66566e75b)

# Conflicts:
#	Cargo.lock
#	genesis/Cargo.toml
#	ledger/Cargo.toml
#	perf/Cargo.toml
#	programs/budget/Cargo.toml

* Fix conflicts

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2021-06-08 03:07:26 +00:00
mergify[bot]
9f35db28e5 Switch EpochSlots to be frozen slots, not completed slots (#17168) (#17776)
(cherry picked from commit 96ba2edfeb)

Co-authored-by: carllin <carl@solana.com>
2021-06-07 22:51:30 +00:00
mergify[bot]
ab19543dff break out hash time in metric (#17721) (#17753)
(cherry picked from commit 00ee84af37)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-07 22:19:52 +00:00
mergify[bot]
3d5f333a3b parallelizes gossip packets receiver with processing of requests (#17647) (#17807)
Gossip packet processing is composed of two stages:
  * The first is consuming packets from the socket, deserializing,
    sanitizing and verifying them:
    https://github.com/solana-labs/solana/blob/7f0349b29/gossip/src/cluster_info.rs#L2510-L2521
  * The second is actually processing the requests/messages:
    https://github.com/solana-labs/solana/blob/7f0349b29/gossip/src/cluster_info.rs#L2585-L2605

The former does not acquire any locks and so can be parallelized with
the later, allowing better pipelineing properties and smaller latency in
responding to gossip requests or propagating messages.

(cherry picked from commit cab30e2356)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2021-06-07 20:30:31 +00:00
mergify[bot]
d06ca605cf Bump jsonrpc crates (backport #17797) (#17805)
* Bump jsonrpc crates (#17797)

* Bump jsonrpc crates

* Update error text

* gitignore for rpc

(cherry picked from commit 2e998ed11d)

# Conflicts:
#	Cargo.lock
#	validator/Cargo.toml

* Fix conflicts

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2021-06-07 20:15:23 +00:00
mergify[bot]
334e11e4b9 rework hash calculation to not keep slot and write version (#17685) (#17794)
* rework hash calculation to not keep slot and write version

* refactor functions and add tests

* always use multiple slot code path

(cherry picked from commit b5bb91b50f)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-07 15:31:18 +00:00
mergify[bot]
fd68b4e7a8 Support out of band dumping of unrooted slots in AccountsDb (#17269) (#17777)
* Accounts dumping logic

* Add test for interaction between cache flush and remove_unrooted_slot()

* Update comments

* Rename

* renaming

* Add more comments

* Renaming

* Fixup test and bad check

(cherry picked from commit bbcdf073ba)

Co-authored-by: carllin <carl@solana.com>
2021-06-07 09:37:55 +00:00
mergify[bot]
e5ea16fad8 system-program: Remove zero lamport check on transfers (#17726) (#17764)
* system-program: Move lamports == 0 check on transfers

* Address feedback

* Update stake split to explicitly allocate + assign

* Update stake tests referring to split instruction

* Revert whitespace

* Update split instruction index in test

* Remove unnecessary `assign_with_seed` from `split_with_seed`

* Fix stake instruction parser

* Update test to allow splitting into account with lamports

(cherry picked from commit 8f5e773caf)

Co-authored-by: Jon Cinque <jon.cinque@gmail.com>
2021-06-06 18:27:29 +00:00
mergify[bot]
6d5a4b5cce Document ProgramTest::new and fix ProgramTest::add_program (#17754) (#17768)
* document ProgramTest::new

* simplify ProgramTest::new doc-string

* make ProgramTest::add_program noisier

`add_program` (and `new`, implicitly) now prints a warning when the user
supplies a bogus program name to a ProgramTest and invokes `test-bpf`.

Additionally, it is now impossible to ask for a regular `test` and for
the generated ProgramTest to load BPF code instead of native code.
Previously, this was caused by a precedence issue: BPF code would always
be preferred over native if the program name was valid, regardless of
user choice.

(cherry picked from commit 2aaf55795f)

Co-authored-by: xuoe <alex@psi.io>
2021-06-06 07:03:13 +00:00
mergify[bot]
ffb6b5a23b add data point for cap mismatch (#17746) (#17751)
(cherry picked from commit f6fb8906c7)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-04 20:39:46 +00:00
mergify[bot]
e247625025 Create solana-poh and move remaining rpc modules to solana-rpc (backport #17698) (#17745)
* Create solana-poh and move remaining rpc modules to solana-rpc (#17698)

* Create solana-poh crate

* Move BigTableUploadService to solana-ledger

* Add solana-rpc to workspace

* Move dependencies to solana-rpc

* Move remaining rpc modules to solana-rpc

* Single use statement solana-poh

* Single use statement solana-rpc

(cherry picked from commit 544b3c0d17)

# Conflicts:
#	Cargo.lock
#	banking-bench/Cargo.toml
#	core/Cargo.toml
#	core/benches/banking_stage.rs
#	local-cluster/Cargo.toml
#	rpc/Cargo.toml
#	stake-monitor/Cargo.toml
#	validator/Cargo.toml

* Fix conflicts & versions

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2021-06-04 18:19:08 +00:00
mergify[bot]
67c07aa5d3 increase bin count for hash scan (#17562) (#17747)
(cherry picked from commit 5197454fea)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-04 18:12:48 +00:00
mergify[bot]
0de1ce0c2c adds fallback logic if retransmit multicast fails (#17714) (#17742)
In retransmit-stage, based on the packet.meta.seed and resulting
children/neighbors, each packet is sent to a different set of peers:
https://github.com/solana-labs/solana/blob/708bbcb00/core/src/retransmit_stage.rs#L421-L457

However, current code errors out as soon as a multicast call fails,
which will skip all the remaining packets:
https://github.com/solana-labs/solana/blob/708bbcb00/core/src/retransmit_stage.rs#L467-L470

This can exacerbate packets loss in turbine.

This commit:
  * keeps iterating over retransmit packets for loop even if some
    intermediate sends fail.
  * adds a fallback to UdpSocket::send_to if multicast fails.

Recent discord chat:
https://discord.com/channels/428295358100013066/689412830075551748/849530845052403733

(cherry picked from commit be957f25c9)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2021-06-04 16:20:44 +00:00
mergify[bot]
59f4fba05c simplify test construction (#17686) (#17693)
(cherry picked from commit 83ceedc091)

# Conflicts:
#	runtime/src/accounts_db.rs

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-04 15:57:11 +00:00
mergify[bot]
c0c764377c excludes caller's crds values from pull responses (#17542) (#17744)
If the crds entry belongs to the caller itself, then the caller will
always have the more recent version of it, regardless of it being
filtered out by the bloom filter or not.

The exception is node-instance types which are meant to detect duplicate
running instances, and those are exempted.

(cherry picked from commit 7cf6e66ddd)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2021-06-04 15:54:00 +00:00
mergify[bot]
c9bc059637 writes epoch-slots to crds table synchronously (#17719) (#17743)
epoch-slots may be overwritten before they are written to crds table:
https://github.com/solana-labs/solana/issues/17711

This commit writes new epoch-slots to crds table synchronously with
push_epoch_slots. The functions is still not thread-safe as commented in
the code, however currently only one threads is invoking this code.

(cherry picked from commit 60b0a13444)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2021-06-04 15:34:39 +00:00
mergify[bot]
78147d48e4 use slots returned from get_snapshot_storages to sort (#17638) (#17695)
* use slots returned from get_snapshot_storages to sort

    * add tests

    (cherry picked from commit 9388aaca15)

    # Conflicts:
    #       runtime/src/accounts_db.rs
    #       runtime/src/sorted_storages.rs

Co-authored-by: Jeff Washington (jwash) <wash678@gmail.com>
2021-06-04 10:14:37 -05:00
mergify[bot]
5e7db52087 Avoid full-range compactions with periodic filtered b.g. ones (backport #16697) (#17741)
* Avoid full-range compactions with periodic filtered b.g. ones (#16697)

* Update rocksdb to v0.16.0

* Promote the infrequent and important log to info!

* Force background compaction by ttl without manual compaction

* Fix test

* Support no compaction mode in test_ledger_cleanup_compaction

* Fix comment

* Make compaction_interval customizable

* Avoid major compaction with periodic filtering...

* Adress lazy_static, special cfs and range check

* Clean up a bit and add comment

* Add comment

* More comments...

* Config code cleanup

* Add comment

* Use .conflicts_with()

* Nullify unneeded delete_range ops for special CFs

* Some clean ups

* Clarify the locking intention

* Ensure special CFs' consistency with PurgeType::CompactionFilter

* Fix comment

* Fix bad copy paste

* Fix various types...

* Don't use tuples

* Add a unit test for compaction_filter

* Fix typo...

* Remove flag and just use new behavior always

* Fix wrong condition negation...

* Doc. about no set_last_purged_slot in purge_slots

* Write a test and fix off-by-one bug....

* Apply suggestions from code review

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>

* Follow up to github review suggestions

* Fix line-wrapping

* Fix conflict

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
(cherry picked from commit 1f97b2365f)

# Conflicts:
#	ledger/src/blockstore_db.rs

* Fix conflict

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2021-06-04 14:38:02 +00:00
mergify[bot]
ae42413d57 support bin divisions up to 65536 (#17563) (#17692)
* support bin divisions up to 65536

* add tests

(cherry picked from commit db8811eacd)

# Conflicts:
#	runtime/src/accounts_db.rs

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-04 06:33:00 +00:00
mergify[bot]
d433bd3d84 remove slot lookup (#17691) (#17694)
(cherry picked from commit 4bd32d891f)

    # Conflicts:
    #       runtime/src/accounts_db.rs

Co-authored-by: Jeff Washington (jwash) <wash678@gmail.com>
2021-06-04 06:09:46 +00:00
mergify[bot]
58dd6dc227 add info for vm.max_map_count incorrectly set. (#17727) (#17730)
(cherry picked from commit 81bafd9daf)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-04 05:02:13 +00:00
mergify[bot]
893df9b277 Rename ValidatorExit and move to sdk (#17728) (#17729)
(cherry picked from commit 3a647c4bea)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-06-04 04:38:49 +00:00
mergify[bot]
17dc13760b sort storages by slot before scan (#17411) (#17724)
* sort storages by slot before scan

* fix return value

(cherry picked from commit ef5169ff24)

# Conflicts:
#	runtime/src/accounts_db.rs

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-04 03:21:20 +00:00
mergify[bot]
498bf911eb Add missing ProgramError to InstructionError mappings (#16231) (#17723)
* Add missing ProgramError to InstructionError mappings

* add note

* Clarify process of adding new program error

(cherry picked from commit 83b9a046d1)

Co-authored-by: Jack May <jack@solana.com>
2021-06-04 00:25:25 +00:00
mergify[bot]
96de58b3a4 reduce copy (#17672) (#17688)
(cherry picked from commit d802eb303c)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-03 22:26:31 +00:00
mergify[bot]
9b61fa24c7 rework test for flexibility (#17592) (#17681)
* rework test for flexibility

* respond to pr feedback

(cherry picked from commit 886898eabf)

# Conflicts:
#	runtime/src/accounts_db.rs

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-03 21:25:58 +00:00
mergify[bot]
e9be3cf6bc implement ancestors as rolling bit field (#17482) (#17673)
(cherry picked from commit eec996ba41)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-03 18:47:37 +00:00
mergify[bot]
ea44a71914 parallel get_snapshot_storages (#17589) (#17679)
(cherry picked from commit 738cc9549f)

# Conflicts:
#	runtime/src/accounts_db.rs
#	runtime/src/bank.rs
#	runtime/src/serde_snapshot/tests.rs

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-03 18:32:04 +00:00
mergify[bot]
a00fbbf5ca add metric for collecting storages (#17527) (#17675)
(cherry picked from commit 72bb271a94)

# Conflicts:
#	runtime/src/accounts_db.rs
#	runtime/src/snapshot_utils.rs

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-03 05:34:32 +00:00
mergify[bot]
eb1a04af65 refactor SnapshotStorage helpers to prepare for later changes (#17560) (#17678)
(cherry picked from commit 654918ab27)

# Conflicts:
#	runtime/src/accounts_db.rs

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-03 05:24:11 +00:00
mergify[bot]
d1fbf77f3f RollingBitField: bug fixes and add tests (#17525) (#17674)
* RollingBitField: bug fixes and add tests

* respond to feedback

(cherry picked from commit 8924fbf6a0)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-03 05:07:02 +00:00
mergify[bot]
04fbf73a29 remove antiquated assert (#17643) (#17689)
(cherry picked from commit 07bac27ac7)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-02 21:53:33 +00:00
mergify[bot]
db70eb3160 minor test code cleanup (#17645) (#17680)
(cherry picked from commit 107af52deb)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-02 20:14:23 +00:00
mergify[bot]
cd974c26b6 add check_hash to non-index hash calculation (#17558) (#17676)
(cherry picked from commit 55c22d3b76)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-02 20:00:50 +00:00
mergify[bot]
53e0f5d61c remove unnecessary clone (#17559) (#17677)
(cherry picked from commit 1d02dba06f)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-06-02 19:59:55 +00:00
mergify[bot]
e864bf4898 Fix CPI recursion depth (#17659) (#17670)
* Fix CPI recursion depth

(cherry picked from commit 80e5b24b38)

Co-authored-by: Jack May <jack@solana.com>
2021-06-02 11:06:50 +00:00
mergify[bot]
81d12b7644 Fix test instruction count (#17655) (#17660)
(cherry picked from commit 4e0db50e4e)

Co-authored-by: Jack May <jack@solana.com>
2021-06-02 09:20:11 +00:00
mergify[bot]
309fcd6270 Fix test instruction count (#17655) (#17660)
(cherry picked from commit 4e0db50e4e)

Co-authored-by: Jack May <jack@solana.com>
2021-06-02 08:51:45 +00:00
mergify[bot]
938112e449 fix getBlock API reference (#17661) (#17662)
(cherry picked from commit 10c6e771b5)

Co-authored-by: oncecsc <74547046+oncecsc@users.noreply.github.com>
2021-06-02 07:32:15 +00:00
mergify[bot]
3e012ea69e Cli: Decode known program custom errors returned from simulation (#17652) (#17658)
(cherry picked from commit b7f98ea18a)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-06-02 07:03:46 +00:00
Michael Vines
975c942ea7 Bump version to v1.7.1 2021-06-02 05:21:14 +00:00
mergify[bot]
2798271da0 Add memory operation syscalls (backport #16447) (#17648)
* Add memory operation syscalls (#16447)

(cherry picked from commit 2b50529265)

# Conflicts:
#	programs/bpf/Cargo.lock

* Resolve conflicts

Co-authored-by: Jack May <jack@solana.com>
2021-06-01 18:43:50 -07:00
mergify[bot]
dc258cebab Add create-stake command to solana-tokens CLI (#17550) (#17649)
* Add create-stake command to solana-tokens CLI

* Add --unlocked-sol arg

Thanks @CriesofCarrots!

(cherry picked from commit 1b7f8777d6)

Co-authored-by: Greg Fitzgerald <greg@solana.com>
2021-06-02 01:16:48 +00:00
mergify[bot]
4b8c5194c7 Purge expired BlockHeight data from blockstore (backport #17634) (#17641)
* Purge expired BlockHeight data from blockstore (#17634)

* Purge expired BlockHeight data from blockstore

* Also call compact_storage and add comment....

(cherry picked from commit 96cdbfdcc0)

# Conflicts:
#	ledger/src/blockstore_db.rs

* Fix conflict

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2021-06-02 01:04:11 +00:00
mergify[bot]
3c7c6dacfb Add BPF rustc option to reduce the optimizations to safer level (#17590) (#17594)
(cherry picked from commit 831e87c65d)

Co-authored-by: Dmitri Makarov <dmakarov@users.noreply.github.com>
2021-05-31 19:15:56 +00:00
mergify[bot]
e36337a764 Make the sys-tuner oneliner actually copy-pastable (#17615) (#17619)
* Make the sys-tuner oneliner actually copy-pastable

* Use `command -v`

(cherry picked from commit 41975016b9)

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2021-05-31 18:13:47 +00:00
mergify[bot]
a49856b898 Rework #17486 (backport #17566) (#17597)
* Revert "Improve missing default signer error messaging (#17486)"

This reverts commit 6d40d0d141.

(cherry picked from commit ca8c1c6c42)

* Improve missing default filepath signer error messaging

(cherry picked from commit 06a926f2f4)

* CI: temporarily skip spl downstream build

(cherry picked from commit d01b4f80f9)

Co-authored-by: Trent Nelson <trent@solana.com>
2021-05-31 17:28:06 +00:00
mergify[bot]
8ca2f52041 Make initialize public (#17605) (#17607)
(cherry picked from commit 2896fc3987)

Co-authored-by: Michael Vines <mvines@gmail.com>
2021-05-31 08:51:05 +00:00
mergify[bot]
2f7f243022 Always bail if program modifies a ro account (backport #17569) (#17584)
* Always bail if program modifies a ro account (#17569)

(cherry picked from commit a3240aebde)

* resolve conflicts

* nudge

Co-authored-by: Jack May <jack@solana.com>
2021-05-28 20:34:10 +00:00
mergify[bot]
7e443770d7 test-validator: add an arg to control faucet genesis balance (#17582)
(cherry picked from commit 974a96738a)

Co-authored-by: Trent Nelson <trent@solana.com>
2021-05-28 17:50:44 +00:00
mergify[bot]
8ec09884b8 Revert bpf-tools to version 1.8 because of a codegen bug suspicion (#17568) (#17577)
(cherry picked from commit 2316ddb90a)

Co-authored-by: Dmitri Makarov <dmakarov@users.noreply.github.com>
2021-05-28 11:10:14 +00:00
mergify[bot]
88c7e636d6 Bump console from 0.11.3 to 0.14.1 (#16301) (#17552)
* Bump console from 0.11.3 to 0.14.1

Bumps [console](https://github.com/mitsuhiko/console) from 0.11.3 to 0.14.1.
- [Release notes](https://github.com/mitsuhiko/console/releases)
- [Changelog](https://github.com/mitsuhiko/console/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mitsuhiko/console/compare/0.11.3...0.14.1)

Signed-off-by: dependabot[bot] <support@github.com>

* Update all Cargo lock files

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
(cherry picked from commit ec1a307a7c)

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-27 21:30:17 +00:00
mergify[bot]
add3fd479d chore: bump chrono-humanize from 0.1.1 to 0.2.1 (#16895) (#17549)
* chore: bump chrono-humanize from 0.1.1 to 0.2.1

Bumps [chrono-humanize](https://gitlab.com/imp/chrono-humanize-rs) from 0.1.1 to 0.2.1.
- [Release notes](https://gitlab.com/imp/chrono-humanize-rs/tags)
- [Commits](https://gitlab.com/imp/chrono-humanize-rs/compare/0.1.1...0.2.1)

Signed-off-by: dependabot[bot] <support@github.com>

* [auto-commit] Update all Cargo lock files

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: dependabot-buildkite <anatoly+githubjenkins@solana.io>
(cherry picked from commit 4f74c77146)

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-27 20:37:42 +00:00
mergify[bot]
70410536b9 Bump serde_bytes from 0.11.4 to 0.11.5 (#16299) (#17546)
* Bump serde_bytes from 0.11.4 to 0.11.5

Bumps [serde_bytes](https://github.com/serde-rs/bytes) from 0.11.4 to 0.11.5.
- [Release notes](https://github.com/serde-rs/bytes/releases)
- [Commits](https://github.com/serde-rs/bytes/compare/0.11.4...0.11.5)

Signed-off-by: dependabot[bot] <support@github.com>

* [auto-commit] Update all Cargo lock files

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: dependabot-buildkite <dependabot-buildkite@noreply.solana.com>
(cherry picked from commit 6e2ae68643)

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-27 20:04:59 +00:00
mergify[bot]
6f3f9b485c Allow configuring testnet slots-per-epoch (#17545) (#17551)
(cherry picked from commit cb1fb28247)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-05-27 19:46:49 +00:00
mergify[bot]
bd9ce3590d Remove redundant copy from RocksDB get_cf() wrapper (#17529) (#17543)
(cherry picked from commit 983828a2a9)

Co-authored-by: steviez <steven@solana.com>
2021-05-27 18:58:47 +00:00
530 changed files with 23030 additions and 14509 deletions

1116
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -39,19 +39,20 @@ members = [
"metrics",
"net-shaper",
"notifier",
"poh",
"poh-bench",
"program-test",
"programs/secp256k1",
"programs/bpf_loader",
"programs/budget",
"programs/config",
"programs/exchange",
"programs/failure",
"programs/noop",
"programs/ownable",
"programs/secp256k1",
"programs/stake",
"programs/vote",
"remote-wallet",
"rpc",
"runtime",
"runtime/store-tool",
"sdk",
@@ -59,7 +60,6 @@ members = [
"sdk/cargo-test-bpf",
"scripts",
"stake-accounts",
"stake-monitor",
"sys-tuner",
"tokens",
"transaction-status",

View File

@@ -19,7 +19,7 @@ $ source $HOME/.cargo/env
$ rustup component add rustfmt
```
Please sure you are always using the latest stable rust version by running:
Please make sure you are always using the latest stable rust version by running:
```bash
$ rustup update

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-account-decoder"
version = "1.7.0"
version = "1.7.5"
description = "Solana account decoder"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
@@ -19,11 +19,10 @@ lazy_static = "1.4.0"
serde = "1.0.122"
serde_derive = "1.0.103"
serde_json = "1.0.56"
solana-config-program = { path = "../programs/config", version = "=1.7.0" }
solana-sdk = { path = "../sdk", version = "=1.7.0" }
solana-stake-program = { path = "../programs/stake", version = "=1.7.0" }
solana-vote-program = { path = "../programs/vote", version = "=1.7.0" }
spl-token-v2-0 = { package = "spl-token", version = "=3.1.0", features = ["no-entrypoint"] }
solana-config-program = { path = "../programs/config", version = "=1.7.5" }
solana-sdk = { path = "../sdk", version = "=1.7.5" }
solana-vote-program = { path = "../programs/vote", version = "=1.7.5" }
spl-token-v2-0 = { package = "spl-token", version = "=3.1.1", features = ["no-entrypoint"] }
thiserror = "1.0"
zstd = "0.5.1"

View File

@@ -69,32 +69,32 @@ impl UiAccount {
) -> 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 {

View File

@@ -9,14 +9,14 @@ use crate::{
};
use inflector::Inflector;
use serde_json::Value;
use solana_sdk::{instruction::InstructionError, pubkey::Pubkey, system_program, sysvar};
use solana_sdk::{instruction::InstructionError, pubkey::Pubkey, stake, system_program, sysvar};
use std::collections::HashMap;
use thiserror::Error;
lazy_static! {
static ref BPF_UPGRADEABLE_LOADER_PROGRAM_ID: Pubkey = solana_sdk::bpf_loader_upgradeable::id();
static ref CONFIG_PROGRAM_ID: Pubkey = solana_config_program::id();
static ref STAKE_PROGRAM_ID: Pubkey = solana_stake_program::id();
static ref STAKE_PROGRAM_ID: Pubkey = stake::program::id();
static ref SYSTEM_PROGRAM_ID: Pubkey = system_program::id();
static ref SYSVAR_PROGRAM_ID: Pubkey = sysvar::id();
static ref TOKEN_PROGRAM_ID: Pubkey = spl_token_id_v2_0();

View File

@@ -6,10 +6,10 @@ use bincode::deserialize;
use serde_json::Value;
use solana_config_program::{get_config_data, ConfigKeys};
use solana_sdk::pubkey::Pubkey;
use solana_stake_program::config::Config as StakeConfig;
use solana_sdk::stake::config::{self as stake_config, Config as StakeConfig};
pub fn parse_config(data: &[u8], pubkey: &Pubkey) -> Result<ConfigAccountType, ParseAccountError> {
let parsed_account = if pubkey == &solana_stake_program::config::id() {
let parsed_account = if pubkey == &stake_config::id() {
get_config_data(data)
.ok()
.and_then(|data| deserialize::<StakeConfig>(data).ok())
@@ -37,7 +37,7 @@ fn parse_config_data<T>(data: &[u8], keys: Vec<(Pubkey, bool)>) -> Option<UiConf
where
T: serde::de::DeserializeOwned,
{
let config_data: T = deserialize(&get_config_data(data).ok()?).ok()?;
let config_data: T = deserialize(get_config_data(data).ok()?).ok()?;
let keys = keys
.iter()
.map(|key| UiConfigKey {
@@ -101,11 +101,7 @@ mod test {
};
let stake_config_account = create_config_account(vec![], &stake_config, 10);
assert_eq!(
parse_config(
&stake_config_account.data(),
&solana_stake_program::config::id()
)
.unwrap(),
parse_config(stake_config_account.data(), &stake_config::id()).unwrap(),
ConfigAccountType::StakeConfig(UiStakeConfig {
warmup_cooldown_rate: 0.25,
slash_penalty: 50,
@@ -125,7 +121,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 {

View File

@@ -4,7 +4,7 @@ use crate::{
};
use bincode::deserialize;
use solana_sdk::clock::{Epoch, UnixTimestamp};
use solana_stake_program::stake_state::{Authorized, Delegation, Lockup, Meta, Stake, StakeState};
use solana_sdk::stake::state::{Authorized, Delegation, Lockup, Meta, Stake, StakeState};
pub fn parse_stake(data: &[u8]) -> Result<StakeAccountType, ParseAccountError> {
let stake_state: StakeState = deserialize(data)

View File

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

View File

@@ -6,6 +6,7 @@ use rayon::prelude::*;
use solana_measure::measure::Measure;
use solana_runtime::{
accounts::{create_test_accounts, update_accounts_bench, Accounts},
accounts_db::AccountShrinkThreshold,
accounts_index::AccountSecondaryIndexes,
ancestors::Ancestors,
};
@@ -64,6 +65,7 @@ fn main() {
&ClusterType::Testnet,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
);
println!("Creating {} accounts", num_accounts);
let mut create_time = Measure::start("create accounts");
@@ -119,6 +121,7 @@ fn main() {
solana_sdk::clock::Slot::default(),
&ancestors,
None,
false,
);
time_store.stop();
if results != results_store {

View File

@@ -2,7 +2,7 @@
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2018"
name = "solana-accounts-cluster-bench"
version = "1.7.0"
version = "1.7.5"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@@ -13,23 +13,23 @@ clap = "2.33.1"
log = "0.4.11"
rand = "0.7.0"
rayon = "1.4.1"
solana-account-decoder = { path = "../account-decoder", version = "=1.7.0" }
solana-clap-utils = { path = "../clap-utils", version = "=1.7.0" }
solana-client = { path = "../client", version = "=1.7.0" }
solana-core = { path = "../core", version = "=1.7.0" }
solana-faucet = { path = "../faucet", version = "=1.7.0" }
solana-gossip = { path = "../gossip", version = "=1.7.0" }
solana-logger = { path = "../logger", version = "=1.7.0" }
solana-measure = { path = "../measure", version = "=1.7.0" }
solana-net-utils = { path = "../net-utils", version = "=1.7.0" }
solana-runtime = { path = "../runtime", version = "=1.7.0" }
solana-sdk = { path = "../sdk", version = "=1.7.0" }
solana-transaction-status = { path = "../transaction-status", version = "=1.7.0" }
solana-version = { path = "../version", version = "=1.7.0" }
spl-token-v2-0 = { package = "spl-token", version = "=3.1.0", features = ["no-entrypoint"] }
solana-account-decoder = { path = "../account-decoder", version = "=1.7.5" }
solana-clap-utils = { path = "../clap-utils", version = "=1.7.5" }
solana-client = { path = "../client", version = "=1.7.5" }
solana-core = { path = "../core", version = "=1.7.5" }
solana-faucet = { path = "../faucet", version = "=1.7.5" }
solana-gossip = { path = "../gossip", version = "=1.7.5" }
solana-logger = { path = "../logger", version = "=1.7.5" }
solana-measure = { path = "../measure", version = "=1.7.5" }
solana-net-utils = { path = "../net-utils", version = "=1.7.5" }
solana-runtime = { path = "../runtime", version = "=1.7.5" }
solana-sdk = { path = "../sdk", version = "=1.7.5" }
solana-transaction-status = { path = "../transaction-status", version = "=1.7.5" }
solana-version = { path = "../version", version = "=1.7.5" }
spl-token-v2-0 = { package = "spl-token", version = "=3.1.1", features = ["no-entrypoint"] }
[dev-dependencies]
solana-local-cluster = { path = "../local-cluster", version = "=1.7.0" }
solana-local-cluster = { path = "../local-cluster", version = "=1.7.5" }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -55,7 +55,7 @@ pub fn airdrop_lamports(
);
let (blockhash, _fee_calculator) = client.get_recent_blockhash().unwrap();
match request_airdrop_transaction(&faucet_addr, &id.pubkey(), airdrop_amount, blockhash) {
match request_airdrop_transaction(faucet_addr, &id.pubkey(), airdrop_amount, blockhash) {
Ok(transaction) => {
let mut tries = 0;
loop {
@@ -363,7 +363,7 @@ fn run_accounts_bench(
iterations: usize,
maybe_space: Option<u64>,
batch_size: usize,
close_nth: u64,
close_nth_batch: u64,
maybe_lamports: Option<u64>,
num_instructions: usize,
mint: Option<Pubkey>,
@@ -431,7 +431,7 @@ fn run_accounts_bench(
if !airdrop_lamports(
&client,
&faucet_addr,
&payer_keypairs[i],
payer_keypairs[i],
lamports * 100_000,
) {
warn!("failed airdrop, exiting");
@@ -441,6 +441,7 @@ fn run_accounts_bench(
}
}
// Create accounts
let sigs_len = executor.num_outstanding();
if sigs_len < batch_size {
let num_to_create = batch_size - sigs_len;
@@ -475,21 +476,25 @@ fn run_accounts_bench(
}
}
if close_nth > 0 {
let expected_closed = total_accounts_created as u64 / close_nth;
if expected_closed > total_accounts_closed {
let txs: Vec<_> = (0..expected_closed - total_accounts_closed)
if close_nth_batch > 0 {
let num_batches_to_close =
total_accounts_created as u64 / (close_nth_batch * batch_size as u64);
let expected_closed = num_batches_to_close * batch_size as u64;
let max_closed_seed = seed_tracker.max_closed.load(Ordering::Relaxed);
// Close every account we've created with seed between max_closed_seed..expected_closed
if max_closed_seed < expected_closed {
let txs: Vec<_> = (0..expected_closed - max_closed_seed)
.into_par_iter()
.map(|_| {
let message = make_close_message(
&payer_keypairs[0],
payer_keypairs[0],
&base_keypair,
seed_tracker.max_closed.clone(),
1,
min_balance,
mint.is_some(),
);
let signers: Vec<&Keypair> = vec![&payer_keypairs[0], &base_keypair];
let signers: Vec<&Keypair> = vec![payer_keypairs[0], &base_keypair];
Transaction::new(&signers, message, recent_blockhash.0)
})
.collect();
@@ -572,14 +577,14 @@ fn main() {
.help("Number of transactions to send per batch"),
)
.arg(
Arg::with_name("close_nth")
Arg::with_name("close_nth_batch")
.long("close-frequency")
.takes_value(true)
.value_name("BYTES")
.help(
"Send close transactions after this many accounts created. \
Note: a `close-frequency` value near or below `batch-size` \
may result in transaction-simulation errors, as the close \
"Every `n` batches, create a batch of close transactions for
the earliest remaining batch of accounts created.
Note: Should be > 1 to avoid situations where the close \
transactions will be submitted before the corresponding \
create transactions have been confirmed",
),
@@ -632,7 +637,7 @@ fn main() {
let space = value_t!(matches, "space", u64).ok();
let lamports = value_t!(matches, "lamports", u64).ok();
let batch_size = value_t!(matches, "batch_size", usize).unwrap_or(4);
let close_nth = value_t!(matches, "close_nth", u64).unwrap_or(0);
let close_nth_batch = value_t!(matches, "close_nth_batch", u64).unwrap_or(0);
let iterations = value_t!(matches, "iterations", usize).unwrap_or(10);
let num_instructions = value_t!(matches, "num_instructions", usize).unwrap_or(1);
if num_instructions == 0 || num_instructions > 500 {
@@ -685,7 +690,7 @@ fn main() {
iterations,
space,
batch_size,
close_nth,
close_nth_batch,
lamports,
num_instructions,
mint,
@@ -720,7 +725,7 @@ pub mod test {
let iterations = 10;
let maybe_space = None;
let batch_size = 100;
let close_nth = 100;
let close_nth_batch = 100;
let maybe_lamports = None;
let num_instructions = 2;
let mut start = Measure::start("total accounts run");
@@ -731,7 +736,7 @@ pub mod test {
iterations,
maybe_space,
batch_size,
close_nth,
close_nth_batch,
maybe_lamports,
num_instructions,
None,

View File

@@ -2,7 +2,7 @@
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2018"
name = "solana-banking-bench"
version = "1.7.0"
version = "1.7.5"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@@ -14,17 +14,18 @@ crossbeam-channel = "0.4"
log = "0.4.11"
rand = "0.7.0"
rayon = "1.5.0"
solana-core = { path = "../core", version = "=1.7.0" }
solana-clap-utils = { path = "../clap-utils", version = "=1.7.0" }
solana-gossip = { path = "../gossip", version = "=1.7.0" }
solana-ledger = { path = "../ledger", version = "=1.7.0" }
solana-logger = { path = "../logger", version = "=1.7.0" }
solana-measure = { path = "../measure", version = "=1.7.0" }
solana-perf = { path = "../perf", version = "=1.7.0" }
solana-runtime = { path = "../runtime", version = "=1.7.0" }
solana-streamer = { path = "../streamer", version = "=1.7.0" }
solana-sdk = { path = "../sdk", version = "=1.7.0" }
solana-version = { path = "../version", version = "=1.7.0" }
solana-core = { path = "../core", version = "=1.7.5" }
solana-clap-utils = { path = "../clap-utils", version = "=1.7.5" }
solana-gossip = { path = "../gossip", version = "=1.7.5" }
solana-ledger = { path = "../ledger", version = "=1.7.5" }
solana-logger = { path = "../logger", version = "=1.7.5" }
solana-measure = { path = "../measure", version = "=1.7.5" }
solana-perf = { path = "../perf", version = "=1.7.5" }
solana-poh = { path = "../poh", version = "=1.7.5" }
solana-runtime = { path = "../runtime", version = "=1.7.5" }
solana-streamer = { path = "../streamer", version = "=1.7.5" }
solana-sdk = { path = "../sdk", version = "=1.7.5" }
solana-version = { path = "../version", version = "=1.7.5" }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -4,11 +4,7 @@ use crossbeam_channel::unbounded;
use log::*;
use rand::{thread_rng, Rng};
use rayon::prelude::*;
use solana_core::{
banking_stage::{create_test_recorder, BankingStage},
poh_recorder::PohRecorder,
poh_recorder::WorkingBankEntry,
};
use solana_core::banking_stage::BankingStage;
use solana_gossip::{cluster_info::ClusterInfo, cluster_info::Node};
use solana_ledger::{
blockstore::Blockstore,
@@ -17,6 +13,7 @@ use solana_ledger::{
};
use solana_measure::measure::Measure;
use solana_perf::packet::to_packets_chunked;
use solana_poh::poh_recorder::{create_test_recorder, PohRecorder, WorkingBankEntry};
use solana_runtime::{
accounts_background_service::AbsRequestSender, bank::Bank, bank_forks::BankForks,
};
@@ -77,7 +74,7 @@ fn make_accounts_txs(
.into_par_iter()
.map(|_| {
let mut new = dummy.clone();
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen::<u8>()).collect();
if !same_payer {
new.message.account_keys[0] = solana_sdk::pubkey::new_rand();
}
@@ -188,7 +185,7 @@ fn main() {
genesis_config.hash(),
);
// Ignore any pesky duplicate signature errors in the case we are using single-payer
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen::<u8>()).collect();
fund.signatures = vec![Signature::new(&sig[0..64])];
let x = bank.process_transaction(&fund);
x.unwrap();
@@ -198,7 +195,7 @@ fn main() {
if !skip_sanity {
//sanity check, make sure all the transactions can execute sequentially
transactions.iter().for_each(|tx| {
let res = bank.process_transaction(&tx);
let res = bank.process_transaction(tx);
assert!(res.is_ok(), "sanity test transactions error: {:?}", res);
});
bank.clear_signatures();
@@ -354,7 +351,7 @@ fn main() {
if bank.slot() > 0 && bank.slot() % 16 == 0 {
for tx in transactions.iter_mut() {
tx.message.recent_blockhash = bank.last_blockhash();
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen::<u8>()).collect();
tx.signatures[0] = Signature::new(&sig[0..64]);
}
verified = to_packets_chunked(&transactions.clone(), packets_per_chunk);

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-banks-client"
version = "1.7.0"
version = "1.7.5"
description = "Solana banks client"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
@@ -11,20 +11,20 @@ edition = "2018"
[dependencies]
bincode = "1.3.1"
borsh = "0.8.1"
borsh-derive = "0.8.1"
borsh = "0.9.0"
borsh-derive = "0.9.0"
futures = "0.3"
mio = "0.7.6"
solana-banks-interface = { path = "../banks-interface", version = "=1.7.0" }
solana-program = { path = "../sdk/program", version = "=1.7.0" }
solana-sdk = { path = "../sdk", version = "=1.7.0" }
solana-banks-interface = { path = "../banks-interface", version = "=1.7.5" }
solana-program = { path = "../sdk/program", version = "=1.7.5" }
solana-sdk = { path = "../sdk", version = "=1.7.5" }
tarpc = { version = "0.24.1", features = ["full"] }
tokio = { version = "1", features = ["full"] }
tokio-serde = { version = "0.8", features = ["bincode"] }
[dev-dependencies]
solana-runtime = { path = "../runtime", version = "=1.7.0" }
solana-banks-server = { path = "../banks-server", version = "=1.7.0" }
solana-runtime = { path = "../runtime", version = "=1.7.5" }
solana-banks-server = { path = "../banks-server", version = "=1.7.5" }
[lib]
crate-type = ["lib"]

View File

@@ -11,7 +11,7 @@ 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,
rent::Rent, sysvar::Sysvar,
};
use solana_sdk::{
account::{from_account, Account},
@@ -124,15 +124,19 @@ impl BanksClient {
self.get_fees_with_commitment_and_context(context::current(), CommitmentLevel::default())
}
/// Return the cluster Sysvar
pub fn get_sysvar<T: Sysvar>(&mut self) -> impl Future<Output = io::Result<T>> + '_ {
self.get_account(T::id()).map(|result| {
let sysvar = result?
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Sysvar not present"))?;
from_account::<T, _>(&sysvar)
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Failed to deserialize sysvar"))
})
}
/// Return the cluster rent
pub fn get_rent(&mut self) -> impl Future<Output = io::Result<Rent>> + '_ {
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(|| {
io::Error::new(io::ErrorKind::Other, "Failed to deserialize Rent sysvar")
})
})
self.get_sysvar::<Rent>()
}
/// Return a recent, rooted blockhash from the server. The cluster will only accept
@@ -377,8 +381,8 @@ mod tests {
let mint_pubkey = &genesis.mint_keypair.pubkey();
let bob_pubkey = solana_sdk::pubkey::new_rand();
let instruction = system_instruction::transfer(&mint_pubkey, &bob_pubkey, 1);
let message = Message::new(&[instruction], Some(&mint_pubkey));
let instruction = system_instruction::transfer(mint_pubkey, &bob_pubkey, 1);
let message = Message::new(&[instruction], Some(mint_pubkey));
Runtime::new()?.block_on(async {
let client_transport = start_local_server(bank_forks, block_commitment_cache).await;

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-banks-interface"
version = "1.7.0"
version = "1.7.5"
description = "Solana banks RPC interface"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
@@ -12,7 +12,7 @@ edition = "2018"
[dependencies]
mio = "0.7.6"
serde = { version = "1.0.122", features = ["derive"] }
solana-sdk = { path = "../sdk", version = "=1.7.0" }
solana-sdk = { path = "../sdk", version = "=1.7.5" }
tarpc = { version = "0.24.1", features = ["full"] }
[dev-dependencies]

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-banks-server"
version = "1.7.0"
version = "1.7.5"
description = "Solana banks server"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
@@ -14,10 +14,10 @@ bincode = "1.3.1"
futures = "0.3"
log = "0.4.11"
mio = "0.7.6"
solana-banks-interface = { path = "../banks-interface", version = "=1.7.0" }
solana-runtime = { path = "../runtime", version = "=1.7.0" }
solana-sdk = { path = "../sdk", version = "=1.7.0" }
solana-metrics = { path = "../metrics", version = "=1.7.0" }
solana-banks-interface = { path = "../banks-interface", version = "=1.7.5" }
solana-runtime = { path = "../runtime", version = "=1.7.5" }
solana-sdk = { path = "../sdk", version = "=1.7.5" }
solana-metrics = { path = "../metrics", version = "=1.7.5" }
tarpc = { version = "0.24.1", features = ["full"] }
tokio = { version = "1", features = ["full"] }
tokio-serde = { version = "0.8", features = ["bincode"] }

View File

@@ -131,10 +131,13 @@ impl BanksServer {
}
}
fn verify_transaction(transaction: &Transaction) -> transaction::Result<()> {
fn verify_transaction(
transaction: &Transaction,
libsecp256k1_0_5_upgrade_enabled: bool,
) -> transaction::Result<()> {
if let Err(err) = transaction.verify() {
Err(err)
} else if let Err(err) = transaction.verify_precompiles() {
} else if let Err(err) = transaction.verify_precompiles(libsecp256k1_0_5_upgrade_enabled) {
Err(err)
} else {
Ok(())
@@ -150,7 +153,7 @@ impl Banks for BanksServer {
.read()
.unwrap()
.root_bank()
.get_blockhash_last_valid_slot(&blockhash)
.get_blockhash_last_valid_slot(blockhash)
.unwrap();
let signature = transaction.signatures.get(0).cloned().unwrap_or_default();
let info =
@@ -215,7 +218,10 @@ impl Banks for BanksServer {
transaction: Transaction,
commitment: CommitmentLevel,
) -> Option<transaction::Result<()>> {
if let Err(err) = verify_transaction(&transaction) {
if let Err(err) = verify_transaction(
&transaction,
self.bank(commitment).libsecp256k1_0_5_upgrade_enabled(),
) {
return Some(Err(err));
}

View File

@@ -138,8 +138,8 @@ impl SendTransactionService {
result.retried += 1;
inc_new_counter_info!("send_transaction_service-retry", 1);
Self::send_transaction(
&send_socket,
&tpu_address,
send_socket,
tpu_address,
&transaction_info.wire_transaction,
);
true

View File

@@ -2,7 +2,7 @@
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2018"
name = "solana-bench-exchange"
version = "1.7.0"
version = "1.7.5"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@@ -18,22 +18,22 @@ rand = "0.7.0"
rayon = "1.5.0"
serde_json = "1.0.56"
serde_yaml = "0.8.13"
solana-clap-utils = { path = "../clap-utils", version = "=1.7.0" }
solana-core = { path = "../core", version = "=1.7.0" }
solana-genesis = { path = "../genesis", version = "=1.7.0" }
solana-client = { path = "../client", version = "=1.7.0" }
solana-exchange-program = { path = "../programs/exchange", version = "=1.7.0" }
solana-faucet = { path = "../faucet", version = "=1.7.0" }
solana-gossip = { path = "../gossip", version = "=1.7.0" }
solana-logger = { path = "../logger", version = "=1.7.0" }
solana-metrics = { path = "../metrics", version = "=1.7.0" }
solana-net-utils = { path = "../net-utils", version = "=1.7.0" }
solana-runtime = { path = "../runtime", version = "=1.7.0" }
solana-sdk = { path = "../sdk", version = "=1.7.0" }
solana-version = { path = "../version", version = "=1.7.0" }
solana-clap-utils = { path = "../clap-utils", version = "=1.7.5" }
solana-core = { path = "../core", version = "=1.7.5" }
solana-genesis = { path = "../genesis", version = "=1.7.5" }
solana-client = { path = "../client", version = "=1.7.5" }
solana-exchange-program = { path = "../programs/exchange", version = "=1.7.5" }
solana-faucet = { path = "../faucet", version = "=1.7.5" }
solana-gossip = { path = "../gossip", version = "=1.7.5" }
solana-logger = { path = "../logger", version = "=1.7.5" }
solana-metrics = { path = "../metrics", version = "=1.7.5" }
solana-net-utils = { path = "../net-utils", version = "=1.7.5" }
solana-runtime = { path = "../runtime", version = "=1.7.5" }
solana-sdk = { path = "../sdk", version = "=1.7.5" }
solana-version = { path = "../version", version = "=1.7.5" }
[dev-dependencies]
solana-local-cluster = { path = "../local-cluster", version = "=1.7.0" }
solana-local-cluster = { path = "../local-cluster", version = "=1.7.5" }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -451,13 +451,13 @@ fn swapper<T>(
let to_swap_txs: Vec<_> = to_swap
.par_iter()
.map(|(signer, swap, profit)| {
let s: &Keypair = &signer;
let s: &Keypair = signer;
let owner = &signer.pubkey();
let instruction = exchange_instruction::swap_request(
owner,
&swap.0.pubkey,
&swap.1.pubkey,
&profit,
profit,
);
let message = Message::new(&[instruction], Some(&s.pubkey()));
Transaction::new(&[s], message, blockhash)
@@ -600,7 +600,7 @@ fn trader<T>(
src,
),
];
let message = Message::new(&instructions, Some(&owner_pubkey));
let message = Message::new(&instructions, Some(owner_pubkey));
Transaction::new(&[owner.as_ref(), trade], message, blockhash)
})
.collect();
@@ -739,7 +739,7 @@ pub fn fund_keys<T: Client>(client: &T, source: &Keypair, dests: &[Arc<Keypair>]
let mut to_fund_txs: Vec<_> = chunk
.par_iter()
.map(|(k, m)| {
let instructions = system_instruction::transfer_many(&k.pubkey(), &m);
let instructions = system_instruction::transfer_many(&k.pubkey(), m);
let message = Message::new(&instructions, Some(&k.pubkey()));
(k.clone(), Transaction::new_unsigned(message))
})
@@ -777,7 +777,7 @@ pub fn fund_keys<T: Client>(client: &T, source: &Keypair, dests: &[Arc<Keypair>]
let mut waits = 0;
loop {
sleep(Duration::from_millis(200));
to_fund_txs.retain(|(_, tx)| !verify_funding_transfer(client, &tx, amount));
to_fund_txs.retain(|(_, tx)| !verify_funding_transfer(client, tx, amount));
if to_fund_txs.is_empty() {
break;
}
@@ -836,7 +836,7 @@ pub fn create_token_accounts<T: Client>(
);
let request_ix =
exchange_instruction::account_request(owner_pubkey, &new_keypair.pubkey());
let message = Message::new(&[create_ix, request_ix], Some(&owner_pubkey));
let message = Message::new(&[create_ix, request_ix], Some(owner_pubkey));
(
(from_keypair, new_keypair),
Transaction::new_unsigned(message),
@@ -872,7 +872,7 @@ pub fn create_token_accounts<T: Client>(
let mut waits = 0;
while !to_create_txs.is_empty() {
sleep(Duration::from_millis(200));
to_create_txs.retain(|(_, tx)| !verify_transaction(client, &tx));
to_create_txs.retain(|(_, tx)| !verify_transaction(client, tx));
if to_create_txs.is_empty() {
break;
}
@@ -958,7 +958,7 @@ fn compute_and_report_stats(maxes: &Arc<RwLock<Vec<(String, SampleStats)>>>, tot
fn generate_keypairs(num: u64) -> Vec<Keypair> {
let mut seed = [0_u8; 32];
seed.copy_from_slice(&Keypair::new().pubkey().as_ref());
seed.copy_from_slice(Keypair::new().pubkey().as_ref());
let mut rnd = GenKeys::new(seed);
rnd.gen_n_keypairs(num)
}
@@ -989,7 +989,7 @@ pub fn airdrop_lamports<T: Client>(
let (blockhash, _fee_calculator, _last_valid_slot) = client
.get_recent_blockhash_with_commitment(CommitmentConfig::processed())
.expect("Failed to get blockhash");
match request_airdrop_transaction(&faucet_addr, &id.pubkey(), amount_to_drop, blockhash) {
match request_airdrop_transaction(faucet_addr, &id.pubkey(), amount_to_drop, blockhash) {
Ok(transaction) => {
let signature = client.async_send_transaction(transaction).unwrap();

View File

@@ -2,7 +2,7 @@
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2018"
name = "solana-bench-streamer"
version = "1.7.0"
version = "1.7.5"
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.7.0" }
solana-streamer = { path = "../streamer", version = "=1.7.0" }
solana-logger = { path = "../logger", version = "=1.7.0" }
solana-net-utils = { path = "../net-utils", version = "=1.7.0" }
solana-version = { path = "../version", version = "=1.7.0" }
solana-clap-utils = { path = "../clap-utils", version = "=1.7.5" }
solana-streamer = { path = "../streamer", version = "=1.7.5" }
solana-logger = { path = "../logger", version = "=1.7.5" }
solana-net-utils = { path = "../net-utils", version = "=1.7.5" }
solana-version = { path = "../version", version = "=1.7.5" }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -18,7 +18,7 @@ fn producer(addr: &SocketAddr, exit: Arc<AtomicBool>) -> JoinHandle<()> {
msgs.packets.resize(10, Packet::default());
for w in msgs.packets.iter_mut() {
w.meta.size = PACKET_DATA_SIZE;
w.meta.set_addr(&addr);
w.meta.set_addr(addr);
}
let msgs = Arc::new(msgs);
spawn(move || loop {
@@ -92,6 +92,7 @@ fn main() -> Result<()> {
recycler.clone(),
"bench-streamer-test",
1,
true,
));
}

View File

@@ -2,7 +2,7 @@
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2018"
name = "solana-bench-tps"
version = "1.7.0"
version = "1.7.5"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@@ -15,23 +15,23 @@ log = "0.4.11"
rayon = "1.5.0"
serde_json = "1.0.56"
serde_yaml = "0.8.13"
solana-clap-utils = { path = "../clap-utils", version = "=1.7.0" }
solana-core = { path = "../core", version = "=1.7.0" }
solana-genesis = { path = "../genesis", version = "=1.7.0" }
solana-client = { path = "../client", version = "=1.7.0" }
solana-faucet = { path = "../faucet", version = "=1.7.0" }
solana-gossip = { path = "../gossip", version = "=1.7.0" }
solana-logger = { path = "../logger", version = "=1.7.0" }
solana-metrics = { path = "../metrics", version = "=1.7.0" }
solana-measure = { path = "../measure", version = "=1.7.0" }
solana-net-utils = { path = "../net-utils", version = "=1.7.0" }
solana-runtime = { path = "../runtime", version = "=1.7.0" }
solana-sdk = { path = "../sdk", version = "=1.7.0" }
solana-version = { path = "../version", version = "=1.7.0" }
solana-clap-utils = { path = "../clap-utils", version = "=1.7.5" }
solana-core = { path = "../core", version = "=1.7.5" }
solana-genesis = { path = "../genesis", version = "=1.7.5" }
solana-client = { path = "../client", version = "=1.7.5" }
solana-faucet = { path = "../faucet", version = "=1.7.5" }
solana-gossip = { path = "../gossip", version = "=1.7.5" }
solana-logger = { path = "../logger", version = "=1.7.5" }
solana-metrics = { path = "../metrics", version = "=1.7.5" }
solana-measure = { path = "../measure", version = "=1.7.5" }
solana-net-utils = { path = "../net-utils", version = "=1.7.5" }
solana-runtime = { path = "../runtime", version = "=1.7.5" }
solana-sdk = { path = "../sdk", version = "=1.7.5" }
solana-version = { path = "../version", version = "=1.7.5" }
[dev-dependencies]
serial_test = "0.4.0"
solana-local-cluster = { path = "../local-cluster", version = "=1.7.0" }
solana-local-cluster = { path = "../local-cluster", version = "=1.7.5" }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -544,12 +544,12 @@ impl<'a> FundingTransactions<'a> for Vec<(&'a Keypair, Transaction)> {
// re-sign retained to_fund_txes with updated blockhash
self.sign(blockhash);
self.send(&client);
self.send(client);
// Sleep a few slots to allow transactions to process
sleep(Duration::from_secs(1));
self.verify(&client, to_lamports);
self.verify(client, to_lamports);
// retry anything that seems to have dropped through cracks
// again since these txs are all or nothing, they're fine to
@@ -564,7 +564,7 @@ impl<'a> FundingTransactions<'a> for Vec<(&'a Keypair, Transaction)> {
let to_fund_txs: Vec<(&Keypair, Transaction)> = to_fund
.par_iter()
.map(|(k, t)| {
let instructions = system_instruction::transfer_many(&k.pubkey(), &t);
let instructions = system_instruction::transfer_many(&k.pubkey(), t);
let message = Message::new(&instructions, Some(&k.pubkey()));
(*k, Transaction::new_unsigned(message))
})
@@ -617,7 +617,7 @@ impl<'a> FundingTransactions<'a> for Vec<(&'a Keypair, Transaction)> {
return None;
}
let verified = if verify_funding_transfer(&client, &tx, to_lamports) {
let verified = if verify_funding_transfer(&client, tx, to_lamports) {
verified_txs.fetch_add(1, Ordering::Relaxed);
Some(k.pubkey())
} else {
@@ -733,7 +733,7 @@ pub fn airdrop_lamports<T: Client>(
);
let (blockhash, _fee_calculator) = get_recent_blockhash(client);
match request_airdrop_transaction(&faucet_addr, &id.pubkey(), airdrop_amount, blockhash) {
match request_airdrop_transaction(faucet_addr, &id.pubkey(), airdrop_amount, blockhash) {
Ok(transaction) => {
let mut tries = 0;
loop {

View File

@@ -39,7 +39,7 @@ fn main() {
let keypair_count = *tx_count * keypair_multiplier;
if *write_to_client_file {
info!("Generating {} keypairs", keypair_count);
let (keypairs, _) = generate_keypairs(&id, keypair_count as u64);
let (keypairs, _) = generate_keypairs(id, keypair_count as u64);
let num_accounts = keypairs.len() as u64;
let max_fee =
FeeRateGovernor::new(*target_lamports_per_signature, 0).max_lamports_per_signature;
@@ -68,7 +68,7 @@ fn main() {
}
info!("Connecting to the cluster");
let nodes = discover_cluster(&entrypoint_addr, *num_nodes).unwrap_or_else(|err| {
let nodes = discover_cluster(entrypoint_addr, *num_nodes).unwrap_or_else(|err| {
eprintln!("Failed to discover {} nodes: {:?}", num_nodes, err);
exit(1);
});
@@ -135,7 +135,7 @@ fn main() {
generate_and_fund_keypairs(
client.clone(),
Some(*faucet_addr),
&id,
id,
keypair_count,
*num_lamports_per_account,
)

View File

@@ -148,6 +148,33 @@ all_test_steps() {
command_step stable ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_stable_docker_image ci/test-stable.sh" 60
wait_step
# BPF test suite
if affects \
.rs$ \
Cargo.lock$ \
Cargo.toml$ \
^ci/rust-version.sh \
^ci/test-stable-bpf.sh \
^ci/test-stable.sh \
^ci/test-local-cluster.sh \
^core/build.rs \
^fetch-perf-libs.sh \
^programs/ \
^sdk/ \
; then
cat >> "$output_file" <<"EOF"
- command: "ci/test-stable-bpf.sh"
name: "stable-bpf"
timeout_in_minutes: 20
artifact_paths: "bpf-dumps.tar.bz2"
agents:
- "queue=default"
EOF
else
annotate --style info \
"Stable-BPF skipped as no relevant files were modified"
fi
# Perf test suite
if affects \
.rs$ \
@@ -165,7 +192,7 @@ all_test_steps() {
cat >> "$output_file" <<"EOF"
- command: "ci/test-stable-perf.sh"
name: "stable-perf"
timeout_in_minutes: 40
timeout_in_minutes: 20
artifact_paths: "log-*.txt"
agents:
- "queue=cuda"

View File

@@ -3,13 +3,19 @@
# Pull requests to not run these steps.
steps:
- command: "ci/publish-tarball.sh"
agents:
- "queue=release-build"
timeout_in_minutes: 60
name: "publish tarball"
- wait
- command: "sdk/docker-solana/build.sh"
agents:
- "queue=release-build"
timeout_in_minutes: 60
name: "publish docker"
- command: "ci/publish-crate.sh"
agents:
- "queue=release-build"
timeout_in_minutes: 240
name: "publish crate"
branches: "!master"

View File

@@ -74,10 +74,13 @@ else
export CI_BUILD_ID=
export CI_COMMIT=
export CI_JOB_ID=
export CI_OS_NAME=
export CI_PULL_REQUEST=
export CI_REPO_SLUG=
export CI_TAG=
# Don't override ci/run-local.sh
if [[ -z $CL_LOCAL_RUN ]]; then
export CI_OS_NAME=
fi
fi
cat <<EOF

View File

@@ -127,7 +127,7 @@ startNode() {
waitForNodeToInit() {
declare initCompleteFile=$1
while [[ ! -r $initCompleteFile ]]; do
if [[ $SECONDS -ge 240 ]]; then
if [[ $SECONDS -ge 300 ]]; then
echo "^^^ +++"
echo "Error: $initCompleteFile not found in $SECONDS seconds"
exit 1

View File

@@ -12,10 +12,14 @@ import json
import subprocess
import sys;
real_file = os.path.realpath(__file__)
ci_path = os.path.dirname(real_file)
src_root = os.path.dirname(ci_path)
def load_metadata():
cmd = f'{src_root}/cargo metadata --no-deps --format-version=1'
return json.loads(subprocess.Popen(
'cargo metadata --no-deps --format-version=1',
shell=True, stdout=subprocess.PIPE).communicate()[0])
cmd, shell=True, stdout=subprocess.PIPE).communicate()[0])
def get_packages():
metadata = load_metadata()

55
ci/run-local.sh Executable file
View File

@@ -0,0 +1,55 @@
#!/usr/bin/env bash
cd "$(dirname "$0")/.."
export CI_LOCAL_RUN=true
set -e
case $(uname -o) in
*/Linux)
export CI_OS_NAME=linux
;;
*)
echo "local CI runs are only supported on Linux" 1>&2
exit 1
;;
esac
steps=()
steps+=(test-sanity)
steps+=(shellcheck)
steps+=(test-checks)
steps+=(test-coverage)
steps+=(test-stable)
steps+=(test-stable-bpf)
steps+=(test-stable-perf)
steps+=(test-downstream-builds)
steps+=(test-bench)
steps+=(test-local-cluster)
step_index=0
if [[ -n "$1" ]]; then
start_step="$1"
while [[ $step_index -lt ${#steps[@]} ]]; do
step="${steps[$step_index]}"
if [[ "$step" = "$start_step" ]]; then
break
fi
step_index=$((step_index + 1))
done
if [[ $step_index -eq ${#steps[@]} ]]; then
echo "unexpected start step: \"$start_step\"" 1>&2
exit 1
else
echo "** starting at step: \"$start_step\" **"
echo
fi
fi
while [[ $step_index -lt ${#steps[@]} ]]; do
step="${steps[$step_index]}"
cmd="ci/${step}.sh"
$cmd
step_index=$((step_index + 1))
done

View File

@@ -49,6 +49,10 @@ _ "$cargo" nightly bench --manifest-path runtime/Cargo.toml ${V:+--verbose} \
_ "$cargo" nightly bench --manifest-path gossip/Cargo.toml ${V:+--verbose} \
-- -Z unstable-options --format=json | tee -a "$BENCH_FILE"
# Run poh benches
_ "$cargo" nightly bench --manifest-path poh/Cargo.toml ${V:+--verbose} \
-- -Z unstable-options --format=json | tee -a "$BENCH_FILE"
# Run core benches
_ "$cargo" nightly bench --manifest-path core/Cargo.toml ${V:+--verbose} \
-- -Z unstable-options --format=json | tee -a "$BENCH_FILE"

View File

@@ -35,8 +35,10 @@ echo --- build environment
"$cargo" stable clippy --version --verbose
"$cargo" nightly clippy --version --verbose
# audit is done only with stable
# audit is done only with "$cargo stable"
"$cargo" stable audit --version
grcov --version
)
export RUST_BACKTRACE=1
@@ -65,7 +67,8 @@ _ ci/order-crates-for-publishing.py
# -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 --deny=clippy::integer_arithmetic --allow=clippy::inconsistent_struct_constructor
_ "$cargo" stable fmt --all -- --check

9
ci/test-downstream-builds.sh Executable file
View File

@@ -0,0 +1,9 @@
#!/usr/bin/env bash
cd "$(dirname "$0")/.."
export CI_LOCAL_RUN=true
set -ex
scripts/build-downstream-projects.sh

1
ci/test-stable-bpf.sh Symbolic link
View File

@@ -0,0 +1 @@
test-stable.sh

View File

@@ -21,10 +21,6 @@ export RUST_BACKTRACE=1
export RUSTFLAGS="-D warnings"
source scripts/ulimit-n.sh
# Clear the C dependency files, if dependency moves these files are not regenerated
test -d target/debug/bpf && find target/debug/bpf -name '*.d' -delete
test -d target/release/bpf && find target/release/bpf -name '*.d' -delete
# Limit compiler jobs to reduce memory usage
# on machines with 2gb/thread of memory
NPROC=$(nproc)
@@ -35,17 +31,25 @@ case $testName in
test-stable)
_ "$cargo" stable test --jobs "$NPROC" --all --exclude solana-local-cluster ${V:+--verbose} -- --nocapture
;;
test-stable-perf)
test-stable-bpf)
# Clear the C dependency files, if dependency moves these files are not regenerated
test -d target/debug/bpf && find target/debug/bpf -name '*.d' -delete
test -d target/release/bpf && find target/release/bpf -name '*.d' -delete
# rustfilt required for dumping BPF assembly listings
"$cargo" install rustfilt
# solana-keygen required when building C programs
_ "$cargo" build --manifest-path=keygen/Cargo.toml
export PATH="$PWD/target/debug":$PATH
cargo_build_bpf="$(realpath ./cargo-build-bpf)"
# BPF solana-sdk legacy compile test
./cargo-build-bpf --manifest-path sdk/Cargo.toml
"$cargo_build_bpf" --manifest-path sdk/Cargo.toml
# BPF Program unit tests
"$cargo" test --manifest-path programs/bpf/Cargo.toml
cargo-build-bpf --manifest-path programs/bpf/Cargo.toml --bpf-sdk sdk/bpf
"$cargo_build_bpf" --manifest-path programs/bpf/Cargo.toml --bpf-sdk sdk/bpf
# BPF program system tests
_ make -C programs/bpf/c tests
@@ -53,11 +57,31 @@ test-stable-perf)
--manifest-path programs/bpf/Cargo.toml \
--no-default-features --features=bpf_c,bpf_rust -- --nocapture
# Dump BPF program assembly listings
for bpf_test in programs/bpf/rust/*; do
if pushd "$bpf_test"; then
"$cargo_build_bpf" --dump
popd
fi
done
# BPF program instruction count assertion
bpf_target_path=programs/bpf/target
_ "$cargo" stable test \
--manifest-path programs/bpf/Cargo.toml \
--no-default-features --features=bpf_c,bpf_rust assert_instruction_count \
-- --nocapture &> "${bpf_target_path}"/deploy/instuction_counts.txt
bpf_dump_archive="bpf-dumps.tar.bz2"
rm -f "$bpf_dump_archive"
tar cjvf "$bpf_dump_archive" "${bpf_target_path}"/{deploy/*.txt,bpfel-unknown-unknown/release/*.so}
;;
test-stable-perf)
if [[ $(uname) = Linux ]]; then
# Enable persistence mode to keep the CUDA kernel driver loaded, avoiding a
# lengthy and unexpected delay the first time CUDA is involved when the driver
# is not yet loaded.
sudo --non-interactive ./net/scripts/enable-nvidia-persistence-mode.sh
sudo --non-interactive ./net/scripts/enable-nvidia-persistence-mode.sh || true
rm -rf target/perf-libs
./fetch-perf-libs.sh

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-clap-utils"
version = "1.7.0"
version = "1.7.5"
description = "Solana utilities for the clap"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
@@ -12,8 +12,8 @@ edition = "2018"
[dependencies]
clap = "2.33.0"
rpassword = "4.0"
solana-remote-wallet = { path = "../remote-wallet", version = "=1.7.0" }
solana-sdk = { path = "../sdk", version = "=1.7.0" }
solana-remote-wallet = { path = "../remote-wallet", version = "=1.7.5" }
solana-sdk = { path = "../sdk", version = "=1.7.5" }
thiserror = "1.0.21"
tiny-bip39 = "0.8.0"
uriparse = "0.6.3"

View File

@@ -24,9 +24,11 @@ use {
},
},
std::{
cell::RefCell,
convert::TryFrom,
error,
io::{stdin, stdout, Write},
ops::Deref,
process::exit,
str::FromStr,
sync::Arc,
@@ -89,33 +91,49 @@ impl CliSignerInfo {
.collect()
}
}
#[derive(Debug)]
#[derive(Debug, Default)]
pub struct DefaultSigner {
pub arg_name: String,
pub path: String,
is_path_checked: RefCell<bool>,
}
impl DefaultSigner {
pub fn new(path: String) -> Self {
pub fn new<AN: AsRef<str>, P: AsRef<str>>(arg_name: AN, path: P) -> Self {
let arg_name = arg_name.as_ref().to_string();
let path = path.as_ref().to_string();
Self {
arg_name: "keypair".to_string(),
arg_name,
path,
..Self::default()
}
}
pub fn from_path(path: String) -> Result<Self, Box<dyn error::Error>> {
std::fs::metadata(&path)
.map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"No default signer found, run \"solana-keygen new -o {}\" to create a new one",
path
),
)
.into()
})
.map(|_| Self::new(path))
fn path(&self) -> Result<&str, Box<dyn std::error::Error>> {
if !self.is_path_checked.borrow().deref() {
parse_signer_source(&self.path)
.and_then(|s| {
if let SignerSourceKind::Filepath(path) = &s.kind {
std::fs::metadata(path).map(|_| ()).map_err(|e| e.into())
} else {
Ok(())
}
})
.map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"No default signer found, run \"solana-keygen new -o {}\" to create a new one",
self.path
),
)
})?;
*self.is_path_checked.borrow_mut() = true;
}
Ok(&self.path)
}
pub fn generate_unique_signers(
&self,
bulk_signers: Vec<Option<Box<dyn Signer>>>,
@@ -145,7 +163,7 @@ impl DefaultSigner {
matches: &ArgMatches,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<Box<dyn Signer>, Box<dyn std::error::Error>> {
signer_from_path(matches, &self.path, &self.arg_name, wallet_manager)
signer_from_path(matches, self.path()?, &self.arg_name, wallet_manager)
}
pub fn signer_from_path_with_config(
@@ -154,7 +172,13 @@ impl DefaultSigner {
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
config: &SignerFromPathConfig,
) -> Result<Box<dyn Signer>, Box<dyn std::error::Error>> {
signer_from_path_with_config(matches, &self.path, &self.arg_name, wallet_manager, config)
signer_from_path_with_config(
matches,
self.path()?,
&self.arg_name,
wallet_manager,
config,
)
}
}
@@ -277,7 +301,9 @@ pub(crate) fn parse_signer_source<S: AsRef<str>>(
ASK_KEYWORD => Ok(SignerSource::new_legacy(SignerSourceKind::Prompt)),
_ => match Pubkey::from_str(source.as_str()) {
Ok(pubkey) => Ok(SignerSource::new(SignerSourceKind::Pubkey(pubkey))),
Err(_) => Ok(SignerSource::new(SignerSourceKind::Filepath(source))),
Err(_) => std::fs::metadata(source.as_str())
.map(|_| SignerSource::new(SignerSourceKind::Filepath(source)))
.map_err(|err| err.into()),
},
}
}
@@ -480,7 +506,7 @@ pub const SKIP_SEED_PHRASE_VALIDATION_ARG: ArgConstant<'static> = ArgConstant {
/// Prompts user for a passphrase and then asks for confirmirmation to check for mistakes
pub fn prompt_passphrase(prompt: &str) -> Result<String, Box<dyn error::Error>> {
let passphrase = prompt_password_stderr(&prompt)?;
let passphrase = prompt_password_stderr(prompt)?;
if !passphrase.is_empty() {
let confirmed = rpassword::prompt_password_stderr("Enter same passphrase again: ")?;
if confirmed != passphrase {
@@ -560,9 +586,9 @@ pub fn keypair_from_seed_phrase(
let keypair = if skip_validation {
let passphrase = prompt_passphrase(&passphrase_prompt)?;
if legacy {
keypair_from_seed_phrase_and_passphrase(&seed_phrase, &passphrase)?
keypair_from_seed_phrase_and_passphrase(seed_phrase, &passphrase)?
} else {
let seed = generate_seed_from_seed_phrase_and_passphrase(&seed_phrase, &passphrase);
let seed = generate_seed_from_seed_phrase_and_passphrase(seed_phrase, &passphrase);
keypair_from_seed_and_derivation_path(&seed, derivation_path)?
}
} else {
@@ -590,7 +616,7 @@ pub fn keypair_from_seed_phrase(
if legacy {
keypair_from_seed(seed.as_bytes())?
} else {
keypair_from_seed_and_derivation_path(&seed.as_bytes(), derivation_path)?
keypair_from_seed_and_derivation_path(seed.as_bytes(), derivation_path)?
}
};
@@ -751,6 +777,10 @@ mod tests {
// Catchall into SignerSource::Filepath fails
let junk = "sometextthatisnotapubkeyorfile".to_string();
assert!(Pubkey::from_str(&junk).is_err());
assert!(matches!(
parse_signer_source(&junk),
Err(SignerSourceError::IoError(_))
));
let prompt = "prompt:".to_string();
assert!(matches!(

View File

@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2018"
name = "solana-cli-config"
description = "Blockchain, Rebuilt for Scale"
version = "1.7.0"
version = "1.7.5"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"

View File

@@ -107,24 +107,24 @@ mod test {
#[test]
fn compute_websocket_url() {
assert_eq!(
Config::compute_websocket_url(&"http://api.devnet.solana.com"),
Config::compute_websocket_url("http://api.devnet.solana.com"),
"ws://api.devnet.solana.com/".to_string()
);
assert_eq!(
Config::compute_websocket_url(&"https://api.devnet.solana.com"),
Config::compute_websocket_url("https://api.devnet.solana.com"),
"wss://api.devnet.solana.com/".to_string()
);
assert_eq!(
Config::compute_websocket_url(&"http://example.com:8899"),
Config::compute_websocket_url("http://example.com:8899"),
"ws://example.com:8900/".to_string()
);
assert_eq!(
Config::compute_websocket_url(&"https://example.com:1234"),
Config::compute_websocket_url("https://example.com:1234"),
"wss://example.com:1235/".to_string()
);
assert_eq!(Config::compute_websocket_url(&"garbage"), String::new());
assert_eq!(Config::compute_websocket_url("garbage"), String::new());
}
}

View File

@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2018"
name = "solana-cli-output"
description = "Blockchain, Rebuilt for Scale"
version = "1.7.0"
version = "1.7.5"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@@ -12,20 +12,19 @@ documentation = "https://docs.rs/solana-cli-output"
[dependencies]
base64 = "0.13.0"
chrono = { version = "0.4.11", features = ["serde"] }
console = "0.11.3"
console = "0.14.1"
humantime = "2.0.1"
Inflector = "0.11.4"
indicatif = "0.15.0"
serde = "1.0.122"
serde_derive = "1.0.103"
serde_json = "1.0.56"
solana-account-decoder = { path = "../account-decoder", version = "=1.7.0" }
solana-clap-utils = { path = "../clap-utils", version = "=1.7.0" }
solana-client = { path = "../client", version = "=1.7.0" }
solana-sdk = { path = "../sdk", version = "=1.7.0" }
solana-stake-program = { path = "../programs/stake", version = "=1.7.0" }
solana-transaction-status = { path = "../transaction-status", version = "=1.7.0" }
solana-vote-program = { path = "../programs/vote", version = "=1.7.0" }
solana-account-decoder = { path = "../account-decoder", version = "=1.7.5" }
solana-clap-utils = { path = "../clap-utils", version = "=1.7.5" }
solana-client = { path = "../client", version = "=1.7.5" }
solana-sdk = { path = "../sdk", version = "=1.7.5" }
solana-transaction-status = { path = "../transaction-status", version = "=1.7.5" }
solana-vote-program = { path = "../programs/vote", version = "=1.7.5" }
spl-memo = { version = "=3.0.1", features = ["no-entrypoint"] }
[package.metadata.docs.rs]

View File

@@ -25,10 +25,10 @@ use {
native_token::lamports_to_sol,
pubkey::Pubkey,
signature::Signature,
stake::state::{Authorized, Lockup},
stake_history::StakeHistoryEntry,
transaction::{Transaction, TransactionError},
},
solana_stake_program::stake_state::{Authorized, Lockup},
solana_transaction_status::{
EncodedConfirmedBlock, EncodedTransaction, TransactionConfirmationStatus,
UiTransactionStatusMeta,
@@ -233,6 +233,10 @@ pub struct CliEpochInfo {
pub epoch_info: EpochInfo,
#[serde(skip)]
pub average_slot_time_ms: u64,
#[serde(skip)]
pub start_block_time: Option<UnixTimestamp>,
#[serde(skip)]
pub current_block_time: Option<UnixTimestamp>,
}
impl QuietDisplay for CliEpochInfo {}
@@ -277,21 +281,41 @@ impl fmt::Display for CliEpochInfo {
remaining_slots_in_epoch
),
)?;
let (time_elapsed, annotation) = if let (Some(start_block_time), Some(current_block_time)) =
(self.start_block_time, self.current_block_time)
{
(
Duration::from_secs((current_block_time - start_block_time) as u64),
None,
)
} else {
(
slot_to_duration(self.epoch_info.slot_index, self.average_slot_time_ms),
Some("* estimated based on current slot durations"),
)
};
let time_remaining = slot_to_duration(remaining_slots_in_epoch, self.average_slot_time_ms);
writeln_name_value(
f,
"Epoch Completed Time:",
&format!(
"{}/{} ({} remaining)",
slot_to_human_time(self.epoch_info.slot_index, self.average_slot_time_ms),
slot_to_human_time(self.epoch_info.slots_in_epoch, self.average_slot_time_ms),
slot_to_human_time(remaining_slots_in_epoch, self.average_slot_time_ms)
"{}{}/{} ({} remaining)",
humantime::format_duration(time_elapsed).to_string(),
if annotation.is_some() { "*" } else { "" },
humantime::format_duration(time_elapsed + time_remaining).to_string(),
humantime::format_duration(time_remaining).to_string(),
),
)
)?;
if let Some(annotation) = annotation {
writeln!(f)?;
writeln!(f, "{}", annotation)?;
}
Ok(())
}
}
fn slot_to_human_time(slot: Slot, slot_time_ms: u64) -> String {
humantime::format_duration(Duration::from_secs((slot * slot_time_ms) / 1000)).to_string()
fn slot_to_duration(slot: Slot, slot_time_ms: u64) -> Duration {
Duration::from_secs((slot * slot_time_ms) / 1000)
}
#[derive(Serialize, Deserialize, Default)]
@@ -323,6 +347,8 @@ pub struct CliValidators {
pub total_current_stake: u64,
pub total_delinquent_stake: u64,
pub validators: Vec<CliValidator>,
pub average_skip_rate: f64,
pub average_stake_weighted_skip_rate: f64,
#[serde(skip_serializing)]
pub validators_sort_order: CliValidatorsSortOrder,
#[serde(skip_serializing)]
@@ -486,6 +512,18 @@ impl fmt::Display for CliValidators {
writeln!(f, "{}", header)?;
}
writeln!(f)?;
writeln_name_value(
f,
"Average Stake-Weighted Skip Rate:",
&format!("{:.2}%", self.average_stake_weighted_skip_rate,),
)?;
writeln_name_value(
f,
"Average Unweighted Skip Rate: ",
&format!("{:.2}%", self.average_skip_rate),
)?;
writeln!(f)?;
writeln_name_value(
f,
@@ -733,6 +771,7 @@ pub struct CliEpochReward {
pub post_balance: u64, // lamports
pub percent_change: f64,
pub apr: Option<f64>,
pub commission: Option<u8>,
}
#[derive(Serialize, Deserialize)]
@@ -777,23 +816,27 @@ impl fmt::Display for CliKeyedEpochRewards {
writeln!(f, "Epoch Rewards:")?;
writeln!(
f,
" {:<44} {:<18} {:<18} {:>14} {:>14}",
"Address", "Amount", "New Balance", "Percent Change", "APR"
" {:<44} {:<18} {:<18} {:>14} {:>14} {:>10}",
"Address", "Amount", "New Balance", "Percent Change", "APR", "Commission"
)?;
for keyed_reward in &self.rewards {
match &keyed_reward.reward {
Some(reward) => {
writeln!(
f,
" {:<44} ◎{:<17.9} ◎{:<17.9} {:>13.2}% {}",
" {:<44} ◎{:<17.9} ◎{:<17.9} {:>13.9}% {:>14} {:>10}",
keyed_reward.address,
lamports_to_sol(reward.amount),
lamports_to_sol(reward.post_balance),
reward.percent_change,
reward
.apr
.map(|apr| format!("{:>13.2}%", apr))
.map(|apr| format!("{:.2}%", apr))
.unwrap_or_default(),
reward
.commission
.map(|commission| format!("{}%", commission))
.unwrap_or_else(|| "-".to_string())
)?;
}
None => {
@@ -910,13 +953,13 @@ fn show_epoch_rewards(
writeln!(f, "Epoch Rewards:")?;
writeln!(
f,
" {:<6} {:<11} {:<18} {:<18} {:>14} {:>14}",
"Epoch", "Reward Slot", "Amount", "New Balance", "Percent Change", "APR"
" {:<6} {:<11} {:<18} {:<18} {:>14} {:>14} {:>10}",
"Epoch", "Reward Slot", "Amount", "New Balance", "Percent Change", "APR", "Commission"
)?;
for reward in epoch_rewards {
writeln!(
f,
" {:<6} {:<11} ◎{:<17.9} ◎{:<17.9} {:>13.2}% {}",
" {:<6} {:<11} ◎{:<17.9} ◎{:<17.9} {:>13.9}% {:>14} {:>10}",
reward.epoch,
reward.effective_slot,
lamports_to_sol(reward.amount),
@@ -924,8 +967,12 @@ fn show_epoch_rewards(
reward.percent_change,
reward
.apr
.map(|apr| format!("{:>13.2}%", apr))
.map(|apr| format!("{:.2}%", apr))
.unwrap_or_default(),
reward
.commission
.map(|commission| format!("{}%", commission))
.unwrap_or_else(|| "-".to_string())
)?;
}
}
@@ -1287,7 +1334,7 @@ impl fmt::Display for CliValidatorInfo {
writeln_name_value(
f,
&format!(" {}:", to_title_case(key)),
&value.as_str().unwrap_or("?"),
value.as_str().unwrap_or("?"),
)?;
}
Ok(())
@@ -1325,8 +1372,8 @@ impl fmt::Display for CliVoteAccount {
build_balance_message(self.account_balance, self.use_lamports_unit, true)
)?;
writeln!(f, "Validator Identity: {}", self.validator_identity)?;
writeln!(f, "Authorized Voters: {}", self.authorized_voters)?;
writeln!(f, "Authorized Withdrawer: {}", self.authorized_withdrawer)?;
writeln!(f, "Vote Authority: {}", self.authorized_voters)?;
writeln!(f, "Withdraw Authority: {}", self.authorized_withdrawer)?;
writeln!(f, "Credits: {}", self.credits)?;
writeln!(f, "Commission: {}%", self.commission)?;
writeln!(
@@ -1513,11 +1560,15 @@ impl fmt::Display for CliInflation {
"Staking rate: {:>5.2}%",
self.current_rate.validator * 100.
)?;
writeln!(
f,
"Foundation rate: {:>5.2}%",
self.current_rate.foundation * 100.
)
if self.current_rate.foundation > 0. {
writeln!(
f,
"Foundation rate: {:>5.2}%",
self.current_rate.foundation * 100.
)?;
}
Ok(())
}
}
@@ -1669,6 +1720,7 @@ pub struct CliFeesInner {
pub blockhash: String,
pub lamports_per_signature: u64,
pub last_valid_slot: Option<Slot>,
pub last_valid_block_height: Option<Slot>,
}
impl QuietDisplay for CliFeesInner {}
@@ -1682,11 +1734,11 @@ impl fmt::Display for CliFeesInner {
"Lamports per signature:",
&self.lamports_per_signature.to_string(),
)?;
let last_valid_slot = self
.last_valid_slot
let last_valid_block_height = self
.last_valid_block_height
.map(|s| s.to_string())
.unwrap_or_default();
writeln_name_value(f, "Last valid slot:", &last_valid_slot)
writeln_name_value(f, "Last valid block height:", &last_valid_block_height)
}
}
@@ -1715,6 +1767,7 @@ impl CliFees {
blockhash: Hash,
lamports_per_signature: u64,
last_valid_slot: Option<Slot>,
last_valid_block_height: Option<Slot>,
) -> Self {
Self {
inner: Some(CliFeesInner {
@@ -1722,6 +1775,7 @@ impl CliFees {
blockhash: blockhash.to_string(),
lamports_per_signature,
last_valid_slot,
last_valid_block_height,
}),
}
}
@@ -1768,7 +1822,7 @@ impl fmt::Display for CliTokenAccount {
writeln_name_value(
f,
"Close authority:",
&account.close_authority.as_ref().unwrap_or(&String::new()),
account.close_authority.as_ref().unwrap_or(&String::new()),
)?;
Ok(())
}
@@ -2006,7 +2060,7 @@ pub fn return_signers_with_config(
}
pub fn parse_sign_only_reply_string(reply: &str) -> SignOnly {
let object: Value = serde_json::from_str(&reply).unwrap();
let object: Value = serde_json::from_str(reply).unwrap();
let blockhash_str = object.get("blockhash").unwrap().as_str().unwrap();
let blockhash = blockhash_str.parse::<Hash>().unwrap();
let mut present_signers: Vec<(Pubkey, Signature)> = Vec::new();
@@ -2136,8 +2190,8 @@ impl fmt::Display for CliBlock {
writeln!(f, "Rewards:")?;
writeln!(
f,
" {:<44} {:^15} {:<15} {:<20} {:>14}",
"Address", "Type", "Amount", "New Balance", "Percent Change"
" {:<44} {:^15} {:<15} {:<20} {:>14} {:>10}",
"Address", "Type", "Amount", "New Balance", "Percent Change", "Commission"
)?;
for reward in rewards {
let sign = if reward.lamports < 0 { "-" } else { "" };
@@ -2145,7 +2199,7 @@ impl fmt::Display for CliBlock {
total_rewards += reward.lamports;
writeln!(
f,
" {:<44} {:^15} {:>15} {}",
" {:<44} {:^15} {:>15} {} {}",
reward.pubkey,
if let Some(reward_type) = reward.reward_type {
format!("{}", reward_type)
@@ -2167,7 +2221,11 @@ impl fmt::Display for CliBlock {
/ (reward.post_balance as f64 - reward.lamports as f64))
* 100.0
)
}
},
reward
.commission
.map(|commission| format!("{:>9}%", commission))
.unwrap_or_else(|| " -".to_string())
)?;
}
@@ -2408,6 +2466,10 @@ mod tests {
fn try_sign_message(&self, _message: &[u8]) -> Result<Signature, SignerError> {
Ok(Signature::new(&[1u8; 64]))
}
fn is_interactive(&self) -> bool {
false
}
}
let present: Box<dyn Signer> = Box::new(keypair_from_seed(&[2u8; 32]).unwrap());

View File

@@ -5,7 +5,7 @@ use {
indicatif::{ProgressBar, ProgressStyle},
solana_sdk::{
clock::UnixTimestamp, hash::Hash, message::Message, native_token::lamports_to_sol,
program_utils::limited_deserialize, pubkey::Pubkey, transaction::Transaction,
program_utils::limited_deserialize, pubkey::Pubkey, stake, transaction::Transaction,
},
solana_transaction_status::UiTransactionStatusMeta,
spl_memo::id as spl_memo_id,
@@ -140,7 +140,7 @@ fn format_account_mode(message: &Message, index: usize) -> String {
} else {
"-"
},
if message.is_writable(index, /*demote_sysvar_write_locks=*/ true) {
if message.is_writable(index) {
"w" // comment for consistent rust fmt (no joking; lol)
} else {
"-"
@@ -244,10 +244,9 @@ pub fn write_transaction<W: io::Write>(
writeln!(w, "{} {:?}", prefix, vote_instruction)?;
raw = false;
}
} else if program_pubkey == solana_stake_program::id() {
if let Ok(stake_instruction) = limited_deserialize::<
solana_stake_program::stake_instruction::StakeInstruction,
>(&instruction.data)
} else if program_pubkey == stake::program::id() {
if let Ok(stake_instruction) =
limited_deserialize::<stake::instruction::StakeInstruction>(&instruction.data)
{
writeln!(w, "{} {:?}", prefix, stake_instruction)?;
raw = false;

View File

@@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2018"
name = "solana-cli"
description = "Blockchain, Rebuilt for Scale"
version = "1.7.0"
version = "1.7.5"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@@ -16,7 +16,8 @@ chrono = { version = "0.4.11", features = ["serde"] }
clap = "2.33.1"
criterion-stats = "0.3.0"
ctrlc = { version = "3.1.5", features = ["termination"] }
console = "0.11.3"
console = "0.14.1"
const_format = "0.2.14"
dirs-next = "2.0.0"
log = "0.4.11"
Inflector = "0.11.4"
@@ -28,30 +29,29 @@ reqwest = { version = "0.11.2", default-features = false, features = ["blocking"
serde = "1.0.122"
serde_derive = "1.0.103"
serde_json = "1.0.56"
solana-account-decoder = { path = "../account-decoder", version = "=1.7.0" }
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.7.0" }
solana-clap-utils = { path = "../clap-utils", version = "=1.7.0" }
solana-cli-config = { path = "../cli-config", version = "=1.7.0" }
solana-cli-output = { path = "../cli-output", version = "=1.7.0" }
solana-client = { path = "../client", version = "=1.7.0" }
solana-config-program = { path = "../programs/config", version = "=1.7.0" }
solana-faucet = { path = "../faucet", version = "=1.7.0" }
solana-logger = { path = "../logger", version = "=1.7.0" }
solana-net-utils = { path = "../net-utils", version = "=1.7.0" }
solana-account-decoder = { path = "../account-decoder", version = "=1.7.5" }
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.7.5" }
solana-clap-utils = { path = "../clap-utils", version = "=1.7.5" }
solana-cli-config = { path = "../cli-config", version = "=1.7.5" }
solana-cli-output = { path = "../cli-output", version = "=1.7.5" }
solana-client = { path = "../client", version = "=1.7.5" }
solana-config-program = { path = "../programs/config", version = "=1.7.5" }
solana-faucet = { path = "../faucet", version = "=1.7.5" }
solana-logger = { path = "../logger", version = "=1.7.5" }
solana-net-utils = { path = "../net-utils", version = "=1.7.5" }
solana_rbpf = "=0.2.11"
solana-remote-wallet = { path = "../remote-wallet", version = "=1.7.0" }
solana-sdk = { path = "../sdk", version = "=1.7.0" }
solana-stake-program = { path = "../programs/stake", version = "=1.7.0" }
solana-transaction-status = { path = "../transaction-status", version = "=1.7.0" }
solana-version = { path = "../version", version = "=1.7.0" }
solana-vote-program = { path = "../programs/vote", version = "=1.7.0" }
solana-remote-wallet = { path = "../remote-wallet", version = "=1.7.5" }
solana-sdk = { path = "../sdk", version = "=1.7.5" }
solana-transaction-status = { path = "../transaction-status", version = "=1.7.5" }
solana-version = { path = "../version", version = "=1.7.5" }
solana-vote-program = { path = "../programs/vote", version = "=1.7.5" }
spl-memo = { version = "=3.0.1", features = ["no-entrypoint"] }
thiserror = "1.0.21"
tiny-bip39 = "0.7.0"
url = "2.1.1"
[dev-dependencies]
solana-core = { path = "../core", version = "=1.7.0" }
solana-core = { path = "../core", version = "=1.7.5" }
tempfile = "3.1.0"
[[bin]]

View File

@@ -25,7 +25,7 @@ use solana_cli_output::{
};
use solana_client::{
blockhash_query::BlockhashQuery,
client_error::{ClientError, ClientErrorKind, Result as ClientResult},
client_error::{ClientError, Result as ClientResult},
nonce_utils,
rpc_client::RpcClient,
rpc_config::{
@@ -44,14 +44,11 @@ use solana_sdk::{
message::Message,
pubkey::Pubkey,
signature::{Signature, Signer, SignerError},
stake::{self, instruction::LockupArgs, state::Lockup},
system_instruction::{self, SystemError},
system_program,
transaction::{Transaction, TransactionError},
};
use solana_stake_program::{
stake_instruction::LockupArgs,
stake_state::{Lockup, StakeAuthorize},
};
use solana_transaction_status::{EncodedTransaction, UiTransactionEncoding};
use solana_vote_program::vote_state::VoteAuthorize;
use std::{
@@ -61,6 +58,8 @@ use std::{
use thiserror::Error;
pub const DEFAULT_RPC_TIMEOUT_SECONDS: &str = "30";
pub const DEFAULT_CONFIRM_TX_TIMEOUT_SECONDS: &str = "5";
const CHECKED: bool = true;
#[derive(Debug, PartialEq)]
#[allow(clippy::large_enum_variant)]
@@ -134,6 +133,8 @@ pub enum CliCommand {
sort_order: CliValidatorsSortOrder,
reverse_sort: bool,
number_validators: bool,
keep_unstaked_delinquents: bool,
delinquent_slot_distance: Option<Slot>,
},
Supply {
print_accounts: bool,
@@ -194,6 +195,7 @@ pub enum CliCommand {
seed: Option<String>,
staker: Option<Pubkey>,
withdrawer: Option<Pubkey>,
withdrawer_signer: Option<SignerIndex>,
lockup: Lockup,
amount: SpendAmount,
sign_only: bool,
@@ -267,7 +269,7 @@ pub enum CliCommand {
},
StakeAuthorize {
stake_account_pubkey: Pubkey,
new_authorizations: Vec<(StakeAuthorize, Pubkey, SignerIndex)>,
new_authorizations: Vec<StakeAuthorizationIndexed>,
sign_only: bool,
dump_transaction_message: bool,
blockhash_query: BlockhashQuery,
@@ -282,6 +284,7 @@ pub enum CliCommand {
stake_account_pubkey: Pubkey,
lockup: LockupArgs,
custodian: SignerIndex,
new_custodian_signer: Option<SignerIndex>,
sign_only: bool,
dump_transaction_message: bool,
blockhash_query: BlockhashQuery,
@@ -339,6 +342,8 @@ pub enum CliCommand {
new_authorized_pubkey: Pubkey,
vote_authorize: VoteAuthorize,
memo: Option<String>,
authorized: SignerIndex,
new_authorized: Option<SignerIndex>,
},
VoteUpdateValidator {
vote_account_pubkey: Pubkey,
@@ -451,6 +456,7 @@ pub struct CliConfig<'a> {
pub output_format: OutputFormat,
pub commitment: CommitmentConfig,
pub send_transaction_config: RpcSendTransactionConfig,
pub confirm_transaction_initial_timeout: Duration,
pub address_labels: HashMap<String, String>,
}
@@ -595,6 +601,9 @@ impl Default for CliConfig<'_> {
output_format: OutputFormat::Display,
commitment: CommitmentConfig::confirmed(),
send_transaction_config: RpcSendTransactionConfig::default(),
confirm_transaction_initial_timeout: Duration::from_secs(
u64::from_str(DEFAULT_CONFIRM_TX_TIMEOUT_SECONDS).unwrap(),
),
address_labels: HashMap::new(),
}
}
@@ -711,7 +720,10 @@ pub fn parse_command(
}
// Stake Commands
("create-stake-account", Some(matches)) => {
parse_create_stake_account(matches, default_signer, wallet_manager)
parse_create_stake_account(matches, default_signer, wallet_manager, !CHECKED)
}
("create-stake-account-checked", Some(matches)) => {
parse_create_stake_account(matches, default_signer, wallet_manager, CHECKED)
}
("delegate-stake", Some(matches)) => {
parse_stake_delegate_stake(matches, default_signer, wallet_manager)
@@ -729,10 +741,16 @@ pub fn parse_command(
parse_merge_stake(matches, default_signer, wallet_manager)
}
("stake-authorize", Some(matches)) => {
parse_stake_authorize(matches, default_signer, wallet_manager)
parse_stake_authorize(matches, default_signer, wallet_manager, !CHECKED)
}
("stake-authorize-checked", Some(matches)) => {
parse_stake_authorize(matches, default_signer, wallet_manager, CHECKED)
}
("stake-set-lockup", Some(matches)) => {
parse_stake_set_lockup(matches, default_signer, wallet_manager)
parse_stake_set_lockup(matches, default_signer, wallet_manager, !CHECKED)
}
("stake-set-lockup-checked", Some(matches)) => {
parse_stake_set_lockup(matches, default_signer, wallet_manager, CHECKED)
}
("stake-account", Some(matches)) => parse_show_stake_account(matches, wallet_manager),
("stake-history", Some(matches)) => parse_show_stake_history(matches),
@@ -759,12 +777,28 @@ pub fn parse_command(
default_signer,
wallet_manager,
VoteAuthorize::Voter,
!CHECKED,
),
("vote-authorize-withdrawer", Some(matches)) => parse_vote_authorize(
matches,
default_signer,
wallet_manager,
VoteAuthorize::Withdrawer,
!CHECKED,
),
("vote-authorize-voter-checked", Some(matches)) => parse_vote_authorize(
matches,
default_signer,
wallet_manager,
VoteAuthorize::Voter,
CHECKED,
),
("vote-authorize-withdrawer-checked", Some(matches)) => parse_vote_authorize(
matches,
default_signer,
wallet_manager,
VoteAuthorize::Withdrawer,
CHECKED,
),
("vote-account", Some(matches)) => parse_vote_get_account_command(matches, wallet_manager),
("withdraw-from-vote-account", Some(matches)) => {
@@ -931,7 +965,7 @@ 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()),
"STAKE" => Some(stake::program::id()),
"VOTE" => Some(solana_vote_program::id()),
_ => pubkey_of(matches, arg_name),
})
@@ -998,7 +1032,7 @@ fn process_airdrop(
let result = request_and_confirm_airdrop(rpc_client, config, &pubkey, lamports);
if let Ok(signature) = result {
let signature_cli_message = log_instruction_custom_error::<SystemError>(result, &config)?;
let signature_cli_message = log_instruction_custom_error::<SystemError>(result, config)?;
println!("{}", signature_cli_message);
let current_balance = rpc_client.get_balance(&pubkey)?;
@@ -1011,7 +1045,7 @@ fn process_airdrop(
Ok(build_balance_message(current_balance, false, true))
}
} else {
log_instruction_custom_error::<SystemError>(result, &config)
log_instruction_custom_error::<SystemError>(result, config)
}
}
@@ -1096,7 +1130,7 @@ fn process_confirm(
#[allow(clippy::unnecessary_wraps)]
fn process_decode_transaction(config: &CliConfig, transaction: &Transaction) -> ProcessResult {
let sigverify_status = CliSignatureVerificationStatus::verify_transaction(&transaction);
let sigverify_status = CliSignatureVerificationStatus::verify_transaction(transaction);
let decode_transaction = CliTransaction {
decoded_transaction: transaction.clone(),
transaction: EncodedTransaction::encode(transaction.clone(), UiTransactionEncoding::Json),
@@ -1267,7 +1301,7 @@ fn process_transfer(
} else {
rpc_client.send_and_confirm_transaction_with_spinner(&tx)
};
log_instruction_custom_error::<SystemError>(result, &config)
log_instruction_custom_error::<SystemError>(result, config)
}
}
@@ -1286,10 +1320,11 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
}
let rpc_client = if config.rpc_client.is_none() {
Arc::new(RpcClient::new_with_timeout_and_commitment(
Arc::new(RpcClient::new_with_timeouts_and_commitment(
config.json_rpc_url.to_string(),
config.rpc_timeout,
config.commitment,
config.confirm_transaction_initial_timeout,
))
} else {
// Primarily for testing
@@ -1322,7 +1357,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
from_pubkey,
seed,
program_id,
} => process_create_address_with_seed(config, from_pubkey.as_ref(), &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::Feature(feature_subcommand) => {
process_feature_subcommand(&rpc_client, config, feature_subcommand)
@@ -1345,8 +1380,8 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
CliCommand::LeaderSchedule { epoch } => {
process_leader_schedule(&rpc_client, config, *epoch)
}
CliCommand::LiveSlots => process_live_slots(&config),
CliCommand::Logs { filter } => process_logs(&config, filter),
CliCommand::LiveSlots => process_live_slots(config),
CliCommand::Logs { filter } => process_logs(config, filter),
CliCommand::Ping {
lamports,
interval,
@@ -1389,6 +1424,8 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
sort_order,
reverse_sort,
number_validators,
keep_unstaked_delinquents,
delinquent_slot_distance,
} => process_show_validators(
&rpc_client,
config,
@@ -1396,6 +1433,8 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
*sort_order,
*reverse_sort,
*number_validators,
*keep_unstaked_delinquents,
*delinquent_slot_distance,
),
CliCommand::Supply { print_accounts } => {
process_supply(&rpc_client, config, *print_accounts)
@@ -1451,7 +1490,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
),
// Get the current nonce
CliCommand::GetNonce(nonce_account_pubkey) => {
process_get_nonce(&rpc_client, config, &nonce_account_pubkey)
process_get_nonce(&rpc_client, config, nonce_account_pubkey)
}
// Get a new nonce
CliCommand::NewNonce {
@@ -1472,7 +1511,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
} => process_show_nonce_account(
&rpc_client,
config,
&nonce_account_pubkey,
nonce_account_pubkey,
*use_lamports_unit,
),
// Withdraw lamports from a nonce account
@@ -1485,10 +1524,10 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
} => process_withdraw_from_nonce_account(
&rpc_client,
config,
&nonce_account,
nonce_account,
*nonce_authority,
memo.as_ref(),
&destination_account_pubkey,
destination_account_pubkey,
*lamports,
),
@@ -1520,6 +1559,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
seed,
staker,
withdrawer,
withdrawer_signer,
lockup,
amount,
sign_only,
@@ -1537,6 +1577,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
seed,
staker,
withdrawer,
*withdrawer_signer,
lockup,
*amount,
*sign_only,
@@ -1562,7 +1603,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
} => process_deactivate_stake_account(
&rpc_client,
config,
&stake_account_pubkey,
stake_account_pubkey,
*stake_authority,
*sign_only,
*dump_transaction_message,
@@ -1588,8 +1629,8 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
} => process_delegate_stake(
&rpc_client,
config,
&stake_account_pubkey,
&vote_account_pubkey,
stake_account_pubkey,
vote_account_pubkey,
*stake_authority,
*force,
*sign_only,
@@ -1616,7 +1657,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
} => process_split_stake(
&rpc_client,
config,
&stake_account_pubkey,
stake_account_pubkey,
*stake_authority,
*sign_only,
*dump_transaction_message,
@@ -1643,8 +1684,8 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
} => process_merge_stake(
&rpc_client,
config,
&stake_account_pubkey,
&source_stake_account_pubkey,
stake_account_pubkey,
source_stake_account_pubkey,
*stake_authority,
*sign_only,
*dump_transaction_message,
@@ -1661,7 +1702,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
} => process_show_stake_account(
&rpc_client,
config,
&stake_account_pubkey,
stake_account_pubkey,
*use_lamports_unit,
*with_rewards,
),
@@ -1684,7 +1725,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
} => process_stake_authorize(
&rpc_client,
config,
&stake_account_pubkey,
stake_account_pubkey,
new_authorizations,
*custodian,
*sign_only,
@@ -1698,8 +1739,9 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
),
CliCommand::StakeSetLockup {
stake_account_pubkey,
mut lockup,
lockup,
custodian,
new_custodian_signer,
sign_only,
dump_transaction_message,
blockhash_query,
@@ -1710,8 +1752,9 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
} => process_stake_set_lockup(
&rpc_client,
config,
&stake_account_pubkey,
&mut lockup,
stake_account_pubkey,
lockup,
*new_custodian_signer,
*custodian,
*sign_only,
*dump_transaction_message,
@@ -1738,8 +1781,8 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
} => process_withdraw_stake(
&rpc_client,
config,
&stake_account_pubkey,
&destination_account_pubkey,
stake_account_pubkey,
destination_account_pubkey,
*amount,
*withdraw_authority,
*custodian,
@@ -1767,7 +1810,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
} => process_set_validator_info(
&rpc_client,
config,
&validator_info,
validator_info,
*force_keybase,
*info_pubkey,
),
@@ -1801,7 +1844,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
} => process_show_vote_account(
&rpc_client,
config,
&vote_account_pubkey,
vote_account_pubkey,
*use_lamports_unit,
*with_rewards,
),
@@ -1825,12 +1868,16 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
new_authorized_pubkey,
vote_authorize,
memo,
authorized,
new_authorized,
} => process_vote_authorize(
&rpc_client,
config,
&vote_account_pubkey,
&new_authorized_pubkey,
vote_account_pubkey,
new_authorized_pubkey,
*vote_authorize,
*authorized,
*new_authorized,
memo.as_ref(),
),
CliCommand::VoteUpdateValidator {
@@ -1841,7 +1888,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
} => process_vote_update_validator(
&rpc_client,
config,
&vote_account_pubkey,
vote_account_pubkey,
*new_identity_account,
*withdraw_authority,
memo.as_ref(),
@@ -1854,7 +1901,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
} => process_vote_update_commission(
&rpc_client,
config,
&vote_account_pubkey,
vote_account_pubkey,
*commission,
*withdraw_authority,
memo.as_ref(),
@@ -1870,7 +1917,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
CliCommand::Balance {
pubkey,
use_lamports_unit,
} => process_balance(&rpc_client, config, &pubkey, *use_lamports_unit),
} => 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) => {
@@ -1887,13 +1934,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
pubkey,
output_file,
use_lamports_unit,
} => process_show_account(
&rpc_client,
config,
&pubkey,
&output_file,
*use_lamports_unit,
),
} => process_show_account(&rpc_client, config, pubkey, output_file, *use_lamports_unit),
CliCommand::Transfer {
amount,
to,
@@ -1947,21 +1988,41 @@ pub fn request_and_confirm_airdrop(
Ok(signature)
}
fn common_error_adapter<E>(ix_error: &InstructionError) -> Option<E>
where
E: 'static + std::error::Error + DecodeError<E> + FromPrimitive,
{
if let InstructionError::Custom(code) = ix_error {
E::decode_custom_error_to_enum(*code)
} else {
None
}
}
pub fn log_instruction_custom_error<E>(
result: ClientResult<Signature>,
config: &CliConfig,
) -> ProcessResult
where
E: 'static + std::error::Error + DecodeError<E> + FromPrimitive,
{
log_instruction_custom_error_ex::<E, _>(result, config, common_error_adapter)
}
pub fn log_instruction_custom_error_ex<E, F>(
result: ClientResult<Signature>,
config: &CliConfig,
error_adapter: F,
) -> ProcessResult
where
E: 'static + std::error::Error + DecodeError<E> + FromPrimitive,
F: Fn(&InstructionError) -> Option<E>,
{
match result {
Err(err) => {
if let ClientErrorKind::TransactionError(TransactionError::InstructionError(
_,
InstructionError::Custom(code),
)) = err.kind()
{
if let Some(specific_error) = E::decode_custom_error_to_enum(*code) {
let maybe_tx_err = err.get_transaction_error();
if let Some(TransactionError::InstructionError(_, ix_error)) = maybe_tx_err {
if let Some(specific_error) = error_adapter(&ix_error) {
return Err(specific_error.into());
}
}
@@ -1999,22 +2060,7 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
.stake_subcommands()
.subcommand(
SubCommand::with_name("airdrop")
.about("Request lamports")
.arg(
Arg::with_name("faucet_host")
.long("faucet-host")
.value_name("URL")
.takes_value(true)
.help("Faucet host to use [default: the --url host]"),
)
.arg(
Arg::with_name("faucet_port")
.long("faucet-port")
.value_name("PORT_NUMBER")
.takes_value(true)
.default_value(solana_faucet::faucet::FAUCET_PORT_STR)
.help("Faucet port to use"),
)
.about("Request SOL from a faucet")
.arg(
Arg::with_name("amount")
.index(1)
@@ -2226,7 +2272,7 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
)
.offline_args()
.nonce_args(false)
.arg(memo_arg())
.arg(memo_arg())
.arg(fee_payer_arg()),
)
.subcommand(
@@ -2299,7 +2345,7 @@ mod tests {
let default_keypair_file = make_tmp_path("keypair_file");
write_keypair_file(&default_keypair, &default_keypair_file).unwrap();
let default_signer = DefaultSigner::new(default_keypair_file);
let default_signer = DefaultSigner::new("keypair", &default_keypair_file);
let signer_info = default_signer
.generate_unique_signers(vec![], &matches, &mut None)
@@ -2377,7 +2423,7 @@ mod tests {
let keypair_file = make_tmp_path("keypair_file");
write_keypair_file(&default_keypair, &keypair_file).unwrap();
let keypair = read_keypair_file(&keypair_file).unwrap();
let default_signer = DefaultSigner::new(keypair_file.clone());
let default_signer = DefaultSigner::new("", &keypair_file);
// Test Airdrop Subcommand
let test_airdrop =
test_commands
@@ -2464,7 +2510,7 @@ mod tests {
let from_pubkey = Some(solana_sdk::pubkey::new_rand());
let from_str = from_pubkey.unwrap().to_string();
for (name, program_id) in &[
("STAKE", solana_stake_program::id()),
("STAKE", stake::program::id()),
("VOTE", solana_vote_program::id()),
("NONCE", system_program::id()),
] {
@@ -2500,7 +2546,7 @@ mod tests {
command: CliCommand::CreateAddressWithSeed {
from_pubkey: None,
seed: "seed".to_string(),
program_id: solana_stake_program::id(),
program_id: stake::program::id(),
},
signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
}
@@ -2633,6 +2679,8 @@ mod tests {
new_authorized_pubkey,
vote_authorize: VoteAuthorize::Voter,
memo: None,
authorized: 0,
new_authorized: None,
};
let result = process_command(&config);
assert!(result.is_ok());
@@ -2656,6 +2704,7 @@ mod tests {
seed: None,
staker: None,
withdrawer: None,
withdrawer_signer: None,
lockup: Lockup {
epoch: 0,
unix_timestamp: 0,
@@ -2763,11 +2812,11 @@ mod tests {
config.command = CliCommand::CreateAddressWithSeed {
from_pubkey: Some(from_pubkey),
seed: "seed".to_string(),
program_id: solana_stake_program::id(),
program_id: stake::program::id(),
};
let address = process_command(&config);
let expected_address =
Pubkey::create_with_seed(&from_pubkey, "seed", &solana_stake_program::id()).unwrap();
Pubkey::create_with_seed(&from_pubkey, "seed", &stake::program::id()).unwrap();
assert_eq!(address.unwrap(), expected_address.to_string());
// Need airdrop cases
@@ -2828,6 +2877,8 @@ mod tests {
new_authorized_pubkey: bob_pubkey,
vote_authorize: VoteAuthorize::Voter,
memo: None,
authorized: 0,
new_authorized: None,
};
assert!(process_command(&config).is_err());
@@ -2905,7 +2956,7 @@ mod tests {
let default_keypair = Keypair::new();
let default_keypair_file = make_tmp_path("keypair_file");
write_keypair_file(&default_keypair, &default_keypair_file).unwrap();
let default_signer = DefaultSigner::new(default_keypair_file.clone());
let default_signer = DefaultSigner::new("", &default_keypair_file);
//Test Transfer Subcommand, SOL
let from_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
@@ -3154,7 +3205,7 @@ mod tests {
memo: None,
fee_payer: 0,
derived_address_seed: Some(derived_address_seed),
derived_address_program_id: Some(solana_stake_program::id()),
derived_address_program_id: Some(stake::program::id()),
},
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into(),],
}

View File

@@ -24,11 +24,12 @@ use solana_client::{
pubsub_client::PubsubClient,
rpc_client::{GetConfirmedSignaturesForAddress2Config, RpcClient},
rpc_config::{
RpcAccountInfoConfig, RpcBlockConfig, RpcLargestAccountsConfig, RpcLargestAccountsFilter,
RpcProgramAccountsConfig, RpcTransactionConfig, RpcTransactionLogsConfig,
RpcTransactionLogsFilter,
RpcAccountInfoConfig, RpcBlockConfig, RpcGetVoteAccountsConfig, RpcLargestAccountsConfig,
RpcLargestAccountsFilter, RpcProgramAccountsConfig, RpcTransactionConfig,
RpcTransactionLogsConfig, RpcTransactionLogsFilter,
},
rpc_filter,
rpc_request::DELINQUENT_VALIDATOR_SLOT_DISTANCE,
rpc_response::SlotInfo,
};
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
@@ -46,7 +47,9 @@ use solana_sdk::{
rent::Rent,
rpc_port::DEFAULT_RPC_PORT_STR,
signature::Signature,
slot_history, system_instruction, system_program,
slot_history,
stake::{self, state::StakeState},
system_instruction, system_program,
sysvar::{
self,
slot_history::SlotHistory,
@@ -55,7 +58,6 @@ use solana_sdk::{
timing,
transaction::Transaction,
};
use solana_stake_program::stake_state::StakeState;
use solana_transaction_status::UiTransactionEncoding;
use solana_vote_program::vote_state::VoteState;
use std::{
@@ -121,7 +123,7 @@ impl ClusterQuerySubCommands for App<'_, '_> {
.long("our-localhost")
.takes_value(false)
.value_name("PORT")
.default_value(&DEFAULT_RPC_PORT_STR)
.default_value(DEFAULT_RPC_PORT_STR)
.validator(is_port)
.help("Guess Identity pubkey and validator rpc node assuming local (possibly private) validator"),
)
@@ -175,7 +177,7 @@ impl ClusterQuerySubCommands for App<'_, '_> {
.takes_value(true)
.value_name("EPOCH")
.validator(is_epoch)
.help("Epoch to show leader schedule for. (default: current)")
.help("Epoch to show leader schedule for. [default: current]")
)
)
.subcommand(
@@ -381,6 +383,25 @@ impl ClusterQuerySubCommands for App<'_, '_> {
])
.default_value("stake")
.help("Sort order (does not affect JSON output)"),
)
.arg(
Arg::with_name("keep_unstaked_delinquents")
.long("keep-unstaked-delinquents")
.takes_value(false)
.help("Don't discard unstaked, delinquent validators")
)
.arg(
Arg::with_name("delinquent_slot_distance")
.long("delinquent-slot-distance")
.takes_value(true)
.value_name("SLOT_DISTANCE")
.validator(is_slot)
.help(
concatcp!(
"Minimum slot distance from the tip to consider a validator delinquent. [default: ",
DELINQUENT_VALIDATOR_SLOT_DISTANCE,
"]",
))
),
)
.subcommand(
@@ -616,6 +637,8 @@ pub fn parse_show_validators(matches: &ArgMatches<'_>) -> Result<CliCommandInfo,
let use_lamports_unit = matches.is_present("lamports");
let number_validators = matches.is_present("number");
let reverse_sort = matches.is_present("reverse");
let keep_unstaked_delinquents = matches.is_present("keep_unstaked_delinquents");
let delinquent_slot_distance = value_of(matches, "delinquent_slot_distance");
let sort_order = match value_t_or_exit!(matches, "sort", String).as_str() {
"delinquent" => CliValidatorsSortOrder::Delinquent,
@@ -636,6 +659,8 @@ pub fn parse_show_validators(matches: &ArgMatches<'_>) -> Result<CliCommandInfo,
sort_order,
reverse_sort,
number_validators,
keep_unstaked_delinquents,
delinquent_slot_distance,
},
signers: vec![],
})
@@ -937,18 +962,19 @@ pub fn process_fees(
*recent_blockhash,
fee_calculator.lamports_per_signature,
None,
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;
let result = rpc_client.get_fees_with_commitment(config.commitment)?;
CliFees::some(
result.context.slot,
recent_blockhash,
fee_calculator.lamports_per_signature,
Some(last_valid_slot),
result.value.blockhash,
result.value.fee_calculator.lamports_per_signature,
None,
Some(result.value.last_valid_block_height),
)
};
Ok(config.output_format.formatted_string(&fees))
@@ -1075,9 +1101,15 @@ pub fn process_get_epoch_info(rpc_client: &RpcClient, config: &CliConfig) -> Pro
(secs as u64).saturating_mul(1000).checked_div(slots)
})
.unwrap_or(clock::DEFAULT_MS_PER_SLOT);
let start_block_time = rpc_client
.get_block_time(epoch_info.absolute_slot - epoch_info.slot_index)
.ok();
let current_block_time = rpc_client.get_block_time(epoch_info.absolute_slot).ok();
let epoch_info = CliEpochInfo {
epoch_info,
average_slot_time_ms,
start_block_time,
current_block_time,
};
Ok(config.output_format.formatted_string(&epoch_info))
}
@@ -1707,7 +1739,7 @@ pub fn process_show_stakes(
}
}
let all_stake_accounts = rpc_client
.get_program_accounts_with_config(&solana_stake_program::id(), program_accounts_config)?;
.get_program_accounts_with_config(&stake::program::id(), program_accounts_config)?;
let stake_history_account = rpc_client.get_account(&stake_history::id())?;
let clock_account = rpc_client.get_account(&sysvar::clock::id())?;
let clock: Clock = from_account(&clock_account).ok_or_else(|| {
@@ -1785,11 +1817,17 @@ pub fn process_show_validators(
validators_sort_order: CliValidatorsSortOrder,
validators_reverse_sort: bool,
number_validators: bool,
keep_unstaked_delinquents: bool,
delinquent_slot_distance: Option<Slot>,
) -> ProcessResult {
let progress_bar = new_spinner_progress_bar();
progress_bar.set_message("Fetching vote accounts...");
let epoch_info = rpc_client.get_epoch_info()?;
let vote_accounts = rpc_client.get_vote_accounts()?;
let vote_accounts = rpc_client.get_vote_accounts_with_config(RpcGetVoteAccountsConfig {
keep_unstaked_delinquents: Some(keep_unstaked_delinquents),
delinquent_slot_distance,
..RpcGetVoteAccountsConfig::default()
})?;
progress_bar.set_message("Fetching block production...");
let skip_rate: HashMap<_, _> = rpc_client
@@ -1888,14 +1926,40 @@ pub fn process_show_validators(
entry.delinquent_active_stake += validator.activated_stake;
}
let validators: Vec<_> = current_validators
.into_iter()
.chain(delinquent_validators.into_iter())
.collect();
let (average_skip_rate, average_stake_weighted_skip_rate) = {
let mut skip_rate_len = 0;
let mut skip_rate_sum = 0.;
let mut skip_rate_weighted_sum = 0.;
for validator in validators.iter() {
if let Some(skip_rate) = validator.skip_rate {
skip_rate_sum += skip_rate;
skip_rate_len += 1;
skip_rate_weighted_sum += skip_rate * validator.activated_stake as f64;
}
}
if skip_rate_len > 0 && total_active_stake > 0 {
(
skip_rate_sum / skip_rate_len as f64,
skip_rate_weighted_sum / total_active_stake as f64,
)
} else {
(100., 100.) // Impossible?
}
};
let cli_validators = CliValidators {
total_active_stake,
total_current_stake,
total_delinquent_stake,
validators: current_validators
.into_iter()
.chain(delinquent_validators.into_iter())
.collect(),
validators,
average_skip_rate,
average_stake_weighted_skip_rate,
validators_sort_order,
validators_reverse_sort,
number_validators,
@@ -2098,7 +2162,7 @@ mod tests {
let default_keypair = Keypair::new();
let (default_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
let default_signer = DefaultSigner::new(default_keypair_file);
let default_signer = DefaultSigner::new("", &default_keypair_file);
let test_cluster_version = test_commands
.clone()

View File

@@ -10,6 +10,7 @@ use solana_cli_output::{QuietDisplay, VerboseDisplay};
use solana_client::{client_error::ClientError, rpc_client::RpcClient};
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{
account::Account,
clock::Slot,
feature::{self, Feature},
feature_set::FEATURE_NAMES,
@@ -312,6 +313,31 @@ fn feature_activation_allowed(rpc_client: &RpcClient, quiet: bool) -> Result<boo
Ok(feature_activation_allowed)
}
fn status_from_account(account: Account) -> Option<CliFeatureStatus> {
feature::from_account(&account).map(|feature| match feature.activated_at {
None => CliFeatureStatus::Pending,
Some(activation_slot) => CliFeatureStatus::Active(activation_slot),
})
}
fn get_feature_status(
rpc_client: &RpcClient,
feature_id: &Pubkey,
) -> Result<Option<CliFeatureStatus>, Box<dyn std::error::Error>> {
rpc_client
.get_account(feature_id)
.map(status_from_account)
.map_err(|e| e.into())
}
pub fn get_feature_is_active(
rpc_client: &RpcClient,
feature_id: &Pubkey,
) -> Result<bool, Box<dyn std::error::Error>> {
get_feature_status(rpc_client, feature_id)
.map(|status| matches!(status, Some(CliFeatureStatus::Active(_))))
}
fn process_status(
rpc_client: &RpcClient,
config: &CliConfig,
@@ -327,11 +353,7 @@ fn process_status(
let feature_id = &feature_ids[i];
let feature_name = FEATURE_NAMES.get(feature_id).unwrap();
if let Some(account) = account {
if let Some(feature) = feature::from_account(&account) {
let feature_status = match feature.activated_at {
None => CliFeatureStatus::Pending,
Some(activation_slot) => CliFeatureStatus::Active(activation_slot),
};
if let Some(feature_status) = status_from_account(account) {
features.push(CliFeature {
id: feature_id.to_string(),
description: feature_name.to_string(),

View File

@@ -102,7 +102,7 @@ fn process_rewards(
rewards_epoch: Option<Epoch>,
) -> ProcessResult {
let rewards = rpc_client
.get_inflation_reward(&addresses, rewards_epoch)
.get_inflation_reward(addresses, rewards_epoch)
.map_err(|err| {
if let Some(epoch) = rewards_epoch {
format!("Rewards not available for epoch {}", epoch)

View File

@@ -18,6 +18,9 @@ macro_rules! pubkey {
};
}
#[macro_use]
extern crate const_format;
extern crate serde_derive;
pub mod checks;

View File

@@ -10,7 +10,7 @@ use solana_clap_utils::{
};
use solana_cli::cli::{
app, parse_command, process_command, CliCommandInfo, CliConfig, SettingType,
DEFAULT_RPC_TIMEOUT_SECONDS,
DEFAULT_CONFIRM_TX_TIMEOUT_SECONDS, DEFAULT_RPC_TIMEOUT_SECONDS,
};
use solana_cli_config::{Config, CONFIG_FILE};
use solana_cli_output::{display::println_name_value, OutputFormat};
@@ -167,23 +167,29 @@ pub fn parse_args<'a>(
let rpc_timeout = value_t_or_exit!(matches, "rpc_timeout", u64);
let rpc_timeout = Duration::from_secs(rpc_timeout);
let confirm_transaction_initial_timeout =
value_t_or_exit!(matches, "confirm_transaction_initial_timeout", u64);
let confirm_transaction_initial_timeout =
Duration::from_secs(confirm_transaction_initial_timeout);
let (_, websocket_url) = CliConfig::compute_websocket_url_setting(
matches.value_of("websocket_url").unwrap_or(""),
&config.websocket_url,
matches.value_of("json_rpc_url").unwrap_or(""),
&config.json_rpc_url,
);
let default_signer_arg_name = "keypair".to_string();
let (_, default_signer_path) = CliConfig::compute_keypair_path_setting(
matches.value_of("keypair").unwrap_or(""),
matches.value_of(&default_signer_arg_name).unwrap_or(""),
&config.keypair_path,
);
let default_signer = DefaultSigner::from_path(default_signer_path.clone())?;
let default_signer = DefaultSigner::new(default_signer_arg_name, &default_signer_path);
let CliCommandInfo {
command,
mut signers,
} = parse_command(&matches, &default_signer, &mut wallet_manager)?;
} = parse_command(matches, &default_signer, &mut wallet_manager)?;
if signers.is_empty() {
if let Ok(signer_info) =
@@ -234,6 +240,7 @@ pub fn parse_args<'a>(
preflight_commitment: Some(commitment.commitment),
..RpcSendTransactionConfig::default()
},
confirm_transaction_initial_timeout,
address_labels,
},
signers,
@@ -256,7 +263,7 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.global(true)
.help("Configuration file to use");
if let Some(ref config_file) = *CONFIG_FILE {
arg.default_value(&config_file)
arg.default_value(config_file)
} else {
arg
}
@@ -349,6 +356,16 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.hidden(true)
.help("Timeout value for RPC requests"),
)
.arg(
Arg::with_name("confirm_transaction_initial_timeout")
.long("confirm-timeout")
.value_name("SECONDS")
.takes_value(true)
.default_value(DEFAULT_CONFIRM_TX_TIMEOUT_SECONDS)
.global(true)
.hidden(true)
.help("Timeout value for initial transaction status"),
)
.subcommand(
SubCommand::with_name("config")
.about("Solana command-line tool configuration settings")
@@ -410,10 +427,10 @@ fn main() -> Result<(), Box<dyn error::Error>> {
}
fn do_main(matches: &ArgMatches<'_>) -> Result<(), Box<dyn error::Error>> {
if parse_settings(&matches)? {
if parse_settings(matches)? {
let mut wallet_manager = None;
let (mut config, signers) = parse_args(&matches, &mut wallet_manager)?;
let (mut config, signers) = parse_args(matches, &mut wallet_manager)?;
config.signers = signers.iter().map(|s| s.as_ref()).collect();
let result = process_command(&config)?;
println!("{}", result);

View File

@@ -1,9 +1,10 @@
use crate::{
checks::{check_account_for_fee_with_commitment, check_unique_pubkeys},
cli::{
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
ProcessResult,
log_instruction_custom_error, log_instruction_custom_error_ex, CliCommand, CliCommandInfo,
CliConfig, CliError, ProcessResult,
},
feature::get_feature_is_active,
memo::WithMemo,
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
};
@@ -12,7 +13,7 @@ use solana_clap_utils::{
input_parsers::*,
input_validators::*,
keypair::{DefaultSigner, SignerIndex},
memo::MEMO_ARG,
memo::{memo_arg, MEMO_ARG},
nonce::*,
};
use solana_cli_output::CliNonceAccount;
@@ -20,16 +21,19 @@ use solana_client::{nonce_utils::*, rpc_client::RpcClient};
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{
account::Account,
feature_set::merge_nonce_error_into_system_error,
hash::Hash,
instruction::InstructionError,
message::Message,
nonce::{self, State},
pubkey::Pubkey,
system_instruction::{
advance_nonce_account, authorize_nonce_account, create_nonce_account,
create_nonce_account_with_seed, withdraw_nonce_account, NonceError, SystemError,
create_nonce_account_with_seed, instruction_to_nonce_error, withdraw_nonce_account,
NonceError, SystemError,
},
system_program,
transaction::Transaction,
transaction::{Transaction, TransactionError},
};
use std::sync::Arc;
@@ -56,7 +60,8 @@ impl NonceSubCommands for App<'_, '_> {
.required(true),
"Account to be granted authority of the nonce account. "),
)
.arg(nonce_authority_arg()),
.arg(nonce_authority_arg())
.arg(memo_arg()),
)
.subcommand(
SubCommand::with_name("create-nonce-account")
@@ -91,7 +96,8 @@ impl NonceSubCommands for App<'_, '_> {
.value_name("STRING")
.takes_value(true)
.help("Seed for address generation; if specified, the resulting account will be at a derived address of the NONCE_ACCOUNT pubkey")
),
)
.arg(memo_arg()),
)
.subcommand(
SubCommand::with_name("nonce")
@@ -115,7 +121,8 @@ impl NonceSubCommands for App<'_, '_> {
.required(true),
"Address of the nonce account. "),
)
.arg(nonce_authority_arg()),
.arg(nonce_authority_arg())
.arg(memo_arg()),
)
.subcommand(
SubCommand::with_name("nonce-account")
@@ -161,7 +168,8 @@ impl NonceSubCommands for App<'_, '_> {
.validator(is_amount)
.help("The amount to withdraw from the nonce account, in SOL"),
)
.arg(nonce_authority_arg()),
.arg(nonce_authority_arg())
.arg(memo_arg()),
)
}
}
@@ -363,8 +371,21 @@ pub fn process_authorize_nonce_account(
&tx.message,
config.commitment,
)?;
let merge_errors =
get_feature_is_active(rpc_client, &merge_nonce_error_into_system_error::id())?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<NonceError>(result, &config)
if merge_errors {
log_instruction_custom_error::<SystemError>(result, config)
} else {
log_instruction_custom_error_ex::<NonceError, _>(result, config, |ix_error| {
if let InstructionError::Custom(_) = ix_error {
instruction_to_nonce_error(ix_error, merge_errors)
} else {
None
}
})
}
}
pub fn process_create_nonce_account(
@@ -448,8 +469,40 @@ pub fn process_create_nonce_account(
let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&config.signers, recent_blockhash)?;
let merge_errors =
get_feature_is_active(rpc_client, &merge_nonce_error_into_system_error::id())?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<SystemError>(result, &config)
let err_ix_index = if let Err(err) = &result {
err.get_transaction_error().and_then(|tx_err| {
if let TransactionError::InstructionError(ix_index, _) = tx_err {
Some(ix_index)
} else {
None
}
})
} else {
None
};
match err_ix_index {
// SystemInstruction::InitializeNonceAccount failed
Some(1) => {
if merge_errors {
log_instruction_custom_error::<SystemError>(result, config)
} else {
log_instruction_custom_error_ex::<NonceError, _>(result, config, |ix_error| {
if let InstructionError::Custom(_) = ix_error {
instruction_to_nonce_error(ix_error, merge_errors)
} else {
None
}
})
}
}
// SystemInstruction::CreateAccount{,WithSeed} failed
_ => log_instruction_custom_error::<SystemError>(result, config),
}
}
pub fn process_get_nonce(
@@ -474,10 +527,10 @@ pub fn process_new_nonce(
) -> ProcessResult {
check_unique_pubkeys(
(&config.signers[0].pubkey(), "cli keypair".to_string()),
(&nonce_account, "nonce_account_pubkey".to_string()),
(nonce_account, "nonce_account_pubkey".to_string()),
)?;
if let Err(err) = rpc_client.get_account(&nonce_account) {
if let Err(err) = rpc_client.get_account(nonce_account) {
return Err(CliError::BadParameter(format!(
"Unable to advance nonce account {}. error: {}",
nonce_account, err
@@ -487,7 +540,7 @@ pub fn process_new_nonce(
let nonce_authority = config.signers[nonce_authority];
let ixs = vec![advance_nonce_account(
&nonce_account,
nonce_account,
&nonce_authority.pubkey(),
)]
.with_memo(memo);
@@ -502,8 +555,21 @@ pub fn process_new_nonce(
&tx.message,
config.commitment,
)?;
let merge_errors =
get_feature_is_active(rpc_client, &merge_nonce_error_into_system_error::id())?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<SystemError>(result, &config)
if merge_errors {
log_instruction_custom_error::<SystemError>(result, config)
} else {
log_instruction_custom_error_ex::<NonceError, _>(result, config, |ix_error| {
if let InstructionError::Custom(_) = ix_error {
instruction_to_nonce_error(ix_error, merge_errors)
} else {
None
}
})
}
}
pub fn process_show_nonce_account(
@@ -522,7 +588,7 @@ pub fn process_show_nonce_account(
use_lamports_unit,
..CliNonceAccount::default()
};
if let Some(ref data) = data {
if let Some(data) = data {
nonce_account.nonce = Some(data.blockhash.to_string());
nonce_account.lamports_per_signature = Some(data.fee_calculator.lamports_per_signature);
nonce_account.authority = Some(data.authority.to_string());
@@ -565,8 +631,21 @@ pub fn process_withdraw_from_nonce_account(
&tx.message,
config.commitment,
)?;
let merge_errors =
get_feature_is_active(rpc_client, &merge_nonce_error_into_system_error::id())?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<NonceError>(result, &config)
if merge_errors {
log_instruction_custom_error::<SystemError>(result, config)
} else {
log_instruction_custom_error_ex::<NonceError, _>(result, config, |ix_error| {
if let InstructionError::Custom(_) = ix_error {
instruction_to_nonce_error(ix_error, merge_errors)
} else {
None
}
})
}
}
#[cfg(test)]
@@ -596,7 +675,7 @@ mod tests {
let default_keypair = Keypair::new();
let (default_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
let default_signer = DefaultSigner::new(default_keypair_file.clone());
let default_signer = DefaultSigner::new("", &default_keypair_file);
let (keypair_file, mut tmp_file) = make_tmp_file();
let nonce_account_keypair = Keypair::new();
write_keypair(&nonce_account_keypair, tmp_file.as_file_mut()).unwrap();

View File

@@ -150,7 +150,7 @@ impl ProgramSubCommands for App<'_, '_> {
pubkey!(Arg::with_name("program_id")
.long("program-id")
.value_name("PROGRAM_ID"),
"Executable program's address, must be a signer for initial deploys, can be a pubkey for upgrades \
"Executable program's address, must be a keypair for initial deploys, can be a pubkey for upgrades \
[default: address of keypair at /path/to/program-keypair.json if present, otherwise a random address]"),
)
.arg(
@@ -767,7 +767,7 @@ fn process_program_deploy(
};
let upgrade_authority_signer = config.signers[upgrade_authority_signer_index];
let default_program_keypair = get_default_program_keypair(&program_location);
let default_program_keypair = get_default_program_keypair(program_location);
let (program_signer, program_pubkey) = if let Some(i) = program_signer_index {
(Some(config.signers[i]), config.signers[i].pubkey())
} else if let Some(program_pubkey) = program_pubkey {
@@ -843,7 +843,7 @@ fn process_program_deploy(
};
let (program_data, program_len) = if let Some(program_location) = program_location {
let program_data = read_and_verify_elf(&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 {
@@ -886,6 +886,11 @@ fn process_program_deploy(
)?;
let result = if do_deploy {
if program_signer.is_none() {
return Err(
"Initial deployments require a keypair be provided for the program id".into(),
);
}
do_process_program_write_and_deploy(
rpc_client.clone(),
config,
@@ -1254,7 +1259,7 @@ fn process_dump(
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)?;
f.write_all(program_data)?;
Ok(format!("Wrote program to {}", output_location))
} else {
Err(
@@ -1274,7 +1279,7 @@ fn process_dump(
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)?;
f.write_all(program_data)?;
Ok(format!("Wrote program to {}", output_location))
} else {
Err(format!(
@@ -1305,8 +1310,8 @@ fn close(
let mut tx = Transaction::new_unsigned(Message::new(
&[bpf_loader_upgradeable::close(
&account_pubkey,
&recipient_pubkey,
account_pubkey,
recipient_pubkey,
&authority_signer.pubkey(),
)],
Some(&config.signers[0].pubkey()),
@@ -1415,7 +1420,7 @@ fn process_close(
if close(
rpc_client,
config,
&address,
address,
&recipient_pubkey,
authority_signer,
)
@@ -1516,7 +1521,7 @@ fn do_process_program_write_and_deploy(
.value
{
complete_partial_program_init(
&loader_id,
loader_id,
&config.signers[0].pubkey(),
buffer_pubkey,
&account,
@@ -1546,7 +1551,7 @@ fn do_process_program_write_and_deploy(
buffer_pubkey,
minimum_balance,
buffer_data_len as u64,
&loader_id,
loader_id,
)],
minimum_balance,
)
@@ -1574,7 +1579,7 @@ fn do_process_program_write_and_deploy(
} else {
loader_instruction::write(
buffer_pubkey,
&loader_id,
loader_id,
(i * DATA_CHUNK_SIZE) as u32,
chunk.to_vec(),
)
@@ -1618,7 +1623,7 @@ fn do_process_program_write_and_deploy(
)
} else {
Message::new(
&[loader_instruction::finalize(buffer_pubkey, &loader_id)],
&[loader_instruction::finalize(buffer_pubkey, loader_id)],
Some(&config.signers[0].pubkey()),
)
};
@@ -1744,8 +1749,8 @@ fn do_process_program_upgrade(
// Create and add final message
let final_message = Message::new(
&[bpf_loader_upgradeable::upgrade(
&program_id,
&buffer_pubkey,
program_id,
buffer_pubkey,
&upgrade_authority.pubkey(),
&config.signers[0].pubkey(),
)],
@@ -1813,7 +1818,7 @@ fn complete_partial_program_init(
account_data_len as u64,
));
if account.owner != *loader_id {
instructions.push(system_instruction::assign(elf_pubkey, &loader_id));
instructions.push(system_instruction::assign(elf_pubkey, loader_id));
}
}
if account.lamports < minimum_balance {
@@ -1885,7 +1890,7 @@ fn send_deploy_messages(
initial_transaction.try_sign(&[payer_signer], blockhash)?;
}
let result = rpc_client.send_and_confirm_transaction_with_spinner(&initial_transaction);
log_instruction_custom_error::<SystemError>(result, &config)
log_instruction_custom_error::<SystemError>(result, config)
.map_err(|err| format!("Account allocation failed: {}", err))?;
} else {
return Err("Buffer account not created yet, must provide a key pair".into());
@@ -2131,7 +2136,7 @@ mod tests {
let default_keypair = Keypair::new();
let keypair_file = make_tmp_path("keypair_file");
write_keypair_file(&default_keypair, &keypair_file).unwrap();
let default_signer = DefaultSigner::new(keypair_file.clone());
let default_signer = DefaultSigner::new("", &keypair_file);
let test_command = test_commands.clone().get_matches_from(vec![
"test",
@@ -2339,7 +2344,7 @@ mod tests {
let default_keypair = Keypair::new();
let keypair_file = make_tmp_path("keypair_file");
write_keypair_file(&default_keypair, &keypair_file).unwrap();
let default_signer = DefaultSigner::new(keypair_file.clone());
let default_signer = DefaultSigner::new("", &keypair_file);
// defaults
let test_command = test_commands.clone().get_matches_from(vec![
@@ -2487,7 +2492,7 @@ mod tests {
let default_keypair = Keypair::new();
let keypair_file = make_tmp_path("keypair_file");
write_keypair_file(&default_keypair, &keypair_file).unwrap();
let default_signer = DefaultSigner::new(keypair_file.clone());
let default_signer = DefaultSigner::new("", &keypair_file);
let program_pubkey = Pubkey::new_unique();
let new_authority_pubkey = Pubkey::new_unique();
@@ -2595,7 +2600,7 @@ mod tests {
let default_keypair = Keypair::new();
let keypair_file = make_tmp_path("keypair_file");
write_keypair_file(&default_keypair, &keypair_file).unwrap();
let default_signer = DefaultSigner::new(keypair_file.clone());
let default_signer = DefaultSigner::new("", &keypair_file);
let buffer_pubkey = Pubkey::new_unique();
let new_authority_pubkey = Pubkey::new_unique();
@@ -2652,7 +2657,7 @@ mod tests {
let default_keypair = Keypair::new();
let keypair_file = make_tmp_path("keypair_file");
write_keypair_file(&default_keypair, &keypair_file).unwrap();
let default_signer = DefaultSigner::new(keypair_file);
let default_signer = DefaultSigner::new("", &keypair_file);
// defaults
let buffer_pubkey = Pubkey::new_unique();
@@ -2751,7 +2756,7 @@ mod tests {
let default_keypair = Keypair::new();
let keypair_file = make_tmp_path("keypair_file");
write_keypair_file(&default_keypair, &keypair_file).unwrap();
let default_signer = DefaultSigner::new(keypair_file.clone());
let default_signer = DefaultSigner::new("", &keypair_file);
// defaults
let buffer_pubkey = Pubkey::new_unique();

View File

@@ -92,7 +92,7 @@ where
Ok((message, spend))
} else {
let from_balance = rpc_client
.get_balance_with_commitment(&from_pubkey, commitment)?
.get_balance_with_commitment(from_pubkey, commitment)?
.value;
let (message, SpendAndFee { spend, fee }) = resolve_spend_message(
amount,

File diff suppressed because it is too large Load Diff

View File

@@ -119,7 +119,7 @@ fn parse_validator_info(
let key_list: ConfigKeys = deserialize(&account.data)?;
if !key_list.keys.is_empty() {
let (validator_pubkey, _) = key_list.keys[1];
let validator_info_string: String = deserialize(&get_config_data(&account.data)?)?;
let validator_info_string: String = deserialize(get_config_data(&account.data)?)?;
let validator_info: Map<_, _> = serde_json::from_str(&validator_info_string)?;
Ok((validator_pubkey, validator_info))
} else {
@@ -246,7 +246,7 @@ pub fn process_set_validator_info(
) -> ProcessResult {
// Validate keybase username
if let Some(string) = validator_info.get("keybaseUsername") {
let result = verify_keybase(&config.signers[0].pubkey(), &string);
let result = verify_keybase(&config.signers[0].pubkey(), string);
if result.is_err() {
if force_keybase {
println!("--force supplied, ignoring: {:?}", result);
@@ -272,7 +272,7 @@ pub fn process_set_validator_info(
},
)
.find(|(pubkey, account)| {
let (validator_pubkey, _) = parse_validator_info(&pubkey, &account).unwrap();
let (validator_pubkey, _) = parse_validator_info(pubkey, account).unwrap();
validator_pubkey == config.signers[0].pubkey()
});
@@ -393,7 +393,7 @@ pub fn process_get_validator_info(
}
for (validator_info_pubkey, validator_info_account) in validator_info.iter() {
let (validator_pubkey, validator_info) =
parse_validator_info(&validator_info_pubkey, &validator_info_account)?;
parse_validator_info(validator_info_pubkey, validator_info_account)?;
validator_info_list.push(CliValidatorInfo {
identity_pubkey: validator_pubkey.to_string(),
info_pubkey: validator_info_pubkey.to_string(),
@@ -451,7 +451,7 @@ mod tests {
"name": "Alice",
"keybaseUsername": "alice_keybase",
});
assert_eq!(parse_args(&matches), expected);
assert_eq!(parse_args(matches), expected);
}
#[test]

View File

@@ -82,7 +82,7 @@ impl VoteSubCommands for App<'_, '_> {
.takes_value(true)
.help("Seed for address generation; if specified, the resulting account will be at a derived address of the VOTE ACCOUNT pubkey")
)
.arg(memo_arg())
.arg(memo_arg())
)
.subcommand(
SubCommand::with_name("vote-authorize-voter")
@@ -109,7 +109,7 @@ impl VoteSubCommands for App<'_, '_> {
.required(true),
"New authorized vote signer. "),
)
.arg(memo_arg())
.arg(memo_arg())
)
.subcommand(
SubCommand::with_name("vote-authorize-withdrawer")
@@ -136,7 +136,65 @@ impl VoteSubCommands for App<'_, '_> {
.required(true),
"New authorized withdrawer. "),
)
.arg(memo_arg())
.arg(memo_arg())
)
.subcommand(
SubCommand::with_name("vote-authorize-voter-checked")
.about("Authorize a new vote signing keypair for the given vote account, \
checking the new authority as a signer")
.arg(
pubkey!(Arg::with_name("vote_account_pubkey")
.index(1)
.value_name("VOTE_ACCOUNT_ADDRESS")
.required(true),
"Vote account in which to set the authorized voter. "),
)
.arg(
Arg::with_name("authorized")
.index(2)
.value_name("AUTHORIZED_KEYPAIR")
.required(true)
.validator(is_valid_signer)
.help("Current authorized vote signer."),
)
.arg(
Arg::with_name("new_authorized")
.index(3)
.value_name("NEW_AUTHORIZED_KEYPAIR")
.required(true)
.validator(is_valid_signer)
.help("New authorized vote signer."),
)
.arg(memo_arg())
)
.subcommand(
SubCommand::with_name("vote-authorize-withdrawer-checked")
.about("Authorize a new withdraw signing keypair for the given vote account, \
checking the new authority as a signer")
.arg(
pubkey!(Arg::with_name("vote_account_pubkey")
.index(1)
.value_name("VOTE_ACCOUNT_ADDRESS")
.required(true),
"Vote account in which to set the authorized withdrawer. "),
)
.arg(
Arg::with_name("authorized")
.index(2)
.value_name("AUTHORIZED_KEYPAIR")
.required(true)
.validator(is_valid_signer)
.help("Current authorized withdrawer."),
)
.arg(
Arg::with_name("new_authorized")
.index(3)
.value_name("NEW_AUTHORIZED_KEYPAIR")
.required(true)
.validator(is_valid_signer)
.help("New authorized withdrawer."),
)
.arg(memo_arg())
)
.subcommand(
SubCommand::with_name("vote-update-validator")
@@ -166,7 +224,7 @@ impl VoteSubCommands for App<'_, '_> {
.validator(is_valid_signer)
.help("Authorized withdrawer keypair"),
)
.arg(memo_arg())
.arg(memo_arg())
)
.subcommand(
SubCommand::with_name("vote-update-commission")
@@ -196,7 +254,7 @@ impl VoteSubCommands for App<'_, '_> {
.validator(is_valid_signer)
.help("Authorized withdrawer keypair"),
)
.arg(memo_arg())
.arg(memo_arg())
)
.subcommand(
SubCommand::with_name("vote-account")
@@ -266,7 +324,7 @@ impl VoteSubCommands for App<'_, '_> {
.validator(is_valid_signer)
.help("Authorized withdrawer [default: cli config keypair]"),
)
.arg(memo_arg())
.arg(memo_arg())
)
}
}
@@ -311,19 +369,25 @@ pub fn parse_vote_authorize(
default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
vote_authorize: VoteAuthorize,
checked: bool,
) -> Result<CliCommandInfo, CliError> {
let vote_account_pubkey =
pubkey_of_signer(matches, "vote_account_pubkey", wallet_manager)?.unwrap();
let new_authorized_pubkey =
pubkey_of_signer(matches, "new_authorized_pubkey", wallet_manager)?.unwrap();
let (authorized, _) = signer_of(matches, "authorized", wallet_manager)?;
let (authorized, authorized_pubkey) = signer_of(matches, "authorized", wallet_manager)?;
let payer_provided = None;
let signer_info = default_signer.generate_unique_signers(
vec![payer_provided, authorized],
matches,
wallet_manager,
)?;
let mut signers = vec![payer_provided, authorized];
let new_authorized_pubkey = if checked {
let (new_authorized_signer, new_authorized_pubkey) =
signer_of(matches, "new_authorized", wallet_manager)?;
signers.push(new_authorized_signer);
new_authorized_pubkey.unwrap()
} else {
pubkey_of_signer(matches, "new_authorized_pubkey", wallet_manager)?.unwrap()
};
let signer_info = default_signer.generate_unique_signers(signers, matches, wallet_manager)?;
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
Ok(CliCommandInfo {
@@ -332,6 +396,12 @@ pub fn parse_vote_authorize(
new_authorized_pubkey,
vote_authorize,
memo,
authorized: signer_info.index_of(authorized_pubkey).unwrap(),
new_authorized: if checked {
signer_info.index_of(Some(new_authorized_pubkey))
} else {
None
},
},
signers: signer_info.signers,
})
@@ -468,7 +538,7 @@ pub fn process_create_vote_account(
let vote_account = config.signers[vote_account];
let vote_account_pubkey = vote_account.pubkey();
let vote_account_address = if let Some(seed) = seed {
Pubkey::create_with_seed(&vote_account_pubkey, &seed, &solana_vote_program::id())?
Pubkey::create_with_seed(&vote_account_pubkey, seed, &solana_vote_program::id())?
} else {
vote_account_pubkey
};
@@ -549,7 +619,7 @@ pub fn process_create_vote_account(
let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&config.signers, recent_blockhash)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<SystemError>(result, &config)
log_instruction_custom_error::<SystemError>(result, config)
}
pub fn process_vote_authorize(
@@ -558,28 +628,34 @@ pub fn process_vote_authorize(
vote_account_pubkey: &Pubkey,
new_authorized_pubkey: &Pubkey,
vote_authorize: VoteAuthorize,
authorized: SignerIndex,
new_authorized: Option<SignerIndex>,
memo: Option<&String>,
) -> ProcessResult {
// If the `authorized_account` is also the fee payer, `config.signers` will only have one
// keypair in it
let authorized = if config.signers.len() == 2 {
config.signers[1]
} else {
config.signers[0]
};
let authorized = config.signers[authorized];
let new_authorized_signer = new_authorized.map(|index| config.signers[index]);
check_unique_pubkeys(
(&authorized.pubkey(), "authorized_account".to_string()),
(new_authorized_pubkey, "new_authorized_pubkey".to_string()),
)?;
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let ixs = vec![vote_instruction::authorize(
vote_account_pubkey, // vote account to update
&authorized.pubkey(), // current authorized
new_authorized_pubkey, // new vote signer/withdrawer
vote_authorize, // vote or withdraw
)]
.with_memo(memo);
let vote_ix = if new_authorized_signer.is_some() {
vote_instruction::authorize_checked(
vote_account_pubkey, // vote account to update
&authorized.pubkey(), // current authorized
new_authorized_pubkey, // new vote signer/withdrawer
vote_authorize, // vote or withdraw
)
} else {
vote_instruction::authorize(
vote_account_pubkey, // vote account to update
&authorized.pubkey(), // current authorized
new_authorized_pubkey, // new vote signer/withdrawer
vote_authorize, // vote or withdraw
)
};
let ixs = vec![vote_ix].with_memo(memo);
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
let mut tx = Transaction::new_unsigned(message);
@@ -592,7 +668,7 @@ pub fn process_vote_authorize(
config.commitment,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<VoteError>(result, &config)
log_instruction_custom_error::<VoteError>(result, config)
}
pub fn process_vote_update_validator(
@@ -629,7 +705,7 @@ pub fn process_vote_update_validator(
config.commitment,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<VoteError>(result, &config)
log_instruction_custom_error::<VoteError>(result, config)
}
pub fn process_vote_update_commission(
@@ -660,7 +736,7 @@ pub fn process_vote_update_commission(
config.commitment,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<VoteError>(result, &config)
log_instruction_custom_error::<VoteError>(result, config)
}
fn get_vote_account(
@@ -763,7 +839,7 @@ pub fn process_withdraw_from_vote_account(
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let withdraw_authority = config.signers[withdraw_authority];
let current_balance = rpc_client.get_balance(&vote_account_pubkey)?;
let current_balance = rpc_client.get_balance(vote_account_pubkey)?;
let minimum_balance = rpc_client.get_minimum_balance_for_rent_exemption(VoteState::size_of())?;
let lamports = match withdraw_amount {
@@ -798,7 +874,7 @@ pub fn process_withdraw_from_vote_account(
config.commitment,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&transaction);
log_instruction_custom_error::<VoteError>(result, &config)
log_instruction_custom_error::<VoteError>(result, config)
}
#[cfg(test)]
@@ -826,7 +902,7 @@ mod tests {
let default_keypair = Keypair::new();
let (default_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
let default_signer = DefaultSigner::new(default_keypair_file.clone());
let default_signer = DefaultSigner::new("", &default_keypair_file);
let test_authorize_voter = test_commands.clone().get_matches_from(vec![
"test",
@@ -843,6 +919,8 @@ mod tests {
new_authorized_pubkey: pubkey2,
vote_authorize: VoteAuthorize::Voter,
memo: None,
authorized: 0,
new_authorized: None,
},
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
}
@@ -867,6 +945,8 @@ mod tests {
new_authorized_pubkey: pubkey2,
vote_authorize: VoteAuthorize::Voter,
memo: None,
authorized: 1,
new_authorized: None,
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
@@ -875,6 +955,70 @@ mod tests {
}
);
let (voter_keypair_file, mut tmp_file) = make_tmp_file();
let voter_keypair = Keypair::new();
write_keypair(&voter_keypair, tmp_file.as_file_mut()).unwrap();
let test_authorize_voter = test_commands.clone().get_matches_from(vec![
"test",
"vote-authorize-voter-checked",
&pubkey_string,
&default_keypair_file,
&voter_keypair_file,
]);
assert_eq!(
parse_command(&test_authorize_voter, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::VoteAuthorize {
vote_account_pubkey: pubkey,
new_authorized_pubkey: voter_keypair.pubkey(),
vote_authorize: VoteAuthorize::Voter,
memo: None,
authorized: 0,
new_authorized: Some(1),
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
read_keypair_file(&voter_keypair_file).unwrap().into()
],
}
);
let test_authorize_voter = test_commands.clone().get_matches_from(vec![
"test",
"vote-authorize-voter-checked",
&pubkey_string,
&authorized_keypair_file,
&voter_keypair_file,
]);
assert_eq!(
parse_command(&test_authorize_voter, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::VoteAuthorize {
vote_account_pubkey: pubkey,
new_authorized_pubkey: voter_keypair.pubkey(),
vote_authorize: VoteAuthorize::Voter,
memo: None,
authorized: 1,
new_authorized: Some(2),
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
read_keypair_file(&authorized_keypair_file).unwrap().into(),
read_keypair_file(&voter_keypair_file).unwrap().into(),
],
}
);
let test_authorize_voter = test_commands.clone().get_matches_from(vec![
"test",
"vote-authorize-voter-checked",
&pubkey_string,
&authorized_keypair_file,
&pubkey2_string,
]);
assert!(parse_command(&test_authorize_voter, &default_signer, &mut None).is_err());
let (keypair_file, mut tmp_file) = make_tmp_file();
let keypair = Keypair::new();
write_keypair(&keypair, tmp_file.as_file_mut()).unwrap();

View File

@@ -68,7 +68,7 @@ fn test_cli_program_deploy_non_upgradeable() {
.unwrap()
.as_str()
.unwrap();
let program_id = Pubkey::from_str(&program_id_str).unwrap();
let program_id = Pubkey::from_str(program_id_str).unwrap();
let account0 = rpc_client.get_account(&program_id).unwrap();
assert_eq!(account0.lamports, minimum_balance_for_rent_exemption);
assert_eq!(account0.owner, bpf_loader::id());
@@ -198,7 +198,7 @@ fn test_cli_program_deploy_no_authority() {
.unwrap()
.as_str()
.unwrap();
let program_id = Pubkey::from_str(&program_id_str).unwrap();
let program_id = Pubkey::from_str(program_id_str).unwrap();
// Attempt to upgrade the program
config.signers = vec![&keypair, &upgrade_authority];
@@ -284,7 +284,7 @@ fn test_cli_program_deploy_with_authority() {
.unwrap();
assert_eq!(
program_keypair.pubkey(),
Pubkey::from_str(&program_pubkey_str).unwrap()
Pubkey::from_str(program_pubkey_str).unwrap()
);
let program_account = rpc_client.get_account(&program_keypair.pubkey()).unwrap();
assert_eq!(program_account.lamports, minimum_balance_for_program);
@@ -328,7 +328,7 @@ fn test_cli_program_deploy_with_authority() {
.unwrap()
.as_str()
.unwrap();
let program_pubkey = Pubkey::from_str(&program_pubkey_str).unwrap();
let program_pubkey = Pubkey::from_str(program_pubkey_str).unwrap();
let program_account = rpc_client.get_account(&program_pubkey).unwrap();
assert_eq!(program_account.lamports, minimum_balance_for_program);
assert_eq!(program_account.owner, bpf_loader_upgradeable::id());
@@ -397,7 +397,7 @@ fn test_cli_program_deploy_with_authority() {
.as_str()
.unwrap();
assert_eq!(
Pubkey::from_str(&new_upgrade_authority_str).unwrap(),
Pubkey::from_str(new_upgrade_authority_str).unwrap(),
new_upgrade_authority.pubkey()
);
@@ -452,7 +452,7 @@ fn test_cli_program_deploy_with_authority() {
.unwrap();
assert_eq!(
new_upgrade_authority.pubkey(),
Pubkey::from_str(&authority_pubkey_str).unwrap()
Pubkey::from_str(authority_pubkey_str).unwrap()
);
// Set no authority
@@ -510,7 +510,7 @@ fn test_cli_program_deploy_with_authority() {
.unwrap()
.as_str()
.unwrap();
let program_pubkey = Pubkey::from_str(&program_pubkey_str).unwrap();
let program_pubkey = Pubkey::from_str(program_pubkey_str).unwrap();
let (programdata_pubkey, _) =
Pubkey::find_program_address(&[program_pubkey.as_ref()], &bpf_loader_upgradeable::id());
let programdata_account = rpc_client.get_account(&programdata_pubkey).unwrap();
@@ -606,7 +606,7 @@ fn test_cli_program_write_buffer() {
.unwrap()
.as_str()
.unwrap();
let new_buffer_pubkey = Pubkey::from_str(&buffer_pubkey_str).unwrap();
let new_buffer_pubkey = Pubkey::from_str(buffer_pubkey_str).unwrap();
let buffer_account = rpc_client.get_account(&new_buffer_pubkey).unwrap();
assert_eq!(buffer_account.lamports, minimum_balance_for_buffer_default);
assert_eq!(buffer_account.owner, bpf_loader_upgradeable::id());
@@ -641,7 +641,7 @@ fn test_cli_program_write_buffer() {
.unwrap();
assert_eq!(
buffer_keypair.pubkey(),
Pubkey::from_str(&buffer_pubkey_str).unwrap()
Pubkey::from_str(buffer_pubkey_str).unwrap()
);
let buffer_account = rpc_client.get_account(&buffer_keypair.pubkey()).unwrap();
assert_eq!(buffer_account.lamports, minimum_balance_for_buffer);
@@ -675,7 +675,7 @@ fn test_cli_program_write_buffer() {
.unwrap();
assert_eq!(
keypair.pubkey(),
Pubkey::from_str(&authority_pubkey_str).unwrap()
Pubkey::from_str(authority_pubkey_str).unwrap()
);
// Specify buffer authority
@@ -700,7 +700,7 @@ fn test_cli_program_write_buffer() {
.unwrap();
assert_eq!(
buffer_keypair.pubkey(),
Pubkey::from_str(&buffer_pubkey_str).unwrap()
Pubkey::from_str(buffer_pubkey_str).unwrap()
);
let buffer_account = rpc_client.get_account(&buffer_keypair.pubkey()).unwrap();
assert_eq!(buffer_account.lamports, minimum_balance_for_buffer_default);
@@ -735,7 +735,7 @@ fn test_cli_program_write_buffer() {
.unwrap()
.as_str()
.unwrap();
let buffer_pubkey = Pubkey::from_str(&buffer_pubkey_str).unwrap();
let buffer_pubkey = Pubkey::from_str(buffer_pubkey_str).unwrap();
let buffer_account = rpc_client.get_account(&buffer_pubkey).unwrap();
assert_eq!(buffer_account.lamports, minimum_balance_for_buffer_default);
assert_eq!(buffer_account.owner, bpf_loader_upgradeable::id());
@@ -768,7 +768,7 @@ fn test_cli_program_write_buffer() {
.unwrap();
assert_eq!(
authority_keypair.pubkey(),
Pubkey::from_str(&authority_pubkey_str).unwrap()
Pubkey::from_str(authority_pubkey_str).unwrap()
);
// Close buffer
@@ -806,7 +806,7 @@ fn test_cli_program_write_buffer() {
.unwrap()
.as_str()
.unwrap();
let new_buffer_pubkey = Pubkey::from_str(&buffer_pubkey_str).unwrap();
let new_buffer_pubkey = Pubkey::from_str(buffer_pubkey_str).unwrap();
// Close buffers and deposit default keypair
let pre_lamports = rpc_client.get_account(&keypair.pubkey()).unwrap().lamports;
@@ -901,7 +901,7 @@ fn test_cli_program_set_buffer_authority() {
.as_str()
.unwrap();
assert_eq!(
Pubkey::from_str(&new_buffer_authority_str).unwrap(),
Pubkey::from_str(new_buffer_authority_str).unwrap(),
new_buffer_authority.pubkey()
);
let buffer_account = rpc_client.get_account(&buffer_keypair.pubkey()).unwrap();
@@ -928,7 +928,7 @@ fn test_cli_program_set_buffer_authority() {
.as_str()
.unwrap();
assert_eq!(
Pubkey::from_str(&buffer_authority_str).unwrap(),
Pubkey::from_str(buffer_authority_str).unwrap(),
buffer_keypair.pubkey()
);
let buffer_account = rpc_client.get_account(&buffer_keypair.pubkey()).unwrap();
@@ -1101,7 +1101,7 @@ fn test_cli_program_show() {
.unwrap();
assert_eq!(
buffer_keypair.pubkey(),
Pubkey::from_str(&address_str).unwrap()
Pubkey::from_str(address_str).unwrap()
);
let authority_str = json
.as_object()
@@ -1112,7 +1112,7 @@ fn test_cli_program_show() {
.unwrap();
assert_eq!(
authority_keypair.pubkey(),
Pubkey::from_str(&authority_str).unwrap()
Pubkey::from_str(authority_str).unwrap()
);
let data_len = json
.as_object()
@@ -1161,7 +1161,7 @@ fn test_cli_program_show() {
.unwrap();
assert_eq!(
program_keypair.pubkey(),
Pubkey::from_str(&address_str).unwrap()
Pubkey::from_str(address_str).unwrap()
);
let programdata_address_str = json
.as_object()
@@ -1176,7 +1176,7 @@ fn test_cli_program_show() {
);
assert_eq!(
programdata_pubkey,
Pubkey::from_str(&programdata_address_str).unwrap()
Pubkey::from_str(programdata_address_str).unwrap()
);
let authority_str = json
.as_object()
@@ -1187,7 +1187,7 @@ fn test_cli_program_show() {
.unwrap();
assert_eq!(
authority_keypair.pubkey(),
Pubkey::from_str(&authority_str).unwrap()
Pubkey::from_str(authority_str).unwrap()
);
let deployed_slot = json
.as_object()

View File

@@ -1,6 +1,7 @@
use solana_cli::{
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
spend_utils::SpendAmount,
stake::StakeAuthorizationIndexed,
test_utils::{check_ready, check_recent_balance},
};
use solana_cli_output::{parse_sign_only_reply_string, OutputFormat};
@@ -17,10 +18,11 @@ use solana_sdk::{
nonce::State as NonceState,
pubkey::Pubkey,
signature::{keypair_from_seed, Keypair, Signer},
};
use solana_stake_program::{
stake_instruction::LockupArgs,
stake_state::{Lockup, StakeAuthorize, StakeState},
stake::{
self,
instruction::LockupArgs,
state::{Lockup, StakeAuthorize, StakeState},
},
};
#[test]
@@ -63,6 +65,7 @@ fn test_stake_delegation_force() {
seed: None,
staker: None,
withdrawer: None,
withdrawer_signer: None,
lockup: Lockup::default(),
amount: SpendAmount::Some(50_000),
sign_only: false,
@@ -139,7 +142,7 @@ fn test_seed_stake_delegation_and_deactivation() {
let stake_address = Pubkey::create_with_seed(
&config_validator.signers[0].pubkey(),
"hi there",
&solana_stake_program::id(),
&stake::program::id(),
)
.expect("bad seed");
@@ -150,6 +153,7 @@ fn test_seed_stake_delegation_and_deactivation() {
seed: Some("hi there".to_string()),
staker: None,
withdrawer: None,
withdrawer_signer: None,
lockup: Lockup::default(),
amount: SpendAmount::Some(50_000),
sign_only: false,
@@ -230,6 +234,7 @@ fn test_stake_delegation_and_deactivation() {
seed: None,
staker: None,
withdrawer: None,
withdrawer_signer: None,
lockup: Lockup::default(),
amount: SpendAmount::Some(50_000),
sign_only: false,
@@ -331,6 +336,7 @@ fn test_offline_stake_delegation_and_deactivation() {
seed: None,
staker: Some(config_offline.signers[0].pubkey()),
withdrawer: None,
withdrawer_signer: None,
lockup: Lockup::default(),
amount: SpendAmount::Some(50_000),
sign_only: false,
@@ -450,6 +456,7 @@ fn test_nonced_stake_delegation_and_deactivation() {
seed: None,
staker: None,
withdrawer: None,
withdrawer_signer: None,
lockup: Lockup::default(),
amount: SpendAmount::Some(50_000),
sign_only: false,
@@ -580,6 +587,7 @@ fn test_stake_authorize() {
seed: None,
staker: None,
withdrawer: None,
withdrawer_signer: None,
lockup: Lockup::default(),
amount: SpendAmount::Some(50_000),
sign_only: false,
@@ -599,7 +607,12 @@ fn test_stake_authorize() {
config.signers.pop();
config.command = CliCommand::StakeAuthorize {
stake_account_pubkey,
new_authorizations: vec![(StakeAuthorize::Staker, online_authority_pubkey, 0)],
new_authorizations: vec![StakeAuthorizationIndexed {
authorization_type: StakeAuthorize::Staker,
new_authority_pubkey: online_authority_pubkey,
authority: 0,
new_authority_signer: None,
}],
sign_only: false,
dump_transaction_message: false,
blockhash_query: BlockhashQuery::default(),
@@ -628,8 +641,18 @@ fn test_stake_authorize() {
config.command = CliCommand::StakeAuthorize {
stake_account_pubkey,
new_authorizations: vec![
(StakeAuthorize::Staker, online_authority2_pubkey, 1),
(StakeAuthorize::Withdrawer, withdraw_authority_pubkey, 0),
StakeAuthorizationIndexed {
authorization_type: StakeAuthorize::Staker,
new_authority_pubkey: online_authority2_pubkey,
authority: 1,
new_authority_signer: None,
},
StakeAuthorizationIndexed {
authorization_type: StakeAuthorize::Withdrawer,
new_authority_pubkey: withdraw_authority_pubkey,
authority: 0,
new_authority_signer: None,
},
],
sign_only: false,
dump_transaction_message: false,
@@ -656,7 +679,12 @@ fn test_stake_authorize() {
config.signers.push(&online_authority2);
config.command = CliCommand::StakeAuthorize {
stake_account_pubkey,
new_authorizations: vec![(StakeAuthorize::Staker, offline_authority_pubkey, 1)],
new_authorizations: vec![StakeAuthorizationIndexed {
authorization_type: StakeAuthorize::Staker,
new_authority_pubkey: offline_authority_pubkey,
authority: 1,
new_authority_signer: None,
}],
sign_only: false,
dump_transaction_message: false,
blockhash_query: BlockhashQuery::default(),
@@ -682,7 +710,12 @@ fn test_stake_authorize() {
let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap();
config_offline.command = CliCommand::StakeAuthorize {
stake_account_pubkey,
new_authorizations: vec![(StakeAuthorize::Staker, nonced_authority_pubkey, 0)],
new_authorizations: vec![StakeAuthorizationIndexed {
authorization_type: StakeAuthorize::Staker,
new_authority_pubkey: nonced_authority_pubkey,
authority: 0,
new_authority_signer: None,
}],
sign_only: true,
dump_transaction_message: false,
blockhash_query: BlockhashQuery::None(blockhash),
@@ -701,7 +734,12 @@ fn test_stake_authorize() {
config.signers = vec![&offline_presigner];
config.command = CliCommand::StakeAuthorize {
stake_account_pubkey,
new_authorizations: vec![(StakeAuthorize::Staker, nonced_authority_pubkey, 0)],
new_authorizations: vec![StakeAuthorizationIndexed {
authorization_type: StakeAuthorize::Staker,
new_authority_pubkey: nonced_authority_pubkey,
authority: 0,
new_authority_signer: None,
}],
sign_only: false,
dump_transaction_message: false,
blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
@@ -752,7 +790,12 @@ fn test_stake_authorize() {
config_offline.signers.push(&nonced_authority);
config_offline.command = CliCommand::StakeAuthorize {
stake_account_pubkey,
new_authorizations: vec![(StakeAuthorize::Staker, online_authority_pubkey, 1)],
new_authorizations: vec![StakeAuthorizationIndexed {
authorization_type: StakeAuthorize::Staker,
new_authority_pubkey: online_authority_pubkey,
authority: 1,
new_authority_signer: None,
}],
sign_only: true,
dump_transaction_message: false,
blockhash_query: BlockhashQuery::None(nonce_hash),
@@ -772,7 +815,12 @@ fn test_stake_authorize() {
config.signers = vec![&offline_presigner, &nonced_authority_presigner];
config.command = CliCommand::StakeAuthorize {
stake_account_pubkey,
new_authorizations: vec![(StakeAuthorize::Staker, online_authority_pubkey, 1)],
new_authorizations: vec![StakeAuthorizationIndexed {
authorization_type: StakeAuthorize::Staker,
new_authority_pubkey: online_authority_pubkey,
authority: 1,
new_authority_signer: None,
}],
sign_only: false,
dump_transaction_message: false,
blockhash_query: BlockhashQuery::FeeCalculator(
@@ -860,6 +908,7 @@ fn test_stake_authorize_with_fee_payer() {
seed: None,
staker: None,
withdrawer: None,
withdrawer_signer: None,
lockup: Lockup::default(),
amount: SpendAmount::Some(50_000),
sign_only: false,
@@ -879,7 +928,12 @@ fn test_stake_authorize_with_fee_payer() {
config.signers = vec![&default_signer, &payer_keypair];
config.command = CliCommand::StakeAuthorize {
stake_account_pubkey,
new_authorizations: vec![(StakeAuthorize::Staker, offline_pubkey, 0)],
new_authorizations: vec![StakeAuthorizationIndexed {
authorization_type: StakeAuthorize::Staker,
new_authority_pubkey: offline_pubkey,
authority: 0,
new_authority_signer: None,
}],
sign_only: false,
dump_transaction_message: false,
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
@@ -901,7 +955,12 @@ fn test_stake_authorize_with_fee_payer() {
let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap();
config_offline.command = CliCommand::StakeAuthorize {
stake_account_pubkey,
new_authorizations: vec![(StakeAuthorize::Staker, payer_pubkey, 0)],
new_authorizations: vec![StakeAuthorizationIndexed {
authorization_type: StakeAuthorize::Staker,
new_authority_pubkey: payer_pubkey,
authority: 0,
new_authority_signer: None,
}],
sign_only: true,
dump_transaction_message: false,
blockhash_query: BlockhashQuery::None(blockhash),
@@ -920,7 +979,12 @@ fn test_stake_authorize_with_fee_payer() {
config.signers = vec![&offline_presigner];
config.command = CliCommand::StakeAuthorize {
stake_account_pubkey,
new_authorizations: vec![(StakeAuthorize::Staker, payer_pubkey, 0)],
new_authorizations: vec![StakeAuthorizationIndexed {
authorization_type: StakeAuthorize::Staker,
new_authority_pubkey: payer_pubkey,
authority: 0,
new_authority_signer: None,
}],
sign_only: false,
dump_transaction_message: false,
blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
@@ -984,6 +1048,7 @@ fn test_stake_split() {
seed: None,
staker: Some(offline_pubkey),
withdrawer: Some(offline_pubkey),
withdrawer_signer: None,
lockup: Lockup::default(),
amount: SpendAmount::Some(10 * minimum_stake_balance),
sign_only: false,
@@ -1134,6 +1199,7 @@ fn test_stake_set_lockup() {
seed: None,
staker: Some(offline_pubkey),
withdrawer: Some(config.signers[0].pubkey()),
withdrawer_signer: None,
lockup,
amount: SpendAmount::Some(10 * minimum_stake_balance),
sign_only: false,
@@ -1162,6 +1228,7 @@ fn test_stake_set_lockup() {
config.command = CliCommand::StakeSetLockup {
stake_account_pubkey,
lockup,
new_custodian_signer: None,
custodian: 0,
sign_only: false,
dump_transaction_message: false,
@@ -1197,6 +1264,7 @@ fn test_stake_set_lockup() {
config.command = CliCommand::StakeSetLockup {
stake_account_pubkey,
lockup,
new_custodian_signer: None,
custodian: 0,
sign_only: false,
dump_transaction_message: false,
@@ -1217,6 +1285,7 @@ fn test_stake_set_lockup() {
config.command = CliCommand::StakeSetLockup {
stake_account_pubkey,
lockup,
new_custodian_signer: None,
custodian: 1,
sign_only: false,
dump_transaction_message: false,
@@ -1249,6 +1318,7 @@ fn test_stake_set_lockup() {
config.command = CliCommand::StakeSetLockup {
stake_account_pubkey,
lockup,
new_custodian_signer: None,
custodian: 1,
sign_only: false,
dump_transaction_message: false,
@@ -1296,6 +1366,7 @@ fn test_stake_set_lockup() {
config_offline.command = CliCommand::StakeSetLockup {
stake_account_pubkey,
lockup,
new_custodian_signer: None,
custodian: 0,
sign_only: true,
dump_transaction_message: false,
@@ -1314,6 +1385,7 @@ fn test_stake_set_lockup() {
config.command = CliCommand::StakeSetLockup {
stake_account_pubkey,
lockup,
new_custodian_signer: None,
custodian: 0,
sign_only: false,
dump_transaction_message: false,
@@ -1408,6 +1480,7 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
seed: None,
staker: None,
withdrawer: None,
withdrawer_signer: None,
lockup: Lockup::default(),
amount: SpendAmount::Some(50_000),
sign_only: true,
@@ -1431,6 +1504,7 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
seed: None,
staker: Some(offline_pubkey),
withdrawer: None,
withdrawer_signer: None,
lockup: Lockup::default(),
amount: SpendAmount::Some(50_000),
sign_only: false,
@@ -1520,6 +1594,7 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
seed: Some(seed.to_string()),
staker: None,
withdrawer: None,
withdrawer_signer: None,
lockup: Lockup::default(),
amount: SpendAmount::Some(50_000),
sign_only: true,
@@ -1541,6 +1616,7 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
seed: Some(seed.to_string()),
staker: Some(offline_pubkey),
withdrawer: Some(offline_pubkey),
withdrawer_signer: None,
lockup: Lockup::default(),
amount: SpendAmount::Some(50_000),
sign_only: false,
@@ -1557,6 +1633,231 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
};
process_command(&config).unwrap();
let seed_address =
Pubkey::create_with_seed(&stake_pubkey, seed, &solana_stake_program::id()).unwrap();
Pubkey::create_with_seed(&stake_pubkey, seed, &stake::program::id()).unwrap();
check_recent_balance(50_000, &rpc_client, &seed_address);
}
#[test]
fn test_stake_checked_instructions() {
solana_logger::setup();
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator = TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr));
let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
let default_signer = Keypair::new();
let mut config = CliConfig::recent_for_tests();
config.json_rpc_url = test_validator.rpc_url();
config.signers = vec![&default_signer];
request_and_confirm_airdrop(&rpc_client, &config, &config.signers[0].pubkey(), 100_000)
.unwrap();
// Create stake account with withdrawer
let stake_keypair = Keypair::new();
let stake_account_pubkey = stake_keypair.pubkey();
let withdrawer_keypair = Keypair::new();
let withdrawer_pubkey = withdrawer_keypair.pubkey();
config.signers.push(&stake_keypair);
config.command = CliCommand::CreateStakeAccount {
stake_account: 1,
seed: None,
staker: None,
withdrawer: Some(withdrawer_pubkey),
withdrawer_signer: Some(1),
lockup: Lockup::default(),
amount: SpendAmount::Some(50_000),
sign_only: false,
dump_transaction_message: false,
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
from: 0,
};
process_command(&config).unwrap_err(); // unsigned authority should fail
config.signers = vec![&default_signer, &stake_keypair, &withdrawer_keypair];
config.command = CliCommand::CreateStakeAccount {
stake_account: 1,
seed: None,
staker: None,
withdrawer: Some(withdrawer_pubkey),
withdrawer_signer: Some(1),
lockup: Lockup::default(),
amount: SpendAmount::Some(50_000),
sign_only: false,
dump_transaction_message: false,
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
from: 0,
};
process_command(&config).unwrap();
// Re-authorize account, checking new authority
let staker_keypair = Keypair::new();
let staker_pubkey = staker_keypair.pubkey();
config.signers = vec![&default_signer];
config.command = CliCommand::StakeAuthorize {
stake_account_pubkey,
new_authorizations: vec![StakeAuthorizationIndexed {
authorization_type: StakeAuthorize::Staker,
new_authority_pubkey: staker_pubkey,
authority: 0,
new_authority_signer: Some(0),
}],
sign_only: false,
dump_transaction_message: false,
blockhash_query: BlockhashQuery::default(),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
custodian: None,
no_wait: false,
};
process_command(&config).unwrap_err(); // unsigned authority should fail
config.signers = vec![&default_signer, &staker_keypair];
config.command = CliCommand::StakeAuthorize {
stake_account_pubkey,
new_authorizations: vec![StakeAuthorizationIndexed {
authorization_type: StakeAuthorize::Staker,
new_authority_pubkey: staker_pubkey,
authority: 0,
new_authority_signer: Some(1),
}],
sign_only: false,
dump_transaction_message: false,
blockhash_query: BlockhashQuery::default(),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
custodian: None,
no_wait: false,
};
process_command(&config).unwrap();
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
let stake_state: StakeState = stake_account.state().unwrap();
let current_authority = match stake_state {
StakeState::Initialized(meta) => meta.authorized.staker,
_ => panic!("Unexpected stake state!"),
};
assert_eq!(current_authority, staker_pubkey);
let new_withdrawer_keypair = Keypair::new();
let new_withdrawer_pubkey = new_withdrawer_keypair.pubkey();
config.signers = vec![&default_signer, &withdrawer_keypair];
config.command = CliCommand::StakeAuthorize {
stake_account_pubkey,
new_authorizations: vec![StakeAuthorizationIndexed {
authorization_type: StakeAuthorize::Withdrawer,
new_authority_pubkey: new_withdrawer_pubkey,
authority: 1,
new_authority_signer: Some(1),
}],
sign_only: false,
dump_transaction_message: false,
blockhash_query: BlockhashQuery::default(),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
custodian: None,
no_wait: false,
};
process_command(&config).unwrap_err(); // unsigned authority should fail
config.signers = vec![
&default_signer,
&withdrawer_keypair,
&new_withdrawer_keypair,
];
config.command = CliCommand::StakeAuthorize {
stake_account_pubkey,
new_authorizations: vec![StakeAuthorizationIndexed {
authorization_type: StakeAuthorize::Withdrawer,
new_authority_pubkey: new_withdrawer_pubkey,
authority: 1,
new_authority_signer: Some(2),
}],
sign_only: false,
dump_transaction_message: false,
blockhash_query: BlockhashQuery::default(),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
custodian: None,
no_wait: false,
};
process_command(&config).unwrap();
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
let stake_state: StakeState = stake_account.state().unwrap();
let current_authority = match stake_state {
StakeState::Initialized(meta) => meta.authorized.withdrawer,
_ => panic!("Unexpected stake state!"),
};
assert_eq!(current_authority, new_withdrawer_pubkey);
// Set lockup, checking new custodian
let custodian = Keypair::new();
let custodian_pubkey = custodian.pubkey();
let lockup = LockupArgs {
unix_timestamp: Some(1_581_534_570),
epoch: Some(200),
custodian: Some(custodian_pubkey),
};
config.signers = vec![&default_signer, &new_withdrawer_keypair];
config.command = CliCommand::StakeSetLockup {
stake_account_pubkey,
lockup,
new_custodian_signer: Some(1),
custodian: 1,
sign_only: false,
dump_transaction_message: false,
blockhash_query: BlockhashQuery::default(),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
};
process_command(&config).unwrap_err(); // unsigned new custodian should fail
config.signers = vec![&default_signer, &new_withdrawer_keypair, &custodian];
config.command = CliCommand::StakeSetLockup {
stake_account_pubkey,
lockup,
new_custodian_signer: Some(2),
custodian: 1,
sign_only: false,
dump_transaction_message: false,
blockhash_query: BlockhashQuery::default(),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
};
process_command(&config).unwrap();
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
let stake_state: StakeState = stake_account.state().unwrap();
let current_lockup = match stake_state {
StakeState::Initialized(meta) => meta.lockup,
_ => panic!("Unexpected stake state!"),
};
assert_eq!(
current_lockup.unix_timestamp,
lockup.unix_timestamp.unwrap()
);
assert_eq!(current_lockup.epoch, lockup.epoch.unwrap());
assert_eq!(current_lockup.custodian, custodian_pubkey);
}

View File

@@ -16,6 +16,7 @@ use solana_sdk::{
nonce::State as NonceState,
pubkey::Pubkey,
signature::{keypair_from_seed, Keypair, NullSigner, Signer},
stake,
};
#[test]
@@ -513,7 +514,7 @@ fn test_transfer_with_seed() {
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_program_id = stake::program::id();
let derived_address = Pubkey::create_with_seed(
&sender_pubkey,
&derived_address_seed,

View File

@@ -83,13 +83,48 @@ fn test_vote_authorize_and_withdraw() {
check_recent_balance(expected_balance, &rpc_client, &vote_account_pubkey);
// Authorize vote account withdrawal to another signer
let withdraw_authority = Keypair::new();
let first_withdraw_authority = Keypair::new();
config.signers = vec![&default_signer];
config.command = CliCommand::VoteAuthorize {
vote_account_pubkey,
new_authorized_pubkey: first_withdraw_authority.pubkey(),
vote_authorize: VoteAuthorize::Withdrawer,
memo: None,
authorized: 0,
new_authorized: None,
};
process_command(&config).unwrap();
let vote_account = rpc_client
.get_account(&vote_account_keypair.pubkey())
.unwrap();
let vote_state: VoteStateVersions = vote_account.state().unwrap();
let authorized_withdrawer = vote_state.convert_to_current().authorized_withdrawer;
assert_eq!(authorized_withdrawer, first_withdraw_authority.pubkey());
// Authorize vote account withdrawal to another signer with checked instruction
let withdraw_authority = Keypair::new();
config.signers = vec![&default_signer, &first_withdraw_authority];
config.command = CliCommand::VoteAuthorize {
vote_account_pubkey,
new_authorized_pubkey: withdraw_authority.pubkey(),
vote_authorize: VoteAuthorize::Withdrawer,
memo: None,
authorized: 1,
new_authorized: Some(1),
};
process_command(&config).unwrap_err(); // unsigned by new authority should fail
config.signers = vec![
&default_signer,
&first_withdraw_authority,
&withdraw_authority,
];
config.command = CliCommand::VoteAuthorize {
vote_account_pubkey,
new_authorized_pubkey: withdraw_authority.pubkey(),
vote_authorize: VoteAuthorize::Withdrawer,
memo: None,
authorized: 1,
new_authorized: Some(2),
};
process_command(&config).unwrap();
let vote_account = rpc_client

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-client"
version = "1.7.0"
version = "1.7.5"
description = "Solana Client"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
@@ -24,14 +24,14 @@ semver = "0.11.0"
serde = "1.0.122"
serde_derive = "1.0.103"
serde_json = "1.0.56"
solana-account-decoder = { path = "../account-decoder", version = "=1.7.0" }
solana-clap-utils = { path = "../clap-utils", version = "=1.7.0" }
solana-faucet = { path = "../faucet", version = "=1.7.0" }
solana-net-utils = { path = "../net-utils", version = "=1.7.0" }
solana-sdk = { path = "../sdk", version = "=1.7.0" }
solana-transaction-status = { path = "../transaction-status", version = "=1.7.0" }
solana-version = { path = "../version", version = "=1.7.0" }
solana-vote-program = { path = "../programs/vote", version = "=1.7.0" }
solana-account-decoder = { path = "../account-decoder", version = "=1.7.5" }
solana-clap-utils = { path = "../clap-utils", version = "=1.7.5" }
solana-faucet = { path = "../faucet", version = "=1.7.5" }
solana-net-utils = { path = "../net-utils", version = "=1.7.5" }
solana-sdk = { path = "../sdk", version = "=1.7.5" }
solana-transaction-status = { path = "../transaction-status", version = "=1.7.5" }
solana-version = { path = "../version", version = "=1.7.5" }
solana-vote-program = { path = "../programs/vote", version = "=1.7.5" }
thiserror = "1.0"
tokio = { version = "1", features = ["full"] }
tungstenite = "0.10.1"
@@ -40,7 +40,7 @@ url = "2.1.1"
[dev-dependencies]
assert_matches = "1.3.0"
jsonrpc-http-server = "17.0.0"
solana-logger = { path = "../logger", version = "=1.7.0" }
solana-logger = { path = "../logger", version = "=1.7.5" }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -1,5 +1,5 @@
use {
crate::rpc_request,
crate::{rpc_request, rpc_response},
solana_faucet::faucet::FaucetError,
solana_sdk::{
signature::SignerError, transaction::TransactionError, transport::TransportError,
@@ -30,6 +30,24 @@ pub enum ClientErrorKind {
Custom(String),
}
impl ClientErrorKind {
pub fn get_transaction_error(&self) -> Option<TransactionError> {
match self {
Self::RpcError(rpc_request::RpcError::RpcResponseError {
data:
rpc_request::RpcResponseErrorData::SendTransactionPreflightFailure(
rpc_response::RpcSimulateTransactionResult {
err: Some(tx_err), ..
},
),
..
}) => Some(tx_err.clone()),
Self::TransactionError(tx_err) => Some(tx_err.clone()),
_ => None,
}
}
}
impl From<TransportError> for ClientErrorKind {
fn from(err: TransportError) -> Self {
match err {
@@ -86,6 +104,10 @@ impl ClientError {
pub fn kind(&self) -> &ClientErrorKind {
&self.kind
}
pub fn get_transaction_error(&self) -> Option<TransactionError> {
self.kind.get_transaction_error()
}
}
impl From<ClientErrorKind> for ClientError {

9
client/src/fees.rs Normal file
View File

@@ -0,0 +1,9 @@
use crate::{fee_calculator::FeeCalculator, hash::Hash};
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Fees {
pub blockhash: Hash,
pub fee_calculator: FeeCalculator,
pub last_valid_block_height: u64,
}

View File

@@ -31,7 +31,7 @@ impl LargestAccountsCache {
&self,
filter: &Option<RpcLargestAccountsFilter>,
) -> Option<(u64, Vec<RpcAccountBalance>)> {
self.cache.get(&filter).and_then(|value| {
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()));

View File

@@ -49,41 +49,36 @@ use {
},
};
pub struct RpcClient {
sender: Box<dyn RpcSender + Send + Sync + 'static>,
#[derive(Default)]
pub struct RpcClientConfig {
commitment_config: CommitmentConfig,
node_version: RwLock<Option<semver::Version>>,
confirm_transaction_initial_timeout: Option<Duration>,
}
fn serialize_encode_transaction(
transaction: &Transaction,
encoding: UiTransactionEncoding,
) -> ClientResult<String> {
let serialized = serialize(transaction)
.map_err(|e| ClientErrorKind::Custom(format!("transaction serialization failed: {}", e)))?;
let encoded = match encoding {
UiTransactionEncoding::Base58 => bs58::encode(serialized).into_string(),
UiTransactionEncoding::Base64 => base64::encode(serialized),
_ => {
return Err(ClientErrorKind::Custom(format!(
"unsupported transaction encoding: {}. Supported encodings: base58, base64",
encoding
))
.into())
impl RpcClientConfig {
fn with_commitment(commitment_config: CommitmentConfig) -> Self {
RpcClientConfig {
commitment_config,
..Self::default()
}
};
Ok(encoded)
}
}
pub struct RpcClient {
sender: Box<dyn RpcSender + Send + Sync + 'static>,
config: RpcClientConfig,
node_version: RwLock<Option<semver::Version>>,
}
impl RpcClient {
fn new_sender<T: RpcSender + Send + Sync + 'static>(
sender: T,
commitment_config: CommitmentConfig,
config: RpcClientConfig,
) -> Self {
Self {
sender: Box::new(sender),
node_version: RwLock::new(None),
commitment_config,
config,
}
}
@@ -92,13 +87,16 @@ impl RpcClient {
}
pub fn new_with_commitment(url: String, commitment_config: CommitmentConfig) -> Self {
Self::new_sender(HttpSender::new(url), commitment_config)
Self::new_sender(
HttpSender::new(url),
RpcClientConfig::with_commitment(commitment_config),
)
}
pub fn new_with_timeout(url: String, timeout: Duration) -> Self {
Self::new_sender(
HttpSender::new_with_timeout(url, timeout),
CommitmentConfig::default(),
RpcClientConfig::with_commitment(CommitmentConfig::default()),
)
}
@@ -109,18 +107,36 @@ impl RpcClient {
) -> Self {
Self::new_sender(
HttpSender::new_with_timeout(url, timeout),
commitment_config,
RpcClientConfig::with_commitment(commitment_config),
)
}
pub fn new_with_timeouts_and_commitment(
url: String,
timeout: Duration,
commitment_config: CommitmentConfig,
confirm_transaction_initial_timeout: Duration,
) -> Self {
Self::new_sender(
HttpSender::new_with_timeout(url, timeout),
RpcClientConfig {
commitment_config,
confirm_transaction_initial_timeout: Some(confirm_transaction_initial_timeout),
},
)
}
pub fn new_mock(url: String) -> Self {
Self::new_sender(MockSender::new(url), CommitmentConfig::default())
Self::new_sender(
MockSender::new(url),
RpcClientConfig::with_commitment(CommitmentConfig::default()),
)
}
pub fn new_mock_with_mocks(url: String, mocks: Mocks) -> Self {
Self::new_sender(
MockSender::new_with_mocks(url, mocks),
CommitmentConfig::default(),
RpcClientConfig::with_commitment(CommitmentConfig::default()),
)
}
@@ -159,7 +175,7 @@ impl RpcClient {
}
pub fn commitment(&self) -> CommitmentConfig {
self.commitment_config
self.config.commitment_config
}
fn use_deprecated_commitment(&self) -> Result<bool, RpcError> {
@@ -201,7 +217,7 @@ impl RpcClient {
pub fn confirm_transaction(&self, signature: &Signature) -> ClientResult<bool> {
Ok(self
.confirm_transaction_with_commitment(signature, self.commitment_config)?
.confirm_transaction_with_commitment(signature, self.commitment())?
.value)
}
@@ -227,8 +243,7 @@ impl RpcClient {
transaction,
RpcSendTransactionConfig {
preflight_commitment: Some(
self.maybe_map_commitment(self.commitment_config)?
.commitment,
self.maybe_map_commitment(self.commitment())?.commitment,
),
..RpcSendTransactionConfig::default()
},
@@ -317,7 +332,7 @@ impl RpcClient {
self.simulate_transaction_with_config(
transaction,
RpcSimulateTransactionConfig {
commitment: Some(self.commitment_config),
commitment: Some(self.commitment()),
..RpcSimulateTransactionConfig::default()
},
)
@@ -355,7 +370,7 @@ impl RpcClient {
&self,
signature: &Signature,
) -> ClientResult<Option<transaction::Result<()>>> {
self.get_signature_status_with_commitment(signature, self.commitment_config)
self.get_signature_status_with_commitment(signature, self.commitment())
}
pub fn get_signature_statuses(
@@ -413,7 +428,7 @@ impl RpcClient {
}
pub fn get_slot(&self) -> ClientResult<Slot> {
self.get_slot_with_commitment(self.commitment_config)
self.get_slot_with_commitment(self.commitment())
}
pub fn get_slot_with_commitment(
@@ -427,7 +442,7 @@ impl RpcClient {
}
pub fn get_block_height(&self) -> ClientResult<u64> {
self.get_block_height_with_commitment(self.commitment_config)
self.get_block_height_with_commitment(self.commitment())
}
pub fn get_block_height_with_commitment(
@@ -481,14 +496,14 @@ impl RpcClient {
stake_account.to_string(),
RpcEpochConfig {
epoch,
commitment: Some(self.commitment_config),
commitment: Some(self.commitment()),
}
]),
)
}
pub fn supply(&self) -> RpcResult<RpcSupply> {
self.supply_with_commitment(self.commitment_config)
self.supply_with_commitment(self.commitment())
}
pub fn supply_with_commitment(
@@ -515,7 +530,7 @@ impl RpcClient {
}
pub fn get_vote_accounts(&self) -> ClientResult<RpcVoteAccountStatus> {
self.get_vote_accounts_with_commitment(self.commitment_config)
self.get_vote_accounts_with_commitment(self.commitment())
}
pub fn get_vote_accounts_with_commitment(
@@ -892,7 +907,7 @@ impl RpcClient {
}
pub fn get_epoch_info(&self) -> ClientResult<EpochInfo> {
self.get_epoch_info_with_commitment(self.commitment_config)
self.get_epoch_info_with_commitment(self.commitment())
}
pub fn get_epoch_info_with_commitment(
@@ -909,7 +924,7 @@ impl RpcClient {
&self,
slot: Option<Slot>,
) -> ClientResult<Option<RpcLeaderSchedule>> {
self.get_leader_schedule_with_commitment(slot, self.commitment_config)
self.get_leader_schedule_with_commitment(slot, self.commitment())
}
pub fn get_leader_schedule_with_commitment(
@@ -979,7 +994,7 @@ impl RpcClient {
addresses,
RpcEpochConfig {
epoch,
commitment: Some(self.commitment_config),
commitment: Some(self.commitment()),
}
]),
)
@@ -997,55 +1012,61 @@ impl RpcClient {
&self,
transaction: &Transaction,
) -> ClientResult<Signature> {
let signature = self.send_transaction(transaction)?;
let recent_blockhash = if uses_durable_nonce(transaction).is_some() {
self.get_recent_blockhash_with_commitment(CommitmentConfig::processed())?
.value
.0
} else {
transaction.message.recent_blockhash
};
let status = loop {
let status = self.get_signature_status(&signature)?;
if status.is_none() {
if self
.get_fee_calculator_for_blockhash_with_commitment(
&recent_blockhash,
CommitmentConfig::processed(),
)?
.value
.is_none()
{
break status;
}
const SEND_RETRIES: usize = 1;
const GET_STATUS_RETRIES: usize = usize::MAX;
'sending: for _ in 0..SEND_RETRIES {
let signature = self.send_transaction(transaction)?;
let recent_blockhash = if uses_durable_nonce(transaction).is_some() {
let (recent_blockhash, ..) = self
.get_recent_blockhash_with_commitment(CommitmentConfig::processed())?
.value;
recent_blockhash
} else {
break status;
transaction.message.recent_blockhash
};
for status_retry in 0..GET_STATUS_RETRIES {
match self.get_signature_status(&signature)? {
Some(Ok(_)) => return Ok(signature),
Some(Err(e)) => return Err(e.into()),
None => {
let fee_calculator = self
.get_fee_calculator_for_blockhash_with_commitment(
&recent_blockhash,
CommitmentConfig::processed(),
)?
.value;
if fee_calculator.is_none() {
// Block hash is not found by some reason
break 'sending;
} else if cfg!(not(test))
// Ignore sleep at last step.
&& status_retry < GET_STATUS_RETRIES
{
// Retry twice a second
sleep(Duration::from_millis(500));
continue;
}
}
}
}
if cfg!(not(test)) {
// Retry twice a second
sleep(Duration::from_millis(500));
}
};
if let Some(result) = status {
match result {
Ok(_) => Ok(signature),
Err(err) => Err(err.into()),
}
} else {
Err(RpcError::ForUser(
"unable to confirm transaction. \
This can happen in situations such as transaction expiration \
and insufficient fee-payer funds"
.to_string(),
)
.into())
}
Err(RpcError::ForUser(
"unable to confirm transaction. \
This can happen in situations such as transaction expiration \
and insufficient fee-payer funds"
.to_string(),
)
.into())
}
/// Note that `get_account` returns `Err(..)` if the account does not exist whereas
/// `get_account_with_commitment` returns `Ok(None)` if the account does not exist.
pub fn get_account(&self, pubkey: &Pubkey) -> ClientResult<Account> {
self.get_account_with_commitment(pubkey, self.commitment_config)?
self.get_account_with_commitment(pubkey, self.commitment())?
.value
.ok_or_else(|| RpcError::ForUser(format!("AccountNotFound: pubkey={}", pubkey)).into())
}
@@ -1101,7 +1122,7 @@ impl RpcClient {
pub fn get_multiple_accounts(&self, pubkeys: &[Pubkey]) -> ClientResult<Vec<Option<Account>>> {
Ok(self
.get_multiple_accounts_with_commitment(pubkeys, self.commitment_config)?
.get_multiple_accounts_with_commitment(pubkeys, self.commitment())?
.value)
}
@@ -1155,7 +1176,7 @@ impl RpcClient {
/// Request the balance of the account `pubkey`.
pub fn get_balance(&self, pubkey: &Pubkey) -> ClientResult<u64> {
Ok(self
.get_balance_with_commitment(pubkey, self.commitment_config)?
.get_balance_with_commitment(pubkey, self.commitment())?
.value)
}
@@ -1213,7 +1234,7 @@ impl RpcClient {
/// Request the transaction count.
pub fn get_transaction_count(&self) -> ClientResult<u64> {
self.get_transaction_count_with_commitment(self.commitment_config)
self.get_transaction_count_with_commitment(self.commitment())
}
pub fn get_transaction_count_with_commitment(
@@ -1226,9 +1247,37 @@ impl RpcClient {
)
}
pub fn get_fees(&self) -> ClientResult<Fees> {
Ok(self.get_fees_with_commitment(self.commitment())?.value)
}
pub fn get_fees_with_commitment(&self, commitment_config: CommitmentConfig) -> RpcResult<Fees> {
let Response {
context,
value: fees,
} = self.send::<Response<RpcFees>>(
RpcRequest::GetFees,
json!([self.maybe_map_commitment(commitment_config)?]),
)?;
let blockhash = fees.blockhash.parse().map_err(|_| {
ClientError::new_with_request(
RpcError::ParseError("Hash".to_string()).into(),
RpcRequest::GetFees,
)
})?;
Ok(Response {
context,
value: Fees {
blockhash,
fee_calculator: fees.fee_calculator,
last_valid_block_height: fees.last_valid_block_height,
},
})
}
pub fn get_recent_blockhash(&self) -> ClientResult<(Hash, FeeCalculator)> {
let (blockhash, fee_calculator, _last_valid_slot) = self
.get_recent_blockhash_with_commitment(self.commitment_config)?
.get_recent_blockhash_with_commitment(self.commitment())?
.value;
Ok((blockhash, fee_calculator))
}
@@ -1301,7 +1350,7 @@ impl RpcClient {
blockhash: &Hash,
) -> ClientResult<Option<FeeCalculator>> {
Ok(self
.get_fee_calculator_for_blockhash_with_commitment(blockhash, self.commitment_config)?
.get_fee_calculator_for_blockhash_with_commitment(blockhash, self.commitment())?
.value)
}
@@ -1383,7 +1432,7 @@ impl RpcClient {
pub fn get_token_account(&self, pubkey: &Pubkey) -> ClientResult<Option<UiTokenAccount>> {
Ok(self
.get_token_account_with_commitment(pubkey, self.commitment_config)?
.get_token_account_with_commitment(pubkey, self.commitment())?
.value)
}
@@ -1444,7 +1493,7 @@ impl RpcClient {
pub fn get_token_account_balance(&self, pubkey: &Pubkey) -> ClientResult<UiTokenAmount> {
Ok(self
.get_token_account_balance_with_commitment(pubkey, self.commitment_config)?
.get_token_account_balance_with_commitment(pubkey, self.commitment())?
.value)
}
@@ -1471,7 +1520,7 @@ impl RpcClient {
.get_token_accounts_by_delegate_with_commitment(
delegate,
token_account_filter,
self.commitment_config,
self.commitment(),
)?
.value)
}
@@ -1510,7 +1559,7 @@ impl RpcClient {
.get_token_accounts_by_owner_with_commitment(
owner,
token_account_filter,
self.commitment_config,
self.commitment(),
)?
.value)
}
@@ -1542,7 +1591,7 @@ impl RpcClient {
pub fn get_token_supply(&self, mint: &Pubkey) -> ClientResult<UiTokenAmount> {
Ok(self
.get_token_supply_with_commitment(mint, self.commitment_config)?
.get_token_supply_with_commitment(mint, self.commitment())?
.value)
}
@@ -1565,7 +1614,7 @@ impl RpcClient {
pubkey,
lamports,
RpcRequestAirdropConfig {
commitment: Some(self.commitment_config),
commitment: Some(self.commitment()),
..RpcRequestAirdropConfig::default()
},
)
@@ -1581,7 +1630,7 @@ impl RpcClient {
pubkey,
lamports,
RpcRequestAirdropConfig {
commitment: Some(self.commitment_config),
commitment: Some(self.commitment()),
recent_blockhash: Some(recent_blockhash.to_string()),
},
)
@@ -1627,7 +1676,7 @@ impl RpcClient {
) -> ClientResult<u64> {
let now = Instant::now();
loop {
match self.get_balance_with_commitment(&pubkey, commitment_config) {
match self.get_balance_with_commitment(pubkey, commitment_config) {
Ok(bal) => {
return Ok(bal.value);
}
@@ -1684,7 +1733,7 @@ impl RpcClient {
/// Poll the server to confirm a transaction.
pub fn poll_for_signature(&self, signature: &Signature) -> ClientResult<()> {
self.poll_for_signature_with_commitment(signature, self.commitment_config)
self.poll_for_signature_with_commitment(signature, self.commitment())
}
/// Poll the server to confirm a transaction.
@@ -1696,7 +1745,7 @@ impl RpcClient {
let now = Instant::now();
loop {
if let Ok(Some(_)) =
self.get_signature_status_with_commitment(&signature, commitment_config)
self.get_signature_status_with_commitment(signature, commitment_config)
{
break;
}
@@ -1794,7 +1843,7 @@ impl RpcClient {
) -> ClientResult<Signature> {
self.send_and_confirm_transaction_with_spinner_and_commitment(
transaction,
self.commitment_config,
self.commitment(),
)
}
@@ -1850,19 +1899,25 @@ impl RpcClient {
"[{}/{}] Finalizing transaction {}",
confirmations, desired_confirmations, signature,
));
let now = Instant::now();
let confirm_transaction_initial_timeout = self
.config
.confirm_transaction_initial_timeout
.unwrap_or_default();
let (signature, status) = loop {
// Get recent commitment in order to count confirmations for successful transactions
let status = self
.get_signature_status_with_commitment(&signature, CommitmentConfig::processed())?;
.get_signature_status_with_commitment(signature, CommitmentConfig::processed())?;
if status.is_none() {
if self
let blockhash_not_found = self
.get_fee_calculator_for_blockhash_with_commitment(
&recent_blockhash,
recent_blockhash,
CommitmentConfig::processed(),
)?
.value
.is_none()
{
.is_none();
if blockhash_not_found && now.elapsed() >= confirm_transaction_initial_timeout {
break (signature, status);
}
} else {
@@ -1891,7 +1946,7 @@ impl RpcClient {
// Return when specified commitment is reached
// Failed transactions have already been eliminated, `is_some` check is sufficient
if self
.get_signature_status_with_commitment(&signature, commitment)?
.get_signature_status_with_commitment(signature, commitment)?
.is_some()
{
progress_bar.set_message("Transaction confirmed");
@@ -1907,7 +1962,7 @@ impl RpcClient {
));
sleep(Duration::from_millis(500));
confirmations = self
.get_num_blocks_since_signature_confirmation(&signature)
.get_num_blocks_since_signature_confirmation(signature)
.unwrap_or(confirmations);
if now.elapsed().as_secs() >= MAX_HASH_AGE_IN_SECONDS as u64 {
return Err(
@@ -1933,6 +1988,26 @@ impl RpcClient {
}
}
fn serialize_encode_transaction(
transaction: &Transaction,
encoding: UiTransactionEncoding,
) -> ClientResult<String> {
let serialized = serialize(transaction)
.map_err(|e| ClientErrorKind::Custom(format!("transaction serialization failed: {}", e)))?;
let encoded = match encoding {
UiTransactionEncoding::Base58 => bs58::encode(serialized).into_string(),
UiTransactionEncoding::Base64 => base64::encode(serialized),
_ => {
return Err(ClientErrorKind::Custom(format!(
"unsupported transaction encoding: {}. Supported encodings: base58, base64",
encoding
))
.into())
}
};
Ok(encoded)
}
#[derive(Debug, Default)]
pub struct GetConfirmedSignaturesForAddress2Config {
pub before: Option<Signature>,

View File

@@ -81,6 +81,8 @@ pub struct RpcGetVoteAccountsConfig {
pub vote_pubkey: Option<String>, // validator vote address, as a base-58 encoded string
#[serde(flatten)]
pub commitment: Option<CommitmentConfig>,
pub keep_unstaked_delinquents: Option<bool>,
pub delinquent_slot_distance: Option<u64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]

View File

@@ -1,5 +1,5 @@
//! Implementation defined RPC server errors
use thiserror::Error;
use {
crate::rpc_response::RpcSimulateTransactionResult,
jsonrpc_core::{Error, ErrorCode},
@@ -17,35 +17,43 @@ pub const JSON_RPC_SERVER_ERROR_NO_SNAPSHOT: i64 = -32008;
pub const JSON_RPC_SERVER_ERROR_LONG_TERM_STORAGE_SLOT_SKIPPED: i64 = -32009;
pub const JSON_RPC_SERVER_ERROR_KEY_EXCLUDED_FROM_SECONDARY_INDEX: i64 = -32010;
pub const JSON_RPC_SERVER_ERROR_TRANSACTION_HISTORY_NOT_AVAILABLE: i64 = -32011;
pub const JSON_RPC_SCAN_ERROR: i64 = -32012;
pub const JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_LEN_MISMATCH: i64 = -32013;
#[derive(Error, Debug)]
pub enum RpcCustomError {
#[error("BlockCleanedUp")]
BlockCleanedUp {
slot: Slot,
first_available_block: Slot,
},
#[error("SendTransactionPreflightFailure")]
SendTransactionPreflightFailure {
message: String,
result: RpcSimulateTransactionResult,
},
#[error("TransactionSignatureVerificationFailure")]
TransactionSignatureVerificationFailure,
BlockNotAvailable {
slot: Slot,
},
NodeUnhealthy {
num_slots_behind: Option<Slot>,
},
#[error("BlockNotAvailable")]
BlockNotAvailable { slot: Slot },
#[error("NodeUnhealthy")]
NodeUnhealthy { num_slots_behind: Option<Slot> },
#[error("TransactionPrecompileVerificationFailure")]
TransactionPrecompileVerificationFailure(solana_sdk::transaction::TransactionError),
SlotSkipped {
slot: Slot,
},
#[error("SlotSkipped")]
SlotSkipped { slot: Slot },
#[error("NoSnapshot")]
NoSnapshot,
LongTermStorageSlotSkipped {
slot: Slot,
},
KeyExcludedFromSecondaryIndex {
index_key: String,
},
#[error("LongTermStorageSlotSkipped")]
LongTermStorageSlotSkipped { slot: Slot },
#[error("KeyExcludedFromSecondaryIndex")]
KeyExcludedFromSecondaryIndex { index_key: String },
#[error("TransactionHistoryNotAvailable")]
TransactionHistoryNotAvailable,
#[error("ScanError")]
ScanError { message: String },
#[error("TransactionSignatureLenMismatch")]
TransactionSignatureLenMismatch,
}
#[derive(Debug, Serialize, Deserialize)]
@@ -141,6 +149,18 @@ impl From<RpcCustomError> for Error {
message: "Transaction history is not available from this node".to_string(),
data: None,
},
RpcCustomError::ScanError { message } => Self {
code: ErrorCode::ServerError(JSON_RPC_SCAN_ERROR),
message,
data: None,
},
RpcCustomError::TransactionSignatureLenMismatch => Self {
code: ErrorCode::ServerError(
JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_LEN_MISMATCH,
),
message: "Transaction signature length mismatch".to_string(),
data: None,
},
}
}
}

View File

@@ -4,6 +4,7 @@ use {
solana_sdk::{
clock::{Epoch, Slot, UnixTimestamp},
fee_calculator::{FeeCalculator, FeeRateGovernor},
hash::Hash,
inflation::Inflation,
transaction::{Result, TransactionError},
},
@@ -57,6 +58,14 @@ pub struct DeprecatedRpcFees {
pub last_valid_slot: Slot,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Fees {
pub blockhash: Hash,
pub fee_calculator: FeeCalculator,
pub last_valid_block_height: u64,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct RpcFeeCalculator {
@@ -394,8 +403,9 @@ pub struct RpcPerfSample {
pub struct RpcInflationReward {
pub epoch: Epoch,
pub effective_slot: Slot,
pub amount: u64, // lamports
pub post_balance: u64, // lamports
pub amount: u64, // lamports
pub post_balance: u64, // lamports
pub commission: Option<u8>, // Vote account commission when the reward was credited
}
impl From<ConfirmedTransactionStatusWithSignature> for RpcConfirmedTransactionStatusWithSignature {

View File

@@ -451,7 +451,7 @@ impl SyncClient for ThinClient {
) -> TransportResult<Option<transaction::Result<()>>> {
let status = self
.rpc_client()
.get_signature_status(&signature)
.get_signature_status(signature)
.map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
@@ -468,7 +468,7 @@ impl SyncClient for ThinClient {
) -> TransportResult<Option<transaction::Result<()>>> {
let status = self
.rpc_client()
.get_signature_status_with_commitment(&signature, commitment_config)
.get_signature_status_with_commitment(signature, commitment_config)
.map_err(|err| {
io::Error::new(
io::ErrorKind::Other,

View File

@@ -121,7 +121,7 @@ struct LeaderTpuCache {
impl LeaderTpuCache {
fn new(rpc_client: &RpcClient, first_slot: Slot) -> Self {
let leaders = Self::fetch_slot_leaders(rpc_client, first_slot).unwrap_or_default();
let leader_tpu_map = Self::fetch_cluster_tpu_sockets(&rpc_client).unwrap_or_default();
let leader_tpu_map = Self::fetch_cluster_tpu_sockets(rpc_client).unwrap_or_default();
Self {
first_slot,
leaders,

View File

@@ -1,7 +1,7 @@
[package]
name = "solana-core"
description = "Blockchain, Rebuilt for Scale"
version = "1.7.0"
version = "1.7.5"
homepage = "https://solana.com/"
documentation = "https://docs.rs/solana-core"
readme = "../README.md"
@@ -22,17 +22,12 @@ bv = { version = "0.11.1", features = ["serde"] }
bs58 = "0.3.1"
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"
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"
libc = "0.2.81"
log = "0.4.11"
lru = "0.6.1"
@@ -44,54 +39,52 @@ rand_chacha = "0.2.2"
rand_core = "0.6.2"
raptorq = "1.4.2"
rayon = "1.5.0"
regex = "1.3.9"
retain_mut = "0.1.2"
serde = "1.0.122"
serde_bytes = "0.11"
serde_derive = "1.0.103"
serde_json = "1.0.56"
solana-account-decoder = { path = "../account-decoder", version = "=1.7.0" }
solana-banks-server = { path = "../banks-server", version = "=1.7.0" }
solana-clap-utils = { path = "../clap-utils", version = "=1.7.0" }
solana-client = { path = "../client", version = "=1.7.0" }
solana-faucet = { path = "../faucet", version = "=1.7.0" }
solana-gossip = { path = "../gossip", version = "=1.7.0" }
solana-ledger = { path = "../ledger", version = "=1.7.0" }
solana-logger = { path = "../logger", version = "=1.7.0" }
solana-merkle-tree = { path = "../merkle-tree", version = "=1.7.0" }
solana-metrics = { path = "../metrics", version = "=1.7.0" }
solana-measure = { path = "../measure", version = "=1.7.0" }
solana-net-utils = { path = "../net-utils", version = "=1.7.0" }
solana-perf = { path = "../perf", version = "=1.7.0" }
solana-program-test = { path = "../program-test", version = "=1.7.0" }
solana-rpc = { path = "../rpc", version = "=1.7.0" }
solana-runtime = { path = "../runtime", version = "=1.7.0" }
solana-sdk = { path = "../sdk", version = "=1.7.0" }
solana-frozen-abi = { path = "../frozen-abi", version = "=1.7.0" }
solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.7.0" }
solana-stake-program = { path = "../programs/stake", version = "=1.7.0" }
solana-storage-bigtable = { path = "../storage-bigtable", version = "=1.7.0" }
solana-streamer = { path = "../streamer", version = "=1.7.0" }
solana-sys-tuner = { path = "../sys-tuner", version = "=1.7.0" }
solana-transaction-status = { path = "../transaction-status", version = "=1.7.0" }
solana-version = { path = "../version", version = "=1.7.0" }
solana-vote-program = { path = "../programs/vote", version = "=1.7.0" }
spl-token-v2-0 = { package = "spl-token", version = "=3.1.0", features = ["no-entrypoint"] }
solana-account-decoder = { path = "../account-decoder", version = "=1.7.5" }
solana-banks-server = { path = "../banks-server", version = "=1.7.5" }
solana-clap-utils = { path = "../clap-utils", version = "=1.7.5" }
solana-client = { path = "../client", version = "=1.7.5" }
solana-gossip = { path = "../gossip", version = "=1.7.5" }
solana-ledger = { path = "../ledger", version = "=1.7.5" }
solana-logger = { path = "../logger", version = "=1.7.5" }
solana-merkle-tree = { path = "../merkle-tree", version = "=1.7.5" }
solana-metrics = { path = "../metrics", version = "=1.7.5" }
solana-measure = { path = "../measure", version = "=1.7.5" }
solana-net-utils = { path = "../net-utils", version = "=1.7.5" }
solana-perf = { path = "../perf", version = "=1.7.5" }
solana-poh = { path = "../poh", version = "=1.7.5" }
solana-program-test = { path = "../program-test", version = "=1.7.5" }
solana-rpc = { path = "../rpc", version = "=1.7.5" }
solana-runtime = { path = "../runtime", version = "=1.7.5" }
solana-sdk = { path = "../sdk", version = "=1.7.5" }
solana-frozen-abi = { path = "../frozen-abi", version = "=1.7.5" }
solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.7.5" }
solana-streamer = { path = "../streamer", version = "=1.7.5" }
solana-transaction-status = { path = "../transaction-status", version = "=1.7.5" }
solana-version = { path = "../version", version = "=1.7.5" }
solana-vote-program = { path = "../programs/vote", version = "=1.7.5" }
spl-token-v2-0 = { package = "spl-token", version = "=3.1.1", features = ["no-entrypoint"] }
tempfile = "3.1.0"
thiserror = "1.0"
tokio = { version = "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.7.0" }
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.7.5" }
trees = "0.2.1"
[dev-dependencies]
jsonrpc-core = "17.1.0"
jsonrpc-core-client = { version = "17.1.0", features = ["ipc", "ws"] }
matches = "0.1.6"
num_cpus = "1.13.0"
reqwest = { version = "0.11.2", default-features = false, features = ["blocking", "rustls-tls", "json"] }
serde_json = "1.0.56"
serial_test = "0.4.0"
solana-stake-program = { path = "../programs/stake", version = "=1.7.5" }
solana-version = { path = "../version", version = "=1.7.5" }
symlink = "0.1.0"
systemstat = "0.1.5"
tokio_02 = { version = "0.2", package = "tokio", features = ["full"] }
[build-dependencies]
rustc_version = "0.2"
@@ -111,9 +104,6 @@ name = "gen_keys"
[[bench]]
name = "sigverify_stage"
[[bench]]
name = "poh"
[[bench]]
name = "retransmit_stage"

View File

@@ -7,8 +7,7 @@ use crossbeam_channel::unbounded;
use log::*;
use rand::{thread_rng, Rng};
use rayon::prelude::*;
use solana_core::banking_stage::{create_test_recorder, BankingStage, BankingStageStats};
use solana_core::poh_recorder::WorkingBankEntry;
use solana_core::banking_stage::{BankingStage, BankingStageStats};
use solana_gossip::cluster_info::ClusterInfo;
use solana_gossip::cluster_info::Node;
use solana_ledger::blockstore_processor::process_entries;
@@ -17,6 +16,7 @@ use solana_ledger::genesis_utils::{create_genesis_config, GenesisConfigInfo};
use solana_ledger::{blockstore::Blockstore, get_tmp_ledger_path};
use solana_perf::packet::to_packets_chunked;
use solana_perf::test_tx::test_tx;
use solana_poh::poh_recorder::{create_test_recorder, WorkingBankEntry};
use solana_runtime::bank::Bank;
use solana_sdk::genesis_config::GenesisConfig;
use solana_sdk::hash::Hash;
@@ -183,7 +183,7 @@ fn bench_banking(bencher: &mut Bencher, tx_type: TransactionType) {
});
//sanity check, make sure all the transactions can execute sequentially
transactions.iter().for_each(|tx| {
let res = bank.process_transaction(&tx);
let res = bank.process_transaction(tx);
assert!(res.is_ok(), "sanity test transactions");
});
bank.clear_signatures();

View File

@@ -3,10 +3,14 @@
extern crate test;
use rand::{thread_rng, Rng};
use solana_core::broadcast_stage::broadcast_metrics::TransmitShredsStats;
use solana_core::broadcast_stage::{broadcast_shreds, get_broadcast_peers};
use solana_gossip::cluster_info::{ClusterInfo, Node};
use solana_gossip::contact_info::ContactInfo;
use solana_core::{
broadcast_stage::{broadcast_metrics::TransmitShredsStats, broadcast_shreds, BroadcastStage},
cluster_nodes::ClusterNodes,
};
use solana_gossip::{
cluster_info::{ClusterInfo, Node},
contact_info::ContactInfo,
};
use solana_ledger::shred::Shred;
use solana_sdk::pubkey;
use solana_sdk::timing::timestamp;
@@ -36,7 +40,7 @@ fn broadcast_shreds_bench(bencher: &mut Bencher) {
stakes.insert(id, thread_rng().gen_range(1, NUM_PEERS) as u64);
}
let cluster_info = Arc::new(cluster_info);
let (peers, peers_and_stakes) = get_broadcast_peers(&cluster_info, Some(&stakes));
let cluster_nodes = ClusterNodes::<BroadcastStage>::new(&cluster_info, &stakes);
let shreds = Arc::new(shreds);
let last_datapoint = Arc::new(AtomicU64::new(0));
bencher.iter(move || {
@@ -44,8 +48,7 @@ fn broadcast_shreds_bench(bencher: &mut Bencher) {
broadcast_shreds(
&socket,
&shreds,
&peers_and_stakes,
&peers,
&cluster_nodes,
&last_datapoint,
&mut TransmitShredsStats::default(),
)

View File

@@ -24,10 +24,10 @@ fn bench_save_tower(bench: &mut Bencher) {
let heaviest_bank = BankForks::new(Bank::default()).working_bank();
let tower = Tower::new(
&node_keypair.pubkey(),
&vote_account_pubkey,
vote_account_pubkey,
0,
&heaviest_bank,
&path,
path,
);
bench.iter(move || {

View File

@@ -39,7 +39,12 @@ fn bench_retransmitter(bencher: &mut Bencher) {
const NUM_PEERS: usize = 4;
let mut peer_sockets = Vec::new();
for _ in 0..NUM_PEERS {
let id = pubkey::new_rand();
// This ensures that cluster_info.id() is the root of turbine
// retransmit tree and so the shreds are retransmited to all other
// nodes in the cluster.
let id = std::iter::repeat_with(pubkey::new_rand)
.find(|pk| cluster_info.id() < *pk)
.unwrap();
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
let mut contact_info = ContactInfo::new_localhost(&id, timestamp());
contact_info.tvu = socket.local_addr().unwrap();

View File

@@ -148,7 +148,7 @@ impl AccountsHashVerifier {
for (slot, hash) in hashes.iter() {
slot_to_hash.insert(*slot, *hash);
}
if Self::should_halt(&cluster_info, trusted_validators, &mut slot_to_hash) {
if Self::should_halt(cluster_info, trusted_validators, &mut slot_to_hash) {
exit.store(true, Ordering::Relaxed);
}
}

View File

@@ -1,20 +1,13 @@
//! The `banking_stage` processes Transaction messages. It is intended to be used
//! to contruct a software pipeline. The stage uses all available CPU cores and
//! can do its processing in parallel with signature verification on the GPU.
use crate::{
packet_hasher::PacketHasher,
poh_recorder::{PohRecorder, PohRecorderError, TransactionRecorder, WorkingBankEntry},
poh_service::{self, PohService},
};
use crate::packet_hasher::PacketHasher;
use crossbeam_channel::{Receiver as CrossbeamReceiver, RecvTimeoutError};
use itertools::Itertools;
use lru::LruCache;
use retain_mut::RetainMut;
use solana_gossip::cluster_info::ClusterInfo;
use solana_ledger::{
blockstore::Blockstore, blockstore_processor::TransactionStatusSender,
entry::hash_transactions, leader_schedule_cache::LeaderScheduleCache,
};
use solana_ledger::{blockstore_processor::TransactionStatusSender, entry::hash_transactions};
use solana_measure::measure::Measure;
use solana_metrics::{inc_new_counter_debug, inc_new_counter_info};
use solana_perf::{
@@ -22,6 +15,7 @@ use solana_perf::{
packet::{limited_deserialize, Packet, Packets, PACKETS_PER_BATCH},
perf_libs,
};
use solana_poh::poh_recorder::{PohRecorder, PohRecorderError, TransactionRecorder};
use solana_runtime::{
accounts_db::ErrorCounters,
bank::{
@@ -39,7 +33,6 @@ use solana_sdk::{
MAX_TRANSACTION_FORWARDING_DELAY_GPU,
},
message::Message,
poh_config::PohConfig,
pubkey::Pubkey,
short_vec::decode_shortu16_len,
signature::Signature,
@@ -57,8 +50,7 @@ use std::{
mem::size_of,
net::UdpSocket,
ops::DerefMut,
sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering},
sync::mpsc::Receiver,
sync::atomic::{AtomicU64, AtomicUsize, Ordering},
sync::{Arc, Mutex},
thread::{self, Builder, JoinHandle},
time::Duration,
@@ -360,9 +352,9 @@ impl BankingStage {
// We've hit the end of this slot, no need to perform more processing,
// just filter the remaining packets for the invalid (e.g. too old) ones
let new_unprocessed_indexes = Self::filter_unprocessed_packets(
&bank,
&msgs,
&original_unprocessed_indexes,
bank,
msgs,
original_unprocessed_indexes,
my_pubkey,
*next_leader,
);
@@ -377,8 +369,8 @@ impl BankingStage {
Self::process_packets_transactions(
&bank,
&bank_creation_time,
&recorder,
&msgs,
recorder,
msgs,
original_unprocessed_indexes.to_owned(),
transaction_status_sender.clone(),
gossip_vote_sender,
@@ -411,7 +403,7 @@ impl BankingStage {
// `original_unprocessed_indexes` must have remaining packets to process
// if not yet processed.
assert!(Self::packet_has_more_unprocessed_transactions(
&original_unprocessed_indexes
original_unprocessed_indexes
));
true
}
@@ -605,7 +597,7 @@ impl BankingStage {
let decision = Self::process_buffered_packets(
&my_pubkey,
&socket,
&poh_recorder,
poh_recorder,
cluster_info,
&mut buffered_packets,
enable_forwarding,
@@ -635,8 +627,8 @@ impl BankingStage {
match Self::process_packets(
&my_pubkey,
&verified_receiver,
&poh_recorder,
verified_receiver,
poh_recorder,
recv_start,
recv_timeout,
id,
@@ -746,7 +738,7 @@ impl BankingStage {
let mut mint_decimals: HashMap<Pubkey, u8> = HashMap::new();
let pre_token_balances = if transaction_status_sender.is_some() {
collect_token_balances(&bank, &batch, &mut mint_decimals)
collect_token_balances(bank, batch, &mut mint_decimals)
} else {
vec![]
};
@@ -806,7 +798,7 @@ impl BankingStage {
if let Some(transaction_status_sender) = transaction_status_sender {
let txs = batch.transactions_iter().cloned().collect();
let post_balances = bank.collect_balances(batch);
let post_token_balances = collect_token_balances(&bank, &batch, &mut mint_decimals);
let post_token_balances = collect_token_balances(bank, batch, &mut mint_decimals);
transaction_status_sender.send_transaction_status_batch(
bank.clone(),
txs,
@@ -984,16 +976,15 @@ impl BankingStage {
fn transactions_from_packets(
msgs: &Packets,
transaction_indexes: &[usize],
secp256k1_program_enabled: bool,
libsecp256k1_0_5_upgrade_enabled: bool,
) -> (Vec<HashedTransaction<'static>>, Vec<usize>) {
transaction_indexes
.iter()
.filter_map(|tx_index| {
let p = &msgs.packets[*tx_index];
let tx: Transaction = limited_deserialize(&p.data[0..p.meta.size]).ok()?;
if secp256k1_program_enabled {
tx.verify_precompiles().ok()?;
}
tx.verify_precompiles(libsecp256k1_0_5_upgrade_enabled)
.ok()?;
let message_bytes = Self::packet_message(p)?;
let message_hash = Message::hash_raw_message(message_bytes);
Some((
@@ -1057,7 +1048,7 @@ impl BankingStage {
let (transactions, transaction_to_packet_indexes) = Self::transactions_from_packets(
msgs,
&packet_indexes,
bank.secp256k1_program_enabled(),
bank.libsecp256k1_0_5_upgrade_enabled(),
);
packet_conversion_time.stop();
@@ -1128,7 +1119,7 @@ impl BankingStage {
let (transactions, transaction_to_packet_indexes) = Self::transactions_from_packets(
msgs,
&transaction_indexes,
bank.secp256k1_program_enabled(),
bank.libsecp256k1_0_5_upgrade_enabled(),
);
let tx_count = transaction_to_packet_indexes.len();
@@ -1257,7 +1248,7 @@ impl BankingStage {
&bank,
&msgs,
&packet_indexes,
&my_pubkey,
my_pubkey,
next_leader,
);
Self::push_unprocessed(
@@ -1392,66 +1383,29 @@ fn next_leader_tpu_forwards(
}
}
pub fn create_test_recorder(
bank: &Arc<Bank>,
blockstore: &Arc<Blockstore>,
poh_config: Option<PohConfig>,
) -> (
Arc<AtomicBool>,
Arc<Mutex<PohRecorder>>,
PohService,
Receiver<WorkingBankEntry>,
) {
let exit = Arc::new(AtomicBool::new(false));
let poh_config = Arc::new(poh_config.unwrap_or_default());
let (mut poh_recorder, entry_receiver, record_receiver) = PohRecorder::new(
bank.tick_height(),
bank.last_blockhash(),
bank.slot(),
Some((4, 4)),
bank.ticks_per_slot(),
&Pubkey::default(),
blockstore,
&Arc::new(LeaderScheduleCache::new_from_bank(&bank)),
&poh_config,
exit.clone(),
);
poh_recorder.set_bank(&bank);
let poh_recorder = Arc::new(Mutex::new(poh_recorder));
let poh_service = PohService::new(
poh_recorder.clone(),
&poh_config,
&exit,
bank.ticks_per_slot(),
poh_service::DEFAULT_PINNED_CPU_CORE,
poh_service::DEFAULT_HASHES_PER_BATCH,
record_receiver,
);
(exit, poh_recorder, poh_service, entry_receiver)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
poh_recorder::Record, poh_recorder::WorkingBank,
transaction_status_service::TransactionStatusService,
};
use crossbeam_channel::unbounded;
use itertools::Itertools;
use solana_gossip::cluster_info::Node;
use solana_ledger::{
blockstore::entries_to_test_shreds,
blockstore::{entries_to_test_shreds, Blockstore},
entry::{next_entry, Entry, EntrySlice},
genesis_utils::{create_genesis_config, GenesisConfigInfo},
get_tmp_ledger_path,
leader_schedule_cache::LeaderScheduleCache,
};
use solana_perf::packet::to_packets_chunked;
use solana_poh::{
poh_recorder::{create_test_recorder, Record, WorkingBank, WorkingBankEntry},
poh_service::PohService,
};
use solana_rpc::transaction_status_service::TransactionStatusService;
use solana_sdk::{
hash::Hash,
instruction::InstructionError,
poh_config::PohConfig,
signature::{Keypair, Signer},
system_instruction::SystemError,
system_transaction,
@@ -1461,7 +1415,10 @@ mod tests {
use std::{
net::SocketAddr,
path::Path,
sync::atomic::{AtomicBool, Ordering},
sync::{
atomic::{AtomicBool, Ordering},
mpsc::Receiver,
},
thread::sleep,
};
@@ -2422,7 +2379,7 @@ mod tests {
let shreds = entries_to_test_shreds(entries, bank.slot(), 0, true, 0);
blockstore.insert_shreds(shreds, None, false).unwrap();
blockstore.set_roots(&[bank.slot()]).unwrap();
blockstore.set_roots(std::iter::once(&bank.slot())).unwrap();
let (transaction_status_sender, transaction_status_receiver) = unbounded();
let transaction_status_service = TransactionStatusService::new(
@@ -2491,7 +2448,7 @@ mod tests {
Receiver<WorkingBankEntry>,
JoinHandle<()>,
) {
Blockstore::destroy(&ledger_path).unwrap();
Blockstore::destroy(ledger_path).unwrap();
let genesis_config_info = create_slow_genesis_config(10_000);
let GenesisConfigInfo {
genesis_config,
@@ -2499,8 +2456,8 @@ mod tests {
..
} = &genesis_config_info;
let blockstore =
Blockstore::open(&ledger_path).expect("Expected to be able to open database ledger");
let bank = Arc::new(Bank::new_no_wallclock_throttle(&genesis_config));
Blockstore::open(ledger_path).expect("Expected to be able to open database ledger");
let bank = Arc::new(Bank::new_no_wallclock_throttle(genesis_config));
let exit = Arc::new(AtomicBool::default());
let (poh_recorder, entry_receiver, record_receiver) = PohRecorder::new(
bank.tick_height(),
@@ -2521,9 +2478,9 @@ mod tests {
let pubkey1 = solana_sdk::pubkey::new_rand();
let pubkey2 = solana_sdk::pubkey::new_rand();
let transactions = vec![
system_transaction::transfer(&mint_keypair, &pubkey0, 1, genesis_config.hash()),
system_transaction::transfer(&mint_keypair, &pubkey1, 1, genesis_config.hash()),
system_transaction::transfer(&mint_keypair, &pubkey2, 1, genesis_config.hash()),
system_transaction::transfer(mint_keypair, &pubkey0, 1, genesis_config.hash()),
system_transaction::transfer(mint_keypair, &pubkey1, 1, genesis_config.hash()),
system_transaction::transfer(mint_keypair, &pubkey2, 1, genesis_config.hash()),
];
let poh_simulator = simulate_poh(record_receiver, &poh_recorder);

View File

@@ -1,27 +1,24 @@
//! A stage to broadcast data from a leader node to validators
#![allow(clippy::rc_buffer)]
use self::{
broadcast_duplicates_run::BroadcastDuplicatesRun,
broadcast_fake_shreds_run::BroadcastFakeShredsRun, broadcast_metrics::*,
fail_entry_verification_broadcast_run::FailEntryVerificationBroadcastRun,
standard_broadcast_run::StandardBroadcastRun,
};
use crate::{
poh_recorder::WorkingBankEntry,
cluster_nodes::ClusterNodes,
result::{Error, Result},
};
use crossbeam_channel::{
Receiver as CrossbeamReceiver, RecvTimeoutError as CrossbeamRecvTimeoutError,
Sender as CrossbeamSender,
};
use solana_gossip::{
cluster_info::{self, ClusterInfo, ClusterInfoError},
contact_info::ContactInfo,
crds_gossip_pull::CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS,
weighted_shuffle::weighted_best,
};
use solana_gossip::cluster_info::{ClusterInfo, ClusterInfoError};
use solana_ledger::{blockstore::Blockstore, shred::Shred};
use solana_measure::measure::Measure;
use solana_metrics::{inc_new_counter_error, inc_new_counter_info};
use solana_poh::poh_recorder::WorkingBankEntry;
use solana_runtime::bank::Bank;
use solana_sdk::timing::timestamp;
use solana_sdk::{clock::Slot, pubkey::Pubkey};
@@ -37,6 +34,7 @@ use std::{
time::{Duration, Instant},
};
mod broadcast_duplicates_run;
mod broadcast_fake_shreds_run;
pub mod broadcast_metrics;
pub(crate) mod broadcast_utils;
@@ -54,11 +52,20 @@ pub enum BroadcastStageReturnType {
ChannelDisconnected,
}
#[derive(PartialEq, Clone, Debug)]
pub struct BroadcastDuplicatesConfig {
/// Percentage of stake to send different version of slots to
pub stake_partition: u8,
/// Number of slots to wait before sending duplicate shreds
pub duplicate_send_delay: usize,
}
#[derive(PartialEq, Clone, Debug)]
pub enum BroadcastStageType {
Standard,
FailEntryVerification,
BroadcastFakeShreds,
BroadcastDuplicates(BroadcastDuplicatesConfig),
}
impl BroadcastStageType {
@@ -103,6 +110,16 @@ impl BroadcastStageType {
blockstore,
BroadcastFakeShredsRun::new(keypair, 0, shred_version),
),
BroadcastStageType::BroadcastDuplicates(config) => BroadcastStage::new(
sock,
cluster_info,
receiver,
retransmit_slots_receiver,
exit_sender,
blockstore,
BroadcastDuplicatesRun::new(keypair, shred_version, config.clone()),
),
}
}
}
@@ -172,15 +189,15 @@ impl BroadcastStage {
fn handle_error(r: Result<()>, name: &str) -> Option<BroadcastStageReturnType> {
if let Err(e) = r {
match e {
Error::RecvTimeoutError(RecvTimeoutError::Disconnected)
| Error::SendError
| Error::RecvError(RecvError)
| Error::CrossbeamRecvTimeoutError(CrossbeamRecvTimeoutError::Disconnected) => {
Error::RecvTimeout(RecvTimeoutError::Disconnected)
| Error::Send
| Error::Recv(RecvError)
| Error::CrossbeamRecvTimeout(CrossbeamRecvTimeoutError::Disconnected) => {
return Some(BroadcastStageReturnType::ChannelDisconnected);
}
Error::RecvTimeoutError(RecvTimeoutError::Timeout)
| Error::CrossbeamRecvTimeoutError(CrossbeamRecvTimeoutError::Timeout) => (),
Error::ClusterInfoError(ClusterInfoError::NoPeers) => (), // TODO: Why are the unit-tests throwing hundreds of these?
Error::RecvTimeout(RecvTimeoutError::Timeout)
| Error::CrossbeamRecvTimeout(CrossbeamRecvTimeoutError::Timeout) => (),
Error::ClusterInfo(ClusterInfoError::NoPeers) => (), // TODO: Why are the unit-tests throwing hundreds of these?
_ => {
inc_new_counter_error!("streamer-broadcaster-error", 1, 1);
error!("{} broadcaster error: {:?}", name, e);
@@ -361,26 +378,16 @@ fn update_peer_stats(
}
}
pub fn get_broadcast_peers(
cluster_info: &ClusterInfo,
stakes: Option<&HashMap<Pubkey, u64>>,
) -> (Vec<ContactInfo>, Vec<(u64, usize)>) {
let mut peers = cluster_info.tvu_peers();
let peers_and_stakes = cluster_info::stake_weight_peers(&mut peers, stakes);
(peers, peers_and_stakes)
}
/// broadcast messages from the leader to layer 1 nodes
/// # Remarks
pub fn broadcast_shreds(
s: &UdpSocket,
shreds: &[Shred],
peers_and_stakes: &[(u64, usize)],
peers: &[ContactInfo],
cluster_nodes: &ClusterNodes<BroadcastStage>,
last_datapoint_submit: &Arc<AtomicU64>,
transmit_stats: &mut TransmitShredsStats,
) -> Result<()> {
let broadcast_len = peers_and_stakes.len();
let broadcast_len = cluster_nodes.num_peers();
if broadcast_len == 0 {
update_peer_stats(1, 1, last_datapoint_submit);
return Ok(());
@@ -388,10 +395,9 @@ pub fn broadcast_shreds(
let mut shred_select = Measure::start("shred_select");
let packets: Vec<_> = shreds
.iter()
.map(|shred| {
let broadcast_index = weighted_best(&peers_and_stakes, shred.seed());
(&shred.payload, &peers[broadcast_index].tvu)
.filter_map(|shred| {
let node = cluster_nodes.get_broadcast_peer(shred.seed())?;
Some((&shred.payload, &node.tvu))
})
.collect();
shred_select.stop();
@@ -410,7 +416,7 @@ pub fn broadcast_shreds(
send_mmsg_time.stop();
transmit_stats.send_mmsg_elapsed += send_mmsg_time.as_us();
let num_live_peers = num_live_peers(&peers);
let num_live_peers = cluster_nodes.num_peers_live(timestamp()) as i64;
update_peer_stats(
num_live_peers,
broadcast_len as i64 + 1,
@@ -419,25 +425,6 @@ pub fn broadcast_shreds(
Ok(())
}
fn distance(a: u64, b: u64) -> u64 {
if a > b {
a - b
} else {
b - a
}
}
fn num_live_peers(peers: &[ContactInfo]) -> i64 {
let mut num_live_peers = 1i64;
peers.iter().for_each(|p| {
// A peer is considered live if they generated their contact info recently
if distance(timestamp(), p.wallclock) <= CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS {
num_live_peers += 1;
}
});
num_live_peers
}
#[cfg(test)]
pub mod test {
use super::*;
@@ -521,19 +508,6 @@ pub mod test {
assert_eq!(num_expected_coding_shreds, coding_index);
}
#[test]
fn test_num_live_peers() {
let mut ci = ContactInfo {
wallclock: std::u64::MAX,
..ContactInfo::default()
};
assert_eq!(num_live_peers(&[ci.clone()]), 1);
ci.wallclock = timestamp() - 1;
assert_eq!(num_live_peers(&[ci.clone()]), 2);
ci.wallclock = timestamp() - CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS - 1;
assert_eq!(num_live_peers(&[ci]), 1);
}
#[test]
fn test_duplicate_retransmit_signal() {
// Setup

View File

@@ -0,0 +1,333 @@
use super::broadcast_utils::ReceiveResults;
use super::*;
use log::*;
use solana_ledger::entry::{create_ticks, Entry, EntrySlice};
use solana_ledger::shred::Shredder;
use solana_runtime::blockhash_queue::BlockhashQueue;
use solana_sdk::clock::Slot;
use solana_sdk::fee_calculator::FeeCalculator;
use solana_sdk::hash::Hash;
use solana_sdk::signature::{Keypair, Signer};
use solana_sdk::transaction::Transaction;
use std::collections::VecDeque;
use std::sync::Mutex;
// Queue which facilitates delivering shreds with a delay
type DelayedQueue = VecDeque<(Option<Pubkey>, Option<Vec<Shred>>)>;
#[derive(Clone)]
pub(super) struct BroadcastDuplicatesRun {
config: BroadcastDuplicatesConfig,
// Local queue for broadcast to track which duplicate blockhashes we've sent
duplicate_queue: BlockhashQueue,
// Shared queue between broadcast and transmit threads
delayed_queue: Arc<Mutex<DelayedQueue>>,
// Buffer for duplicate entries
duplicate_entries_buffer: Vec<Entry>,
last_duplicate_entry_hash: Hash,
last_broadcast_slot: Slot,
next_shred_index: u32,
shred_version: u16,
keypair: Arc<Keypair>,
}
impl BroadcastDuplicatesRun {
pub(super) fn new(
keypair: Arc<Keypair>,
shred_version: u16,
config: BroadcastDuplicatesConfig,
) -> Self {
let mut delayed_queue = DelayedQueue::new();
delayed_queue.resize(config.duplicate_send_delay, (None, None));
Self {
config,
delayed_queue: Arc::new(Mutex::new(delayed_queue)),
duplicate_queue: BlockhashQueue::default(),
duplicate_entries_buffer: vec![],
next_shred_index: u32::MAX,
last_broadcast_slot: 0,
last_duplicate_entry_hash: Hash::default(),
shred_version,
keypair,
}
}
fn queue_or_create_duplicate_entries(
&mut self,
bank: &Arc<Bank>,
receive_results: &ReceiveResults,
) -> (Vec<Entry>, u32) {
// If the last entry hash is default, grab the last blockhash from the parent bank
if self.last_duplicate_entry_hash == Hash::default() {
self.last_duplicate_entry_hash = bank.last_blockhash();
}
// Create duplicate entries by..
// 1) rearranging real entries so that all transaction entries are moved to
// the front and tick entries are moved to the back.
// 2) setting all transaction entries to zero hashes and all tick entries to `hashes_per_tick`.
// 3) removing any transactions which reference blockhashes which aren't in the
// duplicate blockhash queue.
let (duplicate_entries, next_shred_index) = if bank.slot() > MINIMUM_DUPLICATE_SLOT {
let mut tx_entries: Vec<Entry> = receive_results
.entries
.iter()
.filter_map(|entry| {
if entry.is_tick() {
return None;
}
let transactions: Vec<Transaction> = entry
.transactions
.iter()
.filter(|tx| {
self.duplicate_queue
.get_hash_age(&tx.message.recent_blockhash)
.is_some()
})
.cloned()
.collect();
if !transactions.is_empty() {
Some(Entry::new_mut(
&mut self.last_duplicate_entry_hash,
&mut 0,
transactions,
))
} else {
None
}
})
.collect();
let mut tick_entries = create_ticks(
receive_results.entries.tick_count(),
bank.hashes_per_tick().unwrap_or_default(),
self.last_duplicate_entry_hash,
);
self.duplicate_entries_buffer.append(&mut tx_entries);
self.duplicate_entries_buffer.append(&mut tick_entries);
// Only send out duplicate entries when the block is finished otherwise the
// recipient will start repairing for shreds they haven't received yet and
// hit duplicate slot issues before we want them to.
let entries = if receive_results.last_tick_height == bank.max_tick_height() {
self.duplicate_entries_buffer.drain(..).collect()
} else {
vec![]
};
// Set next shred index to 0 since we are sending the full slot
(entries, 0)
} else {
// Send real entries until we hit min duplicate slot
(receive_results.entries.clone(), self.next_shred_index)
};
// Save last duplicate entry hash to avoid invalid entry hash errors
if let Some(last_duplicate_entry) = duplicate_entries.last() {
self.last_duplicate_entry_hash = last_duplicate_entry.hash;
}
(duplicate_entries, next_shred_index)
}
}
/// Duplicate slots should only be sent once all validators have started.
/// This constant is intended to be used as a buffer so that all validators
/// are live before sending duplicate slots.
pub const MINIMUM_DUPLICATE_SLOT: Slot = 20;
impl BroadcastRun for BroadcastDuplicatesRun {
fn run(
&mut self,
blockstore: &Arc<Blockstore>,
receiver: &Receiver<WorkingBankEntry>,
socket_sender: &Sender<(TransmitShreds, Option<BroadcastShredBatchInfo>)>,
blockstore_sender: &Sender<(Arc<Vec<Shred>>, Option<BroadcastShredBatchInfo>)>,
) -> Result<()> {
// 1) Pull entries from banking stage
let receive_results = broadcast_utils::recv_slot_entries(receiver)?;
let bank = receive_results.bank.clone();
let last_tick_height = receive_results.last_tick_height;
if self.next_shred_index == u32::MAX {
self.next_shred_index = blockstore
.meta(bank.slot())
.expect("Database error")
.map(|meta| meta.consumed)
.unwrap_or(0) as u32
}
// We were not the leader, but just became leader again
if bank.slot() > self.last_broadcast_slot + 1 {
self.last_duplicate_entry_hash = Hash::default();
}
self.last_broadcast_slot = bank.slot();
let shredder = Shredder::new(
bank.slot(),
bank.parent().unwrap().slot(),
self.keypair.clone(),
(bank.tick_height() % bank.ticks_per_slot()) as u8,
self.shred_version,
)
.expect("Expected to create a new shredder");
let (data_shreds, coding_shreds, last_shred_index) = shredder.entries_to_shreds(
&receive_results.entries,
last_tick_height == bank.max_tick_height(),
self.next_shred_index,
);
let (duplicate_entries, next_duplicate_shred_index) =
self.queue_or_create_duplicate_entries(&bank, &receive_results);
let (duplicate_data_shreds, duplicate_coding_shreds, _) = if !duplicate_entries.is_empty() {
shredder.entries_to_shreds(
&duplicate_entries,
last_tick_height == bank.max_tick_height(),
next_duplicate_shred_index,
)
} else {
(vec![], vec![], 0)
};
// Manually track the shred index because relying on slot meta consumed is racy
if last_tick_height == bank.max_tick_height() {
self.next_shred_index = 0;
self.duplicate_queue
.register_hash(&self.last_duplicate_entry_hash, &FeeCalculator::default());
} else {
self.next_shred_index = last_shred_index;
}
// Partition network with duplicate and real shreds based on stake
let bank_epoch = bank.get_leader_schedule_epoch(bank.slot());
let mut duplicate_recipients = HashMap::new();
let mut real_recipients = HashMap::new();
let mut stakes: Vec<(Pubkey, u64)> = bank
.epoch_staked_nodes(bank_epoch)
.unwrap()
.into_iter()
.filter(|(pubkey, _)| *pubkey != self.keypair.pubkey())
.collect();
stakes.sort_by(|(l_key, l_stake), (r_key, r_stake)| {
if r_stake == l_stake {
l_key.cmp(r_key)
} else {
r_stake.cmp(l_stake)
}
});
let highest_staked_node = stakes.first().cloned().map(|x| x.0);
let stake_total: u64 = stakes.iter().map(|(_, stake)| *stake).sum();
let mut cumulative_stake: u64 = 0;
for (pubkey, stake) in stakes.into_iter().rev() {
cumulative_stake += stake;
if (100 * cumulative_stake / stake_total) as u8 <= self.config.stake_partition {
duplicate_recipients.insert(pubkey, stake);
} else {
real_recipients.insert(pubkey, stake);
}
}
if let Some(highest_staked_node) = highest_staked_node {
if bank.slot() > MINIMUM_DUPLICATE_SLOT && last_tick_height == bank.max_tick_height() {
warn!(
"{} sent duplicate slot {} to nodes: {:?}",
self.keypair.pubkey(),
bank.slot(),
&duplicate_recipients,
);
warn!(
"Duplicate shreds for slot {} will be broadcast in {} slot(s)",
bank.slot(),
self.config.duplicate_send_delay
);
let delayed_shreds: Option<Vec<Shred>> = vec![
duplicate_data_shreds.last().cloned(),
data_shreds.last().cloned(),
]
.into_iter()
.collect();
self.delayed_queue
.lock()
.unwrap()
.push_back((Some(highest_staked_node), delayed_shreds));
}
}
let duplicate_recipients = Arc::new(duplicate_recipients);
let real_recipients = Arc::new(real_recipients);
let data_shreds = Arc::new(data_shreds);
blockstore_sender.send((data_shreds.clone(), None))?;
// 3) Start broadcast step
socket_sender.send((
(
Some(duplicate_recipients.clone()),
Arc::new(duplicate_data_shreds),
),
None,
))?;
socket_sender.send((
(
Some(duplicate_recipients),
Arc::new(duplicate_coding_shreds),
),
None,
))?;
socket_sender.send(((Some(real_recipients.clone()), data_shreds), None))?;
socket_sender.send(((Some(real_recipients), Arc::new(coding_shreds)), None))?;
Ok(())
}
fn transmit(
&mut self,
receiver: &Arc<Mutex<TransmitReceiver>>,
cluster_info: &ClusterInfo,
sock: &UdpSocket,
) -> Result<()> {
// Check the delay queue for shreds that are ready to be sent
let (delayed_recipient, delayed_shreds) = {
let mut delayed_deque = self.delayed_queue.lock().unwrap();
if delayed_deque.len() > self.config.duplicate_send_delay {
delayed_deque.pop_front().unwrap()
} else {
(None, None)
}
};
let ((stakes, shreds), _) = receiver.lock().unwrap().recv()?;
let stakes = stakes.unwrap();
for peer in cluster_info.tvu_peers() {
// Forward shreds to circumvent gossip
if stakes.get(&peer.id).is_some() {
shreds.iter().for_each(|shred| {
sock.send_to(&shred.payload, &peer.tvu_forwards).unwrap();
});
}
// After a delay, broadcast duplicate shreds to a single node
if let Some(shreds) = delayed_shreds.as_ref() {
if Some(peer.id) == delayed_recipient {
shreds.iter().for_each(|shred| {
sock.send_to(&shred.payload, &peer.tvu).unwrap();
});
}
}
}
Ok(())
}
fn record(
&mut self,
receiver: &Arc<Mutex<RecordReceiver>>,
blockstore: &Arc<Blockstore>,
) -> Result<()> {
let (data_shreds, _) = receiver.lock().unwrap().recv()?;
blockstore.insert_shreds(data_shreds.to_vec(), None, true)?;
Ok(())
}
}

View File

@@ -1,6 +1,6 @@
use crate::poh_recorder::WorkingBankEntry;
use crate::result::Result;
use solana_ledger::{entry::Entry, shred::Shred};
use solana_poh::poh_recorder::WorkingBankEntry;
use solana_runtime::bank::Bank;
use solana_sdk::clock::Slot;
use std::{

View File

@@ -1,4 +1,5 @@
use super::*;
use crate::cluster_nodes::ClusterNodes;
use solana_ledger::shred::Shredder;
use solana_sdk::hash::Hash;
use solana_sdk::signature::Keypair;
@@ -134,13 +135,14 @@ impl BroadcastRun for FailEntryVerificationBroadcastRun {
) -> Result<()> {
let ((stakes, shreds), _) = receiver.lock().unwrap().recv()?;
// Broadcast data
let (peers, peers_and_stakes) = get_broadcast_peers(cluster_info, stakes.as_deref());
let cluster_nodes = ClusterNodes::<BroadcastStage>::new(
cluster_info,
stakes.as_deref().unwrap_or(&HashMap::default()),
);
broadcast_shreds(
sock,
&shreds,
&peers_and_stakes,
&peers,
&cluster_nodes,
&Arc::new(AtomicU64::new(0)),
&mut TransmitShredsStats::default(),
)?;

View File

@@ -4,7 +4,7 @@ use super::{
broadcast_utils::{self, ReceiveResults},
*,
};
use crate::broadcast_stage::broadcast_utils::UnfinishedSlotInfo;
use crate::{broadcast_stage::broadcast_utils::UnfinishedSlotInfo, cluster_nodes::ClusterNodes};
use solana_ledger::{
entry::Entry,
shred::{
@@ -27,16 +27,10 @@ pub struct StandardBroadcastRun {
shred_version: u16,
last_datapoint_submit: Arc<AtomicU64>,
num_batches: usize,
broadcast_peer_cache: Arc<RwLock<BroadcastPeerCache>>,
cluster_nodes: Arc<RwLock<ClusterNodes<BroadcastStage>>>,
last_peer_update: Arc<AtomicU64>,
}
#[derive(Default)]
struct BroadcastPeerCache {
peers: Vec<ContactInfo>,
peers_and_stakes: Vec<(u64, usize)>,
}
impl StandardBroadcastRun {
pub(super) fn new(keypair: Arc<Keypair>, shred_version: u16) -> Self {
Self {
@@ -50,7 +44,7 @@ impl StandardBroadcastRun {
shred_version,
last_datapoint_submit: Arc::default(),
num_batches: 0,
broadcast_peer_cache: Arc::default(),
cluster_nodes: Arc::default(),
last_peer_update: Arc::default(),
}
}
@@ -161,7 +155,7 @@ impl StandardBroadcastRun {
) -> Result<()> {
let (bsend, brecv) = channel();
let (ssend, srecv) = channel();
self.process_receive_results(&blockstore, &ssend, &bsend, receive_results)?;
self.process_receive_results(blockstore, &ssend, &bsend, receive_results)?;
let srecv = Arc::new(Mutex::new(srecv));
let brecv = Arc::new(Mutex::new(brecv));
//data
@@ -354,13 +348,13 @@ impl StandardBroadcastRun {
.compare_and_swap(now, last, Ordering::Relaxed)
== last
{
let mut w_broadcast_peer_cache = self.broadcast_peer_cache.write().unwrap();
let (peers, peers_and_stakes) = get_broadcast_peers(cluster_info, stakes);
w_broadcast_peer_cache.peers = peers;
w_broadcast_peer_cache.peers_and_stakes = peers_and_stakes;
*self.cluster_nodes.write().unwrap() = ClusterNodes::<BroadcastStage>::new(
cluster_info,
stakes.unwrap_or(&HashMap::default()),
);
}
get_peers_time.stop();
let r_broadcast_peer_cache = self.broadcast_peer_cache.read().unwrap();
let cluster_nodes = self.cluster_nodes.read().unwrap();
let mut transmit_stats = TransmitShredsStats::default();
// Broadcast the shreds
@@ -368,12 +362,11 @@ impl StandardBroadcastRun {
broadcast_shreds(
sock,
&shreds,
&r_broadcast_peer_cache.peers_and_stakes,
&r_broadcast_peer_cache.peers,
&cluster_nodes,
&self.last_datapoint_submit,
&mut transmit_stats,
)?;
drop(r_broadcast_peer_cache);
drop(cluster_nodes);
transmit_time.stop();
transmit_stats.transmit_elapsed = transmit_time.as_us();

View File

@@ -1,6 +1,5 @@
use crate::{
optimistic_confirmation_verifier::OptimisticConfirmationVerifier,
poh_recorder::PohRecorder,
replay_stage::DUPLICATE_THRESHOLD,
result::{Error, Result},
sigverify,
@@ -20,6 +19,7 @@ use solana_gossip::{
use solana_ledger::blockstore::Blockstore;
use solana_metrics::inc_new_counter_debug;
use solana_perf::packet::{self, Packets};
use solana_poh::poh_recorder::PohRecorder;
use solana_rpc::{
optimistically_confirmed_bank_tracker::{BankNotification, BankNotificationSender},
rpc_subscriptions::RpcSubscriptions,
@@ -33,7 +33,7 @@ use solana_runtime::{
vote_sender_types::{ReplayVoteReceiver, ReplayedVote},
};
use solana_sdk::{
clock::{Epoch, Slot, DEFAULT_MS_PER_SLOT},
clock::{Epoch, Slot, DEFAULT_MS_PER_SLOT, DEFAULT_TICKS_PER_SLOT},
epoch_schedule::EpochSchedule,
hash::Hash,
pubkey::Pubkey,
@@ -110,7 +110,7 @@ impl VoteTracker {
epoch_schedule: *root_bank.epoch_schedule(),
..VoteTracker::default()
};
vote_tracker.progress_with_new_root_bank(&root_bank);
vote_tracker.progress_with_new_root_bank(root_bank);
assert_eq!(
*vote_tracker.leader_schedule_epoch.read().unwrap(),
root_bank.get_leader_schedule_epoch(root_bank.slot())
@@ -384,15 +384,20 @@ impl ClusterInfoVoteListener {
return Ok(());
}
let would_be_leader = poh_recorder
.lock()
.unwrap()
.would_be_leader(20 * DEFAULT_TICKS_PER_SLOT);
if let Err(e) = verified_vote_packets.receive_and_process_vote_packets(
&verified_vote_label_packets_receiver,
&mut update_version,
would_be_leader,
) {
match e {
Error::CrossbeamRecvTimeoutError(RecvTimeoutError::Disconnected) => {
Error::CrossbeamRecvTimeout(RecvTimeoutError::Disconnected) => {
return Ok(());
}
Error::CrossbeamRecvTimeoutError(RecvTimeoutError::Timeout) => (),
Error::CrossbeamRecvTimeout(RecvTimeoutError::Timeout) => (),
_ => {
error!("thread {:?} error {:?}", thread::current().name(), e);
}
@@ -474,8 +479,8 @@ impl ClusterInfoVoteListener {
.add_new_optimistic_confirmed_slots(confirmed_slots.clone());
}
Err(e) => match e {
Error::CrossbeamRecvTimeoutError(RecvTimeoutError::Timeout)
| Error::ReadyTimeoutError => (),
Error::CrossbeamRecvTimeout(RecvTimeoutError::Timeout)
| Error::ReadyTimeout => (),
_ => {
error!("thread {:?} error {:?}", thread::current().name(), e);
}
@@ -598,7 +603,7 @@ impl ClusterInfoVoteListener {
if slot == last_vote_slot {
let vote_accounts = Stakes::vote_accounts(epoch_stakes.stakes());
let stake = vote_accounts
.get(&vote_pubkey)
.get(vote_pubkey)
.map(|(stake, _)| *stake)
.unwrap_or_default();
let total_stake = epoch_stakes.total_stake();
@@ -687,7 +692,7 @@ impl ClusterInfoVoteListener {
// voters trying to make votes for slots earlier than the epoch for
// which they are authorized
let actual_authorized_voter =
vote_tracker.get_authorized_voter(&vote_pubkey, *last_vote_slot);
vote_tracker.get_authorized_voter(vote_pubkey, *last_vote_slot);
if actual_authorized_voter.is_none() {
return false;
@@ -695,7 +700,7 @@ impl ClusterInfoVoteListener {
// Voting without the correct authorized pubkey, dump the vote
if !VoteTracker::vote_contains_authorized_voter(
&gossip_tx,
gossip_tx,
&actual_authorized_voter.unwrap(),
) {
return false;
@@ -733,7 +738,7 @@ impl ClusterInfoVoteListener {
Self::track_new_votes_and_notify_confirmations(
vote,
&vote_pubkey,
&vote_tracker,
vote_tracker,
root_bank,
subscriptions,
verified_vote_sender,

441
core/src/cluster_nodes.rs Normal file
View File

@@ -0,0 +1,441 @@
use {
crate::{broadcast_stage::BroadcastStage, retransmit_stage::RetransmitStage},
itertools::Itertools,
solana_gossip::{
cluster_info::{compute_retransmit_peers, ClusterInfo},
contact_info::ContactInfo,
crds_gossip_pull::CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS,
weighted_shuffle::{weighted_best, weighted_shuffle},
},
solana_sdk::pubkey::Pubkey,
std::{any::TypeId, cmp::Reverse, collections::HashMap, marker::PhantomData},
};
enum NodeId {
// TVU node obtained through gossip (staked or not).
ContactInfo(ContactInfo),
// Staked node with no contact-info in gossip table.
Pubkey(Pubkey),
}
struct Node {
node: NodeId,
stake: u64,
}
pub struct ClusterNodes<T> {
pubkey: Pubkey, // The local node itself.
// All staked nodes + other known tvu-peers + the node itself;
// sorted by (stake, pubkey) in descending order.
nodes: Vec<Node>,
// Weights and indices for sampling peers. weighted_{shuffle,best} expect
// weights >= 1. For backward compatibility we use max(1, stake) for
// weights and exclude nodes with no contact-info.
index: Vec<(/*weight:*/ u64, /*index:*/ usize)>,
_phantom: PhantomData<T>,
}
impl Node {
#[inline]
fn pubkey(&self) -> Pubkey {
match &self.node {
NodeId::Pubkey(pubkey) => *pubkey,
NodeId::ContactInfo(node) => node.id,
}
}
#[inline]
fn contact_info(&self) -> Option<&ContactInfo> {
match &self.node {
NodeId::Pubkey(_) => None,
NodeId::ContactInfo(node) => Some(node),
}
}
}
impl<T> ClusterNodes<T> {
pub fn num_peers(&self) -> usize {
self.index.len()
}
// A peer is considered live if they generated their contact info recently.
pub fn num_peers_live(&self, now: u64) -> usize {
self.index
.iter()
.filter_map(|(_, index)| self.nodes[*index].contact_info())
.filter(|node| {
let elapsed = if node.wallclock < now {
now - node.wallclock
} else {
node.wallclock - now
};
elapsed < CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS
})
.count()
}
}
impl ClusterNodes<BroadcastStage> {
pub fn new(cluster_info: &ClusterInfo, stakes: &HashMap<Pubkey, u64>) -> Self {
new_cluster_nodes(cluster_info, stakes)
}
/// Returns the root of turbine broadcast tree, which the leader sends the
/// shred to.
pub fn get_broadcast_peer(&self, shred_seed: [u8; 32]) -> Option<&ContactInfo> {
if self.index.is_empty() {
None
} else {
let index = weighted_best(&self.index, shred_seed);
match &self.nodes[index].node {
NodeId::ContactInfo(node) => Some(node),
NodeId::Pubkey(_) => panic!("this should not happen!"),
}
}
}
}
impl ClusterNodes<RetransmitStage> {
pub fn new(cluster_info: &ClusterInfo, stakes: &HashMap<Pubkey, u64>) -> Self {
new_cluster_nodes(cluster_info, stakes)
}
pub fn get_retransmit_peers(
&self,
shred_seed: [u8; 32],
fanout: usize,
slot_leader: Option<Pubkey>,
) -> (
Vec<&ContactInfo>, // neighbors
Vec<&ContactInfo>, // children
) {
// Exclude leader from list of nodes.
let index = self.index.iter().copied();
let (weights, index): (Vec<u64>, Vec<usize>) = match slot_leader {
None => {
error!("unknown leader for shred slot");
index.unzip()
}
Some(slot_leader) if slot_leader == self.pubkey => {
error!("retransmit from slot leader: {}", slot_leader);
index.unzip()
}
Some(slot_leader) => index
.filter(|(_, i)| self.nodes[*i].pubkey() != slot_leader)
.unzip(),
};
let index: Vec<_> = {
let shuffle = weighted_shuffle(&weights, shred_seed);
shuffle.into_iter().map(|i| index[i]).collect()
};
let self_index = index
.iter()
.position(|i| self.nodes[*i].pubkey() == self.pubkey)
.unwrap();
let (neighbors, children) = compute_retransmit_peers(fanout, self_index, &index);
// Assert that the node itself is included in the set of neighbors, at
// the right offset.
debug_assert_eq!(
self.nodes[neighbors[self_index % fanout]].pubkey(),
self.pubkey
);
let get_contact_infos = |index: Vec<usize>| -> Vec<&ContactInfo> {
index
.into_iter()
.map(|i| self.nodes[i].contact_info().unwrap())
.collect()
};
(get_contact_infos(neighbors), get_contact_infos(children))
}
}
fn new_cluster_nodes<T: 'static>(
cluster_info: &ClusterInfo,
stakes: &HashMap<Pubkey, u64>,
) -> ClusterNodes<T> {
let self_pubkey = cluster_info.id();
let nodes = get_nodes(cluster_info, stakes);
let broadcast = TypeId::of::<T>() == TypeId::of::<BroadcastStage>();
// For backward compatibility:
// * nodes which do not have contact-info are excluded.
// * stakes are floored at 1.
// The sorting key here should be equivalent to
// solana_gossip::deprecated::sorted_stakes_with_index.
// Leader itself is excluded when sampling broadcast peers.
let index = nodes
.iter()
.enumerate()
.filter(|(_, node)| node.contact_info().is_some())
.filter(|(_, node)| !broadcast || node.pubkey() != self_pubkey)
.sorted_by_key(|(_, node)| Reverse((node.stake.max(1), node.pubkey())))
.map(|(index, node)| (node.stake.max(1), index))
.collect();
ClusterNodes {
pubkey: self_pubkey,
nodes,
index,
_phantom: PhantomData::default(),
}
}
// All staked nodes + other known tvu-peers + the node itself;
// sorted by (stake, pubkey) in descending order.
fn get_nodes(cluster_info: &ClusterInfo, stakes: &HashMap<Pubkey, u64>) -> Vec<Node> {
let self_pubkey = cluster_info.id();
// The local node itself.
std::iter::once({
let stake = stakes.get(&self_pubkey).copied().unwrap_or_default();
let node = NodeId::from(cluster_info.my_contact_info());
Node { node, stake }
})
// All known tvu-peers from gossip.
.chain(cluster_info.tvu_peers().into_iter().map(|node| {
let stake = stakes.get(&node.id).copied().unwrap_or_default();
let node = NodeId::from(node);
Node { node, stake }
}))
// All staked nodes.
.chain(
stakes
.iter()
.filter(|(_, stake)| **stake > 0)
.map(|(&pubkey, &stake)| Node {
node: NodeId::from(pubkey),
stake,
}),
)
.sorted_by_key(|node| Reverse((node.stake, node.pubkey())))
// Since sorted_by_key is stable, in case of duplicates, this
// will keep nodes with contact-info.
.dedup_by(|a, b| a.pubkey() == b.pubkey())
.collect()
}
impl From<ContactInfo> for NodeId {
fn from(node: ContactInfo) -> Self {
NodeId::ContactInfo(node)
}
}
impl From<Pubkey> for NodeId {
fn from(pubkey: Pubkey) -> Self {
NodeId::Pubkey(pubkey)
}
}
impl<T> Default for ClusterNodes<T> {
fn default() -> Self {
Self {
pubkey: Pubkey::default(),
nodes: Vec::default(),
index: Vec::default(),
_phantom: PhantomData::default(),
}
}
}
#[cfg(test)]
mod tests {
use {
super::*,
rand::{seq::SliceRandom, Rng},
solana_gossip::{
crds_value::{CrdsData, CrdsValue},
deprecated::{
shuffle_peers_and_index, sorted_retransmit_peers_and_stakes,
sorted_stakes_with_index,
},
},
solana_sdk::timing::timestamp,
std::iter::repeat_with,
};
// Legacy methods copied for testing backward compatibility.
fn get_broadcast_peers(
cluster_info: &ClusterInfo,
stakes: Option<&HashMap<Pubkey, u64>>,
) -> (Vec<ContactInfo>, Vec<(u64, usize)>) {
let mut peers = cluster_info.tvu_peers();
let peers_and_stakes = stake_weight_peers(&mut peers, stakes);
(peers, peers_and_stakes)
}
fn stake_weight_peers(
peers: &mut Vec<ContactInfo>,
stakes: Option<&HashMap<Pubkey, u64>>,
) -> Vec<(u64, usize)> {
peers.dedup();
sorted_stakes_with_index(peers, stakes)
}
fn make_cluster<R: Rng>(
rng: &mut R,
) -> (
Vec<ContactInfo>,
HashMap<Pubkey, u64>, // stakes
ClusterInfo,
) {
let mut nodes: Vec<_> = repeat_with(|| ContactInfo::new_rand(rng, None))
.take(1000)
.collect();
nodes.shuffle(rng);
let this_node = nodes[0].clone();
let mut stakes: HashMap<Pubkey, u64> = nodes
.iter()
.filter_map(|node| {
if rng.gen_ratio(1, 7) {
None // No stake for some of the nodes.
} else {
Some((node.id, rng.gen_range(0, 20)))
}
})
.collect();
// Add some staked nodes with no contact-info.
stakes.extend(repeat_with(|| (Pubkey::new_unique(), rng.gen_range(0, 20))).take(100));
let cluster_info = ClusterInfo::new_with_invalid_keypair(this_node);
{
let now = timestamp();
let mut gossip = cluster_info.gossip.write().unwrap();
// First node is pushed to crds table by ClusterInfo constructor.
for node in nodes.iter().skip(1) {
let node = CrdsData::ContactInfo(node.clone());
let node = CrdsValue::new_unsigned(node);
assert_eq!(gossip.crds.insert(node, now), Ok(()));
}
}
(nodes, stakes, cluster_info)
}
#[test]
fn test_cluster_nodes_retransmit() {
let mut rng = rand::thread_rng();
let (nodes, stakes, cluster_info) = make_cluster(&mut rng);
let this_node = cluster_info.my_contact_info();
// ClusterInfo::tvu_peers excludes the node itself.
assert_eq!(cluster_info.tvu_peers().len(), nodes.len() - 1);
let cluster_nodes = ClusterNodes::<RetransmitStage>::new(&cluster_info, &stakes);
// All nodes with contact-info should be in the index.
assert_eq!(cluster_nodes.index.len(), nodes.len());
// Staked nodes with no contact-info should be included.
assert!(cluster_nodes.nodes.len() > nodes.len());
// Assert that all nodes keep their contact-info.
// and, all staked nodes are also included.
{
let cluster_nodes: HashMap<_, _> = cluster_nodes
.nodes
.iter()
.map(|node| (node.pubkey(), node))
.collect();
for node in &nodes {
assert_eq!(cluster_nodes[&node.id].contact_info().unwrap().id, node.id);
}
for (pubkey, stake) in &stakes {
if *stake > 0 {
assert_eq!(cluster_nodes[pubkey].stake, *stake);
}
}
}
let (peers, stakes_and_index) =
sorted_retransmit_peers_and_stakes(&cluster_info, Some(&stakes));
assert_eq!(stakes_and_index.len(), peers.len());
assert_eq!(cluster_nodes.index.len(), peers.len());
for (i, node) in cluster_nodes
.index
.iter()
.map(|(_, i)| &cluster_nodes.nodes[*i])
.enumerate()
{
let (stake, index) = stakes_and_index[i];
// Wallclock may be update by ClusterInfo::push_self.
if node.pubkey() == this_node.id {
assert_eq!(this_node.id, peers[index].id)
} else {
assert_eq!(node.contact_info().unwrap(), &peers[index]);
}
assert_eq!(node.stake.max(1), stake);
}
let slot_leader = nodes[1..].choose(&mut rng).unwrap().id;
// Remove slot leader from peers indices.
let stakes_and_index: Vec<_> = stakes_and_index
.into_iter()
.filter(|(_stake, index)| peers[*index].id != slot_leader)
.collect();
assert_eq!(peers.len(), stakes_and_index.len() + 1);
let mut shred_seed = [0u8; 32];
rng.fill(&mut shred_seed[..]);
let (self_index, shuffled_peers_and_stakes) =
shuffle_peers_and_index(&this_node.id, &peers, &stakes_and_index, shred_seed);
let shuffled_index: Vec<_> = shuffled_peers_and_stakes
.into_iter()
.map(|(_, index)| index)
.collect();
assert_eq!(this_node.id, peers[shuffled_index[self_index]].id);
for fanout in 1..200 {
let (neighbors_indices, children_indices) =
compute_retransmit_peers(fanout, self_index, &shuffled_index);
let (neighbors, children) =
cluster_nodes.get_retransmit_peers(shred_seed, fanout, Some(slot_leader));
assert_eq!(children.len(), children_indices.len());
for (node, index) in children.into_iter().zip(children_indices) {
assert_eq!(*node, peers[index]);
}
assert_eq!(neighbors.len(), neighbors_indices.len());
assert_eq!(neighbors[0].id, peers[neighbors_indices[0]].id);
for (node, index) in neighbors.into_iter().zip(neighbors_indices).skip(1) {
assert_eq!(*node, peers[index]);
}
}
}
#[test]
fn test_cluster_nodes_broadcast() {
let mut rng = rand::thread_rng();
let (nodes, stakes, cluster_info) = make_cluster(&mut rng);
// ClusterInfo::tvu_peers excludes the node itself.
assert_eq!(cluster_info.tvu_peers().len(), nodes.len() - 1);
let cluster_nodes = ClusterNodes::<BroadcastStage>::new(&cluster_info, &stakes);
// All nodes with contact-info should be in the index.
// Excluding this node itself.
assert_eq!(cluster_nodes.index.len() + 1, nodes.len());
// Staked nodes with no contact-info should be included.
assert!(cluster_nodes.nodes.len() > nodes.len());
// Assert that all nodes keep their contact-info.
// and, all staked nodes are also included.
{
let cluster_nodes: HashMap<_, _> = cluster_nodes
.nodes
.iter()
.map(|node| (node.pubkey(), node))
.collect();
for node in &nodes {
assert_eq!(cluster_nodes[&node.id].contact_info().unwrap().id, node.id);
}
for (pubkey, stake) in &stakes {
if *stake > 0 {
assert_eq!(cluster_nodes[pubkey].stake, *stake);
}
}
}
let (peers, peers_and_stakes) = get_broadcast_peers(&cluster_info, Some(&stakes));
assert_eq!(peers_and_stakes.len(), peers.len());
assert_eq!(cluster_nodes.index.len(), peers.len());
for (i, node) in cluster_nodes
.index
.iter()
.map(|(_, i)| &cluster_nodes.nodes[*i])
.enumerate()
{
let (stake, index) = peers_and_stakes[i];
assert_eq!(node.contact_info().unwrap(), &peers[index]);
assert_eq!(node.stake.max(1), stake);
}
for _ in 0..100 {
let mut shred_seed = [0u8; 32];
rng.fill(&mut shred_seed[..]);
let index = weighted_best(&peers_and_stakes, shred_seed);
let peer = cluster_nodes.get_broadcast_peer(shred_seed).unwrap();
assert_eq!(*peer, peers[index]);
}
}
}

View File

@@ -3,8 +3,9 @@ use crate::{
progress_map::ProgressMap,
};
use solana_sdk::{clock::Slot, hash::Hash};
use std::collections::{BTreeMap, HashMap, HashSet};
use std::collections::{BTreeMap, BTreeSet};
pub(crate) type DuplicateSlotsTracker = BTreeSet<Slot>;
pub(crate) type GossipDuplicateConfirmedSlots = BTreeMap<Slot, Hash>;
type SlotStateHandler = fn(Slot, &Hash, Option<&Hash>, bool, bool) -> Vec<ResultingStateChange>;
@@ -191,7 +192,7 @@ fn get_cluster_duplicate_confirmed_hash<'a>(
slot, gossip_duplicate_confirmed_hash, local_duplicate_confirmed_hash
);
}
Some(&local_frozen_hash)
Some(local_frozen_hash)
}
(Some(local_frozen_hash), None) => Some(local_frozen_hash),
_ => gossip_duplicate_confirmed_hash,
@@ -200,19 +201,12 @@ fn get_cluster_duplicate_confirmed_hash<'a>(
fn apply_state_changes(
slot: Slot,
progress: &mut ProgressMap,
fork_choice: &mut HeaviestSubtreeForkChoice,
ancestors: &HashMap<Slot, HashSet<Slot>>,
descendants: &HashMap<Slot, HashSet<Slot>>,
state_changes: Vec<ResultingStateChange>,
) {
for state_change in state_changes {
match state_change {
ResultingStateChange::MarkSlotDuplicate(bank_frozen_hash) => {
progress.set_unconfirmed_duplicate_slot(
slot,
descendants.get(&slot).unwrap_or(&HashSet::default()),
);
fork_choice.mark_fork_invalid_candidate(&(slot, bank_frozen_hash));
}
ResultingStateChange::RepairDuplicateConfirmedVersion(
@@ -223,25 +217,20 @@ fn apply_state_changes(
repair_correct_version(slot, &cluster_duplicate_confirmed_hash);
}
ResultingStateChange::DuplicateConfirmedSlotMatchesCluster(bank_frozen_hash) => {
progress.set_confirmed_duplicate_slot(
slot,
ancestors.get(&slot).unwrap_or(&HashSet::default()),
descendants.get(&slot).unwrap_or(&HashSet::default()),
);
fork_choice.mark_fork_valid_candidate(&(slot, bank_frozen_hash));
}
}
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn check_slot_agrees_with_cluster(
slot: Slot,
root: Slot,
frozen_hash: Option<Hash>,
duplicate_slots_tracker: &mut DuplicateSlotsTracker,
gossip_duplicate_confirmed_slots: &GossipDuplicateConfirmedSlots,
ancestors: &HashMap<Slot, HashSet<Slot>>,
descendants: &HashMap<Slot, HashSet<Slot>>,
progress: &mut ProgressMap,
progress: &ProgressMap,
fork_choice: &mut HeaviestSubtreeForkChoice,
slot_state_update: SlotStateUpdate,
) {
@@ -258,6 +247,15 @@ pub(crate) fn check_slot_agrees_with_cluster(
return;
}
// Needs to happen before the frozen_hash.is_none() check below to account for duplicate
// signals arriving before the bank is constructed in replay.
if matches!(slot_state_update, SlotStateUpdate::Duplicate) {
// If this slot has already been processed before, return
if !duplicate_slots_tracker.insert(slot) {
return;
}
}
if frozen_hash.is_none() {
// If the bank doesn't even exist in BankForks yet,
// then there's nothing to do as replay of the slot
@@ -268,25 +266,18 @@ pub(crate) fn check_slot_agrees_with_cluster(
let frozen_hash = frozen_hash.unwrap();
let gossip_duplicate_confirmed_hash = gossip_duplicate_confirmed_slots.get(&slot);
let is_local_replay_duplicate_confirmed = progress.is_duplicate_confirmed(slot).expect("If the frozen hash exists, then the slot must exist in bank forks and thus in progress map");
// If the bank hasn't been frozen yet, then we haven't duplicate confirmed a local version
// this slot through replay yet.
let is_local_replay_duplicate_confirmed = fork_choice
.is_duplicate_confirmed(&(slot, frozen_hash))
.unwrap_or(false);
let cluster_duplicate_confirmed_hash = get_cluster_duplicate_confirmed_hash(
slot,
gossip_duplicate_confirmed_hash,
&frozen_hash,
is_local_replay_duplicate_confirmed,
);
let mut is_slot_duplicate =
progress.is_unconfirmed_duplicate(slot).expect("If the frozen hash exists, then the slot must exist in bank forks and thus in progress map");
if matches!(slot_state_update, SlotStateUpdate::Duplicate) {
if is_slot_duplicate {
// Already processed duplicate signal for this slot, no need to continue
return;
} else {
// Otherwise, mark the slot as duplicate so the appropriate state changes
// will trigger
is_slot_duplicate = true;
}
}
let is_slot_duplicate = duplicate_slots_tracker.contains(&slot);
let is_dead = progress.is_dead(slot).expect("If the frozen hash exists, then the slot must exist in bank forks and thus in progress map");
info!(
@@ -309,14 +300,7 @@ pub(crate) fn check_slot_agrees_with_cluster(
is_slot_duplicate,
is_dead,
);
apply_state_changes(
slot,
progress,
fork_choice,
ancestors,
descendants,
state_changes,
);
apply_state_changes(slot, fork_choice, state_changes);
}
#[cfg(test)]
@@ -324,15 +308,16 @@ mod test {
use super::*;
use crate::consensus::test::VoteSimulator;
use solana_runtime::bank_forks::BankForks;
use std::sync::RwLock;
use std::{
collections::{HashMap, HashSet},
sync::RwLock,
};
use trees::tr;
struct InitialState {
heaviest_subtree_fork_choice: HeaviestSubtreeForkChoice,
progress: ProgressMap,
ancestors: HashMap<Slot, HashSet<Slot>>,
descendants: HashMap<Slot, HashSet<Slot>>,
slot: Slot,
bank_forks: RwLock<BankForks>,
}
@@ -341,7 +326,6 @@ mod test {
let forks = tr(0) / (tr(1) / (tr(2) / tr(3)));
let mut vote_simulator = VoteSimulator::new(1);
vote_simulator.fill_bank_forks(forks, &HashMap::new());
let ancestors = vote_simulator.bank_forks.read().unwrap().ancestors();
let descendants = vote_simulator
.bank_forks
@@ -353,9 +337,7 @@ mod test {
InitialState {
heaviest_subtree_fork_choice: vote_simulator.heaviest_subtree_fork_choice,
progress: vote_simulator.progress,
ancestors,
descendants,
slot: 0,
bank_forks: vote_simulator.bank_forks,
}
}
@@ -626,75 +608,159 @@ mod test {
// Common state
let InitialState {
mut heaviest_subtree_fork_choice,
mut progress,
ancestors,
descendants,
slot,
bank_forks,
..
} = setup();
// MarkSlotDuplicate should mark progress map and remove
// the slot from fork choice
let slot_hash = bank_forks.read().unwrap().get(slot).unwrap().hash();
let duplicate_slot = bank_forks.read().unwrap().root() + 1;
let duplicate_slot_hash = bank_forks
.read()
.unwrap()
.get(duplicate_slot)
.unwrap()
.hash();
apply_state_changes(
slot,
&mut progress,
duplicate_slot,
&mut heaviest_subtree_fork_choice,
&ancestors,
&descendants,
vec![ResultingStateChange::MarkSlotDuplicate(slot_hash)],
vec![ResultingStateChange::MarkSlotDuplicate(duplicate_slot_hash)],
);
assert!(!heaviest_subtree_fork_choice
.is_candidate_slot(&(slot, slot_hash))
.is_candidate(&(duplicate_slot, duplicate_slot_hash))
.unwrap());
for child_slot in descendants
.get(&slot)
.get(&duplicate_slot)
.unwrap()
.iter()
.chain(std::iter::once(&slot))
.chain(std::iter::once(&duplicate_slot))
{
assert_eq!(
progress
.latest_unconfirmed_duplicate_ancestor(*child_slot)
heaviest_subtree_fork_choice
.latest_invalid_ancestor(&(
*child_slot,
bank_forks.read().unwrap().get(*child_slot).unwrap().hash()
))
.unwrap(),
slot
duplicate_slot
);
}
// DuplicateConfirmedSlotMatchesCluster should re-enable fork choice
apply_state_changes(
slot,
&mut progress,
duplicate_slot,
&mut heaviest_subtree_fork_choice,
&ancestors,
&descendants,
vec![ResultingStateChange::DuplicateConfirmedSlotMatchesCluster(
slot_hash,
duplicate_slot_hash,
)],
);
for child_slot in descendants
.get(&slot)
.get(&duplicate_slot)
.unwrap()
.iter()
.chain(std::iter::once(&slot))
.chain(std::iter::once(&duplicate_slot))
{
assert!(progress
.latest_unconfirmed_duplicate_ancestor(*child_slot)
assert!(heaviest_subtree_fork_choice
.latest_invalid_ancestor(&(
*child_slot,
bank_forks.read().unwrap().get(*child_slot).unwrap().hash()
))
.is_none());
}
assert!(heaviest_subtree_fork_choice
.is_candidate_slot(&(slot, slot_hash))
.is_candidate(&(duplicate_slot, duplicate_slot_hash))
.unwrap());
}
fn run_test_state_duplicate_then_bank_frozen(initial_bank_hash: Option<Hash>) {
// Common state
let InitialState {
mut heaviest_subtree_fork_choice,
progress,
bank_forks,
..
} = setup();
// Setup a duplicate slot state transition with the initial bank state of the duplicate slot
// determined by `initial_bank_hash`, which can be:
// 1) A default hash (unfrozen bank),
// 2) None (a slot that hasn't even started replay yet).
let root = 0;
let mut duplicate_slots_tracker = DuplicateSlotsTracker::default();
let gossip_duplicate_confirmed_slots = GossipDuplicateConfirmedSlots::default();
let duplicate_slot = 2;
check_slot_agrees_with_cluster(
duplicate_slot,
root,
initial_bank_hash,
&mut duplicate_slots_tracker,
&gossip_duplicate_confirmed_slots,
&progress,
&mut heaviest_subtree_fork_choice,
SlotStateUpdate::Duplicate,
);
assert!(duplicate_slots_tracker.contains(&duplicate_slot));
// Nothing should be applied yet to fork choice, since bank was not yet frozen
for slot in 2..=3 {
let slot_hash = bank_forks.read().unwrap().get(slot).unwrap().hash();
assert!(heaviest_subtree_fork_choice
.latest_invalid_ancestor(&(slot, slot_hash))
.is_none());
}
// Now freeze the bank
let frozen_duplicate_slot_hash = bank_forks
.read()
.unwrap()
.get(duplicate_slot)
.unwrap()
.hash();
check_slot_agrees_with_cluster(
duplicate_slot,
root,
Some(frozen_duplicate_slot_hash),
&mut duplicate_slots_tracker,
&gossip_duplicate_confirmed_slots,
&progress,
&mut heaviest_subtree_fork_choice,
SlotStateUpdate::Frozen,
);
// Progress map should have the correct updates, fork choice should mark duplicate
// as unvotable
assert!(heaviest_subtree_fork_choice
.is_unconfirmed_duplicate(&(duplicate_slot, frozen_duplicate_slot_hash))
.unwrap());
// The ancestor of the duplicate slot should be the best slot now
let (duplicate_ancestor, duplicate_parent_hash) = {
let r_bank_forks = bank_forks.read().unwrap();
let parent_bank = r_bank_forks.get(duplicate_slot).unwrap().parent().unwrap();
(parent_bank.slot(), parent_bank.hash())
};
assert_eq!(
heaviest_subtree_fork_choice.best_overall_slot(),
(duplicate_ancestor, duplicate_parent_hash)
);
}
#[test]
fn test_state_unfrozen_bank_duplicate_then_bank_frozen() {
run_test_state_duplicate_then_bank_frozen(Some(Hash::default()));
}
#[test]
fn test_state_unreplayed_bank_duplicate_then_bank_frozen() {
run_test_state_duplicate_then_bank_frozen(None);
}
#[test]
fn test_state_ancestor_confirmed_descendant_duplicate() {
// Common state
let InitialState {
mut heaviest_subtree_fork_choice,
mut progress,
ancestors,
descendants,
progress,
bank_forks,
..
} = setup();
@@ -705,6 +771,7 @@ mod test {
(3, slot3_hash)
);
let root = 0;
let mut duplicate_slots_tracker = DuplicateSlotsTracker::default();
let mut gossip_duplicate_confirmed_slots = GossipDuplicateConfirmedSlots::default();
// Mark slot 2 as duplicate confirmed
@@ -714,36 +781,67 @@ mod test {
2,
root,
Some(slot2_hash),
&mut duplicate_slots_tracker,
&gossip_duplicate_confirmed_slots,
&ancestors,
&descendants,
&mut progress,
&progress,
&mut heaviest_subtree_fork_choice,
SlotStateUpdate::DuplicateConfirmed,
);
assert!(heaviest_subtree_fork_choice
.is_duplicate_confirmed(&(2, slot2_hash))
.unwrap());
assert_eq!(
heaviest_subtree_fork_choice.best_overall_slot(),
(3, slot3_hash)
);
for slot in 0..=2 {
let slot_hash = bank_forks.read().unwrap().get(slot).unwrap().hash();
assert!(heaviest_subtree_fork_choice
.is_duplicate_confirmed(&(slot, slot_hash))
.unwrap());
assert!(heaviest_subtree_fork_choice
.latest_invalid_ancestor(&(slot, slot_hash))
.is_none());
}
// Mark 3 as duplicate, should not remove slot 2 from fork choice
// Mark 3 as duplicate, should not remove the duplicate confirmed slot 2 from
// fork choice
check_slot_agrees_with_cluster(
3,
root,
Some(slot3_hash),
&mut duplicate_slots_tracker,
&gossip_duplicate_confirmed_slots,
&ancestors,
&descendants,
&mut progress,
&progress,
&mut heaviest_subtree_fork_choice,
SlotStateUpdate::Duplicate,
);
assert!(duplicate_slots_tracker.contains(&3));
assert_eq!(
heaviest_subtree_fork_choice.best_overall_slot(),
(2, slot2_hash)
);
for slot in 0..=3 {
let slot_hash = bank_forks.read().unwrap().get(slot).unwrap().hash();
if slot <= 2 {
assert!(heaviest_subtree_fork_choice
.is_duplicate_confirmed(&(slot, slot_hash))
.unwrap());
assert!(heaviest_subtree_fork_choice
.latest_invalid_ancestor(&(slot, slot_hash))
.is_none());
} else {
assert!(!heaviest_subtree_fork_choice
.is_duplicate_confirmed(&(slot, slot_hash))
.unwrap());
assert_eq!(
heaviest_subtree_fork_choice
.latest_invalid_ancestor(&(slot, slot_hash))
.unwrap(),
3
);
}
}
}
#[test]
@@ -751,9 +849,7 @@ mod test {
// Common state
let InitialState {
mut heaviest_subtree_fork_choice,
mut progress,
ancestors,
descendants,
progress,
bank_forks,
..
} = setup();
@@ -764,19 +860,30 @@ mod test {
(3, slot3_hash)
);
let root = 0;
let mut duplicate_slots_tracker = DuplicateSlotsTracker::default();
let mut gossip_duplicate_confirmed_slots = GossipDuplicateConfirmedSlots::default();
// Mark 2 as duplicate confirmed
// Mark 2 as duplicate
check_slot_agrees_with_cluster(
2,
root,
Some(bank_forks.read().unwrap().get(2).unwrap().hash()),
&mut duplicate_slots_tracker,
&gossip_duplicate_confirmed_slots,
&ancestors,
&descendants,
&mut progress,
&progress,
&mut heaviest_subtree_fork_choice,
SlotStateUpdate::Duplicate,
);
assert!(duplicate_slots_tracker.contains(&2));
for slot in 2..=3 {
let slot_hash = bank_forks.read().unwrap().get(slot).unwrap().hash();
assert_eq!(
heaviest_subtree_fork_choice
.latest_invalid_ancestor(&(slot, slot_hash))
.unwrap(),
2
);
}
let slot1_hash = bank_forks.read().unwrap().get(1).unwrap().hash();
assert_eq!(
@@ -790,14 +897,93 @@ mod test {
3,
root,
Some(slot3_hash),
&mut duplicate_slots_tracker,
&gossip_duplicate_confirmed_slots,
&ancestors,
&descendants,
&mut progress,
&progress,
&mut heaviest_subtree_fork_choice,
SlotStateUpdate::DuplicateConfirmed,
);
for slot in 0..=3 {
let slot_hash = bank_forks.read().unwrap().get(slot).unwrap().hash();
assert!(heaviest_subtree_fork_choice
.is_duplicate_confirmed(&(slot, slot_hash))
.unwrap());
assert!(heaviest_subtree_fork_choice
.latest_invalid_ancestor(&(slot, slot_hash))
.is_none());
}
assert_eq!(
heaviest_subtree_fork_choice.best_overall_slot(),
(3, slot3_hash)
);
}
#[test]
fn test_state_descendant_confirmed_ancestor_duplicate() {
// Common state
let InitialState {
mut heaviest_subtree_fork_choice,
progress,
bank_forks,
..
} = setup();
let slot3_hash = bank_forks.read().unwrap().get(3).unwrap().hash();
assert_eq!(
heaviest_subtree_fork_choice.best_overall_slot(),
(3, slot3_hash)
);
let root = 0;
let mut duplicate_slots_tracker = DuplicateSlotsTracker::default();
let mut gossip_duplicate_confirmed_slots = GossipDuplicateConfirmedSlots::default();
// Mark 3 as duplicate confirmed
gossip_duplicate_confirmed_slots.insert(3, slot3_hash);
check_slot_agrees_with_cluster(
3,
root,
Some(slot3_hash),
&mut duplicate_slots_tracker,
&gossip_duplicate_confirmed_slots,
&progress,
&mut heaviest_subtree_fork_choice,
SlotStateUpdate::DuplicateConfirmed,
);
let verify_all_slots_duplicate_confirmed =
|bank_forks: &RwLock<BankForks>,
heaviest_subtree_fork_choice: &HeaviestSubtreeForkChoice| {
for slot in 0..=3 {
let slot_hash = bank_forks.read().unwrap().get(slot).unwrap().hash();
assert!(heaviest_subtree_fork_choice
.is_duplicate_confirmed(&(slot, slot_hash))
.unwrap());
assert!(heaviest_subtree_fork_choice
.latest_invalid_ancestor(&(slot, slot_hash))
.is_none());
}
};
verify_all_slots_duplicate_confirmed(&bank_forks, &heaviest_subtree_fork_choice);
assert_eq!(
heaviest_subtree_fork_choice.best_overall_slot(),
(3, slot3_hash)
);
// Mark ancestor 1 as duplicate, fork choice should be unaffected since
// slot 1 was duplicate confirmed by the confirmation on its
// descendant, 3.
let slot1_hash = bank_forks.read().unwrap().get(1).unwrap().hash();
check_slot_agrees_with_cluster(
1,
root,
Some(slot1_hash),
&mut duplicate_slots_tracker,
&gossip_duplicate_confirmed_slots,
&progress,
&mut heaviest_subtree_fork_choice,
SlotStateUpdate::Duplicate,
);
assert!(duplicate_slots_tracker.contains(&1));
verify_all_slots_duplicate_confirmed(&bank_forks, &heaviest_subtree_fork_choice);
assert_eq!(
heaviest_subtree_fork_choice.best_overall_slot(),
(3, slot3_hash)

View File

@@ -136,6 +136,9 @@ impl ClusterSlots {
}
pub fn compute_weights(&self, slot: Slot, repair_peers: &[ContactInfo]) -> Vec<u64> {
if repair_peers.is_empty() {
return Vec::default();
}
let stakes = {
let validator_stakes = self.validator_stakes.read().unwrap();
repair_peers

View File

@@ -1,29 +1,32 @@
use crate::cluster_slots::ClusterSlots;
use crossbeam_channel::{Receiver, RecvTimeoutError, Sender};
use solana_gossip::cluster_info::ClusterInfo;
use solana_ledger::blockstore::{Blockstore, CompletedSlotsReceiver};
use solana_ledger::blockstore::Blockstore;
use solana_measure::measure::Measure;
use solana_runtime::bank_forks::BankForks;
use solana_sdk::{clock::Slot, pubkey::Pubkey};
use std::{
sync::{
atomic::{AtomicBool, Ordering},
mpsc::RecvTimeoutError,
{Arc, RwLock},
},
thread::{self, Builder, JoinHandle},
time::{Duration, Instant},
};
pub type ClusterSlotsUpdateReceiver = Receiver<Vec<Slot>>;
pub type ClusterSlotsUpdateSender = Sender<Vec<Slot>>;
#[derive(Default, Debug)]
struct ClusterSlotsServiceTiming {
pub lowest_slot_elapsed: u64,
pub update_completed_slots_elapsed: u64,
pub process_cluster_slots_updates_elapsed: u64,
}
impl ClusterSlotsServiceTiming {
fn update(&mut self, lowest_slot_elapsed: u64, update_completed_slots_elapsed: u64) {
fn update(&mut self, lowest_slot_elapsed: u64, process_cluster_slots_updates_elapsed: u64) {
self.lowest_slot_elapsed += lowest_slot_elapsed;
self.update_completed_slots_elapsed += update_completed_slots_elapsed;
self.process_cluster_slots_updates_elapsed += process_cluster_slots_updates_elapsed;
}
}
@@ -37,12 +40,12 @@ impl ClusterSlotsService {
cluster_slots: Arc<ClusterSlots>,
bank_forks: Arc<RwLock<BankForks>>,
cluster_info: Arc<ClusterInfo>,
completed_slots_receiver: CompletedSlotsReceiver,
cluster_slots_update_receiver: ClusterSlotsUpdateReceiver,
exit: Arc<AtomicBool>,
) -> Self {
let id = cluster_info.id();
Self::initialize_lowest_slot(id, &blockstore, &cluster_info);
Self::initialize_epoch_slots(&blockstore, &cluster_info, &completed_slots_receiver);
Self::initialize_epoch_slots(&bank_forks, &cluster_info);
let t_cluster_slots_service = Builder::new()
.name("solana-cluster-slots-service".to_string())
.spawn(move || {
@@ -51,7 +54,7 @@ impl ClusterSlotsService {
cluster_slots,
bank_forks,
cluster_info,
completed_slots_receiver,
cluster_slots_update_receiver,
exit,
)
})
@@ -71,7 +74,7 @@ impl ClusterSlotsService {
cluster_slots: Arc<ClusterSlots>,
bank_forks: Arc<RwLock<BankForks>>,
cluster_info: Arc<ClusterInfo>,
completed_slots_receiver: CompletedSlotsReceiver,
cluster_slots_update_receiver: ClusterSlotsUpdateReceiver,
exit: Arc<AtomicBool>,
) {
let mut cluster_slots_service_timing = ClusterSlotsServiceTiming::default();
@@ -80,7 +83,8 @@ impl ClusterSlotsService {
if exit.load(Ordering::Relaxed) {
break;
}
let slots = match completed_slots_receiver.recv_timeout(Duration::from_millis(200)) {
let slots = match cluster_slots_update_receiver.recv_timeout(Duration::from_millis(200))
{
Ok(slots) => Some(slots),
Err(RecvTimeoutError::Timeout) => None,
Err(RecvTimeoutError::Disconnected) => {
@@ -94,17 +98,21 @@ impl ClusterSlotsService {
let lowest_slot = blockstore.lowest_slot();
Self::update_lowest_slot(&id, lowest_slot, &cluster_info);
lowest_slot_elapsed.stop();
let mut update_completed_slots_elapsed =
Measure::start("update_completed_slots_elapsed");
let mut process_cluster_slots_updates_elapsed =
Measure::start("process_cluster_slots_updates_elapsed");
if let Some(slots) = slots {
Self::update_completed_slots(slots, &completed_slots_receiver, &cluster_info);
Self::process_cluster_slots_updates(
slots,
&cluster_slots_update_receiver,
&cluster_info,
);
}
cluster_slots.update(new_root, &cluster_info, &bank_forks);
update_completed_slots_elapsed.stop();
process_cluster_slots_updates_elapsed.stop();
cluster_slots_service_timing.update(
lowest_slot_elapsed.as_us(),
update_completed_slots_elapsed.as_us(),
process_cluster_slots_updates_elapsed.as_us(),
);
if last_stats.elapsed().as_secs() > 2 {
@@ -116,8 +124,8 @@ impl ClusterSlotsService {
i64
),
(
"update_completed_slots_elapsed",
cluster_slots_service_timing.update_completed_slots_elapsed,
"process_cluster_slots_updates_elapsed",
cluster_slots_service_timing.process_cluster_slots_updates_elapsed,
i64
),
);
@@ -127,12 +135,12 @@ impl ClusterSlotsService {
}
}
fn update_completed_slots(
fn process_cluster_slots_updates(
mut slots: Vec<Slot>,
completed_slots_receiver: &CompletedSlotsReceiver,
cluster_slots_update_receiver: &ClusterSlotsUpdateReceiver,
cluster_info: &ClusterInfo,
) {
while let Ok(mut more) = completed_slots_receiver.try_recv() {
while let Ok(mut more) = cluster_slots_update_receiver.try_recv() {
slots.append(&mut more);
}
#[allow(clippy::stable_sort_primitive)]
@@ -155,30 +163,16 @@ impl ClusterSlotsService {
cluster_info.push_lowest_slot(*id, lowest_slot);
}
fn initialize_epoch_slots(
blockstore: &Blockstore,
cluster_info: &ClusterInfo,
completed_slots_receiver: &CompletedSlotsReceiver,
) {
let root = blockstore.last_root();
let mut slots: Vec<_> = blockstore
.live_slots_iterator(root)
.filter_map(|(slot, slot_meta)| {
if slot_meta.is_full() {
Some(slot)
} else {
None
}
})
.collect();
fn initialize_epoch_slots(bank_forks: &RwLock<BankForks>, cluster_info: &ClusterInfo) {
// TODO: Should probably incorporate slots that were replayed on startup,
// and maybe some that were frozen < snapshot root in case validators restart
// from newer snapshots and lose history.
let frozen_banks = bank_forks.read().unwrap().frozen_banks();
let mut frozen_bank_slots: Vec<Slot> = frozen_banks.keys().cloned().collect();
frozen_bank_slots.sort_unstable();
while let Ok(mut more) = completed_slots_receiver.try_recv() {
slots.append(&mut more);
}
slots.sort_unstable();
slots.dedup();
if !slots.is_empty() {
cluster_info.push_epoch_slots(&slots);
if !frozen_bank_slots.is_empty() {
cluster_info.push_epoch_slots(&frozen_bank_slots);
}
}
}

View File

@@ -352,15 +352,15 @@ mod tests {
if *a <= root {
let mut expected = BlockCommitment::default();
expected.increase_rooted_stake(lamports);
assert_eq!(*commitment.get(&a).unwrap(), expected);
assert_eq!(*commitment.get(a).unwrap(), expected);
} else if i <= 4 {
let mut expected = BlockCommitment::default();
expected.increase_confirmation_stake(2, lamports);
assert_eq!(*commitment.get(&a).unwrap(), expected);
assert_eq!(*commitment.get(a).unwrap(), expected);
} else if i <= 6 {
let mut expected = BlockCommitment::default();
expected.increase_confirmation_stake(1, lamports);
assert_eq!(*commitment.get(&a).unwrap(), expected);
assert_eq!(*commitment.get(a).unwrap(), expected);
}
}
assert_eq!(rooted_stake[0], (root, lamports));

View File

@@ -1,4 +1,5 @@
use crate::{
heaviest_subtree_fork_choice::HeaviestSubtreeForkChoice,
latest_validator_votes_for_frozen_banks::LatestValidatorVotesForFrozenBanks,
progress_map::{LockoutIntervals, ProgressMap},
};
@@ -103,13 +104,13 @@ pub(crate) struct ComputedBankState {
pub my_latest_landed_vote: Option<Slot>,
}
#[frozen_abi(digest = "Eay84NBbJqiMBfE7HHH2o6e51wcvoU79g8zCi5sw6uj3")]
#[frozen_abi(digest = "GMs1FxKteU7K4ZFRofMBqNhBpM4xkPVxfYod6R8DQmpT")]
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, AbiExample)]
pub struct Tower {
node_pubkey: Pubkey,
threshold_depth: usize,
threshold_size: f64,
lockouts: VoteState,
vote_state: VoteState,
last_vote: Vote,
#[serde(skip)]
// The blockhash used in the last vote transaction, may or may not equal the
@@ -140,7 +141,7 @@ impl Default for Tower {
node_pubkey: Pubkey::default(),
threshold_depth: VOTE_THRESHOLD_DEPTH,
threshold_size: VOTE_THRESHOLD_SIZE,
lockouts: VoteState::default(),
vote_state: VoteState::default(),
last_vote: Vote::default(),
last_timestamp: BlockTimestamp::default(),
last_vote_tx_blockhash: Hash::default(),
@@ -150,7 +151,7 @@ impl Default for Tower {
last_switch_threshold_check: Option::default(),
};
// VoteState::root_slot is ensured to be Some in Tower
tower.lockouts.root_slot = Some(Slot::default());
tower.vote_state.root_slot = Some(Slot::default());
tower
}
}
@@ -163,7 +164,7 @@ impl Tower {
bank: &Bank,
path: &Path,
) -> Self {
let path = Self::get_filename(&path, node_pubkey);
let path = Self::get_filename(path, node_pubkey);
let tmp_path = Self::get_tmp_filename(&path);
let mut tower = Self {
node_pubkey: *node_pubkey,
@@ -204,8 +205,8 @@ impl Tower {
crate::replay_stage::ReplayStage::initialize_progress_and_fork_choice(
root_bank.deref(),
bank_forks.frozen_banks().values().cloned().collect(),
&my_pubkey,
&vote_account,
my_pubkey,
vote_account,
);
let root = root_bank.slot();
@@ -217,13 +218,7 @@ impl Tower {
)
.clone();
Self::new(
&my_pubkey,
&vote_account,
root,
&heaviest_bank,
&ledger_path,
)
Self::new(my_pubkey, vote_account, root, &heaviest_bank, ledger_path)
}
pub(crate) fn collect_vote_lockouts<F>(
@@ -378,7 +373,7 @@ impl Tower {
}
pub fn tower_slots(&self) -> Vec<Slot> {
self.lockouts.tower()
self.vote_state.tower()
}
pub fn last_vote_tx_blockhash(&self) -> Hash {
@@ -426,7 +421,7 @@ impl Tower {
let last_voted_slot_in_bank = Self::last_voted_slot_in_bank(bank, vote_account_pubkey);
// Returns the new root if one is made after applying a vote for the given bank to
// `self.lockouts`
// `self.vote_state`
self.record_bank_vote_and_update_lockouts(bank.slot(), bank.hash(), last_voted_slot_in_bank)
}
@@ -439,7 +434,7 @@ impl Tower {
trace!("{} record_vote for {}", self.node_pubkey, vote_slot);
let old_root = self.root();
let mut new_vote = Self::apply_vote_and_generate_vote_diff(
&mut self.lockouts,
&mut self.vote_state,
vote_slot,
vote_hash,
last_voted_slot_in_bank,
@@ -507,12 +502,12 @@ impl Tower {
// snapshot (slot N). In other words, there should be no possibility a Tower doesn't have
// root, unlike young vote accounts.
pub fn root(&self) -> Slot {
self.lockouts.root_slot.unwrap()
self.vote_state.root_slot.unwrap()
}
// a slot is recent if it's newer than the last vote we have
pub fn is_recent(&self, slot: Slot) -> bool {
if let Some(last_voted_slot) = self.lockouts.last_voted_slot() {
if let Some(last_voted_slot) = self.vote_state.last_voted_slot() {
if slot <= last_voted_slot {
return false;
}
@@ -521,7 +516,7 @@ impl Tower {
}
pub fn has_voted(&self, slot: Slot) -> bool {
for vote in &self.lockouts.votes {
for vote in &self.vote_state.votes {
if slot == vote.slot {
return true;
}
@@ -538,15 +533,15 @@ impl Tower {
// slot to the current lockouts to pop any expired votes. If any of the
// remaining voted slots are on a different fork from the checked slot,
// it's still locked out.
let mut lockouts = self.lockouts.clone();
lockouts.process_slot_vote_unchecked(slot);
for vote in &lockouts.votes {
let mut vote_state = self.vote_state.clone();
vote_state.process_slot_vote_unchecked(slot);
for vote in &vote_state.votes {
if slot != vote.slot && !ancestors.contains(&vote.slot) {
return true;
}
}
if let Some(root_slot) = lockouts.root_slot {
if let Some(root_slot) = vote_state.root_slot {
if slot != root_slot {
// This case should never happen because bank forks purges all
// non-descendants of the root every time root is set
@@ -573,6 +568,7 @@ impl Tower {
.map(|candidate_slot_ancestors| candidate_slot_ancestors.contains(&last_voted_slot))
}
#[allow(clippy::too_many_arguments)]
fn make_check_switch_threshold_decision(
&self,
switch_slot: u64,
@@ -582,9 +578,10 @@ impl Tower {
total_stake: u64,
epoch_vote_accounts: &HashMap<Pubkey, (u64, ArcVoteAccount)>,
latest_validator_votes_for_frozen_banks: &LatestValidatorVotesForFrozenBanks,
heaviest_subtree_fork_choice: &HeaviestSubtreeForkChoice,
) -> SwitchForkDecision {
self.last_voted_slot()
.map(|last_voted_slot| {
self.last_voted_slot_hash()
.map(|(last_voted_slot, last_voted_hash)| {
let root = self.root();
let empty_ancestors = HashSet::default();
let empty_ancestors_due_to_minor_unsynced_ledger = || {
@@ -673,7 +670,7 @@ impl Tower {
if last_vote_ancestors.contains(&switch_slot) {
if self.is_stray_last_vote() {
return suspended_decision_due_to_major_unsynced_ledger();
} else if let Some(latest_duplicate_ancestor) = progress.latest_unconfirmed_duplicate_ancestor(last_voted_slot) {
} else if let Some(latest_duplicate_ancestor) = heaviest_subtree_fork_choice.latest_invalid_ancestor(&(last_voted_slot, last_voted_hash)) {
// We're rolling back because one of the ancestors of the last vote was a duplicate. In this
// case, it's acceptable if the switch candidate is one of ancestors of the previous vote,
// just fail the switch check because there's no point in voting on an ancestor. ReplayStage
@@ -733,7 +730,7 @@ impl Tower {
// finding any lockout intervals in the `lockout_intervals` tree
// for this bank that contain `last_vote`.
let lockout_intervals = &progress
.get(&candidate_slot)
.get(candidate_slot)
.unwrap()
.fork_stats
.lockout_intervals;
@@ -821,6 +818,7 @@ impl Tower {
.unwrap_or(SwitchForkDecision::SameFork)
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn check_switch_threshold(
&mut self,
switch_slot: u64,
@@ -830,6 +828,7 @@ impl Tower {
total_stake: u64,
epoch_vote_accounts: &HashMap<Pubkey, (u64, ArcVoteAccount)>,
latest_validator_votes_for_frozen_banks: &LatestValidatorVotesForFrozenBanks,
heaviest_subtree_fork_choice: &HeaviestSubtreeForkChoice,
) -> SwitchForkDecision {
let decision = self.make_check_switch_threshold_decision(
switch_slot,
@@ -839,6 +838,7 @@ impl Tower {
total_stake,
epoch_vote_accounts,
latest_validator_votes_for_frozen_banks,
heaviest_subtree_fork_choice,
);
let new_check = Some((switch_slot, decision.clone()));
if new_check != self.last_switch_threshold_check {
@@ -862,9 +862,9 @@ impl Tower {
voted_stakes: &VotedStakes,
total_stake: Stake,
) -> bool {
let mut lockouts = self.lockouts.clone();
lockouts.process_slot_vote_unchecked(slot);
let vote = lockouts.nth_recent_vote(self.threshold_depth);
let mut vote_state = self.vote_state.clone();
vote_state.process_slot_vote_unchecked(slot);
let vote = vote_state.nth_recent_vote(self.threshold_depth);
if let Some(vote) = vote {
if let Some(fork_stake) = voted_stakes.get(&vote.slot) {
let lockout = *fork_stake as f64 / total_stake as f64;
@@ -873,7 +873,7 @@ impl Tower {
slot, vote.slot, lockout, fork_stake, total_stake
);
if vote.confirmation_count as usize > self.threshold_depth {
for old_vote in &self.lockouts.votes {
for old_vote in &self.vote_state.votes {
if old_vote.slot == vote.slot
&& old_vote.confirmation_count == vote.confirmation_count
{
@@ -928,7 +928,7 @@ impl Tower {
}
fn voted_slots(&self) -> Vec<Slot> {
self.lockouts
self.vote_state
.votes
.iter()
.map(|lockout| lockout.slot)
@@ -964,11 +964,11 @@ impl Tower {
assert_eq!(slot_history.check(replayed_root), Check::Found);
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 == Vote::default() && self.vote_state.votes.is_empty()
|| self.last_vote != Vote::default() && !self.vote_state.votes.is_empty(),
"last vote: {:?} vote_state.votes: {:?}",
self.last_vote,
self.lockouts.votes
self.vote_state.votes
);
if let Some(last_voted_slot) = self.last_voted_slot() {
@@ -1034,7 +1034,7 @@ impl Tower {
let tower_root = self.root();
// retained slots will be consisted only from divergent slots
let mut retain_flags_for_each_vote_in_reverse: Vec<_> =
Vec::with_capacity(self.lockouts.votes.len());
Vec::with_capacity(self.vote_state.votes.len());
let mut still_in_future = true;
let mut past_outside_history = false;
@@ -1112,10 +1112,10 @@ impl Tower {
let mut retain_flags_for_each_vote =
retain_flags_for_each_vote_in_reverse.into_iter().rev();
let original_votes_len = self.lockouts.votes.len();
let original_votes_len = self.vote_state.votes.len();
self.initialize_lockouts(move |_| retain_flags_for_each_vote.next().unwrap());
if self.lockouts.votes.is_empty() {
if self.vote_state.votes.is_empty() {
info!("All restored votes were behind; resetting root_slot and last_vote in tower!");
// we might not have banks for those votes so just reset.
// That's because the votes may well past replayed_root
@@ -1145,7 +1145,7 @@ impl Tower {
bank: &Bank,
) {
if let Some((_stake, vote_account)) = bank.get_vote_account(vote_account_pubkey) {
self.lockouts = vote_account
self.vote_state = vote_account
.vote_state()
.as_ref()
.expect("vote_account isn't a VoteState?")
@@ -1158,7 +1158,7 @@ impl Tower {
bank.slot(),
);
assert_eq!(
self.lockouts.node_pubkey, self.node_pubkey,
self.vote_state.node_pubkey, self.node_pubkey,
"vote account's node_pubkey doesn't match",
);
} else {
@@ -1172,13 +1172,13 @@ impl Tower {
}
fn initialize_lockouts<F: FnMut(&Lockout) -> bool>(&mut self, should_retain: F) {
self.lockouts.votes.retain(should_retain);
self.vote_state.votes.retain(should_retain);
}
// Updating root is needed to correctly restore from newly-saved tower for the next
// boot
fn initialize_root(&mut self, root: Slot) {
self.lockouts.root_slot = Some(root);
self.vote_state.root_slot = Some(root);
}
pub fn get_filename(path: &Path, node_pubkey: &Pubkey) -> PathBuf {
@@ -1322,7 +1322,7 @@ pub fn reconcile_blockstore_roots_with_tower(
if last_blockstore_root < tower_root {
// Ensure tower_root itself to exist and be marked as rooted in the blockstore
// in addition to its ancestors.
let new_roots: Vec<_> = AncestorIterator::new_inclusive(tower_root, &blockstore)
let new_roots: Vec<_> = AncestorIterator::new_inclusive(tower_root, blockstore)
.take_while(|current| match current.cmp(&last_blockstore_root) {
Ordering::Greater => true,
Ordering::Equal => false,
@@ -1337,7 +1337,7 @@ pub fn reconcile_blockstore_roots_with_tower(
"Reconciling slots as root based on tower root: {:?} ({}..{}) ",
new_roots, tower_root, last_blockstore_root
);
blockstore.set_roots(&new_roots)?;
blockstore.set_roots(new_roots.iter())?;
} else {
// This indicates we're in bad state; but still don't panic here.
// That's because we might have a chance of recovering properly with
@@ -1358,11 +1358,11 @@ pub mod test {
use super::*;
use crate::{
cluster_info_vote_listener::VoteTracker,
cluster_slot_state_verifier::GossipDuplicateConfirmedSlots,
cluster_slot_state_verifier::{DuplicateSlotsTracker, GossipDuplicateConfirmedSlots},
cluster_slots::ClusterSlots,
fork_choice::SelectVoteAndResetForkResult,
heaviest_subtree_fork_choice::{HeaviestSubtreeForkChoice, SlotHashKey},
progress_map::{DuplicateStats, ForkProgress},
fork_choice::{ForkChoice, SelectVoteAndResetForkResult},
heaviest_subtree_fork_choice::SlotHashKey,
progress_map::ForkProgress,
replay_stage::{HeaviestForkFailures, ReplayStage},
unfrozen_gossip_verified_vote_hashes::UnfrozenGossipVerifiedVoteHashes,
};
@@ -1439,9 +1439,6 @@ pub mod test {
while let Some(visit) = walk.get() {
let slot = visit.node().data;
self.progress.entry(slot).or_insert_with(|| {
ForkProgress::new(Hash::default(), None, DuplicateStats::default(), None, 0, 0)
});
if self.bank_forks.read().unwrap().get(slot).is_some() {
walk.forward();
continue;
@@ -1449,6 +1446,9 @@ pub mod test {
let parent = walk.get_parent().unwrap().data;
let parent_bank = self.bank_forks.read().unwrap().get(parent).unwrap().clone();
let new_bank = Bank::new_from_parent(&parent_bank, &Pubkey::default(), slot);
self.progress
.entry(slot)
.or_insert_with(|| ForkProgress::new(Hash::default(), None, None, 0, 0));
for (pubkey, vote) in cluster_votes.iter() {
if vote.contains(&parent) {
let keypairs = self.validator_keypairs.get(pubkey).unwrap();
@@ -1484,7 +1484,7 @@ pub mod test {
tower: &mut Tower,
) -> Vec<HeaviestForkFailures> {
// Try to simulate the vote
let my_keypairs = self.validator_keypairs.get(&my_pubkey).unwrap();
let my_keypairs = self.validator_keypairs.get(my_pubkey).unwrap();
let my_vote_pubkey = my_keypairs.vote_keypair.pubkey();
let ancestors = self.bank_forks.read().unwrap().ancestors();
let mut frozen_banks: Vec<_> = self
@@ -1497,7 +1497,7 @@ pub mod test {
.collect();
let _ = ReplayStage::compute_bank_stats(
&my_pubkey,
my_pubkey,
&ancestors,
&mut frozen_banks,
tower,
@@ -1530,6 +1530,7 @@ pub mod test {
&self.progress,
tower,
&self.latest_validator_votes_for_frozen_banks,
&self.heaviest_subtree_fork_choice,
);
// Make sure this slot isn't locked out or failing threshold
@@ -1554,6 +1555,7 @@ pub mod test {
&AbsRequestSender::default(),
None,
&mut self.heaviest_subtree_fork_choice,
&mut DuplicateSlotsTracker::default(),
&mut GossipDuplicateConfirmedSlots::default(),
&mut UnfrozenGossipVerifiedVoteHashes::default(),
&mut true,
@@ -1574,9 +1576,9 @@ pub mod test {
.filter_map(|slot| {
let mut fork_tip_parent = tr(slot - 1);
fork_tip_parent.push_front(tr(slot));
self.fill_bank_forks(fork_tip_parent, &cluster_votes);
self.fill_bank_forks(fork_tip_parent, cluster_votes);
if votes_to_simulate.contains(&slot) {
Some((slot, self.simulate_vote(slot, &my_pubkey, tower)))
Some((slot, self.simulate_vote(slot, my_pubkey, tower)))
} else {
None
}
@@ -1592,9 +1594,7 @@ pub mod test {
) {
self.progress
.entry(slot)
.or_insert_with(|| {
ForkProgress::new(Hash::default(), None, DuplicateStats::default(), None, 0, 0)
})
.or_insert_with(|| ForkProgress::new(Hash::default(), None, None, 0, 0))
.fork_stats
.lockout_intervals
.entry(lockout_interval.1)
@@ -1621,7 +1621,7 @@ pub mod test {
fork_tip_parent.push_front(tr(start_slot + i));
self.fill_bank_forks(fork_tip_parent, cluster_votes);
if self
.simulate_vote(i + start_slot, &my_pubkey, tower)
.simulate_vote(i + start_slot, my_pubkey, tower)
.is_empty()
{
cluster_votes
@@ -1701,10 +1701,10 @@ pub mod test {
let mut progress = ProgressMap::default();
progress.insert(
0,
ForkProgress::new(
bank0.last_blockhash(),
None,
DuplicateStats::default(),
ForkProgress::new_from_bank(
&bank0,
bank0.collector_id(),
&Pubkey::default(),
None,
0,
0,
@@ -1800,8 +1800,8 @@ pub mod test {
}
for i in 0..5 {
assert_eq!(tower.lockouts.votes[i].slot as usize, i);
assert_eq!(tower.lockouts.votes[i].confirmation_count as usize, 6 - i);
assert_eq!(tower.vote_state.votes[i].slot as usize, i);
assert_eq!(tower.vote_state.votes[i].confirmation_count as usize, 6 - i);
}
}
@@ -1867,21 +1867,46 @@ pub mod test {
let mut tower = Tower::new_with_key(&vote_simulator.node_pubkeys[0]);
// Last vote is 47
tower.record_vote(47, Hash::default());
tower.record_vote(
47,
vote_simulator
.bank_forks
.read()
.unwrap()
.get(47)
.unwrap()
.hash(),
);
// Trying to switch to an ancestor of last vote should only not panic
// if the current vote has a duplicate ancestor
let ancestor_of_voted_slot = 43;
let duplicate_ancestor1 = 44;
let duplicate_ancestor2 = 45;
vote_simulator.progress.set_unconfirmed_duplicate_slot(
duplicate_ancestor1,
&descendants.get(&duplicate_ancestor1).unwrap(),
);
vote_simulator.progress.set_unconfirmed_duplicate_slot(
duplicate_ancestor2,
&descendants.get(&duplicate_ancestor2).unwrap(),
);
vote_simulator
.heaviest_subtree_fork_choice
.mark_fork_invalid_candidate(&(
duplicate_ancestor1,
vote_simulator
.bank_forks
.read()
.unwrap()
.get(duplicate_ancestor1)
.unwrap()
.hash(),
));
vote_simulator
.heaviest_subtree_fork_choice
.mark_fork_invalid_candidate(&(
duplicate_ancestor2,
vote_simulator
.bank_forks
.read()
.unwrap()
.get(duplicate_ancestor2)
.unwrap()
.hash(),
));
assert_eq!(
tower.check_switch_threshold(
ancestor_of_voted_slot,
@@ -1890,7 +1915,8 @@ pub mod test {
&vote_simulator.progress,
total_stake,
bank0.epoch_vote_accounts(0).unwrap(),
&vote_simulator.latest_validator_votes_for_frozen_banks
&vote_simulator.latest_validator_votes_for_frozen_banks,
&vote_simulator.heaviest_subtree_fork_choice,
),
SwitchForkDecision::FailedSwitchDuplicateRollback(duplicate_ancestor2)
);
@@ -1903,11 +1929,18 @@ pub mod test {
confirm_ancestors.push(duplicate_ancestor2);
}
for (i, duplicate_ancestor) in confirm_ancestors.into_iter().enumerate() {
vote_simulator.progress.set_confirmed_duplicate_slot(
duplicate_ancestor,
ancestors.get(&duplicate_ancestor).unwrap(),
&descendants.get(&duplicate_ancestor).unwrap(),
);
vote_simulator
.heaviest_subtree_fork_choice
.mark_fork_valid_candidate(&(
duplicate_ancestor,
vote_simulator
.bank_forks
.read()
.unwrap()
.get(duplicate_ancestor)
.unwrap()
.hash(),
));
let res = tower.check_switch_threshold(
ancestor_of_voted_slot,
&ancestors,
@@ -1916,6 +1949,7 @@ pub mod test {
total_stake,
bank0.epoch_vote_accounts(0).unwrap(),
&vote_simulator.latest_validator_votes_for_frozen_banks,
&vote_simulator.heaviest_subtree_fork_choice,
);
if i == 0 {
assert_eq!(
@@ -1951,7 +1985,8 @@ pub mod test {
&vote_simulator.progress,
total_stake,
bank0.epoch_vote_accounts(0).unwrap(),
&vote_simulator.latest_validator_votes_for_frozen_banks
&vote_simulator.latest_validator_votes_for_frozen_banks,
&vote_simulator.heaviest_subtree_fork_choice,
),
SwitchForkDecision::SameFork
);
@@ -1965,7 +2000,8 @@ pub mod test {
&vote_simulator.progress,
total_stake,
bank0.epoch_vote_accounts(0).unwrap(),
&vote_simulator.latest_validator_votes_for_frozen_banks
&vote_simulator.latest_validator_votes_for_frozen_banks,
&vote_simulator.heaviest_subtree_fork_choice,
),
SwitchForkDecision::FailedSwitchThreshold(0, 20000)
);
@@ -1981,7 +2017,8 @@ pub mod test {
&vote_simulator.progress,
total_stake,
bank0.epoch_vote_accounts(0).unwrap(),
&vote_simulator.latest_validator_votes_for_frozen_banks
&vote_simulator.latest_validator_votes_for_frozen_banks,
&vote_simulator.heaviest_subtree_fork_choice,
),
SwitchForkDecision::FailedSwitchThreshold(0, 20000)
);
@@ -1997,7 +2034,8 @@ pub mod test {
&vote_simulator.progress,
total_stake,
bank0.epoch_vote_accounts(0).unwrap(),
&vote_simulator.latest_validator_votes_for_frozen_banks
&vote_simulator.latest_validator_votes_for_frozen_banks,
&vote_simulator.heaviest_subtree_fork_choice,
),
SwitchForkDecision::FailedSwitchThreshold(0, 20000)
);
@@ -2013,7 +2051,8 @@ pub mod test {
&vote_simulator.progress,
total_stake,
bank0.epoch_vote_accounts(0).unwrap(),
&vote_simulator.latest_validator_votes_for_frozen_banks
&vote_simulator.latest_validator_votes_for_frozen_banks,
&vote_simulator.heaviest_subtree_fork_choice,
),
SwitchForkDecision::FailedSwitchThreshold(0, 20000)
);
@@ -2031,7 +2070,8 @@ pub mod test {
&vote_simulator.progress,
total_stake,
bank0.epoch_vote_accounts(0).unwrap(),
&vote_simulator.latest_validator_votes_for_frozen_banks
&vote_simulator.latest_validator_votes_for_frozen_banks,
&vote_simulator.heaviest_subtree_fork_choice,
),
SwitchForkDecision::FailedSwitchThreshold(0, 20000)
);
@@ -2047,7 +2087,8 @@ pub mod test {
&vote_simulator.progress,
total_stake,
bank0.epoch_vote_accounts(0).unwrap(),
&vote_simulator.latest_validator_votes_for_frozen_banks
&vote_simulator.latest_validator_votes_for_frozen_banks,
&vote_simulator.heaviest_subtree_fork_choice,
),
SwitchForkDecision::SwitchProof(Hash::default())
);
@@ -2064,7 +2105,8 @@ pub mod test {
&vote_simulator.progress,
total_stake,
bank0.epoch_vote_accounts(0).unwrap(),
&vote_simulator.latest_validator_votes_for_frozen_banks
&vote_simulator.latest_validator_votes_for_frozen_banks,
&vote_simulator.heaviest_subtree_fork_choice,
),
SwitchForkDecision::SwitchProof(Hash::default())
);
@@ -2072,7 +2114,7 @@ pub mod test {
// If we set a root, then any lockout intervals below the root shouldn't
// count toward the switch threshold. This means the other validator's
// vote lockout no longer counts
tower.lockouts.root_slot = Some(43);
tower.vote_state.root_slot = Some(43);
// Refresh ancestors and descendants for new root.
let ancestors = vote_simulator.bank_forks.read().unwrap().ancestors();
let descendants = vote_simulator
@@ -2090,7 +2132,8 @@ pub mod test {
&vote_simulator.progress,
total_stake,
bank0.epoch_vote_accounts(0).unwrap(),
&vote_simulator.latest_validator_votes_for_frozen_banks
&vote_simulator.latest_validator_votes_for_frozen_banks,
&vote_simulator.heaviest_subtree_fork_choice,
),
SwitchForkDecision::FailedSwitchThreshold(0, 20000)
);
@@ -2122,7 +2165,8 @@ pub mod test {
&vote_simulator.progress,
total_stake,
bank0.epoch_vote_accounts(0).unwrap(),
&vote_simulator.latest_validator_votes_for_frozen_banks
&vote_simulator.latest_validator_votes_for_frozen_banks,
&vote_simulator.heaviest_subtree_fork_choice,
),
SwitchForkDecision::FailedSwitchThreshold(0, num_validators * 10000)
);
@@ -2137,7 +2181,8 @@ pub mod test {
&vote_simulator.progress,
total_stake,
bank0.epoch_vote_accounts(0).unwrap(),
&vote_simulator.latest_validator_votes_for_frozen_banks
&vote_simulator.latest_validator_votes_for_frozen_banks,
&vote_simulator.heaviest_subtree_fork_choice,
),
SwitchForkDecision::FailedSwitchThreshold(0, 20000)
);
@@ -2169,7 +2214,8 @@ pub mod test {
&vote_simulator.progress,
total_stake,
bank0.epoch_vote_accounts(0).unwrap(),
&vote_simulator.latest_validator_votes_for_frozen_banks
&vote_simulator.latest_validator_votes_for_frozen_banks,
&vote_simulator.heaviest_subtree_fork_choice,
),
SwitchForkDecision::SwitchProof(Hash::default())
);
@@ -2193,7 +2239,8 @@ pub mod test {
&vote_simulator.progress,
total_stake,
bank0.epoch_vote_accounts(0).unwrap(),
&vote_simulator.latest_validator_votes_for_frozen_banks
&vote_simulator.latest_validator_votes_for_frozen_banks,
&vote_simulator.heaviest_subtree_fork_choice,
),
SwitchForkDecision::FailedSwitchThreshold(0, 20000)
);
@@ -2307,7 +2354,7 @@ pub mod test {
.is_empty());
}
info!("local tower: {:#?}", tower.lockouts.votes);
info!("local tower: {:#?}", tower.vote_state.votes);
let observed = vote_simulator
.bank_forks
.read()
@@ -2399,14 +2446,14 @@ pub mod test {
};
let root_weight = root.lockout() as u128;
let vote_account_expected_weight = tower
.lockouts
.vote_state
.votes
.iter()
.map(|v| v.lockout() as u128)
.sum::<u128>()
+ root_weight;
let expected_bank_weight = 2 * vote_account_expected_weight;
assert_eq!(tower.lockouts.root_slot, Some(0));
assert_eq!(tower.vote_state.root_slot, Some(0));
let mut latest_validator_votes_for_frozen_banks =
LatestValidatorVotesForFrozenBanks::default();
let ComputedBankState {
@@ -2483,7 +2530,7 @@ pub mod test {
fn test_is_locked_out_root_slot_child_pass() {
let mut tower = Tower::new_for_tests(0, 0.67);
let ancestors: HashSet<Slot> = vec![0].into_iter().collect();
tower.lockouts.root_slot = Some(0);
tower.vote_state.root_slot = Some(0);
assert!(!tower.is_locked_out(1, &ancestors));
}
@@ -2491,7 +2538,7 @@ pub mod test {
fn test_is_locked_out_root_slot_sibling_fail() {
let mut tower = Tower::new_for_tests(0, 0.67);
let ancestors: HashSet<Slot> = vec![0].into_iter().collect();
tower.lockouts.root_slot = Some(0);
tower.vote_state.root_slot = Some(0);
tower.record_vote(1, Hash::default());
assert!(tower.is_locked_out(2, &ancestors));
}
@@ -2552,10 +2599,10 @@ pub mod test {
tower.record_vote(1, Hash::default());
assert!(!tower.is_locked_out(4, &ancestors));
tower.record_vote(4, Hash::default());
assert_eq!(tower.lockouts.votes[0].slot, 0);
assert_eq!(tower.lockouts.votes[0].confirmation_count, 2);
assert_eq!(tower.lockouts.votes[1].slot, 4);
assert_eq!(tower.lockouts.votes[1].confirmation_count, 1);
assert_eq!(tower.vote_state.votes[0].slot, 0);
assert_eq!(tower.vote_state.votes[0].confirmation_count, 2);
assert_eq!(tower.vote_state.votes[1].slot, 4);
assert_eq!(tower.vote_state.votes[1].confirmation_count, 1);
}
#[test]
@@ -2804,7 +2851,7 @@ pub mod test {
tower.save(&identity_keypair).unwrap();
modify_serialized(&tower.path);
let loaded = Tower::restore(&dir.path(), &identity_keypair.pubkey());
let loaded = Tower::restore(dir.path(), &identity_keypair.pubkey());
(tower, loaded)
}
@@ -2872,7 +2919,8 @@ pub mod test {
&vote_simulator.progress,
total_stake,
bank0.epoch_vote_accounts(0).unwrap(),
&vote_simulator.latest_validator_votes_for_frozen_banks
&vote_simulator.latest_validator_votes_for_frozen_banks,
&vote_simulator.heaviest_subtree_fork_choice,
),
SwitchForkDecision::SameFork
);
@@ -2886,7 +2934,8 @@ pub mod test {
&vote_simulator.progress,
total_stake,
bank0.epoch_vote_accounts(0).unwrap(),
&vote_simulator.latest_validator_votes_for_frozen_banks
&vote_simulator.latest_validator_votes_for_frozen_banks,
&vote_simulator.heaviest_subtree_fork_choice,
),
SwitchForkDecision::FailedSwitchThreshold(0, 20000)
);
@@ -2901,7 +2950,8 @@ pub mod test {
&vote_simulator.progress,
total_stake,
bank0.epoch_vote_accounts(0).unwrap(),
&vote_simulator.latest_validator_votes_for_frozen_banks
&vote_simulator.latest_validator_votes_for_frozen_banks,
&vote_simulator.heaviest_subtree_fork_choice,
),
SwitchForkDecision::SwitchProof(Hash::default())
);
@@ -2912,7 +2962,7 @@ pub mod test {
tower.record_vote(110, Hash::default());
tower.record_vote(111, Hash::default());
assert_eq!(tower.voted_slots(), vec![43, 110, 111]);
assert_eq!(tower.lockouts.root_slot, Some(0));
assert_eq!(tower.vote_state.root_slot, Some(0));
}
// Prepare simulated validator restart!
@@ -2971,7 +3021,8 @@ pub mod test {
&vote_simulator.progress,
total_stake,
bank0.epoch_vote_accounts(0).unwrap(),
&vote_simulator.latest_validator_votes_for_frozen_banks
&vote_simulator.latest_validator_votes_for_frozen_banks,
&vote_simulator.heaviest_subtree_fork_choice,
),
SwitchForkDecision::FailedSwitchThreshold(0, 20000)
);
@@ -2986,7 +3037,8 @@ pub mod test {
&vote_simulator.progress,
total_stake,
bank0.epoch_vote_accounts(0).unwrap(),
&vote_simulator.latest_validator_votes_for_frozen_banks
&vote_simulator.latest_validator_votes_for_frozen_banks,
&vote_simulator.heaviest_subtree_fork_choice,
),
SwitchForkDecision::FailedSwitchThreshold(0, 20000)
);
@@ -3001,7 +3053,8 @@ pub mod test {
&vote_simulator.progress,
total_stake,
bank0.epoch_vote_accounts(0).unwrap(),
&vote_simulator.latest_validator_votes_for_frozen_banks
&vote_simulator.latest_validator_votes_for_frozen_banks,
&vote_simulator.heaviest_subtree_fork_choice,
),
SwitchForkDecision::SwitchProof(Hash::default())
);
@@ -3009,7 +3062,7 @@ pub mod test {
tower.record_vote(110, Hash::default());
tower.record_vote(111, Hash::default());
assert_eq!(tower.voted_slots(), vec![110, 111]);
assert_eq!(tower.lockouts.root_slot, Some(replayed_root_slot));
assert_eq!(tower.vote_state.root_slot, Some(replayed_root_slot));
}
#[test]
@@ -3099,7 +3152,7 @@ pub mod test {
assert!(!blockstore.is_root(4));
let mut tower = Tower::new_with_key(&Pubkey::default());
tower.lockouts.root_slot = Some(4);
tower.vote_state.root_slot = Some(4);
reconcile_blockstore_roots_with_tower(&tower, &blockstore).unwrap();
assert!(!blockstore.is_root(0));
@@ -3124,14 +3177,14 @@ pub mod test {
blockstore.insert_shreds(shreds, None, false).unwrap();
let (shreds, _) = make_slot_entries(4, 1, 42);
blockstore.insert_shreds(shreds, None, false).unwrap();
blockstore.set_roots(&[3]).unwrap();
blockstore.set_roots(std::iter::once(&3)).unwrap();
assert!(!blockstore.is_root(0));
assert!(!blockstore.is_root(1));
assert!(blockstore.is_root(3));
assert!(!blockstore.is_root(4));
let mut tower = Tower::new_with_key(&Pubkey::default());
tower.lockouts.root_slot = Some(4);
tower.vote_state.root_slot = Some(4);
reconcile_blockstore_roots_with_tower(&tower, &blockstore).unwrap();
}
Blockstore::destroy(&blockstore_path).expect("Expected successful database destruction");
@@ -3153,7 +3206,7 @@ pub mod test {
assert!(!blockstore.is_root(3));
let mut tower = Tower::new_with_key(&Pubkey::default());
tower.lockouts.root_slot = Some(4);
tower.vote_state.root_slot = Some(4);
assert_eq!(blockstore.last_root(), 0);
reconcile_blockstore_roots_with_tower(&tower, &blockstore).unwrap();
assert_eq!(blockstore.last_root(), 0);
@@ -3305,7 +3358,7 @@ pub mod test {
#[test]
fn test_adjust_lockouts_after_replay_all_not_found_even_if_rooted() {
let mut tower = Tower::new_for_tests(10, 0.9);
tower.lockouts.root_slot = Some(4);
tower.vote_state.root_slot = Some(4);
tower.record_vote(5, Hash::default());
tower.record_vote(6, Hash::default());
@@ -3327,7 +3380,7 @@ pub mod test {
#[test]
fn test_adjust_lockouts_after_replay_all_future_votes_only_root_found() {
let mut tower = Tower::new_for_tests(10, 0.9);
tower.lockouts.root_slot = Some(2);
tower.vote_state.root_slot = Some(2);
tower.record_vote(3, Hash::default());
tower.record_vote(4, Hash::default());
tower.record_vote(5, Hash::default());
@@ -3383,8 +3436,8 @@ pub mod test {
#[test]
fn test_adjust_lockouts_after_replay_time_warped() {
let mut tower = Tower::new_for_tests(10, 0.9);
tower.lockouts.votes.push_back(Lockout::new(1));
tower.lockouts.votes.push_back(Lockout::new(0));
tower.vote_state.votes.push_back(Lockout::new(1));
tower.vote_state.votes.push_back(Lockout::new(0));
let vote = Vote::new(vec![0], Hash::default());
tower.last_vote = vote;
@@ -3401,8 +3454,8 @@ pub mod test {
#[test]
fn test_adjust_lockouts_after_replay_diverged_ancestor() {
let mut tower = Tower::new_for_tests(10, 0.9);
tower.lockouts.votes.push_back(Lockout::new(1));
tower.lockouts.votes.push_back(Lockout::new(2));
tower.vote_state.votes.push_back(Lockout::new(1));
tower.vote_state.votes.push_back(Lockout::new(2));
let vote = Vote::new(vec![2], Hash::default());
tower.last_vote = vote;
@@ -3423,11 +3476,11 @@ pub mod test {
let mut tower = Tower::new_for_tests(10, 0.9);
tower
.lockouts
.vote_state
.votes
.push_back(Lockout::new(MAX_ENTRIES - 1));
tower.lockouts.votes.push_back(Lockout::new(0));
tower.lockouts.votes.push_back(Lockout::new(1));
tower.vote_state.votes.push_back(Lockout::new(0));
tower.vote_state.votes.push_back(Lockout::new(1));
let vote = Vote::new(vec![1], Hash::default());
tower.last_vote = vote;
@@ -3445,8 +3498,8 @@ pub mod test {
#[should_panic(expected = "slot_in_tower(2) < checked_slot(1)")]
fn test_adjust_lockouts_after_replay_reversed_votes() {
let mut tower = Tower::new_for_tests(10, 0.9);
tower.lockouts.votes.push_back(Lockout::new(2));
tower.lockouts.votes.push_back(Lockout::new(1));
tower.vote_state.votes.push_back(Lockout::new(2));
tower.vote_state.votes.push_back(Lockout::new(1));
let vote = Vote::new(vec![1], Hash::default());
tower.last_vote = vote;
@@ -3463,9 +3516,9 @@ pub mod test {
#[should_panic(expected = "slot_in_tower(3) < checked_slot(3)")]
fn test_adjust_lockouts_after_replay_repeated_non_root_votes() {
let mut tower = Tower::new_for_tests(10, 0.9);
tower.lockouts.votes.push_back(Lockout::new(2));
tower.lockouts.votes.push_back(Lockout::new(3));
tower.lockouts.votes.push_back(Lockout::new(3));
tower.vote_state.votes.push_back(Lockout::new(2));
tower.vote_state.votes.push_back(Lockout::new(3));
tower.vote_state.votes.push_back(Lockout::new(3));
let vote = Vote::new(vec![3], Hash::default());
tower.last_vote = vote;
@@ -3481,10 +3534,10 @@ pub mod test {
#[test]
fn test_adjust_lockouts_after_replay_vote_on_root() {
let mut tower = Tower::new_for_tests(10, 0.9);
tower.lockouts.root_slot = Some(42);
tower.lockouts.votes.push_back(Lockout::new(42));
tower.lockouts.votes.push_back(Lockout::new(43));
tower.lockouts.votes.push_back(Lockout::new(44));
tower.vote_state.root_slot = Some(42);
tower.vote_state.votes.push_back(Lockout::new(42));
tower.vote_state.votes.push_back(Lockout::new(43));
tower.vote_state.votes.push_back(Lockout::new(44));
let vote = Vote::new(vec![44], Hash::default());
tower.last_vote = vote;
@@ -3498,7 +3551,7 @@ pub mod test {
#[test]
fn test_adjust_lockouts_after_replay_vote_on_genesis() {
let mut tower = Tower::new_for_tests(10, 0.9);
tower.lockouts.votes.push_back(Lockout::new(0));
tower.vote_state.votes.push_back(Lockout::new(0));
let vote = Vote::new(vec![0], Hash::default());
tower.last_vote = vote;
@@ -3511,8 +3564,8 @@ pub mod test {
#[test]
fn test_adjust_lockouts_after_replay_future_tower() {
let mut tower = Tower::new_for_tests(10, 0.9);
tower.lockouts.votes.push_back(Lockout::new(13));
tower.lockouts.votes.push_back(Lockout::new(14));
tower.vote_state.votes.push_back(Lockout::new(13));
tower.vote_state.votes.push_back(Lockout::new(14));
let vote = Vote::new(vec![14], Hash::default());
tower.last_vote = vote;
tower.initialize_root(12);

View File

@@ -1,11 +1,11 @@
//! 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::poh_recorder::PohRecorder;
use crate::result::{Error, Result};
use solana_metrics::{inc_new_counter_debug, inc_new_counter_info};
use solana_perf::packet::PacketsRecycler;
use solana_perf::recycler::Recycler;
use solana_poh::poh_recorder::PohRecorder;
use solana_sdk::clock::DEFAULT_TICKS_PER_SLOT;
use solana_streamer::streamer::{self, PacketReceiver, PacketSender};
use std::net::UdpSocket;
@@ -34,7 +34,7 @@ impl FetchStage {
tpu_forwards_sockets,
exit,
&sender,
&poh_recorder,
poh_recorder,
coalesce_ms,
),
receiver,
@@ -54,8 +54,8 @@ impl FetchStage {
tx_sockets,
tpu_forwards_sockets,
exit,
&sender,
&poh_recorder,
sender,
poh_recorder,
coalesce_ms,
)
}
@@ -85,7 +85,7 @@ impl FetchStage {
inc_new_counter_debug!("fetch_stage-honor_forwards", len);
for packets in batch {
if sendr.send(packets).is_err() {
return Err(Error::SendError);
return Err(Error::Send);
}
}
} else {
@@ -108,11 +108,12 @@ impl FetchStage {
let tpu_threads = sockets.into_iter().map(|socket| {
streamer::receiver(
socket,
&exit,
exit,
sender.clone(),
recycler.clone(),
"fetch_stage",
coalesce_ms,
true,
)
});
@@ -120,11 +121,12 @@ impl FetchStage {
let tpu_forwards_threads = tpu_forwards_sockets.into_iter().map(|socket| {
streamer::receiver(
socket,
&exit,
exit,
forward_sender.clone(),
recycler.clone(),
"fetch_forward_stage",
coalesce_ms,
true,
)
});
@@ -138,10 +140,10 @@ impl FetchStage {
Self::handle_forwarded_packets(&forward_receiver, &sender, &poh_recorder)
{
match e {
Error::RecvTimeoutError(RecvTimeoutError::Disconnected) => break,
Error::RecvTimeoutError(RecvTimeoutError::Timeout) => (),
Error::RecvError(_) => break,
Error::SendError => break,
Error::RecvTimeout(RecvTimeoutError::Disconnected) => break,
Error::RecvTimeout(RecvTimeoutError::Timeout) => (),
Error::Recv(_) => break,
Error::Send => break,
_ => error!("{:?}", e),
}
}

View File

@@ -30,7 +30,17 @@ const MAX_ROOT_PRINT_SECONDS: u64 = 30;
enum UpdateLabel {
Aggregate,
Add,
MarkValid,
// Notify a fork in the tree that a particular slot in that fork is now
// marked as valid. If there are multiple MarkValid operations for
// a single node, should apply the one with the smaller slot first (hence
// why the actual slot is included here).
MarkValid(Slot),
// Notify a fork in the tree that a particular slot in that fork is now
// marked as invalid. If there are multiple MarkInvalid operations for
// a single node, should apply the one with the smaller slot first (hence
// why the actual slot is included here).
MarkInvalid(Slot),
Subtract,
}
@@ -53,7 +63,10 @@ impl GetSlotHash for Slot {
#[derive(PartialEq, Eq, Clone, Debug)]
enum UpdateOperation {
Add(u64),
MarkValid,
MarkValid(Slot),
// Notify a fork in the tree that a particular slot in that fork is now
// marked as invalid.
MarkInvalid(Slot),
Subtract(u64),
Aggregate,
}
@@ -63,7 +76,8 @@ impl UpdateOperation {
match self {
Self::Aggregate => panic!("Should not get here"),
Self::Add(stake) => *stake += new_stake,
Self::MarkValid => panic!("Should not get here"),
Self::MarkValid(_slot) => panic!("Should not get here"),
Self::MarkInvalid(_slot) => panic!("Should not get here"),
Self::Subtract(stake) => *stake += new_stake,
}
}
@@ -80,9 +94,68 @@ struct ForkInfo {
best_slot: SlotHashKey,
parent: Option<SlotHashKey>,
children: Vec<SlotHashKey>,
// Whether the fork rooted at this slot is a valid contender
// for the best fork
is_candidate: bool,
// The latest ancestor of this node that has been marked invalid. If the slot
// itself is a duplicate, this is set to the slot itself.
latest_invalid_ancestor: Option<Slot>,
// Set to true if this slot or a child node was duplicate confirmed.
is_duplicate_confirmed: bool,
}
impl ForkInfo {
/// Returns if this node has been explicitly marked as a duplicate
/// slot
fn is_unconfirmed_duplicate(&self, my_slot: Slot) -> bool {
self.latest_invalid_ancestor
.map(|ancestor| ancestor == my_slot)
.unwrap_or(false)
}
/// Returns if the fork rooted at this node is included in fork choice
fn is_candidate(&self) -> bool {
self.latest_invalid_ancestor.is_none()
}
fn is_duplicate_confirmed(&self) -> bool {
self.is_duplicate_confirmed
}
fn set_duplicate_confirmed(&mut self) {
self.is_duplicate_confirmed = true;
self.latest_invalid_ancestor = None;
}
fn update_with_newly_valid_ancestor(
&mut self,
my_key: &SlotHashKey,
newly_valid_ancestor: Slot,
) {
if let Some(latest_invalid_ancestor) = self.latest_invalid_ancestor {
if latest_invalid_ancestor <= newly_valid_ancestor {
info!("Fork choice for {:?} clearing latest invalid ancestor {:?} because {:?} was duplicate confirmed", my_key, latest_invalid_ancestor, newly_valid_ancestor);
self.latest_invalid_ancestor = None;
}
}
}
fn update_with_newly_invalid_ancestor(
&mut self,
my_key: &SlotHashKey,
newly_invalid_ancestor: Slot,
) {
// Should not be marking a duplicate confirmed slot as invalid
assert!(!self.is_duplicate_confirmed);
if self
.latest_invalid_ancestor
.map(|latest_invalid_ancestor| newly_invalid_ancestor > latest_invalid_ancestor)
.unwrap_or(true)
{
info!(
"Fork choice for {:?} setting latest invalid ancestor from {:?} to {}",
my_key, self.latest_invalid_ancestor, newly_invalid_ancestor
);
self.latest_invalid_ancestor = Some(newly_invalid_ancestor);
}
}
}
pub struct HeaviestSubtreeForkChoice {
@@ -182,12 +255,6 @@ impl HeaviestSubtreeForkChoice {
.map(|fork_info| fork_info.stake_voted_subtree)
}
pub fn is_candidate_slot(&self, key: &SlotHashKey) -> Option<bool> {
self.fork_infos
.get(key)
.map(|fork_info| fork_info.is_candidate)
}
pub fn root(&self) -> SlotHashKey {
self.root
}
@@ -252,35 +319,41 @@ impl HeaviestSubtreeForkChoice {
best_slot: root_info.best_slot,
children: vec![self.root],
parent: None,
is_candidate: true,
latest_invalid_ancestor: None,
is_duplicate_confirmed: root_info.is_duplicate_confirmed,
};
self.fork_infos.insert(root_parent, root_parent_info);
self.root = root_parent;
}
pub fn add_new_leaf_slot(&mut self, slot: SlotHashKey, parent: Option<SlotHashKey>) {
pub fn add_new_leaf_slot(&mut self, slot_hash_key: SlotHashKey, parent: Option<SlotHashKey>) {
if self.last_root_time.elapsed().as_secs() > MAX_ROOT_PRINT_SECONDS {
self.print_state();
self.last_root_time = Instant::now();
}
if self.fork_infos.contains_key(&slot) {
if self.fork_infos.contains_key(&slot_hash_key) {
// Can potentially happen if we repair the same version of the duplicate slot, after
// dumping the original version
return;
}
let parent_latest_invalid_ancestor =
parent.and_then(|parent| self.latest_invalid_ancestor(&parent));
self.fork_infos
.entry(slot)
.and_modify(|slot_info| slot_info.parent = parent)
.entry(slot_hash_key)
.and_modify(|fork_info| fork_info.parent = parent)
.or_insert(ForkInfo {
stake_voted_at: 0,
stake_voted_subtree: 0,
// The `best_slot` of a leaf is itself
best_slot: slot,
best_slot: slot_hash_key,
children: vec![],
parent,
is_candidate: true,
latest_invalid_ancestor: parent_latest_invalid_ancestor,
// If the parent is none, then this is the root, which implies this must
// have reached the duplicate confirmed threshold
is_duplicate_confirmed: parent.is_none(),
});
if parent.is_none() {
@@ -294,11 +367,11 @@ impl HeaviestSubtreeForkChoice {
.get_mut(&parent)
.unwrap()
.children
.push(slot);
.push(slot_hash_key);
// Propagate leaf up the tree to any ancestors who considered the previous leaf
// the `best_slot`
self.propagate_new_leaf(&slot, &parent)
self.propagate_new_leaf(&slot_hash_key, &parent)
}
// Returns if the given `maybe_best_child` is the heaviest among the children
@@ -316,10 +389,7 @@ impl HeaviestSubtreeForkChoice {
.expect("child must exist in `self.fork_infos`");
// Don't count children currently marked as invalid
if !self
.is_candidate_slot(child)
.expect("child must exist in tree")
{
if !self.is_candidate(child).expect("child must exist in tree") {
continue;
}
@@ -378,6 +448,34 @@ impl HeaviestSubtreeForkChoice {
.map(|fork_info| fork_info.stake_voted_at)
}
pub fn latest_invalid_ancestor(&self, slot_hash_key: &SlotHashKey) -> Option<Slot> {
self.fork_infos
.get(slot_hash_key)
.map(|fork_info| fork_info.latest_invalid_ancestor)
.unwrap_or(None)
}
pub fn is_duplicate_confirmed(&self, slot_hash_key: &SlotHashKey) -> Option<bool> {
self.fork_infos
.get(slot_hash_key)
.map(|fork_info| fork_info.is_duplicate_confirmed())
}
/// Returns if the exact node with the specified key has been explicitly marked as a duplicate
/// slot (doesn't count ancestors being marked as duplicate).
pub fn is_unconfirmed_duplicate(&self, slot_hash_key: &SlotHashKey) -> Option<bool> {
self.fork_infos
.get(slot_hash_key)
.map(|fork_info| fork_info.is_unconfirmed_duplicate(slot_hash_key.0))
}
/// Returns false if the node or any of its ancestors have been marked as duplicate
pub fn is_candidate(&self, slot_hash_key: &SlotHashKey) -> Option<bool> {
self.fork_infos
.get(slot_hash_key)
.map(|fork_info| fork_info.is_candidate())
}
fn propagate_new_leaf(
&mut self,
slot_hash_key: &SlotHashKey,
@@ -406,45 +504,72 @@ impl HeaviestSubtreeForkChoice {
}
}
fn insert_mark_valid_aggregate_operations(
&self,
update_operations: &mut BTreeMap<(SlotHashKey, UpdateLabel), UpdateOperation>,
slot_hash_key: SlotHashKey,
) {
self.do_insert_aggregate_operations(update_operations, true, slot_hash_key);
}
fn insert_aggregate_operations(
&self,
update_operations: &mut BTreeMap<(SlotHashKey, UpdateLabel), UpdateOperation>,
slot_hash_key: SlotHashKey,
) {
self.do_insert_aggregate_operations(update_operations, false, slot_hash_key);
self.do_insert_aggregate_operations_across_ancestors(
update_operations,
None,
slot_hash_key,
);
}
#[allow(clippy::map_entry)]
fn do_insert_aggregate_operations(
fn do_insert_aggregate_operations_across_ancestors(
&self,
update_operations: &mut BTreeMap<(SlotHashKey, UpdateLabel), UpdateOperation>,
should_mark_valid: bool,
modify_fork_validity: Option<UpdateOperation>,
slot_hash_key: SlotHashKey,
) {
for parent_slot_hash_key in self.ancestor_iterator(slot_hash_key) {
let aggregate_label = (parent_slot_hash_key, UpdateLabel::Aggregate);
if update_operations.contains_key(&aggregate_label) {
if !self.do_insert_aggregate_operation(
update_operations,
&modify_fork_validity,
parent_slot_hash_key,
) {
// If this parent was already inserted, we assume all the other parents have also
// already been inserted. This is to prevent iterating over the parents multiple times
// when we are aggregating leaves that have a lot of shared ancestors
break;
} else {
if should_mark_valid {
update_operations.insert(
(parent_slot_hash_key, UpdateLabel::MarkValid),
UpdateOperation::MarkValid,
);
}
update_operations.insert(aggregate_label, UpdateOperation::Aggregate);
}
}
}
#[allow(clippy::map_entry)]
fn do_insert_aggregate_operation(
&self,
update_operations: &mut BTreeMap<(SlotHashKey, UpdateLabel), UpdateOperation>,
modify_fork_validity: &Option<UpdateOperation>,
slot_hash_key: SlotHashKey,
) -> bool {
let aggregate_label = (slot_hash_key, UpdateLabel::Aggregate);
if update_operations.contains_key(&aggregate_label) {
false
} else {
if let Some(mark_fork_validity) = modify_fork_validity {
match mark_fork_validity {
UpdateOperation::MarkValid(slot) => {
update_operations.insert(
(slot_hash_key, UpdateLabel::MarkValid(*slot)),
UpdateOperation::MarkValid(*slot),
);
}
UpdateOperation::MarkInvalid(slot) => {
update_operations.insert(
(slot_hash_key, UpdateLabel::MarkInvalid(*slot)),
UpdateOperation::MarkInvalid(*slot),
);
}
_ => (),
}
}
update_operations.insert(aggregate_label, UpdateOperation::Aggregate);
true
}
}
fn ancestor_iterator(&self, start_slot_hash_key: SlotHashKey) -> AncestorIterator {
AncestorIterator::new(start_slot_hash_key, &self.fork_infos)
}
@@ -452,12 +577,18 @@ impl HeaviestSubtreeForkChoice {
fn aggregate_slot(&mut self, slot_hash_key: SlotHashKey) {
let mut stake_voted_subtree;
let mut best_slot_hash_key = slot_hash_key;
let mut is_duplicate_confirmed = false;
if let Some(fork_info) = self.fork_infos.get(&slot_hash_key) {
stake_voted_subtree = fork_info.stake_voted_at;
let mut best_child_stake_voted_subtree = 0;
let mut best_child_slot = slot_hash_key;
for child in &fork_info.children {
let child_stake_voted_subtree = self.stake_voted_subtree(child).unwrap();
let mut best_child_slot_key = slot_hash_key;
for child_key in &fork_info.children {
let child_fork_info = self
.fork_infos
.get(child_key)
.expect("Child must exist in fork_info map");
let child_stake_voted_subtree = child_fork_info.stake_voted_subtree;
is_duplicate_confirmed |= child_fork_info.is_duplicate_confirmed;
// Child forks that are not candidates still contribute to the weight
// of the subtree rooted at `slot_hash_key`. For instance:
@@ -482,19 +613,15 @@ impl HeaviestSubtreeForkChoice {
// Note: If there's no valid children, then the best slot should default to the
// input `slot` itself.
if self
.is_candidate_slot(child)
.expect("Child must exist in fork_info map")
&& (best_child_slot == slot_hash_key ||
if child_fork_info.is_candidate()
&& (best_child_slot_key == slot_hash_key ||
child_stake_voted_subtree > best_child_stake_voted_subtree ||
// tiebreaker by slot height, prioritize earlier slot
(child_stake_voted_subtree == best_child_stake_voted_subtree && child < &best_child_slot))
(child_stake_voted_subtree == best_child_stake_voted_subtree && child_key < &best_child_slot_key))
{
best_child_stake_voted_subtree = child_stake_voted_subtree;
best_child_slot = *child;
best_slot_hash_key = self
.best_slot(child)
.expect("`child` must exist in `self.fork_infos`");
best_child_slot_key = *child_key;
best_slot_hash_key = child_fork_info.best_slot;
}
}
} else {
@@ -502,19 +629,38 @@ impl HeaviestSubtreeForkChoice {
}
let fork_info = self.fork_infos.get_mut(&slot_hash_key).unwrap();
if is_duplicate_confirmed {
if !fork_info.is_duplicate_confirmed {
info!(
"Fork choice setting {:?} to duplicate confirmed",
slot_hash_key
);
}
fork_info.set_duplicate_confirmed();
}
fork_info.stake_voted_subtree = stake_voted_subtree;
fork_info.best_slot = best_slot_hash_key;
}
fn mark_slot_valid(&mut self, valid_slot_hash_key: (Slot, Hash)) {
if let Some(fork_info) = self.fork_infos.get_mut(&valid_slot_hash_key) {
if !fork_info.is_candidate {
info!(
"marked previously invalid fork starting at slot: {:?} as valid",
valid_slot_hash_key
);
/// Mark that `valid_slot` on the fork starting at `fork_to_modify` has been marked
/// valid. Note we don't need the hash for `valid_slot` because slot number uniquely
/// identifies a node on a single fork.
fn mark_fork_valid(&mut self, fork_to_modify_key: SlotHashKey, valid_slot: Slot) {
if let Some(fork_info_to_modify) = self.fork_infos.get_mut(&fork_to_modify_key) {
fork_info_to_modify.update_with_newly_valid_ancestor(&fork_to_modify_key, valid_slot);
if fork_to_modify_key.0 == valid_slot {
fork_info_to_modify.is_duplicate_confirmed = true;
}
fork_info.is_candidate = true;
}
}
/// Mark that `invalid_slot` on the fork starting at `fork_to_modify` has been marked
/// invalid. Note we don't need the hash for `invalid_slot` because slot number uniquely
/// identifies a node on a single fork.
fn mark_fork_invalid(&mut self, fork_to_modify_key: SlotHashKey, invalid_slot: Slot) {
if let Some(fork_info_to_modify) = self.fork_infos.get_mut(&fork_to_modify_key) {
fork_info_to_modify
.update_with_newly_invalid_ancestor(&fork_to_modify_key, invalid_slot);
}
}
@@ -624,7 +770,7 @@ impl HeaviestSubtreeForkChoice {
let epoch = epoch_schedule.get_epoch(new_vote_slot_hash.0);
let stake_update = epoch_stakes
.get(&epoch)
.map(|epoch_stakes| epoch_stakes.vote_account_stake(&pubkey))
.map(|epoch_stakes| epoch_stakes.vote_account_stake(pubkey))
.unwrap_or(0);
update_operations
@@ -641,7 +787,12 @@ impl HeaviestSubtreeForkChoice {
// Iterate through the update operations from greatest to smallest slot
for ((slot_hash_key, _), operation) in update_operations.into_iter().rev() {
match operation {
UpdateOperation::MarkValid => self.mark_slot_valid(slot_hash_key),
UpdateOperation::MarkValid(valid_slot) => {
self.mark_fork_valid(slot_hash_key, valid_slot)
}
UpdateOperation::MarkInvalid(invalid_slot) => {
self.mark_fork_invalid(slot_hash_key, invalid_slot)
}
UpdateOperation::Aggregate => self.aggregate_slot(slot_hash_key),
UpdateOperation::Add(stake) => self.add_slot_stake(&slot_hash_key, stake),
UpdateOperation::Subtract(stake) => self.subtract_slot_stake(&slot_hash_key, stake),
@@ -745,7 +896,7 @@ impl TreeDiff for HeaviestSubtreeForkChoice {
fn children(&self, slot_hash_key: &SlotHashKey) -> Option<&[SlotHashKey]> {
self.fork_infos
.get(&slot_hash_key)
.get(slot_hash_key)
.map(|fork_info| &fork_info.children[..])
}
}
@@ -810,35 +961,48 @@ impl ForkChoice for HeaviestSubtreeForkChoice {
fn mark_fork_invalid_candidate(&mut self, invalid_slot_hash_key: &SlotHashKey) {
info!(
"marking fork starting at slot: {:?} invalid candidate",
"marking fork starting at: {:?} invalid candidate",
invalid_slot_hash_key
);
let fork_info = self.fork_infos.get_mut(invalid_slot_hash_key);
if let Some(fork_info) = fork_info {
if fork_info.is_candidate {
fork_info.is_candidate = false;
// Aggregate to find the new best slots excluding this fork
let mut update_operations = UpdateOperations::default();
self.insert_aggregate_operations(&mut update_operations, *invalid_slot_hash_key);
self.process_update_operations(update_operations);
// Should not be marking duplicate confirmed blocks as invalid candidates
assert!(!fork_info.is_duplicate_confirmed);
let mut update_operations = UpdateOperations::default();
// Notify all the children of this node that a parent was marked as invalid
for child_hash_key in self.subtree_diff(*invalid_slot_hash_key, SlotHashKey::default())
{
self.do_insert_aggregate_operation(
&mut update_operations,
&Some(UpdateOperation::MarkInvalid(invalid_slot_hash_key.0)),
child_hash_key,
);
}
// Aggregate across all ancestors to find the new best slots excluding this fork
self.insert_aggregate_operations(&mut update_operations, *invalid_slot_hash_key);
self.process_update_operations(update_operations);
}
}
fn mark_fork_valid_candidate(&mut self, valid_slot_hash_key: &SlotHashKey) {
info!(
"marking fork starting at: {:?} valid candidate",
valid_slot_hash_key
);
let mut update_operations = UpdateOperations::default();
let fork_info = self.fork_infos.get_mut(valid_slot_hash_key);
if let Some(fork_info) = fork_info {
// If a bunch of slots on the same fork are confirmed at once, then only the latest
// slot will incur this aggregation operation
fork_info.is_candidate = true;
self.insert_mark_valid_aggregate_operations(
// Notify all the children of this node that a parent was marked as valid
for child_hash_key in self.subtree_diff(*valid_slot_hash_key, SlotHashKey::default()) {
self.do_insert_aggregate_operation(
&mut update_operations,
*valid_slot_hash_key,
&Some(UpdateOperation::MarkValid(valid_slot_hash_key.0)),
child_hash_key,
);
}
// Aggregate to find the new best slots including this fork
// Aggregate across all ancestors to find the new best slots including this fork
self.insert_aggregate_operations(&mut update_operations, *valid_slot_hash_key);
self.process_update_operations(update_operations);
}
}
@@ -1333,7 +1497,7 @@ mod test {
.chain(std::iter::once(&duplicate_leaves_descended_from_4[1]))
{
assert!(heaviest_subtree_fork_choice
.children(&duplicate_leaf)
.children(duplicate_leaf)
.unwrap()
.is_empty(),);
}
@@ -2740,34 +2904,50 @@ mod test {
(expected_best_slot, Hash::default()),
);
// Mark slot 5 as invalid, the best fork should be its ancestor 3,
// not the other for at 4.
let invalid_candidate = (5, Hash::default());
// Simulate a vote on slot 5
let last_voted_slot_hash = (5, Hash::default());
let mut tower = Tower::new_for_tests(10, 0.9);
tower.record_vote(last_voted_slot_hash.0, last_voted_slot_hash.1);
// The heaviest_slot_on_same_voted_fork() should be 6, descended from 5.
assert_eq!(
heaviest_subtree_fork_choice
.heaviest_slot_on_same_voted_fork(&tower)
.unwrap(),
(6, Hash::default())
);
// Mark slot 5 as invalid
let invalid_candidate = last_voted_slot_hash;
heaviest_subtree_fork_choice.mark_fork_invalid_candidate(&invalid_candidate);
assert_eq!(heaviest_subtree_fork_choice.best_overall_slot().0, 3);
assert!(!heaviest_subtree_fork_choice
.is_candidate_slot(&invalid_candidate)
.is_candidate(&invalid_candidate)
.unwrap());
// The ancestor is still a candidate
// The ancestor 3 is still a candidate
assert!(heaviest_subtree_fork_choice
.is_candidate_slot(&(3, Hash::default()))
.is_candidate(&(3, Hash::default()))
.unwrap());
// The best fork should be its ancestor 3, not the other fork at 4.
assert_eq!(heaviest_subtree_fork_choice.best_overall_slot().0, 3);
// After marking the last vote in the tower as invalid, `heaviest_slot_on_same_voted_fork()`
// should disregard all descendants of that invalid vote
assert!(heaviest_subtree_fork_choice
.heaviest_slot_on_same_voted_fork(&tower)
.is_none());
// Adding another descendant to the invalid candidate won't
// update the best slot, even if it contains votes
let new_leaf_slot7 = 7;
heaviest_subtree_fork_choice.add_new_leaf_slot(
(new_leaf_slot7, Hash::default()),
Some((6, Hash::default())),
);
let new_leaf7 = (7, Hash::default());
heaviest_subtree_fork_choice.add_new_leaf_slot(new_leaf7, Some((6, Hash::default())));
let invalid_slot_ancestor = 3;
assert_eq!(
heaviest_subtree_fork_choice.best_overall_slot().0,
invalid_slot_ancestor
);
let pubkey_votes: Vec<(Pubkey, SlotHashKey)> =
vec![(vote_pubkeys[0], (new_leaf_slot7, Hash::default()))];
let pubkey_votes: Vec<(Pubkey, SlotHashKey)> = vec![(vote_pubkeys[0], new_leaf7)];
assert_eq!(
heaviest_subtree_fork_choice.add_votes(
pubkey_votes.iter(),
@@ -2777,34 +2957,51 @@ mod test {
(invalid_slot_ancestor, Hash::default()),
);
// This shouldn't update the `heaviest_slot_on_same_voted_fork` either
assert!(heaviest_subtree_fork_choice
.heaviest_slot_on_same_voted_fork(&tower)
.is_none());
// Adding a descendant to the ancestor of the invalid candidate *should* update
// the best slot though, since the ancestor is on the heaviest fork
let new_leaf_slot8 = 8;
heaviest_subtree_fork_choice.add_new_leaf_slot(
(new_leaf_slot8, Hash::default()),
Some((invalid_slot_ancestor, Hash::default())),
);
assert_eq!(
heaviest_subtree_fork_choice.best_overall_slot().0,
new_leaf_slot8,
);
let new_leaf8 = (8, Hash::default());
heaviest_subtree_fork_choice
.add_new_leaf_slot(new_leaf8, Some((invalid_slot_ancestor, Hash::default())));
assert_eq!(heaviest_subtree_fork_choice.best_overall_slot(), new_leaf8,);
// Should not update the `heaviest_slot_on_same_voted_fork` because the new leaf
// is not descended from the last vote
assert!(heaviest_subtree_fork_choice
.heaviest_slot_on_same_voted_fork(&tower)
.is_none());
// If we mark slot a descendant of `invalid_candidate` as valid, then that
// should also mark `invalid_candidate` as valid, and the best slot should
// be the leaf of the heaviest fork, `new_leaf_slot`.
heaviest_subtree_fork_choice.mark_fork_valid_candidate(&invalid_candidate);
assert!(heaviest_subtree_fork_choice
.is_candidate_slot(&invalid_candidate)
.is_candidate(&invalid_candidate)
.unwrap());
assert_eq!(
heaviest_subtree_fork_choice.best_overall_slot().0,
heaviest_subtree_fork_choice.best_overall_slot(),
// Should pick the smaller slot of the two new equally weighted leaves
new_leaf_slot7
new_leaf7
);
// Should update the `heaviest_slot_on_same_voted_fork` as well
assert_eq!(
heaviest_subtree_fork_choice
.heaviest_slot_on_same_voted_fork(&tower)
.unwrap(),
new_leaf7
);
}
#[test]
fn test_mark_valid_invalid_forks_duplicate() {
fn setup_mark_invalid_forks_duplicate_tests() -> (
HeaviestSubtreeForkChoice,
Vec<SlotHashKey>,
SlotHashKey,
Bank,
Vec<Pubkey>,
) {
let (
mut heaviest_subtree_fork_choice,
duplicate_leaves_descended_from_4,
@@ -2832,11 +3029,27 @@ mod test {
// the other branch at slot 5
let invalid_candidate = (4, Hash::default());
heaviest_subtree_fork_choice.mark_fork_invalid_candidate(&invalid_candidate);
assert_eq!(
heaviest_subtree_fork_choice.best_overall_slot(),
(2, Hash::default())
);
(
heaviest_subtree_fork_choice,
duplicate_leaves_descended_from_4,
invalid_candidate,
bank,
vote_pubkeys,
)
}
#[test]
fn test_mark_invalid_then_valid_duplicate() {
let (
mut heaviest_subtree_fork_choice,
duplicate_leaves_descended_from_4,
invalid_candidate,
..,
) = setup_mark_invalid_forks_duplicate_tests();
// Marking candidate as valid again will choose the the heaviest leaf of
// the newly valid branch
@@ -2851,16 +3064,26 @@ mod test {
heaviest_subtree_fork_choice.best_overall_slot(),
duplicate_descendant
);
}
// Mark the current heaviest branch as invalid again
heaviest_subtree_fork_choice.mark_fork_invalid_candidate(&invalid_candidate);
#[test]
fn test_mark_invalid_then_add_new_heavier_duplicate_slot() {
let (
mut heaviest_subtree_fork_choice,
duplicate_leaves_descended_from_4,
_invalid_candidate,
bank,
vote_pubkeys,
) = setup_mark_invalid_forks_duplicate_tests();
// If we add a new version of the duplicate slot that is not descended from the invalid
// candidate and votes for that duplicate slot, the new duplicate slot should be picked
// once it has more weight
let new_duplicate_hash = Hash::default();
// The hash has to be smaller in order for the votes to be counted
assert!(new_duplicate_hash < duplicate_leaves_descended_from_4[0].1);
let duplicate_slot = duplicate_leaves_descended_from_4[0].0;
let new_duplicate = (duplicate_slot, new_duplicate_hash);
heaviest_subtree_fork_choice.add_new_leaf_slot(new_duplicate, Some((3, Hash::default())));
@@ -2881,6 +3104,285 @@ mod test {
);
}
#[test]
fn test_mark_valid_then_descendant_invalid() {
let forks = tr(0) / (tr(1) / (tr(2) / (tr(3) / (tr(4) / (tr(5) / tr(6))))));
let mut heaviest_subtree_fork_choice = HeaviestSubtreeForkChoice::new_from_tree(forks);
let duplicate_confirmed_slot: Slot = 1;
let duplicate_confirmed_key = duplicate_confirmed_slot.slot_hash();
heaviest_subtree_fork_choice.mark_fork_valid_candidate(&duplicate_confirmed_key);
for slot_hash_key in heaviest_subtree_fork_choice.fork_infos.keys() {
let slot = slot_hash_key.0;
if slot <= duplicate_confirmed_slot {
assert!(heaviest_subtree_fork_choice
.is_duplicate_confirmed(slot_hash_key)
.unwrap());
} else {
assert!(!heaviest_subtree_fork_choice
.is_duplicate_confirmed(slot_hash_key)
.unwrap());
}
assert!(heaviest_subtree_fork_choice
.latest_invalid_ancestor(slot_hash_key)
.is_none());
}
// Mark a later descendant invalid
let invalid_descendant_slot = 5;
let invalid_descendant_key = invalid_descendant_slot.slot_hash();
heaviest_subtree_fork_choice.mark_fork_invalid_candidate(&invalid_descendant_key);
for slot_hash_key in heaviest_subtree_fork_choice.fork_infos.keys() {
let slot = slot_hash_key.0;
if slot <= duplicate_confirmed_slot {
// All ancestors of the duplicate confirmed slot should:
// 1) Be duplicate confirmed
// 2) Have no invalid ancestors
assert!(heaviest_subtree_fork_choice
.is_duplicate_confirmed(slot_hash_key)
.unwrap());
assert!(heaviest_subtree_fork_choice
.latest_invalid_ancestor(slot_hash_key)
.is_none());
} else if slot >= invalid_descendant_slot {
// Anything descended from the invalid slot should:
// 1) Not be duplicate confirmed
// 2) Should have an invalid ancestor == `invalid_descendant_slot`
assert!(!heaviest_subtree_fork_choice
.is_duplicate_confirmed(slot_hash_key)
.unwrap());
assert_eq!(
heaviest_subtree_fork_choice
.latest_invalid_ancestor(slot_hash_key)
.unwrap(),
invalid_descendant_slot
);
} else {
// Anything in between the duplicate confirmed slot and the invalid slot should:
// 1) Not be duplicate confirmed
// 2) Should not have an invalid ancestor
assert!(!heaviest_subtree_fork_choice
.is_duplicate_confirmed(slot_hash_key)
.unwrap());
assert!(heaviest_subtree_fork_choice
.latest_invalid_ancestor(slot_hash_key)
.is_none());
}
}
// Mark later descendant `d` duplicate confirmed where `duplicate_confirmed_slot < d < invalid_descendant_slot`.
let later_duplicate_confirmed_slot = 4;
assert!(
later_duplicate_confirmed_slot > duplicate_confirmed_slot
&& later_duplicate_confirmed_slot < invalid_descendant_slot
);
let later_duplicate_confirmed_key = later_duplicate_confirmed_slot.slot_hash();
heaviest_subtree_fork_choice.mark_fork_valid_candidate(&later_duplicate_confirmed_key);
for slot_hash_key in heaviest_subtree_fork_choice.fork_infos.keys() {
let slot = slot_hash_key.0;
if slot <= later_duplicate_confirmed_slot {
// All ancestors of the later_duplicate_confirmed_slot should:
// 1) Be duplicate confirmed
// 2) Have no invalid ancestors
assert!(heaviest_subtree_fork_choice
.is_duplicate_confirmed(slot_hash_key)
.unwrap());
assert!(heaviest_subtree_fork_choice
.latest_invalid_ancestor(slot_hash_key)
.is_none());
} else if slot >= invalid_descendant_slot {
// Anything descended from the invalid slot should:
// 1) Not be duplicate confirmed
// 2) Should have an invalid ancestor == `invalid_descendant_slot`
assert!(!heaviest_subtree_fork_choice
.is_duplicate_confirmed(slot_hash_key)
.unwrap());
assert_eq!(
heaviest_subtree_fork_choice
.latest_invalid_ancestor(slot_hash_key)
.unwrap(),
invalid_descendant_slot
);
} else {
// Anything in between the duplicate confirmed slot and the invalid slot should:
// 1) Not be duplicate confirmed
// 2) Should not have an invalid ancestor
assert!(!heaviest_subtree_fork_choice
.is_duplicate_confirmed(slot_hash_key)
.unwrap());
assert!(heaviest_subtree_fork_choice
.latest_invalid_ancestor(slot_hash_key)
.is_none());
}
}
// Mark all slots duplicate confirmed
let last_duplicate_confirmed_slot = 6;
let last_duplicate_confirmed_key = last_duplicate_confirmed_slot.slot_hash();
heaviest_subtree_fork_choice.mark_fork_valid_candidate(&last_duplicate_confirmed_key);
for slot_hash_key in heaviest_subtree_fork_choice.fork_infos.keys() {
assert!(heaviest_subtree_fork_choice
.is_duplicate_confirmed(slot_hash_key)
.unwrap());
assert!(heaviest_subtree_fork_choice
.latest_invalid_ancestor(slot_hash_key)
.is_none());
}
}
#[test]
#[should_panic]
fn test_mark_valid_then_ancestor_invalid() {
let forks = tr(0) / (tr(1) / (tr(2) / (tr(3) / (tr(4) / (tr(5) / tr(6))))));
let mut heaviest_subtree_fork_choice = HeaviestSubtreeForkChoice::new_from_tree(forks);
let duplicate_confirmed_slot: Slot = 4;
let duplicate_confirmed_key = duplicate_confirmed_slot.slot_hash();
heaviest_subtree_fork_choice.mark_fork_valid_candidate(&duplicate_confirmed_key);
// Now mark an ancestor of this fork invalid, should panic since this ancestor
// was duplicate confirmed by its descendant 4 already
heaviest_subtree_fork_choice.mark_fork_invalid_candidate(&3.slot_hash());
}
fn setup_set_unconfirmed_and_confirmed_duplicate_slot_tests(
smaller_duplicate_slot: Slot,
larger_duplicate_slot: Slot,
) -> HeaviestSubtreeForkChoice {
// Create simple fork 0 -> 1 -> 2 -> 3 -> 4 -> 5
let forks = tr(0) / (tr(1) / (tr(2) / (tr(3) / (tr(4) / tr(5)))));
let mut heaviest_subtree_fork_choice = HeaviestSubtreeForkChoice::new_from_tree(forks);
// Mark the slots as unconfirmed duplicates
heaviest_subtree_fork_choice
.mark_fork_invalid_candidate(&smaller_duplicate_slot.slot_hash());
heaviest_subtree_fork_choice
.mark_fork_invalid_candidate(&larger_duplicate_slot.slot_hash());
// Correctness checks
for slot_hash_key in heaviest_subtree_fork_choice.fork_infos.keys() {
let slot = slot_hash_key.0;
if slot < smaller_duplicate_slot {
assert!(heaviest_subtree_fork_choice
.latest_invalid_ancestor(slot_hash_key)
.is_none());
} else if slot < larger_duplicate_slot {
assert_eq!(
heaviest_subtree_fork_choice
.latest_invalid_ancestor(slot_hash_key)
.unwrap(),
smaller_duplicate_slot
);
} else {
assert_eq!(
heaviest_subtree_fork_choice
.latest_invalid_ancestor(slot_hash_key)
.unwrap(),
larger_duplicate_slot
);
}
}
heaviest_subtree_fork_choice
}
#[test]
fn test_set_unconfirmed_duplicate_confirm_smaller_slot_first() {
let smaller_duplicate_slot = 1;
let larger_duplicate_slot = 4;
let mut heaviest_subtree_fork_choice =
setup_set_unconfirmed_and_confirmed_duplicate_slot_tests(
smaller_duplicate_slot,
larger_duplicate_slot,
);
// Mark the smaller duplicate slot as confirmed
heaviest_subtree_fork_choice.mark_fork_valid_candidate(&smaller_duplicate_slot.slot_hash());
for slot_hash_key in heaviest_subtree_fork_choice.fork_infos.keys() {
let slot = slot_hash_key.0;
if slot < larger_duplicate_slot {
// Only slots <= smaller_duplicate_slot have been duplicate confirmed
if slot <= smaller_duplicate_slot {
assert!(heaviest_subtree_fork_choice
.is_duplicate_confirmed(slot_hash_key)
.unwrap());
} else {
assert!(!heaviest_subtree_fork_choice
.is_duplicate_confirmed(slot_hash_key)
.unwrap());
}
// The unconfirmed duplicate flag has been cleared on the smaller
// descendants because their most recent duplicate ancestor has
// been confirmed
assert!(heaviest_subtree_fork_choice
.latest_invalid_ancestor(slot_hash_key)
.is_none());
} else {
assert!(!heaviest_subtree_fork_choice
.is_duplicate_confirmed(slot_hash_key)
.unwrap(),);
// The unconfirmed duplicate flag has not been cleared on the smaller
// descendants because their most recent duplicate ancestor,
// `larger_duplicate_slot` has not yet been confirmed
assert_eq!(
heaviest_subtree_fork_choice
.latest_invalid_ancestor(slot_hash_key)
.unwrap(),
larger_duplicate_slot
);
}
}
// Mark the larger duplicate slot as confirmed, all slots should no longer
// have any unconfirmed duplicate ancestors, and should be marked as duplciate confirmed
heaviest_subtree_fork_choice.mark_fork_valid_candidate(&larger_duplicate_slot.slot_hash());
for slot_hash_key in heaviest_subtree_fork_choice.fork_infos.keys() {
let slot = slot_hash_key.0;
// All slots <= the latest duplciate confirmed slot are ancestors of
// that slot, so they should all be marked duplicate confirmed
assert_eq!(
heaviest_subtree_fork_choice
.is_duplicate_confirmed(slot_hash_key)
.unwrap(),
slot <= larger_duplicate_slot
);
assert!(heaviest_subtree_fork_choice
.latest_invalid_ancestor(slot_hash_key)
.is_none());
}
}
#[test]
fn test_set_unconfirmed_duplicate_confirm_larger_slot_first() {
let smaller_duplicate_slot = 1;
let larger_duplicate_slot = 4;
let mut heaviest_subtree_fork_choice =
setup_set_unconfirmed_and_confirmed_duplicate_slot_tests(
smaller_duplicate_slot,
larger_duplicate_slot,
);
// Mark the larger duplicate slot as confirmed
heaviest_subtree_fork_choice.mark_fork_valid_candidate(&larger_duplicate_slot.slot_hash());
// All slots should no longer have any unconfirmed duplicate ancestors
heaviest_subtree_fork_choice.mark_fork_valid_candidate(&smaller_duplicate_slot.slot_hash());
for slot_hash_key in heaviest_subtree_fork_choice.fork_infos.keys() {
let slot = slot_hash_key.0;
// All slots <= the latest duplciate confirmed slot are ancestors of
// that slot, so they should all be marked duplicate confirmed
assert_eq!(
heaviest_subtree_fork_choice
.is_duplicate_confirmed(slot_hash_key)
.unwrap(),
slot <= larger_duplicate_slot
);
assert!(heaviest_subtree_fork_choice
.latest_invalid_ancestor(slot_hash_key)
.is_none());
}
}
fn setup_forks() -> HeaviestSubtreeForkChoice {
/*
Build fork structure:

View File

@@ -187,7 +187,7 @@ impl LedgerCleanupService {
*last_purge_slot = root;
let (slots_to_clean, purge_first_slot, lowest_cleanup_slot, total_shreds) =
Self::find_slots_to_clean(&blockstore, root, max_ledger_shreds);
Self::find_slots_to_clean(blockstore, root, max_ledger_shreds);
if slots_to_clean {
let purge_complete = Arc::new(AtomicBool::new(false));
@@ -207,11 +207,25 @@ impl LedgerCleanupService {
);
let mut purge_time = Measure::start("purge_slots");
blockstore.purge_slots(
purge_first_slot,
lowest_cleanup_slot,
PurgeType::PrimaryIndex,
PurgeType::CompactionFilter,
);
// Update only after purge operation.
// Safety: This value can be used by compaction_filters shared via Arc<AtomicU64>.
// Compactions are async and run as a multi-threaded background job. However, this
// shouldn't cause consistency issues for iterators and getters because we have
// already expired all affected keys (older than or equal to lowest_cleanup_slot)
// by the above `purge_slots`. According to the general RocksDB design where SST
// files are immutable, even running iterators aren't affected; the database grabs
// a snapshot of the live set of sst files at iterator's creation.
// Also, we passed the PurgeType::CompactionFilter, meaning no delete_range for
// transaction_status and address_signatures CFs. These are fine because they
// don't require strong consistent view for their operation.
blockstore.set_max_expired_slot(lowest_cleanup_slot);
purge_time.stop();
info!("{}", purge_time);

View File

@@ -9,10 +9,10 @@
pub mod accounts_hash_verifier;
pub mod banking_stage;
pub mod bigtable_upload_service;
pub mod broadcast_stage;
pub mod cache_block_meta_service;
pub mod cluster_info_vote_listener;
pub mod cluster_nodes;
pub mod cluster_slot_state_verifier;
pub mod cluster_slots;
pub mod cluster_slots_service;
@@ -28,8 +28,6 @@ pub mod ledger_cleanup_service;
pub mod optimistic_confirmation_verifier;
pub mod outstanding_requests;
pub mod packet_hasher;
pub mod poh_recorder;
pub mod poh_service;
pub mod progress_map;
pub mod repair_response;
pub mod repair_service;
@@ -40,11 +38,7 @@ pub mod request_response;
mod result;
pub mod retransmit_stage;
pub mod rewards_recorder_service;
pub mod rpc;
pub mod rpc_health;
pub mod rpc_service;
pub mod sample_performance_service;
pub mod send_transaction_service;
pub mod serve_repair;
pub mod serve_repair_service;
pub mod shred_fetch_stage;
@@ -54,7 +48,6 @@ pub mod sigverify_stage;
pub mod snapshot_packager_service;
pub mod test_validator;
pub mod tpu;
pub mod transaction_status_service;
pub mod tree_diff;
pub mod tvu;
pub mod unfrozen_gossip_verified_vote_hashes;
@@ -69,10 +62,6 @@ extern crate log;
#[macro_use]
extern crate serde_derive;
#[cfg(test)]
#[macro_use]
extern crate serde_json;
#[macro_use]
extern crate solana_metrics;

View File

@@ -36,7 +36,7 @@ impl OptimisticConfirmationVerifier {
.into_iter()
.filter(|(optimistic_slot, optimistic_hash)| {
(*optimistic_slot == root && *optimistic_hash != root_bank.hash())
|| (!root_ancestors.contains_key(&optimistic_slot) &&
|| (!root_ancestors.contains_key(optimistic_slot) &&
// In this second part of the `and`, we account for the possibility that
// there was some other root `rootX` set in BankForks where:
//
@@ -316,7 +316,7 @@ mod test {
assert!(optimistic_confirmation_verifier.unchecked_slots.is_empty());
// If we know set the root in blockstore, should return nothing
blockstore.set_roots(&[1, 3]).unwrap();
blockstore.set_roots(vec![1, 3].iter()).unwrap();
optimistic_confirmation_verifier.add_new_optimistic_confirmed_slots(optimistic_slots);
assert!(optimistic_confirmation_verifier
.verify_for_unrooted_optimistic_slots(&bank7, &blockstore)

View File

@@ -63,6 +63,16 @@ impl ReplaySlotStats {
("load_us", self.execute_timings.load_us, i64),
("execute_us", self.execute_timings.execute_us, i64),
("store_us", self.execute_timings.store_us, i64),
(
"total_batches_len",
self.execute_timings.total_batches_len,
i64
),
(
"num_execute_batches",
self.execute_timings.num_execute_batches,
i64
),
(
"serialize_us",
self.execute_timings.details.serialize_us,
@@ -140,7 +150,6 @@ pub(crate) struct ForkProgress {
pub(crate) propagated_stats: PropagatedStats,
pub(crate) replay_stats: ReplaySlotStats,
pub(crate) replay_progress: ConfirmationProgress,
pub(crate) duplicate_stats: DuplicateStats,
// Note `num_blocks_on_fork` and `num_dropped_blocks_on_fork` only
// count new blocks replayed since last restart, which won't include
// blocks already existing in the ledger/before snapshot at start,
@@ -153,7 +162,6 @@ impl ForkProgress {
pub fn new(
last_entry: Hash,
prev_leader_slot: Option<Slot>,
duplicate_stats: DuplicateStats,
validator_stake_info: Option<ValidatorStakeInfo>,
num_blocks_on_fork: u64,
num_dropped_blocks_on_fork: u64,
@@ -187,7 +195,6 @@ impl ForkProgress {
fork_stats: ForkStats::default(),
replay_stats: ReplaySlotStats::default(),
replay_progress: ConfirmationProgress::new(last_entry),
duplicate_stats,
num_blocks_on_fork,
num_dropped_blocks_on_fork,
propagated_stats: PropagatedStats {
@@ -207,16 +214,14 @@ impl ForkProgress {
my_pubkey: &Pubkey,
voting_pubkey: &Pubkey,
prev_leader_slot: Option<Slot>,
duplicate_stats: DuplicateStats,
num_blocks_on_fork: u64,
num_dropped_blocks_on_fork: u64,
) -> Self {
let validator_fork_info = {
let validator_stake_info = {
if bank.collector_id() == my_pubkey {
let stake = bank.epoch_vote_account_stake(voting_pubkey);
Some(ValidatorStakeInfo::new(
*voting_pubkey,
stake,
bank.epoch_vote_account_stake(voting_pubkey),
bank.total_epoch_stake(),
))
} else {
@@ -227,20 +232,11 @@ impl ForkProgress {
Self::new(
bank.last_blockhash(),
prev_leader_slot,
duplicate_stats,
validator_fork_info,
validator_stake_info,
num_blocks_on_fork,
num_dropped_blocks_on_fork,
)
}
pub fn is_duplicate_confirmed(&self) -> bool {
self.duplicate_stats.is_duplicate_confirmed
}
pub fn set_duplicate_confirmed(&mut self) {
self.duplicate_stats.set_duplicate_confirmed();
}
}
#[derive(Debug, Clone, Default)]
@@ -275,38 +271,6 @@ pub(crate) struct PropagatedStats {
pub(crate) total_epoch_stake: u64,
}
#[derive(Clone, Default)]
pub(crate) struct DuplicateStats {
latest_unconfirmed_duplicate_ancestor: Option<Slot>,
is_duplicate_confirmed: bool,
}
impl DuplicateStats {
pub fn new_with_unconfirmed_duplicate_ancestor(
latest_unconfirmed_duplicate_ancestor: Option<Slot>,
) -> Self {
Self {
latest_unconfirmed_duplicate_ancestor,
is_duplicate_confirmed: false,
}
}
fn set_duplicate_confirmed(&mut self) {
self.is_duplicate_confirmed = true;
self.latest_unconfirmed_duplicate_ancestor = None;
}
fn update_with_newly_confirmed_duplicate_ancestor(&mut self, newly_confirmed_ancestor: Slot) {
if let Some(latest_unconfirmed_duplicate_ancestor) =
self.latest_unconfirmed_duplicate_ancestor
{
if latest_unconfirmed_duplicate_ancestor <= newly_confirmed_ancestor {
self.latest_unconfirmed_duplicate_ancestor = None;
}
}
}
}
impl PropagatedStats {
pub fn add_vote_pubkey(&mut self, vote_pubkey: Pubkey, stake: u64) {
if self.propagated_validators.insert(vote_pubkey) {
@@ -317,7 +281,7 @@ impl PropagatedStats {
pub fn add_node_pubkey(&mut self, node_pubkey: &Pubkey, bank: &Bank) {
if !self.propagated_node_ids.contains(node_pubkey) {
let node_vote_accounts = bank
.epoch_vote_accounts_for_node_id(&node_pubkey)
.epoch_vote_accounts_for_node_id(node_pubkey)
.map(|v| &v.vote_accounts);
if let Some(node_vote_accounts) = node_vote_accounts {
@@ -438,101 +402,6 @@ impl ProgressMap {
}
}
pub fn is_unconfirmed_duplicate(&self, slot: Slot) -> Option<bool> {
self.get(&slot).map(|p| {
p.duplicate_stats
.latest_unconfirmed_duplicate_ancestor
.map(|ancestor| ancestor == slot)
.unwrap_or(false)
})
}
pub fn latest_unconfirmed_duplicate_ancestor(&self, slot: Slot) -> Option<Slot> {
self.get(&slot)
.map(|p| p.duplicate_stats.latest_unconfirmed_duplicate_ancestor)
.unwrap_or(None)
}
pub fn set_unconfirmed_duplicate_slot(&mut self, slot: Slot, descendants: &HashSet<u64>) {
if let Some(fork_progress) = self.get_mut(&slot) {
if fork_progress.is_duplicate_confirmed() {
assert!(fork_progress
.duplicate_stats
.latest_unconfirmed_duplicate_ancestor
.is_none());
return;
}
if fork_progress
.duplicate_stats
.latest_unconfirmed_duplicate_ancestor
== Some(slot)
{
// Already been marked
return;
}
fork_progress
.duplicate_stats
.latest_unconfirmed_duplicate_ancestor = Some(slot);
for d in descendants {
if let Some(fork_progress) = self.get_mut(&d) {
fork_progress
.duplicate_stats
.latest_unconfirmed_duplicate_ancestor = Some(std::cmp::max(
fork_progress
.duplicate_stats
.latest_unconfirmed_duplicate_ancestor
.unwrap_or(0),
slot,
));
}
}
}
}
pub fn set_confirmed_duplicate_slot(
&mut self,
slot: Slot,
ancestors: &HashSet<u64>,
descendants: &HashSet<u64>,
) {
for a in ancestors {
if let Some(fork_progress) = self.get_mut(&a) {
fork_progress.set_duplicate_confirmed();
}
}
if let Some(slot_fork_progress) = self.get_mut(&slot) {
// Setting the fields here is nly correct and necessary if the loop above didn't
// already do this, so check with an assert.
assert!(!ancestors.contains(&slot));
let slot_had_unconfirmed_duplicate_ancestor = slot_fork_progress
.duplicate_stats
.latest_unconfirmed_duplicate_ancestor
.is_some();
slot_fork_progress.set_duplicate_confirmed();
if slot_had_unconfirmed_duplicate_ancestor {
for d in descendants {
if let Some(descendant_fork_progress) = self.get_mut(&d) {
descendant_fork_progress
.duplicate_stats
.update_with_newly_confirmed_duplicate_ancestor(slot);
}
}
} else {
// Neither this slot `S`, nor earlier ancestors were marked as duplicate,
// so this means all descendants either:
// 1) Have no duplicate ancestors
// 2) Have a duplicate ancestor > `S`
// In both cases, there's no need to iterate through descendants because
// this confirmation on `S` is irrelevant to them.
}
}
}
pub fn my_latest_landed_vote(&self, slot: Slot) -> Option<Slot> {
self.progress_map
.get(&slot)
@@ -550,12 +419,6 @@ impl ProgressMap {
.map(|s| s.fork_stats.is_supermajority_confirmed)
}
pub fn is_duplicate_confirmed(&self, slot: Slot) -> Option<bool> {
self.progress_map
.get(&slot)
.map(|s| s.is_duplicate_confirmed())
}
pub fn get_bank_prev_leader_slot(&self, bank: &Bank) -> Option<Slot> {
let parent_slot = bank.parent_slot();
self.get_propagated_stats(parent_slot)
@@ -598,8 +461,6 @@ impl ProgressMap {
#[cfg(test)]
mod test {
use super::*;
use crate::consensus::test::VoteSimulator;
use trees::tr;
#[test]
fn test_add_vote_pubkey() {
@@ -690,21 +551,13 @@ mod test {
fn test_is_propagated_status_on_construction() {
// If the given ValidatorStakeInfo == None, then this is not
// a leader slot and is_propagated == false
let progress = ForkProgress::new(
Hash::default(),
Some(9),
DuplicateStats::default(),
None,
0,
0,
);
let progress = ForkProgress::new(Hash::default(), Some(9), None, 0, 0);
assert!(!progress.propagated_stats.is_propagated);
// If the stake is zero, then threshold is always achieved
let progress = ForkProgress::new(
Hash::default(),
Some(9),
DuplicateStats::default(),
Some(ValidatorStakeInfo {
total_epoch_stake: 0,
..ValidatorStakeInfo::default()
@@ -719,7 +572,6 @@ mod test {
let progress = ForkProgress::new(
Hash::default(),
Some(9),
DuplicateStats::default(),
Some(ValidatorStakeInfo {
total_epoch_stake: 2,
..ValidatorStakeInfo::default()
@@ -733,7 +585,6 @@ mod test {
let progress = ForkProgress::new(
Hash::default(),
Some(9),
DuplicateStats::default(),
Some(ValidatorStakeInfo {
stake: 1,
total_epoch_stake: 2,
@@ -750,7 +601,6 @@ mod test {
let progress = ForkProgress::new(
Hash::default(),
Some(9),
DuplicateStats::default(),
Some(ValidatorStakeInfo::default()),
0,
0,
@@ -764,23 +614,12 @@ mod test {
// Insert new ForkProgress for slot 10 (not a leader slot) and its
// previous leader slot 9 (leader slot)
progress_map.insert(
10,
ForkProgress::new(
Hash::default(),
Some(9),
DuplicateStats::default(),
None,
0,
0,
),
);
progress_map.insert(10, ForkProgress::new(Hash::default(), Some(9), None, 0, 0));
progress_map.insert(
9,
ForkProgress::new(
Hash::default(),
None,
DuplicateStats::default(),
Some(ValidatorStakeInfo::default()),
0,
0,
@@ -795,17 +634,7 @@ mod test {
// The previous leader before 8, slot 7, does not exist in
// progress map, so is_propagated(8) should return true as
// this implies the parent is rooted
progress_map.insert(
8,
ForkProgress::new(
Hash::default(),
Some(7),
DuplicateStats::default(),
None,
0,
0,
),
);
progress_map.insert(8, ForkProgress::new(Hash::default(), Some(7), None, 0, 0));
assert!(progress_map.is_propagated(8));
// If we set the is_propagated = true, is_propagated should return true
@@ -828,157 +657,4 @@ mod test {
.is_leader_slot = true;
assert!(!progress_map.is_propagated(10));
}
fn setup_set_unconfirmed_and_confirmed_duplicate_slot_tests(
smaller_duplicate_slot: Slot,
larger_duplicate_slot: Slot,
) -> (ProgressMap, RwLock<BankForks>) {
// Create simple fork 0 -> 1 -> 2 -> 3 -> 4 -> 5
let forks = tr(0) / (tr(1) / (tr(2) / (tr(3) / (tr(4) / tr(5)))));
let mut vote_simulator = VoteSimulator::new(1);
vote_simulator.fill_bank_forks(forks, &HashMap::new());
let VoteSimulator {
mut progress,
bank_forks,
..
} = vote_simulator;
let descendants = bank_forks.read().unwrap().descendants().clone();
// Mark the slots as unconfirmed duplicates
progress.set_unconfirmed_duplicate_slot(
smaller_duplicate_slot,
&descendants.get(&smaller_duplicate_slot).unwrap(),
);
progress.set_unconfirmed_duplicate_slot(
larger_duplicate_slot,
&descendants.get(&larger_duplicate_slot).unwrap(),
);
// Correctness checks
for slot in bank_forks.read().unwrap().banks().keys() {
if *slot < smaller_duplicate_slot {
assert!(progress
.latest_unconfirmed_duplicate_ancestor(*slot)
.is_none());
} else if *slot < larger_duplicate_slot {
assert_eq!(
progress
.latest_unconfirmed_duplicate_ancestor(*slot)
.unwrap(),
smaller_duplicate_slot
);
} else {
assert_eq!(
progress
.latest_unconfirmed_duplicate_ancestor(*slot)
.unwrap(),
larger_duplicate_slot
);
}
}
(progress, bank_forks)
}
#[test]
fn test_set_unconfirmed_duplicate_confirm_smaller_slot_first() {
let smaller_duplicate_slot = 1;
let larger_duplicate_slot = 4;
let (mut progress, bank_forks) = setup_set_unconfirmed_and_confirmed_duplicate_slot_tests(
smaller_duplicate_slot,
larger_duplicate_slot,
);
let descendants = bank_forks.read().unwrap().descendants().clone();
let ancestors = bank_forks.read().unwrap().ancestors();
// Mark the smaller duplicate slot as confirmed
progress.set_confirmed_duplicate_slot(
smaller_duplicate_slot,
&ancestors.get(&smaller_duplicate_slot).unwrap(),
&descendants.get(&smaller_duplicate_slot).unwrap(),
);
for slot in bank_forks.read().unwrap().banks().keys() {
if *slot < larger_duplicate_slot {
// Only slots <= smaller_duplicate_slot have been duplicate confirmed
if *slot <= smaller_duplicate_slot {
assert!(progress.is_duplicate_confirmed(*slot).unwrap());
} else {
assert!(!progress.is_duplicate_confirmed(*slot).unwrap());
}
// The unconfirmed duplicate flag has been cleared on the smaller
// descendants because their most recent duplicate ancestor has
// been confirmed
assert!(progress
.latest_unconfirmed_duplicate_ancestor(*slot)
.is_none());
} else {
assert!(!progress.is_duplicate_confirmed(*slot).unwrap(),);
// The unconfirmed duplicate flag has not been cleared on the smaller
// descendants because their most recent duplicate ancestor,
// `larger_duplicate_slot` has not yet been confirmed
assert_eq!(
progress
.latest_unconfirmed_duplicate_ancestor(*slot)
.unwrap(),
larger_duplicate_slot
);
}
}
// Mark the larger duplicate slot as confirmed, all slots should no longer
// have any unconfirmed duplicate ancestors, and should be marked as duplciate confirmed
progress.set_confirmed_duplicate_slot(
larger_duplicate_slot,
&ancestors.get(&larger_duplicate_slot).unwrap(),
&descendants.get(&larger_duplicate_slot).unwrap(),
);
for slot in bank_forks.read().unwrap().banks().keys() {
// All slots <= the latest duplciate confirmed slot are ancestors of
// that slot, so they should all be marked duplicate confirmed
assert_eq!(
progress.is_duplicate_confirmed(*slot).unwrap(),
*slot <= larger_duplicate_slot
);
assert!(progress
.latest_unconfirmed_duplicate_ancestor(*slot)
.is_none());
}
}
#[test]
fn test_set_unconfirmed_duplicate_confirm_larger_slot_first() {
let smaller_duplicate_slot = 1;
let larger_duplicate_slot = 4;
let (mut progress, bank_forks) = setup_set_unconfirmed_and_confirmed_duplicate_slot_tests(
smaller_duplicate_slot,
larger_duplicate_slot,
);
let descendants = bank_forks.read().unwrap().descendants().clone();
let ancestors = bank_forks.read().unwrap().ancestors();
// Mark the larger duplicate slot as confirmed
progress.set_confirmed_duplicate_slot(
larger_duplicate_slot,
&ancestors.get(&larger_duplicate_slot).unwrap(),
&descendants.get(&larger_duplicate_slot).unwrap(),
);
// All slots should no longer have any unconfirmed duplicate ancestors
progress.set_confirmed_duplicate_slot(
larger_duplicate_slot,
&ancestors.get(&larger_duplicate_slot).unwrap(),
&descendants.get(&larger_duplicate_slot).unwrap(),
);
for slot in bank_forks.read().unwrap().banks().keys() {
// All slots <= the latest duplciate confirmed slot are ancestors of
// that slot, so they should all be marked duplicate confirmed
assert_eq!(
progress.is_duplicate_confirmed(*slot).unwrap(),
*slot <= larger_duplicate_slot
);
assert!(progress
.latest_unconfirmed_duplicate_ancestor(*slot)
.is_none());
}
}
}

View File

@@ -5,20 +5,25 @@ use crate::{
cluster_slots::ClusterSlots,
outstanding_requests::OutstandingRequests,
repair_weight::RepairWeight,
replay_stage::DUPLICATE_THRESHOLD,
result::Result,
serve_repair::{RepairType, ServeRepair},
serve_repair::{RepairType, ServeRepair, REPAIR_PEERS_CACHE_CAPACITY},
};
use crossbeam_channel::{Receiver as CrossbeamReceiver, Sender as CrossbeamSender};
use lru::LruCache;
use solana_gossip::cluster_info::ClusterInfo;
use solana_ledger::{
blockstore::{Blockstore, SlotMeta},
shred::Nonce,
};
use solana_measure::measure::Measure;
use solana_runtime::{
bank::Bank, bank_forks::BankForks, commitment::VOTE_THRESHOLD_SIZE, contains::Contains,
use solana_runtime::{bank::Bank, bank_forks::BankForks, contains::Contains};
use solana_sdk::{
clock::{BankId, Slot},
epoch_schedule::EpochSchedule,
pubkey::Pubkey,
timing::timestamp,
};
use solana_sdk::{clock::Slot, epoch_schedule::EpochSchedule, pubkey::Pubkey, timing::timestamp};
use std::{
collections::{HashMap, HashSet},
iter::Iterator,
@@ -33,6 +38,8 @@ use std::{
pub type DuplicateSlotsResetSender = CrossbeamSender<Slot>;
pub type DuplicateSlotsResetReceiver = CrossbeamReceiver<Slot>;
pub type ConfirmedSlotsSender = CrossbeamSender<Vec<Slot>>;
pub type ConfirmedSlotsReceiver = CrossbeamReceiver<Vec<Slot>>;
pub type OutstandingRepairs = OutstandingRequests<RepairType>;
@@ -187,6 +194,7 @@ impl RepairService {
let mut last_stats = Instant::now();
let duplicate_slot_repair_statuses: HashMap<Slot, DuplicateSlotRepairStatus> =
HashMap::new();
let mut peers_cache = LruCache::new(REPAIR_PEERS_CACHE_CAPACITY);
loop {
if exit.load(Ordering::Relaxed) {
@@ -223,7 +231,7 @@ impl RepairService {
add_votes_elapsed = Measure::start("add_votes");
repair_weight.add_votes(
&blockstore,
blockstore,
slot_to_vote_pubkeys.into_iter(),
root_bank.epoch_stakes_map(),
root_bank.epoch_schedule(),
@@ -266,14 +274,13 @@ impl RepairService {
)
};
let mut cache = HashMap::new();
let mut send_repairs_elapsed = Measure::start("send_repairs_elapsed");
let mut outstanding_requests = outstanding_requests.write().unwrap();
repairs.into_iter().for_each(|repair_request| {
if let Ok((to, req)) = serve_repair.repair_request(
&cluster_slots,
cluster_slots,
repair_request,
&mut cache,
&mut peers_cache,
&mut repair_stats,
&repair_info.repair_validators,
&mut outstanding_requests,
@@ -487,7 +494,7 @@ impl RepairService {
repair_validators,
);
if let Some((repair_pubkey, repair_addr)) = status.repair_pubkey_and_addr {
let repairs = Self::generate_duplicate_repairs_for_slot(&blockstore, *slot);
let repairs = Self::generate_duplicate_repairs_for_slot(blockstore, *slot);
if let Some(repairs) = repairs {
let mut outstanding_requests = outstanding_requests.write().unwrap();
@@ -529,7 +536,7 @@ impl RepairService {
nonce: Nonce,
) -> Result<()> {
let req =
serve_repair.map_repair_request(&repair_type, repair_pubkey, repair_stats, nonce)?;
serve_repair.map_repair_request(repair_type, repair_pubkey, repair_stats, nonce)?;
repair_socket.send_to(&req, to)?;
Ok(())
}
@@ -558,7 +565,7 @@ impl RepairService {
#[allow(dead_code)]
fn process_new_duplicate_slots(
new_duplicate_slots: &[Slot],
new_duplicate_slots: &[(Slot, BankId)],
duplicate_slot_repair_statuses: &mut HashMap<Slot, DuplicateSlotRepairStatus>,
cluster_slots: &ClusterSlots,
root_bank: &Bank,
@@ -567,16 +574,16 @@ impl RepairService {
duplicate_slots_reset_sender: &DuplicateSlotsResetSender,
repair_validators: &Option<HashSet<Pubkey>>,
) {
for slot in new_duplicate_slots {
for (slot, bank_id) in new_duplicate_slots {
warn!(
"Cluster completed slot: {}, dumping our current version and repairing",
"Cluster confirmed slot: {}, dumping our current version and repairing",
slot
);
// Clear the slot signatures from status cache for this slot
root_bank.clear_slot_signatures(*slot);
// Clear the accounts for this slot
root_bank.remove_unrooted_slot(*slot);
root_bank.remove_unrooted_slots(&[(*slot, *bank_id)]);
// Clear the slot-related data in blockstore. This will:
// 1) Clear old shreds allowing new ones to be inserted
@@ -641,7 +648,7 @@ impl RepairService {
})
.sum();
if total_completed_slot_stake as f64 / total_stake as f64
> VOTE_THRESHOLD_SIZE
> DUPLICATE_THRESHOLD
{
Some(dead_slot)
} else {
@@ -1059,7 +1066,7 @@ mod test {
let serve_repair = ServeRepair::new(cluster_info.clone());
let valid_repair_peer = Node::new_localhost().info;
// Signal that this peer has completed the dead slot, and is thus
// Signal that this peer has confirmed the dead slot, and is thus
// a valid target for repair
let dead_slot = 9;
let cluster_slots = ClusterSlots::default();
@@ -1138,6 +1145,7 @@ mod test {
);
let bank0 = Arc::new(Bank::new(&genesis_config));
let bank9 = Bank::new_from_parent(&bank0, &Pubkey::default(), duplicate_slot);
let duplicate_bank_id = bank9.bank_id();
let old_balance = bank9.get_balance(&keypairs.node_keypair.pubkey());
bank9
.transfer(10_000, &mint_keypair, &keypairs.node_keypair.pubkey())
@@ -1155,7 +1163,7 @@ mod test {
assert!(bank9.get_signature_status(&vote_tx.signatures[0]).is_some());
RepairService::process_new_duplicate_slots(
&[duplicate_slot],
&[(duplicate_slot, duplicate_bank_id)],
&mut duplicate_slot_repair_statuses,
&cluster_slots,
&bank9,

View File

@@ -495,7 +495,7 @@ impl RepairWeight {
for ((slot, _), _) in all_slots {
*self
.slot_to_tree
.get_mut(&slot)
.get_mut(slot)
.expect("Nodes in tree must exist in `self.slot_to_tree`") = root2;
}
}
@@ -521,9 +521,9 @@ impl RepairWeight {
fn sort_by_stake_weight_slot(slot_stake_voted: &mut Vec<(Slot, u64)>) {
slot_stake_voted.sort_by(|(slot, stake_voted), (slot_, stake_voted_)| {
if stake_voted == stake_voted_ {
slot.cmp(&slot_)
slot.cmp(slot_)
} else {
stake_voted.cmp(&stake_voted_).reverse()
stake_voted.cmp(stake_voted_).reverse()
}
});
}
@@ -757,7 +757,7 @@ mod test {
);
for slot in &[8, 10, 11] {
assert_eq!(*repair_weight.slot_to_tree.get(&slot).unwrap(), 8);
assert_eq!(*repair_weight.slot_to_tree.get(slot).unwrap(), 8);
}
for slot in 0..=1 {
assert_eq!(*repair_weight.slot_to_tree.get(&slot).unwrap(), 0);
@@ -772,7 +772,7 @@ mod test {
);
for slot in &[8, 10, 11] {
assert_eq!(*repair_weight.slot_to_tree.get(&slot).unwrap(), 0);
assert_eq!(*repair_weight.slot_to_tree.get(slot).unwrap(), 0);
}
assert_eq!(repair_weight.trees.len(), 1);
assert!(repair_weight.trees.contains_key(&0));
@@ -1088,10 +1088,10 @@ mod test {
let purged_slots = vec![0, 1, 2, 4, 8, 10];
let mut expected_unrooted_len = 0;
for purged_slot in &purged_slots {
assert!(!repair_weight.slot_to_tree.contains_key(&purged_slot));
assert!(!repair_weight.trees.contains_key(&purged_slot));
assert!(!repair_weight.slot_to_tree.contains_key(purged_slot));
assert!(!repair_weight.trees.contains_key(purged_slot));
if *purged_slot > 3 {
assert!(repair_weight.unrooted_slots.contains(&purged_slot));
assert!(repair_weight.unrooted_slots.contains(purged_slot));
expected_unrooted_len += 1;
}
}

View File

@@ -101,7 +101,7 @@ pub fn get_best_repair_shreds<'a>(
let new_repairs = RepairService::generate_repairs_for_slot(
blockstore,
slot,
&slot_meta,
slot_meta,
max_repairs - repairs.len(),
);
repairs.extend(new_repairs);

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