Compare commits

..

256 Commits

Author SHA1 Message Date
8631be42ac Add support for monitoring system account balances (#9345)
automerge

(cherry picked from commit 03978ac5a5)
2020-04-06 22:57:47 -07:00
09367369ef Reinstate commitment param to support old clients (#9324) (#9329)
automerge
2020-04-06 12:21:19 -07:00
23b8c95cc4 Update getSignatureStatuses to return historical statuses (#9314) (#9321)
automerge
2020-04-06 04:24:19 -07:00
e61392b057 Rework TransactionStatus index in blockstore (#9281) (#9304) (#9312)
automerge

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2020-04-05 12:52:15 -06:00
7deba20395 Deprecate confirmTransaction, getSignatureStatus, and getSignatureConfirmation (bp #9298) (#9308)
automerge
2020-04-05 02:41:10 -07:00
274c097f84 RPC: add err field to TransactionStatus, alongside the now deprecated status field (#9296) (#9307)
automerge
2020-04-05 00:21:15 -07:00
1c7cea1af4 Add stake-monitor 2020-04-03 20:37:28 -07:00
4406496d2f Add log before opening database
(cherry picked from commit b557b3170e)
2020-04-03 15:10:57 -07:00
a15fa4840c Fix repair dos (#9056) 2020-04-03 13:20:39 -07:00
c9030660d6 Bump version to 1.0.14 2020-04-02 22:42:28 -07:00
fdeda769d0 Backport wallet doc changes to v1.0 (#9268)
* Add ledger live screenshots and reduce duplicate instructions (#9258)

automerge

* Add instructions for Trust Wallet Beta for Android (#9261)

automerge
2020-04-02 15:37:26 -06:00
53edd26578 Add instructions for Trust Wallet Beta for Android (#9261) (#9264)
automerge
2020-04-02 12:25:32 -07:00
2433cdd6d6 Set checks timeout to 20 minutes 2020-04-02 13:11:03 -06:00
77b34c278e Do not trigger tests if only docs were modified (#9240) (#9257) 2020-04-02 10:44:51 -06:00
90e993fd9a Add epoch subcommand (#9249) (#9254)
automerge
2020-04-01 22:29:18 -07:00
251054d8c9 Backport confirmations fix. (#9252)
automerge
2020-04-01 19:42:04 -07:00
d4bb7cec69 Undo breaking rpc removal of getSignatureConfirmation (#9247) 2020-04-01 18:04:11 -06:00
a3f6b04345 Add fee-payer option to docs (bp #9230) (#9236)
automerge
2020-04-01 15:11:47 -07:00
ba4a5053dd Undo getSignatureStatus breaking change, add getSignatureStatuses (#9231)
automerge
2020-04-01 12:21:56 -07:00
6b47a259c3 Add a support page for wallet docs (#9229) (#9234)
automerge
2020-04-01 11:22:50 -07:00
c9ec13cf1f Tune udp buffers and vmmap immediately (#9194) (#9216)
automerge
2020-04-01 00:52:50 -07:00
906a6ab837 Fix error with account hash list getting too big for gossip (#9197) (#9214)
automerge
2020-03-31 23:27:18 -07:00
d0e478a9f8 Fix panic (#9195) (#9208)
automerge
2020-03-31 22:03:46 -07:00
b560b64d33 Remove unecessary security exception and add a new one (bp #9200) (#9205)
* Remove unecessary exception and add a new one (#9200)

(cherry picked from commit 62e12e3af5)

# Conflicts:
#	ci/test-checks.sh

* Update test-checks.sh

Co-authored-by: sakridge <sakridge@gmail.com>
Co-authored-by: Michael Vines <mvines@gmail.com>
2020-03-31 19:50:04 -07:00
057af41716 Fix links (#9184) (#9187)
automerge
2020-03-31 09:57:23 -07:00
a44b8abd48 Bump version to v1.0.13 2020-03-30 23:05:41 -07:00
8778ecaed5 Ledger cleanup fixes (#9131) (#9175)
automerge
2020-03-30 20:42:17 -07:00
a02542ada3 Bump version to v1.0.12 2020-03-30 08:45:50 -07:00
ea17c6883f Remove chatty 'setting snapshot root:' info log 2020-03-30 08:44:24 -07:00
706306645b solana account now displays the account's rent epoch 2020-03-30 08:44:24 -07:00
da9e930788 Calculate ref counts earlier to prevent bad clean (#9153) 2020-03-29 14:42:57 -07:00
8b8e066bbe Add RPC subscription api for rooted slots (#9118) (#9126)
automerge
2020-03-27 13:09:32 -07:00
3473350b62 fix links (#9125) (#9128)
automerge
2020-03-27 10:28:23 -07:00
59d7eb5216 Fix links in docs (#9119) (#9127)
automerge
2020-03-27 09:50:28 -07:00
55ba934137 Document transaction field in getConfirmedBlock responses (#9121) (#9124)
automerge
2020-03-27 09:45:30 -07:00
4c3dcb7f7e Exclude all executable accounts from rent collection (#9116) (#9120)
automerge
2020-03-27 09:27:07 -07:00
3a879db8af Fix broken gitbook links (#9107) (#9108)
automerge
2020-03-26 20:33:20 -07:00
d2107270ea Consolidate signature-status rpcs (bp #9069) (#9105)
automerge
2020-03-26 20:30:46 -07:00
007afe22d0 Add docs for app wallets (#9098) (#9103)
automerge
2020-03-26 18:26:23 -07:00
93a1d10e15 Revert setting the default toolchain (#9093) (#9097)
automerge
2020-03-26 15:59:26 -07:00
d57a7c8f21 Restructure wallet docs to prep for app wallet content (#9088) (#9095)
automerge
2020-03-26 14:39:32 -07:00
6db39829c8 Install xargo using CI dictated cargo version if available (#9068) (#9092)
automerge
2020-03-26 14:02:09 -07:00
ec76826493 Unflake rpc subscriptions test by reducing sub count (#9078) (#9082)
automerge
2020-03-25 22:56:52 -07:00
d4ddb6265b Convert Banks (#9033)
* Store and compute needed state in EpochStakes struct
Co-authored-by: Carl <carl@solana.com>
2020-03-25 20:43:48 -07:00
7a8233d7ca Bump version to 1.0.11 2020-03-25 19:17:06 -07:00
95bc051129 Cargo update bumpalo (#9067) (#9077)
automerge
2020-03-25 18:51:38 -07:00
02bcf4f8e2 ledger-tool can now decode stake instructions (bp #9045) (#9076)
automerge
2020-03-25 18:02:31 -07:00
4b0d4e9834 Remove accounts unwrap (#9063)
automerge
2020-03-25 10:59:52 -07:00
bf4cdc091a Fix xargo to version 0.3.19 to avoid unstable feature (#9065) (#9066)
automerge

(cherry picked from commit c558db2a48)

Co-authored-by: Justin Starry <justin@solana.com>
2020-03-25 08:48:22 -07:00
5f2cf2b44d Increase vmap count in sys-tuner (#8940) (#9064)
(cherry picked from commit a70008cc5c)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-03-25 08:06:36 -07:00
03aae5eb5f Ignore RUSTSEC-2020-0006 for the moment (#9057) (#9059)
automerge
2020-03-24 21:06:15 -07:00
5f1ce81fbc ledger tool now outputs transaction status information if available (#9024) (#9026)
automerge
2020-03-24 12:23:25 -07:00
fc582aa57c Refactor how pubsub subscriptions are added (#9042) (#9049)
automerge
2020-03-24 11:05:33 -07:00
13676e9614 Fix timeout for subscriptions test (#9043) (#9044)
automerge
2020-03-24 02:57:25 -07:00
7636a0521f solana-install-init: --pubkey is no longer required on platforms without a default update manifest 2020-03-23 22:40:53 -07:00
ad06354a18 Remove , 2020-03-23 22:11:56 -07:00
953cb93e44 Bump version to 1.0.10 2020-03-23 21:58:45 -07:00
38b2957a8e Remove unused default update manifest pubkeys (#9041)
automerge
2020-03-23 20:57:43 -07:00
9eb39df93f Backport: add slot to signature notification & respect confirmations param (#9036)
automerge
2020-03-23 18:32:05 -07:00
f34ce94347 Remove Ledger-specific analysis of hardware wallets (#9028) (#9030)
automerge
2020-03-23 14:48:58 -07:00
5c6411fc06 Fix link in gitbook (#9027) (#9029)
automerge
2020-03-23 14:48:41 -07:00
3ab428693a Manual v1.0 backports (#9025)
automerge
2020-03-23 13:55:03 -07:00
7ffaf2ad29 Manual v1.0 backports (#9015)
automerge
2020-03-22 22:44:55 -07:00
a5e4a1f2d8 Drop storage lock (#8997) 2020-03-21 18:55:59 -07:00
6ae99848f4 Revert "Move Install Solana doc into the Command-line Guide (#8982)" (#8992) (#8993)
This reverts commit 5fa36bbab3.

(cherry picked from commit 1aab959d4e)

Co-authored-by: Greg Fitzgerald <greg@solana.com>
2020-03-21 14:52:53 -06:00
5342b3a53f Shred fetch comment and debug message tweak (#8980) (#8990)
automerge

(cherry picked from commit 909321928c)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-03-21 09:25:08 -07:00
50a3c98d21 Shred filter (#8975) (#8987)
Thread bank_forks into shred fetch and filter by slot.
2020-03-20 11:33:19 -07:00
ca2b7a2c5b Cargo.lock 2020-03-20 11:05:45 -07:00
e900623817 Update value names in docs (#8983) (#8985)
automerge
2020-03-20 09:36:55 -07:00
a46e953ebb Move Install Solana doc into the Command-line Guide (#8982) (#8984)
automerge
2020-03-20 09:24:56 -07:00
3bbc51a6ae Improve CLI usage messages (#8972) (#8977)
automerge
2020-03-19 21:52:37 -07:00
7ae2464cf3 Fix windows build by removing sys-info (#8860) (#8973)
automerge
2020-03-19 18:14:01 -07:00
58a36ce484 Bump version to 1.0.9 2020-03-19 17:10:35 -07:00
61bd9e6a28 Fix windows binary build on v1.0 (#8968)
Co-authored-by: Michael Vines <mvines@gmail.com>
2020-03-19 15:58:34 -07:00
495ab631d6 Some Cli polish (#8966) (#8970)
automerge
2020-03-19 14:11:52 -07:00
93906847f9 Cli: polish transaction progress bar (#8963) (#8967)
automerge
2020-03-19 12:54:50 -07:00
70f4c4a974 Build windows binaries on v1.0 (#8965) 2020-03-19 11:06:17 -07:00
ad6078da2d CLI: Fix create-nonce-account with seed (#8929) (#8961)
automerge
2020-03-19 10:32:35 -07:00
2a617f2d07 v1.0: Backport of #8939 (#8957)
automerge
2020-03-19 02:08:36 -07:00
6af3c6ecbc CLI: Add multi-session signing support (#8927) (#8953)
* SDK: Add `NullSigner` implementation

* SDK: Split `Transaction::verify()` to gain access to results

* CLI: Minor refactor of --sign_only result parsing

* CLI: Enable paritial signing

Signers specified by pubkey, but without a matching --signer arg
supplied fall back to a `NullSigner` when --sign-only is in effect.
This allows their pubkey to be used for TX construction as usual,
but leaves their `sign_message()` a NOP. As such, with --sign-only
in effect, signing and verification must be done separately, with
the latter's per-signature results considered

* CLI: Surface/report missing/bad signers to user

* CLI: Suppress --sign-only JSON output

* nits

* Docs for multi-session offline signing

(cherry picked from commit 98228c392e)

Co-authored-by: Trent Nelson <trent@solana.com>
2020-03-18 23:39:44 -07:00
a5938e5a21 Bump bv from 0.11.0 to 0.11.1 (bp #8952) (#8955)
automerge
2020-03-18 22:33:05 -07:00
8eaa8d8788 Add docs on wallets and generating keys (bp #8905) (#8946)
automerge
2020-03-18 19:15:34 -07:00
51940eec9b Delete broken link (#8950) (#8951)
automerge
2020-03-18 18:20:36 -07:00
d1e16b2bc4 Remove product string from device keypair URL (#8942) (#8945)
automerge
2020-03-18 14:11:41 -07:00
97051c87b4 Document account/signer requirements for vote instructions (#8941)
automerge
2020-03-18 12:20:02 -07:00
2521913654 Add counter for accounts hash verification. (#8928) (#8934)
automerge
2020-03-18 09:57:38 -07:00
803c87b4bd Add docs for --trusted-validator options (#8911) (#8933)
automerge
2020-03-18 08:37:15 -07:00
8ff9ee3a06 Cli: add spinner progress bar when waiting for transaction confirmation (#8916) (#8920)
automerge
2020-03-17 20:06:01 -07:00
25bf2c6062 Extend local-cluster CI timeout (#8921) (#8923)
automerge
2020-03-17 19:48:57 -07:00
fc80b77fc4 Increase buffer on low SOL fault to over a week (#8903) (#8904)
automerge
2020-03-17 10:32:28 -07:00
5a7707362c Sort device paths for select (#8896) (#8897)
automerge
2020-03-16 19:43:11 -07:00
0102ee3fa9 Hoist USB URL docs (#8894) (#8895)
automerge
2020-03-16 16:31:35 -07:00
d29cb19a73 Cli: enable flexible flexible signer paths for pubkey args (#8892) (#8893)
automerge
2020-03-16 16:16:58 -07:00
1cc66f0cd7 Add Accounts hash consistency halting (#8772) (#8889)
* Accounts hash consistency halting

* Add option to inject account hash faults for testing.

Enable option in local cluster test to see that node halts.
2020-03-16 14:29:44 -07:00
e2cfc513eb Add genesis token counter test to system test (#8824) (#8891)
automerge
2020-03-16 13:32:58 -07:00
8115a962e9 Cleanup CLI types (#8888) (#8890)
automerge
2020-03-16 12:28:22 -07:00
0c804e2ef2 Use types for CLI value names (#8878) (#8886)
automerge
2020-03-16 09:23:15 -07:00
cbe36a7a63 Lower error level of InvalidTickCount (bp #8880) (#8885)
automerge
2020-03-16 08:53:33 -07:00
174825fecf Fix faucet command in run.sh (#8883) (#8884)
automerge
2020-03-16 05:38:31 -07:00
c7e80bebdc Bump version to 1.0.8 2020-03-15 21:50:06 -07:00
57abc370fa Quietly re-introduce legacy --voting-keypair/--identity-keypair args for v1.0.6 compatibility
(cherry picked from commit 49706172f3)
2020-03-15 20:21:23 -07:00
42ab421a87 Rename leader to validator, drop _keypair/-keypair suffix (#8876) (#8877)
automerge
2020-03-15 14:14:32 -07:00
9ab27291f5 Validators now run a full gossip node while looking for a snapshot (#8872)
automerge
2020-03-15 10:29:13 -07:00
27e4e9cb8d Cli: Add resolve-signer subcommand (#8859) (#8870)
automerge
2020-03-14 22:14:24 -07:00
b0cf65dfc8 Refactor system tests dir structure (#8865) (#8869)
automerge
2020-03-14 19:38:19 -07:00
bfc97c682c Apply s/faucet-keypair/faucet renaming to net scripts (#8868)
automerge
2020-03-14 18:14:49 -07:00
231c9b25d1 Rework validator vote account defaults to half voting fees (#8858)
automerge
2020-03-13 21:47:32 -07:00
f536d805ed Rework cluster metrics dashboard to support the modern clusters
(cherry picked from commit 5f5824d78d)

# Conflicts:
#	system-test/automation_utils.sh
2020-03-13 20:18:06 -07:00
cb6e7426a4 Drop :8899 port from http://devnet.solana.com references
(cherry picked from commit 9e0a26628b)
2020-03-13 20:14:02 -07:00
71f391ae04 Enable any signer in various cli subcommands (#8844) (#8856)
automerge
2020-03-13 18:11:05 -07:00
12bf34f059 Remove holding Poh lock (#8838) (#8850)
automerge
2020-03-13 16:56:22 -07:00
a3a14d6b5b Docs: Use correct flag in keypair verification instructions (#8677)
automerge

(cherry picked from commit 542691c4e4)
2020-03-13 16:00:33 -07:00
5e22db017a Surface the missing pubkey
(cherry picked from commit ce88602ced)
2020-03-13 16:00:01 -07:00
2cd09957b4 Cli: add subcommand to withdraw from vote account (#8550) (#8840)
automerge
2020-03-13 15:31:47 -07:00
d1ec6c0b8b Upgrade to Rust 1.42 (#8836) (#8839)
automerge
2020-03-13 14:29:18 -07:00
7722723400 Bump version to 1.0.7 2020-03-13 08:10:09 -07:00
4a42cfc42a Cli error cleanup 1.0 (#8834)
* Don't use move semantics if not needed (#8793)

* SDK: Deboilerplate `TransportError` with thiserror

* Enable interchange between `TransportError` and `ClientError`

* SDK: Retval consistency between `Client` and `AsyncClient` traits

* Client: Introduce/use `Result` type

* Client: Remove unused `RpcResponseIn` type

* Client: Rename `RpcResponse` to more appropriate `RpcResult`

* Client: Death to `io::Result` return types

* Client: Struct-ify `ClientError`

* Client: Add optional `command` parameter to `ClientError`

* RpcClient: Stop abusing `io::Error` (low-fruit)

* ClientError: Use `thiserror`'s `Display` impl

* Extend `RpcError`'s utility

* RpcClient: Stop abusing `io::Error` (the rest)

* CLI: Shim `main()` so we can `Display` format errors

* claputils: format input validator errors with `Display`

They are intended to be displayed to users

* SDK: `thiserror` for hash and sig parse erros

* Keygen: Shim main to format errors with `Display`

* SDK: `thiserror` for `InstructionError`

* CLI: `thiserror` for `CliError`

* CLI: Format user messages with `Display`

* Client: Tweak `Display` for `ClientError`

* RpcClient: Improve messaging when TX cannot be confirmed

* fu death io res retval

* CLI/Keygen - fix shell return value on error

* Tweak `InstructionError` `Display` messages as per review

* Cleanup hackjob return code fix

* Embrace that which you hate most

* Too much...

Co-authored-by: Jack May <jack@solana.com>
2020-03-13 07:42:25 -06:00
976d744b0d Enable conservative out-of-bound snapshot cleaning (#8811) (#8832)
automerge
2020-03-12 23:40:17 -07:00
62de02c8d4 Avoid early clean and bad snapshot by ref-counting (#8724) (#8831)
automerge
2020-03-12 23:09:45 -07:00
2741a5ce3b Move history out of intro (bp #8825) (#8826)
automerge
2020-03-12 18:21:56 -07:00
0c818acd90 Move intro out of README (#8735) (#8827)
automerge

(cherry picked from commit c0fd017906)

Co-authored-by: Greg Fitzgerald <greg@solana.com>
2020-03-12 18:49:51 -06:00
4d04915302 Update source markdown in CI 2020-03-12 14:56:00 -07:00
7576411c59 Don't tell users to install unreleased software versions 2020-03-12 14:55:46 -07:00
ab2bed6e8f Add all of docs/src 2020-03-12 14:45:48 -07:00
f66a5ba579 Update keys (#8821) (#8822)
automerge
2020-03-12 14:15:32 -07:00
9253d786f8 Revert over-zealous versioning text (#8819)
automerge
2020-03-12 09:49:26 -07:00
d1be1ee49e Fix malformed doc link (#8817) (#8818)
automerge
2020-03-12 09:46:26 -07:00
65833eacd8 Update keys (#8814) (#8816)
automerge
2020-03-12 09:39:31 -07:00
921994e588 Restrict which nodes can run stable and coverage (#8807)
automerge
2020-03-11 19:18:10 -07:00
91dfd962e5 Update keys (#8800) (#8806)
automerge
2020-03-11 19:04:46 -07:00
18067dcb55 CLI: Plumb nonce-stored fees (#8750)
automerge

(cherry picked from commit 0422af2aae)
2020-03-11 13:09:40 -07:00
7501e1b0f0 Update keys (#8791)
(cherry picked from commit a0d0d4c0e9)
2020-03-11 13:08:48 -07:00
1bd2c1de20 Notify when validator balance goes below 1 SOL (#8792)
automerge
2020-03-11 11:47:08 -07:00
a9ef20d43f Update keys (#8783) (#8784)
automerge
2020-03-10 21:39:34 -07:00
00a9d66fed Improve install messaging (#8477) (#8785)
automerge
2020-03-10 20:30:39 -07:00
7508ffe703 Add checkmark (#8781) (#8782)
automerge
2020-03-10 18:32:53 -07:00
6c2368bfc9 Permit fee-payer/split-stake accounts to be the same when using --seed
(cherry picked from commit 775ce3a03f)
2020-03-10 16:16:26 -07:00
6bcb797260 Revert to a computed websocket_url value when json_rpc_url is changed
(cherry picked from commit f655372b08)
2020-03-10 16:12:23 -07:00
329945e56f Print approved msg after Ledger interaction (#8771) (#8775)
automerge
2020-03-10 15:06:43 -07:00
1f80346d97 watchtower: Add --monitor-active-stake flag (bp #8765) (#8769)
automerge
2020-03-10 13:27:19 -07:00
38e661d7ba CLI Nonce account access dereplicode (#8743) (#8767)
automerge
2020-03-10 13:15:18 -07:00
592c4efd17 Configure the cluster right after installing it (#8761) (#8762)
automerge
2020-03-10 10:14:55 -07:00
1629745bd3 Move docs to imperative mood (bp #8643) (#8763)
automerge
2020-03-10 09:50:59 -07:00
02e078c22e Fix Gitbook's markdown rendering (#8759) (#8760)
automerge
2020-03-10 08:09:51 -07:00
ea9e7c710a Bump version to 1.0.6 2020-03-09 22:38:57 -07:00
ee5aa0c1a2 Support monitoring multiple validators 2020-03-09 21:07:32 -07:00
HM
55dee2901e watchtower: flag to suppress duplicate notifications (#8549)
* watchtower: send error message as notification

* watchtower: send all clear notification when ok again

* watchtower: add twilio sms notifications

* watchtower: flag to suppress duplicate notifications

* remove trailing space character

* changes as per suggestion on PR

* all changes together

* cargo fmt
2020-03-09 21:07:32 -07:00
2b0824d18b watchtower now uses cli-config/
(cherry picked from commit 74e7da214a)
2020-03-09 20:40:18 -07:00
b0709ea0ac Move cli-config default out of cli/ into cli-config/
(cherry picked from commit 756ba07b16)
2020-03-09 20:40:18 -07:00
81b5499f7a Rename 'url' to 'json_rpc_url'
(cherry picked from commit 5c236fd06c)
2020-03-09 20:40:18 -07:00
c96ce99705 Wait for 80% of the active stake instead of 75% 2020-03-09 20:31:44 -07:00
8dcd2d11e1 Docs: Fix missing CLI usage.md (#8745) (#8749)
automerge
2020-03-09 20:05:24 -07:00
777aae9059 Remove --derivation-path option (#8741) (#8747)
automerge
2020-03-09 19:17:14 -07:00
4dd1340236 Limit waiting-message to single- or last-chunk apdus (#8730) (#8733)
automerge
2020-03-09 15:44:17 -07:00
889b06e1d4 Allow passing of program_id to programs (bp #8639) (#8670)
automerge
2020-03-09 12:36:34 -07:00
f511296ee8 Fix account tests (#8615) (#8729)
automerge
2020-03-09 11:19:14 -07:00
c19eb717b4 Update rust-bpf to include matching cargo (#8598) (#8727)
automerge
2020-03-09 10:42:19 -07:00
dd54369e1b Cli: Fix create-with-seed (#8706) (#8723)
automerge
2020-03-09 00:16:29 -07:00
bb563b4835 Permit --no-untrusted-rpc without any --trusted-validators 2020-03-08 22:34:53 -07:00
d061fadede Add purge function to ledger-tool (#8719) (#8720)
(cherry picked from commit de34187db0)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-03-08 21:38:38 -07:00
577cd2bd3a Remove unnecessary snapshot hash verification (#8711) (#8714)
(cherry picked from commit f992ee3140)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-03-07 14:41:45 -07:00
9d1c8657e2 Groom ledger-tool bounds output (#8710) (#8715)
(cherry picked from commit acb23e8ef0)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-03-07 14:15:50 -07:00
67216ac7f5 Split staker infos (#8683)
automerge
2020-03-06 23:44:06 -08:00
e63b24c39f Remove ask-seed-phrase arg from validator, archiver (#8697) (#8713)
automerge
2020-03-06 23:34:20 -08:00
d963f7afb4 Set withdrawer keys (#8707)
automerge
2020-03-06 21:21:00 -08:00
a07bf4870a Fix Ledger docs (#8705) (#8708)
automerge
2020-03-06 21:03:15 -08:00
8422d4b3fb Disable setLogFilter RPC API by default (#8693) (#8699)
automerge
2020-03-06 18:16:04 -08:00
ff4731cce2 RPC: Add getFeeCalculatorForBlockhash method call (#8687) (#8698)
automerge
2020-03-06 17:25:33 -08:00
659aaafff6 Ledger: return specific error if ledger-app-solana is not running (#8684) (#8695)
automerge
2020-03-06 16:28:53 -08:00
175651c497 Add shred version support to net/ (#8689) (#8694)
automerge
2020-03-06 15:41:16 -08:00
085e773f27 Properly escape current version (#8686) (#8688)
(cherry picked from commit a78a339407)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-03-06 14:37:41 -07:00
5d3140c040 Cargo.lock 2020-03-06 14:25:54 -07:00
d8d7238920 Bump version to 1.0.5 2020-03-06 14:14:53 -07:00
418c3cd4cf Delete Archiver installation docs (#8665) (#8669)
automerge
2020-03-06 12:49:52 -08:00
9f532cb50f Publish initial snapshot hash in gossip on validator startup (#8678) 2020-03-05 23:01:35 -07:00
c35f4927cd Bump version to 1.0.4 2020-03-05 16:10:24 -07:00
2d0f4b5c8c Bump version to 1.0.3 2020-03-05 13:05:12 -07:00
fe59ee61e6 Add find_incomplete_slots (#8654) (#8671)
automerge
2020-03-05 11:52:20 -08:00
bda2acb06d SDK: Allow RecentBlockhashes to hold the entire BlockhashQueue (#8632) (#8672)
automerge

(cherry picked from commit 9d667db634)

Co-authored-by: Grimes <39311140+solana-grimes@users.noreply.github.com>
2020-03-05 12:09:13 -07:00
ac3fe1da02 Fix docs build (#8663) (#8667)
automerge
2020-03-05 10:25:43 -08:00
57813041d2 Remove solana-archiver from release artifacts 2020-03-05 11:02:18 -07:00
7e446da82c Choose a cluster before checking balances (#8666) (#8668)
automerge
2020-03-05 10:00:53 -08:00
d94f5c94a3 Nonce fees 1.0 (#8664)
automerge
2020-03-05 09:41:45 -08:00
4d9aee4794 Always and fully normalize stored 0-lamport accts. (#8657) (#8661)
(cherry picked from commit f146c92e88)

Co-authored-by: Michael Vines <mvines@gmail.com>
2020-03-05 09:49:26 -07:00
f32c152bce genesis: Add support for multiple bootstrap validators (#8656) (#8658)
automerge
2020-03-05 00:53:40 -08:00
5caf9110e0 Add --bind-address and --rpc-bind-address validator arguments (#8628) (#8655)
automerge
2020-03-04 22:39:38 -08:00
d1c80143ea Add NextSlotsIterator (#8652) (#8653)
automerge
2020-03-04 21:59:04 -08:00
bb132df121 Add orphan iterator (#8636) (#8651)
automerge
2020-03-04 20:02:46 -08:00
b3af1c7e57 Connect partition flag to validators (#8622) (#8646)
automerge
2020-03-04 18:16:54 -08:00
14cba53338 Install Solana before using it (#8638) (#8644)
automerge
2020-03-04 16:08:25 -08:00
dc8abbe9e3 solana catchup now detects when you try to catchup to yourself (#8635) (#8641)
automerge
2020-03-04 16:00:20 -08:00
dd06001ed8 compute_bank_stats needs to return newly computed ForkStats (#8608) (#8634)
* Fix broken confirmation, add test

(cherry picked from commit f23dc11a86)

Co-authored-by: carllin <wumu727@gmail.com>
2020-03-04 14:28:17 -08:00
298b7de2e2 catchup now supports an optional RPC URL argument for validators with private RPC (#8629) (#8633)
automerge
2020-03-04 12:48:18 -08:00
74cbc6953f Expose executable and rent_epoch in AccountInfo (#8619) (#8631)
automerge
2020-03-04 12:09:14 -08:00
27e5203078 Fix sendTransaction doc (#8625) (#8626)
automerge
2020-03-04 08:53:30 -08:00
73787e162c Keep GenesisConfig binary compatible with v0.23 (#8617) (#8620)
automerge
2020-03-04 02:02:28 -08:00
74ae40be41 Check transaction signatures in entry verify (#8596) (#8614)
automerge
2020-03-03 22:10:35 -08:00
8dd58e9cea Remove accounts hack and correctly restore accounts store counts (#8569) (#8613)
automerge
2020-03-03 22:05:50 -08:00
7d86179c60 Split signature throughput tracking out of FeeCalculator (#8447) (#8610)
automerge
2020-03-03 20:36:52 -08:00
8115cf1360 Docs: Update CLI offline cmds (#8548) (#8558)
automerge
2020-03-03 20:26:28 -08:00
3e5d45053d Cargo.lock 2020-03-03 21:14:00 -07:00
061319f35a Add commitment flag to vote-account and validators commands (#8597) (#8606)
automerge
2020-03-03 17:47:14 -08:00
dc75837500 Use fs::rename which is much faster than move_items (#8579) (#8595)
automerge
2020-03-03 11:23:11 -08:00
dfe26f5275 Add Ledger wallet installation instructions (#8581) (#8592)
automerge
2020-03-03 08:29:12 -08:00
f4385f7ad2 Do periodic inbound cleaning for rooted slots (#8436) (#8583)
automerge
2020-03-02 23:04:57 -08:00
8df4cf2895 Don't advertise the snapshot that the node was loaded from
snapshot_packager_service will remove this snapshot hash from gossip
when it starts
2020-03-02 19:16:06 -07:00
dad62e132e Remove granularity from genesis (bp #8514) (#8518)
automerge
2020-03-02 15:42:46 -08:00
0d4131ae68 Bump version to 1.0.2 2020-03-02 15:38:26 -07:00
a2539e1892 Allow stake lockup fields to be updated independently (#8568) (#8574)
automerge
2020-03-02 14:19:18 -08:00
210659e6c3 Only gossip packaged snapshots
(cherry picked from commit 42c5c59800)
2020-03-02 14:21:04 -07:00
15a0fb1fa9 --wait-for-supermajority now requires a SLOT
(cherry picked from commit 13551885c2)
2020-03-02 13:40:52 -07:00
4db31f5b48 Add ---no-untrusted-rpc flag
(cherry picked from commit d677e83ed4)
2020-03-02 11:53:15 -07:00
b38a535c63 Hack to skip cleanup_dead_slots upon snapshot load (#8562)
automerge
2020-03-02 10:40:41 -08:00
218b02aaf8 Demote gossip responder error log messages to info! 2020-03-01 10:42:32 -07:00
d6e7cbd4e8 feat: implement websocket_url as a get/set-able global parameter w/ value computation (#8553)
automerge
2020-03-01 01:09:09 -08:00
f2fda14333 Reduce max snapshot hashes to stay under MTU (bp #8541) (#8544)
automerge
2020-02-29 09:17:09 -08:00
1c576d4a68 Upgrade to Rust 1.41.1
(cherry picked from commit 7d27be2a73)
2020-02-29 09:27:11 -07:00
f6232e1b3c Fix skipping own leader slots (#8533) (#8540)
automerge
2020-02-29 01:27:46 -08:00
ad71fa3f12 rpc: GET for /snapshot.tar.bz2 now redirects to the latest snapshot (bp #8536) (#8538)
automerge
2020-02-28 23:45:15 -08:00
9c326c7c71 Ensure the validator's identity pubkey is not provided as a --trusted-validator (#8525) (#8527)
automerge
2020-02-27 22:07:07 -08:00
ac545cadaf Add versioning (#8348) (#8524)
automerge
2020-02-27 20:12:49 -08:00
082d8fff36 Use legit solana message in verify (#8513) (#8523)
automerge
2020-02-27 19:36:37 -08:00
2c3632a042 Determine vote_state ahead of time (#8303) (#8521)
automerge
2020-02-27 18:32:27 -08:00
7b23e79922 Add snapshot hash of full accounts state (#8295) (#8515)
* Add snapshot hash of full accounts state

* Use normal hashing for the accounts delta state

* Add merkle

(cherry picked from commit 947a339714)

Co-authored-by: sakridge <sakridge@gmail.com>
2020-02-27 17:36:28 -08:00
9afd14a0c6 Import Tour de SOL docs (#8516) (#8519)
automerge
2020-02-27 17:15:36 -08:00
f42aa34ab6 Reorder InstructionError to remain compatible with v0.23
(cherry picked from commit 7dac8e2dde)
2020-02-27 18:07:00 -07:00
43af91ff4b Ledger messaging cleanup (#8506) (#8508)
automerge
2020-02-27 12:38:09 -08:00
a920a0f946 Fix cluster economics figures and spelling in docs (#8502) (#8505)
automerge
2020-02-27 02:49:10 -08:00
4a8a6d0b3f Remove loop (#8496)
automerge
2020-02-27 00:38:27 -08:00
a64b8a2705 Rename snapshot.tar.bz2 to snapshot-<slot>-<hash>.tar.bz2 (bp #8482) (#8501)
automerge
2020-02-26 23:31:44 -08:00
0198f9e8af Peg snapshot version to 1.0.0 2020-02-26 22:07:50 -07:00
1582a3a927 Cargo.lock 2020-02-26 21:17:40 -07:00
e713f0e5fb Update voting simulation (#8489) 2020-02-26 20:00:20 -08:00
77031000c4 Choose more appropriate options for pubsub websocket server (#8354) (#8492)
automerge
2020-02-26 18:23:50 -08:00
635a962fba Reference the v1.0.0 installer 2020-02-26 19:20:58 -07:00
c59ec2dcff Add flag to confirm key on device (#8478) (#8490)
automerge
2020-02-26 17:31:52 -08:00
abc6c5e264 Limit leader schedule search space (#8468) (#8486)
automerge
2020-02-26 16:11:14 -08:00
87cfac12dd Validate the genesis config downloaded over RPC before accepting it (bp #8474) (#8481)
automerge
2020-02-26 15:12:06 -08:00
60b43e34b6 Ledger hardware wallet docs (#8472) (#8479)
automerge
2020-02-26 14:42:08 -08:00
9ab6222f03 Move docs from book/ to docs/ (#8469) (#8471)
automerge
2020-02-26 08:01:44 -08:00
2298dd5c07 Use runtime executor to send pubsub notifications (#8353) (#8465)
automerge
2020-02-25 21:45:18 -08:00
822d166115 live-slots now displays the rate the root slot is advancing (#8464)
automerge
2020-02-25 21:18:10 -08:00
8f71580615 Allow withdrawer to change the authorized stake key (#8456) (#8462)
automerge
2020-02-25 19:30:59 -08:00
cc3352ff06 Ledger key path rework (#8453) (#8457)
automerge
2020-02-25 18:00:53 -08:00
8f5928b7c7 Promote dangerous cond. from just warning to panic (#8439) (#8449)
automerge
2020-02-25 15:31:06 -08:00
888e9617ff 🐌🐌 Publish crates for even longer longer 2020-02-25 09:23:20 -07:00
4695c4cf7d Add --no-check-vote-account argument (#8430) (#8435)
automerge
2020-02-25 00:23:50 -08:00
bbfc56ff7f Make solana root key accessible on Ledger (#8421) (#8431)
automerge
2020-02-24 22:28:28 -08:00
4103d99018 Bump version to 1.0.1 2020-02-24 23:24:27 -07:00
c375ce1fcd CLI: collect and deduplicate signers (#8398) (#8423)
automerge
2020-02-24 17:29:34 -08:00
df813b31c5 Fix SDK deps 2020-02-24 17:30:46 -07:00
7db92e2951 Drop print- prefix from slot/accounts command
(cherry picked from commit 89baa94002)
2020-02-24 17:28:36 -07:00
6585518f78 Add genesis subcommand
(cherry picked from commit 1ef3478709)
2020-02-24 17:28:36 -07:00
6398e256e4 Move shred_version module to sdk/
(cherry picked from commit 73063544bd)
2020-02-24 17:28:36 -07:00
b64ebb45e7 validator: snapshot fetching cleanup (bp #8413) (#8417)
automerge
2020-02-24 15:22:34 -08:00
307ac66d9c Reinstate create-stale-account w/ seed test (#8401) (#8402)
automerge
2020-02-22 10:56:39 -08:00
512 changed files with 27412 additions and 45466 deletions

42
.appveyor.yml Normal file
View File

@ -0,0 +1,42 @@
version: '{build}'
branches:
only:
- master
- /^v[0-9.]+\.[0-9.]+/
cache:
- '%USERPROFILE%\.cargo'
- '%APPVEYOR_BUILD_FOLDER%\target'
clone_folder: d:\projects\solana
build_script:
- bash ci/publish-tarball.sh
notifications:
- provider: Slack
incoming_webhook:
secure: GJsBey+F5apAtUm86MHVJ68Uqa6WN1SImcuIc4TsTZrDhA8K1QWUNw9FFQPybUWDyOcS5dly3kubnUqlGt9ux6Ad2efsfRIQYWv0tOVXKeY=
channel: ci-status
on_build_success: false
on_build_failure: true
on_build_status_changed: true
deploy:
- provider: S3
access_key_id:
secure: fTbJl6JpFebR40J7cOWZ2mXBa3kIvEiXgzxAj6L3N7A=
secret_access_key:
secure: vItsBXb2rEFLvkWtVn/Rcxu5a5+2EwC+b7GsA0waJy9hXh6XuBAD0lnHd9re3g/4
bucket: release.solana.com
region: us-west-1
set_public: true
- provider: GitHub
auth_token:
secure: 81fEmPZ0cV1wLtNuUrcmtgxKF6ROQF1+/ft5m+fHX21z6PoeCbaNo8cTyLioWBj7
draft: false
prerelease: false
on:
appveyor_repo_tag: true

View File

@ -1,12 +1,12 @@
{
"_public_key": "ae29f4f7ad2fc92de70d470e411c8426d5d48db8817c9e3dae574b122192335f",
"environment": {
"CODECOV_TOKEN": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:3K68mE38LJ2RB98VWmjuNLFBNn1XTGR4:cR4r05/TOZQKmEZp1v4CSgUJtC6QJiOaL85QjXW0qZ061fMnsBA8AtAPMDoDq4WCGOZM1A==]",
"CRATES_IO_TOKEN": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:GGRTYDjMXksevzR6kq4Jx+FaIQZz50RU:xkbwDxcgoCyU+aT2tiI9mymigrEl6YiOr3axe3aX70ELIBKbCdPGilXP/wixvKi94g2u]",
"GEOLOCATION_API_KEY": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:U2PZLi5MU3Ru/zK1SilianEeizcMvxml:AJKf2OAtDHmJh0KyXrBnNnistItZvVVP3cZ7ZLtrVupjmWN/PzmKwSsXeCNObWS+]",
"GITHUB_TOKEN": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:0NJNlpD/O19mvOakCGBYDhIDfySxWFSC:Dz4NXv9x6ncRQ1u9sVoWOcqmkg0sI09qmefghB0GXZgPcFGgn6T0mw7ynNnbUvjyH8dLruKHauk=]",
"INFLUX_DATABASE": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:SzwHIeOVpmbTcGQOGngoFgYumsLZJUGq:t7Rpk49njsWvoM+ztv5Uwuiz]",
"INFLUX_PASSWORD": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:/MUs+q7pdGrUjzwcq+6pgIFxur4hxdqu:am22z2E2dtmw1f1J1Mq5JLcUHZsrEjQAJ0pp21M4AZeJbNO6bVb44d9zSkHj7xdN6U+GNlCk+wU=]",
"INFLUX_USERNAME": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:XjghH20xGVWro9B+epGlJaJcW8Wze0Bi:ZIdOtXudTY5TqKseDU7gVvQXfmXV99Xh]"
"CODECOV_TOKEN": "EJ[1:yGpTmjdbyjW2kjgIHkFoJv7Ue7EbUvUbqHyw6anGgWg=:JnxhrIxh09AvqdJgrVSYmb7PxSrh19aE:07WzVExCHEd1lJ1m8QizRRthGri+WBNeZRKjjEvsy5eo4gv3HD7zVEm42tVTGkqITKkBNQ==]",
"CRATES_IO_TOKEN": "EJ[1:yGpTmjdbyjW2kjgIHkFoJv7Ue7EbUvUbqHyw6anGgWg=:d0jJqC32/axwzq/N7kMRmpxKhnRrhtpt:zvcPHwkOzGnjhNkAQSejwdy1Jkr9wR1qXFFCnfIjyt/XQYubzB1tLkoly/qdmeb5]",
"GEOLOCATION_API_KEY": "EJ[1:yGpTmjdbyjW2kjgIHkFoJv7Ue7EbUvUbqHyw6anGgWg=:R4gfB6Ey4i50HyfLt4UZDLBqg3qHEUye:UfZCOgt8XI6Y2g+ivCRVoS1fjFycFs7/GSevvCqh1B50mG0+hzpEyzXQLuKG5OeI]",
"GITHUB_TOKEN": "EJ[1:yGpTmjdbyjW2kjgIHkFoJv7Ue7EbUvUbqHyw6anGgWg=:Vq2dkGTOzfEpRht0BAGHFp/hDogMvXJe:tFXHg1epVt2mq9hkuc5sRHe+KAnVREi/p8S+IZu67XRyzdiA/nGak1k860FXYuuzuaE0QWekaEc=]",
"INFLUX_DATABASE": "EJ[1:yGpTmjdbyjW2kjgIHkFoJv7Ue7EbUvUbqHyw6anGgWg=:5KI9WBkXx3R/W4m256mU5MJOE7N8aAT9:Cb8QFELZ9I60t5zhJ9h55Kcs]",
"INFLUX_PASSWORD": "EJ[1:yGpTmjdbyjW2kjgIHkFoJv7Ue7EbUvUbqHyw6anGgWg=:hQRMpLCrav+OYkNphkeM4hagdVoZv5Iw:AUO76rr6+gF1OLJA8ZLSG8wHKXgYCPNk6gRCV8rBhZBJ4KwDaxpvOhMl7bxxXG6jol7v4aRa/Lk=]",
"INFLUX_USERNAME": "EJ[1:yGpTmjdbyjW2kjgIHkFoJv7Ue7EbUvUbqHyw6anGgWg=:R7BNmQjfeqoGDAFTJu9bYTGHol2NgnYN:Q2tOT/EBcFvhFk+DKLKmVU7tLCpVC3Ui]"
}
}

View File

@ -3,16 +3,3 @@ root: ./docs/src
structure:
readme: introduction.md
summary: SUMMARY.md
redirects:
wallet: ./wallet-guide/README.md
wallet/app-wallets: ./wallet-guide/apps.md
wallet/app-wallets/trust-wallet: ./wallet-guide/trust-wallet.md
wallet/app-wallets/ledger-live: ./wallet-guide/ledger-live.md
wallet/cli-wallets: ./wallet-guide/cli.md
wallet/cli-wallets/paper-wallet: ./paper-wallet/README.md
wallet/cli-wallets/paper-wallet/paper-wallet-usage: ./paper-wallet/paper-wallet-usage.md
wallet/cli-wallets/remote-wallet: ./hardware-wallets/README.md
wallet/cli-wallets/remote-wallet/ledger: ./hardware-wallets/ledger.md
wallet/cli-wallets/file-system-wallet: ./file-system-wallet/README.md
wallet/support: ./wallet-guide/support.md

1
.gitignore vendored
View File

@ -1,6 +1,5 @@
/docs/html/
/docs/src/tests.ok
/docs/src/cli/usage.md
/docs/src/.gitbook/assets/*.svg
/farf/
/solana-release/

View File

@ -1,40 +1,9 @@
# Validate your changes with:
#
# $ curl -F 'data=@.mergify.yml' https://gh.mergify.io/validate/
# $ curl -F 'data=@.mergify.yml' https://gh.mergify.io/validate
#
# https://doc.mergify.io/
pull_request_rules:
- name: automatic merge (squash) on CI success
conditions:
- status-success=buildkite/solana
#- status-success=Travis CI - Pull Request
- status-success=ci-gate
- label=automerge
- author≠@dont-squash-my-commits
actions:
merge:
method: squash
# Join the dont-squash-my-commits group if you won't like your commits squashed
- name: automatic merge (rebase) on CI success
conditions:
- status-success=buildkite/solana
#- status-success=Travis CI - Pull Request
- status-success=ci-gate
- label=automerge
- author=@dont-squash-my-commits
actions:
merge:
method: rebase
- name: remove automerge label on CI failure
conditions:
- label=automerge
- "#status-failure!=0"
actions:
label:
remove:
- automerge
comment:
message: automerge label removed due to a CI failure
- name: remove outdated reviews
conditions:
- base=master
@ -50,13 +19,20 @@ pull_request_rules:
label:
add:
- automerge
- name: v0.23 backport
conditions:
- base=master
- label=v0.23
actions:
backport:
branches:
- v0.23
- name: v1.0 backport
conditions:
- base=master
- label=v1.0
actions:
backport:
ignore_conflicts: true
branches:
- v1.0
- name: v1.1 backport
@ -65,7 +41,6 @@ pull_request_rules:
- label=v1.1
actions:
backport:
ignore_conflicts: true
branches:
- v1.1
- name: v1.2 backport
@ -74,6 +49,5 @@ pull_request_rules:
- label=v1.2
actions:
backport:
ignore_conflicts: true
branches:
- v1.2

View File

@ -18,8 +18,6 @@ branches:
- master
- /^v\d+\.\d+/
if: type IN (api, cron) OR tag IS present
notifications:
slack:
on_success: change

View File

@ -45,7 +45,7 @@ $ git pull --rebase upstream master
If there are no functional changes, PRs can be very large and that's no
problem. If, however, your changes are making meaningful changes or additions,
then about 1,000 lines of changes is about the most you should ask a Solana
then about 1000 lines of changes is about the most you should ask a Solana
maintainer to review.
### Should I send small PRs as I develop large, new components?

6902
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,6 @@ members = [
"bench-exchange",
"bench-streamer",
"bench-tps",
"accounts-bench",
"banking-bench",
"chacha",
"chacha-cuda",
@ -11,8 +10,6 @@ members = [
"cli-config",
"client",
"core",
"dos",
"download-utils",
"faucet",
"perf",
"validator",
@ -27,7 +24,6 @@ members = [
"logger",
"log-analyzer",
"merkle-tree",
"streamer",
"measure",
"metrics",
"net-shaper",
@ -52,13 +48,11 @@ members = [
"sdk",
"sdk-c",
"scripts",
"stake-accounts",
"stake-monitor",
"sys-tuner",
"transaction-status",
"upload-perf",
"net-utils",
"version",
"vote-signer",
"cli",
"rayon-threadlimit",

View File

@ -9,7 +9,46 @@ Blockchain Rebuilt for Scale
Solana&trade; is a new blockchain architecture built from the ground up for scale. The architecture supports
up to 710 thousand transactions per second on a gigabit network.
Read all about it at [Solana: Blockchain Rebuilt for Scale](https://docs.solana.com/v/master).
Documentation
===
Before you jump into the code, review the documentation [Solana: Blockchain Rebuilt for Scale](https://docs.solana.com).
(The _latest_ development version of the docs is [available here](https://docs.solana.com/v/master).)
Release Binaries
===
Official release binaries are available at [Github Releases](https://github.com/solana-labs/solana/releases).
Additionally we provide pre-release binaries for the latest code on the edge and
beta channels. Note that these pre-release binaries may be less stable than an
official release.
### Edge channel
#### Linux (x86_64-unknown-linux-gnu)
* [solana.tar.bz2](http://release.solana.com/edge/solana-release-x86_64-unknown-linux-gnu.tar.bz2)
* [solana-install-init](http://release.solana.com/edge/solana-install-init-x86_64-unknown-linux-gnu) as a stand-alone executable
#### mac OS (x86_64-apple-darwin)
* [solana.tar.bz2](http://release.solana.com/edge/solana-release-x86_64-apple-darwin.tar.bz2)
* [solana-install-init](http://release.solana.com/edge/solana-install-init-x86_64-apple-darwin) as a stand-alone executable
#### Windows (x86_64-pc-windows-msvc)
* [solana.tar.bz2](http://release.solana.com/edge/solana-release-x86_64-pc-windows-msvc.tar.bz2)
* [solana-install-init.exe](http://release.solana.com/edge/solana-install-init-x86_64-pc-windows-msvc.exe) as a stand-alone executable
#### All platforms
* [solana-metrics.tar.bz2](http://release.solana.com.s3.amazonaws.com/edge/solana-metrics.tar.bz2)
### Beta channel
#### Linux (x86_64-unknown-linux-gnu)
* [solana.tar.bz2](http://release.solana.com/beta/solana-release-x86_64-unknown-linux-gnu.tar.bz2)
* [solana-install-init](http://release.solana.com/beta/solana-install-init-x86_64-unknown-linux-gnu) as a stand-alone executable
#### mac OS (x86_64-apple-darwin)
* [solana.tar.bz2](http://release.solana.com/beta/solana-release-x86_64-apple-darwin.tar.bz2)
* [solana-install-init](http://release.solana.com/beta/solana-install-init-x86_64-apple-darwin) as a stand-alone executable
#### Windows (x86_64-pc-windows-msvc)
* [solana.tar.bz2](http://release.solana.com/beta/solana-release-x86_64-pc-windows-msvc.tar.bz2)
* [solana-install-init.exe](http://release.solana.com/beta/solana-install-init-x86_64-pc-windows-msvc.exe) as a stand-alone executable
#### All platforms
* [solana-metrics.tar.bz2](http://release.solana.com.s3.amazonaws.com/beta/solana-metrics.tar.bz2)
Developing
===

View File

@ -1,22 +0,0 @@
[package]
authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-accounts-bench"
version = "1.1.23"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
[dependencies]
log = "0.4.6"
rayon = "1.3.0"
solana-logger = { path = "../logger", version = "1.1.23" }
solana-runtime = { path = "../runtime", version = "1.1.23" }
solana-measure = { path = "../measure", version = "1.1.23" }
solana-sdk = { path = "../sdk", version = "1.1.23" }
rand = "0.7.0"
clap = "2.33.0"
crossbeam-channel = "0.4"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@ -1,105 +0,0 @@
use clap::{value_t, App, Arg};
use rayon::prelude::*;
use solana_measure::measure::Measure;
use solana_runtime::{
accounts::{create_test_accounts, update_accounts, Accounts},
accounts_index::Ancestors,
};
use solana_sdk::pubkey::Pubkey;
use std::fs;
use std::path::PathBuf;
fn main() {
solana_logger::setup();
let matches = App::new("crate")
.about("about")
.version("version")
.arg(
Arg::with_name("num_slots")
.long("num_slots")
.takes_value(true)
.value_name("SLOTS")
.help("Number of slots to store to."),
)
.arg(
Arg::with_name("num_accounts")
.long("num_accounts")
.takes_value(true)
.value_name("NUM_ACCOUNTS")
.help("Total number of accounts"),
)
.arg(
Arg::with_name("iterations")
.long("iterations")
.takes_value(true)
.value_name("ITERATIONS")
.help("Number of bench iterations"),
)
.arg(
Arg::with_name("clean")
.long("clean")
.takes_value(false)
.help("Run clean"),
)
.get_matches();
let num_slots = value_t!(matches, "num_slots", usize).unwrap_or(4);
let num_accounts = value_t!(matches, "num_accounts", usize).unwrap_or(10_000);
let iterations = value_t!(matches, "iterations", usize).unwrap_or(20);
let clean = matches.is_present("clean");
println!("clean: {:?}", clean);
let path = PathBuf::from("farf/accounts-bench");
if fs::remove_dir_all(path.clone()).is_err() {
println!("Warning: Couldn't remove {:?}", path);
}
let accounts = Accounts::new(vec![path]);
println!("Creating {} accounts", num_accounts);
let mut create_time = Measure::start("create accounts");
let pubkeys: Vec<_> = (0..num_slots)
.into_par_iter()
.map(|slot| {
let mut pubkeys: Vec<Pubkey> = vec![];
create_test_accounts(
&accounts,
&mut pubkeys,
num_accounts / num_slots,
slot as u64,
);
pubkeys
})
.collect();
let pubkeys: Vec<_> = pubkeys.into_iter().flatten().collect();
create_time.stop();
println!(
"created {} accounts in {} slots {}",
(num_accounts / num_slots) * num_slots,
num_slots,
create_time
);
let mut ancestors: Ancestors = vec![(0, 0)].into_iter().collect();
for i in 1..num_slots {
ancestors.insert(i as u64, i - 1);
accounts.add_root(i as u64);
}
for x in 0..iterations {
if clean {
let mut time = Measure::start("clean");
accounts.accounts_db.clean_accounts();
time.stop();
println!("{}", time);
for slot in 0..num_slots {
update_accounts(&accounts, &pubkeys, ((x + 1) * num_slots + slot) as u64);
accounts.add_root((x * num_slots + slot) as u64);
}
} else {
let mut pubkeys: Vec<Pubkey> = vec![];
let mut time = Measure::start("hash");
let hash = accounts.accounts_db.update_accounts_hash(0, &ancestors);
time.stop();
println!("hash: {} {}", hash, time);
create_test_accounts(&accounts, &mut pubkeys, 1, 0);
}
}
}

View File

@ -1,6 +1,6 @@
[package]
name = "solana-archiver-lib"
version = "1.1.23"
version = "1.0.14"
description = "Solana Archiver Library"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@ -10,34 +10,30 @@ edition = "2018"
[dependencies]
bincode = "1.2.1"
crossbeam-channel = "0.4"
ed25519-dalek = "=1.0.0-pre.3"
crossbeam-channel = "0.3"
ed25519-dalek = "=1.0.0-pre.1"
log = "0.4.8"
rand = "0.7.0"
rand_chacha = "0.2.2"
solana-client = { path = "../client", version = "1.1.23" }
solana-storage-program = { path = "../programs/storage", version = "1.1.23" }
rand = "0.6.5"
rand_chacha = "0.1.1"
solana-client = { path = "../client", version = "1.0.14" }
solana-storage-program = { path = "../programs/storage", version = "1.0.14" }
thiserror = "1.0"
serde = "1.0.105"
serde_json = "1.0.48"
serde = "1.0.104"
serde_json = "1.0.46"
serde_derive = "1.0.103"
solana-net-utils = { path = "../net-utils", version = "1.1.23" }
solana-chacha = { path = "../chacha", version = "1.1.23" }
solana-chacha-sys = { path = "../chacha-sys", version = "1.1.23" }
solana-ledger = { path = "../ledger", version = "1.1.23" }
solana-logger = { path = "../logger", version = "1.1.23" }
solana-perf = { path = "../perf", version = "1.1.23" }
solana-sdk = { path = "../sdk", version = "1.1.23" }
solana-core = { path = "../core", version = "1.1.23" }
solana-streamer = { path = "../streamer", version = "1.1.23" }
solana-archiver-utils = { path = "../archiver-utils", version = "1.1.23" }
solana-metrics = { path = "../metrics", version = "1.1.23" }
solana-net-utils = { path = "../net-utils", version = "1.0.14" }
solana-chacha = { path = "../chacha", version = "1.0.14" }
solana-chacha-sys = { path = "../chacha-sys", version = "1.0.14" }
solana-ledger = { path = "../ledger", version = "1.0.14" }
solana-logger = { path = "../logger", version = "1.0.14" }
solana-perf = { path = "../perf", version = "1.0.14" }
solana-sdk = { path = "../sdk", version = "1.0.14" }
solana-core = { path = "../core", version = "1.0.14" }
solana-archiver-utils = { path = "../archiver-utils", version = "1.0.14" }
solana-metrics = { path = "../metrics", version = "1.0.14" }
[dev-dependencies]
hex = "0.4.2"
hex = "0.4.0"
[lib]
name = "solana_archiver_lib"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@ -1,7 +1,7 @@
use crate::result::ArchiverError;
use crossbeam_channel::unbounded;
use rand::{thread_rng, Rng};
use rand_chacha::{rand_core::SeedableRng, ChaChaRng};
use rand::{thread_rng, Rng, SeedableRng};
use rand_chacha::ChaChaRng;
use solana_archiver_utils::sample_file;
use solana_chacha::chacha::{chacha_cbc_encrypt_ledger, CHACHA_BLOCK_SIZE};
use solana_client::{
@ -10,14 +10,16 @@ use solana_client::{
};
use solana_core::{
cluster_info::{ClusterInfo, Node, VALIDATOR_PORT_RANGE},
cluster_slots::ClusterSlots,
contact_info::ContactInfo,
gossip_service::GossipService,
repair_service::{self, RepairService, RepairSlotRange, RepairStats, RepairStrategy},
packet::{limited_deserialize, PACKET_DATA_SIZE},
repair_service,
repair_service::{RepairService, RepairSlotRange, RepairStrategy},
serve_repair::ServeRepair,
shred_fetch_stage::ShredFetchStage,
sigverify_stage::{DisabledSigVerifier, SigVerifyStage},
storage_stage::NUM_STORAGE_SAMPLES,
streamer::{receiver, responder, PacketReceiver},
window_service::WindowService,
};
use solana_ledger::{
@ -25,7 +27,6 @@ use solana_ledger::{
};
use solana_net_utils::bind_in_range;
use solana_perf::packet::Packets;
use solana_perf::packet::{limited_deserialize, PACKET_DATA_SIZE};
use solana_perf::recycler::Recycler;
use solana_sdk::packet::Packet;
use solana_sdk::{
@ -44,7 +45,6 @@ use solana_storage_program::{
storage_contract::StorageContract,
storage_instruction::{self, StorageAccountType},
};
use solana_streamer::streamer::{receiver, responder, PacketReceiver};
use std::{
io::{self, ErrorKind},
net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket},
@ -52,7 +52,7 @@ use std::{
result,
sync::atomic::{AtomicBool, Ordering},
sync::mpsc::{channel, Receiver, Sender},
sync::Arc,
sync::{Arc, RwLock},
thread::{sleep, spawn, JoinHandle},
time::Duration,
};
@ -184,10 +184,10 @@ impl Archiver {
info!("Archiver: id: {}", keypair.pubkey());
info!("Creating cluster info....");
let cluster_info = ClusterInfo::new(node.info.clone(), keypair.clone());
let mut cluster_info = ClusterInfo::new(node.info.clone(), keypair.clone());
cluster_info.set_entrypoint(cluster_entrypoint.clone());
let cluster_info = Arc::new(cluster_info);
let cluster_slots = Arc::new(ClusterSlots::default());
let cluster_info = Arc::new(RwLock::new(cluster_info));
// Note for now, this ledger will not contain any of the existing entries
// in the ledger located at ledger_path, and will only append on newly received
// entries after being passed to window_service
@ -199,7 +199,7 @@ impl Archiver {
info!("Connecting to the cluster via {:?}", cluster_entrypoint);
let (nodes, _) =
match solana_core::gossip_service::discover_cluster(&cluster_entrypoint.gossip, 2) {
match solana_core::gossip_service::discover_cluster(&cluster_entrypoint.gossip, 1) {
Ok(nodes_and_archivers) => nodes_and_archivers,
Err(e) => {
//shutdown services before exiting
@ -262,7 +262,6 @@ impl Archiver {
repair_socket,
shred_fetch_receiver,
slot_sender,
cluster_slots,
) {
Ok(window_service) => window_service,
Err(e) => {
@ -307,7 +306,7 @@ impl Archiver {
fn run(
meta: &mut ArchiverMeta,
blockstore: &Arc<Blockstore>,
cluster_info: Arc<ClusterInfo>,
cluster_info: Arc<RwLock<ClusterInfo>>,
archiver_keypair: &Arc<Keypair>,
storage_keypair: &Arc<Keypair>,
exit: &Arc<AtomicBool>,
@ -364,12 +363,12 @@ impl Archiver {
}
fn redeem_rewards(
cluster_info: &ClusterInfo,
cluster_info: &Arc<RwLock<ClusterInfo>>,
archiver_keypair: &Arc<Keypair>,
storage_keypair: &Arc<Keypair>,
client_commitment: CommitmentConfig,
) {
let nodes = cluster_info.tvu_peers();
let nodes = cluster_info.read().unwrap().tvu_peers();
let client = solana_core::gossip_service::get_client(&nodes);
if let Ok(Some(account)) =
@ -401,10 +400,9 @@ impl Archiver {
}
// Find a segment to replicate and download it.
#[allow(clippy::too_many_arguments)]
fn setup(
meta: &mut ArchiverMeta,
cluster_info: Arc<ClusterInfo>,
cluster_info: Arc<RwLock<ClusterInfo>>,
blockstore: &Arc<Blockstore>,
exit: &Arc<AtomicBool>,
node_info: &ContactInfo,
@ -412,7 +410,6 @@ impl Archiver {
repair_socket: Arc<UdpSocket>,
shred_fetch_receiver: PacketReceiver,
slot_sender: Sender<u64>,
cluster_slots: Arc<ClusterSlots>,
) -> Result<WindowService> {
let slots_per_segment =
match Self::get_segment_config(&cluster_info, meta.client_commitment) {
@ -470,7 +467,6 @@ impl Archiver {
RepairStrategy::RepairRange(repair_slot_range),
&Arc::new(LeaderScheduleCache::default()),
|_, _, _, _| true,
cluster_slots,
);
info!("waiting for ledger download");
Self::wait_for_segment_download(
@ -490,7 +486,7 @@ impl Archiver {
blockstore: &Arc<Blockstore>,
exit: &Arc<AtomicBool>,
node_info: &ContactInfo,
cluster_info: Arc<ClusterInfo>,
cluster_info: Arc<RwLock<ClusterInfo>>,
) {
info!(
"window created, waiting for ledger download starting at slot {:?}",
@ -518,8 +514,11 @@ impl Archiver {
contact_info.tvu = "0.0.0.0:0".parse().unwrap();
contact_info.wallclock = timestamp();
// copy over the adopted shred_version from the entrypoint
contact_info.shred_version = cluster_info.my_shred_version();
cluster_info.update_contact_info(|current| *current = contact_info);
contact_info.shred_version = cluster_info.read().unwrap().my_data().shred_version;
{
let mut cluster_info_w = cluster_info.write().unwrap();
cluster_info_w.insert_self(contact_info);
}
}
fn encrypt_ledger(meta: &mut ArchiverMeta, blockstore: &Arc<Blockstore>) -> Result<()> {
@ -622,12 +621,12 @@ impl Archiver {
fn submit_mining_proof(
meta: &ArchiverMeta,
cluster_info: &ClusterInfo,
cluster_info: &Arc<RwLock<ClusterInfo>>,
archiver_keypair: &Arc<Keypair>,
storage_keypair: &Arc<Keypair>,
) {
// No point if we've got no storage account...
let nodes = cluster_info.tvu_peers();
let nodes = cluster_info.read().unwrap().tvu_peers();
let client = solana_core::gossip_service::get_client(&nodes);
let storage_balance = client
.poll_get_balance_with_commitment(&storage_keypair.pubkey(), meta.client_commitment);
@ -685,10 +684,13 @@ impl Archiver {
}
fn get_segment_config(
cluster_info: &ClusterInfo,
cluster_info: &Arc<RwLock<ClusterInfo>>,
client_commitment: CommitmentConfig,
) -> Result<u64> {
let rpc_peers = cluster_info.all_rpc_peers();
let rpc_peers = {
let cluster_info = cluster_info.read().unwrap();
cluster_info.all_rpc_peers()
};
debug!("rpc peers: {:?}", rpc_peers);
if !rpc_peers.is_empty() {
let rpc_client = {
@ -696,10 +698,16 @@ impl Archiver {
RpcClient::new_socket(rpc_peers[node_index].rpc)
};
Ok(rpc_client
.send::<u64>(
RpcRequest::GetSlotsPerSegment,
.send(
&RpcRequest::GetSlotsPerSegment,
serde_json::json!([client_commitment]),
0,
)
.map_err(|err| {
warn!("Error while making rpc request {:?}", err);
ArchiverError::ClientError(err)
})?
.as_u64()
.unwrap())
} else {
Err(ArchiverError::NoRpcPeers)
@ -708,7 +716,7 @@ impl Archiver {
/// Waits until the first segment is ready, and returns the current segment
fn poll_for_segment(
cluster_info: &ClusterInfo,
cluster_info: &Arc<RwLock<ClusterInfo>>,
slots_per_segment: u64,
previous_blockhash: &Hash,
exit: &Arc<AtomicBool>,
@ -728,24 +736,38 @@ impl Archiver {
/// Poll for a different blockhash and associated max_slot than `previous_blockhash`
fn poll_for_blockhash_and_slot(
cluster_info: &ClusterInfo,
cluster_info: &Arc<RwLock<ClusterInfo>>,
slots_per_segment: u64,
previous_blockhash: &Hash,
exit: &Arc<AtomicBool>,
) -> Result<(Hash, u64)> {
info!("waiting for the next turn...");
loop {
let rpc_peers = cluster_info.all_rpc_peers();
let rpc_peers = {
let cluster_info = cluster_info.read().unwrap();
cluster_info.all_rpc_peers()
};
debug!("rpc peers: {:?}", rpc_peers);
if !rpc_peers.is_empty() {
let rpc_client = {
let node_index = thread_rng().gen_range(0, rpc_peers.len());
RpcClient::new_socket(rpc_peers[node_index].rpc)
};
let response = rpc_client
.send(
&RpcRequest::GetStorageTurn,
serde_json::value::Value::Null,
0,
)
.map_err(|err| {
warn!("Error while making rpc request {:?}", err);
ArchiverError::ClientError(err)
})?;
let RpcStorageTurn {
blockhash: storage_blockhash,
slot: turn_slot,
} = rpc_client.send(RpcRequest::GetStorageTurn, serde_json::value::Value::Null)?;
} = serde_json::from_value::<RpcStorageTurn>(response)
.map_err(ArchiverError::JsonError)?;
let turn_blockhash = storage_blockhash.parse().map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
@ -817,14 +839,13 @@ impl Archiver {
repair_service::MAX_REPAIR_LENGTH,
&repair_slot_range,
);
let mut repair_stats = RepairStats::default();
//iter over the repairs and send them
if let Ok(repairs) = repairs {
let reqs: Vec<_> = repairs
.into_iter()
.filter_map(|repair_request| {
serve_repair
.map_repair_request(&repair_request, &mut repair_stats, Some(0))
.map_repair_request(&repair_request)
.map(|result| ((archiver_info.gossip, result), repair_request))
.ok()
})

View File

@ -1,3 +1,4 @@
use serde_json;
use solana_client::client_error;
use solana_ledger::blockstore;
use solana_sdk::transport;

View File

@ -1,6 +1,6 @@
[package]
name = "solana-archiver-utils"
version = "1.1.23"
version = "1.0.14"
description = "Solana Archiver Utils"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@ -10,19 +10,16 @@ edition = "2018"
[dependencies]
log = "0.4.8"
rand = "0.7.0"
solana-chacha = { path = "../chacha", version = "1.1.23" }
solana-chacha-sys = { path = "../chacha-sys", version = "1.1.23" }
solana-ledger = { path = "../ledger", version = "1.1.23" }
solana-logger = { path = "../logger", version = "1.1.23" }
solana-perf = { path = "../perf", version = "1.1.23" }
solana-sdk = { path = "../sdk", version = "1.1.23" }
rand = "0.6.5"
solana-chacha = { path = "../chacha", version = "1.0.14" }
solana-chacha-sys = { path = "../chacha-sys", version = "1.0.14" }
solana-ledger = { path = "../ledger", version = "1.0.14" }
solana-logger = { path = "../logger", version = "1.0.14" }
solana-perf = { path = "../perf", version = "1.0.14" }
solana-sdk = { path = "../sdk", version = "1.0.14" }
[dev-dependencies]
hex = "0.4.2"
hex = "0.4.0"
[lib]
name = "solana_archiver_utils"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@ -2,22 +2,19 @@
authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-archiver"
version = "1.1.23"
version = "1.0.14"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
[dependencies]
clap = "2.33.0"
console = "0.10.0"
solana-clap-utils = { path = "../clap-utils", version = "1.1.23" }
solana-core = { path = "../core", version = "1.1.23" }
solana-logger = { path = "../logger", version = "1.1.23" }
solana-metrics = { path = "../metrics", version = "1.1.23" }
solana-archiver-lib = { path = "../archiver-lib", version = "1.1.23" }
solana-net-utils = { path = "../net-utils", version = "1.1.23" }
solana-sdk = { path = "../sdk", version = "1.1.23" }
console = "0.9.2"
solana-clap-utils = { path = "../clap-utils", version = "1.0.14" }
solana-core = { path = "../core", version = "1.0.14" }
solana-logger = { path = "../logger", version = "1.0.14" }
solana-metrics = { path = "../metrics", version = "1.0.14" }
solana-archiver-lib = { path = "../archiver-lib", version = "1.0.14" }
solana-net-utils = { path = "../net-utils", version = "1.0.14" }
solana-sdk = { path = "../sdk", version = "1.0.14" }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@ -2,26 +2,19 @@
authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-banking-bench"
version = "1.1.23"
version = "1.0.14"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
[dependencies]
clap = "2.33.1"
crossbeam-channel = "0.4"
log = "0.4.6"
rand = "0.7.0"
rayon = "1.3.0"
solana-core = { path = "../core", version = "1.1.23" }
solana-streamer = { path = "../streamer", version = "1.1.23" }
solana-perf = { path = "../perf", version = "1.1.23" }
solana-ledger = { path = "../ledger", version = "1.1.23" }
solana-logger = { path = "../logger", version = "1.1.23" }
solana-runtime = { path = "../runtime", version = "1.1.23" }
solana-measure = { path = "../measure", version = "1.1.23" }
solana-sdk = { path = "../sdk", version = "1.1.23" }
solana-version = { path = "../version", version = "1.1.23" }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
rayon = "1.2.0"
solana-core = { path = "../core", version = "1.0.14" }
solana-ledger = { path = "../ledger", version = "1.0.14" }
solana-logger = { path = "../logger", version = "1.0.14" }
solana-runtime = { path = "../runtime", version = "1.0.14" }
solana-measure = { path = "../measure", version = "1.0.14" }
solana-sdk = { path = "../sdk", version = "1.0.14" }
rand = "0.6.5"
crossbeam-channel = "0.3"

View File

@ -1,38 +1,30 @@
use clap::{crate_description, crate_name, value_t, App, Arg};
use crossbeam_channel::unbounded;
use log::*;
use rand::{thread_rng, Rng};
use rayon::prelude::*;
use solana_core::{
banking_stage::{create_test_recorder, BankingStage},
cluster_info::ClusterInfo,
cluster_info::Node,
poh_recorder::PohRecorder,
poh_recorder::WorkingBankEntry,
};
use solana_ledger::{
bank_forks::BankForks,
blockstore::Blockstore,
genesis_utils::{create_genesis_config, GenesisConfigInfo},
get_tmp_ledger_path,
};
use solana_core::banking_stage::{create_test_recorder, BankingStage};
use solana_core::cluster_info::ClusterInfo;
use solana_core::cluster_info::Node;
use solana_core::genesis_utils::{create_genesis_config, GenesisConfigInfo};
use solana_core::packet::to_packets_chunked;
use solana_core::poh_recorder::PohRecorder;
use solana_core::poh_recorder::WorkingBankEntry;
use solana_ledger::bank_forks::BankForks;
use solana_ledger::{blockstore::Blockstore, get_tmp_ledger_path};
use solana_measure::measure::Measure;
use solana_perf::packet::to_packets_chunked;
use solana_runtime::bank::Bank;
use solana_sdk::{
hash::Hash,
pubkey::Pubkey,
signature::Keypair,
signature::Signature,
system_transaction,
timing::{duration_as_us, timestamp},
transaction::Transaction,
};
use std::{
sync::{atomic::Ordering, mpsc::Receiver, Arc, Mutex},
thread::sleep,
time::{Duration, Instant},
};
use solana_sdk::hash::Hash;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::Keypair;
use solana_sdk::signature::Signature;
use solana_sdk::system_transaction;
use solana_sdk::timing::{duration_as_us, timestamp};
use solana_sdk::transaction::Transaction;
use std::sync::atomic::Ordering;
use std::sync::mpsc::Receiver;
use std::sync::{Arc, Mutex, RwLock};
use std::thread::sleep;
use std::time::{Duration, Instant};
fn check_txs(
receiver: &Arc<Receiver<WorkingBankEntry>>,
@ -65,22 +57,15 @@ fn check_txs(
no_bank
}
fn make_accounts_txs(
total_num_transactions: usize,
hash: Hash,
same_payer: bool,
) -> Vec<Transaction> {
fn make_accounts_txs(txes: usize, mint_keypair: &Keypair, hash: Hash) -> Vec<Transaction> {
let to_pubkey = Pubkey::new_rand();
let payer_key = Keypair::new();
let dummy = system_transaction::transfer(&payer_key, &to_pubkey, 1, hash);
(0..total_num_transactions)
let dummy = system_transaction::transfer(mint_keypair, &to_pubkey, 1, hash);
(0..txes)
.into_par_iter()
.map(|_| {
let mut new = dummy.clone();
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
if !same_payer {
new.message.account_keys[0] = Pubkey::new_rand();
}
new.message.account_keys[0] = Pubkey::new_rand();
new.message.account_keys[1] = Pubkey::new_rand();
new.signatures = vec![Signature::new(&sig[0..64])];
new
@ -104,61 +89,13 @@ fn bytes_as_usize(bytes: &[u8]) -> usize {
bytes[0] as usize | (bytes[1] as usize) << 8
}
#[allow(clippy::cognitive_complexity)]
fn main() {
solana_logger::setup();
let matches = App::new(crate_name!())
.about(crate_description!())
.version(solana_version::version!())
.arg(
Arg::with_name("num_chunks")
.long("num-chunks")
.takes_value(true)
.value_name("SIZE")
.help("Number of transaction chunks."),
)
.arg(
Arg::with_name("packets_per_chunk")
.long("packets-per-chunk")
.takes_value(true)
.value_name("SIZE")
.help("Packets per chunk"),
)
.arg(
Arg::with_name("skip_sanity")
.long("skip-sanity")
.takes_value(false)
.help("Skip transaction sanity execution"),
)
.arg(
Arg::with_name("same_payer")
.long("same-payer")
.takes_value(false)
.help("Use the same payer for transfers"),
)
.arg(
Arg::with_name("iterations")
.long("iterations")
.takes_value(true)
.help("Number of iterations"),
)
.arg(
Arg::with_name("num_threads")
.long("num-threads")
.takes_value(true)
.help("Number of iterations"),
)
.get_matches();
let num_threads =
value_t!(matches, "num_threads", usize).unwrap_or(BankingStage::num_threads() as usize);
let num_threads = BankingStage::num_threads() as usize;
// a multiple of packet chunk duplicates to avoid races
let num_chunks = value_t!(matches, "num_chunks", usize).unwrap_or(16);
let packets_per_chunk = value_t!(matches, "packets_per_chunk", usize).unwrap_or(192);
let iterations = value_t!(matches, "iterations", usize).unwrap_or(1000);
let total_num_transactions = num_chunks * num_threads * packets_per_chunk;
const CHUNKS: usize = 8 * 2;
const PACKETS_PER_BATCH: usize = 192;
let txes = PACKETS_PER_BATCH * num_threads * CHUNKS;
let mint_total = 1_000_000_000_000;
let GenesisConfigInfo {
genesis_config,
@ -172,44 +109,34 @@ fn main() {
let mut bank_forks = BankForks::new(0, bank0);
let mut bank = bank_forks.working_bank();
info!("threads: {} txs: {}", num_threads, total_num_transactions);
info!("threads: {} txs: {}", num_threads, txes);
let same_payer = matches.is_present("same_payer");
let mut transactions =
make_accounts_txs(total_num_transactions, genesis_config.hash(), same_payer);
let mut transactions = make_accounts_txs(txes, &mint_keypair, genesis_config.hash());
// fund all the accounts
transactions.iter().for_each(|tx| {
let mut fund = system_transaction::transfer(
let fund = system_transaction::transfer(
&mint_keypair,
&tx.message.account_keys[0],
mint_total / total_num_transactions as u64,
mint_total / txes as u64,
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();
fund.signatures = vec![Signature::new(&sig[0..64])];
let x = bank.process_transaction(&fund);
x.unwrap();
});
let skip_sanity = matches.is_present("skip_sanity");
if !skip_sanity {
//sanity check, make sure all the transactions can execute sequentially
transactions.iter().for_each(|tx| {
let res = bank.process_transaction(&tx);
assert!(res.is_ok(), "sanity test transactions error: {:?}", res);
});
bank.clear_signatures();
//sanity check, make sure all the transactions can execute in parallel
let res = bank.process_transactions(&transactions);
for r in res {
assert!(r.is_ok(), "sanity parallel execution error: {:?}", r);
}
bank.clear_signatures();
//sanity check, make sure all the transactions can execute sequentially
transactions.iter().for_each(|tx| {
let res = bank.process_transaction(&tx);
assert!(res.is_ok(), "sanity test transactions");
});
bank.clear_signatures();
//sanity check, make sure all the transactions can execute in parallel
let res = bank.process_transactions(&transactions);
for r in res {
assert!(r.is_ok(), "sanity parallel execution");
}
let mut verified: Vec<_> = to_packets_chunked(&transactions.clone(), packets_per_chunk);
bank.clear_signatures();
let mut verified: Vec<_> = to_packets_chunked(&transactions.clone(), PACKETS_PER_BATCH);
let ledger_path = get_tmp_ledger_path!();
{
let blockstore = Arc::new(
@ -218,7 +145,7 @@ fn main() {
let (exit, poh_recorder, poh_service, signal_receiver) =
create_test_recorder(&bank, &blockstore, None);
let cluster_info = ClusterInfo::new_with_invalid_keypair(Node::new_localhost().info);
let cluster_info = Arc::new(cluster_info);
let cluster_info = Arc::new(RwLock::new(cluster_info));
let banking_stage = BankingStage::new(
&cluster_info,
&poh_recorder,
@ -228,7 +155,7 @@ fn main() {
);
poh_recorder.lock().unwrap().set_bank(&bank);
let chunk_len = verified.len() / num_chunks;
let chunk_len = verified.len() / CHUNKS;
let mut start = 0;
// This is so that the signal_receiver does not go out of scope after the closure.
@ -237,17 +164,17 @@ fn main() {
let signal_receiver = Arc::new(signal_receiver);
let mut total_us = 0;
let mut tx_total_us = 0;
let base_tx_count = bank.transaction_count();
let mut txs_processed = 0;
let mut root = 1;
let collector = Pubkey::new_rand();
const ITERS: usize = 1_000;
let config = Config {
packets_per_batch: packets_per_chunk,
packets_per_batch: PACKETS_PER_BATCH,
chunk_len,
num_threads,
};
let mut total_sent = 0;
for _ in 0..iterations {
for _ in 0..ITERS {
let now = Instant::now();
let mut sent = 0;
@ -288,11 +215,7 @@ fn main() {
sleep(Duration::from_millis(5));
}
}
if check_txs(
&signal_receiver,
total_num_transactions / num_chunks,
&poh_recorder,
) {
if check_txs(&signal_receiver, txes / CHUNKS, &poh_recorder) {
debug!(
"resetting bank {} tx count: {} txs_proc: {}",
bank.slot(),
@ -323,7 +246,7 @@ fn main() {
poh_recorder.lock().unwrap().set_bank(&bank);
assert!(poh_recorder.lock().unwrap().bank().is_some());
if bank.slot() > 32 {
bank_forks.set_root(root, &None, None);
bank_forks.set_root(root, &None);
root += 1;
}
debug!(
@ -344,7 +267,7 @@ fn main() {
debug!(
"time: {} us checked: {} sent: {}",
duration_as_us(&now.elapsed()),
total_num_transactions / num_chunks,
txes / CHUNKS,
sent,
);
total_sent += sent;
@ -355,26 +278,20 @@ fn main() {
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
tx.signatures[0] = Signature::new(&sig[0..64]);
}
verified = to_packets_chunked(&transactions.clone(), packets_per_chunk);
verified = to_packets_chunked(&transactions.clone(), PACKETS_PER_BATCH);
}
start += chunk_len;
start %= verified.len();
}
let txs_processed = bank_forks.working_bank().transaction_count();
debug!("processed: {} base: {}", txs_processed, base_tx_count);
eprintln!(
"{{'name': 'banking_bench_total', 'median': '{:.2}'}}",
"{{'name': 'banking_bench_total', 'median': '{}'}}",
(1000.0 * 1000.0 * total_sent as f64) / (total_us as f64),
);
eprintln!(
"{{'name': 'banking_bench_tx_total', 'median': '{:.2}'}}",
"{{'name': 'banking_bench_tx_total', 'median': '{}'}}",
(1000.0 * 1000.0 * total_sent as f64) / (tx_total_us as f64),
);
eprintln!(
"{{'name': 'banking_bench_success_tx_total', 'median': '{:.2}'}}",
(1000.0 * 1000.0 * (txs_processed - base_tx_count) as f64) / (total_us as f64),
);
drop(verified_sender);
drop(vote_sender);

View File

@ -2,7 +2,7 @@
authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-bench-exchange"
version = "1.1.23"
version = "1.0.14"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@ -10,28 +10,25 @@ publish = false
[dependencies]
clap = "2.32.0"
itertools = "0.9.0"
itertools = "0.8.2"
log = "0.4.8"
num-derive = "0.3"
num-traits = "0.2"
rand = "0.7.0"
rayon = "1.3.0"
serde_json = "1.0.48"
rand = "0.6.5"
rayon = "1.2.0"
serde_json = "1.0.46"
serde_yaml = "0.8.11"
solana-clap-utils = { path = "../clap-utils", version = "1.1.23" }
solana-core = { path = "../core", version = "1.1.23" }
solana-genesis = { path = "../genesis", version = "1.1.23" }
solana-client = { path = "../client", version = "1.1.23" }
solana-faucet = { path = "../faucet", version = "1.1.23" }
solana-exchange-program = { path = "../programs/exchange", version = "1.1.23" }
solana-logger = { path = "../logger", version = "1.1.23" }
solana-metrics = { path = "../metrics", version = "1.1.23" }
solana-net-utils = { path = "../net-utils", version = "1.1.23" }
solana-runtime = { path = "../runtime", version = "1.1.23" }
solana-sdk = { path = "../sdk", version = "1.1.23" }
solana-clap-utils = { path = "../clap-utils", version = "1.0.14" }
solana-core = { path = "../core", version = "1.0.14" }
solana-genesis = { path = "../genesis", version = "1.0.14" }
solana-client = { path = "../client", version = "1.0.14" }
solana-faucet = { path = "../faucet", version = "1.0.14" }
solana-exchange-program = { path = "../programs/exchange", version = "1.0.14" }
solana-logger = { path = "../logger", version = "1.0.14" }
solana-metrics = { path = "../metrics", version = "1.0.14" }
solana-net-utils = { path = "../net-utils", version = "1.0.14" }
solana-runtime = { path = "../runtime", version = "1.0.14" }
solana-sdk = { path = "../sdk", version = "1.0.14" }
[dev-dependencies]
solana-local-cluster = { path = "../local-cluster", version = "1.1.23" }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
solana-local-cluster = { path = "../local-cluster", version = "1.0.14" }

View File

@ -86,7 +86,7 @@ fn test_exchange_bank_client() {
solana_logger::setup();
let (genesis_config, identity) = create_genesis_config(100_000_000_000_000);
let mut bank = Bank::new(&genesis_config);
bank.add_static_program("exchange_program", id(), process_instruction);
bank.add_instruction_processor(id(), process_instruction);
let clients = vec![BankClient::new(bank)];
let mut config = Config::default();

View File

@ -2,17 +2,14 @@
authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-bench-streamer"
version = "1.1.23"
version = "1.0.14"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
[dependencies]
clap = "2.33.0"
solana-clap-utils = { path = "../clap-utils", version = "1.1.23" }
solana-streamer = { path = "../streamer", version = "1.1.23" }
solana-logger = { path = "../logger", version = "1.1.23" }
solana-net-utils = { path = "../net-utils", version = "1.1.23" }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
solana-clap-utils = { path = "../clap-utils", version = "1.0.14" }
solana-core = { path = "../core", version = "1.0.14" }
solana-logger = { path = "../logger", version = "1.0.14" }
solana-net-utils = { path = "../net-utils", version = "1.0.14" }

View File

@ -1,6 +1,6 @@
use clap::{crate_description, crate_name, App, Arg};
use solana_streamer::packet::{Packet, Packets, PacketsRecycler, PACKET_DATA_SIZE};
use solana_streamer::streamer::{receiver, PacketReceiver};
use solana_core::packet::{Packet, Packets, PacketsRecycler, PACKET_DATA_SIZE};
use solana_core::streamer::{receiver, PacketReceiver};
use std::cmp::max;
use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};

View File

@ -2,7 +2,7 @@
authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-bench-tps"
version = "1.1.23"
version = "1.0.14"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@ -11,30 +11,27 @@ homepage = "https://solana.com/"
bincode = "1.2.1"
clap = "2.33.0"
log = "0.4.8"
rayon = "1.3.0"
serde_json = "1.0.48"
rayon = "1.2.0"
serde_json = "1.0.46"
serde_yaml = "0.8.11"
solana-clap-utils = { path = "../clap-utils", version = "1.1.23" }
solana-core = { path = "../core", version = "1.1.23" }
solana-genesis = { path = "../genesis", version = "1.1.23" }
solana-client = { path = "../client", version = "1.1.23" }
solana-faucet = { path = "../faucet", version = "1.1.23" }
#solana-librapay = { path = "../programs/librapay", version = "1.1.8", optional = true }
solana-logger = { path = "../logger", version = "1.1.23" }
solana-metrics = { path = "../metrics", version = "1.1.23" }
solana-measure = { path = "../measure", version = "1.1.23" }
solana-net-utils = { path = "../net-utils", version = "1.1.23" }
solana-runtime = { path = "../runtime", version = "1.1.23" }
solana-sdk = { path = "../sdk", version = "1.1.23" }
#solana-move-loader-program = { path = "../programs/move_loader", version = "1.1.8", optional = true }
solana-clap-utils = { path = "../clap-utils", version = "1.0.14" }
solana-core = { path = "../core", version = "1.0.14" }
solana-genesis = { path = "../genesis", version = "1.0.14" }
solana-client = { path = "../client", version = "1.0.14" }
solana-faucet = { path = "../faucet", version = "1.0.14" }
solana-librapay = { path = "../programs/librapay", version = "1.0.14", optional = true }
solana-logger = { path = "../logger", version = "1.0.14" }
solana-metrics = { path = "../metrics", version = "1.0.14" }
solana-measure = { path = "../measure", version = "1.0.14" }
solana-net-utils = { path = "../net-utils", version = "1.0.14" }
solana-runtime = { path = "../runtime", version = "1.0.14" }
solana-sdk = { path = "../sdk", version = "1.0.14" }
solana-move-loader-program = { path = "../programs/move_loader", version = "1.0.14", optional = true }
[dev-dependencies]
serial_test = "0.4.0"
serial_test = "0.3.2"
serial_test_derive = "0.4.0"
solana-local-cluster = { path = "../local-cluster", version = "1.1.23" }
solana-local-cluster = { path = "../local-cluster", version = "1.0.14" }
#[features]
#move = ["solana-librapay", "solana-move-loader-program"]
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[features]
move = ["solana-librapay", "solana-move-loader-program"]

View File

@ -1,6 +1,6 @@
[package]
name = "solana-chacha-cuda"
version = "1.1.23"
version = "1.0.14"
description = "Solana Chacha Cuda APIs"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@ -10,18 +10,15 @@ edition = "2018"
[dependencies]
log = "0.4.8"
solana-archiver-utils = { path = "../archiver-utils", version = "1.1.23" }
solana-chacha = { path = "../chacha", version = "1.1.23" }
solana-ledger = { path = "../ledger", version = "1.1.23" }
solana-logger = { path = "../logger", version = "1.1.23" }
solana-perf = { path = "../perf", version = "1.1.23" }
solana-sdk = { path = "../sdk", version = "1.1.23" }
solana-archiver-utils = { path = "../archiver-utils", version = "1.0.14" }
solana-chacha = { path = "../chacha", version = "1.0.14" }
solana-ledger = { path = "../ledger", version = "1.0.14" }
solana-logger = { path = "../logger", version = "1.0.14" }
solana-perf = { path = "../perf", version = "1.0.14" }
solana-sdk = { path = "../sdk", version = "1.0.14" }
[dev-dependencies]
hex-literal = "0.2.1"
[lib]
name = "solana_chacha_cuda"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@ -1,6 +1,6 @@
[package]
name = "solana-chacha-sys"
version = "1.1.23"
version = "1.0.14"
description = "Solana chacha-sys"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@ -10,6 +10,3 @@ edition = "2018"
[build-dependencies]
cc = "1.0.49"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@ -1,6 +1,6 @@
[package]
name = "solana-chacha"
version = "1.1.23"
version = "1.0.14"
description = "Solana Chacha APIs"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@ -10,19 +10,16 @@ edition = "2018"
[dependencies]
log = "0.4.8"
rand = "0.7.0"
rand_chacha = "0.2.2"
solana-chacha-sys = { path = "../chacha-sys", version = "1.1.23" }
solana-ledger = { path = "../ledger", version = "1.1.23" }
solana-logger = { path = "../logger", version = "1.1.23" }
solana-perf = { path = "../perf", version = "1.1.23" }
solana-sdk = { path = "../sdk", version = "1.1.23" }
rand = "0.6.5"
rand_chacha = "0.1.1"
solana-chacha-sys = { path = "../chacha-sys", version = "1.0.14" }
solana-ledger = { path = "../ledger", version = "1.0.14" }
solana-logger = { path = "../logger", version = "1.0.14" }
solana-perf = { path = "../perf", version = "1.0.14" }
solana-sdk = { path = "../sdk", version = "1.0.14" }
[dev-dependencies]
hex-literal = "0.2.1"
[lib]
name = "solana_chacha"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@ -2,16 +2,6 @@
# Build steps that run after the primary pipeline on pushes and tags.
# Pull requests to not run these steps.
steps:
- command: "ci/publish-tarball.sh"
timeout_in_minutes: 60
name: "publish tarball"
- command: "ci/publish-docs.sh"
timeout_in_minutes: 15
name: "publish docs"
- command: "ci/publish-bpf-sdk.sh"
timeout_in_minutes: 5
name: "publish bpf sdk"
- wait
- command: "sdk/docker-solana/build.sh"
timeout_in_minutes: 60
name: "publish docker"
@ -19,6 +9,12 @@ steps:
timeout_in_minutes: 240
name: "publish crate"
branches: "!master"
# - command: ". ci/rust-version.sh; ci/docker-run.sh $$rust_stable_docker_image ci/test-move.sh"
# name: "move"
# timeout_in_minutes: 20
- command: "ci/publish-bpf-sdk.sh"
timeout_in_minutes: 5
name: "publish bpf sdk"
- command: "ci/publish-tarball.sh"
timeout_in_minutes: 60
name: "publish tarball"
- command: "ci/publish-docs.sh"
timeout_in_minutes: 15
name: "publish docs"

View File

@ -2,15 +2,12 @@
# other than those in docs/ are modified
steps:
- command: ". ci/rust-version.sh; ci/docker-run.sh $$rust_nightly_docker_image ci/test-coverage.sh"
name: "coverage"
timeout_in_minutes: 30
- wait
- command: ". ci/rust-version.sh; ci/docker-run.sh $$rust_stable_docker_image ci/test-stable.sh"
name: "stable"
timeout_in_minutes: 60
artifact_paths: "log-*.txt"
- command: "ci/shellcheck.sh"
name: "shellcheck"
timeout_in_minutes: 5
- wait
- command: "ci/test-stable-perf.sh"
name: "stable-perf"
timeout_in_minutes: 40
@ -20,7 +17,21 @@ steps:
- command: "ci/test-bench.sh"
name: "bench"
timeout_in_minutes: 30
- command: ". ci/rust-version.sh; ci/docker-run.sh $$rust_stable_docker_image ci/test-stable.sh"
name: "stable"
timeout_in_minutes: 60
artifact_paths: "log-*.txt"
agents:
- "queue=rpc-test-capable"
- command: ". ci/rust-version.sh; ci/docker-run.sh $$rust_stable_docker_image ci/test-move.sh"
name: "move"
timeout_in_minutes: 20
- command: ". ci/rust-version.sh; ci/docker-run.sh $$rust_stable_docker_image ci/test-local-cluster.sh"
name: "local-cluster"
timeout_in_minutes: 45
artifact_paths: "log-*.txt"
- command: ". ci/rust-version.sh; ci/docker-run.sh $$rust_nightly_docker_image ci/test-coverage.sh"
name: "coverage"
timeout_in_minutes: 30
agents:
- "queue=rpc-test-capable"

View File

@ -5,15 +5,9 @@
# Release tags use buildkite-release.yml instead
steps:
- command: "ci/test-sanity.sh"
name: "sanity"
timeout_in_minutes: 5
- command: ". ci/rust-version.sh; ci/docker-run.sh $$rust_nightly_docker_image ci/test-checks.sh"
name: "checks"
timeout_in_minutes: 20
- command: "ci/shellcheck.sh"
name: "shellcheck"
timeout_in_minutes: 5
- wait

View File

@ -49,7 +49,7 @@ else
# ~/.cargo
ARGS+=(--volume "$PWD:/home")
fi
ARGS+=(--env "HOME=/home" --env "CARGO_HOME=/home/.cargo")
ARGS+=(--env "CARGO_HOME=/home/.cargo")
# kcov tries to set the personality of the binary which docker
# doesn't allow by default.

View File

@ -1,4 +1,4 @@
FROM solanalabs/rust:1.43.0
FROM solanalabs/rust:1.42.0
ARG date
RUN set -x \

View File

@ -1,6 +1,6 @@
# Note: when the rust version is changed also modify
# ci/rust-version.sh to pick up the new image tag
FROM rust:1.43.0
FROM rust:1.42.0
# Add Google Protocol Buffers for Libra's metrics library.
ENV PROTOC_VERSION 3.8.0
@ -32,7 +32,6 @@ RUN set -x \
&& cargo install cargo-audit \
&& cargo install svgbob_cli \
&& cargo install mdbook \
&& cargo install mdbook-linkcheck \
&& rustc --version \
&& cargo --version \
&& curl -OL https://github.com/google/protobuf/releases/download/v$PROTOC_VERSION/$PROTOC_ZIP \

View File

@ -71,7 +71,7 @@ echo --- Creating release tarball
export CHANNEL
source ci/rust-version.sh stable
scripts/cargo-install-all.sh +"$rust_stable" solana-release
scripts/cargo-install-all.sh +"$rust_stable" --use-move solana-release
tar cvf solana-release-$TARGET.tar solana-release
bzip2 solana-release-$TARGET.tar
@ -95,8 +95,9 @@ fi
source ci/upload-ci-artifact.sh
for file in solana-release-$TARGET.tar.bz2 solana-release-$TARGET.yml solana-install-init-"$TARGET"* $MAYBE_TARBALLS; do
upload-ci-artifact "$file"
if [[ -n $DO_NOT_PUBLISH_TAR ]]; then
upload-ci-artifact "$file"
echo "Skipped $file due to DO_NOT_PUBLISH_TAR"
continue
fi

View File

@ -2,10 +2,8 @@
set -e
cd "$(dirname "$0")/.."
# shellcheck source=multinode-demo/common.sh
source multinode-demo/common.sh
rm -rf config/run/init-completed config/ledger config/snapshot-ledger
rm -f config/run/init-completed
timeout 15 ./run.sh &
pid=$!
@ -19,16 +17,6 @@ while [[ ! -f config/run/init-completed ]]; do
fi
done
snapshot_slot=1
# wait a bit longer than snapshot_slot
while [[ $($solana_cli --url http://localhost:8899 slot --commitment recent) -le $((snapshot_slot + 1)) ]]; do
sleep 1
done
curl -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1, "method":"validatorExit"}' http://localhost:8899
wait $pid
$solana_ledger_tool create-snapshot --ledger config/ledger "$snapshot_slot" config/snapshot-ledger
cp config/ledger/genesis.tar.bz2 config/snapshot-ledger
$solana_ledger_tool verify --ledger config/snapshot-ledger

View File

@ -1,30 +1,28 @@
#
# This file maintains the rust versions for use by CI.
#
# Build with stable rust, updating the stable toolchain if necessary:
# $ source ci/rust-version.sh stable
# $ cargo +"$rust_stable" build
#
# Build with nightly rust, updating the nightly toolchain if necessary:
# $ source ci/rust-version.sh nightly
# $ cargo +"$rust_nightly" build
#
# Obtain the environment variables without any automatic toolchain updating:
# $ source ci/rust-version.sh
#
# Obtain the environment variables updating both stable and nightly, only stable, or
# only nightly:
# $ source ci/rust-version.sh all
# $ source ci/rust-version.sh stable
# $ source ci/rust-version.sh nightly
# Then to build with either stable or nightly:
# $ cargo +"$rust_stable" build
# $ cargo +"$rust_nightly" build
#
if [[ -n $RUST_STABLE_VERSION ]]; then
stable_version="$RUST_STABLE_VERSION"
else
stable_version=1.43.0
stable_version=1.42.0
fi
if [[ -n $RUST_NIGHTLY_VERSION ]]; then
nightly_version="$RUST_NIGHTLY_VERSION"
else
nightly_version=2020-04-23
nightly_version=2020-03-12
fi
@ -53,10 +51,6 @@ export rust_nightly_docker_image=solanalabs/rust-nightly:"$nightly_version"
nightly)
rustup_install "$rust_nightly"
;;
all)
rustup_install "$rust_stable"
rustup_install "$rust_nightly"
;;
*)
echo "Note: ignoring unknown argument: $1"
;;

View File

@ -25,7 +25,7 @@ source ci/_
source ci/upload-ci-artifact.sh
eval "$(ci/channel-info.sh)"
source ci/rust-version.sh all
source ci/rust-version.sh nightly
set -o pipefail
export RUST_BACKTRACE=1
@ -67,9 +67,8 @@ _ cargo +$rust_nightly bench --manifest-path core/Cargo.toml ${V:+--verbose} \
_ cargo +$rust_nightly bench --manifest-path programs/bpf/Cargo.toml ${V:+--verbose} --features=bpf_c \
-- -Z unstable-options --format=json --nocapture | tee -a "$BENCH_FILE"
# Run banking/accounts bench. Doesn't require nightly, but use since it is already built.
# Run banking bench. Doesn't require nightly, but use since it is already built.
_ cargo +$rust_nightly run --release --manifest-path banking-bench/Cargo.toml ${V:+--verbose} | tee -a "$BENCH_FILE"
_ cargo +$rust_nightly run --release --manifest-path accounts-bench/Cargo.toml ${V:+--verbose} -- --num_accounts 10000 --num_slots 4 | tee -a "$BENCH_FILE"
# `solana-upload-perf` disabled as it can take over 30 minutes to complete for some
# reason

View File

@ -10,6 +10,9 @@ source ci/rust-version.sh nightly
export RUST_BACKTRACE=1
export RUSTFLAGS="-D warnings"
# Look for failed mergify.io backports
_ git show HEAD --check --oneline
_ cargo +"$rust_stable" fmt --all -- --check
# Clippy gets stuck for unknown reasons if sdk-c is included in the build, so check it separately.
@ -20,8 +23,10 @@ _ cargo +"$rust_stable" clippy --manifest-path sdk-c/Cargo.toml -- --deny=warnin
_ cargo +"$rust_stable" audit --version
_ cargo +"$rust_stable" audit --ignore RUSTSEC-2020-0002 --ignore RUSTSEC-2020-0008
_ ci/nits.sh
_ ci/order-crates-for-publishing.py
_ docs/build.sh
_ ci/check-ssh-keys.sh
{
cd programs/bpf

View File

@ -1,27 +0,0 @@
#!/usr/bin/env bash
set -e
cd "$(dirname "$0")/.."
source ci/_
(
echo --- git diff --check
set -x
# Look for failed mergify.io backports by searching leftover conflict markers
# Also check for any trailing whitespaces!
if [[ -n $BUILDKITE_PULL_REQUEST_BASE_BRANCH ]]; then
base_branch=$BUILDKITE_PULL_REQUEST_BASE_BRANCH
else
base_branch=$BUILDKITE_BRANCH
fi
git fetch origin "$base_branch"
git diff "$(git merge-base HEAD "origin/$base_branch")..HEAD" --check --oneline
)
echo
_ ci/nits.sh
_ ci/check-ssh-keys.sh
echo --- ok

View File

@ -38,16 +38,11 @@ test -d target/release/bpf && find target/release/bpf -name '*.d' -delete
# Clear the BPF sysroot files, they are not automatically rebuilt
rm -rf target/xargo # Issue #3105
# Limit compiler jobs to reduce memory usage
# on machines with 2gb/thread of memory
NPROC=$(nproc)
NPROC=$((NPROC>14 ? 14 : NPROC))
echo "Executing $testName"
case $testName in
test-stable)
_ cargo +"$rust_stable" test --jobs "$NPROC" --all --exclude solana-local-cluster ${V:+--verbose} -- --nocapture
#_ cargo +"$rust_stable" test --manifest-path bench-tps/Cargo.toml --features=move ${V:+--verbose} test_bench_tps_local_cluster_move -- --nocapture
_ cargo +"$rust_stable" test --all --exclude solana-local-cluster ${V:+--verbose} -- --nocapture
_ cargo +"$rust_stable" test --manifest-path bench-tps/Cargo.toml --features=move ${V:+--verbose} test_bench_tps_local_cluster_move -- --nocapture
;;
test-stable-perf)
ci/affects-files.sh \

View File

@ -23,14 +23,10 @@ if [[ -z $CI_TAG ]]; then
exit 1
fi
# Force CI_REPO_SLUG since sometimes
# BUILDKITE_TRIGGERED_FROM_BUILD_PIPELINE_SLUG is not set correctly, causing the
# artifact upload to fail
CI_REPO_SLUG=solana-labs/solana
#if [[ -z $CI_REPO_SLUG ]]; then
# echo Error: CI_REPO_SLUG not defined
# exit 1
#fi
if [[ -z $CI_REPO_SLUG ]]; then
echo Error: CI_REPO_SLUG not defined
exit 1
fi
releaseId=$( \
curl -s "https://api.github.com/repos/$CI_REPO_SLUG/releases/tags/$CI_TAG" \

View File

@ -1,6 +1,6 @@
[package]
name = "solana-clap-utils"
version = "1.1.23"
version = "1.0.14"
description = "Solana utilities for the clap"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@ -11,8 +11,8 @@ edition = "2018"
[dependencies]
clap = "2.33.0"
rpassword = "4.0"
solana-remote-wallet = { path = "../remote-wallet", version = "1.1.23" }
solana-sdk = { path = "../sdk", version = "1.1.23" }
solana-remote-wallet = { path = "../remote-wallet", version = "1.0.14" }
solana-sdk = { path = "../sdk", version = "1.0.14" }
thiserror = "1.0.11"
tiny-bip39 = "0.7.0"
url = "2.1.0"
@ -20,6 +20,3 @@ chrono = "0.4"
[lib]
name = "solana_clap_utils"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@ -1,22 +0,0 @@
use crate::ArgConstant;
use clap::Arg;
pub const COMMITMENT_ARG: ArgConstant<'static> = ArgConstant {
name: "commitment",
long: "commitment",
help: "Return information at the selected commitment level",
};
pub fn commitment_arg<'a, 'b>() -> Arg<'a, 'b> {
commitment_arg_with_default("recent")
}
pub fn commitment_arg_with_default<'a, 'b>(default_value: &'static str) -> Arg<'a, 'b> {
Arg::with_name(COMMITMENT_ARG.name)
.long(COMMITMENT_ARG.long)
.takes_value(true)
.possible_values(&["recent", "root", "max"])
.default_value(default_value)
.value_name("COMMITMENT_LEVEL")
.help(COMMITMENT_ARG.help)
}

View File

@ -7,7 +7,6 @@ use clap::ArgMatches;
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{
clock::UnixTimestamp,
commitment_config::CommitmentConfig,
native_token::sol_to_lamports,
pubkey::Pubkey,
signature::{read_keypair_file, Keypair, Signature, Signer},
@ -63,21 +62,6 @@ pub fn keypair_of(matches: &ArgMatches<'_>, name: &str) -> Option<Keypair> {
}
}
pub fn keypairs_of(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<Keypair>> {
matches.values_of(name).map(|values| {
values
.filter_map(|value| {
if value == ASK_KEYWORD {
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
keypair_from_seed_phrase(name, skip_validation, true).ok()
} else {
read_keypair_file(value).ok()
}
})
.collect()
})
}
// Return a pubkey for an argument that can itself be parsed into a pubkey,
// or is a filename that can be read as a keypair
pub fn pubkey_of(matches: &ArgMatches<'_>, name: &str) -> Option<Pubkey> {
@ -117,7 +101,7 @@ pub fn pubkeys_sigs_of(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<(Pubk
pub fn signer_of(
matches: &ArgMatches<'_>,
name: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<(Option<Box<dyn Signer>>, Option<Pubkey>), Box<dyn std::error::Error>> {
if let Some(location) = matches.value_of(name) {
let signer = signer_from_path(matches, location, name, wallet_manager)?;
@ -131,7 +115,7 @@ pub fn signer_of(
pub fn pubkey_of_signer(
matches: &ArgMatches<'_>,
name: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<Option<Pubkey>, Box<dyn std::error::Error>> {
if let Some(location) = matches.value_of(name) {
Ok(Some(pubkey_from_path(
@ -148,7 +132,7 @@ pub fn pubkey_of_signer(
pub fn pubkeys_of_multiple_signers(
matches: &ArgMatches<'_>,
name: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<Option<Vec<Pubkey>>, Box<dyn std::error::Error>> {
if let Some(pubkey_matches) = matches.values_of(name) {
let mut pubkeys: Vec<Pubkey> = vec![];
@ -164,7 +148,7 @@ pub fn pubkeys_of_multiple_signers(
pub fn resolve_signer(
matches: &ArgMatches<'_>,
name: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<Option<String>, Box<dyn std::error::Error>> {
Ok(resolve_signer_from_path(
matches,
@ -178,15 +162,6 @@ pub fn lamports_of_sol(matches: &ArgMatches<'_>, name: &str) -> Option<u64> {
value_of(matches, name).map(sol_to_lamports)
}
pub fn commitment_of(matches: &ArgMatches<'_>, name: &str) -> Option<CommitmentConfig> {
matches.value_of(name).map(|value| match value {
"max" => CommitmentConfig::max(),
"recent" => CommitmentConfig::recent(),
"root" => CommitmentConfig::root(),
_ => CommitmentConfig::default(),
})
}
#[cfg(test)]
mod tests {
use super::*;

View File

@ -6,86 +6,50 @@ use solana_sdk::{
pubkey::Pubkey,
signature::{read_keypair_file, Signature},
};
use std::fmt::Display;
use std::str::FromStr;
fn is_parsable_generic<U, T>(string: T) -> Result<(), String>
where
T: AsRef<str> + Display,
U: FromStr,
U::Err: Display,
{
string
.as_ref()
.parse::<U>()
.map(|_| ())
.map_err(|err| format!("error parsing '{}': {}", string, err))
}
// Return an error if string cannot be parsed as type T.
// Takes a String to avoid second type parameter when used as a clap validator
pub fn is_parsable<T>(string: String) -> Result<(), String>
where
T: FromStr,
T::Err: Display,
{
is_parsable_generic::<T, String>(string)
}
// Return an error if a pubkey cannot be parsed.
pub fn is_pubkey<T>(string: T) -> Result<(), String>
where
T: AsRef<str> + Display,
{
is_parsable_generic::<Pubkey, _>(string)
pub fn is_pubkey(string: String) -> Result<(), String> {
match string.parse::<Pubkey>() {
Ok(_) => Ok(()),
Err(err) => Err(format!("{}", err)),
}
}
// Return an error if a hash cannot be parsed.
pub fn is_hash<T>(string: T) -> Result<(), String>
where
T: AsRef<str> + Display,
{
is_parsable_generic::<Hash, _>(string)
pub fn is_hash(string: String) -> Result<(), String> {
match string.parse::<Hash>() {
Ok(_) => Ok(()),
Err(err) => Err(format!("{}", err)),
}
}
// Return an error if a keypair file cannot be parsed.
pub fn is_keypair<T>(string: T) -> Result<(), String>
where
T: AsRef<str> + Display,
{
read_keypair_file(string.as_ref())
pub fn is_keypair(string: String) -> Result<(), String> {
read_keypair_file(&string)
.map(|_| ())
.map_err(|err| format!("{}", err))
}
// Return an error if a keypair file cannot be parsed
pub fn is_keypair_or_ask_keyword<T>(string: T) -> Result<(), String>
where
T: AsRef<str> + Display,
{
if string.as_ref() == ASK_KEYWORD {
pub fn is_keypair_or_ask_keyword(string: String) -> Result<(), String> {
if string.as_str() == ASK_KEYWORD {
return Ok(());
}
read_keypair_file(string.as_ref())
read_keypair_file(&string)
.map(|_| ())
.map_err(|err| format!("{}", err))
}
// Return an error if string cannot be parsed as pubkey string or keypair file location
pub fn is_pubkey_or_keypair<T>(string: T) -> Result<(), String>
where
T: AsRef<str> + Display,
{
is_pubkey(string.as_ref()).or_else(|_| is_keypair(string))
pub fn is_pubkey_or_keypair(string: String) -> Result<(), String> {
is_pubkey(string.clone()).or_else(|_| is_keypair(string))
}
// Return an error if string cannot be parsed as a pubkey string, or a valid Signer that can
// produce a pubkey()
pub fn is_valid_pubkey<T>(string: T) -> Result<(), String>
where
T: AsRef<str> + Display,
{
match parse_keypair_path(string.as_ref()) {
pub fn is_valid_pubkey(string: String) -> Result<(), String> {
match parse_keypair_path(&string) {
KeypairUrl::Filepath(path) => is_keypair(path),
_ => Ok(()),
}
@ -99,19 +63,13 @@ where
// when paired with an offline `--signer` argument to provide a Presigner (pubkey + signature).
// Clap validators can't check multiple fields at once, so the verification that a `--signer` is
// also provided and correct happens in parsing, not in validation.
pub fn is_valid_signer<T>(string: T) -> Result<(), String>
where
T: AsRef<str> + Display,
{
pub fn is_valid_signer(string: String) -> Result<(), String> {
is_valid_pubkey(string)
}
// Return an error if string cannot be parsed as pubkey=signature string
pub fn is_pubkey_sig<T>(string: T) -> Result<(), String>
where
T: AsRef<str> + Display,
{
let mut signer = string.as_ref().split('=');
pub fn is_pubkey_sig(string: String) -> Result<(), String> {
let mut signer = string.split('=');
match Pubkey::from_str(
signer
.next()
@ -132,11 +90,8 @@ where
}
// Return an error if a url cannot be parsed.
pub fn is_url<T>(string: T) -> Result<(), String>
where
T: AsRef<str> + Display,
{
match url::Url::parse(string.as_ref()) {
pub fn is_url(string: String) -> Result<(), String> {
match url::Url::parse(&string) {
Ok(url) => {
if url.has_host() {
Ok(())
@ -148,26 +103,20 @@ where
}
}
pub fn is_slot<T>(slot: T) -> Result<(), String>
where
T: AsRef<str> + Display,
{
is_parsable_generic::<Slot, _>(slot)
pub fn is_slot(slot: String) -> Result<(), String> {
slot.parse::<Slot>()
.map(|_| ())
.map_err(|e| format!("{}", e))
}
pub fn is_port<T>(port: T) -> Result<(), String>
where
T: AsRef<str> + Display,
{
is_parsable_generic::<u16, _>(port)
pub fn is_port(port: String) -> Result<(), String> {
port.parse::<u16>()
.map(|_| ())
.map_err(|e| format!("{}", e))
}
pub fn is_valid_percentage<T>(percentage: T) -> Result<(), String>
where
T: AsRef<str> + Display,
{
pub fn is_valid_percentage(percentage: String) -> Result<(), String> {
percentage
.as_ref()
.parse::<u8>()
.map_err(|e| {
format!(
@ -187,11 +136,8 @@ where
})
}
pub fn is_amount<T>(amount: T) -> Result<(), String>
where
T: AsRef<str> + Display,
{
if amount.as_ref().parse::<u64>().is_ok() || amount.as_ref().parse::<f64>().is_ok() {
pub fn is_amount(amount: String) -> Result<(), String> {
if amount.parse::<u64>().is_ok() || amount.parse::<f64>().is_ok() {
Ok(())
} else {
Err(format!(
@ -201,20 +147,14 @@ where
}
}
pub fn is_rfc3339_datetime<T>(value: T) -> Result<(), String>
where
T: AsRef<str> + Display,
{
DateTime::parse_from_rfc3339(value.as_ref())
pub fn is_rfc3339_datetime(value: String) -> Result<(), String> {
DateTime::parse_from_rfc3339(&value)
.map(|_| ())
.map_err(|e| format!("{}", e))
}
pub fn is_derivation<T>(value: T) -> Result<(), String>
where
T: AsRef<str> + Display,
{
let value = value.as_ref().replace("'", "");
pub fn is_derivation(value: String) -> Result<(), String> {
let value = value.replace("'", "");
let mut parts = value.split('/');
let account = parts.next().unwrap();
account
@ -246,14 +186,14 @@ mod tests {
#[test]
fn test_is_derivation() {
assert_eq!(is_derivation("2"), Ok(()));
assert_eq!(is_derivation("0"), Ok(()));
assert_eq!(is_derivation("65537"), Ok(()));
assert_eq!(is_derivation("0/2"), Ok(()));
assert_eq!(is_derivation("0'/2'"), Ok(()));
assert!(is_derivation("a").is_err());
assert!(is_derivation("4294967296").is_err());
assert!(is_derivation("a/b").is_err());
assert!(is_derivation("0/4294967296").is_err());
assert_eq!(is_derivation("2".to_string()), Ok(()));
assert_eq!(is_derivation("0".to_string()), Ok(()));
assert_eq!(is_derivation("65537".to_string()), Ok(()));
assert_eq!(is_derivation("0/2".to_string()), Ok(()));
assert_eq!(is_derivation("0'/2'".to_string()), Ok(()));
assert!(is_derivation("a".to_string()).is_err());
assert!(is_derivation("4294967296".to_string()).is_err());
assert!(is_derivation("a/b".to_string()).is_err());
assert!(is_derivation("0/4294967296".to_string()).is_err());
}
}

View File

@ -8,7 +8,7 @@ use clap::ArgMatches;
use rpassword::prompt_password_stderr;
use solana_remote_wallet::{
remote_keypair::generate_remote_keypair,
remote_wallet::{maybe_wallet_manager, RemoteWalletError, RemoteWalletManager},
remote_wallet::{RemoteWalletError, RemoteWalletManager},
};
use solana_sdk::{
pubkey::Pubkey,
@ -64,7 +64,7 @@ pub fn signer_from_path(
matches: &ArgMatches,
path: &str,
keypair_name: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<Box<dyn Signer>, Box<dyn error::Error>> {
match parse_keypair_path(path) {
KeypairUrl::Ask => {
@ -88,9 +88,6 @@ pub fn signer_from_path(
Ok(Box::new(read_keypair(&mut stdin)?))
}
KeypairUrl::Usb(path) => {
if wallet_manager.is_none() {
*wallet_manager = maybe_wallet_manager()?;
}
if let Some(wallet_manager) = wallet_manager {
Ok(Box::new(generate_remote_keypair(
path,
@ -125,7 +122,7 @@ pub fn pubkey_from_path(
matches: &ArgMatches,
path: &str,
keypair_name: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<Pubkey, Box<dyn error::Error>> {
match parse_keypair_path(path) {
KeypairUrl::Pubkey(pubkey) => Ok(pubkey),
@ -137,7 +134,7 @@ pub fn resolve_signer_from_path(
matches: &ArgMatches,
path: &str,
keypair_name: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<Option<String>, Box<dyn error::Error>> {
match parse_keypair_path(path) {
KeypairUrl::Ask => {
@ -161,9 +158,6 @@ pub fn resolve_signer_from_path(
read_keypair(&mut stdin).map(|_| None)
}
KeypairUrl::Usb(path) => {
if wallet_manager.is_none() {
*wallet_manager = maybe_wallet_manager()?;
}
if let Some(wallet_manager) = wallet_manager {
let path = generate_remote_keypair(
path,

View File

@ -42,7 +42,6 @@ impl std::fmt::Debug for DisplayError {
}
}
pub mod commitment;
pub mod input_parsers;
pub mod input_validators;
pub mod keypair;

View File

@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-cli-config"
description = "Blockchain, Rebuilt for Scale"
version = "1.1.23"
version = "1.0.14"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@ -11,10 +11,7 @@ homepage = "https://solana.com/"
[dependencies]
dirs = "2.0.2"
lazy_static = "1.4.0"
serde = "1.0.105"
serde = "1.0.104"
serde_derive = "1.0.103"
serde_yaml = "0.8.11"
url = "2.1.1"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@ -1,6 +1,6 @@
// Wallet settings that can be configured for long-term use
use serde_derive::{Deserialize, Serialize};
use std::{collections::HashMap, io};
use std::io;
use url::Url;
lazy_static! {
@ -17,8 +17,6 @@ pub struct Config {
pub json_rpc_url: String,
pub websocket_url: String,
pub keypair_path: String,
#[serde(default)]
pub address_labels: HashMap<String, String>,
}
impl Default for Config {
@ -28,7 +26,7 @@ impl Default for Config {
keypair_path.extend(&[".config", "solana", "id.json"]);
keypair_path.to_str().unwrap().to_string()
};
let json_rpc_url = "https://api.mainnet-beta.solana.com".to_string();
let json_rpc_url = "http://127.0.0.1:8899".to_string();
// Empty websocket_url string indicates the client should
// `Config::compute_websocket_url(&json_rpc_url)`
@ -38,7 +36,6 @@ impl Default for Config {
json_rpc_url,
websocket_url,
keypair_path,
address_labels: HashMap::new(),
}
}
}
@ -63,38 +60,17 @@ impl Config {
ws_url
.set_scheme(if is_secure { "wss" } else { "ws" })
.expect("unable to set scheme");
if let Some(port) = json_rpc_url.port() {
ws_url.set_port(Some(port + 1)).expect("unable to set port");
}
let ws_port = match json_rpc_url.port() {
Some(port) => port + 1,
None => {
if is_secure {
8901
} else {
8900
}
}
};
ws_url.set_port(Some(ws_port)).expect("unable to set port");
ws_url.to_string()
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn compute_websocket_url() {
assert_eq!(
Config::compute_websocket_url(&"http://devnet.solana.com"),
"ws://devnet.solana.com/".to_string()
);
assert_eq!(
Config::compute_websocket_url(&"https://devnet.solana.com"),
"wss://devnet.solana.com/".to_string()
);
assert_eq!(
Config::compute_websocket_url(&"http://example.com:8899"),
"ws://example.com:8900/".to_string()
);
assert_eq!(
Config::compute_websocket_url(&"https://example.com:1234"),
"wss://example.com:1235/".to_string()
);
assert_eq!(Config::compute_websocket_url(&"garbage"), String::new());
}
}

View File

@ -3,7 +3,7 @@ authors = ["Solana Maintainers <maintainers@solana.com>"]
edition = "2018"
name = "solana-cli"
description = "Blockchain, Rebuilt for Scale"
version = "1.1.23"
version = "1.0.14"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@ -11,49 +11,45 @@ homepage = "https://solana.com/"
[dependencies]
bincode = "1.2.1"
bs58 = "0.3.0"
chrono = { version = "0.4.11", features = ["serde"] }
chrono = { version = "0.4.10", features = ["serde"] }
clap = "2.33.0"
criterion-stats = "0.3.0"
ctrlc = { version = "3.1.4", features = ["termination"] }
console = "0.10.0"
console = "0.9.2"
dirs = "2.0.2"
log = "0.4.8"
Inflector = "0.11.4"
indicatif = "0.14.0"
humantime = "2.0.0"
num-traits = "0.2"
pretty-hex = "0.1.1"
reqwest = { version = "0.10.4", default-features = false, features = ["blocking", "rustls-tls", "json"] }
serde = "1.0.105"
reqwest = { version = "0.10.1", default-features = false, features = ["blocking", "rustls-tls"] }
serde = "1.0.104"
serde_derive = "1.0.103"
serde_json = "1.0.48"
solana-budget-program = { path = "../programs/budget", version = "1.1.23" }
solana-clap-utils = { path = "../clap-utils", version = "1.1.23" }
solana-cli-config = { path = "../cli-config", version = "1.1.23" }
solana-client = { path = "../client", version = "1.1.23" }
solana-config-program = { path = "../programs/config", version = "1.1.23" }
solana-faucet = { path = "../faucet", version = "1.1.23" }
solana-logger = { path = "../logger", version = "1.1.23" }
solana-net-utils = { path = "../net-utils", version = "1.1.23" }
solana-remote-wallet = { path = "../remote-wallet", version = "1.1.23" }
solana-runtime = { path = "../runtime", version = "1.1.23" }
solana-sdk = { path = "../sdk", version = "1.1.23" }
solana-stake-program = { path = "../programs/stake", version = "1.1.23" }
solana-storage-program = { path = "../programs/storage", version = "1.1.23" }
solana-transaction-status = { path = "../transaction-status", version = "1.1.23" }
solana-vote-program = { path = "../programs/vote", version = "1.1.23" }
solana-vote-signer = { path = "../vote-signer", version = "1.1.23" }
thiserror = "1.0.13"
serde_json = "1.0.46"
solana-budget-program = { path = "../programs/budget", version = "1.0.14" }
solana-clap-utils = { path = "../clap-utils", version = "1.0.14" }
solana-cli-config = { path = "../cli-config", version = "1.0.14" }
solana-client = { path = "../client", version = "1.0.14" }
solana-config-program = { path = "../programs/config", version = "1.0.14" }
solana-faucet = { path = "../faucet", version = "1.0.14" }
solana-logger = { path = "../logger", version = "1.0.14" }
solana-net-utils = { path = "../net-utils", version = "1.0.14" }
solana-remote-wallet = { path = "../remote-wallet", version = "1.0.14" }
solana-runtime = { path = "../runtime", version = "1.0.14" }
solana-sdk = { path = "../sdk", version = "1.0.14" }
solana-stake-program = { path = "../programs/stake", version = "1.0.14" }
solana-storage-program = { path = "../programs/storage", version = "1.0.14" }
solana-vote-program = { path = "../programs/vote", version = "1.0.14" }
solana-vote-signer = { path = "../vote-signer", version = "1.0.14" }
titlecase = "1.1.0"
thiserror = "1.0.11"
url = "2.1.1"
[dev-dependencies]
solana-core = { path = "../core", version = "1.1.23" }
solana-budget-program = { path = "../programs/budget", version = "1.1.23" }
solana-core = { path = "../core", version = "1.0.14" }
solana-budget-program = { path = "../programs/budget", version = "1.0.14" }
tempfile = "3.1.0"
[[bin]]
name = "solana"
path = "src/main.rs"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

File diff suppressed because it is too large Load Diff

View File

@ -1,982 +0,0 @@
use crate::{cli::build_balance_message, display::writeln_name_value};
use chrono::{DateTime, NaiveDateTime, SecondsFormat, Utc};
use console::{style, Emoji};
use inflector::cases::titlecase::to_title_case;
use serde::Serialize;
use serde_json::{Map, Value};
use solana_client::rpc_response::{
RpcAccountBalance, RpcEpochInfo, RpcKeyedAccount, RpcSupply, RpcVoteAccountInfo,
};
use solana_sdk::{
clock::{self, Epoch, Slot, UnixTimestamp},
native_token::lamports_to_sol,
stake_history::StakeHistoryEntry,
};
use solana_stake_program::stake_state::{Authorized, Lockup};
use solana_vote_program::{
authorized_voters::AuthorizedVoters,
vote_state::{BlockTimestamp, Lockout},
};
use std::{collections::BTreeMap, fmt, time::Duration};
static WARNING: Emoji = Emoji("⚠️", "!");
#[derive(PartialEq)]
pub enum OutputFormat {
Display,
Json,
JsonCompact,
}
impl OutputFormat {
pub fn formatted_string<T>(&self, item: &T) -> String
where
T: Serialize + fmt::Display,
{
match self {
OutputFormat::Display => format!("{}", item),
OutputFormat::Json => serde_json::to_string_pretty(item).unwrap(),
OutputFormat::JsonCompact => serde_json::to_value(item).unwrap().to_string(),
}
}
}
#[derive(Serialize, Deserialize)]
pub struct CliAccount {
#[serde(flatten)]
pub keyed_account: RpcKeyedAccount,
#[serde(skip_serializing)]
pub use_lamports_unit: bool,
}
impl fmt::Display for CliAccount {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f)?;
writeln_name_value(f, "Public Key:", &self.keyed_account.pubkey)?;
writeln_name_value(
f,
"Balance:",
&build_balance_message(
self.keyed_account.account.lamports,
self.use_lamports_unit,
true,
),
)?;
writeln_name_value(f, "Owner:", &self.keyed_account.account.owner)?;
writeln_name_value(
f,
"Executable:",
&self.keyed_account.account.executable.to_string(),
)?;
writeln_name_value(
f,
"Rent Epoch:",
&self.keyed_account.account.rent_epoch.to_string(),
)?;
Ok(())
}
}
#[derive(Default, Serialize, Deserialize)]
pub struct CliBlockProduction {
pub epoch: Epoch,
pub start_slot: Slot,
pub end_slot: Slot,
pub total_slots: usize,
pub total_blocks_produced: usize,
pub total_slots_skipped: usize,
pub leaders: Vec<CliBlockProductionEntry>,
pub individual_slot_status: Vec<CliSlotStatus>,
#[serde(skip_serializing)]
pub verbose: bool,
}
impl fmt::Display for CliBlockProduction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f)?;
writeln!(
f,
"{}",
style(format!(
" {:<44} {:>15} {:>15} {:>15} {:>23}",
"Identity Pubkey",
"Leader Slots",
"Blocks Produced",
"Skipped Slots",
"Skipped Slot Percentage",
))
.bold()
)?;
for leader in &self.leaders {
writeln!(
f,
" {:<44} {:>15} {:>15} {:>15} {:>22.2}%",
leader.identity_pubkey,
leader.leader_slots,
leader.blocks_produced,
leader.skipped_slots,
leader.skipped_slots as f64 / leader.leader_slots as f64 * 100.
)?;
}
writeln!(f)?;
writeln!(
f,
" {:<44} {:>15} {:>15} {:>15} {:>22.2}%",
format!("Epoch {} total:", self.epoch),
self.total_slots,
self.total_blocks_produced,
self.total_slots_skipped,
self.total_slots_skipped as f64 / self.total_slots as f64 * 100.
)?;
writeln!(
f,
" (using data from {} slots: {} to {})",
self.total_slots, self.start_slot, self.end_slot
)?;
if self.verbose {
writeln!(f)?;
writeln!(f)?;
writeln!(
f,
"{}",
style(format!(" {:<15} {:<44}", "Slot", "Identity Pubkey")).bold(),
)?;
for status in &self.individual_slot_status {
if status.skipped {
writeln!(
f,
"{}",
style(format!(
" {:<15} {:<44} SKIPPED",
status.slot, status.leader
))
.red()
)?;
} else {
writeln!(
f,
"{}",
style(format!(" {:<15} {:<44}", status.slot, status.leader))
)?;
}
}
}
Ok(())
}
}
#[derive(Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliBlockProductionEntry {
pub identity_pubkey: String,
pub leader_slots: u64,
pub blocks_produced: u64,
pub skipped_slots: u64,
}
#[derive(Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliSlotStatus {
pub slot: Slot,
pub leader: String,
pub skipped: bool,
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliEpochInfo {
#[serde(flatten)]
pub epoch_info: RpcEpochInfo,
}
impl From<RpcEpochInfo> for CliEpochInfo {
fn from(epoch_info: RpcEpochInfo) -> Self {
Self { epoch_info }
}
}
impl fmt::Display for CliEpochInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f)?;
writeln_name_value(f, "Slot:", &self.epoch_info.absolute_slot.to_string())?;
writeln_name_value(f, "Epoch:", &self.epoch_info.epoch.to_string())?;
let start_slot = self.epoch_info.absolute_slot - self.epoch_info.slot_index;
let end_slot = start_slot + self.epoch_info.slots_in_epoch;
writeln_name_value(
f,
"Epoch Slot Range:",
&format!("[{}..{})", start_slot, end_slot),
)?;
writeln_name_value(
f,
"Epoch Completed Percent:",
&format!(
"{:>3.3}%",
self.epoch_info.slot_index as f64 / self.epoch_info.slots_in_epoch as f64 * 100_f64
),
)?;
let remaining_slots_in_epoch = self.epoch_info.slots_in_epoch - self.epoch_info.slot_index;
writeln_name_value(
f,
"Epoch Completed Slots:",
&format!(
"{}/{} ({} remaining)",
self.epoch_info.slot_index,
self.epoch_info.slots_in_epoch,
remaining_slots_in_epoch
),
)?;
writeln_name_value(
f,
"Epoch Completed Time:",
&format!(
"{}/{} ({} remaining)",
slot_to_human_time(self.epoch_info.slot_index),
slot_to_human_time(self.epoch_info.slots_in_epoch),
slot_to_human_time(remaining_slots_in_epoch)
),
)
}
}
fn slot_to_human_time(slot: Slot) -> String {
humantime::format_duration(Duration::from_secs(
slot * clock::DEFAULT_TICKS_PER_SLOT / clock::DEFAULT_TICKS_PER_SECOND,
))
.to_string()
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliValidators {
pub total_active_stake: u64,
pub total_current_stake: u64,
pub total_deliquent_stake: u64,
pub current_validators: Vec<CliValidator>,
pub delinquent_validators: Vec<CliValidator>,
#[serde(skip_serializing)]
pub use_lamports_unit: bool,
}
impl fmt::Display for CliValidators {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn write_vote_account(
f: &mut fmt::Formatter,
validator: &CliValidator,
total_active_stake: u64,
use_lamports_unit: bool,
delinquent: bool,
) -> fmt::Result {
fn non_zero_or_dash(v: u64) -> String {
if v == 0 {
"-".into()
} else {
format!("{}", v)
}
}
writeln!(
f,
"{} {:<44} {:<44} {:>9}% {:>8} {:>10} {:>7} {}",
if delinquent {
WARNING.to_string()
} else {
" ".to_string()
},
validator.identity_pubkey,
validator.vote_account_pubkey,
validator.commission,
non_zero_or_dash(validator.last_vote),
non_zero_or_dash(validator.root_slot),
validator.credits,
if validator.activated_stake > 0 {
format!(
"{} ({:.2}%)",
build_balance_message(validator.activated_stake, use_lamports_unit, true),
100. * validator.activated_stake as f64 / total_active_stake as f64
)
} else {
"-".into()
},
)
}
writeln_name_value(
f,
"Active Stake:",
&build_balance_message(self.total_active_stake, self.use_lamports_unit, true),
)?;
if self.total_deliquent_stake > 0 {
writeln_name_value(
f,
"Current Stake:",
&format!(
"{} ({:0.2}%)",
&build_balance_message(self.total_current_stake, self.use_lamports_unit, true),
100. * self.total_current_stake as f64 / self.total_active_stake as f64
),
)?;
writeln_name_value(
f,
"Delinquent Stake:",
&format!(
"{} ({:0.2}%)",
&build_balance_message(
self.total_deliquent_stake,
self.use_lamports_unit,
true
),
100. * self.total_deliquent_stake as f64 / self.total_active_stake as f64
),
)?;
}
writeln!(f)?;
writeln!(
f,
"{}",
style(format!(
" {:<44} {:<44} {} {} {} {:>7} {}",
"Identity Pubkey",
"Vote Account Pubkey",
"Commission",
"Last Vote",
"Root Block",
"Credits",
"Active Stake",
))
.bold()
)?;
for validator in &self.current_validators {
write_vote_account(
f,
validator,
self.total_active_stake,
self.use_lamports_unit,
false,
)?;
}
for validator in &self.delinquent_validators {
write_vote_account(
f,
validator,
self.total_active_stake,
self.use_lamports_unit,
true,
)?;
}
Ok(())
}
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliValidator {
pub identity_pubkey: String,
pub vote_account_pubkey: String,
pub commission: u8,
pub last_vote: u64,
pub root_slot: u64,
pub credits: u64,
pub activated_stake: u64,
}
impl CliValidator {
pub fn new(vote_account: &RpcVoteAccountInfo, current_epoch: Epoch) -> Self {
Self {
identity_pubkey: vote_account.node_pubkey.to_string(),
vote_account_pubkey: vote_account.vote_pubkey.to_string(),
commission: vote_account.commission,
last_vote: vote_account.last_vote,
root_slot: vote_account.root_slot,
credits: vote_account
.epoch_credits
.iter()
.find_map(|(epoch, credits, _)| {
if *epoch == current_epoch {
Some(*credits)
} else {
None
}
})
.unwrap_or(0),
activated_stake: vote_account.activated_stake,
}
}
}
#[derive(Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliNonceAccount {
pub balance: u64,
pub minimum_balance_for_rent_exemption: u64,
pub nonce: Option<String>,
pub lamports_per_signature: Option<u64>,
pub authority: Option<String>,
#[serde(skip_serializing)]
pub use_lamports_unit: bool,
}
impl fmt::Display for CliNonceAccount {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(
f,
"Balance: {}",
build_balance_message(self.balance, self.use_lamports_unit, true)
)?;
writeln!(
f,
"Minimum Balance Required: {}",
build_balance_message(
self.minimum_balance_for_rent_exemption,
self.use_lamports_unit,
true
)
)?;
let nonce = self.nonce.as_deref().unwrap_or("uninitialized");
writeln!(f, "Nonce: {}", nonce)?;
if let Some(fees) = self.lamports_per_signature {
writeln!(f, "Fee: {} lamports per signature", fees)?;
} else {
writeln!(f, "Fees: uninitialized")?;
}
let authority = self.authority.as_deref().unwrap_or("uninitialized");
writeln!(f, "Authority: {}", authority)
}
}
#[derive(Serialize, Deserialize)]
pub struct CliStakeVec(Vec<CliKeyedStakeState>);
impl CliStakeVec {
pub fn new(list: Vec<CliKeyedStakeState>) -> Self {
Self(list)
}
}
impl fmt::Display for CliStakeVec {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for state in &self.0 {
writeln!(f)?;
write!(f, "{}", state)?;
}
Ok(())
}
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliKeyedStakeState {
pub stake_pubkey: String,
#[serde(flatten)]
pub stake_state: CliStakeState,
}
impl fmt::Display for CliKeyedStakeState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "Stake Pubkey: {}", self.stake_pubkey)?;
write!(f, "{}", self.stake_state)
}
}
#[derive(Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliStakeState {
pub stake_type: CliStakeType,
pub total_stake: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub delegated_stake: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub delegated_vote_account_address: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub activation_epoch: Option<Epoch>,
#[serde(skip_serializing_if = "Option::is_none")]
pub deactivation_epoch: Option<Epoch>,
#[serde(flatten, skip_serializing_if = "Option::is_none")]
pub authorized: Option<CliAuthorized>,
#[serde(flatten, skip_serializing_if = "Option::is_none")]
pub lockup: Option<CliLockup>,
#[serde(skip_serializing)]
pub use_lamports_unit: bool,
}
impl fmt::Display for CliStakeState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn show_authorized(f: &mut fmt::Formatter, authorized: &CliAuthorized) -> fmt::Result {
writeln!(f, "Stake Authority: {}", authorized.staker)?;
writeln!(f, "Withdraw Authority: {}", authorized.withdrawer)?;
Ok(())
}
fn show_lockup(f: &mut fmt::Formatter, lockup: &CliLockup) -> fmt::Result {
writeln!(
f,
"Lockup Timestamp: {} (UnixTimestamp: {})",
DateTime::<Utc>::from_utc(
NaiveDateTime::from_timestamp(lockup.unix_timestamp, 0),
Utc
)
.to_rfc3339_opts(SecondsFormat::Secs, true),
lockup.unix_timestamp
)?;
writeln!(f, "Lockup Epoch: {}", lockup.epoch)?;
writeln!(f, "Lockup Custodian: {}", lockup.custodian)?;
Ok(())
}
match self.stake_type {
CliStakeType::RewardsPool => writeln!(f, "Stake account is a rewards pool")?,
CliStakeType::Uninitialized => writeln!(f, "Stake account is uninitialized")?,
CliStakeType::Initialized => {
writeln!(
f,
"Total Stake: {}",
build_balance_message(self.total_stake, self.use_lamports_unit, true)
)?;
writeln!(f, "Stake account is undelegated")?;
show_authorized(f, self.authorized.as_ref().unwrap())?;
show_lockup(f, self.lockup.as_ref().unwrap())?;
}
CliStakeType::Stake => {
writeln!(
f,
"Total Stake: {}",
build_balance_message(self.total_stake, self.use_lamports_unit, true)
)?;
writeln!(
f,
"Delegated Stake: {}",
build_balance_message(
self.delegated_stake.unwrap(),
self.use_lamports_unit,
true
)
)?;
if let Some(delegated_vote_account_address) = &self.delegated_vote_account_address {
writeln!(
f,
"Delegated Vote Account Address: {}",
delegated_vote_account_address
)?;
}
writeln!(
f,
"Stake activates starting from epoch: {}",
self.activation_epoch.unwrap()
)?;
if let Some(deactivation_epoch) = self.deactivation_epoch {
writeln!(
f,
"Stake deactivates starting from epoch: {}",
deactivation_epoch
)?;
}
show_authorized(f, self.authorized.as_ref().unwrap())?;
show_lockup(f, self.lockup.as_ref().unwrap())?;
}
}
Ok(())
}
}
#[derive(Serialize, Deserialize)]
pub enum CliStakeType {
Stake,
RewardsPool,
Uninitialized,
Initialized,
}
impl Default for CliStakeType {
fn default() -> Self {
Self::Uninitialized
}
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliStakeHistory {
pub entries: Vec<CliStakeHistoryEntry>,
#[serde(skip_serializing)]
pub use_lamports_unit: bool,
}
impl fmt::Display for CliStakeHistory {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f)?;
writeln!(
f,
"{}",
style(format!(
" {:<5} {:>20} {:>20} {:>20}",
"Epoch", "Effective Stake", "Activating Stake", "Deactivating Stake",
))
.bold()
)?;
for entry in &self.entries {
writeln!(
f,
" {:>5} {:>20} {:>20} {:>20} {}",
entry.epoch,
build_balance_message(entry.effective_stake, self.use_lamports_unit, false),
build_balance_message(entry.activating_stake, self.use_lamports_unit, false),
build_balance_message(entry.deactivating_stake, self.use_lamports_unit, false),
if self.use_lamports_unit {
"lamports"
} else {
"SOL"
}
)?;
}
Ok(())
}
}
impl From<&(Epoch, StakeHistoryEntry)> for CliStakeHistoryEntry {
fn from((epoch, entry): &(Epoch, StakeHistoryEntry)) -> Self {
Self {
epoch: *epoch,
effective_stake: entry.effective,
activating_stake: entry.activating,
deactivating_stake: entry.deactivating,
}
}
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliStakeHistoryEntry {
pub epoch: Epoch,
pub effective_stake: u64,
pub activating_stake: u64,
pub deactivating_stake: u64,
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliAuthorized {
pub staker: String,
pub withdrawer: String,
}
impl From<&Authorized> for CliAuthorized {
fn from(authorized: &Authorized) -> Self {
Self {
staker: authorized.staker.to_string(),
withdrawer: authorized.withdrawer.to_string(),
}
}
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliLockup {
pub unix_timestamp: UnixTimestamp,
pub epoch: Epoch,
pub custodian: String,
}
impl From<&Lockup> for CliLockup {
fn from(lockup: &Lockup) -> Self {
Self {
unix_timestamp: lockup.unix_timestamp,
epoch: lockup.epoch,
custodian: lockup.custodian.to_string(),
}
}
}
#[derive(Serialize, Deserialize)]
pub struct CliValidatorInfoVec(Vec<CliValidatorInfo>);
impl CliValidatorInfoVec {
pub fn new(list: Vec<CliValidatorInfo>) -> Self {
Self(list)
}
}
impl fmt::Display for CliValidatorInfoVec {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.0.is_empty() {
writeln!(f, "No validator info accounts found")?;
}
for validator_info in &self.0 {
writeln!(f)?;
write!(f, "{}", validator_info)?;
}
Ok(())
}
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliValidatorInfo {
pub identity_pubkey: String,
pub info_pubkey: String,
pub info: Map<String, Value>,
}
impl fmt::Display for CliValidatorInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln_name_value(f, "Validator Identity Pubkey:", &self.identity_pubkey)?;
writeln_name_value(f, " Info Pubkey:", &self.info_pubkey)?;
for (key, value) in self.info.iter() {
writeln_name_value(
f,
&format!(" {}:", to_title_case(key)),
&value.as_str().unwrap_or("?"),
)?;
}
Ok(())
}
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliVoteAccount {
pub account_balance: u64,
pub validator_identity: String,
#[serde(flatten)]
pub authorized_voters: CliAuthorizedVoters,
pub authorized_withdrawer: String,
pub credits: u64,
pub commission: u8,
pub root_slot: Option<Slot>,
pub recent_timestamp: BlockTimestamp,
pub votes: Vec<CliLockout>,
pub epoch_voting_history: Vec<CliEpochVotingHistory>,
#[serde(skip_serializing)]
pub use_lamports_unit: bool,
}
impl fmt::Display for CliVoteAccount {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(
f,
"Account Balance: {}",
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, "Credits: {}", self.credits)?;
writeln!(f, "Commission: {}%", self.commission)?;
writeln!(
f,
"Root Slot: {}",
match self.root_slot {
Some(slot) => slot.to_string(),
None => "~".to_string(),
}
)?;
writeln!(f, "Recent Timestamp: {:?}", self.recent_timestamp)?;
if !self.votes.is_empty() {
writeln!(f, "Recent Votes:")?;
for vote in &self.votes {
writeln!(
f,
"- slot: {}\n confirmation count: {}",
vote.slot, vote.confirmation_count
)?;
}
writeln!(f, "Epoch Voting History:")?;
for epoch_info in &self.epoch_voting_history {
writeln!(
f,
"- epoch: {}\n slots in epoch: {}\n credits earned: {}",
epoch_info.epoch, epoch_info.slots_in_epoch, epoch_info.credits_earned,
)?;
}
}
Ok(())
}
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliAuthorizedVoters {
authorized_voters: BTreeMap<Epoch, String>,
}
impl fmt::Display for CliAuthorizedVoters {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.authorized_voters)
}
}
impl From<&AuthorizedVoters> for CliAuthorizedVoters {
fn from(authorized_voters: &AuthorizedVoters) -> Self {
let mut voter_map: BTreeMap<Epoch, String> = BTreeMap::new();
for (epoch, voter) in authorized_voters.iter() {
voter_map.insert(*epoch, voter.to_string());
}
Self {
authorized_voters: voter_map,
}
}
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliEpochVotingHistory {
pub epoch: Epoch,
pub slots_in_epoch: u64,
pub credits_earned: u64,
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliLockout {
pub slot: Slot,
pub confirmation_count: u32,
}
impl From<&Lockout> for CliLockout {
fn from(lockout: &Lockout) -> Self {
Self {
slot: lockout.slot,
confirmation_count: lockout.confirmation_count,
}
}
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliBlockTime {
pub slot: Slot,
pub timestamp: UnixTimestamp,
}
impl fmt::Display for CliBlockTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln_name_value(f, "Block:", &self.slot.to_string())?;
writeln_name_value(
f,
"Date:",
&format!(
"{} (UnixTimestamp: {})",
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(self.timestamp, 0), Utc)
.to_rfc3339_opts(SecondsFormat::Secs, true),
self.timestamp
),
)
}
}
#[derive(Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct CliSignOnlyData {
pub blockhash: String,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub signers: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub absent: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub bad_sig: Vec<String>,
}
impl fmt::Display for CliSignOnlyData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f)?;
writeln_name_value(f, "Blockhash:", &self.blockhash)?;
if !self.signers.is_empty() {
writeln!(f, "{}", style("Signers (Pubkey=Signature):").bold())?;
for signer in self.signers.iter() {
writeln!(f, " {}", signer)?;
}
}
if !self.absent.is_empty() {
writeln!(f, "{}", style("Absent Signers (Pubkey):").bold())?;
for pubkey in self.absent.iter() {
writeln!(f, " {}", pubkey)?;
}
}
if !self.bad_sig.is_empty() {
writeln!(f, "{}", style("Bad Signatures (Pubkey):").bold())?;
for pubkey in self.bad_sig.iter() {
writeln!(f, " {}", pubkey)?;
}
}
Ok(())
}
}
#[derive(Serialize, Deserialize)]
pub struct CliSignature {
pub signature: String,
}
impl fmt::Display for CliSignature {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f)?;
writeln_name_value(f, "Signature:", &self.signature)?;
Ok(())
}
}
#[derive(Serialize, Deserialize)]
pub struct CliAccountBalances {
pub accounts: Vec<RpcAccountBalance>,
}
impl fmt::Display for CliAccountBalances {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(
f,
"{}",
style(format!("{:<44} {}", "Address", "Balance",)).bold()
)?;
for account in &self.accounts {
writeln!(
f,
"{:<44} {}",
account.address,
&format!("{} SOL", lamports_to_sol(account.lamports))
)?;
}
Ok(())
}
}
#[derive(Serialize, Deserialize)]
pub struct CliSupply {
pub total: u64,
pub circulating: u64,
pub non_circulating: u64,
pub non_circulating_accounts: Vec<String>,
#[serde(skip_serializing)]
pub print_accounts: bool,
}
impl From<RpcSupply> for CliSupply {
fn from(rpc_supply: RpcSupply) -> Self {
Self {
total: rpc_supply.total,
circulating: rpc_supply.circulating,
non_circulating: rpc_supply.non_circulating,
non_circulating_accounts: rpc_supply.non_circulating_accounts,
print_accounts: false,
}
}
}
impl fmt::Display for CliSupply {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln_name_value(f, "Total:", &format!("{} SOL", lamports_to_sol(self.total)))?;
writeln_name_value(
f,
"Circulating:",
&format!("{} SOL", lamports_to_sol(self.circulating)),
)?;
writeln_name_value(
f,
"Non-Circulating:",
&format!("{} SOL", lamports_to_sol(self.non_circulating)),
)?;
if self.print_accounts {
writeln!(f)?;
writeln_name_value(f, "Non-Circulating Accounts:", " ")?;
for account in &self.non_circulating_accounts {
writeln!(f, " {}", account)?;
}
}
Ok(())
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,6 @@
use crate::cli::SettingType;
use console::style;
use indicatif::{ProgressBar, ProgressStyle};
use solana_sdk::{
hash::Hash, native_token::lamports_to_sol, program_utils::limited_deserialize,
transaction::Transaction,
};
use solana_transaction_status::RpcTransactionStatusMeta;
use std::{fmt, io};
use solana_sdk::hash::Hash;
// Pretty print a "name value"
pub fn println_name_value(name: &str, value: &str) {
@ -18,15 +12,6 @@ pub fn println_name_value(name: &str, value: &str) {
println!("{} {}", style(name).bold(), styled_value);
}
pub fn writeln_name_value(f: &mut fmt::Formatter, name: &str, value: &str) -> fmt::Result {
let styled_value = if value == "" {
style("(not set)").italic()
} else {
style(value)
};
writeln!(f, "{} {}", style(name).bold(), styled_value)
}
pub fn println_name_value_or(name: &str, value: &str, setting_type: SettingType) {
let description = match setting_type {
SettingType::Explicit => "",
@ -64,149 +49,3 @@ pub fn println_signers(
}
println!();
}
pub fn write_transaction<W: io::Write>(
w: &mut W,
transaction: &Transaction,
transaction_status: &Option<RpcTransactionStatusMeta>,
prefix: &str,
) -> io::Result<()> {
let message = &transaction.message;
writeln!(
w,
"{}Recent Blockhash: {:?}",
prefix, message.recent_blockhash
)?;
for (signature_index, signature) in transaction.signatures.iter().enumerate() {
writeln!(
w,
"{}Signature {}: {:?}",
prefix, signature_index, signature
)?;
}
writeln!(w, "{}{:?}", prefix, message.header)?;
for (account_index, account) in message.account_keys.iter().enumerate() {
writeln!(w, "{}Account {}: {:?}", prefix, account_index, account)?;
}
for (instruction_index, instruction) in message.instructions.iter().enumerate() {
let program_pubkey = message.account_keys[instruction.program_id_index as usize];
writeln!(w, "{}Instruction {}", prefix, instruction_index)?;
writeln!(
w,
"{} Program: {} ({})",
prefix, program_pubkey, instruction.program_id_index
)?;
for (account_index, account) in instruction.accounts.iter().enumerate() {
let account_pubkey = message.account_keys[*account as usize];
writeln!(
w,
"{} Account {}: {} ({})",
prefix, account_index, account_pubkey, account
)?;
}
let mut raw = true;
if program_pubkey == solana_vote_program::id() {
if let Ok(vote_instruction) = limited_deserialize::<
solana_vote_program::vote_instruction::VoteInstruction,
>(&instruction.data)
{
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)
{
writeln!(w, "{} {:?}", prefix, stake_instruction)?;
raw = false;
}
} else if program_pubkey == solana_sdk::system_program::id() {
if let Ok(system_instruction) = limited_deserialize::<
solana_sdk::system_instruction::SystemInstruction,
>(&instruction.data)
{
writeln!(w, "{} {:?}", prefix, system_instruction)?;
raw = false;
}
}
if raw {
writeln!(w, "{} Data: {:?}", prefix, instruction.data)?;
}
}
if let Some(transaction_status) = transaction_status {
writeln!(
w,
"{}Status: {}",
prefix,
match &transaction_status.status {
Ok(_) => "Ok".into(),
Err(err) => err.to_string(),
}
)?;
writeln!(
w,
"{} Fee: {} SOL",
prefix,
lamports_to_sol(transaction_status.fee)
)?;
assert_eq!(
transaction_status.pre_balances.len(),
transaction_status.post_balances.len()
);
for (i, (pre, post)) in transaction_status
.pre_balances
.iter()
.zip(transaction_status.post_balances.iter())
.enumerate()
{
if pre == post {
writeln!(
w,
"{} Account {} balance: {} SOL",
prefix,
i,
lamports_to_sol(*pre)
)?;
} else {
writeln!(
w,
"{} Account {} balance: {} SOL -> {} SOL",
prefix,
i,
lamports_to_sol(*pre),
lamports_to_sol(*post)
)?;
}
}
} else {
writeln!(w, "{}Status: Unavailable", prefix)?;
}
Ok(())
}
pub fn println_transaction(
transaction: &Transaction,
transaction_status: &Option<RpcTransactionStatusMeta>,
prefix: &str,
) {
let mut w = Vec::new();
if write_transaction(&mut w, transaction, transaction_status, prefix).is_ok() {
if let Ok(s) = String::from_utf8(w) {
print!("{}", s);
}
}
}
/// Creates a new process bar for processing that will take an unknown amount of time
pub fn new_spinner_progress_bar() -> ProgressBar {
let progress_bar = ProgressBar::new(42);
progress_bar
.set_style(ProgressStyle::default_spinner().template("{spinner:.green} {wide_msg}"));
progress_bar.enable_steady_tick(100);
progress_bar
}

View File

@ -1,8 +1,4 @@
#[macro_use]
extern crate serde_derive;
pub mod cli;
pub mod cli_output;
pub mod cluster_query;
pub mod display;
pub mod nonce;

View File

@ -2,15 +2,15 @@ use clap::{crate_description, crate_name, AppSettings, Arg, ArgGroup, ArgMatches
use console::style;
use solana_clap_utils::{
input_validators::is_url, keypair::SKIP_SEED_PHRASE_VALIDATION_ARG, DisplayError,
input_validators::is_url, keypair::SKIP_SEED_PHRASE_VALIDATION_ARG, offline::SIGN_ONLY_ARG,
DisplayError,
};
use solana_cli::{
cli::{app, parse_command, process_command, CliCommandInfo, CliConfig, CliSigners},
cli_output::OutputFormat,
display::{println_name_value, println_name_value_or},
};
use solana_cli_config::{Config, CONFIG_FILE};
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_remote_wallet::remote_wallet::{maybe_wallet_manager, RemoteWalletManager};
use std::{error, sync::Arc};
fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error>> {
@ -102,7 +102,7 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
pub fn parse_args<'a>(
matches: &ArgMatches<'_>,
mut wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<Arc<RemoteWalletManager>>,
) -> Result<(CliConfig<'a>, CliSigners), Box<dyn error::Error>> {
let config = if let Some(config_file) = matches.value_of("config_file") {
Config::load(config_file).unwrap_or_default()
@ -125,16 +125,7 @@ pub fn parse_args<'a>(
);
let CliCommandInfo { command, signers } =
parse_command(&matches, &default_signer_path, &mut wallet_manager)?;
let output_format = matches
.value_of("output_format")
.map(|value| match value {
"json" => OutputFormat::Json,
"json-compact" => OutputFormat::JsonCompact,
_ => unreachable!(),
})
.unwrap_or(OutputFormat::Display);
parse_command(&matches, &default_signer_path, wallet_manager.as_ref())?;
Ok((
CliConfig {
@ -145,7 +136,6 @@ pub fn parse_args<'a>(
keypair_path: default_signer_path,
rpc_client: None,
verbose: matches.is_present("verbose"),
output_format,
},
signers,
))
@ -207,14 +197,6 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.global(true)
.help("Show additional information"),
)
.arg(
Arg::with_name("output_format")
.long("output")
.global(true)
.takes_value(true)
.possible_values(&["json", "json-compact"])
.help("Return information in specified output format. Supports: json, json-compact"),
)
.arg(
Arg::with_name(SKIP_SEED_PHRASE_VALIDATION_ARG.name)
.long(SKIP_SEED_PHRASE_VALIDATION_ARG.long)
@ -256,12 +238,18 @@ fn main() -> Result<(), Box<dyn error::Error>> {
fn do_main(matches: &ArgMatches<'_>) -> Result<(), Box<dyn error::Error>> {
if parse_settings(&matches)? {
let mut wallet_manager = None;
let wallet_manager = maybe_wallet_manager()?;
let (mut config, signers) = parse_args(&matches, &mut wallet_manager)?;
let (mut config, signers) = parse_args(&matches, wallet_manager)?;
config.signers = signers.iter().map(|s| s.as_ref()).collect();
let result = process_command(&config)?;
println!("{}", result);
let (_, submatches) = matches.subcommand();
let sign_only = submatches
.map(|m| m.is_present(SIGN_ONLY_ARG.name))
.unwrap_or(false);
if !sign_only {
println!("{}", result);
}
};
Ok(())
}

View File

@ -1,10 +1,7 @@
use crate::{
cli::{
check_account_for_fee, check_unique_pubkeys, generate_unique_signers,
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
ProcessResult, SignerIndex,
},
cli_output::CliNonceAccount,
use crate::cli::{
build_balance_message, check_account_for_fee, check_unique_pubkeys, generate_unique_signers,
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult,
SignerIndex,
};
use clap::{App, Arg, ArgMatches, SubCommand};
use solana_clap_utils::{
@ -24,8 +21,9 @@ use solana_sdk::{
},
pubkey::Pubkey,
system_instruction::{
advance_nonce_account, authorize_nonce_account, create_nonce_account,
create_nonce_account_with_seed, withdraw_nonce_account, NonceError, SystemError,
advance_nonce_account, authorize_nonce_account, create_address_with_seed,
create_nonce_account, create_nonce_account_with_seed, withdraw_nonce_account, NonceError,
SystemError,
},
system_program,
transaction::Transaction,
@ -279,7 +277,7 @@ pub fn data_from_state(state: &State) -> Result<&Data, CliNonceError> {
pub fn parse_authorize_nonce_account(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap();
let new_authority = pubkey_of_signer(matches, "new_authority", wallet_manager)?.unwrap();
@ -307,7 +305,7 @@ pub fn parse_authorize_nonce_account(
pub fn parse_nonce_create_account(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let (nonce_account, nonce_account_pubkey) =
signer_of(matches, "nonce_account_keypair", wallet_manager)?;
@ -336,7 +334,7 @@ pub fn parse_nonce_create_account(
pub fn parse_get_nonce(
matches: &ArgMatches<'_>,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let nonce_account_pubkey =
pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap();
@ -350,7 +348,7 @@ pub fn parse_get_nonce(
pub fn parse_new_nonce(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap();
let (nonce_authority, nonce_authority_pubkey) =
@ -375,7 +373,7 @@ pub fn parse_new_nonce(
pub fn parse_show_nonce_account(
matches: &ArgMatches<'_>,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let nonce_account_pubkey =
pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap();
@ -393,7 +391,7 @@ pub fn parse_show_nonce_account(
pub fn parse_withdraw_from_nonce_account(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap();
let destination_account_pubkey =
@ -462,8 +460,8 @@ pub fn process_authorize_nonce_account(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<NonceError>(result, &config)
let result = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers);
log_instruction_custom_error::<NonceError>(result)
}
pub fn process_create_nonce_account(
@ -476,7 +474,7 @@ pub fn process_create_nonce_account(
) -> ProcessResult {
let nonce_account_pubkey = config.signers[nonce_account].pubkey();
let nonce_account_address = if let Some(seed) = seed.clone() {
Pubkey::create_with_seed(&nonce_account_pubkey, &seed, &system_program::id())?
create_address_with_seed(&nonce_account_pubkey, &seed, &system_program::id())?
} else {
nonce_account_pubkey
};
@ -539,8 +537,8 @@ pub fn process_create_nonce_account(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<SystemError>(result, &config)
let result = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers);
log_instruction_custom_error::<SystemError>(result)
}
pub fn process_get_nonce(rpc_client: &RpcClient, nonce_account_pubkey: &Pubkey) -> ProcessResult {
@ -580,32 +578,46 @@ pub fn process_new_nonce(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<SystemError>(result, &config)
let result = rpc_client
.send_and_confirm_transaction_with_spinner(&mut tx, &[config.signers[0], nonce_authority]);
log_instruction_custom_error::<SystemError>(result)
}
pub fn process_show_nonce_account(
rpc_client: &RpcClient,
config: &CliConfig,
nonce_account_pubkey: &Pubkey,
use_lamports_unit: bool,
) -> ProcessResult {
let nonce_account = get_account(rpc_client, nonce_account_pubkey)?;
let print_account = |data: Option<&nonce::state::Data>| {
let mut nonce_account = CliNonceAccount {
balance: nonce_account.lamports,
minimum_balance_for_rent_exemption: rpc_client
.get_minimum_balance_for_rent_exemption(State::size())?,
use_lamports_unit,
..CliNonceAccount::default()
};
if let Some(ref 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());
println!(
"Balance: {}",
build_balance_message(nonce_account.lamports, use_lamports_unit, true)
);
println!(
"Minimum Balance Required: {}",
build_balance_message(
rpc_client.get_minimum_balance_for_rent_exemption(State::size())?,
use_lamports_unit,
true
)
);
match data {
Some(ref data) => {
println!("Nonce: {}", data.blockhash);
println!(
"Fee: {} lamports per signature",
data.fee_calculator.lamports_per_signature
);
println!("Authority: {}", data.authority);
}
None => {
println!("Nonce: uninitialized");
println!("Fees: uninitialized");
println!("Authority: uninitialized");
}
}
Ok(config.output_format.formatted_string(&nonce_account))
Ok("".to_string())
};
match state_from_account(&nonce_account)? {
State::Uninitialized => print_account(None),
@ -639,8 +651,8 @@ pub fn process_withdraw_from_nonce_account(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<NonceError>(result, &config)
let result = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers);
log_instruction_custom_error::<NonceError>(result)
}
#[cfg(test)]
@ -686,12 +698,7 @@ mod tests {
&Pubkey::default().to_string(),
]);
assert_eq!(
parse_command(
&test_authorize_nonce_account,
&default_keypair_file,
&mut None
)
.unwrap(),
parse_command(&test_authorize_nonce_account, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::AuthorizeNonceAccount {
nonce_account: nonce_account_pubkey,
@ -712,12 +719,7 @@ mod tests {
&authority_keypair_file,
]);
assert_eq!(
parse_command(
&test_authorize_nonce_account,
&default_keypair_file,
&mut None
)
.unwrap(),
parse_command(&test_authorize_nonce_account, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::AuthorizeNonceAccount {
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
@ -739,7 +741,7 @@ mod tests {
"50",
]);
assert_eq!(
parse_command(&test_create_nonce_account, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_create_nonce_account, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::CreateNonceAccount {
nonce_account: 1,
@ -764,7 +766,7 @@ mod tests {
&authority_keypair_file,
]);
assert_eq!(
parse_command(&test_create_nonce_account, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_create_nonce_account, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::CreateNonceAccount {
nonce_account: 1,
@ -786,7 +788,7 @@ mod tests {
&nonce_account_string,
]);
assert_eq!(
parse_command(&test_get_nonce, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_get_nonce, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::GetNonce(nonce_account_keypair.pubkey()),
signers: vec![],
@ -800,7 +802,7 @@ mod tests {
.get_matches_from(vec!["test", "new-nonce", &keypair_file]);
let nonce_account = read_keypair_file(&keypair_file).unwrap();
assert_eq!(
parse_command(&test_new_nonce, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_new_nonce, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::NewNonce {
nonce_account: nonce_account.pubkey(),
@ -820,7 +822,7 @@ mod tests {
]);
let nonce_account = read_keypair_file(&keypair_file).unwrap();
assert_eq!(
parse_command(&test_new_nonce, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_new_nonce, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::NewNonce {
nonce_account: nonce_account.pubkey(),
@ -840,7 +842,7 @@ mod tests {
&nonce_account_string,
]);
assert_eq!(
parse_command(&test_show_nonce_account, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_show_nonce_account, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::ShowNonceAccount {
nonce_account_pubkey: nonce_account_keypair.pubkey(),
@ -862,7 +864,7 @@ mod tests {
parse_command(
&test_withdraw_from_nonce_account,
&default_keypair_file,
&mut None
None
)
.unwrap(),
CliCommandInfo {
@ -890,7 +892,7 @@ mod tests {
parse_command(
&test_withdraw_from_nonce_account,
&default_keypair_file,
&mut None
None
)
.unwrap(),
CliCommandInfo {

View File

@ -79,47 +79,32 @@ pub fn parse_sign_only_reply_string(reply: &str) -> SignOnly {
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();
let signer_strings = object.get("signers");
if let Some(sig_strings) = signer_strings {
present_signers = sig_strings
.as_array()
.unwrap()
.iter()
.map(|signer_string| {
let mut signer = signer_string.as_str().unwrap().split('=');
let key = Pubkey::from_str(signer.next().unwrap()).unwrap();
let sig = Signature::from_str(signer.next().unwrap()).unwrap();
(key, sig)
})
.collect();
}
let mut absent_signers: Vec<Pubkey> = Vec::new();
let signer_strings = object.get("absent");
if let Some(sig_strings) = signer_strings {
absent_signers = sig_strings
.as_array()
.unwrap()
.iter()
.map(|val| {
let s = val.as_str().unwrap();
Pubkey::from_str(s).unwrap()
})
.collect();
}
let mut bad_signers: Vec<Pubkey> = Vec::new();
let signer_strings = object.get("badSig");
if let Some(sig_strings) = signer_strings {
bad_signers = sig_strings
.as_array()
.unwrap()
.iter()
.map(|val| {
let s = val.as_str().unwrap();
Pubkey::from_str(s).unwrap()
})
.collect();
}
let signer_strings = object.get("signers").unwrap().as_array().unwrap();
let present_signers = signer_strings
.iter()
.map(|signer_string| {
let mut signer = signer_string.as_str().unwrap().split('=');
let key = Pubkey::from_str(signer.next().unwrap()).unwrap();
let sig = Signature::from_str(signer.next().unwrap()).unwrap();
(key, sig)
})
.collect();
let signer_strings = object.get("absent").unwrap().as_array().unwrap();
let absent_signers = signer_strings
.iter()
.map(|val| {
let s = val.as_str().unwrap();
Pubkey::from_str(s).unwrap()
})
.collect();
let signer_strings = object.get("badSig").unwrap().as_array().unwrap();
let bad_signers = signer_strings
.iter()
.map(|val| {
let s = val.as_str().unwrap();
Pubkey::from_str(s).unwrap()
})
.collect();
SignOnly {
blockhash,
present_signers,

View File

@ -1,14 +1,15 @@
use crate::{
cli::{
check_account_for_fee, check_unique_pubkeys, fee_payer_arg, generate_unique_signers,
log_instruction_custom_error, nonce_authority_arg, return_signers, CliCommand,
CliCommandInfo, CliConfig, CliError, ProcessResult, SignerIndex, FEE_PAYER_ARG,
build_balance_message, check_account_for_fee, check_unique_pubkeys, fee_payer_arg,
generate_unique_signers, log_instruction_custom_error, nonce_authority_arg, return_signers,
CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult, SignerIndex, FEE_PAYER_ARG,
},
cli_output::{CliStakeHistory, CliStakeHistoryEntry, CliStakeState, CliStakeType},
nonce::{check_nonce_account, nonce_arg, NONCE_ARG, NONCE_AUTHORITY_ARG},
offline::{blockhash_query::BlockhashQuery, *},
};
use chrono::{DateTime, NaiveDateTime, SecondsFormat, Utc};
use clap::{App, Arg, ArgGroup, ArgMatches, SubCommand};
use console::style;
use solana_clap_utils::{input_parsers::*, input_validators::*, offline::*, ArgConstant};
use solana_client::rpc_client::RpcClient;
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
@ -16,7 +17,7 @@ use solana_sdk::{
account_utils::StateMut,
message::Message,
pubkey::Pubkey,
system_instruction::SystemError,
system_instruction::{create_address_with_seed, SystemError},
sysvar::{
stake_history::{self, StakeHistory},
Sysvar,
@ -85,7 +86,7 @@ impl StakeSubCommands for App<'_, '_> {
.takes_value(true)
.validator(is_amount)
.required(true)
.help("The amount to send to the stake account, in SOL")
.help("The amount of send to the vote account, in SOL")
)
.arg(
Arg::with_name("custodian")
@ -313,14 +314,6 @@ impl StakeSubCommands for App<'_, '_> {
.arg(nonce_arg())
.arg(nonce_authority_arg())
.arg(fee_payer_arg())
.arg(
Arg::with_name("custodian")
.long("custodian")
.takes_value(true)
.value_name("KEYPAIR")
.validator(is_valid_signer)
.help("Authority to override account lockup")
)
)
.subcommand(
SubCommand::with_name("stake-set-lockup")
@ -410,7 +403,7 @@ impl StakeSubCommands for App<'_, '_> {
pub fn parse_stake_create_account(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let seed = matches.value_of("seed").map(|s| s.to_string());
let epoch = value_of(matches, "lockup_epoch").unwrap_or(0);
@ -462,7 +455,7 @@ pub fn parse_stake_create_account(
pub fn parse_stake_delegate_stake(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let stake_account_pubkey =
pubkey_of_signer(matches, "stake_account_pubkey", wallet_manager)?.unwrap();
@ -504,7 +497,7 @@ pub fn parse_stake_delegate_stake(
pub fn parse_stake_authorize(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let stake_account_pubkey =
pubkey_of_signer(matches, "stake_account_pubkey", wallet_manager)?.unwrap();
@ -587,7 +580,7 @@ pub fn parse_stake_authorize(
pub fn parse_split_stake(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let stake_account_pubkey =
pubkey_of_signer(matches, "stake_account_pubkey", wallet_manager)?.unwrap();
@ -632,7 +625,7 @@ pub fn parse_split_stake(
pub fn parse_stake_deactivate_stake(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let stake_account_pubkey =
pubkey_of_signer(matches, "stake_account_pubkey", wallet_manager)?.unwrap();
@ -669,7 +662,7 @@ pub fn parse_stake_deactivate_stake(
pub fn parse_stake_withdraw_stake(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let stake_account_pubkey =
pubkey_of_signer(matches, "stake_account_pubkey", wallet_manager)?.unwrap();
@ -684,15 +677,11 @@ pub fn parse_stake_withdraw_stake(
let (nonce_authority, nonce_authority_pubkey) =
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
let (fee_payer, fee_payer_pubkey) = signer_of(matches, FEE_PAYER_ARG.name, wallet_manager)?;
let (custodian, custodian_pubkey) = signer_of(matches, "custodian", wallet_manager)?;
let mut bulk_signers = vec![withdraw_authority, fee_payer];
if nonce_account.is_some() {
bulk_signers.push(nonce_authority);
}
if custodian.is_some() {
bulk_signers.push(custodian);
}
let signer_info =
generate_unique_signers(bulk_signers, matches, default_signer_path, wallet_manager)?;
@ -707,7 +696,6 @@ pub fn parse_stake_withdraw_stake(
nonce_account,
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
fee_payer: signer_info.index_of(fee_payer_pubkey).unwrap(),
custodian: custodian_pubkey.and_then(|_| signer_info.index_of(custodian_pubkey)),
},
signers: signer_info.signers,
})
@ -716,7 +704,7 @@ pub fn parse_stake_withdraw_stake(
pub fn parse_stake_set_lockup(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let stake_account_pubkey =
pubkey_of_signer(matches, "stake_account_pubkey", wallet_manager)?.unwrap();
@ -761,7 +749,7 @@ pub fn parse_stake_set_lockup(
pub fn parse_show_stake_account(
matches: &ArgMatches<'_>,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let stake_account_pubkey =
pubkey_of_signer(matches, "stake_account_pubkey", wallet_manager)?.unwrap();
@ -802,7 +790,7 @@ pub fn process_create_stake_account(
) -> ProcessResult {
let stake_account = config.signers[stake_account];
let stake_account_address = if let Some(seed) = seed {
Pubkey::create_with_seed(&stake_account.pubkey(), &seed, &solana_stake_program::id())?
create_address_with_seed(&stake_account.pubkey(), &seed, &solana_stake_program::id())?
} else {
stake_account.pubkey()
};
@ -881,7 +869,7 @@ pub fn process_create_stake_account(
if sign_only {
tx.try_partial_sign(&config.signers, recent_blockhash)?;
return_signers(&tx, &config)
return_signers(&tx)
} else {
tx.try_sign(&config.signers, recent_blockhash)?;
if let Some(nonce_account) = &nonce_account {
@ -894,8 +882,8 @@ pub fn process_create_stake_account(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<SystemError>(result, &config)
let result = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers);
log_instruction_custom_error::<SystemError>(result)
}
}
@ -946,7 +934,7 @@ pub fn process_stake_authorize(
if sign_only {
tx.try_partial_sign(&config.signers, recent_blockhash)?;
return_signers(&tx, &config)
return_signers(&tx)
} else {
tx.try_sign(&config.signers, recent_blockhash)?;
if let Some(nonce_account) = &nonce_account {
@ -959,8 +947,8 @@ pub fn process_stake_authorize(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<StakeError>(result, &config)
let result = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers);
log_instruction_custom_error::<StakeError>(result)
}
}
@ -1000,7 +988,7 @@ pub fn process_deactivate_stake_account(
if sign_only {
tx.try_partial_sign(&config.signers, recent_blockhash)?;
return_signers(&tx, &config)
return_signers(&tx)
} else {
tx.try_sign(&config.signers, recent_blockhash)?;
if let Some(nonce_account) = &nonce_account {
@ -1013,8 +1001,8 @@ pub fn process_deactivate_stake_account(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<StakeError>(result, &config)
let result = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers);
log_instruction_custom_error::<StakeError>(result)
}
}
@ -1026,7 +1014,6 @@ pub fn process_withdraw_stake(
destination_account_pubkey: &Pubkey,
lamports: u64,
withdraw_authority: SignerIndex,
custodian: Option<SignerIndex>,
sign_only: bool,
blockhash_query: &BlockhashQuery,
nonce_account: Option<&Pubkey>,
@ -1036,14 +1023,12 @@ pub fn process_withdraw_stake(
let (recent_blockhash, fee_calculator) =
blockhash_query.get_blockhash_and_fee_calculator(rpc_client)?;
let withdraw_authority = config.signers[withdraw_authority];
let custodian = custodian.map(|index| config.signers[index]);
let ixs = vec![stake_instruction::withdraw(
stake_account_pubkey,
&withdraw_authority.pubkey(),
destination_account_pubkey,
lamports,
custodian.map(|signer| signer.pubkey()).as_ref(),
)];
let fee_payer = config.signers[fee_payer];
@ -1063,7 +1048,7 @@ pub fn process_withdraw_stake(
if sign_only {
tx.try_partial_sign(&config.signers, recent_blockhash)?;
return_signers(&tx, &config)
return_signers(&tx)
} else {
tx.try_sign(&config.signers, recent_blockhash)?;
if let Some(nonce_account) = &nonce_account {
@ -1076,8 +1061,8 @@ pub fn process_withdraw_stake(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<SystemError>(result, &config)
let result = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers);
log_instruction_custom_error::<SystemError>(result)
}
}
@ -1123,7 +1108,7 @@ pub fn process_split_stake(
let stake_authority = config.signers[stake_authority];
let split_stake_account_address = if let Some(seed) = split_stake_account_seed {
Pubkey::create_with_seed(
create_address_with_seed(
&split_stake_account.pubkey(),
&seed,
&solana_stake_program::id(),
@ -1197,7 +1182,7 @@ pub fn process_split_stake(
if sign_only {
tx.try_partial_sign(&config.signers, recent_blockhash)?;
return_signers(&tx, &config)
return_signers(&tx)
} else {
tx.try_sign(&config.signers, recent_blockhash)?;
if let Some(nonce_account) = &nonce_account {
@ -1210,8 +1195,8 @@ pub fn process_split_stake(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<StakeError>(result, &config)
let result = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers);
log_instruction_custom_error::<StakeError>(result)
}
}
@ -1254,7 +1239,7 @@ pub fn process_stake_set_lockup(
if sign_only {
tx.try_partial_sign(&config.signers, recent_blockhash)?;
return_signers(&tx, &config)
return_signers(&tx)
} else {
tx.try_sign(&config.signers, recent_blockhash)?;
if let Some(nonce_account) = &nonce_account {
@ -1267,66 +1252,84 @@ pub fn process_stake_set_lockup(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<StakeError>(result, &config)
let result = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers);
log_instruction_custom_error::<StakeError>(result)
}
}
pub fn build_stake_state(
stake_lamports: u64,
stake_state: &StakeState,
use_lamports_unit: bool,
) -> CliStakeState {
pub fn print_stake_state(stake_lamports: u64, stake_state: &StakeState, use_lamports_unit: bool) {
fn show_authorized(authorized: &Authorized) {
println!("Stake Authority: {}", authorized.staker);
println!("Withdraw Authority: {}", authorized.withdrawer);
}
fn show_lockup(lockup: &Lockup) {
println!(
"Lockup Timestamp: {} (UnixTimestamp: {})",
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(lockup.unix_timestamp, 0), Utc)
.to_rfc3339_opts(SecondsFormat::Secs, true),
lockup.unix_timestamp
);
println!("Lockup Epoch: {}", lockup.epoch);
println!("Lockup Custodian: {}", lockup.custodian);
}
match stake_state {
StakeState::Stake(
Meta {
authorized, lockup, ..
},
stake,
) => CliStakeState {
stake_type: CliStakeType::Stake,
total_stake: stake_lamports,
delegated_stake: Some(stake.delegation.stake),
delegated_vote_account_address: if stake.delegation.voter_pubkey != Pubkey::default() {
Some(stake.delegation.voter_pubkey.to_string())
} else {
None
},
activation_epoch: Some(if stake.delegation.activation_epoch < std::u64::MAX {
stake.delegation.activation_epoch
} else {
0
}),
deactivation_epoch: if stake.delegation.deactivation_epoch < std::u64::MAX {
Some(stake.delegation.deactivation_epoch)
} else {
None
},
authorized: Some(authorized.into()),
lockup: Some(lockup.into()),
use_lamports_unit,
},
StakeState::RewardsPool => CliStakeState {
stake_type: CliStakeType::RewardsPool,
..CliStakeState::default()
},
StakeState::Uninitialized => CliStakeState::default(),
) => {
println!(
"Total Stake: {}",
build_balance_message(stake_lamports, use_lamports_unit, true)
);
println!("Credits Observed: {}", stake.credits_observed);
println!(
"Delegated Stake: {}",
build_balance_message(stake.delegation.stake, use_lamports_unit, true)
);
if stake.delegation.voter_pubkey != Pubkey::default() {
println!(
"Delegated Vote Account Address: {}",
stake.delegation.voter_pubkey
);
}
println!(
"Stake activates starting from epoch: {}",
if stake.delegation.activation_epoch < std::u64::MAX {
stake.delegation.activation_epoch
} else {
0
}
);
if stake.delegation.deactivation_epoch < std::u64::MAX {
println!(
"Stake deactivates starting from epoch: {}",
stake.delegation.deactivation_epoch
);
}
show_authorized(&authorized);
show_lockup(&lockup);
}
StakeState::RewardsPool => println!("Stake account is a rewards pool"),
StakeState::Uninitialized => println!("Stake account is uninitialized"),
StakeState::Initialized(Meta {
authorized, lockup, ..
}) => CliStakeState {
stake_type: CliStakeType::Initialized,
total_stake: stake_lamports,
authorized: Some(authorized.into()),
lockup: Some(lockup.into()),
use_lamports_unit,
..CliStakeState::default()
},
}) => {
println!(
"Total Stake: {}",
build_balance_message(stake_lamports, use_lamports_unit, true)
);
println!("Stake account is undelegated");
show_authorized(&authorized);
show_lockup(&lockup);
}
}
}
pub fn process_show_stake_account(
rpc_client: &RpcClient,
config: &CliConfig,
_config: &CliConfig,
stake_account_pubkey: &Pubkey,
use_lamports_unit: bool,
) -> ProcessResult {
@ -1340,8 +1343,8 @@ pub fn process_show_stake_account(
}
match stake_account.state() {
Ok(stake_state) => {
let state = build_stake_state(stake_account.lamports, &stake_state, use_lamports_unit);
Ok(config.output_format.formatted_string(&state))
print_stake_state(stake_account.lamports, &stake_state, use_lamports_unit);
Ok("".to_string())
}
Err(err) => Err(CliError::RpcRequestError(format!(
"Account data could not be deserialized to stake state: {}",
@ -1353,7 +1356,7 @@ pub fn process_show_stake_account(
pub fn process_show_stake_history(
rpc_client: &RpcClient,
config: &CliConfig,
_config: &CliConfig,
use_lamports_unit: bool,
) -> ProcessResult {
let stake_history_account = rpc_client.get_account(&stake_history::id())?;
@ -1361,15 +1364,27 @@ pub fn process_show_stake_history(
CliError::RpcRequestError("Failed to deserialize stake history".to_string())
})?;
let mut entries: Vec<CliStakeHistoryEntry> = vec![];
for entry in stake_history.deref() {
entries.push(entry.into());
println!();
println!(
"{}",
style(format!(
" {:<5} {:>20} {:>20} {:>20}",
"Epoch", "Effective Stake", "Activating Stake", "Deactivating Stake",
))
.bold()
);
for (epoch, entry) in stake_history.deref() {
println!(
" {:>5} {:>20} {:>20} {:>20} {}",
epoch,
build_balance_message(entry.effective, use_lamports_unit, false),
build_balance_message(entry.activating, use_lamports_unit, false),
build_balance_message(entry.deactivating, use_lamports_unit, false),
if use_lamports_unit { "lamports" } else { "SOL" }
);
}
let stake_history_output = CliStakeHistory {
entries,
use_lamports_unit,
};
Ok(config.output_format.formatted_string(&stake_history_output))
Ok("".to_string())
}
#[allow(clippy::too_many_arguments)]
@ -1462,7 +1477,7 @@ pub fn process_delegate_stake(
if sign_only {
tx.try_partial_sign(&config.signers, recent_blockhash)?;
return_signers(&tx, &config)
return_signers(&tx)
} else {
tx.try_sign(&config.signers, recent_blockhash)?;
if let Some(nonce_account) = &nonce_account {
@ -1475,8 +1490,8 @@ pub fn process_delegate_stake(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<StakeError>(result, &config)
let result = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers);
log_instruction_custom_error::<StakeError>(result)
}
}
@ -1510,9 +1525,6 @@ mod tests {
let (stake_authority_keypair_file, mut tmp_file) = make_tmp_file();
let stake_authority_keypair = Keypair::new();
write_keypair(&stake_authority_keypair, tmp_file.as_file_mut()).unwrap();
let (custodian_keypair_file, mut tmp_file) = make_tmp_file();
let custodian_keypair = Keypair::new();
write_keypair(&custodian_keypair, tmp_file.as_file_mut()).unwrap();
// stake-authorize subcommand
let stake_account_string = stake_account_pubkey.to_string();
@ -1530,7 +1542,7 @@ mod tests {
&new_withdraw_string,
]);
assert_eq!(
parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_stake_authorize, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::StakeAuthorize {
stake_account_pubkey,
@ -1564,7 +1576,7 @@ mod tests {
&withdraw_authority_keypair_file,
]);
assert_eq!(
parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_stake_authorize, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::StakeAuthorize {
stake_account_pubkey,
@ -1602,7 +1614,7 @@ mod tests {
&withdraw_authority_keypair_file,
]);
assert_eq!(
parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_stake_authorize, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::StakeAuthorize {
stake_account_pubkey,
@ -1632,7 +1644,7 @@ mod tests {
&new_stake_string,
]);
assert_eq!(
parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_stake_authorize, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::StakeAuthorize {
stake_account_pubkey,
@ -1656,7 +1668,7 @@ mod tests {
&stake_authority_keypair_file,
]);
assert_eq!(
parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_stake_authorize, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::StakeAuthorize {
stake_account_pubkey,
@ -1686,7 +1698,7 @@ mod tests {
&withdraw_authority_keypair_file,
]);
assert_eq!(
parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_stake_authorize, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::StakeAuthorize {
stake_account_pubkey,
@ -1713,7 +1725,7 @@ mod tests {
&new_withdraw_string,
]);
assert_eq!(
parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_stake_authorize, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::StakeAuthorize {
stake_account_pubkey,
@ -1741,7 +1753,7 @@ mod tests {
&withdraw_authority_keypair_file,
]);
assert_eq!(
parse_command(&test_stake_authorize, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_stake_authorize, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::StakeAuthorize {
stake_account_pubkey,
@ -1779,7 +1791,7 @@ mod tests {
"--sign-only",
]);
assert_eq!(
parse_command(&test_authorize, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_authorize, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::StakeAuthorize {
stake_account_pubkey,
@ -1812,7 +1824,7 @@ mod tests {
&pubkey.to_string(),
]);
assert_eq!(
parse_command(&test_authorize, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_authorize, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::StakeAuthorize {
stake_account_pubkey,
@ -1858,7 +1870,7 @@ mod tests {
&pubkey2.to_string(),
]);
assert_eq!(
parse_command(&test_authorize, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_authorize, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::StakeAuthorize {
stake_account_pubkey,
@ -1890,7 +1902,7 @@ mod tests {
&blockhash_string,
]);
assert_eq!(
parse_command(&test_authorize, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_authorize, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::StakeAuthorize {
stake_account_pubkey,
@ -1927,7 +1939,7 @@ mod tests {
&nonce_keypair_file,
]);
assert_eq!(
parse_command(&test_authorize, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_authorize, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::StakeAuthorize {
stake_account_pubkey,
@ -1963,7 +1975,7 @@ mod tests {
&fee_payer_keypair_file,
]);
assert_eq!(
parse_command(&test_authorize, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_authorize, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::StakeAuthorize {
stake_account_pubkey,
@ -1997,7 +2009,7 @@ mod tests {
&signer,
]);
assert_eq!(
parse_command(&test_authorize, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_authorize, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::StakeAuthorize {
stake_account_pubkey,
@ -2038,7 +2050,7 @@ mod tests {
"43",
]);
assert_eq!(
parse_command(&test_create_stake_account, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_create_stake_account, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::CreateStakeAccount {
stake_account: 1,
@ -2079,12 +2091,7 @@ mod tests {
]);
assert_eq!(
parse_command(
&test_create_stake_account2,
&default_keypair_file,
&mut None
)
.unwrap(),
parse_command(&test_create_stake_account2, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::CreateStakeAccount {
stake_account: 1,
@ -2137,12 +2144,7 @@ mod tests {
]);
assert_eq!(
parse_command(
&test_create_stake_account2,
&default_keypair_file,
&mut None
)
.unwrap(),
parse_command(&test_create_stake_account2, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::CreateStakeAccount {
stake_account: 1,
@ -2178,7 +2180,7 @@ mod tests {
&vote_account_string,
]);
assert_eq!(
parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_delegate_stake, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::DelegateStake {
stake_account_pubkey,
@ -2207,7 +2209,7 @@ mod tests {
&stake_authority_keypair_file,
]);
assert_eq!(
parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_delegate_stake, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::DelegateStake {
stake_account_pubkey,
@ -2238,7 +2240,7 @@ mod tests {
&vote_account_string,
]);
assert_eq!(
parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_delegate_stake, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::DelegateStake {
stake_account_pubkey,
@ -2267,7 +2269,7 @@ mod tests {
&blockhash_string,
]);
assert_eq!(
parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_delegate_stake, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::DelegateStake {
stake_account_pubkey,
@ -2297,7 +2299,7 @@ mod tests {
"--sign-only",
]);
assert_eq!(
parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_delegate_stake, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::DelegateStake {
stake_account_pubkey,
@ -2331,7 +2333,7 @@ mod tests {
&key1.to_string(),
]);
assert_eq!(
parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_delegate_stake, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::DelegateStake {
stake_account_pubkey,
@ -2377,7 +2379,7 @@ mod tests {
&key2.to_string(),
]);
assert_eq!(
parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_delegate_stake, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::DelegateStake {
stake_account_pubkey,
@ -2414,7 +2416,7 @@ mod tests {
&fee_payer_keypair_file,
]);
assert_eq!(
parse_command(&test_delegate_stake, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_delegate_stake, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::DelegateStake {
stake_account_pubkey,
@ -2444,14 +2446,13 @@ mod tests {
]);
assert_eq!(
parse_command(&test_withdraw_stake, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_withdraw_stake, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::WithdrawStake {
stake_account_pubkey,
destination_account_pubkey: stake_account_pubkey,
lamports: 42_000_000_000,
withdraw_authority: 0,
custodian: None,
sign_only: false,
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
nonce_account: None,
@ -2474,14 +2475,13 @@ mod tests {
]);
assert_eq!(
parse_command(&test_withdraw_stake, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_withdraw_stake, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::WithdrawStake {
stake_account_pubkey,
destination_account_pubkey: stake_account_pubkey,
lamports: 42_000_000_000,
withdraw_authority: 1,
custodian: None,
sign_only: false,
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
nonce_account: None,
@ -2497,39 +2497,6 @@ mod tests {
}
);
// Test WithdrawStake Subcommand w/ custodian
let test_withdraw_stake = test_commands.clone().get_matches_from(vec![
"test",
"withdraw-stake",
&stake_account_string,
&stake_account_string,
"42",
"--custodian",
&custodian_keypair_file,
]);
assert_eq!(
parse_command(&test_withdraw_stake, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::WithdrawStake {
stake_account_pubkey,
destination_account_pubkey: stake_account_pubkey,
lamports: 42_000_000_000,
withdraw_authority: 0,
custodian: Some(1),
sign_only: false,
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
nonce_account: None,
nonce_authority: 0,
fee_payer: 0,
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
read_keypair_file(&custodian_keypair_file).unwrap().into()
],
}
);
// Test WithdrawStake Subcommand w/ authority and offline nonce
let test_withdraw_stake = test_commands.clone().get_matches_from(vec![
"test",
@ -2552,14 +2519,13 @@ mod tests {
]);
assert_eq!(
parse_command(&test_withdraw_stake, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_withdraw_stake, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::WithdrawStake {
stake_account_pubkey,
destination_account_pubkey: stake_account_pubkey,
lamports: 42_000_000_000,
withdraw_authority: 0,
custodian: None,
sign_only: false,
blockhash_query: BlockhashQuery::FeeCalculator(
blockhash_query::Source::NonceAccount(nonce_account),
@ -2585,7 +2551,7 @@ mod tests {
&stake_account_string,
]);
assert_eq!(
parse_command(&test_deactivate_stake, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_deactivate_stake, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::DeactivateStake {
stake_account_pubkey,
@ -2609,7 +2575,7 @@ mod tests {
&stake_authority_keypair_file,
]);
assert_eq!(
parse_command(&test_deactivate_stake, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_deactivate_stake, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::DeactivateStake {
stake_account_pubkey,
@ -2640,7 +2606,7 @@ mod tests {
&blockhash_string,
]);
assert_eq!(
parse_command(&test_deactivate_stake, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_deactivate_stake, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::DeactivateStake {
stake_account_pubkey,
@ -2667,7 +2633,7 @@ mod tests {
"--sign-only",
]);
assert_eq!(
parse_command(&test_deactivate_stake, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_deactivate_stake, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::DeactivateStake {
stake_account_pubkey,
@ -2698,7 +2664,7 @@ mod tests {
&key1.to_string(),
]);
assert_eq!(
parse_command(&test_deactivate_stake, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_deactivate_stake, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::DeactivateStake {
stake_account_pubkey,
@ -2741,7 +2707,7 @@ mod tests {
&key2.to_string(),
]);
assert_eq!(
parse_command(&test_deactivate_stake, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_deactivate_stake, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::DeactivateStake {
stake_account_pubkey,
@ -2772,7 +2738,7 @@ mod tests {
&fee_payer_keypair_file,
]);
assert_eq!(
parse_command(&test_deactivate_stake, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_deactivate_stake, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::DeactivateStake {
stake_account_pubkey,
@ -2806,7 +2772,7 @@ mod tests {
"50",
]);
assert_eq!(
parse_command(&test_split_stake_account, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_split_stake_account, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::SplitStake {
stake_account_pubkey: stake_account_keypair.pubkey(),
@ -2867,7 +2833,7 @@ mod tests {
&stake_signer,
]);
assert_eq!(
parse_command(&test_split_stake_account, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_split_stake_account, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::SplitStake {
stake_account_pubkey: stake_account_keypair.pubkey(),

View File

@ -102,7 +102,7 @@ impl StorageSubCommands for App<'_, '_> {
pub fn parse_storage_create_archiver_account(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let account_owner =
pubkey_of_signer(matches, "storage_account_owner", wallet_manager)?.unwrap();
@ -130,7 +130,7 @@ pub fn parse_storage_create_archiver_account(
pub fn parse_storage_create_validator_account(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let account_owner =
pubkey_of_signer(matches, "storage_account_owner", wallet_manager)?.unwrap();
@ -158,7 +158,7 @@ pub fn parse_storage_create_validator_account(
pub fn parse_storage_claim_reward(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let node_account_pubkey =
pubkey_of_signer(matches, "node_account_pubkey", wallet_manager)?.unwrap();
@ -180,7 +180,7 @@ pub fn parse_storage_claim_reward(
pub fn parse_storage_get_account_command(
matches: &ArgMatches<'_>,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let storage_account_pubkey =
pubkey_of_signer(matches, "storage_account_pubkey", wallet_manager)?.unwrap();
@ -242,8 +242,8 @@ pub fn process_create_storage_account(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<SystemError>(result, &config)
let result = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers);
log_instruction_custom_error::<SystemError>(result)
}
pub fn process_claim_storage_reward(
@ -266,7 +266,7 @@ pub fn process_claim_storage_reward(
&fee_calculator,
&tx.message,
)?;
let signature = rpc_client.send_and_confirm_transaction_with_spinner(&tx)?;
let signature = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &signers)?;
Ok(signature.to_string())
}
@ -330,7 +330,7 @@ mod tests {
parse_command(
&test_create_archiver_storage_account,
&default_keypair_file,
&mut None
None
)
.unwrap(),
CliCommandInfo {
@ -362,7 +362,7 @@ mod tests {
parse_command(
&test_create_validator_storage_account,
&default_keypair_file,
&mut None
None
)
.unwrap(),
CliCommandInfo {
@ -385,7 +385,7 @@ mod tests {
&storage_account_string,
]);
assert_eq!(
parse_command(&test_claim_storage_reward, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_claim_storage_reward, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::ClaimStorageReward {
node_account_pubkey: pubkey,

View File

@ -1,6 +1,6 @@
use crate::{
cli::{check_account_for_fee, CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult},
cli_output::{CliValidatorInfo, CliValidatorInfoVec},
display::println_name_value,
};
use bincode::deserialize;
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
@ -25,6 +25,7 @@ use solana_sdk::{
transaction::Transaction,
};
use std::{error, sync::Arc};
use titlecase::titlecase;
pub const MAX_SHORT_FIELD_LENGTH: usize = 70;
pub const MAX_LONG_FIELD_LENGTH: usize = 300;
@ -228,7 +229,7 @@ impl ValidatorInfoSubCommands for App<'_, '_> {
pub fn parse_validator_info_command(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let info_pubkey = pubkey_of(matches, "info_pubkey");
// Prepare validator info
@ -367,18 +368,14 @@ pub fn process_set_validator_info(
&fee_calculator,
&tx.message,
)?;
let signature_str = rpc_client.send_and_confirm_transaction_with_spinner(&tx)?;
let signature_str = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &signers)?;
println!("Success! Validator info published at: {:?}", info_pubkey);
println!("{}", signature_str);
Ok("".to_string())
}
pub fn process_get_validator_info(
rpc_client: &RpcClient,
config: &CliConfig,
pubkey: Option<Pubkey>,
) -> ProcessResult {
pub fn process_get_validator_info(rpc_client: &RpcClient, pubkey: Option<Pubkey>) -> ProcessResult {
let validator_info: Vec<(Pubkey, Account)> = if let Some(validator_info_pubkey) = pubkey {
vec![(
validator_info_pubkey,
@ -397,22 +394,24 @@ pub fn process_get_validator_info(
.collect()
};
let mut validator_info_list: Vec<CliValidatorInfo> = vec![];
if validator_info.is_empty() {
println!("No validator info accounts found");
}
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)?;
validator_info_list.push(CliValidatorInfo {
identity_pubkey: validator_pubkey.to_string(),
info_pubkey: validator_info_pubkey.to_string(),
info: validator_info,
});
println!();
println_name_value("Validator Identity Pubkey:", &validator_pubkey.to_string());
println_name_value(" Info Pubkey:", &validator_info_pubkey.to_string());
for (key, value) in validator_info.iter() {
println_name_value(
&format!(" {}:", titlecase(key)),
&value.as_str().unwrap_or("?"),
);
}
}
Ok(config
.output_format
.formatted_string(&CliValidatorInfoVec::new(validator_info_list)))
Ok("".to_string())
}
#[cfg(test)]

View File

@ -1,22 +1,19 @@
use crate::{
cli::{
check_account_for_fee, check_unique_pubkeys, generate_unique_signers,
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
ProcessResult, SignerIndex,
},
cli_output::{CliEpochVotingHistory, CliLockout, CliVoteAccount},
use crate::cli::{
build_balance_message, check_account_for_fee, check_unique_pubkeys, generate_unique_signers,
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError, CliSignerInfo,
ProcessResult, SignerIndex,
};
use clap::{value_t_or_exit, App, Arg, ArgMatches, SubCommand};
use solana_clap_utils::{
commitment::{commitment_arg, COMMITMENT_ARG},
input_parsers::*,
input_validators::*,
};
use solana_clap_utils::{input_parsers::*, input_validators::*};
use solana_client::rpc_client::RpcClient;
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{
account::Account, commitment_config::CommitmentConfig, message::Message, pubkey::Pubkey,
system_instruction::SystemError, transaction::Transaction,
account::Account,
commitment_config::CommitmentConfig,
message::Message,
pubkey::Pubkey,
system_instruction::{create_address_with_seed, SystemError},
transaction::Transaction,
};
use solana_vote_program::{
vote_instruction::{self, withdraw, VoteError},
@ -95,17 +92,9 @@ impl VoteSubCommands for App<'_, '_> {
.validator(is_valid_pubkey)
.help("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_pubkey")
.index(3)
.index(2)
.value_name("AUTHORIZED_PUBKEY")
.takes_value(true)
.required(true)
@ -125,17 +114,9 @@ impl VoteSubCommands for App<'_, '_> {
.validator(is_valid_pubkey)
.help("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_pubkey")
.index(3)
.index(2)
.value_name("AUTHORIZED_PUBKEY")
.takes_value(true)
.required(true)
@ -174,41 +155,18 @@ impl VoteSubCommands for App<'_, '_> {
.help("Authorized withdrawer keypair"),
)
)
.subcommand(
SubCommand::with_name("vote-update-commission")
.about("Update the vote account's commission")
.arg(
Arg::with_name("vote_account_pubkey")
.index(1)
.value_name("VOTE_ACCOUNT_ADDRESS")
.takes_value(true)
.required(true)
.validator(is_valid_pubkey)
.help("Vote account to update"),
)
.arg(
Arg::with_name("commission")
.index(2)
.value_name("PERCENTAGE")
.takes_value(true)
.required(true)
.validator(is_valid_percentage)
.help("The new commission")
)
.arg(
Arg::with_name("authorized_withdrawer")
.index(3)
.value_name("AUTHORIZED_KEYPAIR")
.takes_value(true)
.required(true)
.validator(is_valid_signer)
.help("Authorized withdrawer keypair"),
)
)
.subcommand(
SubCommand::with_name("vote-account")
.about("Show the contents of a vote account")
.alias("show-vote-account")
.arg(
Arg::with_name("confirmed")
.long("confirmed")
.takes_value(false)
.help(
"Return information at maximum-lockout commitment level",
),
)
.arg(
Arg::with_name("vote_account_pubkey")
.index(1)
@ -223,8 +181,7 @@ impl VoteSubCommands for App<'_, '_> {
.long("lamports")
.takes_value(false)
.help("Display balance in lamports instead of SOL"),
)
.arg(commitment_arg()),
),
)
.subcommand(
SubCommand::with_name("withdraw-from-vote-account")
@ -271,9 +228,9 @@ impl VoteSubCommands for App<'_, '_> {
pub fn parse_create_vote_account(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let (vote_account, vote_account_pubkey) = signer_of(matches, "vote_account", wallet_manager)?;
let (vote_account, _) = signer_of(matches, "vote_account", wallet_manager)?;
let seed = matches.value_of("seed").map(|s| s.to_string());
let (identity_account, identity_pubkey) =
signer_of(matches, "identity_account", wallet_manager)?;
@ -291,7 +248,6 @@ pub fn parse_create_vote_account(
Ok(CliCommandInfo {
command: CliCommand::CreateVoteAccount {
vote_account: signer_info.index_of(vote_account_pubkey).unwrap(),
seed,
identity_account: signer_info.index_of(identity_pubkey).unwrap(),
authorized_voter,
@ -305,18 +261,17 @@ pub fn parse_create_vote_account(
pub fn parse_vote_authorize(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
vote_authorize: VoteAuthorize,
) -> 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 payer_provided = None;
let signer_info = generate_unique_signers(
vec![payer_provided, authorized],
let authorized_voter_provided = None;
let CliSignerInfo { signers } = generate_unique_signers(
vec![authorized_voter_provided],
matches,
default_signer_path,
wallet_manager,
@ -328,21 +283,20 @@ pub fn parse_vote_authorize(
new_authorized_pubkey,
vote_authorize,
},
signers: signer_info.signers,
signers,
})
}
pub fn parse_vote_update_validator(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let vote_account_pubkey =
pubkey_of_signer(matches, "vote_account_pubkey", wallet_manager)?.unwrap();
let (new_identity_account, new_identity_pubkey) =
signer_of(matches, "new_identity_account", wallet_manager)?;
let (authorized_withdrawer, authorized_withdrawer_pubkey) =
signer_of(matches, "authorized_withdrawer", wallet_manager)?;
let (authorized_withdrawer, _) = signer_of(matches, "authorized_withdrawer", wallet_manager)?;
let payer_provided = None;
let signer_info = generate_unique_signers(
@ -356,36 +310,6 @@ pub fn parse_vote_update_validator(
command: CliCommand::VoteUpdateValidator {
vote_account_pubkey,
new_identity_account: signer_info.index_of(new_identity_pubkey).unwrap(),
withdraw_authority: signer_info.index_of(authorized_withdrawer_pubkey).unwrap(),
},
signers: signer_info.signers,
})
}
pub fn parse_vote_update_commission(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let vote_account_pubkey =
pubkey_of_signer(matches, "vote_account_pubkey", wallet_manager)?.unwrap();
let (authorized_withdrawer, authorized_withdrawer_pubkey) =
signer_of(matches, "authorized_withdrawer", wallet_manager)?;
let commission = value_t_or_exit!(matches, "commission", u8);
let payer_provided = None;
let signer_info = generate_unique_signers(
vec![payer_provided, authorized_withdrawer],
matches,
default_signer_path,
wallet_manager,
)?;
Ok(CliCommandInfo {
command: CliCommand::VoteUpdateCommission {
vote_account_pubkey,
commission,
withdraw_authority: signer_info.index_of(authorized_withdrawer_pubkey).unwrap(),
},
signers: signer_info.signers,
})
@ -393,12 +317,16 @@ pub fn parse_vote_update_commission(
pub fn parse_vote_get_account_command(
matches: &ArgMatches<'_>,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let vote_account_pubkey =
pubkey_of_signer(matches, "vote_account_pubkey", wallet_manager)?.unwrap();
let use_lamports_unit = matches.is_present("lamports");
let commitment_config = commitment_of(matches, COMMITMENT_ARG.long).unwrap();
let commitment_config = if matches.is_present("confirmed") {
CommitmentConfig::default()
} else {
CommitmentConfig::recent()
};
Ok(CliCommandInfo {
command: CliCommand::ShowVoteAccount {
pubkey: vote_account_pubkey,
@ -412,7 +340,7 @@ pub fn parse_vote_get_account_command(
pub fn parse_withdraw_from_vote_account(
matches: &ArgMatches<'_>,
default_signer_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
wallet_manager: Option<&Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let vote_account_pubkey =
pubkey_of_signer(matches, "vote_account_pubkey", wallet_manager)?.unwrap();
@ -444,17 +372,16 @@ pub fn parse_withdraw_from_vote_account(
pub fn process_create_vote_account(
rpc_client: &RpcClient,
config: &CliConfig,
vote_account: SignerIndex,
seed: &Option<String>,
identity_account: SignerIndex,
authorized_voter: &Option<Pubkey>,
authorized_withdrawer: &Option<Pubkey>,
commission: u8,
) -> ProcessResult {
let vote_account = config.signers[vote_account];
let vote_account = config.signers[1];
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())?
create_address_with_seed(&vote_account_pubkey, &seed, &solana_vote_program::id())?
} else {
vote_account_pubkey
};
@ -521,8 +448,8 @@ pub fn process_create_vote_account(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<SystemError>(result, &config)
let result = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers);
log_instruction_custom_error::<SystemError>(result)
}
pub fn process_vote_authorize(
@ -532,24 +459,16 @@ pub fn process_vote_authorize(
new_authorized_pubkey: &Pubkey,
vote_authorize: VoteAuthorize,
) -> 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]
};
check_unique_pubkeys(
(&authorized.pubkey(), "authorized_account".to_string()),
(vote_account_pubkey, "vote_account_pubkey".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
vote_account_pubkey, // vote account to update
&config.signers[0].pubkey(), // current authorized voter
new_authorized_pubkey, // new vote signer/withdrawer
vote_authorize, // vote or withdraw
)];
let message = Message::new_with_payer(&ixs, Some(&config.signers[0].pubkey()));
@ -561,8 +480,9 @@ pub fn process_vote_authorize(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<VoteError>(result, &config)
let result =
rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &[config.signers[0]]);
log_instruction_custom_error::<VoteError>(result)
}
pub fn process_vote_update_validator(
@ -570,9 +490,8 @@ pub fn process_vote_update_validator(
config: &CliConfig,
vote_account_pubkey: &Pubkey,
new_identity_account: SignerIndex,
withdraw_authority: SignerIndex,
) -> ProcessResult {
let authorized_withdrawer = config.signers[withdraw_authority];
let authorized_withdrawer = config.signers[1];
let new_identity_account = config.signers[new_identity_account];
let new_identity_pubkey = new_identity_account.pubkey();
check_unique_pubkeys(
@ -580,7 +499,7 @@ pub fn process_vote_update_validator(
(&new_identity_pubkey, "new_identity_account".to_string()),
)?;
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let ixs = vec![vote_instruction::update_validator_identity(
let ixs = vec![vote_instruction::update_node(
vote_account_pubkey,
&authorized_withdrawer.pubkey(),
&new_identity_pubkey,
@ -595,36 +514,8 @@ pub fn process_vote_update_validator(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<VoteError>(result, &config)
}
pub fn process_vote_update_commission(
rpc_client: &RpcClient,
config: &CliConfig,
vote_account_pubkey: &Pubkey,
commission: u8,
withdraw_authority: SignerIndex,
) -> ProcessResult {
let authorized_withdrawer = config.signers[withdraw_authority];
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let ixs = vec![vote_instruction::update_commission(
vote_account_pubkey,
&authorized_withdrawer.pubkey(),
commission,
)];
let message = Message::new_with_payer(&ixs, Some(&config.signers[0].pubkey()));
let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&config.signers, recent_blockhash)?;
check_account_for_fee(
rpc_client,
&config.signers[0].pubkey(),
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<VoteError>(result, &config)
let result = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers);
log_instruction_custom_error::<VoteError>(result)
}
fn get_vote_account(
@ -657,7 +548,7 @@ fn get_vote_account(
pub fn process_show_vote_account(
rpc_client: &RpcClient,
config: &CliConfig,
_config: &CliConfig,
vote_account_pubkey: &Pubkey,
use_lamports_unit: bool,
commitment_config: CommitmentConfig,
@ -667,38 +558,46 @@ pub fn process_show_vote_account(
let epoch_schedule = rpc_client.get_epoch_schedule()?;
let mut votes: Vec<CliLockout> = vec![];
let mut epoch_voting_history: Vec<CliEpochVotingHistory> = vec![];
if !vote_state.votes.is_empty() {
for vote in &vote_state.votes {
votes.push(vote.into());
println!(
"Account Balance: {}",
build_balance_message(vote_account.lamports, use_lamports_unit, true)
);
println!("Validator Identity: {}", vote_state.node_pubkey);
println!("Authorized Voter: {:?}", vote_state.authorized_voters());
println!(
"Authorized Withdrawer: {}",
vote_state.authorized_withdrawer
);
println!("Credits: {}", vote_state.credits());
println!("Commission: {}%", vote_state.commission);
println!(
"Root Slot: {}",
match vote_state.root_slot {
Some(slot) => slot.to_string(),
None => "~".to_string(),
}
);
println!("Recent Timestamp: {:?}", vote_state.last_timestamp);
if !vote_state.votes.is_empty() {
println!("recent votes:");
for vote in &vote_state.votes {
println!(
"- slot: {}\n confirmation count: {}",
vote.slot, vote.confirmation_count
);
}
println!("Epoch Voting History:");
for (epoch, credits, prev_credits) in vote_state.epoch_credits() {
let credits_earned = credits - prev_credits;
let slots_in_epoch = epoch_schedule.get_slots_in_epoch(*epoch);
epoch_voting_history.push(CliEpochVotingHistory {
epoch: *epoch,
slots_in_epoch,
credits_earned,
});
println!(
"- epoch: {}\n slots in epoch: {}\n credits earned: {}",
epoch, slots_in_epoch, credits_earned,
);
}
}
let vote_account_data = CliVoteAccount {
account_balance: vote_account.lamports,
validator_identity: vote_state.node_pubkey.to_string(),
authorized_voters: vote_state.authorized_voters().into(),
authorized_withdrawer: vote_state.authorized_withdrawer.to_string(),
credits: vote_state.credits(),
commission: vote_state.commission,
root_slot: vote_state.root_slot,
recent_timestamp: vote_state.last_timestamp.clone(),
votes,
epoch_voting_history,
use_lamports_unit,
};
Ok(config.output_format.formatted_string(&vote_account_data))
Ok("".to_string())
}
pub fn process_withdraw_from_vote_account(
@ -728,8 +627,9 @@ pub fn process_withdraw_from_vote_account(
&fee_calculator,
&transaction.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&transaction);
log_instruction_custom_error::<VoteError>(result, &config)
let result =
rpc_client.send_and_confirm_transaction_with_spinner(&mut transaction, &config.signers);
log_instruction_custom_error::<VoteError>(result)
}
#[cfg(test)]
@ -762,11 +662,10 @@ mod tests {
"test",
"vote-authorize-voter",
&pubkey_string,
&default_keypair_file,
&pubkey2_string,
]);
assert_eq!(
parse_command(&test_authorize_voter, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_authorize_voter, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::VoteAuthorize {
vote_account_pubkey: pubkey,
@ -777,32 +676,6 @@ mod tests {
}
);
let authorized_keypair = Keypair::new();
let (authorized_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&authorized_keypair, tmp_file.as_file_mut()).unwrap();
let test_authorize_voter = test_commands.clone().get_matches_from(vec![
"test",
"vote-authorize-voter",
&pubkey_string,
&authorized_keypair_file,
&pubkey2_string,
]);
assert_eq!(
parse_command(&test_authorize_voter, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::VoteAuthorize {
vote_account_pubkey: pubkey,
new_authorized_pubkey: pubkey2,
vote_authorize: VoteAuthorize::Voter
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
read_keypair_file(&authorized_keypair_file).unwrap().into(),
],
}
);
let (keypair_file, mut tmp_file) = make_tmp_file();
let keypair = Keypair::new();
write_keypair(&keypair, tmp_file.as_file_mut()).unwrap();
@ -819,10 +692,9 @@ mod tests {
"10",
]);
assert_eq!(
parse_command(&test_create_vote_account, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_create_vote_account, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::CreateVoteAccount {
vote_account: 1,
seed: None,
identity_account: 2,
authorized_voter: None,
@ -848,10 +720,9 @@ mod tests {
&identity_keypair_file,
]);
assert_eq!(
parse_command(&test_create_vote_account2, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_create_vote_account2, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::CreateVoteAccount {
vote_account: 1,
seed: None,
identity_account: 2,
authorized_voter: None,
@ -881,10 +752,9 @@ mod tests {
&authed.to_string(),
]);
assert_eq!(
parse_command(&test_create_vote_account3, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_create_vote_account3, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::CreateVoteAccount {
vote_account: 1,
seed: None,
identity_account: 2,
authorized_voter: Some(authed),
@ -912,10 +782,9 @@ mod tests {
&authed.to_string(),
]);
assert_eq!(
parse_command(&test_create_vote_account4, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_create_vote_account4, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::CreateVoteAccount {
vote_account: 1,
seed: None,
identity_account: 2,
authorized_voter: None,
@ -938,12 +807,11 @@ mod tests {
&keypair_file,
]);
assert_eq!(
parse_command(&test_update_validator, &default_keypair_file, &mut None).unwrap(),
parse_command(&test_update_validator, &default_keypair_file, None).unwrap(),
CliCommandInfo {
command: CliCommand::VoteUpdateValidator {
vote_account_pubkey: pubkey,
new_identity_account: 2,
withdraw_authority: 1,
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
@ -953,28 +821,6 @@ mod tests {
}
);
let test_update_commission = test_commands.clone().get_matches_from(vec![
"test",
"vote-update-commission",
&pubkey_string,
"42",
&keypair_file,
]);
assert_eq!(
parse_command(&test_update_commission, &default_keypair_file, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::VoteUpdateCommission {
vote_account_pubkey: pubkey,
commission: 42,
withdraw_authority: 1,
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
Box::new(read_keypair_file(&keypair_file).unwrap()),
],
}
);
// Test WithdrawFromVoteAccount subcommand
let test_withdraw_from_vote_account = test_commands.clone().get_matches_from(vec![
"test",
@ -987,7 +833,7 @@ mod tests {
parse_command(
&test_withdraw_from_vote_account,
&default_keypair_file,
&mut None
None
)
.unwrap(),
CliCommandInfo {
@ -1018,7 +864,7 @@ mod tests {
parse_command(
&test_withdraw_from_vote_account,
&default_keypair_file,
&mut None
None
)
.unwrap(),
CliCommandInfo {

View File

@ -1,6 +1,5 @@
use solana_cli::{
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
cli_output::OutputFormat,
nonce,
offline::{
blockhash_query::{self, BlockhashQuery},
@ -14,6 +13,7 @@ use solana_sdk::{
hash::Hash,
pubkey::Pubkey,
signature::{keypair_from_seed, Keypair, Signer},
system_instruction::create_address_with_seed,
system_program,
};
use std::{fs::remove_dir_all, sync::mpsc::channel, thread::sleep, time::Duration};
@ -120,7 +120,6 @@ fn full_battery_tests(
&faucet_addr,
&config_payer.signers[0].pubkey(),
2000,
&config_payer,
)
.unwrap();
check_balance(2000, &rpc_client, &config_payer.signers[0].pubkey());
@ -131,7 +130,7 @@ fn full_battery_tests(
config_nonce.signers = vec![&nonce_keypair];
let nonce_account = if let Some(seed) = seed.as_ref() {
Pubkey::create_with_seed(
create_address_with_seed(
&config_nonce.signers[0].pubkey(),
seed,
&system_program::id(),
@ -267,7 +266,6 @@ fn test_create_account_with_seed() {
} = TestValidator::run_with_options(TestValidatorOptions {
fees: 1,
bootstrap_validator_lamports: 42_000,
..TestValidatorOptions::default()
});
let (sender, receiver) = channel();
@ -277,7 +275,6 @@ fn test_create_account_with_seed() {
let offline_nonce_authority_signer = keypair_from_seed(&[1u8; 32]).unwrap();
let online_nonce_creator_signer = keypair_from_seed(&[2u8; 32]).unwrap();
let to_address = Pubkey::new(&[3u8; 32]);
let config = CliConfig::default();
// Setup accounts
let rpc_client = RpcClient::new_socket(leader_data.rpc);
@ -286,7 +283,6 @@ fn test_create_account_with_seed() {
&faucet_addr,
&offline_nonce_authority_signer.pubkey(),
42,
&config,
)
.unwrap();
request_and_confirm_airdrop(
@ -294,7 +290,6 @@ fn test_create_account_with_seed() {
&faucet_addr,
&online_nonce_creator_signer.pubkey(),
4242,
&config,
)
.unwrap();
check_balance(42, &rpc_client, &offline_nonce_authority_signer.pubkey());
@ -306,7 +301,7 @@ fn test_create_account_with_seed() {
let authority_pubkey = offline_nonce_authority_signer.pubkey();
let seed = authority_pubkey.to_string()[0..32].to_string();
let nonce_address =
Pubkey::create_with_seed(&creator_pubkey, &seed, &system_program::id()).unwrap();
create_address_with_seed(&creator_pubkey, &seed, &system_program::id()).unwrap();
check_balance(0, &rpc_client, &nonce_address);
let mut creator_config = CliConfig::default();
@ -343,13 +338,11 @@ fn test_create_account_with_seed() {
to: to_address,
from: 0,
sign_only: true,
no_wait: false,
blockhash_query: BlockhashQuery::None(nonce_hash),
nonce_account: Some(nonce_address),
nonce_authority: 0,
fee_payer: 0,
};
authority_config.output_format = OutputFormat::JsonCompact;
let sign_only_reply = process_command(&authority_config).unwrap();
let sign_only = parse_sign_only_reply_string(&sign_only_reply);
let authority_presigner = sign_only.presigner_of(&authority_pubkey).unwrap();
@ -365,7 +358,6 @@ fn test_create_account_with_seed() {
to: to_address,
from: 0,
sign_only: false,
no_wait: false,
blockhash_query: BlockhashQuery::FeeCalculator(
blockhash_query::Source::NonceAccount(nonce_address),
sign_only.blockhash,

View File

@ -2,7 +2,6 @@ use chrono::prelude::*;
use serde_json::Value;
use solana_cli::{
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig, PayCommand},
cli_output::OutputFormat,
nonce,
offline::{
blockhash_query::{self, BlockhashQuery},
@ -70,7 +69,6 @@ fn test_cli_timestamp_tx() {
&faucet_addr,
&config_payer.signers[0].pubkey(),
50,
&config_witness,
)
.unwrap();
check_balance(50, &rpc_client, &config_payer.signers[0].pubkey());
@ -80,7 +78,6 @@ fn test_cli_timestamp_tx() {
&faucet_addr,
&config_witness.signers[0].pubkey(),
1,
&config_witness,
)
.unwrap();
@ -157,7 +154,6 @@ fn test_cli_witness_tx() {
&faucet_addr,
&config_payer.signers[0].pubkey(),
50,
&config_witness,
)
.unwrap();
request_and_confirm_airdrop(
@ -165,7 +161,6 @@ fn test_cli_witness_tx() {
&faucet_addr,
&config_witness.signers[0].pubkey(),
1,
&config_witness,
)
.unwrap();
@ -239,7 +234,6 @@ fn test_cli_cancel_tx() {
&faucet_addr,
&config_payer.signers[0].pubkey(),
50,
&config_witness,
)
.unwrap();
@ -313,7 +307,6 @@ fn test_offline_pay_tx() {
&faucet_addr,
&config_offline.signers[0].pubkey(),
50,
&config_offline,
)
.unwrap();
@ -322,7 +315,6 @@ fn test_offline_pay_tx() {
&faucet_addr,
&config_online.signers[0].pubkey(),
50,
&config_offline,
)
.unwrap();
check_balance(50, &rpc_client, &config_offline.signers[0].pubkey());
@ -336,7 +328,6 @@ fn test_offline_pay_tx() {
sign_only: true,
..PayCommand::default()
});
config_offline.output_format = OutputFormat::JsonCompact;
let sig_response = process_command(&config_offline).unwrap();
check_balance(50, &rpc_client, &config_offline.signers[0].pubkey());
@ -397,7 +388,6 @@ fn test_nonced_pay_tx() {
&faucet_addr,
&config.signers[0].pubkey(),
50 + minimum_nonce_balance,
&config,
)
.unwrap();
check_balance(

View File

@ -1,6 +1,5 @@
use solana_cli::{
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
cli_output::OutputFormat,
nonce,
offline::{
blockhash_query::{self, BlockhashQuery},
@ -15,6 +14,7 @@ use solana_sdk::{
nonce::State as NonceState,
pubkey::Pubkey,
signature::{keypair_from_seed, Keypair, Signer},
system_instruction::create_address_with_seed,
};
use solana_stake_program::{
stake_instruction::LockupArgs,
@ -60,7 +60,6 @@ fn test_stake_delegation_force() {
&faucet_addr,
&config.signers[0].pubkey(),
100_000,
&config,
)
.unwrap();
@ -68,7 +67,6 @@ fn test_stake_delegation_force() {
let vote_keypair = Keypair::new();
config.signers = vec![&default_signer, &vote_keypair];
config.command = CliCommand::CreateVoteAccount {
vote_account: 1,
seed: None,
identity_account: 0,
authorized_voter: None,
@ -158,12 +156,11 @@ fn test_seed_stake_delegation_and_deactivation() {
&faucet_addr,
&config_validator.signers[0].pubkey(),
100_000,
&config_validator,
)
.unwrap();
check_balance(100_000, &rpc_client, &config_validator.signers[0].pubkey());
let stake_address = Pubkey::create_with_seed(
let stake_address = create_address_with_seed(
&config_validator.signers[0].pubkey(),
"hi there",
&solana_stake_program::id(),
@ -249,7 +246,6 @@ fn test_stake_delegation_and_deactivation() {
&faucet_addr,
&config_validator.signers[0].pubkey(),
100_000,
&config_validator,
)
.unwrap();
check_balance(100_000, &rpc_client, &config_validator.signers[0].pubkey());
@ -346,7 +342,6 @@ fn test_offline_stake_delegation_and_deactivation() {
&faucet_addr,
&config_validator.signers[0].pubkey(),
100_000,
&config_offline,
)
.unwrap();
check_balance(100_000, &rpc_client, &config_validator.signers[0].pubkey());
@ -356,7 +351,6 @@ fn test_offline_stake_delegation_and_deactivation() {
&faucet_addr,
&config_offline.signers[0].pubkey(),
100_000,
&config_validator,
)
.unwrap();
check_balance(100_000, &rpc_client, &config_offline.signers[0].pubkey());
@ -392,7 +386,6 @@ fn test_offline_stake_delegation_and_deactivation() {
nonce_authority: 0,
fee_payer: 0,
};
config_offline.output_format = OutputFormat::JsonCompact;
let sig_response = process_command(&config_offline).unwrap();
let sign_only = parse_sign_only_reply_string(&sig_response);
assert!(sign_only.has_all_signers());
@ -478,7 +471,6 @@ fn test_nonced_stake_delegation_and_deactivation() {
&faucet_addr,
&config.signers[0].pubkey(),
100_000,
&config,
)
.unwrap();
@ -588,7 +580,6 @@ fn test_stake_authorize() {
&faucet_addr,
&config.signers[0].pubkey(),
100_000,
&config,
)
.unwrap();
@ -606,7 +597,6 @@ fn test_stake_authorize() {
&faucet_addr,
&config_offline.signers[0].pubkey(),
100_000,
&config,
)
.unwrap();
@ -714,7 +704,6 @@ fn test_stake_authorize() {
nonce_authority: 0,
fee_payer: 0,
};
config_offline.output_format = OutputFormat::JsonCompact;
let sign_reply = process_command(&config_offline).unwrap();
let sign_only = parse_sign_only_reply_string(&sign_reply);
assert!(sign_only.has_all_signers());
@ -823,7 +812,6 @@ fn test_stake_authorize_with_fee_payer() {
} = TestValidator::run_with_options(TestValidatorOptions {
fees: SIG_FEE,
bootstrap_validator_lamports: 42_000,
..TestValidatorOptions::default()
});
let (sender, receiver) = channel();
run_local_faucet(alice, sender, None);
@ -853,16 +841,13 @@ fn test_stake_authorize_with_fee_payer() {
config_offline.command = CliCommand::ClusterVersion;
process_command(&config_offline).unwrap_err();
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &default_pubkey, 100_000, &config)
.unwrap();
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &default_pubkey, 100_000).unwrap();
check_balance(100_000, &rpc_client, &config.signers[0].pubkey());
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &payer_pubkey, 100_000, &config)
.unwrap();
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &payer_pubkey, 100_000).unwrap();
check_balance(100_000, &rpc_client, &payer_pubkey);
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 100_000, &config)
.unwrap();
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 100_000).unwrap();
check_balance(100_000, &rpc_client, &offline_pubkey);
// Create stake account, identity is authority
@ -916,7 +901,6 @@ fn test_stake_authorize_with_fee_payer() {
nonce_authority: 0,
fee_payer: 0,
};
config_offline.output_format = OutputFormat::JsonCompact;
let sign_reply = process_command(&config_offline).unwrap();
let sign_only = parse_sign_only_reply_string(&sign_reply);
assert!(sign_only.has_all_signers());
@ -955,7 +939,6 @@ fn test_stake_split() {
} = TestValidator::run_with_options(TestValidatorOptions {
fees: 1,
bootstrap_validator_lamports: 42_000,
..TestValidatorOptions::default()
});
let (sender, receiver) = channel();
run_local_faucet(alice, sender, None);
@ -982,13 +965,11 @@ fn test_stake_split() {
&faucet_addr,
&config.signers[0].pubkey(),
500_000,
&config,
)
.unwrap();
check_balance(500_000, &rpc_client, &config.signers[0].pubkey());
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 100_000, &config)
.unwrap();
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 100_000).unwrap();
check_balance(100_000, &rpc_client, &offline_pubkey);
// Create stake account, identity is authority
@ -1056,7 +1037,6 @@ fn test_stake_split() {
lamports: 2 * minimum_stake_balance,
fee_payer: 0,
};
config_offline.output_format = OutputFormat::JsonCompact;
let sig_response = process_command(&config_offline).unwrap();
let sign_only = parse_sign_only_reply_string(&sig_response);
assert!(sign_only.has_all_signers());
@ -1106,7 +1086,6 @@ fn test_stake_set_lockup() {
} = TestValidator::run_with_options(TestValidatorOptions {
fees: 1,
bootstrap_validator_lamports: 42_000,
..TestValidatorOptions::default()
});
let (sender, receiver) = channel();
run_local_faucet(alice, sender, None);
@ -1133,13 +1112,11 @@ fn test_stake_set_lockup() {
&faucet_addr,
&config.signers[0].pubkey(),
500_000,
&config,
)
.unwrap();
check_balance(500_000, &rpc_client, &config.signers[0].pubkey());
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 100_000, &config)
.unwrap();
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 100_000).unwrap();
check_balance(100_000, &rpc_client, &offline_pubkey);
// Create stake account, identity is authority
@ -1313,7 +1290,6 @@ fn test_stake_set_lockup() {
nonce_authority: 0,
fee_payer: 0,
};
config_offline.output_format = OutputFormat::JsonCompact;
let sig_response = process_command(&config_offline).unwrap();
let sign_only = parse_sign_only_reply_string(&sig_response);
assert!(sign_only.has_all_signers());
@ -1386,13 +1362,11 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
&faucet_addr,
&config.signers[0].pubkey(),
200_000,
&config,
)
.unwrap();
check_balance(200_000, &rpc_client, &config.signers[0].pubkey());
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 100_000, &config)
.unwrap();
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 100_000).unwrap();
check_balance(100_000, &rpc_client, &offline_pubkey);
// Create nonce account
@ -1434,7 +1408,6 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
fee_payer: 0,
from: 0,
};
config_offline.output_format = OutputFormat::JsonCompact;
let sig_response = process_command(&config_offline).unwrap();
let sign_only = parse_sign_only_reply_string(&sig_response);
assert!(sign_only.has_all_signers());
@ -1476,7 +1449,6 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
destination_account_pubkey: recipient_pubkey,
lamports: 42,
withdraw_authority: 0,
custodian: None,
sign_only: true,
blockhash_query: BlockhashQuery::None(nonce_hash),
nonce_account: Some(nonce_pubkey),
@ -1492,7 +1464,6 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
destination_account_pubkey: recipient_pubkey,
lamports: 42,
withdraw_authority: 0,
custodian: None,
sign_only: false,
blockhash_query: BlockhashQuery::FeeCalculator(
blockhash_query::Source::NonceAccount(nonce_pubkey),
@ -1552,7 +1523,7 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
};
process_command(&config).unwrap();
let seed_address =
Pubkey::create_with_seed(&stake_pubkey, seed, &solana_stake_program::id()).unwrap();
create_address_with_seed(&stake_pubkey, seed, &solana_stake_program::id()).unwrap();
check_balance(50_000, &rpc_client, &seed_address);
server.close().unwrap();

View File

@ -1,6 +1,5 @@
use solana_cli::{
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
cli_output::OutputFormat,
nonce,
offline::{
blockhash_query::{self, BlockhashQuery},
@ -41,7 +40,6 @@ fn test_transfer() {
} = TestValidator::run_with_options(TestValidatorOptions {
fees: 1,
bootstrap_validator_lamports: 42_000,
..TestValidatorOptions::default()
});
let (sender, receiver) = channel();
@ -60,8 +58,7 @@ fn test_transfer() {
let sender_pubkey = config.signers[0].pubkey();
let recipient_pubkey = Pubkey::new(&[1u8; 32]);
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &sender_pubkey, 50_000, &config)
.unwrap();
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &sender_pubkey, 50_000).unwrap();
check_balance(50_000, &rpc_client, &sender_pubkey);
check_balance(0, &rpc_client, &recipient_pubkey);
@ -71,7 +68,6 @@ fn test_transfer() {
to: recipient_pubkey,
from: 0,
sign_only: false,
no_wait: false,
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
nonce_account: None,
nonce_authority: 0,
@ -89,7 +85,7 @@ fn test_transfer() {
process_command(&offline).unwrap_err();
let offline_pubkey = offline.signers[0].pubkey();
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 50, &config).unwrap();
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 50).unwrap();
check_balance(50, &rpc_client, &offline_pubkey);
// Offline transfer
@ -99,13 +95,11 @@ fn test_transfer() {
to: recipient_pubkey,
from: 0,
sign_only: true,
no_wait: false,
blockhash_query: BlockhashQuery::None(blockhash),
nonce_account: None,
nonce_authority: 0,
fee_payer: 0,
};
offline.output_format = OutputFormat::JsonCompact;
let sign_only_reply = process_command(&offline).unwrap();
let sign_only = parse_sign_only_reply_string(&sign_only_reply);
assert!(sign_only.has_all_signers());
@ -116,7 +110,6 @@ fn test_transfer() {
to: recipient_pubkey,
from: 0,
sign_only: false,
no_wait: false,
blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
nonce_account: None,
nonce_authority: 0,
@ -154,7 +147,6 @@ fn test_transfer() {
to: recipient_pubkey,
from: 0,
sign_only: false,
no_wait: false,
blockhash_query: BlockhashQuery::FeeCalculator(
blockhash_query::Source::NonceAccount(nonce_account.pubkey()),
nonce_hash,
@ -195,7 +187,6 @@ fn test_transfer() {
to: recipient_pubkey,
from: 0,
sign_only: true,
no_wait: false,
blockhash_query: BlockhashQuery::None(nonce_hash),
nonce_account: Some(nonce_account.pubkey()),
nonce_authority: 0,
@ -211,7 +202,6 @@ fn test_transfer() {
to: recipient_pubkey,
from: 0,
sign_only: false,
no_wait: false,
blockhash_query: BlockhashQuery::FeeCalculator(
blockhash_query::Source::NonceAccount(nonce_account.pubkey()),
sign_only.blockhash,
@ -239,7 +229,6 @@ fn test_transfer_multisession_signing() {
} = TestValidator::run_with_options(TestValidatorOptions {
fees: 1,
bootstrap_validator_lamports: 42_000,
..TestValidatorOptions::default()
});
let (sender, receiver) = channel();
@ -250,24 +239,16 @@ fn test_transfer_multisession_signing() {
let offline_from_signer = keypair_from_seed(&[2u8; 32]).unwrap();
let offline_fee_payer_signer = keypair_from_seed(&[3u8; 32]).unwrap();
let from_null_signer = NullSigner::new(&offline_from_signer.pubkey());
let config = CliConfig::default();
// Setup accounts
let rpc_client = RpcClient::new_socket(leader_data.rpc);
request_and_confirm_airdrop(
&rpc_client,
&faucet_addr,
&offline_from_signer.pubkey(),
43,
&config,
)
.unwrap();
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_from_signer.pubkey(), 43)
.unwrap();
request_and_confirm_airdrop(
&rpc_client,
&faucet_addr,
&offline_fee_payer_signer.pubkey(),
3,
&config,
)
.unwrap();
check_balance(43, &rpc_client, &offline_from_signer.pubkey());
@ -288,13 +269,11 @@ fn test_transfer_multisession_signing() {
to: to_pubkey,
from: 1,
sign_only: true,
no_wait: false,
blockhash_query: BlockhashQuery::None(blockhash),
nonce_account: None,
nonce_authority: 0,
fee_payer: 0,
};
fee_payer_config.output_format = OutputFormat::JsonCompact;
let sign_only_reply = process_command(&fee_payer_config).unwrap();
let sign_only = parse_sign_only_reply_string(&sign_only_reply);
assert!(!sign_only.has_all_signers());
@ -314,13 +293,11 @@ fn test_transfer_multisession_signing() {
to: to_pubkey,
from: 1,
sign_only: true,
no_wait: false,
blockhash_query: BlockhashQuery::None(blockhash),
nonce_account: None,
nonce_authority: 0,
fee_payer: 0,
};
from_config.output_format = OutputFormat::JsonCompact;
let sign_only_reply = process_command(&from_config).unwrap();
let sign_only = parse_sign_only_reply_string(&sign_only_reply);
assert!(sign_only.has_all_signers());
@ -337,7 +314,6 @@ fn test_transfer_multisession_signing() {
to: to_pubkey,
from: 1,
sign_only: false,
no_wait: false,
blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
nonce_account: None,
nonce_authority: 0,

View File

@ -48,7 +48,6 @@ fn test_vote_authorize_and_withdraw() {
&faucet_addr,
&config.signers[0].pubkey(),
100_000,
&config,
)
.unwrap();
@ -57,7 +56,6 @@ fn test_vote_authorize_and_withdraw() {
let vote_account_pubkey = vote_account_keypair.pubkey();
config.signers = vec![&default_signer, &vote_account_keypair];
config.command = CliCommand::CreateVoteAccount {
vote_account: 1,
seed: None,
identity_account: 0,
authorized_voter: None,
@ -112,7 +110,6 @@ fn test_vote_authorize_and_withdraw() {
config.command = CliCommand::VoteUpdateValidator {
vote_account_pubkey,
new_identity_account: 2,
withdraw_authority: 1,
};
process_command(&config).unwrap();

View File

@ -1,6 +1,6 @@
[package]
name = "solana-client"
version = "1.1.23"
version = "1.0.14"
description = "Solana Client"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
@ -14,15 +14,15 @@ bs58 = "0.3.0"
indicatif = "0.14.0"
jsonrpc-core = "14.0.5"
log = "0.4.8"
rayon = "1.3.0"
reqwest = { version = "0.10.4", default-features = false, features = ["blocking", "rustls-tls", "json"] }
serde = "1.0.105"
rayon = "1.2.0"
reqwest = { version = "0.10.1", default-features = false, features = ["blocking", "rustls-tls"] }
serde = "1.0.104"
serde_derive = "1.0.103"
serde_json = "1.0.48"
solana-transaction-status = { path = "../transaction-status", version = "1.1.23" }
solana-net-utils = { path = "../net-utils", version = "1.1.23" }
solana-sdk = { path = "../sdk", version = "1.1.23" }
solana-vote-program = { path = "../programs/vote", version = "1.1.23" }
serde_json = "1.0.46"
solana-net-utils = { path = "../net-utils", version = "1.0.14" }
solana-sdk = { path = "../sdk", version = "1.0.14" }
solana-transaction-status = { path = "../transaction-status", version = "1.0.14" }
solana-vote-program = { path = "../programs/vote", version = "1.0.14" }
thiserror = "1.0"
tungstenite = "0.10.1"
url = "2.1.1"
@ -31,7 +31,4 @@ url = "2.1.1"
assert_matches = "1.3.0"
jsonrpc-core = "14.0.5"
jsonrpc-http-server = "14.0.6"
solana-logger = { path = "../logger", version = "1.1.23" }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
solana-logger = { path = "../logger", version = "1.0.14" }

View File

@ -50,29 +50,29 @@ impl Into<TransportError> for ClientErrorKind {
#[derive(Error, Debug)]
#[error("{kind}")]
pub struct ClientError {
request: Option<rpc_request::RpcRequest>,
command: Option<&'static str>,
#[source]
#[error(transparent)]
kind: ClientErrorKind,
}
impl ClientError {
pub fn new_with_request(kind: ClientErrorKind, request: rpc_request::RpcRequest) -> Self {
pub fn new_with_command(kind: ClientErrorKind, command: &'static str) -> Self {
Self {
request: Some(request),
command: Some(command),
kind,
}
}
pub fn into_with_request(self, request: rpc_request::RpcRequest) -> Self {
pub fn into_with_command(self, command: &'static str) -> Self {
Self {
request: Some(request),
command: Some(command),
..self
}
}
pub fn request(&self) -> Option<&rpc_request::RpcRequest> {
self.request.as_ref()
pub fn command(&self) -> Option<&'static str> {
self.command
}
pub fn kind(&self) -> &ClientErrorKind {
@ -83,7 +83,7 @@ impl ClientError {
impl From<ClientErrorKind> for ClientError {
fn from(kind: ClientErrorKind) -> Self {
Self {
request: None,
command: None,
kind,
}
}
@ -92,7 +92,7 @@ impl From<ClientErrorKind> for ClientError {
impl From<TransportError> for ClientError {
fn from(err: TransportError) -> Self {
Self {
request: None,
command: None,
kind: err.into(),
}
}
@ -107,7 +107,7 @@ impl Into<TransportError> for ClientError {
impl From<std::io::Error> for ClientError {
fn from(err: std::io::Error) -> Self {
Self {
request: None,
command: None,
kind: err.into(),
}
}
@ -116,7 +116,7 @@ impl From<std::io::Error> for ClientError {
impl From<reqwest::Error> for ClientError {
fn from(err: reqwest::Error) -> Self {
Self {
request: None,
command: None,
kind: err.into(),
}
}
@ -125,7 +125,7 @@ impl From<reqwest::Error> for ClientError {
impl From<rpc_request::RpcError> for ClientError {
fn from(err: rpc_request::RpcError) -> Self {
Self {
request: None,
command: None,
kind: err.into(),
}
}
@ -134,7 +134,7 @@ impl From<rpc_request::RpcError> for ClientError {
impl From<serde_json::error::Error> for ClientError {
fn from(err: serde_json::error::Error) -> Self {
Self {
request: None,
command: None,
kind: err.into(),
}
}
@ -143,7 +143,7 @@ impl From<serde_json::error::Error> for ClientError {
impl From<SignerError> for ClientError {
fn from(err: SignerError) -> Self {
Self {
request: None,
command: None,
kind: err.into(),
}
}
@ -152,7 +152,7 @@ impl From<SignerError> for ClientError {
impl From<TransactionError> for ClientError {
fn from(err: TransactionError) -> Self {
Self {
request: None,
command: None,
kind: err.into(),
}
}

View File

@ -1,5 +1,10 @@
use crate::{client_error::Result, rpc_request::RpcRequest};
pub(crate) trait GenericRpcClientRequest {
fn send(&self, request: RpcRequest, params: serde_json::Value) -> Result<serde_json::Value>;
fn send(
&self,
request: &RpcRequest,
params: serde_json::Value,
retries: usize,
) -> Result<serde_json::Value>;
}

View File

@ -8,7 +8,6 @@ pub mod perf_utils;
pub mod pubsub_client;
pub mod rpc_client;
pub mod rpc_client_request;
pub mod rpc_config;
pub mod rpc_request;
pub mod rpc_response;
pub mod thin_client;

View File

@ -8,8 +8,7 @@ use serde_json::{Number, Value};
use solana_sdk::{
fee_calculator::{FeeCalculator, FeeRateGovernor},
instruction::InstructionError,
signature::Signature,
transaction::{self, Transaction, TransactionError},
transaction::{self, TransactionError},
};
use solana_transaction_status::TransactionStatus;
use std::{collections::HashMap, sync::RwLock};
@ -38,8 +37,13 @@ impl MockRpcClientRequest {
}
impl GenericRpcClientRequest for MockRpcClientRequest {
fn send(&self, request: RpcRequest, params: serde_json::Value) -> Result<serde_json::Value> {
if let Some(value) = self.mocks.write().unwrap().remove(&request) {
fn send(
&self,
request: &RpcRequest,
_params: serde_json::Value,
_retries: usize,
) -> Result<serde_json::Value> {
if let Some(value) = self.mocks.write().unwrap().remove(request) {
return Ok(value);
}
if self.url == "fails" {
@ -101,17 +105,7 @@ impl GenericRpcClientRequest for MockRpcClientRequest {
}
RpcRequest::GetTransactionCount => Value::Number(Number::from(1234)),
RpcRequest::GetSlot => Value::Number(Number::from(0)),
RpcRequest::SendTransaction => {
let signature = if self.url == "malicious" {
Signature::new(&[8; 64]).to_string()
} else {
let tx_str = params.as_array().unwrap()[0].as_str().unwrap().to_string();
let data = bs58::decode(tx_str).into_vec().unwrap();
let tx: Transaction = bincode::deserialize(&data).unwrap();
tx.signatures[0].to_string()
};
Value::String(signature)
}
RpcRequest::SendTransaction => Value::String(SIGNATURE.to_string()),
RpcRequest::GetMinimumBalanceForRentExemption => Value::Number(Number::from(1234)),
_ => Value::Null,
};

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,8 @@ use crate::{
rpc_request::{RpcError, RpcRequest},
};
use log::*;
use reqwest::{self, header::CONTENT_TYPE, StatusCode};
use reqwest::{self, header::CONTENT_TYPE};
use solana_sdk::clock::{DEFAULT_TICKS_PER_SECOND, DEFAULT_TICKS_PER_SLOT};
use std::{thread::sleep, time::Duration};
pub struct RpcClientRequest {
@ -28,13 +29,17 @@ impl RpcClientRequest {
}
impl GenericRpcClientRequest for RpcClientRequest {
fn send(&self, request: RpcRequest, params: serde_json::Value) -> Result<serde_json::Value> {
fn send(
&self,
request: &RpcRequest,
params: serde_json::Value,
mut retries: usize,
) -> Result<serde_json::Value> {
// Concurrent requests are not supported so reuse the same request id for all requests
let request_id = 1;
let request_json = request.build_request_json(request_id, params);
let mut too_many_requests_retries = 5;
loop {
match self
.client
@ -45,19 +50,6 @@ impl GenericRpcClientRequest for RpcClientRequest {
{
Ok(response) => {
if !response.status().is_success() {
if response.status() == StatusCode::TOO_MANY_REQUESTS
&& too_many_requests_retries > 0
{
too_many_requests_retries -= 1;
debug!(
"Server responded with {:?}, {} retries left",
response, too_many_requests_retries
);
// Sleep for 500ms to give the server a break
sleep(Duration::from_millis(500));
continue;
}
return Err(response.error_for_status().unwrap_err().into());
}
@ -71,8 +63,17 @@ impl GenericRpcClientRequest for RpcClientRequest {
}
return Ok(json["result"].clone());
}
Err(err) => {
return Err(err.into());
Err(e) => {
info!("{:?} failed, {} retries left: {:?}", request, retries, e);
if retries == 0 {
return Err(e.into());
}
retries -= 1;
// Sleep for approximately half a slot
sleep(Duration::from_millis(
500 * DEFAULT_TICKS_PER_SLOT / DEFAULT_TICKS_PER_SECOND,
));
}
}
}

View File

@ -1,45 +0,0 @@
use solana_sdk::{clock::Epoch, commitment_config::CommitmentConfig};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcSignatureStatusConfig {
pub search_transaction_history: Option<bool>,
// DEPRECATED
#[serde(flatten)]
pub commitment: Option<CommitmentConfig>,
}
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcSendTransactionConfig {
pub skip_preflight: bool,
}
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcSimulateTransactionConfig {
pub sig_verify: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum RpcLargestAccountsFilter {
Circulating,
NonCirculating,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcLargestAccountsConfig {
#[serde(flatten)]
pub commitment: Option<CommitmentConfig>,
pub filter: Option<RpcLargestAccountsFilter>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcStakeConfig {
pub epoch: Option<Epoch>,
#[serde(flatten)]
pub commitment: Option<CommitmentConfig>,
}

View File

@ -1,8 +1,7 @@
use serde_json::{json, Value};
use std::fmt;
use thiserror::Error;
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
#[derive(Debug, PartialEq, Eq, Hash)]
pub enum RpcRequest {
DeregisterNode,
ValidatorExit,
@ -12,22 +11,16 @@ pub enum RpcRequest {
GetClusterNodes,
GetConfirmedBlock,
GetConfirmedBlocks,
GetConfirmedSignaturesForAddress,
GetConfirmedTransaction,
GetEpochInfo,
GetEpochSchedule,
GetFeeCalculatorForBlockhash,
GetFeeRateGovernor,
GetFees,
GetGenesisHash,
GetIdentity,
GetInflationGovernor,
GetInflationRate,
GetLargestAccounts,
GetInflation,
GetLeaderSchedule,
GetMinimumBalanceForRentExemption,
GetProgramAccounts,
GetRecentBlockhash,
GetFeeCalculatorForBlockhash,
GetFeeRateGovernor,
GetSignatureStatuses,
GetSlot,
GetSlotLeader,
@ -35,21 +28,20 @@ pub enum RpcRequest {
GetStorageTurnRate,
GetSlotsPerSegment,
GetStoragePubkeysForSlot,
GetSupply,
GetTotalSupply,
GetTransactionCount,
GetVersion,
GetVoteAccounts,
MinimumLedgerSlot,
RegisterNode,
RequestAirdrop,
SendTransaction,
SimulateTransaction,
SignVote,
GetMinimumBalanceForRentExemption,
MinimumLedgerSlot,
}
impl fmt::Display for RpcRequest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
impl RpcRequest {
pub(crate) fn build_request_json(&self, id: u64, params: Value) -> Value {
let jsonrpc = "2.0";
let method = match self {
RpcRequest::DeregisterNode => "deregisterNode",
RpcRequest::ValidatorExit => "validatorExit",
@ -59,22 +51,16 @@ impl fmt::Display for RpcRequest {
RpcRequest::GetClusterNodes => "getClusterNodes",
RpcRequest::GetConfirmedBlock => "getConfirmedBlock",
RpcRequest::GetConfirmedBlocks => "getConfirmedBlocks",
RpcRequest::GetConfirmedSignaturesForAddress => "getConfirmedSignaturesForAddress",
RpcRequest::GetConfirmedTransaction => "getConfirmedTransaction",
RpcRequest::GetEpochInfo => "getEpochInfo",
RpcRequest::GetEpochSchedule => "getEpochSchedule",
RpcRequest::GetFeeCalculatorForBlockhash => "getFeeCalculatorForBlockhash",
RpcRequest::GetFeeRateGovernor => "getFeeRateGovernor",
RpcRequest::GetFees => "getFees",
RpcRequest::GetGenesisHash => "getGenesisHash",
RpcRequest::GetIdentity => "getIdentity",
RpcRequest::GetInflationGovernor => "getInflationGovernor",
RpcRequest::GetInflationRate => "getInflationRate",
RpcRequest::GetLargestAccounts => "getLargestAccounts",
RpcRequest::GetInflation => "getInflation",
RpcRequest::GetLeaderSchedule => "getLeaderSchedule",
RpcRequest::GetMinimumBalanceForRentExemption => "getMinimumBalanceForRentExemption",
RpcRequest::GetProgramAccounts => "getProgramAccounts",
RpcRequest::GetRecentBlockhash => "getRecentBlockhash",
RpcRequest::GetFeeCalculatorForBlockhash => "getFeeCalculatorForBlockhash",
RpcRequest::GetFeeRateGovernor => "getFeeRateGovernor",
RpcRequest::GetSignatureStatuses => "getSignatureStatuses",
RpcRequest::GetSlot => "getSlot",
RpcRequest::GetSlotLeader => "getSlotLeader",
@ -82,35 +68,20 @@ impl fmt::Display for RpcRequest {
RpcRequest::GetStorageTurnRate => "getStorageTurnRate",
RpcRequest::GetSlotsPerSegment => "getSlotsPerSegment",
RpcRequest::GetStoragePubkeysForSlot => "getStoragePubkeysForSlot",
RpcRequest::GetSupply => "getSupply",
RpcRequest::GetTotalSupply => "getTotalSupply",
RpcRequest::GetTransactionCount => "getTransactionCount",
RpcRequest::GetVersion => "getVersion",
RpcRequest::GetVoteAccounts => "getVoteAccounts",
RpcRequest::MinimumLedgerSlot => "minimumLedgerSlot",
RpcRequest::RegisterNode => "registerNode",
RpcRequest::RequestAirdrop => "requestAirdrop",
RpcRequest::SendTransaction => "sendTransaction",
RpcRequest::SimulateTransaction => "simulateTransaction",
RpcRequest::SignVote => "signVote",
RpcRequest::GetMinimumBalanceForRentExemption => "getMinimumBalanceForRentExemption",
RpcRequest::MinimumLedgerSlot => "minimumLedgerSlot",
};
write!(f, "{}", method)
}
}
pub const NUM_LARGEST_ACCOUNTS: usize = 20;
pub const MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS: usize = 256;
pub const MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS_SLOT_RANGE: u64 = 10_000;
pub const MAX_GET_CONFIRMED_BLOCKS_RANGE: u64 = 500_000;
impl RpcRequest {
pub(crate) fn build_request_json(self, id: u64, params: Value) -> Value {
let jsonrpc = "2.0";
json!({
"jsonrpc": jsonrpc,
"id": id,
"method": format!("{}", self),
"method": method,
"params": params,
})
}
@ -149,6 +120,10 @@ mod tests {
let request = test_request.build_request_json(1, Value::Null);
assert_eq!(request["method"], "getEpochInfo");
let test_request = RpcRequest::GetInflation;
let request = test_request.build_request_json(1, Value::Null);
assert_eq!(request["method"], "getInflation");
let test_request = RpcRequest::GetRecentBlockhash;
let request = test_request.build_request_json(1, Value::Null);
assert_eq!(request["method"], "getRecentBlockhash");

View File

@ -3,7 +3,6 @@ use solana_sdk::{
account::Account,
clock::{Epoch, Slot},
fee_calculator::{FeeCalculator, FeeRateGovernor},
inflation::Inflation,
pubkey::Pubkey,
transaction::{Result, TransactionError},
};
@ -36,14 +35,6 @@ pub struct RpcBlockhashFeeCalculator {
pub fee_calculator: FeeCalculator,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct RpcFees {
pub blockhash: String,
pub fee_calculator: FeeCalculator,
pub last_valid_slot: Slot,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct RpcFeeCalculator {
@ -56,37 +47,6 @@ pub struct RpcFeeRateGovernor {
pub fee_rate_governor: FeeRateGovernor,
}
#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct RpcInflationGovernor {
pub initial: f64,
pub terminal: f64,
pub taper: f64,
pub foundation: f64,
pub foundation_term: f64,
}
impl From<Inflation> for RpcInflationGovernor {
fn from(inflation: Inflation) -> Self {
Self {
initial: inflation.initial,
terminal: inflation.terminal,
taper: inflation.taper,
foundation: inflation.foundation,
foundation_term: inflation.foundation_term,
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct RpcInflationRate {
pub total: f64,
pub validator: f64,
pub foundation: f64,
pub epoch: Epoch,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct RpcKeyedAccount {
@ -148,8 +108,6 @@ pub struct RpcContactInfo {
pub tpu: Option<SocketAddr>,
/// JSON RPC port
pub rpc: Option<SocketAddr>,
/// Software version
pub version: Option<String>,
}
/// Map of leader base58 identity pubkeys to the slot indices relative to the first epoch slot
@ -169,9 +127,6 @@ pub struct RpcEpochInfo {
/// The absolute current slot
pub absolute_slot: Slot,
/// The current block height
pub block_height: u64,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
@ -231,49 +186,9 @@ pub struct RpcSignatureConfirmation {
pub status: Result<()>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct RpcSimulateTransactionResult {
pub err: Option<TransactionError>,
pub logs: Option<Vec<String>>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct RpcStorageTurn {
pub blockhash: String,
pub slot: Slot,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct RpcAccountBalance {
pub address: String,
pub lamports: u64,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct RpcSupply {
pub total: u64,
pub circulating: u64,
pub non_circulating: u64,
pub non_circulating_accounts: Vec<String>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub enum StakeActivationState {
Activating,
Active,
Deactivating,
Inactive,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct RpcStakeActivation {
pub state: StakeActivationState,
pub active: u64,
pub inactive: u64,
}

View File

@ -440,7 +440,7 @@ impl SyncClient for ThinClient {
match recent_blockhash {
Ok(Response { value, .. }) => {
self.optimizer.report(index, duration_as_ms(&now.elapsed()));
Ok((value.0, value.1))
Ok(value)
}
Err(e) => {
self.optimizer.report(index, std::u64::MAX);

View File

@ -1,7 +1,7 @@
[package]
name = "solana-core"
description = "Blockchain, Rebuilt for Scale"
version = "1.1.23"
version = "1.0.14"
documentation = "https://docs.rs/solana"
homepage = "https://solana.com/"
readme = "../README.md"
@ -17,68 +17,66 @@ codecov = { repository = "solana-labs/solana", branch = "master", service = "git
bincode = "1.2.1"
bv = { version = "0.11.1", features = ["serde"] }
bs58 = "0.3.0"
byteorder = "1.3.4"
chrono = { version = "0.4.11", features = ["serde"] }
byteorder = "1.3.2"
chrono = { version = "0.4.10", features = ["serde"] }
compression = "0.1.5"
core_affinity = "0.5.10"
crossbeam-channel = "0.4"
ed25519-dalek = "=1.0.0-pre.3"
crossbeam-channel = "0.3"
fs_extra = "1.1.0"
flate2 = "1.0"
indexmap = "1.3"
itertools = "0.9.0"
itertools = "0.8.2"
jsonrpc-core = "14.0.5"
jsonrpc-core-client = { version = "14.0.5", features = ["ws"] }
jsonrpc-derive = "14.0.5"
jsonrpc-http-server = "14.0.6"
jsonrpc-pubsub = "14.0.6"
jsonrpc-ws-server = "14.0.6"
libc = "0.2.66"
log = "0.4.8"
nix = "0.17.0"
num_cpus = "1.0.0"
num-traits = "0.2"
rand = "0.7.0"
rand_chacha = "0.2.2"
rayon = "1.3.0"
regex = "1.3.6"
serde = "1.0.105"
rand = "0.6.5"
rand_chacha = "0.1.1"
rayon = "1.2.0"
regex = "1.3.4"
serde = "1.0.104"
serde_derive = "1.0.103"
serde_json = "1.0.48"
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "1.1.23" }
solana-budget-program = { path = "../programs/budget", version = "1.1.23" }
solana-clap-utils = { path = "../clap-utils", version = "1.1.23" }
solana-client = { path = "../client", version = "1.1.23" }
solana-transaction-status = { path = "../transaction-status", version = "1.1.23" }
solana-faucet = { path = "../faucet", version = "1.1.23" }
solana-genesis-programs = { path = "../genesis-programs", version = "1.1.23" }
solana-ledger = { path = "../ledger", version = "1.1.23" }
solana-logger = { path = "../logger", version = "1.1.23" }
solana-merkle-tree = { path = "../merkle-tree", version = "1.1.23" }
solana-metrics = { path = "../metrics", version = "1.1.23" }
solana-measure = { path = "../measure", version = "1.1.23" }
solana-net-utils = { path = "../net-utils", version = "1.1.23" }
solana-chacha-cuda = { path = "../chacha-cuda", version = "1.1.23" }
solana-perf = { path = "../perf", version = "1.1.23" }
solana-runtime = { path = "../runtime", version = "1.1.23" }
solana-sdk = { path = "../sdk", version = "1.1.23" }
solana-stake-program = { path = "../programs/stake", version = "1.1.23" }
solana-storage-program = { path = "../programs/storage", version = "1.1.23" }
solana-streamer = { path = "../streamer", version = "1.1.23" }
solana-version = { path = "../version", version = "1.1.23" }
solana-vote-program = { path = "../programs/vote", version = "1.1.23" }
solana-vote-signer = { path = "../vote-signer", version = "1.1.23" }
solana-sys-tuner = { path = "../sys-tuner", version = "1.1.23" }
serde_json = "1.0.46"
solana-budget-program = { path = "../programs/budget", version = "1.0.14" }
solana-clap-utils = { path = "../clap-utils", version = "1.0.14" }
solana-client = { path = "../client", version = "1.0.14" }
solana-transaction-status = { path = "../transaction-status", version = "1.0.14" }
solana-faucet = { path = "../faucet", version = "1.0.14" }
ed25519-dalek = "=1.0.0-pre.1"
solana-ledger = { path = "../ledger", version = "1.0.14" }
solana-logger = { path = "../logger", version = "1.0.14" }
solana-merkle-tree = { path = "../merkle-tree", version = "1.0.14" }
solana-metrics = { path = "../metrics", version = "1.0.14" }
solana-measure = { path = "../measure", version = "1.0.14" }
solana-net-utils = { path = "../net-utils", version = "1.0.14" }
solana-chacha-cuda = { path = "../chacha-cuda", version = "1.0.14" }
solana-perf = { path = "../perf", version = "1.0.14" }
solana-runtime = { path = "../runtime", version = "1.0.14" }
solana-sdk = { path = "../sdk", version = "1.0.14" }
solana-stake-program = { path = "../programs/stake", version = "1.0.14" }
solana-storage-program = { path = "../programs/storage", version = "1.0.14" }
solana-vote-program = { path = "../programs/vote", version = "1.0.14" }
solana-vote-signer = { path = "../vote-signer", version = "1.0.14" }
solana-sys-tuner = { path = "../sys-tuner", version = "1.0.14" }
tempfile = "3.1.0"
thiserror = "1.0"
tokio = "0.1"
tokio-codec = "0.1"
tokio-fs = "0.1"
tokio-io = "0.1"
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.1.23" }
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.0.14" }
trees = "0.2.1"
[dev-dependencies]
matches = "0.1.6"
reqwest = { version = "0.10.4", default-features = false, features = ["blocking", "rustls-tls", "json"] }
serial_test = "0.4.0"
reqwest = { version = "0.10.1", default-features = false, features = ["blocking", "rustls-tls"] }
serial_test = "0.3.2"
serial_test_derive = "0.4.0"
systemstat = "0.1.5"
@ -106,6 +104,3 @@ name = "cluster_info"
[[bench]]
name = "chacha"
required-features = ["chacha"]
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@ -9,12 +9,12 @@ use rayon::prelude::*;
use solana_core::banking_stage::{create_test_recorder, BankingStage};
use solana_core::cluster_info::ClusterInfo;
use solana_core::cluster_info::Node;
use solana_core::genesis_utils::{create_genesis_config, GenesisConfigInfo};
use solana_core::packet::to_packets_chunked;
use solana_core::poh_recorder::WorkingBankEntry;
use solana_ledger::blockstore_processor::process_entries;
use solana_ledger::entry::{next_hash, Entry};
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_runtime::bank::Bank;
use solana_sdk::genesis_config::GenesisConfig;
@ -190,7 +190,7 @@ fn bench_banking(bencher: &mut Bencher, tx_type: TransactionType) {
let (exit, poh_recorder, poh_service, signal_receiver) =
create_test_recorder(&bank, &blockstore, None);
let cluster_info = ClusterInfo::new_with_invalid_keypair(Node::new_localhost().info);
let cluster_info = Arc::new(cluster_info);
let cluster_info = Arc::new(RwLock::new(cluster_info));
let _banking_stage = BankingStage::new(
&cluster_info,
&poh_recorder,

View File

@ -3,19 +3,13 @@
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_core::cluster_info::{ClusterInfo, Node};
use solana_core::contact_info::ContactInfo;
use solana_ledger::shred::{Shred, NONCE_SHRED_PAYLOAD_SIZE};
use solana_sdk::pubkey::Pubkey;
use solana_sdk::timing::timestamp;
use std::sync::RwLock;
use std::{
collections::HashMap,
net::UdpSocket,
sync::{atomic::AtomicU64, Arc},
};
use std::collections::HashMap;
use std::net::UdpSocket;
use std::sync::Arc;
use test::Bencher;
#[bench]
@ -26,8 +20,10 @@ fn broadcast_shreds_bench(bencher: &mut Bencher) {
let mut cluster_info = ClusterInfo::new_with_invalid_keypair(leader_info.info.clone());
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
const SHRED_SIZE: usize = 1024;
const NUM_SHREDS: usize = 32;
let shreds = vec![Shred::new_empty_data_shred(NONCE_SHRED_PAYLOAD_SIZE); NUM_SHREDS];
let shreds = vec![vec![0; SHRED_SIZE]; NUM_SHREDS];
let seeds = vec![[0u8; 32]; NUM_SHREDS];
let mut stakes = HashMap::new();
const NUM_PEERS: usize = 200;
for _ in 0..NUM_PEERS {
@ -37,20 +33,10 @@ fn broadcast_shreds_bench(bencher: &mut Bencher) {
stakes.insert(id, thread_rng().gen_range(1, NUM_PEERS) as u64);
}
let stakes = Arc::new(stakes);
let cluster_info = Arc::new(cluster_info);
let (peers, peers_and_stakes) = get_broadcast_peers(&cluster_info, Some(stakes.clone()));
let shreds = Arc::new(shreds);
let last_datapoint = Arc::new(AtomicU64::new(0));
bencher.iter(move || {
let shreds = shreds.clone();
broadcast_shreds(
&socket,
&shreds,
&peers_and_stakes,
&peers,
&last_datapoint,
&mut TransmitShredsStats::default(),
)
.unwrap();
cluster_info
.broadcast_shreds(&socket, shreds, &seeds, Some(stakes.clone()))
.unwrap();
});
}

View File

@ -6,12 +6,12 @@ extern crate test;
use log::*;
use solana_core::cluster_info::{ClusterInfo, Node};
use solana_core::contact_info::ContactInfo;
use solana_core::genesis_utils::{create_genesis_config, GenesisConfigInfo};
use solana_core::packet::to_packets_chunked;
use solana_core::retransmit_stage::retransmitter;
use solana_ledger::bank_forks::BankForks;
use solana_ledger::genesis_utils::{create_genesis_config, GenesisConfigInfo};
use solana_ledger::leader_schedule_cache::LeaderScheduleCache;
use solana_measure::measure::Measure;
use solana_perf::packet::to_packets_chunked;
use solana_perf::test_tx::test_tx;
use solana_runtime::bank::Bank;
use solana_sdk::pubkey::Pubkey;
@ -45,7 +45,7 @@ fn bench_retransmitter(bencher: &mut Bencher) {
peer_sockets.push(socket);
}
let peer_sockets = Arc::new(peer_sockets);
let cluster_info = Arc::new(cluster_info);
let cluster_info = Arc::new(RwLock::new(cluster_info));
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(100_000);
let bank0 = Bank::new(&genesis_config);

View File

@ -5,7 +5,7 @@ extern crate test;
use solana_ledger::entry::{create_ticks, Entry};
use solana_ledger::shred::{
max_entries_per_n_shred, max_ticks_per_n_shreds, Shred, Shredder, RECOMMENDED_FEC_RATE,
SIZE_OF_NONCE_DATA_SHRED_PAYLOAD,
SIZE_OF_DATA_SHRED_PAYLOAD,
};
use solana_perf::test_tx;
use solana_sdk::hash::Hash;
@ -29,11 +29,10 @@ fn make_large_unchained_entries(txs_per_entry: u64, num_entries: u64) -> Vec<Ent
#[bench]
fn bench_shredder_ticks(bencher: &mut Bencher) {
let kp = Arc::new(Keypair::new());
let shred_size = SIZE_OF_NONCE_DATA_SHRED_PAYLOAD;
let shred_size = SIZE_OF_DATA_SHRED_PAYLOAD;
let num_shreds = ((1000 * 1000) + (shred_size - 1)) / shred_size;
// ~1Mb
let num_ticks =
max_ticks_per_n_shreds(1, Some(SIZE_OF_NONCE_DATA_SHRED_PAYLOAD)) * num_shreds as u64;
let num_ticks = max_ticks_per_n_shreds(1) * num_shreds as u64;
let entries = create_ticks(num_ticks, 0, Hash::default());
bencher.iter(|| {
let shredder = Shredder::new(1, 0, RECOMMENDED_FEC_RATE, kp.clone(), 0, 0).unwrap();
@ -44,14 +43,10 @@ fn bench_shredder_ticks(bencher: &mut Bencher) {
#[bench]
fn bench_shredder_large_entries(bencher: &mut Bencher) {
let kp = Arc::new(Keypair::new());
let shred_size = SIZE_OF_NONCE_DATA_SHRED_PAYLOAD;
let shred_size = SIZE_OF_DATA_SHRED_PAYLOAD;
let num_shreds = ((1000 * 1000) + (shred_size - 1)) / shred_size;
let txs_per_entry = 128;
let num_entries = max_entries_per_n_shred(
&make_test_entry(txs_per_entry),
num_shreds as u64,
Some(shred_size),
);
let num_entries = max_entries_per_n_shred(&make_test_entry(txs_per_entry), num_shreds as u64);
let entries = make_large_unchained_entries(txs_per_entry, num_entries);
// 1Mb
bencher.iter(|| {
@ -63,10 +58,10 @@ fn bench_shredder_large_entries(bencher: &mut Bencher) {
#[bench]
fn bench_deshredder(bencher: &mut Bencher) {
let kp = Arc::new(Keypair::new());
let shred_size = SIZE_OF_NONCE_DATA_SHRED_PAYLOAD;
let shred_size = SIZE_OF_DATA_SHRED_PAYLOAD;
// ~10Mb
let num_shreds = ((10000 * 1000) + (shred_size - 1)) / shred_size;
let num_ticks = max_ticks_per_n_shreds(1, Some(shred_size)) * num_shreds as u64;
let num_ticks = max_ticks_per_n_shreds(1) * num_shreds as u64;
let entries = create_ticks(num_ticks, 0, Hash::default());
let shredder = Shredder::new(1, 0, RECOMMENDED_FEC_RATE, kp, 0, 0).unwrap();
let data_shreds = shredder.entries_to_shreds(&entries, true, 0).0;
@ -78,7 +73,7 @@ fn bench_deshredder(bencher: &mut Bencher) {
#[bench]
fn bench_deserialize_hdr(bencher: &mut Bencher) {
let data = vec![0; SIZE_OF_NONCE_DATA_SHRED_PAYLOAD];
let data = vec![0; SIZE_OF_DATA_SHRED_PAYLOAD];
let shred = Shred::new_from_data(2, 1, 1, Some(&data), true, true, 0, 0, 1);

View File

@ -6,9 +6,9 @@ extern crate test;
use crossbeam_channel::unbounded;
use log::*;
use rand::{thread_rng, Rng};
use solana_core::packet::to_packets_chunked;
use solana_core::sigverify::TransactionSigVerifier;
use solana_core::sigverify_stage::SigVerifyStage;
use solana_perf::packet::to_packets_chunked;
use solana_perf::test_tx::test_tx;
use solana_sdk::hash::Hash;
use solana_sdk::signature::{Keypair, Signer};

View File

@ -1,51 +0,0 @@
// Service to clean up dead slots in accounts_db
//
// This can be expensive since we have to walk the append vecs being cleaned up.
use solana_ledger::bank_forks::BankForks;
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc, RwLock,
};
use std::thread::{self, sleep, Builder, JoinHandle};
use std::time::Duration;
pub struct AccountsBackgroundService {
t_background: JoinHandle<()>,
}
const INTERVAL_MS: u64 = 100;
impl AccountsBackgroundService {
pub fn new(bank_forks: Arc<RwLock<BankForks>>, exit: &Arc<AtomicBool>) -> Self {
info!("AccountsBackgroundService active");
let exit = exit.clone();
let t_background = Builder::new()
.name("solana-accounts-background".to_string())
.spawn(move || loop {
if exit.load(Ordering::Relaxed) {
break;
}
let bank = bank_forks.read().unwrap().working_bank();
bank.process_dead_slots();
// Currently, given INTERVAL_MS, we process 1 slot/100 ms
let shrunken_account_count = bank.process_stale_slot();
if shrunken_account_count > 0 {
datapoint_info!(
"stale_slot_shrink",
("accounts", shrunken_account_count, i64)
);
}
sleep(Duration::from_millis(INTERVAL_MS));
})
.unwrap();
Self { t_background }
}
pub fn join(self) -> thread::Result<()> {
self.t_background.join()
}
}

View File

@ -15,7 +15,7 @@ use std::{
sync::{
atomic::{AtomicBool, Ordering},
mpsc::RecvTimeoutError,
Arc,
Arc, RwLock,
},
thread::{self, Builder, JoinHandle},
time::Duration,
@ -30,7 +30,7 @@ impl AccountsHashVerifier {
snapshot_package_receiver: SnapshotPackageReceiver,
snapshot_package_sender: Option<SnapshotPackageSender>,
exit: &Arc<AtomicBool>,
cluster_info: &Arc<ClusterInfo>,
cluster_info: &Arc<RwLock<ClusterInfo>>,
trusted_validators: Option<HashSet<Pubkey>>,
halt_on_trusted_validators_accounts_hash_mismatch: bool,
fault_injection_rate_slots: u64,
@ -72,7 +72,7 @@ impl AccountsHashVerifier {
fn process_snapshot(
snapshot_package: SnapshotPackage,
cluster_info: &ClusterInfo,
cluster_info: &Arc<RwLock<ClusterInfo>>,
trusted_validators: &Option<HashSet<Pubkey>>,
halt_on_trusted_validator_accounts_hash_mismatch: bool,
snapshot_package_sender: &Option<SnapshotPackageSender>,
@ -111,11 +111,14 @@ impl AccountsHashVerifier {
if sender.send(snapshot_package).is_err() {}
}
cluster_info.push_accounts_hashes(hashes.clone());
cluster_info
.write()
.unwrap()
.push_accounts_hashes(hashes.clone());
}
fn should_halt(
cluster_info: &ClusterInfo,
cluster_info: &Arc<RwLock<ClusterInfo>>,
trusted_validators: &Option<HashSet<Pubkey>>,
slot_to_hash: &mut HashMap<Slot, Hash>,
) -> bool {
@ -123,9 +126,11 @@ impl AccountsHashVerifier {
let mut highest_slot = 0;
if let Some(trusted_validators) = trusted_validators.as_ref() {
for trusted_validator in trusted_validators {
let is_conflicting = cluster_info.get_accounts_hash_for_node(trusted_validator, |accounts_hashes|
let cluster_info_r = cluster_info.read().unwrap();
if let Some(accounts_hashes) =
cluster_info_r.get_accounts_hash_for_node(trusted_validator)
{
accounts_hashes.iter().any(|(slot, hash)| {
for (slot, hash) in accounts_hashes {
if let Some(reference_hash) = slot_to_hash.get(slot) {
if *hash != *reference_hash {
error!("Trusted validator {} produced conflicting hashes for slot: {} ({} != {})",
@ -134,21 +139,16 @@ impl AccountsHashVerifier {
hash,
reference_hash,
);
true
return true;
} else {
verified_count += 1;
false
}
} else {
highest_slot = std::cmp::max(*slot, highest_slot);
slot_to_hash.insert(*slot, *hash);
false
}
})
}).unwrap_or(false);
if is_conflicting {
return true;
}
}
}
}
@ -181,7 +181,7 @@ mod tests {
let contact_info = ContactInfo::new_localhost(&keypair.pubkey(), 0);
let cluster_info = ClusterInfo::new_with_invalid_keypair(contact_info);
let cluster_info = Arc::new(cluster_info);
let cluster_info = Arc::new(RwLock::new(cluster_info));
let mut trusted_validators = HashSet::new();
let mut slot_to_hash = HashMap::new();
@ -196,7 +196,8 @@ mod tests {
let hash2 = hash(&[2]);
{
let message = make_accounts_hashes_message(&validator1, vec![(0, hash1)]).unwrap();
cluster_info.push_message(message);
let mut cluster_info_w = cluster_info.write().unwrap();
cluster_info_w.push_message(message);
}
slot_to_hash.insert(0, hash2);
trusted_validators.insert(validator1.pubkey());
@ -216,7 +217,7 @@ mod tests {
let contact_info = ContactInfo::new_localhost(&keypair.pubkey(), 0);
let cluster_info = ClusterInfo::new_with_invalid_keypair(contact_info);
let cluster_info = Arc::new(cluster_info);
let cluster_info = Arc::new(RwLock::new(cluster_info));
let trusted_validators = HashSet::new();
let exit = Arc::new(AtomicBool::new(false));
@ -243,8 +244,9 @@ mod tests {
0,
);
}
let cluster_hashes = cluster_info
.get_accounts_hash_for_node(&keypair.pubkey(), |c| c.clone())
let cluster_info_r = cluster_info.read().unwrap();
let cluster_hashes = cluster_info_r
.get_accounts_hash_for_node(&keypair.pubkey())
.unwrap();
info!("{:?}", cluster_hashes);
assert_eq!(hashes.len(), MAX_SNAPSHOT_HASHES);

View File

@ -3,6 +3,7 @@
//! can do its processing in parallel with signature verification on the GPU.
use crate::{
cluster_info::ClusterInfo,
packet::{limited_deserialize, Packet, Packets, PACKETS_PER_BATCH},
poh_recorder::{PohRecorder, PohRecorderError, WorkingBankEntry},
poh_service::PohService,
};
@ -16,11 +17,7 @@ use solana_ledger::{
};
use solana_measure::{measure::Measure, thread_mem_usage};
use solana_metrics::{inc_new_counter_debug, inc_new_counter_info, inc_new_counter_warn};
use solana_perf::{
cuda_runtime::PinnedVec,
packet::{limited_deserialize, Packet, Packets, PACKETS_PER_BATCH},
perf_libs,
};
use solana_perf::{cuda_runtime::PinnedVec, perf_libs};
use solana_runtime::{
accounts_db::ErrorCounters,
bank::{Bank, TransactionBalancesSet, TransactionProcessResult},
@ -41,7 +38,7 @@ use std::{
net::UdpSocket,
sync::atomic::AtomicBool,
sync::mpsc::Receiver,
sync::{Arc, Mutex},
sync::{Arc, Mutex, RwLock},
thread::{self, Builder, JoinHandle},
time::Duration,
time::Instant,
@ -51,7 +48,7 @@ type PacketsAndOffsets = (Packets, Vec<usize>);
pub type UnprocessedPackets = Vec<PacketsAndOffsets>;
/// Transaction forwarding
pub const FORWARD_TRANSACTIONS_TO_LEADER_AT_SLOT_OFFSET: u64 = 1;
pub const FORWARD_TRANSACTIONS_TO_LEADER_AT_SLOT_OFFSET: u64 = 4;
// Fixed thread size seems to be fastest on GCP setup
pub const NUM_THREADS: u32 = 4;
@ -76,7 +73,7 @@ impl BankingStage {
/// Create the stage using `bank`. Exit when `verified_receiver` is dropped.
#[allow(clippy::new_ret_no_self)]
pub fn new(
cluster_info: &Arc<ClusterInfo>,
cluster_info: &Arc<RwLock<ClusterInfo>>,
poh_recorder: &Arc<Mutex<PohRecorder>>,
verified_receiver: CrossbeamReceiver<Vec<Packets>>,
verified_vote_receiver: CrossbeamReceiver<Vec<Packets>>,
@ -93,7 +90,7 @@ impl BankingStage {
}
fn new_num_threads(
cluster_info: &Arc<ClusterInfo>,
cluster_info: &Arc<RwLock<ClusterInfo>>,
poh_recorder: &Arc<Mutex<PohRecorder>>,
verified_receiver: CrossbeamReceiver<Vec<Packets>>,
verified_vote_receiver: CrossbeamReceiver<Vec<Packets>>,
@ -104,7 +101,7 @@ impl BankingStage {
// Single thread to generate entries from many banks.
// This thread talks to poh_service and broadcasts the entries once they have been recorded.
// Once an entry has been recorded, its blockhash is registered with the bank.
let my_pubkey = cluster_info.id();
let my_pubkey = cluster_info.read().unwrap().id();
// Many banks that process transactions in parallel.
let bank_thread_hdls: Vec<JoinHandle<()>> = (0..num_threads)
.map(|i| {
@ -287,12 +284,12 @@ impl BankingStage {
my_pubkey: &Pubkey,
socket: &std::net::UdpSocket,
poh_recorder: &Arc<Mutex<PohRecorder>>,
cluster_info: &ClusterInfo,
cluster_info: &Arc<RwLock<ClusterInfo>>,
buffered_packets: &mut Vec<PacketsAndOffsets>,
enable_forwarding: bool,
batch_limit: usize,
transaction_status_sender: Option<TransactionStatusSender>,
) -> BufferedPacketsDecision {
) {
let (leader_at_slot_offset, poh_has_bank, would_be_leader) = {
let poh = poh_recorder.lock().unwrap();
(
@ -331,7 +328,10 @@ impl BankingStage {
next_leader.map_or((), |leader_pubkey| {
let leader_addr = {
cluster_info
.lookup_contact_info(&leader_pubkey, |leader| leader.tpu_forwards)
.read()
.unwrap()
.lookup(&leader_pubkey)
.map(|leader| leader.tpu_forwards)
};
leader_addr.map_or((), |leader_addr| {
@ -349,14 +349,13 @@ impl BankingStage {
}
_ => (),
}
decision
}
pub fn process_loop(
my_pubkey: Pubkey,
verified_receiver: &CrossbeamReceiver<Vec<Packets>>,
poh_recorder: &Arc<Mutex<PohRecorder>>,
cluster_info: &ClusterInfo,
cluster_info: &Arc<RwLock<ClusterInfo>>,
recv_start: &mut Instant,
enable_forwarding: bool,
id: u32,
@ -366,8 +365,8 @@ impl BankingStage {
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
let mut buffered_packets = vec![];
loop {
while !buffered_packets.is_empty() {
let decision = Self::process_buffered_packets(
if !buffered_packets.is_empty() {
Self::process_buffered_packets(
&my_pubkey,
&socket,
poh_recorder,
@ -377,11 +376,6 @@ impl BankingStage {
batch_limit,
transaction_status_sender.clone(),
);
if decision == BufferedPacketsDecision::Hold {
// If we are waiting on a new bank,
// check the receiver for more transactions/for exiting
break;
}
}
let recv_timeout = if !buffered_packets.is_empty() {
@ -509,7 +503,7 @@ impl BankingStage {
// expires.
let txs = batch.transactions();
let pre_balances = if transaction_status_sender.is_some() {
bank.collect_balances(batch)
bank.collect_balances(txs)
} else {
vec![]
};
@ -545,11 +539,10 @@ impl BankingStage {
.processing_results;
if let Some(sender) = transaction_status_sender {
let post_balances = bank.collect_balances(batch);
let post_balances = bank.collect_balances(txs);
send_transaction_status_batch(
bank.clone(),
batch.transactions(),
batch.iteration_order_vec(),
transaction_statuses,
TransactionBalancesSet::new(pre_balances, post_balances),
sender,
@ -1016,7 +1009,10 @@ pub fn create_test_recorder(
mod tests {
use super::*;
use crate::{
cluster_info::Node, poh_recorder::WorkingBank,
cluster_info::Node,
genesis_utils::{create_genesis_config, GenesisConfigInfo},
packet::to_packets,
poh_recorder::WorkingBank,
transaction_status_service::TransactionStatusService,
};
use crossbeam_channel::unbounded;
@ -1024,10 +1020,8 @@ mod tests {
use solana_ledger::{
blockstore::entries_to_test_shreds,
entry::{next_entry, Entry, EntrySlice},
genesis_utils::{create_genesis_config, GenesisConfigInfo},
get_tmp_ledger_path,
};
use solana_perf::packet::to_packets;
use solana_runtime::bank::HashAgeKind;
use solana_sdk::{
instruction::InstructionError,
@ -1053,7 +1047,7 @@ mod tests {
let (exit, poh_recorder, poh_service, _entry_receiever) =
create_test_recorder(&bank, &blockstore, None);
let cluster_info = ClusterInfo::new_with_invalid_keypair(Node::new_localhost().info);
let cluster_info = Arc::new(cluster_info);
let cluster_info = Arc::new(RwLock::new(cluster_info));
let banking_stage = BankingStage::new(
&cluster_info,
&poh_recorder,
@ -1093,7 +1087,7 @@ mod tests {
let (exit, poh_recorder, poh_service, entry_receiver) =
create_test_recorder(&bank, &blockstore, Some(poh_config));
let cluster_info = ClusterInfo::new_with_invalid_keypair(Node::new_localhost().info);
let cluster_info = Arc::new(cluster_info);
let cluster_info = Arc::new(RwLock::new(cluster_info));
let banking_stage = BankingStage::new(
&cluster_info,
&poh_recorder,
@ -1156,7 +1150,7 @@ mod tests {
let (exit, poh_recorder, poh_service, entry_receiver) =
create_test_recorder(&bank, &blockstore, Some(poh_config));
let cluster_info = ClusterInfo::new_with_invalid_keypair(Node::new_localhost().info);
let cluster_info = Arc::new(cluster_info);
let cluster_info = Arc::new(RwLock::new(cluster_info));
let banking_stage = BankingStage::new(
&cluster_info,
&poh_recorder,
@ -1297,7 +1291,7 @@ mod tests {
create_test_recorder(&bank, &blockstore, Some(poh_config));
let cluster_info =
ClusterInfo::new_with_invalid_keypair(Node::new_localhost().info);
let cluster_info = Arc::new(cluster_info);
let cluster_info = Arc::new(RwLock::new(cluster_info));
let _banking_stage = BankingStage::new_num_threads(
&cluster_info,
&poh_recorder,
@ -1995,14 +1989,14 @@ mod tests {
meta.err,
Some(TransactionError::InstructionError(
0,
InstructionError::Custom(1)
InstructionError::CustomError(1)
))
);
assert_eq!(
meta.status,
Err(TransactionError::InstructionError(
0,
InstructionError::Custom(1)
InstructionError::CustomError(1)
))
);
} else {

283
core/src/blockstream.rs Normal file
View File

@ -0,0 +1,283 @@
//! The `blockstream` module provides a method for streaming entries out via a
//! local unix socket, to provide client services such as a block explorer with
//! real-time access to entries.
use bincode::serialize;
use chrono::{SecondsFormat, Utc};
use serde_json::json;
use solana_ledger::entry::Entry;
use solana_sdk::{clock::Slot, hash::Hash, pubkey::Pubkey};
use std::cell::RefCell;
use std::io::Result;
use std::path::{Path, PathBuf};
pub trait EntryWriter: std::fmt::Debug {
fn write(&self, payload: String) -> Result<()>;
}
#[derive(Debug, Default)]
pub struct EntryVec {
values: RefCell<Vec<String>>,
}
impl EntryWriter for EntryVec {
fn write(&self, payload: String) -> Result<()> {
self.values.borrow_mut().push(payload);
Ok(())
}
}
impl EntryVec {
pub fn new() -> Self {
EntryVec {
values: RefCell::new(Vec::new()),
}
}
pub fn entries(&self) -> Vec<String> {
self.values.borrow().clone()
}
}
#[derive(Debug)]
pub struct EntrySocket {
unix_socket: PathBuf,
}
impl EntryWriter for EntrySocket {
#[cfg(not(windows))]
fn write(&self, payload: String) -> Result<()> {
use std::io::prelude::*;
use std::net::Shutdown;
use std::os::unix::net::UnixStream;
const MESSAGE_TERMINATOR: &str = "\n";
let mut socket = UnixStream::connect(&self.unix_socket)?;
socket.write_all(payload.as_bytes())?;
socket.write_all(MESSAGE_TERMINATOR.as_bytes())?;
socket.shutdown(Shutdown::Write)?;
Ok(())
}
#[cfg(windows)]
fn write(&self, _payload: String) -> Result<()> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"EntryWriter::write() not implemented for windows",
))
}
}
pub trait BlockstreamEvents {
fn emit_entry_event(
&self,
slot: Slot,
tick_height: u64,
leader_pubkey: &Pubkey,
entries: &Entry,
) -> Result<()>;
fn emit_block_event(
&self,
slot: Slot,
tick_height: u64,
leader_pubkey: &Pubkey,
blockhash: Hash,
) -> Result<()>;
}
#[derive(Debug)]
pub struct Blockstream<T: EntryWriter> {
pub output: T,
}
impl<T> BlockstreamEvents for Blockstream<T>
where
T: EntryWriter,
{
fn emit_entry_event(
&self,
slot: Slot,
tick_height: u64,
leader_pubkey: &Pubkey,
entry: &Entry,
) -> Result<()> {
let transactions: Vec<Vec<u8>> = serialize_transactions(entry);
let stream_entry = json!({
"num_hashes": entry.num_hashes,
"hash": entry.hash,
"transactions": transactions
});
let json_entry = serde_json::to_string(&stream_entry)?;
let payload = format!(
r#"{{"dt":"{}","t":"entry","s":{},"h":{},"l":"{:?}","entry":{}}}"#,
Utc::now().to_rfc3339_opts(SecondsFormat::Nanos, true),
slot,
tick_height,
leader_pubkey,
json_entry,
);
self.output.write(payload)?;
Ok(())
}
fn emit_block_event(
&self,
slot: Slot,
tick_height: u64,
leader_pubkey: &Pubkey,
blockhash: Hash,
) -> Result<()> {
let payload = format!(
r#"{{"dt":"{}","t":"block","s":{},"h":{},"l":"{:?}","hash":"{:?}"}}"#,
Utc::now().to_rfc3339_opts(SecondsFormat::Nanos, true),
slot,
tick_height,
leader_pubkey,
blockhash,
);
self.output.write(payload)?;
Ok(())
}
}
pub type SocketBlockstream = Blockstream<EntrySocket>;
impl SocketBlockstream {
pub fn new(unix_socket: &Path) -> Self {
Blockstream {
output: EntrySocket {
unix_socket: unix_socket.to_path_buf(),
},
}
}
}
pub type MockBlockstream = Blockstream<EntryVec>;
impl MockBlockstream {
pub fn new(_: &Path) -> Self {
Blockstream {
output: EntryVec::new(),
}
}
pub fn entries(&self) -> Vec<String> {
self.output.entries()
}
}
fn serialize_transactions(entry: &Entry) -> Vec<Vec<u8>> {
entry
.transactions
.iter()
.map(|tx| serialize(&tx).unwrap())
.collect()
}
#[cfg(test)]
mod test {
use super::*;
use chrono::{DateTime, FixedOffset};
use serde_json::Value;
use solana_sdk::hash::Hash;
use solana_sdk::signature::{Keypair, Signer};
use solana_sdk::system_transaction;
use std::collections::HashSet;
use std::path::PathBuf;
#[test]
fn test_serialize_transactions() {
let entry = Entry::new(&Hash::default(), 1, vec![]);
let empty_vec: Vec<Vec<u8>> = vec![];
assert_eq!(serialize_transactions(&entry), empty_vec);
let keypair0 = Keypair::new();
let keypair1 = Keypair::new();
let tx0 = system_transaction::transfer(&keypair0, &keypair1.pubkey(), 1, Hash::default());
let tx1 = system_transaction::transfer(&keypair1, &keypair0.pubkey(), 2, Hash::default());
let serialized_tx0 = serialize(&tx0).unwrap();
let serialized_tx1 = serialize(&tx1).unwrap();
let entry = Entry::new(&Hash::default(), 1, vec![tx0, tx1]);
assert_eq!(
serialize_transactions(&entry),
vec![serialized_tx0, serialized_tx1]
);
}
#[test]
fn test_blockstream() -> () {
let blockstream = MockBlockstream::new(&PathBuf::from("test_stream"));
let ticks_per_slot = 5;
let mut blockhash = Hash::default();
let mut entries = Vec::new();
let mut expected_entries = Vec::new();
let tick_height_initial = 1;
let tick_height_final = tick_height_initial + ticks_per_slot + 2;
let mut curr_slot = 0;
let leader_pubkey = Pubkey::new_rand();
for tick_height in tick_height_initial..=tick_height_final {
if tick_height == 5 {
blockstream
.emit_block_event(curr_slot, tick_height, &leader_pubkey, blockhash)
.unwrap();
curr_slot += 1;
}
let entry = Entry::new(&mut blockhash, 1, vec![]); // just ticks
blockhash = entry.hash;
blockstream
.emit_entry_event(curr_slot, tick_height, &leader_pubkey, &entry)
.unwrap();
expected_entries.push(entry.clone());
entries.push(entry);
}
assert_eq!(
blockstream.entries().len() as u64,
// one entry per tick (1..=N+2) is +3, plus one block
ticks_per_slot + 3 + 1
);
let mut j = 0;
let mut matched_entries = 0;
let mut matched_slots = HashSet::new();
let mut matched_blocks = HashSet::new();
for item in blockstream.entries() {
let json: Value = serde_json::from_str(&item).unwrap();
let dt_str = json["dt"].as_str().unwrap();
// Ensure `ts` field parses as valid DateTime
let _dt: DateTime<FixedOffset> = DateTime::parse_from_rfc3339(dt_str).unwrap();
let item_type = json["t"].as_str().unwrap();
match item_type {
"block" => {
let hash = json["hash"].to_string();
matched_blocks.insert(hash);
}
"entry" => {
let slot = json["s"].as_u64().unwrap();
matched_slots.insert(slot);
let entry_obj = json["entry"].clone();
let entry: Entry = serde_json::from_value(entry_obj).unwrap();
assert_eq!(entry, expected_entries[j]);
matched_entries += 1;
j += 1;
}
_ => {
assert!(false, "unknown item type {}", item);
}
}
}
assert_eq!(matched_entries, expected_entries.len());
assert_eq!(matched_slots.len(), 2);
assert_eq!(matched_blocks.len(), 1);
}
}

View File

@ -0,0 +1,228 @@
//! The `blockstream_service` implements optional streaming of entries and block metadata
//! using the `blockstream` module, providing client services such as a block explorer with
//! real-time access to entries.
use crate::blockstream::BlockstreamEvents;
#[cfg(test)]
use crate::blockstream::MockBlockstream as Blockstream;
#[cfg(not(test))]
use crate::blockstream::SocketBlockstream as Blockstream;
use crate::result::{Error, Result};
use solana_ledger::blockstore::Blockstore;
use solana_sdk::pubkey::Pubkey;
use std::path::Path;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{Receiver, RecvTimeoutError};
use std::sync::Arc;
use std::thread::{self, Builder, JoinHandle};
use std::time::Duration;
pub struct BlockstreamService {
t_blockstream: JoinHandle<()>,
}
impl BlockstreamService {
#[allow(clippy::new_ret_no_self)]
pub fn new(
slot_full_receiver: Receiver<(u64, Pubkey)>,
blockstore: Arc<Blockstore>,
unix_socket: &Path,
exit: &Arc<AtomicBool>,
) -> Self {
let mut blockstream = Blockstream::new(unix_socket);
let exit = exit.clone();
let t_blockstream = Builder::new()
.name("solana-blockstream".to_string())
.spawn(move || loop {
if exit.load(Ordering::Relaxed) {
break;
}
if let Err(e) =
Self::process_entries(&slot_full_receiver, &blockstore, &mut blockstream)
{
match e {
Error::RecvTimeoutError(RecvTimeoutError::Disconnected) => break,
Error::RecvTimeoutError(RecvTimeoutError::Timeout) => (),
_ => info!("Error from process_entries: {:?}", e),
}
}
})
.unwrap();
Self { t_blockstream }
}
fn process_entries(
slot_full_receiver: &Receiver<(u64, Pubkey)>,
blockstore: &Arc<Blockstore>,
blockstream: &mut Blockstream,
) -> Result<()> {
let timeout = Duration::new(1, 0);
let (slot, slot_leader) = slot_full_receiver.recv_timeout(timeout)?;
// Slot might not exist due to LedgerCleanupService, check first
let blockstore_meta = blockstore.meta(slot).unwrap();
if let Some(blockstore_meta) = blockstore_meta {
// Return error to main loop. Thread won't exit, will just log the error
let entries = blockstore.get_slot_entries(slot, 0, None)?;
let _parent_slot = if slot == 0 {
None
} else {
Some(blockstore_meta.parent_slot)
};
let ticks_per_slot = entries.iter().filter(|entry| entry.is_tick()).count() as u64;
let mut tick_height = ticks_per_slot * slot;
for (i, entry) in entries.iter().enumerate() {
if entry.is_tick() {
tick_height += 1;
}
blockstream
.emit_entry_event(slot, tick_height, &slot_leader, &entry)
.unwrap_or_else(|e| {
debug!("Blockstream error: {:?}, {:?}", e, blockstream.output);
});
if i == entries.len() - 1 {
blockstream
.emit_block_event(slot, tick_height, &slot_leader, entry.hash)
.unwrap_or_else(|e| {
debug!("Blockstream error: {:?}, {:?}", e, blockstream.output);
});
}
}
}
Ok(())
}
pub fn join(self) -> thread::Result<()> {
self.t_blockstream.join()
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::genesis_utils::{create_genesis_config, GenesisConfigInfo};
use bincode::{deserialize, serialize};
use chrono::{DateTime, FixedOffset};
use serde_json::Value;
use solana_ledger::create_new_tmp_ledger;
use solana_ledger::entry::{create_ticks, Entry};
use solana_sdk::hash::Hash;
use solana_sdk::signature::{Keypair, Signer};
use solana_sdk::system_transaction;
use std::path::PathBuf;
use std::sync::mpsc::channel;
#[test]
fn test_blockstream_service_process_entries() {
let ticks_per_slot = 5;
let leader_pubkey = Pubkey::new_rand();
// Set up genesis config and blockstore
let GenesisConfigInfo {
mut genesis_config, ..
} = create_genesis_config(1000);
genesis_config.ticks_per_slot = ticks_per_slot;
let (ledger_path, _blockhash) = create_new_tmp_ledger!(&genesis_config);
let blockstore = Blockstore::open(&ledger_path).unwrap();
// Set up blockstream
let mut blockstream = Blockstream::new(&PathBuf::from("test_stream"));
// Set up dummy channel to receive a full-slot notification
let (slot_full_sender, slot_full_receiver) = channel();
// Create entries - 4 ticks + 1 populated entry + 1 tick
let mut entries = create_ticks(4, 0, Hash::default());
let keypair = Keypair::new();
let mut blockhash = entries[3].hash;
let tx = system_transaction::transfer(&keypair, &keypair.pubkey(), 1, Hash::default());
let entry = Entry::new(&mut blockhash, 1, vec![tx]);
blockhash = entry.hash;
entries.push(entry);
let final_tick = create_ticks(1, 0, blockhash);
entries.extend_from_slice(&final_tick);
let expected_entries = entries.clone();
let expected_tick_heights = [6, 7, 8, 9, 9, 10];
blockstore
.write_entries(
1,
0,
0,
ticks_per_slot,
None,
true,
&Arc::new(Keypair::new()),
entries,
0,
)
.unwrap();
slot_full_sender.send((1, leader_pubkey)).unwrap();
BlockstreamService::process_entries(
&slot_full_receiver,
&Arc::new(blockstore),
&mut blockstream,
)
.unwrap();
assert_eq!(blockstream.entries().len(), 7);
let (entry_events, block_events): (Vec<Value>, Vec<Value>) = blockstream
.entries()
.iter()
.map(|item| {
let json: Value = serde_json::from_str(&item).unwrap();
let dt_str = json["dt"].as_str().unwrap();
// Ensure `ts` field parses as valid DateTime
let _dt: DateTime<FixedOffset> = DateTime::parse_from_rfc3339(dt_str).unwrap();
json
})
.partition(|json| {
let item_type = json["t"].as_str().unwrap();
item_type == "entry"
});
for (i, json) in entry_events.iter().enumerate() {
let height = json["h"].as_u64().unwrap();
assert_eq!(height, expected_tick_heights[i]);
let entry_obj = json["entry"].clone();
let tx = entry_obj["transactions"].as_array().unwrap();
let entry: Entry;
if tx.len() == 0 {
entry = serde_json::from_value(entry_obj).unwrap();
} else {
let entry_json = entry_obj.as_object().unwrap();
entry = Entry {
num_hashes: entry_json.get("num_hashes").unwrap().as_u64().unwrap(),
hash: serde_json::from_value(entry_json.get("hash").unwrap().clone()).unwrap(),
transactions: entry_json
.get("transactions")
.unwrap()
.as_array()
.unwrap()
.into_iter()
.enumerate()
.map(|(j, tx)| {
let tx_vec: Vec<u8> = serde_json::from_value(tx.clone()).unwrap();
// Check explicitly that transaction matches bincode-serialized format
assert_eq!(
tx_vec,
serialize(&expected_entries[i].transactions[j]).unwrap()
);
deserialize(&tx_vec).unwrap()
})
.collect(),
};
}
assert_eq!(entry, expected_entries[i]);
}
for json in block_events {
let slot = json["s"].as_u64().unwrap();
assert_eq!(1, slot);
let height = json["h"].as_u64().unwrap();
assert_eq!(2 * ticks_per_slot, height);
}
}
}

View File

@ -1,51 +1,30 @@
//! A stage to broadcast data from a leader node to validators
use self::{
broadcast_fake_shreds_run::BroadcastFakeShredsRun, broadcast_metrics::*,
fail_entry_verification_broadcast_run::FailEntryVerificationBroadcastRun,
standard_broadcast_run::StandardBroadcastRun,
};
use crate::contact_info::ContactInfo;
use crate::crds_gossip_pull::CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS;
use crate::weighted_shuffle::weighted_best;
use crate::{
cluster_info::{ClusterInfo, ClusterInfoError},
poh_recorder::WorkingBankEntry,
result::{Error, Result},
};
use crossbeam_channel::{
Receiver as CrossbeamReceiver, RecvTimeoutError as CrossbeamRecvTimeoutError,
Sender as CrossbeamSender,
};
use solana_ledger::{blockstore::Blockstore, shred::Shred, staking_utils};
use solana_measure::measure::Measure;
use self::broadcast_fake_shreds_run::BroadcastFakeShredsRun;
use self::fail_entry_verification_broadcast_run::FailEntryVerificationBroadcastRun;
use self::standard_broadcast_run::StandardBroadcastRun;
use crate::cluster_info::{ClusterInfo, ClusterInfoError};
use crate::poh_recorder::WorkingBankEntry;
use crate::result::{Error, Result};
use solana_ledger::blockstore::Blockstore;
use solana_ledger::shred::Shred;
use solana_ledger::staking_utils;
use solana_metrics::{inc_new_counter_error, inc_new_counter_info};
use solana_runtime::bank::Bank;
use solana_sdk::timing::timestamp;
use solana_sdk::{clock::Slot, pubkey::Pubkey};
use solana_streamer::sendmmsg::send_mmsg;
use std::sync::atomic::AtomicU64;
use std::{
collections::HashMap,
net::UdpSocket,
sync::atomic::{AtomicBool, Ordering},
sync::mpsc::{channel, Receiver, RecvError, RecvTimeoutError, Sender},
sync::{Arc, Mutex},
thread::{self, Builder, JoinHandle},
time::{Duration, Instant},
};
use solana_sdk::pubkey::Pubkey;
use std::collections::HashMap;
use std::net::UdpSocket;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{channel, Receiver, RecvError, RecvTimeoutError, Sender};
use std::sync::{Arc, Mutex, RwLock};
use std::thread::{self, Builder, JoinHandle};
use std::time::Instant;
pub const NUM_INSERT_THREADS: usize = 2;
mod broadcast_fake_shreds_run;
pub mod broadcast_metrics;
pub(crate) mod broadcast_utils;
mod fail_entry_verification_broadcast_run;
mod standard_broadcast_run;
pub(crate) const NUM_INSERT_THREADS: usize = 2;
pub(crate) type RetransmitSlotsSender = CrossbeamSender<HashMap<Slot, Arc<Bank>>>;
pub(crate) type RetransmitSlotsReceiver = CrossbeamReceiver<HashMap<Slot, Arc<Bank>>>;
pub(crate) type RecordReceiver = Receiver<(Arc<Vec<Shred>>, Option<BroadcastShredBatchInfo>)>;
pub(crate) type TransmitReceiver = Receiver<(TransmitShreds, Option<BroadcastShredBatchInfo>)>;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum BroadcastStageReturnType {
ChannelDisconnected,
@ -62,20 +41,18 @@ impl BroadcastStageType {
pub fn new_broadcast_stage(
&self,
sock: Vec<UdpSocket>,
cluster_info: Arc<ClusterInfo>,
cluster_info: Arc<RwLock<ClusterInfo>>,
receiver: Receiver<WorkingBankEntry>,
retransmit_slots_receiver: RetransmitSlotsReceiver,
exit_sender: &Arc<AtomicBool>,
blockstore: &Arc<Blockstore>,
shred_version: u16,
) -> BroadcastStage {
let keypair = cluster_info.keypair.clone();
let keypair = cluster_info.read().unwrap().keypair.clone();
match self {
BroadcastStageType::Standard => BroadcastStage::new(
sock,
cluster_info,
receiver,
retransmit_slots_receiver,
exit_sender,
blockstore,
StandardBroadcastRun::new(keypair, shred_version),
@ -85,7 +62,6 @@ impl BroadcastStageType {
sock,
cluster_info,
receiver,
retransmit_slots_receiver,
exit_sender,
blockstore,
FailEntryVerificationBroadcastRun::new(keypair, shred_version),
@ -95,7 +71,6 @@ impl BroadcastStageType {
sock,
cluster_info,
receiver,
retransmit_slots_receiver,
exit_sender,
blockstore,
BroadcastFakeShredsRun::new(keypair, 0, shred_version),
@ -104,24 +79,24 @@ impl BroadcastStageType {
}
}
pub type TransmitShreds = (Option<Arc<HashMap<Pubkey, u64>>>, Arc<Vec<Shred>>);
type TransmitShreds = (Option<Arc<HashMap<Pubkey, u64>>>, Arc<Vec<Shred>>);
trait BroadcastRun {
fn run(
&mut self,
blockstore: &Arc<Blockstore>,
receiver: &Receiver<WorkingBankEntry>,
socket_sender: &Sender<(TransmitShreds, Option<BroadcastShredBatchInfo>)>,
blockstore_sender: &Sender<(Arc<Vec<Shred>>, Option<BroadcastShredBatchInfo>)>,
socket_sender: &Sender<TransmitShreds>,
blockstore_sender: &Sender<Arc<Vec<Shred>>>,
) -> Result<()>;
fn transmit(
&mut self,
receiver: &Arc<Mutex<TransmitReceiver>>,
cluster_info: &ClusterInfo,
&self,
receiver: &Arc<Mutex<Receiver<TransmitShreds>>>,
cluster_info: &Arc<RwLock<ClusterInfo>>,
sock: &UdpSocket,
) -> Result<()>;
fn record(
&mut self,
receiver: &Arc<Mutex<RecordReceiver>>,
&self,
receiver: &Arc<Mutex<Receiver<Arc<Vec<Shred>>>>>,
blockstore: &Arc<Blockstore>,
) -> Result<()>;
}
@ -153,34 +128,32 @@ impl BroadcastStage {
fn run(
blockstore: &Arc<Blockstore>,
receiver: &Receiver<WorkingBankEntry>,
socket_sender: &Sender<(TransmitShreds, Option<BroadcastShredBatchInfo>)>,
blockstore_sender: &Sender<(Arc<Vec<Shred>>, Option<BroadcastShredBatchInfo>)>,
socket_sender: &Sender<TransmitShreds>,
blockstore_sender: &Sender<Arc<Vec<Shred>>>,
mut broadcast_stage_run: impl BroadcastRun,
) -> BroadcastStageReturnType {
loop {
let res =
broadcast_stage_run.run(blockstore, receiver, socket_sender, blockstore_sender);
let res = Self::handle_error(res, "run");
let res = Self::handle_error(res);
if let Some(res) = res {
return res;
}
}
}
fn handle_error(r: Result<()>, name: &str) -> Option<BroadcastStageReturnType> {
fn handle_error(r: Result<()>) -> Option<BroadcastStageReturnType> {
if let Err(e) = r {
match e {
Error::RecvTimeoutError(RecvTimeoutError::Disconnected)
| Error::SendError
| Error::RecvError(RecvError)
| Error::CrossbeamRecvTimeoutError(CrossbeamRecvTimeoutError::Disconnected) => {
| Error::RecvError(RecvError) => {
return Some(BroadcastStageReturnType::ChannelDisconnected);
}
Error::RecvTimeoutError(RecvTimeoutError::Timeout)
| Error::CrossbeamRecvTimeoutError(CrossbeamRecvTimeoutError::Timeout) => (),
Error::RecvTimeoutError(RecvTimeoutError::Timeout) => (),
Error::ClusterInfoError(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);
error!("broadcaster error: {:?}", e);
}
}
}
@ -205,9 +178,8 @@ impl BroadcastStage {
#[allow(clippy::too_many_arguments)]
fn new(
socks: Vec<UdpSocket>,
cluster_info: Arc<ClusterInfo>,
cluster_info: Arc<RwLock<ClusterInfo>>,
receiver: Receiver<WorkingBankEntry>,
retransmit_slots_receiver: RetransmitSlotsReceiver,
exit_sender: &Arc<AtomicBool>,
blockstore: &Arc<Blockstore>,
broadcast_stage_run: impl BroadcastRun + Send + 'static + Clone,
@ -217,8 +189,6 @@ impl BroadcastStage {
let (socket_sender, socket_receiver) = channel();
let (blockstore_sender, blockstore_receiver) = channel();
let bs_run = broadcast_stage_run.clone();
let socket_sender_ = socket_sender.clone();
let thread_hdl = Builder::new()
.name("solana-broadcaster".to_string())
.spawn(move || {
@ -226,7 +196,7 @@ impl BroadcastStage {
Self::run(
&btree,
&receiver,
&socket_sender_,
&socket_sender,
&blockstore_sender,
bs_run,
)
@ -236,13 +206,13 @@ impl BroadcastStage {
let socket_receiver = Arc::new(Mutex::new(socket_receiver));
for sock in socks.into_iter() {
let socket_receiver = socket_receiver.clone();
let mut bs_transmit = broadcast_stage_run.clone();
let bs_transmit = broadcast_stage_run.clone();
let cluster_info = cluster_info.clone();
let t = Builder::new()
.name("solana-broadcaster-transmit".to_string())
.spawn(move || loop {
let res = bs_transmit.transmit(&socket_receiver, &cluster_info, &sock);
let res = Self::handle_error(res, "solana-broadcaster-transmit");
let res = Self::handle_error(res);
if let Some(res) = res {
return res;
}
@ -253,13 +223,13 @@ impl BroadcastStage {
let blockstore_receiver = Arc::new(Mutex::new(blockstore_receiver));
for _ in 0..NUM_INSERT_THREADS {
let blockstore_receiver = blockstore_receiver.clone();
let mut bs_record = broadcast_stage_run.clone();
let bs_record = broadcast_stage_run.clone();
let btree = blockstore.clone();
let t = Builder::new()
.name("solana-broadcaster-record".to_string())
.spawn(move || loop {
let res = bs_record.record(&blockstore_receiver, &btree);
let res = Self::handle_error(res, "solana-broadcaster-record");
let res = Self::handle_error(res);
if let Some(res) = res {
return res;
}
@ -268,68 +238,9 @@ impl BroadcastStage {
thread_hdls.push(t);
}
let blockstore = blockstore.clone();
let retransmit_thread = Builder::new()
.name("solana-broadcaster-retransmit".to_string())
.spawn(move || loop {
if let Some(res) = Self::handle_error(
Self::check_retransmit_signals(
&blockstore,
&retransmit_slots_receiver,
&socket_sender,
),
"solana-broadcaster-retransmit-check_retransmit_signals",
) {
return res;
}
})
.unwrap();
thread_hdls.push(retransmit_thread);
Self { thread_hdls }
}
fn check_retransmit_signals(
blockstore: &Blockstore,
retransmit_slots_receiver: &RetransmitSlotsReceiver,
socket_sender: &Sender<(TransmitShreds, Option<BroadcastShredBatchInfo>)>,
) -> Result<()> {
let timer = Duration::from_millis(100);
// Check for a retransmit signal
let mut retransmit_slots = retransmit_slots_receiver.recv_timeout(timer)?;
while let Ok(new_retransmit_slots) = retransmit_slots_receiver.try_recv() {
retransmit_slots.extend(new_retransmit_slots);
}
for (_, bank) in retransmit_slots.iter() {
let bank_epoch = bank.get_leader_schedule_epoch(bank.slot());
let stakes = staking_utils::staked_nodes_at_epoch(&bank, bank_epoch);
let stakes = stakes.map(Arc::new);
let data_shreds = Arc::new(
blockstore
.get_data_shreds_for_slot(bank.slot(), 0)
.expect("My own shreds must be reconstructable"),
);
if !data_shreds.is_empty() {
socket_sender.send(((stakes.clone(), data_shreds), None))?;
}
let coding_shreds = Arc::new(
blockstore
.get_coding_shreds_for_slot(bank.slot(), 0)
.expect("My own shreds must be reconstructable"),
);
if !coding_shreds.is_empty() {
socket_sender.send(((stakes.clone(), coding_shreds), None))?;
}
}
Ok(())
}
pub fn join(self) -> thread::Result<BroadcastStageReturnType> {
for thread_hdl in self.thread_hdls.into_iter() {
let _ = thread_hdl.join();
@ -338,241 +249,23 @@ impl BroadcastStage {
}
}
fn update_peer_stats(
num_live_peers: i64,
broadcast_len: i64,
last_datapoint_submit: &Arc<AtomicU64>,
) {
let now = timestamp();
let last = last_datapoint_submit.load(Ordering::Relaxed);
if now - last > 1000
&& last_datapoint_submit.compare_and_swap(last, now, Ordering::Relaxed) == last
{
datapoint_info!(
"cluster_info-num_nodes",
("live_count", num_live_peers, i64),
("broadcast_count", broadcast_len, i64)
);
}
}
pub fn get_broadcast_peers<S: std::hash::BuildHasher>(
cluster_info: &ClusterInfo,
stakes: Option<Arc<HashMap<Pubkey, u64, S>>>,
) -> (Vec<ContactInfo>, Vec<(u64, usize)>) {
use crate::cluster_info;
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: &Arc<Vec<Shred>>,
peers_and_stakes: &[(u64, usize)],
peers: &[ContactInfo],
last_datapoint_submit: &Arc<AtomicU64>,
transmit_stats: &mut TransmitShredsStats,
) -> Result<()> {
let broadcast_len = peers_and_stakes.len();
if broadcast_len == 0 {
update_peer_stats(1, 1, last_datapoint_submit);
return Ok(());
}
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)
})
.collect();
shred_select.stop();
transmit_stats.shred_select += shred_select.as_us();
let mut sent = 0;
let mut send_mmsg_time = Measure::start("send_mmsg");
while sent < packets.len() {
match send_mmsg(s, &packets[sent..]) {
Ok(n) => sent += n,
Err(e) => {
return Err(Error::IO(e));
}
}
}
send_mmsg_time.stop();
transmit_stats.send_mmsg_elapsed += send_mmsg_time.as_us();
let num_live_peers = num_live_peers(&peers);
update_peer_stats(
num_live_peers,
broadcast_len as i64 + 1,
last_datapoint_submit,
);
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 {
mod test {
use super::*;
use crate::cluster_info::{ClusterInfo, Node};
use crossbeam_channel::unbounded;
use solana_ledger::{
blockstore::{make_slot_entries, Blockstore},
entry::create_ticks,
genesis_utils::{create_genesis_config, GenesisConfigInfo},
get_tmp_ledger_path,
shred::{max_ticks_per_n_shreds, Shredder, RECOMMENDED_FEC_RATE},
};
use crate::genesis_utils::{create_genesis_config, GenesisConfigInfo};
use solana_ledger::entry::create_ticks;
use solana_ledger::{blockstore::Blockstore, get_tmp_ledger_path};
use solana_runtime::bank::Bank;
use solana_sdk::{
hash::Hash,
pubkey::Pubkey,
signature::{Keypair, Signer},
};
use std::{
path::Path, sync::atomic::AtomicBool, sync::mpsc::channel, sync::Arc, thread::sleep,
};
pub fn make_transmit_shreds(
slot: Slot,
num: u64,
stakes: Option<Arc<HashMap<Pubkey, u64>>>,
) -> (
Vec<Shred>,
Vec<Shred>,
Vec<TransmitShreds>,
Vec<TransmitShreds>,
) {
let num_entries = max_ticks_per_n_shreds(num, None);
let (data_shreds, _) = make_slot_entries(slot, 0, num_entries);
let keypair = Arc::new(Keypair::new());
let shredder = Shredder::new(slot, 0, RECOMMENDED_FEC_RATE, keypair, 0, 0)
.expect("Expected to create a new shredder");
let coding_shreds = shredder.data_shreds_to_coding_shreds(&data_shreds[0..]);
(
data_shreds.clone(),
coding_shreds.clone(),
data_shreds
.into_iter()
.map(|s| (stakes.clone(), Arc::new(vec![s])))
.collect(),
coding_shreds
.into_iter()
.map(|s| (stakes.clone(), Arc::new(vec![s])))
.collect(),
)
}
fn check_all_shreds_received(
transmit_receiver: &TransmitReceiver,
mut data_index: u64,
mut coding_index: u64,
num_expected_data_shreds: u64,
num_expected_coding_shreds: u64,
) {
while let Ok((new_retransmit_slots, _)) = transmit_receiver.try_recv() {
if new_retransmit_slots.1[0].is_data() {
for data_shred in new_retransmit_slots.1.iter() {
assert_eq!(data_shred.index() as u64, data_index);
data_index += 1;
}
} else {
assert_eq!(new_retransmit_slots.1[0].index() as u64, coding_index);
for coding_shred in new_retransmit_slots.1.iter() {
assert_eq!(coding_shred.index() as u64, coding_index);
coding_index += 1;
}
}
}
assert_eq!(num_expected_data_shreds, data_index);
assert_eq!(num_expected_coding_shreds, coding_index);
}
#[test]
fn test_num_live_peers() {
let mut ci = ContactInfo::default();
ci.wallclock = std::u64::MAX;
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
let ledger_path = get_tmp_ledger_path!();
let blockstore = Arc::new(Blockstore::open(&ledger_path).unwrap());
let (transmit_sender, transmit_receiver) = channel();
let (retransmit_slots_sender, retransmit_slots_receiver) = unbounded();
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(100_000);
let bank0 = Arc::new(Bank::new(&genesis_config));
// Make some shreds
let updated_slot = 0;
let (all_data_shreds, all_coding_shreds, _, _all_coding_transmit_shreds) =
make_transmit_shreds(updated_slot, 10, None);
let num_data_shreds = all_data_shreds.len();
let num_coding_shreds = all_coding_shreds.len();
assert!(num_data_shreds >= 10);
// Insert all the shreds
blockstore
.insert_shreds(all_data_shreds, None, true)
.unwrap();
blockstore
.insert_shreds(all_coding_shreds, None, true)
.unwrap();
// Insert duplicate retransmit signal, blocks should
// only be retransmitted once
retransmit_slots_sender
.send(vec![(updated_slot, bank0.clone())].into_iter().collect())
.unwrap();
retransmit_slots_sender
.send(vec![(updated_slot, bank0.clone())].into_iter().collect())
.unwrap();
BroadcastStage::check_retransmit_signals(
&blockstore,
&retransmit_slots_receiver,
&transmit_sender,
)
.unwrap();
// Check all the data shreds were received only once
check_all_shreds_received(
&transmit_receiver,
0,
0,
num_data_shreds as u64,
num_coding_shreds as u64,
);
}
use solana_sdk::hash::Hash;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{Keypair, Signer};
use std::path::Path;
use std::sync::atomic::AtomicBool;
use std::sync::mpsc::channel;
use std::sync::{Arc, RwLock};
use std::thread::sleep;
use std::time::Duration;
struct MockBroadcastStage {
blockstore: Arc<Blockstore>,
@ -584,7 +277,6 @@ pub mod test {
leader_pubkey: &Pubkey,
ledger_path: &Path,
entry_receiver: Receiver<WorkingBankEntry>,
retransmit_slots_receiver: RetransmitSlotsReceiver,
) -> MockBroadcastStage {
// Make the database ledger
let blockstore = Arc::new(Blockstore::open(ledger_path).unwrap());
@ -597,22 +289,21 @@ pub mod test {
let broadcast_buddy = Node::new_localhost_with_pubkey(&buddy_keypair.pubkey());
// Fill the cluster_info with the buddy's info
let cluster_info = ClusterInfo::new_with_invalid_keypair(leader_info.info.clone());
let mut cluster_info = ClusterInfo::new_with_invalid_keypair(leader_info.info.clone());
cluster_info.insert_info(broadcast_buddy.info);
let cluster_info = Arc::new(cluster_info);
let cluster_info = Arc::new(RwLock::new(cluster_info));
let exit_sender = Arc::new(AtomicBool::new(false));
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
let bank = Arc::new(Bank::new(&genesis_config));
let leader_keypair = cluster_info.keypair.clone();
let leader_keypair = cluster_info.read().unwrap().keypair.clone();
// Start up the broadcast stage
let broadcast_service = BroadcastStage::new(
leader_info.sockets.broadcast,
cluster_info,
entry_receiver,
retransmit_slots_receiver,
&exit_sender,
&blockstore,
StandardBroadcastRun::new(leader_keypair, 0),
@ -635,12 +326,10 @@ pub mod test {
let leader_keypair = Keypair::new();
let (entry_sender, entry_receiver) = channel();
let (retransmit_slots_sender, retransmit_slots_receiver) = unbounded();
let broadcast_service = setup_dummy_broadcast_service(
&leader_keypair.pubkey(),
&ledger_path,
entry_receiver,
retransmit_slots_receiver,
);
let start_tick_height;
let max_tick_height;
@ -659,7 +348,6 @@ pub mod test {
.expect("Expect successful send to broadcast service");
}
}
sleep(Duration::from_millis(2000));
trace!(
@ -676,7 +364,6 @@ pub mod test {
assert_eq!(entries.len(), max_tick_height as usize);
drop(entry_sender);
drop(retransmit_slots_sender);
broadcast_service
.broadcast_service
.join()

View File

@ -28,8 +28,8 @@ impl BroadcastRun for BroadcastFakeShredsRun {
&mut self,
blockstore: &Arc<Blockstore>,
receiver: &Receiver<WorkingBankEntry>,
socket_sender: &Sender<(TransmitShreds, Option<BroadcastShredBatchInfo>)>,
blockstore_sender: &Sender<(Arc<Vec<Shred>>, Option<BroadcastShredBatchInfo>)>,
socket_sender: &Sender<TransmitShreds>,
blockstore_sender: &Sender<Arc<Vec<Shred>>>,
) -> Result<()> {
// 1) Pull entries from banking stage
let receive_results = broadcast_utils::recv_slot_entries(receiver)?;
@ -83,32 +83,26 @@ impl BroadcastRun for BroadcastFakeShredsRun {
}
let data_shreds = Arc::new(data_shreds);
blockstore_sender.send((data_shreds.clone(), None))?;
blockstore_sender.send(data_shreds.clone())?;
// 3) Start broadcast step
//some indicates fake shreds
socket_sender.send((
(Some(Arc::new(HashMap::new())), Arc::new(fake_data_shreds)),
None,
))?;
socket_sender.send((
(Some(Arc::new(HashMap::new())), Arc::new(fake_coding_shreds)),
None,
))?;
socket_sender.send((Some(Arc::new(HashMap::new())), Arc::new(fake_data_shreds)))?;
socket_sender.send((Some(Arc::new(HashMap::new())), Arc::new(fake_coding_shreds)))?;
//none indicates real shreds
socket_sender.send(((None, data_shreds), None))?;
socket_sender.send(((None, Arc::new(coding_shreds)), None))?;
socket_sender.send((None, data_shreds))?;
socket_sender.send((None, Arc::new(coding_shreds)))?;
Ok(())
}
fn transmit(
&mut self,
receiver: &Arc<Mutex<TransmitReceiver>>,
cluster_info: &ClusterInfo,
&self,
receiver: &Arc<Mutex<Receiver<TransmitShreds>>>,
cluster_info: &Arc<RwLock<ClusterInfo>>,
sock: &UdpSocket,
) -> Result<()> {
for ((stakes, data_shreds), _) in receiver.lock().unwrap().iter() {
let peers = cluster_info.tvu_peers();
for (stakes, data_shreds) in receiver.lock().unwrap().iter() {
let peers = cluster_info.read().unwrap().tvu_peers();
peers.iter().enumerate().for_each(|(i, peer)| {
if i <= self.partition && stakes.is_some() {
// Send fake shreds to the first N peers
@ -125,11 +119,11 @@ impl BroadcastRun for BroadcastFakeShredsRun {
Ok(())
}
fn record(
&mut self,
receiver: &Arc<Mutex<RecordReceiver>>,
&self,
receiver: &Arc<Mutex<Receiver<Arc<Vec<Shred>>>>>,
blockstore: &Arc<Blockstore>,
) -> Result<()> {
for (data_shreds, _) in receiver.lock().unwrap().iter() {
for data_shreds in receiver.lock().unwrap().iter() {
blockstore.insert_shreds(data_shreds.to_vec(), None, true)?;
}
Ok(())
@ -145,7 +139,7 @@ mod tests {
#[test]
fn test_tvu_peers_ordering() {
let cluster = ClusterInfo::new_with_invalid_keypair(ContactInfo::new_localhost(
let mut cluster = ClusterInfo::new_with_invalid_keypair(ContactInfo::new_localhost(
&Pubkey::new_rand(),
0,
));

View File

@ -1,296 +0,0 @@
use super::*;
pub(crate) trait BroadcastStats {
fn update(&mut self, new_stats: &Self);
fn report_stats(&mut self, slot: Slot, slot_start: Instant);
}
#[derive(Clone)]
pub(crate) struct BroadcastShredBatchInfo {
pub(crate) slot: Slot,
pub(crate) num_expected_batches: Option<usize>,
pub(crate) slot_start_ts: Instant,
}
#[derive(Default, Clone)]
pub(crate) struct ProcessShredsStats {
// Per-slot elapsed time
pub(crate) shredding_elapsed: u64,
pub(crate) receive_elapsed: u64,
}
impl ProcessShredsStats {
pub(crate) fn update(&mut self, new_stats: &ProcessShredsStats) {
self.shredding_elapsed += new_stats.shredding_elapsed;
self.receive_elapsed += new_stats.receive_elapsed;
}
pub(crate) fn reset(&mut self) {
*self = Self::default();
}
}
#[derive(Default, Clone)]
pub struct TransmitShredsStats {
pub transmit_elapsed: u64,
pub send_mmsg_elapsed: u64,
pub get_peers_elapsed: u64,
pub shred_select: u64,
pub num_shreds: usize,
}
impl BroadcastStats for TransmitShredsStats {
fn update(&mut self, new_stats: &TransmitShredsStats) {
self.transmit_elapsed += new_stats.transmit_elapsed;
self.send_mmsg_elapsed += new_stats.send_mmsg_elapsed;
self.get_peers_elapsed += new_stats.get_peers_elapsed;
self.num_shreds += new_stats.num_shreds;
self.shred_select += new_stats.shred_select;
}
fn report_stats(&mut self, slot: Slot, slot_start: Instant) {
datapoint_info!(
"broadcast-transmit-shreds-stats",
("slot", slot as i64, i64),
(
"end_to_end_elapsed",
// `slot_start` signals when the first batch of shreds was
// received, used to measure duration of broadcast
slot_start.elapsed().as_micros() as i64,
i64
),
("transmit_elapsed", self.transmit_elapsed as i64, i64),
("send_mmsg_elapsed", self.send_mmsg_elapsed as i64, i64),
("get_peers_elapsed", self.get_peers_elapsed as i64, i64),
("num_shreds", self.num_shreds as i64, i64),
("shred_select", self.shred_select as i64, i64),
);
}
}
#[derive(Default, Clone)]
pub(crate) struct InsertShredsStats {
pub(crate) insert_shreds_elapsed: u64,
pub(crate) num_shreds: usize,
}
impl BroadcastStats for InsertShredsStats {
fn update(&mut self, new_stats: &InsertShredsStats) {
self.insert_shreds_elapsed += new_stats.insert_shreds_elapsed;
self.num_shreds += new_stats.num_shreds;
}
fn report_stats(&mut self, slot: Slot, slot_start: Instant) {
datapoint_info!(
"broadcast-insert-shreds-stats",
("slot", slot as i64, i64),
(
"end_to_end_elapsed",
// `slot_start` signals when the first batch of shreds was
// received, used to measure duration of broadcast
slot_start.elapsed().as_micros() as i64,
i64
),
(
"insert_shreds_elapsed",
self.insert_shreds_elapsed as i64,
i64
),
("num_shreds", self.num_shreds as i64, i64),
);
}
}
// Tracks metrics of type `T` acrosss multiple threads
#[derive(Default)]
pub(crate) struct BatchCounter<T: BroadcastStats + Default> {
// The number of batches processed across all threads so far
num_batches: usize,
// Filled in when the last batch of shreds is received,
// signals how many batches of shreds to expect
num_expected_batches: Option<usize>,
broadcast_shred_stats: T,
}
impl<T: BroadcastStats + Default> BatchCounter<T> {
#[cfg(test)]
pub(crate) fn num_batches(&self) -> usize {
self.num_batches
}
}
#[derive(Default)]
pub(crate) struct SlotBroadcastStats<T: BroadcastStats + Default>(HashMap<Slot, BatchCounter<T>>);
impl<T: BroadcastStats + Default> SlotBroadcastStats<T> {
#[cfg(test)]
pub(crate) fn get(&self, slot: Slot) -> Option<&BatchCounter<T>> {
self.0.get(&slot)
}
pub(crate) fn update(&mut self, new_stats: &T, batch_info: &Option<BroadcastShredBatchInfo>) {
if let Some(batch_info) = batch_info {
let mut should_delete = false;
{
let slot_batch_counter = self.0.entry(batch_info.slot).or_default();
slot_batch_counter.broadcast_shred_stats.update(new_stats);
// Only count the ones where `broadcast_shred_batch_info`.is_some(), because
// there could potentially be other `retransmit` slots inserted into the
// transmit pipeline (signaled by ReplayStage) that are not created by the
// main shredding/broadcast pipeline
slot_batch_counter.num_batches += 1;
if let Some(num_expected_batches) = batch_info.num_expected_batches {
slot_batch_counter.num_expected_batches = Some(num_expected_batches);
}
if let Some(num_expected_batches) = slot_batch_counter.num_expected_batches {
if slot_batch_counter.num_batches == num_expected_batches {
slot_batch_counter
.broadcast_shred_stats
.report_stats(batch_info.slot, batch_info.slot_start_ts);
should_delete = true;
}
}
}
if should_delete {
self.0
.remove(&batch_info.slot)
.expect("delete should be successful");
}
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[derive(Default)]
struct TestStats {
sender: Option<Sender<(usize, Slot, Instant)>>,
count: usize,
}
impl BroadcastStats for TestStats {
fn update(&mut self, new_stats: &TestStats) {
self.count += new_stats.count;
self.sender = new_stats.sender.clone();
}
fn report_stats(&mut self, slot: Slot, slot_start: Instant) {
self.sender
.as_ref()
.unwrap()
.send((self.count, slot, slot_start))
.unwrap()
}
}
#[test]
fn test_update_broadcast() {
let start = Instant::now();
let mut slot_broadcast_stats = SlotBroadcastStats::default();
slot_broadcast_stats.update(
&TransmitShredsStats {
transmit_elapsed: 1,
get_peers_elapsed: 2,
send_mmsg_elapsed: 3,
shred_select: 4,
num_shreds: 5,
},
&Some(BroadcastShredBatchInfo {
slot: 0,
num_expected_batches: Some(2),
slot_start_ts: start.clone(),
}),
);
// Singular update
let slot_0_stats = slot_broadcast_stats.0.get(&0).unwrap();
assert_eq!(slot_0_stats.num_batches, 1);
assert_eq!(slot_0_stats.num_expected_batches.unwrap(), 2);
assert_eq!(slot_0_stats.broadcast_shred_stats.transmit_elapsed, 1);
assert_eq!(slot_0_stats.broadcast_shred_stats.get_peers_elapsed, 2);
assert_eq!(slot_0_stats.broadcast_shred_stats.send_mmsg_elapsed, 3);
assert_eq!(slot_0_stats.broadcast_shred_stats.shred_select, 4);
assert_eq!(slot_0_stats.broadcast_shred_stats.num_shreds, 5);
slot_broadcast_stats.update(
&TransmitShredsStats {
transmit_elapsed: 7,
get_peers_elapsed: 8,
send_mmsg_elapsed: 9,
shred_select: 10,
num_shreds: 11,
},
&None,
);
// If BroadcastShredBatchInfo == None, then update should be ignored
let slot_0_stats = slot_broadcast_stats.0.get(&0).unwrap();
assert_eq!(slot_0_stats.num_batches, 1);
assert_eq!(slot_0_stats.num_expected_batches.unwrap(), 2);
assert_eq!(slot_0_stats.broadcast_shred_stats.transmit_elapsed, 1);
assert_eq!(slot_0_stats.broadcast_shred_stats.get_peers_elapsed, 2);
assert_eq!(slot_0_stats.broadcast_shred_stats.send_mmsg_elapsed, 3);
assert_eq!(slot_0_stats.broadcast_shred_stats.shred_select, 4);
assert_eq!(slot_0_stats.broadcast_shred_stats.num_shreds, 5);
// If another batch is given, then total number of batches == num_expected_batches == 2,
// so the batch should be purged from the HashMap
slot_broadcast_stats.update(
&TransmitShredsStats {
transmit_elapsed: 1,
get_peers_elapsed: 1,
send_mmsg_elapsed: 1,
shred_select: 1,
num_shreds: 1,
},
&Some(BroadcastShredBatchInfo {
slot: 0,
num_expected_batches: None,
slot_start_ts: start.clone(),
}),
);
assert!(slot_broadcast_stats.0.get(&0).is_none());
}
#[test]
fn test_update_multi_threaded() {
for round in 0..50 {
let start = Instant::now();
let slot_broadcast_stats = Arc::new(Mutex::new(SlotBroadcastStats::default()));
let num_threads = 5;
let slot = 0;
let (sender, receiver) = channel();
let thread_handles: Vec<_> = (0..num_threads)
.into_iter()
.map(|i| {
let slot_broadcast_stats = slot_broadcast_stats.clone();
let sender = Some(sender.clone());
let test_stats = TestStats { sender, count: 1 };
let mut broadcast_batch_info = BroadcastShredBatchInfo {
slot,
num_expected_batches: None,
slot_start_ts: start.clone(),
};
if i == round % num_threads {
broadcast_batch_info.num_expected_batches = Some(num_threads);
}
Builder::new()
.name("test_update_multi_threaded".to_string())
.spawn(move || {
slot_broadcast_stats
.lock()
.unwrap()
.update(&test_stats, &Some(broadcast_batch_info))
})
.unwrap()
})
.collect();
for t in thread_handles {
t.join().unwrap();
}
assert!(slot_broadcast_stats.lock().unwrap().0.get(&slot).is_none());
let (returned_count, returned_slot, returned_instant) = receiver.recv().unwrap();
assert_eq!(returned_count, num_threads);
assert_eq!(returned_slot, slot);
assert_eq!(returned_instant, returned_instant);
}
}
}

View File

@ -76,7 +76,7 @@ pub(super) fn recv_slot_entries(receiver: &Receiver<WorkingBankEntry>) -> Result
#[cfg(test)]
mod tests {
use super::*;
use solana_ledger::genesis_utils::{create_genesis_config, GenesisConfigInfo};
use crate::genesis_utils::{create_genesis_config, GenesisConfigInfo};
use solana_sdk::genesis_config::GenesisConfig;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::system_transaction;

View File

@ -23,8 +23,8 @@ impl BroadcastRun for FailEntryVerificationBroadcastRun {
&mut self,
blockstore: &Arc<Blockstore>,
receiver: &Receiver<WorkingBankEntry>,
socket_sender: &Sender<(TransmitShreds, Option<BroadcastShredBatchInfo>)>,
blockstore_sender: &Sender<(Arc<Vec<Shred>>, Option<BroadcastShredBatchInfo>)>,
socket_sender: &Sender<TransmitShreds>,
blockstore_sender: &Sender<Arc<Vec<Shred>>>,
) -> Result<()> {
// 1) Pull entries from banking stage
let mut receive_results = broadcast_utils::recv_slot_entries(receiver)?;
@ -61,43 +61,38 @@ impl BroadcastRun for FailEntryVerificationBroadcastRun {
);
let data_shreds = Arc::new(data_shreds);
blockstore_sender.send((data_shreds.clone(), None))?;
blockstore_sender.send(data_shreds.clone())?;
// 3) Start broadcast step
let bank_epoch = bank.get_leader_schedule_epoch(bank.slot());
let stakes = staking_utils::staked_nodes_at_epoch(&bank, bank_epoch);
let stakes = stakes.map(Arc::new);
socket_sender.send(((stakes.clone(), data_shreds), None))?;
socket_sender.send(((stakes, Arc::new(coding_shreds)), None))?;
socket_sender.send((stakes.clone(), data_shreds))?;
socket_sender.send((stakes, Arc::new(coding_shreds)))?;
Ok(())
}
fn transmit(
&mut self,
receiver: &Arc<Mutex<TransmitReceiver>>,
cluster_info: &ClusterInfo,
&self,
receiver: &Arc<Mutex<Receiver<TransmitShreds>>>,
cluster_info: &Arc<RwLock<ClusterInfo>>,
sock: &UdpSocket,
) -> Result<()> {
let ((stakes, shreds), _) = receiver.lock().unwrap().recv()?;
let (stakes, shreds) = receiver.lock().unwrap().recv()?;
let all_seeds: Vec<[u8; 32]> = shreds.iter().map(|s| s.seed()).collect();
// Broadcast data
let (peers, peers_and_stakes) = get_broadcast_peers(cluster_info, stakes);
broadcast_shreds(
sock,
&shreds,
&peers_and_stakes,
&peers,
&Arc::new(AtomicU64::new(0)),
&mut TransmitShredsStats::default(),
)?;
let all_shred_bufs: Vec<Vec<u8>> = shreds.to_vec().into_iter().map(|s| s.payload).collect();
cluster_info
.write()
.unwrap()
.broadcast_shreds(sock, all_shred_bufs, &all_seeds, stakes)?;
Ok(())
}
fn record(
&mut self,
receiver: &Arc<Mutex<RecordReceiver>>,
&self,
receiver: &Arc<Mutex<Receiver<Arc<Vec<Shred>>>>>,
blockstore: &Arc<Blockstore>,
) -> Result<()> {
let (all_shreds, _) = receiver.lock().unwrap().recv()?;
let all_shreds = receiver.lock().unwrap().recv()?;
blockstore
.insert_shreds(all_shreds.to_vec(), None, true)
.expect("Failed to insert shreds in blockstore");

View File

@ -1,54 +1,53 @@
use super::{
broadcast_utils::{self, ReceiveResults},
*,
};
use super::broadcast_utils::{self, ReceiveResults};
use super::*;
use crate::broadcast_stage::broadcast_utils::UnfinishedSlotInfo;
use solana_ledger::{
entry::Entry,
shred::{Shred, Shredder, RECOMMENDED_FEC_RATE, SHRED_TICK_REFERENCE_MASK},
};
use solana_sdk::{pubkey::Pubkey, signature::Keypair, timing::duration_as_us};
use solana_ledger::entry::Entry;
use solana_ledger::shred::{Shred, Shredder, RECOMMENDED_FEC_RATE, SHRED_TICK_REFERENCE_MASK};
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::Keypair;
use solana_sdk::timing::duration_as_us;
use std::collections::HashMap;
use std::sync::RwLock;
use std::time::Duration;
#[derive(Default)]
struct BroadcastStats {
// Per-slot elapsed time
shredding_elapsed: u64,
insert_shreds_elapsed: u64,
broadcast_elapsed: u64,
receive_elapsed: u64,
seed_elapsed: u64,
}
impl BroadcastStats {
fn reset(&mut self) {
self.insert_shreds_elapsed = 0;
self.shredding_elapsed = 0;
self.broadcast_elapsed = 0;
self.receive_elapsed = 0;
self.seed_elapsed = 0;
}
}
#[derive(Clone)]
pub struct StandardBroadcastRun {
process_shreds_stats: ProcessShredsStats,
transmit_shreds_stats: Arc<Mutex<SlotBroadcastStats<TransmitShredsStats>>>,
insert_shreds_stats: Arc<Mutex<SlotBroadcastStats<InsertShredsStats>>>,
pub(super) struct StandardBroadcastRun {
stats: Arc<RwLock<BroadcastStats>>,
unfinished_slot: Option<UnfinishedSlotInfo>,
current_slot_and_parent: Option<(u64, u64)>,
slot_broadcast_start: Option<Instant>,
keypair: Arc<Keypair>,
shred_version: u16,
last_datapoint_submit: Arc<AtomicU64>,
num_batches: usize,
broadcast_peer_cache: Arc<RwLock<BroadcastPeerCache>>,
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 {
process_shreds_stats: ProcessShredsStats::default(),
transmit_shreds_stats: Arc::new(Mutex::new(SlotBroadcastStats::default())),
insert_shreds_stats: Arc::new(Mutex::new(SlotBroadcastStats::default())),
stats: Arc::new(RwLock::new(BroadcastStats::default())),
unfinished_slot: None,
current_slot_and_parent: None,
slot_broadcast_start: None,
keypair,
shred_version,
last_datapoint_submit: Arc::new(AtomicU64::new(0)),
num_batches: 0,
broadcast_peer_cache: Arc::new(RwLock::new(BroadcastPeerCache::default())),
last_peer_update: Arc::new(AtomicU64::new(0)),
}
}
@ -131,7 +130,7 @@ impl StandardBroadcastRun {
#[cfg(test)]
fn test_process_receive_results(
&mut self,
cluster_info: &ClusterInfo,
cluster_info: &Arc<RwLock<ClusterInfo>>,
sock: &UdpSocket,
blockstore: &Arc<Blockstore>,
receive_results: ReceiveResults,
@ -143,7 +142,6 @@ impl StandardBroadcastRun {
let brecv = Arc::new(Mutex::new(brecv));
//data
let _ = self.transmit(&srecv, cluster_info, sock);
let _ = self.record(&brecv, blockstore);
//coding
let _ = self.transmit(&srecv, cluster_info, sock);
let _ = self.record(&brecv, blockstore);
@ -153,8 +151,8 @@ impl StandardBroadcastRun {
fn process_receive_results(
&mut self,
blockstore: &Arc<Blockstore>,
socket_sender: &Sender<(TransmitShreds, Option<BroadcastShredBatchInfo>)>,
blockstore_sender: &Sender<(Arc<Vec<Shred>>, Option<BroadcastShredBatchInfo>)>,
socket_sender: &Sender<TransmitShreds>,
blockstore_sender: &Sender<Arc<Vec<Shred>>>,
receive_results: ReceiveResults,
) -> Result<()> {
let mut receive_elapsed = receive_results.time_elapsed;
@ -162,13 +160,11 @@ impl StandardBroadcastRun {
let bank = receive_results.bank.clone();
let last_tick_height = receive_results.last_tick_height;
inc_new_counter_info!("broadcast_service-entries_received", num_entries);
let old_broadcast_start = self.slot_broadcast_start;
let old_num_batches = self.num_batches;
if self.current_slot_and_parent.is_none()
|| bank.slot() != self.current_slot_and_parent.unwrap().0
{
self.slot_broadcast_start = Some(Instant::now());
self.num_batches = 0;
let slot = bank.slot();
let parent_slot = bank.parent_slot();
@ -183,19 +179,19 @@ impl StandardBroadcastRun {
self.check_for_interrupted_slot(bank.ticks_per_slot() as u8);
// 2) Convert entries to shreds and coding shreds
let (shredder, next_shred_index) = self.init_shredder(
blockstore,
(bank.tick_height() % bank.ticks_per_slot()) as u8,
);
let is_last_in_slot = last_tick_height == bank.max_tick_height();
let data_shreds = self.entries_to_data_shreds(
let mut data_shreds = self.entries_to_data_shreds(
&shredder,
next_shred_index,
&receive_results.entries,
is_last_in_slot,
last_tick_height == bank.max_tick_height(),
);
// Insert the first shred so blockstore stores that the leader started this block
// This must be done before the blocks are sent out over the wire.
//Insert the first shred so blockstore stores that the leader started this block
//This must be done before the blocks are sent out over the wire.
if !data_shreds.is_empty() && data_shreds[0].index() == 0 {
let first = vec![data_shreds[0].clone()];
blockstore
@ -203,56 +199,26 @@ impl StandardBroadcastRun {
.expect("Failed to insert shreds in blockstore");
}
let last_data_shred = data_shreds.len();
if let Some(last_shred) = last_unfinished_slot_shred {
data_shreds.push(last_shred);
}
let to_shreds_elapsed = to_shreds_start.elapsed();
let bank_epoch = bank.get_leader_schedule_epoch(bank.slot());
let stakes = staking_utils::staked_nodes_at_epoch(&bank, bank_epoch);
let stakes = stakes.map(Arc::new);
// Broadcast the last shred of the interrupted slot if necessary
if let Some(last_shred) = last_unfinished_slot_shred {
let batch_info = Some(BroadcastShredBatchInfo {
slot: last_shred.slot(),
num_expected_batches: Some(old_num_batches + 1),
slot_start_ts: old_broadcast_start.expect(
"Old broadcast start time for previous slot must exist if the previous slot
was interrupted",
),
});
let last_shred = Arc::new(vec![last_shred]);
socket_sender.send(((stakes.clone(), last_shred.clone()), batch_info.clone()))?;
blockstore_sender.send((last_shred, batch_info))?;
}
// Increment by two batches, one for the data batch, one for the coding batch.
self.num_batches += 2;
let num_expected_batches = {
if is_last_in_slot {
Some(self.num_batches)
} else {
None
}
};
let batch_info = Some(BroadcastShredBatchInfo {
slot: bank.slot(),
num_expected_batches,
slot_start_ts: self
.slot_broadcast_start
.clone()
.expect("Start timestamp must exist for a slot if we're broadcasting the slot"),
});
let data_shreds = Arc::new(data_shreds);
socket_sender.send(((stakes.clone(), data_shreds.clone()), batch_info.clone()))?;
blockstore_sender.send((data_shreds.clone(), batch_info.clone()))?;
socket_sender.send((stakes.clone(), data_shreds.clone()))?;
blockstore_sender.send(data_shreds.clone())?;
let coding_shreds = shredder.data_shreds_to_coding_shreds(&data_shreds[0..last_data_shred]);
let coding_shreds = Arc::new(coding_shreds);
socket_sender.send(((stakes, coding_shreds.clone()), batch_info.clone()))?;
blockstore_sender.send((coding_shreds, batch_info))?;
self.process_shreds_stats.update(&ProcessShredsStats {
socket_sender.send((stakes, coding_shreds))?;
self.update_broadcast_stats(BroadcastStats {
shredding_elapsed: duration_as_us(&to_shreds_elapsed),
receive_elapsed: duration_as_us(&receive_elapsed),
..BroadcastStats::default()
});
if last_tick_height == bank.max_tick_height() {
self.report_and_reset_stats();
self.unfinished_slot = None;
@ -261,15 +227,10 @@ impl StandardBroadcastRun {
Ok(())
}
fn insert(
&mut self,
blockstore: &Arc<Blockstore>,
shreds: Arc<Vec<Shred>>,
broadcast_shred_batch_info: Option<BroadcastShredBatchInfo>,
) -> Result<()> {
fn insert(&self, blockstore: &Arc<Blockstore>, shreds: Arc<Vec<Shred>>) -> Result<()> {
// Insert shreds into blockstore
let insert_shreds_start = Instant::now();
// The first shred is inserted synchronously
//The first shred is inserted synchronously
let data_shreds = if !shreds.is_empty() && shreds[0].index() == 0 {
shreds[1..].to_vec()
} else {
@ -279,93 +240,66 @@ impl StandardBroadcastRun {
.insert_shreds(data_shreds, None, true)
.expect("Failed to insert shreds in blockstore");
let insert_shreds_elapsed = insert_shreds_start.elapsed();
let new_insert_shreds_stats = InsertShredsStats {
self.update_broadcast_stats(BroadcastStats {
insert_shreds_elapsed: duration_as_us(&insert_shreds_elapsed),
num_shreds: shreds.len(),
};
self.update_insertion_metrics(&new_insert_shreds_stats, &broadcast_shred_batch_info);
..BroadcastStats::default()
});
Ok(())
}
fn update_insertion_metrics(
&mut self,
new_insertion_shreds_stats: &InsertShredsStats,
broadcast_shred_batch_info: &Option<BroadcastShredBatchInfo>,
) {
let mut insert_shreds_stats = self.insert_shreds_stats.lock().unwrap();
insert_shreds_stats.update(new_insertion_shreds_stats, broadcast_shred_batch_info);
}
fn broadcast(
&mut self,
&self,
sock: &UdpSocket,
cluster_info: &ClusterInfo,
cluster_info: &Arc<RwLock<ClusterInfo>>,
stakes: Option<Arc<HashMap<Pubkey, u64>>>,
shreds: Arc<Vec<Shred>>,
broadcast_shred_batch_info: Option<BroadcastShredBatchInfo>,
) -> Result<()> {
const BROADCAST_PEER_UPDATE_INTERVAL_MS: u64 = 1000;
trace!("Broadcasting {:?} shreds", shreds.len());
// Get the list of peers to broadcast to
let mut get_peers_time = Measure::start("broadcast::get_peers");
let now = timestamp();
let last = self.last_peer_update.load(Ordering::Relaxed);
if now - last > BROADCAST_PEER_UPDATE_INTERVAL_MS
&& self
.last_peer_update
.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;
}
get_peers_time.stop();
let r_broadcast_peer_cache = self.broadcast_peer_cache.read().unwrap();
let seed_start = Instant::now();
let seeds: Vec<[u8; 32]> = shreds.iter().map(|s| s.seed()).collect();
let seed_elapsed = seed_start.elapsed();
let mut transmit_stats = TransmitShredsStats::default();
// Broadcast the shreds
let mut transmit_time = Measure::start("broadcast_shreds");
broadcast_shreds(
sock,
&shreds,
&r_broadcast_peer_cache.peers_and_stakes,
&r_broadcast_peer_cache.peers,
&self.last_datapoint_submit,
&mut transmit_stats,
)?;
drop(r_broadcast_peer_cache);
transmit_time.stop();
let broadcast_start = Instant::now();
let shred_bufs: Vec<Vec<u8>> = shreds.to_vec().into_iter().map(|s| s.payload).collect();
trace!("Broadcasting {:?} shreds", shred_bufs.len());
transmit_stats.transmit_elapsed = transmit_time.as_us();
transmit_stats.get_peers_elapsed = get_peers_time.as_us();
transmit_stats.num_shreds = shreds.len();
cluster_info
.write()
.unwrap()
.broadcast_shreds(sock, shred_bufs, &seeds, stakes)?;
// Process metrics
self.update_transmit_metrics(&transmit_stats, &broadcast_shred_batch_info);
let broadcast_elapsed = broadcast_start.elapsed();
self.update_broadcast_stats(BroadcastStats {
broadcast_elapsed: duration_as_us(&broadcast_elapsed),
seed_elapsed: duration_as_us(&seed_elapsed),
..BroadcastStats::default()
});
Ok(())
}
fn update_transmit_metrics(
&mut self,
new_transmit_shreds_stats: &TransmitShredsStats,
broadcast_shred_batch_info: &Option<BroadcastShredBatchInfo>,
) {
let mut transmit_shreds_stats = self.transmit_shreds_stats.lock().unwrap();
transmit_shreds_stats.update(new_transmit_shreds_stats, broadcast_shred_batch_info);
fn update_broadcast_stats(&self, stats: BroadcastStats) {
let mut wstats = self.stats.write().unwrap();
wstats.receive_elapsed += stats.receive_elapsed;
wstats.shredding_elapsed += stats.shredding_elapsed;
wstats.insert_shreds_elapsed += stats.insert_shreds_elapsed;
wstats.broadcast_elapsed += stats.broadcast_elapsed;
wstats.seed_elapsed += stats.seed_elapsed;
}
fn report_and_reset_stats(&mut self) {
let stats = &self.process_shreds_stats;
let stats = self.stats.read().unwrap();
assert!(self.unfinished_slot.is_some());
datapoint_info!(
"broadcast-process-shreds-stats",
"broadcast-bank-stats",
("slot", self.unfinished_slot.unwrap().slot as i64, i64),
("shredding_time", stats.shredding_elapsed as i64, i64),
("insertion_time", stats.insert_shreds_elapsed as i64, i64),
("broadcast_time", stats.broadcast_elapsed as i64, i64),
("receive_time", stats.receive_elapsed as i64, i64),
("seed", stats.seed_elapsed as i64, i64),
(
"num_data_shreds",
"num_shreds",
i64::from(self.unfinished_slot.unwrap().next_shred_index),
i64
),
@ -375,7 +309,8 @@ impl StandardBroadcastRun {
i64
),
);
self.process_shreds_stats.reset();
drop(stats);
self.stats.write().unwrap().reset();
}
}
@ -384,8 +319,8 @@ impl BroadcastRun for StandardBroadcastRun {
&mut self,
blockstore: &Arc<Blockstore>,
receiver: &Receiver<WorkingBankEntry>,
socket_sender: &Sender<(TransmitShreds, Option<BroadcastShredBatchInfo>)>,
blockstore_sender: &Sender<(Arc<Vec<Shred>>, Option<BroadcastShredBatchInfo>)>,
socket_sender: &Sender<TransmitShreds>,
blockstore_sender: &Sender<Arc<Vec<Shred>>>,
) -> Result<()> {
let receive_results = broadcast_utils::recv_slot_entries(receiver)?;
self.process_receive_results(
@ -396,21 +331,21 @@ impl BroadcastRun for StandardBroadcastRun {
)
}
fn transmit(
&mut self,
receiver: &Arc<Mutex<TransmitReceiver>>,
cluster_info: &ClusterInfo,
&self,
receiver: &Arc<Mutex<Receiver<TransmitShreds>>>,
cluster_info: &Arc<RwLock<ClusterInfo>>,
sock: &UdpSocket,
) -> Result<()> {
let ((stakes, shreds), slot_start_ts) = receiver.lock().unwrap().recv()?;
self.broadcast(sock, cluster_info, stakes, shreds, slot_start_ts)
let (stakes, shreds) = receiver.lock().unwrap().recv()?;
self.broadcast(sock, cluster_info, stakes, shreds)
}
fn record(
&mut self,
receiver: &Arc<Mutex<RecordReceiver>>,
&self,
receiver: &Arc<Mutex<Receiver<Arc<Vec<Shred>>>>>,
blockstore: &Arc<Blockstore>,
) -> Result<()> {
let (shreds, slot_start_ts) = receiver.lock().unwrap().recv()?;
self.insert(blockstore, shreds, slot_start_ts)
let shreds = receiver.lock().unwrap().recv()?;
self.insert(blockstore, shreds)
}
}
@ -418,17 +353,18 @@ impl BroadcastRun for StandardBroadcastRun {
mod test {
use super::*;
use crate::cluster_info::{ClusterInfo, Node};
use solana_ledger::genesis_utils::create_genesis_config;
use crate::genesis_utils::create_genesis_config;
use solana_ledger::{
blockstore::Blockstore, entry::create_ticks, get_tmp_ledger_path,
shred::max_ticks_per_n_shreds,
};
use solana_runtime::bank::Bank;
use solana_sdk::{
clock::Slot,
genesis_config::GenesisConfig,
signature::{Keypair, Signer},
};
use std::sync::Arc;
use std::sync::{Arc, RwLock};
use std::time::Duration;
fn setup(
@ -436,7 +372,7 @@ mod test {
) -> (
Arc<Blockstore>,
GenesisConfig,
Arc<ClusterInfo>,
Arc<RwLock<ClusterInfo>>,
Arc<Bank>,
Arc<Keypair>,
UdpSocket,
@ -449,12 +385,12 @@ mod test {
let leader_keypair = Arc::new(Keypair::new());
let leader_pubkey = leader_keypair.pubkey();
let leader_info = Node::new_localhost_with_pubkey(&leader_pubkey);
let cluster_info = Arc::new(ClusterInfo::new_with_invalid_keypair(
let cluster_info = Arc::new(RwLock::new(ClusterInfo::new_with_invalid_keypair(
leader_info.info.clone(),
));
)));
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
let mut genesis_config = create_genesis_config(10_000).genesis_config;
genesis_config.ticks_per_slot = max_ticks_per_n_shreds(num_shreds_per_slot, None) + 1;
genesis_config.ticks_per_slot = max_ticks_per_n_shreds(num_shreds_per_slot) + 1;
let bank0 = Arc::new(Bank::new(&genesis_config));
(
blockstore,
@ -526,48 +462,29 @@ mod test {
// Make sure the slot is not complete
assert!(!blockstore.is_full(0));
// Modify the stats, should reset later
standard_broadcast_run.process_shreds_stats.receive_elapsed = 10;
// Broadcast stats should exist, and 2 batches should have been sent,
// one for data, one for coding
assert_eq!(
standard_broadcast_run
.transmit_shreds_stats
.lock()
.unwrap()
.get(unfinished_slot.slot)
.unwrap()
.num_batches(),
2
);
assert_eq!(
standard_broadcast_run
.insert_shreds_stats
.lock()
.unwrap()
.get(unfinished_slot.slot)
.unwrap()
.num_batches(),
2
);
standard_broadcast_run
.stats
.write()
.unwrap()
.receive_elapsed = 10;
// Try to fetch ticks from blockstore, nothing should break
assert_eq!(blockstore.get_slot_entries(0, 0).unwrap(), ticks0);
assert_eq!(blockstore.get_slot_entries(0, 0, None).unwrap(), ticks0);
assert_eq!(
blockstore.get_slot_entries(0, num_shreds_per_slot).unwrap(),
blockstore
.get_slot_entries(0, num_shreds_per_slot, None)
.unwrap(),
vec![],
);
// Step 2: Make a transmission for another bank that interrupts the transmission for
// slot 0
let bank2 = Arc::new(Bank::new_from_parent(&bank0, &leader_keypair.pubkey(), 2));
let interrupted_slot = unfinished_slot.slot;
// Interrupting the slot should cause the unfinished_slot and stats to reset
let num_shreds = 1;
assert!(num_shreds < num_shreds_per_slot);
let ticks1 = create_ticks(
max_ticks_per_n_shreds(num_shreds, None),
0,
genesis_config.hash(),
);
let ticks1 = create_ticks(max_ticks_per_n_shreds(num_shreds), 0, genesis_config.hash());
let receive_results = ReceiveResults {
entries: ticks1.clone(),
time_elapsed: Duration::new(2, 0),
@ -587,28 +504,16 @@ mod test {
// Check that the stats were reset as well
assert_eq!(
standard_broadcast_run.process_shreds_stats.receive_elapsed,
standard_broadcast_run.stats.read().unwrap().receive_elapsed,
0
);
// Broadcast stats for interrupted slot should be cleared
assert!(standard_broadcast_run
.transmit_shreds_stats
.lock()
.unwrap()
.get(interrupted_slot)
.is_none());
assert!(standard_broadcast_run
.insert_shreds_stats
.lock()
.unwrap()
.get(interrupted_slot)
.is_none());
// Try to fetch the incomplete ticks from blockstore, should succeed
assert_eq!(blockstore.get_slot_entries(0, 0).unwrap(), ticks0);
assert_eq!(blockstore.get_slot_entries(0, 0, None).unwrap(), ticks0);
assert_eq!(
blockstore.get_slot_entries(0, num_shreds_per_slot).unwrap(),
blockstore
.get_slot_entries(0, num_shreds_per_slot, None)
.unwrap(),
vec![],
);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,343 +0,0 @@
use crate::{
cluster_info::ClusterInfo, contact_info::ContactInfo, epoch_slots::EpochSlots,
serve_repair::RepairType,
};
use solana_ledger::bank_forks::BankForks;
use solana_runtime::epoch_stakes::NodeIdToVoteAccounts;
use solana_sdk::{clock::Slot, pubkey::Pubkey};
use std::{
collections::{HashMap, HashSet},
sync::{Arc, RwLock},
};
pub type SlotPubkeys = HashMap<Arc<Pubkey>, u64>;
pub type ClusterSlotsMap = RwLock<HashMap<Slot, Arc<RwLock<SlotPubkeys>>>>;
#[derive(Default)]
pub struct ClusterSlots {
cluster_slots: ClusterSlotsMap,
keys: RwLock<HashSet<Arc<Pubkey>>>,
since: RwLock<Option<u64>>,
validator_stakes: RwLock<Arc<NodeIdToVoteAccounts>>,
epoch: RwLock<Option<u64>>,
self_id: RwLock<Pubkey>,
}
impl ClusterSlots {
pub fn lookup(&self, slot: Slot) -> Option<Arc<RwLock<SlotPubkeys>>> {
self.cluster_slots.read().unwrap().get(&slot).cloned()
}
pub fn update(&self, root: Slot, cluster_info: &ClusterInfo, bank_forks: &RwLock<BankForks>) {
self.update_peers(cluster_info, bank_forks);
let since = *self.since.read().unwrap();
let epoch_slots = cluster_info.get_epoch_slots_since(since);
self.update_internal(root, epoch_slots);
}
fn update_internal(&self, root: Slot, epoch_slots: (Vec<EpochSlots>, Option<u64>)) {
let (epoch_slots_list, since) = epoch_slots;
for epoch_slots in epoch_slots_list {
let slots = epoch_slots.to_slots(root);
for slot in &slots {
if *slot <= root {
continue;
}
let pubkey = Arc::new(epoch_slots.from);
let exists = self.keys.read().unwrap().get(&pubkey).is_some();
if !exists {
self.keys.write().unwrap().insert(pubkey.clone());
}
let from = self.keys.read().unwrap().get(&pubkey).unwrap().clone();
let balance = self
.validator_stakes
.read()
.unwrap()
.get(&from)
.map(|v| v.total_stake)
.unwrap_or(0);
let mut slot_pubkeys = self.cluster_slots.read().unwrap().get(slot).cloned();
if slot_pubkeys.is_none() {
let new_slot_pubkeys = Arc::new(RwLock::new(HashMap::default()));
self.cluster_slots
.write()
.unwrap()
.insert(*slot, new_slot_pubkeys.clone());
slot_pubkeys = Some(new_slot_pubkeys);
}
slot_pubkeys
.unwrap()
.write()
.unwrap()
.insert(from.clone(), balance);
}
}
self.cluster_slots.write().unwrap().retain(|x, _| *x > root);
self.keys
.write()
.unwrap()
.retain(|x| Arc::strong_count(x) > 1);
*self.since.write().unwrap() = since;
}
pub fn collect(&self, id: &Pubkey) -> HashSet<Slot> {
self.cluster_slots
.read()
.unwrap()
.iter()
.filter(|(_, keys)| keys.read().unwrap().get(id).is_some())
.map(|(slot, _)| slot)
.cloned()
.collect()
}
fn update_peers(&self, cluster_info: &ClusterInfo, bank_forks: &RwLock<BankForks>) {
let root_bank = bank_forks.read().unwrap().root_bank().clone();
let root_epoch = root_bank.epoch();
let my_epoch = *self.epoch.read().unwrap();
if Some(root_epoch) != my_epoch {
let validator_stakes = root_bank
.epoch_stakes(root_epoch)
.expect(
"Bank must have epoch stakes
for its own epoch",
)
.node_id_to_vote_accounts()
.clone();
*self.validator_stakes.write().unwrap() = validator_stakes;
let id = cluster_info.id();
*self.self_id.write().unwrap() = id;
*self.epoch.write().unwrap() = Some(root_epoch);
}
}
pub fn compute_weights(&self, slot: Slot, repair_peers: &[ContactInfo]) -> Vec<(u64, usize)> {
let slot_peers = self.lookup(slot);
repair_peers
.iter()
.enumerate()
.map(|(i, x)| {
let peer_stake = slot_peers
.as_ref()
.and_then(|v| v.read().unwrap().get(&x.id).cloned())
.unwrap_or(0);
(
1 + peer_stake
+ self
.validator_stakes
.read()
.unwrap()
.get(&x.id)
.map(|v| v.total_stake)
.unwrap_or(0),
i,
)
})
.collect()
}
pub fn generate_repairs_for_missing_slots(
&self,
self_id: &Pubkey,
root: Slot,
) -> Vec<RepairType> {
let my_slots = self.collect(self_id);
self.cluster_slots
.read()
.unwrap()
.keys()
.filter(|x| **x > root)
.filter(|x| !my_slots.contains(*x))
.map(|x| RepairType::HighestShred(*x, 0))
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
use solana_runtime::epoch_stakes::NodeVoteAccounts;
#[test]
fn test_default() {
let cs = ClusterSlots::default();
assert!(cs.cluster_slots.read().unwrap().is_empty());
assert!(cs.since.read().unwrap().is_none());
}
#[test]
fn test_update_noop() {
let cs = ClusterSlots::default();
cs.update_internal(0, (vec![], None));
assert!(cs.cluster_slots.read().unwrap().is_empty());
assert!(cs.since.read().unwrap().is_none());
}
#[test]
fn test_update_empty() {
let cs = ClusterSlots::default();
let epoch_slot = EpochSlots::default();
cs.update_internal(0, (vec![epoch_slot], Some(0)));
assert_eq!(*cs.since.read().unwrap(), Some(0));
assert!(cs.lookup(0).is_none());
}
#[test]
fn test_update_rooted() {
//root is 0, so it should clear out the slot
let cs = ClusterSlots::default();
let mut epoch_slot = EpochSlots::default();
epoch_slot.fill(&[0], 0);
cs.update_internal(0, (vec![epoch_slot], Some(0)));
assert_eq!(*cs.since.read().unwrap(), Some(0));
assert!(cs.lookup(0).is_none());
}
#[test]
fn test_update_new_slot() {
let cs = ClusterSlots::default();
let mut epoch_slot = EpochSlots::default();
epoch_slot.fill(&[1], 0);
cs.update_internal(0, (vec![epoch_slot], Some(0)));
assert_eq!(*cs.since.read().unwrap(), Some(0));
assert!(cs.lookup(0).is_none());
assert!(cs.lookup(1).is_some());
assert_eq!(
cs.lookup(1)
.unwrap()
.read()
.unwrap()
.get(&Pubkey::default()),
Some(&0)
);
}
#[test]
fn test_compute_weights() {
let cs = ClusterSlots::default();
let ci = ContactInfo::default();
assert_eq!(cs.compute_weights(0, &[ci]), vec![(1, 0)]);
}
#[test]
fn test_best_peer_2() {
let cs = ClusterSlots::default();
let mut c1 = ContactInfo::default();
let mut c2 = ContactInfo::default();
let mut map = HashMap::new();
let k1 = Pubkey::new_rand();
let k2 = Pubkey::new_rand();
map.insert(Arc::new(k1.clone()), std::u64::MAX / 2);
map.insert(Arc::new(k2.clone()), 0);
cs.cluster_slots
.write()
.unwrap()
.insert(0, Arc::new(RwLock::new(map)));
c1.id = k1;
c2.id = k2;
assert_eq!(
cs.compute_weights(0, &[c1, c2]),
vec![(std::u64::MAX / 2 + 1, 0), (1, 1)]
);
}
#[test]
fn test_best_peer_3() {
let cs = ClusterSlots::default();
let mut c1 = ContactInfo::default();
let mut c2 = ContactInfo::default();
let mut map = HashMap::new();
let k1 = Pubkey::new_rand();
let k2 = Pubkey::new_rand();
map.insert(Arc::new(k2.clone()), 0);
cs.cluster_slots
.write()
.unwrap()
.insert(0, Arc::new(RwLock::new(map)));
//make sure default weights are used as well
let validator_stakes: HashMap<_, _> = vec![(
*Arc::new(k1.clone()),
NodeVoteAccounts {
total_stake: std::u64::MAX / 2,
vote_accounts: vec![Pubkey::default()],
},
)]
.into_iter()
.collect();
*cs.validator_stakes.write().unwrap() = Arc::new(validator_stakes);
c1.id = k1;
c2.id = k2;
assert_eq!(
cs.compute_weights(0, &[c1, c2]),
vec![(std::u64::MAX / 2 + 1, 0), (1, 1)]
);
}
#[test]
fn test_update_new_staked_slot() {
let cs = ClusterSlots::default();
let mut epoch_slot = EpochSlots::default();
epoch_slot.fill(&[1], 0);
let map = Arc::new(
vec![(
Pubkey::default(),
NodeVoteAccounts {
total_stake: 1,
vote_accounts: vec![Pubkey::default()],
},
)]
.into_iter()
.collect(),
);
*cs.validator_stakes.write().unwrap() = map;
cs.update_internal(0, (vec![epoch_slot], None));
assert!(cs.lookup(1).is_some());
assert_eq!(
cs.lookup(1)
.unwrap()
.read()
.unwrap()
.get(&Pubkey::default()),
Some(&1)
);
}
#[test]
fn test_generate_repairs() {
let cs = ClusterSlots::default();
let mut epoch_slot = EpochSlots::default();
epoch_slot.fill(&[1], 0);
cs.update_internal(0, (vec![epoch_slot], None));
let self_id = Pubkey::new_rand();
assert_eq!(
cs.generate_repairs_for_missing_slots(&self_id, 0),
vec![RepairType::HighestShred(1, 0)]
)
}
#[test]
fn test_collect_my_slots() {
let cs = ClusterSlots::default();
let mut epoch_slot = EpochSlots::default();
epoch_slot.fill(&[1], 0);
let self_id = epoch_slot.from;
cs.update_internal(0, (vec![epoch_slot], None));
let slots: Vec<Slot> = cs.collect(&self_id).into_iter().collect();
assert_eq!(slots, vec![1]);
}
#[test]
fn test_generate_repairs_existing() {
let cs = ClusterSlots::default();
let mut epoch_slot = EpochSlots::default();
epoch_slot.fill(&[1], 0);
let self_id = epoch_slot.from;
cs.update_internal(0, (vec![epoch_slot], None));
assert!(cs
.generate_repairs_for_missing_slots(&self_id, 0)
.is_empty());
}
}

View File

@ -1,7 +1,6 @@
use crate::{consensus::VOTE_THRESHOLD_SIZE, rpc_subscriptions::RpcSubscriptions};
use solana_ledger::blockstore::Blockstore;
use crate::consensus::VOTE_THRESHOLD_SIZE;
use solana_measure::measure::Measure;
use solana_metrics::datapoint_info;
use solana_metrics::inc_new_counter_info;
use solana_runtime::bank::Bank;
use solana_sdk::clock::Slot;
use solana_vote_program::{vote_state::VoteState, vote_state::MAX_LOCKOUT_HISTORY};
@ -14,19 +13,9 @@ use std::{
time::Duration,
};
#[derive(Default)]
pub struct CacheSlotInfo {
pub current_slot: Slot,
pub node_root: Slot,
pub largest_confirmed_root: Slot,
pub highest_confirmed_slot: Slot,
}
pub type BlockCommitmentArray = [u64; MAX_LOCKOUT_HISTORY + 1];
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct BlockCommitment {
pub commitment: BlockCommitmentArray,
pub commitment: [u64; MAX_LOCKOUT_HISTORY],
}
impl BlockCommitment {
@ -39,29 +28,18 @@ impl BlockCommitment {
assert!(confirmation_count > 0 && confirmation_count <= MAX_LOCKOUT_HISTORY);
self.commitment[confirmation_count - 1]
}
pub fn increase_rooted_stake(&mut self, stake: u64) {
self.commitment[MAX_LOCKOUT_HISTORY] += stake;
}
pub fn get_rooted_stake(&self) -> u64 {
self.commitment[MAX_LOCKOUT_HISTORY]
}
#[cfg(test)]
pub(crate) fn new(commitment: BlockCommitmentArray) -> Self {
pub(crate) fn new(commitment: [u64; MAX_LOCKOUT_HISTORY]) -> Self {
Self { commitment }
}
}
#[derive(Default)]
pub struct BlockCommitmentCache {
block_commitment: HashMap<Slot, BlockCommitment>,
largest_confirmed_root: Slot,
total_stake: u64,
bank: Arc<Bank>,
blockstore: Arc<Blockstore>,
root: Slot,
highest_confirmed_slot: Slot,
}
impl std::fmt::Debug for BlockCommitmentCache {
@ -81,33 +59,15 @@ impl std::fmt::Debug for BlockCommitmentCache {
impl BlockCommitmentCache {
pub fn new(
block_commitment: HashMap<Slot, BlockCommitment>,
largest_confirmed_root: Slot,
total_stake: u64,
bank: Arc<Bank>,
blockstore: Arc<Blockstore>,
root: Slot,
highest_confirmed_slot: Slot,
) -> Self {
Self {
block_commitment,
largest_confirmed_root,
total_stake,
bank,
blockstore,
root,
highest_confirmed_slot,
}
}
pub fn default_with_blockstore(blockstore: Arc<Blockstore>) -> Self {
Self {
block_commitment: HashMap::default(),
largest_confirmed_root: Slot::default(),
total_stake: u64::default(),
bank: Arc::new(Bank::default()),
blockstore,
root: Slot::default(),
highest_confirmed_slot: Slot::default(),
}
}
@ -115,10 +75,6 @@ impl BlockCommitmentCache {
self.block_commitment.get(&slot)
}
pub fn largest_confirmed_root(&self) -> Slot {
self.largest_confirmed_root
}
pub fn total_stake(&self) -> u64 {
self.total_stake
}
@ -135,26 +91,6 @@ impl BlockCommitmentCache {
self.root
}
pub fn highest_confirmed_slot(&self) -> Slot {
self.highest_confirmed_slot
}
fn highest_slot_with_confirmation_count(&self, confirmation_count: usize) -> Slot {
assert!(confirmation_count > 0 && confirmation_count <= MAX_LOCKOUT_HISTORY);
for slot in (self.root()..self.slot()).rev() {
if let Some(count) = self.get_confirmation_count(slot) {
if count >= confirmation_count {
return slot;
}
}
}
self.root
}
fn calculate_highest_confirmed_slot(&self) -> Slot {
self.highest_slot_with_confirmation_count(1)
}
pub fn get_confirmation_count(&self, slot: Slot) -> Option<usize> {
self.get_lockout_count(slot, VOTE_THRESHOLD_SIZE)
}
@ -174,49 +110,16 @@ impl BlockCommitmentCache {
0
})
}
pub fn is_confirmed_rooted(&self, slot: Slot) -> bool {
slot <= self.largest_confirmed_root()
&& (self.blockstore.is_root(slot) || self.bank.status_cache_ancestors().contains(&slot))
}
#[cfg(test)]
pub fn new_for_tests_with_blockstore(blockstore: Arc<Blockstore>) -> Self {
pub fn new_for_tests() -> Self {
let mut block_commitment: HashMap<Slot, BlockCommitment> = HashMap::new();
block_commitment.insert(0, BlockCommitment::default());
Self {
block_commitment,
blockstore,
total_stake: 42,
largest_confirmed_root: Slot::default(),
bank: Arc::new(Bank::default()),
root: Slot::default(),
highest_confirmed_slot: Slot::default(),
..Self::default()
}
}
#[cfg(test)]
pub fn new_for_tests_with_blockstore_bank(
blockstore: Arc<Blockstore>,
bank: Arc<Bank>,
root: Slot,
) -> Self {
let mut block_commitment: HashMap<Slot, BlockCommitment> = HashMap::new();
block_commitment.insert(0, BlockCommitment::default());
Self {
block_commitment,
blockstore,
total_stake: 42,
largest_confirmed_root: root,
bank,
root,
highest_confirmed_slot: root,
}
}
pub(crate) fn set_largest_confirmed_root(&mut self, root: Slot) {
self.largest_confirmed_root = root;
}
}
pub struct CommitmentAggregationData {
@ -235,18 +138,6 @@ impl CommitmentAggregationData {
}
}
fn get_largest_confirmed_root(mut rooted_stake: Vec<(Slot, u64)>, total_stake: u64) -> Slot {
rooted_stake.sort_by(|a, b| a.0.cmp(&b.0).reverse());
let mut stake_sum = 0;
for (root, stake) in rooted_stake {
stake_sum += stake;
if (stake_sum as f64 / total_stake as f64) > VOTE_THRESHOLD_SIZE {
return root;
}
}
0
}
pub struct AggregateCommitmentService {
t_commitment: JoinHandle<()>,
}
@ -255,7 +146,6 @@ impl AggregateCommitmentService {
pub fn new(
exit: &Arc<AtomicBool>,
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
subscriptions: Arc<RpcSubscriptions>,
) -> (Sender<CommitmentAggregationData>, Self) {
let (sender, receiver): (
Sender<CommitmentAggregationData>,
@ -273,7 +163,7 @@ impl AggregateCommitmentService {
}
if let Err(RecvTimeoutError::Disconnected) =
Self::run(&receiver, &block_commitment_cache, &subscriptions, &exit_)
Self::run(&receiver, &block_commitment_cache, &exit_)
{
break;
}
@ -286,7 +176,6 @@ impl AggregateCommitmentService {
fn run(
receiver: &Receiver<CommitmentAggregationData>,
block_commitment_cache: &RwLock<BlockCommitmentCache>,
subscriptions: &Arc<RpcSubscriptions>,
exit: &Arc<AtomicBool>,
) -> Result<(), RecvTimeoutError> {
loop {
@ -306,50 +195,27 @@ impl AggregateCommitmentService {
}
let mut aggregate_commitment_time = Measure::start("aggregate-commitment-ms");
let (block_commitment, rooted_stake) =
Self::aggregate_commitment(&ancestors, &aggregation_data.bank);
let largest_confirmed_root =
get_largest_confirmed_root(rooted_stake, aggregation_data.total_staked);
let block_commitment = Self::aggregate_commitment(&ancestors, &aggregation_data.bank);
let mut new_block_commitment = BlockCommitmentCache::new(
block_commitment,
largest_confirmed_root,
aggregation_data.total_staked,
aggregation_data.bank,
block_commitment_cache.read().unwrap().blockstore.clone(),
aggregation_data.root,
aggregation_data.root,
);
new_block_commitment.highest_confirmed_slot =
new_block_commitment.calculate_highest_confirmed_slot();
let mut w_block_commitment_cache = block_commitment_cache.write().unwrap();
std::mem::swap(&mut *w_block_commitment_cache, &mut new_block_commitment);
aggregate_commitment_time.stop();
datapoint_info!(
"block-commitment-cache",
(
"aggregate-commitment-ms",
aggregate_commitment_time.as_ms() as i64,
i64
)
inc_new_counter_info!(
"aggregate-commitment-ms",
aggregate_commitment_time.as_ms() as usize
);
subscriptions.notify_subscribers(CacheSlotInfo {
current_slot: w_block_commitment_cache.slot(),
node_root: w_block_commitment_cache.root(),
largest_confirmed_root: w_block_commitment_cache.largest_confirmed_root(),
highest_confirmed_slot: w_block_commitment_cache.highest_confirmed_slot(),
});
}
}
pub fn aggregate_commitment(
ancestors: &[Slot],
bank: &Bank,
) -> (HashMap<Slot, BlockCommitment>, Vec<(Slot, u64)>) {
pub fn aggregate_commitment(ancestors: &[Slot], bank: &Bank) -> HashMap<Slot, BlockCommitment> {
assert!(!ancestors.is_empty());
// Check ancestors is sorted
@ -358,7 +224,6 @@ impl AggregateCommitmentService {
}
let mut commitment = HashMap::new();
let mut rooted_stake: Vec<(Slot, u64)> = Vec::new();
for (_, (lamports, account)) in bank.vote_accounts().into_iter() {
if lamports == 0 {
continue;
@ -371,19 +236,17 @@ impl AggregateCommitmentService {
let vote_state = vote_state.unwrap();
Self::aggregate_commitment_for_vote_account(
&mut commitment,
&mut rooted_stake,
&vote_state,
ancestors,
lamports,
);
}
(commitment, rooted_stake)
commitment
}
fn aggregate_commitment_for_vote_account(
commitment: &mut HashMap<Slot, BlockCommitment>,
rooted_stake: &mut Vec<(Slot, u64)>,
vote_state: &VoteState,
ancestors: &[Slot],
lamports: u64,
@ -396,13 +259,12 @@ impl AggregateCommitmentService {
commitment
.entry(*a)
.or_insert_with(BlockCommitment::default)
.increase_rooted_stake(lamports);
.increase_confirmation_stake(MAX_LOCKOUT_HISTORY, lamports);
} else {
ancestors_index = i;
break;
}
}
rooted_stake.push((root, lamports));
}
for vote in &vote_state.votes {
@ -428,11 +290,8 @@ impl AggregateCommitmentService {
#[cfg(test)]
mod tests {
use super::*;
use solana_ledger::{
genesis_utils::{create_genesis_config, GenesisConfigInfo},
get_tmp_ledger_path,
};
use solana_sdk::{genesis_config::GenesisConfig, pubkey::Pubkey};
use crate::genesis_utils::{create_genesis_config, GenesisConfigInfo};
use solana_sdk::pubkey::Pubkey;
use solana_stake_program::stake_state;
use solana_vote_program::vote_state::{self, VoteStateVersions};
@ -449,8 +308,6 @@ mod tests {
#[test]
fn test_get_confirmations() {
let bank = Arc::new(Bank::default());
let ledger_path = get_tmp_ledger_path!();
let blockstore = Arc::new(Blockstore::open(&ledger_path).unwrap());
// Build BlockCommitmentCache with votes at depths 0 and 1 for 2 slots
let mut cache0 = BlockCommitment::default();
cache0.increase_confirmation_stake(1, 5);
@ -468,8 +325,7 @@ mod tests {
block_commitment.entry(0).or_insert(cache0.clone());
block_commitment.entry(1).or_insert(cache1.clone());
block_commitment.entry(2).or_insert(cache2.clone());
let block_commitment_cache =
BlockCommitmentCache::new(block_commitment, 0, 50, bank, blockstore, 0, 0);
let block_commitment_cache = BlockCommitmentCache::new(block_commitment, 50, bank, 0);
assert_eq!(block_commitment_cache.get_confirmation_count(0), Some(2));
assert_eq!(block_commitment_cache.get_confirmation_count(1), Some(1));
@ -477,177 +333,17 @@ mod tests {
assert_eq!(block_commitment_cache.get_confirmation_count(3), None,);
}
#[test]
fn test_is_confirmed_rooted() {
let bank = Arc::new(Bank::default());
let ledger_path = get_tmp_ledger_path!();
let blockstore = Arc::new(Blockstore::open(&ledger_path).unwrap());
blockstore.set_roots(&[0, 1]).unwrap();
// Build BlockCommitmentCache with rooted slots
let mut cache0 = BlockCommitment::default();
cache0.increase_rooted_stake(50);
let mut cache1 = BlockCommitment::default();
cache1.increase_rooted_stake(40);
let mut cache2 = BlockCommitment::default();
cache2.increase_rooted_stake(20);
let mut block_commitment = HashMap::new();
block_commitment.entry(1).or_insert(cache0.clone());
block_commitment.entry(2).or_insert(cache1.clone());
block_commitment.entry(3).or_insert(cache2.clone());
let largest_confirmed_root = 1;
let block_commitment_cache = BlockCommitmentCache::new(
block_commitment,
largest_confirmed_root,
50,
bank,
blockstore,
0,
0,
);
assert!(block_commitment_cache.is_confirmed_rooted(0));
assert!(block_commitment_cache.is_confirmed_rooted(1));
assert!(!block_commitment_cache.is_confirmed_rooted(2));
assert!(!block_commitment_cache.is_confirmed_rooted(3));
}
#[test]
fn test_get_largest_confirmed_root() {
assert_eq!(get_largest_confirmed_root(vec![], 10), 0);
let mut rooted_stake = vec![];
rooted_stake.push((0, 5));
rooted_stake.push((1, 5));
assert_eq!(get_largest_confirmed_root(rooted_stake, 10), 0);
let mut rooted_stake = vec![];
rooted_stake.push((1, 5));
rooted_stake.push((0, 10));
rooted_stake.push((2, 5));
rooted_stake.push((1, 4));
assert_eq!(get_largest_confirmed_root(rooted_stake, 10), 1);
}
#[test]
fn test_highest_confirmed_slot() {
let bank = Arc::new(Bank::new(&GenesisConfig::default()));
let bank_slot_5 = Arc::new(Bank::new_from_parent(&bank, &Pubkey::default(), 5));
let ledger_path = get_tmp_ledger_path!();
let blockstore = Arc::new(Blockstore::open(&ledger_path).unwrap());
let total_stake = 50;
// Build cache with confirmation_count 2 given total_stake
let mut cache0 = BlockCommitment::default();
cache0.increase_confirmation_stake(1, 5);
cache0.increase_confirmation_stake(2, 40);
// Build cache with confirmation_count 1 given total_stake
let mut cache1 = BlockCommitment::default();
cache1.increase_confirmation_stake(1, 40);
cache1.increase_confirmation_stake(2, 5);
// Build cache with confirmation_count 0 given total_stake
let mut cache2 = BlockCommitment::default();
cache2.increase_confirmation_stake(1, 20);
cache2.increase_confirmation_stake(2, 5);
let mut block_commitment = HashMap::new();
block_commitment.entry(1).or_insert(cache0.clone()); // Slot 1, conf 2
block_commitment.entry(2).or_insert(cache1.clone()); // Slot 2, conf 1
block_commitment.entry(3).or_insert(cache2.clone()); // Slot 3, conf 0
let block_commitment_cache = BlockCommitmentCache::new(
block_commitment,
0,
total_stake,
bank_slot_5.clone(),
blockstore.clone(),
0,
0,
);
assert_eq!(block_commitment_cache.calculate_highest_confirmed_slot(), 2);
// Build map with multiple slots at conf 1
let mut block_commitment = HashMap::new();
block_commitment.entry(1).or_insert(cache1.clone()); // Slot 1, conf 1
block_commitment.entry(2).or_insert(cache1.clone()); // Slot 2, conf 1
block_commitment.entry(3).or_insert(cache2.clone()); // Slot 3, conf 0
let block_commitment_cache = BlockCommitmentCache::new(
block_commitment,
0,
total_stake,
bank_slot_5.clone(),
blockstore.clone(),
0,
0,
);
assert_eq!(block_commitment_cache.calculate_highest_confirmed_slot(), 2);
// Build map with slot gaps
let mut block_commitment = HashMap::new();
block_commitment.entry(1).or_insert(cache1.clone()); // Slot 1, conf 1
block_commitment.entry(3).or_insert(cache1.clone()); // Slot 3, conf 1
block_commitment.entry(5).or_insert(cache2.clone()); // Slot 5, conf 0
let block_commitment_cache = BlockCommitmentCache::new(
block_commitment,
0,
total_stake,
bank_slot_5.clone(),
blockstore.clone(),
0,
0,
);
assert_eq!(block_commitment_cache.calculate_highest_confirmed_slot(), 3);
// Build map with no conf 1 slots, but one higher
let mut block_commitment = HashMap::new();
block_commitment.entry(1).or_insert(cache0.clone()); // Slot 1, conf 2
block_commitment.entry(2).or_insert(cache2.clone()); // Slot 2, conf 0
block_commitment.entry(3).or_insert(cache2.clone()); // Slot 3, conf 0
let block_commitment_cache = BlockCommitmentCache::new(
block_commitment,
0,
total_stake,
bank_slot_5.clone(),
blockstore.clone(),
0,
0,
);
assert_eq!(block_commitment_cache.calculate_highest_confirmed_slot(), 1);
// Build map with no conf 1 or higher slots
let mut block_commitment = HashMap::new();
block_commitment.entry(1).or_insert(cache2.clone()); // Slot 1, conf 0
block_commitment.entry(2).or_insert(cache2.clone()); // Slot 2, conf 0
block_commitment.entry(3).or_insert(cache2.clone()); // Slot 3, conf 0
let block_commitment_cache = BlockCommitmentCache::new(
block_commitment,
0,
total_stake,
bank_slot_5.clone(),
blockstore.clone(),
0,
0,
);
assert_eq!(block_commitment_cache.calculate_highest_confirmed_slot(), 0);
}
#[test]
fn test_aggregate_commitment_for_vote_account_1() {
let ancestors = vec![3, 4, 5, 7, 9, 11];
let mut commitment = HashMap::new();
let mut rooted_stake = vec![];
let lamports = 5;
let mut vote_state = VoteState::default();
let root = ancestors.last().unwrap().clone();
vote_state.root_slot = Some(root);
let root = ancestors.last().unwrap();
vote_state.root_slot = Some(*root);
AggregateCommitmentService::aggregate_commitment_for_vote_account(
&mut commitment,
&mut rooted_stake,
&vote_state,
&ancestors,
lamports,
@ -655,17 +351,15 @@ mod tests {
for a in ancestors {
let mut expected = BlockCommitment::default();
expected.increase_rooted_stake(lamports);
expected.increase_confirmation_stake(MAX_LOCKOUT_HISTORY, lamports);
assert_eq!(*commitment.get(&a).unwrap(), expected);
}
assert_eq!(rooted_stake[0], (root, lamports));
}
#[test]
fn test_aggregate_commitment_for_vote_account_2() {
let ancestors = vec![3, 4, 5, 7, 9, 11];
let mut commitment = HashMap::new();
let mut rooted_stake = vec![];
let lamports = 5;
let mut vote_state = VoteState::default();
@ -674,7 +368,6 @@ mod tests {
vote_state.process_slot_vote_unchecked(*ancestors.last().unwrap());
AggregateCommitmentService::aggregate_commitment_for_vote_account(
&mut commitment,
&mut rooted_stake,
&vote_state,
&ancestors,
lamports,
@ -683,7 +376,7 @@ mod tests {
for a in ancestors {
if a <= root {
let mut expected = BlockCommitment::default();
expected.increase_rooted_stake(lamports);
expected.increase_confirmation_stake(MAX_LOCKOUT_HISTORY, lamports);
assert_eq!(*commitment.get(&a).unwrap(), expected);
} else {
let mut expected = BlockCommitment::default();
@ -691,14 +384,12 @@ mod tests {
assert_eq!(*commitment.get(&a).unwrap(), expected);
}
}
assert_eq!(rooted_stake[0], (root, lamports));
}
#[test]
fn test_aggregate_commitment_for_vote_account_3() {
let ancestors = vec![3, 4, 5, 7, 9, 10, 11];
let mut commitment = HashMap::new();
let mut rooted_stake = vec![];
let lamports = 5;
let mut vote_state = VoteState::default();
@ -709,7 +400,6 @@ mod tests {
vote_state.process_slot_vote_unchecked(ancestors[6]);
AggregateCommitmentService::aggregate_commitment_for_vote_account(
&mut commitment,
&mut rooted_stake,
&vote_state,
&ancestors,
lamports,
@ -718,7 +408,7 @@ mod tests {
for (i, a) in ancestors.iter().enumerate() {
if *a <= root {
let mut expected = BlockCommitment::default();
expected.increase_rooted_stake(lamports);
expected.increase_confirmation_stake(MAX_LOCKOUT_HISTORY, lamports);
assert_eq!(*commitment.get(&a).unwrap(), expected);
} else if i <= 4 {
let mut expected = BlockCommitment::default();
@ -730,7 +420,6 @@ mod tests {
assert_eq!(*commitment.get(&a).unwrap(), expected);
}
}
assert_eq!(rooted_stake[0], (root, lamports));
}
#[test]
@ -740,8 +429,6 @@ mod tests {
mut genesis_config, ..
} = create_genesis_config(10_000);
let rooted_stake_amount = 40;
let sk1 = Pubkey::new_rand();
let pk1 = Pubkey::new_rand();
let mut vote_account1 = vote_state::create_account(&pk1, &Pubkey::new_rand(), 0, 100);
@ -752,36 +439,12 @@ mod tests {
let mut vote_account2 = vote_state::create_account(&pk2, &Pubkey::new_rand(), 0, 50);
let stake_account2 =
stake_state::create_account(&sk2, &pk2, &vote_account2, &genesis_config.rent, 50);
let sk3 = Pubkey::new_rand();
let pk3 = Pubkey::new_rand();
let mut vote_account3 = vote_state::create_account(&pk3, &Pubkey::new_rand(), 0, 1);
let stake_account3 = stake_state::create_account(
&sk3,
&pk3,
&vote_account3,
&genesis_config.rent,
rooted_stake_amount,
);
let sk4 = Pubkey::new_rand();
let pk4 = Pubkey::new_rand();
let mut vote_account4 = vote_state::create_account(&pk4, &Pubkey::new_rand(), 0, 1);
let stake_account4 = stake_state::create_account(
&sk4,
&pk4,
&vote_account4,
&genesis_config.rent,
rooted_stake_amount,
);
genesis_config.accounts.extend(vec![
(pk1, vote_account1.clone()),
(sk1, stake_account1),
(pk2, vote_account2.clone()),
(sk2, stake_account2),
(pk3, vote_account3.clone()),
(sk3, stake_account3),
(pk4, vote_account4.clone()),
(sk4, stake_account4),
]);
// Create bank
@ -801,20 +464,7 @@ mod tests {
VoteState::to(&versioned, &mut vote_account2).unwrap();
bank.store_account(&pk2, &vote_account2);
let mut vote_state3 = VoteState::from(&vote_account3).unwrap();
vote_state3.root_slot = Some(1);
let versioned = VoteStateVersions::Current(Box::new(vote_state3));
VoteState::to(&versioned, &mut vote_account3).unwrap();
bank.store_account(&pk3, &vote_account3);
let mut vote_state4 = VoteState::from(&vote_account4).unwrap();
vote_state4.root_slot = Some(2);
let versioned = VoteStateVersions::Current(Box::new(vote_state4));
VoteState::to(&versioned, &mut vote_account4).unwrap();
bank.store_account(&pk4, &vote_account4);
let (commitment, rooted_stake) =
AggregateCommitmentService::aggregate_commitment(&ancestors, &bank);
let commitment = AggregateCommitmentService::aggregate_commitment(&ancestors, &bank);
for a in ancestors {
if a <= 3 {
@ -838,7 +488,5 @@ mod tests {
assert!(commitment.get(&a).is_none());
}
}
assert_eq!(rooted_stake.len(), 2);
assert_eq!(get_largest_confirmed_root(rooted_stake, 100), 1)
}
}

View File

@ -1,4 +1,3 @@
use crate::progress_map::ProgressMap;
use chrono::prelude::*;
use solana_ledger::bank_forks::BankForks;
use solana_runtime::bank::Bank;
@ -321,7 +320,7 @@ impl Tower {
}
pub fn check_vote_stake_threshold(
&self,
slot: Slot,
slot: u64,
stake_lockouts: &HashMap<u64, StakeLockout>,
total_staked: u64,
) -> bool {
@ -332,8 +331,11 @@ impl Tower {
if let Some(fork_stake) = stake_lockouts.get(&vote.slot) {
let lockout = fork_stake.stake as f64 / total_staked as f64;
trace!(
"fork_stake slot: {}, vote slot: {}, lockout: {} fork_stake: {} total_stake: {}",
slot, vote.slot, lockout, fork_stake.stake, total_staked
"fork_stake slot: {} lockout: {} fork_stake: {} total_stake: {}",
slot,
lockout,
fork_stake.stake,
total_staked
);
if vote.confirmation_count as usize > self.threshold_depth {
for old_vote in &self.lockouts.votes {
@ -353,18 +355,6 @@ impl Tower {
}
}
pub(crate) fn check_switch_threshold(
&self,
_slot: Slot,
_ancestors: &HashMap<Slot, HashSet<u64>>,
_descendants: &HashMap<Slot, HashSet<u64>>,
_progress: &ProgressMap,
_total_epoch_stake: u64,
_epoch_vote_accounts: &HashMap<Pubkey, (u64, Account)>,
) -> bool {
true
}
/// Update lockouts for all the ancestors
fn update_ancestor_lockouts(
stake_lockouts: &mut HashMap<Slot, StakeLockout>,
@ -478,12 +468,7 @@ impl Tower {
#[cfg(test)]
pub mod test {
use super::*;
use crate::{
cluster_info_vote_listener::VoteTracker,
cluster_slots::ClusterSlots,
progress_map::ForkProgress,
replay_stage::{HeaviestForkFailures, ReplayStage},
};
use crate::replay_stage::{ForkProgress, ReplayStage};
use solana_ledger::bank_forks::BankForks;
use solana_runtime::{
bank::Bank,
@ -496,96 +481,107 @@ pub mod test {
hash::Hash,
pubkey::Pubkey,
signature::{Keypair, Signer},
transaction::Transaction,
};
use solana_vote_program::{
vote_instruction,
vote_state::{Vote, VoteStateVersions},
vote_transaction,
};
use std::collections::HashMap;
use std::collections::{HashMap, VecDeque};
use std::sync::RwLock;
use std::{thread::sleep, time::Duration};
use trees::{tr, Tree, TreeWalk};
use trees::{tr, Node, Tree};
pub(crate) struct VoteSimulator {
pub validator_keypairs: HashMap<Pubkey, ValidatorVoteKeypairs>,
pub node_pubkeys: Vec<Pubkey>,
pub vote_pubkeys: Vec<Pubkey>,
pub bank_forks: RwLock<BankForks>,
pub progress: ProgressMap,
pub(crate) struct VoteSimulator<'a> {
searchable_nodes: HashMap<u64, &'a Node<u64>>,
}
impl VoteSimulator {
pub(crate) fn new(num_keypairs: usize) -> Self {
let (validator_keypairs, node_pubkeys, vote_pubkeys, bank_forks, progress) =
Self::init_state(num_keypairs);
Self {
validator_keypairs,
node_pubkeys,
vote_pubkeys,
bank_forks: RwLock::new(bank_forks),
progress,
}
}
pub(crate) fn fill_bank_forks(
&mut self,
forks: Tree<u64>,
cluster_votes: &HashMap<Pubkey, Vec<u64>>,
) {
let root = forks.root().data;
assert!(self.bank_forks.read().unwrap().get(root).is_some());
let mut walk = TreeWalk::from(forks);
loop {
if let Some(visit) = walk.get() {
let slot = visit.node().data;
self.progress
.entry(slot)
.or_insert_with(|| ForkProgress::new(Hash::default(), None, None, 0, 0));
if self.bank_forks.read().unwrap().get(slot).is_some() {
walk.forward();
continue;
}
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);
for (pubkey, vote) in cluster_votes.iter() {
if vote.contains(&parent) {
let keypairs = self.validator_keypairs.get(pubkey).unwrap();
let last_blockhash = parent_bank.last_blockhash();
let vote_tx = vote_transaction::new_vote_transaction(
// Must vote > root to be processed
vec![parent],
parent_bank.hash(),
last_blockhash,
&keypairs.node_keypair,
&keypairs.vote_keypair,
&keypairs.vote_keypair,
);
info!("voting {} {}", parent_bank.slot(), parent_bank.hash());
new_bank.process_transaction(&vote_tx).unwrap();
}
}
new_bank.freeze();
self.bank_forks.write().unwrap().insert(new_bank);
walk.forward();
} else {
break;
}
}
impl<'a> VoteSimulator<'a> {
pub(crate) fn new(forks: &'a Tree<u64>) -> Self {
let mut searchable_nodes = HashMap::new();
let root = forks.root();
searchable_nodes.insert(root.data, root);
Self { searchable_nodes }
}
pub(crate) fn simulate_vote(
&mut self,
vote_slot: Slot,
my_pubkey: &Pubkey,
bank_forks: &RwLock<BankForks>,
cluster_votes: &mut HashMap<Pubkey, Vec<u64>>,
validator_keypairs: &HashMap<Pubkey, ValidatorVoteKeypairs>,
my_keypairs: &ValidatorVoteKeypairs,
progress: &mut HashMap<u64, ForkProgress>,
tower: &mut Tower,
) -> Vec<HeaviestForkFailures> {
// Try to simulate the vote
let my_keypairs = self.validator_keypairs.get(&my_pubkey).unwrap();
) -> Vec<VoteFailures> {
let node = self
.find_node_and_update_simulation(vote_slot)
.expect("Vote to simulate must be for a slot in the tree");
let mut missing_nodes = VecDeque::new();
let mut current = node;
loop {
let current_slot = current.data;
if bank_forks.read().unwrap().get(current_slot).is_some()
|| tower.root().map(|r| current_slot < r).unwrap_or(false)
{
break;
} else {
missing_nodes.push_front(current);
}
if let Some(parent) = current.parent() {
current = parent;
} else {
break;
}
}
// Create any missing banks along the path
for missing_node in missing_nodes {
let missing_slot = missing_node.data;
let parent = missing_node.parent().unwrap().data;
let parent_bank = bank_forks
.read()
.unwrap()
.get(parent)
.expect("parent bank must exist")
.clone();
info!("parent of {} is {}", missing_slot, parent_bank.slot(),);
progress
.entry(missing_slot)
.or_insert_with(|| ForkProgress::new(parent_bank.last_blockhash()));
// Create the missing bank
let new_bank =
Bank::new_from_parent(&parent_bank, &Pubkey::default(), missing_slot);
// Simulate ingesting the cluster's votes for the parent into this bank
for (pubkey, vote) in cluster_votes.iter() {
if vote.contains(&parent_bank.slot()) {
let keypairs = validator_keypairs.get(pubkey).unwrap();
let node_pubkey = keypairs.node_keypair.pubkey();
let vote_pubkey = keypairs.vote_keypair.pubkey();
let last_blockhash = parent_bank.last_blockhash();
let votes = Vote::new(vec![parent_bank.slot()], parent_bank.hash());
info!("voting {} {}", parent_bank.slot(), parent_bank.hash());
let vote_ix = vote_instruction::vote(&vote_pubkey, &vote_pubkey, votes);
let mut vote_tx =
Transaction::new_with_payer(vec![vote_ix], Some(&node_pubkey));
vote_tx.partial_sign(&[&keypairs.node_keypair], last_blockhash);
vote_tx.partial_sign(&[&keypairs.vote_keypair], last_blockhash);
new_bank.process_transaction(&vote_tx).unwrap();
}
}
new_bank.freeze();
bank_forks.write().unwrap().insert(new_bank);
}
// Now try to simulate the vote
let my_pubkey = my_keypairs.node_keypair.pubkey();
let my_vote_pubkey = my_keypairs.vote_keypair.pubkey();
let ancestors = self.bank_forks.read().unwrap().ancestors();
let mut frozen_banks: Vec<_> = self
.bank_forks
let ancestors = bank_forks.read().unwrap().ancestors();
let mut frozen_banks: Vec<_> = bank_forks
.read()
.unwrap()
.frozen_banks()
@ -598,133 +594,99 @@ pub mod test {
&ancestors,
&mut frozen_banks,
tower,
&mut self.progress,
&VoteTracker::default(),
&ClusterSlots::default(),
&self.bank_forks,
&mut HashSet::new(),
progress,
);
let vote_bank = self
.bank_forks
let bank = bank_forks
.read()
.unwrap()
.get(vote_slot)
.expect("Bank must have been created before vote simulation")
.clone();
// Try to vote on the given slot
let descendants = self.bank_forks.read().unwrap().descendants();
let (_, _, failure_reasons) = ReplayStage::select_vote_and_reset_forks(
&Some(vote_bank.clone()),
&None,
&ancestors,
&descendants,
&self.progress,
&tower,
);
// Make sure this slot isn't locked out or failing threshold
info!("Checking vote: {}", vote_bank.slot());
if !failure_reasons.is_empty() {
return failure_reasons;
let fork_progress = progress
.get(&vote_slot)
.expect("Slot for vote must exist in progress map");
info!("Checking vote: {}", vote_slot);
info!("lockouts: {:?}", fork_progress.fork_stats.stake_lockouts);
let mut failures = vec![];
if fork_progress.fork_stats.is_locked_out {
failures.push(VoteFailures::LockedOut(vote_slot));
}
let vote = tower.new_vote_from_bank(&vote_bank, &my_vote_pubkey).0;
if !fork_progress.fork_stats.vote_threshold {
failures.push(VoteFailures::FailedThreshold(vote_slot));
}
if !failures.is_empty() {
return failures;
}
let vote = tower.new_vote_from_bank(&bank, &my_vote_pubkey).0;
if let Some(new_root) = tower.record_bank_vote(vote) {
ReplayStage::handle_new_root(
new_root,
&self.bank_forks,
&mut self.progress,
&None,
&mut HashSet::new(),
None,
);
ReplayStage::handle_new_root(new_root, bank_forks, progress, &None);
}
// Mark the vote for this bank under this node's pubkey so it will be
// integrated into any future child banks
cluster_votes.entry(my_pubkey).or_default().push(vote_slot);
vec![]
}
fn can_progress_on_fork(
&mut self,
my_pubkey: &Pubkey,
tower: &mut Tower,
start_slot: u64,
num_slots: u64,
cluster_votes: &mut HashMap<Pubkey, Vec<u64>>,
) -> bool {
// Check that within some reasonable time, validator can make a new
// root on this fork
let old_root = tower.root();
for i in 1..num_slots {
// The parent of the tip of the fork
let mut fork_tip_parent = tr(start_slot + i - 1);
// The tip of the fork
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)
.is_empty()
{
cluster_votes
.entry(*my_pubkey)
.or_default()
.push(start_slot + i);
}
if old_root != tower.root() {
return true;
// Find a node representing the given slot
fn find_node_and_update_simulation(&mut self, slot: u64) -> Option<&'a Node<u64>> {
let mut successful_search_node: Option<&'a Node<u64>> = None;
let mut found_node = None;
for search_node in self.searchable_nodes.values() {
if let Some((target, new_searchable_nodes)) = Self::find_node(search_node, slot) {
successful_search_node = Some(search_node);
found_node = Some(target);
for node in new_searchable_nodes {
self.searchable_nodes.insert(node.data, node);
}
break;
}
}
false
successful_search_node.map(|node| {
self.searchable_nodes.remove(&node.data);
});
found_node
}
fn init_state(
num_keypairs: usize,
) -> (
HashMap<Pubkey, ValidatorVoteKeypairs>,
Vec<Pubkey>,
Vec<Pubkey>,
BankForks,
ProgressMap,
) {
let keypairs: HashMap<_, _> = std::iter::repeat_with(|| {
let node_keypair = Keypair::new();
let vote_keypair = Keypair::new();
let stake_keypair = Keypair::new();
let node_pubkey = node_keypair.pubkey();
(
node_pubkey,
ValidatorVoteKeypairs::new(node_keypair, vote_keypair, stake_keypair),
)
})
.take(num_keypairs)
.collect();
let node_pubkeys: Vec<_> = keypairs
.values()
.map(|keys| keys.node_keypair.pubkey())
.collect();
let vote_pubkeys: Vec<_> = keypairs
.values()
.map(|keys| keys.vote_keypair.pubkey())
.collect();
fn find_node(
node: &'a Node<u64>,
slot: u64,
) -> Option<(&'a Node<u64>, Vec<&'a Node<u64>>)> {
if node.data == slot {
Some((node, node.iter().collect()))
} else {
let mut search_result: Option<(&'a Node<u64>, Vec<&'a Node<u64>>)> = None;
for child in node.iter() {
if let Some((_, ref mut new_searchable_nodes)) = search_result {
new_searchable_nodes.push(child);
continue;
}
search_result = Self::find_node(child, slot);
}
let (bank_forks, progress) = initialize_state(&keypairs, 10_000);
(keypairs, node_pubkeys, vote_pubkeys, bank_forks, progress)
search_result
}
}
}
#[derive(PartialEq, Debug)]
pub(crate) enum VoteFailures {
LockedOut(u64),
FailedThreshold(u64),
}
// Setup BankForks with bank 0 and all the validator accounts
pub(crate) fn initialize_state(
validator_keypairs_map: &HashMap<Pubkey, ValidatorVoteKeypairs>,
stake: u64,
) -> (BankForks, ProgressMap) {
) -> (BankForks, HashMap<u64, ForkProgress>) {
let validator_keypairs: Vec<_> = validator_keypairs_map.values().collect();
let GenesisConfigInfo {
genesis_config,
mint_keypair,
voting_keypair: _,
} = create_genesis_config_with_vote_accounts(1_000_000_000, &validator_keypairs, stake);
} = create_genesis_config_with_vote_accounts(1_000_000_000, &validator_keypairs);
let bank0 = Bank::new(&genesis_config);
@ -733,11 +695,8 @@ pub mod test {
}
bank0.freeze();
let mut progress = ProgressMap::default();
progress.insert(
0,
ForkProgress::new(bank0.last_blockhash(), None, None, 0, 0),
);
let mut progress = HashMap::new();
progress.insert(0, ForkProgress::new(bank0.last_blockhash()));
(BankForks::new(0, bank0), progress)
}
@ -761,26 +720,84 @@ pub mod test {
stakes
}
fn can_progress_on_fork(
my_pubkey: &Pubkey,
tower: &mut Tower,
start_slot: u64,
num_slots: u64,
bank_forks: &RwLock<BankForks>,
cluster_votes: &mut HashMap<Pubkey, Vec<u64>>,
keypairs: &HashMap<Pubkey, ValidatorVoteKeypairs>,
progress: &mut HashMap<u64, ForkProgress>,
) -> bool {
// Check that within some reasonable time, validator can make a new
// root on this fork
let old_root = tower.root();
let mut main_fork = tr(start_slot);
let mut tip = main_fork.root_mut();
for i in 1..num_slots {
tip.push_front(tr(start_slot + i));
tip = tip.first_mut().unwrap();
}
let mut voting_simulator = VoteSimulator::new(&main_fork);
for i in 1..num_slots {
voting_simulator.simulate_vote(
i + start_slot,
&bank_forks,
cluster_votes,
&keypairs,
keypairs.get(&my_pubkey).unwrap(),
progress,
tower,
);
if old_root != tower.root() {
return true;
}
}
false
}
#[test]
fn test_simple_votes() {
// Init state
let mut vote_simulator = VoteSimulator::new(1);
let node_pubkey = vote_simulator.node_pubkeys[0];
let mut tower = Tower::new_with_key(&node_pubkey);
let node_keypair = Keypair::new();
let vote_keypair = Keypair::new();
let stake_keypair = Keypair::new();
let node_pubkey = node_keypair.pubkey();
let mut keypairs = HashMap::new();
keypairs.insert(
node_pubkey,
ValidatorVoteKeypairs::new(node_keypair, vote_keypair, stake_keypair),
);
// Initialize BankForks
let (bank_forks, mut progress) = initialize_state(&keypairs);
let bank_forks = RwLock::new(bank_forks);
// Create the tree of banks
let forks = tr(0) / (tr(1) / (tr(2) / (tr(3) / (tr(4) / tr(5)))));
// Set the voting behavior
let mut cluster_votes = HashMap::new();
let mut voting_simulator = VoteSimulator::new(&forks);
let votes = vec![0, 1, 2, 3, 4, 5];
cluster_votes.insert(node_pubkey, votes.clone());
vote_simulator.fill_bank_forks(forks, &cluster_votes);
// Simulate the votes
let mut tower = Tower::new_with_key(&node_pubkey);
let mut cluster_votes = HashMap::new();
for vote in votes {
assert!(vote_simulator
.simulate_vote(vote, &node_pubkey, &mut tower,)
assert!(voting_simulator
.simulate_vote(
vote,
&bank_forks,
&mut cluster_votes,
&keypairs,
keypairs.get(&node_pubkey).unwrap(),
&mut progress,
&mut tower,
)
.is_empty());
}
@ -792,14 +809,21 @@ pub mod test {
#[test]
fn test_double_partition() {
// Init state
let mut vote_simulator = VoteSimulator::new(2);
let node_pubkey = vote_simulator.node_pubkeys[0];
let vote_pubkey = vote_simulator.vote_pubkeys[0];
let mut tower = Tower::new_with_key(&node_pubkey);
solana_logger::setup();
let node_keypair = Keypair::new();
let vote_keypair = Keypair::new();
let stake_keypair = Keypair::new();
let node_pubkey = node_keypair.pubkey();
let vote_pubkey = vote_keypair.pubkey();
let num_slots_to_try = 200;
// Create the tree of banks
let mut keypairs = HashMap::new();
info!("my_pubkey: {}", node_pubkey);
keypairs.insert(
node_pubkey,
ValidatorVoteKeypairs::new(node_keypair, vote_keypair, stake_keypair),
);
// Create the tree of banks in a BankForks object
let forks = tr(0)
/ (tr(1)
/ (tr(2)
@ -816,37 +840,56 @@ pub mod test {
/ (tr(44)
// Minor fork 2
/ (tr(45) / (tr(46) / (tr(47) / (tr(48) / (tr(49) / (tr(50)))))))
/ (tr(110) / (tr(110 + 2 * num_slots_to_try))))))))))))));
/ (tr(110)))))))))))));
// Set the successful voting behavior
let mut cluster_votes = HashMap::new();
let mut my_votes: Vec<Slot> = vec![];
let next_unlocked_slot = 110;
// Set the voting behavior
let mut voting_simulator = VoteSimulator::new(&forks);
let mut votes: Vec<Slot> = vec![];
// Vote on the first minor fork
my_votes.extend((0..=14).into_iter());
votes.extend((0..=14).into_iter());
// Come back to the main fork
my_votes.extend((43..=44).into_iter());
votes.extend((43..=44).into_iter());
// Vote on the second minor fork
my_votes.extend((45..=50).into_iter());
// Vote to come back to main fork
my_votes.push(next_unlocked_slot);
cluster_votes.insert(node_pubkey, my_votes.clone());
// Make the other validator vote fork to pass the threshold checks
let other_votes = my_votes.clone();
cluster_votes.insert(vote_simulator.node_pubkeys[1], other_votes);
vote_simulator.fill_bank_forks(forks, &cluster_votes);
votes.extend((45..=50).into_iter());
// Simulate the votes.
for vote in &my_votes {
let mut cluster_votes: HashMap<Pubkey, Vec<Slot>> = HashMap::new();
let (bank_forks, mut progress) = initialize_state(&keypairs);
let bank_forks = RwLock::new(bank_forks);
// Simulate the votes. Should fail on trying to come back to the main fork
// at 106 exclusively due to threshold failure
let mut tower = Tower::new_with_key(&node_pubkey);
for vote in &votes {
// All these votes should be ok
assert!(vote_simulator
.simulate_vote(*vote, &node_pubkey, &mut tower,)
assert!(voting_simulator
.simulate_vote(
*vote,
&bank_forks,
&mut cluster_votes,
&keypairs,
keypairs.get(&node_pubkey).unwrap(),
&mut progress,
&mut tower,
)
.is_empty());
}
// Try to come back to main fork
let next_unlocked_slot = 110;
assert!(voting_simulator
.simulate_vote(
next_unlocked_slot,
&bank_forks,
&mut cluster_votes,
&keypairs,
keypairs.get(&node_pubkey).unwrap(),
&mut progress,
&mut tower,
)
.is_empty());
info!("local tower: {:#?}", tower.lockouts.votes);
let vote_accounts = vote_simulator
.bank_forks
let vote_accounts = bank_forks
.read()
.unwrap()
.get(next_unlocked_slot)
@ -856,17 +899,15 @@ pub mod test {
let state = VoteState::from(&observed.1).unwrap();
info!("observed tower: {:#?}", state.votes);
let num_slots_to_try = 200;
cluster_votes
.get_mut(&vote_simulator.node_pubkeys[1])
.unwrap()
.extend(next_unlocked_slot + 1..next_unlocked_slot + num_slots_to_try);
assert!(vote_simulator.can_progress_on_fork(
assert!(can_progress_on_fork(
&node_pubkey,
&mut tower,
next_unlocked_slot,
num_slots_to_try,
200,
&bank_forks,
&mut cluster_votes,
&keypairs,
&mut progress
));
}

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