Compare commits

..

88 Commits

Author SHA1 Message Date
Trent Nelson
03b21f2e9d Bump version to v1.6.2 2021-03-30 00:06:01 -06:00
carllin
cc5565b17e Setup ReplayStage confirmation scaffolding for duplicate slots (#9698)
(cherry picked from commit 52703badfa)
2021-03-29 22:07:14 -06:00
mergify[bot]
50beef0b15 Allow incomplete features in frozen-abi (#16205)
(cherry picked from commit 9ba9d2a8ae)

Co-authored-by: Trent Nelson <trent@solana.com>
2021-03-30 03:46:10 +00:00
mergify[bot]
06a54e1423 remove old code (#15988) (#15993)
(cherry picked from commit 9760fded2d)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-03-30 00:50:27 +00:00
mergify[bot]
4d731ecd08 eliminate lock on record (#15929) (#16073)
* eliminate lock on record

* use same error as MaxHeightReached

* clippy

* review feedback

* refactor should_tick code

* pr feedback

(cherry picked from commit 57ba86c821)

Co-authored-by: Jeff Washington (jwash) <75863576+jeffwashington@users.noreply.github.com>
2021-03-30 00:46:13 +00:00
mergify[bot]
ee06789a66 sdk: Add try_from_slice_unchecked for Borsh (#16098) (#16158)
* sdk: Add try_from_slice_unchecked for Borsh

* Add tests

* Rename + clarify comment

* Rename back to unchecked

(cherry picked from commit cffa851e0f)

Co-authored-by: Jon Cinque <jon.cinque@gmail.com>
2021-03-29 23:15:34 +00:00
mergify[bot]
2dabe1d706 Add handling to close accounts to many-accounts bench (#16199) (#16201)
* gitignore farf

* Improve cli args

* Use derived addresses for accounts

* Add parameter to close every nth account created

(cherry picked from commit 1d145e1fc2)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-03-29 22:54:09 +00:00
Tyera Eulberg
3b1279a005 Future-aware enum name 2021-03-29 14:58:35 -06:00
mergify[bot]
5c9f85f28d Rpc: enable getConfirmedBlocks and getConfirmedBlocksWithLimit to return confirmed (not yet finalized) data (#16161) (#16198)
* Add commitment config capabilities

* Use rpc limit if no end_slot provided

* Limit to actually finalized blocks

* Support confirmed blocks in getConfirmedBlocks and getConfirmedBlocksWithLimit

* Update docs

* Add client plumbing

* Rename config enum

(cherry picked from commit 60ed8e2892)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-03-29 19:53:17 +00:00
mergify[bot]
e12dd46ef3 Derive PartialEq for StakeActivationState (#16196)
(cherry picked from commit 4e7bd45d4c)

Co-authored-by: Michael Vines <mvines@gmail.com>
2021-03-29 18:16:44 +00:00
mergify[bot]
c4fa03b478 Status cache improvements (#16174) (#16178)
(cherry picked from commit 5e5b63712b)

Co-authored-by: sakridge <sakridge@gmail.com>
2021-03-29 10:11:16 -07:00
mergify[bot]
9fb749deb7 Print the rust version when building bpf programs (#16181) (#16183)
(cherry picked from commit abada56ba1)

Co-authored-by: Justin Starry <justin@solana.com>
2021-03-29 07:18:55 +00:00
mergify[bot]
bd48344de2 Fix handling of invoked ix accounts in program-test (#16170) (#16176)
(cherry picked from commit 27ab415ecc)

Co-authored-by: Justin Starry <justin@solana.com>
2021-03-29 01:55:11 +00:00
mergify[bot]
78e54f1d2c Implement mnemonic support for solana-keygen grind (solana-labs#9325) (#16108) (#16173)
* Implement mnemonic support for solana-keygen grind (solana-labs#9325)

* Updated to include feedback from review.

* Renaming as per review feedback

* Fixed an incorrectly transcribed underscore

* Properly re-use string constants.

(cherry picked from commit e50f598449)

Co-authored-by: bji <bryan@ischo.com>
2021-03-28 07:05:17 +00:00
mergify[bot]
76a6576976 sdk: Use u32::MAX from std to unbreak BPF builds (#16171) (#16172)
(cherry picked from commit aabe186e3f)

Co-authored-by: Justin Starry <justin@solana.com>
2021-03-27 17:05:53 +00:00
mergify[bot]
92ec1ae255 Switch to a single use (#16169)
(cherry picked from commit 16e4ccca13)

Co-authored-by: Michael Vines <mvines@gmail.com>
2021-03-27 06:58:31 +00:00
Michael Vines
0d203728cc Add RpcClient::get_stake_activation() 2021-03-26 22:33:06 -07:00
mergify[bot]
625773e5b8 Rpc: enable getConfirmedBlock and getConfirmedTransaction to return confirmed (not yet finalized) data (bp #16142) (#16160)
* Rpc: enable getConfirmedBlock and getConfirmedTransaction to return confirmed (not yet finalized) data (#16142)

* Add Blockstore block and tx apis that allow unrooted responses

* Add TransactionStatusMessage, and send on bank freeze; also refactor TransactionStatusSender

* Track highest slot with tx-status writes complete

* Rename and unpub fn

* Add commitment to GetConfirmed input configs

* Support confirmed blocks in getConfirmedBlock

* Support confirmed txs in getConfirmedTransaction

* Update sigs-for-addr2 comment

* Enable confirmed block in cli

* Enable confirmed transaction in cli

* Review comments

* Rename blockstore method

(cherry picked from commit 433f1ead1c)

# Conflicts:
#	core/src/replay_stage.rs

* Fix conflict

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2021-03-27 04:51:53 +00:00
mergify[bot]
a4cb1e45ae Only print skipped leader slot message when the node is actually leader (#16156) (#16164)
Also, check vote signature after the vote is signed

(cherry picked from commit 60b4771fc6)

Co-authored-by: sakridge <sakridge@gmail.com>
2021-03-27 02:03:10 +00:00
mergify[bot]
8aded2778e Bump bpf-tools to version v1.4 (#16152) (#16154)
(cherry picked from commit 658ddd1c9c)

Co-authored-by: Dmitri Makarov <dmakarov@users.noreply.github.com>
2021-03-26 20:51:25 +00:00
mergify[bot]
d940c5b1a3 Skip leader slots until a vote lands (#15607) (#16147)
(cherry picked from commit b99ae8f334)

Co-authored-by: sakridge <sakridge@gmail.com>
2021-03-26 19:07:24 +00:00
Trent Nelson
1be045df94 sq: optimize
(cherry picked from commit 482c027d3b)
2021-03-25 21:31:52 -06:00
Trent Nelson
86191911c7 perf: use saturating/checked integer arithmetic
(cherry picked from commit 834fae684b)
2021-03-25 21:31:52 -06:00
mergify[bot]
8f852d8a6b makes test_pull_request_time_pruning smaller (#16128) (#16144)
(cherry picked from commit b041b55028)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2021-03-26 01:20:26 +00:00
Kristofer Peterson
68a439f8da Refactored ShortU16Visitor::visit_seq() to reject overflows, extra leading zeros and ensure one-to-one encoding. 2021-03-26 01:20:22 +00:00
Trent Nelson
e021832708 sdk: ShortU16 - rename variables for clarity
ShortU16's implementation embeds its usage as the length of a
ShortVec, confusingly referring to both a 'len' and a 'size'
at the same time.
2021-03-26 01:20:22 +00:00
Trent Nelson
87b11aa187 sdk: Add ShortU16 deser test 2021-03-26 01:20:22 +00:00
mergify[bot]
7475a6f444 makes turbine peer computation consistent between broadcast and retransmit (#14910) (#16143)
get_broadcast_peers is using tvu_peers:
https://github.com/solana-labs/solana/blob/84e52b606/core/src/broadcast_stage.rs#L362-L370
which is potentially inconsistent with retransmit_peers:
https://github.com/solana-labs/solana/blob/84e52b606/core/src/cluster_info.rs#L1332-L1345

Also, the leader does not include its own contact-info when broadcasting
shreds:
https://github.com/solana-labs/solana/blob/84e52b606/core/src/cluster_info.rs#L1324
but on the retransmit side, slot leader is removed only _after_ neighbors and
children are computed:
https://github.com/solana-labs/solana/blob/84e52b606/core/src/retransmit_stage.rs#L383-L384
So the turbine broadcast tree is different between the two stages.

This commit:
* Removes retransmit_peers. Broadcast and retransmit stages will use tvu_peers
  consistently.
* Retransmit stage removes slot leader _before_ computing children and
  neighbors.

(cherry picked from commit 570fd3f810)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2021-03-26 00:16:48 +00:00
mergify[bot]
86ce650661 Add timeout for local cluster partition tests (bp #16123) (#16137)
* Add timeout for local cluster partition tests (#16123)

* Add timeout for local cluster partition tests

* fix optimistic conf test logs

* Bump instruction count assertions

(cherry picked from commit e817a6db00)

# Conflicts:
#	local-cluster/Cargo.toml

* Fix conflict

Co-authored-by: Justin Starry <justin@solana.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
2021-03-25 22:56:05 +00:00
mergify[bot]
4dc5a53014 Show bpf-tools download progress (#16135)
(cherry picked from commit 07273bfa9e)

Co-authored-by: Michael Vines <mvines@gmail.com>
2021-03-25 20:55:11 +00:00
mergify[bot]
5e35cf3536 program: Correct clamp in Message::signer_keys() (#16114)
(cherry picked from commit 8b3de72e2a)

Co-authored-by: Trent Nelson <trent@solana.com>
2021-03-25 17:53:34 +00:00
Trent Nelson
e8a8d1efb3 clap-utils: Allow NullSigners outside sign-only mode
(cherry picked from commit 7f0ac6a67c)
2021-03-25 11:10:53 -06:00
mergify[bot]
defd9238fa Simplify account.rent_epoch handling for sysvar rent (bp #16049) (#16118)
* Simplify account.rent_epoch handling for sysvar rent (#16049)

* Add some code for special local testing

* Add comment to store_account_and_update_capitalization

* Simplify account.rent_epoch handling for sysvar rent

* Introduce *_for_test functions

* Add deprecation messages to existing api

(cherry picked from commit 6d5c6c17c5)

# Conflicts:
#	sdk/src/native_loader.rs

* Fix conflicts

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2021-03-25 17:17:43 +09:00
mergify[bot]
5f061dcea1 Support getBlockTime for unfinalized blocks (#16103) (#16110)
(cherry picked from commit a8ef29df27)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-03-25 04:18:00 +00:00
mergify[bot]
e6ee27a738 Add Exodus as Solana Mobile app option (#16100) (#16101)
* Add Exodus as Solana Mobile app option

* Update docs/src/wallet-guide/apps.md

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

Co-authored-by: Davey <35187388+davidzelaya@users.noreply.github.com>
2021-03-24 21:34:58 +00:00
mergify[bot]
dd2d25d698 limits CrdsGossipPull::pull_request_time size (#15793) (#16097)
There is no pruning logic on CrdsGossipPull::pull_request_time
https://github.com/solana-labs/solana/blob/79ac1997d/core/src/crds_gossip_pull.rs#L172-L174
potentially allowing this to take too much memory.

Additionally, CrdsGossipPush::last_pushed_to is pruning recent push
timestamps:
https://github.com/solana-labs/solana/blob/79ac1997d/core/src/crds_gossip_push.rs#L275-L279
instead of the older ones.

Co-authored-by: Nathan Hawkins <utsl@utsl.org>
(cherry picked from commit a6c23648cb)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2021-03-24 20:05:04 +00:00
Dmitri Makarov
9096c3df02 Adjust BPF test programs instruction counts 2021-03-24 11:59:59 +01:00
Dmitri Makarov
9f94c2a9a0 Bump bpf-tools to version v1.3
This brings in the fix for increased compute budget that wasn't caught
when bpf-tools v1.2 were released.
2021-03-24 11:59:59 +01:00
Dmitri Makarov
34213da9f4 Bump bpf-tools to v1.2 and get rid of xargo 2021-03-24 11:59:59 +01:00
mergify[bot]
c3c4991c44 rpc: add getSlotLeaders method (#16057) (#16079)
(cherry picked from commit e7fd7d46cf)

Co-authored-by: Justin Starry <justin@solana.com>
2021-03-23 19:27:18 +00:00
mergify[bot]
9d37a33dcd buffers data shreds to make larger erasure coded sets (bp #15849) (#16074)
* buffers data shreds to make larger erasure coded sets (#15849)

Broadcast stage batches up to 8 entries:
https://github.com/solana-labs/solana/blob/79280b304/core/src/broadcast_stage/broadcast_utils.rs#L26-L29
which will be serialized into some number of shreds and chunked into FEC
sets of at most 32 shreds each:
https://github.com/solana-labs/solana/blob/79280b304/ledger/src/shred.rs#L576-L597
So depending on the size of entries, FEC sets can be small, which may
aggravate loss rate.
For example 16 FEC sets of 2:2 data/code shreds each have higher loss
rate than one 32:32 set.

This commit broadcasts data shreds immediately, but also buffers them
until it has a batch of 32 data shreds, at which point 32 coding shreds
are generated and broadcasted.

(cherry picked from commit 4f82b897bc)

# Conflicts:
#	ledger/src/shred.rs

* removes backport merge conflicts

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2021-03-23 18:23:09 +00:00
mergify[bot]
a04ca03fee renames is_last_in_fec_set back to is_last_data (#15848) (#16075)
https://github.com/solana-labs/solana/pull/10095
renamed is_last_data to is_last_in_fec_set. However, the code shows that
this is actually meant to indicate where the serialized data is
complete:
https://github.com/solana-labs/solana/blob/420174d3d/ledger/src/shred.rs#L599-L600
https://github.com/solana-labs/solana/blob/420174d3d/ledger/src/shred.rs#L229-L231

There are multiple FEC sets for each `&[Entry]` serialized and this flag
does not represent shreds last in FEC sets (only the very last one by
overlap). So the name is wrong and confusing

(cherry picked from commit 3b85cbc504)

Co-authored-by: behzad nouri <behzadnouri@gmail.com>
2021-03-23 16:59:47 +00:00
mergify[bot]
64ce4a6203 solana transfer now requires --allow-unfunded-recipient if the recipient doesn't exist (bp #16060) (#16067)
* transfer now requires --allow-unfunded-recipient if the recipient doesn't exist

(cherry picked from commit 3dff5c9dee)

* Avoid RPC in `--sign-only` mode

Co-authored-by: Trent Nelson <trent.a.b.nelson@gmail.com>
(cherry picked from commit 6271665ba6)

Co-authored-by: Michael Vines <mvines@gmail.com>
2021-03-23 03:54:42 +00:00
mergify[bot]
7ac3c9ec76 Handle blockstore insert dup checks (#16051) (#16066)
(cherry picked from commit d76ad33597)

Co-authored-by: carllin <carl@solana.com>
2021-03-23 00:49:10 +00:00
mergify[bot]
7d91515e8d Make getStakeActivation response consistent for undelegated accounts (#16038) (#16040)
(cherry picked from commit 2ec24d438f)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-03-19 22:07:30 +00:00
mergify[bot]
4e3f2c3d2d program-test: Fix warp and staking issue (#16002) (#16031)
Since program-test creates a test genesis and then adds fees and rent,
some of the genesis accounts get rent-collected after warping.  Most
notably, `StakeConfig` gets rent-collected, causing any stake operations
to fail after warp.  This fix creates genesis with the `Rent` and
`FeeRateGovernor` actually used by the bank.

(cherry picked from commit 6cc22e62d4)

Co-authored-by: Jon Cinque <jon.cinque@gmail.com>
2021-03-19 14:54:58 +00:00
mergify[bot]
8b67ba6d3d docs: SIGUSR1 killing wrapper shell scripts (#16009)
(cherry picked from commit 07dc522981)

Co-authored-by: Trent Nelson <trent@solana.com>
2021-03-19 07:45:34 +00:00
mergify[bot]
c2ce68ab90 Santize instruction index when loading instruction from sysvar (#15942) (#16004)
(cherry picked from commit 4c5660ba7a)

Co-authored-by: Justin Starry <justin@solana.com>
2021-03-19 02:48:41 +00:00
mergify[bot]
fe87cb1cd1 Update to reqwest 0.11.2 (#16000)
(cherry picked from commit 02b81dd05d)

Co-authored-by: Michael Vines <mvines@gmail.com>
2021-03-18 22:12:01 +00:00
mergify[bot]
1c8f6a836a cli cleanup (#15990) (#15997)
(cherry picked from commit 067b390194)

Co-authored-by: Jack May <jack@solana.com>
2021-03-18 20:03:04 +00:00
mergify[bot]
3d5ff7968e rpc: Add config options limiting getConfirmedBlock response data (#15970) (#15995)
* Add new confirmed block struct

* Add RpcConfirmedBlockConfig options

* Configure block response based on new options

* Add client api, use in cli fetch_epoch_rewards

* Update docs

* Apply review suggestions

(cherry picked from commit aa54c468ea)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-03-18 19:33:01 +00:00
Tyera Eulberg
d6160f7744 Avoid panic when validator doesn't have performance samples (#15976)
(cherry picked from commit ba33c9e18e)
2021-03-18 08:28:31 -07:00
mergify[bot]
5e9ce99abf remote-wallet: Expose Ledger app settings (#15978)
(cherry picked from commit 2dabcac0da)

Co-authored-by: Trent Nelson <trent@solana.com>
2021-03-18 09:13:24 +00:00
mergify[bot]
ebd6fe7acb Avoid a panic when --slots-per-epoch is less than MINIMUM_SLOTS_PER_EPOCH (#15975)
(cherry picked from commit 4ab98fff02)

Co-authored-by: Michael Vines <mvines@gmail.com>
2021-03-18 07:17:50 +00:00
mergify[bot]
9e91a2c2fd Add Close instrruction and tooling to upgradeable loader (#15887) (#15972)
(cherry picked from commit 7f500d610c)

Co-authored-by: Jack May <jack@solana.com>
2021-03-18 06:02:57 +00:00
Michael Vines
899f57962a Add --slots-per-epoch argument
(cherry picked from commit 04c99cf7ea)
2021-03-17 17:25:51 -07:00
Michael Vines
3176b00e57 Add --slots-per-epoch validator
(cherry picked from commit c06ff47a90)
2021-03-17 17:25:51 -07:00
Jeff Washington (jwash)
08b9da8397 drop poh lock after record (#15930)
(cherry picked from commit 5460fb10a2)
2021-03-17 17:24:53 -07:00
mergify[bot]
2bc21ecba2 Allow unbounded wallclock processing time in tests (#15961) (#15966)
(cherry picked from commit f548a04fae)

Co-authored-by: carllin <carl@solana.com>
2021-03-18 00:22:06 +00:00
Jeff Washington (jwash)
5b2a65fab3 add metrics for tick producer and poh_recorder (#15931)
(cherry picked from commit 40997d0aef)
2021-03-17 16:36:50 -07:00
mergify[bot]
f5d56eabf3 Build full SPL in CI (bp #15886) (#15964)
* Build full SPL in CI

(cherry picked from commit 82269f1351)

* Avoid changing signature of ProgramTest::add_account

(cherry picked from commit 03180b502d)

Co-authored-by: Michael Vines <mvines@gmail.com>
2021-03-17 22:46:55 +00:00
Michael Vines
af45efb62c Notice the user when the --mint, --bpf-program, or --clone arguments are ignored
(cherry picked from commit 59c19d9fbf)
2021-03-17 14:10:14 -07:00
mergify[bot]
f528cda832 Ignore flaky test_banking_stage_entries_only and test_banking_stage_entryfication (#15959)
(cherry picked from commit 8a9b51952e)

Co-authored-by: Michael Vines <mvines@gmail.com>
2021-03-17 20:34:30 +00:00
mergify[bot]
eeef9f4e59 Separate snapshot location (bp #15840) (#15956)
* Add option for separate snapshot location

(cherry picked from commit 6126878f509c69e23480a5ec22b3271e2b16e072)
(cherry picked from commit 0209d334bd)

* Apply suggestions from code review

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

* add missed suggestion

(cherry picked from commit a43b3674c7)

* Revert to snapshots

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

* Revert to snapshots 2

(cherry picked from commit 20b53eb4b4)

* Revert to removing only tmp-

(cherry picked from commit a5d144b00f)

Co-authored-by: DimAn <diman@diman.io>
Co-authored-by: DimAn <andiman7000@gmail.com>
2021-03-17 20:25:18 +00:00
Michael Vines
32124b59e9 Download snapshot files with a tmp- prefix so they'll automatically be cleaned up if interrupted
(cherry picked from commit 58b980f9cd)
2021-03-17 10:18:18 -07:00
Michael Vines
aa9772f9c0 Replace solana-program-test when building example-helloworld 2021-03-17 09:08:41 -07:00
mergify[bot]
5f183bd773 Add helper for paring down signers to those requried by a tx message (bp #15899) (#15938)
* sdk: Add accessor for signer pubkeys of a tx message

(cherry picked from commit bf33ce8906)

* clap-utils: Add helper to `CliSignerInfo` for getting signers for a message

(cherry picked from commit 4e99f1e634)

Co-authored-by: Trent Nelson <trent@solana.com>
2021-03-17 07:48:47 +00:00
mergify[bot]
2238e5001b solana-install init can now select a pre-release from Github (#15936)
(cherry picked from commit d9176c1903)

Co-authored-by: Michael Vines <mvines@gmail.com>
2021-03-17 04:31:55 +00:00
mergify[bot]
79fa7ef55c CLI: Support dumping the TX message in sign-only mode (#15933)
(cherry picked from commit 672e9c640f)

Co-authored-by: Trent Nelson <trent@solana.com>
2021-03-17 04:13:21 +00:00
mergify[bot]
07df827411 Bump tokio to 1.1 (#15926) (#15928)
(cherry picked from commit 654449ce91)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-03-16 23:29:55 +00:00
mergify[bot]
a259ff0e72 Wallclock BankingStage Throttle (#15731) (#15890)
(cherry picked from commit c1ba265dd9)

Co-authored-by: carllin <carl@solana.com>
2021-03-16 21:12:59 +00:00
mergify[bot]
d7d3e767e7 fix: compute pre/post token balances on all accounts if token program present (#15900) (#15923)
* fix: compute pre/post token balances on all accounts if token program present

* fix: skip token program in balance query

* fix: prevent program ids from being collected

(cherry picked from commit 61112d4826)

Co-authored-by: Josh <josh.hundley@gmail.com>
2021-03-16 18:23:29 +00:00
mergify[bot]
6e8aa9af17 nit: fix spelling (#15908) (#15911)
(cherry picked from commit 5760cf0f41)

# Conflicts:
#	sdk/src/feature_set.rs

Co-authored-by: Jack May <jack@solana.com>
2021-03-16 10:58:39 -07:00
mergify[bot]
0236de7bc8 Encourage use of the default --ledger location (#15921)
(cherry picked from commit 1c261d293f)

Co-authored-by: Michael Vines <mvines@gmail.com>
2021-03-16 16:58:14 +00:00
mergify[bot]
899bd1572a Show flags for accounts in tx by solana confirm (#15804) (#15906)
* Show flags for accounts in tx by solana confirm

* Address review comments

* Improve comment a bit

* Apply suggestions from code review

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

* Further apply review suggestions

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

Co-authored-by: Ryo Onodera <ryoqun@gmail.com>
2021-03-16 10:43:06 +00:00
mergify[bot]
97ec4cd44e Cli: better estimate of epoch time elapsed/remaining (#15893) (#15918)
* Add rpc_client api for getRecentPerformanceSamples

* Prep fn for variable avg slot time

* Use recent-perf-samples to more-accurately estimate epoch completed times

* Spell out average

(cherry picked from commit 3726358f51)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-03-16 09:58:51 +00:00
mergify[bot]
5500970a7e Add cargo-bpf-test --no-run flag, matching cargo-test (#15916)
(cherry picked from commit eb19e11688)

Co-authored-by: Michael Vines <mvines@gmail.com>
2021-03-16 09:45:35 +00:00
Michael Vines
caea04d8d5 Pin solana crate versions to prevent downstream users from accidentally mixing crate versions 2021-03-16 08:41:28 +00:00
Michael Vines
b1a90c3580 =1.6.1 2021-03-16 08:41:28 +00:00
mergify[bot]
5bd4e38345 Charge compute budget for bytes passed via cpi (#15874) (#15905)
(cherry picked from commit ad9901d7c6)

Co-authored-by: Jack May <jack@solana.com>
2021-03-16 07:57:32 +00:00
mergify[bot]
fddba08571 Improve Instruction::new deprecation warning (#15896)
(cherry picked from commit 8567b41d5f)

Co-authored-by: Michael Vines <mvines@gmail.com>
2021-03-16 05:18:31 +00:00
Michael Vines
87963764fa Export tokio for program-test clients
(cherry picked from commit 430ed6d774)
2021-03-15 22:14:17 -07:00
mergify[bot]
b691a159dd increment_cargo_version.sh tune ups (bp #15880) (#15892)
* Disallow version bump with dirty working tree

(cherry picked from commit 853e735edf)

* Ignore `not_paths` for `*.md` files when bumping version

(cherry picked from commit 510760d81b)

* Also ignore `*/node_modules/*` paths when bumping version

(cherry picked from commit 2bf46b789f)

Co-authored-by: Trent Nelson <trent@solana.com>
2021-03-16 02:07:46 +00:00
mergify[bot]
5af1d48be8 Display actual account length (#15875) (#15884)
(cherry picked from commit 60e5fd11c9)

Co-authored-by: Jack May <jack@solana.com>
2021-03-16 01:01:25 +00:00
mergify[bot]
3b3ec3313f Fix real_number_string_trimmed zero-decimal behavior (#15873) (#15877)
* Add failing test

* Don't strip zeroes from zero-decimal amounts

* Add zero-case test

(cherry picked from commit c40bd5f394)

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
2021-03-15 21:33:01 +00:00
Michael Vines
be00246fb5 Bump version to v1.6.1 2021-03-15 14:47:58 -06:00
Michael Vines
1d80ba9edf Update cargo lock files on version bump 2021-03-15 14:47:58 -06:00
mergify[bot]
4bcf976ecd Fix delinquent stake display (#15839)
(cherry picked from commit eab182188a)

Co-authored-by: Michael Vines <mvines@gmail.com>
2021-03-13 20:27:25 +00:00
1183 changed files with 65178 additions and 147347 deletions

View File

@@ -2,6 +2,6 @@
"_public_key": "ae29f4f7ad2fc92de70d470e411c8426d5d48db8817c9e3dae574b122192335f",
"_comment": "These credentials are encrypted and pose no risk",
"environment": {
"CODECOV_TOKEN": "EJ[1:KToenD1Sr3w82lHGxz1n+j3hwNlLk/1pYrjZHlvY6kE=:hN1Q25omtJ+4yYVn+qzIsPLKT3O6J9XN:DMLNLXi/pkWgvwF6gNIcNF222sgsRR9LnwLZYj0P0wGj7q6w8YQnd1Rskj+sRroI/z5pQg==]"
"CODECOV_TOKEN": "EJ[1:Z7OneT3RdJJ0DipCHQ7rC84snQ+FPbgHwZADQiz54wk=:3K68mE38LJ2RB98VWmjuNLFBNn1XTGR4:cR4r05/TOZQKmEZp1v4CSgUJtC6QJiOaL85QjXW0qZ061fMnsBA8AtAPMDoDq4WCGOZM1A==]"
}
}

View File

@@ -36,7 +36,4 @@ export CARGO_TARGET_CACHE=$HOME/cargo-target-cache/"$CHANNEL"-"$BUILDKITE_LABEL"
# `std:
# "found possibly newer version of crate `std` which `xyz` depends on
rm -rf target/bpfel-unknown-unknown
if [[ $BUILDKITE_LABEL = "stable-perf" ]]; then
rm -rf target/release
fi
)

View File

@@ -38,7 +38,6 @@ jobs:
- readlink -f .
script:
- source ci/env.sh
- rustup set profile default
- ci/publish-tarball.sh
deploy:
- provider: s3
@@ -74,7 +73,7 @@ jobs:
language: node_js
node_js:
- "lts/*"
- "node"
cache:
directories:

2307
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -21,7 +21,6 @@ members = [
"perf",
"validator",
"genesis",
"genesis-utils",
"gossip",
"install",
"keygen",
@@ -32,6 +31,7 @@ members = [
"log-analyzer",
"merkle-root-bench",
"merkle-tree",
"stake-o-matic",
"storage-bigtable",
"storage-proto",
"streamer",
@@ -39,20 +39,21 @@ members = [
"metrics",
"net-shaper",
"notifier",
"poh",
"poh-bench",
"program-test",
"programs/secp256k1",
"programs/bpf_loader",
"programs/budget",
"programs/config",
"programs/exchange",
"programs/failure",
"programs/noop",
"programs/ownable",
"programs/secp256k1",
"programs/stake",
"programs/vest",
"programs/vote",
"remote-wallet",
"rpc",
"ramp-tps",
"runtime",
"runtime/store-tool",
"sdk",
@@ -60,6 +61,7 @@ members = [
"sdk/cargo-test-bpf",
"scripts",
"stake-accounts",
"stake-monitor",
"sys-tuner",
"tokens",
"transaction-status",
@@ -75,11 +77,3 @@ members = [
exclude = [
"programs/bpf",
]
# TODO: Remove once the "simd-accel" feature from the reed-solomon-erasure
# dependency is supported on Apple M1. v2 of the feature resolver is needed to
# specify arch-specific features.
resolver = "2"
[profile.dev]
split-debuginfo = "unpacked"

View File

@@ -1,6 +1,6 @@
<p align="center">
<a href="https://solana.com">
<img alt="Solana" src="https://i.imgur.com/IKyzQ6T.png" width="250" />
<img alt="Solana" src="https://i.imgur.com/OMnvVEz.png" width="250" />
</a>
</p>
@@ -19,7 +19,7 @@ $ source $HOME/.cargo/env
$ rustup component add rustfmt
```
Please make sure you are always using the latest stable rust version by running:
Please sure you are always using the latest stable rust version by running:
```bash
$ rustup update
@@ -32,12 +32,6 @@ $ sudo apt-get update
$ sudo apt-get install libssl-dev libudev-dev pkg-config zlib1g-dev llvm clang make
```
On Mac M1s, make sure you set up your terminal & homebrew [to use](https://5balloons.info/correct-way-to-install-and-use-homebrew-on-m1-macs/) Rosetta. You can install it with:
```bash
$ softwareupdate --install-rosetta
```
## **2. Download the source code.**
```bash
@@ -51,6 +45,11 @@ $ cd solana
$ cargo build
```
## **4. Run a minimal local cluster.**
```bash
$ ./run.sh
```
# Testing
**Run the test suite:**

View File

@@ -42,36 +42,14 @@ RPC DoS/Crashes:
$5,000 USD in locked SOL tokens (locked for 12 months)
* RPC attacks
Out of Scope:
The following components are out of scope for the bounty program
* Metrics: `/metrics` in the monorepo as well as https://metrics.solana.com
* Explorer: `/explorer` in the monorepo as well as https://explorer.solana.com
* Any encrypted credentials, auth tokens, etc. checked into the repo
* Bugs in dependencies. Please take them upstream!
* Attacks that require social engineering
Eligibility:
* The participant submitting the bug report shall follow the process outlined within this document
* The participant submitting the bug bounty shall follow the process outlined within this document
* Valid exploits can be eligible even if they are not successfully executed on the cluster
* Multiple submissions for the same class of exploit are still eligible for compensation, though may be compensated at a lower rate, however these will be assessed on a case-by-case basis
* Participants must complete KYC and sign the participation agreement here when the registrations are open https://solana.com/validator-registration. Security exploits will still be assessed and open for submission at all times. This needs only be done prior to distribution of tokens.
Payment of Bug Bounties:
* Payments for eligible bug reports are distributed monthly.
* Bounties for all bug reports submitted in a given month are paid out in the middle of the
following month.
* The SOL/USD conversion rate used for payments is the market price at the end of
the last day of the month for the month in which the bug was submitted.
* The reference for this price is the Closing Price given by Coingecko.com on
that date given here:
https://www.coingecko.com/en/coins/solana/historical_data/usd#panel
* For example, for all bugs submitted in March 2021, the SOL/USD price for bug
payouts is the Close price on 2021-03-31 of $19.49. This applies to all bugs
submitted in March 2021, to be paid in mid-April 2021.
* Bug bounties are paid out in
[stake accounts](https://solana.com/staking) with a
[lockup](https://docs.solana.com/staking/stake-accounts#lockups)
expiring 12 months from the last day of the month in which the bug was submitted.
Notes:
* All locked tokens can be staked during the lockup period
<a name="process"></a>
## Incident Response Process

View File

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

View File

@@ -28,7 +28,6 @@ use {
pub type StringAmount = String;
pub type StringDecimals = String;
pub const MAX_BASE58_BYTES: usize = 128;
/// A duplicate representation of an Account for pretty JSON serialization
#[derive(Serialize, Deserialize, Clone, Debug)]
@@ -49,7 +48,7 @@ pub enum UiAccountData {
Binary(String, UiAccountEncoding),
}
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum UiAccountEncoding {
Binary, // Legacy. Retained for RPC backwards compatibility
@@ -61,53 +60,41 @@ pub enum UiAccountEncoding {
}
impl UiAccount {
fn encode_bs58<T: ReadableAccount>(
account: &T,
data_slice_config: Option<UiDataSliceConfig>,
) -> String {
if account.data().len() <= MAX_BASE58_BYTES {
bs58::encode(slice_data(account.data(), data_slice_config)).into_string()
} else {
"error: data too large for bs58 encoding".to_string()
}
}
pub fn encode<T: ReadableAccount>(
pubkey: &Pubkey,
account: &T,
account: T,
encoding: UiAccountEncoding,
additional_data: Option<AccountAdditionalData>,
data_slice_config: Option<UiDataSliceConfig>,
) -> Self {
let data = match encoding {
UiAccountEncoding::Binary => {
let data = Self::encode_bs58(account, data_slice_config);
UiAccountData::LegacyBinary(data)
}
UiAccountEncoding::Base58 => {
let data = Self::encode_bs58(account, data_slice_config);
UiAccountData::Binary(data, encoding)
}
UiAccountEncoding::Binary => UiAccountData::LegacyBinary(
bs58::encode(slice_data(&account.data(), data_slice_config)).into_string(),
),
UiAccountEncoding::Base58 => UiAccountData::Binary(
bs58::encode(slice_data(&account.data(), data_slice_config)).into_string(),
encoding,
),
UiAccountEncoding::Base64 => UiAccountData::Binary(
base64::encode(slice_data(account.data(), data_slice_config)),
base64::encode(slice_data(&account.data(), data_slice_config)),
encoding,
),
UiAccountEncoding::Base64Zstd => {
let mut encoder = zstd::stream::write::Encoder::new(Vec::new(), 0).unwrap();
match encoder
.write_all(slice_data(account.data(), data_slice_config))
.write_all(slice_data(&account.data(), data_slice_config))
.and_then(|()| encoder.finish())
{
Ok(zstd_data) => UiAccountData::Binary(base64::encode(zstd_data), encoding),
Err(_) => UiAccountData::Binary(
base64::encode(slice_data(account.data(), data_slice_config)),
base64::encode(slice_data(&account.data(), data_slice_config)),
UiAccountEncoding::Base64,
),
}
}
UiAccountEncoding::JsonParsed => {
if let Ok(parsed_data) =
parse_account_data(pubkey, account.owner(), account.data(), additional_data)
parse_account_data(pubkey, &account.owner(), &account.data(), additional_data)
{
UiAccountData::Json(parsed_data)
} else {
@@ -179,7 +166,7 @@ impl Default for UiFeeCalculator {
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UiDataSliceConfig {
pub offset: usize,
@@ -237,7 +224,7 @@ mod test {
fn test_base64_zstd() {
let encoded_account = UiAccount::encode(
&Pubkey::default(),
&AccountSharedData::from(Account {
AccountSharedData::from(Account {
data: vec![0; 1024],
..Account::default()
}),

View File

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

View File

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

View File

@@ -9,13 +9,7 @@ pub fn parse_nonce(data: &[u8]) -> Result<UiNonceState, ParseAccountError> {
.map_err(|_| ParseAccountError::from(InstructionError::InvalidAccountData))?;
let nonce_state = nonce_state.convert_to_current();
match nonce_state {
// This prevents parsing an allocated System-owned account with empty data of any non-zero
// length as `uninitialized` nonce. An empty account of the wrong length can never be
// initialized as a nonce account, and an empty account of the correct length may not be an
// uninitialized nonce account, since it can be assigned to another program.
State::Uninitialized => Err(ParseAccountError::from(
InstructionError::InvalidAccountData,
)),
State::Uninitialized => Ok(UiNonceState::Uninitialized),
State::Initialized(data) => Ok(UiNonceState::Initialized(UiNonceData {
authority: data.authority.to_string(),
blockhash: data.blockhash.to_string(),

View File

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

View File

@@ -14,23 +14,23 @@ use std::str::FromStr;
// A helper function to convert spl_token_v2_0::id() as spl_sdk::pubkey::Pubkey to
// solana_sdk::pubkey::Pubkey
pub fn spl_token_id_v2_0() -> Pubkey {
Pubkey::new_from_array(spl_token_v2_0::id().to_bytes())
Pubkey::from_str(&spl_token_v2_0::id().to_string()).unwrap()
}
// A helper function to convert spl_token_v2_0::native_mint::id() as spl_sdk::pubkey::Pubkey to
// solana_sdk::pubkey::Pubkey
pub fn spl_token_v2_0_native_mint() -> Pubkey {
Pubkey::new_from_array(spl_token_v2_0::native_mint::id().to_bytes())
Pubkey::from_str(&spl_token_v2_0::native_mint::id().to_string()).unwrap()
}
// A helper function to convert a solana_sdk::pubkey::Pubkey to spl_sdk::pubkey::Pubkey
pub fn spl_token_v2_0_pubkey(pubkey: &Pubkey) -> SplTokenPubkey {
SplTokenPubkey::new_from_array(pubkey.to_bytes())
SplTokenPubkey::from_str(&pubkey.to_string()).unwrap()
}
// A helper function to convert a spl_sdk::pubkey::Pubkey to solana_sdk::pubkey::Pubkey
pub fn pubkey_from_spl_token_v2_0(pubkey: &SplTokenPubkey) -> Pubkey {
Pubkey::new_from_array(pubkey.to_bytes())
Pubkey::from_str(&pubkey.to_string()).unwrap()
}
pub fn parse_token(

View File

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

View File

@@ -6,12 +6,10 @@ use rayon::prelude::*;
use solana_measure::measure::Measure;
use solana_runtime::{
accounts::{create_test_accounts, update_accounts_bench, Accounts},
accounts_db::AccountShrinkThreshold,
accounts_index::AccountSecondaryIndexes,
ancestors::Ancestors,
accounts_index::Ancestors,
};
use solana_sdk::{genesis_config::ClusterType, pubkey::Pubkey};
use std::{env, fs, path::PathBuf};
use std::{collections::HashSet, env, fs, path::PathBuf};
fn main() {
solana_logger::setup();
@@ -60,13 +58,8 @@ fn main() {
if fs::remove_dir_all(path.clone()).is_err() {
println!("Warning: Couldn't remove {:?}", path);
}
let accounts = Accounts::new_with_config(
vec![path],
&ClusterType::Testnet,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
);
let accounts =
Accounts::new_with_config(vec![path], &ClusterType::Testnet, HashSet::new(), false);
println!("Creating {} accounts", num_accounts);
let mut create_time = Measure::start("create accounts");
let pubkeys: Vec<_> = (0..num_slots)
@@ -90,19 +83,17 @@ fn main() {
num_slots,
create_time
);
let mut ancestors = Vec::with_capacity(num_slots);
ancestors.push(0);
let mut ancestors: Ancestors = vec![(0, 0)].into_iter().collect();
for i in 1..num_slots {
ancestors.push(i as u64);
ancestors.insert(i as u64, i - 1);
accounts.add_root(i as u64);
}
let ancestors = Ancestors::from(ancestors);
let mut elapsed = vec![0; iterations];
let mut elapsed_store = vec![0; iterations];
for x in 0..iterations {
if clean {
let mut time = Measure::start("clean");
accounts.accounts_db.clean_accounts(None, false);
accounts.accounts_db.clean_accounts(None);
time.stop();
println!("{}", time);
for slot in 0..num_slots {
@@ -121,8 +112,6 @@ fn main() {
solana_sdk::clock::Slot::default(),
&ancestors,
None,
false,
None,
);
time_store.stop();
if results != results_store {

View File

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

View File

@@ -1,13 +1,13 @@
#![allow(clippy::integer_arithmetic)]
use clap::{crate_description, crate_name, value_t, values_t_or_exit, App, Arg};
use clap::{crate_description, crate_name, value_t, value_t_or_exit, App, Arg};
use log::*;
use rand::{thread_rng, Rng};
use rayon::prelude::*;
use solana_account_decoder::parse_token::spl_token_v2_0_pubkey;
use solana_clap_utils::input_parsers::pubkey_of;
use solana_client::rpc_client::RpcClient;
use solana_core::gossip_service::discover;
use solana_faucet::faucet::{request_airdrop_transaction, FAUCET_PORT};
use solana_gossip::gossip_service::discover;
use solana_measure::measure::Measure;
use solana_runtime::inline_spl_token_v2_0;
use solana_sdk::{
@@ -20,7 +20,6 @@ use solana_sdk::{
timing::timestamp,
transaction::Transaction,
};
use solana_streamer::socket::SocketAddrSpace;
use solana_transaction_status::parse_token::spl_token_v2_0_instruction;
use std::{
net::SocketAddr,
@@ -33,10 +32,6 @@ use std::{
time::{Duration, Instant},
};
// Create and close messages both require 2 signatures; if transaction construction changes, update
// this magic number
const NUM_SIGNATURES: u64 = 2;
pub fn airdrop_lamports(
client: &RpcClient,
faucet_addr: &SocketAddr,
@@ -56,7 +51,7 @@ pub fn airdrop_lamports(
);
let (blockhash, _fee_calculator) = client.get_recent_blockhash().unwrap();
match request_airdrop_transaction(faucet_addr, &id.pubkey(), airdrop_amount, blockhash) {
match request_airdrop_transaction(&faucet_addr, &id.pubkey(), airdrop_amount, blockhash) {
Ok(transaction) => {
let mut tries = 0;
loop {
@@ -190,13 +185,14 @@ impl TransactionExecutor {
let mut start = Measure::start("sig_status");
let statuses: Vec<_> = sigs_w
.chunks(200)
.flat_map(|sig_chunk| {
.map(|sig_chunk| {
let only_sigs: Vec<_> = sig_chunk.iter().map(|s| s.0).collect();
client
.get_signature_statuses(&only_sigs)
.expect("status fail")
.value
})
.flatten()
.collect();
let mut num_cleared = 0;
let start_len = sigs_w.len();
@@ -360,11 +356,11 @@ fn make_close_message(
fn run_accounts_bench(
entrypoint_addr: SocketAddr,
faucet_addr: SocketAddr,
payer_keypairs: &[&Keypair],
keypair: &Keypair,
iterations: usize,
maybe_space: Option<u64>,
batch_size: usize,
close_nth_batch: u64,
close_nth: u64,
maybe_lamports: Option<u64>,
num_instructions: usize,
mint: Option<Pubkey>,
@@ -373,7 +369,7 @@ fn run_accounts_bench(
let client =
RpcClient::new_socket_with_commitment(entrypoint_addr, CommitmentConfig::confirmed());
info!("Targeting {}", entrypoint_addr);
info!("Targetting {}", entrypoint_addr);
let mut last_blockhash = Instant::now();
let mut last_log = Instant::now();
@@ -382,10 +378,7 @@ fn run_accounts_bench(
let mut tx_sent_count = 0;
let mut total_accounts_created = 0;
let mut total_accounts_closed = 0;
let mut balances: Vec<_> = payer_keypairs
.iter()
.map(|keypair| client.get_balance(&keypair.pubkey()).unwrap_or(0))
.collect();
let mut balance = client.get_balance(&keypair.pubkey()).unwrap_or(0);
let mut last_balance = Instant::now();
let default_max_lamports = 1000;
@@ -402,7 +395,7 @@ fn run_accounts_bench(
max_closed: Arc::new(AtomicU64::default()),
};
info!("Starting balance(s): {:?}", balances);
info!("Starting balance: {}", balance);
let executor = TransactionExecutor::new(entrypoint_addr);
@@ -412,94 +405,81 @@ fn run_accounts_bench(
last_blockhash = Instant::now();
}
let fee = recent_blockhash
.1
.lamports_per_signature
.saturating_mul(NUM_SIGNATURES);
let message = make_create_message(
keypair,
&base_keypair,
seed_tracker.max_created.clone(),
num_instructions,
min_balance,
maybe_space,
mint,
);
let fee = recent_blockhash.1.calculate_fee(&message);
let lamports = min_balance + fee;
for (i, balance) in balances.iter_mut().enumerate() {
if *balance < lamports || last_balance.elapsed().as_millis() > 2000 {
if let Ok(b) = client.get_balance(&payer_keypairs[i].pubkey()) {
*balance = b;
}
last_balance = Instant::now();
if *balance < lamports * 2 {
info!(
"Balance {} is less than needed: {}, doing aidrop...",
balance, lamports
);
if !airdrop_lamports(
&client,
&faucet_addr,
payer_keypairs[i],
lamports * 100_000,
) {
warn!("failed airdrop, exiting");
return;
}
if balance < lamports || last_balance.elapsed().as_millis() > 2000 {
if let Ok(b) = client.get_balance(&keypair.pubkey()) {
balance = b;
}
last_balance = Instant::now();
if balance < lamports {
info!(
"Balance {} is less than needed: {}, doing aidrop...",
balance, lamports
);
if !airdrop_lamports(&client, &faucet_addr, keypair, lamports * 100_000) {
warn!("failed airdrop, exiting");
return;
}
}
}
// Create accounts
let sigs_len = executor.num_outstanding();
if sigs_len < batch_size {
let num_to_create = batch_size - sigs_len;
if num_to_create >= payer_keypairs.len() {
info!("creating {} new", num_to_create);
let chunk_size = num_to_create / payer_keypairs.len();
if chunk_size > 0 {
for (i, keypair) in payer_keypairs.iter().enumerate() {
let txs: Vec<_> = (0..chunk_size)
.into_par_iter()
.map(|_| {
let message = make_create_message(
keypair,
&base_keypair,
seed_tracker.max_created.clone(),
num_instructions,
min_balance,
maybe_space,
mint,
);
let signers: Vec<&Keypair> = vec![keypair, &base_keypair];
Transaction::new(&signers, message, recent_blockhash.0)
})
.collect();
balances[i] = balances[i].saturating_sub(lamports * txs.len() as u64);
info!("txs: {}", txs.len());
let new_ids = executor.push_transactions(txs);
info!("ids: {}", new_ids.len());
tx_sent_count += new_ids.len();
total_accounts_created += num_instructions * new_ids.len();
}
}
}
info!("creating {} new", num_to_create);
let txs: Vec<_> = (0..num_to_create)
.into_par_iter()
.map(|_| {
let message = make_create_message(
keypair,
&base_keypair,
seed_tracker.max_created.clone(),
num_instructions,
min_balance,
maybe_space,
mint,
);
let signers: Vec<&Keypair> = vec![keypair, &base_keypair];
Transaction::new(&signers, message, recent_blockhash.0)
})
.collect();
balance = balance.saturating_sub(lamports * txs.len() as u64);
info!("txs: {}", txs.len());
let new_ids = executor.push_transactions(txs);
info!("ids: {}", new_ids.len());
tx_sent_count += new_ids.len();
total_accounts_created += num_instructions * new_ids.len();
if close_nth_batch > 0 {
let num_batches_to_close =
total_accounts_created as u64 / (close_nth_batch * batch_size as u64);
let expected_closed = num_batches_to_close * batch_size as u64;
let max_closed_seed = seed_tracker.max_closed.load(Ordering::Relaxed);
// Close every account we've created with seed between max_closed_seed..expected_closed
if max_closed_seed < expected_closed {
let txs: Vec<_> = (0..expected_closed - max_closed_seed)
if close_nth > 0 {
let expected_closed = total_accounts_created as u64 / close_nth;
if expected_closed > total_accounts_closed {
let txs: Vec<_> = (0..expected_closed - total_accounts_closed)
.into_par_iter()
.map(|_| {
let message = make_close_message(
payer_keypairs[0],
keypair,
&base_keypair,
seed_tracker.max_closed.clone(),
1,
min_balance,
mint.is_some(),
);
let signers: Vec<&Keypair> = vec![payer_keypairs[0], &base_keypair];
let signers: Vec<&Keypair> = vec![keypair, &base_keypair];
Transaction::new(&signers, message, recent_blockhash.0)
})
.collect();
balances[0] = balances[0].saturating_sub(fee * txs.len() as u64);
balance = balance.saturating_sub(fee * txs.len() as u64);
info!("close txs: {}", txs.len());
let new_ids = executor.push_transactions(txs);
info!("close ids: {}", new_ids.len());
@@ -514,8 +494,8 @@ fn run_accounts_bench(
count += 1;
if last_log.elapsed().as_millis() > 3000 {
info!(
"total_accounts_created: {} total_accounts_closed: {} tx_sent_count: {} loop_count: {} balance(s): {:?}",
total_accounts_created, total_accounts_closed, tx_sent_count, count, balances
"total_accounts_created: {} total_accounts_closed: {} tx_sent_count: {} loop_count: {} balance: {}",
total_accounts_created, total_accounts_closed, tx_sent_count, count, balance
);
last_log = Instant::now();
}
@@ -566,7 +546,6 @@ fn main() {
Arg::with_name("identity")
.long("identity")
.takes_value(true)
.multiple(true)
.value_name("FILE")
.help("keypair file"),
)
@@ -578,17 +557,11 @@ fn main() {
.help("Number of transactions to send per batch"),
)
.arg(
Arg::with_name("close_nth_batch")
Arg::with_name("close_nth")
.long("close-frequency")
.takes_value(true)
.value_name("BYTES")
.help(
"Every `n` batches, create a batch of close transactions for
the earliest remaining batch of accounts created.
Note: Should be > 1 to avoid situations where the close \
transactions will be submitted before the corresponding \
create transactions have been confirmed",
),
.help("Send close transactions after this many accounts created"),
)
.arg(
Arg::with_name("num_instructions")
@@ -638,7 +611,7 @@ fn main() {
let space = value_t!(matches, "space", u64).ok();
let lamports = value_t!(matches, "lamports", u64).ok();
let batch_size = value_t!(matches, "batch_size", usize).unwrap_or(4);
let close_nth_batch = value_t!(matches, "close_nth_batch", u64).unwrap_or(0);
let close_nth = value_t!(matches, "close_nth", u64).unwrap_or(0);
let iterations = value_t!(matches, "iterations", usize).unwrap_or(10);
let num_instructions = value_t!(matches, "num_instructions", usize).unwrap_or(1);
if num_instructions == 0 || num_instructions > 500 {
@@ -648,30 +621,20 @@ fn main() {
let mint = pubkey_of(&matches, "mint");
let payer_keypairs: Vec<_> = values_t_or_exit!(matches, "identity", String)
.iter()
.map(|keypair_string| {
read_keypair_file(keypair_string)
.unwrap_or_else(|_| panic!("bad keypair {:?}", keypair_string))
})
.collect();
let mut payer_keypair_refs: Vec<&Keypair> = vec![];
for keypair in payer_keypairs.iter() {
payer_keypair_refs.push(keypair);
}
let keypair =
read_keypair_file(&value_t_or_exit!(matches, "identity", String)).expect("bad keypair");
let rpc_addr = if !skip_gossip {
info!("Finding cluster entry: {:?}", entrypoint_addr);
let (gossip_nodes, _validators) = discover(
None, // keypair
None,
Some(&entrypoint_addr),
None, // num_nodes
Duration::from_secs(60), // timeout
None, // find_node_by_pubkey
Some(&entrypoint_addr), // find_node_by_gossip_addr
None, // my_gossip_addr
0, // my_shred_version
SocketAddrSpace::Unspecified,
None,
Some(60),
None,
Some(&entrypoint_addr),
None,
0,
)
.unwrap_or_else(|err| {
eprintln!("Failed to discover {} node: {:?}", entrypoint_addr, err);
@@ -688,11 +651,11 @@ fn main() {
run_accounts_bench(
rpc_addr,
faucet_addr,
&payer_keypair_refs,
&keypair,
iterations,
space,
batch_size,
close_nth_batch,
close_nth,
lamports,
num_instructions,
mint,
@@ -723,22 +686,22 @@ pub mod test {
};
let faucet_addr = SocketAddr::from(([127, 0, 0, 1], 9900));
let cluster = LocalCluster::new(&mut config, SocketAddrSpace::Unspecified);
let cluster = LocalCluster::new(&mut config);
let iterations = 10;
let maybe_space = None;
let batch_size = 100;
let close_nth_batch = 100;
let close_nth = 100;
let maybe_lamports = None;
let num_instructions = 2;
let mut start = Measure::start("total accounts run");
run_accounts_bench(
cluster.entry_point_info.rpc,
faucet_addr,
&[&cluster.funding_keypair],
&cluster.funding_keypair,
iterations,
maybe_space,
batch_size,
close_nth_batch,
close_nth,
maybe_lamports,
num_instructions,
None,

View File

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

View File

@@ -4,8 +4,13 @@ use crossbeam_channel::unbounded;
use log::*;
use rand::{thread_rng, Rng};
use rayon::prelude::*;
use solana_core::banking_stage::BankingStage;
use solana_gossip::{cluster_info::ClusterInfo, cluster_info::Node};
use solana_core::{
banking_stage::{create_test_recorder, BankingStage},
cluster_info::ClusterInfo,
cluster_info::Node,
poh_recorder::PohRecorder,
poh_recorder::WorkingBankEntry,
};
use solana_ledger::{
blockstore::Blockstore,
genesis_utils::{create_genesis_config, GenesisConfigInfo},
@@ -13,7 +18,6 @@ use solana_ledger::{
};
use solana_measure::measure::Measure;
use solana_perf::packet::to_packets_chunked;
use solana_poh::poh_recorder::{create_test_recorder, PohRecorder, WorkingBankEntry};
use solana_runtime::{
accounts_background_service::AbsRequestSender, bank::Bank, bank_forks::BankForks,
};
@@ -25,7 +29,6 @@ use solana_sdk::{
timing::{duration_as_us, timestamp},
transaction::Transaction,
};
use solana_streamer::socket::SocketAddrSpace;
use std::{
sync::{atomic::Ordering, mpsc::Receiver, Arc, Mutex},
thread::sleep,
@@ -75,7 +78,7 @@ fn make_accounts_txs(
.into_par_iter()
.map(|_| {
let mut new = dummy.clone();
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen::<u8>()).collect();
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect();
if !same_payer {
new.message.account_keys[0] = solana_sdk::pubkey::new_rand();
}
@@ -166,7 +169,6 @@ fn main() {
let (verified_sender, verified_receiver) = unbounded();
let (vote_sender, vote_receiver) = unbounded();
let (tpu_vote_sender, tpu_vote_receiver) = unbounded();
let (replay_vote_sender, _replay_vote_receiver) = unbounded();
let bank0 = Bank::new(&genesis_config);
let mut bank_forks = BankForks::new(bank0);
@@ -187,7 +189,7 @@ fn main() {
genesis_config.hash(),
);
// Ignore any pesky duplicate signature errors in the case we are using single-payer
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen::<u8>()).collect();
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();
@@ -197,7 +199,7 @@ fn main() {
if !skip_sanity {
//sanity check, make sure all the transactions can execute sequentially
transactions.iter().for_each(|tx| {
let res = bank.process_transaction(tx);
let res = bank.process_transaction(&tx);
assert!(res.is_ok(), "sanity test transactions error: {:?}", res);
});
bank.clear_signatures();
@@ -217,17 +219,12 @@ fn main() {
);
let (exit, poh_recorder, poh_service, signal_receiver) =
create_test_recorder(&bank, &blockstore, None);
let cluster_info = ClusterInfo::new(
Node::new_localhost().info,
Arc::new(Keypair::new()),
SocketAddrSpace::Unspecified,
);
let cluster_info = ClusterInfo::new_with_invalid_keypair(Node::new_localhost().info);
let cluster_info = Arc::new(cluster_info);
let banking_stage = BankingStage::new(
&cluster_info,
&poh_recorder,
verified_receiver,
tpu_vote_receiver,
vote_receiver,
None,
replay_vote_sender,
@@ -358,7 +355,7 @@ fn main() {
if bank.slot() > 0 && bank.slot() % 16 == 0 {
for tx in transactions.iter_mut() {
tx.message.recent_blockhash = bank.last_blockhash();
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen::<u8>()).collect();
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);
@@ -383,7 +380,6 @@ fn main() {
);
drop(verified_sender);
drop(tpu_vote_sender);
drop(vote_sender);
exit.store(true, Ordering::Relaxed);
banking_stage.join().unwrap();

View File

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

View File

@@ -10,14 +10,8 @@ use futures::{future::join_all, Future, FutureExt};
pub use solana_banks_interface::{BanksClient as TarpcClient, TransactionStatus};
use solana_banks_interface::{BanksRequest, BanksResponse};
use solana_program::{
clock::Clock,
clock::Slot,
fee_calculator::FeeCalculator,
hash::Hash,
program_pack::Pack,
pubkey::Pubkey,
rent::Rent,
sysvar::{self, Sysvar},
clock::Slot, fee_calculator::FeeCalculator, hash::Hash, program_pack::Pack, pubkey::Pubkey,
rent::Rent, sysvar,
};
use solana_sdk::{
account::{from_account, Account},
@@ -69,7 +63,7 @@ impl BanksClient {
&mut self,
ctx: Context,
commitment: CommitmentLevel,
) -> impl Future<Output = io::Result<(FeeCalculator, Hash, u64)>> + '_ {
) -> impl Future<Output = io::Result<(FeeCalculator, Hash, Slot)>> + '_ {
self.inner
.get_fees_with_commitment_and_context(ctx, commitment)
}
@@ -91,14 +85,6 @@ impl BanksClient {
self.inner.get_slot_with_context(ctx, commitment)
}
pub fn get_block_height_with_context(
&mut self,
ctx: Context,
commitment: CommitmentLevel,
) -> impl Future<Output = io::Result<Slot>> + '_ {
self.inner.get_block_height_with_context(ctx, commitment)
}
pub fn process_transaction_with_commitment_and_context(
&mut self,
ctx: Context,
@@ -129,39 +115,24 @@ impl BanksClient {
self.send_transaction_with_context(context::current(), transaction)
}
/// Return the cluster clock
pub fn get_clock(&mut self) -> impl Future<Output = io::Result<Clock>> + '_ {
self.get_account(sysvar::clock::id()).map(|result| {
let clock_sysvar = result?
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Clock sysvar not present"))?;
from_account::<Clock, _>(&clock_sysvar).ok_or_else(|| {
io::Error::new(io::ErrorKind::Other, "Failed to deserialize Clock sysvar")
})
})
}
/// Return the fee parameters associated with a recent, rooted blockhash. The cluster
/// will use the transaction's blockhash to look up these same fee parameters and
/// use them to calculate the transaction fee.
pub fn get_fees(
&mut self,
) -> impl Future<Output = io::Result<(FeeCalculator, Hash, u64)>> + '_ {
) -> impl Future<Output = io::Result<(FeeCalculator, Hash, Slot)>> + '_ {
self.get_fees_with_commitment_and_context(context::current(), CommitmentLevel::default())
}
/// Return the cluster Sysvar
pub fn get_sysvar<T: Sysvar>(&mut self) -> impl Future<Output = io::Result<T>> + '_ {
self.get_account(T::id()).map(|result| {
let sysvar = result?
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Sysvar not present"))?;
from_account::<T, _>(&sysvar)
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Failed to deserialize sysvar"))
})
}
/// Return the cluster rent
pub fn get_rent(&mut self) -> impl Future<Output = io::Result<Rent>> + '_ {
self.get_sysvar::<Rent>()
self.get_account(sysvar::rent::id()).map(|result| {
let rent_sysvar = result?
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Rent sysvar not present"))?;
from_account::<Rent, _>(&rent_sysvar).ok_or_else(|| {
io::Error::new(io::ErrorKind::Other, "Failed to deserialize Rent sysvar")
})
})
}
/// Return a recent, rooted blockhash from the server. The cluster will only accept
@@ -221,18 +192,12 @@ impl BanksClient {
self.process_transactions_with_commitment(transactions, CommitmentLevel::default())
}
/// Return the most recent rooted slot. All transactions at or below this slot
/// are said to be finalized. The cluster will not fork to a higher slot.
/// Return the most recent rooted slot height. All transactions at or below this height
/// are said to be finalized. The cluster will not fork to a higher slot height.
pub fn get_root_slot(&mut self) -> impl Future<Output = io::Result<Slot>> + '_ {
self.get_slot_with_context(context::current(), CommitmentLevel::default())
}
/// Return the most recent rooted block height. All transactions at or below this height
/// are said to be finalized. The cluster will not fork to a higher block height.
pub fn get_root_block_height(&mut self) -> impl Future<Output = io::Result<Slot>> + '_ {
self.get_block_height_with_context(context::current(), CommitmentLevel::default())
}
/// Return the account at the given address at the slot corresponding to the given
/// commitment level. If the account is not found, None is returned.
pub fn get_account_with_commitment(
@@ -412,13 +377,13 @@ mod tests {
let mint_pubkey = &genesis.mint_keypair.pubkey();
let bob_pubkey = solana_sdk::pubkey::new_rand();
let instruction = system_instruction::transfer(mint_pubkey, &bob_pubkey, 1);
let message = Message::new(&[instruction], Some(mint_pubkey));
let instruction = system_instruction::transfer(&mint_pubkey, &bob_pubkey, 1);
let message = Message::new(&[instruction], Some(&mint_pubkey));
Runtime::new()?.block_on(async {
let client_transport = start_local_server(bank_forks, block_commitment_cache).await;
let mut banks_client = start_client(client_transport).await?;
let (_, recent_blockhash, last_valid_block_height) = banks_client.get_fees().await?;
let (_, recent_blockhash, last_valid_slot) = banks_client.get_fees().await?;
let transaction = Transaction::new(&[&genesis.mint_keypair], message, recent_blockhash);
let signature = transaction.signatures[0];
banks_client.send_transaction(transaction).await?;
@@ -426,8 +391,8 @@ mod tests {
let mut status = banks_client.get_transaction_status(signature).await?;
while status.is_none() {
let root_block_height = banks_client.get_root_block_height().await?;
if root_block_height > last_valid_block_height {
let root_slot = banks_client.get_root_slot().await?;
if root_slot > last_valid_slot {
break;
}
sleep(Duration::from_millis(100)).await;

View File

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

View File

@@ -34,7 +34,6 @@ pub trait Banks {
async fn get_transaction_status_with_context(signature: Signature)
-> Option<TransactionStatus>;
async fn get_slot_with_context(commitment: CommitmentLevel) -> Slot;
async fn get_block_height_with_context(commitment: CommitmentLevel) -> u64;
async fn process_transaction_with_commitment_and_context(
transaction: Transaction,
commitment: CommitmentLevel,

View File

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

View File

@@ -113,7 +113,7 @@ impl BanksServer {
self,
signature: &Signature,
blockhash: &Hash,
last_valid_block_height: u64,
last_valid_slot: Slot,
commitment: CommitmentLevel,
) -> Option<transaction::Result<()>> {
let mut status = self
@@ -122,7 +122,7 @@ impl BanksServer {
while status.is_none() {
sleep(Duration::from_millis(200)).await;
let bank = self.bank(commitment);
if bank.block_height() > last_valid_block_height {
if bank.slot() > last_valid_slot {
break;
}
status = bank.get_signature_status_with_blockhash(signature, blockhash);
@@ -131,13 +131,10 @@ impl BanksServer {
}
}
fn verify_transaction(
transaction: &Transaction,
libsecp256k1_0_5_upgrade_enabled: bool,
) -> transaction::Result<()> {
fn verify_transaction(transaction: &Transaction) -> transaction::Result<()> {
if let Err(err) = transaction.verify() {
Err(err)
} else if let Err(err) = transaction.verify_precompiles(libsecp256k1_0_5_upgrade_enabled) {
} else if let Err(err) = transaction.verify_precompiles() {
Err(err)
} else {
Ok(())
@@ -148,19 +145,16 @@ fn verify_transaction(
impl Banks for BanksServer {
async fn send_transaction_with_context(self, _: Context, transaction: Transaction) {
let blockhash = &transaction.message.recent_blockhash;
let last_valid_block_height = self
let last_valid_slot = self
.bank_forks
.read()
.unwrap()
.root_bank()
.get_blockhash_last_valid_block_height(blockhash)
.get_blockhash_last_valid_slot(&blockhash)
.unwrap();
let signature = transaction.signatures.get(0).cloned().unwrap_or_default();
let info = TransactionInfo::new(
signature,
serialize(&transaction).unwrap(),
last_valid_block_height,
);
let info =
TransactionInfo::new(signature, serialize(&transaction).unwrap(), last_valid_slot);
self.transaction_sender.send(info).unwrap();
}
@@ -168,13 +162,11 @@ impl Banks for BanksServer {
self,
_: Context,
commitment: CommitmentLevel,
) -> (FeeCalculator, Hash, u64) {
) -> (FeeCalculator, Hash, Slot) {
let bank = self.bank(commitment);
let (blockhash, fee_calculator) = bank.last_blockhash_with_fee_calculator();
let last_valid_block_height = bank
.get_blockhash_last_valid_block_height(&blockhash)
.unwrap();
(fee_calculator, blockhash, last_valid_block_height)
let last_valid_slot = bank.get_blockhash_last_valid_slot(&blockhash).unwrap();
(fee_calculator, blockhash, last_valid_slot)
}
async fn get_transaction_status_with_context(
@@ -217,39 +209,29 @@ impl Banks for BanksServer {
self.slot(commitment)
}
async fn get_block_height_with_context(self, _: Context, commitment: CommitmentLevel) -> u64 {
self.bank(commitment).block_height()
}
async fn process_transaction_with_commitment_and_context(
self,
_: Context,
transaction: Transaction,
commitment: CommitmentLevel,
) -> Option<transaction::Result<()>> {
if let Err(err) = verify_transaction(
&transaction,
self.bank(commitment).libsecp256k1_0_5_upgrade_enabled(),
) {
if let Err(err) = verify_transaction(&transaction) {
return Some(Err(err));
}
let blockhash = &transaction.message.recent_blockhash;
let last_valid_block_height = self
let last_valid_slot = self
.bank_forks
.read()
.unwrap()
.root_bank()
.get_blockhash_last_valid_block_height(blockhash)
.get_blockhash_last_valid_slot(blockhash)
.unwrap();
let signature = transaction.signatures.get(0).cloned().unwrap_or_default();
let info = TransactionInfo::new(
signature,
serialize(&transaction).unwrap(),
last_valid_block_height,
);
let info =
TransactionInfo::new(signature, serialize(&transaction).unwrap(), last_valid_slot);
self.transaction_sender.send(info).unwrap();
self.poll_signature_status(&signature, blockhash, last_valid_block_height, commitment)
self.poll_signature_status(&signature, blockhash, last_valid_slot, commitment)
.await
}

View File

@@ -2,7 +2,7 @@
use log::*;
use solana_metrics::{datapoint_warn, inc_new_counter_info};
use solana_runtime::{bank::Bank, bank_forks::BankForks};
use solana_sdk::signature::Signature;
use solana_sdk::{clock::Slot, signature::Signature};
use std::{
collections::HashMap,
net::{SocketAddr, UdpSocket},
@@ -24,19 +24,15 @@ pub struct SendTransactionService {
pub struct TransactionInfo {
pub signature: Signature,
pub wire_transaction: Vec<u8>,
pub last_valid_block_height: u64,
pub last_valid_slot: Slot,
}
impl TransactionInfo {
pub fn new(
signature: Signature,
wire_transaction: Vec<u8>,
last_valid_block_height: u64,
) -> Self {
pub fn new(signature: Signature, wire_transaction: Vec<u8>, last_valid_slot: Slot) -> Self {
Self {
signature,
wire_transaction,
last_valid_block_height,
last_valid_slot,
}
}
}
@@ -128,7 +124,7 @@ impl SendTransactionService {
result.rooted += 1;
inc_new_counter_info!("send_transaction_service-rooted", 1);
false
} else if transaction_info.last_valid_block_height < root_bank.block_height() {
} else if transaction_info.last_valid_slot < root_bank.slot() {
info!("Dropping expired transaction: {}", signature);
result.expired += 1;
inc_new_counter_info!("send_transaction_service-expired", 1);
@@ -142,8 +138,8 @@ impl SendTransactionService {
result.retried += 1;
inc_new_counter_info!("send_transaction_service-retry", 1);
Self::send_transaction(
send_socket,
tpu_address,
&send_socket,
&tpu_address,
&transaction_info.wire_transaction,
);
true

View File

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

View File

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

View File

@@ -5,9 +5,8 @@ pub mod order_book;
use crate::bench::{airdrop_lamports, create_client_accounts_file, do_bench_exchange, Config};
use log::*;
use solana_gossip::gossip_service::{discover_cluster, get_multi_client};
use solana_core::gossip_service::{discover_cluster, get_multi_client};
use solana_sdk::signature::Signer;
use solana_streamer::socket::SocketAddrSpace;
fn main() {
solana_logger::setup();
@@ -56,12 +55,11 @@ fn main() {
);
} else {
info!("Connecting to the cluster");
let nodes = discover_cluster(&entrypoint_addr, num_nodes, SocketAddrSpace::Unspecified)
.unwrap_or_else(|_| {
panic!("Failed to discover nodes");
});
let nodes = discover_cluster(&entrypoint_addr, num_nodes).unwrap_or_else(|_| {
panic!("Failed to discover nodes");
});
let (client, num_clients) = get_multi_client(&nodes, &SocketAddrSpace::Unspecified);
let (client, num_clients) = get_multi_client(&nodes);
info!("{} nodes found", num_clients);
if num_clients < num_nodes {

View File

@@ -1,11 +1,13 @@
use log::*;
use solana_bench_exchange::bench::{airdrop_lamports, do_bench_exchange, Config};
use solana_core::validator::ValidatorConfig;
use solana_core::{
gossip_service::{discover_cluster, get_multi_client},
validator::ValidatorConfig,
};
use solana_exchange_program::{
exchange_processor::process_instruction, id, solana_exchange_program,
};
use solana_faucet::faucet::run_local_faucet_with_port;
use solana_gossip::gossip_service::{discover_cluster, get_multi_client};
use solana_local_cluster::{
local_cluster::{ClusterConfig, LocalCluster},
validator_configs::make_identical_validator_configs,
@@ -15,7 +17,6 @@ use solana_sdk::{
genesis_config::create_genesis_config,
signature::{Keypair, Signer},
};
use solana_streamer::socket::SocketAddrSpace;
use std::{process::exit, sync::mpsc::channel, time::Duration};
#[test]
@@ -44,19 +45,13 @@ fn test_exchange_local_cluster() {
} = config;
let accounts_in_groups = batch_size * account_groups;
let cluster = LocalCluster::new(
&mut ClusterConfig {
node_stakes: vec![100_000; NUM_NODES],
cluster_lamports: 100_000_000_000_000,
validator_configs: make_identical_validator_configs(
&ValidatorConfig::default(),
NUM_NODES,
),
native_instruction_processors: [solana_exchange_program!()].to_vec(),
..ClusterConfig::default()
},
SocketAddrSpace::Unspecified,
);
let cluster = LocalCluster::new(&mut ClusterConfig {
node_stakes: vec![100_000; NUM_NODES],
cluster_lamports: 100_000_000_000_000,
validator_configs: make_identical_validator_configs(&ValidatorConfig::default(), NUM_NODES),
native_instruction_processors: [solana_exchange_program!()].to_vec(),
..ClusterConfig::default()
});
let faucet_keypair = Keypair::new();
cluster.transfer(
@@ -73,17 +68,13 @@ fn test_exchange_local_cluster() {
.expect("faucet_addr");
info!("Connecting to the cluster");
let nodes = discover_cluster(
&cluster.entry_point_info.gossip,
NUM_NODES,
SocketAddrSpace::Unspecified,
)
.unwrap_or_else(|err| {
error!("Failed to discover {} nodes: {:?}", NUM_NODES, err);
exit(1);
});
let nodes =
discover_cluster(&cluster.entry_point_info.gossip, NUM_NODES).unwrap_or_else(|err| {
error!("Failed to discover {} nodes: {:?}", NUM_NODES, err);
exit(1);
});
let (client, num_clients) = get_multi_client(&nodes, &SocketAddrSpace::Unspecified);
let (client, num_clients) = get_multi_client(&nodes);
info!("clients: {}", num_clients);
assert!(num_clients >= NUM_NODES);

View File

@@ -2,7 +2,7 @@
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
edition = "2018"
name = "solana-bench-streamer"
version = "1.7.13"
version = "1.6.2"
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
@@ -10,11 +10,11 @@ publish = false
[dependencies]
clap = "2.33.1"
solana-clap-utils = { path = "../clap-utils", version = "=1.7.13" }
solana-streamer = { path = "../streamer", version = "=1.7.13" }
solana-logger = { path = "../logger", version = "=1.7.13" }
solana-net-utils = { path = "../net-utils", version = "=1.7.13" }
solana-version = { path = "../version", version = "=1.7.13" }
solana-clap-utils = { path = "../clap-utils", version = "=1.6.2" }
solana-streamer = { path = "../streamer", version = "=1.6.2" }
solana-logger = { path = "../logger", version = "=1.6.2" }
solana-net-utils = { path = "../net-utils", version = "=1.6.2" }
solana-version = { path = "../version", version = "=1.6.2" }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -18,7 +18,7 @@ fn producer(addr: &SocketAddr, exit: Arc<AtomicBool>) -> JoinHandle<()> {
msgs.packets.resize(10, Packet::default());
for w in msgs.packets.iter_mut() {
w.meta.size = PACKET_DATA_SIZE;
w.meta.set_addr(addr);
w.meta.set_addr(&addr);
}
let msgs = Arc::new(msgs);
spawn(move || loop {
@@ -75,7 +75,7 @@ fn main() -> Result<()> {
let mut read_channels = Vec::new();
let mut read_threads = Vec::new();
let recycler = PacketsRecycler::default();
let recycler = PacketsRecycler::new_without_limit("bench-streamer-recycler-shrink-stats");
for _ in 0..num_sockets {
let read = solana_net_utils::bind_to(ip_addr, port, false).unwrap();
read.set_read_timeout(Some(Duration::new(1, 0))).unwrap();
@@ -92,7 +92,6 @@ fn main() -> Result<()> {
recycler.clone(),
"bench-streamer-test",
1,
true,
));
}

View File

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

View File

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

View File

@@ -2,12 +2,11 @@
use log::*;
use solana_bench_tps::bench::{do_bench_tps, generate_and_fund_keypairs, generate_keypairs};
use solana_bench_tps::cli;
use solana_core::gossip_service::{discover_cluster, get_client, get_multi_client};
use solana_genesis::Base64Account;
use solana_gossip::gossip_service::{discover_cluster, get_client, get_multi_client};
use solana_sdk::fee_calculator::FeeRateGovernor;
use solana_sdk::signature::{Keypair, Signer};
use solana_sdk::system_program;
use solana_streamer::socket::SocketAddrSpace;
use std::{collections::HashMap, fs::File, io::prelude::*, path::Path, process::exit, sync::Arc};
/// Number of signatures for all transactions in ~1 week at ~100K TPS
@@ -40,7 +39,7 @@ fn main() {
let keypair_count = *tx_count * keypair_multiplier;
if *write_to_client_file {
info!("Generating {} keypairs", keypair_count);
let (keypairs, _) = generate_keypairs(id, keypair_count as u64);
let (keypairs, _) = generate_keypairs(&id, keypair_count as u64);
let num_accounts = keypairs.len() as u64;
let max_fee =
FeeRateGovernor::new(*target_lamports_per_signature, 0).max_lamports_per_signature;
@@ -69,14 +68,13 @@ fn main() {
}
info!("Connecting to the cluster");
let nodes = discover_cluster(entrypoint_addr, *num_nodes, SocketAddrSpace::Unspecified)
.unwrap_or_else(|err| {
eprintln!("Failed to discover {} nodes: {:?}", num_nodes, err);
exit(1);
});
let nodes = discover_cluster(&entrypoint_addr, *num_nodes).unwrap_or_else(|err| {
eprintln!("Failed to discover {} nodes: {:?}", num_nodes, err);
exit(1);
});
let client = if *multi_client {
let (client, num_clients) = get_multi_client(&nodes, &SocketAddrSpace::Unspecified);
let (client, num_clients) = get_multi_client(&nodes);
if nodes.len() < num_clients {
eprintln!(
"Error: Insufficient nodes discovered. Expecting {} or more",
@@ -90,7 +88,7 @@ fn main() {
let mut target_client = None;
for node in nodes {
if node.id == *target_node {
target_client = Some(Arc::new(get_client(&[node], &SocketAddrSpace::Unspecified)));
target_client = Some(Arc::new(get_client(&[node])));
break;
}
}
@@ -99,7 +97,7 @@ fn main() {
exit(1);
})
} else {
Arc::new(get_client(&nodes, &SocketAddrSpace::Unspecified))
Arc::new(get_client(&nodes))
};
let keypairs = if *read_from_client_file {
@@ -137,7 +135,7 @@ fn main() {
generate_and_fund_keypairs(
client.clone(),
Some(*faucet_addr),
id,
&id,
keypair_count,
*num_lamports_per_account,
)

View File

@@ -5,15 +5,13 @@ use solana_bench_tps::{
cli::Config,
};
use solana_client::thin_client::create_client;
use solana_core::validator::ValidatorConfig;
use solana_core::{cluster_info::VALIDATOR_PORT_RANGE, validator::ValidatorConfig};
use solana_faucet::faucet::run_local_faucet_with_port;
use solana_gossip::cluster_info::VALIDATOR_PORT_RANGE;
use solana_local_cluster::{
local_cluster::{ClusterConfig, LocalCluster},
validator_configs::make_identical_validator_configs,
};
use solana_sdk::signature::{Keypair, Signer};
use solana_streamer::socket::SocketAddrSpace;
use std::{
sync::{mpsc::channel, Arc},
time::Duration,
@@ -24,19 +22,13 @@ fn test_bench_tps_local_cluster(config: Config) {
solana_logger::setup();
const NUM_NODES: usize = 1;
let cluster = LocalCluster::new(
&mut ClusterConfig {
node_stakes: vec![999_990; NUM_NODES],
cluster_lamports: 200_000_000,
validator_configs: make_identical_validator_configs(
&ValidatorConfig::default(),
NUM_NODES,
),
native_instruction_processors,
..ClusterConfig::default()
},
SocketAddrSpace::Unspecified,
);
let cluster = LocalCluster::new(&mut ClusterConfig {
node_stakes: vec![999_990; NUM_NODES],
cluster_lamports: 200_000_000,
validator_configs: make_identical_validator_configs(&ValidatorConfig::default(), NUM_NODES),
native_instruction_processors,
..ClusterConfig::default()
});
let faucet_keypair = Keypair::new();
cluster.transfer(

9
cargo
View File

@@ -3,22 +3,25 @@
# shellcheck source=ci/rust-version.sh
here=$(dirname "$0")
source "${here}"/ci/rust-version.sh all
toolchain=
case "$1" in
stable)
source "${here}"/ci/rust-version.sh stable
# shellcheck disable=SC2054 # rust_stable is sourced from rust-version.sh
toolchain="$rust_stable"
shift
;;
nightly)
source "${here}"/ci/rust-version.sh nightly
# shellcheck disable=SC2054 # rust_nightly is sourced from rust-version.sh
toolchain="$rust_nightly"
shift
;;
+*)
toolchain="${1#+}"
shift
;;
*)
source "${here}"/ci/rust-version.sh stable
# shellcheck disable=SC2054 # rust_stable is sourced from rust-version.sh
toolchain="$rust_stable"
;;

View File

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

View File

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

View File

@@ -105,18 +105,11 @@ if [[ -z "$CHANNEL" ]]; then
fi
fi
if [[ $CHANNEL = beta ]]; then
CHANNEL_LATEST_TAG="$BETA_CHANNEL_LATEST_TAG"
elif [[ $CHANNEL = stable ]]; then
CHANNEL_LATEST_TAG="$STABLE_CHANNEL_LATEST_TAG"
fi
echo EDGE_CHANNEL="$EDGE_CHANNEL"
echo BETA_CHANNEL="$BETA_CHANNEL"
echo BETA_CHANNEL_LATEST_TAG="$BETA_CHANNEL_LATEST_TAG"
echo STABLE_CHANNEL="$STABLE_CHANNEL"
echo STABLE_CHANNEL_LATEST_TAG="$STABLE_CHANNEL_LATEST_TAG"
echo CHANNEL="$CHANNEL"
echo CHANNEL_LATEST_TAG="$CHANNEL_LATEST_TAG"
exit 0

View File

@@ -7,6 +7,8 @@ src_root="$(readlink -f "${here}/..")"
cd "${src_root}"
source ci/rust-version.sh stable
cargo_audit_ignores=(
# failure is officially deprecated/unmaintained
#
@@ -28,22 +30,16 @@ cargo_audit_ignores=(
# Blocked on multiple crates updating `time` to >= 0.2.23
--ignore RUSTSEC-2020-0071
# difference is unmaintained
#
# Blocked on predicates v1.0.6 removing its dependency on `difference`
--ignore RUSTSEC-2020-0095
# generic-array: arr! macro erases lifetimes
#
# Blocked on libsecp256k1 releasing with upgraded dependencies
# https://github.com/paritytech/libsecp256k1/issues/66
--ignore RUSTSEC-2020-0146
# hyper: Lenient `hyper` header parsing of `Content-Length` could allow request smuggling
#
# Blocked on jsonrpc removing dependency on unmaintained `websocket`
# https://github.com/paritytech/jsonrpc/issues/605
--ignore RUSTSEC-2021-0078
# hyper: Integer overflow in `hyper`'s parsing of the `Transfer-Encoding` header leads to data loss
#
# Blocked on jsonrpc removing dependency on unmaintained `websocket`
# https://github.com/paritytech/jsonrpc/issues/605
--ignore RUSTSEC-2021-0079
)
scripts/cargo-for-all-lock-files.sh stable audit "${cargo_audit_ignores[@]}"
scripts/cargo-for-all-lock-files.sh +"$rust_stable" audit "${cargo_audit_ignores[@]}"

View File

@@ -1,4 +1,4 @@
FROM solanalabs/rust:1.52.1
FROM solanalabs/rust:1.50.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.52.1
FROM rust:1.50.0
# Add Google Protocol Buffers for Libra's metrics library.
ENV PROTOC_VERSION 3.8.0

View File

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

View File

@@ -70,7 +70,7 @@ done
source ci/upload-ci-artifact.sh
source scripts/configure-metrics.sh
source multinode-demo/common.sh --prebuild
source multinode-demo/common.sh
nodes=(
"multinode-demo/bootstrap-validator.sh \
@@ -127,7 +127,7 @@ startNode() {
waitForNodeToInit() {
declare initCompleteFile=$1
while [[ ! -r $initCompleteFile ]]; do
if [[ $SECONDS -ge 300 ]]; then
if [[ $SECONDS -ge 240 ]]; then
echo "^^^ +++"
echo "Error: $initCompleteFile not found in $SECONDS seconds"
exit 1

View File

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

27
ci/publish-bpf-sdk.sh Executable file
View File

@@ -0,0 +1,27 @@
#!/usr/bin/env bash
set -e
cd "$(dirname "$0")/.."
eval "$(ci/channel-info.sh)"
if [[ -n "$CI_TAG" ]]; then
CHANNEL_OR_TAG=$CI_TAG
else
CHANNEL_OR_TAG=$CHANNEL
fi
(
set -x
sdk/bpf/scripts/package.sh
[[ -f bpf-sdk.tar.bz2 ]]
)
source ci/upload-ci-artifact.sh
echo --- AWS S3 Store
if [[ -z $CHANNEL_OR_TAG ]]; then
echo Skipped
else
upload-s3-artifact "/solana/bpf-sdk.tar.bz2" "s3://solana-sdk/$CHANNEL_OR_TAG/bpf-sdk.tar.bz2"
fi
exit 0

View File

@@ -83,7 +83,7 @@ echo --- Creating release tarball
export CHANNEL
source ci/rust-version.sh stable
scripts/cargo-install-all.sh stable "${RELEASE_BASENAME}"
scripts/cargo-install-all.sh +"$rust_stable" "${RELEASE_BASENAME}"
tar cvf "${TARBALL_BASENAME}"-$TARGET.tar "${RELEASE_BASENAME}"
bzip2 "${TARBALL_BASENAME}"-$TARGET.tar

View File

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

View File

@@ -7,7 +7,7 @@ source multinode-demo/common.sh
rm -rf config/run/init-completed config/ledger config/snapshot-ledger
SOLANA_RUN_SH_VALIDATOR_ARGS="--snapshot-interval-slots 200" timeout 120 ./scripts/run.sh &
timeout 120 ./run.sh &
pid=$!
attempts=20
@@ -16,17 +16,14 @@ while [[ ! -f config/run/init-completed ]]; do
if ((--attempts == 0)); then
echo "Error: validator failed to boot"
exit 1
else
echo "Checking init"
fi
done
snapshot_slot=1
# wait a bit longer than snapshot_slot
while [[ $($solana_cli --url http://localhost:8899 slot --commitment processed) -le $((snapshot_slot + 1)) ]]; do
while [[ $($solana_cli --url http://localhost:8899 slot --commitment recent) -le $((snapshot_slot + 1)) ]]; do
sleep 1
echo "Checking slot"
done
$solana_validator --ledger config/ledger exit --force || true

View File

@@ -18,13 +18,13 @@
if [[ -n $RUST_STABLE_VERSION ]]; then
stable_version="$RUST_STABLE_VERSION"
else
stable_version=1.52.1
stable_version=1.50.0
fi
if [[ -n $RUST_NIGHTLY_VERSION ]]; then
nightly_version="$RUST_NIGHTLY_VERSION"
else
nightly_version=2021-05-18
nightly_version=2021-02-18
fi

View File

@@ -76,7 +76,7 @@ RestartForceExitStatus=SIGPIPE
TimeoutStartSec=10
TimeoutStopSec=0
KillMode=process
LimitNOFILE=1000000
LimitNOFILE=700000
[Install]
WantedBy=multi-user.target

View File

@@ -8,5 +8,5 @@ source "$HERE"/utils.sh
ensure_env || exit 1
# Allow more files to be opened by a user
echo "* - nofile 1000000" > /etc/security/limits.d/90-solana-nofiles.conf
echo "* - nofile 700000" > /etc/security/limits.d/90-solana-nofiles.conf

View File

@@ -27,7 +27,7 @@ BENCH_ARTIFACT=current_bench_results.log
_ "$cargo" build --manifest-path=keygen/Cargo.toml
export PATH="$PWD/target/debug":$PATH
# Clear the C dependency files, if dependency moves these files are not regenerated
# Clear the C dependency files, if dependeny moves these files are not regenerated
test -d target/debug/bpf && find target/debug/bpf -name '*.d' -delete
test -d target/release/bpf && find target/release/bpf -name '*.d' -delete
@@ -45,14 +45,6 @@ _ "$cargo" nightly bench --manifest-path sdk/Cargo.toml ${V:+--verbose} \
_ "$cargo" nightly bench --manifest-path runtime/Cargo.toml ${V:+--verbose} \
-- -Z unstable-options --format=json | tee -a "$BENCH_FILE"
# Run gossip benches
_ "$cargo" nightly bench --manifest-path gossip/Cargo.toml ${V:+--verbose} \
-- -Z unstable-options --format=json | tee -a "$BENCH_FILE"
# Run poh benches
_ "$cargo" nightly bench --manifest-path poh/Cargo.toml ${V:+--verbose} \
-- -Z unstable-options --format=json | tee -a "$BENCH_FILE"
# Run core benches
_ "$cargo" nightly bench --manifest-path core/Cargo.toml ${V:+--verbose} \
-- -Z unstable-options --format=json | tee -a "$BENCH_FILE"

View File

@@ -14,7 +14,7 @@ scripts/increment-cargo-version.sh check
# Disallow uncommitted Cargo.lock changes
(
_ scripts/cargo-for-all-lock-files.sh tree >/dev/null
_ scripts/cargo-for-all-lock-files.sh tree
set +e
if ! _ git diff --exit-code; then
echo -e "\nError: Uncommitted Cargo.lock changes" 1>&2
@@ -35,10 +35,8 @@ echo --- build environment
"$cargo" stable clippy --version --verbose
"$cargo" nightly clippy --version --verbose
# audit is done only with "$cargo stable"
# audit is done only with stable
"$cargo" stable audit --version
grcov --version
)
export RUST_BACKTRACE=1
@@ -47,7 +45,7 @@ export RUSTFLAGS="-D warnings -A incomplete_features"
# Only force up-to-date lock files on edge
if [[ $CI_BASE_BRANCH = "$EDGE_CHANNEL" ]]; then
# Exclude --benches as it's not available in rust stable yet
if _ scripts/cargo-for-all-lock-files.sh stable check --locked --tests --bins --examples; then
if _ scripts/cargo-for-all-lock-files.sh +"$rust_stable" check --locked --tests --bins --examples; then
true
else
check_status=$?
@@ -58,7 +56,7 @@ if [[ $CI_BASE_BRANCH = "$EDGE_CHANNEL" ]]; then
fi
# Ensure nightly and --benches
_ scripts/cargo-for-all-lock-files.sh nightly check --locked --all-targets
_ scripts/cargo-for-all-lock-files.sh +"$rust_nightly" check --locked --all-targets
else
echo "Note: cargo-for-all-lock-files.sh skipped because $CI_BASE_BRANCH != $EDGE_CHANNEL"
fi
@@ -67,8 +65,7 @@ _ ci/order-crates-for-publishing.py
# -Z... is needed because of clippy bug: https://github.com/rust-lang/rust-clippy/issues/4612
# run nightly clippy for `sdk/` as there's a moderate amount of nightly-only code there
_ "$cargo" nightly clippy -Zunstable-options --workspace --all-targets -- \
--deny=warnings --deny=clippy::integer_arithmetic --allow=clippy::inconsistent_struct_constructor
_ "$cargo" nightly clippy -Zunstable-options --workspace --all-targets -- --deny=warnings --deny=clippy::integer_arithmetic
_ "$cargo" stable fmt --all -- --check
@@ -82,6 +79,7 @@ _ ci/do-audit.sh
cd "$project"
_ "$cargo" nightly clippy -- --deny=warnings --allow=clippy::missing_safety_doc
_ "$cargo" stable fmt -- --check
_ "$cargo" nightly test
)
done
}

View File

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

View File

@@ -25,29 +25,4 @@ echo
_ ci/nits.sh
_ ci/check-ssh-keys.sh
# Ensure the current channel version is not equal ("greater") than
# the version of the latest tag
if [[ -z $CI_TAG ]]; then
echo "--- channel version check"
(
eval "$(ci/channel-info.sh)"
if [[ -n $CHANNEL_LATEST_TAG ]]; then
source scripts/read-cargo-variable.sh
version=$(readCargoVariable version "version/Cargo.toml")
echo "version: v$version"
echo "latest channel tag: $CHANNEL_LATEST_TAG"
if [[ $CHANNEL_LATEST_TAG = v$version ]]; then
echo "Error: please run ./scripts/increment-cargo-version.sh"
exit 1
fi
else
echo "Skipped. CHANNEL_LATEST_TAG (CHANNEL=$CHANNEL) unset"
fi
)
fi
echo --- ok

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-clap-utils"
version = "1.7.13"
version = "1.6.2"
description = "Solana utilities for the clap"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
@@ -12,17 +12,13 @@ edition = "2018"
[dependencies]
clap = "2.33.0"
rpassword = "4.0"
solana-remote-wallet = { path = "../remote-wallet", version = "=1.7.13" }
solana-sdk = { path = "../sdk", version = "=1.7.13" }
solana-remote-wallet = { path = "../remote-wallet", version = "=1.6.2" }
solana-sdk = { path = "../sdk", version = "=1.6.2" }
thiserror = "1.0.21"
tiny-bip39 = "0.8.1"
uriparse = "0.6.3"
tiny-bip39 = "0.8.0"
url = "2.1.0"
chrono = "0.4"
[dev-dependencies]
tempfile = "3.1.0"
[lib]
name = "solana_clap_utils"

View File

@@ -1,7 +1,5 @@
use {
crate::{input_validators, ArgConstant},
clap::Arg,
};
use crate::{input_validators, ArgConstant};
use clap::Arg;
pub const FEE_PAYER_ARG: ArgConstant<'static> = ArgConstant {
name: "fee_payer",

View File

@@ -1,24 +1,19 @@
use {
crate::keypair::{
keypair_from_seed_phrase, pubkey_from_path, resolve_signer_from_path, signer_from_path,
ASK_KEYWORD, SKIP_SEED_PHRASE_VALIDATION_ARG,
},
chrono::DateTime,
clap::ArgMatches,
solana_remote_wallet::remote_wallet::RemoteWalletManager,
solana_sdk::{
clock::UnixTimestamp,
commitment_config::CommitmentConfig,
genesis_config::ClusterType,
native_token::sol_to_lamports,
pubkey::Pubkey,
signature::{read_keypair_file, Keypair, Signature, Signer},
},
std::{str::FromStr, sync::Arc},
use crate::keypair::{
keypair_from_seed_phrase, pubkey_from_path, resolve_signer_from_path, signer_from_path,
ASK_KEYWORD, SKIP_SEED_PHRASE_VALIDATION_ARG,
};
// Sentinel value used to indicate to write to screen instead of file
pub const STDOUT_OUTFILE_TOKEN: &str = "-";
use chrono::DateTime;
use clap::ArgMatches;
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{
clock::UnixTimestamp,
commitment_config::CommitmentConfig,
genesis_config::ClusterType,
native_token::sol_to_lamports,
pubkey::Pubkey,
signature::{read_keypair_file, Keypair, Signature, Signer},
};
use std::{str::FromStr, sync::Arc};
// Return parsed values from matches at `name`
pub fn values_of<T>(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<T>>
@@ -60,7 +55,7 @@ pub fn keypair_of(matches: &ArgMatches<'_>, name: &str) -> Option<Keypair> {
if let Some(value) = matches.value_of(name) {
if value == ASK_KEYWORD {
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
keypair_from_seed_phrase(name, skip_validation, true, None, true).ok()
keypair_from_seed_phrase(name, skip_validation, true).ok()
} else {
read_keypair_file(value).ok()
}
@@ -75,7 +70,7 @@ pub fn keypairs_of(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<Keypair>>
.filter_map(|value| {
if value == ASK_KEYWORD {
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
keypair_from_seed_phrase(name, skip_validation, true, None, true).ok()
keypair_from_seed_phrase(name, skip_validation, true).ok()
} else {
read_keypair_file(value).ok()
}

View File

@@ -1,15 +1,13 @@
use {
crate::keypair::{parse_signer_source, SignerSourceKind, ASK_KEYWORD},
chrono::DateTime,
solana_sdk::{
clock::{Epoch, Slot},
hash::Hash,
pubkey::{Pubkey, MAX_SEED_LEN},
signature::{read_keypair_file, Signature},
},
std::fmt::Display,
std::str::FromStr,
use crate::keypair::{parse_keypair_path, KeypairUrl, ASK_KEYWORD};
use chrono::DateTime;
use solana_sdk::{
clock::{Epoch, Slot},
hash::Hash,
pubkey::{Pubkey, MAX_SEED_LEN},
signature::{read_keypair_file, Signature},
};
use std::fmt::Display;
use std::str::FromStr;
fn is_parsable_generic<U, T>(string: T) -> Result<(), String>
where
@@ -34,29 +32,6 @@ where
is_parsable_generic::<T, String>(string)
}
// Return an error if string cannot be parsed as numeric type T, and value not within specified
// range
pub fn is_within_range<T>(string: String, range_min: T, range_max: T) -> Result<(), String>
where
T: FromStr + Copy + std::fmt::Debug + PartialOrd + std::ops::Add<Output = T> + From<usize>,
T::Err: Display,
{
match string.parse::<T>() {
Ok(input) => {
let range = range_min..range_max + 1.into();
if !range.contains(&input) {
Err(format!(
"input '{:?}' out of range ({:?}..{:?}]",
input, range_min, range_max
))
} else {
Ok(())
}
}
Err(err) => Err(format!("error parsing '{}': {}", string, err)),
}
}
// Return an error if a pubkey cannot be parsed.
pub fn is_pubkey<T>(string: T) -> Result<(), String>
where
@@ -96,26 +71,6 @@ where
.map_err(|err| format!("{}", err))
}
// Return an error if a `SignerSourceKind::Prompt` cannot be parsed
pub fn is_prompt_signer_source<T>(string: T) -> Result<(), String>
where
T: AsRef<str> + Display,
{
if string.as_ref() == ASK_KEYWORD {
return Ok(());
}
match parse_signer_source(string.as_ref())
.map_err(|err| format!("{}", err))?
.kind
{
SignerSourceKind::Prompt => Ok(()),
_ => Err(format!(
"Unable to parse input as `prompt:` URI scheme or `ASK` keyword: {}",
string
)),
}
}
// Return an error if string cannot be parsed as pubkey string or keypair file location
pub fn is_pubkey_or_keypair<T>(string: T) -> Result<(), String>
where
@@ -130,11 +85,8 @@ pub fn is_valid_pubkey<T>(string: T) -> Result<(), String>
where
T: AsRef<str> + Display,
{
match parse_signer_source(string.as_ref())
.map_err(|err| format!("{}", err))?
.kind
{
SignerSourceKind::Filepath(path) => is_keypair(path),
match parse_keypair_path(string.as_ref()) {
KeypairUrl::Filepath(path) => is_keypair(path),
_ => Ok(()),
}
}
@@ -215,8 +167,8 @@ where
pub fn normalize_to_url_if_moniker<T: AsRef<str>>(url_or_moniker: T) -> String {
match url_or_moniker.as_ref() {
"m" | "mainnet-beta" => "https://api.mainnet-beta.solana.com",
"t" | "testnet" => "https://api.testnet.solana.com",
"d" | "devnet" => "https://api.devnet.solana.com",
"t" | "testnet" => "https://testnet.solana.com",
"d" | "devnet" => "https://devnet.solana.com",
"l" | "localhost" => "http://localhost:8899",
url => url,
}

View File

@@ -1,39 +1,30 @@
use {
crate::{
input_parsers::{pubkeys_sigs_of, STDOUT_OUTFILE_TOKEN},
offline::{SIGNER_ARG, SIGN_ONLY_ARG},
ArgConstant,
use crate::{
input_parsers::pubkeys_sigs_of,
offline::{SIGNER_ARG, SIGN_ONLY_ARG},
ArgConstant,
};
use bip39::{Language, Mnemonic, Seed};
use clap::ArgMatches;
use rpassword::prompt_password_stderr;
use solana_remote_wallet::{
remote_keypair::generate_remote_keypair,
remote_wallet::{maybe_wallet_manager, RemoteWalletError, RemoteWalletManager},
};
use solana_sdk::{
hash::Hash,
message::Message,
pubkey::Pubkey,
signature::{
keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair,
read_keypair_file, Keypair, NullSigner, Presigner, Signature, Signer,
},
bip39::{Language, Mnemonic, Seed},
clap::ArgMatches,
rpassword::prompt_password_stderr,
solana_remote_wallet::{
locator::{Locator as RemoteWalletLocator, LocatorError as RemoteWalletLocatorError},
remote_keypair::generate_remote_keypair,
remote_wallet::{maybe_wallet_manager, RemoteWalletError, RemoteWalletManager},
},
solana_sdk::{
derivation_path::{DerivationPath, DerivationPathError},
hash::Hash,
message::Message,
pubkey::Pubkey,
signature::{
generate_seed_from_seed_phrase_and_passphrase, keypair_from_seed,
keypair_from_seed_and_derivation_path, keypair_from_seed_phrase_and_passphrase,
read_keypair, read_keypair_file, Keypair, NullSigner, Presigner, Signature, Signer,
},
},
std::{
cell::RefCell,
convert::TryFrom,
error,
io::{stdin, stdout, Write},
ops::Deref,
process::exit,
str::FromStr,
sync::Arc,
},
thiserror::Error,
};
use std::{
error,
io::{stdin, stdout, Write},
process::exit,
str::FromStr,
sync::Arc,
};
pub struct SignOnly {
@@ -92,48 +83,12 @@ impl CliSignerInfo {
}
}
#[derive(Debug, Default)]
pub struct DefaultSigner {
pub arg_name: String,
pub path: String,
is_path_checked: RefCell<bool>,
}
impl DefaultSigner {
pub fn new<AN: AsRef<str>, P: AsRef<str>>(arg_name: AN, path: P) -> Self {
let arg_name = arg_name.as_ref().to_string();
let path = path.as_ref().to_string();
Self {
arg_name,
path,
..Self::default()
}
}
fn path(&self) -> Result<&str, Box<dyn std::error::Error>> {
if !self.is_path_checked.borrow().deref() {
parse_signer_source(&self.path)
.and_then(|s| {
if let SignerSourceKind::Filepath(path) = &s.kind {
std::fs::metadata(path).map(|_| ()).map_err(|e| e.into())
} else {
Ok(())
}
})
.map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"No default signer found, run \"solana-keygen new -o {}\" to create a new one",
self.path
),
)
})?;
*self.is_path_checked.borrow_mut() = true;
}
Ok(&self.path)
}
pub fn generate_unique_signers(
&self,
bulk_signers: Vec<Option<Box<dyn Signer>>>,
@@ -148,9 +103,11 @@ impl DefaultSigner {
unique_signers.push(default_signer);
}
for signer in bulk_signers.into_iter().flatten() {
if !unique_signers.iter().any(|s| s == &signer) {
unique_signers.push(signer);
for signer in bulk_signers.into_iter() {
if let Some(signer) = signer {
if !unique_signers.iter().any(|s| s == &signer) {
unique_signers.push(signer);
}
}
}
Ok(CliSignerInfo {
@@ -163,7 +120,7 @@ impl DefaultSigner {
matches: &ArgMatches,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<Box<dyn Signer>, Box<dyn std::error::Error>> {
signer_from_path(matches, self.path()?, &self.arg_name, wallet_manager)
signer_from_path(matches, &self.path, &self.arg_name, wallet_manager)
}
pub fn signer_from_path_with_config(
@@ -172,142 +129,29 @@ impl DefaultSigner {
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
config: &SignerFromPathConfig,
) -> Result<Box<dyn Signer>, Box<dyn std::error::Error>> {
signer_from_path_with_config(
matches,
self.path()?,
&self.arg_name,
wallet_manager,
config,
)
signer_from_path_with_config(matches, &self.path, &self.arg_name, wallet_manager, config)
}
}
pub(crate) struct SignerSource {
pub kind: SignerSourceKind,
pub derivation_path: Option<DerivationPath>,
pub legacy: bool,
}
impl SignerSource {
fn new(kind: SignerSourceKind) -> Self {
Self {
kind,
derivation_path: None,
legacy: false,
}
}
fn new_legacy(kind: SignerSourceKind) -> Self {
Self {
kind,
derivation_path: None,
legacy: true,
}
}
}
const SIGNER_SOURCE_PROMPT: &str = "prompt";
const SIGNER_SOURCE_FILEPATH: &str = "file";
const SIGNER_SOURCE_USB: &str = "usb";
const SIGNER_SOURCE_STDIN: &str = "stdin";
const SIGNER_SOURCE_PUBKEY: &str = "pubkey";
pub(crate) enum SignerSourceKind {
Prompt,
pub enum KeypairUrl {
Ask,
Filepath(String),
Usb(RemoteWalletLocator),
Usb(String),
Stdin,
Pubkey(Pubkey),
}
impl AsRef<str> for SignerSourceKind {
fn as_ref(&self) -> &str {
match self {
Self::Prompt => SIGNER_SOURCE_PROMPT,
Self::Filepath(_) => SIGNER_SOURCE_FILEPATH,
Self::Usb(_) => SIGNER_SOURCE_USB,
Self::Stdin => SIGNER_SOURCE_STDIN,
Self::Pubkey(_) => SIGNER_SOURCE_PUBKEY,
}
}
}
impl std::fmt::Debug for SignerSourceKind {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let s: &str = self.as_ref();
write!(f, "{}", s)
}
}
#[derive(Debug, Error)]
pub(crate) enum SignerSourceError {
#[error("unrecognized signer source")]
UnrecognizedSource,
#[error(transparent)]
RemoteWalletLocatorError(#[from] RemoteWalletLocatorError),
#[error(transparent)]
DerivationPathError(#[from] DerivationPathError),
#[error(transparent)]
IoError(#[from] std::io::Error),
}
pub(crate) fn parse_signer_source<S: AsRef<str>>(
source: S,
) -> Result<SignerSource, SignerSourceError> {
let source = source.as_ref();
let source = {
#[cfg(target_family = "windows")]
{
source.replace("\\", "/")
}
#[cfg(not(target_family = "windows"))]
{
source.to_string()
}
};
match uriparse::URIReference::try_from(source.as_str()) {
Err(_) => Err(SignerSourceError::UnrecognizedSource),
Ok(uri) => {
if let Some(scheme) = uri.scheme() {
let scheme = scheme.as_str().to_ascii_lowercase();
match scheme.as_str() {
SIGNER_SOURCE_PROMPT => Ok(SignerSource {
kind: SignerSourceKind::Prompt,
derivation_path: DerivationPath::from_uri_any_query(&uri)?,
legacy: false,
}),
SIGNER_SOURCE_FILEPATH => Ok(SignerSource::new(SignerSourceKind::Filepath(
uri.path().to_string(),
))),
SIGNER_SOURCE_USB => Ok(SignerSource {
kind: SignerSourceKind::Usb(RemoteWalletLocator::new_from_uri(&uri)?),
derivation_path: DerivationPath::from_uri_key_query(&uri)?,
legacy: false,
}),
SIGNER_SOURCE_STDIN => Ok(SignerSource::new(SignerSourceKind::Stdin)),
_ => {
#[cfg(target_family = "windows")]
// On Windows, an absolute path's drive letter will be parsed as the URI
// scheme. Assume a filepath source in case of a single character shceme.
if scheme.len() == 1 {
return Ok(SignerSource::new(SignerSourceKind::Filepath(source)));
}
Err(SignerSourceError::UnrecognizedSource)
}
}
} else {
match source.as_str() {
STDOUT_OUTFILE_TOKEN => Ok(SignerSource::new(SignerSourceKind::Stdin)),
ASK_KEYWORD => Ok(SignerSource::new_legacy(SignerSourceKind::Prompt)),
_ => match Pubkey::from_str(source.as_str()) {
Ok(pubkey) => Ok(SignerSource::new(SignerSourceKind::Pubkey(pubkey))),
Err(_) => std::fs::metadata(source.as_str())
.map(|_| SignerSource::new(SignerSourceKind::Filepath(source)))
.map_err(|err| err.into()),
},
}
}
}
pub fn parse_keypair_path(path: &str) -> KeypairUrl {
if path == "-" {
KeypairUrl::Stdin
} else if path == ASK_KEYWORD {
KeypairUrl::Ask
} else if path.starts_with("usb://") {
KeypairUrl::Usb(path.to_string())
} else if let Ok(pubkey) = Pubkey::from_str(path) {
KeypairUrl::Pubkey(pubkey)
} else {
KeypairUrl::Filepath(path.to_string())
}
}
@@ -354,23 +198,16 @@ pub fn signer_from_path_with_config(
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
config: &SignerFromPathConfig,
) -> Result<Box<dyn Signer>, Box<dyn error::Error>> {
let SignerSource {
kind,
derivation_path,
legacy,
} = parse_signer_source(path)?;
match kind {
SignerSourceKind::Prompt => {
match parse_keypair_path(path) {
KeypairUrl::Ask => {
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
Ok(Box::new(keypair_from_seed_phrase(
keypair_name,
skip_validation,
false,
derivation_path,
legacy,
)?))
}
SignerSourceKind::Filepath(path) => match read_keypair_file(&path) {
KeypairUrl::Filepath(path) => match read_keypair_file(&path) {
Err(e) => Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("could not read keypair file \"{}\". Run \"solana-keygen new\" to create a keypair file: {}", path, e),
@@ -378,18 +215,17 @@ pub fn signer_from_path_with_config(
.into()),
Ok(file) => Ok(Box::new(file)),
},
SignerSourceKind::Stdin => {
KeypairUrl::Stdin => {
let mut stdin = std::io::stdin();
Ok(Box::new(read_keypair(&mut stdin)?))
}
SignerSourceKind::Usb(locator) => {
KeypairUrl::Usb(path) => {
if wallet_manager.is_none() {
*wallet_manager = maybe_wallet_manager()?;
}
if let Some(wallet_manager) = wallet_manager {
Ok(Box::new(generate_remote_keypair(
locator,
derivation_path.unwrap_or_default(),
path,
wallet_manager,
matches.is_present("confirm_key"),
keypair_name,
@@ -398,7 +234,7 @@ pub fn signer_from_path_with_config(
Err(RemoteWalletError::NoDeviceFound.into())
}
}
SignerSourceKind::Pubkey(pubkey) => {
KeypairUrl::Pubkey(pubkey) => {
let presigner = pubkeys_sigs_of(matches, SIGNER_ARG.name)
.as_ref()
.and_then(|presigners| presigner_from_pubkey_sigs(&pubkey, presigners));
@@ -423,9 +259,8 @@ pub fn pubkey_from_path(
keypair_name: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<Pubkey, Box<dyn error::Error>> {
let SignerSource { kind, .. } = parse_signer_source(path)?;
match kind {
SignerSourceKind::Pubkey(pubkey) => Ok(pubkey),
match parse_keypair_path(path) {
KeypairUrl::Pubkey(pubkey) => Ok(pubkey),
_ => Ok(signer_from_path(matches, path, keypair_name, wallet_manager)?.pubkey()),
}
}
@@ -436,51 +271,34 @@ pub fn resolve_signer_from_path(
keypair_name: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<Option<String>, Box<dyn error::Error>> {
let SignerSource {
kind,
derivation_path,
legacy,
} = parse_signer_source(path)?;
match kind {
SignerSourceKind::Prompt => {
match parse_keypair_path(path) {
KeypairUrl::Ask => {
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
// This method validates the seed phrase, but returns `None` because there is no path
// on disk or to a device
keypair_from_seed_phrase(
keypair_name,
skip_validation,
false,
derivation_path,
legacy,
)
.map(|_| None)
keypair_from_seed_phrase(keypair_name, skip_validation, false).map(|_| None)
}
SignerSourceKind::Filepath(path) => match read_keypair_file(&path) {
KeypairUrl::Filepath(path) => match read_keypair_file(&path) {
Err(e) => Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"could not read keypair file \"{}\". \
Run \"solana-keygen new\" to create a keypair file: {}",
path, e
),
format!("could not read keypair file \"{}\". Run \"solana-keygen new\" to create a keypair file: {}", path, e),
)
.into()),
Ok(_) => Ok(Some(path.to_string())),
},
SignerSourceKind::Stdin => {
KeypairUrl::Stdin => {
let mut stdin = std::io::stdin();
// This method validates the keypair from stdin, but returns `None` because there is no
// path on disk or to a device
read_keypair(&mut stdin).map(|_| None)
}
SignerSourceKind::Usb(locator) => {
KeypairUrl::Usb(path) => {
if wallet_manager.is_none() {
*wallet_manager = maybe_wallet_manager()?;
}
if let Some(wallet_manager) = wallet_manager {
let path = generate_remote_keypair(
locator,
derivation_path.unwrap_or_default(),
path,
wallet_manager,
matches.is_present("confirm_key"),
keypair_name,
@@ -495,7 +313,7 @@ pub fn resolve_signer_from_path(
}
}
// Keyword used to indicate that the user should be prompted for a keypair seed phrase
// Keyword used to indicate that the user should be asked for a keypair seed phrase
pub const ASK_KEYWORD: &str = "ASK";
pub const SKIP_SEED_PHRASE_VALIDATION_ARG: ArgConstant<'static> = ArgConstant {
@@ -506,7 +324,7 @@ pub const SKIP_SEED_PHRASE_VALIDATION_ARG: ArgConstant<'static> = ArgConstant {
/// Prompts user for a passphrase and then asks for confirmirmation to check for mistakes
pub fn prompt_passphrase(prompt: &str) -> Result<String, Box<dyn error::Error>> {
let passphrase = prompt_password_stderr(prompt)?;
let passphrase = prompt_password_stderr(&prompt)?;
if !passphrase.is_empty() {
let confirmed = rpassword::prompt_password_stderr("Enter same passphrase again: ")?;
if confirmed != passphrase {
@@ -516,56 +334,6 @@ pub fn prompt_passphrase(prompt: &str) -> Result<String, Box<dyn error::Error>>
Ok(passphrase)
}
/// Parses a path into a SignerSource and returns a Keypair for supporting SignerSourceKinds
pub fn keypair_from_path(
matches: &ArgMatches,
path: &str,
keypair_name: &str,
confirm_pubkey: bool,
) -> Result<Keypair, Box<dyn error::Error>> {
let SignerSource {
kind,
derivation_path,
legacy,
} = parse_signer_source(path)?;
match kind {
SignerSourceKind::Prompt => {
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
Ok(keypair_from_seed_phrase(
keypair_name,
skip_validation,
confirm_pubkey,
derivation_path,
legacy,
)?)
}
SignerSourceKind::Filepath(path) => match read_keypair_file(&path) {
Err(e) => Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"could not read keypair file \"{}\". \
Run \"solana-keygen new\" to create a keypair file: {}",
path, e
),
)
.into()),
Ok(file) => Ok(file),
},
SignerSourceKind::Stdin => {
let mut stdin = std::io::stdin();
Ok(read_keypair(&mut stdin)?)
}
_ => Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"signer of type `{:?}` does not support Keypair output",
kind
),
)
.into()),
}
}
/// Reads user input from stdin to retrieve a seed phrase and passphrase for keypair derivation
/// Optionally skips validation of seed phrase
/// Optionally confirms recovered public key
@@ -573,8 +341,6 @@ pub fn keypair_from_seed_phrase(
keypair_name: &str,
skip_validation: bool,
confirm_pubkey: bool,
derivation_path: Option<DerivationPath>,
legacy: bool,
) -> Result<Keypair, Box<dyn error::Error>> {
let seed_phrase = prompt_password_stderr(&format!("[{}] seed phrase: ", keypair_name))?;
let seed_phrase = seed_phrase.trim();
@@ -585,12 +351,7 @@ pub fn keypair_from_seed_phrase(
let keypair = if skip_validation {
let passphrase = prompt_passphrase(&passphrase_prompt)?;
if legacy {
keypair_from_seed_phrase_and_passphrase(seed_phrase, &passphrase)?
} else {
let seed = generate_seed_from_seed_phrase_and_passphrase(seed_phrase, &passphrase);
keypair_from_seed_and_derivation_path(&seed, derivation_path)?
}
keypair_from_seed_phrase_and_passphrase(&seed_phrase, &passphrase)?
} else {
let sanitized = sanitize_seed_phrase(seed_phrase);
let parse_language_fn = || {
@@ -613,11 +374,7 @@ pub fn keypair_from_seed_phrase(
let mnemonic = parse_language_fn()?;
let passphrase = prompt_passphrase(&passphrase_prompt)?;
let seed = Seed::new(&mnemonic, &passphrase);
if legacy {
keypair_from_seed(seed.as_bytes())?
} else {
keypair_from_seed_and_derivation_path(seed.as_bytes(), derivation_path)?
}
keypair_from_seed(seed.as_bytes())?
};
if confirm_pubkey {
@@ -645,9 +402,7 @@ fn sanitize_seed_phrase(seed_phrase: &str) -> String {
#[cfg(test)]
mod tests {
use super::*;
use solana_remote_wallet::locator::Manufacturer;
use solana_sdk::system_instruction;
use tempfile::NamedTempFile;
#[test]
fn test_sanitize_seed_phrase() {
@@ -688,122 +443,4 @@ mod tests {
];
assert_eq!(signer_pubkeys, expect);
}
#[test]
fn test_parse_signer_source() {
assert!(matches!(
parse_signer_source(STDOUT_OUTFILE_TOKEN).unwrap(),
SignerSource {
kind: SignerSourceKind::Stdin,
derivation_path: None,
legacy: false,
}
));
let stdin = "stdin:".to_string();
assert!(matches!(
parse_signer_source(&stdin).unwrap(),
SignerSource {
kind: SignerSourceKind::Stdin,
derivation_path: None,
legacy: false,
}
));
assert!(matches!(
parse_signer_source(ASK_KEYWORD).unwrap(),
SignerSource {
kind: SignerSourceKind::Prompt,
derivation_path: None,
legacy: true,
}
));
let pubkey = Pubkey::new_unique();
assert!(
matches!(parse_signer_source(&pubkey.to_string()).unwrap(), SignerSource {
kind: SignerSourceKind::Pubkey(p),
derivation_path: None,
legacy: false,
}
if p == pubkey)
);
// Set up absolute and relative path strs
let file0 = NamedTempFile::new().unwrap();
let path = file0.path();
assert!(path.is_absolute());
let absolute_path_str = path.to_str().unwrap();
let file1 = NamedTempFile::new_in(std::env::current_dir().unwrap()).unwrap();
let path = file1.path().file_name().unwrap().to_str().unwrap();
let path = std::path::Path::new(path);
assert!(path.is_relative());
let relative_path_str = path.to_str().unwrap();
assert!(
matches!(parse_signer_source(absolute_path_str).unwrap(), SignerSource {
kind: SignerSourceKind::Filepath(p),
derivation_path: None,
legacy: false,
} if p == absolute_path_str)
);
assert!(
matches!(parse_signer_source(&relative_path_str).unwrap(), SignerSource {
kind: SignerSourceKind::Filepath(p),
derivation_path: None,
legacy: false,
} if p == relative_path_str)
);
let usb = "usb://ledger".to_string();
let expected_locator = RemoteWalletLocator {
manufacturer: Manufacturer::Ledger,
pubkey: None,
};
assert!(matches!(parse_signer_source(&usb).unwrap(), SignerSource {
kind: SignerSourceKind::Usb(u),
derivation_path: None,
legacy: false,
} if u == expected_locator));
let usb = "usb://ledger?key=0/0".to_string();
let expected_locator = RemoteWalletLocator {
manufacturer: Manufacturer::Ledger,
pubkey: None,
};
let expected_derivation_path = Some(DerivationPath::new_bip44(Some(0), Some(0)));
assert!(matches!(parse_signer_source(&usb).unwrap(), SignerSource {
kind: SignerSourceKind::Usb(u),
derivation_path: d,
legacy: false,
} if u == expected_locator && d == expected_derivation_path));
// Catchall into SignerSource::Filepath fails
let junk = "sometextthatisnotapubkeyorfile".to_string();
assert!(Pubkey::from_str(&junk).is_err());
assert!(matches!(
parse_signer_source(&junk),
Err(SignerSourceError::IoError(_))
));
let prompt = "prompt:".to_string();
assert!(matches!(
parse_signer_source(&prompt).unwrap(),
SignerSource {
kind: SignerSourceKind::Prompt,
derivation_path: None,
legacy: false,
}
));
assert!(
matches!(parse_signer_source(&format!("file:{}", absolute_path_str)).unwrap(), SignerSource {
kind: SignerSourceKind::Filepath(p),
derivation_path: None,
legacy: false,
} if p == absolute_path_str)
);
assert!(
matches!(parse_signer_source(&format!("file:{}", relative_path_str)).unwrap(), SignerSource {
kind: SignerSourceKind::Filepath(p),
derivation_path: None,
legacy: false,
} if p == relative_path_str)
);
}
}

View File

@@ -27,6 +27,5 @@ pub mod fee_payer;
pub mod input_parsers;
pub mod input_validators;
pub mod keypair;
pub mod memo;
pub mod nonce;
pub mod offline;

View File

@@ -1,15 +0,0 @@
use {crate::ArgConstant, clap::Arg};
pub const MEMO_ARG: ArgConstant<'static> = ArgConstant {
name: "memo",
long: "--with-memo",
help: "Specify a memo string to include in the transaction.",
};
pub fn memo_arg<'a, 'b>() -> Arg<'a, 'b> {
Arg::with_name(MEMO_ARG.name)
.long(MEMO_ARG.long)
.takes_value(true)
.value_name("MEMO")
.help(MEMO_ARG.help)
}

View File

@@ -1,7 +1,5 @@
use {
crate::{input_validators::*, offline::BLOCKHASH_ARG, ArgConstant},
clap::{App, Arg},
};
use crate::{input_validators::*, offline::BLOCKHASH_ARG, ArgConstant};
use clap::{App, Arg};
pub const NONCE_ARG: ArgConstant<'static> = ArgConstant {
name: "nonce",

View File

@@ -1,7 +1,5 @@
use {
crate::{input_validators::*, ArgConstant},
clap::{App, Arg},
};
use crate::{input_validators::*, ArgConstant};
use clap::{App, Arg};
pub const BLOCKHASH_ARG: ArgConstant<'static> = ArgConstant {
name: "blockhash",

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -5,11 +5,9 @@ use {
indicatif::{ProgressBar, ProgressStyle},
solana_sdk::{
clock::UnixTimestamp, hash::Hash, message::Message, native_token::lamports_to_sol,
program_utils::limited_deserialize, pubkey::Pubkey, stake, transaction::Transaction,
program_utils::limited_deserialize, transaction::Transaction,
},
solana_transaction_status::UiTransactionStatusMeta,
spl_memo::id as spl_memo_id,
spl_memo::v1::id as spl_memo_v1_id,
std::{collections::HashMap, fmt, io},
};
@@ -30,11 +28,6 @@ impl Default for BuildBalanceMessageConfig {
}
}
fn is_memo_program(k: &Pubkey) -> bool {
let k_str = k.to_string();
(k_str == spl_memo_v1_id().to_string()) || (k_str == spl_memo_id().to_string())
}
pub fn build_balance_message_with_config(
lamports: u64,
config: &BuildBalanceMessageConfig,
@@ -140,7 +133,7 @@ fn format_account_mode(message: &Message, index: usize) -> String {
} else {
"-"
},
if message.is_writable(index, /*demote_program_write_locks=*/ true) {
if message.is_writable(index) {
"w" // comment for consistent rust fmt (no joking; lol)
} else {
"-"
@@ -244,9 +237,10 @@ pub fn write_transaction<W: io::Write>(
writeln!(w, "{} {:?}", prefix, vote_instruction)?;
raw = false;
}
} else if program_pubkey == stake::program::id() {
if let Ok(stake_instruction) =
limited_deserialize::<stake::instruction::StakeInstruction>(&instruction.data)
} 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;
@@ -259,11 +253,6 @@ pub fn write_transaction<W: io::Write>(
writeln!(w, "{} {:?}", prefix, system_instruction)?;
raw = false;
}
} else if is_memo_program(&program_pubkey) {
if let Ok(s) = std::str::from_utf8(&instruction.data) {
writeln!(w, "{} Data: \"{}\"", prefix, s)?;
raw = false;
}
}
if raw {
@@ -321,38 +310,7 @@ pub fn write_transaction<W: io::Write>(
if !log_messages.is_empty() {
writeln!(w, "{}Log Messages:", prefix,)?;
for log_message in log_messages {
writeln!(w, "{} {}", prefix, log_message)?;
}
}
}
if let Some(rewards) = &transaction_status.rewards {
if !rewards.is_empty() {
writeln!(w, "{}Rewards:", prefix,)?;
writeln!(
w,
"{} {:<44} {:^15} {:<15} {:<20}",
prefix, "Address", "Type", "Amount", "New Balance"
)?;
for reward in rewards {
let sign = if reward.lamports < 0 { "-" } else { "" };
writeln!(
w,
"{} {:<44} {:^15} {:<15} {}",
prefix,
reward.pubkey,
if let Some(reward_type) = reward.reward_type {
format!("{}", reward_type)
} else {
"-".to_string()
},
format!(
"{}{:<14.9}",
sign,
lamports_to_sol(reward.lamports.abs() as u64)
),
format!("{:<18.9}", lamports_to_sol(reward.post_balance),)
)?;
writeln!(w, "{} {}", prefix, log_message,)?;
}
}
}

View File

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

View File

@@ -231,9 +231,18 @@ mod tests {
mocks.insert(RpcRequest::GetBalance, account_balance_response);
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
assert!(check_account_for_balance(&rpc_client, &pubkey, 1).unwrap());
assert!(check_account_for_balance(&rpc_client, &pubkey, account_balance).unwrap());
assert!(!check_account_for_balance(&rpc_client, &pubkey, account_balance + 1).unwrap());
assert_eq!(
check_account_for_balance(&rpc_client, &pubkey, 1).unwrap(),
true
);
assert_eq!(
check_account_for_balance(&rpc_client, &pubkey, account_balance).unwrap(),
true
);
assert_eq!(
check_account_for_balance(&rpc_client, &pubkey, account_balance + 1).unwrap(),
false
);
}
#[test]

View File

@@ -1,202 +0,0 @@
use crate::{
cli::*, cluster_query::*, feature::*, inflation::*, nonce::*, program::*, stake::*,
validator_info::*, vote::*, wallet::*,
};
use clap::{App, AppSettings, Arg, ArgGroup, SubCommand};
use solana_clap_utils::{self, input_validators::*, keypair::*};
use solana_cli_config::CONFIG_FILE;
pub fn get_clap_app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, 'v> {
App::new(name)
.about(about)
.version(version)
.setting(AppSettings::SubcommandRequiredElseHelp)
.arg({
let arg = Arg::with_name("config_file")
.short("C")
.long("config")
.value_name("FILEPATH")
.takes_value(true)
.global(true)
.help("Configuration file to use");
if let Some(ref config_file) = *CONFIG_FILE {
arg.default_value(config_file)
} else {
arg
}
})
.arg(
Arg::with_name("json_rpc_url")
.short("u")
.long("url")
.value_name("URL_OR_MONIKER")
.takes_value(true)
.global(true)
.validator(is_url_or_moniker)
.help(
"URL for Solana's JSON RPC or moniker (or their first letter): \
[mainnet-beta, testnet, devnet, localhost]",
),
)
.arg(
Arg::with_name("websocket_url")
.long("ws")
.value_name("URL")
.takes_value(true)
.global(true)
.validator(is_url)
.help("WebSocket URL for the solana cluster"),
)
.arg(
Arg::with_name("keypair")
.short("k")
.long("keypair")
.value_name("KEYPAIR")
.global(true)
.takes_value(true)
.help("Filepath or URL to a keypair"),
)
.arg(
Arg::with_name("commitment")
.long("commitment")
.takes_value(true)
.possible_values(&[
"processed",
"confirmed",
"finalized",
"recent", // Deprecated as of v1.5.5
"single", // Deprecated as of v1.5.5
"singleGossip", // Deprecated as of v1.5.5
"root", // Deprecated as of v1.5.5
"max", // Deprecated as of v1.5.5
])
.value_name("COMMITMENT_LEVEL")
.hide_possible_values(true)
.global(true)
.help("Return information at the selected commitment level [possible values: processed, confirmed, finalized]"),
)
.arg(
Arg::with_name("verbose")
.long("verbose")
.short("v")
.global(true)
.help("Show additional information"),
)
.arg(
Arg::with_name("no_address_labels")
.long("no-address-labels")
.global(true)
.help("Do not use address labels in the output"),
)
.arg(
Arg::with_name("output_format")
.long("output")
.value_name("FORMAT")
.global(true)
.takes_value(true)
.possible_values(&["json", "json-compact"])
.help("Return information in specified output format"),
)
.arg(
Arg::with_name(SKIP_SEED_PHRASE_VALIDATION_ARG.name)
.long(SKIP_SEED_PHRASE_VALIDATION_ARG.long)
.global(true)
.help(SKIP_SEED_PHRASE_VALIDATION_ARG.help),
)
.arg(
Arg::with_name("rpc_timeout")
.long("rpc-timeout")
.value_name("SECONDS")
.takes_value(true)
.default_value(DEFAULT_RPC_TIMEOUT_SECONDS)
.global(true)
.hidden(true)
.help("Timeout value for RPC requests"),
)
.arg(
Arg::with_name("confirm_transaction_initial_timeout")
.long("confirm-timeout")
.value_name("SECONDS")
.takes_value(true)
.default_value(DEFAULT_CONFIRM_TX_TIMEOUT_SECONDS)
.global(true)
.hidden(true)
.help("Timeout value for initial transaction status"),
)
.cluster_query_subcommands()
.feature_subcommands()
.inflation_subcommands()
.nonce_subcommands()
.program_subcommands()
.stake_subcommands()
.validator_info_subcommands()
.vote_subcommands()
.wallet_subcommands()
.subcommand(
SubCommand::with_name("config")
.about("Solana command-line tool configuration settings")
.aliases(&["get", "set"])
.setting(AppSettings::SubcommandRequiredElseHelp)
.subcommand(
SubCommand::with_name("get")
.about("Get current config settings")
.arg(
Arg::with_name("specific_setting")
.index(1)
.value_name("CONFIG_FIELD")
.takes_value(true)
.possible_values(&[
"json_rpc_url",
"websocket_url",
"keypair",
"commitment",
])
.help("Return a specific config setting"),
),
)
.subcommand(
SubCommand::with_name("set")
.about("Set a config setting")
.group(
ArgGroup::with_name("config_settings")
.args(&["json_rpc_url", "websocket_url", "keypair", "commitment"])
.multiple(true)
.required(true),
),
)
.subcommand(
SubCommand::with_name("import-address-labels")
.about("Import a list of address labels")
.arg(
Arg::with_name("filename")
.index(1)
.value_name("FILENAME")
.takes_value(true)
.help("YAML file of address labels"),
),
)
.subcommand(
SubCommand::with_name("export-address-labels")
.about("Export the current address labels")
.arg(
Arg::with_name("filename")
.index(1)
.value_name("FILENAME")
.takes_value(true)
.help("YAML file to receive the current address labels"),
),
),
)
.subcommand(
SubCommand::with_name("completion")
.about("Generate completion scripts for various shells")
.arg(
Arg::with_name("shell")
.long("shell")
.short("s")
.takes_value(true)
.possible_values(&["bash", "fish", "zsh", "powershell", "elvish"])
.default_value("bash")
)
)
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
use crate::{
cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult},
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
stake::is_stake_program_v2_enabled,
};
use clap::{value_t, value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand};
use console::{style, Emoji};
@@ -23,12 +24,11 @@ use solana_client::{
pubsub_client::PubsubClient,
rpc_client::{GetConfirmedSignaturesForAddress2Config, RpcClient},
rpc_config::{
RpcAccountInfoConfig, RpcBlockConfig, RpcGetVoteAccountsConfig, RpcLargestAccountsConfig,
RpcLargestAccountsFilter, RpcProgramAccountsConfig, RpcTransactionConfig,
RpcTransactionLogsConfig, RpcTransactionLogsFilter,
RpcAccountInfoConfig, RpcConfirmedBlockConfig, RpcLargestAccountsConfig,
RpcLargestAccountsFilter, RpcProgramAccountsConfig, RpcTransactionLogsConfig,
RpcTransactionLogsFilter,
},
rpc_filter,
rpc_request::DELINQUENT_VALIDATOR_SLOT_DISTANCE,
rpc_response::SlotInfo,
};
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
@@ -41,28 +41,23 @@ use solana_sdk::{
hash::Hash,
message::Message,
native_token::lamports_to_sol,
nonce::State as NonceState,
pubkey::{self, Pubkey},
rent::Rent,
rpc_port::DEFAULT_RPC_PORT_STR,
signature::Signature,
slot_history,
stake::{self, state::StakeState},
system_instruction, system_program,
sysvar::{
self,
slot_history::SlotHistory,
stake_history::{self},
},
timing,
transaction::Transaction,
};
use solana_transaction_status::UiTransactionEncoding;
use solana_vote_program::vote_state::VoteState;
use std::{
collections::{BTreeMap, HashMap, VecDeque},
fmt,
str::FromStr,
net::SocketAddr,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
@@ -70,7 +65,6 @@ use std::{
thread::sleep,
time::{Duration, Instant, SystemTime, UNIX_EPOCH},
};
use thiserror::Error;
static CHECK_MARK: Emoji = Emoji("", "");
static CROSS_MARK: Emoji = Emoji("", "");
@@ -122,7 +116,7 @@ impl ClusterQuerySubCommands for App<'_, '_> {
.long("our-localhost")
.takes_value(false)
.value_name("PORT")
.default_value(DEFAULT_RPC_PORT_STR)
.default_value(&DEFAULT_RPC_PORT_STR)
.validator(is_port)
.help("Guess Identity pubkey and validator rpc node assuming local (possibly private) validator"),
)
@@ -176,7 +170,7 @@ impl ClusterQuerySubCommands for App<'_, '_> {
.takes_value(true)
.value_name("EPOCH")
.validator(is_epoch)
.help("Epoch to show leader schedule for. [default: current]")
.help("Epoch to show leader schedule for. (default: current)")
)
)
.subcommand(
@@ -350,57 +344,6 @@ impl ClusterQuerySubCommands for App<'_, '_> {
.long("lamports")
.takes_value(false)
.help("Display balance in lamports instead of SOL"),
)
.arg(
Arg::with_name("number")
.long("number")
.short("n")
.takes_value(false)
.help("Number the validators"),
)
.arg(
Arg::with_name("reverse")
.long("reverse")
.short("r")
.takes_value(false)
.help("Reverse order while sorting"),
)
.arg(
Arg::with_name("sort")
.long("sort")
.takes_value(true)
.possible_values(&[
"delinquent",
"commission",
"credits",
"identity",
"last-vote",
"root",
"skip-rate",
"stake",
"vote-account",
])
.default_value("stake")
.help("Sort order (does not affect JSON output)"),
)
.arg(
Arg::with_name("keep_unstaked_delinquents")
.long("keep-unstaked-delinquents")
.takes_value(false)
.help("Don't discard unstaked, delinquent validators")
)
.arg(
Arg::with_name("delinquent_slot_distance")
.long("delinquent-slot-distance")
.takes_value(true)
.value_name("SLOT_DISTANCE")
.validator(is_slot)
.help(
concatcp!(
"Minimum slot distance from the tip to consider a validator delinquent. [default: ",
DELINQUENT_VALIDATOR_SLOT_DISTANCE,
"]",
))
),
)
.subcommand(
@@ -454,14 +397,9 @@ impl ClusterQuerySubCommands for App<'_, '_> {
.arg(
Arg::with_name("data_length")
.index(1)
.value_name("DATA_LENGTH_OR_MONIKER")
.value_name("DATA_LENGTH")
.required(true)
.validator(|s| {
RentLengthValue::from_str(&s)
.map(|_| ())
.map_err(|e| e.to_string())
})
.help("Length of data in the account to calculate rent for, or moniker: [nonce, stake, system, vote]"),
.help("Length of data in the account to calculate rent for"),
)
.arg(
Arg::with_name("lamports")
@@ -634,33 +572,9 @@ pub fn parse_show_stakes(
pub fn parse_show_validators(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
let use_lamports_unit = matches.is_present("lamports");
let number_validators = matches.is_present("number");
let reverse_sort = matches.is_present("reverse");
let keep_unstaked_delinquents = matches.is_present("keep_unstaked_delinquents");
let delinquent_slot_distance = value_of(matches, "delinquent_slot_distance");
let sort_order = match value_t_or_exit!(matches, "sort", String).as_str() {
"delinquent" => CliValidatorsSortOrder::Delinquent,
"commission" => CliValidatorsSortOrder::Commission,
"credits" => CliValidatorsSortOrder::EpochCredits,
"identity" => CliValidatorsSortOrder::Identity,
"last-vote" => CliValidatorsSortOrder::LastVote,
"root" => CliValidatorsSortOrder::Root,
"skip-rate" => CliValidatorsSortOrder::SkipRate,
"stake" => CliValidatorsSortOrder::Stake,
"vote-account" => CliValidatorsSortOrder::VoteAccount,
_ => unreachable!(),
};
Ok(CliCommandInfo {
command: CliCommand::ShowValidators {
use_lamports_unit,
sort_order,
reverse_sort,
number_validators,
keep_unstaked_delinquents,
delinquent_slot_distance,
},
command: CliCommand::ShowValidators { use_lamports_unit },
signers: vec![],
})
}
@@ -961,19 +875,18 @@ pub fn process_fees(
*recent_blockhash,
fee_calculator.lamports_per_signature,
None,
None,
)
} else {
CliFees::none()
}
} else {
let result = rpc_client.get_fees_with_commitment(config.commitment)?;
let result = rpc_client.get_recent_blockhash_with_commitment(config.commitment)?;
let (recent_blockhash, fee_calculator, last_valid_slot) = result.value;
CliFees::some(
result.context.slot,
result.value.blockhash,
result.value.fee_calculator.lamports_per_signature,
None,
Some(result.value.last_valid_block_height),
recent_blockhash,
fee_calculator.lamports_per_signature,
Some(last_valid_slot),
)
};
Ok(config.output_format.formatted_string(&fees))
@@ -1052,12 +965,12 @@ pub fn process_get_block(
};
let encoded_confirmed_block = rpc_client
.get_block_with_config(
.get_confirmed_block_with_config(
slot,
RpcBlockConfig {
RpcConfirmedBlockConfig {
encoding: Some(UiTransactionEncoding::Base64),
commitment: Some(CommitmentConfig::confirmed()),
..RpcBlockConfig::default()
..RpcConfirmedBlockConfig::default()
},
)?
.into();
@@ -1100,15 +1013,9 @@ pub fn process_get_epoch_info(rpc_client: &RpcClient, config: &CliConfig) -> Pro
(secs as u64).saturating_mul(1000).checked_div(slots)
})
.unwrap_or(clock::DEFAULT_MS_PER_SLOT);
let start_block_time = rpc_client
.get_block_time(epoch_info.absolute_slot - epoch_info.slot_index)
.ok();
let current_block_time = rpc_client.get_block_time(epoch_info.absolute_slot).ok();
let epoch_info = CliEpochInfo {
epoch_info,
average_slot_time_ms,
start_block_time,
current_block_time,
};
Ok(config.output_format.formatted_string(&epoch_info))
}
@@ -1124,8 +1031,8 @@ pub fn process_get_slot(rpc_client: &RpcClient, _config: &CliConfig) -> ProcessR
}
pub fn process_get_block_height(rpc_client: &RpcClient, _config: &CliConfig) -> ProcessResult {
let block_height = rpc_client.get_block_height()?;
Ok(block_height.to_string())
let epoch_info = rpc_client.get_epoch_info()?;
Ok(epoch_info.block_height.to_string())
}
pub fn parse_show_block_production(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
@@ -1152,6 +1059,8 @@ pub fn process_show_block_production(
return Err(format!("Epoch {} is in the future", epoch).into());
}
let minimum_ledger_slot = rpc_client.minimum_ledger_slot()?;
let first_slot_in_epoch = epoch_schedule.get_first_slot_in_epoch(epoch);
let end_slot = std::cmp::min(
epoch_info.absolute_slot,
@@ -1164,60 +1073,32 @@ pub fn process_show_block_production(
first_slot_in_epoch
};
if minimum_ledger_slot > end_slot {
return Err(format!(
"Ledger data not available for slots {} to {} (minimum ledger slot is {})",
start_slot, end_slot, minimum_ledger_slot
)
.into());
}
if minimum_ledger_slot > start_slot {
println!(
"\n{}",
style(format!(
"Note: Requested start slot was {} but minimum ledger slot is {}",
start_slot, minimum_ledger_slot
))
.italic(),
);
start_slot = minimum_ledger_slot;
}
let progress_bar = new_spinner_progress_bar();
progress_bar.set_message(&format!(
"Fetching confirmed blocks between slots {} and {}...",
start_slot, end_slot
));
let slot_history_account = rpc_client
.get_account_with_commitment(&sysvar::slot_history::id(), CommitmentConfig::finalized())?
.value
.unwrap();
let slot_history: SlotHistory = from_account(&slot_history_account).ok_or_else(|| {
CliError::RpcRequestError("Failed to deserialize slot history".to_string())
})?;
let (confirmed_blocks, start_slot) =
if start_slot >= slot_history.oldest() && end_slot <= slot_history.newest() {
// Fast, more reliable path using the SlotHistory sysvar
let confirmed_blocks: Vec<_> = (start_slot..=end_slot)
.filter(|slot| slot_history.check(*slot) == slot_history::Check::Found)
.collect();
(confirmed_blocks, start_slot)
} else {
// Slow, less reliable path using `getBlocks`.
//
// "less reliable" because if the RPC node has holds in its ledger then the block production data will be
// incorrect. This condition currently can't be detected over RPC
//
let minimum_ledger_slot = rpc_client.minimum_ledger_slot()?;
if minimum_ledger_slot > end_slot {
return Err(format!(
"Ledger data not available for slots {} to {} (minimum ledger slot is {})",
start_slot, end_slot, minimum_ledger_slot
)
.into());
}
if minimum_ledger_slot > start_slot {
progress_bar.println(format!(
"{}",
style(format!(
"Note: Requested start slot was {} but minimum ledger slot is {}",
start_slot, minimum_ledger_slot
))
.italic(),
));
start_slot = minimum_ledger_slot;
}
let confirmed_blocks = rpc_client.get_blocks(start_slot, Some(end_slot))?;
(confirmed_blocks, start_slot)
};
let confirmed_blocks = rpc_client.get_confirmed_blocks(start_slot, Some(end_slot))?;
let start_slot_index = (start_slot - first_slot_in_epoch) as usize;
let end_slot_index = (end_slot - first_slot_in_epoch) as usize;
@@ -1343,8 +1224,8 @@ pub fn process_supply(
}
pub fn process_total_supply(rpc_client: &RpcClient, _config: &CliConfig) -> ProcessResult {
let supply = rpc_client.supply()?.value;
Ok(format!("{} SOL", lamports_to_sol(supply.total)))
let total_supply = rpc_client.total_supply()?;
Ok(format!("{} SOL", lamports_to_sol(total_supply)))
}
pub fn process_get_transaction_count(rpc_client: &RpcClient, _config: &CliConfig) -> ProcessResult {
@@ -1685,14 +1566,40 @@ pub fn process_live_slots(config: &CliConfig) -> ProcessResult {
pub fn process_show_gossip(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
let cluster_nodes = rpc_client.get_cluster_nodes()?;
let nodes: Vec<_> = cluster_nodes
fn format_port(addr: Option<SocketAddr>) -> String {
addr.map(|addr| addr.port().to_string())
.unwrap_or_else(|| "none".to_string())
}
let s: Vec<_> = cluster_nodes
.into_iter()
.map(|node| CliGossipNode::new(node, &config.address_labels))
.map(|node| {
format!(
"{:15} | {:44} | {:6} | {:5} | {:21} | {}",
node.gossip
.map(|addr| addr.ip().to_string())
.unwrap_or_else(|| "none".to_string()),
format_labeled_address(&node.pubkey, &config.address_labels),
format_port(node.gossip),
format_port(node.tpu),
node.rpc
.map(|addr| addr.to_string())
.unwrap_or_else(|| "none".to_string()),
node.version.unwrap_or_else(|| "unknown".to_string()),
)
})
.collect();
Ok(config
.output_format
.formatted_string(&CliGossipNodes(nodes)))
Ok(format!(
"IP Address | Node identifier \
| Gossip | TPU | RPC Address | Version\n\
----------------+----------------------------------------------+\
--------+-------+-----------------------+----------------\n\
{}\n\
Nodes: {}",
s.join("\n"),
s.len(),
))
}
pub fn process_show_stakes(
@@ -1702,16 +1609,17 @@ pub fn process_show_stakes(
vote_account_pubkeys: Option<&[Pubkey]>,
) -> ProcessResult {
use crate::stake::build_stake_state;
use solana_stake_program::stake_state::StakeState;
let progress_bar = new_spinner_progress_bar();
progress_bar.set_message("Fetching stake accounts...");
let mut program_accounts_config = RpcProgramAccountsConfig {
filters: None,
account_config: RpcAccountInfoConfig {
encoding: Some(solana_account_decoder::UiAccountEncoding::Base64),
..RpcAccountInfoConfig::default()
},
..RpcProgramAccountsConfig::default()
};
if let Some(vote_account_pubkeys) = vote_account_pubkeys {
@@ -1738,7 +1646,7 @@ pub fn process_show_stakes(
}
}
let all_stake_accounts = rpc_client
.get_program_accounts_with_config(&stake::program::id(), program_accounts_config)?;
.get_program_accounts_with_config(&solana_stake_program::id(), program_accounts_config)?;
let stake_history_account = rpc_client.get_account(&stake_history::id())?;
let clock_account = rpc_client.get_account(&sysvar::clock::id())?;
let clock: Clock = from_account(&clock_account).ok_or_else(|| {
@@ -1749,6 +1657,8 @@ pub fn process_show_stakes(
let stake_history = from_account(&stake_history_account).ok_or_else(|| {
CliError::RpcRequestError("Failed to deserialize stake history".to_string())
})?;
// At v1.6, this check can be removed and simply passed as `true`
let stake_program_v2_enabled = is_stake_program_v2_enabled(rpc_client)?;
let mut stake_accounts: Vec<CliKeyedStakeState> = vec![];
for (stake_pubkey, stake_account) in all_stake_accounts {
@@ -1764,6 +1674,7 @@ pub fn process_show_stakes(
use_lamports_unit,
&stake_history,
&clock,
stake_program_v2_enabled,
),
});
}
@@ -1782,6 +1693,7 @@ pub fn process_show_stakes(
use_lamports_unit,
&stake_history,
&clock,
stake_program_v2_enabled,
),
});
}
@@ -1809,42 +1721,10 @@ pub fn process_show_validators(
rpc_client: &RpcClient,
config: &CliConfig,
use_lamports_unit: bool,
validators_sort_order: CliValidatorsSortOrder,
validators_reverse_sort: bool,
number_validators: bool,
keep_unstaked_delinquents: bool,
delinquent_slot_distance: Option<Slot>,
) -> ProcessResult {
let progress_bar = new_spinner_progress_bar();
progress_bar.set_message("Fetching vote accounts...");
let epoch_info = rpc_client.get_epoch_info()?;
let vote_accounts = rpc_client.get_vote_accounts_with_config(RpcGetVoteAccountsConfig {
keep_unstaked_delinquents: Some(keep_unstaked_delinquents),
delinquent_slot_distance,
..RpcGetVoteAccountsConfig::default()
})?;
let vote_accounts = rpc_client.get_vote_accounts()?;
progress_bar.set_message("Fetching block production...");
let skip_rate: HashMap<_, _> = rpc_client
.get_block_production()
.ok()
.map(|result| {
result
.value
.by_identity
.into_iter()
.map(|(identity, (leader_slots, blocks_produced))| {
(
identity,
100. * (leader_slots.saturating_sub(blocks_produced)) as f64
/ leader_slots as f64,
)
})
.collect()
})
.unwrap_or_default();
progress_bar.set_message("Fetching version information...");
let mut node_version = HashMap::new();
let unknown_version = "unknown".to_string();
for contact_info in rpc_client.get_cluster_nodes()? {
@@ -1856,8 +1736,6 @@ pub fn process_show_validators(
);
}
progress_bar.finish_and_clear();
let total_active_stake = vote_accounts
.current
.iter()
@@ -1872,8 +1750,9 @@ pub fn process_show_validators(
.sum();
let total_current_stake = total_active_stake - total_delinquent_stake;
let current_validators: Vec<CliValidator> = vote_accounts
.current
let mut current = vote_accounts.current;
current.sort_by(|a, b| b.activated_stake.cmp(&a.activated_stake));
let current_validators: Vec<CliValidator> = current
.iter()
.map(|vote_account| {
CliValidator::new(
@@ -1883,23 +1762,22 @@ pub fn process_show_validators(
.get(&vote_account.node_pubkey)
.unwrap_or(&unknown_version)
.clone(),
skip_rate.get(&vote_account.node_pubkey).cloned(),
&config.address_labels,
)
})
.collect();
let delinquent_validators: Vec<CliValidator> = vote_accounts
.delinquent
let mut delinquent = vote_accounts.delinquent;
delinquent.sort_by(|a, b| b.activated_stake.cmp(&a.activated_stake));
let delinquent_validators: Vec<CliValidator> = delinquent
.iter()
.map(|vote_account| {
CliValidator::new_delinquent(
CliValidator::new(
vote_account,
epoch_info.epoch,
node_version
.get(&vote_account.node_pubkey)
.unwrap_or(&unknown_version)
.clone(),
skip_rate.get(&vote_account.node_pubkey).cloned(),
&config.address_labels,
)
})
@@ -1921,43 +1799,12 @@ pub fn process_show_validators(
entry.delinquent_active_stake += validator.activated_stake;
}
let validators: Vec<_> = current_validators
.into_iter()
.chain(delinquent_validators.into_iter())
.collect();
let (average_skip_rate, average_stake_weighted_skip_rate) = {
let mut skip_rate_len = 0;
let mut skip_rate_sum = 0.;
let mut skip_rate_weighted_sum = 0.;
for validator in validators.iter() {
if let Some(skip_rate) = validator.skip_rate {
skip_rate_sum += skip_rate;
skip_rate_len += 1;
skip_rate_weighted_sum += skip_rate * validator.activated_stake as f64;
}
}
if skip_rate_len > 0 && total_active_stake > 0 {
(
skip_rate_sum / skip_rate_len as f64,
skip_rate_weighted_sum / total_active_stake as f64,
)
} else {
(100., 100.) // Impossible?
}
};
let cli_validators = CliValidators {
total_active_stake,
total_current_stake,
total_delinquent_stake,
validators,
average_skip_rate,
average_stake_weighted_skip_rate,
validators_sort_order,
validators_reverse_sort,
number_validators,
current_validators,
delinquent_validators,
stake_by_version,
use_lamports_unit,
};
@@ -1973,13 +1820,12 @@ pub fn process_transaction_history(
limit: usize,
show_transactions: bool,
) -> ProcessResult {
let results = rpc_client.get_signatures_for_address_with_config(
let results = rpc_client.get_confirmed_signatures_for_address2_with_config(
address,
GetConfirmedSignaturesForAddress2Config {
before,
until,
limit: Some(limit),
commitment: Some(CommitmentConfig::confirmed()),
},
)?;
@@ -1996,13 +1842,9 @@ pub fn process_transaction_history(
Some(block_time) =>
format!("timestamp={} ", unix_timestamp_to_string(block_time)),
},
if let Some(err) = result.err {
format!("Failed: {:?}", err)
} else {
match result.confirmation_status {
None => "Finalized".to_string(),
Some(status) => format!("{:?}", status),
}
match result.err {
None => "Confirmed".to_string(),
Some(err) => format!("Failed: {:?}", err),
},
result.memo.unwrap_or_else(|| "".to_string()),
);
@@ -2012,13 +1854,9 @@ pub fn process_transaction_history(
if show_transactions {
if let Ok(signature) = result.signature.parse::<Signature>() {
match rpc_client.get_transaction_with_config(
&signature,
RpcTransactionConfig {
encoding: Some(UiTransactionEncoding::Base64),
commitment: Some(CommitmentConfig::confirmed()),
},
) {
match rpc_client
.get_confirmed_transaction(&signature, UiTransactionEncoding::Base64)
{
Ok(confirmed_transaction) => {
println_transaction(
&confirmed_transaction
@@ -2071,47 +1909,6 @@ impl fmt::Display for CliRentCalculation {
impl QuietDisplay for CliRentCalculation {}
impl VerboseDisplay for CliRentCalculation {}
#[derive(Debug, PartialEq)]
pub enum RentLengthValue {
Nonce,
Stake,
System,
Vote,
Bytes(usize),
}
impl RentLengthValue {
pub fn length(&self) -> usize {
match self {
Self::Nonce => NonceState::size(),
Self::Stake => std::mem::size_of::<StakeState>(),
Self::System => 0,
Self::Vote => VoteState::size_of(),
Self::Bytes(l) => *l,
}
}
}
#[derive(Debug, Error)]
#[error("expected number or moniker, got \"{0}\"")]
pub struct RentLengthValueError(pub String);
impl FromStr for RentLengthValue {
type Err = RentLengthValueError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.to_ascii_lowercase();
match s.as_str() {
"nonce" => Ok(Self::Nonce),
"stake" => Ok(Self::Stake),
"system" => Ok(Self::System),
"vote" => Ok(Self::Vote),
_ => usize::from_str(&s)
.map(Self::Bytes)
.map_err(|_| RentLengthValueError(s)),
}
}
}
pub fn process_calculate_rent(
rpc_client: &RpcClient,
config: &CliConfig,
@@ -2141,7 +1938,7 @@ pub fn process_calculate_rent(
#[cfg(test)]
mod tests {
use super::*;
use crate::{clap_app::get_clap_app, cli::parse_command};
use crate::cli::{app, parse_command};
use solana_sdk::signature::{write_keypair, Keypair};
use std::str::FromStr;
use tempfile::NamedTempFile;
@@ -2153,11 +1950,14 @@ mod tests {
#[test]
fn test_parse_command() {
let test_commands = get_clap_app("test", "desc", "version");
let test_commands = app("test", "desc", "version");
let default_keypair = Keypair::new();
let (default_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
let default_signer = DefaultSigner::new("", &default_keypair_file);
let default_signer = DefaultSigner {
path: default_keypair_file,
arg_name: String::new(),
};
let test_cluster_version = test_commands
.clone()

View File

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

View File

@@ -1,22 +1,14 @@
use crate::cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult};
use clap::{App, Arg, ArgMatches, SubCommand};
use solana_clap_utils::{
input_parsers::{pubkeys_of, value_of},
input_validators::is_valid_pubkey,
keypair::*,
};
use solana_cli_output::{
CliEpochRewardshMetadata, CliInflation, CliKeyedEpochReward, CliKeyedEpochRewards,
};
use clap::{App, ArgMatches, SubCommand};
use solana_clap_utils::keypair::*;
use solana_cli_output::CliInflation;
use solana_client::rpc_client::RpcClient;
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{clock::Epoch, pubkey::Pubkey};
use std::sync::Arc;
#[derive(Debug, PartialEq)]
pub enum InflationCliCommand {
Show,
Rewards(Vec<Pubkey>, Option<Epoch>),
}
pub trait InflationSubCommands {
@@ -25,47 +17,17 @@ pub trait InflationSubCommands {
impl InflationSubCommands for App<'_, '_> {
fn inflation_subcommands(self) -> Self {
self.subcommand(
SubCommand::with_name("inflation")
.about("Show inflation information")
.subcommand(
SubCommand::with_name("rewards")
.about("Show inflation rewards for a set of addresses")
.arg(pubkey!(
Arg::with_name("addresses")
.value_name("ADDRESS")
.index(1)
.multiple(true)
.required(true),
"Address of account to query for rewards. "
))
.arg(
Arg::with_name("rewards_epoch")
.long("rewards-epoch")
.takes_value(true)
.value_name("EPOCH")
.help("Display rewards for specific epoch [default: latest epoch]"),
),
),
)
self.subcommand(SubCommand::with_name("inflation").about("Show inflation information"))
}
}
pub fn parse_inflation_subcommand(
matches: &ArgMatches<'_>,
_matches: &ArgMatches<'_>,
_default_signer: &DefaultSigner,
_wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let command = match matches.subcommand() {
("rewards", Some(matches)) => {
let addresses = pubkeys_of(matches, "addresses").unwrap();
let rewards_epoch = value_of(matches, "rewards_epoch");
InflationCliCommand::Rewards(addresses, rewards_epoch)
}
_ => InflationCliCommand::Show,
};
Ok(CliCommandInfo {
command: CliCommand::Inflation(command),
command: CliCommand::Inflation(InflationCliCommand::Show),
signers: vec![],
})
}
@@ -75,15 +37,8 @@ pub fn process_inflation_subcommand(
config: &CliConfig,
inflation_subcommand: &InflationCliCommand,
) -> ProcessResult {
match inflation_subcommand {
InflationCliCommand::Show => process_show(rpc_client, config),
InflationCliCommand::Rewards(ref addresses, rewards_epoch) => {
process_rewards(rpc_client, config, addresses, *rewards_epoch)
}
}
}
assert_eq!(*inflation_subcommand, InflationCliCommand::Show);
fn process_show(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
let governor = rpc_client.get_inflation_governor()?;
let current_rate = rpc_client.get_inflation_rate()?;
@@ -94,49 +49,3 @@ fn process_show(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
Ok(config.output_format.formatted_string(&inflation))
}
fn process_rewards(
rpc_client: &RpcClient,
config: &CliConfig,
addresses: &[Pubkey],
rewards_epoch: Option<Epoch>,
) -> ProcessResult {
let rewards = rpc_client
.get_inflation_reward(addresses, rewards_epoch)
.map_err(|err| {
if let Some(epoch) = rewards_epoch {
format!("Rewards not available for epoch {}", epoch)
} else {
format!("Rewards not available {}", err)
}
})?;
let epoch_schedule = rpc_client.get_epoch_schedule()?;
let mut epoch_rewards: Vec<CliKeyedEpochReward> = vec![];
let epoch_metadata = if let Some(Some(first_reward)) = rewards.iter().find(|&v| v.is_some()) {
let (epoch_start_time, epoch_end_time) =
crate::stake::get_epoch_boundary_timestamps(rpc_client, first_reward, &epoch_schedule)?;
for (reward, address) in rewards.iter().zip(addresses) {
let cli_reward = reward.as_ref().and_then(|reward| {
crate::stake::make_cli_reward(reward, epoch_start_time, epoch_end_time)
});
epoch_rewards.push(CliKeyedEpochReward {
address: address.to_string(),
reward: cli_reward,
});
}
let block_time = rpc_client.get_block_time(first_reward.effective_slot)?;
Some(CliEpochRewardshMetadata {
epoch: first_reward.epoch,
effective_slot: first_reward.effective_slot,
block_time,
})
} else {
None
};
let cli_rewards = CliKeyedEpochRewards {
epoch_metadata,
rewards: epoch_rewards,
};
Ok(config.output_format.formatted_string(&cli_rewards))
}

View File

@@ -10,6 +10,7 @@ macro_rules! ACCOUNT_STRING {
};
}
#[macro_use]
macro_rules! pubkey {
($arg:expr, $help:expr) => {
$arg.takes_value(true)
@@ -18,23 +19,18 @@ macro_rules! pubkey {
};
}
#[macro_use]
extern crate const_format;
extern crate serde_derive;
pub mod checks;
pub mod clap_app;
pub mod cli;
pub mod cluster_query;
pub mod feature;
pub mod inflation;
pub mod memo;
pub mod nonce;
pub mod program;
pub mod send_tpu;
pub mod spend_utils;
pub mod stake;
pub mod test_utils;
pub mod validator_info;
pub mod vote;
pub mod wallet;

View File

@@ -1,15 +1,18 @@
use clap::{crate_description, crate_name, value_t_or_exit, ArgMatches};
use clap::{
crate_description, crate_name, value_t_or_exit, AppSettings, Arg, ArgGroup, ArgMatches,
SubCommand,
};
use console::style;
use solana_clap_utils::{
input_validators::normalize_to_url_if_moniker,
keypair::{CliSigners, DefaultSigner},
input_validators::{is_url, is_url_or_moniker, normalize_to_url_if_moniker},
keypair::{CliSigners, DefaultSigner, SKIP_SEED_PHRASE_VALIDATION_ARG},
DisplayError,
};
use solana_cli::{
clap_app::get_clap_app,
cli::{parse_command, process_command, CliCommandInfo, CliConfig, SettingType},
use solana_cli::cli::{
app, parse_command, process_command, CliCommandInfo, CliConfig, SettingType,
DEFAULT_RPC_TIMEOUT_SECONDS,
};
use solana_cli_config::Config;
use solana_cli_config::{Config, CONFIG_FILE};
use solana_cli_output::{display::println_name_value, OutputFormat};
use solana_client::rpc_config::RpcSendTransactionConfig;
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
@@ -164,11 +167,6 @@ pub fn parse_args<'a>(
let rpc_timeout = value_t_or_exit!(matches, "rpc_timeout", u64);
let rpc_timeout = Duration::from_secs(rpc_timeout);
let confirm_transaction_initial_timeout =
value_t_or_exit!(matches, "confirm_transaction_initial_timeout", u64);
let confirm_transaction_initial_timeout =
Duration::from_secs(confirm_transaction_initial_timeout);
let (_, websocket_url) = CliConfig::compute_websocket_url_setting(
matches.value_of("websocket_url").unwrap_or(""),
&config.websocket_url,
@@ -181,12 +179,15 @@ pub fn parse_args<'a>(
&config.keypair_path,
);
let default_signer = DefaultSigner::new(default_signer_arg_name, &default_signer_path);
let default_signer = DefaultSigner {
arg_name: default_signer_arg_name,
path: default_signer_path.clone(),
};
let CliCommandInfo {
command,
mut signers,
} = parse_command(matches, &default_signer, &mut wallet_manager)?;
} = parse_command(&matches, &default_signer, &mut wallet_manager)?;
if signers.is_empty() {
if let Ok(signer_info) =
@@ -197,7 +198,18 @@ pub fn parse_args<'a>(
}
let verbose = matches.is_present("verbose");
let output_format = OutputFormat::from_matches(matches, "output_format", verbose);
let output_format = matches
.value_of("output_format")
.map(|value| match value {
"json" => OutputFormat::Json,
"json-compact" => OutputFormat::JsonCompact,
_ => unreachable!(),
})
.unwrap_or(if verbose {
OutputFormat::DisplayVerbose
} else {
OutputFormat::Display
});
let (_, commitment) = CliConfig::compute_commitment_config(
matches.value_of("commitment").unwrap_or(""),
@@ -226,7 +238,6 @@ pub fn parse_args<'a>(
preflight_commitment: Some(commitment.commitment),
..RpcSendTransactionConfig::default()
},
confirm_transaction_initial_timeout,
address_labels,
},
signers,
@@ -235,21 +246,178 @@ pub fn parse_args<'a>(
fn main() -> Result<(), Box<dyn error::Error>> {
solana_logger::setup_with_default("off");
let matches = get_clap_app(
let matches = app(
crate_name!(),
crate_description!(),
solana_version::version!(),
)
.arg({
let arg = Arg::with_name("config_file")
.short("C")
.long("config")
.value_name("FILEPATH")
.takes_value(true)
.global(true)
.help("Configuration file to use");
if let Some(ref config_file) = *CONFIG_FILE {
arg.default_value(&config_file)
} else {
arg
}
})
.arg(
Arg::with_name("json_rpc_url")
.short("u")
.long("url")
.value_name("URL_OR_MONIKER")
.takes_value(true)
.global(true)
.validator(is_url_or_moniker)
.help(
"URL for Solana's JSON RPC or moniker (or their first letter): \
[mainnet-beta, testnet, devnet, localhost]",
),
)
.arg(
Arg::with_name("websocket_url")
.long("ws")
.value_name("URL")
.takes_value(true)
.global(true)
.validator(is_url)
.help("WebSocket URL for the solana cluster"),
)
.arg(
Arg::with_name("keypair")
.short("k")
.long("keypair")
.value_name("KEYPAIR")
.global(true)
.takes_value(true)
.help("Filepath or URL to a keypair"),
)
.arg(
Arg::with_name("commitment")
.long("commitment")
.takes_value(true)
.possible_values(&[
"processed",
"confirmed",
"finalized",
"recent", // Deprecated as of v1.5.5
"single", // Deprecated as of v1.5.5
"singleGossip", // Deprecated as of v1.5.5
"root", // Deprecated as of v1.5.5
"max", // Deprecated as of v1.5.5
])
.value_name("COMMITMENT_LEVEL")
.hide_possible_values(true)
.global(true)
.help("Return information at the selected commitment level [possible values: processed, confirmed, finalized]"),
)
.arg(
Arg::with_name("verbose")
.long("verbose")
.short("v")
.global(true)
.help("Show additional information"),
)
.arg(
Arg::with_name("no_address_labels")
.long("no-address-labels")
.global(true)
.help("Do not use address labels in the output"),
)
.arg(
Arg::with_name("output_format")
.long("output")
.value_name("FORMAT")
.global(true)
.takes_value(true)
.possible_values(&["json", "json-compact"])
.help("Return information in specified output format"),
)
.arg(
Arg::with_name(SKIP_SEED_PHRASE_VALIDATION_ARG.name)
.long(SKIP_SEED_PHRASE_VALIDATION_ARG.long)
.global(true)
.help(SKIP_SEED_PHRASE_VALIDATION_ARG.help),
)
.arg(
Arg::with_name("rpc_timeout")
.long("rpc-timeout")
.value_name("SECONDS")
.takes_value(true)
.default_value(DEFAULT_RPC_TIMEOUT_SECONDS)
.global(true)
.hidden(true)
.help("Timeout value for RPC requests"),
)
.subcommand(
SubCommand::with_name("config")
.about("Solana command-line tool configuration settings")
.aliases(&["get", "set"])
.setting(AppSettings::SubcommandRequiredElseHelp)
.subcommand(
SubCommand::with_name("get")
.about("Get current config settings")
.arg(
Arg::with_name("specific_setting")
.index(1)
.value_name("CONFIG_FIELD")
.takes_value(true)
.possible_values(&[
"json_rpc_url",
"websocket_url",
"keypair",
"commitment",
])
.help("Return a specific config setting"),
),
)
.subcommand(
SubCommand::with_name("set")
.about("Set a config setting")
.group(
ArgGroup::with_name("config_settings")
.args(&["json_rpc_url", "websocket_url", "keypair", "commitment"])
.multiple(true)
.required(true),
),
)
.subcommand(
SubCommand::with_name("import-address-labels")
.about("Import a list of address labels")
.arg(
Arg::with_name("filename")
.index(1)
.value_name("FILENAME")
.takes_value(true)
.help("YAML file of address labels"),
),
)
.subcommand(
SubCommand::with_name("export-address-labels")
.about("Export the current address labels")
.arg(
Arg::with_name("filename")
.index(1)
.value_name("FILENAME")
.takes_value(true)
.help("YAML file to receive the current address labels"),
),
),
)
.get_matches();
do_main(&matches).map_err(|err| DisplayError::new_as_boxed(err).into())
}
fn do_main(matches: &ArgMatches<'_>) -> Result<(), Box<dyn error::Error>> {
if parse_settings(matches)? {
if parse_settings(&matches)? {
let mut wallet_manager = None;
let (mut config, signers) = parse_args(matches, &mut wallet_manager)?;
let (mut config, signers) = parse_args(&matches, &mut wallet_manager)?;
config.signers = signers.iter().map(|s| s.as_ref()).collect();
let result = process_command(&config)?;
println!("{}", result);

View File

@@ -1,22 +0,0 @@
use solana_sdk::instruction::Instruction;
use solana_sdk::pubkey::Pubkey;
use spl_memo::id;
pub trait WithMemo {
fn with_memo<T: AsRef<str>>(self, memo: Option<T>) -> Self;
}
impl WithMemo for Vec<Instruction> {
fn with_memo<T: AsRef<str>>(mut self, memo: Option<T>) -> Self {
if let Some(memo) = &memo {
let memo = memo.as_ref();
let memo_ix = Instruction {
program_id: Pubkey::new(&id().to_bytes()),
accounts: vec![],
data: memo.as_bytes().to_vec(),
};
self.push(memo_ix);
}
self
}
}

View File

@@ -1,11 +1,9 @@
use crate::{
checks::{check_account_for_fee_with_commitment, check_unique_pubkeys},
cli::{
log_instruction_custom_error, log_instruction_custom_error_ex, CliCommand, CliCommandInfo,
CliConfig, CliError, ProcessResult,
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
ProcessResult,
},
feature::get_feature_is_active,
memo::WithMemo,
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
};
use clap::{App, Arg, ArgMatches, SubCommand};
@@ -13,7 +11,6 @@ use solana_clap_utils::{
input_parsers::*,
input_validators::*,
keypair::{DefaultSigner, SignerIndex},
memo::{memo_arg, MEMO_ARG},
nonce::*,
};
use solana_cli_output::CliNonceAccount;
@@ -21,19 +18,16 @@ use solana_client::{nonce_utils::*, rpc_client::RpcClient};
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{
account::Account,
feature_set::merge_nonce_error_into_system_error,
hash::Hash,
instruction::InstructionError,
message::Message,
nonce::{self, State},
pubkey::Pubkey,
system_instruction::{
advance_nonce_account, authorize_nonce_account, create_nonce_account,
create_nonce_account_with_seed, instruction_to_nonce_error, withdraw_nonce_account,
NonceError, SystemError,
create_nonce_account_with_seed, withdraw_nonce_account, NonceError, SystemError,
},
system_program,
transaction::{Transaction, TransactionError},
transaction::Transaction,
};
use std::sync::Arc;
@@ -60,8 +54,7 @@ impl NonceSubCommands for App<'_, '_> {
.required(true),
"Account to be granted authority of the nonce account. "),
)
.arg(nonce_authority_arg())
.arg(memo_arg()),
.arg(nonce_authority_arg()),
)
.subcommand(
SubCommand::with_name("create-nonce-account")
@@ -96,8 +89,7 @@ impl NonceSubCommands for App<'_, '_> {
.value_name("STRING")
.takes_value(true)
.help("Seed for address generation; if specified, the resulting account will be at a derived address of the NONCE_ACCOUNT pubkey")
)
.arg(memo_arg()),
),
)
.subcommand(
SubCommand::with_name("nonce")
@@ -121,8 +113,7 @@ impl NonceSubCommands for App<'_, '_> {
.required(true),
"Address of the nonce account. "),
)
.arg(nonce_authority_arg())
.arg(memo_arg()),
.arg(nonce_authority_arg()),
)
.subcommand(
SubCommand::with_name("nonce-account")
@@ -168,8 +159,7 @@ impl NonceSubCommands for App<'_, '_> {
.validator(is_amount)
.help("The amount to withdraw from the nonce account, in SOL"),
)
.arg(nonce_authority_arg())
.arg(memo_arg()),
.arg(nonce_authority_arg()),
)
}
}
@@ -181,7 +171,6 @@ pub fn parse_authorize_nonce_account(
) -> Result<CliCommandInfo, CliError> {
let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap();
let new_authority = pubkey_of_signer(matches, "new_authority", wallet_manager)?.unwrap();
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
let (nonce_authority, nonce_authority_pubkey) =
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
@@ -196,7 +185,6 @@ pub fn parse_authorize_nonce_account(
command: CliCommand::AuthorizeNonceAccount {
nonce_account,
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
memo,
new_authority,
},
signers: signer_info.signers,
@@ -213,7 +201,6 @@ pub fn parse_nonce_create_account(
let seed = matches.value_of("seed").map(|s| s.to_string());
let amount = SpendAmount::new_from_matches(matches, "amount");
let nonce_authority = pubkey_of_signer(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
let payer_provided = None;
let signer_info = default_signer.generate_unique_signers(
@@ -227,7 +214,6 @@ pub fn parse_nonce_create_account(
nonce_account: signer_info.index_of(nonce_account_pubkey).unwrap(),
seed,
nonce_authority,
memo,
amount,
},
signers: signer_info.signers,
@@ -253,7 +239,6 @@ pub fn parse_new_nonce(
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap();
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
let (nonce_authority, nonce_authority_pubkey) =
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
@@ -268,7 +253,6 @@ pub fn parse_new_nonce(
command: CliCommand::NewNonce {
nonce_account,
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
memo,
},
signers: signer_info.signers,
})
@@ -300,7 +284,6 @@ pub fn parse_withdraw_from_nonce_account(
let destination_account_pubkey =
pubkey_of_signer(matches, "destination_account_pubkey", wallet_manager)?.unwrap();
let lamports = lamports_of_sol(matches, "amount").unwrap();
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
let (nonce_authority, nonce_authority_pubkey) =
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
@@ -315,7 +298,6 @@ pub fn parse_withdraw_from_nonce_account(
command: CliCommand::WithdrawFromNonceAccount {
nonce_account,
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
memo,
destination_account_pubkey,
lamports,
},
@@ -348,19 +330,13 @@ pub fn process_authorize_nonce_account(
config: &CliConfig,
nonce_account: &Pubkey,
nonce_authority: SignerIndex,
memo: Option<&String>,
new_authority: &Pubkey,
) -> ProcessResult {
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let nonce_authority = config.signers[nonce_authority];
let ixs = vec![authorize_nonce_account(
nonce_account,
&nonce_authority.pubkey(),
new_authority,
)]
.with_memo(memo);
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
let ix = authorize_nonce_account(nonce_account, &nonce_authority.pubkey(), new_authority);
let message = Message::new(&[ix], Some(&config.signers[0].pubkey()));
let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&config.signers, recent_blockhash)?;
@@ -371,21 +347,8 @@ pub fn process_authorize_nonce_account(
&tx.message,
config.commitment,
)?;
let merge_errors =
get_feature_is_active(rpc_client, &merge_nonce_error_into_system_error::id())?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
if merge_errors {
log_instruction_custom_error::<SystemError>(result, config)
} else {
log_instruction_custom_error_ex::<NonceError, _>(result, config, |ix_error| {
if let InstructionError::Custom(_) = ix_error {
instruction_to_nonce_error(ix_error, merge_errors)
} else {
None
}
})
}
log_instruction_custom_error::<NonceError>(result, &config)
}
pub fn process_create_nonce_account(
@@ -394,7 +357,6 @@ pub fn process_create_nonce_account(
nonce_account: SignerIndex,
seed: Option<String>,
nonce_authority: Option<Pubkey>,
memo: Option<&String>,
amount: SpendAmount,
) -> ProcessResult {
let nonce_account_pubkey = config.signers[nonce_account].pubkey();
@@ -421,7 +383,6 @@ pub fn process_create_nonce_account(
&nonce_authority,
lamports,
)
.with_memo(memo)
} else {
create_nonce_account(
&config.signers[0].pubkey(),
@@ -429,7 +390,6 @@ pub fn process_create_nonce_account(
&nonce_authority,
lamports,
)
.with_memo(memo)
};
Message::new(&ixs, Some(&config.signers[0].pubkey()))
};
@@ -469,40 +429,8 @@ pub fn process_create_nonce_account(
let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&config.signers, recent_blockhash)?;
let merge_errors =
get_feature_is_active(rpc_client, &merge_nonce_error_into_system_error::id())?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
let err_ix_index = if let Err(err) = &result {
err.get_transaction_error().and_then(|tx_err| {
if let TransactionError::InstructionError(ix_index, _) = tx_err {
Some(ix_index)
} else {
None
}
})
} else {
None
};
match err_ix_index {
// SystemInstruction::InitializeNonceAccount failed
Some(1) => {
if merge_errors {
log_instruction_custom_error::<SystemError>(result, config)
} else {
log_instruction_custom_error_ex::<NonceError, _>(result, config, |ix_error| {
if let InstructionError::Custom(_) = ix_error {
instruction_to_nonce_error(ix_error, merge_errors)
} else {
None
}
})
}
}
// SystemInstruction::CreateAccount{,WithSeed} failed
_ => log_instruction_custom_error::<SystemError>(result, config),
}
log_instruction_custom_error::<SystemError>(result, &config)
}
pub fn process_get_nonce(
@@ -523,14 +451,13 @@ pub fn process_new_nonce(
config: &CliConfig,
nonce_account: &Pubkey,
nonce_authority: SignerIndex,
memo: Option<&String>,
) -> ProcessResult {
check_unique_pubkeys(
(&config.signers[0].pubkey(), "cli keypair".to_string()),
(nonce_account, "nonce_account_pubkey".to_string()),
(&nonce_account, "nonce_account_pubkey".to_string()),
)?;
if let Err(err) = rpc_client.get_account(nonce_account) {
if let Err(err) = rpc_client.get_account(&nonce_account) {
return Err(CliError::BadParameter(format!(
"Unable to advance nonce account {}. error: {}",
nonce_account, err
@@ -539,13 +466,9 @@ pub fn process_new_nonce(
}
let nonce_authority = config.signers[nonce_authority];
let ixs = vec![advance_nonce_account(
nonce_account,
&nonce_authority.pubkey(),
)]
.with_memo(memo);
let ix = advance_nonce_account(&nonce_account, &nonce_authority.pubkey());
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
let message = Message::new(&[ix], Some(&config.signers[0].pubkey()));
let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&config.signers, recent_blockhash)?;
check_account_for_fee_with_commitment(
@@ -555,21 +478,8 @@ pub fn process_new_nonce(
&tx.message,
config.commitment,
)?;
let merge_errors =
get_feature_is_active(rpc_client, &merge_nonce_error_into_system_error::id())?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
if merge_errors {
log_instruction_custom_error::<SystemError>(result, config)
} else {
log_instruction_custom_error_ex::<NonceError, _>(result, config, |ix_error| {
if let InstructionError::Custom(_) = ix_error {
instruction_to_nonce_error(ix_error, merge_errors)
} else {
None
}
})
}
log_instruction_custom_error::<SystemError>(result, &config)
}
pub fn process_show_nonce_account(
@@ -588,7 +498,7 @@ pub fn process_show_nonce_account(
use_lamports_unit,
..CliNonceAccount::default()
};
if let Some(data) = data {
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());
@@ -607,21 +517,19 @@ pub fn process_withdraw_from_nonce_account(
config: &CliConfig,
nonce_account: &Pubkey,
nonce_authority: SignerIndex,
memo: Option<&String>,
destination_account_pubkey: &Pubkey,
lamports: u64,
) -> ProcessResult {
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let nonce_authority = config.signers[nonce_authority];
let ixs = vec![withdraw_nonce_account(
let ix = withdraw_nonce_account(
nonce_account,
&nonce_authority.pubkey(),
destination_account_pubkey,
lamports,
)]
.with_memo(memo);
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
);
let message = Message::new(&[ix], Some(&config.signers[0].pubkey()));
let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&config.signers, recent_blockhash)?;
check_account_for_fee_with_commitment(
@@ -631,27 +539,14 @@ pub fn process_withdraw_from_nonce_account(
&tx.message,
config.commitment,
)?;
let merge_errors =
get_feature_is_active(rpc_client, &merge_nonce_error_into_system_error::id())?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
if merge_errors {
log_instruction_custom_error::<SystemError>(result, config)
} else {
log_instruction_custom_error_ex::<NonceError, _>(result, config, |ix_error| {
if let InstructionError::Custom(_) = ix_error {
instruction_to_nonce_error(ix_error, merge_errors)
} else {
None
}
})
}
log_instruction_custom_error::<NonceError>(result, &config)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{clap_app::get_clap_app, cli::parse_command};
use crate::cli::{app, parse_command};
use solana_sdk::{
account::Account,
account_utils::StateMut,
@@ -671,11 +566,14 @@ mod tests {
#[test]
fn test_parse_command() {
let test_commands = get_clap_app("test", "desc", "version");
let test_commands = app("test", "desc", "version");
let default_keypair = Keypair::new();
let (default_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
let default_signer = DefaultSigner::new("", &default_keypair_file);
let default_signer = DefaultSigner {
path: default_keypair_file.clone(),
arg_name: String::new(),
};
let (keypair_file, mut tmp_file) = make_tmp_file();
let nonce_account_keypair = Keypair::new();
write_keypair(&nonce_account_keypair, tmp_file.as_file_mut()).unwrap();
@@ -699,7 +597,6 @@ mod tests {
command: CliCommand::AuthorizeNonceAccount {
nonce_account: nonce_account_pubkey,
nonce_authority: 0,
memo: None,
new_authority: Pubkey::default(),
},
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
@@ -721,7 +618,6 @@ mod tests {
command: CliCommand::AuthorizeNonceAccount {
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
nonce_authority: 1,
memo: None,
new_authority: Pubkey::default(),
},
signers: vec![
@@ -745,7 +641,6 @@ mod tests {
nonce_account: 1,
seed: None,
nonce_authority: None,
memo: None,
amount: SpendAmount::Some(50_000_000_000),
},
signers: vec![
@@ -771,7 +666,6 @@ mod tests {
nonce_account: 1,
seed: None,
nonce_authority: Some(nonce_authority_keypair.pubkey()),
memo: None,
amount: SpendAmount::Some(50_000_000_000),
},
signers: vec![
@@ -807,7 +701,6 @@ mod tests {
command: CliCommand::NewNonce {
nonce_account: nonce_account.pubkey(),
nonce_authority: 0,
memo: None,
},
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
}
@@ -828,7 +721,6 @@ mod tests {
command: CliCommand::NewNonce {
nonce_account: nonce_account.pubkey(),
nonce_authority: 1,
memo: None,
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
@@ -873,7 +765,6 @@ mod tests {
command: CliCommand::WithdrawFromNonceAccount {
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
nonce_authority: 0,
memo: None,
destination_account_pubkey: nonce_account_pubkey,
lamports: 42_000_000_000
},
@@ -902,7 +793,6 @@ mod tests {
command: CliCommand::WithdrawFromNonceAccount {
nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
nonce_authority: 1,
memo: None,
destination_account_pubkey: nonce_account_pubkey,
lamports: 42_000_000_000
},

File diff suppressed because it is too large Load Diff

46
cli/src/send_tpu.rs Normal file
View File

@@ -0,0 +1,46 @@
use log::*;
use solana_client::rpc_response::{RpcContactInfo, RpcLeaderSchedule};
use solana_sdk::clock::NUM_CONSECUTIVE_LEADER_SLOTS;
use std::net::{SocketAddr, UdpSocket};
pub fn get_leader_tpus(
slot_index: u64,
num_leaders: u64,
leader_schedule: Option<&RpcLeaderSchedule>,
cluster_nodes: Option<&Vec<RpcContactInfo>>,
) -> Vec<SocketAddr> {
let leaders: Vec<_> = (0..num_leaders)
.filter_map(|i| {
leader_schedule?
.iter()
.find(|(_pubkey, slots)| {
slots.iter().any(|slot| {
*slot as u64 == (slot_index + (i * NUM_CONSECUTIVE_LEADER_SLOTS))
})
})
.and_then(|(pubkey, _)| {
cluster_nodes?
.iter()
.find(|contact_info| contact_info.pubkey == *pubkey)
.and_then(|contact_info| contact_info.tpu)
})
})
.collect();
let mut unique_leaders = vec![];
for leader in leaders.into_iter() {
if !unique_leaders.contains(&leader) {
unique_leaders.push(leader);
}
}
unique_leaders
}
pub fn send_transaction_tpu(
send_socket: &UdpSocket,
tpu_address: &SocketAddr,
wire_transaction: &[u8],
) {
if let Err(err) = send_socket.send_to(wire_transaction, tpu_address) {
warn!("Failed to send transaction to {}: {:?}", tpu_address, err);
}
}

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -4,16 +4,13 @@ use crate::{
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
ProcessResult,
},
memo::WithMemo,
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
stake::check_current_authority,
};
use clap::{value_t_or_exit, App, Arg, ArgMatches, SubCommand};
use solana_clap_utils::{
input_parsers::*,
input_validators::*,
keypair::{DefaultSigner, SignerIndex},
memo::{memo_arg, MEMO_ARG},
};
use solana_cli_output::{CliEpochVotingHistory, CliLockout, CliVoteAccount};
use solana_client::rpc_client::RpcClient;
@@ -56,15 +53,6 @@ impl VoteSubCommands for App<'_, '_> {
.validator(is_valid_signer)
.help("Keypair of validator that will vote with this account"),
)
.arg(
pubkey!(Arg::with_name("authorized_withdrawer")
.index(3)
.value_name("WITHDRAWER_PUBKEY")
.takes_value(true)
.required(true)
.long("authorized-withdrawer"),
"Public key of the authorized withdrawer")
)
.arg(
Arg::with_name("commission")
.long("commission")
@@ -80,12 +68,10 @@ impl VoteSubCommands for App<'_, '_> {
"Public key of the authorized voter [default: validator identity pubkey]. "),
)
.arg(
Arg::with_name("allow_unsafe_authorized_withdrawer")
.long("allow-unsafe-authorized-withdrawer")
.takes_value(false)
.help("Allow an authorized withdrawer pubkey to be identical to the validator identity \
account pubkey or vote account pubkey, which is normally an unsafe \
configuration and should be avoided."),
pubkey!(Arg::with_name("authorized_withdrawer")
.long("authorized-withdrawer")
.value_name("WITHDRAWER_PUBKEY"),
"Public key of the authorized withdrawer [default: validator identity pubkey]. "),
)
.arg(
Arg::with_name("seed")
@@ -93,8 +79,7 @@ impl VoteSubCommands for App<'_, '_> {
.value_name("STRING")
.takes_value(true)
.help("Seed for address generation; if specified, the resulting account will be at a derived address of the VOTE ACCOUNT pubkey")
)
.arg(memo_arg())
),
)
.subcommand(
SubCommand::with_name("vote-authorize-voter")
@@ -120,8 +105,7 @@ impl VoteSubCommands for App<'_, '_> {
.value_name("NEW_AUTHORIZED_PUBKEY")
.required(true),
"New authorized vote signer. "),
)
.arg(memo_arg())
),
)
.subcommand(
SubCommand::with_name("vote-authorize-withdrawer")
@@ -147,66 +131,7 @@ impl VoteSubCommands for App<'_, '_> {
.value_name("AUTHORIZED_PUBKEY")
.required(true),
"New authorized withdrawer. "),
)
.arg(memo_arg())
)
.subcommand(
SubCommand::with_name("vote-authorize-voter-checked")
.about("Authorize a new vote signing keypair for the given vote account, \
checking the new authority as a signer")
.arg(
pubkey!(Arg::with_name("vote_account_pubkey")
.index(1)
.value_name("VOTE_ACCOUNT_ADDRESS")
.required(true),
"Vote account in which to set the authorized voter. "),
)
.arg(
Arg::with_name("authorized")
.index(2)
.value_name("AUTHORIZED_KEYPAIR")
.required(true)
.validator(is_valid_signer)
.help("Current authorized vote signer."),
)
.arg(
Arg::with_name("new_authorized")
.index(3)
.value_name("NEW_AUTHORIZED_KEYPAIR")
.required(true)
.validator(is_valid_signer)
.help("New authorized vote signer."),
)
.arg(memo_arg())
)
.subcommand(
SubCommand::with_name("vote-authorize-withdrawer-checked")
.about("Authorize a new withdraw signing keypair for the given vote account, \
checking the new authority as a signer")
.arg(
pubkey!(Arg::with_name("vote_account_pubkey")
.index(1)
.value_name("VOTE_ACCOUNT_ADDRESS")
.required(true),
"Vote account in which to set the authorized withdrawer. "),
)
.arg(
Arg::with_name("authorized")
.index(2)
.value_name("AUTHORIZED_KEYPAIR")
.required(true)
.validator(is_valid_signer)
.help("Current authorized withdrawer."),
)
.arg(
Arg::with_name("new_authorized")
.index(3)
.value_name("NEW_AUTHORIZED_KEYPAIR")
.required(true)
.validator(is_valid_signer)
.help("New authorized withdrawer."),
)
.arg(memo_arg())
),
)
.subcommand(
SubCommand::with_name("vote-update-validator")
@@ -236,7 +161,6 @@ impl VoteSubCommands for App<'_, '_> {
.validator(is_valid_signer)
.help("Authorized withdrawer keypair"),
)
.arg(memo_arg())
)
.subcommand(
SubCommand::with_name("vote-update-commission")
@@ -266,7 +190,6 @@ impl VoteSubCommands for App<'_, '_> {
.validator(is_valid_signer)
.help("Authorized withdrawer keypair"),
)
.arg(memo_arg())
)
.subcommand(
SubCommand::with_name("vote-account")
@@ -284,22 +207,6 @@ impl VoteSubCommands for App<'_, '_> {
.long("lamports")
.takes_value(false)
.help("Display balance in lamports instead of SOL"),
)
.arg(
Arg::with_name("with_rewards")
.long("with-rewards")
.takes_value(false)
.help("Display inflation rewards"),
)
.arg(
Arg::with_name("num_rewards_epochs")
.long("num-rewards-epochs")
.takes_value(true)
.value_name("NUM")
.validator(|s| is_within_range(s, 1, 10))
.default_value_if("with_rewards", None, "1")
.requires("with_rewards")
.help("Display rewards for NUM recent epochs, max 10 [default: latest epoch only]"),
),
)
.subcommand(
@@ -336,7 +243,6 @@ impl VoteSubCommands for App<'_, '_> {
.validator(is_valid_signer)
.help("Authorized withdrawer [default: cli config keypair]"),
)
.arg(memo_arg())
)
}
}
@@ -352,27 +258,7 @@ pub fn parse_create_vote_account(
signer_of(matches, "identity_account", wallet_manager)?;
let commission = value_t_or_exit!(matches, "commission", u8);
let authorized_voter = pubkey_of_signer(matches, "authorized_voter", wallet_manager)?;
let authorized_withdrawer =
pubkey_of_signer(matches, "authorized_withdrawer", wallet_manager)?.unwrap();
let allow_unsafe = matches.is_present("allow_unsafe_authorized_withdrawer");
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
if !allow_unsafe {
if authorized_withdrawer == vote_account_pubkey.unwrap() {
return Err(CliError::BadParameter(
"Authorized withdrawer pubkey is identical to vote \
account pubkey, an unsafe configuration"
.to_owned(),
));
}
if authorized_withdrawer == identity_pubkey.unwrap() {
return Err(CliError::BadParameter(
"Authorized withdrawer pubkey is identical to identity \
account pubkey, an unsafe configuration"
.to_owned(),
));
}
}
let authorized_withdrawer = pubkey_of_signer(matches, "authorized_withdrawer", wallet_manager)?;
let payer_provided = None;
let signer_info = default_signer.generate_unique_signers(
@@ -389,7 +275,6 @@ pub fn parse_create_vote_account(
authorized_voter,
authorized_withdrawer,
commission,
memo,
},
signers: signer_info.signers,
})
@@ -400,39 +285,25 @@ pub fn parse_vote_authorize(
default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
vote_authorize: VoteAuthorize,
checked: bool,
) -> Result<CliCommandInfo, CliError> {
let vote_account_pubkey =
pubkey_of_signer(matches, "vote_account_pubkey", wallet_manager)?.unwrap();
let (authorized, authorized_pubkey) = signer_of(matches, "authorized", wallet_manager)?;
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 mut signers = vec![payer_provided, authorized];
let new_authorized_pubkey = if checked {
let (new_authorized_signer, new_authorized_pubkey) =
signer_of(matches, "new_authorized", wallet_manager)?;
signers.push(new_authorized_signer);
new_authorized_pubkey.unwrap()
} else {
pubkey_of_signer(matches, "new_authorized_pubkey", wallet_manager)?.unwrap()
};
let signer_info = default_signer.generate_unique_signers(signers, matches, wallet_manager)?;
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
let signer_info = default_signer.generate_unique_signers(
vec![payer_provided, authorized],
matches,
wallet_manager,
)?;
Ok(CliCommandInfo {
command: CliCommand::VoteAuthorize {
vote_account_pubkey,
new_authorized_pubkey,
vote_authorize,
memo,
authorized: signer_info.index_of(authorized_pubkey).unwrap(),
new_authorized: if checked {
signer_info.index_of(Some(new_authorized_pubkey))
} else {
None
},
},
signers: signer_info.signers,
})
@@ -456,14 +327,12 @@ pub fn parse_vote_update_validator(
matches,
wallet_manager,
)?;
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
Ok(CliCommandInfo {
command: CliCommand::VoteUpdateValidator {
vote_account_pubkey,
new_identity_account: signer_info.index_of(new_identity_pubkey).unwrap(),
withdraw_authority: signer_info.index_of(authorized_withdrawer_pubkey).unwrap(),
memo,
},
signers: signer_info.signers,
})
@@ -486,14 +355,12 @@ pub fn parse_vote_update_commission(
matches,
wallet_manager,
)?;
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
Ok(CliCommandInfo {
command: CliCommand::VoteUpdateCommission {
vote_account_pubkey,
commission,
withdraw_authority: signer_info.index_of(authorized_withdrawer_pubkey).unwrap(),
memo,
},
signers: signer_info.signers,
})
@@ -506,16 +373,10 @@ pub fn parse_vote_get_account_command(
let vote_account_pubkey =
pubkey_of_signer(matches, "vote_account_pubkey", wallet_manager)?.unwrap();
let use_lamports_unit = matches.is_present("lamports");
let with_rewards = if matches.is_present("with_rewards") {
Some(value_of(matches, "num_rewards_epochs").unwrap())
} else {
None
};
Ok(CliCommandInfo {
command: CliCommand::ShowVoteAccount {
pubkey: vote_account_pubkey,
use_lamports_unit,
with_rewards,
},
signers: vec![],
})
@@ -541,7 +402,6 @@ pub fn parse_withdraw_from_vote_account(
matches,
wallet_manager,
)?;
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
Ok(CliCommandInfo {
command: CliCommand::WithdrawFromVoteAccount {
@@ -549,7 +409,6 @@ pub fn parse_withdraw_from_vote_account(
destination_account_pubkey,
withdraw_authority: signer_info.index_of(withdraw_authority_pubkey).unwrap(),
withdraw_amount,
memo,
},
signers: signer_info.signers,
})
@@ -562,14 +421,13 @@ pub fn process_create_vote_account(
seed: &Option<String>,
identity_account: SignerIndex,
authorized_voter: &Option<Pubkey>,
authorized_withdrawer: Pubkey,
authorized_withdrawer: &Option<Pubkey>,
commission: u8,
memo: Option<&String>,
) -> ProcessResult {
let vote_account = config.signers[vote_account];
let vote_account_pubkey = vote_account.pubkey();
let vote_account_address = if let Some(seed) = seed {
Pubkey::create_with_seed(&vote_account_pubkey, seed, &solana_vote_program::id())?
Pubkey::create_with_seed(&vote_account_pubkey, &seed, &solana_vote_program::id())?
} else {
vote_account_pubkey
};
@@ -594,7 +452,7 @@ pub fn process_create_vote_account(
let vote_init = VoteInit {
node_pubkey: identity_pubkey,
authorized_voter: authorized_voter.unwrap_or(identity_pubkey),
authorized_withdrawer,
authorized_withdrawer: authorized_withdrawer.unwrap_or(identity_pubkey),
commission,
};
@@ -607,7 +465,6 @@ pub fn process_create_vote_account(
&vote_init,
lamports,
)
.with_memo(memo)
} else {
vote_instruction::create_account(
&config.signers[0].pubkey(),
@@ -615,7 +472,6 @@ pub fn process_create_vote_account(
&vote_init,
lamports,
)
.with_memo(memo)
};
Message::new(&ixs, Some(&config.signers[0].pubkey()))
};
@@ -650,7 +506,7 @@ pub fn process_create_vote_account(
let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&config.signers, recent_blockhash)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<SystemError>(result, config)
log_instruction_custom_error::<SystemError>(result, &config)
}
pub fn process_vote_authorize(
@@ -659,54 +515,26 @@ pub fn process_vote_authorize(
vote_account_pubkey: &Pubkey,
new_authorized_pubkey: &Pubkey,
vote_authorize: VoteAuthorize,
authorized: SignerIndex,
new_authorized: Option<SignerIndex>,
memo: Option<&String>,
) -> ProcessResult {
let authorized = config.signers[authorized];
let new_authorized_signer = new_authorized.map(|index| config.signers[index]);
// 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()),
(new_authorized_pubkey, "new_authorized_pubkey".to_string()),
)?;
let (_, vote_state) = get_vote_account(rpc_client, vote_account_pubkey, config.commitment)?;
match vote_authorize {
VoteAuthorize::Voter => {
let current_authorized_voter = vote_state
.authorized_voters()
.last()
.ok_or_else(|| {
CliError::RpcRequestError(
"Invalid vote account state; no authorized voters found".to_string(),
)
})?
.1;
check_current_authority(current_authorized_voter, &authorized.pubkey())?
}
VoteAuthorize::Withdrawer => {
check_current_authority(&vote_state.authorized_withdrawer, &authorized.pubkey())?
}
}
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let vote_ix = if new_authorized_signer.is_some() {
vote_instruction::authorize_checked(
vote_account_pubkey, // vote account to update
&authorized.pubkey(), // current authorized
new_authorized_pubkey, // new vote signer/withdrawer
vote_authorize, // vote or withdraw
)
} else {
vote_instruction::authorize(
vote_account_pubkey, // vote account to update
&authorized.pubkey(), // current authorized
new_authorized_pubkey, // new vote signer/withdrawer
vote_authorize, // vote or withdraw
)
};
let ixs = vec![vote_ix].with_memo(memo);
let 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
)];
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
let mut tx = Transaction::new_unsigned(message);
@@ -719,7 +547,7 @@ pub fn process_vote_authorize(
config.commitment,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<VoteError>(result, config)
log_instruction_custom_error::<VoteError>(result, &config)
}
pub fn process_vote_update_validator(
@@ -728,7 +556,6 @@ pub fn process_vote_update_validator(
vote_account_pubkey: &Pubkey,
new_identity_account: SignerIndex,
withdraw_authority: SignerIndex,
memo: Option<&String>,
) -> ProcessResult {
let authorized_withdrawer = config.signers[withdraw_authority];
let new_identity_account = config.signers[new_identity_account];
@@ -742,8 +569,7 @@ pub fn process_vote_update_validator(
vote_account_pubkey,
&authorized_withdrawer.pubkey(),
&new_identity_pubkey,
)]
.with_memo(memo);
)];
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
let mut tx = Transaction::new_unsigned(message);
@@ -756,7 +582,7 @@ pub fn process_vote_update_validator(
config.commitment,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<VoteError>(result, config)
log_instruction_custom_error::<VoteError>(result, &config)
}
pub fn process_vote_update_commission(
@@ -765,7 +591,6 @@ pub fn process_vote_update_commission(
vote_account_pubkey: &Pubkey,
commission: u8,
withdraw_authority: SignerIndex,
memo: Option<&String>,
) -> ProcessResult {
let authorized_withdrawer = config.signers[withdraw_authority];
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
@@ -773,8 +598,7 @@ pub fn process_vote_update_commission(
vote_account_pubkey,
&authorized_withdrawer.pubkey(),
commission,
)]
.with_memo(memo);
)];
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
let mut tx = Transaction::new_unsigned(message);
@@ -787,7 +611,7 @@ pub fn process_vote_update_commission(
config.commitment,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<VoteError>(result, config)
log_instruction_custom_error::<VoteError>(result, &config)
}
fn get_vote_account(
@@ -823,7 +647,6 @@ pub fn process_show_vote_account(
config: &CliConfig,
vote_account_address: &Pubkey,
use_lamports_unit: bool,
with_rewards: Option<usize>,
) -> ProcessResult {
let (vote_account, vote_state) =
get_vote_account(rpc_client, vote_account_address, config.commitment)?;
@@ -849,16 +672,14 @@ pub fn process_show_vote_account(
}
}
let epoch_rewards =
with_rewards.and_then(|num_epochs| {
match crate::stake::fetch_epoch_rewards(rpc_client, vote_account_address, num_epochs) {
Ok(rewards) => Some(rewards),
Err(error) => {
eprintln!("Failed to fetch epoch rewards: {:?}", error);
None
}
}
});
let epoch_rewards = match crate::stake::fetch_epoch_rewards(rpc_client, vote_account_address, 1)
{
Ok(rewards) => Some(rewards),
Err(error) => {
eprintln!("Failed to fetch epoch rewards: {:?}", error);
None
}
};
let vote_account_data = CliVoteAccount {
account_balance: vote_account.lamports,
@@ -885,12 +706,11 @@ pub fn process_withdraw_from_vote_account(
withdraw_authority: SignerIndex,
withdraw_amount: SpendAmount,
destination_account_pubkey: &Pubkey,
memo: Option<&String>,
) -> ProcessResult {
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let withdraw_authority = config.signers[withdraw_authority];
let current_balance = rpc_client.get_balance(vote_account_pubkey)?;
let current_balance = rpc_client.get_balance(&vote_account_pubkey)?;
let minimum_balance = rpc_client.get_minimum_balance_for_rent_exemption(VoteState::size_of())?;
let lamports = match withdraw_amount {
@@ -906,15 +726,14 @@ pub fn process_withdraw_from_vote_account(
}
};
let ixs = vec![withdraw(
let ix = withdraw(
vote_account_pubkey,
&withdraw_authority.pubkey(),
lamports,
destination_account_pubkey,
)]
.with_memo(memo);
);
let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
let message = Message::new(&[ix], Some(&config.signers[0].pubkey()));
let mut transaction = Transaction::new_unsigned(message);
transaction.try_sign(&config.signers, recent_blockhash)?;
check_account_for_fee_with_commitment(
@@ -925,13 +744,13 @@ pub fn process_withdraw_from_vote_account(
config.commitment,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&transaction);
log_instruction_custom_error::<VoteError>(result, config)
log_instruction_custom_error::<VoteError>(result, &config)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{clap_app::get_clap_app, cli::parse_command};
use crate::cli::{app, parse_command};
use solana_sdk::signature::{read_keypair_file, write_keypair, Keypair, Signer};
use tempfile::NamedTempFile;
@@ -942,7 +761,7 @@ mod tests {
#[test]
fn test_parse_command() {
let test_commands = get_clap_app("test", "desc", "version");
let test_commands = app("test", "desc", "version");
let keypair = Keypair::new();
let pubkey = keypair.pubkey();
let pubkey_string = pubkey.to_string();
@@ -953,7 +772,10 @@ mod tests {
let default_keypair = Keypair::new();
let (default_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&default_keypair, tmp_file.as_file_mut()).unwrap();
let default_signer = DefaultSigner::new("", &default_keypair_file);
let default_signer = DefaultSigner {
path: default_keypair_file.clone(),
arg_name: String::new(),
};
let test_authorize_voter = test_commands.clone().get_matches_from(vec![
"test",
@@ -968,10 +790,7 @@ mod tests {
command: CliCommand::VoteAuthorize {
vote_account_pubkey: pubkey,
new_authorized_pubkey: pubkey2,
vote_authorize: VoteAuthorize::Voter,
memo: None,
authorized: 0,
new_authorized: None,
vote_authorize: VoteAuthorize::Voter
},
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
}
@@ -994,10 +813,7 @@ mod tests {
command: CliCommand::VoteAuthorize {
vote_account_pubkey: pubkey,
new_authorized_pubkey: pubkey2,
vote_authorize: VoteAuthorize::Voter,
memo: None,
authorized: 1,
new_authorized: None,
vote_authorize: VoteAuthorize::Voter
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
@@ -1006,84 +822,18 @@ mod tests {
}
);
let (voter_keypair_file, mut tmp_file) = make_tmp_file();
let voter_keypair = Keypair::new();
write_keypair(&voter_keypair, tmp_file.as_file_mut()).unwrap();
let test_authorize_voter = test_commands.clone().get_matches_from(vec![
"test",
"vote-authorize-voter-checked",
&pubkey_string,
&default_keypair_file,
&voter_keypair_file,
]);
assert_eq!(
parse_command(&test_authorize_voter, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::VoteAuthorize {
vote_account_pubkey: pubkey,
new_authorized_pubkey: voter_keypair.pubkey(),
vote_authorize: VoteAuthorize::Voter,
memo: None,
authorized: 0,
new_authorized: Some(1),
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
read_keypair_file(&voter_keypair_file).unwrap().into()
],
}
);
let test_authorize_voter = test_commands.clone().get_matches_from(vec![
"test",
"vote-authorize-voter-checked",
&pubkey_string,
&authorized_keypair_file,
&voter_keypair_file,
]);
assert_eq!(
parse_command(&test_authorize_voter, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::VoteAuthorize {
vote_account_pubkey: pubkey,
new_authorized_pubkey: voter_keypair.pubkey(),
vote_authorize: VoteAuthorize::Voter,
memo: None,
authorized: 1,
new_authorized: Some(2),
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
read_keypair_file(&authorized_keypair_file).unwrap().into(),
read_keypair_file(&voter_keypair_file).unwrap().into(),
],
}
);
let test_authorize_voter = test_commands.clone().get_matches_from(vec![
"test",
"vote-authorize-voter-checked",
&pubkey_string,
&authorized_keypair_file,
&pubkey2_string,
]);
assert!(parse_command(&test_authorize_voter, &default_signer, &mut None).is_err());
let (keypair_file, mut tmp_file) = make_tmp_file();
let keypair = Keypair::new();
write_keypair(&keypair, tmp_file.as_file_mut()).unwrap();
// Test CreateVoteAccount SubCommand
let (identity_keypair_file, mut tmp_file) = make_tmp_file();
let identity_keypair = Keypair::new();
let authorized_withdrawer = Keypair::new().pubkey();
write_keypair(&identity_keypair, tmp_file.as_file_mut()).unwrap();
let test_create_vote_account = test_commands.clone().get_matches_from(vec![
"test",
"create-vote-account",
&keypair_file,
&identity_keypair_file,
&authorized_withdrawer.to_string(),
"--commission",
"10",
]);
@@ -1095,9 +845,8 @@ mod tests {
seed: None,
identity_account: 2,
authorized_voter: None,
authorized_withdrawer,
authorized_withdrawer: None,
commission: 10,
memo: None,
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
@@ -1116,7 +865,6 @@ mod tests {
"create-vote-account",
&keypair_file,
&identity_keypair_file,
&authorized_withdrawer.to_string(),
]);
assert_eq!(
parse_command(&test_create_vote_account2, &default_signer, &mut None).unwrap(),
@@ -1126,9 +874,8 @@ mod tests {
seed: None,
identity_account: 2,
authorized_voter: None,
authorized_withdrawer,
authorized_withdrawer: None,
commission: 100,
memo: None,
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
@@ -1149,7 +896,6 @@ mod tests {
"create-vote-account",
&keypair_file,
&identity_keypair_file,
&authorized_withdrawer.to_string(),
"--authorized-voter",
&authed.to_string(),
]);
@@ -1161,9 +907,8 @@ mod tests {
seed: None,
identity_account: 2,
authorized_voter: Some(authed),
authorized_withdrawer,
commission: 100,
memo: None,
authorized_withdrawer: None,
commission: 100
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
@@ -1176,14 +921,14 @@ mod tests {
let (keypair_file, mut tmp_file) = make_tmp_file();
let keypair = Keypair::new();
write_keypair(&keypair, tmp_file.as_file_mut()).unwrap();
// succeed even though withdrawer unsafe (because forcefully allowed)
// test init with an authed withdrawer
let test_create_vote_account4 = test_commands.clone().get_matches_from(vec![
"test",
"create-vote-account",
&keypair_file,
&identity_keypair_file,
&identity_keypair_file,
"--allow-unsafe-authorized-withdrawer",
"--authorized-withdrawer",
&authed.to_string(),
]);
assert_eq!(
parse_command(&test_create_vote_account4, &default_signer, &mut None).unwrap(),
@@ -1193,9 +938,8 @@ mod tests {
seed: None,
identity_account: 2,
authorized_voter: None,
authorized_withdrawer: identity_keypair.pubkey(),
commission: 100,
memo: None,
authorized_withdrawer: Some(authed),
commission: 100
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
@@ -1219,7 +963,6 @@ mod tests {
vote_account_pubkey: pubkey,
new_identity_account: 2,
withdraw_authority: 1,
memo: None,
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
@@ -1243,7 +986,6 @@ mod tests {
vote_account_pubkey: pubkey,
commission: 42,
withdraw_authority: 1,
memo: None,
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),
@@ -1268,7 +1010,6 @@ mod tests {
destination_account_pubkey: pubkey,
withdraw_authority: 0,
withdraw_amount: SpendAmount::Some(42_000_000_000),
memo: None,
},
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
}
@@ -1290,7 +1031,6 @@ mod tests {
destination_account_pubkey: pubkey,
withdraw_authority: 0,
withdraw_amount: SpendAmount::All,
memo: None,
},
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
}
@@ -1317,7 +1057,6 @@ mod tests {
destination_account_pubkey: pubkey,
withdraw_authority: 1,
withdraw_amount: SpendAmount::Some(42_000_000_000),
memo: None,
},
signers: vec![
read_keypair_file(&default_keypair_file).unwrap().into(),

View File

@@ -1,744 +0,0 @@
use crate::{
cli::{
log_instruction_custom_error, request_and_confirm_airdrop, CliCommand, CliCommandInfo,
CliConfig, CliError, ProcessResult,
},
memo::WithMemo,
nonce::check_nonce_account,
spend_utils::{resolve_spend_tx_and_check_account_balances, SpendAmount},
};
use clap::{value_t_or_exit, App, Arg, ArgMatches, SubCommand};
use solana_account_decoder::{UiAccount, UiAccountEncoding};
use solana_clap_utils::{
fee_payer::*,
input_parsers::*,
input_validators::*,
keypair::{DefaultSigner, SignerIndex},
memo::*,
nonce::*,
offline::*,
};
use solana_cli_output::{
display::build_balance_message, return_signers_with_config, CliAccount,
CliSignatureVerificationStatus, CliTransaction, CliTransactionConfirmation, OutputFormat,
ReturnSignersConfig,
};
use solana_client::{
blockhash_query::BlockhashQuery, nonce_utils, rpc_client::RpcClient,
rpc_config::RpcTransactionConfig, rpc_response::RpcKeyedAccount,
};
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{
commitment_config::CommitmentConfig,
message::Message,
pubkey::Pubkey,
signature::Signature,
stake,
system_instruction::{self, SystemError},
system_program,
transaction::Transaction,
};
use solana_transaction_status::{EncodedTransaction, UiTransactionEncoding};
use std::{fmt::Write as FmtWrite, fs::File, io::Write, sync::Arc};
pub trait WalletSubCommands {
fn wallet_subcommands(self) -> Self;
}
impl WalletSubCommands for App<'_, '_> {
fn wallet_subcommands(self) -> Self {
self.subcommand(
SubCommand::with_name("account")
.about("Show the contents of an account")
.alias("account")
.arg(
pubkey!(Arg::with_name("account_pubkey")
.index(1)
.value_name("ACCOUNT_ADDRESS")
.required(true),
"Account key URI. ")
)
.arg(
Arg::with_name("output_file")
.long("output-file")
.short("o")
.value_name("FILEPATH")
.takes_value(true)
.help("Write the account data to this file"),
)
.arg(
Arg::with_name("lamports")
.long("lamports")
.takes_value(false)
.help("Display balance in lamports instead of SOL"),
),
)
.subcommand(
SubCommand::with_name("address")
.about("Get your public key")
.arg(
Arg::with_name("confirm_key")
.long("confirm-key")
.takes_value(false)
.help("Confirm key on device; only relevant if using remote wallet"),
),
)
.subcommand(
SubCommand::with_name("airdrop")
.about("Request SOL from a faucet")
.arg(
Arg::with_name("amount")
.index(1)
.value_name("AMOUNT")
.takes_value(true)
.validator(is_amount)
.required(true)
.help("The airdrop amount to request, in SOL"),
)
.arg(
pubkey!(Arg::with_name("to")
.index(2)
.value_name("RECIPIENT_ADDRESS"),
"The account address of airdrop recipient. "),
),
)
.subcommand(
SubCommand::with_name("balance")
.about("Get your balance")
.arg(
pubkey!(Arg::with_name("pubkey")
.index(1)
.value_name("ACCOUNT_ADDRESS"),
"The account address of the balance to check. ")
)
.arg(
Arg::with_name("lamports")
.long("lamports")
.takes_value(false)
.help("Display balance in lamports instead of SOL"),
),
)
.subcommand(
SubCommand::with_name("confirm")
.about("Confirm transaction by signature")
.arg(
Arg::with_name("signature")
.index(1)
.value_name("TRANSACTION_SIGNATURE")
.takes_value(true)
.required(true)
.help("The transaction signature to confirm"),
)
.after_help(// Formatted specifically for the manually-indented heredoc string
"Note: This will show more detailed information for finalized transactions with verbose mode (-v/--verbose).\
\n\
\nAccount modes:\
\n |srwx|\
\n s: signed\
\n r: readable (always true)\
\n w: writable\
\n x: program account (inner instructions excluded)\
"
),
)
.subcommand(
SubCommand::with_name("create-address-with-seed")
.about("Generate a derived account address with a seed")
.arg(
Arg::with_name("seed")
.index(1)
.value_name("SEED_STRING")
.takes_value(true)
.required(true)
.validator(is_derived_address_seed)
.help("The seed. Must not take more than 32 bytes to encode as utf-8"),
)
.arg(
Arg::with_name("program_id")
.index(2)
.value_name("PROGRAM_ID")
.takes_value(true)
.required(true)
.help(
"The program_id that the address will ultimately be used for, \n\
or one of NONCE, STAKE, and VOTE keywords",
),
)
.arg(
pubkey!(Arg::with_name("from")
.long("from")
.value_name("FROM_PUBKEY")
.required(false),
"From (base) key, [default: cli config keypair]. "),
),
)
.subcommand(
SubCommand::with_name("decode-transaction")
.about("Decode a serialized transaction")
.arg(
Arg::with_name("transaction")
.index(1)
.value_name("TRANSACTION")
.takes_value(true)
.required(true)
.help("transaction to decode"),
)
.arg(
Arg::with_name("encoding")
.index(2)
.value_name("ENCODING")
.possible_values(&["base58", "base64"]) // Subset of `UiTransactionEncoding` enum
.default_value("base58")
.takes_value(true)
.required(true)
.help("transaction encoding"),
),
)
.subcommand(
SubCommand::with_name("resolve-signer")
.about("Checks that a signer is valid, and returns its specific path; useful for signers that may be specified generally, eg. usb://ledger")
.arg(
Arg::with_name("signer")
.index(1)
.value_name("SIGNER_KEYPAIR")
.takes_value(true)
.required(true)
.validator(is_valid_signer)
.help("The signer path to resolve")
)
)
.subcommand(
SubCommand::with_name("transfer")
.about("Transfer funds between system accounts")
.alias("pay")
.arg(
pubkey!(Arg::with_name("to")
.index(1)
.value_name("RECIPIENT_ADDRESS")
.required(true),
"The account address of recipient. "),
)
.arg(
Arg::with_name("amount")
.index(2)
.value_name("AMOUNT")
.takes_value(true)
.validator(is_amount_or_all)
.required(true)
.help("The amount to send, in SOL; accepts keyword ALL"),
)
.arg(
pubkey!(Arg::with_name("from")
.long("from")
.value_name("FROM_ADDRESS"),
"Source account of funds (if different from client local account). "),
)
.arg(
Arg::with_name("no_wait")
.long("no-wait")
.takes_value(false)
.help("Return signature immediately after submitting the transaction, instead of waiting for confirmations"),
)
.arg(
Arg::with_name("derived_address_seed")
.long("derived-address-seed")
.takes_value(true)
.value_name("SEED_STRING")
.requires("derived_address_program_id")
.validator(is_derived_address_seed)
.hidden(true)
)
.arg(
Arg::with_name("derived_address_program_id")
.long("derived-address-program-id")
.takes_value(true)
.value_name("PROGRAM_ID")
.requires("derived_address_seed")
.hidden(true)
)
.arg(
Arg::with_name("allow_unfunded_recipient")
.long("allow-unfunded-recipient")
.takes_value(false)
.help("Complete the transfer even if the recipient address is not funded")
)
.offline_args()
.nonce_args(false)
.arg(memo_arg())
.arg(fee_payer_arg()),
)
}
}
fn resolve_derived_address_program_id(matches: &ArgMatches<'_>, arg_name: &str) -> Option<Pubkey> {
matches.value_of(arg_name).and_then(|v| match v {
"NONCE" => Some(system_program::id()),
"STAKE" => Some(stake::program::id()),
"VOTE" => Some(solana_vote_program::id()),
_ => pubkey_of(matches, arg_name),
})
}
pub fn parse_account(
matches: &ArgMatches<'_>,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let account_pubkey = pubkey_of_signer(matches, "account_pubkey", wallet_manager)?.unwrap();
let output_file = matches.value_of("output_file");
let use_lamports_unit = matches.is_present("lamports");
Ok(CliCommandInfo {
command: CliCommand::ShowAccount {
pubkey: account_pubkey,
output_file: output_file.map(ToString::to_string),
use_lamports_unit,
},
signers: vec![],
})
}
pub fn parse_airdrop(
matches: &ArgMatches<'_>,
default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let pubkey = pubkey_of_signer(matches, "to", wallet_manager)?;
let signers = if pubkey.is_some() {
vec![]
} else {
vec![default_signer.signer_from_path(matches, wallet_manager)?]
};
let lamports = lamports_of_sol(matches, "amount").unwrap();
Ok(CliCommandInfo {
command: CliCommand::Airdrop { pubkey, lamports },
signers,
})
}
pub fn parse_balance(
matches: &ArgMatches<'_>,
default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let pubkey = pubkey_of_signer(matches, "pubkey", wallet_manager)?;
let signers = if pubkey.is_some() {
vec![]
} else {
vec![default_signer.signer_from_path(matches, wallet_manager)?]
};
Ok(CliCommandInfo {
command: CliCommand::Balance {
pubkey,
use_lamports_unit: matches.is_present("lamports"),
},
signers,
})
}
pub fn parse_decode_transaction(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
let blob = value_t_or_exit!(matches, "transaction", String);
let encoding = match matches.value_of("encoding").unwrap() {
"base58" => UiTransactionEncoding::Base58,
"base64" => UiTransactionEncoding::Base64,
_ => unreachable!(),
};
let encoded_transaction = EncodedTransaction::Binary(blob, encoding);
if let Some(transaction) = encoded_transaction.decode() {
Ok(CliCommandInfo {
command: CliCommand::DecodeTransaction(transaction),
signers: vec![],
})
} else {
Err(CliError::BadParameter(
"Unable to decode transaction".to_string(),
))
}
}
pub fn parse_create_address_with_seed(
matches: &ArgMatches<'_>,
default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let from_pubkey = pubkey_of_signer(matches, "from", wallet_manager)?;
let signers = if from_pubkey.is_some() {
vec![]
} else {
vec![default_signer.signer_from_path(matches, wallet_manager)?]
};
let program_id = resolve_derived_address_program_id(matches, "program_id").unwrap();
let seed = matches.value_of("seed").unwrap().to_string();
Ok(CliCommandInfo {
command: CliCommand::CreateAddressWithSeed {
from_pubkey,
seed,
program_id,
},
signers,
})
}
pub fn parse_transfer(
matches: &ArgMatches<'_>,
default_signer: &DefaultSigner,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
let amount = SpendAmount::new_from_matches(matches, "amount");
let to = pubkey_of_signer(matches, "to", wallet_manager)?.unwrap();
let sign_only = matches.is_present(SIGN_ONLY_ARG.name);
let dump_transaction_message = matches.is_present(DUMP_TRANSACTION_MESSAGE.name);
let no_wait = matches.is_present("no_wait");
let blockhash_query = BlockhashQuery::new_from_matches(matches);
let nonce_account = pubkey_of_signer(matches, NONCE_ARG.name, wallet_manager)?;
let (nonce_authority, nonce_authority_pubkey) =
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
let memo = matches.value_of(MEMO_ARG.name).map(String::from);
let (fee_payer, fee_payer_pubkey) = signer_of(matches, FEE_PAYER_ARG.name, wallet_manager)?;
let (from, from_pubkey) = signer_of(matches, "from", wallet_manager)?;
let allow_unfunded_recipient = matches.is_present("allow_unfunded_recipient");
let mut bulk_signers = vec![fee_payer, from];
if nonce_account.is_some() {
bulk_signers.push(nonce_authority);
}
let signer_info =
default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
let derived_address_seed = matches
.value_of("derived_address_seed")
.map(|s| s.to_string());
let derived_address_program_id =
resolve_derived_address_program_id(matches, "derived_address_program_id");
Ok(CliCommandInfo {
command: CliCommand::Transfer {
amount,
to,
sign_only,
dump_transaction_message,
allow_unfunded_recipient,
no_wait,
blockhash_query,
nonce_account,
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
memo,
fee_payer: signer_info.index_of(fee_payer_pubkey).unwrap(),
from: signer_info.index_of(from_pubkey).unwrap(),
derived_address_seed,
derived_address_program_id,
},
signers: signer_info.signers,
})
}
pub fn process_show_account(
rpc_client: &RpcClient,
config: &CliConfig,
account_pubkey: &Pubkey,
output_file: &Option<String>,
use_lamports_unit: bool,
) -> ProcessResult {
let account = rpc_client.get_account(account_pubkey)?;
let data = account.data.clone();
let cli_account = CliAccount {
keyed_account: RpcKeyedAccount {
pubkey: account_pubkey.to_string(),
account: UiAccount::encode(
account_pubkey,
&account,
UiAccountEncoding::Base64,
None,
None,
),
},
use_lamports_unit,
};
let mut account_string = config.output_format.formatted_string(&cli_account);
if config.output_format == OutputFormat::Display
|| config.output_format == OutputFormat::DisplayVerbose
{
if let Some(output_file) = output_file {
let mut f = File::create(output_file)?;
f.write_all(&data)?;
writeln!(&mut account_string)?;
writeln!(&mut account_string, "Wrote account data to {}", output_file)?;
} else if !data.is_empty() {
use pretty_hex::*;
writeln!(&mut account_string, "{:?}", data.hex_dump())?;
}
}
Ok(account_string)
}
pub fn process_airdrop(
rpc_client: &RpcClient,
config: &CliConfig,
pubkey: &Option<Pubkey>,
lamports: u64,
) -> ProcessResult {
let pubkey = if let Some(pubkey) = pubkey {
*pubkey
} else {
config.pubkey()?
};
println!(
"Requesting airdrop of {}",
build_balance_message(lamports, false, true),
);
let pre_balance = rpc_client.get_balance(&pubkey)?;
let result = request_and_confirm_airdrop(rpc_client, config, &pubkey, lamports);
if let Ok(signature) = result {
let signature_cli_message = log_instruction_custom_error::<SystemError>(result, config)?;
println!("{}", signature_cli_message);
let current_balance = rpc_client.get_balance(&pubkey)?;
if current_balance < pre_balance.saturating_add(lamports) {
println!("Balance unchanged");
println!("Run `solana confirm -v {:?}` for more info", signature);
Ok("".to_string())
} else {
Ok(build_balance_message(current_balance, false, true))
}
} else {
log_instruction_custom_error::<SystemError>(result, config)
}
}
pub fn process_balance(
rpc_client: &RpcClient,
config: &CliConfig,
pubkey: &Option<Pubkey>,
use_lamports_unit: bool,
) -> ProcessResult {
let pubkey = if let Some(pubkey) = pubkey {
*pubkey
} else {
config.pubkey()?
};
let balance = rpc_client.get_balance(&pubkey)?;
Ok(build_balance_message(balance, use_lamports_unit, true))
}
pub fn process_confirm(
rpc_client: &RpcClient,
config: &CliConfig,
signature: &Signature,
) -> ProcessResult {
match rpc_client.get_signature_statuses_with_history(&[*signature]) {
Ok(status) => {
let cli_transaction = if let Some(transaction_status) = &status.value[0] {
let mut transaction = None;
let mut get_transaction_error = None;
if config.verbose {
match rpc_client.get_transaction_with_config(
signature,
RpcTransactionConfig {
encoding: Some(UiTransactionEncoding::Base64),
commitment: Some(CommitmentConfig::confirmed()),
},
) {
Ok(confirmed_transaction) => {
let decoded_transaction = confirmed_transaction
.transaction
.transaction
.decode()
.expect("Successful decode");
let json_transaction = EncodedTransaction::encode(
decoded_transaction.clone(),
UiTransactionEncoding::Json,
);
transaction = Some(CliTransaction {
transaction: json_transaction,
meta: confirmed_transaction.transaction.meta,
block_time: confirmed_transaction.block_time,
slot: Some(confirmed_transaction.slot),
decoded_transaction,
prefix: " ".to_string(),
sigverify_status: vec![],
});
}
Err(err) => {
get_transaction_error = Some(format!("{:?}", err));
}
}
}
CliTransactionConfirmation {
confirmation_status: Some(transaction_status.confirmation_status()),
transaction,
get_transaction_error,
err: transaction_status.err.clone(),
}
} else {
CliTransactionConfirmation {
confirmation_status: None,
transaction: None,
get_transaction_error: None,
err: None,
}
};
Ok(config.output_format.formatted_string(&cli_transaction))
}
Err(err) => Err(CliError::RpcRequestError(format!("Unable to confirm: {}", err)).into()),
}
}
#[allow(clippy::unnecessary_wraps)]
pub fn process_decode_transaction(config: &CliConfig, transaction: &Transaction) -> ProcessResult {
let sigverify_status = CliSignatureVerificationStatus::verify_transaction(transaction);
let decode_transaction = CliTransaction {
decoded_transaction: transaction.clone(),
transaction: EncodedTransaction::encode(transaction.clone(), UiTransactionEncoding::Json),
meta: None,
block_time: None,
slot: None,
prefix: "".to_string(),
sigverify_status,
};
Ok(config.output_format.formatted_string(&decode_transaction))
}
pub fn process_create_address_with_seed(
config: &CliConfig,
from_pubkey: Option<&Pubkey>,
seed: &str,
program_id: &Pubkey,
) -> ProcessResult {
let from_pubkey = if let Some(pubkey) = from_pubkey {
*pubkey
} else {
config.pubkey()?
};
let address = Pubkey::create_with_seed(&from_pubkey, seed, program_id)?;
Ok(address.to_string())
}
#[allow(clippy::too_many_arguments)]
pub fn process_transfer(
rpc_client: &RpcClient,
config: &CliConfig,
amount: SpendAmount,
to: &Pubkey,
from: SignerIndex,
sign_only: bool,
dump_transaction_message: bool,
allow_unfunded_recipient: bool,
no_wait: bool,
blockhash_query: &BlockhashQuery,
nonce_account: Option<&Pubkey>,
nonce_authority: SignerIndex,
memo: Option<&String>,
fee_payer: SignerIndex,
derived_address_seed: Option<String>,
derived_address_program_id: Option<&Pubkey>,
) -> ProcessResult {
let from = config.signers[from];
let mut from_pubkey = from.pubkey();
let (recent_blockhash, fee_calculator) =
blockhash_query.get_blockhash_and_fee_calculator(rpc_client, config.commitment)?;
if !sign_only && !allow_unfunded_recipient {
let recipient_balance = rpc_client
.get_balance_with_commitment(to, config.commitment)?
.value;
if recipient_balance == 0 {
return Err(format!(
"The recipient address ({}) is not funded. \
Add `--allow-unfunded-recipient` to complete the transfer \
",
to
)
.into());
}
}
let nonce_authority = config.signers[nonce_authority];
let fee_payer = config.signers[fee_payer];
let derived_parts = derived_address_seed.zip(derived_address_program_id);
let with_seed = if let Some((seed, program_id)) = derived_parts {
let base_pubkey = from_pubkey;
from_pubkey = Pubkey::create_with_seed(&base_pubkey, &seed, program_id)?;
Some((base_pubkey, seed, program_id, from_pubkey))
} else {
None
};
let build_message = |lamports| {
let ixs = if let Some((base_pubkey, seed, program_id, from_pubkey)) = with_seed.as_ref() {
vec![system_instruction::transfer_with_seed(
from_pubkey,
base_pubkey,
seed.clone(),
program_id,
to,
lamports,
)]
.with_memo(memo)
} else {
vec![system_instruction::transfer(&from_pubkey, to, lamports)].with_memo(memo)
};
if let Some(nonce_account) = &nonce_account {
Message::new_with_nonce(
ixs,
Some(&fee_payer.pubkey()),
nonce_account,
&nonce_authority.pubkey(),
)
} else {
Message::new(&ixs, Some(&fee_payer.pubkey()))
}
};
let (message, _) = resolve_spend_tx_and_check_account_balances(
rpc_client,
sign_only,
amount,
&fee_calculator,
&from_pubkey,
&fee_payer.pubkey(),
build_message,
config.commitment,
)?;
let mut tx = Transaction::new_unsigned(message);
if sign_only {
tx.try_partial_sign(&config.signers, recent_blockhash)?;
return_signers_with_config(
&tx,
&config.output_format,
&ReturnSignersConfig {
dump_transaction_message,
},
)
} else {
if let Some(nonce_account) = &nonce_account {
let nonce_account = nonce_utils::get_account_with_commitment(
rpc_client,
nonce_account,
config.commitment,
)?;
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
}
tx.try_sign(&config.signers, recent_blockhash)?;
let result = if no_wait {
rpc_client.send_transaction(&tx)
} else {
rpc_client.send_and_confirm_transaction_with_spinner(&tx)
};
log_instruction_custom_error::<SystemError>(result, config)
}
}

View File

@@ -18,46 +18,48 @@ use solana_sdk::{
signature::{keypair_from_seed, Keypair, Signer},
system_program,
};
use solana_streamer::socket::SocketAddrSpace;
#[test]
fn test_nonce() {
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator =
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
full_battery_tests(test_validator, None, false);
full_battery_tests(
TestValidator::with_no_fees(mint_keypair.pubkey()),
mint_keypair,
None,
false,
);
}
#[test]
fn test_nonce_with_seed() {
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator =
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
full_battery_tests(test_validator, Some(String::from("seed")), false);
full_battery_tests(
TestValidator::with_no_fees(mint_keypair.pubkey()),
mint_keypair,
Some(String::from("seed")),
false,
);
}
#[test]
fn test_nonce_with_authority() {
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator =
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
full_battery_tests(test_validator, None, true);
full_battery_tests(
TestValidator::with_no_fees(mint_keypair.pubkey()),
mint_keypair,
None,
true,
);
}
fn full_battery_tests(
test_validator: TestValidator,
mint_keypair: Keypair,
seed: Option<String>,
use_nonce_authority: bool,
) {
let faucet_addr = run_local_faucet(mint_keypair, None);
let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
let json_rpc_url = test_validator.rpc_url();
@@ -69,9 +71,10 @@ fn full_battery_tests(
request_and_confirm_airdrop(
&rpc_client,
&config_payer,
&faucet_addr,
&config_payer.signers[0].pubkey(),
2000,
&config_payer,
)
.unwrap();
check_recent_balance(2000, &rpc_client, &config_payer.signers[0].pubkey());
@@ -105,7 +108,6 @@ fn full_battery_tests(
nonce_account: 1,
seed,
nonce_authority: optional_authority,
memo: None,
amount: SpendAmount::Some(1000),
};
@@ -139,7 +141,6 @@ fn full_battery_tests(
config_payer.command = CliCommand::NewNonce {
nonce_account,
nonce_authority: index,
memo: None,
};
process_command(&config_payer).unwrap();
@@ -157,7 +158,6 @@ fn full_battery_tests(
config_payer.command = CliCommand::WithdrawFromNonceAccount {
nonce_account,
nonce_authority: index,
memo: None,
destination_account_pubkey: payee_pubkey,
lamports: 100,
};
@@ -178,7 +178,6 @@ fn full_battery_tests(
config_payer.command = CliCommand::AuthorizeNonceAccount {
nonce_account,
nonce_authority: index,
memo: None,
new_authority: new_authority.pubkey(),
};
process_command(&config_payer).unwrap();
@@ -187,7 +186,6 @@ fn full_battery_tests(
config_payer.command = CliCommand::NewNonce {
nonce_account,
nonce_authority: index,
memo: None,
};
process_command(&config_payer).unwrap_err();
@@ -196,7 +194,6 @@ fn full_battery_tests(
config_payer.command = CliCommand::NewNonce {
nonce_account,
nonce_authority: 1,
memo: None,
};
process_command(&config_payer).unwrap();
@@ -204,7 +201,6 @@ fn full_battery_tests(
config_payer.command = CliCommand::WithdrawFromNonceAccount {
nonce_account,
nonce_authority: 1,
memo: None,
destination_account_pubkey: payee_pubkey,
lamports: 100,
};
@@ -218,34 +214,32 @@ fn full_battery_tests(
fn test_create_account_with_seed() {
solana_logger::setup();
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator = TestValidator::with_custom_fees(
mint_pubkey,
1,
Some(faucet_addr),
SocketAddrSpace::Unspecified,
);
let offline_nonce_authority_signer = keypair_from_seed(&[1u8; 32]).unwrap();
let online_nonce_creator_signer = keypair_from_seed(&[2u8; 32]).unwrap();
let to_address = Pubkey::new(&[3u8; 32]);
let config = CliConfig::recent_for_tests();
// Setup accounts
let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
request_and_confirm_airdrop(
&rpc_client,
&CliConfig::recent_for_tests(),
&faucet_addr,
&offline_nonce_authority_signer.pubkey(),
42,
&config,
)
.unwrap();
request_and_confirm_airdrop(
&rpc_client,
&CliConfig::recent_for_tests(),
&faucet_addr,
&online_nonce_creator_signer.pubkey(),
4242,
&config,
)
.unwrap();
check_recent_balance(42, &rpc_client, &offline_nonce_authority_signer.pubkey());
@@ -269,7 +263,6 @@ fn test_create_account_with_seed() {
nonce_account: 0,
seed: Some(seed),
nonce_authority: Some(authority_pubkey),
memo: None,
amount: SpendAmount::Some(241),
};
process_command(&creator_config).unwrap();
@@ -306,7 +299,6 @@ fn test_create_account_with_seed() {
blockhash_query: BlockhashQuery::None(nonce_hash),
nonce_account: Some(nonce_address),
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,
@@ -335,7 +327,6 @@ fn test_create_account_with_seed() {
),
nonce_account: Some(nonce_address),
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,

View File

@@ -15,7 +15,6 @@ use solana_sdk::{
pubkey::Pubkey,
signature::{Keypair, Signer},
};
use solana_streamer::socket::SocketAddrSpace;
use std::{env, fs::File, io::Read, path::PathBuf, str::FromStr};
#[test]
@@ -29,10 +28,8 @@ fn test_cli_program_deploy_non_upgradeable() {
pathbuf.set_extension("so");
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator =
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
@@ -49,6 +46,8 @@ fn test_cli_program_deploy_non_upgradeable() {
config.json_rpc_url = test_validator.rpc_url();
config.signers = vec![&keypair];
config.command = CliCommand::Airdrop {
faucet_host: None,
faucet_port: faucet_addr.port(),
pubkey: None,
lamports: 4 * minimum_balance_for_rent_exemption, // min balance for rent exemption for three programs + leftover for tx processing
};
@@ -70,11 +69,11 @@ fn test_cli_program_deploy_non_upgradeable() {
.unwrap()
.as_str()
.unwrap();
let program_id = Pubkey::from_str(program_id_str).unwrap();
let program_id = Pubkey::from_str(&program_id_str).unwrap();
let account0 = rpc_client.get_account(&program_id).unwrap();
assert_eq!(account0.lamports, minimum_balance_for_rent_exemption);
assert_eq!(account0.owner, bpf_loader::id());
assert!(account0.executable);
assert_eq!(account0.executable, true);
let mut file = File::open(pathbuf.to_str().unwrap().to_string()).unwrap();
let mut elf = Vec::new();
file.read_to_end(&mut elf).unwrap();
@@ -95,7 +94,7 @@ fn test_cli_program_deploy_non_upgradeable() {
.unwrap();
assert_eq!(account1.lamports, minimum_balance_for_rent_exemption);
assert_eq!(account1.owner, bpf_loader::id());
assert!(account1.executable);
assert_eq!(account1.executable, true);
assert_eq!(account1.data, account0.data);
// Attempt to redeploy to the same address
@@ -105,6 +104,8 @@ fn test_cli_program_deploy_non_upgradeable() {
let custom_address_keypair = Keypair::new();
config.signers = vec![&custom_address_keypair];
config.command = CliCommand::Airdrop {
faucet_host: None,
faucet_port: faucet_addr.port(),
pubkey: None,
lamports: 2 * minimum_balance_for_rent_exemption, // Anything over minimum_balance_for_rent_exemption should trigger err
};
@@ -131,7 +132,7 @@ fn test_cli_program_deploy_non_upgradeable() {
.unwrap();
assert_eq!(account2.lamports, 2 * minimum_balance_for_rent_exemption);
assert_eq!(account2.owner, bpf_loader::id());
assert!(account2.executable);
assert_eq!(account2.executable, true);
assert_eq!(account2.data, account0.data);
}
@@ -146,10 +147,8 @@ fn test_cli_program_deploy_no_authority() {
pathbuf.set_extension("so");
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator =
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
@@ -172,6 +171,8 @@ fn test_cli_program_deploy_no_authority() {
let keypair = Keypair::new();
config.json_rpc_url = test_validator.rpc_url();
config.command = CliCommand::Airdrop {
faucet_host: None,
faucet_port: faucet_addr.port(),
pubkey: None,
lamports: 100 * minimum_balance_for_programdata + minimum_balance_for_program,
};
@@ -201,7 +202,7 @@ fn test_cli_program_deploy_no_authority() {
.unwrap()
.as_str()
.unwrap();
let program_id = Pubkey::from_str(program_id_str).unwrap();
let program_id = Pubkey::from_str(&program_id_str).unwrap();
// Attempt to upgrade the program
config.signers = vec![&keypair, &upgrade_authority];
@@ -230,10 +231,8 @@ fn test_cli_program_deploy_with_authority() {
pathbuf.set_extension("so");
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator =
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
@@ -257,6 +256,8 @@ fn test_cli_program_deploy_with_authority() {
config.json_rpc_url = test_validator.rpc_url();
config.signers = vec![&keypair];
config.command = CliCommand::Airdrop {
faucet_host: None,
faucet_port: faucet_addr.port(),
pubkey: None,
lamports: 100 * minimum_balance_for_programdata + minimum_balance_for_program,
};
@@ -288,12 +289,12 @@ fn test_cli_program_deploy_with_authority() {
.unwrap();
assert_eq!(
program_keypair.pubkey(),
Pubkey::from_str(program_pubkey_str).unwrap()
Pubkey::from_str(&program_pubkey_str).unwrap()
);
let program_account = rpc_client.get_account(&program_keypair.pubkey()).unwrap();
assert_eq!(program_account.lamports, minimum_balance_for_program);
assert_eq!(program_account.owner, bpf_loader_upgradeable::id());
assert!(program_account.executable);
assert_eq!(program_account.executable, true);
let (programdata_pubkey, _) = Pubkey::find_program_address(
&[program_keypair.pubkey().as_ref()],
&bpf_loader_upgradeable::id(),
@@ -304,7 +305,7 @@ fn test_cli_program_deploy_with_authority() {
minimum_balance_for_programdata
);
assert_eq!(programdata_account.owner, bpf_loader_upgradeable::id());
assert!(!programdata_account.executable);
assert_eq!(programdata_account.executable, false);
assert_eq!(
programdata_account.data[UpgradeableLoaderState::programdata_data_offset().unwrap()..],
program_data[..]
@@ -332,11 +333,11 @@ fn test_cli_program_deploy_with_authority() {
.unwrap()
.as_str()
.unwrap();
let program_pubkey = Pubkey::from_str(program_pubkey_str).unwrap();
let program_pubkey = Pubkey::from_str(&program_pubkey_str).unwrap();
let program_account = rpc_client.get_account(&program_pubkey).unwrap();
assert_eq!(program_account.lamports, minimum_balance_for_program);
assert_eq!(program_account.owner, bpf_loader_upgradeable::id());
assert!(program_account.executable);
assert_eq!(program_account.executable, true);
let (programdata_pubkey, _) =
Pubkey::find_program_address(&[program_pubkey.as_ref()], &bpf_loader_upgradeable::id());
let programdata_account = rpc_client.get_account(&programdata_pubkey).unwrap();
@@ -345,7 +346,7 @@ fn test_cli_program_deploy_with_authority() {
minimum_balance_for_programdata
);
assert_eq!(programdata_account.owner, bpf_loader_upgradeable::id());
assert!(!programdata_account.executable);
assert_eq!(programdata_account.executable, false);
assert_eq!(
programdata_account.data[UpgradeableLoaderState::programdata_data_offset().unwrap()..],
program_data[..]
@@ -368,7 +369,7 @@ fn test_cli_program_deploy_with_authority() {
let program_account = rpc_client.get_account(&program_pubkey).unwrap();
assert_eq!(program_account.lamports, minimum_balance_for_program);
assert_eq!(program_account.owner, bpf_loader_upgradeable::id());
assert!(program_account.executable);
assert_eq!(program_account.executable, true);
let (programdata_pubkey, _) =
Pubkey::find_program_address(&[program_pubkey.as_ref()], &bpf_loader_upgradeable::id());
let programdata_account = rpc_client.get_account(&programdata_pubkey).unwrap();
@@ -377,7 +378,7 @@ fn test_cli_program_deploy_with_authority() {
minimum_balance_for_programdata
);
assert_eq!(programdata_account.owner, bpf_loader_upgradeable::id());
assert!(!programdata_account.executable);
assert_eq!(programdata_account.executable, false);
assert_eq!(
programdata_account.data[UpgradeableLoaderState::programdata_data_offset().unwrap()..],
program_data[..]
@@ -401,7 +402,7 @@ fn test_cli_program_deploy_with_authority() {
.as_str()
.unwrap();
assert_eq!(
Pubkey::from_str(new_upgrade_authority_str).unwrap(),
Pubkey::from_str(&new_upgrade_authority_str).unwrap(),
new_upgrade_authority.pubkey()
);
@@ -422,7 +423,7 @@ fn test_cli_program_deploy_with_authority() {
let program_account = rpc_client.get_account(&program_pubkey).unwrap();
assert_eq!(program_account.lamports, minimum_balance_for_program);
assert_eq!(program_account.owner, bpf_loader_upgradeable::id());
assert!(program_account.executable);
assert_eq!(program_account.executable, true);
let (programdata_pubkey, _) =
Pubkey::find_program_address(&[program_pubkey.as_ref()], &bpf_loader_upgradeable::id());
let programdata_account = rpc_client.get_account(&programdata_pubkey).unwrap();
@@ -431,7 +432,7 @@ fn test_cli_program_deploy_with_authority() {
minimum_balance_for_programdata
);
assert_eq!(programdata_account.owner, bpf_loader_upgradeable::id());
assert!(!programdata_account.executable);
assert_eq!(programdata_account.executable, false);
assert_eq!(
programdata_account.data[UpgradeableLoaderState::programdata_data_offset().unwrap()..],
program_data[..]
@@ -442,8 +443,6 @@ fn test_cli_program_deploy_with_authority() {
config.command = CliCommand::Program(ProgramCliCommand::Show {
account_pubkey: Some(program_pubkey),
authority_pubkey: keypair.pubkey(),
get_programs: false,
get_buffers: false,
all: false,
use_lamports_unit: false,
});
@@ -458,7 +457,7 @@ fn test_cli_program_deploy_with_authority() {
.unwrap();
assert_eq!(
new_upgrade_authority.pubkey(),
Pubkey::from_str(authority_pubkey_str).unwrap()
Pubkey::from_str(&authority_pubkey_str).unwrap()
);
// Set no authority
@@ -516,7 +515,7 @@ fn test_cli_program_deploy_with_authority() {
.unwrap()
.as_str()
.unwrap();
let program_pubkey = Pubkey::from_str(program_pubkey_str).unwrap();
let program_pubkey = Pubkey::from_str(&program_pubkey_str).unwrap();
let (programdata_pubkey, _) =
Pubkey::find_program_address(&[program_pubkey.as_ref()], &bpf_loader_upgradeable::id());
let programdata_account = rpc_client.get_account(&programdata_pubkey).unwrap();
@@ -527,7 +526,7 @@ fn test_cli_program_deploy_with_authority() {
{
assert_eq!(upgrade_authority_address, None);
} else {
panic!("not a ProgramData account");
panic!("not a buffer account");
}
// Get buffer authority
@@ -535,8 +534,6 @@ fn test_cli_program_deploy_with_authority() {
config.command = CliCommand::Program(ProgramCliCommand::Show {
account_pubkey: Some(program_pubkey),
authority_pubkey: keypair.pubkey(),
get_programs: false,
get_buffers: false,
all: false,
use_lamports_unit: false,
});
@@ -552,88 +549,6 @@ fn test_cli_program_deploy_with_authority() {
assert_eq!("none", authority_pubkey_str);
}
#[test]
fn test_cli_program_close_program() {
solana_logger::setup();
let mut pathbuf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
pathbuf.push("tests");
pathbuf.push("fixtures");
pathbuf.push("noop");
pathbuf.set_extension("so");
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator =
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
let mut file = File::open(pathbuf.to_str().unwrap()).unwrap();
let mut program_data = Vec::new();
file.read_to_end(&mut program_data).unwrap();
let max_len = program_data.len();
let minimum_balance_for_programdata = rpc_client
.get_minimum_balance_for_rent_exemption(
UpgradeableLoaderState::programdata_len(max_len).unwrap(),
)
.unwrap();
let minimum_balance_for_program = rpc_client
.get_minimum_balance_for_rent_exemption(UpgradeableLoaderState::program_len().unwrap())
.unwrap();
let upgrade_authority = Keypair::new();
let mut config = CliConfig::recent_for_tests();
let keypair = Keypair::new();
config.json_rpc_url = test_validator.rpc_url();
config.signers = vec![&keypair];
config.command = CliCommand::Airdrop {
pubkey: None,
lamports: 100 * minimum_balance_for_programdata + minimum_balance_for_program,
};
process_command(&config).unwrap();
// Deploy the upgradeable program
let program_keypair = Keypair::new();
config.signers = vec![&keypair, &upgrade_authority, &program_keypair];
config.command = CliCommand::Program(ProgramCliCommand::Deploy {
program_location: Some(pathbuf.to_str().unwrap().to_string()),
program_signer_index: Some(2),
program_pubkey: Some(program_keypair.pubkey()),
buffer_signer_index: None,
buffer_pubkey: None,
allow_excessive_balance: false,
upgrade_authority_signer_index: 1,
is_final: false,
max_len: Some(max_len),
});
config.output_format = OutputFormat::JsonCompact;
process_command(&config).unwrap();
let (programdata_pubkey, _) = Pubkey::find_program_address(
&[program_keypair.pubkey().as_ref()],
&bpf_loader_upgradeable::id(),
);
// Close program
let close_account = rpc_client.get_account(&programdata_pubkey).unwrap();
let programdata_lamports = close_account.lamports;
let recipient_pubkey = Pubkey::new_unique();
config.signers = vec![&keypair, &upgrade_authority];
config.command = CliCommand::Program(ProgramCliCommand::Close {
account_pubkey: Some(program_keypair.pubkey()),
recipient_pubkey,
authority_index: 1,
use_lamports_unit: false,
});
process_command(&config).unwrap();
rpc_client.get_account(&programdata_pubkey).unwrap_err();
let recipient_account = rpc_client.get_account(&recipient_pubkey).unwrap();
assert_eq!(programdata_lamports, recipient_account.lamports);
}
#[test]
fn test_cli_program_write_buffer() {
solana_logger::setup();
@@ -645,10 +560,8 @@ fn test_cli_program_write_buffer() {
pathbuf.set_extension("so");
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator =
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
@@ -673,6 +586,8 @@ fn test_cli_program_write_buffer() {
config.json_rpc_url = test_validator.rpc_url();
config.signers = vec![&keypair];
config.command = CliCommand::Airdrop {
faucet_host: None,
faucet_port: faucet_addr.port(),
pubkey: None,
lamports: 100 * minimum_balance_for_buffer,
};
@@ -697,7 +612,7 @@ fn test_cli_program_write_buffer() {
.unwrap()
.as_str()
.unwrap();
let new_buffer_pubkey = Pubkey::from_str(buffer_pubkey_str).unwrap();
let new_buffer_pubkey = Pubkey::from_str(&buffer_pubkey_str).unwrap();
let buffer_account = rpc_client.get_account(&new_buffer_pubkey).unwrap();
assert_eq!(buffer_account.lamports, minimum_balance_for_buffer_default);
assert_eq!(buffer_account.owner, bpf_loader_upgradeable::id());
@@ -732,7 +647,7 @@ fn test_cli_program_write_buffer() {
.unwrap();
assert_eq!(
buffer_keypair.pubkey(),
Pubkey::from_str(buffer_pubkey_str).unwrap()
Pubkey::from_str(&buffer_pubkey_str).unwrap()
);
let buffer_account = rpc_client.get_account(&buffer_keypair.pubkey()).unwrap();
assert_eq!(buffer_account.lamports, minimum_balance_for_buffer);
@@ -752,8 +667,6 @@ fn test_cli_program_write_buffer() {
config.command = CliCommand::Program(ProgramCliCommand::Show {
account_pubkey: Some(buffer_keypair.pubkey()),
authority_pubkey: keypair.pubkey(),
get_programs: false,
get_buffers: false,
all: false,
use_lamports_unit: false,
});
@@ -768,7 +681,7 @@ fn test_cli_program_write_buffer() {
.unwrap();
assert_eq!(
keypair.pubkey(),
Pubkey::from_str(authority_pubkey_str).unwrap()
Pubkey::from_str(&authority_pubkey_str).unwrap()
);
// Specify buffer authority
@@ -793,7 +706,7 @@ fn test_cli_program_write_buffer() {
.unwrap();
assert_eq!(
buffer_keypair.pubkey(),
Pubkey::from_str(buffer_pubkey_str).unwrap()
Pubkey::from_str(&buffer_pubkey_str).unwrap()
);
let buffer_account = rpc_client.get_account(&buffer_keypair.pubkey()).unwrap();
assert_eq!(buffer_account.lamports, minimum_balance_for_buffer_default);
@@ -828,7 +741,7 @@ fn test_cli_program_write_buffer() {
.unwrap()
.as_str()
.unwrap();
let buffer_pubkey = Pubkey::from_str(buffer_pubkey_str).unwrap();
let buffer_pubkey = Pubkey::from_str(&buffer_pubkey_str).unwrap();
let buffer_account = rpc_client.get_account(&buffer_pubkey).unwrap();
assert_eq!(buffer_account.lamports, minimum_balance_for_buffer_default);
assert_eq!(buffer_account.owner, bpf_loader_upgradeable::id());
@@ -847,8 +760,6 @@ fn test_cli_program_write_buffer() {
config.command = CliCommand::Program(ProgramCliCommand::Show {
account_pubkey: Some(buffer_pubkey),
authority_pubkey: keypair.pubkey(),
get_programs: false,
get_buffers: false,
all: false,
use_lamports_unit: false,
});
@@ -863,7 +774,7 @@ fn test_cli_program_write_buffer() {
.unwrap();
assert_eq!(
authority_keypair.pubkey(),
Pubkey::from_str(authority_pubkey_str).unwrap()
Pubkey::from_str(&authority_pubkey_str).unwrap()
);
// Close buffer
@@ -901,7 +812,7 @@ fn test_cli_program_write_buffer() {
.unwrap()
.as_str()
.unwrap();
let new_buffer_pubkey = Pubkey::from_str(buffer_pubkey_str).unwrap();
let new_buffer_pubkey = Pubkey::from_str(&buffer_pubkey_str).unwrap();
// Close buffers and deposit default keypair
let pre_lamports = rpc_client.get_account(&keypair.pubkey()).unwrap().lamports;
@@ -932,10 +843,8 @@ fn test_cli_program_set_buffer_authority() {
pathbuf.set_extension("so");
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator =
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
@@ -955,6 +864,8 @@ fn test_cli_program_set_buffer_authority() {
config.json_rpc_url = test_validator.rpc_url();
config.signers = vec![&keypair];
config.command = CliCommand::Airdrop {
faucet_host: None,
faucet_port: faucet_addr.port(),
pubkey: None,
lamports: 100 * minimum_balance_for_buffer,
};
@@ -997,7 +908,7 @@ fn test_cli_program_set_buffer_authority() {
.as_str()
.unwrap();
assert_eq!(
Pubkey::from_str(new_buffer_authority_str).unwrap(),
Pubkey::from_str(&new_buffer_authority_str).unwrap(),
new_buffer_authority.pubkey()
);
let buffer_account = rpc_client.get_account(&buffer_keypair.pubkey()).unwrap();
@@ -1024,7 +935,7 @@ fn test_cli_program_set_buffer_authority() {
.as_str()
.unwrap();
assert_eq!(
Pubkey::from_str(buffer_authority_str).unwrap(),
Pubkey::from_str(&buffer_authority_str).unwrap(),
buffer_keypair.pubkey()
);
let buffer_account = rpc_client.get_account(&buffer_keypair.pubkey()).unwrap();
@@ -1046,10 +957,8 @@ fn test_cli_program_mismatch_buffer_authority() {
pathbuf.set_extension("so");
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator =
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
@@ -1069,6 +978,8 @@ fn test_cli_program_mismatch_buffer_authority() {
config.json_rpc_url = test_validator.rpc_url();
config.signers = vec![&keypair];
config.command = CliCommand::Airdrop {
faucet_host: None,
faucet_port: faucet_addr.port(),
pubkey: None,
lamports: 100 * minimum_balance_for_buffer,
};
@@ -1136,10 +1047,8 @@ fn test_cli_program_show() {
pathbuf.set_extension("so");
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator =
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
@@ -1162,6 +1071,8 @@ fn test_cli_program_show() {
// Airdrop
config.signers = vec![&keypair];
config.command = CliCommand::Airdrop {
faucet_host: None,
faucet_port: faucet_addr.port(),
pubkey: None,
lamports: 100 * minimum_balance_for_buffer,
};
@@ -1185,8 +1096,6 @@ fn test_cli_program_show() {
config.command = CliCommand::Program(ProgramCliCommand::Show {
account_pubkey: Some(buffer_keypair.pubkey()),
authority_pubkey: keypair.pubkey(),
get_programs: false,
get_buffers: false,
all: false,
use_lamports_unit: false,
});
@@ -1201,7 +1110,7 @@ fn test_cli_program_show() {
.unwrap();
assert_eq!(
buffer_keypair.pubkey(),
Pubkey::from_str(address_str).unwrap()
Pubkey::from_str(&address_str).unwrap()
);
let authority_str = json
.as_object()
@@ -1212,7 +1121,7 @@ fn test_cli_program_show() {
.unwrap();
assert_eq!(
authority_keypair.pubkey(),
Pubkey::from_str(authority_str).unwrap()
Pubkey::from_str(&authority_str).unwrap()
);
let data_len = json
.as_object()
@@ -1247,8 +1156,6 @@ fn test_cli_program_show() {
config.command = CliCommand::Program(ProgramCliCommand::Show {
account_pubkey: Some(program_keypair.pubkey()),
authority_pubkey: keypair.pubkey(),
get_programs: false,
get_buffers: false,
all: false,
use_lamports_unit: false,
});
@@ -1263,7 +1170,7 @@ fn test_cli_program_show() {
.unwrap();
assert_eq!(
program_keypair.pubkey(),
Pubkey::from_str(address_str).unwrap()
Pubkey::from_str(&address_str).unwrap()
);
let programdata_address_str = json
.as_object()
@@ -1278,7 +1185,7 @@ fn test_cli_program_show() {
);
assert_eq!(
programdata_pubkey,
Pubkey::from_str(programdata_address_str).unwrap()
Pubkey::from_str(&programdata_address_str).unwrap()
);
let authority_str = json
.as_object()
@@ -1289,7 +1196,7 @@ fn test_cli_program_show() {
.unwrap();
assert_eq!(
authority_keypair.pubkey(),
Pubkey::from_str(authority_str).unwrap()
Pubkey::from_str(&authority_str).unwrap()
);
let deployed_slot = json
.as_object()
@@ -1321,10 +1228,8 @@ fn test_cli_program_dump() {
pathbuf.set_extension("so");
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator =
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
@@ -1347,6 +1252,8 @@ fn test_cli_program_dump() {
// Airdrop
config.signers = vec![&keypair];
config.command = CliCommand::Airdrop {
faucet_host: None,
faucet_port: faucet_addr.port(),
pubkey: None,
lamports: 100 * minimum_balance_for_buffer,
};

View File

@@ -6,19 +6,19 @@ use solana_sdk::{
commitment_config::CommitmentConfig,
signature::{Keypair, Signer},
};
use solana_streamer::socket::SocketAddrSpace;
#[test]
fn test_cli_request_airdrop() {
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator =
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
let mut bob_config = CliConfig::recent_for_tests();
bob_config.json_rpc_url = test_validator.rpc_url();
bob_config.command = CliCommand::Airdrop {
faucet_host: None,
faucet_port: faucet_addr.port(),
pubkey: None,
lamports: 50,
};

File diff suppressed because it is too large Load Diff

View File

@@ -16,22 +16,14 @@ use solana_sdk::{
nonce::State as NonceState,
pubkey::Pubkey,
signature::{keypair_from_seed, Keypair, NullSigner, Signer},
stake,
};
use solana_streamer::socket::SocketAddrSpace;
#[test]
fn test_transfer() {
solana_logger::setup();
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator = TestValidator::with_custom_fees(
mint_pubkey,
1,
Some(faucet_addr),
SocketAddrSpace::Unspecified,
);
let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
@@ -46,7 +38,8 @@ fn test_transfer() {
let sender_pubkey = config.signers[0].pubkey();
let recipient_pubkey = Pubkey::new(&[1u8; 32]);
request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 50_000).unwrap();
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &sender_pubkey, 50_000, &config)
.unwrap();
check_recent_balance(50_000, &rpc_client, &sender_pubkey);
check_recent_balance(0, &rpc_client, &recipient_pubkey);
@@ -64,7 +57,6 @@ fn test_transfer() {
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,
@@ -85,7 +77,6 @@ fn test_transfer() {
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,
@@ -102,7 +93,7 @@ fn test_transfer() {
process_command(&offline).unwrap_err();
let offline_pubkey = offline.signers[0].pubkey();
request_and_confirm_airdrop(&rpc_client, &offline, &offline_pubkey, 50).unwrap();
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 50, &config).unwrap();
check_recent_balance(50, &rpc_client, &offline_pubkey);
// Offline transfer
@@ -118,7 +109,6 @@ fn test_transfer() {
blockhash_query: BlockhashQuery::None(blockhash),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,
@@ -140,7 +130,6 @@ fn test_transfer() {
blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,
@@ -159,7 +148,6 @@ fn test_transfer() {
nonce_account: 1,
seed: None,
nonce_authority: None,
memo: None,
amount: SpendAmount::Some(minimum_nonce_balance),
};
process_command(&config).unwrap();
@@ -191,7 +179,6 @@ fn test_transfer() {
),
nonce_account: Some(nonce_account.pubkey()),
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,
@@ -214,7 +201,6 @@ fn test_transfer() {
config.command = CliCommand::AuthorizeNonceAccount {
nonce_account: nonce_account.pubkey(),
nonce_authority: 0,
memo: None,
new_authority: offline_pubkey,
};
process_command(&config).unwrap();
@@ -243,7 +229,6 @@ fn test_transfer() {
blockhash_query: BlockhashQuery::None(nonce_hash),
nonce_account: Some(nonce_account.pubkey()),
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,
@@ -267,7 +252,6 @@ fn test_transfer() {
),
nonce_account: Some(nonce_account.pubkey()),
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,
@@ -281,35 +265,32 @@ fn test_transfer() {
fn test_transfer_multisession_signing() {
solana_logger::setup();
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator = TestValidator::with_custom_fees(
mint_pubkey,
1,
Some(faucet_addr),
SocketAddrSpace::Unspecified,
);
let to_pubkey = Pubkey::new(&[1u8; 32]);
let offline_from_signer = keypair_from_seed(&[2u8; 32]).unwrap();
let offline_fee_payer_signer = keypair_from_seed(&[3u8; 32]).unwrap();
let from_null_signer = NullSigner::new(&offline_from_signer.pubkey());
let config = CliConfig::recent_for_tests();
// Setup accounts
let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
request_and_confirm_airdrop(
&rpc_client,
&CliConfig::recent_for_tests(),
&faucet_addr,
&offline_from_signer.pubkey(),
43,
&config,
)
.unwrap();
request_and_confirm_airdrop(
&rpc_client,
&CliConfig::recent_for_tests(),
&faucet_addr,
&offline_fee_payer_signer.pubkey(),
3,
&config,
)
.unwrap();
check_recent_balance(43, &rpc_client, &offline_from_signer.pubkey());
@@ -338,7 +319,6 @@ fn test_transfer_multisession_signing() {
blockhash_query: BlockhashQuery::None(blockhash),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,
@@ -369,7 +349,6 @@ fn test_transfer_multisession_signing() {
blockhash_query: BlockhashQuery::None(blockhash),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,
@@ -397,7 +376,6 @@ fn test_transfer_multisession_signing() {
blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,
@@ -413,14 +391,8 @@ fn test_transfer_multisession_signing() {
fn test_transfer_all() {
solana_logger::setup();
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator = TestValidator::with_custom_fees(
mint_pubkey,
1,
Some(faucet_addr),
SocketAddrSpace::Unspecified,
);
let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
@@ -434,7 +406,8 @@ fn test_transfer_all() {
let sender_pubkey = config.signers[0].pubkey();
let recipient_pubkey = Pubkey::new(&[1u8; 32]);
request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 50_000).unwrap();
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &sender_pubkey, 50_000, &config)
.unwrap();
check_recent_balance(50_000, &rpc_client, &sender_pubkey);
check_recent_balance(0, &rpc_client, &recipient_pubkey);
@@ -452,7 +425,6 @@ fn test_transfer_all() {
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,
@@ -466,14 +438,8 @@ fn test_transfer_all() {
fn test_transfer_unfunded_recipient() {
solana_logger::setup();
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator = TestValidator::with_custom_fees(
mint_pubkey,
1,
Some(faucet_addr),
SocketAddrSpace::Unspecified,
);
let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
@@ -487,7 +453,8 @@ fn test_transfer_unfunded_recipient() {
let sender_pubkey = config.signers[0].pubkey();
let recipient_pubkey = Pubkey::new(&[1u8; 32]);
request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 50_000).unwrap();
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &sender_pubkey, 50_000, &config)
.unwrap();
check_recent_balance(50_000, &rpc_client, &sender_pubkey);
check_recent_balance(0, &rpc_client, &recipient_pubkey);
@@ -505,7 +472,6 @@ fn test_transfer_unfunded_recipient() {
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,
@@ -519,14 +485,8 @@ fn test_transfer_unfunded_recipient() {
fn test_transfer_with_seed() {
solana_logger::setup();
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator = TestValidator::with_custom_fees(
mint_pubkey,
1,
Some(faucet_addr),
SocketAddrSpace::Unspecified,
);
let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
@@ -540,7 +500,7 @@ fn test_transfer_with_seed() {
let sender_pubkey = config.signers[0].pubkey();
let recipient_pubkey = Pubkey::new(&[1u8; 32]);
let derived_address_seed = "seed".to_string();
let derived_address_program_id = stake::program::id();
let derived_address_program_id = solana_stake_program::id();
let derived_address = Pubkey::create_with_seed(
&sender_pubkey,
&derived_address_seed,
@@ -548,8 +508,9 @@ fn test_transfer_with_seed() {
)
.unwrap();
request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 1).unwrap();
request_and_confirm_airdrop(&rpc_client, &config, &derived_address, 50_000).unwrap();
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &sender_pubkey, 1, &config).unwrap();
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &derived_address, 50_000, &config)
.unwrap();
check_recent_balance(1, &rpc_client, &sender_pubkey);
check_recent_balance(50_000, &rpc_client, &derived_address);
check_recent_balance(0, &rpc_client, &recipient_pubkey);
@@ -568,7 +529,6 @@ fn test_transfer_with_seed() {
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: Some(derived_address_seed),
derived_address_program_id: Some(derived_address_program_id),

View File

@@ -14,16 +14,13 @@ use solana_sdk::{
commitment_config::CommitmentConfig,
signature::{Keypair, Signer},
};
use solana_streamer::socket::SocketAddrSpace;
use solana_vote_program::vote_state::{VoteAuthorize, VoteState, VoteStateVersions};
#[test]
fn test_vote_authorize_and_withdraw() {
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator =
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
@@ -33,8 +30,14 @@ fn test_vote_authorize_and_withdraw() {
config.json_rpc_url = test_validator.rpc_url();
config.signers = vec![&default_signer];
request_and_confirm_airdrop(&rpc_client, &config, &config.signers[0].pubkey(), 100_000)
.unwrap();
request_and_confirm_airdrop(
&rpc_client,
&faucet_addr,
&config.signers[0].pubkey(),
100_000,
&config,
)
.unwrap();
// Create vote account
let vote_account_keypair = Keypair::new();
@@ -45,9 +48,8 @@ fn test_vote_authorize_and_withdraw() {
seed: None,
identity_account: 0,
authorized_voter: None,
authorized_withdrawer: config.signers[0].pubkey(),
authorized_withdrawer: Some(config.signers[0].pubkey()),
commission: 0,
memo: None,
};
process_command(&config).unwrap();
let vote_account = rpc_client
@@ -75,7 +77,6 @@ fn test_vote_authorize_and_withdraw() {
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
derived_address_seed: None,
derived_address_program_id: None,
@@ -85,48 +86,12 @@ fn test_vote_authorize_and_withdraw() {
check_recent_balance(expected_balance, &rpc_client, &vote_account_pubkey);
// Authorize vote account withdrawal to another signer
let first_withdraw_authority = Keypair::new();
let withdraw_authority = Keypair::new();
config.signers = vec![&default_signer];
config.command = CliCommand::VoteAuthorize {
vote_account_pubkey,
new_authorized_pubkey: first_withdraw_authority.pubkey(),
vote_authorize: VoteAuthorize::Withdrawer,
memo: None,
authorized: 0,
new_authorized: None,
};
process_command(&config).unwrap();
let vote_account = rpc_client
.get_account(&vote_account_keypair.pubkey())
.unwrap();
let vote_state: VoteStateVersions = vote_account.state().unwrap();
let authorized_withdrawer = vote_state.convert_to_current().authorized_withdrawer;
assert_eq!(authorized_withdrawer, first_withdraw_authority.pubkey());
// Authorize vote account withdrawal to another signer with checked instruction
let withdraw_authority = Keypair::new();
config.signers = vec![&default_signer, &first_withdraw_authority];
config.command = CliCommand::VoteAuthorize {
vote_account_pubkey,
new_authorized_pubkey: withdraw_authority.pubkey(),
vote_authorize: VoteAuthorize::Withdrawer,
memo: None,
authorized: 1,
new_authorized: Some(1),
};
process_command(&config).unwrap_err(); // unsigned by new authority should fail
config.signers = vec![
&default_signer,
&first_withdraw_authority,
&withdraw_authority,
];
config.command = CliCommand::VoteAuthorize {
vote_account_pubkey,
new_authorized_pubkey: withdraw_authority.pubkey(),
vote_authorize: VoteAuthorize::Withdrawer,
memo: None,
authorized: 1,
new_authorized: Some(2),
};
process_command(&config).unwrap();
let vote_account = rpc_client
@@ -144,7 +109,6 @@ fn test_vote_authorize_and_withdraw() {
withdraw_authority: 1,
withdraw_amount: SpendAmount::Some(100),
destination_account_pubkey: destination_account,
memo: None,
};
process_command(&config).unwrap();
check_recent_balance(expected_balance - 100, &rpc_client, &vote_account_pubkey);
@@ -157,7 +121,6 @@ fn test_vote_authorize_and_withdraw() {
vote_account_pubkey,
new_identity_account: 2,
withdraw_authority: 1,
memo: None,
};
process_command(&config).unwrap();
}

View File

@@ -1,6 +1,6 @@
[package]
name = "solana-client"
version = "1.7.13"
version = "1.6.2"
description = "Solana Client"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
@@ -15,7 +15,7 @@ bincode = "1.3.1"
bs58 = "0.3.1"
clap = "2.33.0"
indicatif = "0.15.0"
jsonrpc-core = "18.0.0"
jsonrpc-core = "17.0.0"
log = "0.4.11"
net2 = "0.2.37"
rayon = "1.5.0"
@@ -24,23 +24,21 @@ semver = "0.11.0"
serde = "1.0.122"
serde_derive = "1.0.103"
serde_json = "1.0.56"
solana-account-decoder = { path = "../account-decoder", version = "=1.7.13" }
solana-clap-utils = { path = "../clap-utils", version = "=1.7.13" }
solana-faucet = { path = "../faucet", version = "=1.7.13" }
solana-net-utils = { path = "../net-utils", version = "=1.7.13" }
solana-sdk = { path = "../sdk", version = "=1.7.13" }
solana-transaction-status = { path = "../transaction-status", version = "=1.7.13" }
solana-version = { path = "../version", version = "=1.7.13" }
solana-vote-program = { path = "../programs/vote", version = "=1.7.13" }
solana-account-decoder = { path = "../account-decoder", version = "=1.6.2" }
solana-clap-utils = { path = "../clap-utils", version = "=1.6.2" }
solana-net-utils = { path = "../net-utils", version = "=1.6.2" }
solana-sdk = { path = "../sdk", version = "=1.6.2" }
solana-transaction-status = { path = "../transaction-status", version = "=1.6.2" }
solana-version = { path = "../version", version = "=1.6.2" }
solana-vote-program = { path = "../programs/vote", version = "=1.6.2" }
thiserror = "1.0"
tokio = { version = "1", features = ["full"] }
tungstenite = "0.10.1"
url = "2.1.1"
[dev-dependencies]
assert_matches = "1.3.0"
jsonrpc-http-server = "18.0.0"
solana-logger = { path = "../logger", version = "=1.7.13" }
jsonrpc-http-server = "17.0.0"
solana-logger = { path = "../logger", version = "=1.6.2" }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@@ -122,10 +122,10 @@ mod tests {
use crate::{
blockhash_query,
rpc_request::RpcRequest,
rpc_response::{Response, RpcFeeCalculator, RpcFees, RpcResponseContext},
rpc_response::{Response, RpcFeeCalculator, RpcResponseContext},
};
use clap::App;
use serde_json::{self, json};
use serde_json::{self, json, Value};
use solana_account_decoder::{UiAccount, UiAccountEncoding};
use solana_sdk::{account::Account, hash::hash, nonce, system_program};
use std::collections::HashMap;
@@ -288,12 +288,10 @@ mod tests {
let rpc_fee_calc = FeeCalculator::new(42);
let get_recent_blockhash_response = json!(Response {
context: RpcResponseContext { slot: 1 },
value: json!(RpcFees {
blockhash: rpc_blockhash.to_string(),
fee_calculator: rpc_fee_calc.clone(),
last_valid_slot: 42,
last_valid_block_height: 42,
}),
value: json!((
Value::String(rpc_blockhash.to_string()),
serde_json::to_value(rpc_fee_calc.clone()).unwrap()
)),
});
let get_fee_calculator_for_blockhash_response = json!(Response {
context: RpcResponseContext { slot: 1 },
@@ -302,7 +300,10 @@ mod tests {
}),
});
let mut mocks = HashMap::new();
mocks.insert(RpcRequest::GetFees, get_recent_blockhash_response.clone());
mocks.insert(
RpcRequest::GetRecentBlockhash,
get_recent_blockhash_response.clone(),
);
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
assert_eq!(
BlockhashQuery::default()
@@ -311,7 +312,10 @@ mod tests {
(rpc_blockhash, rpc_fee_calc.clone()),
);
let mut mocks = HashMap::new();
mocks.insert(RpcRequest::GetFees, get_recent_blockhash_response.clone());
mocks.insert(
RpcRequest::GetRecentBlockhash,
get_recent_blockhash_response.clone(),
);
mocks.insert(
RpcRequest::GetFeeCalculatorForBlockhash,
get_fee_calculator_for_blockhash_response,
@@ -324,7 +328,10 @@ mod tests {
(test_blockhash, rpc_fee_calc),
);
let mut mocks = HashMap::new();
mocks.insert(RpcRequest::GetFees, get_recent_blockhash_response);
mocks.insert(
RpcRequest::GetRecentBlockhash,
get_recent_blockhash_response,
);
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
assert_eq!(
BlockhashQuery::None(test_blockhash)
@@ -354,7 +361,7 @@ mod tests {
let nonce_pubkey = Pubkey::new(&[4u8; 32]);
let rpc_nonce_account = UiAccount::encode(
&nonce_pubkey,
&nonce_account,
nonce_account,
UiAccountEncoding::Base64,
None,
None,

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